Updated translation support
This commit is contained in:
parent
b1e8cd68cd
commit
d25565b6f8
114 changed files with 42095 additions and 1902 deletions
3
.eslintrc
Normal file
3
.eslintrc
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
{
|
||||||
|
"extends": "nodemailer"
|
||||||
|
}
|
70
.eslintrc.js
70
.eslintrc.js
|
@ -1,70 +0,0 @@
|
||||||
'use strict';
|
|
||||||
|
|
||||||
module.exports = {
|
|
||||||
rules: {
|
|
||||||
indent: [2, 4, {
|
|
||||||
SwitchCase: 1
|
|
||||||
}],
|
|
||||||
quotes: [2, 'single'],
|
|
||||||
'linebreak-style': [2, 'unix'],
|
|
||||||
semi: [2, 'always'],
|
|
||||||
strict: [2, 'global'],
|
|
||||||
eqeqeq: 2,
|
|
||||||
'dot-notation': 2,
|
|
||||||
curly: 2,
|
|
||||||
'no-fallthrough': 2,
|
|
||||||
'quote-props': [2, 'as-needed'],
|
|
||||||
'no-unused-expressions': [2, {
|
|
||||||
allowShortCircuit: true
|
|
||||||
}],
|
|
||||||
'no-unused-vars': 2,
|
|
||||||
'no-undefined': 2,
|
|
||||||
'handle-callback-err': 2,
|
|
||||||
'no-new': 2,
|
|
||||||
'new-cap': 2,
|
|
||||||
'no-eval': 2,
|
|
||||||
'no-invalid-this': 2,
|
|
||||||
radix: [2, 'always'],
|
|
||||||
'no-use-before-define': [2, 'nofunc'],
|
|
||||||
'callback-return': [2, ['callback', 'cb', 'done']],
|
|
||||||
'comma-dangle': [2, 'never'],
|
|
||||||
'comma-style': [2, 'last'],
|
|
||||||
'no-regex-spaces': 2,
|
|
||||||
'no-empty': 2,
|
|
||||||
'no-duplicate-case': 2,
|
|
||||||
'no-empty-character-class': 2,
|
|
||||||
'no-redeclare': [2, {
|
|
||||||
builtinGlobals: true
|
|
||||||
}],
|
|
||||||
'block-scoped-var': 2,
|
|
||||||
'no-sequences': 2,
|
|
||||||
'no-throw-literal': 2,
|
|
||||||
'no-useless-call': 2,
|
|
||||||
'no-useless-concat': 2,
|
|
||||||
'no-void': 2,
|
|
||||||
yoda: 2,
|
|
||||||
'no-undef': 2,
|
|
||||||
'global-require': 2,
|
|
||||||
'no-var': 2,
|
|
||||||
'no-bitwise': 2,
|
|
||||||
'no-lonely-if': 2,
|
|
||||||
'no-mixed-spaces-and-tabs': 2,
|
|
||||||
'arrow-body-style': [2, 'as-needed'],
|
|
||||||
'arrow-parens': [2, 'as-needed'],
|
|
||||||
'prefer-arrow-callback': 2,
|
|
||||||
'object-shorthand': 2,
|
|
||||||
'prefer-spread': 2
|
|
||||||
},
|
|
||||||
env: {
|
|
||||||
es6: true,
|
|
||||||
node: true
|
|
||||||
},
|
|
||||||
extends: 'eslint:recommended',
|
|
||||||
globals: {
|
|
||||||
it: true,
|
|
||||||
describe: true,
|
|
||||||
beforeEach: true,
|
|
||||||
afterEach: true
|
|
||||||
},
|
|
||||||
fix: true
|
|
||||||
};
|
|
10
README.md
10
README.md
|
@ -180,7 +180,15 @@ This command generates a CSV file with 100 000 subscriber accounts
|
||||||
|
|
||||||
## Translations
|
## Translations
|
||||||
|
|
||||||
Mailtrain is currently not translated but it supports translations. To add translations you first need to add translation support for the translatable strings
|
Mailtrain is currently not translated but it supports translations. To add translations you first need to add translation support for the translatable strings. To test if strings are translatable or not, use a fake language with code "zz"
|
||||||
|
|
||||||
|
```toml
|
||||||
|
language="zz"
|
||||||
|
```
|
||||||
|
|
||||||
|
This would modify all input strings. If a string is not modified then it does not support translations.
|
||||||
|
|
||||||
|
![](https://cldup.com/qXxAbaq2F1.png)
|
||||||
|
|
||||||
### Translating JavaScript files
|
### Translating JavaScript files
|
||||||
|
|
||||||
|
|
10
app.js
10
app.js
|
@ -3,7 +3,7 @@
|
||||||
let config = require('config');
|
let config = require('config');
|
||||||
let log = require('npmlog');
|
let log = require('npmlog');
|
||||||
|
|
||||||
let translate = require('./lib/translate');
|
let _ = require('./lib/translate')._;
|
||||||
let util = require('util');
|
let util = require('util');
|
||||||
|
|
||||||
let express = require('express');
|
let express = require('express');
|
||||||
|
@ -103,7 +103,7 @@ hbs.registerHelper('translate', function (context, options) { // eslint-disable-
|
||||||
context = false;
|
context = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
let result = translate._(options.fn(this)); // eslint-disable-line no-invalid-this
|
let result = _(options.fn(this)); // eslint-disable-line no-invalid-this
|
||||||
|
|
||||||
if (Array.isArray(context)) {
|
if (Array.isArray(context)) {
|
||||||
result = util.format(result, ...context);
|
result = util.format(result, ...context);
|
||||||
|
@ -137,7 +137,7 @@ app.use(session({
|
||||||
app.use(flash());
|
app.use(flash());
|
||||||
|
|
||||||
app.use((req, res, next) => {
|
app.use((req, res, next) => {
|
||||||
req._ = str => translate._(str);
|
req._ = str => _(str);
|
||||||
next();
|
next();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -166,7 +166,7 @@ app.use((req, res, next) => {
|
||||||
};
|
};
|
||||||
|
|
||||||
let menu = [{
|
let menu = [{
|
||||||
title: 'Home',
|
title: _('Home'),
|
||||||
url: '/',
|
url: '/',
|
||||||
selected: true
|
selected: true
|
||||||
}];
|
}];
|
||||||
|
@ -208,7 +208,7 @@ app.use('/api', api);
|
||||||
|
|
||||||
// catch 404 and forward to error handler
|
// catch 404 and forward to error handler
|
||||||
app.use((req, res, next) => {
|
app.use((req, res, next) => {
|
||||||
let err = new Error('Not Found');
|
let err = new Error(_('Not Found'));
|
||||||
err.status = 404;
|
err.status = 404;
|
||||||
next(err);
|
next(err);
|
||||||
});
|
});
|
||||||
|
|
BIN
languages/et.mo
BIN
languages/et.mo
Binary file not shown.
2841
languages/et.po
2841
languages/et.po
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
26
lib/fakelang.js
Normal file
26
lib/fakelang.js
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
/* lloyd|2012|http://wtfpl.org */
|
||||||
|
|
||||||
|
/* eslint-disable */
|
||||||
|
|
||||||
|
module.exports = str => {
|
||||||
|
let from = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!@#$%^&*()-_=+\\|`~[{]};:'\",<.>/?";
|
||||||
|
let to = "ɐqɔpǝɟƃɥıɾʞʅɯuodbɹsʇnʌʍxʎz∀ԐↃᗡƎℲ⅁HIſӼ⅂WNOԀÒᴚS⊥∩ɅMX⅄Z0123456789¡@#$%ᵥ⅋⁎()-_=+\\|,~[{]};:,„´<.>/¿";
|
||||||
|
|
||||||
|
return str.replace(/(\{\{[^\}]+\}\}|%s)/g, '\x00\x04$1\x00').split('\x00').map(c => {
|
||||||
|
if (c.charAt(0) === '\x04') {
|
||||||
|
return c;
|
||||||
|
}
|
||||||
|
let r = '';
|
||||||
|
for (let i = 0, len = c.length; i < len; i++) {
|
||||||
|
let pos = from.indexOf(c.charAt(i));
|
||||||
|
if (pos < 0) {
|
||||||
|
r += c.charAt(i);
|
||||||
|
} else {
|
||||||
|
r += to.charAt(pos);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return r;
|
||||||
|
}).join('\x00').replace(/[\x00\x04]/g, '');
|
||||||
|
}
|
|
@ -2,6 +2,8 @@
|
||||||
|
|
||||||
let FeedParser = require('feedparser');
|
let FeedParser = require('feedparser');
|
||||||
let request = require('request');
|
let request = require('request');
|
||||||
|
let _ = require('./translate')._;
|
||||||
|
let util = require('util');
|
||||||
|
|
||||||
module.exports.fetch = (url, callback) => {
|
module.exports.fetch = (url, callback) => {
|
||||||
let req = request(url);
|
let req = request(url);
|
||||||
|
@ -26,7 +28,7 @@ module.exports.fetch = (url, callback) => {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (res.statusCode !== 200) {
|
if (res.statusCode !== 200) {
|
||||||
return req.emit('error', new Error('Bad status code'));
|
return req.emit('error', new Error(util.format(_('Bad status code %s'), res.statusCode)));
|
||||||
}
|
}
|
||||||
|
|
||||||
req.pipe(feedparser);
|
req.pipe(feedparser);
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
|
|
||||||
let lists = require('./models/lists');
|
let lists = require('./models/lists');
|
||||||
let fields = require('./models/fields');
|
let fields = require('./models/fields');
|
||||||
|
let _ = require('./translate')._;
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
getDefaultMergeTags,
|
getDefaultMergeTags,
|
||||||
|
@ -13,34 +14,34 @@ function getDefaultMergeTags(callback) {
|
||||||
callback(null, [
|
callback(null, [
|
||||||
{
|
{
|
||||||
key: 'LINK_UNSUBSCRIBE',
|
key: 'LINK_UNSUBSCRIBE',
|
||||||
value: 'URL that points to the unsubscribe page'
|
value: _('URL that points to the unsubscribe page')
|
||||||
}, {
|
}, {
|
||||||
key: 'LINK_PREFERENCES',
|
key: 'LINK_PREFERENCES',
|
||||||
value: 'URL that points to the preferences page of the subscriber'
|
value: _('URL that points to the preferences page of the subscriber')
|
||||||
}, {
|
}, {
|
||||||
key: 'LINK_BROWSER',
|
key: 'LINK_BROWSER',
|
||||||
value: 'URL to preview the message in a browser'
|
value: _('URL to preview the message in a browser')
|
||||||
}, {
|
}, {
|
||||||
key: 'EMAIL',
|
key: 'EMAIL',
|
||||||
value: 'Email address'
|
value: _('Email address')
|
||||||
}, {
|
}, {
|
||||||
key: 'FIRST_NAME',
|
key: 'FIRST_NAME',
|
||||||
value: 'First name'
|
value: _('First name')
|
||||||
}, {
|
}, {
|
||||||
key: 'LAST_NAME',
|
key: 'LAST_NAME',
|
||||||
value: 'Last name'
|
value: _('Last name')
|
||||||
}, {
|
}, {
|
||||||
key: 'FULL_NAME',
|
key: 'FULL_NAME',
|
||||||
value: 'Full name (first and last name combined)'
|
value: _('Full name (first and last name combined)')
|
||||||
}, {
|
}, {
|
||||||
key: 'SUBSCRIPTION_ID',
|
key: 'SUBSCRIPTION_ID',
|
||||||
value: 'Unique ID that identifies the recipient'
|
value: _('Unique ID that identifies the recipient')
|
||||||
}, {
|
}, {
|
||||||
key: 'LIST_ID',
|
key: 'LIST_ID',
|
||||||
value: 'Unique ID that identifies the list used for this campaign'
|
value: _('Unique ID that identifies the list used for this campaign')
|
||||||
}, {
|
}, {
|
||||||
key: 'CAMPAIGN_ID',
|
key: 'CAMPAIGN_ID',
|
||||||
value: 'Unique ID that identifies current campaign'
|
value: _('Unique ID that identifies current campaign')
|
||||||
}
|
}
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,6 +14,23 @@ let templates = new Map();
|
||||||
let htmlToText = require('html-to-text');
|
let htmlToText = require('html-to-text');
|
||||||
let aws = require('aws-sdk');
|
let aws = require('aws-sdk');
|
||||||
|
|
||||||
|
let _ = require('./translate')._;
|
||||||
|
let util = require('util');
|
||||||
|
|
||||||
|
Handlebars.registerHelper('translate', function (context, options) { // eslint-disable-line prefer-arrow-callback
|
||||||
|
if (typeof options === 'undefined' && context) {
|
||||||
|
options = context;
|
||||||
|
context = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
let result = _(options.fn(this)); // eslint-disable-line no-invalid-this
|
||||||
|
|
||||||
|
if (Array.isArray(context)) {
|
||||||
|
result = util.format(result, ...context);
|
||||||
|
}
|
||||||
|
return new Handlebars.SafeString(result);
|
||||||
|
});
|
||||||
|
|
||||||
module.exports.transport = false;
|
module.exports.transport = false;
|
||||||
|
|
||||||
module.exports.update = () => {
|
module.exports.update = () => {
|
||||||
|
@ -195,7 +212,7 @@ function createMailer(callback) {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
} else {
|
} else {
|
||||||
return callback(new Error('Invalid mail transport'));
|
return callback(new Error(_('Invalid mail transport')));
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports.transport = nodemailer.createTransport(transportOptions, config.nodemailer);
|
module.exports.transport = nodemailer.createTransport(transportOptions, config.nodemailer);
|
||||||
|
|
|
@ -12,6 +12,7 @@ let feed = require('../feed');
|
||||||
let log = require('npmlog');
|
let log = require('npmlog');
|
||||||
let mailer = require('../mailer');
|
let mailer = require('../mailer');
|
||||||
let humanize = require('humanize');
|
let humanize = require('humanize');
|
||||||
|
let _ = require('../translate')._;
|
||||||
|
|
||||||
let allowedKeys = ['description', 'from', 'address', 'reply_to', 'subject', 'editor_name', 'editor_data', 'template', 'source_url', 'list', 'segment', 'html', 'text', 'tracking_disabled'];
|
let allowedKeys = ['description', 'from', 'address', 'reply_to', 'subject', 'editor_name', 'editor_data', 'template', 'source_url', 'list', 'segment', 'html', 'text', 'tracking_disabled'];
|
||||||
|
|
||||||
|
@ -267,7 +268,7 @@ module.exports.filterStatusSubscribers = (campaign, status, request, columns, ca
|
||||||
module.exports.getByCid = (cid, callback) => {
|
module.exports.getByCid = (cid, callback) => {
|
||||||
cid = (cid || '').toString().trim();
|
cid = (cid || '').toString().trim();
|
||||||
if (!cid) {
|
if (!cid) {
|
||||||
return callback(new Error('Missing Campaign ID'));
|
return callback(new Error(_('Missing Campaign ID')));
|
||||||
}
|
}
|
||||||
db.getConnection((err, connection) => {
|
db.getConnection((err, connection) => {
|
||||||
if (err) {
|
if (err) {
|
||||||
|
@ -294,7 +295,7 @@ module.exports.get = (id, withSegment, callback) => {
|
||||||
id = Number(id) || 0;
|
id = Number(id) || 0;
|
||||||
|
|
||||||
if (id < 1) {
|
if (id < 1) {
|
||||||
return callback(new Error('Missing Campaign ID'));
|
return callback(new Error(_('Missing Campaign ID')));
|
||||||
}
|
}
|
||||||
|
|
||||||
db.getConnection((err, connection) => {
|
db.getConnection((err, connection) => {
|
||||||
|
@ -367,7 +368,7 @@ module.exports.getAttachments = (campaign, callback) => {
|
||||||
campaign = Number(campaign) || 0;
|
campaign = Number(campaign) || 0;
|
||||||
|
|
||||||
if (campaign < 1) {
|
if (campaign < 1) {
|
||||||
return callback(new Error('Missing Campaign ID'));
|
return callback(new Error(_('Missing Campaign ID')));
|
||||||
}
|
}
|
||||||
|
|
||||||
db.getConnection((err, connection) => {
|
db.getConnection((err, connection) => {
|
||||||
|
@ -403,7 +404,7 @@ module.exports.addAttachment = (id, attachment, callback) => {
|
||||||
let size = attachment.content ? attachment.content.length : 0;
|
let size = attachment.content ? attachment.content.length : 0;
|
||||||
|
|
||||||
if (!size) {
|
if (!size) {
|
||||||
return callback(new Error('Emtpy or too large attahcment'));
|
return callback(new Error(_('Emtpy or too large attahcment')));
|
||||||
}
|
}
|
||||||
db.getConnection((err, connection) => {
|
db.getConnection((err, connection) => {
|
||||||
if (err) {
|
if (err) {
|
||||||
|
@ -490,7 +491,7 @@ module.exports.getLinks = (id, linkId, callback) => {
|
||||||
linkId = Number(linkId) || 0;
|
linkId = Number(linkId) || 0;
|
||||||
|
|
||||||
if (id < 1) {
|
if (id < 1) {
|
||||||
return callback(new Error('Missing Campaign ID'));
|
return callback(new Error(_('Missing Campaign ID')));
|
||||||
}
|
}
|
||||||
|
|
||||||
db.getConnection((err, connection) => {
|
db.getConnection((err, connection) => {
|
||||||
|
@ -569,11 +570,11 @@ module.exports.create = (campaign, opts, callback) => {
|
||||||
campaign.template = Number(campaign.template) || 0;
|
campaign.template = Number(campaign.template) || 0;
|
||||||
|
|
||||||
if (!name) {
|
if (!name) {
|
||||||
return callback(new Error('Campaign Name must be set'));
|
return callback(new Error(_('Campaign Name must be set')));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (campaign.type === 2 && (!campaign.sourceUrl || !isUrl(campaign.sourceUrl))) {
|
if (campaign.type === 2 && (!campaign.sourceUrl || !isUrl(campaign.sourceUrl))) {
|
||||||
return callback(new Error('RSS URL must be set and needs to be a valid URL'));
|
return callback(new Error(_('RSS URL must be set and needs to be a valid URL')));
|
||||||
}
|
}
|
||||||
|
|
||||||
let getList = (listId, callback) => {
|
let getList = (listId, callback) => {
|
||||||
|
@ -726,7 +727,7 @@ module.exports.create = (campaign, opts, callback) => {
|
||||||
return callback(err);
|
return callback(err);
|
||||||
}
|
}
|
||||||
if (!template) {
|
if (!template) {
|
||||||
return callback(new Error('Selected template not found'));
|
return callback(new Error(_('Selected template not found')));
|
||||||
}
|
}
|
||||||
|
|
||||||
campaign.editorName = template.editorName;
|
campaign.editorName = template.editorName;
|
||||||
|
@ -748,7 +749,7 @@ module.exports.update = (id, updates, callback) => {
|
||||||
id = Number(id) || 0;
|
id = Number(id) || 0;
|
||||||
|
|
||||||
if (id < 1) {
|
if (id < 1) {
|
||||||
return callback(new Error('Missing Campaign ID'));
|
return callback(new Error(_('Missing Campaign ID')));
|
||||||
}
|
}
|
||||||
|
|
||||||
let campaign = tools.convertKeys(updates);
|
let campaign = tools.convertKeys(updates);
|
||||||
|
@ -757,7 +758,7 @@ module.exports.update = (id, updates, callback) => {
|
||||||
campaign.trackingDisabled = campaign.trackingDisabled ? 1 : 0;
|
campaign.trackingDisabled = campaign.trackingDisabled ? 1 : 0;
|
||||||
|
|
||||||
if (!name) {
|
if (!name) {
|
||||||
return callback(new Error('Campaign Name must be set'));
|
return callback(new Error(_('Campaign Name must be set')));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (/^\d+:\d+$/.test(campaign.list)) {
|
if (/^\d+:\d+$/.test(campaign.list)) {
|
||||||
|
@ -877,7 +878,7 @@ module.exports.delete = (id, callback) => {
|
||||||
id = Number(id) || 0;
|
id = Number(id) || 0;
|
||||||
|
|
||||||
if (id < 1) {
|
if (id < 1) {
|
||||||
return callback(new Error('Missing Campaign ID'));
|
return callback(new Error(_('Missing Campaign ID')));
|
||||||
}
|
}
|
||||||
|
|
||||||
db.getConnection((err, connection) => {
|
db.getConnection((err, connection) => {
|
||||||
|
@ -1078,7 +1079,7 @@ module.exports.getMail = (campaignId, listId, subscriptionId, callback) => {
|
||||||
subscriptionId = Number(subscriptionId) || 0;
|
subscriptionId = Number(subscriptionId) || 0;
|
||||||
|
|
||||||
if (campaignId < 1 || listId < 1 || subscriptionId < 1) {
|
if (campaignId < 1 || listId < 1 || subscriptionId < 1) {
|
||||||
return callback(new Error('Invalid or missing message ID'));
|
return callback(new Error(_('Invalid or missing message ID')));
|
||||||
}
|
}
|
||||||
|
|
||||||
db.getConnection((err, connection) => {
|
db.getConnection((err, connection) => {
|
||||||
|
|
|
@ -6,26 +6,28 @@ let slugify = require('slugify');
|
||||||
let lists = require('./lists');
|
let lists = require('./lists');
|
||||||
let shortid = require('shortid');
|
let shortid = require('shortid');
|
||||||
let Handlebars = require('handlebars');
|
let Handlebars = require('handlebars');
|
||||||
|
let _ = require('../translate')._;
|
||||||
|
let util = require('util');
|
||||||
|
|
||||||
let allowedKeys = ['name', 'key', 'default_value', 'group', 'group_template', 'visible'];
|
let allowedKeys = ['name', 'key', 'default_value', 'group', 'group_template', 'visible'];
|
||||||
let allowedTypes;
|
let allowedTypes;
|
||||||
|
|
||||||
module.exports.grouped = ['radio', 'checkbox', 'dropdown'];
|
module.exports.grouped = ['radio', 'checkbox', 'dropdown'];
|
||||||
module.exports.types = {
|
module.exports.types = {
|
||||||
text: 'Text',
|
text: _('Text'),
|
||||||
website: 'Website',
|
website: _('Website'),
|
||||||
longtext: 'Multi-line text',
|
longtext: _('Multi-line text'),
|
||||||
gpg: 'GPG Public Key',
|
gpg: _('GPG Public Key'),
|
||||||
number: 'Number',
|
number: _('Number'),
|
||||||
radio: 'Radio Buttons',
|
radio: _('Radio Buttons'),
|
||||||
checkbox: 'Checkboxes',
|
checkbox: _('Checkboxes'),
|
||||||
dropdown: 'Drop Down',
|
dropdown: _('Drop Down'),
|
||||||
'date-us': 'Date (MM/DD/YYY)',
|
'date-us': _('Date (MM/DD/YYY)'),
|
||||||
'date-eur': 'Date (DD/MM/YYYY)',
|
'date-eur': _('Date (DD/MM/YYYY)'),
|
||||||
'birthday-us': 'Birthday (MM/DD)',
|
'birthday-us': _('Birthday (MM/DD)'),
|
||||||
'birthday-eur': 'Birthday (DD/MM)',
|
'birthday-eur': _('Birthday (DD/MM)'),
|
||||||
json: 'JSON value for custom rendering',
|
json: _('JSON value for custom rendering'),
|
||||||
option: 'Option'
|
option: _('Option')
|
||||||
};
|
};
|
||||||
|
|
||||||
module.exports.allowedTypes = allowedTypes = Object.keys(module.exports.types);
|
module.exports.allowedTypes = allowedTypes = Object.keys(module.exports.types);
|
||||||
|
@ -48,7 +50,7 @@ module.exports.list = (listId, callback) => {
|
||||||
listId = Number(listId) || 0;
|
listId = Number(listId) || 0;
|
||||||
|
|
||||||
if (listId < 1) {
|
if (listId < 1) {
|
||||||
return callback(new Error('Missing List ID'));
|
return callback(new Error(_('Missing List ID')));
|
||||||
}
|
}
|
||||||
|
|
||||||
db.getConnection((err, connection) => {
|
db.getConnection((err, connection) => {
|
||||||
|
@ -93,7 +95,7 @@ module.exports.get = (id, callback) => {
|
||||||
id = Number(id) || 0;
|
id = Number(id) || 0;
|
||||||
|
|
||||||
if (id < 1) {
|
if (id < 1) {
|
||||||
return callback(new Error('Missing List ID'));
|
return callback(new Error(_('Missing List ID')));
|
||||||
}
|
}
|
||||||
|
|
||||||
db.getConnection((err, connection) => {
|
db.getConnection((err, connection) => {
|
||||||
|
@ -118,13 +120,13 @@ module.exports.create = (listId, field, callback) => {
|
||||||
listId = Number(listId) || 0;
|
listId = Number(listId) || 0;
|
||||||
|
|
||||||
if (listId < 1) {
|
if (listId < 1) {
|
||||||
return callback(new Error('Missing List ID'));
|
return callback(new Error(_('Missing List ID')));
|
||||||
}
|
}
|
||||||
|
|
||||||
field = tools.convertKeys(field);
|
field = tools.convertKeys(field);
|
||||||
|
|
||||||
if (field.type === 'option' && !field.group) {
|
if (field.type === 'option' && !field.group) {
|
||||||
return callback(new Error('Option field requires a group to be selected'));
|
return callback(new Error(_('Option field requires a group to be selected')));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (field.type !== 'option') {
|
if (field.type !== 'option') {
|
||||||
|
@ -144,11 +146,11 @@ module.exports.update = (id, updates, callback) => {
|
||||||
updates = tools.convertKeys(updates);
|
updates = tools.convertKeys(updates);
|
||||||
|
|
||||||
if (id < 1) {
|
if (id < 1) {
|
||||||
return callback(new Error('Missing Field ID'));
|
return callback(new Error(_('Missing Field ID')));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!(updates.name || '').toString().trim()) {
|
if (!(updates.name || '').toString().trim()) {
|
||||||
return callback(new Error('Field Name must be set'));
|
return callback(new Error(_('Field Name must be set')));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (updates.key) {
|
if (updates.key) {
|
||||||
|
@ -194,7 +196,7 @@ module.exports.delete = (fieldId, callback) => {
|
||||||
fieldId = Number(fieldId) || 0;
|
fieldId = Number(fieldId) || 0;
|
||||||
|
|
||||||
if (fieldId < 1) {
|
if (fieldId < 1) {
|
||||||
return callback(new Error('Missing Field ID'));
|
return callback(new Error(_('Missing Field ID')));
|
||||||
}
|
}
|
||||||
|
|
||||||
db.getConnection((err, connection) => {
|
db.getConnection((err, connection) => {
|
||||||
|
@ -211,7 +213,7 @@ module.exports.delete = (fieldId, callback) => {
|
||||||
|
|
||||||
if (!rows || !rows.length) {
|
if (!rows || !rows.length) {
|
||||||
connection.release();
|
connection.release();
|
||||||
return callback(new Error('Custom field not found'));
|
return callback(new Error(_('Custom field not found')));
|
||||||
}
|
}
|
||||||
|
|
||||||
let field = tools.convertKeys(rows[0]);
|
let field = tools.convertKeys(rows[0]);
|
||||||
|
@ -284,15 +286,15 @@ function addCustomField(listId, name, defaultValue, type, group, groupTemplate,
|
||||||
let key = slugify('merge ' + name, '_').toUpperCase();
|
let key = slugify('merge ' + name, '_').toUpperCase();
|
||||||
|
|
||||||
if (allowedTypes.indexOf(type) < 0) {
|
if (allowedTypes.indexOf(type) < 0) {
|
||||||
return callback(new Error('Unknown column type ' + type));
|
return callback(new Error(util.format(_('Unknown column type %s'), type)));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!name) {
|
if (!name) {
|
||||||
return callback(new Error('Missing column name'));
|
return callback(new Error(_('Missing column name')));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (listId <= 0) {
|
if (listId <= 0) {
|
||||||
return callback(new Error('Missing list ID'));
|
return callback(new Error(_('Missing list ID')));
|
||||||
}
|
}
|
||||||
|
|
||||||
lists.get(listId, (err, list) => {
|
lists.get(listId, (err, list) => {
|
||||||
|
@ -300,7 +302,7 @@ function addCustomField(listId, name, defaultValue, type, group, groupTemplate,
|
||||||
return callback(err);
|
return callback(err);
|
||||||
}
|
}
|
||||||
if (!list) {
|
if (!list) {
|
||||||
return callback('Provided List ID not found');
|
return callback(_('Provided List ID not found'));
|
||||||
}
|
}
|
||||||
|
|
||||||
db.getConnection((err, connection) => {
|
db.getConnection((err, connection) => {
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
let db = require('../db');
|
let db = require('../db');
|
||||||
let shortid = require('shortid');
|
let shortid = require('shortid');
|
||||||
let util = require('util');
|
let util = require('util');
|
||||||
|
let _ = require('../translate')._;
|
||||||
|
|
||||||
let geoip = require('geoip-ultralight');
|
let geoip = require('geoip-ultralight');
|
||||||
let campaigns = require('./campaigns');
|
let campaigns = require('./campaigns');
|
||||||
|
@ -324,7 +325,7 @@ function getSubscriptionData(campaignCid, listCid, subscriptionCid, callback) {
|
||||||
return callback(err);
|
return callback(err);
|
||||||
}
|
}
|
||||||
if (!campaign) {
|
if (!campaign) {
|
||||||
return callback(new Error('Campaign not found'));
|
return callback(new Error(_('Campaign not found')));
|
||||||
}
|
}
|
||||||
|
|
||||||
lists.getByCid(listCid, (err, list) => {
|
lists.getByCid(listCid, (err, list) => {
|
||||||
|
@ -332,7 +333,7 @@ function getSubscriptionData(campaignCid, listCid, subscriptionCid, callback) {
|
||||||
return callback(err);
|
return callback(err);
|
||||||
}
|
}
|
||||||
if (!list) {
|
if (!list) {
|
||||||
return callback(new Error('Campaign not found'));
|
return callback(new Error(_('List not found')));
|
||||||
}
|
}
|
||||||
|
|
||||||
subscriptions.get(list.id, subscriptionCid, (err, subscription) => {
|
subscriptions.get(list.id, subscriptionCid, (err, subscription) => {
|
||||||
|
@ -340,7 +341,7 @@ function getSubscriptionData(campaignCid, listCid, subscriptionCid, callback) {
|
||||||
return callback(err);
|
return callback(err);
|
||||||
}
|
}
|
||||||
if (!subscription) {
|
if (!subscription) {
|
||||||
return callback(new Error('Subscription not found'));
|
return callback(new Error(_('Subscription not found')));
|
||||||
}
|
}
|
||||||
|
|
||||||
return callback(null, {
|
return callback(null, {
|
||||||
|
|
|
@ -4,6 +4,7 @@ let db = require('../db');
|
||||||
let tools = require('../tools');
|
let tools = require('../tools');
|
||||||
let shortid = require('shortid');
|
let shortid = require('shortid');
|
||||||
let segments = require('./segments');
|
let segments = require('./segments');
|
||||||
|
let _ = require('../translate')._;
|
||||||
|
|
||||||
let allowedKeys = ['description'];
|
let allowedKeys = ['description'];
|
||||||
|
|
||||||
|
@ -77,7 +78,7 @@ module.exports.get = (id, callback) => {
|
||||||
id = Number(id) || 0;
|
id = Number(id) || 0;
|
||||||
|
|
||||||
if (id < 1) {
|
if (id < 1) {
|
||||||
return callback(new Error('Missing List ID'));
|
return callback(new Error(_('Missing List ID')));
|
||||||
}
|
}
|
||||||
|
|
||||||
db.getConnection((err, connection) => {
|
db.getConnection((err, connection) => {
|
||||||
|
@ -113,7 +114,7 @@ module.exports.create = (list, callback) => {
|
||||||
let name = (data.name || '').toString().trim();
|
let name = (data.name || '').toString().trim();
|
||||||
|
|
||||||
if (!data) {
|
if (!data) {
|
||||||
return callback(new Error('List Name must be set'));
|
return callback(new Error(_('List Name must be set')));
|
||||||
}
|
}
|
||||||
|
|
||||||
let keys = ['name'];
|
let keys = ['name'];
|
||||||
|
@ -171,11 +172,11 @@ module.exports.update = (id, updates, callback) => {
|
||||||
let values = [name];
|
let values = [name];
|
||||||
|
|
||||||
if (id < 1) {
|
if (id < 1) {
|
||||||
return callback(new Error('Missing List ID'));
|
return callback(new Error(_('Missing List ID')));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!name) {
|
if (!name) {
|
||||||
return callback(new Error('List Name must be set'));
|
return callback(new Error(_('List Name must be set')));
|
||||||
}
|
}
|
||||||
|
|
||||||
Object.keys(updates).forEach(key => {
|
Object.keys(updates).forEach(key => {
|
||||||
|
@ -208,7 +209,7 @@ module.exports.delete = (id, callback) => {
|
||||||
id = Number(id) || 0;
|
id = Number(id) || 0;
|
||||||
|
|
||||||
if (id < 1) {
|
if (id < 1) {
|
||||||
return callback(new Error('Missing List ID'));
|
return callback(new Error(_('Missing List ID')));
|
||||||
}
|
}
|
||||||
|
|
||||||
db.getConnection((err, connection) => {
|
db.getConnection((err, connection) => {
|
||||||
|
@ -237,7 +238,7 @@ module.exports.delete = (id, callback) => {
|
||||||
function resolveCid(cid, callback) {
|
function resolveCid(cid, callback) {
|
||||||
cid = (cid || '').toString().trim();
|
cid = (cid || '').toString().trim();
|
||||||
if (!cid) {
|
if (!cid) {
|
||||||
return callback(new Error('Missing List CID'));
|
return callback(new Error(_('Missing List CID')));
|
||||||
}
|
}
|
||||||
|
|
||||||
db.getConnection((err, connection) => {
|
db.getConnection((err, connection) => {
|
||||||
|
|
|
@ -3,34 +3,36 @@
|
||||||
let tools = require('../tools');
|
let tools = require('../tools');
|
||||||
let db = require('../db');
|
let db = require('../db');
|
||||||
let fields = require('./fields');
|
let fields = require('./fields');
|
||||||
|
let util = require('util');
|
||||||
|
let _ = require('../translate')._;
|
||||||
|
|
||||||
module.exports.defaultColumns = [{
|
module.exports.defaultColumns = [{
|
||||||
column: 'email',
|
column: 'email',
|
||||||
name: 'Email address',
|
name: _('Email address'),
|
||||||
type: 'string'
|
type: 'string'
|
||||||
}, {
|
}, {
|
||||||
column: 'opt_in_country',
|
column: 'opt_in_country',
|
||||||
name: 'Signup country',
|
name: _('Signup country'),
|
||||||
type: 'string'
|
type: 'string'
|
||||||
}, {
|
}, {
|
||||||
column: 'created',
|
column: 'created',
|
||||||
name: 'Sign up date',
|
name: _('Sign up date'),
|
||||||
type: 'date'
|
type: 'date'
|
||||||
}, {
|
}, {
|
||||||
column: 'latest_open',
|
column: 'latest_open',
|
||||||
name: 'Latest open',
|
name: _('Latest open'),
|
||||||
type: 'date'
|
type: 'date'
|
||||||
}, {
|
}, {
|
||||||
column: 'latest_click',
|
column: 'latest_click',
|
||||||
name: 'Latest click',
|
name: _('Latest click'),
|
||||||
type: 'date'
|
type: 'date'
|
||||||
}, {
|
}, {
|
||||||
column: 'first_name',
|
column: 'first_name',
|
||||||
name: 'First name',
|
name: _('First name'),
|
||||||
type: 'string'
|
type: 'string'
|
||||||
}, {
|
}, {
|
||||||
column: 'last_name',
|
column: 'last_name',
|
||||||
name: 'Last name',
|
name: _('Last name'),
|
||||||
type: 'string'
|
type: 'string'
|
||||||
}];
|
}];
|
||||||
|
|
||||||
|
@ -38,7 +40,7 @@ module.exports.list = (listId, callback) => {
|
||||||
listId = Number(listId) || 0;
|
listId = Number(listId) || 0;
|
||||||
|
|
||||||
if (listId < 1) {
|
if (listId < 1) {
|
||||||
return callback(new Error('Missing List ID'));
|
return callback(new Error(_('Missing List ID')));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -64,7 +66,7 @@ module.exports.get = (id, callback) => {
|
||||||
id = Number(id) || 0;
|
id = Number(id) || 0;
|
||||||
|
|
||||||
if (id < 1) {
|
if (id < 1) {
|
||||||
return callback(new Error('Missing Segment ID'));
|
return callback(new Error(_('Missing Segment ID')));
|
||||||
}
|
}
|
||||||
|
|
||||||
db.getConnection((err, connection) => {
|
db.getConnection((err, connection) => {
|
||||||
|
@ -80,7 +82,7 @@ module.exports.get = (id, callback) => {
|
||||||
}
|
}
|
||||||
if (!rows || !rows.length) {
|
if (!rows || !rows.length) {
|
||||||
connection.release();
|
connection.release();
|
||||||
return callback(new Error('Segment not found'));
|
return callback(new Error(_('Segment not found')));
|
||||||
}
|
}
|
||||||
|
|
||||||
let segment = tools.convertKeys(rows[0]);
|
let segment = tools.convertKeys(rows[0]);
|
||||||
|
@ -141,7 +143,9 @@ module.exports.get = (id, callback) => {
|
||||||
case 'date':
|
case 'date':
|
||||||
case 'birthday':
|
case 'birthday':
|
||||||
if (rule.value.relativeRange) {
|
if (rule.value.relativeRange) {
|
||||||
rule.formatted = (rule.value.start ? rule.value.start + ' days ' + (rule.value.startDirection ? 'after' : 'before') + ' today' : 'today') + ' … ' + (rule.value.end ? rule.value.end + ' days ' + (rule.value.endDirection ? 'after' : 'before') + ' today' : 'today');
|
let startString = rule.value.startDirection ? util.format(_('%s days after today'), rule.value.start) : util.format(_('%s days before today'), rule.value.start);
|
||||||
|
let endString = rule.value.endDirection ? util.format(_('%s days after today'), rule.value.end) : util.format(_('%s days before today'), rule.value.end);
|
||||||
|
rule.formatted = (rule.value.start ? startString : _('today')) + ' … ' + (rule.value.end ? endString : _('today'));
|
||||||
} else if (rule.value.range) {
|
} else if (rule.value.range) {
|
||||||
rule.formatted = (rule.value.start || '') + ' … ' + (rule.value.end || '');
|
rule.formatted = (rule.value.start || '') + ' … ' + (rule.value.end || '');
|
||||||
} else {
|
} else {
|
||||||
|
@ -149,7 +153,7 @@ module.exports.get = (id, callback) => {
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 'boolean':
|
case 'boolean':
|
||||||
rule.formatted = rule.value.value ? 'Selected' : 'Not selected';
|
rule.formatted = rule.value.value ? _('Selected') : _('Not selected');
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
rule.formatted = rule.value.value || '';
|
rule.formatted = rule.value.value || '';
|
||||||
|
@ -169,7 +173,7 @@ module.exports.create = (listId, segment, callback) => {
|
||||||
listId = Number(listId) || 0;
|
listId = Number(listId) || 0;
|
||||||
|
|
||||||
if (listId < 1) {
|
if (listId < 1) {
|
||||||
return callback(new Error('Missing List ID'));
|
return callback(new Error(_('Missing List ID')));
|
||||||
}
|
}
|
||||||
|
|
||||||
segment = tools.convertKeys(segment);
|
segment = tools.convertKeys(segment);
|
||||||
|
@ -178,11 +182,11 @@ module.exports.create = (listId, segment, callback) => {
|
||||||
segment.type = Number(segment.type) || 0;
|
segment.type = Number(segment.type) || 0;
|
||||||
|
|
||||||
if (!segment.name) {
|
if (!segment.name) {
|
||||||
return callback(new Error('Field Name must be set'));
|
return callback(new Error(_('Field Name must be set')));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (segment.type <= 0) {
|
if (segment.type <= 0) {
|
||||||
return callback(new Error('Invalid segment rule type'));
|
return callback(new Error(_('Invalid segment rule type')));
|
||||||
}
|
}
|
||||||
|
|
||||||
let keys = ['list', 'name', 'type'];
|
let keys = ['list', 'name', 'type'];
|
||||||
|
@ -209,7 +213,7 @@ module.exports.update = (id, updates, callback) => {
|
||||||
id = Number(id) || 0;
|
id = Number(id) || 0;
|
||||||
|
|
||||||
if (id < 1) {
|
if (id < 1) {
|
||||||
return callback(new Error('Missing Segment ID'));
|
return callback(new Error(_('Missing Segment ID')));
|
||||||
}
|
}
|
||||||
|
|
||||||
let segment = tools.convertKeys(updates);
|
let segment = tools.convertKeys(updates);
|
||||||
|
@ -218,11 +222,11 @@ module.exports.update = (id, updates, callback) => {
|
||||||
segment.type = Number(segment.type) || 0;
|
segment.type = Number(segment.type) || 0;
|
||||||
|
|
||||||
if (!segment.name) {
|
if (!segment.name) {
|
||||||
return callback(new Error('Field Name must be set'));
|
return callback(new Error(_('Field Name must be set')));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (segment.type <= 0) {
|
if (segment.type <= 0) {
|
||||||
return callback(new Error('Invalid segment rule type'));
|
return callback(new Error(_('Invalid segment rule type')));
|
||||||
}
|
}
|
||||||
|
|
||||||
let keys = ['name', 'type'];
|
let keys = ['name', 'type'];
|
||||||
|
@ -249,7 +253,7 @@ module.exports.delete = (id, callback) => {
|
||||||
id = Number(id) || 0;
|
id = Number(id) || 0;
|
||||||
|
|
||||||
if (id < 1) {
|
if (id < 1) {
|
||||||
return callback(new Error('Missing Segment ID'));
|
return callback(new Error(_('Missing Segment ID')));
|
||||||
}
|
}
|
||||||
|
|
||||||
db.getConnection((err, connection) => {
|
db.getConnection((err, connection) => {
|
||||||
|
@ -271,7 +275,7 @@ module.exports.createRule = (segmentId, rule, callback) => {
|
||||||
segmentId = Number(segmentId) || 0;
|
segmentId = Number(segmentId) || 0;
|
||||||
|
|
||||||
if (segmentId < 1) {
|
if (segmentId < 1) {
|
||||||
return callback(new Error('Missing Segment ID'));
|
return callback(new Error(_('Missing Segment ID')));
|
||||||
}
|
}
|
||||||
|
|
||||||
rule = tools.convertKeys(rule);
|
rule = tools.convertKeys(rule);
|
||||||
|
@ -282,12 +286,12 @@ module.exports.createRule = (segmentId, rule, callback) => {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!segment) {
|
if (!segment) {
|
||||||
return callback(new Error('Selected segment not found'));
|
return callback(new Error(_('Selected segment not found')));
|
||||||
}
|
}
|
||||||
|
|
||||||
let column = segment.columns.filter(column => column.column === rule.column).pop();
|
let column = segment.columns.filter(column => column.column === rule.column).pop();
|
||||||
if (!column) {
|
if (!column) {
|
||||||
return callback(new Error('Invalid rule type'));
|
return callback(new Error(_('Invalid rule type')));
|
||||||
}
|
}
|
||||||
|
|
||||||
let value;
|
let value;
|
||||||
|
@ -351,7 +355,7 @@ module.exports.getRule = (id, callback) => {
|
||||||
id = Number(id) || 0;
|
id = Number(id) || 0;
|
||||||
|
|
||||||
if (id < 1) {
|
if (id < 1) {
|
||||||
return callback(new Error('Missing Rule ID'));
|
return callback(new Error(_('Missing Rule ID')));
|
||||||
}
|
}
|
||||||
|
|
||||||
db.getConnection((err, connection) => {
|
db.getConnection((err, connection) => {
|
||||||
|
@ -367,7 +371,7 @@ module.exports.getRule = (id, callback) => {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!rows || !rows.length) {
|
if (!rows || !rows.length) {
|
||||||
return callback(new Error('Specified rule not found'));
|
return callback(new Error(_('Specified rule not found')));
|
||||||
}
|
}
|
||||||
|
|
||||||
let rule = tools.convertKeys(rows[0]);
|
let rule = tools.convertKeys(rows[0]);
|
||||||
|
@ -378,7 +382,7 @@ module.exports.getRule = (id, callback) => {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!segment) {
|
if (!segment) {
|
||||||
return callback(new Error('Specified segment not found'));
|
return callback(new Error(_('Specified segment not found')));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (rule.value) {
|
if (rule.value) {
|
||||||
|
@ -400,7 +404,10 @@ module.exports.getRule = (id, callback) => {
|
||||||
case 'date':
|
case 'date':
|
||||||
case 'birthday':
|
case 'birthday':
|
||||||
if (rule.value.relativeRange) {
|
if (rule.value.relativeRange) {
|
||||||
rule.formatted = (rule.value.start ? rule.value.start + ' days ' + (rule.value.startDirection ? 'after' : 'before') + ' today' : 'today') + ' … ' + (rule.value.end ? rule.value.end + ' days ' + (rule.value.endDirection ? 'after' : 'before') + ' today' : 'today');
|
|
||||||
|
let startString = rule.value.startDirection ? util.format(_('%s days after today'), rule.value.start) : util.format(_('%s days before today'), rule.value.start);
|
||||||
|
let endString = rule.value.endDirection ? util.format(_('%s days after today'), rule.value.end) : util.format(_('%s days before today'), rule.value.end);
|
||||||
|
rule.formatted = (rule.value.start ? startString : _('today')) + ' … ' + (rule.value.end ? endString : _('today'));
|
||||||
} else if (rule.value.range) {
|
} else if (rule.value.range) {
|
||||||
rule.formatted = (rule.value.start || '') + ' … ' + (rule.value.end || '');
|
rule.formatted = (rule.value.start || '') + ' … ' + (rule.value.end || '');
|
||||||
} else {
|
} else {
|
||||||
|
@ -408,7 +415,7 @@ module.exports.getRule = (id, callback) => {
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 'boolean':
|
case 'boolean':
|
||||||
rule.formatted = rule.value.value ? 'Selected' : 'Not selected';
|
rule.formatted = rule.value.value ? _('Selected') : _('Not selected');
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
rule.formatted = rule.value.value || '';
|
rule.formatted = rule.value.value || '';
|
||||||
|
@ -424,7 +431,7 @@ module.exports.updateRule = (id, rule, callback) => {
|
||||||
id = Number(id) || 0;
|
id = Number(id) || 0;
|
||||||
|
|
||||||
if (id < 1) {
|
if (id < 1) {
|
||||||
return callback(new Error('Missing Rule ID'));
|
return callback(new Error(_('Missing Rule ID')));
|
||||||
}
|
}
|
||||||
|
|
||||||
rule = tools.convertKeys(rule);
|
rule = tools.convertKeys(rule);
|
||||||
|
@ -435,7 +442,7 @@ module.exports.updateRule = (id, rule, callback) => {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!existingRule) {
|
if (!existingRule) {
|
||||||
return callback(new Error('Selected rule not found'));
|
return callback(new Error(_('Selected rule not found')));
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports.get(existingRule.segment, (err, segment) => {
|
module.exports.get(existingRule.segment, (err, segment) => {
|
||||||
|
@ -444,12 +451,12 @@ module.exports.updateRule = (id, rule, callback) => {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!segment) {
|
if (!segment) {
|
||||||
return callback(new Error('Selected segment not found'));
|
return callback(new Error(_('Selected segment not found')));
|
||||||
}
|
}
|
||||||
|
|
||||||
let column = segment.columns.filter(column => column.column === existingRule.column).pop();
|
let column = segment.columns.filter(column => column.column === existingRule.column).pop();
|
||||||
if (!column) {
|
if (!column) {
|
||||||
return callback(new Error('Invalid rule type'));
|
return callback(new Error(_('Invalid rule type')));
|
||||||
}
|
}
|
||||||
|
|
||||||
let value;
|
let value;
|
||||||
|
@ -514,7 +521,7 @@ module.exports.deleteRule = (id, callback) => {
|
||||||
id = Number(id) || 0;
|
id = Number(id) || 0;
|
||||||
|
|
||||||
if (id < 1) {
|
if (id < 1) {
|
||||||
return callback(new Error('Missing Rule ID'));
|
return callback(new Error(_('Missing Rule ID')));
|
||||||
}
|
}
|
||||||
|
|
||||||
db.getConnection((err, connection) => {
|
db.getConnection((err, connection) => {
|
||||||
|
@ -539,7 +546,7 @@ module.exports.getQuery = (id, prefix, callback) => {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!segment) {
|
if (!segment) {
|
||||||
return callback(new Error('Segment not found'));
|
return callback(new Error(_('Segment not found')));
|
||||||
}
|
}
|
||||||
|
|
||||||
prefix = prefix ? prefix + '.' : '';
|
prefix = prefix ? prefix + '.' : '';
|
||||||
|
@ -648,7 +655,7 @@ module.exports.subscribers = (id, onlySubscribed, callback) => {
|
||||||
return callback(err);
|
return callback(err);
|
||||||
}
|
}
|
||||||
if (!segment) {
|
if (!segment) {
|
||||||
return callback(new Error('Segment not found'));
|
return callback(new Error(_('Segment not found')));
|
||||||
}
|
}
|
||||||
module.exports.getQuery(id, false, (err, queryData) => {
|
module.exports.getQuery(id, false, (err, queryData) => {
|
||||||
if (err) {
|
if (err) {
|
||||||
|
|
|
@ -10,6 +10,8 @@ let settings = require('./settings');
|
||||||
let mailer = require('../mailer');
|
let mailer = require('../mailer');
|
||||||
let urllib = require('url');
|
let urllib = require('url');
|
||||||
let log = require('npmlog');
|
let log = require('npmlog');
|
||||||
|
let _ = require('../translate')._;
|
||||||
|
let util = require('util');
|
||||||
|
|
||||||
module.exports.list = (listId, start, limit, callback) => {
|
module.exports.list = (listId, start, limit, callback) => {
|
||||||
listId = Number(listId) || 0;
|
listId = Number(listId) || 0;
|
||||||
|
@ -83,7 +85,7 @@ module.exports.filter = (listId, request, columns, segmentId, callback) => {
|
||||||
segmentId = Number(segmentId) || 0;
|
segmentId = Number(segmentId) || 0;
|
||||||
|
|
||||||
if (!listId) {
|
if (!listId) {
|
||||||
return callback(new Error('Missing List ID'));
|
return callback(new Error(_('Missing List ID')));
|
||||||
}
|
}
|
||||||
|
|
||||||
let processQuery = queryData => {
|
let processQuery = queryData => {
|
||||||
|
@ -228,7 +230,7 @@ module.exports.addConfirmation = (list, email, optInIp, data, callback) => {
|
||||||
name: [].concat(data.firstName || []).concat(data.lastName || []).join(' '),
|
name: [].concat(data.firstName || []).concat(data.lastName || []).join(' '),
|
||||||
address: email
|
address: email
|
||||||
},
|
},
|
||||||
subject: list.name + ': Please Confirm Subscription',
|
subject: util.format(_('%s: Please Confirm Subscription'),list.name),
|
||||||
encryptionKeys
|
encryptionKeys
|
||||||
}, {
|
}, {
|
||||||
html: 'emails/confirm-html.hbs',
|
html: 'emails/confirm-html.hbs',
|
||||||
|
@ -319,7 +321,7 @@ module.exports.subscribe = (cid, optInIp, callback) => {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!result.entryId) {
|
if (!result.entryId) {
|
||||||
return callback(new Error('Could not save subscription'));
|
return callback(new Error(_('Could not save subscription')));
|
||||||
}
|
}
|
||||||
|
|
||||||
db.getConnection((err, connection) => {
|
db.getConnection((err, connection) => {
|
||||||
|
@ -502,7 +504,7 @@ module.exports.get = (listId, cid, callback) => {
|
||||||
cid = (cid || '').toString().trim();
|
cid = (cid || '').toString().trim();
|
||||||
|
|
||||||
if (!cid) {
|
if (!cid) {
|
||||||
return callback(new Error('Missing Subbscription ID'));
|
return callback(new Error(_('Missing Subbscription ID')));
|
||||||
}
|
}
|
||||||
|
|
||||||
db.getConnection((err, connection) => {
|
db.getConnection((err, connection) => {
|
||||||
|
@ -532,7 +534,7 @@ module.exports.getById = (listId, id, callback) => {
|
||||||
id = Number(id) || 0;
|
id = Number(id) || 0;
|
||||||
|
|
||||||
if (!id) {
|
if (!id) {
|
||||||
return callback(new Error('Missing Subbscription ID'));
|
return callback(new Error(_('Missing Subbscription ID')));
|
||||||
}
|
}
|
||||||
|
|
||||||
db.getConnection((err, connection) => {
|
db.getConnection((err, connection) => {
|
||||||
|
@ -560,7 +562,7 @@ module.exports.getById = (listId, id, callback) => {
|
||||||
|
|
||||||
module.exports.getByEmail = (listId, email, callback) => {
|
module.exports.getByEmail = (listId, email, callback) => {
|
||||||
if (!email) {
|
if (!email) {
|
||||||
return callback(new Error('Missing Subbscription email address'));
|
return callback(new Error(_('Missing Subbscription email address')));
|
||||||
}
|
}
|
||||||
|
|
||||||
db.getConnection((err, connection) => {
|
db.getConnection((err, connection) => {
|
||||||
|
@ -635,11 +637,11 @@ module.exports.update = (listId, cid, updates, allowEmail, callback) => {
|
||||||
let values = [];
|
let values = [];
|
||||||
|
|
||||||
if (listId < 1) {
|
if (listId < 1) {
|
||||||
return callback(new Error('Missing List ID'));
|
return callback(new Error(_('Missing List ID')));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!cid) {
|
if (!cid) {
|
||||||
return callback(new Error('Missing subscription ID'));
|
return callback(new Error(_('Missing subscription ID')));
|
||||||
}
|
}
|
||||||
|
|
||||||
fields.list(listId, (err, fieldList) => {
|
fields.list(listId, (err, fieldList) => {
|
||||||
|
@ -698,11 +700,11 @@ module.exports.unsubscribe = (listId, email, campaignId, callback) => {
|
||||||
campaignId = (campaignId || '').toString().trim() || false;
|
campaignId = (campaignId || '').toString().trim() || false;
|
||||||
|
|
||||||
if (listId < 1) {
|
if (listId < 1) {
|
||||||
return callback(new Error('Missing List ID'));
|
return callback(new Error(_('Missing List ID')));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!email) {
|
if (!email) {
|
||||||
return callback(new Error('Missing email address'));
|
return callback(new Error(_('Missing email address')));
|
||||||
}
|
}
|
||||||
|
|
||||||
db.getConnection((err, connection) => {
|
db.getConnection((err, connection) => {
|
||||||
|
@ -884,11 +886,11 @@ module.exports.delete = (listId, cid, callback) => {
|
||||||
cid = (cid || '').toString().trim();
|
cid = (cid || '').toString().trim();
|
||||||
|
|
||||||
if (listId < 1) {
|
if (listId < 1) {
|
||||||
return callback(new Error('Missing List ID'));
|
return callback(new Error(_('Missing List ID')));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!cid) {
|
if (!cid) {
|
||||||
return callback(new Error('Missing subscription ID'));
|
return callback(new Error(_('Missing subscription ID')));
|
||||||
}
|
}
|
||||||
|
|
||||||
db.getConnection((err, connection) => {
|
db.getConnection((err, connection) => {
|
||||||
|
@ -987,11 +989,11 @@ module.exports.updateImport = (listId, importId, data, callback) => {
|
||||||
importId = Number(importId) || 0;
|
importId = Number(importId) || 0;
|
||||||
|
|
||||||
if (listId < 1) {
|
if (listId < 1) {
|
||||||
return callback(new Error('Missing List ID'));
|
return callback(new Error(_('Missing List ID')));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (importId < 1) {
|
if (importId < 1) {
|
||||||
return callback(new Error('Missing Import ID'));
|
return callback(new Error(_('Missing Import ID')));
|
||||||
}
|
}
|
||||||
|
|
||||||
let keys = [];
|
let keys = [];
|
||||||
|
@ -1041,11 +1043,11 @@ module.exports.getImport = (listId, importId, callback) => {
|
||||||
importId = Number(importId) || 0;
|
importId = Number(importId) || 0;
|
||||||
|
|
||||||
if (listId < 1) {
|
if (listId < 1) {
|
||||||
return callback(new Error('Missing List ID'));
|
return callback(new Error(_('Missing List ID')));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (importId < 1) {
|
if (importId < 1) {
|
||||||
return callback(new Error('Missing Import ID'));
|
return callback(new Error(_('Missing Import ID')));
|
||||||
}
|
}
|
||||||
|
|
||||||
db.getConnection((err, connection) => {
|
db.getConnection((err, connection) => {
|
||||||
|
@ -1081,7 +1083,7 @@ module.exports.getFailedImports = (importId, callback) => {
|
||||||
importId = Number(importId) || 0;
|
importId = Number(importId) || 0;
|
||||||
|
|
||||||
if (importId < 1) {
|
if (importId < 1) {
|
||||||
return callback(new Error('Missing Import ID'));
|
return callback(new Error(_('Missing Import ID')));
|
||||||
}
|
}
|
||||||
|
|
||||||
db.getConnection((err, connection) => {
|
db.getConnection((err, connection) => {
|
||||||
|
@ -1104,7 +1106,7 @@ module.exports.listImports = (listId, callback) => {
|
||||||
listId = Number(listId) || 0;
|
listId = Number(listId) || 0;
|
||||||
|
|
||||||
if (listId < 1) {
|
if (listId < 1) {
|
||||||
return callback(new Error('Missing List ID'));
|
return callback(new Error(_('Missing List ID')));
|
||||||
}
|
}
|
||||||
|
|
||||||
db.getConnection((err, connection) => {
|
db.getConnection((err, connection) => {
|
||||||
|
@ -1147,11 +1149,11 @@ module.exports.updateAddress = (list, cid, updates, optInIp, callback) => {
|
||||||
let emailNew = (updates.emailNew || '').toString().trim();
|
let emailNew = (updates.emailNew || '').toString().trim();
|
||||||
|
|
||||||
if (!list || !list.id) {
|
if (!list || !list.id) {
|
||||||
return callback(new Error('Missing List ID'));
|
return callback(new Error(_('Missing List ID')));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!cid) {
|
if (!cid) {
|
||||||
return callback(new Error('Missing subscription ID'));
|
return callback(new Error(_('Missing subscription ID')));
|
||||||
}
|
}
|
||||||
|
|
||||||
tools.validateEmail(emailNew, false, err => {
|
tools.validateEmail(emailNew, false, err => {
|
||||||
|
@ -1173,12 +1175,12 @@ module.exports.updateAddress = (list, cid, updates, optInIp, callback) => {
|
||||||
}
|
}
|
||||||
if (!rows || !rows.length) {
|
if (!rows || !rows.length) {
|
||||||
connection.release();
|
connection.release();
|
||||||
return callback(new Error('Unknown subscription ID'));
|
return callback(new Error(_('Unknown subscription ID')));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (rows[0].email === emailNew) {
|
if (rows[0].email === emailNew) {
|
||||||
connection.release();
|
connection.release();
|
||||||
return callback(new Error('Nothing seems to be changed'));
|
return callback(new Error(_('Nothing seems to be changed')));
|
||||||
}
|
}
|
||||||
|
|
||||||
let old = rows[0];
|
let old = rows[0];
|
||||||
|
@ -1192,7 +1194,7 @@ module.exports.updateAddress = (list, cid, updates, optInIp, callback) => {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (rows && rows[0] && rows[0].id) {
|
if (rows && rows[0] && rows[0].id) {
|
||||||
return callback(new Error('This address is already registered by someone else'));
|
return callback(new Error(_('This address is already registered by someone else')));
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports.addConfirmation(list, emailNew, optInIp, {
|
module.exports.addConfirmation(list, emailNew, optInIp, {
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
|
|
||||||
let db = require('../db');
|
let db = require('../db');
|
||||||
let tools = require('../tools');
|
let tools = require('../tools');
|
||||||
|
let _ = require('../translate')._;
|
||||||
|
|
||||||
let allowedKeys = ['description', 'editor_name', 'editor_data', 'html', 'text'];
|
let allowedKeys = ['description', 'editor_name', 'editor_data', 'html', 'text'];
|
||||||
|
|
||||||
|
@ -47,7 +48,7 @@ module.exports.get = (id, callback) => {
|
||||||
id = Number(id) || 0;
|
id = Number(id) || 0;
|
||||||
|
|
||||||
if (id < 1) {
|
if (id < 1) {
|
||||||
return callback(new Error('Missing Template ID'));
|
return callback(new Error(_('Missing Template ID')));
|
||||||
}
|
}
|
||||||
|
|
||||||
db.getConnection((err, connection) => {
|
db.getConnection((err, connection) => {
|
||||||
|
@ -76,7 +77,7 @@ module.exports.create = (template, callback) => {
|
||||||
let data = tools.convertKeys(template);
|
let data = tools.convertKeys(template);
|
||||||
|
|
||||||
if (!(data.name || '').toString().trim()) {
|
if (!(data.name || '').toString().trim()) {
|
||||||
return callback(new Error('Template Name must be set'));
|
return callback(new Error(_('Template Name must be set')));
|
||||||
}
|
}
|
||||||
|
|
||||||
let name = (template.name || '').toString().trim();
|
let name = (template.name || '').toString().trim();
|
||||||
|
@ -118,11 +119,11 @@ module.exports.update = (id, updates, callback) => {
|
||||||
let data = tools.convertKeys(updates);
|
let data = tools.convertKeys(updates);
|
||||||
|
|
||||||
if (id < 1) {
|
if (id < 1) {
|
||||||
return callback(new Error('Missing Template ID'));
|
return callback(new Error(_('Missing Template ID')));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!(data.name || '').toString().trim()) {
|
if (!(data.name || '').toString().trim()) {
|
||||||
return callback(new Error('Template Name must be set'));
|
return callback(new Error(_('Template Name must be set')));
|
||||||
}
|
}
|
||||||
|
|
||||||
let name = (updates.name || '').toString().trim();
|
let name = (updates.name || '').toString().trim();
|
||||||
|
@ -159,7 +160,7 @@ module.exports.delete = (id, callback) => {
|
||||||
id = Number(id) || 0;
|
id = Number(id) || 0;
|
||||||
|
|
||||||
if (id < 1) {
|
if (id < 1) {
|
||||||
return callback(new Error('Missing Template ID'));
|
return callback(new Error(_('Missing Template ID')));
|
||||||
}
|
}
|
||||||
|
|
||||||
db.getConnection((err, connection) => {
|
db.getConnection((err, connection) => {
|
||||||
|
|
|
@ -4,36 +4,37 @@ let tools = require('../tools');
|
||||||
let db = require('../db');
|
let db = require('../db');
|
||||||
let lists = require('./lists');
|
let lists = require('./lists');
|
||||||
let util = require('util');
|
let util = require('util');
|
||||||
|
let _ = require('../translate')._;
|
||||||
|
|
||||||
module.exports.defaultColumns = [{
|
module.exports.defaultColumns = [{
|
||||||
column: 'created',
|
column: 'created',
|
||||||
name: 'Sign up date',
|
name: _('Sign up date'),
|
||||||
type: 'date'
|
type: 'date'
|
||||||
}, {
|
}, {
|
||||||
column: 'latest_open',
|
column: 'latest_open',
|
||||||
name: 'Latest open',
|
name: _('Latest open'),
|
||||||
type: 'date'
|
type: 'date'
|
||||||
}, {
|
}, {
|
||||||
column: 'latest_click',
|
column: 'latest_click',
|
||||||
name: 'Latest click',
|
name: _('Latest click'),
|
||||||
type: 'date'
|
type: 'date'
|
||||||
}];
|
}];
|
||||||
|
|
||||||
module.exports.defaultCampaignEvents = [{
|
module.exports.defaultCampaignEvents = [{
|
||||||
option: 'delivered',
|
option: 'delivered',
|
||||||
name: 'Delivered'
|
name: _('Delivered')
|
||||||
}, {
|
}, {
|
||||||
option: 'opened',
|
option: 'opened',
|
||||||
name: 'Has Opened'
|
name: _('Has Opened')
|
||||||
}, {
|
}, {
|
||||||
option: 'clicked',
|
option: 'clicked',
|
||||||
name: 'Has Clicked'
|
name: _('Has Clicked')
|
||||||
}, {
|
}, {
|
||||||
option: 'not_opened',
|
option: 'not_opened',
|
||||||
name: 'Not Opened'
|
name: _('Not Opened')
|
||||||
}, {
|
}, {
|
||||||
option: 'not_clicked',
|
option: 'not_clicked',
|
||||||
name: 'Not Clicked'
|
name: _('Not Clicked')
|
||||||
}];
|
}];
|
||||||
|
|
||||||
let defaultColumnMap = {};
|
let defaultColumnMap = {};
|
||||||
|
@ -170,36 +171,36 @@ module.exports.create = (trigger, callback) => {
|
||||||
let column;
|
let column;
|
||||||
|
|
||||||
if (!listId) {
|
if (!listId) {
|
||||||
return callback(new Error('Missing or invalid list ID'));
|
return callback(new Error(_('Missing or invalid list ID')));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (seconds < 0) {
|
if (seconds < 0) {
|
||||||
return callback(new Error('Days in the past are not allowed'));
|
return callback(new Error(_('Days in the past are not allowed')));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!rule || ['campaign', 'subscription'].indexOf(rule) < 0) {
|
if (!rule || ['campaign', 'subscription'].indexOf(rule) < 0) {
|
||||||
return callback(new Error('Missing or invalid trigger rule'));
|
return callback(new Error(_('Missing or invalid trigger rule')));
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (rule) {
|
switch (rule) {
|
||||||
case 'subscription':
|
case 'subscription':
|
||||||
column = (trigger.column || '').toString().toLowerCase().trim();
|
column = (trigger.column || '').toString().toLowerCase().trim();
|
||||||
if (!column) {
|
if (!column) {
|
||||||
return callback(new Error('Invalid subscription configuration'));
|
return callback(new Error(_('Invalid subscription configuration')));
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 'campaign':
|
case 'campaign':
|
||||||
column = (trigger.campaignOption || '').toString().toLowerCase().trim();
|
column = (trigger.campaignOption || '').toString().toLowerCase().trim();
|
||||||
sourceCampaign = Number(trigger.sourceCampaign) || 0;
|
sourceCampaign = Number(trigger.sourceCampaign) || 0;
|
||||||
if (!column || !sourceCampaign) {
|
if (!column || !sourceCampaign) {
|
||||||
return callback(new Error('Invalid campaign configuration'));
|
return callback(new Error(_('Invalid campaign configuration')));
|
||||||
}
|
}
|
||||||
if (sourceCampaign === destCampaign) {
|
if (sourceCampaign === destCampaign) {
|
||||||
return callback(new Error('A campaing can not be a target for itself'));
|
return callback(new Error(_('A campaing can not be a target for itself')));
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
return callback(new Error('Missing or invalid trigger rule'));
|
return callback(new Error(_('Missing or invalid trigger rule')));
|
||||||
}
|
}
|
||||||
|
|
||||||
lists.get(listId, (err, list) => {
|
lists.get(listId, (err, list) => {
|
||||||
|
@ -207,7 +208,7 @@ module.exports.create = (trigger, callback) => {
|
||||||
return callback(err);
|
return callback(err);
|
||||||
}
|
}
|
||||||
if (!list) {
|
if (!list) {
|
||||||
return callback(new Error('Missing or invalid list ID'));
|
return callback(new Error(_('Missing or invalid list ID')));
|
||||||
}
|
}
|
||||||
|
|
||||||
db.getConnection((err, connection) => {
|
db.getConnection((err, connection) => {
|
||||||
|
@ -228,7 +229,7 @@ module.exports.create = (trigger, callback) => {
|
||||||
|
|
||||||
let id = result && result.insertId;
|
let id = result && result.insertId;
|
||||||
if (!id) {
|
if (!id) {
|
||||||
return callback(new Error('Could not store trigger row'));
|
return callback(new Error(_('Could not store trigger row')));
|
||||||
}
|
}
|
||||||
|
|
||||||
createTriggerTable(id, err => {
|
createTriggerTable(id, err => {
|
||||||
|
@ -245,7 +246,7 @@ module.exports.create = (trigger, callback) => {
|
||||||
module.exports.update = (id, trigger, callback) => {
|
module.exports.update = (id, trigger, callback) => {
|
||||||
id = Number(id) || 0;
|
id = Number(id) || 0;
|
||||||
if (id < 1) {
|
if (id < 1) {
|
||||||
return callback(new Error('Missing or invalid Trigger ID'));
|
return callback(new Error(_('Missing or invalid Trigger ID')));
|
||||||
}
|
}
|
||||||
|
|
||||||
trigger = tools.convertKeys(trigger);
|
trigger = tools.convertKeys(trigger);
|
||||||
|
@ -259,32 +260,32 @@ module.exports.update = (id, trigger, callback) => {
|
||||||
let column;
|
let column;
|
||||||
|
|
||||||
if (seconds < 0) {
|
if (seconds < 0) {
|
||||||
return callback(new Error('Days in the past are not allowed'));
|
return callback(new Error(_('Days in the past are not allowed')));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!rule || ['campaign', 'subscription'].indexOf(rule) < 0) {
|
if (!rule || ['campaign', 'subscription'].indexOf(rule) < 0) {
|
||||||
return callback(new Error('Missing or invalid trigger rule'));
|
return callback(new Error(_('Missing or invalid trigger rule')));
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (rule) {
|
switch (rule) {
|
||||||
case 'subscription':
|
case 'subscription':
|
||||||
column = (trigger.column || '').toString().toLowerCase().trim();
|
column = (trigger.column || '').toString().toLowerCase().trim();
|
||||||
if (!column) {
|
if (!column) {
|
||||||
return callback(new Error('Invalid subscription configuration'));
|
return callback(new Error(_('Invalid subscription configuration')));
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 'campaign':
|
case 'campaign':
|
||||||
column = (trigger.campaignOption || '').toString().toLowerCase().trim();
|
column = (trigger.campaignOption || '').toString().toLowerCase().trim();
|
||||||
sourceCampaign = Number(trigger.sourceCampaign) || 0;
|
sourceCampaign = Number(trigger.sourceCampaign) || 0;
|
||||||
if (!column || !sourceCampaign) {
|
if (!column || !sourceCampaign) {
|
||||||
return callback(new Error('Invalid campaign configuration'));
|
return callback(new Error(_('Invalid campaign configuration')));
|
||||||
}
|
}
|
||||||
if (sourceCampaign === destCampaign) {
|
if (sourceCampaign === destCampaign) {
|
||||||
return callback(new Error('A campaing can not be a target for itself'));
|
return callback(new Error(_('A campaing can not be a target for itself')));
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
return callback(new Error('Missing or invalid trigger rule'));
|
return callback(new Error(_('Missing or invalid trigger rule')));
|
||||||
}
|
}
|
||||||
|
|
||||||
db.getConnection((err, connection) => {
|
db.getConnection((err, connection) => {
|
||||||
|
@ -312,7 +313,7 @@ module.exports.delete = (id, callback) => {
|
||||||
id = Number(id) || 0;
|
id = Number(id) || 0;
|
||||||
|
|
||||||
if (id < 1) {
|
if (id < 1) {
|
||||||
return callback(new Error('Missing Trigger ID'));
|
return callback(new Error(_('Missing Trigger ID')));
|
||||||
}
|
}
|
||||||
|
|
||||||
db.getConnection((err, connection) => {
|
db.getConnection((err, connection) => {
|
||||||
|
|
|
@ -9,6 +9,7 @@ let mailer = require('../mailer');
|
||||||
let settings = require('./settings');
|
let settings = require('./settings');
|
||||||
let crypto = require('crypto');
|
let crypto = require('crypto');
|
||||||
let urllib = require('url');
|
let urllib = require('url');
|
||||||
|
let _ = require('../translate')._;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Fetches user by ID value
|
* Fetches user by ID value
|
||||||
|
@ -99,7 +100,7 @@ module.exports.add = (username, password, email, callback) => {
|
||||||
|
|
||||||
let id = result && result.insertId;
|
let id = result && result.insertId;
|
||||||
if (!id) {
|
if (!id) {
|
||||||
return callback(new Error('Could not store user row'));
|
return callback(new Error(_('Could not store user row')));
|
||||||
}
|
}
|
||||||
|
|
||||||
return callback(null, id);
|
return callback(null, id);
|
||||||
|
@ -169,7 +170,7 @@ module.exports.authenticate = (username, password, callback) => {
|
||||||
module.exports.update = (id, updates, callback) => {
|
module.exports.update = (id, updates, callback) => {
|
||||||
|
|
||||||
if (!updates.email) {
|
if (!updates.email) {
|
||||||
return callback(new Error('Email Address must be set'));
|
return callback(new Error(_('Email Address must be set')));
|
||||||
}
|
}
|
||||||
|
|
||||||
let update = (connection, callback) => {
|
let update = (connection, callback) => {
|
||||||
|
@ -180,7 +181,7 @@ module.exports.update = (id, updates, callback) => {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!rows.length) {
|
if (!rows.length) {
|
||||||
return callback('Failed to check user data');
|
return callback(_('Failed to check user data'));
|
||||||
}
|
}
|
||||||
|
|
||||||
let keys = ['email'];
|
let keys = ['email'];
|
||||||
|
@ -191,7 +192,7 @@ module.exports.update = (id, updates, callback) => {
|
||||||
connection.query('UPDATE users SET ' + keys.map(key => key + '=?').join(', ') + ' WHERE id=? LIMIT 1', values, (err, result) => {
|
connection.query('UPDATE users SET ' + keys.map(key => key + '=?').join(', ') + ' WHERE id=? LIMIT 1', values, (err, result) => {
|
||||||
if (err) {
|
if (err) {
|
||||||
if (err.code === 'ER_DUP_ENTRY') {
|
if (err.code === 'ER_DUP_ENTRY') {
|
||||||
err = new Error('Can\'t change email as another user with the same email address already exists');
|
err = new Error(_('Can\'t change email as another user with the same email address already exists'));
|
||||||
}
|
}
|
||||||
return callback(err);
|
return callback(err);
|
||||||
}
|
}
|
||||||
|
@ -208,15 +209,15 @@ module.exports.update = (id, updates, callback) => {
|
||||||
return callback(err);
|
return callback(err);
|
||||||
}
|
}
|
||||||
if (!result) {
|
if (!result) {
|
||||||
return callback('Incorrect current password');
|
return callback(_('Incorrect current password'));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!updates.password) {
|
if (!updates.password) {
|
||||||
return callback(new Error('New password not set'));
|
return callback(new Error(_('New password not set')));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (updates.password !== updates.password2) {
|
if (updates.password !== updates.password2) {
|
||||||
return callback(new Error('Passwords do not match'));
|
return callback(new Error(_('Passwords do not match')));
|
||||||
}
|
}
|
||||||
|
|
||||||
bcrypt.hash(updates.password, null, null, (err, hash) => {
|
bcrypt.hash(updates.password, null, null, (err, hash) => {
|
||||||
|
@ -254,7 +255,7 @@ module.exports.resetToken = (id, callback) => {
|
||||||
id = Number(id) || 0;
|
id = Number(id) || 0;
|
||||||
|
|
||||||
if (!id) {
|
if (!id) {
|
||||||
return callback(new Error('User ID not set'));
|
return callback(new Error(_('User ID not set')));
|
||||||
}
|
}
|
||||||
|
|
||||||
db.getConnection((err, connection) => {
|
db.getConnection((err, connection) => {
|
||||||
|
@ -282,7 +283,7 @@ module.exports.sendReset = (username, callback) => {
|
||||||
username = (username || '').toString().trim();
|
username = (username || '').toString().trim();
|
||||||
|
|
||||||
if (!username) {
|
if (!username) {
|
||||||
return callback(new Error('Username must be set'));
|
return callback(new Error(_('Username must be set')));
|
||||||
}
|
}
|
||||||
|
|
||||||
db.getConnection((err, connection) => {
|
db.getConnection((err, connection) => {
|
||||||
|
@ -319,7 +320,7 @@ module.exports.sendReset = (username, callback) => {
|
||||||
to: {
|
to: {
|
||||||
address: rows[0].email
|
address: rows[0].email
|
||||||
},
|
},
|
||||||
subject: 'Mailer password change request'
|
subject: _('Mailer password change request')
|
||||||
}, {
|
}, {
|
||||||
html: 'emails/password-reset-html.hbs',
|
html: 'emails/password-reset-html.hbs',
|
||||||
text: 'emails/password-reset-text.hbs',
|
text: 'emails/password-reset-text.hbs',
|
||||||
|
@ -343,7 +344,7 @@ module.exports.sendReset = (username, callback) => {
|
||||||
|
|
||||||
module.exports.checkResetToken = (username, resetToken, callback) => {
|
module.exports.checkResetToken = (username, resetToken, callback) => {
|
||||||
if (!username || !resetToken) {
|
if (!username || !resetToken) {
|
||||||
return callback(new Error('Missing username or reset token'));
|
return callback(new Error(_('Missing username or reset token')));
|
||||||
}
|
}
|
||||||
db.getConnection((err, connection) => {
|
db.getConnection((err, connection) => {
|
||||||
if (err) {
|
if (err) {
|
||||||
|
@ -363,11 +364,11 @@ module.exports.resetPassword = (data, callback) => {
|
||||||
let updates = tools.convertKeys(data);
|
let updates = tools.convertKeys(data);
|
||||||
|
|
||||||
if (!updates.username || !updates.resetToken) {
|
if (!updates.username || !updates.resetToken) {
|
||||||
return callback(new Error('Missing username or reset token'));
|
return callback(new Error(_('Missing username or reset token')));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!updates.password || !updates.password2 || updates.password !== updates.password2) {
|
if (!updates.password || !updates.password2 || updates.password !== updates.password2) {
|
||||||
return callback(new Error('Invalid new password'));
|
return callback(new Error(_('Invalid new password')));
|
||||||
}
|
}
|
||||||
|
|
||||||
bcrypt.hash(updates.password, null, null, (err, hash) => {
|
bcrypt.hash(updates.password, null, null, (err, hash) => {
|
||||||
|
|
|
@ -2,6 +2,8 @@
|
||||||
|
|
||||||
let config = require('config');
|
let config = require('config');
|
||||||
let log = require('npmlog');
|
let log = require('npmlog');
|
||||||
|
let _ = require('./translate')._;
|
||||||
|
let util = require('util');
|
||||||
|
|
||||||
let passport = require('passport');
|
let passport = require('passport');
|
||||||
let LocalStrategy = require('passport-local').Strategy;
|
let LocalStrategy = require('passport-local').Strategy;
|
||||||
|
@ -33,7 +35,7 @@ module.exports.setup = app => {
|
||||||
|
|
||||||
module.exports.logout = (req, res) => {
|
module.exports.logout = (req, res) => {
|
||||||
if (req.user) {
|
if (req.user) {
|
||||||
req.flash('info', req.user.username + ' logged out');
|
req.flash('info', util.format(_('%s logged out'), req.user.username));
|
||||||
req.logout();
|
req.logout();
|
||||||
}
|
}
|
||||||
res.redirect('/');
|
res.redirect('/');
|
||||||
|
@ -46,7 +48,7 @@ module.exports.login = (req, res, next) => {
|
||||||
return next(err);
|
return next(err);
|
||||||
}
|
}
|
||||||
if (!user) {
|
if (!user) {
|
||||||
req.flash('danger', info && info.message || 'Failed to authenticate user');
|
req.flash('danger', info && info.message || _('Failed to authenticate user'));
|
||||||
return res.redirect('/users/login' + (req.body.next ? '?next=' + encodeURIComponent(req.body.next) : ''));
|
return res.redirect('/users/login' + (req.body.next ? '?next=' + encodeURIComponent(req.body.next) : ''));
|
||||||
}
|
}
|
||||||
req.logIn(user, err => {
|
req.logIn(user, err => {
|
||||||
|
@ -62,7 +64,7 @@ module.exports.login = (req, res, next) => {
|
||||||
req.session.cookie.expires = false;
|
req.session.cookie.expires = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
req.flash('success', 'Logged in as ' + user.username);
|
req.flash('success', util.format(_('Logged in as %s'), user.username));
|
||||||
return res.redirect(req.body.next || '/');
|
return res.redirect(req.body.next || '/');
|
||||||
});
|
});
|
||||||
})(req, res, next);
|
})(req, res, next);
|
||||||
|
@ -120,7 +122,7 @@ if (config.ldap.enabled && LdapStrategy) {
|
||||||
|
|
||||||
if (!user) {
|
if (!user) {
|
||||||
return done(null, false, {
|
return done(null, false, {
|
||||||
message: 'Incorrect username or password'
|
message: _('Incorrect username or password')
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
20
lib/tools.js
20
lib/tools.js
|
@ -6,6 +6,8 @@ let Isemail = require('isemail');
|
||||||
let urllib = require('url');
|
let urllib = require('url');
|
||||||
let juice = require('juice');
|
let juice = require('juice');
|
||||||
let jsdom = require('jsdom');
|
let jsdom = require('jsdom');
|
||||||
|
let _ = require('./translate')._;
|
||||||
|
let util = require('util');
|
||||||
|
|
||||||
let blockedUsers = ['abuse', 'admin', 'billing', 'compliance', 'devnull', 'dns', 'ftp', 'hostmaster', 'inoc', 'ispfeedback', 'ispsupport', 'listrequest', 'list', 'maildaemon', 'noc', 'noreply', 'noreply', 'null', 'phish', 'phishing', 'postmaster', 'privacy', 'registrar', 'root', 'security', 'spam', 'support', 'sysadmin', 'tech', 'undisclosedrecipients', 'unsubscribe', 'usenet', 'uucp', 'webmaster', 'www'];
|
let blockedUsers = ['abuse', 'admin', 'billing', 'compliance', 'devnull', 'dns', 'ftp', 'hostmaster', 'inoc', 'ispfeedback', 'ispsupport', 'listrequest', 'list', 'maildaemon', 'noc', 'noreply', 'noreply', 'null', 'phish', 'phishing', 'postmaster', 'privacy', 'registrar', 'root', 'security', 'spam', 'support', 'sysadmin', 'tech', 'undisclosedrecipients', 'unsubscribe', 'usenet', 'uucp', 'webmaster', 'www'];
|
||||||
|
|
||||||
|
@ -106,19 +108,19 @@ function updateMenu(res) {
|
||||||
}
|
}
|
||||||
|
|
||||||
res.locals.menu.push({
|
res.locals.menu.push({
|
||||||
title: 'Lists',
|
title: _('Lists'),
|
||||||
url: '/lists',
|
url: '/lists',
|
||||||
key: 'lists'
|
key: 'lists'
|
||||||
}, {
|
}, {
|
||||||
title: 'Templates',
|
title: _('Templates'),
|
||||||
url: '/templates',
|
url: '/templates',
|
||||||
key: 'templates'
|
key: 'templates'
|
||||||
}, {
|
}, {
|
||||||
title: 'Campaigns',
|
title: _('Campaigns'),
|
||||||
url: '/campaigns',
|
url: '/campaigns',
|
||||||
key: 'campaigns'
|
key: 'campaigns'
|
||||||
}, {
|
}, {
|
||||||
title: 'Automation',
|
title: _('Automation'),
|
||||||
url: '/triggers',
|
url: '/triggers',
|
||||||
key: 'triggers'
|
key: 'triggers'
|
||||||
});
|
});
|
||||||
|
@ -128,7 +130,7 @@ function validateEmail(address, checkBlocked, callback) {
|
||||||
|
|
||||||
let user = (address || '').toString().split('@').shift().toLowerCase().replace(/[^a-z0-9]/g, '');
|
let user = (address || '').toString().split('@').shift().toLowerCase().replace(/[^a-z0-9]/g, '');
|
||||||
if (checkBlocked && blockedUsers.indexOf(user) >= 0) {
|
if (checkBlocked && blockedUsers.indexOf(user) >= 0) {
|
||||||
return callback(new Error('Blocked email address "' + address + '"'));
|
return callback(new Error(util.format(_('Blocked email address "%s"'), address)));
|
||||||
}
|
}
|
||||||
|
|
||||||
Isemail.validate(address, {
|
Isemail.validate(address, {
|
||||||
|
@ -137,16 +139,16 @@ function validateEmail(address, checkBlocked, callback) {
|
||||||
}, result => {
|
}, result => {
|
||||||
|
|
||||||
if (result !== 0) {
|
if (result !== 0) {
|
||||||
let message = 'Invalid email address "' + address + '"';
|
let message = util.format(_('Invalid email address "%s".'), address);
|
||||||
switch (result) {
|
switch (result) {
|
||||||
case 5:
|
case 5:
|
||||||
message += '. MX record not found for domain';
|
message += ' ' + _('MX record not found for domain');
|
||||||
break;
|
break;
|
||||||
case 6:
|
case 6:
|
||||||
message += '. Address domain not found';
|
message += ' ' + _('Address domain not found');
|
||||||
break;
|
break;
|
||||||
case 12:
|
case 12:
|
||||||
message += '. Address domain name is required';
|
message += ' ' + _('Address domain name is required');
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
return callback(new Error(message));
|
return callback(new Error(message));
|
||||||
|
|
|
@ -8,6 +8,7 @@ const fs = require('fs');
|
||||||
const path = require('path');
|
const path = require('path');
|
||||||
const log = require('npmlog');
|
const log = require('npmlog');
|
||||||
const gettextParser = require('gettext-parser');
|
const gettextParser = require('gettext-parser');
|
||||||
|
const fakelang = require('./fakelang');
|
||||||
|
|
||||||
const language = config.language || 'en';
|
const language = config.language || 'en';
|
||||||
|
|
||||||
|
@ -31,5 +32,10 @@ module.exports._ = str => {
|
||||||
if (typeof str !== 'string') {
|
if (typeof str !== 'string') {
|
||||||
str = String(str);
|
str = String(str);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (language === 'zz') {
|
||||||
|
return fakelang(str);
|
||||||
|
}
|
||||||
|
|
||||||
return gt.dgettext(language, str);
|
return gt.dgettext(language, str);
|
||||||
};
|
};
|
||||||
|
|
11
package.json
11
package.json
|
@ -26,6 +26,7 @@
|
||||||
"node": ">=5.0.0"
|
"node": ">=5.0.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
"eslint-config-nodemailer": "^1.0.0",
|
||||||
"grunt": "^1.0.1",
|
"grunt": "^1.0.1",
|
||||||
"grunt-cli": "^1.2.0",
|
"grunt-cli": "^1.2.0",
|
||||||
"grunt-contrib-nodeunit": "^1.0.0",
|
"grunt-contrib-nodeunit": "^1.0.0",
|
||||||
|
@ -33,9 +34,9 @@
|
||||||
"jsxgettext-andris": "^0.9.0-patch.1"
|
"jsxgettext-andris": "^0.9.0-patch.1"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"aws-sdk": "^2.22.0",
|
"aws-sdk": "^2.23.0",
|
||||||
"bcrypt-nodejs": "0.0.3",
|
"bcrypt-nodejs": "0.0.3",
|
||||||
"body-parser": "^1.17.0",
|
"body-parser": "^1.17.1",
|
||||||
"bounce-handler": "^7.3.2-fork.2",
|
"bounce-handler": "^7.3.2-fork.2",
|
||||||
"compression": "^1.6.2",
|
"compression": "^1.6.2",
|
||||||
"config": "^1.25.1",
|
"config": "^1.25.1",
|
||||||
|
@ -46,7 +47,7 @@
|
||||||
"csv-generate": "^1.0.0",
|
"csv-generate": "^1.0.0",
|
||||||
"csv-parse": "^1.2.0",
|
"csv-parse": "^1.2.0",
|
||||||
"escape-html": "^1.0.3",
|
"escape-html": "^1.0.3",
|
||||||
"express": "^4.15.0",
|
"express": "^4.15.2",
|
||||||
"express-session": "^1.15.1",
|
"express-session": "^1.15.1",
|
||||||
"faker": "^4.1.0",
|
"faker": "^4.1.0",
|
||||||
"feedparser": "^2.1.0",
|
"feedparser": "^2.1.0",
|
||||||
|
@ -71,14 +72,14 @@
|
||||||
"nodemailer": "^3.1.4",
|
"nodemailer": "^3.1.4",
|
||||||
"nodemailer-openpgp": "^1.0.2",
|
"nodemailer-openpgp": "^1.0.2",
|
||||||
"npmlog": "^4.0.2",
|
"npmlog": "^4.0.2",
|
||||||
"openpgp": "^2.3.8",
|
"openpgp": "^2.4.0",
|
||||||
"passport": "^0.3.2",
|
"passport": "^0.3.2",
|
||||||
"passport-local": "^1.0.0",
|
"passport-local": "^1.0.0",
|
||||||
"redfour": "^1.0.0",
|
"redfour": "^1.0.0",
|
||||||
"redis": "^2.6.5",
|
"redis": "^2.6.5",
|
||||||
"request": "^2.80.0",
|
"request": "^2.80.0",
|
||||||
"serve-favicon": "^2.4.1",
|
"serve-favicon": "^2.4.1",
|
||||||
"shortid": "^2.2.6",
|
"shortid": "^2.2.8",
|
||||||
"slugify": "^1.1.0",
|
"slugify": "^1.1.0",
|
||||||
"smtp-server": "^2.0.2",
|
"smtp-server": "^2.0.2",
|
||||||
"striptags": "^3.0.1",
|
"striptags": "^3.0.1",
|
||||||
|
|
|
@ -11,6 +11,8 @@ let request = require('request');
|
||||||
let router = new express.Router();
|
let router = new express.Router();
|
||||||
let passport = require('../lib/passport');
|
let passport = require('../lib/passport');
|
||||||
let marked = require('marked');
|
let marked = require('marked');
|
||||||
|
let _ = require('../lib/translate')._;
|
||||||
|
let util = require('util');
|
||||||
|
|
||||||
router.get('/:campaign/:list/:subscription', passport.csrfProtection, (req, res, next) => {
|
router.get('/:campaign/:list/:subscription', passport.csrfProtection, (req, res, next) => {
|
||||||
settings.get('serviceUrl', (err, serviceUrl) => {
|
settings.get('serviceUrl', (err, serviceUrl) => {
|
||||||
|
@ -26,7 +28,7 @@ router.get('/:campaign/:list/:subscription', passport.csrfProtection, (req, res,
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!campaign) {
|
if (!campaign) {
|
||||||
err = new Error('Not Found');
|
err = new Error(_('Not Found'));
|
||||||
err.status = 404;
|
err.status = 404;
|
||||||
return next(err);
|
return next(err);
|
||||||
}
|
}
|
||||||
|
@ -38,7 +40,7 @@ router.get('/:campaign/:list/:subscription', passport.csrfProtection, (req, res,
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!list) {
|
if (!list) {
|
||||||
err = new Error('Not Found');
|
err = new Error(_('Not Found'));
|
||||||
err.status = 404;
|
err.status = 404;
|
||||||
return next(err);
|
return next(err);
|
||||||
}
|
}
|
||||||
|
@ -50,7 +52,7 @@ router.get('/:campaign/:list/:subscription', passport.csrfProtection, (req, res,
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!subscription) {
|
if (!subscription) {
|
||||||
err = new Error('Not Found');
|
err = new Error(_('Not Found'));
|
||||||
err.status = 404;
|
err.status = 404;
|
||||||
return next(err);
|
return next(err);
|
||||||
}
|
}
|
||||||
|
@ -105,7 +107,7 @@ router.get('/:campaign/:list/:subscription', passport.csrfProtection, (req, res,
|
||||||
return next(err);
|
return next(err);
|
||||||
}
|
}
|
||||||
if (httpResponse.statusCode !== 200) {
|
if (httpResponse.statusCode !== 200) {
|
||||||
return next(new Error('Received status code ' + httpResponse.statusCode + ' from ' + campaign.sourceUrl));
|
return next(new Error(util.format(_('Received status code %s from %s'), httpResponse.statusCode, campaign.sourceUrl)));
|
||||||
}
|
}
|
||||||
renderAndShow(body && body.toString(), false);
|
renderAndShow(body && body.toString(), false);
|
||||||
});
|
});
|
||||||
|
@ -129,7 +131,7 @@ router.post('/attachment/download', passport.parseForm, passport.csrfProtection,
|
||||||
let url = '/archive/' + encodeURIComponent(req.body.campaign || '') + '/' + encodeURIComponent(req.body.list || '') + '/' + encodeURIComponent(req.body.subscription || '');
|
let url = '/archive/' + encodeURIComponent(req.body.campaign || '') + '/' + encodeURIComponent(req.body.list || '') + '/' + encodeURIComponent(req.body.subscription || '');
|
||||||
campaigns.getByCid(req.body.campaign, (err, campaign) => {
|
campaigns.getByCid(req.body.campaign, (err, campaign) => {
|
||||||
if (err || !campaign) {
|
if (err || !campaign) {
|
||||||
req.flash('danger', err && err.message || err || 'Could not find campaign with specified ID');
|
req.flash('danger', err && err.message || err || _('Could not find campaign with specified ID'));
|
||||||
return res.redirect(url);
|
return res.redirect(url);
|
||||||
}
|
}
|
||||||
campaigns.getAttachment(campaign.id, Number(req.body.attachment), (err, attachment) => {
|
campaigns.getAttachment(campaign.id, Number(req.body.attachment), (err, attachment) => {
|
||||||
|
@ -137,7 +139,7 @@ router.post('/attachment/download', passport.parseForm, passport.csrfProtection,
|
||||||
req.flash('danger', err && err.message || err);
|
req.flash('danger', err && err.message || err);
|
||||||
return res.redirect(url);
|
return res.redirect(url);
|
||||||
} else if (!attachment) {
|
} else if (!attachment) {
|
||||||
req.flash('warning', 'Attachment not found');
|
req.flash('warning', _('Attachment not found'));
|
||||||
return res.redirect(url);
|
return res.redirect(url);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -14,6 +14,8 @@ let striptags = require('striptags');
|
||||||
let passport = require('../lib/passport');
|
let passport = require('../lib/passport');
|
||||||
let htmlescape = require('escape-html');
|
let htmlescape = require('escape-html');
|
||||||
let multer = require('multer');
|
let multer = require('multer');
|
||||||
|
let _ = require('../lib/translate')._;
|
||||||
|
let util = require('util');
|
||||||
let uploadStorage = multer.memoryStorage();
|
let uploadStorage = multer.memoryStorage();
|
||||||
let uploads = multer({
|
let uploads = multer({
|
||||||
storage: uploadStorage
|
storage: uploadStorage
|
||||||
|
@ -21,7 +23,7 @@ let uploads = multer({
|
||||||
|
|
||||||
router.all('/*', (req, res, next) => {
|
router.all('/*', (req, res, next) => {
|
||||||
if (!req.user) {
|
if (!req.user) {
|
||||||
req.flash('danger', 'Need to be logged in to access restricted content');
|
req.flash('danger', _('Need to be logged in to access restricted content'));
|
||||||
return res.redirect('/users/login?next=' + encodeURIComponent(req.originalUrl));
|
return res.redirect('/users/login?next=' + encodeURIComponent(req.originalUrl));
|
||||||
}
|
}
|
||||||
res.setSelectedMenu('campaigns');
|
res.setSelectedMenu('campaigns');
|
||||||
|
@ -30,7 +32,7 @@ router.all('/*', (req, res, next) => {
|
||||||
|
|
||||||
router.get('/', (req, res) => {
|
router.get('/', (req, res) => {
|
||||||
res.render('campaigns/campaigns', {
|
res.render('campaigns/campaigns', {
|
||||||
title: 'Campaigns'
|
title: _('Campaigns')
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -112,13 +114,13 @@ router.get('/create', passport.csrfProtection, (req, res) => {
|
||||||
router.post('/create', passport.parseForm, passport.csrfProtection, (req, res) => {
|
router.post('/create', passport.parseForm, passport.csrfProtection, (req, res) => {
|
||||||
campaigns.create(req.body, false, (err, id) => {
|
campaigns.create(req.body, false, (err, id) => {
|
||||||
if (err || !id) {
|
if (err || !id) {
|
||||||
req.flash('danger', err && err.message || err || 'Could not create campaign');
|
req.flash('danger', err && err.message || err || _('Could not create campaign'));
|
||||||
return res.redirect('/campaigns/create?' + tools.queryParams(req.body));
|
return res.redirect('/campaigns/create?' + tools.queryParams(req.body));
|
||||||
}
|
}
|
||||||
req.flash('success', 'Campaign “' + req.body.name + '” created');
|
req.flash('success', util.format(_('Campaign “%s” created'), req.body.name));
|
||||||
res.redirect((req.body.type === 'rss')
|
res.redirect((req.body.type === 'rss') ?
|
||||||
? '/campaigns/edit/' + id
|
'/campaigns/edit/' + id :
|
||||||
: '/campaigns/edit/' + id + '?tab=template'
|
'/campaigns/edit/' + id + '?tab=template'
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -126,7 +128,7 @@ router.post('/create', passport.parseForm, passport.csrfProtection, (req, res) =
|
||||||
router.get('/edit/:id', passport.csrfProtection, (req, res, next) => {
|
router.get('/edit/:id', passport.csrfProtection, (req, res, next) => {
|
||||||
campaigns.get(req.params.id, false, (err, campaign) => {
|
campaigns.get(req.params.id, false, (err, campaign) => {
|
||||||
if (err || !campaign) {
|
if (err || !campaign) {
|
||||||
req.flash('danger', err && err.message || err || 'Could not find campaign with specified ID');
|
req.flash('danger', err && err.message || err || _('Could not find campaign with specified ID'));
|
||||||
return res.redirect('/campaigns');
|
return res.redirect('/campaigns');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -199,7 +201,7 @@ router.get('/edit/:id', passport.csrfProtection, (req, res, next) => {
|
||||||
campaign.mergeTags = defaultMergeTags.concat(listMergeTags);
|
campaign.mergeTags = defaultMergeTags.concat(listMergeTags);
|
||||||
campaign.type === 2 && campaign.mergeTags.push({
|
campaign.type === 2 && campaign.mergeTags.push({
|
||||||
key: 'RSS_ENTRY',
|
key: 'RSS_ENTRY',
|
||||||
value: 'content from an RSS entry'
|
value: _('content from an RSS entry')
|
||||||
});
|
});
|
||||||
res.render(view, campaign);
|
res.render(view, campaign);
|
||||||
});
|
});
|
||||||
|
@ -215,9 +217,9 @@ router.post('/edit', passport.parseForm, passport.csrfProtection, (req, res) =>
|
||||||
if (err) {
|
if (err) {
|
||||||
req.flash('danger', err.message || err);
|
req.flash('danger', err.message || err);
|
||||||
} else if (updated) {
|
} else if (updated) {
|
||||||
req.flash('success', 'Campaign settings updated');
|
req.flash('success', _('Campaign settings updated'));
|
||||||
} else {
|
} else {
|
||||||
req.flash('info', 'Campaign settings not updated');
|
req.flash('info', _('Campaign settings not updated'));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (req.body.id) {
|
if (req.body.id) {
|
||||||
|
@ -233,9 +235,9 @@ router.post('/delete', passport.parseForm, passport.csrfProtection, (req, res) =
|
||||||
if (err) {
|
if (err) {
|
||||||
req.flash('danger', err && err.message || err);
|
req.flash('danger', err && err.message || err);
|
||||||
} else if (deleted) {
|
} else if (deleted) {
|
||||||
req.flash('success', 'Campaign deleted');
|
req.flash('success', _('Campaign deleted'));
|
||||||
} else {
|
} else {
|
||||||
req.flash('info', 'Could not delete specified campaign');
|
req.flash('info', _('Could not delete specified campaign'));
|
||||||
}
|
}
|
||||||
|
|
||||||
return res.redirect('/campaigns');
|
return res.redirect('/campaigns');
|
||||||
|
@ -254,22 +256,22 @@ router.post('/ajax', (req, res) => {
|
||||||
let getStatusText = data => {
|
let getStatusText = data => {
|
||||||
switch (data.status) {
|
switch (data.status) {
|
||||||
case 1:
|
case 1:
|
||||||
return 'Idling';
|
return _('Idling');
|
||||||
case 2:
|
case 2:
|
||||||
if (data.scheduled && data.scheduled > new Date()) {
|
if (data.scheduled && data.scheduled > new Date()) {
|
||||||
return 'Scheduled';
|
return _('Scheduled');
|
||||||
}
|
}
|
||||||
return '<span class="glyphicon glyphicon-refresh spinning"></span> Sending…';
|
return '<span class="glyphicon glyphicon-refresh spinning"></span> ' + _('Sending') + '…';
|
||||||
case 3:
|
case 3:
|
||||||
return 'Finished';
|
return _('Finished');
|
||||||
case 4:
|
case 4:
|
||||||
return 'Paused';
|
return _('Paused');
|
||||||
case 5:
|
case 5:
|
||||||
return 'Inactive';
|
return _('Inactive');
|
||||||
case 6:
|
case 6:
|
||||||
return 'Active';
|
return _('Active');
|
||||||
}
|
}
|
||||||
return 'Other';
|
return _('Other');
|
||||||
};
|
};
|
||||||
|
|
||||||
res.json({
|
res.json({
|
||||||
|
@ -282,7 +284,7 @@ router.post('/ajax', (req, res) => {
|
||||||
htmlescape(striptags(row.description) || ''),
|
htmlescape(striptags(row.description) || ''),
|
||||||
getStatusText(row),
|
getStatusText(row),
|
||||||
'<span class="datestring" data-date="' + row.created.toISOString() + '" title="' + row.created.toISOString() + '">' + row.created.toISOString() + '</span>'
|
'<span class="datestring" data-date="' + row.created.toISOString() + '" title="' + row.created.toISOString() + '">' + row.created.toISOString() + '</span>'
|
||||||
].concat('<span class="glyphicon glyphicon-wrench" aria-hidden="true"></span><a href="/campaigns/edit/' + row.id + '">Edit</a>'))
|
].concat('<span class="glyphicon glyphicon-wrench" aria-hidden="true"></span><a href="/campaigns/edit/' + row.id + '">' + _('Edit') + '</a>'))
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -290,7 +292,7 @@ router.post('/ajax', (req, res) => {
|
||||||
router.get('/view/:id', passport.csrfProtection, (req, res) => {
|
router.get('/view/:id', passport.csrfProtection, (req, res) => {
|
||||||
campaigns.get(req.params.id, true, (err, campaign) => {
|
campaigns.get(req.params.id, true, (err, campaign) => {
|
||||||
if (err || !campaign) {
|
if (err || !campaign) {
|
||||||
req.flash('danger', err && err.message || err || 'Could not find campaign with specified ID');
|
req.flash('danger', err && err.message || err || _('Could not find campaign with specified ID'));
|
||||||
return res.redirect('/campaigns');
|
return res.redirect('/campaigns');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -385,7 +387,7 @@ router.post('/preview/:id', passport.parseForm, passport.csrfProtection, (req, r
|
||||||
router.get('/opened/:id', passport.csrfProtection, (req, res) => {
|
router.get('/opened/:id', passport.csrfProtection, (req, res) => {
|
||||||
campaigns.get(req.params.id, true, (err, campaign) => {
|
campaigns.get(req.params.id, true, (err, campaign) => {
|
||||||
if (err || !campaign) {
|
if (err || !campaign) {
|
||||||
req.flash('danger', err && err.message || err || 'Could not find campaign with specified ID');
|
req.flash('danger', err && err.message || err || _('Could not find campaign with specified ID'));
|
||||||
return res.redirect('/campaigns');
|
return res.redirect('/campaigns');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -424,13 +426,13 @@ router.get('/status/:id/:status', passport.csrfProtection, (req, res) => {
|
||||||
status = 4;
|
status = 4;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
req.flash('danger', 'Unknown status selector');
|
req.flash('danger', _('Unknown status selector'));
|
||||||
return res.redirect('/campaigns');
|
return res.redirect('/campaigns');
|
||||||
}
|
}
|
||||||
|
|
||||||
campaigns.get(id, true, (err, campaign) => {
|
campaigns.get(id, true, (err, campaign) => {
|
||||||
if (err || !campaign) {
|
if (err || !campaign) {
|
||||||
req.flash('danger', err && err.message || err || 'Could not find campaign with specified ID');
|
req.flash('danger', err && err.message || err || _('Could not find campaign with specified ID'));
|
||||||
return res.redirect('/campaigns');
|
return res.redirect('/campaigns');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -470,7 +472,7 @@ router.get('/status/:id/:status', passport.csrfProtection, (req, res) => {
|
||||||
router.get('/clicked/:id/:linkId', passport.csrfProtection, (req, res) => {
|
router.get('/clicked/:id/:linkId', passport.csrfProtection, (req, res) => {
|
||||||
campaigns.get(req.params.id, true, (err, campaign) => {
|
campaigns.get(req.params.id, true, (err, campaign) => {
|
||||||
if (err || !campaign) {
|
if (err || !campaign) {
|
||||||
req.flash('danger', err && err.message || err || 'Could not find campaign with specified ID');
|
req.flash('danger', err && err.message || err || _('Could not find campaign with specified ID'));
|
||||||
return res.redirect('/campaigns');
|
return res.redirect('/campaigns');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -536,7 +538,7 @@ router.post('/clicked/ajax/:id/:linkId', (req, res) => {
|
||||||
campaigns.get(req.params.id, true, (err, campaign) => {
|
campaigns.get(req.params.id, true, (err, campaign) => {
|
||||||
if (err || !campaign) {
|
if (err || !campaign) {
|
||||||
return res.json({
|
return res.json({
|
||||||
error: err && err.message || err || 'Campaign not found',
|
error: err && err.message || err || _('Campaign not found'),
|
||||||
data: []
|
data: []
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -571,7 +573,7 @@ router.post('/clicked/ajax/:id/:linkId', (req, res) => {
|
||||||
htmlescape(row.lastName || ''),
|
htmlescape(row.lastName || ''),
|
||||||
row.created && row.created.toISOString ? '<span class="datestring" data-date="' + row.created.toISOString() + '" title="' + row.created.toISOString() + '">' + row.created.toISOString() + '</span>' : 'N/A',
|
row.created && row.created.toISOString ? '<span class="datestring" data-date="' + row.created.toISOString() + '" title="' + row.created.toISOString() + '">' + row.created.toISOString() + '</span>' : 'N/A',
|
||||||
row.count,
|
row.count,
|
||||||
'<span class="glyphicon glyphicon-wrench" aria-hidden="true"></span><a href="/lists/subscription/' + campaign.list + '/edit/' + row.cid + '">Edit</a>'
|
'<span class="glyphicon glyphicon-wrench" aria-hidden="true"></span><a href="/lists/subscription/' + campaign.list + '/edit/' + row.cid + '">' + _('Edit') + '</a>'
|
||||||
])
|
])
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -585,7 +587,7 @@ router.post('/status/ajax/:id/:status', (req, res) => {
|
||||||
campaigns.get(req.params.id, true, (err, campaign) => {
|
campaigns.get(req.params.id, true, (err, campaign) => {
|
||||||
if (err || !campaign) {
|
if (err || !campaign) {
|
||||||
return res.json({
|
return res.json({
|
||||||
error: err && err.message || err || 'Campaign not found',
|
error: err && err.message || err || _('Campaign not found'),
|
||||||
data: []
|
data: []
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -621,7 +623,7 @@ router.post('/status/ajax/:id/:status', (req, res) => {
|
||||||
htmlescape(row.lastName || ''),
|
htmlescape(row.lastName || ''),
|
||||||
htmlescape(row.response || ''),
|
htmlescape(row.response || ''),
|
||||||
row.updated && row.created.toISOString ? '<span class="datestring" data-date="' + row.updated.toISOString() + '" title="' + row.updated.toISOString() + '">' + row.updated.toISOString() + '</span>' : 'N/A',
|
row.updated && row.created.toISOString ? '<span class="datestring" data-date="' + row.updated.toISOString() + '" title="' + row.updated.toISOString() + '">' + row.updated.toISOString() + '</span>' : 'N/A',
|
||||||
'<span class="glyphicon glyphicon-wrench" aria-hidden="true"></span><a href="/lists/subscription/' + campaign.list + '/edit/' + row.cid + '">Edit</a>'
|
'<span class="glyphicon glyphicon-wrench" aria-hidden="true"></span><a href="/lists/subscription/' + campaign.list + '/edit/' + row.cid + '">' + _('Edit') + '</a>'
|
||||||
])
|
])
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -634,9 +636,9 @@ router.post('/delete', passport.parseForm, passport.csrfProtection, (req, res) =
|
||||||
if (err) {
|
if (err) {
|
||||||
req.flash('danger', err && err.message || err);
|
req.flash('danger', err && err.message || err);
|
||||||
} else if (deleted) {
|
} else if (deleted) {
|
||||||
req.flash('success', 'Campaign deleted');
|
req.flash('success', _('Campaign deleted'));
|
||||||
} else {
|
} else {
|
||||||
req.flash('info', 'Could not delete specified campaign');
|
req.flash('info', _('Could not delete specified campaign'));
|
||||||
}
|
}
|
||||||
|
|
||||||
return res.redirect('/campaigns');
|
return res.redirect('/campaigns');
|
||||||
|
@ -652,9 +654,9 @@ router.post('/send', passport.parseForm, passport.csrfProtection, (req, res) =>
|
||||||
if (err) {
|
if (err) {
|
||||||
req.flash('danger', err && err.message || err);
|
req.flash('danger', err && err.message || err);
|
||||||
} else if (scheduled) {
|
} else if (scheduled) {
|
||||||
req.flash('success', 'Scheduled sending');
|
req.flash('success', _('Scheduled sending'));
|
||||||
} else {
|
} else {
|
||||||
req.flash('info', 'Could not schedule sending');
|
req.flash('info', _('Could not schedule sending'));
|
||||||
}
|
}
|
||||||
|
|
||||||
return res.redirect('/campaigns/view/' + encodeURIComponent(req.body.id));
|
return res.redirect('/campaigns/view/' + encodeURIComponent(req.body.id));
|
||||||
|
@ -666,9 +668,9 @@ router.post('/resume', passport.parseForm, passport.csrfProtection, (req, res) =
|
||||||
if (err) {
|
if (err) {
|
||||||
req.flash('danger', err && err.message || err);
|
req.flash('danger', err && err.message || err);
|
||||||
} else if (scheduled) {
|
} else if (scheduled) {
|
||||||
req.flash('success', 'Sending resumed');
|
req.flash('success', _('Sending resumed'));
|
||||||
} else {
|
} else {
|
||||||
req.flash('info', 'Could not resume sending');
|
req.flash('info', _('Could not resume sending'));
|
||||||
}
|
}
|
||||||
|
|
||||||
return res.redirect('/campaigns/view/' + encodeURIComponent(req.body.id));
|
return res.redirect('/campaigns/view/' + encodeURIComponent(req.body.id));
|
||||||
|
@ -680,9 +682,9 @@ router.post('/reset', passport.parseForm, passport.csrfProtection, (req, res) =>
|
||||||
if (err) {
|
if (err) {
|
||||||
req.flash('danger', err && err.message || err);
|
req.flash('danger', err && err.message || err);
|
||||||
} else if (reset) {
|
} else if (reset) {
|
||||||
req.flash('success', 'Sending reset');
|
req.flash('success', _('Sending reset'));
|
||||||
} else {
|
} else {
|
||||||
req.flash('info', 'Could not reset sending');
|
req.flash('info', _('Could not reset sending'));
|
||||||
}
|
}
|
||||||
|
|
||||||
return res.redirect('/campaigns/view/' + encodeURIComponent(req.body.id));
|
return res.redirect('/campaigns/view/' + encodeURIComponent(req.body.id));
|
||||||
|
@ -694,9 +696,9 @@ router.post('/pause', passport.parseForm, passport.csrfProtection, (req, res) =>
|
||||||
if (err) {
|
if (err) {
|
||||||
req.flash('danger', err && err.message || err);
|
req.flash('danger', err && err.message || err);
|
||||||
} else if (reset) {
|
} else if (reset) {
|
||||||
req.flash('success', 'Sending paused');
|
req.flash('success', _('Sending paused'));
|
||||||
} else {
|
} else {
|
||||||
req.flash('info', 'Could not pause sending');
|
req.flash('info', _('Could not pause sending'));
|
||||||
}
|
}
|
||||||
|
|
||||||
return res.redirect('/campaigns/view/' + encodeURIComponent(req.body.id));
|
return res.redirect('/campaigns/view/' + encodeURIComponent(req.body.id));
|
||||||
|
@ -708,9 +710,9 @@ router.post('/activate', passport.parseForm, passport.csrfProtection, (req, res)
|
||||||
if (err) {
|
if (err) {
|
||||||
req.flash('danger', err && err.message || err);
|
req.flash('danger', err && err.message || err);
|
||||||
} else if (reset) {
|
} else if (reset) {
|
||||||
req.flash('success', 'Sending activated');
|
req.flash('success', _('Sending activated'));
|
||||||
} else {
|
} else {
|
||||||
req.flash('info', 'Could not activate sending');
|
req.flash('info', _('Could not activate sending'));
|
||||||
}
|
}
|
||||||
|
|
||||||
return res.redirect('/campaigns/view/' + encodeURIComponent(req.body.id));
|
return res.redirect('/campaigns/view/' + encodeURIComponent(req.body.id));
|
||||||
|
@ -722,9 +724,9 @@ router.post('/inactivate', passport.parseForm, passport.csrfProtection, (req, re
|
||||||
if (err) {
|
if (err) {
|
||||||
req.flash('danger', err && err.message || err);
|
req.flash('danger', err && err.message || err);
|
||||||
} else if (reset) {
|
} else if (reset) {
|
||||||
req.flash('success', 'Sending paused');
|
req.flash('success', _('Sending paused'));
|
||||||
} else {
|
} else {
|
||||||
req.flash('info', 'Could not pause sending');
|
req.flash('info', _('Could not pause sending'));
|
||||||
}
|
}
|
||||||
|
|
||||||
return res.redirect('/campaigns/view/' + encodeURIComponent(req.body.id));
|
return res.redirect('/campaigns/view/' + encodeURIComponent(req.body.id));
|
||||||
|
@ -734,7 +736,7 @@ router.post('/inactivate', passport.parseForm, passport.csrfProtection, (req, re
|
||||||
router.post('/attachment', uploads.single('attachment'), passport.parseForm, passport.csrfProtection, (req, res) => {
|
router.post('/attachment', uploads.single('attachment'), passport.parseForm, passport.csrfProtection, (req, res) => {
|
||||||
campaigns.get(req.body.id, false, (err, campaign) => {
|
campaigns.get(req.body.id, false, (err, campaign) => {
|
||||||
if (err || !campaign) {
|
if (err || !campaign) {
|
||||||
req.flash('danger', err && err.message || err || 'Could not find campaign with specified ID');
|
req.flash('danger', err && err.message || err || _('Could not find campaign with specified ID'));
|
||||||
return res.redirect('/campaigns');
|
return res.redirect('/campaigns');
|
||||||
}
|
}
|
||||||
campaigns.addAttachment(campaign.id, {
|
campaigns.addAttachment(campaign.id, {
|
||||||
|
@ -745,9 +747,9 @@ router.post('/attachment', uploads.single('attachment'), passport.parseForm, pas
|
||||||
if (err) {
|
if (err) {
|
||||||
req.flash('danger', err && err.message || err);
|
req.flash('danger', err && err.message || err);
|
||||||
} else if (attachmentId) {
|
} else if (attachmentId) {
|
||||||
req.flash('success', 'Attachment uploaded');
|
req.flash('success', _('Attachment uploaded'));
|
||||||
} else {
|
} else {
|
||||||
req.flash('info', 'Could not store attachment');
|
req.flash('info', _('Could not store attachment'));
|
||||||
}
|
}
|
||||||
return res.redirect('/campaigns/edit/' + campaign.id + '?tab=attachments');
|
return res.redirect('/campaigns/edit/' + campaign.id + '?tab=attachments');
|
||||||
});
|
});
|
||||||
|
@ -757,16 +759,16 @@ router.post('/attachment', uploads.single('attachment'), passport.parseForm, pas
|
||||||
router.post('/attachment/delete', passport.parseForm, passport.csrfProtection, (req, res) => {
|
router.post('/attachment/delete', passport.parseForm, passport.csrfProtection, (req, res) => {
|
||||||
campaigns.get(req.body.id, false, (err, campaign) => {
|
campaigns.get(req.body.id, false, (err, campaign) => {
|
||||||
if (err || !campaign) {
|
if (err || !campaign) {
|
||||||
req.flash('danger', err && err.message || err || 'Could not find campaign with specified ID');
|
req.flash('danger', err && err.message || err || _('Could not find campaign with specified ID'));
|
||||||
return res.redirect('/campaigns');
|
return res.redirect('/campaigns');
|
||||||
}
|
}
|
||||||
campaigns.deleteAttachment(campaign.id, Number(req.body.attachment), (err, deleted) => {
|
campaigns.deleteAttachment(campaign.id, Number(req.body.attachment), (err, deleted) => {
|
||||||
if (err) {
|
if (err) {
|
||||||
req.flash('danger', err && err.message || err);
|
req.flash('danger', err && err.message || err);
|
||||||
} else if (deleted) {
|
} else if (deleted) {
|
||||||
req.flash('success', 'Attachment deleted');
|
req.flash('success', _('Attachment deleted'));
|
||||||
} else {
|
} else {
|
||||||
req.flash('info', 'Could not delete attachment');
|
req.flash('info', _('Could not delete attachment'));
|
||||||
}
|
}
|
||||||
return res.redirect('/campaigns/edit/' + campaign.id + '?tab=attachments');
|
return res.redirect('/campaigns/edit/' + campaign.id + '?tab=attachments');
|
||||||
});
|
});
|
||||||
|
@ -776,7 +778,7 @@ router.post('/attachment/delete', passport.parseForm, passport.csrfProtection, (
|
||||||
router.post('/attachment/download', passport.parseForm, passport.csrfProtection, (req, res) => {
|
router.post('/attachment/download', passport.parseForm, passport.csrfProtection, (req, res) => {
|
||||||
campaigns.get(req.body.id, false, (err, campaign) => {
|
campaigns.get(req.body.id, false, (err, campaign) => {
|
||||||
if (err || !campaign) {
|
if (err || !campaign) {
|
||||||
req.flash('danger', err && err.message || err || 'Could not find campaign with specified ID');
|
req.flash('danger', err && err.message || err || _('Could not find campaign with specified ID'));
|
||||||
return res.redirect('/campaigns');
|
return res.redirect('/campaigns');
|
||||||
}
|
}
|
||||||
campaigns.getAttachment(campaign.id, Number(req.body.attachment), (err, attachment) => {
|
campaigns.getAttachment(campaign.id, Number(req.body.attachment), (err, attachment) => {
|
||||||
|
@ -784,7 +786,7 @@ router.post('/attachment/download', passport.parseForm, passport.csrfProtection,
|
||||||
req.flash('danger', err && err.message || err);
|
req.flash('danger', err && err.message || err);
|
||||||
return res.redirect('/campaigns/edit/' + campaign.id + '?tab=attachments');
|
return res.redirect('/campaigns/edit/' + campaign.id + '?tab=attachments');
|
||||||
} else if (!attachment) {
|
} else if (!attachment) {
|
||||||
req.flash('warning', 'Attachment not found');
|
req.flash('warning', _('Attachment not found'));
|
||||||
return res.redirect('/campaigns/edit/' + campaign.id + '?tab=attachments');
|
return res.redirect('/campaigns/edit/' + campaign.id + '?tab=attachments');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -798,7 +800,7 @@ router.post('/attachment/download', passport.parseForm, passport.csrfProtection,
|
||||||
router.get('/attachment/:campaign', passport.csrfProtection, (req, res) => {
|
router.get('/attachment/:campaign', passport.csrfProtection, (req, res) => {
|
||||||
campaigns.get(req.params.campaign, false, (err, campaign) => {
|
campaigns.get(req.params.campaign, false, (err, campaign) => {
|
||||||
if (err || !campaign) {
|
if (err || !campaign) {
|
||||||
req.flash('danger', err && err.message || err || 'Could not find campaign with specified ID');
|
req.flash('danger', err && err.message || err || _('Could not find campaign with specified ID'));
|
||||||
return res.redirect('/campaigns');
|
return res.redirect('/campaigns');
|
||||||
}
|
}
|
||||||
campaign.csrfToken = req.csrfToken();
|
campaign.csrfToken = req.csrfToken();
|
||||||
|
|
|
@ -6,10 +6,11 @@ let lists = require('../lib/models/lists');
|
||||||
let fields = require('../lib/models/fields');
|
let fields = require('../lib/models/fields');
|
||||||
let tools = require('../lib/tools');
|
let tools = require('../lib/tools');
|
||||||
let passport = require('../lib/passport');
|
let passport = require('../lib/passport');
|
||||||
|
let _ = require('../lib/translate')._;
|
||||||
|
|
||||||
router.all('/*', (req, res, next) => {
|
router.all('/*', (req, res, next) => {
|
||||||
if (!req.user) {
|
if (!req.user) {
|
||||||
req.flash('danger', 'Need to be logged in to access restricted content');
|
req.flash('danger', _('Need to be logged in to access restricted content'));
|
||||||
return res.redirect('/users/login?next=' + encodeURIComponent(req.originalUrl));
|
return res.redirect('/users/login?next=' + encodeURIComponent(req.originalUrl));
|
||||||
}
|
}
|
||||||
res.setSelectedMenu('lists');
|
res.setSelectedMenu('lists');
|
||||||
|
@ -24,7 +25,7 @@ router.get('/:list', (req, res) => {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!list) {
|
if (!list) {
|
||||||
req.flash('danger', 'Selected list ID not found');
|
req.flash('danger', _('Selected list ID not found'));
|
||||||
return res.redirect('/');
|
return res.redirect('/');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -60,7 +61,7 @@ router.get('/:list/create', passport.csrfProtection, (req, res) => {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!list) {
|
if (!list) {
|
||||||
req.flash('danger', 'Selected list ID not found');
|
req.flash('danger', _('Selected list ID not found'));
|
||||||
return res.redirect('/');
|
return res.redirect('/');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -98,7 +99,7 @@ router.get('/:list/create', passport.csrfProtection, (req, res) => {
|
||||||
router.post('/:list/create', passport.parseForm, passport.csrfProtection, (req, res) => {
|
router.post('/:list/create', passport.parseForm, passport.csrfProtection, (req, res) => {
|
||||||
fields.create(req.params.list, req.body, (err, id) => {
|
fields.create(req.params.list, req.body, (err, id) => {
|
||||||
if (err || !id) {
|
if (err || !id) {
|
||||||
req.flash('danger', err && err.message || err || 'Could not create custom field');
|
req.flash('danger', err && err.message || err || _('Could not create custom field'));
|
||||||
return res.redirect('/fields/' + encodeURIComponent(req.params.list) + '/create?' + tools.queryParams(req.body));
|
return res.redirect('/fields/' + encodeURIComponent(req.params.list) + '/create?' + tools.queryParams(req.body));
|
||||||
}
|
}
|
||||||
req.flash('success', 'Custom field created');
|
req.flash('success', 'Custom field created');
|
||||||
|
@ -114,7 +115,7 @@ router.get('/:list/edit/:field', passport.csrfProtection, (req, res) => {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!list) {
|
if (!list) {
|
||||||
req.flash('danger', 'Selected list ID not found');
|
req.flash('danger', _('Selected list ID not found'));
|
||||||
return res.redirect('/');
|
return res.redirect('/');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -125,7 +126,7 @@ router.get('/:list/edit/:field', passport.csrfProtection, (req, res) => {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!field) {
|
if (!field) {
|
||||||
req.flash('danger', 'Selected field not found');
|
req.flash('danger', _('Selected field not found'));
|
||||||
return res.redirect('/fields/' + encodeURIComponent(req.params.list));
|
return res.redirect('/fields/' + encodeURIComponent(req.params.list));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -161,9 +162,9 @@ router.post('/:list/edit', passport.parseForm, passport.csrfProtection, (req, re
|
||||||
if (err) {
|
if (err) {
|
||||||
req.flash('danger', err.message || err);
|
req.flash('danger', err.message || err);
|
||||||
} else if (updated) {
|
} else if (updated) {
|
||||||
req.flash('success', 'Field settings updated');
|
req.flash('success', _('Field settings updated'));
|
||||||
} else {
|
} else {
|
||||||
req.flash('info', 'Field settings not updated');
|
req.flash('info', _('Field settings not updated'));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (req.body.id) {
|
if (req.body.id) {
|
||||||
|
@ -179,9 +180,9 @@ router.post('/:list/delete', passport.parseForm, passport.csrfProtection, (req,
|
||||||
if (err) {
|
if (err) {
|
||||||
req.flash('danger', err && err.message || err);
|
req.flash('danger', err && err.message || err);
|
||||||
} else if (deleted) {
|
} else if (deleted) {
|
||||||
req.flash('success', 'Custom field deleted');
|
req.flash('success', _('Custom field deleted'));
|
||||||
} else {
|
} else {
|
||||||
req.flash('info', 'Could not delete specified field');
|
req.flash('info', _('Could not delete specified field'));
|
||||||
}
|
}
|
||||||
|
|
||||||
return res.redirect('/fields/' + encodeURIComponent(req.params.list));
|
return res.redirect('/fields/' + encodeURIComponent(req.params.list));
|
||||||
|
|
|
@ -5,6 +5,7 @@ let settings = require('../lib/models/settings');
|
||||||
let lists = require('../lib/models/lists');
|
let lists = require('../lib/models/lists');
|
||||||
let subscriptions = require('../lib/models/subscriptions');
|
let subscriptions = require('../lib/models/subscriptions');
|
||||||
let tools = require('../lib/tools');
|
let tools = require('../lib/tools');
|
||||||
|
let _ = require('../lib/translate')._;
|
||||||
|
|
||||||
let log = require('npmlog');
|
let log = require('npmlog');
|
||||||
let express = require('express');
|
let express = require('express');
|
||||||
|
@ -36,7 +37,7 @@ router.get('/:campaign/:list/:subscription/:link', (req, res) => {
|
||||||
res.status(404);
|
res.status(404);
|
||||||
return res.render('archive/view', {
|
return res.render('archive/view', {
|
||||||
layout: 'archive/layout',
|
layout: 'archive/layout',
|
||||||
message: 'Oops, we couldn\'t find a link for the URL you clicked',
|
message: _('Oops, we couldn\'t find a link for the URL you clicked'),
|
||||||
campaign: {
|
campaign: {
|
||||||
subject: 'Error 404'
|
subject: 'Error 404'
|
||||||
}
|
}
|
||||||
|
|
108
routes/lists.js
108
routes/lists.js
|
@ -17,6 +17,8 @@ let humanize = require('humanize');
|
||||||
let mkdirp = require('mkdirp');
|
let mkdirp = require('mkdirp');
|
||||||
let pathlib = require('path');
|
let pathlib = require('path');
|
||||||
let log = require('npmlog');
|
let log = require('npmlog');
|
||||||
|
let _ = require('../lib/translate')._;
|
||||||
|
let util = require('util');
|
||||||
|
|
||||||
let uploadStorage = multer.diskStorage({
|
let uploadStorage = multer.diskStorage({
|
||||||
destination: (req, file, callback) => {
|
destination: (req, file, callback) => {
|
||||||
|
@ -44,7 +46,7 @@ let moment = require('moment-timezone');
|
||||||
|
|
||||||
router.all('/*', (req, res, next) => {
|
router.all('/*', (req, res, next) => {
|
||||||
if (!req.user) {
|
if (!req.user) {
|
||||||
req.flash('danger', 'Need to be logged in to access restricted content');
|
req.flash('danger', _('Need to be logged in to access restricted content'));
|
||||||
return res.redirect('/users/login?next=' + encodeURIComponent(req.originalUrl));
|
return res.redirect('/users/login?next=' + encodeURIComponent(req.originalUrl));
|
||||||
}
|
}
|
||||||
res.setSelectedMenu('lists');
|
res.setSelectedMenu('lists');
|
||||||
|
@ -85,10 +87,10 @@ router.get('/create', passport.csrfProtection, (req, res) => {
|
||||||
router.post('/create', passport.parseForm, passport.csrfProtection, (req, res) => {
|
router.post('/create', passport.parseForm, passport.csrfProtection, (req, res) => {
|
||||||
lists.create(req.body, (err, id) => {
|
lists.create(req.body, (err, id) => {
|
||||||
if (err || !id) {
|
if (err || !id) {
|
||||||
req.flash('danger', err && err.message || err || 'Could not create list');
|
req.flash('danger', err && err.message || err || _('Could not create list'));
|
||||||
return res.redirect('/lists/create?' + tools.queryParams(req.body));
|
return res.redirect('/lists/create?' + tools.queryParams(req.body));
|
||||||
}
|
}
|
||||||
req.flash('success', 'List created');
|
req.flash('success', _('List created'));
|
||||||
res.redirect('/lists/view/' + id);
|
res.redirect('/lists/view/' + id);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -96,7 +98,7 @@ router.post('/create', passport.parseForm, passport.csrfProtection, (req, res) =
|
||||||
router.get('/edit/:id', passport.csrfProtection, (req, res) => {
|
router.get('/edit/:id', passport.csrfProtection, (req, res) => {
|
||||||
lists.get(req.params.id, (err, list) => {
|
lists.get(req.params.id, (err, list) => {
|
||||||
if (err || !list) {
|
if (err || !list) {
|
||||||
req.flash('danger', err && err.message || err || 'Could not find list with specified ID');
|
req.flash('danger', err && err.message || err || _('Could not find list with specified ID'));
|
||||||
return res.redirect('/lists');
|
return res.redirect('/lists');
|
||||||
}
|
}
|
||||||
list.csrfToken = req.csrfToken();
|
list.csrfToken = req.csrfToken();
|
||||||
|
@ -110,9 +112,9 @@ router.post('/edit', passport.parseForm, passport.csrfProtection, (req, res) =>
|
||||||
if (err) {
|
if (err) {
|
||||||
req.flash('danger', err.message || err);
|
req.flash('danger', err.message || err);
|
||||||
} else if (updated) {
|
} else if (updated) {
|
||||||
req.flash('success', 'List settings updated');
|
req.flash('success', _('List settings updated'));
|
||||||
} else {
|
} else {
|
||||||
req.flash('info', 'List settings not updated');
|
req.flash('info', _('List settings not updated'));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (req.body.id) {
|
if (req.body.id) {
|
||||||
|
@ -128,9 +130,9 @@ router.post('/delete', passport.parseForm, passport.csrfProtection, (req, res) =
|
||||||
if (err) {
|
if (err) {
|
||||||
req.flash('danger', err && err.message || err);
|
req.flash('danger', err && err.message || err);
|
||||||
} else if (deleted) {
|
} else if (deleted) {
|
||||||
req.flash('success', 'List deleted');
|
req.flash('success', _('List deleted'));
|
||||||
} else {
|
} else {
|
||||||
req.flash('info', 'Could not delete specified list');
|
req.flash('info', _('Could not delete specified list'));
|
||||||
}
|
}
|
||||||
|
|
||||||
return res.redirect('/lists');
|
return res.redirect('/lists');
|
||||||
|
@ -141,7 +143,7 @@ router.post('/ajax/:id', (req, res) => {
|
||||||
lists.get(req.params.id, (err, list) => {
|
lists.get(req.params.id, (err, list) => {
|
||||||
if (err || !list) {
|
if (err || !list) {
|
||||||
return res.json({
|
return res.json({
|
||||||
error: err && err.message || err || 'List not found',
|
error: err && err.message || err || _('List not found'),
|
||||||
data: []
|
data: []
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -166,7 +168,7 @@ router.post('/ajax/:id', (req, res) => {
|
||||||
row.customFields = fields.getRow(fieldList, row);
|
row.customFields = fields.getRow(fieldList, row);
|
||||||
});
|
});
|
||||||
|
|
||||||
let statuses = ['Unknown', 'Subscribed', 'Unsubscribed', 'Bounced', 'Complained'];
|
let statuses = [_('Unknown'), _('Subscribed'), _('Unsubscribed'), _('Bounced'), _('Complained')];
|
||||||
|
|
||||||
res.json({
|
res.json({
|
||||||
draw: req.body.draw,
|
draw: req.body.draw,
|
||||||
|
@ -197,11 +199,11 @@ router.post('/ajax/:id', (req, res) => {
|
||||||
let key = keys[i];
|
let key = keys[i];
|
||||||
switch (key.verifyPrimaryKey()) {
|
switch (key.verifyPrimaryKey()) {
|
||||||
case 0:
|
case 0:
|
||||||
return 'Invalid key';
|
return _('Invalid key');
|
||||||
case 1:
|
case 1:
|
||||||
return 'Expired key';
|
return _('Expired key');
|
||||||
case 2:
|
case 2:
|
||||||
return 'Revoked key';
|
return _('Revoked key');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -217,7 +219,7 @@ router.post('/ajax/:id', (req, res) => {
|
||||||
} else {
|
} else {
|
||||||
return htmlescape(cRow.value || '');
|
return htmlescape(cRow.value || '');
|
||||||
}
|
}
|
||||||
})).concat(statuses[row.status]).concat(row.created && row.created.toISOString ? '<span class="datestring" data-date="' + row.created.toISOString() + '" title="' + row.created.toISOString() + '">' + row.created.toISOString() + '</span>' : 'N/A').concat('<span class="glyphicon glyphicon-wrench" aria-hidden="true"></span><a href="/lists/subscription/' + list.id + '/edit/' + row.cid + '">Edit</a>'))
|
})).concat(statuses[row.status]).concat(row.created && row.created.toISOString ? '<span class="datestring" data-date="' + row.created.toISOString() + '" title="' + row.created.toISOString() + '">' + row.created.toISOString() + '</span>' : 'N/A').concat('<span class="glyphicon glyphicon-wrench" aria-hidden="true"></span><a href="/lists/subscription/' + list.id + '/edit/' + row.cid + '">' + _('Edit') + '</a>'))
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -231,7 +233,7 @@ router.get('/view/:id', passport.csrfProtection, (req, res) => {
|
||||||
|
|
||||||
lists.get(req.params.id, (err, list) => {
|
lists.get(req.params.id, (err, list) => {
|
||||||
if (err || !list) {
|
if (err || !list) {
|
||||||
req.flash('danger', err && err.message || err || 'Could not find list with specified ID');
|
req.flash('danger', err && err.message || err || _('Could not find list with specified ID'));
|
||||||
return res.redirect('/lists');
|
return res.redirect('/lists');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -248,22 +250,22 @@ router.get('/view/:id', passport.csrfProtection, (req, res) => {
|
||||||
|
|
||||||
list.imports = imports.map((entry, i) => {
|
list.imports = imports.map((entry, i) => {
|
||||||
entry.index = i + 1;
|
entry.index = i + 1;
|
||||||
entry.importType = entry.type === 1 ? 'Subscribe' : 'Unsubscribe';
|
entry.importType = entry.type === 1 ? _('Subscribe') : _('Unsubscribe');
|
||||||
switch (entry.status) {
|
switch (entry.status) {
|
||||||
case 0:
|
case 0:
|
||||||
entry.importStatus = 'Initializing';
|
entry.importStatus = _('Initializing');
|
||||||
break;
|
break;
|
||||||
case 1:
|
case 1:
|
||||||
entry.importStatus = 'Initialized';
|
entry.importStatus = _('Initialized');
|
||||||
break;
|
break;
|
||||||
case 2:
|
case 2:
|
||||||
entry.importStatus = 'Importing...';
|
entry.importStatus = _('Importing') + '…';
|
||||||
break;
|
break;
|
||||||
case 3:
|
case 3:
|
||||||
entry.importStatus = 'Finished';
|
entry.importStatus = _('Finished');
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
entry.importStatus = 'Errored' + (entry.error ? ' (' + entry.error + ')' : '');
|
entry.importStatus = _('Errored') + (entry.error ? ' (' + entry.error + ')' : '');
|
||||||
entry.error = true;
|
entry.error = true;
|
||||||
}
|
}
|
||||||
entry.created = entry.created && entry.created.toISOString();
|
entry.created = entry.created && entry.created.toISOString();
|
||||||
|
@ -296,7 +298,7 @@ router.get('/view/:id', passport.csrfProtection, (req, res) => {
|
||||||
router.get('/subscription/:id/add', passport.csrfProtection, (req, res) => {
|
router.get('/subscription/:id/add', passport.csrfProtection, (req, res) => {
|
||||||
lists.get(req.params.id, (err, list) => {
|
lists.get(req.params.id, (err, list) => {
|
||||||
if (err || !list) {
|
if (err || !list) {
|
||||||
req.flash('danger', err && err.message || err || 'Could not find list with specified ID');
|
req.flash('danger', err && err.message || err || _('Could not find list with specified ID'));
|
||||||
return res.redirect('/lists');
|
return res.redirect('/lists');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -335,13 +337,13 @@ router.get('/subscription/:id/add', passport.csrfProtection, (req, res) => {
|
||||||
router.get('/subscription/:id/edit/:cid', passport.csrfProtection, (req, res) => {
|
router.get('/subscription/:id/edit/:cid', passport.csrfProtection, (req, res) => {
|
||||||
lists.get(req.params.id, (err, list) => {
|
lists.get(req.params.id, (err, list) => {
|
||||||
if (err || !list) {
|
if (err || !list) {
|
||||||
req.flash('danger', err && err.message || err || 'Could not find list with specified ID');
|
req.flash('danger', err && err.message || err || _('Could not find list with specified ID'));
|
||||||
return res.redirect('/lists');
|
return res.redirect('/lists');
|
||||||
}
|
}
|
||||||
|
|
||||||
subscriptions.get(list.id, req.params.cid, (err, subscription) => {
|
subscriptions.get(list.id, req.params.cid, (err, subscription) => {
|
||||||
if (err || !subscription) {
|
if (err || !subscription) {
|
||||||
req.flash('danger', err && err.message || err || 'Could not find subscriber with specified ID');
|
req.flash('danger', err && err.message || err || _('Could not find subscriber with specified ID'));
|
||||||
return res.redirect('/lists/view/' + req.params.id);
|
return res.redirect('/lists/view/' + req.params.id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -387,14 +389,14 @@ router.get('/subscription/:id/edit/:cid', passport.csrfProtection, (req, res) =>
|
||||||
router.post('/subscription/add', passport.parseForm, passport.csrfProtection, (req, res) => {
|
router.post('/subscription/add', passport.parseForm, passport.csrfProtection, (req, res) => {
|
||||||
subscriptions.insert(req.body.list, false, req.body, (err, response) => {
|
subscriptions.insert(req.body.list, false, req.body, (err, response) => {
|
||||||
if (err) {
|
if (err) {
|
||||||
req.flash('danger', err && err.message || err || 'Could not add subscription');
|
req.flash('danger', err && err.message || err || _('Could not add subscription'));
|
||||||
return res.redirect('/lists/subscription/' + encodeURIComponent(req.body.list) + '/add?' + tools.queryParams(req.body));
|
return res.redirect('/lists/subscription/' + encodeURIComponent(req.body.list) + '/add?' + tools.queryParams(req.body));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (response.entryId) {
|
if (response.entryId) {
|
||||||
req.flash('success', req.body.email + ' was successfully added to your list');
|
req.flash('success', util.format(_('%s was successfully added to your list'), req.body.email));
|
||||||
} else {
|
} else {
|
||||||
req.flash('warning', req.body.email + ' was not added to your list');
|
req.flash('warning', util.format(_('%s was not added to your list'), req.body.email));
|
||||||
}
|
}
|
||||||
|
|
||||||
res.redirect('/lists/subscription/' + encodeURIComponent(req.body.list) + '/add');
|
res.redirect('/lists/subscription/' + encodeURIComponent(req.body.list) + '/add');
|
||||||
|
@ -404,22 +406,22 @@ router.post('/subscription/add', passport.parseForm, passport.csrfProtection, (r
|
||||||
router.post('/subscription/unsubscribe', passport.parseForm, passport.csrfProtection, (req, res) => {
|
router.post('/subscription/unsubscribe', passport.parseForm, passport.csrfProtection, (req, res) => {
|
||||||
lists.get(req.body.list, (err, list) => {
|
lists.get(req.body.list, (err, list) => {
|
||||||
if (err || !list) {
|
if (err || !list) {
|
||||||
req.flash('danger', err && err.message || err || 'Could not find list with specified ID');
|
req.flash('danger', err && err.message || err || _('Could not find list with specified ID'));
|
||||||
return res.redirect('/lists');
|
return res.redirect('/lists');
|
||||||
}
|
}
|
||||||
|
|
||||||
subscriptions.get(list.id, req.body.cid, (err, subscription) => {
|
subscriptions.get(list.id, req.body.cid, (err, subscription) => {
|
||||||
if (err || !subscription) {
|
if (err || !subscription) {
|
||||||
req.flash('danger', err && err.message || err || 'Could not find subscriber with specified ID');
|
req.flash('danger', err && err.message || err || _('Could not find subscriber with specified ID'));
|
||||||
return res.redirect('/lists/view/' + list.id);
|
return res.redirect('/lists/view/' + list.id);
|
||||||
}
|
}
|
||||||
|
|
||||||
subscriptions.unsubscribe(list.id, subscription.email, false, err => {
|
subscriptions.unsubscribe(list.id, subscription.email, false, err => {
|
||||||
if (err) {
|
if (err) {
|
||||||
req.flash('danger', err && err.message || err || 'Could not unsubscribe user');
|
req.flash('danger', err && err.message || err || _('Could not unsubscribe user'));
|
||||||
return res.redirect('/lists/subscription/' + list.id + '/edit/' + subscription.cid);
|
return res.redirect('/lists/subscription/' + list.id + '/edit/' + subscription.cid);
|
||||||
}
|
}
|
||||||
req.flash('success', subscription.email + ' was successfully unsubscribed from your list');
|
req.flash('success', util.format(_('%s was successfully unsubscribed from your list'), subscription.email));
|
||||||
res.redirect('/lists/view/' + list.id);
|
res.redirect('/lists/view/' + list.id);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -429,17 +431,17 @@ router.post('/subscription/unsubscribe', passport.parseForm, passport.csrfProtec
|
||||||
router.post('/subscription/delete', passport.parseForm, passport.csrfProtection, (req, res) => {
|
router.post('/subscription/delete', passport.parseForm, passport.csrfProtection, (req, res) => {
|
||||||
lists.get(req.body.list, (err, list) => {
|
lists.get(req.body.list, (err, list) => {
|
||||||
if (err || !list) {
|
if (err || !list) {
|
||||||
req.flash('danger', err && err.message || err || 'Could not find list with specified ID');
|
req.flash('danger', err && err.message || err || _('Could not find list with specified ID'));
|
||||||
return res.redirect('/lists');
|
return res.redirect('/lists');
|
||||||
}
|
}
|
||||||
|
|
||||||
subscriptions.delete(list.id, req.body.cid, (err, email) => {
|
subscriptions.delete(list.id, req.body.cid, (err, email) => {
|
||||||
if (err || !email) {
|
if (err || !email) {
|
||||||
req.flash('danger', err && err.message || err || 'Could not find subscriber with specified ID');
|
req.flash('danger', err && err.message || err || _('Could not find subscriber with specified ID'));
|
||||||
return res.redirect('/lists/view/' + list.id);
|
return res.redirect('/lists/view/' + list.id);
|
||||||
}
|
}
|
||||||
|
|
||||||
req.flash('success', email + ' was successfully removed from your list');
|
req.flash('success', util.format(_('%s was successfully removed from your list'), email));
|
||||||
res.redirect('/lists/view/' + list.id);
|
res.redirect('/lists/view/' + list.id);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -451,16 +453,16 @@ router.post('/subscription/edit', passport.parseForm, passport.csrfProtection, (
|
||||||
|
|
||||||
if (err) {
|
if (err) {
|
||||||
if (err.code === 'ER_DUP_ENTRY') {
|
if (err.code === 'ER_DUP_ENTRY') {
|
||||||
req.flash('danger', 'Another subscriber with email address ' + req.body.email + ' already exists');
|
req.flash('danger', util.format(_('Another subscriber with email address %s already exists'), req.body.email));
|
||||||
return res.redirect('/lists/subscription/' + encodeURIComponent(req.body.list) + '/edit/' + req.body.cid);
|
return res.redirect('/lists/subscription/' + encodeURIComponent(req.body.list) + '/edit/' + req.body.cid);
|
||||||
} else {
|
} else {
|
||||||
req.flash('danger', err.message || err);
|
req.flash('danger', err.message || err);
|
||||||
}
|
}
|
||||||
|
|
||||||
} else if (updated) {
|
} else if (updated) {
|
||||||
req.flash('success', 'Subscription settings updated');
|
req.flash('success', _('Subscription settings updated'));
|
||||||
} else {
|
} else {
|
||||||
req.flash('info', 'Subscription settings not updated');
|
req.flash('info', _('Subscription settings not updated'));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (req.body.list) {
|
if (req.body.list) {
|
||||||
|
@ -474,7 +476,7 @@ router.post('/subscription/edit', passport.parseForm, passport.csrfProtection, (
|
||||||
router.get('/subscription/:id/import', passport.csrfProtection, (req, res) => {
|
router.get('/subscription/:id/import', passport.csrfProtection, (req, res) => {
|
||||||
lists.get(req.params.id, (err, list) => {
|
lists.get(req.params.id, (err, list) => {
|
||||||
if (err || !list) {
|
if (err || !list) {
|
||||||
req.flash('danger', err && err.message || err || 'Could not find list with specified ID');
|
req.flash('danger', err && err.message || err || _('Could not find list with specified ID'));
|
||||||
return res.redirect('/lists');
|
return res.redirect('/lists');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -496,13 +498,13 @@ router.get('/subscription/:id/import', passport.csrfProtection, (req, res) => {
|
||||||
router.get('/subscription/:id/import/:importId', passport.csrfProtection, (req, res) => {
|
router.get('/subscription/:id/import/:importId', passport.csrfProtection, (req, res) => {
|
||||||
lists.get(req.params.id, (err, list) => {
|
lists.get(req.params.id, (err, list) => {
|
||||||
if (err || !list) {
|
if (err || !list) {
|
||||||
req.flash('danger', err && err.message || err || 'Could not find list with specified ID');
|
req.flash('danger', err && err.message || err || _('Could not find list with specified ID'));
|
||||||
return res.redirect('/lists');
|
return res.redirect('/lists');
|
||||||
}
|
}
|
||||||
|
|
||||||
subscriptions.getImport(req.params.id, req.params.importId, (err, data) => {
|
subscriptions.getImport(req.params.id, req.params.importId, (err, data) => {
|
||||||
if (err || !data) {
|
if (err || !data) {
|
||||||
req.flash('danger', err && err.message || err || 'Could not find import data with specified ID');
|
req.flash('danger', err && err.message || err || _('Could not find import data with specified ID'));
|
||||||
return res.redirect('/lists');
|
return res.redirect('/lists');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -525,7 +527,7 @@ router.get('/subscription/:id/import/:importId', passport.csrfProtection, (req,
|
||||||
router.post('/subscription/import', uploads.single('listimport'), passport.parseForm, passport.csrfProtection, (req, res) => {
|
router.post('/subscription/import', uploads.single('listimport'), passport.parseForm, passport.csrfProtection, (req, res) => {
|
||||||
lists.get(req.body.list, (err, list) => {
|
lists.get(req.body.list, (err, list) => {
|
||||||
if (err || !list) {
|
if (err || !list) {
|
||||||
req.flash('danger', err && err.message || err || 'Could not find list with specified ID');
|
req.flash('danger', err && err.message || err || _('Could not find list with specified ID'));
|
||||||
return res.redirect('/lists');
|
return res.redirect('/lists');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -533,7 +535,7 @@ router.post('/subscription/import', uploads.single('listimport'), passport.parse
|
||||||
|
|
||||||
getPreview(req.file.path, req.file.size, delimiter, (err, rows) => {
|
getPreview(req.file.path, req.file.size, delimiter, (err, rows) => {
|
||||||
if (err) {
|
if (err) {
|
||||||
req.flash('danger', err && err.message || err || 'Could not process CSV');
|
req.flash('danger', err && err.message || err || _('Could not process CSV'));
|
||||||
return res.redirect('/lists');
|
return res.redirect('/lists');
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
|
@ -542,7 +544,7 @@ router.post('/subscription/import', uploads.single('listimport'), passport.parse
|
||||||
example: rows[1] || []
|
example: rows[1] || []
|
||||||
}, (err, importId) => {
|
}, (err, importId) => {
|
||||||
if (err) {
|
if (err) {
|
||||||
req.flash('danger', err && err.message || err || 'Could not create importer');
|
req.flash('danger', err && err.message || err || _('Could not create importer'));
|
||||||
return res.redirect('/lists');
|
return res.redirect('/lists');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -593,7 +595,7 @@ function getPreview(path, size, delimiter, callback) {
|
||||||
// just ignore
|
// just ignore
|
||||||
});
|
});
|
||||||
if (!data || !data.length) {
|
if (!data || !data.length) {
|
||||||
return callback(null, new Error('Empty file'));
|
return callback(null, new Error(_('Empty file')));
|
||||||
}
|
}
|
||||||
callback(err, data);
|
callback(err, data);
|
||||||
});
|
});
|
||||||
|
@ -604,13 +606,13 @@ function getPreview(path, size, delimiter, callback) {
|
||||||
router.post('/subscription/import-confirm', passport.parseForm, passport.csrfProtection, (req, res) => {
|
router.post('/subscription/import-confirm', passport.parseForm, passport.csrfProtection, (req, res) => {
|
||||||
lists.get(req.body.list, (err, list) => {
|
lists.get(req.body.list, (err, list) => {
|
||||||
if (err || !list) {
|
if (err || !list) {
|
||||||
req.flash('danger', err && err.message || err || 'Could not find list with specified ID');
|
req.flash('danger', err && err.message || err || _('Could not find list with specified ID'));
|
||||||
return res.redirect('/lists');
|
return res.redirect('/lists');
|
||||||
}
|
}
|
||||||
|
|
||||||
subscriptions.getImport(list.id, req.body.import, (err, data) => {
|
subscriptions.getImport(list.id, req.body.import, (err, data) => {
|
||||||
if (err || !list) {
|
if (err || !list) {
|
||||||
req.flash('danger', err && err.message || err || 'Could not find import data with specified ID');
|
req.flash('danger', err && err.message || err || _('Could not find import data with specified ID'));
|
||||||
return res.redirect('/lists');
|
return res.redirect('/lists');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -646,11 +648,11 @@ router.post('/subscription/import-confirm', passport.parseForm, passport.csrfPro
|
||||||
mapping: JSON.stringify(data.mapping)
|
mapping: JSON.stringify(data.mapping)
|
||||||
}, (err, importer) => {
|
}, (err, importer) => {
|
||||||
if (err || !importer) {
|
if (err || !importer) {
|
||||||
req.flash('danger', err && err.message || err || 'Could not find import data with specified ID');
|
req.flash('danger', err && err.message || err || _('Could not find import data with specified ID'));
|
||||||
return res.redirect('/lists');
|
return res.redirect('/lists');
|
||||||
}
|
}
|
||||||
|
|
||||||
req.flash('success', 'Import started');
|
req.flash('success', _('Import started'));
|
||||||
res.redirect('/lists/view/' + list.id + '?tab=imports');
|
res.redirect('/lists/view/' + list.id + '?tab=imports');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -661,7 +663,7 @@ router.post('/subscription/import-confirm', passport.parseForm, passport.csrfPro
|
||||||
router.post('/subscription/import-restart', passport.parseForm, passport.csrfProtection, (req, res) => {
|
router.post('/subscription/import-restart', passport.parseForm, passport.csrfProtection, (req, res) => {
|
||||||
lists.get(req.body.list, (err, list) => {
|
lists.get(req.body.list, (err, list) => {
|
||||||
if (err || !list) {
|
if (err || !list) {
|
||||||
req.flash('danger', err && err.message || err || 'Could not find list with specified ID');
|
req.flash('danger', err && err.message || err || _('Could not find list with specified ID'));
|
||||||
return res.redirect('/lists');
|
return res.redirect('/lists');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -674,11 +676,11 @@ router.post('/subscription/import-restart', passport.parseForm, passport.csrfPro
|
||||||
failed: 0
|
failed: 0
|
||||||
}, (err, importer) => {
|
}, (err, importer) => {
|
||||||
if (err || !importer) {
|
if (err || !importer) {
|
||||||
req.flash('danger', err && err.message || err || 'Could not find import data with specified ID');
|
req.flash('danger', err && err.message || err || _('Could not find import data with specified ID'));
|
||||||
return res.redirect('/lists');
|
return res.redirect('/lists');
|
||||||
}
|
}
|
||||||
|
|
||||||
req.flash('success', 'Import restarted');
|
req.flash('success', _('Import restarted'));
|
||||||
res.redirect('/lists/view/' + list.id + '?tab=imports');
|
res.redirect('/lists/view/' + list.id + '?tab=imports');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -688,13 +690,13 @@ router.get('/subscription/:id/import/:importId/failed', (req, res) => {
|
||||||
let start = 0;
|
let start = 0;
|
||||||
lists.get(req.params.id, (err, list) => {
|
lists.get(req.params.id, (err, list) => {
|
||||||
if (err || !list) {
|
if (err || !list) {
|
||||||
req.flash('danger', err && err.message || err || 'Could not find list with specified ID');
|
req.flash('danger', err && err.message || err || _('Could not find list with specified ID'));
|
||||||
return res.redirect('/lists');
|
return res.redirect('/lists');
|
||||||
}
|
}
|
||||||
|
|
||||||
subscriptions.getImport(req.params.id, req.params.importId, (err, data) => {
|
subscriptions.getImport(req.params.id, req.params.importId, (err, data) => {
|
||||||
if (err || !data) {
|
if (err || !data) {
|
||||||
req.flash('danger', err && err.message || err || 'Could not find import data with specified ID');
|
req.flash('danger', err && err.message || err || _('Could not find import data with specified ID'));
|
||||||
return res.redirect('/lists');
|
return res.redirect('/lists');
|
||||||
}
|
}
|
||||||
subscriptions.getFailedImports(req.params.importId, (err, rows) => {
|
subscriptions.getFailedImports(req.params.importId, (err, rows) => {
|
||||||
|
|
|
@ -6,10 +6,11 @@ let passport = require('../lib/passport');
|
||||||
let lists = require('../lib/models/lists');
|
let lists = require('../lib/models/lists');
|
||||||
let segments = require('../lib/models/segments');
|
let segments = require('../lib/models/segments');
|
||||||
let tools = require('../lib/tools');
|
let tools = require('../lib/tools');
|
||||||
|
let _ = require('../lib/translate')._;
|
||||||
|
|
||||||
router.all('/*', (req, res, next) => {
|
router.all('/*', (req, res, next) => {
|
||||||
if (!req.user) {
|
if (!req.user) {
|
||||||
req.flash('danger', 'Need to be logged in to access restricted content');
|
req.flash('danger', _('Need to be logged in to access restricted content'));
|
||||||
return res.redirect('/users/login?next=' + encodeURIComponent(req.originalUrl));
|
return res.redirect('/users/login?next=' + encodeURIComponent(req.originalUrl));
|
||||||
}
|
}
|
||||||
res.setSelectedMenu('lists');
|
res.setSelectedMenu('lists');
|
||||||
|
@ -24,7 +25,7 @@ router.get('/:list', (req, res) => {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!list) {
|
if (!list) {
|
||||||
req.flash('danger', 'Selected list ID not found');
|
req.flash('danger', _('Selected list ID not found'));
|
||||||
return res.redirect('/');
|
return res.redirect('/');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -55,7 +56,7 @@ router.get('/:list/create', passport.csrfProtection, (req, res) => {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!list) {
|
if (!list) {
|
||||||
req.flash('danger', 'Selected list ID not found');
|
req.flash('danger', _('Selected list ID not found'));
|
||||||
return res.redirect('/');
|
return res.redirect('/');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -82,10 +83,10 @@ router.get('/:list/create', passport.csrfProtection, (req, res) => {
|
||||||
router.post('/:list/create', passport.parseForm, passport.csrfProtection, (req, res) => {
|
router.post('/:list/create', passport.parseForm, passport.csrfProtection, (req, res) => {
|
||||||
segments.create(req.params.list, req.body, (err, id) => {
|
segments.create(req.params.list, req.body, (err, id) => {
|
||||||
if (err || !id) {
|
if (err || !id) {
|
||||||
req.flash('danger', err && err.message || err || 'Could not create segment');
|
req.flash('danger', err && err.message || err || _('Could not create segment'));
|
||||||
return res.redirect('/segments/' + encodeURIComponent(req.params.list) + '/create?' + tools.queryParams(req.body));
|
return res.redirect('/segments/' + encodeURIComponent(req.params.list) + '/create?' + tools.queryParams(req.body));
|
||||||
}
|
}
|
||||||
req.flash('success', 'Segment created');
|
req.flash('success', _('Segment created'));
|
||||||
res.redirect('/segments/' + encodeURIComponent(req.params.list) + '/view/' + id);
|
res.redirect('/segments/' + encodeURIComponent(req.params.list) + '/view/' + id);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -98,7 +99,7 @@ router.get('/:list/view/:id', (req, res) => {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!list) {
|
if (!list) {
|
||||||
req.flash('danger', 'Selected list ID not found');
|
req.flash('danger', _('Selected list ID not found'));
|
||||||
return res.redirect('/');
|
return res.redirect('/');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -109,7 +110,7 @@ router.get('/:list/view/:id', (req, res) => {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!segment) {
|
if (!segment) {
|
||||||
req.flash('danger', 'Selected segment ID not found');
|
req.flash('danger', _('Selected segment ID not found'));
|
||||||
return res.redirect('/');
|
return res.redirect('/');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -147,7 +148,7 @@ router.get('/:list/edit/:segment', passport.csrfProtection, (req, res) => {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!list) {
|
if (!list) {
|
||||||
req.flash('danger', 'Selected list ID not found');
|
req.flash('danger', _('Selected list ID not found'));
|
||||||
return res.redirect('/');
|
return res.redirect('/');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -184,9 +185,9 @@ router.post('/:list/edit', passport.parseForm, passport.csrfProtection, (req, re
|
||||||
if (err) {
|
if (err) {
|
||||||
req.flash('danger', err.message || err);
|
req.flash('danger', err.message || err);
|
||||||
} else if (updated) {
|
} else if (updated) {
|
||||||
req.flash('success', 'Segment settings updated');
|
req.flash('success', _('Segment settings updated'));
|
||||||
} else {
|
} else {
|
||||||
req.flash('info', 'Segment settings not updated');
|
req.flash('info', _('Segment settings not updated'));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (req.body.id) {
|
if (req.body.id) {
|
||||||
|
@ -202,9 +203,9 @@ router.post('/:list/delete', passport.parseForm, passport.csrfProtection, (req,
|
||||||
if (err) {
|
if (err) {
|
||||||
req.flash('danger', err && err.message || err);
|
req.flash('danger', err && err.message || err);
|
||||||
} else if (deleted) {
|
} else if (deleted) {
|
||||||
req.flash('success', 'Segment deleted');
|
req.flash('success', _('Segment deleted'));
|
||||||
} else {
|
} else {
|
||||||
req.flash('info', 'Could not delete specified segment');
|
req.flash('info', _('Could not delete specified segment'));
|
||||||
}
|
}
|
||||||
|
|
||||||
return res.redirect('/segments/' + encodeURIComponent(req.params.list));
|
return res.redirect('/segments/' + encodeURIComponent(req.params.list));
|
||||||
|
@ -219,7 +220,7 @@ router.get('/:list/rules/:segment/create', passport.csrfProtection, (req, res) =
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!list) {
|
if (!list) {
|
||||||
req.flash('danger', 'Selected list ID not found');
|
req.flash('danger', _('Selected list ID not found'));
|
||||||
return res.redirect('/');
|
return res.redirect('/');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -251,7 +252,7 @@ router.post('/:list/rules/:segment/next', passport.parseForm, passport.csrfProte
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!list) {
|
if (!list) {
|
||||||
req.flash('danger', 'Selected list ID not found');
|
req.flash('danger', _('Selected list ID not found'));
|
||||||
return res.redirect('/');
|
return res.redirect('/');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -262,13 +263,13 @@ router.post('/:list/rules/:segment/next', passport.parseForm, passport.csrfProte
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!segment) {
|
if (!segment) {
|
||||||
req.flash('danger', 'Selected segment not found');
|
req.flash('danger', _('Selected segment not found'));
|
||||||
return res.redirect('/segments/' + encodeURIComponent(req.params.list));
|
return res.redirect('/segments/' + encodeURIComponent(req.params.list));
|
||||||
}
|
}
|
||||||
|
|
||||||
let column = segment.columns.filter(column => column.column === req.body.column).pop();
|
let column = segment.columns.filter(column => column.column === req.body.column).pop();
|
||||||
if (!column) {
|
if (!column) {
|
||||||
req.flash('danger', 'Invalid rule type');
|
req.flash('danger', _('Invalid rule type'));
|
||||||
return res.redirect('/segments/' + encodeURIComponent(req.params.list) + '/rules/' + segment.id + '/create?' + tools.queryParams(req.body));
|
return res.redirect('/segments/' + encodeURIComponent(req.params.list) + '/rules/' + segment.id + '/create?' + tools.queryParams(req.body));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -285,7 +286,7 @@ router.get('/:list/rules/:segment/configure', passport.csrfProtection, (req, res
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!list) {
|
if (!list) {
|
||||||
req.flash('danger', 'Selected list ID not found');
|
req.flash('danger', _('Selected list ID not found'));
|
||||||
return res.redirect('/');
|
return res.redirect('/');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -296,13 +297,13 @@ router.get('/:list/rules/:segment/configure', passport.csrfProtection, (req, res
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!segment) {
|
if (!segment) {
|
||||||
req.flash('danger', 'Selected segment not found');
|
req.flash('danger', _('Selected segment not found'));
|
||||||
return res.redirect('/segments/' + encodeURIComponent(req.params.list));
|
return res.redirect('/segments/' + encodeURIComponent(req.params.list));
|
||||||
}
|
}
|
||||||
|
|
||||||
let column = segment.columns.filter(column => column.column === req.query.column).pop();
|
let column = segment.columns.filter(column => column.column === req.query.column).pop();
|
||||||
if (!column) {
|
if (!column) {
|
||||||
req.flash('danger', 'Invalid rule type');
|
req.flash('danger', _('Invalid rule type'));
|
||||||
return res.redirect('/segments/' + encodeURIComponent(req.params.list) + '/rules/' + segment.id + '/create?' + tools.queryParams(req.body));
|
return res.redirect('/segments/' + encodeURIComponent(req.params.list) + '/rules/' + segment.id + '/create?' + tools.queryParams(req.body));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -332,16 +333,16 @@ router.post('/:list/rules/:segment/create', passport.parseForm, passport.csrfPro
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!list) {
|
if (!list) {
|
||||||
req.flash('danger', 'Selected list ID not found');
|
req.flash('danger', _('Selected list ID not found'));
|
||||||
return res.redirect('/');
|
return res.redirect('/');
|
||||||
}
|
}
|
||||||
|
|
||||||
segments.createRule(req.params.segment, req.body, (err, id) => {
|
segments.createRule(req.params.segment, req.body, (err, id) => {
|
||||||
if (err || !id) {
|
if (err || !id) {
|
||||||
req.flash('danger', err && err.message || err || 'Could not create rule');
|
req.flash('danger', err && err.message || err || _('Could not create rule'));
|
||||||
return res.redirect('/segments/' + encodeURIComponent(req.params.list) + '/rules/' + encodeURIComponent(req.params.segment) + '/configure?' + tools.queryParams(req.body));
|
return res.redirect('/segments/' + encodeURIComponent(req.params.list) + '/rules/' + encodeURIComponent(req.params.segment) + '/configure?' + tools.queryParams(req.body));
|
||||||
}
|
}
|
||||||
req.flash('success', 'Rule created');
|
req.flash('success', _('Rule created'));
|
||||||
res.redirect('/segments/' + encodeURIComponent(req.params.list) + '/view/' + encodeURIComponent(req.params.segment));
|
res.redirect('/segments/' + encodeURIComponent(req.params.list) + '/view/' + encodeURIComponent(req.params.segment));
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -355,7 +356,7 @@ router.get('/:list/rules/:segment/edit/:rule', passport.csrfProtection, (req, re
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!list) {
|
if (!list) {
|
||||||
req.flash('danger', 'Selected list ID not found');
|
req.flash('danger', _('Selected list ID not found'));
|
||||||
return res.redirect('/');
|
return res.redirect('/');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -366,7 +367,7 @@ router.get('/:list/rules/:segment/edit/:rule', passport.csrfProtection, (req, re
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!segment) {
|
if (!segment) {
|
||||||
req.flash('danger', 'Selected segment not found');
|
req.flash('danger', _('Selected segment not found'));
|
||||||
return res.redirect('/segments/' + encodeURIComponent(req.params.list));
|
return res.redirect('/segments/' + encodeURIComponent(req.params.list));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -377,13 +378,13 @@ router.get('/:list/rules/:segment/edit/:rule', passport.csrfProtection, (req, re
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!segment) {
|
if (!segment) {
|
||||||
req.flash('danger', 'Selected segment not found');
|
req.flash('danger', _('Selected segment not found'));
|
||||||
return res.redirect('/segments/' + encodeURIComponent(req.params.list));
|
return res.redirect('/segments/' + encodeURIComponent(req.params.list));
|
||||||
}
|
}
|
||||||
|
|
||||||
let column = segment.columns.filter(column => column.column === rule.column).pop();
|
let column = segment.columns.filter(column => column.column === rule.column).pop();
|
||||||
if (!column) {
|
if (!column) {
|
||||||
req.flash('danger', 'Invalid rule type');
|
req.flash('danger', _('Invalid rule type'));
|
||||||
return res.redirect('/segments/' + encodeURIComponent(req.params.list) + '/view/' + segment.id);
|
return res.redirect('/segments/' + encodeURIComponent(req.params.list) + '/view/' + segment.id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -406,9 +407,9 @@ router.post('/:list/rules/:segment/edit', passport.parseForm, passport.csrfProte
|
||||||
if (err) {
|
if (err) {
|
||||||
req.flash('danger', err.message || err);
|
req.flash('danger', err.message || err);
|
||||||
} else if (updated) {
|
} else if (updated) {
|
||||||
req.flash('success', 'Rule settings updated');
|
req.flash('success', _('Rule settings updated'));
|
||||||
} else {
|
} else {
|
||||||
req.flash('info', 'Rule settings not updated');
|
req.flash('info', _('Rule settings not updated'));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (req.params.segment) {
|
if (req.params.segment) {
|
||||||
|
@ -424,9 +425,9 @@ router.post('/:list/rules/:segment/delete', passport.parseForm, passport.csrfPro
|
||||||
if (err) {
|
if (err) {
|
||||||
req.flash('danger', err && err.message || err);
|
req.flash('danger', err && err.message || err);
|
||||||
} else if (deleted) {
|
} else if (deleted) {
|
||||||
req.flash('success', 'Rule deleted');
|
req.flash('success', _('Rule deleted'));
|
||||||
} else {
|
} else {
|
||||||
req.flash('info', 'Could not delete specified rule');
|
req.flash('info', _('Could not delete specified rule'));
|
||||||
}
|
}
|
||||||
|
|
||||||
return res.redirect('/segments/' + encodeURIComponent(req.params.list) + '/view/' + encodeURIComponent(req.params.segment));
|
return res.redirect('/segments/' + encodeURIComponent(req.params.list) + '/view/' + encodeURIComponent(req.params.segment));
|
||||||
|
|
|
@ -13,11 +13,13 @@ let fields = require('../lib/models/fields');
|
||||||
let subscriptions = require('../lib/models/subscriptions');
|
let subscriptions = require('../lib/models/subscriptions');
|
||||||
let settings = require('../lib/models/settings');
|
let settings = require('../lib/models/settings');
|
||||||
let openpgp = require('openpgp');
|
let openpgp = require('openpgp');
|
||||||
|
let _ = require('../lib/translate')._;
|
||||||
|
let util = require('util');
|
||||||
|
|
||||||
router.get('/subscribe/:cid', (req, res, next) => {
|
router.get('/subscribe/:cid', (req, res, next) => {
|
||||||
subscriptions.subscribe(req.params.cid, req.ip, (err, subscription) => {
|
subscriptions.subscribe(req.params.cid, req.ip, (err, subscription) => {
|
||||||
if (!err && !subscription) {
|
if (!err && !subscription) {
|
||||||
err = new Error('Selected subscription not found');
|
err = new Error(_('Selected subscription not found'));
|
||||||
err.status = 404;
|
err.status = 404;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -27,7 +29,7 @@ router.get('/subscribe/:cid', (req, res, next) => {
|
||||||
|
|
||||||
lists.get(subscription.list, (err, list) => {
|
lists.get(subscription.list, (err, list) => {
|
||||||
if (!err && !list) {
|
if (!err && !list) {
|
||||||
err = new Error('Selected list not found');
|
err = new Error(_('Selected list not found'));
|
||||||
err.status = 404;
|
err.status = 404;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -73,7 +75,7 @@ router.get('/subscribe/:cid', (req, res, next) => {
|
||||||
name: [].concat(subscription.firstName || []).concat(subscription.lastName || []).join(' '),
|
name: [].concat(subscription.firstName || []).concat(subscription.lastName || []).join(' '),
|
||||||
address: subscription.email
|
address: subscription.email
|
||||||
},
|
},
|
||||||
subject: list.name + ': Subscription Confirmed',
|
subject: util.format(_('%s: Subscription Confirmed'), list.name),
|
||||||
encryptionKeys
|
encryptionKeys
|
||||||
}, {
|
}, {
|
||||||
html: 'emails/subscription-confirmed-html.hbs',
|
html: 'emails/subscription-confirmed-html.hbs',
|
||||||
|
@ -98,7 +100,7 @@ router.get('/subscribe/:cid', (req, res, next) => {
|
||||||
router.get('/:cid', passport.csrfProtection, (req, res, next) => {
|
router.get('/:cid', passport.csrfProtection, (req, res, next) => {
|
||||||
lists.getByCid(req.params.cid, (err, list) => {
|
lists.getByCid(req.params.cid, (err, list) => {
|
||||||
if (!err && !list) {
|
if (!err && !list) {
|
||||||
err = new Error('Selected list not found');
|
err = new Error(_('Selected list not found'));
|
||||||
err.status = 404;
|
err.status = 404;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -136,7 +138,7 @@ router.get('/:cid', passport.csrfProtection, (req, res, next) => {
|
||||||
router.get('/:cid/confirm-notice', (req, res, next) => {
|
router.get('/:cid/confirm-notice', (req, res, next) => {
|
||||||
lists.getByCid(req.params.cid, (err, list) => {
|
lists.getByCid(req.params.cid, (err, list) => {
|
||||||
if (!err && !list) {
|
if (!err && !list) {
|
||||||
err = new Error('Selected list not found');
|
err = new Error(_('Selected list not found'));
|
||||||
err.status = 404;
|
err.status = 404;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -161,7 +163,7 @@ router.get('/:cid/confirm-notice', (req, res, next) => {
|
||||||
router.get('/:cid/updated-notice', (req, res, next) => {
|
router.get('/:cid/updated-notice', (req, res, next) => {
|
||||||
lists.getByCid(req.params.cid, (err, list) => {
|
lists.getByCid(req.params.cid, (err, list) => {
|
||||||
if (!err && !list) {
|
if (!err && !list) {
|
||||||
err = new Error('Selected list not found');
|
err = new Error(_('Selected list not found'));
|
||||||
err.status = 404;
|
err.status = 404;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -186,7 +188,7 @@ router.get('/:cid/updated-notice', (req, res, next) => {
|
||||||
router.get('/:cid/unsubscribe-notice', (req, res, next) => {
|
router.get('/:cid/unsubscribe-notice', (req, res, next) => {
|
||||||
lists.getByCid(req.params.cid, (err, list) => {
|
lists.getByCid(req.params.cid, (err, list) => {
|
||||||
if (!err && !list) {
|
if (!err && !list) {
|
||||||
err = new Error('Selected list not found');
|
err = new Error(_('Selected list not found'));
|
||||||
err.status = 404;
|
err.status = 404;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -212,7 +214,7 @@ router.post('/:cid/subscribe', passport.parseForm, passport.csrfProtection, (req
|
||||||
let email = (req.body.email || '').toString().trim();
|
let email = (req.body.email || '').toString().trim();
|
||||||
|
|
||||||
if (!email) {
|
if (!email) {
|
||||||
req.flash('danger', 'Email address not set');
|
req.flash('danger', _('Email address not set'));
|
||||||
return res.redirect('/subscription/' + encodeURIComponent(req.params.cid) + '?' + tools.queryParams(req.body));
|
return res.redirect('/subscription/' + encodeURIComponent(req.params.cid) + '?' + tools.queryParams(req.body));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -227,7 +229,7 @@ router.post('/:cid/subscribe', passport.parseForm, passport.csrfProtection, (req
|
||||||
|
|
||||||
lists.getByCid(req.params.cid, (err, list) => {
|
lists.getByCid(req.params.cid, (err, list) => {
|
||||||
if (!err && !list) {
|
if (!err && !list) {
|
||||||
err = new Error('Selected list not found');
|
err = new Error(_('Selected list not found'));
|
||||||
err.status = 404;
|
err.status = 404;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -250,7 +252,7 @@ router.post('/:cid/subscribe', passport.parseForm, passport.csrfProtection, (req
|
||||||
|
|
||||||
subscriptions.addConfirmation(list, email, req.ip, data, (err, confirmCid) => {
|
subscriptions.addConfirmation(list, email, req.ip, data, (err, confirmCid) => {
|
||||||
if (!err && !confirmCid) {
|
if (!err && !confirmCid) {
|
||||||
err = new Error('Could not store confirmation data');
|
err = new Error(_('Could not store confirmation data'));
|
||||||
}
|
}
|
||||||
if (err) {
|
if (err) {
|
||||||
req.flash('danger', err.message || err);
|
req.flash('danger', err.message || err);
|
||||||
|
@ -265,7 +267,7 @@ router.post('/:cid/subscribe', passport.parseForm, passport.csrfProtection, (req
|
||||||
router.get('/:lcid/manage/:ucid', passport.csrfProtection, (req, res, next) => {
|
router.get('/:lcid/manage/:ucid', passport.csrfProtection, (req, res, next) => {
|
||||||
lists.getByCid(req.params.lcid, (err, list) => {
|
lists.getByCid(req.params.lcid, (err, list) => {
|
||||||
if (!err && !list) {
|
if (!err && !list) {
|
||||||
err = new Error('Selected list not found');
|
err = new Error(_('Selected list not found'));
|
||||||
err.status = 404;
|
err.status = 404;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -279,7 +281,7 @@ router.get('/:lcid/manage/:ucid', passport.csrfProtection, (req, res, next) => {
|
||||||
}
|
}
|
||||||
subscriptions.get(list.id, req.params.ucid, (err, subscription) => {
|
subscriptions.get(list.id, req.params.ucid, (err, subscription) => {
|
||||||
if (!err && !subscription) {
|
if (!err && !subscription) {
|
||||||
err = new Error('Subscription not found from this list');
|
err = new Error(_('Subscription not found from this list'));
|
||||||
err.status = 404;
|
err.status = 404;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -312,7 +314,7 @@ router.get('/:lcid/manage/:ucid', passport.csrfProtection, (req, res, next) => {
|
||||||
router.post('/:lcid/manage', passport.parseForm, passport.csrfProtection, (req, res, next) => {
|
router.post('/:lcid/manage', passport.parseForm, passport.csrfProtection, (req, res, next) => {
|
||||||
lists.getByCid(req.params.lcid, (err, list) => {
|
lists.getByCid(req.params.lcid, (err, list) => {
|
||||||
if (!err && !list) {
|
if (!err && !list) {
|
||||||
err = new Error('Selected list not found');
|
err = new Error(_('Selected list not found'));
|
||||||
err.status = 404;
|
err.status = 404;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -334,7 +336,7 @@ router.post('/:lcid/manage', passport.parseForm, passport.csrfProtection, (req,
|
||||||
router.get('/:lcid/manage-address/:ucid', passport.csrfProtection, (req, res, next) => {
|
router.get('/:lcid/manage-address/:ucid', passport.csrfProtection, (req, res, next) => {
|
||||||
lists.getByCid(req.params.lcid, (err, list) => {
|
lists.getByCid(req.params.lcid, (err, list) => {
|
||||||
if (!err && !list) {
|
if (!err && !list) {
|
||||||
err = new Error('Selected list not found');
|
err = new Error(_('Selected list not found'));
|
||||||
err.status = 404;
|
err.status = 404;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -344,7 +346,7 @@ router.get('/:lcid/manage-address/:ucid', passport.csrfProtection, (req, res, ne
|
||||||
|
|
||||||
subscriptions.get(list.id, req.params.ucid, (err, subscription) => {
|
subscriptions.get(list.id, req.params.ucid, (err, subscription) => {
|
||||||
if (!err && !subscription) {
|
if (!err && !subscription) {
|
||||||
err = new Error('Subscription not found from this list');
|
err = new Error(_('Subscription not found from this list'));
|
||||||
err.status = 404;
|
err.status = 404;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -363,7 +365,7 @@ router.get('/:lcid/manage-address/:ucid', passport.csrfProtection, (req, res, ne
|
||||||
router.post('/:lcid/manage-address', passport.parseForm, passport.csrfProtection, (req, res, next) => {
|
router.post('/:lcid/manage-address', passport.parseForm, passport.csrfProtection, (req, res, next) => {
|
||||||
lists.getByCid(req.params.lcid, (err, list) => {
|
lists.getByCid(req.params.lcid, (err, list) => {
|
||||||
if (!err && !list) {
|
if (!err && !list) {
|
||||||
err = new Error('Selected list not found');
|
err = new Error(_('Selected list not found'));
|
||||||
err.status = 404;
|
err.status = 404;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -378,7 +380,7 @@ router.post('/:lcid/manage-address', passport.parseForm, passport.csrfProtection
|
||||||
return res.redirect('/subscription/' + encodeURIComponent(req.params.lcid) + '/manage-address/' + encodeURIComponent(req.body.cid) + '?' + tools.queryParams(req.body));
|
return res.redirect('/subscription/' + encodeURIComponent(req.params.lcid) + '/manage-address/' + encodeURIComponent(req.body.cid) + '?' + tools.queryParams(req.body));
|
||||||
}
|
}
|
||||||
|
|
||||||
req.flash('info', 'Email address updated, check your mailbox for verification instructions');
|
req.flash('info', _('Email address updated, check your mailbox for verification instructions'));
|
||||||
res.redirect('/subscription/' + req.params.lcid + '/manage/' + req.body.cid);
|
res.redirect('/subscription/' + req.params.lcid + '/manage/' + req.body.cid);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -387,7 +389,7 @@ router.post('/:lcid/manage-address', passport.parseForm, passport.csrfProtection
|
||||||
router.get('/:lcid/unsubscribe/:ucid', passport.csrfProtection, (req, res, next) => {
|
router.get('/:lcid/unsubscribe/:ucid', passport.csrfProtection, (req, res, next) => {
|
||||||
lists.getByCid(req.params.lcid, (err, list) => {
|
lists.getByCid(req.params.lcid, (err, list) => {
|
||||||
if (!err && !list) {
|
if (!err && !list) {
|
||||||
err = new Error('Selected list not found');
|
err = new Error(_('Selected list not found'));
|
||||||
err.status = 404;
|
err.status = 404;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -397,7 +399,7 @@ router.get('/:lcid/unsubscribe/:ucid', passport.csrfProtection, (req, res, next)
|
||||||
|
|
||||||
subscriptions.get(list.id, req.params.ucid, (err, subscription) => {
|
subscriptions.get(list.id, req.params.ucid, (err, subscription) => {
|
||||||
if (!err && !subscription) {
|
if (!err && !subscription) {
|
||||||
err = new Error('Subscription not found from this list');
|
err = new Error(_('Subscription not found from this list'));
|
||||||
err.status = 404;
|
err.status = 404;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -419,7 +421,7 @@ router.get('/:lcid/unsubscribe/:ucid', passport.csrfProtection, (req, res, next)
|
||||||
router.post('/:lcid/unsubscribe', passport.parseForm, passport.csrfProtection, (req, res, next) => {
|
router.post('/:lcid/unsubscribe', passport.parseForm, passport.csrfProtection, (req, res, next) => {
|
||||||
lists.getByCid(req.params.lcid, (err, list) => {
|
lists.getByCid(req.params.lcid, (err, list) => {
|
||||||
if (!err && !list) {
|
if (!err && !list) {
|
||||||
err = new Error('Selected list not found');
|
err = new Error(_('Selected list not found'));
|
||||||
err.status = 404;
|
err.status = 404;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -467,7 +469,7 @@ router.post('/:lcid/unsubscribe', passport.parseForm, passport.csrfProtection, (
|
||||||
name: [].concat(subscription.firstName || []).concat(subscription.lastName || []).join(' '),
|
name: [].concat(subscription.firstName || []).concat(subscription.lastName || []).join(' '),
|
||||||
address: subscription.email
|
address: subscription.email
|
||||||
},
|
},
|
||||||
subject: list.name + ': Subscription Confirmed',
|
subject: util.format(_('%s: Subscription Confirmed'), list.name),
|
||||||
encryptionKeys
|
encryptionKeys
|
||||||
}, {
|
}, {
|
||||||
html: 'emails/unsubscribe-confirmed-html.hbs',
|
html: 'emails/unsubscribe-confirmed-html.hbs',
|
||||||
|
@ -494,7 +496,7 @@ router.post('/publickey', passport.parseForm, passport.csrfProtection, (req, res
|
||||||
return next(err);
|
return next(err);
|
||||||
}
|
}
|
||||||
if (!configItems.pgpPrivateKey) {
|
if (!configItems.pgpPrivateKey) {
|
||||||
err = new Error('Public key is not set');
|
err = new Error(_('Public key is not set'));
|
||||||
err.status = 404;
|
err.status = 404;
|
||||||
return next(err);
|
return next(err);
|
||||||
}
|
}
|
||||||
|
@ -510,7 +512,7 @@ router.post('/publickey', passport.parseForm, passport.csrfProtection, (req, res
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!privKey) {
|
if (!privKey) {
|
||||||
err = new Error('Public key is not set');
|
err = new Error(_('Public key is not set'));
|
||||||
err.status = 404;
|
err.status = 404;
|
||||||
return next(err);
|
return next(err);
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,10 +10,11 @@ let helpers = require('../lib/helpers');
|
||||||
let striptags = require('striptags');
|
let striptags = require('striptags');
|
||||||
let passport = require('../lib/passport');
|
let passport = require('../lib/passport');
|
||||||
let mailer = require('../lib/mailer');
|
let mailer = require('../lib/mailer');
|
||||||
|
let _ = require('../lib/translate')._;
|
||||||
|
|
||||||
router.all('/*', (req, res, next) => {
|
router.all('/*', (req, res, next) => {
|
||||||
if (!req.user) {
|
if (!req.user) {
|
||||||
req.flash('danger', 'Need to be logged in to access restricted content');
|
req.flash('danger', _('Need to be logged in to access restricted content'));
|
||||||
return res.redirect('/users/login?next=' + encodeURIComponent(req.originalUrl));
|
return res.redirect('/users/login?next=' + encodeURIComponent(req.originalUrl));
|
||||||
}
|
}
|
||||||
res.setSelectedMenu('templates');
|
res.setSelectedMenu('templates');
|
||||||
|
@ -68,19 +69,19 @@ router.get('/create', passport.csrfProtection, (req, res, next) => {
|
||||||
data.text = data.text || rendererText(configItems);
|
data.text = data.text || rendererText(configItems);
|
||||||
data.disableWysiwyg = configItems.disableWysiwyg;
|
data.disableWysiwyg = configItems.disableWysiwyg;
|
||||||
|
|
||||||
data.editors = config.editors || [['summernote', 'Summernote']];
|
data.editors = config.editors || [
|
||||||
|
['summernote', 'Summernote']
|
||||||
|
];
|
||||||
data.editors = data.editors.map(ed => {
|
data.editors = data.editors.map(ed => {
|
||||||
let editor = {
|
let editor = {
|
||||||
name: ed[0],
|
name: ed[0],
|
||||||
label: ed[1],
|
label: ed[1]
|
||||||
};
|
};
|
||||||
if (config[editor.name] && config[editor.name].templates) {
|
if (config[editor.name] && config[editor.name].templates) {
|
||||||
editor.templates = config[editor.name].templates.map(tmpl => {
|
editor.templates = config[editor.name].templates.map(tmpl => ({
|
||||||
return {
|
|
||||||
name: tmpl[0],
|
name: tmpl[0],
|
||||||
label: tmpl[1],
|
label: tmpl[1]
|
||||||
}
|
}));
|
||||||
});
|
|
||||||
}
|
}
|
||||||
return editor;
|
return editor;
|
||||||
});
|
});
|
||||||
|
@ -94,10 +95,10 @@ router.get('/create', passport.csrfProtection, (req, res, next) => {
|
||||||
router.post('/create', passport.parseForm, passport.csrfProtection, (req, res) => {
|
router.post('/create', passport.parseForm, passport.csrfProtection, (req, res) => {
|
||||||
templates.create(req.body, (err, id) => {
|
templates.create(req.body, (err, id) => {
|
||||||
if (err || !id) {
|
if (err || !id) {
|
||||||
req.flash('danger', err && err.message || err || 'Could not create template');
|
req.flash('danger', err && err.message || err || _('Could not create template'));
|
||||||
return res.redirect('/templates/create?' + tools.queryParams(req.body));
|
return res.redirect('/templates/create?' + tools.queryParams(req.body));
|
||||||
}
|
}
|
||||||
req.flash('success', 'Template created');
|
req.flash('success', _('Template created'));
|
||||||
res.redirect('/templates/edit/' + id);
|
res.redirect('/templates/edit/' + id);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -105,7 +106,7 @@ router.post('/create', passport.parseForm, passport.csrfProtection, (req, res) =
|
||||||
router.get('/edit/:id', passport.csrfProtection, (req, res, next) => {
|
router.get('/edit/:id', passport.csrfProtection, (req, res, next) => {
|
||||||
templates.get(req.params.id, (err, template) => {
|
templates.get(req.params.id, (err, template) => {
|
||||||
if (err || !template) {
|
if (err || !template) {
|
||||||
req.flash('danger', err && err.message || err || 'Could not find template with specified ID');
|
req.flash('danger', err && err.message || err || _('Could not find template with specified ID'));
|
||||||
return res.redirect('/templates');
|
return res.redirect('/templates');
|
||||||
}
|
}
|
||||||
settings.list(['disableWysiwyg'], (err, configItems) => {
|
settings.list(['disableWysiwyg'], (err, configItems) => {
|
||||||
|
@ -136,9 +137,9 @@ router.post('/edit', passport.parseForm, passport.csrfProtection, (req, res) =>
|
||||||
if (err) {
|
if (err) {
|
||||||
req.flash('danger', err.message || err);
|
req.flash('danger', err.message || err);
|
||||||
} else if (updated) {
|
} else if (updated) {
|
||||||
req.flash('success', 'Template settings updated');
|
req.flash('success', _('Template settings updated'));
|
||||||
} else {
|
} else {
|
||||||
req.flash('info', 'Template settings not updated');
|
req.flash('info', _('Template settings not updated'));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (req.body.id) {
|
if (req.body.id) {
|
||||||
|
@ -154,9 +155,9 @@ router.post('/delete', passport.parseForm, passport.csrfProtection, (req, res) =
|
||||||
if (err) {
|
if (err) {
|
||||||
req.flash('danger', err && err.message || err);
|
req.flash('danger', err && err.message || err);
|
||||||
} else if (deleted) {
|
} else if (deleted) {
|
||||||
req.flash('success', 'Template deleted');
|
req.flash('success', _('Template deleted'));
|
||||||
} else {
|
} else {
|
||||||
req.flash('info', 'Could not delete specified template');
|
req.flash('info', _('Could not delete specified template'));
|
||||||
}
|
}
|
||||||
|
|
||||||
return res.redirect('/templates');
|
return res.redirect('/templates');
|
||||||
|
|
|
@ -10,10 +10,12 @@ let striptags = require('striptags');
|
||||||
let passport = require('../lib/passport');
|
let passport = require('../lib/passport');
|
||||||
let tools = require('../lib/tools');
|
let tools = require('../lib/tools');
|
||||||
let htmlescape = require('escape-html');
|
let htmlescape = require('escape-html');
|
||||||
|
let _ = require('../lib/translate')._;
|
||||||
|
let util = require('util');
|
||||||
|
|
||||||
router.all('/*', (req, res, next) => {
|
router.all('/*', (req, res, next) => {
|
||||||
if (!req.user) {
|
if (!req.user) {
|
||||||
req.flash('danger', 'Need to be logged in to access restricted content');
|
req.flash('danger', _('Need to be logged in to access restricted content'));
|
||||||
return res.redirect('/users/login?next=' + encodeURIComponent(req.originalUrl));
|
return res.redirect('/users/login?next=' + encodeURIComponent(req.originalUrl));
|
||||||
}
|
}
|
||||||
res.setSelectedMenu('triggers');
|
res.setSelectedMenu('triggers');
|
||||||
|
@ -57,7 +59,7 @@ router.get('/create-select', passport.csrfProtection, (req, res, next) => {
|
||||||
|
|
||||||
router.post('/create-select', passport.parseForm, passport.csrfProtection, (req, res) => {
|
router.post('/create-select', passport.parseForm, passport.csrfProtection, (req, res) => {
|
||||||
if (!req.body.list) {
|
if (!req.body.list) {
|
||||||
req.flash('danger', 'Could not find selected list');
|
req.flash('danger', _('Could not find selected list'));
|
||||||
return res.redirect('/triggers/create-select');
|
return res.redirect('/triggers/create-select');
|
||||||
}
|
}
|
||||||
res.redirect('/triggers/' + encodeURIComponent(req.body.list) + '/create');
|
res.redirect('/triggers/' + encodeURIComponent(req.body.list) + '/create');
|
||||||
|
@ -74,7 +76,7 @@ router.get('/:listId/create', passport.csrfProtection, (req, res, next) => {
|
||||||
|
|
||||||
lists.get(req.params.listId, (err, list) => {
|
lists.get(req.params.listId, (err, list) => {
|
||||||
if (err || !list) {
|
if (err || !list) {
|
||||||
req.flash('danger', err && err.message || err || 'Could not find selected list');
|
req.flash('danger', err && err.message || err || _('Could not find selected list'));
|
||||||
return res.redirect('/triggers/create-select');
|
return res.redirect('/triggers/create-select');
|
||||||
}
|
}
|
||||||
fields.list(list.id, (err, fieldList) => {
|
fields.list(list.id, (err, fieldList) => {
|
||||||
|
@ -126,14 +128,14 @@ router.get('/:listId/create', passport.csrfProtection, (req, res, next) => {
|
||||||
router.post('/create', passport.parseForm, passport.csrfProtection, (req, res) => {
|
router.post('/create', passport.parseForm, passport.csrfProtection, (req, res) => {
|
||||||
triggers.create(req.body, (err, id) => {
|
triggers.create(req.body, (err, id) => {
|
||||||
if (err || !id) {
|
if (err || !id) {
|
||||||
req.flash('danger', err && err.message || err || 'Could not create trigger');
|
req.flash('danger', err && err.message || err || _('Could not create trigger'));
|
||||||
if (req.body.list) {
|
if (req.body.list) {
|
||||||
return res.redirect('/triggers/' + encodeURIComponent(req.body.list) + '/create?' + tools.queryParams(req.body));
|
return res.redirect('/triggers/' + encodeURIComponent(req.body.list) + '/create?' + tools.queryParams(req.body));
|
||||||
} else {
|
} else {
|
||||||
return res.redirect('/triggers');
|
return res.redirect('/triggers');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
req.flash('success', 'Trigger “' + req.body.name + '” created');
|
req.flash('success', util.format(_('Trigger “%s” created'), req.body.name));
|
||||||
res.redirect('/triggers');
|
res.redirect('/triggers');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -141,7 +143,7 @@ router.post('/create', passport.parseForm, passport.csrfProtection, (req, res) =
|
||||||
router.get('/edit/:id', passport.csrfProtection, (req, res, next) => {
|
router.get('/edit/:id', passport.csrfProtection, (req, res, next) => {
|
||||||
triggers.get(req.params.id, (err, trigger) => {
|
triggers.get(req.params.id, (err, trigger) => {
|
||||||
if (err || !trigger) {
|
if (err || !trigger) {
|
||||||
req.flash('danger', err && err.message || err || 'Could not find campaign with specified ID');
|
req.flash('danger', err && err.message || err || _('Could not find campaign with specified ID'));
|
||||||
return res.redirect('/campaigns');
|
return res.redirect('/campaigns');
|
||||||
}
|
}
|
||||||
trigger.csrfToken = req.csrfToken();
|
trigger.csrfToken = req.csrfToken();
|
||||||
|
@ -149,7 +151,7 @@ router.get('/edit/:id', passport.csrfProtection, (req, res, next) => {
|
||||||
|
|
||||||
lists.get(trigger.list, (err, list) => {
|
lists.get(trigger.list, (err, list) => {
|
||||||
if (err || !list) {
|
if (err || !list) {
|
||||||
req.flash('danger', err && err.message || err || 'Could not find selected list');
|
req.flash('danger', err && err.message || err || _('Could not find selected list'));
|
||||||
return res.redirect('/triggers');
|
return res.redirect('/triggers');
|
||||||
}
|
}
|
||||||
fields.list(list.id, (err, fieldList) => {
|
fields.list(list.id, (err, fieldList) => {
|
||||||
|
@ -209,9 +211,9 @@ router.post('/edit', passport.parseForm, passport.csrfProtection, (req, res) =>
|
||||||
req.flash('danger', err.message || err);
|
req.flash('danger', err.message || err);
|
||||||
return res.redirect('/triggers/edit/' + encodeURIComponent(req.body.id));
|
return res.redirect('/triggers/edit/' + encodeURIComponent(req.body.id));
|
||||||
} else if (updated) {
|
} else if (updated) {
|
||||||
req.flash('success', 'Trigger settings updated');
|
req.flash('success', _('Trigger settings updated'));
|
||||||
} else {
|
} else {
|
||||||
req.flash('info', 'Trigger settings not updated');
|
req.flash('info', _('Trigger settings not updated'));
|
||||||
}
|
}
|
||||||
|
|
||||||
return res.redirect('/triggers');
|
return res.redirect('/triggers');
|
||||||
|
@ -223,9 +225,9 @@ router.post('/delete', passport.parseForm, passport.csrfProtection, (req, res) =
|
||||||
if (err) {
|
if (err) {
|
||||||
req.flash('danger', err && err.message || err);
|
req.flash('danger', err && err.message || err);
|
||||||
} else if (deleted) {
|
} else if (deleted) {
|
||||||
req.flash('success', 'Trigger deleted');
|
req.flash('success', _('Trigger deleted'));
|
||||||
} else {
|
} else {
|
||||||
req.flash('info', 'Could not delete specified trigger');
|
req.flash('info', _('Could not delete specified trigger'));
|
||||||
}
|
}
|
||||||
|
|
||||||
return res.redirect('/triggers');
|
return res.redirect('/triggers');
|
||||||
|
@ -237,7 +239,7 @@ router.get('/status/:id', passport.csrfProtection, (req, res) => {
|
||||||
|
|
||||||
triggers.get(id, (err, trigger) => {
|
triggers.get(id, (err, trigger) => {
|
||||||
if (err || !trigger) {
|
if (err || !trigger) {
|
||||||
req.flash('danger', err && err.message || err || 'Could not find trigger with specified ID');
|
req.flash('danger', err && err.message || err || _('Could not find trigger with specified ID'));
|
||||||
return res.redirect('/triggers');
|
return res.redirect('/triggers');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -250,7 +252,7 @@ router.post('/status/ajax/:id', (req, res) => {
|
||||||
triggers.get(req.params.id, (err, trigger) => {
|
triggers.get(req.params.id, (err, trigger) => {
|
||||||
if (err || !trigger) {
|
if (err || !trigger) {
|
||||||
return res.json({
|
return res.json({
|
||||||
error: err && err.message || err || 'Trigger not found',
|
error: err && err.message || err || _('Trigger not found'),
|
||||||
data: []
|
data: []
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -292,7 +294,7 @@ router.post('/status/ajax/:id', (req, res) => {
|
||||||
htmlescape(row.firstName || ''),
|
htmlescape(row.firstName || ''),
|
||||||
htmlescape(row.lastName || ''),
|
htmlescape(row.lastName || ''),
|
||||||
'<span class="datestring" data-date="' + row.created.toISOString() + '" title="' + row.created.toISOString() + '">' + row.created.toISOString() + '</span>',
|
'<span class="datestring" data-date="' + row.created.toISOString() + '" title="' + row.created.toISOString() + '">' + row.created.toISOString() + '</span>',
|
||||||
'<span class="glyphicon glyphicon-wrench" aria-hidden="true"></span><a href="/lists/subscription/' + trigger.list + '/edit/' + row.cid + '">Edit</a>'
|
'<span class="glyphicon glyphicon-wrench" aria-hidden="true"></span><a href="/lists/subscription/' + trigger.list + '/edit/' + row.cid + '">' + _('Edit') + '</a>'
|
||||||
])
|
])
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -6,6 +6,7 @@ let router = new express.Router();
|
||||||
let users = require('../lib/models/users');
|
let users = require('../lib/models/users');
|
||||||
let fields = require('../lib/models/fields');
|
let fields = require('../lib/models/fields');
|
||||||
let settings = require('../lib/models/settings');
|
let settings = require('../lib/models/settings');
|
||||||
|
let _ = require('../lib/translate')._;
|
||||||
|
|
||||||
router.get('/logout', (req, res) => passport.logout(req, res));
|
router.get('/logout', (req, res) => passport.logout(req, res));
|
||||||
|
|
||||||
|
@ -28,7 +29,7 @@ router.post('/forgot', passport.parseForm, passport.csrfProtection, (req, res) =
|
||||||
req.flash('danger', err.message || err);
|
req.flash('danger', err.message || err);
|
||||||
return res.redirect('/users/forgot');
|
return res.redirect('/users/forgot');
|
||||||
} else {
|
} else {
|
||||||
req.flash('success', 'An email with password reset instructions has been sent to your email address, if it exists on our system.');
|
req.flash('success', _('An email with password reset instructions has been sent to your email address, if it exists on our system.'));
|
||||||
}
|
}
|
||||||
return res.redirect('/users/login');
|
return res.redirect('/users/login');
|
||||||
});
|
});
|
||||||
|
@ -42,7 +43,7 @@ router.get('/reset', passport.csrfProtection, (req, res) => {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!status) {
|
if (!status) {
|
||||||
req.flash('danger', 'Unknown or expired reset token');
|
req.flash('danger', _('Unknown or expired reset token'));
|
||||||
return res.redirect('/users/login');
|
return res.redirect('/users/login');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -60,9 +61,9 @@ router.post('/reset', passport.parseForm, passport.csrfProtection, (req, res) =>
|
||||||
req.flash('danger', err.message || err);
|
req.flash('danger', err.message || err);
|
||||||
return res.redirect('/users/reset?username=' + encodeURIComponent(req.body.username) + '&token=' + encodeURIComponent(req.body['reset-token']));
|
return res.redirect('/users/reset?username=' + encodeURIComponent(req.body.username) + '&token=' + encodeURIComponent(req.body['reset-token']));
|
||||||
} else if (!status) {
|
} else if (!status) {
|
||||||
req.flash('danger', 'Unknown or expired reset token');
|
req.flash('danger', _('Unknown or expired reset token'));
|
||||||
} else {
|
} else {
|
||||||
req.flash('success', 'Your password has been changed successfully');
|
req.flash('success', _('Your password has been changed successfully'));
|
||||||
}
|
}
|
||||||
|
|
||||||
return res.redirect('/users/login');
|
return res.redirect('/users/login');
|
||||||
|
@ -71,7 +72,7 @@ router.post('/reset', passport.parseForm, passport.csrfProtection, (req, res) =>
|
||||||
|
|
||||||
router.all('/api', (req, res, next) => {
|
router.all('/api', (req, res, next) => {
|
||||||
if (!req.user) {
|
if (!req.user) {
|
||||||
req.flash('danger', 'Need to be logged in to access restricted content');
|
req.flash('danger', _('Need to be logged in to access restricted content'));
|
||||||
return res.redirect('/users/login?next=' + encodeURIComponent(req.originalUrl));
|
return res.redirect('/users/login?next=' + encodeURIComponent(req.originalUrl));
|
||||||
}
|
}
|
||||||
next();
|
next();
|
||||||
|
@ -83,7 +84,7 @@ router.get('/api', passport.csrfProtection, (req, res, next) => {
|
||||||
return next(err);
|
return next(err);
|
||||||
}
|
}
|
||||||
if (!user) {
|
if (!user) {
|
||||||
return next(new Error('User data not found'));
|
return next(new Error(_('User data not found')));
|
||||||
}
|
}
|
||||||
settings.list(['serviceUrl'], (err, configItems) => {
|
settings.list(['serviceUrl'], (err, configItems) => {
|
||||||
if (err) {
|
if (err) {
|
||||||
|
@ -106,9 +107,9 @@ router.post('/api/reset-token', passport.parseForm, passport.csrfProtection, (re
|
||||||
if (err) {
|
if (err) {
|
||||||
req.flash('danger', err.message || err);
|
req.flash('danger', err.message || err);
|
||||||
} else if (success) {
|
} else if (success) {
|
||||||
req.flash('success', 'Access token updated');
|
req.flash('success', _('Access token updated'));
|
||||||
} else {
|
} else {
|
||||||
req.flash('info', 'Access token not updated');
|
req.flash('info', _('Access token not updated'));
|
||||||
}
|
}
|
||||||
return res.redirect('/users/api');
|
return res.redirect('/users/api');
|
||||||
});
|
});
|
||||||
|
@ -116,7 +117,7 @@ router.post('/api/reset-token', passport.parseForm, passport.csrfProtection, (re
|
||||||
|
|
||||||
router.all('/account', (req, res, next) => {
|
router.all('/account', (req, res, next) => {
|
||||||
if (!req.user) {
|
if (!req.user) {
|
||||||
req.flash('danger', 'Need to be logged in to access restricted content');
|
req.flash('danger', _('Need to be logged in to access restricted content'));
|
||||||
return res.redirect('/users/login?next=' + encodeURIComponent(req.originalUrl));
|
return res.redirect('/users/login?next=' + encodeURIComponent(req.originalUrl));
|
||||||
}
|
}
|
||||||
next();
|
next();
|
||||||
|
@ -135,9 +136,9 @@ router.post('/account', passport.parseForm, passport.csrfProtection, (req, res)
|
||||||
if (err) {
|
if (err) {
|
||||||
req.flash('danger', err.message || err);
|
req.flash('danger', err.message || err);
|
||||||
} else if (success) {
|
} else if (success) {
|
||||||
req.flash('success', 'Account information updated');
|
req.flash('success', _('Account information updated'));
|
||||||
} else {
|
} else {
|
||||||
req.flash('info', 'Account information not updated');
|
req.flash('info', _('Account information not updated'));
|
||||||
}
|
}
|
||||||
return res.redirect('/users/account');
|
return res.redirect('/users/account');
|
||||||
});
|
});
|
||||||
|
|
|
@ -6,6 +6,8 @@ let db = require('../lib/db');
|
||||||
let tools = require('../lib/tools');
|
let tools = require('../lib/tools');
|
||||||
let feed = require('../lib/feed');
|
let feed = require('../lib/feed');
|
||||||
let campaigns = require('../lib/models/campaigns');
|
let campaigns = require('../lib/models/campaigns');
|
||||||
|
let _ = require('../lib/translate')._;
|
||||||
|
let util = require('util');
|
||||||
|
|
||||||
const feed_timeout = 15 * 1000;
|
const feed_timeout = 15 * 1000;
|
||||||
const rss_timeout = 1 * 1000;
|
const rss_timeout = 1 * 1000;
|
||||||
|
@ -46,12 +48,12 @@ function feedLoop() {
|
||||||
let message;
|
let message;
|
||||||
if (err) {
|
if (err) {
|
||||||
log.error('Feed', err);
|
log.error('Feed', err);
|
||||||
message = 'Feed error: ' + err.message;
|
message = util.format(_('Feed error: %s'), err.message);
|
||||||
} else if (result) {
|
} else if (result) {
|
||||||
log.verbose('Feed', 'Added %s new campaigns for %s', result, parent.id);
|
log.verbose('Feed', 'Added %s new campaigns for %s', result, parent.id);
|
||||||
message = 'Found ' + result + ' new campaign messages from feed';
|
message = util.format(_('Found %s new campaign messages from feed'), result);
|
||||||
} else {
|
} else {
|
||||||
message = 'Found nothing new from the feed';
|
message = _('Found nothing new from the feed');
|
||||||
}
|
}
|
||||||
return updateRssInfo(parent.id, false, message, () => {
|
return updateRssInfo(parent.id, false, message, () => {
|
||||||
setTimeout(feedLoop, rss_timeout);
|
setTimeout(feedLoop, rss_timeout);
|
||||||
|
@ -138,7 +140,7 @@ function checkEntries(parent, entries, callback) {
|
||||||
|
|
||||||
let campaign = {
|
let campaign = {
|
||||||
type: 'entry',
|
type: 'entry',
|
||||||
name: entry.title || 'RSS entry ' + (entry.guid.substr(0, 67)),
|
name: entry.title || util.format(_('RSS entry %s'), entry.guid.substr(0, 67)),
|
||||||
from: parent.from,
|
from: parent.from,
|
||||||
address: parent.address,
|
address: parent.address,
|
||||||
subject: entry.title || parent.subject,
|
subject: entry.title || parent.subject,
|
||||||
|
|
|
@ -4,6 +4,7 @@ let log = require('npmlog');
|
||||||
|
|
||||||
let db = require('../lib/db');
|
let db = require('../lib/db');
|
||||||
let tools = require('../lib/tools');
|
let tools = require('../lib/tools');
|
||||||
|
let _ = require('../lib/translate')._;
|
||||||
|
|
||||||
let fields = require('../lib/models/fields');
|
let fields = require('../lib/models/fields');
|
||||||
let subscriptions = require('../lib/models/subscriptions');
|
let subscriptions = require('../lib/models/subscriptions');
|
||||||
|
@ -239,7 +240,7 @@ let importLoop = () => {
|
||||||
let failed = null;
|
let failed = null;
|
||||||
if (err) {
|
if (err) {
|
||||||
if (err.code === 'ENOENT') {
|
if (err.code === 'ENOENT') {
|
||||||
failed = 'Could not access import file';
|
failed = _('Could not access import file');
|
||||||
} else {
|
} else {
|
||||||
failed = err.message || err;
|
failed = err.message || err;
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,6 +16,8 @@ let url = require('url');
|
||||||
let htmlToText = require('html-to-text');
|
let htmlToText = require('html-to-text');
|
||||||
let request = require('request');
|
let request = require('request');
|
||||||
let libmime = require('libmime');
|
let libmime = require('libmime');
|
||||||
|
let _ = require('../lib/translate')._;
|
||||||
|
let util = require('util');
|
||||||
|
|
||||||
let attachmentCache = new Map();
|
let attachmentCache = new Map();
|
||||||
let attachmentCacheSize = 0;
|
let attachmentCacheSize = 0;
|
||||||
|
@ -299,14 +301,14 @@ function formatMessage(message, callback) {
|
||||||
return callback(err);
|
return callback(err);
|
||||||
}
|
}
|
||||||
if (!campaign) {
|
if (!campaign) {
|
||||||
return callback(new Error('Campaign not found'));
|
return callback(new Error(_('Campaign not found')));
|
||||||
}
|
}
|
||||||
lists.get(message.listId, (err, list) => {
|
lists.get(message.listId, (err, list) => {
|
||||||
if (err) {
|
if (err) {
|
||||||
return callback(err);
|
return callback(err);
|
||||||
}
|
}
|
||||||
if (!list) {
|
if (!list) {
|
||||||
return callback(new Error('List not found'));
|
return callback(new Error(_('List not found')));
|
||||||
}
|
}
|
||||||
|
|
||||||
settings.list(['serviceUrl', 'verpUse', 'verpHostname'], (err, configItems) => {
|
settings.list(['serviceUrl', 'verpUse', 'verpHostname'], (err, configItems) => {
|
||||||
|
@ -442,7 +444,7 @@ function formatMessage(message, callback) {
|
||||||
return callback(err);
|
return callback(err);
|
||||||
}
|
}
|
||||||
if (httpResponse.statusCode !== 200) {
|
if (httpResponse.statusCode !== 200) {
|
||||||
return callback(new Error('Received status code ' + httpResponse.statusCode + ' from ' + campaign.sourceUrl));
|
return callback(new Error(util.format(_('Received status code %s from %s'), httpResponse.statusCode, campaign.sourceUrl)));
|
||||||
}
|
}
|
||||||
renderAndSend(body && body.toString(), '', false);
|
renderAndSend(body && body.toString(), '', false);
|
||||||
});
|
});
|
||||||
|
|
|
@ -4,6 +4,8 @@ let log = require('npmlog');
|
||||||
let db = require('../lib/db');
|
let db = require('../lib/db');
|
||||||
let tools = require('../lib/tools');
|
let tools = require('../lib/tools');
|
||||||
let triggers = require('../lib/models/triggers');
|
let triggers = require('../lib/models/triggers');
|
||||||
|
let _ = require('../lib/translate')._;
|
||||||
|
let util = require('util');
|
||||||
|
|
||||||
function triggerLoop() {
|
function triggerLoop() {
|
||||||
checkTrigger((err, triggerId) => {
|
checkTrigger((err, triggerId) => {
|
||||||
|
@ -46,7 +48,7 @@ function checkTrigger(callback) {
|
||||||
return callback(err);
|
return callback(err);
|
||||||
}
|
}
|
||||||
if (!query) {
|
if (!query) {
|
||||||
return callback(new Error('Unknown trigger type ' + trigger.id));
|
return callback(new Error(util.format(_('Unknown trigger type %s'), trigger.id)));
|
||||||
}
|
}
|
||||||
trigger.query = query;
|
trigger.query = query;
|
||||||
fireTrigger(trigger, callback);
|
fireTrigger(trigger, callback);
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
<!-- The above 3 meta tags *must* come first in the head; any other head content must come *after* these tags -->
|
<!-- The above 3 meta tags *must* come first in the head; any other head content must come *after* these tags -->
|
||||||
<meta name="description" content="Self hosted email newsletter app">
|
<meta name="description" content="{{#translate}}Self hosted email newsletter app{{/translate}}">
|
||||||
<meta name="author" content="Andris Reinman">
|
<meta name="author" content="Andris Reinman">
|
||||||
<link rel="icon" href="/favicon.ico">
|
<link rel="icon" href="/favicon.ico">
|
||||||
|
|
||||||
|
|
|
@ -1,14 +1,14 @@
|
||||||
<ol class="breadcrumb">
|
<ol class="breadcrumb">
|
||||||
<li><a href="/">Home</a></li>
|
<li><a href="/">{{#translate}}Home{{/translate}}</a></li>
|
||||||
<li><a href="/campaigns">Campaigns</a></li>
|
<li><a href="/campaigns">{{#translate}}Campaigns{{/translate}}</a></li>
|
||||||
{{#if parent}}
|
{{#if parent}}
|
||||||
<li><a href="/campaigns/view/{{parent.id}}">{{parent.name}}</a></li>
|
<li><a href="/campaigns/view/{{parent.id}}">{{parent.name}}</a></li>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
<li><a href="/campaigns/view/{{id}}">{{name}}</a></li>
|
<li><a href="/campaigns/view/{{id}}">{{name}}</a></li>
|
||||||
<li class="active">Bounced info</li>
|
<li class="active">{{#translate}}Bounced info{{/translate}}</li>
|
||||||
</ol>
|
</ol>
|
||||||
|
|
||||||
<h2><span class="glyphicon glyphicon-inbox" aria-hidden="true"></span> {{name}} <small>Bounced info</small> <a class="btn btn-default btn-xs" href="/campaigns/view/{{id}}?tab=overview" role="button"><span class="glyphicon glyphicon-arrow-left" aria-hidden="true"></span> View campaign</a></h2>
|
<h2><span class="glyphicon glyphicon-inbox" aria-hidden="true"></span> {{name}} <small>{{#translate}}Bounced info{{/translate}}</small> <a class="btn btn-default btn-xs" href="/campaigns/view/{{id}}?tab=overview" role="button"><span class="glyphicon glyphicon-arrow-left" aria-hidden="true"></span> {{#translate}}View campaign{{/translate}}</a></h2>
|
||||||
|
|
||||||
<hr>
|
<hr>
|
||||||
|
|
||||||
|
@ -20,7 +20,7 @@
|
||||||
|
|
||||||
<div class="panel panel-info">
|
<div class="panel panel-info">
|
||||||
<!-- Default panel contents -->
|
<!-- Default panel contents -->
|
||||||
<div class="panel-heading">Subscribers who bounced and were unsubscribed:</div>
|
<div class="panel-heading">{{#translate}}Subscribers who bounced and were unsubscribed:{{/translate}}</div>
|
||||||
<div class="panel-body">
|
<div class="panel-body">
|
||||||
<div class="table-responsive">
|
<div class="table-responsive">
|
||||||
<table data-topic-url="/campaigns/status" data-topic-id="{{id}}/3" data-sort-column="1" data-sort-order="asc" class="table table-bordered table-hover data-table-ajax display nowrap" width="100%" data-row-sort="0,1,1,1,0,1,0">
|
<table data-topic-url="/campaigns/status" data-topic-id="{{id}}/3" data-sort-column="1" data-sort-order="asc" class="table table-bordered table-hover data-table-ajax display nowrap" width="100%" data-row-sort="0,1,1,1,0,1,0">
|
||||||
|
@ -30,19 +30,19 @@
|
||||||
#
|
#
|
||||||
</th>
|
</th>
|
||||||
<th>
|
<th>
|
||||||
Address
|
{{#translate}}Address{{/translate}}
|
||||||
</th>
|
</th>
|
||||||
<th>
|
<th>
|
||||||
First Name
|
{{#translate}}First Name{{/translate}}
|
||||||
</th>
|
</th>
|
||||||
<th>
|
<th>
|
||||||
Last Name
|
{{#translate}}Last Name{{/translate}}
|
||||||
</th>
|
</th>
|
||||||
<th>
|
<th>
|
||||||
SMTP response
|
{{#translate}}SMTP response{{/translate}}
|
||||||
</th>
|
</th>
|
||||||
<th>
|
<th>
|
||||||
Bounced
|
{{#translate}}Bounce time{{/translate}}
|
||||||
</th>
|
</th>
|
||||||
<th></th>
|
<th></th>
|
||||||
</tr>
|
</tr>
|
||||||
|
|
|
@ -1,22 +1,22 @@
|
||||||
<ol class="breadcrumb">
|
<ol class="breadcrumb">
|
||||||
<li><a href="/">Home</a></li>
|
<li><a href="/">{{#translate}}Home{{/translate}}</a></li>
|
||||||
<li class="active">Campaigns</li>
|
<li class="active">{{#translate}}Campaigns{{/translate}}</li>
|
||||||
</ol>
|
</ol>
|
||||||
|
|
||||||
<div class="pull-right">
|
<div class="pull-right">
|
||||||
<div class="btn-group">
|
<div class="btn-group">
|
||||||
<button type="button" class="btn btn-default dropdown-toggle" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
|
<button type="button" class="btn btn-default dropdown-toggle" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
|
||||||
Create Campaign <span class="caret"></span>
|
{{#translate}}Create Campaign{{/translate}} <span class="caret"></span>
|
||||||
</button>
|
</button>
|
||||||
<ul class="dropdown-menu">
|
<ul class="dropdown-menu">
|
||||||
<li><a href="/campaigns/create"><i class="glyphicon glyphicon-plus"></i> Normal Campaign</a></li>
|
<li><a href="/campaigns/create"><i class="glyphicon glyphicon-plus"></i> {{#translate}}Regular Campaign{{/translate}}</a></li>
|
||||||
<li><a href="/campaigns/create?type=rss"><i class="glyphicon glyphicon-signal"></i> RSS Campaign</a></li>
|
<li><a href="/campaigns/create?type=rss"><i class="glyphicon glyphicon-signal"></i> {{#translate}}RSS Campaign{{/translate}}</a></li>
|
||||||
<li><a href="/campaigns/create?type=triggered"><i class="glyphicon glyphicon-console"></i> Triggered Campaign</a></li>
|
<li><a href="/campaigns/create?type=triggered"><i class="glyphicon glyphicon-console"></i> {{#translate}}Triggered Campaign{{/translate}}</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<h2>Campaigns</h2>
|
<h2>{{#translate}}Campaigns{{/translate}}</h2>
|
||||||
|
|
||||||
<hr>
|
<hr>
|
||||||
|
|
||||||
|
@ -27,16 +27,16 @@
|
||||||
#
|
#
|
||||||
</th>
|
</th>
|
||||||
<th>
|
<th>
|
||||||
Name
|
{{#translate}}Name{{/translate}}
|
||||||
</th>
|
</th>
|
||||||
<th>
|
<th>
|
||||||
Description
|
{{#translate}}Description{{/translate}}
|
||||||
</th>
|
</th>
|
||||||
<th>
|
<th>
|
||||||
Status
|
{{#translate}}Status{{/translate}}
|
||||||
</th>
|
</th>
|
||||||
<th>
|
<th>
|
||||||
Created
|
{{#translate}}Created{{/translate}}
|
||||||
</th>
|
</th>
|
||||||
<th class="col-md-1">
|
<th class="col-md-1">
|
||||||
|
|
||||||
|
|
|
@ -1,14 +1,14 @@
|
||||||
<ol class="breadcrumb">
|
<ol class="breadcrumb">
|
||||||
<li><a href="/">Home</a></li>
|
<li><a href="/">{{#translate}}Home{{/translate}}</a></li>
|
||||||
<li><a href="/campaigns">Campaigns</a></li>
|
<li><a href="/campaigns">{{#translate}}Campaigns{{/translate}}</a></li>
|
||||||
{{#if parent}}
|
{{#if parent}}
|
||||||
<li><a href="/campaigns/view/{{parent.id}}">{{parent.name}}</a></li>
|
<li><a href="/campaigns/view/{{parent.id}}">{{parent.name}}</a></li>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
<li><a href="/campaigns/view/{{id}}">{{name}}</a></li>
|
<li><a href="/campaigns/view/{{id}}">{{name}}</a></li>
|
||||||
<li class="active">Link info</li>
|
<li class="active">{{#translate}}Link info{{/translate}}</li>
|
||||||
</ol>
|
</ol>
|
||||||
|
|
||||||
<h2><span class="glyphicon glyphicon-inbox" aria-hidden="true"></span> {{name}} <small>Link info</small> <a class="btn btn-default btn-xs" href="/campaigns/view/{{id}}?tab=links" role="button"><span class="glyphicon glyphicon-arrow-left" aria-hidden="true"></span> View campaign</a></h2>
|
<h2><span class="glyphicon glyphicon-inbox" aria-hidden="true"></span> {{name}} <small>{{#translate}}Link info{{/translate}}</small> <a class="btn btn-default btn-xs" href="/campaigns/view/{{id}}?tab=links" role="button"><span class="glyphicon glyphicon-arrow-left" aria-hidden="true"></span> {{#translate}}View campaign{{/translate}}</a></h2>
|
||||||
|
|
||||||
<hr>
|
<hr>
|
||||||
|
|
||||||
|
@ -20,23 +20,23 @@
|
||||||
<table class="table table-bordered table-hover">
|
<table class="table table-bordered table-hover">
|
||||||
<thead>
|
<thead>
|
||||||
<th>
|
<th>
|
||||||
URL
|
{{#translate}}URL{{/translate}}
|
||||||
</th>
|
</th>
|
||||||
<th class="col-md-1">
|
<th class="col-md-1">
|
||||||
Clicks
|
{{#translate}}Clicks{{/translate}}
|
||||||
</th>
|
</th>
|
||||||
<th class="col-md-1">
|
<th class="col-md-1">
|
||||||
% of clicks
|
{{#translate}}% of clicks{{/translate}}
|
||||||
</th>
|
</th>
|
||||||
<th class="col-md-1">
|
<th class="col-md-1">
|
||||||
% of messages
|
{{#translate}}% of messages{{/translate}}
|
||||||
</th>
|
</th>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
<tr class="success">
|
<tr class="success">
|
||||||
{{#if aggregated}}
|
{{#if aggregated}}
|
||||||
<th>
|
<th>
|
||||||
Aggregated clicks
|
{{#translate}}Aggregated clicks{{/translate}}
|
||||||
</th>
|
</th>
|
||||||
<th>
|
<th>
|
||||||
{{clicks}}
|
{{clicks}}
|
||||||
|
@ -67,7 +67,7 @@
|
||||||
|
|
||||||
<div class="panel panel-info">
|
<div class="panel panel-info">
|
||||||
<!-- Default panel contents -->
|
<!-- Default panel contents -->
|
||||||
<div class="panel-heading">{{#if aggregated}}Subscribers who clicked on a link:{{else}}Subscribers who clicked on this link:{{/if}}</div>
|
<div class="panel-heading">{{#if aggregated}}{{#translate}}Subscribers who clicked on a link:{{/translate}}{{else}}{{#translate}}Subscribers who clicked on this link:{{/translate}}{{/if}}</div>
|
||||||
<div class="panel-body">
|
<div class="panel-body">
|
||||||
<div class="table-responsive">
|
<div class="table-responsive">
|
||||||
<table data-topic-url="/campaigns/clicked" data-topic-id="{{id}}/{{link.id}}" data-sort-column="1" data-sort-order="asc" class="table table-bordered table-hover data-table-ajax display nowrap" width="100%" data-row-sort="0,1,1,1,1,1,0">
|
<table data-topic-url="/campaigns/clicked" data-topic-id="{{id}}/{{link.id}}" data-sort-column="1" data-sort-order="asc" class="table table-bordered table-hover data-table-ajax display nowrap" width="100%" data-row-sort="0,1,1,1,1,1,0">
|
||||||
|
@ -77,19 +77,19 @@
|
||||||
#
|
#
|
||||||
</th>
|
</th>
|
||||||
<th>
|
<th>
|
||||||
Address
|
{{#translate}}Address{{/translate}}
|
||||||
</th>
|
</th>
|
||||||
<th>
|
<th>
|
||||||
First Name
|
{{#translate}}First Name{{/translate}}
|
||||||
</th>
|
</th>
|
||||||
<th>
|
<th>
|
||||||
Last Name
|
{{#translate}}Last Name{{/translate}}
|
||||||
</th>
|
</th>
|
||||||
<th>
|
<th>
|
||||||
First click
|
{{#translate}}First click time{{/translate}}
|
||||||
</th>
|
</th>
|
||||||
<th>
|
<th>
|
||||||
Click count
|
{{#translate}}Click count{{/translate}}
|
||||||
</th>
|
</th>
|
||||||
<th></th>
|
<th></th>
|
||||||
</tr>
|
</tr>
|
||||||
|
|
|
@ -1,14 +1,14 @@
|
||||||
<ol class="breadcrumb">
|
<ol class="breadcrumb">
|
||||||
<li><a href="/">Home</a></li>
|
<li><a href="/">{{#translate}}Home{{/translate}}</a></li>
|
||||||
<li><a href="/campaigns">Campaigns</a></li>
|
<li><a href="/campaigns">{{#translate}}Campaigns{{/translate}}</a></li>
|
||||||
{{#if parent}}
|
{{#if parent}}
|
||||||
<li><a href="/campaigns/view/{{parent.id}}">{{parent.name}}</a></li>
|
<li><a href="/campaigns/view/{{parent.id}}">{{parent.name}}</a></li>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
<li><a href="/campaigns/view/{{id}}">{{name}}</a></li>
|
<li><a href="/campaigns/view/{{id}}">{{name}}</a></li>
|
||||||
<li class="active">Complained info</li>
|
<li class="active">{{#translate}}Complained info{{/translate}}</li>
|
||||||
</ol>
|
</ol>
|
||||||
|
|
||||||
<h2><span class="glyphicon glyphicon-inbox" aria-hidden="true"></span> {{name}} <small>Complained info</small> <a class="btn btn-default btn-xs" href="/campaigns/view/{{id}}?tab=overview" role="button"><span class="glyphicon glyphicon-arrow-left" aria-hidden="true"></span> View campaign</a></h2>
|
<h2><span class="glyphicon glyphicon-inbox" aria-hidden="true"></span> {{name}} <small>{{#translate}}Complained info{{/translate}}</small> <a class="btn btn-default btn-xs" href="/campaigns/view/{{id}}?tab=overview" role="button"><span class="glyphicon glyphicon-arrow-left" aria-hidden="true"></span> {{#translate}}View campaign{{/translate}}</a></h2>
|
||||||
|
|
||||||
<hr>
|
<hr>
|
||||||
|
|
||||||
|
@ -20,7 +20,7 @@
|
||||||
|
|
||||||
<div class="panel panel-info">
|
<div class="panel panel-info">
|
||||||
<!-- Default panel contents -->
|
<!-- Default panel contents -->
|
||||||
<div class="panel-heading">Subscribers who complained and were unsubscribed:</div>
|
<div class="panel-heading">{{#translate}}Subscribers who complained and were unsubscribed:{{/translate}}</div>
|
||||||
<div class="panel-body">
|
<div class="panel-body">
|
||||||
<div class="table-responsive">
|
<div class="table-responsive">
|
||||||
<table data-topic-url="/campaigns/status" data-topic-id="{{id}}/4" data-sort-column="1" data-sort-order="asc" class="table table-bordered table-hover data-table-ajax display nowrap" width="100%" data-row-sort="0,1,1,1,0,1,0">
|
<table data-topic-url="/campaigns/status" data-topic-id="{{id}}/4" data-sort-column="1" data-sort-order="asc" class="table table-bordered table-hover data-table-ajax display nowrap" width="100%" data-row-sort="0,1,1,1,0,1,0">
|
||||||
|
@ -30,19 +30,19 @@
|
||||||
#
|
#
|
||||||
</th>
|
</th>
|
||||||
<th>
|
<th>
|
||||||
Address
|
{{#translate}}Address{{/translate}}
|
||||||
</th>
|
</th>
|
||||||
<th>
|
<th>
|
||||||
First Name
|
{{#translate}}First Name{{/translate}}
|
||||||
</th>
|
</th>
|
||||||
<th>
|
<th>
|
||||||
Last Name
|
{{#translate}}Last Name{{/translate}}
|
||||||
</th>
|
</th>
|
||||||
<th>
|
<th>
|
||||||
SMTP response
|
{{#translate}}SMTP response{{/translate}}
|
||||||
</th>
|
</th>
|
||||||
<th>
|
<th>
|
||||||
Complained
|
{{#translate}}Complain time{{/translate}}
|
||||||
</th>
|
</th>
|
||||||
<th></th>
|
<th></th>
|
||||||
</tr>
|
</tr>
|
||||||
|
|
|
@ -1,16 +1,16 @@
|
||||||
<ol class="breadcrumb">
|
<ol class="breadcrumb">
|
||||||
<li><a href="/">Home</a></li>
|
<li><a href="/">{{#translate}}Home{{/translate}}</a></li>
|
||||||
<li><a href="/campaigns">Campaigns</a></li>
|
<li><a href="/campaigns">{{#translate}}Campaigns{{/translate}}</a></li>
|
||||||
<li class="active">Create RSS Campaign</li>
|
<li class="active">{{#translate}}Create RSS Campaign{{/translate}}</li>
|
||||||
</ol>
|
</ol>
|
||||||
|
|
||||||
<h2>Create RSS Campaign</h2>
|
<h2>{{#translate}}Create RSS Campaign{{/translate}}</h2>
|
||||||
|
|
||||||
<hr>
|
<hr>
|
||||||
|
|
||||||
<div class="panel panel-default">
|
<div class="panel panel-default">
|
||||||
<div class="panel-body">
|
<div class="panel-body">
|
||||||
RSS campaign sets up a tracker against selected RSS feed address. Whenever a new entry is found from this feed it is sent to selected list as an email message.
|
{{#translate}}RSS campaign sets up a tracker against selected RSS feed address. Whenever a new entry is found from this feed it is sent to selected list as an email message.{{/translate}}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@ -19,28 +19,28 @@
|
||||||
<input type="hidden" name="type" value="rss">
|
<input type="hidden" name="type" value="rss">
|
||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="name" class="col-sm-2 control-label">Name</label>
|
<label for="name" class="col-sm-2 control-label">{{#translate}}Name{{/translate}}</label>
|
||||||
<div class="col-sm-10">
|
<div class="col-sm-10">
|
||||||
<input type="text" class="form-control input-lg" name="name" id="name" value="{{name}}" placeholder="Campaign Name" autofocus required>
|
<input type="text" class="form-control input-lg" name="name" id="name" value="{{name}}" placeholder="{{#translate}}Campaign Name{{/translate}}" autofocus required>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="description" class="col-sm-2 control-label">Description</label>
|
<label for="description" class="col-sm-2 control-label">{{#translate}}Description{{/translate}}</label>
|
||||||
<div class="col-sm-10">
|
<div class="col-sm-10">
|
||||||
<textarea class="form-control" rows="3" name="description" id="description">{{description}}</textarea>
|
<textarea class="form-control" rows="3" name="description" id="description">{{description}}</textarea>
|
||||||
<span class="help-block">HTML is allowed</span>
|
<span class="help-block">{{#translate}}HTML is allowed{{/translate}}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="list" class="col-sm-2 control-label">List</label>
|
<label for="list" class="col-sm-2 control-label">{{#translate}}List{{/translate}}</label>
|
||||||
<div class="col-sm-10">
|
<div class="col-sm-10">
|
||||||
<select class="form-control" id="list" name="list" required>
|
<select class="form-control" id="list" name="list" required>
|
||||||
<option value=""> –– Select –– </option>
|
<option value=""> –– {{#translate}}Select{{/translate}} –– </option>
|
||||||
{{#each listItems}}
|
{{#each listItems}}
|
||||||
<option value="{{id}}" {{#if selected}} selected {{/if}}>
|
<option value="{{id}}" {{#if selected}} selected {{/if}}>
|
||||||
{{name}} <span class="text-muted"> — {{subscribers}} subscribers</span>
|
{{name}} <span class="text-muted"> — {{subscribers}} {{#translate}}subscribers{{/translate}}</span>
|
||||||
</option>
|
</option>
|
||||||
|
|
||||||
{{#if segments}}
|
{{#if segments}}
|
||||||
|
@ -58,33 +58,33 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="source-url" class="col-sm-2 control-label">RSS Feed Url</label>
|
<label for="source-url" class="col-sm-2 control-label">{{#translate}}RSS Feed Url{{/translate}}</label>
|
||||||
<div class="col-sm-10">
|
<div class="col-sm-10">
|
||||||
<input type="url" class="form-control" name="source-url" id="source-url" value="{{sourceUrl}}" placeholder="http://example.com/rss.php" required>
|
<input type="url" class="form-control" name="source-url" id="source-url" value="{{sourceUrl}}" placeholder="http://example.com/rss.php" required>
|
||||||
<span class="help-block">New entries from this RSS URL are sent out to list subscribers as email messages</span>
|
<span class="help-block">{{#translate}}New entries from this RSS URL are sent out to list subscribers as email messages{{/translate}}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<hr />
|
<hr />
|
||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="from" class="col-sm-2 control-label">Email "from name"</label>
|
<label for="from" class="col-sm-2 control-label">{{#translate}}Email "from name"{{/translate}}</label>
|
||||||
<div class="col-sm-10">
|
<div class="col-sm-10">
|
||||||
<input type="text" class="form-control" name="from" id="from" value="{{from}}" placeholder="This is the name your emails will come from" required>
|
<input type="text" class="form-control" name="from" id="from" value="{{from}}" placeholder="{{#translate}}This is the name your emails will come from{{/translate}}" required>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="address" class="col-sm-2 control-label">Email "from" address</label>
|
<label for="address" class="col-sm-2 control-label">{{#translate}}Email "from" address{{/translate}}</label>
|
||||||
<div class="col-sm-10">
|
<div class="col-sm-10">
|
||||||
<input type="email" class="form-control" name="address" id="address" value="{{address}}" placeholder="This is the address people will send replies to" required>
|
<input type="email" class="form-control" name="address" id="address" value="{{address}}" placeholder="{{#translate}}This is the address people will send replies to{{/translate}}" required>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="col-sm-offset-2">
|
<div class="col-sm-offset-2">
|
||||||
<div class="checkbox">
|
<div class="checkbox">
|
||||||
<label>
|
<label>
|
||||||
<input type="checkbox" name="tracking-disabled" value="1" {{#if trackingDisabled}} checked {{/if}}> Disable clicked/opened tracking
|
<input type="checkbox" name="tracking-disabled" value="1" {{#if trackingDisabled}} checked {{/if}}> {{#translate}}Disable clicked/opened tracking{{/translate}}
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -93,7 +93,7 @@
|
||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<div class="col-sm-offset-2 col-sm-10">
|
<div class="col-sm-offset-2 col-sm-10">
|
||||||
<button type="submit" class="btn btn-primary"><i class="glyphicon glyphicon-plus"></i> Create RSS Campaign</button>
|
<button type="submit" class="btn btn-primary"><i class="glyphicon glyphicon-plus"></i> {{#translate}}Create RSS Campaign{{/translate}}</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
<ol class="breadcrumb">
|
<ol class="breadcrumb">
|
||||||
<li><a href="/">Home</a></li>
|
<li><a href="/">{{#translate}}Home{{/translate}}</a></li>
|
||||||
<li><a href="/campaigns">Campaigns</a></li>
|
<li><a href="/campaigns">{{#translate}}Campaigns{{/translate}}</a></li>
|
||||||
<li class="active">Create Triggered Campaign</li>
|
<li class="active">{{#translate}}Create Triggered Campaign{{/translate}}</li>
|
||||||
</ol>
|
</ol>
|
||||||
|
|
||||||
<h2>Create Triggered Campaign</h2>
|
<h2>{{#translate}}Create Triggered Campaign{{/translate}}</h2>
|
||||||
|
|
||||||
<hr>
|
<hr>
|
||||||
|
|
||||||
|
@ -13,28 +13,28 @@
|
||||||
<input type="hidden" name="type" value="triggered">
|
<input type="hidden" name="type" value="triggered">
|
||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="name" class="col-sm-2 control-label">Name</label>
|
<label for="name" class="col-sm-2 control-label">{{#translate}}Name{{/translate}}</label>
|
||||||
<div class="col-sm-10">
|
<div class="col-sm-10">
|
||||||
<input type="text" class="form-control input-lg" name="name" id="name" value="{{name}}" placeholder="Campaign Name" autofocus required>
|
<input type="text" class="form-control input-lg" name="name" id="name" value="{{name}}" placeholder="{{#translate}}Campaign Name{{/translate}}" autofocus required>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="description" class="col-sm-2 control-label">Description</label>
|
<label for="description" class="col-sm-2 control-label">{{#translate}}Description{{/translate}}</label>
|
||||||
<div class="col-sm-10">
|
<div class="col-sm-10">
|
||||||
<textarea class="form-control" rows="3" name="description" id="description">{{description}}</textarea>
|
<textarea class="form-control" rows="3" name="description" id="description">{{description}}</textarea>
|
||||||
<span class="help-block">HTML is allowed</span>
|
<span class="help-block">{{#translate}}HTML is allowed{{/translate}}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="list" class="col-sm-2 control-label">List</label>
|
<label for="list" class="col-sm-2 control-label">{{#translate}}List{{/translate}}</label>
|
||||||
<div class="col-sm-10">
|
<div class="col-sm-10">
|
||||||
<select class="form-control" id="list" name="list" required>
|
<select class="form-control" id="list" name="list" required>
|
||||||
<option value=""> –– Select –– </option>
|
<option value=""> –– {{#translate}}Select{{/translate}} –– </option>
|
||||||
{{#each listItems}}
|
{{#each listItems}}
|
||||||
<option value="{{id}}" {{#if selected}} selected {{/if}}>
|
<option value="{{id}}" {{#if selected}} selected {{/if}}>
|
||||||
{{name}} <span class="text-muted"> — {{subscribers}} subscribers</span>
|
{{name}} <span class="text-muted"> — {{subscribers}} {{#translate}}subscribers{{/translate}}</span>
|
||||||
</option>
|
</option>
|
||||||
|
|
||||||
{{#if segments}}
|
{{#if segments}}
|
||||||
|
@ -52,7 +52,7 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="template" class="col-sm-2 control-label">Template</label>
|
<label for="template" class="col-sm-2 control-label">{{#translate}}Template{{/translate}}</label>
|
||||||
<div class="col-sm-10">
|
<div class="col-sm-10">
|
||||||
|
|
||||||
<p class="form-control-static">
|
<p class="form-control-static">
|
||||||
|
@ -60,21 +60,21 @@
|
||||||
</p>
|
</p>
|
||||||
<div>
|
<div>
|
||||||
<select class="form-control" id="template" name="template">
|
<select class="form-control" id="template" name="template">
|
||||||
<option value=""> –– Select –– </option>
|
<option value=""> –– {{#translate}}Select{{/translate}} –– </option>
|
||||||
{{#each templateItems}}
|
{{#each templateItems}}
|
||||||
<option value="{{id}}" {{#if selected}} selected {{/if}}>
|
<option value="{{id}}" {{#if selected}} selected {{/if}}>
|
||||||
{{name}}
|
{{name}}
|
||||||
</option>
|
</option>
|
||||||
{{/each}}
|
{{/each}}
|
||||||
</select>
|
</select>
|
||||||
<span class="help-block">Selecting a template creates a campaign specific copy from it</span>
|
<span class="help-block">{{#translate}}Selecting a template creates a campaign specific copy from it{{/translate}}</span>
|
||||||
</div>
|
</div>
|
||||||
<p class="form-control-static">
|
<p class="form-control-static">
|
||||||
Or alternatively use an URL as the message content source:
|
{{#translate}}Or alternatively use an URL as the message content source:{{/translate}}
|
||||||
</p>
|
</p>
|
||||||
<div>
|
<div>
|
||||||
<input type="url" class="form-control" name="source-url" id="source-url" value="{{sourceUrl}}" placeholder="http://example.com/message-render.php">
|
<input type="url" class="form-control" name="source-url" id="source-url" value="{{sourceUrl}}" placeholder="http://example.com/message-render.php">
|
||||||
<span class="help-block">If a message is sent then this URL will be POSTed to using Merge Tags as POST body. Use this if you want to generate the HTML message yourself</span>
|
<span class="help-block">{{#translate}}If a message is sent then this URL will be POSTed to using Merge Tags as POST body. Use this if you want to generate the HTML message yourself{{/translate}}</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
@ -83,28 +83,28 @@
|
||||||
<hr />
|
<hr />
|
||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="from" class="col-sm-2 control-label">Email "from name"</label>
|
<label for="from" class="col-sm-2 control-label">{{#translate}}Email "from name"{{/translate}}</label>
|
||||||
<div class="col-sm-10">
|
<div class="col-sm-10">
|
||||||
<input type="text" class="form-control" name="from" id="from" value="{{from}}" placeholder="This is the name your emails will come from" required>
|
<input type="text" class="form-control" name="from" id="from" value="{{from}}" placeholder="{{#translate}}This is the name your emails will come from{{/translate}}" required>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="address" class="col-sm-2 control-label">Email "from" address</label>
|
<label for="address" class="col-sm-2 control-label">{{#translate}}Email "from" address{{/translate}}</label>
|
||||||
<div class="col-sm-10">
|
<div class="col-sm-10">
|
||||||
<input type="email" class="form-control" name="address" id="address" value="{{address}}" placeholder="This is the address people will send replies to" required>
|
<input type="email" class="form-control" name="address" id="address" value="{{address}}" placeholder="{{#translate}}This is the address people will send replies to{{/translate}}" required>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="subject" class="col-sm-2 control-label">Email "subject line"</label>
|
<label for="subject" class="col-sm-2 control-label">{{#translate}}Email "subject line"{{/translate}}</label>
|
||||||
<div class="col-sm-10">
|
<div class="col-sm-10">
|
||||||
<input type="text" class="form-control" name="subject" id="subject" value="{{subject}}" placeholder="Keep it relevant and non-spammy" required>
|
<input type="text" class="form-control" name="subject" id="subject" value="{{subject}}" placeholder="{{#translate}}Keep it relevant and non-spammy{{/translate}}" required>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="col-sm-offset-2">
|
<div class="col-sm-offset-2">
|
||||||
<div class="checkbox">
|
<div class="checkbox">
|
||||||
<label>
|
<label>
|
||||||
<input type="checkbox" name="tracking-disabled" value="1" {{#if trackingDisabled}} checked {{/if}}> Disable clicked/opened tracking
|
<input type="checkbox" name="tracking-disabled" value="1" {{#if trackingDisabled}} checked {{/if}}> {{#translate}}Disable clicked/opened tracking{{/translate}}
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -113,7 +113,7 @@
|
||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<div class="col-sm-offset-2 col-sm-10">
|
<div class="col-sm-offset-2 col-sm-10">
|
||||||
<button type="submit" class="btn btn-primary"><i class="glyphicon glyphicon-plus"></i> Create Campaign</button>
|
<button type="submit" class="btn btn-primary"><i class="glyphicon glyphicon-plus"></i> {{#translate}}Create Campaign{{/translate}}</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
<ol class="breadcrumb">
|
<ol class="breadcrumb">
|
||||||
<li><a href="/">Home</a></li>
|
<li><a href="/">{{#translate}}Home{{/translate}}</a></li>
|
||||||
<li><a href="/campaigns">Campaigns</a></li>
|
<li><a href="/campaigns">{{#translate}}Campaigns{{/translate}}</a></li>
|
||||||
<li class="active">Create Campaign</li>
|
<li class="active">{{#translate}}Create Campaign{{/translate}}</li>
|
||||||
</ol>
|
</ol>
|
||||||
|
|
||||||
<h2>Create Campaign</h2>
|
<h2>{{#translate}}Create Campaign{{/translate}}</h2>
|
||||||
|
|
||||||
<hr>
|
<hr>
|
||||||
|
|
||||||
|
@ -13,28 +13,28 @@
|
||||||
<input type="hidden" name="type" value="normal">
|
<input type="hidden" name="type" value="normal">
|
||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="name" class="col-sm-2 control-label">Name</label>
|
<label for="name" class="col-sm-2 control-label">{{#translate}}Name{{/translate}}</label>
|
||||||
<div class="col-sm-10">
|
<div class="col-sm-10">
|
||||||
<input type="text" class="form-control input-lg" name="name" id="name" value="{{name}}" placeholder="Campaign Name" autofocus required>
|
<input type="text" class="form-control input-lg" name="name" id="name" value="{{name}}" placeholder="{{#translate}}Campaign Name{{/translate}}" autofocus required>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="description" class="col-sm-2 control-label">Description</label>
|
<label for="description" class="col-sm-2 control-label">{{#translate}}Description{{/translate}}</label>
|
||||||
<div class="col-sm-10">
|
<div class="col-sm-10">
|
||||||
<textarea class="form-control" rows="3" name="description" id="description">{{description}}</textarea>
|
<textarea class="form-control" rows="3" name="description" id="description">{{description}}</textarea>
|
||||||
<span class="help-block">HTML is allowed</span>
|
<span class="help-block">{{#translate}}HTML is allowed{{/translate}}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="list" class="col-sm-2 control-label">List</label>
|
<label for="list" class="col-sm-2 control-label">{{#translate}}List{{/translate}}</label>
|
||||||
<div class="col-sm-10">
|
<div class="col-sm-10">
|
||||||
<select class="form-control" id="list" name="list" required>
|
<select class="form-control" id="list" name="list" required>
|
||||||
<option value=""> –– Select –– </option>
|
<option value=""> –– {{#translate}}Select{{/translate}} –– </option>
|
||||||
{{#each listItems}}
|
{{#each listItems}}
|
||||||
<option value="{{id}}" {{#if selected}} selected {{/if}}>
|
<option value="{{id}}" {{#if selected}} selected {{/if}}>
|
||||||
{{name}} <span class="text-muted"> — {{subscribers}} subscribers</span>
|
{{name}} <span class="text-muted"> — {{subscribers}} {{#translate}}subscribers{{/translate}}</span>
|
||||||
</option>
|
</option>
|
||||||
|
|
||||||
{{#if segments}}
|
{{#if segments}}
|
||||||
|
@ -52,29 +52,29 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="template" class="col-sm-2 control-label">Template</label>
|
<label for="template" class="col-sm-2 control-label">{{#translate}}Template{{/translate}}</label>
|
||||||
<div class="col-sm-10">
|
<div class="col-sm-10">
|
||||||
|
|
||||||
<p class="form-control-static">
|
<p class="form-control-static">
|
||||||
Select a template:
|
{{#translate}}Select a template:{{/translate}}
|
||||||
</p>
|
</p>
|
||||||
<div>
|
<div>
|
||||||
<select class="form-control" id="template" name="template">
|
<select class="form-control" id="template" name="template">
|
||||||
<option value=""> –– Select –– </option>
|
<option value=""> –– {{#translate}}Select{{/translate}} –– </option>
|
||||||
{{#each templateItems}}
|
{{#each templateItems}}
|
||||||
<option value="{{id}}" {{#if selected}} selected {{/if}}>
|
<option value="{{id}}" {{#if selected}} selected {{/if}}>
|
||||||
{{name}}
|
{{name}}
|
||||||
</option>
|
</option>
|
||||||
{{/each}}
|
{{/each}}
|
||||||
</select>
|
</select>
|
||||||
<span class="help-block">Selecting a template creates a campaign specific copy from it</span>
|
<span class="help-block">{{#translate}}Selecting a template creates a campaign specific copy from it{{/translate}}</span>
|
||||||
</div>
|
</div>
|
||||||
<p class="form-control-static">
|
<p class="form-control-static">
|
||||||
Or alternatively use an URL as the message content source:
|
{{#translate}}Or alternatively use an URL as the message content source:{{/translate}}
|
||||||
</p>
|
</p>
|
||||||
<div>
|
<div>
|
||||||
<input type="url" class="form-control" name="source-url" id="source-url" value="{{sourceUrl}}" placeholder="http://example.com/message-render.php">
|
<input type="url" class="form-control" name="source-url" id="source-url" value="{{sourceUrl}}" placeholder="http://example.com/message-render.php">
|
||||||
<span class="help-block">If a message is sent then this URL will be POSTed to using Merge Tags as POST body. Use this if you want to generate the HTML message yourself</span>
|
<span class="help-block">{{#translate}}If a message is sent then this URL will be POSTed to using Merge Tags as POST body. Use this if you want to generate the HTML message yourself{{/translate}}</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
@ -83,34 +83,34 @@
|
||||||
<hr />
|
<hr />
|
||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="from" class="col-sm-2 control-label">Email "from name"</label>
|
<label for="from" class="col-sm-2 control-label">{{#translate}}Email "from name"{{/translate}}</label>
|
||||||
<div class="col-sm-10">
|
<div class="col-sm-10">
|
||||||
<input type="text" class="form-control" name="from" id="from" value="{{from}}" placeholder="This is the name your emails will come from" required>
|
<input type="text" class="form-control" name="from" id="from" value="{{from}}" placeholder="{{#translate}}This is the name your emails will come from{{/translate}}" required>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="address" class="col-sm-2 control-label">Email "from" address</label>
|
<label for="address" class="col-sm-2 control-label">{{#translate}}Email "from" address{{/translate}}</label>
|
||||||
<div class="col-sm-10">
|
<div class="col-sm-10">
|
||||||
<input type="email" class="form-control" name="address" id="address" value="{{address}}" placeholder="This is the address people will send replies to unless reply-to address is set" required>
|
<input type="email" class="form-control" name="address" id="address" value="{{address}}" placeholder="{{#translate}}This is the address people will send replies to unless reply-to address is set{{/translate}}" required>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="address" class="col-sm-2 control-label">Email "reply-to" address</label>
|
<label for="address" class="col-sm-2 control-label">{{#translate}}Email "reply-to" address{{/translate}}</label>
|
||||||
<div class="col-sm-10">
|
<div class="col-sm-10">
|
||||||
<input type="email" class="form-control" name="reply-to" id="reply-to" value="{{replyTo}}" placeholder="If set, this is the address people will send replies to">
|
<input type="email" class="form-control" name="reply-to" id="reply-to" value="{{replyTo}}" placeholder="{{#translate}}If set, this is the address people will send replies to{{/translate}}">
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="subject" class="col-sm-2 control-label">Email "subject line"</label>
|
<label for="subject" class="col-sm-2 control-label">{{#translate}}Email "subject line"{{/translate}}</label>
|
||||||
<div class="col-sm-10">
|
<div class="col-sm-10">
|
||||||
<input type="text" class="form-control" name="subject" id="subject" value="{{subject}}" placeholder="Keep it relevant and non-spammy" required>
|
<input type="text" class="form-control" name="subject" id="subject" value="{{subject}}" placeholder="{{#translate}}Keep it relevant and non-spammy{{/translate}}" required>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="col-sm-offset-2">
|
<div class="col-sm-offset-2">
|
||||||
<div class="checkbox">
|
<div class="checkbox">
|
||||||
<label>
|
<label>
|
||||||
<input type="checkbox" name="tracking-disabled" value="1" {{#if trackingDisabled}} checked {{/if}}> Disable clicked/opened tracking
|
<input type="checkbox" name="tracking-disabled" value="1" {{#if trackingDisabled}} checked {{/if}}> {{#translate}}Disable clicked/opened tracking{{/translate}}
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -119,7 +119,7 @@
|
||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<div class="col-sm-offset-2 col-sm-10">
|
<div class="col-sm-offset-2 col-sm-10">
|
||||||
<button type="submit" class="btn btn-primary"><i class="glyphicon glyphicon-plus"></i> Create Campaign</button>
|
<button type="submit" class="btn btn-primary"><i class="glyphicon glyphicon-plus"></i> {{#translate}}Create Campaign{{/translate}}</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
|
|
|
@ -1,14 +1,14 @@
|
||||||
<ol class="breadcrumb">
|
<ol class="breadcrumb">
|
||||||
<li><a href="/">Home</a></li>
|
<li><a href="/">{{#translate}}Home{{/translate}}</a></li>
|
||||||
<li><a href="/campaigns">Campaigns</a></li>
|
<li><a href="/campaigns">{{#translate}}Campaigns{{/translate}}</a></li>
|
||||||
{{#if parent}}
|
{{#if parent}}
|
||||||
<li><a href="/campaigns/view/{{parent.id}}">{{parent.name}}</a></li>
|
<li><a href="/campaigns/view/{{parent.id}}">{{parent.name}}</a></li>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
<li><a href="/campaigns/view/{{id}}">{{name}}</a></li>
|
<li><a href="/campaigns/view/{{id}}">{{name}}</a></li>
|
||||||
<li class="active">Delivered info</li>
|
<li class="active">{{#translate}}Delivered info{{/translate}}</li>
|
||||||
</ol>
|
</ol>
|
||||||
|
|
||||||
<h2><span class="glyphicon glyphicon-inbox" aria-hidden="true"></span> {{name}} <small>Delivered info</small> <a class="btn btn-default btn-xs" href="/campaigns/view/{{id}}?tab=overview" role="button"><span class="glyphicon glyphicon-arrow-left" aria-hidden="true"></span> View campaign</a></h2>
|
<h2><span class="glyphicon glyphicon-inbox" aria-hidden="true"></span> {{name}} <small>{{#translate}}Delivered info{{/translate}}</small> <a class="btn btn-default btn-xs" href="/campaigns/view/{{id}}?tab=overview" role="button"><span class="glyphicon glyphicon-arrow-left" aria-hidden="true"></span> {{#translate}}View campaign{{/translate}}</a></h2>
|
||||||
|
|
||||||
<hr>
|
<hr>
|
||||||
|
|
||||||
|
@ -20,7 +20,7 @@
|
||||||
|
|
||||||
<div class="panel panel-info">
|
<div class="panel panel-info">
|
||||||
<!-- Default panel contents -->
|
<!-- Default panel contents -->
|
||||||
<div class="panel-heading">Subscribers who received the message and did not bounce/unsubscribe:</div>
|
<div class="panel-heading">{{#translate}}Subscribers who received the message and did not bounce/unsubscribe:{{/translate}}</div>
|
||||||
<div class="panel-body">
|
<div class="panel-body">
|
||||||
<div class="table-responsive">
|
<div class="table-responsive">
|
||||||
<table data-topic-url="/campaigns/status" data-topic-id="{{id}}/1" data-sort-column="1" data-sort-order="asc" class="table table-bordered table-hover data-table-ajax display nowrap" width="100%" data-row-sort="0,1,1,1,0,1,0">
|
<table data-topic-url="/campaigns/status" data-topic-id="{{id}}/1" data-sort-column="1" data-sort-order="asc" class="table table-bordered table-hover data-table-ajax display nowrap" width="100%" data-row-sort="0,1,1,1,0,1,0">
|
||||||
|
@ -30,19 +30,19 @@
|
||||||
#
|
#
|
||||||
</th>
|
</th>
|
||||||
<th>
|
<th>
|
||||||
Address
|
{{#translate}}Address{{/translate}}
|
||||||
</th>
|
</th>
|
||||||
<th>
|
<th>
|
||||||
First Name
|
{{#translate}}First Name{{/translate}}
|
||||||
</th>
|
</th>
|
||||||
<th>
|
<th>
|
||||||
Last Name
|
{{#translate}}Last Name{{/translate}}
|
||||||
</th>
|
</th>
|
||||||
<th>
|
<th>
|
||||||
SMTP response
|
{{#translate}}SMTP response{{/translate}}
|
||||||
</th>
|
</th>
|
||||||
<th>
|
<th>
|
||||||
Delivered
|
{{#translate}}Delivery time{{/translate}}
|
||||||
</th>
|
</th>
|
||||||
<th></th>
|
<th></th>
|
||||||
</tr>
|
</tr>
|
||||||
|
|
|
@ -1,20 +1,20 @@
|
||||||
<ol class="breadcrumb">
|
<ol class="breadcrumb">
|
||||||
<li><a href="/">Home</a></li>
|
<li><a href="/">{{#translate}}Home{{/translate}}</a></li>
|
||||||
<li><a href="/campaigns">Campaigns</a></li>
|
<li><a href="/campaigns">{{#translate}}Campaigns{{/translate}}</a></li>
|
||||||
{{#if parent}}
|
{{#if parent}}
|
||||||
<li><a href="/campaigns/view/{{parent.id}}">{{parent.name}}</a></li>
|
<li><a href="/campaigns/view/{{parent.id}}">{{parent.name}}</a></li>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
<li><a href="/campaigns/view/{{id}}">{{name}}</a></li>
|
<li><a href="/campaigns/view/{{id}}">{{name}}</a></li>
|
||||||
<li class="active">Edit RSS Campaign</li>
|
<li class="active">{{#translate}}Edit RSS Campaign{{/translate}}</li>
|
||||||
</ol>
|
</ol>
|
||||||
|
|
||||||
<h2>Edit RSS Campaign <a class="btn btn-default btn-xs" href="/campaigns/view/{{id}}" role="button"><span class="glyphicon glyphicon-arrow-left" aria-hidden="true"></span> View campaign</a></h2>
|
<h2>{{#translate}}Edit RSS Campaign{{/translate}} <a class="btn btn-default btn-xs" href="/campaigns/view/{{id}}" role="button"><span class="glyphicon glyphicon-arrow-left" aria-hidden="true"></span> {{#translate}}View campaign{{/translate}}</a></h2>
|
||||||
|
|
||||||
<hr>
|
<hr>
|
||||||
|
|
||||||
<div class="panel panel-default">
|
<div class="panel panel-default">
|
||||||
<div class="panel-body">
|
<div class="panel-body">
|
||||||
RSS campaign sets up a tracker against selected RSS feed address. Whenever a new entry is found from this feed it is sent to selected list as an email message.
|
{{#translate}}RSS campaign sets up a tracker against selected RSS feed address. Whenever a new entry is found from this feed it is sent to selected list as an email message.{{/translate}}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@ -29,32 +29,32 @@
|
||||||
|
|
||||||
<fieldset>
|
<fieldset>
|
||||||
<legend>
|
<legend>
|
||||||
General Settings
|
{{#translate}}General Settings{{/translate}}
|
||||||
</legend>
|
</legend>
|
||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="name" class="col-sm-2 control-label">Name</label>
|
<label for="name" class="col-sm-2 control-label">{{#translate}}Name{{/translate}}</label>
|
||||||
<div class="col-sm-10">
|
<div class="col-sm-10">
|
||||||
<input type="text" class="form-control input-lg" name="name" id="name" value="{{name}}" placeholder="Campaign Name" autofocus required>
|
<input type="text" class="form-control input-lg" name="name" id="name" value="{{name}}" placeholder="{{#translate}}Campaign Name{{/translate}}" autofocus required>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="description" class="col-sm-2 control-label">Description</label>
|
<label for="description" class="col-sm-2 control-label">{{#translate}}Description{{/translate}}</label>
|
||||||
<div class="col-sm-10">
|
<div class="col-sm-10">
|
||||||
<textarea class="form-control" rows="3" name="description" id="description">{{description}}</textarea>
|
<textarea class="form-control" rows="3" name="description" id="description">{{description}}</textarea>
|
||||||
<span class="help-block">HTML is allowed</span>
|
<span class="help-block">{{#translate}}HTML is allowed{{/translate}}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="list" class="col-sm-2 control-label">List</label>
|
<label for="list" class="col-sm-2 control-label">{{#translate}}List{{/translate}}</label>
|
||||||
<div class="col-sm-10">
|
<div class="col-sm-10">
|
||||||
<select class="form-control" id="list" name="list" required>
|
<select class="form-control" id="list" name="list" required>
|
||||||
<option value=""> –– Select –– </option>
|
<option value=""> –– {{#translate}}Select{{/translate}} –– </option>
|
||||||
{{#each listItems}}
|
{{#each listItems}}
|
||||||
<option value="{{id}}" {{#if selected}} selected {{/if}}>
|
<option value="{{id}}" {{#if selected}} selected {{/if}}>
|
||||||
{{name}} <span class="text-muted"> — {{subscribers}} subscribers</span>
|
{{name}} <span class="text-muted"> — {{subscribers}} {{#translate}}subscribers{{/translate}}</span>
|
||||||
</option>
|
</option>
|
||||||
|
|
||||||
{{#if segments}}
|
{{#if segments}}
|
||||||
|
@ -74,10 +74,10 @@
|
||||||
{{> merge_tag_reference}}
|
{{> merge_tag_reference}}
|
||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="template" class="col-sm-2 control-label">RSS Feed Url</label>
|
<label for="template" class="col-sm-2 control-label">{{#translate}}RSS Feed Url{{/translate}}</label>
|
||||||
<div class="col-sm-10">
|
<div class="col-sm-10">
|
||||||
<input type="url" class="form-control" name="source-url" id="source-url" value="{{sourceUrl}}" placeholder="http://example.com/rss.php" required>
|
<input type="url" class="form-control" name="source-url" id="source-url" value="{{sourceUrl}}" placeholder="http://example.com/rss.php" required>
|
||||||
<span class="help-block">New entries from this RSS URL are sent out to list subscribers as email messages</span>
|
<span class="help-block">{{#translate}}New entries from this RSS URL are sent out to list subscribers as email messages{{/translate}}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@ -89,29 +89,29 @@
|
||||||
|
|
||||||
<div class="form-group" style="margin-top: -15px;">
|
<div class="form-group" style="margin-top: -15px;">
|
||||||
<div class="col-sm-offset-2 col-sm-10">
|
<div class="col-sm-offset-2 col-sm-10">
|
||||||
<span class="help-block">Use special merge tag <code>[RSS_ENTRY]</code> to mark the position for the RSS post content. Additionally you can use any valid merge tag as well.</span>
|
<span class="help-block">{{#translate}}Use special merge tag [RSS_ENTRY] to mark the position for the RSS post content. Additionally you can use any valid merge tag as well.{{/translate}}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<hr />
|
<hr />
|
||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="from" class="col-sm-2 control-label">Email "from name"</label>
|
<label for="from" class="col-sm-2 control-label">{{#translate}}Email "from name"{{/translate}}</label>
|
||||||
<div class="col-sm-10">
|
<div class="col-sm-10">
|
||||||
<input type="text" class="form-control" name="from" id="from" value="{{from}}" placeholder="This is the name your emails will come from" required>
|
<input type="text" class="form-control" name="from" id="from" value="{{from}}" placeholder="{{#translate}}This is the name your emails will come from{{/translate}}" required>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="address" class="col-sm-2 control-label">Email "from" address</label>
|
<label for="address" class="col-sm-2 control-label">{{#translate}}Email "from" address{{/translate}}</label>
|
||||||
<div class="col-sm-10">
|
<div class="col-sm-10">
|
||||||
<input type="email" class="form-control" name="address" id="address" value="{{address}}" placeholder="This is the address people will send replies to" required>
|
<input type="email" class="form-control" name="address" id="address" value="{{address}}" placeholder="{{#translate}}This is the address people will send replies to{{/translate}}" required>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="col-sm-offset-2">
|
<div class="col-sm-offset-2">
|
||||||
<div class="checkbox">
|
<div class="checkbox">
|
||||||
<label>
|
<label>
|
||||||
<input type="checkbox" name="tracking-disabled" value="1" {{#if trackingDisabled}} checked {{/if}}> Disable clicked/opened tracking
|
<input type="checkbox" name="tracking-disabled" value="1" {{#if trackingDisabled}} checked {{/if}}> {{#translate}}Disable clicked/opened tracking{{/translate}}
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -123,9 +123,9 @@
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<div class="col-sm-offset-2 col-sm-10">
|
<div class="col-sm-offset-2 col-sm-10">
|
||||||
<div class="pull-right">
|
<div class="pull-right">
|
||||||
<button type="submit" form="campaigns-delete" class="btn btn-danger"><i class="glyphicon glyphicon-remove"></i> Delete Campaign</button>
|
<button type="submit" form="campaigns-delete" class="btn btn-danger"><i class="glyphicon glyphicon-remove"></i> {{#translate}}Delete Campaign{{/translate}}</button>
|
||||||
</div>
|
</div>
|
||||||
<button type="submit" class="btn btn-primary"><i class="glyphicon glyphicon-ok"></i> Update</button>
|
<button type="submit" class="btn btn-primary"><i class="glyphicon glyphicon-ok"></i> {{#translate}}Update{{/translate}}</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
|
|
|
@ -1,14 +1,14 @@
|
||||||
<ol class="breadcrumb">
|
<ol class="breadcrumb">
|
||||||
<li><a href="/">Home</a></li>
|
<li><a href="/">{{#translate}}Home{{/translate}}</a></li>
|
||||||
<li><a href="/campaigns">Campaigns</a></li>
|
<li><a href="/campaigns">{{#translate}}Campaigns{{/translate}}</a></li>
|
||||||
{{#if parent}}
|
{{#if parent}}
|
||||||
<li><a href="/campaigns/view/{{parent.id}}">{{parent.name}}</a></li>
|
<li><a href="/campaigns/view/{{parent.id}}">{{parent.name}}</a></li>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
<li><a href="/campaigns/view/{{id}}">{{name}}</a></li>
|
<li><a href="/campaigns/view/{{id}}">{{name}}</a></li>
|
||||||
<li class="active">Edit Triggered Campaign</li>
|
<li class="active">{{#translate}}Edit Triggered Campaign{{/translate}}</li>
|
||||||
</ol>
|
</ol>
|
||||||
|
|
||||||
<h2>Edit Triggered Campaign <a class="btn btn-default btn-xs" href="/campaigns/view/{{id}}" role="button"><span class="glyphicon glyphicon-arrow-left" aria-hidden="true"></span> View campaign</a></h2>
|
<h2>{{#translate}}Edit Triggered Campaign{{/translate}} <a class="btn btn-default btn-xs" href="/campaigns/view/{{id}}" role="button"><span class="glyphicon glyphicon-arrow-left" aria-hidden="true"></span> {{#translate}}View campaign{{/translate}}</a></h2>
|
||||||
|
|
||||||
<hr>
|
<hr>
|
||||||
|
|
||||||
|
@ -26,8 +26,8 @@
|
||||||
|
|
||||||
<!-- Nav tabs -->
|
<!-- Nav tabs -->
|
||||||
<ul class="nav nav-tabs" role="tablist">
|
<ul class="nav nav-tabs" role="tablist">
|
||||||
<li role="presentation" class="{{#if showGeneral}}active{{/if}}"><a href="#general" aria-controls="general" role="tab" data-toggle="tab">General</a></li>
|
<li role="presentation" class="{{#if showGeneral}}active{{/if}}"><a href="#general" aria-controls="general" role="tab" data-toggle="tab">{{#translate}}General{{/translate}}</a></li>
|
||||||
<li role="presentation" class="{{#if showTemplate}}active{{/if}}"><a href="#template" aria-controls="template" role="tab" data-toggle="tab">Template</a></li>
|
<li role="presentation" class="{{#if showTemplate}}active{{/if}}"><a href="#template" aria-controls="template" role="tab" data-toggle="tab">{{#translate}}Template{{/translate}}</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
<div class="tab-content">
|
<div class="tab-content">
|
||||||
|
@ -37,32 +37,32 @@
|
||||||
|
|
||||||
<fieldset>
|
<fieldset>
|
||||||
<legend>
|
<legend>
|
||||||
General Settings
|
{{#translate}}General Settings{{/translate}}
|
||||||
</legend>
|
</legend>
|
||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="name" class="col-sm-2 control-label">Name</label>
|
<label for="name" class="col-sm-2 control-label">{{#translate}}Name{{/translate}}</label>
|
||||||
<div class="col-sm-10">
|
<div class="col-sm-10">
|
||||||
<input type="text" class="form-control input-lg" name="name" id="name" value="{{name}}" placeholder="Campaign Name" autofocus required>
|
<input type="text" class="form-control input-lg" name="name" id="name" value="{{name}}" placeholder="{{#translate}}Campaign Name{{/translate}}" autofocus required>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="description" class="col-sm-2 control-label">Description</label>
|
<label for="description" class="col-sm-2 control-label">{{#translate}}Description{{/translate}}</label>
|
||||||
<div class="col-sm-10">
|
<div class="col-sm-10">
|
||||||
<textarea class="form-control" rows="3" name="description" id="description">{{description}}</textarea>
|
<textarea class="form-control" rows="3" name="description" id="description">{{description}}</textarea>
|
||||||
<span class="help-block">HTML is allowed</span>
|
<span class="help-block">{{#translate}}HTML is allowed{{/translate}}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="list" class="col-sm-2 control-label">List</label>
|
<label for="list" class="col-sm-2 control-label">{{#translate}}List{{/translate}}</label>
|
||||||
<div class="col-sm-10">
|
<div class="col-sm-10">
|
||||||
<select class="form-control" id="list" name="list" required>
|
<select class="form-control" id="list" name="list" required>
|
||||||
<option value=""> –– Select –– </option>
|
<option value=""> –– {{#translate}}Select{{/translate}} –– </option>
|
||||||
{{#each listItems}}
|
{{#each listItems}}
|
||||||
<option value="{{id}}" {{#if selected}} selected {{/if}}>
|
<option value="{{id}}" {{#if selected}} selected {{/if}}>
|
||||||
{{name}} <span class="text-muted"> — {{subscribers}} subscribers</span>
|
{{name}} <span class="text-muted"> — {{subscribers}} {{#translate}}subscribers{{/translate}}</span>
|
||||||
</option>
|
</option>
|
||||||
|
|
||||||
{{#if segments}}
|
{{#if segments}}
|
||||||
|
@ -82,28 +82,28 @@
|
||||||
<hr />
|
<hr />
|
||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="from" class="col-sm-2 control-label">Email "from name"</label>
|
<label for="from" class="col-sm-2 control-label">{{#translate}}Email "from name"{{/translate}}</label>
|
||||||
<div class="col-sm-10">
|
<div class="col-sm-10">
|
||||||
<input type="text" class="form-control" name="from" id="from" value="{{from}}" placeholder="This is the name your emails will come from" required>
|
<input type="text" class="form-control" name="from" id="from" value="{{from}}" placeholder="T{{#translate}}his is the name your emails will come from{{/translate}}" required>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="address" class="col-sm-2 control-label">Email "from" address</label>
|
<label for="address" class="col-sm-2 control-label">{{#translate}}Email "from" address{{/translate}}</label>
|
||||||
<div class="col-sm-10">
|
<div class="col-sm-10">
|
||||||
<input type="email" class="form-control" name="address" id="address" value="{{address}}" placeholder="This is the address people will send replies to" required>
|
<input type="email" class="form-control" name="address" id="address" value="{{address}}" placeholder="{{#translate}}This is the address people will send replies to{{/translate}}" required>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="subject" class="col-sm-2 control-label">Email "subject line"</label>
|
<label for="subject" class="col-sm-2 control-label">{{#translate}}Email "subject line"{{/translate}}</label>
|
||||||
<div class="col-sm-10">
|
<div class="col-sm-10">
|
||||||
<input type="text" class="form-control" name="subject" id="subject" value="{{subject}}" placeholder="Keep it relevant and non-spammy" required>
|
<input type="text" class="form-control" name="subject" id="subject" value="{{subject}}" placeholder="{{#translate}}Keep it relevant and non-spammy{{/translate}}" required>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="col-sm-offset-2">
|
<div class="col-sm-offset-2">
|
||||||
<div class="checkbox">
|
<div class="checkbox">
|
||||||
<label>
|
<label>
|
||||||
<input type="checkbox" name="tracking-disabled" value="1" {{#if trackingDisabled}} checked {{/if}}> Disable clicked/opened tracking
|
<input type="checkbox" name="tracking-disabled" value="1" {{#if trackingDisabled}} checked {{/if}}> {{#translate}}Disable clicked/opened tracking{{/translate}}
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -116,15 +116,15 @@
|
||||||
|
|
||||||
<fieldset>
|
<fieldset>
|
||||||
<legend>
|
<legend>
|
||||||
Template Settings
|
{{#translate}}Template Settings{{/translate}}
|
||||||
</legend>
|
</legend>
|
||||||
|
|
||||||
{{#if sourceUrl}}
|
{{#if sourceUrl}}
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="source-url" class="col-sm-2 control-label">Template URL</label>
|
<label for="source-url" class="col-sm-2 control-label">{{#translate}}Template URL{{/translate}}</label>
|
||||||
<div class="col-sm-10">
|
<div class="col-sm-10">
|
||||||
<input type="url" class="form-control" name="source-url" id="source-url" value="{{sourceUrl}}" placeholder="http://example.com/message-render.php">
|
<input type="url" class="form-control" name="source-url" id="source-url" value="{{sourceUrl}}" placeholder="http://example.com/message-render.php">
|
||||||
<span class="help-block">If a message is sent then this URL will be POSTed to using Merge Tags as POST body. Use this if you want to generate the HTML message yourself</span>
|
<span class="help-block">{{#translate}}If a message is sent then this URL will be POSTed to using Merge Tags as POST body. Use this if you want to generate the HTML message yourself{{/translate}}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{{else}}
|
{{else}}
|
||||||
|
@ -150,9 +150,9 @@
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<div class="col-sm-offset-2 col-sm-10">
|
<div class="col-sm-offset-2 col-sm-10">
|
||||||
<div class="pull-right">
|
<div class="pull-right">
|
||||||
<button type="submit" form="campaigns-delete" class="btn btn-danger"><i class="glyphicon glyphicon-remove"></i> Delete Campaign</button>
|
<button type="submit" form="campaigns-delete" class="btn btn-danger"><i class="glyphicon glyphicon-remove"></i> {{#translate}}Delete Campaign{{/translate}}</button>
|
||||||
</div>
|
</div>
|
||||||
<button type="submit" class="btn btn-primary"><i class="glyphicon glyphicon-ok"></i> Update</button>
|
<button type="submit" class="btn btn-primary"><i class="glyphicon glyphicon-ok"></i> {{#translate}}Update{{/translate}}</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
|
|
|
@ -1,14 +1,14 @@
|
||||||
<ol class="breadcrumb">
|
<ol class="breadcrumb">
|
||||||
<li><a href="/">Home</a></li>
|
<li><a href="/">{{#translate}}Home{{/translate}}</a></li>
|
||||||
<li><a href="/campaigns">Campaigns</a></li>
|
<li><a href="/campaigns">{{#translate}}Campaigns{{/translate}}</a></li>
|
||||||
{{#if parent}}
|
{{#if parent}}
|
||||||
<li><a href="/campaigns/view/{{parent.id}}">{{parent.name}}</a></li>
|
<li><a href="/campaigns/view/{{parent.id}}">{{parent.name}}</a></li>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
<li><a href="/campaigns/view/{{id}}">{{name}}</a></li>
|
<li><a href="/campaigns/view/{{id}}">{{name}}</a></li>
|
||||||
<li class="active">Edit Campaign</li>
|
<li class="active">{{#translate}}Edit Campaign{{/translate}}</li>
|
||||||
</ol>
|
</ol>
|
||||||
|
|
||||||
<h2>Edit Campaign <a class="btn btn-default btn-xs" href="/campaigns/view/{{id}}" role="button"><span class="glyphicon glyphicon-arrow-left" aria-hidden="true"></span> View campaign</a></h2>
|
<h2>{{#translate}}Edit Campaign{{/translate}} <a class="btn btn-default btn-xs" href="/campaigns/view/{{id}}" role="button"><span class="glyphicon glyphicon-arrow-left" aria-hidden="true"></span> {{#translate}}View campaign{{/translate}}</a></h2>
|
||||||
|
|
||||||
<hr>
|
<hr>
|
||||||
|
|
||||||
|
@ -37,9 +37,9 @@
|
||||||
<div>
|
<div>
|
||||||
<!-- Nav tabs -->
|
<!-- Nav tabs -->
|
||||||
<ul class="nav nav-tabs" role="tablist">
|
<ul class="nav nav-tabs" role="tablist">
|
||||||
<li role="presentation" class="{{#if showGeneral}}active{{/if}}"><a href="#general" aria-controls="general" role="tab" data-toggle="tab">General</a></li>
|
<li role="presentation" class="{{#if showGeneral}}active{{/if}}"><a href="#general" aria-controls="general" role="tab" data-toggle="tab">{{#translate}}General{{/translate}}</a></li>
|
||||||
<li role="presentation" class="{{#if showTemplate}}active{{/if}}"><a href="#template" aria-controls="template" role="tab" data-toggle="tab">Template</a></li>
|
<li role="presentation" class="{{#if showTemplate}}active{{/if}}"><a href="#template" aria-controls="template" role="tab" data-toggle="tab">{{#translate}}Template{{/translate}}</a></li>
|
||||||
<li role="presentation" class="{{#if showAttachments}}active{{/if}}"><a href="#attachments" aria-controls="attachments" role="tab" data-toggle="tab">Attachments{{#if attachments}} <span class="badge">{{attachments.length}}</span>{{/if}}</a></li>
|
<li role="presentation" class="{{#if showAttachments}}active{{/if}}"><a href="#attachments" aria-controls="attachments" role="tab" data-toggle="tab">{{#translate}}Attachments{{/translate}}{{#if attachments}} <span class="badge">{{attachments.length}}</span>{{/if}}</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
<div class="tab-content">
|
<div class="tab-content">
|
||||||
|
@ -49,32 +49,32 @@
|
||||||
|
|
||||||
<fieldset>
|
<fieldset>
|
||||||
<legend>
|
<legend>
|
||||||
General Settings
|
{{#translate}}General Settings{{/translate}}
|
||||||
</legend>
|
</legend>
|
||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="name" class="col-sm-2 control-label">Name</label>
|
<label for="name" class="col-sm-2 control-label">{{#translate}}Name{{/translate}}</label>
|
||||||
<div class="col-sm-10">
|
<div class="col-sm-10">
|
||||||
<input type="text" class="form-control input-lg" name="name" id="name" value="{{name}}" placeholder="Campaign Name" autofocus required>
|
<input type="text" class="form-control input-lg" name="name" id="name" value="{{name}}" placeholder="{{#translate}}Campaign Name{{/translate}}" autofocus required>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="description" class="col-sm-2 control-label">Description</label>
|
<label for="description" class="col-sm-2 control-label">{{#translate}}Description{{/translate}}</label>
|
||||||
<div class="col-sm-10">
|
<div class="col-sm-10">
|
||||||
<textarea class="form-control" rows="3" name="description" id="description">{{description}}</textarea>
|
<textarea class="form-control" rows="3" name="description" id="description">{{description}}</textarea>
|
||||||
<span class="help-block">HTML is allowed</span>
|
<span class="help-block">{{#translate}}HTML is allowed{{/translate}}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="list" class="col-sm-2 control-label">List</label>
|
<label for="list" class="col-sm-2 control-label">{{#translate}}List{{/translate}}</label>
|
||||||
<div class="col-sm-10">
|
<div class="col-sm-10">
|
||||||
<select class="form-control" id="list" name="list" required>
|
<select class="form-control" id="list" name="list" required>
|
||||||
<option value=""> –– Select –– </option>
|
<option value=""> –– {{#translate}}Select{{/translate}} –– </option>
|
||||||
{{#each listItems}}
|
{{#each listItems}}
|
||||||
<option value="{{id}}" {{#if selected}} selected {{/if}}>
|
<option value="{{id}}" {{#if selected}} selected {{/if}}>
|
||||||
{{name}} <span class="text-muted"> — {{subscribers}} subscribers</span>
|
{{name}} <span class="text-muted"> — {{subscribers}} {{#translate}}subscribers{{/translate}}</span>
|
||||||
</option>
|
</option>
|
||||||
|
|
||||||
{{#if segments}}
|
{{#if segments}}
|
||||||
|
@ -94,34 +94,34 @@
|
||||||
<hr />
|
<hr />
|
||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="from" class="col-sm-2 control-label">Email "from name"</label>
|
<label for="from" class="col-sm-2 control-label">{{#translate}}Email "from name"{{/translate}}</label>
|
||||||
<div class="col-sm-10">
|
<div class="col-sm-10">
|
||||||
<input type="text" class="form-control" name="from" id="from" value="{{from}}" placeholder="This is the name your emails will come from" required>
|
<input type="text" class="form-control" name="from" id="from" value="{{from}}" placeholder="{{#translate}}This is the name your emails will come from{{/translate}}" required>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="address" class="col-sm-2 control-label">Email "from" address</label>
|
<label for="address" class="col-sm-2 control-label">{{#translate}}Email "from" address{{/translate}}</label>
|
||||||
<div class="col-sm-10">
|
<div class="col-sm-10">
|
||||||
<input type="email" class="form-control" name="address" id="address" value="{{address}}" placeholder="This is the address people will send replies to unless reply-to address is set" required>
|
<input type="email" class="form-control" name="address" id="address" value="{{address}}" placeholder="{{#translate}}This is the address people will send replies to unless reply-to address is set{{/translate}}" required>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="reply-to" class="col-sm-2 control-label">Email "reply-to" address</label>
|
<label for="reply-to" class="col-sm-2 control-label">{{#translate}}Email "reply-to" address{{/translate}}</label>
|
||||||
<div class="col-sm-10">
|
<div class="col-sm-10">
|
||||||
<input type="email" class="form-control" name="reply-to" id="reply-to" value="{{replyTo}}" placeholder="If set, this is the address people will send replies to">
|
<input type="email" class="form-control" name="reply-to" id="reply-to" value="{{replyTo}}" placeholder="{{#translate}}If set, this is the address people will send replies to{{/translate}}">
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="subject" class="col-sm-2 control-label">Email "subject line"</label>
|
<label for="subject" class="col-sm-2 control-label">{{#translate}}Email "subject line"{{/translate}}</label>
|
||||||
<div class="col-sm-10">
|
<div class="col-sm-10">
|
||||||
<input type="text" class="form-control" name="subject" id="subject" value="{{subject}}" placeholder="Keep it relevant and non-spammy" required>
|
<input type="text" class="form-control" name="subject" id="subject" value="{{subject}}" placeholder="{{#translate}}Keep it relevant and non-spammy{{/translate}}" required>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="col-sm-offset-2">
|
<div class="col-sm-offset-2">
|
||||||
<div class="checkbox">
|
<div class="checkbox">
|
||||||
<label>
|
<label>
|
||||||
<input type="checkbox" name="tracking-disabled" value="1" {{#if trackingDisabled}} checked {{/if}}> Disable clicked/opened tracking
|
<input type="checkbox" name="tracking-disabled" value="1" {{#if trackingDisabled}} checked {{/if}}> {{#translate}}Disable clicked/opened tracking{{/translate}}
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -132,15 +132,15 @@
|
||||||
|
|
||||||
<fieldset>
|
<fieldset>
|
||||||
<legend>
|
<legend>
|
||||||
Template Settings
|
{{#translate}}Template Settings{{/translate}}
|
||||||
</legend>
|
</legend>
|
||||||
|
|
||||||
{{#if sourceUrl}}
|
{{#if sourceUrl}}
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="source-url" class="col-sm-2 control-label">Template URL</label>
|
<label for="source-url" class="col-sm-2 control-label">{{#translate}}Template URL{{/translate}}</label>
|
||||||
<div class="col-sm-10">
|
<div class="col-sm-10">
|
||||||
<input type="url" class="form-control" name="source-url" id="source-url" value="{{sourceUrl}}" placeholder="http://example.com/message-render.php">
|
<input type="url" class="form-control" name="source-url" id="source-url" value="{{sourceUrl}}" placeholder="http://example.com/message-render.php">
|
||||||
<span class="help-block">If a message is sent then this URL will be POSTed to using Merge Tags as POST body. Use this if you want to generate the HTML message yourself</span>
|
<span class="help-block">{{#translate}}If a message is sent then this URL will be POSTed to using Merge Tags as POST body. Use this if you want to generate the HTML message yourself{{/translate}}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{{else}}
|
{{else}}
|
||||||
|
@ -166,7 +166,7 @@
|
||||||
|
|
||||||
<fieldset>
|
<fieldset>
|
||||||
<legend>
|
<legend>
|
||||||
Attachments
|
{{#translate}}Attachments{{/translate}}
|
||||||
</legend>
|
</legend>
|
||||||
|
|
||||||
<div class="table-responsive">
|
<div class="table-responsive">
|
||||||
|
@ -176,10 +176,10 @@
|
||||||
#
|
#
|
||||||
</th>
|
</th>
|
||||||
<th>
|
<th>
|
||||||
File
|
{{#translate}}File{{/translate}}
|
||||||
</th>
|
</th>
|
||||||
<th class="col-md-1">
|
<th class="col-md-1">
|
||||||
Size
|
{{#translate}}Size{{/translate}}
|
||||||
</th>
|
</th>
|
||||||
<th class="col-md-1">
|
<th class="col-md-1">
|
||||||
|
|
||||||
|
@ -206,7 +206,7 @@
|
||||||
{{else}}
|
{{else}}
|
||||||
<tr>
|
<tr>
|
||||||
<td colspan="4">
|
<td colspan="4">
|
||||||
No data available in table
|
{{#translate}}No data available in table{{/translate}}
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
|
@ -214,7 +214,7 @@
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
<div class="pull-right">
|
<div class="pull-right">
|
||||||
<a class="btn btn-info btn-sm" href="/campaigns/attachment/{{id}}" role="button"><span class="glyphicon glyphicon-plus" aria-hidden="true"></span> Add Attachment</a>
|
<a class="btn btn-info btn-sm" href="/campaigns/attachment/{{id}}" role="button"><span class="glyphicon glyphicon-plus" aria-hidden="true"></span> {{#translate}}Add Attachment{{/translate}}</a>
|
||||||
</div>
|
</div>
|
||||||
</fieldset>
|
</fieldset>
|
||||||
</div>
|
</div>
|
||||||
|
@ -226,9 +226,9 @@
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<div class="col-sm-offset-2 col-sm-10">
|
<div class="col-sm-offset-2 col-sm-10">
|
||||||
<div class="pull-right">
|
<div class="pull-right">
|
||||||
<button type="submit" form="campaigns-delete" class="btn btn-danger"><i class="glyphicon glyphicon-remove"></i> Delete Campaign</button>
|
<button type="submit" form="campaigns-delete" class="btn btn-danger"><i class="glyphicon glyphicon-remove"></i> {{#translate}}Delete Campaign{{/translate}}</button>
|
||||||
</div>
|
</div>
|
||||||
<button type="submit" class="btn btn-primary"><i class="glyphicon glyphicon-ok"></i> Update</button>
|
<button type="submit" class="btn btn-primary"><i class="glyphicon glyphicon-ok"></i> {{#translate}}Update{{/translate}}</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -1,14 +1,14 @@
|
||||||
<ol class="breadcrumb">
|
<ol class="breadcrumb">
|
||||||
<li><a href="/">Home</a></li>
|
<li><a href="/">{{#translate}}Home{{/translate}}</a></li>
|
||||||
<li><a href="/campaigns">Campaigns</a></li>
|
<li><a href="/campaigns">{{#translate}}Campaigns{{/translate}}</a></li>
|
||||||
{{#if parent}}
|
{{#if parent}}
|
||||||
<li><a href="/campaigns/view/{{parent.id}}">{{parent.name}}</a></li>
|
<li><a href="/campaigns/view/{{parent.id}}">{{parent.name}}</a></li>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
<li><a href="/campaigns/view/{{id}}">{{name}}</a></li>
|
<li><a href="/campaigns/view/{{id}}">{{name}}</a></li>
|
||||||
<li class="active">Opened info</li>
|
<li class="active">{{#translate}}Opened info{{/translate}}</li>
|
||||||
</ol>
|
</ol>
|
||||||
|
|
||||||
<h2><span class="glyphicon glyphicon-inbox" aria-hidden="true"></span> {{name}} <small>Opened info</small> <a class="btn btn-default btn-xs" href="/campaigns/view/{{id}}?tab=overview" role="button"><span class="glyphicon glyphicon-arrow-left" aria-hidden="true"></span> View campaign</a></h2>
|
<h2><span class="glyphicon glyphicon-inbox" aria-hidden="true"></span> {{name}} <small>{{#translate}}Opened info{{/translate}}</small> <a class="btn btn-default btn-xs" href="/campaigns/view/{{id}}?tab=overview" role="button"><span class="glyphicon glyphicon-arrow-left" aria-hidden="true"></span> {{#translate}}View campaign{{/translate}}</a></h2>
|
||||||
|
|
||||||
<hr>
|
<hr>
|
||||||
|
|
||||||
|
@ -20,7 +20,7 @@
|
||||||
|
|
||||||
<div class="panel panel-info">
|
<div class="panel panel-info">
|
||||||
<!-- Default panel contents -->
|
<!-- Default panel contents -->
|
||||||
<div class="panel-heading">Subscribers who opened this message:</div>
|
<div class="panel-heading">{{#translate}}Subscribers who opened this message:{{/translate}}</div>
|
||||||
<div class="panel-body">
|
<div class="panel-body">
|
||||||
<div class="table-responsive">
|
<div class="table-responsive">
|
||||||
<table data-topic-url="/campaigns/clicked" data-topic-id="{{id}}/-1" data-sort-column="1" data-sort-order="asc" class="table table-bordered table-hover data-table-ajax display nowrap" width="100%" data-row-sort="0,1,1,1,1,1,0">
|
<table data-topic-url="/campaigns/clicked" data-topic-id="{{id}}/-1" data-sort-column="1" data-sort-order="asc" class="table table-bordered table-hover data-table-ajax display nowrap" width="100%" data-row-sort="0,1,1,1,1,1,0">
|
||||||
|
@ -30,19 +30,19 @@
|
||||||
#
|
#
|
||||||
</th>
|
</th>
|
||||||
<th>
|
<th>
|
||||||
Address
|
{{#translate}}Address{{/translate}}
|
||||||
</th>
|
</th>
|
||||||
<th>
|
<th>
|
||||||
First Name
|
{{#translate}}First Name{{/translate}}
|
||||||
</th>
|
</th>
|
||||||
<th>
|
<th>
|
||||||
Last Name
|
{{#translate}}Last Name{{/translate}}
|
||||||
</th>
|
</th>
|
||||||
<th>
|
<th>
|
||||||
First open
|
{{#translate}}First open{{/translate}}
|
||||||
</th>
|
</th>
|
||||||
<th>
|
<th>
|
||||||
Opened count
|
{{#translate}}Opened count{{/translate}}
|
||||||
</th>
|
</th>
|
||||||
<th></th>
|
<th></th>
|
||||||
</tr>
|
</tr>
|
||||||
|
|
|
@ -1,14 +1,14 @@
|
||||||
<ol class="breadcrumb">
|
<ol class="breadcrumb">
|
||||||
<li><a href="/">Home</a></li>
|
<li><a href="/">{{#translate}}Home{{/translate}}</a></li>
|
||||||
<li><a href="/campaigns">Campaigns</a></li>
|
<li><a href="/campaigns">{{#translate}}Campaigns{{/translate}}</a></li>
|
||||||
{{#if parent}}
|
{{#if parent}}
|
||||||
<li><a href="/campaigns/view/{{parent.id}}">{{parent.name}}</a></li>
|
<li><a href="/campaigns/view/{{parent.id}}">{{parent.name}}</a></li>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
<li><a href="/campaigns/view/{{id}}">{{name}}</a></li>
|
<li><a href="/campaigns/view/{{id}}">{{name}}</a></li>
|
||||||
<li class="active">Unsubscribed info</li>
|
<li class="active">{{#translate}}Unsubscribed info{{/translate}}</li>
|
||||||
</ol>
|
</ol>
|
||||||
|
|
||||||
<h2><span class="glyphicon glyphicon-inbox" aria-hidden="true"></span> {{name}} <small>Unsubscribed info</small> <a class="btn btn-default btn-xs" href="/campaigns/view/{{id}}?tab=overview" role="button"><span class="glyphicon glyphicon-arrow-left" aria-hidden="true"></span> View campaign</a></h2>
|
<h2><span class="glyphicon glyphicon-inbox" aria-hidden="true"></span> {{name}} <small>{{#translate}}Unsubscribed info{{/translate}}</small> <a class="btn btn-default btn-xs" href="/campaigns/view/{{id}}?tab=overview" role="button"><span class="glyphicon glyphicon-arrow-left" aria-hidden="true"></span> {{#translate}}View campaign{{/translate}}</a></h2>
|
||||||
|
|
||||||
<hr>
|
<hr>
|
||||||
|
|
||||||
|
@ -20,7 +20,7 @@
|
||||||
|
|
||||||
<div class="panel panel-info">
|
<div class="panel panel-info">
|
||||||
<!-- Default panel contents -->
|
<!-- Default panel contents -->
|
||||||
<div class="panel-heading">Subscribers who unsubscribed:</div>
|
<div class="panel-heading">{{#translate}}Subscribers who unsubscribed:{{/translate}}</div>
|
||||||
<div class="panel-body">
|
<div class="panel-body">
|
||||||
<div class="table-responsive">
|
<div class="table-responsive">
|
||||||
<table data-topic-url="/campaigns/status" data-topic-id="{{id}}/2" data-sort-column="1" data-sort-order="asc" class="table table-bordered table-hover data-table-ajax display nowrap" width="100%" data-row-sort="0,1,1,1,0,1,0">
|
<table data-topic-url="/campaigns/status" data-topic-id="{{id}}/2" data-sort-column="1" data-sort-order="asc" class="table table-bordered table-hover data-table-ajax display nowrap" width="100%" data-row-sort="0,1,1,1,0,1,0">
|
||||||
|
@ -30,19 +30,19 @@
|
||||||
#
|
#
|
||||||
</th>
|
</th>
|
||||||
<th>
|
<th>
|
||||||
Address
|
{{#translate}}Address{{/translate}}
|
||||||
</th>
|
</th>
|
||||||
<th>
|
<th>
|
||||||
First Name
|
{{#translate}}First Name{{/translate}}
|
||||||
</th>
|
</th>
|
||||||
<th>
|
<th>
|
||||||
Last Name
|
{{#translate}}Last Name{{/translate}}
|
||||||
</th>
|
</th>
|
||||||
<th>
|
<th>
|
||||||
SMTP response
|
{{#translate}}SMTP response{{/translate}}
|
||||||
</th>
|
</th>
|
||||||
<th>
|
<th>
|
||||||
Unsubscribed
|
{{#translate}}Unsubscribed{{/translate}}
|
||||||
</th>
|
</th>
|
||||||
<th></th>
|
<th></th>
|
||||||
</tr>
|
</tr>
|
||||||
|
|
|
@ -1,15 +1,15 @@
|
||||||
<ol class="breadcrumb">
|
<ol class="breadcrumb">
|
||||||
<li><a href="/">Home</a></li>
|
<li><a href="/">{{#translate}}Home{{/translate}}</a></li>
|
||||||
<li><a href="/campaigns">Campaigns</a></li>
|
<li><a href="/campaigns">{{#translate}}Campaigns{{/translate}}</a></li>
|
||||||
{{#if parent}}
|
{{#if parent}}
|
||||||
<li><a href="/campaigns/view/{{parent.id}}">{{parent.name}}</a></li>
|
<li><a href="/campaigns/view/{{parent.id}}">{{parent.name}}</a></li>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
<li><a href="/campaigns/view/{{id}}">{{name}}</a></li>
|
<li><a href="/campaigns/view/{{id}}">{{name}}</a></li>
|
||||||
<li><a href="/campaigns/edit/{{id}}?tab=attachments">Edit Campaign</a></li>
|
<li><a href="/campaigns/edit/{{id}}?tab=attachments">{{#translate}}Edit Campaign{{/translate}}</a></li>
|
||||||
<li class="active">Add Attachment</li>
|
<li class="active">{{#translate}}Add Attachment{{/translate}}</li>
|
||||||
</ol>
|
</ol>
|
||||||
|
|
||||||
<h2>Edit Campaign <a class="btn btn-default btn-xs" href="/campaigns/view/{{id}}" role="button"><span class="glyphicon glyphicon-arrow-left" aria-hidden="true"></span> View campaign</a></h2>
|
<h2>{{#translate}}Edit Campaign{{/translate}} <a class="btn btn-default btn-xs" href="/campaigns/view/{{id}}" role="button"><span class="glyphicon glyphicon-arrow-left" aria-hidden="true"></span> {{#translate}}View campaign{{/translate}}</a></h2>
|
||||||
|
|
||||||
<hr>
|
<hr>
|
||||||
|
|
||||||
|
@ -25,7 +25,7 @@
|
||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<div class="col-sm-offset-2 col-sm-10">
|
<div class="col-sm-offset-2 col-sm-10">
|
||||||
<button type="submit" class="btn btn-primary"><i class="glyphicon glyphicon-cloud-upload"></i> Upload</button>
|
<button type="submit" class="btn btn-primary"><i class="glyphicon glyphicon-cloud-upload"></i> {{#translate}}Upload{{/translate}}</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
<ol class="breadcrumb">
|
<ol class="breadcrumb">
|
||||||
<li><a href="/">Home</a></li>
|
<li><a href="/">{{#translate}}Home{{/translate}}</a></li>
|
||||||
<li><a href="/campaigns">Campaigns</a></li>
|
<li><a href="/campaigns">{{#translate}}Campaigns{{/translate}}</a></li>
|
||||||
{{#if parent}}
|
{{#if parent}}
|
||||||
<li><a href="/campaigns/view/{{parent.id}}">{{parent.name}}</a></li>
|
<li><a href="/campaigns/view/{{parent.id}}">{{parent.name}}</a></li>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
|
@ -8,7 +8,7 @@
|
||||||
</ol>
|
</ol>
|
||||||
|
|
||||||
<div class="pull-right">
|
<div class="pull-right">
|
||||||
<a class="btn btn-primary" href="/campaigns/edit/{{id}}" role="button"><span class="glyphicon glyphicon-wrench" aria-hidden="true"></span> Edit Campaign</a>
|
<a class="btn btn-primary" href="/campaigns/edit/{{id}}" role="button"><span class="glyphicon glyphicon-wrench" aria-hidden="true"></span> {{#translate}}Edit Campaign{{/translate}}</a>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<h2><span class="glyphicon glyphicon-inbox" aria-hidden="true"></span> {{name}}</h2>
|
<h2><span class="glyphicon glyphicon-inbox" aria-hidden="true"></span> {{name}}</h2>
|
||||||
|
@ -21,9 +21,9 @@
|
||||||
|
|
||||||
<!-- Nav tabs -->
|
<!-- Nav tabs -->
|
||||||
<ul class="nav nav-tabs" role="tablist">
|
<ul class="nav nav-tabs" role="tablist">
|
||||||
<li role="presentation" class="{{#if showOverview}}active{{/if}}"><a href="#overview" aria-controls="overview" role="tab" data-toggle="tab">Overview</a></li>
|
<li role="presentation" class="{{#if showOverview}}active{{/if}}"><a href="#overview" aria-controls="overview" role="tab" data-toggle="tab">{{#translate}}Overview{{/translate}}</a></li>
|
||||||
{{#if links}}
|
{{#if links}}
|
||||||
<li role="presentation" class="{{#if showLinks}}active{{/if}}"><a href="#links" aria-controls="links" role="tab" data-toggle="tab">Links</a></li>
|
<li role="presentation" class="{{#if showLinks}}active{{/if}}"><a href="#links" aria-controls="links" role="tab" data-toggle="tab">{{#translate}}Links{{/translate}}</a></li>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
|
@ -34,7 +34,7 @@
|
||||||
|
|
||||||
<dl class="dl-horizontal">
|
<dl class="dl-horizontal">
|
||||||
{{#if list}}
|
{{#if list}}
|
||||||
<dt>List</dt>
|
<dt>{{#translate}}List{{/translate}}</dt>
|
||||||
<dd>
|
<dd>
|
||||||
{{#if segment}}
|
{{#if segment}}
|
||||||
<a href="/lists/view/{{list.id}}?segment={{segment.id}}">{{list.name}}: {{segment.name}}</a>
|
<a href="/lists/view/{{list.id}}?segment={{segment.id}}">{{list.name}}: {{segment.name}}</a>
|
||||||
|
@ -54,43 +54,43 @@
|
||||||
{{/if}}
|
{{/if}}
|
||||||
|
|
||||||
{{#if isRss}}
|
{{#if isRss}}
|
||||||
<dt>Feed URL</dt>
|
<dt>{{#translate}}Feed URL{{/translate}}</dt>
|
||||||
<dd><a href="{{sourceUrl}}">{{sourceUrl}}</a></dd>
|
<dd><a href="{{sourceUrl}}">{{sourceUrl}}</a></dd>
|
||||||
<dt>Last check</dt>
|
<dt>{{#translate}}Last check{{/translate}}</dt>
|
||||||
<dd>
|
<dd>
|
||||||
{{#if lastCheck}}<span class="datestring" data-date="{{lastCheck}}" title="{{lastCheck}}">{{lastCheck}}</span>{{else}}
|
{{#if lastCheck}}<span class="datestring" data-date="{{lastCheck}}" title="{{lastCheck}}">{{lastCheck}}</span>{{else}}
|
||||||
Not yet checked{{/if}}
|
{{#translate}}Not yet checked{{/translate}}{{/if}}
|
||||||
{{#unless isActive}}<span class="text-muted">(activate campaign to start checking feed for new messages)</span>{{/unless}}
|
{{#unless isActive}}<span class="text-muted">({{#translate}}activate campaign to start checking feed for new messages{{/translate}})</span>{{/unless}}
|
||||||
</dd>
|
</dd>
|
||||||
{{#if checkStatus}}
|
{{#if checkStatus}}
|
||||||
<dt>RSS status</dt>
|
<dt>{{#translate}}RSS status{{/translate}}</dt>
|
||||||
<dd>{{checkStatus}}</dd>
|
<dd>{{checkStatus}}</dd>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
{{/if}}
|
{{/if}}
|
||||||
|
|
||||||
{{#if from}}
|
{{#if from}}
|
||||||
<dt>Email "from name"</dt>
|
<dt>{{#translate}}Email "from name"{{/translate}}</dt>
|
||||||
<dd>{{from}}</dd>
|
<dd>{{from}}</dd>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
|
|
||||||
{{#if address}}
|
{{#if address}}
|
||||||
<dt>Email "from" address</dt>
|
<dt>{{#translate}}Email "from" address{{/translate}}</dt>
|
||||||
<dd>{{address}}</dd>
|
<dd>{{address}}</dd>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
|
|
||||||
{{#if replyTo}}
|
{{#if replyTo}}
|
||||||
<dt>Email "reply-to" address</dt>
|
<dt>{{#translate}}Email "reply-to" address{{/translate}}</dt>
|
||||||
<dd>{{replyTo}}</dd>
|
<dd>{{replyTo}}</dd>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
|
|
||||||
{{#if subject}}
|
{{#if subject}}
|
||||||
<dt>Email "subject line"</dt>
|
<dt>{{#translate}}Email "subject line"{{/translate}}</dt>
|
||||||
<dd>{{subject}}</dd>
|
<dd>{{subject}}</dd>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
|
|
||||||
{{#unless isRss}}
|
{{#unless isRss}}
|
||||||
|
|
||||||
<dt>Preview campaign as</dt>
|
<dt>{{#translate}}Preview campaign as{{/translate}}</dt>
|
||||||
<dd>
|
<dd>
|
||||||
<form method="post" action="/campaigns/preview/{{id}}" class="form-inline">
|
<form method="post" action="/campaigns/preview/{{id}}" class="form-inline">
|
||||||
<input type="hidden" name="_csrf" value="{{csrfToken}}">
|
<input type="hidden" name="_csrf" value="{{csrfToken}}">
|
||||||
|
@ -104,26 +104,26 @@
|
||||||
{{/each}}
|
{{/each}}
|
||||||
{{#if testUsers}}
|
{{#if testUsers}}
|
||||||
<optgroup label="Actions">
|
<optgroup label="Actions">
|
||||||
<option value="_create">Add new test user ...</option>
|
<option value="_create">{{#translate}}Add new test user{{/translate}}…</option>
|
||||||
</optgroup>
|
</optgroup>
|
||||||
{{else}}
|
{{else}}
|
||||||
<option value="_create">No test users yet, create one here ...</option>
|
<option value="_create">{{#translate}}No test users yet, create one here{{/translate}}…</option>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
<button type="submit" class="btn btn-default">Go</button>
|
<button type="submit" class="btn btn-default">{{#translate}}Go{{/translate}}</button>
|
||||||
</form>
|
</form>
|
||||||
</dd>
|
</dd>
|
||||||
|
|
||||||
{{#unless isIdling}}
|
{{#unless isIdling}}
|
||||||
<dt>Delivered <a href="/campaigns/status/{{id}}/delivered" title="List subscribers who received this message"><span class="glyphicon glyphicon-zoom-in" aria-hidden="true"></span></a></dt>
|
<dt>{{#translate}}Delivered{{/translate}} <a href="/campaigns/status/{{id}}/delivered" title="{{#translate}}List subscribers who received this message{{/translate}}"><span class="glyphicon glyphicon-zoom-in" aria-hidden="true"></span></a></dt>
|
||||||
<dd>{{delivered}}</dd>
|
<dd>{{delivered}}</dd>
|
||||||
|
|
||||||
</dl>
|
</dl>
|
||||||
<hr />
|
<hr />
|
||||||
<dl class="dl-horizontal">
|
<dl class="dl-horizontal">
|
||||||
|
|
||||||
<dt>Bounced <a href="/campaigns/status/{{id}}/bounced" title="List subscribers who bounced"><span class="glyphicon glyphicon-zoom-in" aria-hidden="true"></span></a></dt>
|
<dt>{{#translate}}Bounced{{/translate}} <a href="/campaigns/status/{{id}}/bounced" title="{{#translate}}List subscribers who bounced{{/translate}}"><span class="glyphicon glyphicon-zoom-in" aria-hidden="true"></span></a></dt>
|
||||||
<dd>
|
<dd>
|
||||||
<div class="progress">
|
<div class="progress">
|
||||||
<div class="progress-bar progress-bar-info" role="progressbar" aria-valuenow="{{bounceRate}}" aria-valuemin="0" aria-valuemax="100" style="min-width: 6em; width: {{bounceRate}}%;">
|
<div class="progress-bar progress-bar-info" role="progressbar" aria-valuenow="{{bounceRate}}" aria-valuemin="0" aria-valuemax="100" style="min-width: 6em; width: {{bounceRate}}%;">
|
||||||
|
@ -132,7 +132,7 @@
|
||||||
</div>
|
</div>
|
||||||
</dd>
|
</dd>
|
||||||
|
|
||||||
<dt>Complaints <a href="/campaigns/status/{{id}}/complained" title="List subscribers who complained for this message"><span class="glyphicon glyphicon-zoom-in" aria-hidden="true"></span></a></dt>
|
<dt>{{#translate}}Complaints{{/translate}} <a href="/campaigns/status/{{id}}/complained" title="{{#translate}}List subscribers who complained for this message{{/translate}}"><span class="glyphicon glyphicon-zoom-in" aria-hidden="true"></span></a></dt>
|
||||||
<dd>
|
<dd>
|
||||||
<div class="progress">
|
<div class="progress">
|
||||||
<div class="progress-bar progress-bar-danger" role="progressbar" aria-valuenow="{{complaintRate}}" aria-valuemin="0" aria-valuemax="100" style="min-width: 6em; width: {{complaintRate}}%;">
|
<div class="progress-bar progress-bar-danger" role="progressbar" aria-valuenow="{{complaintRate}}" aria-valuemin="0" aria-valuemax="100" style="min-width: 6em; width: {{complaintRate}}%;">
|
||||||
|
@ -141,7 +141,7 @@
|
||||||
</div>
|
</div>
|
||||||
</dd>
|
</dd>
|
||||||
|
|
||||||
<dt>Unsubscribed <a href="/campaigns/status/{{id}}/unsubscribed" title="List subscribers who unsubscribed after this message"><span class="glyphicon glyphicon-zoom-in" aria-hidden="true"></span></a></dt>
|
<dt>{{#translate}}Unsubscribed{{/translate}} <a href="/campaigns/status/{{id}}/unsubscribed" title="{{#translate}}List subscribers who unsubscribed after this message{{/translate}}"><span class="glyphicon glyphicon-zoom-in" aria-hidden="true"></span></a></dt>
|
||||||
<dd>
|
<dd>
|
||||||
<div class="progress">
|
<div class="progress">
|
||||||
<div class="progress-bar progress-bar-warning" role="progressbar" aria-valuenow="{{unsubscribeRate}}" aria-valuemin="0" aria-valuemax="100" style="min-width: 6em; width: {{unsubscribeRate}}%;">
|
<div class="progress-bar progress-bar-warning" role="progressbar" aria-valuenow="{{unsubscribeRate}}" aria-valuemin="0" aria-valuemax="100" style="min-width: 6em; width: {{unsubscribeRate}}%;">
|
||||||
|
@ -152,7 +152,7 @@
|
||||||
|
|
||||||
{{#unless trackingDisabled}}
|
{{#unless trackingDisabled}}
|
||||||
|
|
||||||
<dt>Opened <a href="/campaigns/opened/{{id}}" title="List subscribers who opened this message"><span class="glyphicon glyphicon-zoom-in" aria-hidden="true"></span></a></dt>
|
<dt>{{#translate}}Opened{{/translate}} <a href="/campaigns/opened/{{id}}" title="{{#translate}}List subscribers who opened this message{{/translate}}"><span class="glyphicon glyphicon-zoom-in" aria-hidden="true"></span></a></dt>
|
||||||
<dd>
|
<dd>
|
||||||
<div class="progress">
|
<div class="progress">
|
||||||
<div class="progress-bar progress-bar-success" role="progressbar" aria-valuenow="{{openRate}}" aria-valuemin="0" aria-valuemax="100" style="min-width: 6em; width: {{openRate}}%;">
|
<div class="progress-bar progress-bar-success" role="progressbar" aria-valuenow="{{openRate}}" aria-valuemin="0" aria-valuemax="100" style="min-width: 6em; width: {{openRate}}%;">
|
||||||
|
@ -161,7 +161,7 @@
|
||||||
</div>
|
</div>
|
||||||
</dd>
|
</dd>
|
||||||
|
|
||||||
<dt>Clicked <a href="/campaigns/clicked/{{id}}/all" title="List subscribers who clicked on a link"> <span class="glyphicon glyphicon-zoom-in" aria-hidden="true"></span></a></dt>
|
<dt>{{#translate}}Clicked{{/translate}} <a href="/campaigns/clicked/{{id}}/all" title="{{#translate}}List subscribers who clicked on a link{{/translate}}"> <span class="glyphicon glyphicon-zoom-in" aria-hidden="true"></span></a></dt>
|
||||||
<dd>
|
<dd>
|
||||||
<div class="progress">
|
<div class="progress">
|
||||||
<div class="progress-bar progress-bar-success" role="progressbar" aria-valuenow="{{clicksRate}}" aria-valuemin="0" aria-valuemax="100" style="min-width: 6em; width: {{clicksRate}}%;">
|
<div class="progress-bar progress-bar-success" role="progressbar" aria-valuenow="{{clicksRate}}" aria-valuemin="0" aria-valuemax="100" style="min-width: 6em; width: {{clicksRate}}%;">
|
||||||
|
@ -180,34 +180,34 @@
|
||||||
<div class="panel panel-default">
|
<div class="panel panel-default">
|
||||||
<div class="panel-body">
|
<div class="panel-body">
|
||||||
{{#if isIdling}}
|
{{#if isIdling}}
|
||||||
<form class="form-inline confirm-submit" data-confirm-message="Are you sure? This action would start sending messages to the selected list" method="post" action="/campaigns/send">
|
<form class="form-inline confirm-submit" data-confirm-message="{{#translate}}Are you sure? This action would start sending messages to the selected list{{/translate}}" method="post" action="/campaigns/send">
|
||||||
<input type="hidden" name="_csrf" value="{{csrfToken}}">
|
<input type="hidden" name="_csrf" value="{{csrfToken}}">
|
||||||
<input type="hidden" name="id" value="{{id}}" />
|
<input type="hidden" name="id" value="{{id}}" />
|
||||||
|
|
||||||
<div class="pull-right">
|
<div class="pull-right">
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<p class="form-control-static">Delay sending</p>
|
<p class="form-control-static">{{#translate}}Delay sending{{/translate}}</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<div class="input-group">
|
<div class="input-group">
|
||||||
<input type="number" class="form-control" name="delay-hours" id="delay-hours" placeholder="0">
|
<input type="number" class="form-control" name="delay-hours" id="delay-hours" placeholder="0">
|
||||||
<div class="input-group-addon"> hours</div>
|
<div class="input-group-addon"> {{#translate}}hours{{/translate}}</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<div class="input-group">
|
<div class="input-group">
|
||||||
<input type="number" class="form-control" name="delay-minutes" id="delay-minutes" placeholder="0">
|
<input type="number" class="form-control" name="delay-minutes" id="delay-minutes" placeholder="0">
|
||||||
<div class="input-group-addon"> minutes</div>
|
<div class="input-group-addon"> {{#translate}}minutes{{/translate}}</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<button type="submit" class="btn btn-success"><span class="glyphicon glyphicon-send" aria-hidden="true"></span> Send to
|
<button type="submit" class="btn btn-success"><span class="glyphicon glyphicon-send" aria-hidden="true"></span> {{#translate}}Send to subscribers:{{/translate}}
|
||||||
{{#if segment}}
|
{{#if segment}}
|
||||||
{{segment.subscribers}}
|
{{segment.subscribers}}
|
||||||
{{else}}
|
{{else}}
|
||||||
{{list.subscribers}}
|
{{list.subscribers}}
|
||||||
{{/if}} subscribers</button>
|
{{/if}}</button>
|
||||||
</form>
|
</form>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
|
|
||||||
|
@ -216,45 +216,45 @@
|
||||||
<div class="page-refresh" data-interval="20"></div>
|
<div class="page-refresh" data-interval="20"></div>
|
||||||
{{#if isScheduled}}
|
{{#if isScheduled}}
|
||||||
<div class="pull-right">
|
<div class="pull-right">
|
||||||
<form class="form-horizontal confirm-submit" data-confirm-message="Are you sure? This action would reset scheduling" method="post" action="/campaigns/reset">
|
<form class="form-horizontal confirm-submit" data-confirm-message="{{#translate}}Are you sure? This action would reset scheduling{{/translate}}" method="post" action="/campaigns/reset">
|
||||||
<input type="hidden" name="_csrf" value="{{csrfToken}}">
|
<input type="hidden" name="_csrf" value="{{csrfToken}}">
|
||||||
<input type="hidden" name="id" value="{{id}}" />
|
<input type="hidden" name="id" value="{{id}}" />
|
||||||
|
|
||||||
<button type="submit" class="btn btn-danger"><span class="glyphicon glyphicon-remove" aria-hidden="true"></span> Cancel
|
<button type="submit" class="btn btn-danger"><span class="glyphicon glyphicon-remove" aria-hidden="true"></span> {{#translate}}Cancel{{/translate}}
|
||||||
</button>
|
</button>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
<h4>Sending scheduled <span class="datestring text-info" data-date="{{scheduled}}" title="{{scheduled}}">{{scheduled}}</span></h4>
|
<h4>{{#translate}}Sending scheduled{{/translate}} <span class="datestring text-info" data-date="{{scheduled}}" title="{{scheduled}}">{{scheduled}}</span></h4>
|
||||||
{{else}}
|
{{else}}
|
||||||
<div class="pull-right">
|
<div class="pull-right">
|
||||||
<form class="form-horizontal" method="post" action="/campaigns/pause">
|
<form class="form-horizontal" method="post" action="/campaigns/pause">
|
||||||
<input type="hidden" name="_csrf" value="{{csrfToken}}">
|
<input type="hidden" name="_csrf" value="{{csrfToken}}">
|
||||||
<input type="hidden" name="id" value="{{id}}" />
|
<input type="hidden" name="id" value="{{id}}" />
|
||||||
|
|
||||||
<button type="submit" class="btn btn-info"><span class="glyphicon glyphicon-pause" aria-hidden="true"></span> Pause
|
<button type="submit" class="btn btn-info"><span class="glyphicon glyphicon-pause" aria-hidden="true"></span> {{#translate}}Pause{{/translate}}
|
||||||
</button>
|
</button>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
<h4><span class="glyphicon glyphicon-refresh spinning"></span> Sending…</h4>
|
<h4><span class="glyphicon glyphicon-refresh spinning"></span> {{#translate}}Sending{{/translate}}…</h4>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
{{/if}}
|
{{/if}}
|
||||||
|
|
||||||
{{#if isPaused}}
|
{{#if isPaused}}
|
||||||
<div class="pull-right">
|
<div class="pull-right">
|
||||||
<form id="resume-sending" class="form-horizontal confirm-submit" data-confirm-message="Are you sure? This action would resume sending messages to the selected list" method="post" action="/campaigns/resume">
|
<form id="resume-sending" class="form-horizontal confirm-submit" data-confirm-message="{{#translate}}Are you sure? This action would resume sending messages to the selected list{{/translate}}" method="post" action="/campaigns/resume">
|
||||||
<input type="hidden" name="_csrf" value="{{csrfToken}}">
|
<input type="hidden" name="_csrf" value="{{csrfToken}}">
|
||||||
<input type="hidden" name="id" value="{{id}}" />
|
<input type="hidden" name="id" value="{{id}}" />
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
<form id="reset-sending" class="confirm-submit" data-confirm-message="Are you sure? This action would reset all stats about current progress" method="post" action="/campaigns/reset">
|
<form id="reset-sending" class="confirm-submit" data-confirm-message="{{#translate}}Are you sure? This action would reset all stats about current progress{{/translate}}" method="post" action="/campaigns/reset">
|
||||||
<input type="hidden" name="_csrf" value="{{csrfToken}}">
|
<input type="hidden" name="_csrf" value="{{csrfToken}}">
|
||||||
<input type="hidden" name="id" value="{{id}}" />
|
<input type="hidden" name="id" value="{{id}}" />
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
<button type="submit" form="resume-sending" class="btn btn-info"><span class="glyphicon glyphicon-play" aria-hidden="true"></span> Resume
|
<button type="submit" form="resume-sending" class="btn btn-info"><span class="glyphicon glyphicon-play" aria-hidden="true"></span> {{#translate}}Resume{{/translate}}
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
<button type="submit" form="reset-sending" class="btn btn-danger"><span class="glyphicon glyphicon-refresh" aria-hidden="true"></span> Reset
|
<button type="submit" form="reset-sending" class="btn btn-danger"><span class="glyphicon glyphicon-refresh" aria-hidden="true"></span> {{#translate}}Reset{{/translate}}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<h4>Sending paused</h4>
|
<h4>Sending paused</h4>
|
||||||
|
@ -262,24 +262,24 @@
|
||||||
|
|
||||||
{{#if isFinished}}
|
{{#if isFinished}}
|
||||||
<div class="pull-right">
|
<div class="pull-right">
|
||||||
<form id="continue-sending" class="confirm-submit" data-confirm-message="Are you sure? This action would resume sending messages to the selected list" method="post" action="/campaigns/send">
|
<form id="continue-sending" class="confirm-submit" data-confirm-message="{{#translate}}Are you sure? This action would resume sending messages to the selected list{{/translate}}" method="post" action="/campaigns/send">
|
||||||
<input type="hidden" name="_csrf" value="{{csrfToken}}">
|
<input type="hidden" name="_csrf" value="{{csrfToken}}">
|
||||||
<input type="hidden" name="id" value="{{id}}" />
|
<input type="hidden" name="id" value="{{id}}" />
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
<form id="reset-sending" class="confirm-submit" data-confirm-message="Are you sure? This action would reset all stats about current progress" method="post" action="/campaigns/reset">
|
<form id="reset-sending" class="confirm-submit" data-confirm-message="{{#translate}}Are you sure? This action would reset all stats about current progress{{/translate}}" method="post" action="/campaigns/reset">
|
||||||
<input type="hidden" name="_csrf" value="{{csrfToken}}">
|
<input type="hidden" name="_csrf" value="{{csrfToken}}">
|
||||||
<input type="hidden" name="id" value="{{id}}" />
|
<input type="hidden" name="id" value="{{id}}" />
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
<button type="submit" form="continue-sending" class="btn btn-info"><span class="glyphicon glyphicon-play" aria-hidden="true"></span> Continue
|
<button type="submit" form="continue-sending" class="btn btn-info"><span class="glyphicon glyphicon-play" aria-hidden="true"></span> {{#translate}}Continue{{/translate}}
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
<button type="submit" form="reset-sending" class="btn btn-danger"><span class="glyphicon glyphicon-refresh" aria-hidden="true"></span> Reset
|
<button type="submit" form="reset-sending" class="btn btn-danger"><span class="glyphicon glyphicon-refresh" aria-hidden="true"></span> {{#translate}}Reset{{/translate}}
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
<h4>All messages sent! Hit "Continue" if you you want to send this campaign to new subscribers</h4>
|
<h4>{{#translate}}All messages sent! Hit "Continue" if you you want to send this campaign to new subscribers{{/translate}}</h4>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
@ -292,28 +292,28 @@
|
||||||
<div class="panel-body">
|
<div class="panel-body">
|
||||||
{{#if isActive}}
|
{{#if isActive}}
|
||||||
<div class="pull-right">
|
<div class="pull-right">
|
||||||
<form id="inactivate-sending" class="confirm-submit" data-confirm-message="Are you sure? This action would pause sending new entries in RSS feed as email messages to the selected list" method="post" action="/campaigns/inactivate">
|
<form id="inactivate-sending" class="confirm-submit" data-confirm-message="{{#translate}}Are you sure? This action would pause sending new entries in RSS feed as email messages to the selected list{{/translate}}" method="post" action="/campaigns/inactivate">
|
||||||
<input type="hidden" name="_csrf" value="{{csrfToken}}">
|
<input type="hidden" name="_csrf" value="{{csrfToken}}">
|
||||||
<input type="hidden" name="id" value="{{id}}" />
|
<input type="hidden" name="id" value="{{id}}" />
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
<button type="submit" form="inactivate-sending" class="btn btn-warning"><span class="glyphicon glyphicon-pause" aria-hidden="true"></span> Pause
|
<button type="submit" form="inactivate-sending" class="btn btn-warning"><span class="glyphicon glyphicon-pause" aria-hidden="true"></span> {{#translate}}Pause{{/translate}}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
Campaign status: <span class="label label-primary">ACTIVE</span>
|
{{#translate}}Campaign status:{{/translate}} <span class="label label-primary">{{#translate}}ACTIVE{{/translate}}</span>
|
||||||
{{else}}
|
{{else}}
|
||||||
<div class="pull-right">
|
<div class="pull-right">
|
||||||
<form id="activate-sending" class="confirm-submit" data-confirm-message="Are you sure? This action would start sending new entries in RSS feed as email messages to the selected list" method="post" action="/campaigns/activate">
|
<form id="activate-sending" class="confirm-submit" data-confirm-message="{{#translate}}Are you sure? This action would start sending new entries in RSS feed as email messages to the selected list{{/translate}}" method="post" action="/campaigns/activate">
|
||||||
<input type="hidden" name="_csrf" value="{{csrfToken}}">
|
<input type="hidden" name="_csrf" value="{{csrfToken}}">
|
||||||
<input type="hidden" name="id" value="{{id}}" />
|
<input type="hidden" name="id" value="{{id}}" />
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
<button type="submit" form="activate-sending" class="btn btn-info"><span class="glyphicon glyphicon-play" aria-hidden="true"></span> Activate
|
<button type="submit" form="activate-sending" class="btn btn-info"><span class="glyphicon glyphicon-play" aria-hidden="true"></span> {{#translate}}Activate{{/translate}}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
Campaign status: <span class="label label-default">INACTIVE</span>
|
{{#translate}}Campaign status:{{/translate}} <span class="label label-default">{{#translate}}INACTIVE{{/translate}}</span>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -322,7 +322,7 @@
|
||||||
{{#if isTriggered}}
|
{{#if isTriggered}}
|
||||||
<div class="panel panel-default">
|
<div class="panel panel-default">
|
||||||
<div class="panel-body">
|
<div class="panel-body">
|
||||||
This is a <a href="/triggers">triggered</a> campaign. Messages are only sent to subscribers that hit some trigger that invokes this campaign
|
{{#translate}}This is a triggered campaign. Messages are only sent to subscribers that hit some trigger that invokes this campaign{{/translate}} (<a href="/triggers">{{#translate}}see more{{/translate}}</a>)
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
|
@ -341,16 +341,16 @@
|
||||||
#
|
#
|
||||||
</th>
|
</th>
|
||||||
<th>
|
<th>
|
||||||
URL
|
{{#translate}}URL{{/translate}}
|
||||||
</th>
|
</th>
|
||||||
<th class="col-md-1">
|
<th class="col-md-1">
|
||||||
Clicks
|
{{#translate}}Clicks{{/translate}}
|
||||||
</th>
|
</th>
|
||||||
<th class="col-md-1">
|
<th class="col-md-1">
|
||||||
% of clicks
|
{{#translate}}% of clicks{{/translate}}
|
||||||
</th>
|
</th>
|
||||||
<th class="col-md-1">
|
<th class="col-md-1">
|
||||||
% of messages
|
{{#translate}}% of messages{{/translate}}
|
||||||
</th>
|
</th>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
|
@ -365,7 +365,7 @@
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
<div class="pull-right">
|
<div class="pull-right">
|
||||||
<a href="/campaigns/clicked/{{../id}}/{{id}}" title="List subscribers who clicked this link"><span class="glyphicon glyphicon-zoom-in" aria-hidden="true"></span></a>
|
<a href="/campaigns/clicked/{{../id}}/{{id}}" title="{{#translate}}List subscribers who clicked this link{{/translate}}"><span class="glyphicon glyphicon-zoom-in" aria-hidden="true"></span></a>
|
||||||
</div>
|
</div>
|
||||||
{{clicks}}
|
{{clicks}}
|
||||||
</td>
|
</td>
|
||||||
|
@ -380,7 +380,7 @@
|
||||||
{{else}}
|
{{else}}
|
||||||
<tr>
|
<tr>
|
||||||
<td colspan="5">
|
<td colspan="5">
|
||||||
No data available in table
|
{{#translate}}No data available in table{{/translate}}
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
|
@ -389,11 +389,11 @@
|
||||||
<tr>
|
<tr>
|
||||||
<th></th>
|
<th></th>
|
||||||
<th>
|
<th>
|
||||||
Aggregated clicks
|
{{#translate}}Aggregated clicks{{/translate}}
|
||||||
</th>
|
</th>
|
||||||
<th>
|
<th>
|
||||||
<div class="pull-right">
|
<div class="pull-right">
|
||||||
<a href="/campaigns/clicked/{{id}}/all" title="List subscribers who clicked on a link"><span class="glyphicon glyphicon-zoom-in" aria-hidden="true"></span></a>
|
<a href="/campaigns/clicked/{{id}}/all" title="{{#translate}}List subscribers who clicked on a link{{/translate}}"><span class="glyphicon glyphicon-zoom-in" aria-hidden="true"></span></a>
|
||||||
</div>
|
</div>
|
||||||
{{clicks}}
|
{{clicks}}
|
||||||
</th>
|
</th>
|
||||||
|
@ -408,7 +408,7 @@
|
||||||
</table>
|
</table>
|
||||||
|
|
||||||
<p class="text-muted">
|
<p class="text-muted">
|
||||||
Clicks are counted as unique subscribers that clicked on a specific link or on any link (in aggregated view)
|
{{#translate}}Clicks are counted as unique subscribers that clicked on a specific link or on any link (in aggregated view){{/translate}}
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
@ -421,7 +421,7 @@
|
||||||
{{#if isRss}}
|
{{#if isRss}}
|
||||||
<div class="table-responsive">
|
<div class="table-responsive">
|
||||||
<div class="well text-info">
|
<div class="well text-info">
|
||||||
If a new entry is found from campaign feed a new subcampaign is created of that entry and it will be listed here
|
{{#translate}}If a new entry is found from campaign feed a new subcampaign is created of that entry and it will be listed here{{/translate}}
|
||||||
</div>
|
</div>
|
||||||
<table data-topic-url="/campaigns" data-sort-column="4" data-sort-order="desc" class="table table-bordered table-hover data-table-ajax display nowrap" data-topic-args="parent={{id}}" width="100%" data-row-sort="0,1,0,1,1,0">
|
<table data-topic-url="/campaigns" data-sort-column="4" data-sort-order="desc" class="table table-bordered table-hover data-table-ajax display nowrap" data-topic-args="parent={{id}}" width="100%" data-row-sort="0,1,0,1,1,0">
|
||||||
<thead>
|
<thead>
|
||||||
|
@ -429,16 +429,16 @@
|
||||||
#
|
#
|
||||||
</th>
|
</th>
|
||||||
<th>
|
<th>
|
||||||
Name
|
{{#translate}}Name{{/translate}}
|
||||||
</th>
|
</th>
|
||||||
<th>
|
<th>
|
||||||
Description
|
{{#translate}}Description{{/translate}}
|
||||||
</th>
|
</th>
|
||||||
<th>
|
<th>
|
||||||
Status
|
{{#translate}}Status{{/translate}}
|
||||||
</th>
|
</th>
|
||||||
<th>
|
<th>
|
||||||
Created
|
{{#translate}}Created{{/translate}}
|
||||||
</th>
|
</th>
|
||||||
<th class="col-md-1">
|
<th class="col-md-1">
|
||||||
|
|
||||||
|
|
File diff suppressed because one or more lines are too long
|
@ -1,10 +1,10 @@
|
||||||
{{{title}}}
|
{{{title}}}
|
||||||
Please Confirm Subscription
|
{{#translate}}Please Confirm Subscription{{/translate}}
|
||||||
===========================
|
===========================
|
||||||
|
|
||||||
Yes, subscribe me to this list: {{{confirmUrl}}}
|
{{#translate}}Yes, subscribe me to this list{{/translate}}: {{{confirmUrl}}}
|
||||||
|
|
||||||
If you received this email by mistake, simply delete it. You won't be subscribed unless you click the confirmation link above.
|
{{#translate}}If you received this email by mistake, simply delete it. You won't be subscribed unless you click the confirmation link above.{{/translate}}
|
||||||
|
|
||||||
For questions about this list, please contact:
|
{{#translate}}For questions about this list, please contact:{{/translate}}
|
||||||
{{{contactAddress}}}
|
{{{contactAddress}}}
|
||||||
|
|
File diff suppressed because one or more lines are too long
|
@ -1,9 +1,9 @@
|
||||||
{{{title}}}
|
{{{title}}}
|
||||||
Change your password
|
{{#translate}}Change your password{{/translate}}
|
||||||
====================
|
====================
|
||||||
|
|
||||||
We have received a password change request for your Mailtrain account ({{{username}}}).
|
{{#translate}}We have received a password change request for your Mailtrain account:{{/translate}} ({{{username}}}).
|
||||||
|
|
||||||
Reset password: {{{confirmUrl}}}
|
{{#translate}}Reset password{{/translate}}: {{{confirmUrl}}}
|
||||||
|
|
||||||
If you did not ask to change your password, then you can ignore this email and your password will not be changed.
|
{{#translate}}If you did not ask to change your password, then you can ignore this email and your password will not be changed.{{/translate}}
|
||||||
|
|
|
@ -12,11 +12,11 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<p>
|
<p>
|
||||||
<a href="[LINK_PREFERENCES]" style="color: #666666; text-decoration: none;">Preferences</a>
|
<a href="[LINK_PREFERENCES]" style="color: #666666; text-decoration: none;">{{#translate}}Preferences{{/translate}}</a>
|
||||||
<span style="color: #444444;"> | </span>
|
<span style="color: #444444;"> | </span>
|
||||||
<a href="[LINK_UNSUBSCRIBE]" style="color: #666666; text-decoration: none;">Unsubscribe</a>
|
<a href="[LINK_UNSUBSCRIBE]" style="color: #666666; text-decoration: none;">{{#translate}}Unsubscribe{{/translate}}</a>
|
||||||
<span style="color: #444444;"> | </span>
|
<span style="color: #444444;"> | </span>
|
||||||
<a href="[LINK_BROWSER]" style="color: #666666; text-decoration: none;">View this email in your browser</a>
|
<a href="[LINK_BROWSER]" style="color: #666666; text-decoration: none;">{{#translate}}View this email in your browser{{/translate}}</a>
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
</body>
|
</body>
|
||||||
|
|
|
@ -7,24 +7,24 @@
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
|
|
||||||
<p>Hey [FIRST_NAME/Customer],</p>
|
<p>{{#translate}}Hey [FIRST_NAME/Customer],{{/translate}}</p>
|
||||||
|
|
||||||
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed varius, leo a ullamcorper feugiat, ante purus sodales justo, a faucibus libero lacus a est.</p>
|
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed varius, leo a ullamcorper feugiat, ante purus sodales justo, a faucibus libero lacus a est.</p>
|
||||||
|
|
||||||
<p>Sed varius, leo a ullamcorper feugiat, ante purus sodales justo, a faucibus libero lacus a est. Aenean at mollis ipsum. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed varius, leo a ullamcorper feugiat, ante purus sodales justo, a faucibus
|
<p>Sed varius, leo a ullamcorper feugiat, ante purus sodales justo, a faucibus libero lacus a est. Aenean at mollis ipsum. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed varius, leo a ullamcorper feugiat, ante purus sodales justo, a faucibus
|
||||||
libero lacus a est.</p>
|
libero lacus a est.</p>
|
||||||
|
|
||||||
<p>Cheers,
|
<p>{{#translate}}Cheers,{{/translate}}
|
||||||
<br/> {{defaultSender}}
|
<br/> {{defaultSender}}
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
{{defaultPostaddress}}
|
{{defaultPostaddress}}
|
||||||
<br/>
|
<br/>
|
||||||
<a href="[LINK_PREFERENCES]" style="color: #666666; text-decoration: none;">Preferences</a>
|
<a href="[LINK_PREFERENCES]" style="color: #666666; text-decoration: none;">{{#translate}}Preferences{{/translate}}</a>
|
||||||
<span style="color: #444444;"> | </span>
|
<span style="color: #444444;"> | </span>
|
||||||
<a href="[LINK_UNSUBSCRIBE]" style="color: #666666; text-decoration: none;">Unsubscribe</a>
|
<a href="[LINK_UNSUBSCRIBE]" style="color: #666666; text-decoration: none;">{{#translate}}Unsubscribe{{/translate}}</a>
|
||||||
<span style="color: #444444;"> | </span>
|
<span style="color: #444444;"> | </span>
|
||||||
<a href="[LINK_BROWSER]" style="color: #666666; text-decoration: none;">View this email in your browser</a>
|
<a href="[LINK_BROWSER]" style="color: #666666; text-decoration: none;">{{#translate}}View this email in your browser{{/translate}}</a>
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
</body>
|
</body>
|
||||||
|
|
|
@ -1,14 +1,14 @@
|
||||||
Hey [FIRST_NAME/Customer],
|
{{#translate}}Hey [FIRST_NAME/Customer],{{/translate}}
|
||||||
|
|
||||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed varius, leo a ullamcorper feugiat, ante purus sodales justo, a faucibus libero lacus a est.
|
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed varius, leo a ullamcorper feugiat, ante purus sodales justo, a faucibus libero lacus a est.
|
||||||
|
|
||||||
Sed varius, leo a ullamcorper feugiat, ante purus sodales justo, a faucibus libero lacus a est. Aenean at mollis ipsum. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed varius, leo a ullamcorper feugiat, ante purus sodales justo, a faucibus libero lacus a est.
|
Sed varius, leo a ullamcorper feugiat, ante purus sodales justo, a faucibus libero lacus a est. Aenean at mollis ipsum. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed varius, leo a ullamcorper feugiat, ante purus sodales justo, a faucibus libero lacus a est.
|
||||||
|
|
||||||
Cheers,
|
{{#translate}}Cheers,{{/translate}}
|
||||||
{{defaultSender}}
|
{{defaultSender}}
|
||||||
|
|
||||||
{{defaultPostaddress}}
|
{{defaultPostaddress}}
|
||||||
|
|
||||||
Preferences: [LINK_PREFERENCES]
|
{{#translate}}Preferences{{/translate}}: [LINK_PREFERENCES]
|
||||||
Unsubscribe: [LINK_UNSUBSCRIBE]
|
{{#translate}}Unsubscribe{{/translate}}: [LINK_UNSUBSCRIBE]
|
||||||
View this email in your browser: [LINK_BROWSER]
|
{{#translate}}View this email in your browser{{/translate}}: [LINK_BROWSER]
|
||||||
|
|
File diff suppressed because one or more lines are too long
|
@ -1,16 +1,16 @@
|
||||||
{{{title}}}
|
{{{title}}}
|
||||||
Subscription Confirmed
|
{{#translate}}Subscription Confirmed{{/translate}}
|
||||||
======================
|
======================
|
||||||
|
|
||||||
Your subscription to our list has been confirmed.
|
{{#translate}}Your subscription to our list has been confirmed.{{/translate}}
|
||||||
|
|
||||||
If you want to modify your subscription then you can:
|
{{#translate}}If you want to modify your subscription then you can:{{/translate}}
|
||||||
|
|
||||||
manage your preferences: {{preferencesUrl}}
|
{{#translate}}manage your preferences{{/translate}}: {{preferencesUrl}}
|
||||||
|
|
||||||
- or -
|
- {{#translate}}or{{/translate}} -
|
||||||
|
|
||||||
unsubscribe here: {{unsubscribeUrl}}
|
{{#translate}}unsubscribe here{{/translate}}: {{unsubscribeUrl}}
|
||||||
|
|
||||||
For questions about this list, please contact:
|
{{#translate}}For questions about this list, please contact:{{/translate}}
|
||||||
{{{contactAddress}}}
|
{{{contactAddress}}}
|
||||||
|
|
File diff suppressed because one or more lines are too long
|
@ -1,12 +1,12 @@
|
||||||
{{{title}}}
|
{{{title}}}
|
||||||
You are now unsubscribed
|
{{#translate}}You are now unsubscribed{{/translate}}
|
||||||
========================
|
========================
|
||||||
|
|
||||||
We have removed your email address from our list.
|
{{#translate}}We have removed your email address from our list.{{/translate}}
|
||||||
|
|
||||||
If you unsubscribed by mistake, you can re-subscribe at:
|
{{#translate}}If you unsubscribed by mistake, you can re-subscribe at:{{/translate}}
|
||||||
|
|
||||||
Subscribe: {{subscribeUrl}}
|
{{#translate}}Subscribe{{/translate}}: {{subscribeUrl}}
|
||||||
|
|
||||||
For questions about this list, please contact:
|
{{#translate}}For questions about this list, please contact:{{/translate}}
|
||||||
{{{contactAddress}}}
|
{{{contactAddress}}}
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
<div class="media-body">
|
<div class="media-body">
|
||||||
<h4 class="media-heading"><a href="http://www.iredmail.org/">iRedMail</a></h4> Free, open source mail server solution
|
<h4 class="media-heading"><a href="http://www.iredmail.org/">iRedMail</a></h4> {{#translate}}Free, open source mail server solution{{/translate}}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -22,7 +22,7 @@
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
<div class="media-body">
|
<div class="media-body">
|
||||||
<h4 class="media-heading"><a href="https://sendpulse.com/?utm_source=mailtrain&utm_medium=logo">SendPulse</a></h4> A reliable SMTP server, easy integration, and 12,000 messages a month free
|
<h4 class="media-heading"><a href="https://sendpulse.com/?utm_source=mailtrain&utm_medium=logo">SendPulse</a></h4> {{#translate}}A reliable SMTP server, easy integration, and 12,000 messages a month free{{/translate}}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -31,17 +31,17 @@
|
||||||
|
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-md-4">
|
<div class="col-md-4">
|
||||||
<h2>List management</h2>
|
<h2>{{#translate}}List management{{/translate}}</h2>
|
||||||
<p>Mailtrain allows you to easily manage even very large lists. Million subscribers? Not a problem. You can add subscribers manually, through the API or import from a CSV file. All lists come with support for custom fields and merge tags as well.</p>
|
<p>{{#translate}}Mailtrain allows you to easily manage even very large lists. Million subscribers? Not a problem. You can add subscribers manually, through the API or import from a CSV file. All lists come with support for custom fields and merge tags as well.{{/translate}}</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-md-4">
|
<div class="col-md-4">
|
||||||
<h2>Custom fields</h2>
|
<h2>{{#translate}}Custom fields{{/translate}}</h2>
|
||||||
<p>Text fields, numbers, drop downs or checkboxes, Mailtrain has them all. Every custom field can be included in the generated newsletters through merge tags.</p>
|
<p>{{#translate}}Text fields, numbers, drop downs or checkboxes, Mailtrain has them all. Every custom field can be included in the generated newsletters through merge tags.{{/translate}}</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="col-md-4">
|
<div class="col-md-4">
|
||||||
<h2>List segmentation</h2>
|
<h2>{{#translate}}List segmentation{{/translate}}</h2>
|
||||||
<p>Send messages only to list subscribers that match predefined segmentation rules. No need to create separate lists with small differences.</p>
|
<p>{{#translate}}Send messages only to list subscribers that match predefined segmentation rules. No need to create separate lists with small differences.{{/translate}}</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@ -49,11 +49,11 @@
|
||||||
<div class="row">
|
<div class="row">
|
||||||
|
|
||||||
<div class="col-md-8">
|
<div class="col-md-8">
|
||||||
<h3>Donate to author</h3>
|
<h3>{{#translate}}Donate to author{{/translate}}</h3>
|
||||||
<p>If you really like Mailtrain or your business benefits from it financially then I would really appreciate a small donation to keep the Mailtrain development engines running. You can either use Bitcoin or PayPal for donations. My Bitcoin wallet is <code>15Z8ADxhssKUiwP3jbbqJwA21744KMCfTM</code></p>
|
<p>{{#translate}}If you really like Mailtrain or your business benefits from it financially then I would really appreciate a small donation to keep the Mailtrain development engines running. You can either use Bitcoin or PayPal for donations. My Bitcoin wallet is <code>15Z8ADxhssKUiwP3jbbqJwA21744KMCfTM</code>{{/translate}}</p>
|
||||||
|
|
||||||
<p>
|
<p>
|
||||||
<a href="https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=DB26KWR2BQX5W" class="btn btn-info">or donate using PayPal</a>
|
<a href="https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=DB26KWR2BQX5W" class="btn btn-info">{{#translate}}or donate using PayPal{{/translate}}</a>
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
@ -63,32 +63,31 @@
|
||||||
|
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-md-4">
|
<div class="col-md-4">
|
||||||
<h2>RSS Campaigns</h2>
|
<h2>{{#translate}}RSS Campaigns{{/translate}}</h2>
|
||||||
<p>Setup Mailtrain to <a href="https://github.com/andris9/mailtrain/wiki/RSS-Campaigns">track RSS feeds</a> and if a new entry is detected in a feed then Mailtrain auto-generates a new campaign using entry data as message contents and sends it to
|
<p>{{#translate}}Setup Mailtrain to track RSS feeds and if a new entry is detected in a feed then Mailtrain auto-generates a new campaign using entry data as message contents and sends it to selected subscribers.{{/translate}}</p>
|
||||||
selected subscribers.</p>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="col-md-4">
|
<div class="col-md-4">
|
||||||
<h2>GPG Encryption</h2>
|
<h2>{{#translate}}GPG Encryption{{/translate}}</h2>
|
||||||
<p>If a list has a custom field for a GPG Public Key set then subscribers can upload their GPG public key to receive <a href="https://github.com/andris9/mailtrain/wiki/Adding-GPG-encryption-option-to-a-list">encrypted messages</a> from the list.</p>
|
<p>{{#translate}}If a list has a custom field for a GPG Public Key set then subscribers can upload their GPG public key to receive encrypted messages from the list.{{/translate}}</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-md-4">
|
<div class="col-md-4">
|
||||||
<h2>Click stats</h2>
|
<h2>{{#translate}}Click stats{{/translate}}</h2>
|
||||||
<p>After a campaign is sent, check individual <a href="https://github.com/andris9/mailtrain/wiki/Clicks-and-opens-stats">click statistics</a> for every link included in the message.</p>
|
<p>{{#translate}}After a campaign is sent, check individual click statistics for every link included in the message.{{/translate}}</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-md-4">
|
<div class="col-md-4">
|
||||||
<h2>Open source</h2>
|
<h2>{{#translate}}Open source{{/translate}}</h2>
|
||||||
<p>Mailtrain is available under <a href="https://spdx.org/licenses/MIT.html#licenseText">MIT</a> license and completely open source.</p>
|
<p>{{#translate}}Mailtrain is available under GPLv3 license and completely open source.{{/translate}}</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-md-4">
|
<div class="col-md-4">
|
||||||
<h2>Send via any provider</h2>
|
<h2>{{#translate}}Send via any provider{{/translate}}</h2>
|
||||||
<p>Mailtrain recommends <a href="https://sendpulse.com/?utm_source=mailtrain&utm_medium=providerlist">SendPulse</a> even though you can use any provider that supports SMTP protocol to send out your newsletters. Bounce and complaints handling via webhooks is supported for SES, SparkPost, SendGrid and Mailgun, also for Postfix and ZoneMTA.</p>
|
<p>{{#translate}}Mailtrain recommends <a href="https://sendpulse.com/?utm_source=mailtrain&utm_medium=providerlist">SendPulse</a> even though you can use any provider that supports SMTP protocol to send out your newsletters. Bounce and complaints handling via webhooks is supported for SES, SparkPost, SendGrid and Mailgun, also for Postfix and ZoneMTA.{{/translate}}</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-md-4">
|
<div class="col-md-4">
|
||||||
<h2>Trigger based automation</h2>
|
<h2>{{#translate}}Trigger based automation{{/translate}}</h2>
|
||||||
<p>Define <a href="https://github.com/andris9/mailtrain/wiki/Automation-in-Mailtrain">automation triggers</a> to send specific messages when a user activates the trigger.</p>
|
<p>{{#translate}}Define automation triggers to send specific messages when a user activates the trigger.{{/translate}}</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
|
@ -34,7 +34,7 @@
|
||||||
<!-- Brand and toggle get grouped for better mobile display -->
|
<!-- Brand and toggle get grouped for better mobile display -->
|
||||||
<div class="navbar-header">
|
<div class="navbar-header">
|
||||||
<button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#bs-example-navbar-collapse-1" aria-expanded="false">
|
<button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#bs-example-navbar-collapse-1" aria-expanded="false">
|
||||||
<span class="sr-only">Toggle navigation</span>
|
<span class="sr-only">{{#translate}}Toggle navigation{{/translate}}</span>
|
||||||
<span class="icon-bar"></span>
|
<span class="icon-bar"></span>
|
||||||
<span class="icon-bar"></span>
|
<span class="icon-bar"></span>
|
||||||
<span class="icon-bar"></span>
|
<span class="icon-bar"></span>
|
||||||
|
@ -52,8 +52,8 @@
|
||||||
<li><a href="{{url}}">{{title}}</a></li>
|
<li><a href="{{url}}">{{title}}</a></li>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
{{/each}}
|
{{/each}}
|
||||||
<li><a href="https://github.com/andris9/mailtrain/wiki"><span class="glyphicon glyphicon-share-alt" aria-hidden="true"></span> Wiki</a></li>
|
<li><a href="https://github.com/andris9/mailtrain/wiki"><span class="glyphicon glyphicon-share-alt" aria-hidden="true"></span> {{#translate}}Wiki{{/translate}}</a></li>
|
||||||
<li><a href="https://mailtrain.wordpress.com/"><span class="glyphicon glyphicon-share-alt" aria-hidden="true"></span> Blog</a></li>
|
<li><a href="https://mailtrain.wordpress.com/"><span class="glyphicon glyphicon-share-alt" aria-hidden="true"></span> {{#translate}}Blog{{/translate}}</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
{{#if user }}
|
{{#if user }}
|
||||||
|
@ -66,22 +66,22 @@
|
||||||
<ul class="dropdown-menu">
|
<ul class="dropdown-menu">
|
||||||
<li>
|
<li>
|
||||||
<a href="/users/account">
|
<a href="/users/account">
|
||||||
<span class="glyphicon glyphicon-user" aria-hidden="true"></span> Account
|
<span class="glyphicon glyphicon-user" aria-hidden="true"></span> {{#translate}}Account{{/translate}}
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<a href="/settings">
|
<a href="/settings">
|
||||||
<span class="glyphicon glyphicon-cog" aria-hidden="true"></span> Settings
|
<span class="glyphicon glyphicon-cog" aria-hidden="true"></span> {{#translate}}Settings{{/translate}}
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<a href="/users/api">
|
<a href="/users/api">
|
||||||
<span class="glyphicon glyphicon-retweet" aria-hidden="true"></span> API
|
<span class="glyphicon glyphicon-retweet" aria-hidden="true"></span> {{#translate}}API{{/translate}}
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<a href="/users/logout">
|
<a href="/users/logout">
|
||||||
<span class="glyphicon glyphicon-log-out" aria-hidden="true"></span> Log out
|
<span class="glyphicon glyphicon-log-out" aria-hidden="true"></span> {{#translate}}Log out{{/translate}}
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
@ -93,7 +93,7 @@
|
||||||
<ul class="nav navbar-nav navbar-right">
|
<ul class="nav navbar-nav navbar-right">
|
||||||
<li>
|
<li>
|
||||||
<a href="/users/login" role="button" aria-haspopup="true" aria-expanded="false">
|
<a href="/users/login" role="button" aria-haspopup="true" aria-expanded="false">
|
||||||
<span class="glyphicon glyphicon-log-in" aria-hidden="true"></span> Sign in
|
<span class="glyphicon glyphicon-log-in" aria-hidden="true"></span> {{#translate}}Sign in{{/translate}}
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
@ -114,11 +114,11 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<h1><img class="img-responsive" src="/mailtrain-header.png"></h1>
|
<h1><img class="img-responsive" src="/mailtrain-header.png"></h1>
|
||||||
<p>Self hosted newsletter app built on top of <a href="http://nodemailer.com">Nodemailer</a></p>
|
<p>{{#translate}}Self hosted newsletter app built on top of Nodemailer{{/translate}}</p>
|
||||||
<p>
|
<p>
|
||||||
<a class="btn btn-info btn-md" href="https://github.com/andris9/mailtrain" role="button"><span class="glyphicon glyphicon-cloud-download" aria-hidden="true"></span> Source on GitHub</a>
|
<a class="btn btn-info btn-md" href="https://github.com/andris9/mailtrain" role="button"><span class="glyphicon glyphicon-cloud-download" aria-hidden="true"></span> {{#translate}}Source on GitHub{{/translate}}</a>
|
||||||
|
|
||||||
<a class="btn btn-success btn-md" href="http://mailtrain.org/subscription/EysIv8sAx" role="button"><span class="glyphicon glyphicon-envelope" aria-hidden="true"></span> Subscribe to our newsletter</a>
|
<a class="btn btn-success btn-md" href="http://mailtrain.org/subscription/EysIv8sAx" role="button"><span class="glyphicon glyphicon-envelope" aria-hidden="true"></span> {{#translate}}Subscribe to our newsletter{{/translate}}</a>
|
||||||
</p>
|
</p>
|
||||||
<div class="clearfix"></div>
|
<div class="clearfix"></div>
|
||||||
|
|
||||||
|
@ -134,7 +134,7 @@
|
||||||
|
|
||||||
<footer class="footer">
|
<footer class="footer">
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<p class="text-muted">© 2016 Kreata OÜ <a href="https://mailtrain.org">Mailtrain.org</a>, <a href="mailto:info@mailtrain.org">info@mailtrain.org</a>. Source on <a href="https://github.com/andris9/mailtrain">GitHub</a></p>
|
<p class="text-muted">© 2016 Kreata OÜ <a href="https://mailtrain.org">Mailtrain.org</a>, <a href="mailto:info@mailtrain.org">info@mailtrain.org</a>. <a href="https://github.com/andris9/mailtrain">{{#translate}}Source on GitHub{{/translate}}</a></p>
|
||||||
</div>
|
</div>
|
||||||
</footer>
|
</footer>
|
||||||
|
|
||||||
|
|
|
@ -1,26 +1,26 @@
|
||||||
<ol class="breadcrumb">
|
<ol class="breadcrumb">
|
||||||
<li><a href="/">Home</a></li>
|
<li><a href="/">{{#translate}}Home{{/translate}}</a></li>
|
||||||
<li><a href="/lists/">Lists</a></li>
|
<li><a href="/lists/">{{#translate}}Lists{{/translate}}</a></li>
|
||||||
<li class="active">Create List</li>
|
<li class="active">{{#translate}}Create List{{/translate}}</li>
|
||||||
</ol>
|
</ol>
|
||||||
|
|
||||||
<h2>Create List</h2>
|
<h2>{{#translate}}Create List{{/translate}}</h2>
|
||||||
|
|
||||||
<hr>
|
<hr>
|
||||||
|
|
||||||
<form class="form-horizontal" method="post" action="/lists/create">
|
<form class="form-horizontal" method="post" action="/lists/create">
|
||||||
<input type="hidden" name="_csrf" value="{{csrfToken}}">
|
<input type="hidden" name="_csrf" value="{{csrfToken}}">
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="name" class="col-sm-2 control-label">Name</label>
|
<label for="name" class="col-sm-2 control-label">{{#translate}}Name{{/translate}}</label>
|
||||||
<div class="col-sm-10">
|
<div class="col-sm-10">
|
||||||
<input type="text" class="form-control input-lg" name="name" id="name" value="{{name}}" placeholder="List Name" autofocus required>
|
<input type="text" class="form-control input-lg" name="name" id="name" value="{{name}}" placeholder="{{#translate}}List Name{{/translate}}" autofocus required>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="description" class="col-sm-2 control-label">Description</label>
|
<label for="description" class="col-sm-2 control-label">{{#translate}}Description{{/translate}}</label>
|
||||||
<div class="col-sm-10">
|
<div class="col-sm-10">
|
||||||
<textarea class="form-control" rows="3" name="description" id="description">{{description}}</textarea>
|
<textarea class="form-control" rows="3" name="description" id="description">{{description}}</textarea>
|
||||||
<span class="help-block">HTML is allowed</span>
|
<span class="help-block">{{#translate}}HTML is allowed{{/translate}}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@ -28,7 +28,7 @@
|
||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<div class="col-sm-offset-2 col-sm-10">
|
<div class="col-sm-offset-2 col-sm-10">
|
||||||
<button type="submit" class="btn btn-primary"><i class="glyphicon glyphicon-plus"></i> Create List</button>
|
<button type="submit" class="btn btn-primary"><i class="glyphicon glyphicon-plus"></i> {{#translate}}Create List{{/translate}}</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
<ol class="breadcrumb">
|
<ol class="breadcrumb">
|
||||||
<li><a href="/">Home</a></li>
|
<li><a href="/">{{#translate}}Home{{/translate}}</a></li>
|
||||||
<li><a href="/lists/">Lists</a></li>
|
<li><a href="/lists/">{{#translate}}Lists{{/translate}}</a></li>
|
||||||
<li><a href="/lists/view/{{id}}">{{name}}</a></li>
|
<li><a href="/lists/view/{{id}}">{{name}}</a></li>
|
||||||
<li class="active">Edit List</li>
|
<li class="active">{{#translate}}Edit List{{/translate}}</li>
|
||||||
</ol>
|
</ol>
|
||||||
|
|
||||||
<h2>Edit List <a class="btn btn-default btn-xs" href="/lists/view/{{id}}" role="button"><span class="glyphicon glyphicon-arrow-left" aria-hidden="true"></span> View List</a></h2>
|
<h2>{{#translate}}Edit List{{/translate}} <a class="btn btn-default btn-xs" href="/lists/view/{{id}}" role="button"><span class="glyphicon glyphicon-arrow-left" aria-hidden="true"></span> {{#translate}}View List{{/translate}}</a></h2>
|
||||||
|
|
||||||
<hr>
|
<hr>
|
||||||
|
|
||||||
|
@ -18,23 +18,23 @@
|
||||||
<input type="hidden" name="_csrf" value="{{csrfToken}}">
|
<input type="hidden" name="_csrf" value="{{csrfToken}}">
|
||||||
<input type="hidden" name="id" value="{{id}}" />
|
<input type="hidden" name="id" value="{{id}}" />
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="name" class="col-sm-2 control-label">Name</label>
|
<label for="name" class="col-sm-2 control-label">{{#translate}}Name{{/translate}}</label>
|
||||||
<div class="col-sm-10">
|
<div class="col-sm-10">
|
||||||
<input type="text" class="form-control input-lg" name="name" id="name" value="{{name}}" placeholder="List Name" autofocus required>
|
<input type="text" class="form-control input-lg" name="name" id="name" value="{{name}}" placeholder="{{#translate}}List Name{{/translate}}" autofocus required>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="name" class="col-sm-2 control-label">List ID</label>
|
<label for="name" class="col-sm-2 control-label">{{#translate}}List ID{{/translate}}</label>
|
||||||
<div class="col-sm-10">
|
<div class="col-sm-10">
|
||||||
<input type="text" class="form-control" id="cid" value="{{cid}}" readonly>
|
<input type="text" class="form-control" id="cid" value="{{cid}}" readonly>
|
||||||
<span class="help-block">This is the list ID displayed to the subscribers</span>
|
<span class="help-block">{{#translate}}This is the list ID displayed to the subscribers{{/translate}}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="description" class="col-sm-2 control-label">Description</label>
|
<label for="description" class="col-sm-2 control-label">{{#translate}}Description{{/translate}}</label>
|
||||||
<div class="col-sm-10">
|
<div class="col-sm-10">
|
||||||
<textarea class="form-control" rows="3" name="description" id="description">{{description}}</textarea>
|
<textarea class="form-control" rows="3" name="description" id="description">{{description}}</textarea>
|
||||||
<span class="help-block">HTML is allowed</span>
|
<span class="help-block">{{#translate}}HTML is allowed{{/translate}}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@ -43,9 +43,9 @@
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<div class="col-sm-offset-2 col-sm-10">
|
<div class="col-sm-offset-2 col-sm-10">
|
||||||
<div class="pull-right">
|
<div class="pull-right">
|
||||||
<button type="submit" form="lists-delete" class="btn btn-danger"><i class="glyphicon glyphicon-remove"></i> Delete List</button>
|
<button type="submit" form="lists-delete" class="btn btn-danger"><i class="glyphicon glyphicon-remove"></i> {{#translate}}Delete List{{/translate}}</button>
|
||||||
</div>
|
</div>
|
||||||
<button type="submit" class="btn btn-primary"><i class="glyphicon glyphicon-ok"></i> Update</button>
|
<button type="submit" class="btn btn-primary"><i class="glyphicon glyphicon-ok"></i> {{#translate}}Update{{/translate}}</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
|
|
|
@ -1,77 +1,77 @@
|
||||||
<ol class="breadcrumb">
|
<ol class="breadcrumb">
|
||||||
<li><a href="/">Home</a></li>
|
<li><a href="/">{{#translate}}Home{{/translate}}</a></li>
|
||||||
<li><a href="/lists/">Lists</a></li>
|
<li><a href="/lists/">{{#translate}}Lists{{/translate}}</a></li>
|
||||||
<li><a href="/lists/view/{{list.id}}">{{list.name}}</a></li>
|
<li><a href="/lists/view/{{list.id}}">{{list.name}}</a></li>
|
||||||
<li><a href="/fields/{{list.id}}">Custom Fields</a></li>
|
<li><a href="/fields/{{list.id}}">{{#translate}}Custom Fields{{/translate}}</a></li>
|
||||||
<li class="active">Create Field</li>
|
<li class="active">{{#translate}}Create Field{{/translate}}</li>
|
||||||
</ol>
|
</ol>
|
||||||
|
|
||||||
<h2>{{list.name}} <small>Create Custom Field</small></h2>
|
<h2>{{list.name}} <small>{{#translate}}Create Custom Field{{/translate}}</small></h2>
|
||||||
|
|
||||||
<hr>
|
<hr>
|
||||||
|
|
||||||
<form class="form-horizontal" method="post" action="/fields/{{list.id}}/create">
|
<form class="form-horizontal" method="post" action="/fields/{{list.id}}/create">
|
||||||
<input type="hidden" name="_csrf" value="{{csrfToken}}">
|
<input type="hidden" name="_csrf" value="{{csrfToken}}">
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="name" class="col-sm-2 control-label">Field Name</label>
|
<label for="name" class="col-sm-2 control-label">{{#translate}}Field Name{{/translate}}</label>
|
||||||
<div class="col-sm-10">
|
<div class="col-sm-10">
|
||||||
<input type="text" class="form-control input-lg" name="name" id="name" value="{{name}}" placeholder="Field Name" required>
|
<input type="text" class="form-control input-lg" name="name" id="name" value="{{name}}" placeholder="{{#translate}}Field Name{{/translate}}" required>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="type" class="col-sm-2 control-label">Field Type</label>
|
<label for="type" class="col-sm-2 control-label">{{#translate}}Field Type{{/translate}}</label>
|
||||||
<div class="col-sm-10">
|
<div class="col-sm-10">
|
||||||
<select class="form-control" name="type">
|
<select class="form-control" name="type">
|
||||||
<option value="text" {{#if selectedText}} selected {{/if}}>Text</option>
|
<option value="text" {{#if selectedText}} selected {{/if}}>{{#translate}}Text{{/translate}}</option>
|
||||||
<option value="number" {{#if selectedNumber}} selected {{/if}}>Number</option>
|
<option value="number" {{#if selectedNumber}} selected {{/if}}>{{#translate}}Number{{/translate}}</option>
|
||||||
<option value="website" {{#if selectedWebsite}} selected {{/if}}>Website</option>
|
<option value="website" {{#if selectedWebsite}} selected {{/if}}>{{#translate}}Website{{/translate}}</option>
|
||||||
<option value="gpg" {{#if selectedGpg}} selected {{/if}}>GPG Public Key</option>
|
<option value="gpg" {{#if selectedGpg}} selected {{/if}}>{{#translate}}GPG Public Key{{/translate}}</option>
|
||||||
<option value="longtext" {{#if selectedLongtext}} selected {{/if}}>Multi-line text</option>
|
<option value="longtext" {{#if selectedLongtext}} selected {{/if}}>{{#translate}}Multi-line text{{/translate}}</option>
|
||||||
<option value="json" {{#if selectedJson}} selected {{/if}}>JSON</option>
|
<option value="json" {{#if selectedJson}} selected {{/if}}>{{#translate}}JSON{{/translate}}</option>
|
||||||
<optgroup label="Date">
|
<optgroup label="{{#translate}}Date{{/translate}}">
|
||||||
<option value="date-us" {{#if selectedDateUs}} selected {{/if}}>Date (MM/DD/YYYY)</option>
|
<option value="date-us" {{#if selectedDateUs}} selected {{/if}}>{{#translate}}Date (MM/DD/YYYY){{/translate}}</option>
|
||||||
<option value="date-eur" {{#if selectedDateEur}} selected {{/if}}>Date (DD/MM/YYYY)</option>
|
<option value="date-eur" {{#if selectedDateEur}} selected {{/if}}>{{#translate}}Date (DD/MM/YYYY){{/translate}}</option>
|
||||||
</optgroup>
|
</optgroup>
|
||||||
<optgroup label="Birthday">
|
<optgroup label="{{#translate}}Birthday{{/translate}}">
|
||||||
<option value="birthday-us" {{#if selectedBirthdayUs}} selected {{/if}}>Birthday (MM/DD)</option>
|
<option value="birthday-us" {{#if selectedBirthdayUs}} selected {{/if}}>{{#translate}}Birthday (MM/DD){{/translate}}</option>
|
||||||
<option value="birthday-eur" {{#if selectedBirthdayEur}} selected {{/if}}>Birthday (DD/MM)</option>
|
<option value="birthday-eur" {{#if selectedBirthdayEur}} selected {{/if}}>{{#translate}}Birthday (DD/MM){{/translate}}</option>
|
||||||
</optgroup>
|
</optgroup>
|
||||||
<optgroup label="Grouped">
|
<optgroup label="{{#translate}}Grouped{{/translate}}">
|
||||||
<option value="dropdown" {{#if selectedDropdown}} selected {{/if}}>Drop Downs</option>
|
<option value="dropdown" {{#if selectedDropdown}} selected {{/if}}>{{#translate}}Drop Downs{{/translate}}</option>
|
||||||
<option value="radio" {{#if selectedRadio}} selected {{/if}}>Radio Buttons</option>
|
<option value="radio" {{#if selectedRadio}} selected {{/if}}>{{#translate}}Radio Buttons{{/translate}}</option>
|
||||||
<option value="checkbox" {{#if selectedCheckbox}} selected {{/if}}>Checkboxes</option>
|
<option value="checkbox" {{#if selectedCheckbox}} selected {{/if}}>{{#translate}}Checkboxes{{/translate}}</option>
|
||||||
</optgroup>
|
</optgroup>
|
||||||
<option value="option" {{#if selectedOption}} selected {{/if}}>Option for a group value</option>
|
<option value="option" {{#if selectedOption}} selected {{/if}}>{{#translate}}Option for a group value{{/translate}}</option>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="group" class="col-sm-2 control-label">Group</label>
|
<label for="group" class="col-sm-2 control-label">{{#translate}}Group{{/translate}}</label>
|
||||||
<div class="col-sm-10">
|
<div class="col-sm-10">
|
||||||
<select class="form-control" name="group">
|
<select class="form-control" name="group">
|
||||||
<option value=""> –– Select ––</option>
|
<option value=""> –– {{#translate}}Select{{/translate}} ––</option>
|
||||||
{{#each groups}}
|
{{#each groups}}
|
||||||
<option value="{{id}}" {{#if selected}} selected {{/if}}>{{name}}</option>
|
<option value="{{id}}" {{#if selected}} selected {{/if}}>{{name}}</option>
|
||||||
{{/each}}
|
{{/each}}
|
||||||
</select>
|
</select>
|
||||||
<span class="help-block">Required for group options</span>
|
<span class="help-block">{{#translate}}Required for group options{{/translate}}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="default-value" class="col-sm-2 control-label">Default merge tag value</label>
|
<label for="default-value" class="col-sm-2 control-label">{{#translate}}Default merge tag value{{/translate}}</label>
|
||||||
<div class="col-sm-10">
|
<div class="col-sm-10">
|
||||||
<input type="text" class="form-control" name="default-value" id="default-value" value="{{field.defaultValue}}" placeholder="Default merge tag value">
|
<input type="text" class="form-control" name="default-value" id="default-value" value="{{field.defaultValue}}" placeholder="{{#translate}}Default merge tag value{{/translate}}">
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="group-template" class="col-sm-2 control-label">Template</label>
|
<label for="group-template" class="col-sm-2 control-label">{{#translate}}Template{{/translate}}</label>
|
||||||
<div class="col-sm-10">
|
<div class="col-sm-10">
|
||||||
<textarea class="form-control gpg-text" rows="3" name="group-template" id="group-template">{{field.groupTemplate}}</textarea>
|
<textarea class="form-control gpg-text" rows="3" name="group-template" id="group-template">{{field.groupTemplate}}</textarea>
|
||||||
<span class="help-block">For group elements like checkboxes you can control the appearance of the merge tag with an optional template. The template uses handlebars syntax and you can find all values from <code>\{{values}}</code> array, for example <code>\{{#each values}} \{{this}} \{{/each}}</code>. If template is not defined then multiple values are joined with commas. You can also use this template to render JSON values (if the JSON is an array then the array is exposed as <code>values</code>, otherwise you can access the JSON keys directly).</span>
|
<span class="help-block">{{#translate}}For group elements like checkboxes you can control the appearance of the merge tag with an optional template. The template uses handlebars syntax and you can find all values from <code>\{{values}}</code> array, for example <code>\{{#each values}} \{{this}} \{{/each}}</code>. If template is not defined then multiple values are joined with commas. You can also use this template to render JSON values (if the JSON is an array then the array is exposed as <code>values</code>, otherwise you can access the JSON keys directly).{{/translate}}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@ -79,7 +79,7 @@
|
||||||
<div class="col-sm-offset-2 col-xs-4">
|
<div class="col-sm-offset-2 col-xs-4">
|
||||||
<div class="checkbox">
|
<div class="checkbox">
|
||||||
<label>
|
<label>
|
||||||
<input type="checkbox" name="visible" {{#if visible}} checked {{/if}}> Visible
|
<input type="checkbox" name="visible" {{#if visible}} checked {{/if}}> {{#translate}}Visible{{/translate}}
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -87,7 +87,7 @@
|
||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<div class="col-sm-offset-2 col-sm-10">
|
<div class="col-sm-offset-2 col-sm-10">
|
||||||
<button type="submit" class="btn btn-primary"><i class="glyphicon glyphicon-plus"></i> Add Field</button>
|
<button type="submit" class="btn btn-primary"><i class="glyphicon glyphicon-plus"></i> {{#translate}}Add Field{{/translate}}</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
|
|
|
@ -1,12 +1,12 @@
|
||||||
<ol class="breadcrumb">
|
<ol class="breadcrumb">
|
||||||
<li><a href="/">Home</a></li>
|
<li><a href="/">{{#translate}}Home{{/translate}}</a></li>
|
||||||
<li><a href="/lists/">Lists</a></li>
|
<li><a href="/lists/">{{#translate}}Lists{{/translate}}</a></li>
|
||||||
<li><a href="/lists/view/{{list.id}}">{{list.name}}</a></li>
|
<li><a href="/lists/view/{{list.id}}">{{list.name}}</a></li>
|
||||||
<li><a href="/fields/{{list.id}}">Custom Fields</a></li>
|
<li><a href="/fields/{{list.id}}">{{#translate}}Custom Fields{{/translate}}</a></li>
|
||||||
<li class="active">Edit Field</li>
|
<li class="active">{{#translate}}Edit Field{{/translate}}</li>
|
||||||
</ol>
|
</ol>
|
||||||
|
|
||||||
<h2>{{list.name}} <small>Edit Custom Field</small> <a class="btn btn-default btn-xs" href="/fields/{{list.id}}" role="button"><span class="glyphicon glyphicon-arrow-left" aria-hidden="true"></span> Back to fields</a></h2>
|
<h2>{{list.name}} <small>{{#translate}}Edit Custom Field{{/translate}}</small> <a class="btn btn-default btn-xs" href="/fields/{{list.id}}" role="button"><span class="glyphicon glyphicon-arrow-left" aria-hidden="true"></span> {{#translate}}Back to fields{{/translate}}</a></h2>
|
||||||
|
|
||||||
<hr>
|
<hr>
|
||||||
|
|
||||||
|
@ -20,36 +20,36 @@
|
||||||
<input type="hidden" name="id" value="{{field.id}}" />
|
<input type="hidden" name="id" value="{{field.id}}" />
|
||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="name" class="col-sm-2 control-label">Field Name</label>
|
<label for="name" class="col-sm-2 control-label">{{#translate}}Field Name{{/translate}}</label>
|
||||||
<div class="col-sm-10">
|
<div class="col-sm-10">
|
||||||
<input type="text" class="form-control input-lg" name="name" id="name" value="{{field.name}}" placeholder="Field Name" required>
|
<input type="text" class="form-control input-lg" name="name" id="name" value="{{field.name}}" placeholder="{{#translate}}Field Name{{/translate}}" required>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="type" class="col-sm-2 control-label">Field Type</label>
|
<label for="type" class="col-sm-2 control-label">{{#translate}}Field Type{{/translate}}</label>
|
||||||
<div class="col-sm-10">
|
<div class="col-sm-10">
|
||||||
<select class="form-control" disabled>
|
<select class="form-control" disabled>
|
||||||
<option value="text" {{#if selectedText}} selected {{/if}}>Text</option>
|
<option value="text" {{#if selectedText}} selected {{/if}}>{{#translate}}Text{{/translate}}</option>
|
||||||
<option value="number" {{#if selectedNumber}} selected {{/if}}>Number</option>
|
<option value="number" {{#if selectedNumber}} selected {{/if}}>{{#translate}}Number{{/translate}}</option>
|
||||||
<option value="website" {{#if selectedWebsite}} selected {{/if}}>Website</option>
|
<option value="website" {{#if selectedWebsite}} selected {{/if}}>{{#translate}}Website{{/translate}}</option>
|
||||||
<option value="gpg" {{#if selectedGpg}} selected {{/if}}>GPG Public Key</option>
|
<option value="gpg" {{#if selectedGpg}} selected {{/if}}>{{#translate}}GPG Public Key{{/translate}}</option>
|
||||||
<option value="longtext" {{#if selectedLongtext}} selected {{/if}}>Multi-line text</option>
|
<option value="longtext" {{#if selectedLongtext}} selected {{/if}}>{{#translate}}Multi-line text{{/translate}}</option>
|
||||||
<option value="json" {{#if selectedJson}} selected {{/if}}>JSON</option>
|
<option value="json" {{#if selectedJson}} selected {{/if}}>{{#translate}}JSON{{/translate}}</option>
|
||||||
<optgroup label="Date">
|
<optgroup label="{{#translate}}Date{{/translate}}">
|
||||||
<option value="date-us" {{#if selectedDateUs}} selected {{/if}}>Date (MM/DD/YYYY)</option>
|
<option value="date-us" {{#if selectedDateUs}} selected {{/if}}>{{#translate}}Date (MM/DD/YYYY){{/translate}}</option>
|
||||||
<option value="date-eur" {{#if selectedDateEur}} selected {{/if}}>Date (DD/MM/YYYY)</option>
|
<option value="date-eur" {{#if selectedDateEur}} selected {{/if}}>{{#translate}}Date (DD/MM/YYYY){{/translate}}</option>
|
||||||
</optgroup>
|
</optgroup>
|
||||||
<optgroup label="Birthday">
|
<optgroup label="{{#translate}}Birthday{{/translate}}">
|
||||||
<option value="birthday-us" {{#if selectedBirthdayUs}} selected {{/if}}>Birthday (MM/DD)</option>
|
<option value="birthday-us" {{#if selectedBirthdayUs}} selected {{/if}}>{{#translate}}Birthday (MM/DD){{/translate}}</option>
|
||||||
<option value="birthday-eur" {{#if selectedBirthdayEur}} selected {{/if}}>Birthday (DD/MM)</option>
|
<option value="birthday-eur" {{#if selectedBirthdayEur}} selected {{/if}}>{{#translate}}Birthday (DD/MM){{/translate}}</option>
|
||||||
</optgroup>
|
</optgroup>
|
||||||
<optgroup label="Grouped">
|
<optgroup label="{{#translate}}Grouped{{/translate}}">
|
||||||
<option value="dropdown" {{#if selectedDropdown}} selected {{/if}}>Drop Downs</option>
|
<option value="dropdown" {{#if selectedDropdown}} selected {{/if}}>{{#translate}}Drop Downs{{/translate}}</option>
|
||||||
<option value="radio" {{#if selectedRadio}} selected {{/if}}>Radio Buttons</option>
|
<option value="radio" {{#if selectedRadio}} selected {{/if}}>{{#translate}}Radio Buttons{{/translate}}</option>
|
||||||
<option value="checkbox" {{#if selectedCheckbox}} selected {{/if}}>Checkboxes</option>
|
<option value="checkbox" {{#if selectedCheckbox}} selected {{/if}}>{{#translate}}Checkboxes{{/translate}}</option>
|
||||||
</optgroup>
|
</optgroup>
|
||||||
<option value="option" {{#if selectedOption}} selected {{/if}}>Option for a group value</option>
|
<option value="option" {{#if selectedOption}} selected {{/if}}>{{#translate}}Option for a group value{{/translate}}</option>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -57,43 +57,43 @@
|
||||||
{{#if groups}}
|
{{#if groups}}
|
||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="group" class="col-sm-2 control-label">Group</label>
|
<label for="group" class="col-sm-2 control-label">{{#translate}}Group{{/translate}}</label>
|
||||||
<div class="col-sm-10">
|
<div class="col-sm-10">
|
||||||
<select class="form-control" name="group">
|
<select class="form-control" name="group">
|
||||||
<option value=""> –– Select ––</option>
|
<option value=""> –– {{#translate}}Select{{/translate}} ––</option>
|
||||||
{{#each groups}}
|
{{#each groups}}
|
||||||
<option value="{{id}}" {{#if selected}} selected {{/if}}>{{name}}</option>
|
<option value="{{id}}" {{#if selected}} selected {{/if}}>{{name}}</option>
|
||||||
{{/each}}
|
{{/each}}
|
||||||
</select>
|
</select>
|
||||||
<span class="help-block">Required for group options</span>
|
<span class="help-block">{{#translate}}Required for group options{{/translate}}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{{/if}}
|
{{/if}}
|
||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="key" class="col-sm-2 control-label">Merge tag</label>
|
<label for="key" class="col-sm-2 control-label">{{#translate}}Merge tag{{/translate}}</label>
|
||||||
<div class="col-sm-10">
|
<div class="col-sm-10">
|
||||||
<input type="text" class="form-control text-uppercase" name="key" id="key" value="{{field.key}}" placeholder="Merge Tag">
|
<input type="text" class="form-control text-uppercase" name="key" id="key" value="{{field.key}}" placeholder="{{#translate}}Merge Tag{{/translate}}">
|
||||||
<span class="help-block">Put this tag in your content: <strong>[{{#if field.key}}{{field.key}}{{else}}TAG_VALUE{{/if}}]</strong></span>
|
<span class="help-block">{{#translate}}Put this tag in your content:{{/translate}} <strong>[{{#if field.key}}{{field.key}}{{else}}TAG_VALUE{{/if}}]</strong></span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{{#if field.isGroup}}
|
{{#if field.isGroup}}
|
||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="group-template" class="col-sm-2 control-label">Template</label>
|
<label for="group-template" class="col-sm-2 control-label">{{#translate}}Template{{/translate}}</label>
|
||||||
<div class="col-sm-10">
|
<div class="col-sm-10">
|
||||||
<textarea class="form-control gpg-text" rows="3" name="group-template" id="description">{{field.groupTemplate}}</textarea>
|
<textarea class="form-control gpg-text" rows="3" name="group-template" id="description">{{field.groupTemplate}}</textarea>
|
||||||
<span class="help-block">For group elements like checkboxes you can control the appearance of the merge tag with an optional template. The template uses handlebars syntax and you can find all values from <code>\{{values}}</code> array, for example <code>\{{#each values}} \{{this}} \{{/each}}</code>. If template is not defined then multiple values are joined with commas. You can also use this template to render JSON values (if the JSON is an array then the array is exposed as <code>values</code>, otherwise you can access the JSON keys directly).</span>
|
<span class="help-block">{{#translate}}For group elements like checkboxes you can control the appearance of the merge tag with an optional template. The template uses handlebars syntax and you can find all values from <code>\{{values}}</code> array, for example <code>\{{#each values}} \{{this}} \{{/each}}</code>. If template is not defined then multiple values are joined with commas. You can also use this template to render JSON values (if the JSON is an array then the array is exposed as <code>values</code>, otherwise you can access the JSON keys directly).{{/translate}}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{{else}}
|
{{else}}
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="default-value" class="col-sm-2 control-label">Default merge tag value</label>
|
<label for="default-value" class="col-sm-2 control-label">{{#translate}}Default merge tag value{{/translate}}</label>
|
||||||
<div class="col-sm-10">
|
<div class="col-sm-10">
|
||||||
<input type="text" class="form-control" name="default-value" id="default-value" value="{{field.defaultValue}}" placeholder="Default merge tag value">
|
<input type="text" class="form-control" name="default-value" id="default-value" value="{{field.defaultValue}}" placeholder="{{#translate}}Default merge tag value{{/translate}}">
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
|
@ -102,7 +102,7 @@
|
||||||
<div class="col-sm-offset-2 col-xs-4">
|
<div class="col-sm-offset-2 col-xs-4">
|
||||||
<div class="checkbox">
|
<div class="checkbox">
|
||||||
<label>
|
<label>
|
||||||
<input type="checkbox" name="visible" {{#if field.visible}} checked {{/if}}> Visible
|
<input type="checkbox" name="visible" {{#if field.visible}} checked {{/if}}> {{#translate}}Visible{{/translate}}
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -111,9 +111,9 @@
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<div class="col-sm-offset-2 col-sm-10">
|
<div class="col-sm-offset-2 col-sm-10">
|
||||||
<div class="pull-right">
|
<div class="pull-right">
|
||||||
<button type="submit" form="fields-delete" class="btn btn-danger"><i class="glyphicon glyphicon-remove"></i> Delete Field</button>
|
<button type="submit" form="fields-delete" class="btn btn-danger"><i class="glyphicon glyphicon-remove"></i> {{#translate}}Delete Field{{/translate}}</button>
|
||||||
</div>
|
</div>
|
||||||
<button type="submit" class="btn btn-primary"><i class="glyphicon glyphicon-ok"></i> Update</button>
|
<button type="submit" class="btn btn-primary"><i class="glyphicon glyphicon-ok"></i> {{#translate}}Update{{/translate}}</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
|
|
|
@ -1,15 +1,15 @@
|
||||||
<ol class="breadcrumb">
|
<ol class="breadcrumb">
|
||||||
<li><a href="/">Home</a></li>
|
<li><a href="/">{{#translate}}Home{{/translate}}</a></li>
|
||||||
<li><a href="/lists/">Lists</a></li>
|
<li><a href="/lists/">{{#translate}}Lists{{/translate}}</a></li>
|
||||||
<li><a href="/lists/view/{{list.id}}">{{list.name}}</a></li>
|
<li><a href="/lists/view/{{list.id}}">{{list.name}}</a></li>
|
||||||
<li class="active">Custom Fields</li>
|
<li class="active">{{#translate}}Custom Fields{{/translate}}</li>
|
||||||
</ol>
|
</ol>
|
||||||
|
|
||||||
<div class="pull-right">
|
<div class="pull-right">
|
||||||
<a class="btn btn-primary" href="/fields/{{list.id}}/create" role="button"><i class="glyphicon glyphicon-plus"></i> Create Custom Field</a>
|
<a class="btn btn-primary" href="/fields/{{list.id}}/create" role="button"><i class="glyphicon glyphicon-plus"></i> {{#translate}}Create Custom Field{{/translate}}</a>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<h2>{{list.name}} <small>Custom Fields</small></h2>
|
<h2>{{list.name}} <small>{{#translate}}Custom Fields{{/translate}}</small></h2>
|
||||||
|
|
||||||
<hr>
|
<hr>
|
||||||
|
|
||||||
|
@ -20,16 +20,16 @@
|
||||||
#
|
#
|
||||||
</th>
|
</th>
|
||||||
<th>
|
<th>
|
||||||
Name
|
{{#translate}}Name{{/translate}}
|
||||||
</th>
|
</th>
|
||||||
<th class="col-md-2">
|
<th class="col-md-2">
|
||||||
Type
|
{{#translate}}Type{{/translate}}
|
||||||
</th>
|
</th>
|
||||||
<th class="col-md-2">
|
<th class="col-md-2">
|
||||||
Merge tag
|
{{#translate}}Merge tag{{/translate}}
|
||||||
</th>
|
</th>
|
||||||
<th class="col-md-2">
|
<th class="col-md-2">
|
||||||
Default merge tag value
|
{{#translate}}Default merge tag value{{/translate}}
|
||||||
</th>
|
</th>
|
||||||
<th class="col-md-1">
|
<th class="col-md-1">
|
||||||
|
|
||||||
|
@ -56,7 +56,7 @@
|
||||||
<td>
|
<td>
|
||||||
<span class="glyphicon glyphicon-wrench" aria-hidden="true"></span>
|
<span class="glyphicon glyphicon-wrench" aria-hidden="true"></span>
|
||||||
<a href="/fields/{{../list.id}}/edit/{{id}}">
|
<a href="/fields/{{../list.id}}/edit/{{id}}">
|
||||||
Edit
|
{{#translate}}Edit{{/translate}}
|
||||||
</a>
|
</a>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
@ -77,7 +77,7 @@
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
<span class="glyphicon glyphicon-wrench" aria-hidden="true"></span>
|
<span class="glyphicon glyphicon-wrench" aria-hidden="true"></span>
|
||||||
<a href="/fields/{{../../list.id}}/edit/{{id}}">Edit</a>
|
<a href="/fields/{{../../list.id}}/edit/{{id}}">{{#translate}}Edit{{/translate}}</a>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
{{/each}}
|
{{/each}}
|
||||||
|
@ -86,7 +86,7 @@
|
||||||
{{#unless rows}}
|
{{#unless rows}}
|
||||||
<tr>
|
<tr>
|
||||||
<td colspan="6">
|
<td colspan="6">
|
||||||
No data available in table
|
{{#translate}}No data available in table{{/translate}}
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
{{/unless}}
|
{{/unless}}
|
||||||
|
|
|
@ -1,13 +1,13 @@
|
||||||
<ol class="breadcrumb">
|
<ol class="breadcrumb">
|
||||||
<li><a href="/">Home</a></li>
|
<li><a href="/">{{#translate}}Home{{/translate}}</a></li>
|
||||||
<li class="active">Lists</li>
|
<li class="active">{{#translate}}Lists{{/translate}}</li>
|
||||||
</ol>
|
</ol>
|
||||||
|
|
||||||
<div class="pull-right">
|
<div class="pull-right">
|
||||||
<a class="btn btn-primary" href="/lists/create" role="button"><i class="glyphicon glyphicon-plus"></i> Create List</a>
|
<a class="btn btn-primary" href="/lists/create" role="button"><i class="glyphicon glyphicon-plus"></i> {{#translate}}Create List{{/translate}}</a>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<h2>Lists</h2>
|
<h2>{{#translate}}Lists{{/translate}}</h2>
|
||||||
|
|
||||||
<hr>
|
<hr>
|
||||||
|
|
||||||
|
@ -18,16 +18,16 @@
|
||||||
#
|
#
|
||||||
</th>
|
</th>
|
||||||
<th>
|
<th>
|
||||||
Name
|
{{#translate}}Name{{/translate}}
|
||||||
</th>
|
</th>
|
||||||
<th class="col-md-2">
|
<th class="col-md-2">
|
||||||
ID
|
{{#translate}}ID{{/translate}}
|
||||||
</th>
|
</th>
|
||||||
<th class="col-md-1">
|
<th class="col-md-1">
|
||||||
Subscribers
|
{{#translate}}Subscribers{{/translate}}
|
||||||
</th>
|
</th>
|
||||||
<th>
|
<th>
|
||||||
Description
|
{{#translate}}Description{{/translate}}
|
||||||
</th>
|
</th>
|
||||||
<th class="col-md-1">
|
<th class="col-md-1">
|
||||||
|
|
||||||
|
@ -59,7 +59,7 @@
|
||||||
<td>
|
<td>
|
||||||
<span class="glyphicon glyphicon-wrench" aria-hidden="true"></span>
|
<span class="glyphicon glyphicon-wrench" aria-hidden="true"></span>
|
||||||
<a href="/lists/edit/{{id}}">
|
<a href="/lists/edit/{{id}}">
|
||||||
Edit
|
{{#translate}}Edit{{/translate}}
|
||||||
</a>
|
</a>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
|
|
@ -1,38 +1,38 @@
|
||||||
<ol class="breadcrumb">
|
<ol class="breadcrumb">
|
||||||
<li><a href="/">Home</a></li>
|
<li><a href="/">{{#translate}}Home{{/translate}}</a></li>
|
||||||
<li><a href="/lists/">Lists</a></li>
|
<li><a href="/lists/">{{#translate}}Lists{{/translate}}</a></li>
|
||||||
<li><a href="/lists/view/{{list.id}}">{{list.name}}</a></li>
|
<li><a href="/lists/view/{{list.id}}">{{list.name}}</a></li>
|
||||||
<li><a href="/segments/{{list.id}}">Segments</a></li>
|
<li><a href="/segments/{{list.id}}">{{#translate}}Segments{{/translate}}</a></li>
|
||||||
<li class="active">Create Segment</li>
|
<li class="active">{{#translate}}Create Segment{{/translate}}</li>
|
||||||
</ol>
|
</ol>
|
||||||
|
|
||||||
<h2>{{list.name}} <small>Create Segment</small></h2>
|
<h2>{{list.name}} <small>{{#translate}}Create Segment{{/translate}}</small></h2>
|
||||||
|
|
||||||
<hr>
|
<hr>
|
||||||
|
|
||||||
<form class="form-horizontal" method="post" action="/segments/{{list.id}}/create">
|
<form class="form-horizontal" method="post" action="/segments/{{list.id}}/create">
|
||||||
<input type="hidden" name="_csrf" value="{{csrfToken}}">
|
<input type="hidden" name="_csrf" value="{{csrfToken}}">
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="name" class="col-sm-2 control-label">Segment Name</label>
|
<label for="name" class="col-sm-2 control-label">{{#translate}}Segment Name{{/translate}}</label>
|
||||||
<div class="col-sm-10">
|
<div class="col-sm-10">
|
||||||
<input type="text" class="form-control input-lg" name="name" id="name" value="{{name}}" placeholder="Segment Name" required>
|
<input type="text" class="form-control input-lg" name="name" id="name" value="{{name}}" placeholder="{{#translate}}Segment Name{{/translate}}" required>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="group" class="col-sm-2 control-label">Rule match</label>
|
<label for="group" class="col-sm-2 control-label">{{#translate}}Rule match{{/translate}}</label>
|
||||||
<div class="col-sm-10">
|
<div class="col-sm-10">
|
||||||
<select class="form-control" name="type">
|
<select class="form-control" name="type">
|
||||||
<option value=""> –– Select ––</option>
|
<option value=""> –– {{#translate}}Select{{/translate}} ––</option>
|
||||||
<option value="1" {{#if matchAll}} selected {{/if}}>All rules must match</option>
|
<option value="1" {{#if matchAll}} selected {{/if}}>{{#translate}}All rules must match{{/translate}}</option>
|
||||||
<option value="2" {{#if matchAny}} selected {{/if}}>Any rule can match</option>
|
<option value="2" {{#if matchAny}} selected {{/if}}>{{#translate}}Any rule can match{{/translate}}</option>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<div class="col-sm-offset-2 col-sm-10">
|
<div class="col-sm-offset-2 col-sm-10">
|
||||||
<button type="submit" class="btn btn-primary"><i class="glyphicon glyphicon-plus"></i> Add Segment</button>
|
<button type="submit" class="btn btn-primary"><i class="glyphicon glyphicon-plus"></i> {{#translate}}Add Segment{{/translate}}</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
|
|
|
@ -1,12 +1,12 @@
|
||||||
<ol class="breadcrumb">
|
<ol class="breadcrumb">
|
||||||
<li><a href="/">Home</a></li>
|
<li><a href="/">{{#translate}}Home{{/translate}}</a></li>
|
||||||
<li><a href="/lists/">Lists</a></li>
|
<li><a href="/lists/">{{#translate}}Lists{{/translate}}</a></li>
|
||||||
<li><a href="/lists/view/{{list.id}}">{{list.name}}</a></li>
|
<li><a href="/lists/view/{{list.id}}">{{list.name}}</a></li>
|
||||||
<li><a href="/segments/{{list.id}}">Segments</a></li>
|
<li><a href="/segments/{{list.id}}">{{#translate}}Segments{{/translate}}</a></li>
|
||||||
<li class="active">Edit Segment</li>
|
<li class="active">{{#translate}}Edit Segment{{/translate}}</li>
|
||||||
</ol>
|
</ol>
|
||||||
|
|
||||||
<h2>{{list.name}} <small>Edit Segment</small> <a class="btn btn-default btn-xs" href="/segments/{{list.id}}" role="button"><span class="glyphicon glyphicon-arrow-left" aria-hidden="true"></span> Back to segments</a></h2>
|
<h2>{{list.name}} <small>{{#translate}}Edit Segment{{/translate}}</small> <a class="btn btn-default btn-xs" href="/segments/{{list.id}}" role="button"><span class="glyphicon glyphicon-arrow-left" aria-hidden="true"></span> {{#translate}}Back to segments{{/translate}}</a></h2>
|
||||||
|
|
||||||
<hr>
|
<hr>
|
||||||
|
|
||||||
|
@ -20,19 +20,19 @@
|
||||||
<input type="hidden" name="id" value="{{id}}" />
|
<input type="hidden" name="id" value="{{id}}" />
|
||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="name" class="col-sm-2 control-label">Segment Name</label>
|
<label for="name" class="col-sm-2 control-label">{{#translate}}Segment Name{{/translate}}</label>
|
||||||
<div class="col-sm-10">
|
<div class="col-sm-10">
|
||||||
<input type="text" class="form-control input-lg" name="name" id="name" value="{{name}}" placeholder="Segment Name" required>
|
<input type="text" class="form-control input-lg" name="name" id="name" value="{{name}}" placeholder="{{#translate}}Segment Name{{/translate}}" required>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="group" class="col-sm-2 control-label">Rule match</label>
|
<label for="group" class="col-sm-2 control-label">{{#translate}}Rule match{{/translate}}</label>
|
||||||
<div class="col-sm-10">
|
<div class="col-sm-10">
|
||||||
<select class="form-control" name="type">
|
<select class="form-control" name="type">
|
||||||
<option value=""> –– Select ––</option>
|
<option value=""> –– {{#translate}}Select{{/translate}} ––</option>
|
||||||
<option value="1" {{#if matchAll}} selected {{/if}}>All rules must match</option>
|
<option value="1" {{#if matchAll}} selected {{/if}}>{{#translate}}All rules must match{{/translate}}</option>
|
||||||
<option value="2" {{#if matchAny}} selected {{/if}}>Any rule can match</option>
|
<option value="2" {{#if matchAny}} selected {{/if}}>{{#translate}}Any rule can match{{/translate}}</option>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -40,9 +40,9 @@
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<div class="col-sm-offset-2 col-sm-10">
|
<div class="col-sm-offset-2 col-sm-10">
|
||||||
<div class="pull-right">
|
<div class="pull-right">
|
||||||
<button type="submit" form="segments-delete" class="btn btn-danger"><i class="glyphicon glyphicon-remove"></i> Delete Segment</button>
|
<button type="submit" form="segments-delete" class="btn btn-danger"><i class="glyphicon glyphicon-remove"></i> {{#translate}}Delete Segment{{/translate}}</button>
|
||||||
</div>
|
</div>
|
||||||
<button type="submit" class="btn btn-primary"><i class="glyphicon glyphicon-ok"></i> Update</button>
|
<button type="submit" class="btn btn-primary"><i class="glyphicon glyphicon-ok"></i> {{#translate}}Update{{/translate}}</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
|
|
|
@ -1,13 +1,13 @@
|
||||||
<ol class="breadcrumb">
|
<ol class="breadcrumb">
|
||||||
<li><a href="/">Home</a></li>
|
<li><a href="/">{{#translate}}Home{{/translate}}</a></li>
|
||||||
<li><a href="/lists/">Lists</a></li>
|
<li><a href="/lists/">{{#translate}}Lists{{/translate}}</a></li>
|
||||||
<li><a href="/lists/view/{{list.id}}">{{list.name}}</a></li>
|
<li><a href="/lists/view/{{list.id}}">{{list.name}}</a></li>
|
||||||
<li><a href="/segments/{{list.id}}">Segments</a></li>
|
<li><a href="/segments/{{list.id}}">{{#translate}}Segments{{/translate}}</a></li>
|
||||||
<li><a href="/segments/{{list.id}}/view/{{id}}">{{name}}</a></li>
|
<li><a href="/segments/{{list.id}}/view/{{id}}">{{name}}</a></li>
|
||||||
<li class="active">Create Segment</li>
|
<li class="active">{{#translate}}Create Segment{{/translate}}</li>
|
||||||
</ol>
|
</ol>
|
||||||
|
|
||||||
<h2>{{list.name}} <small>Create Rule</small></h2>
|
<h2>{{list.name}} <small>{{#translate}}Create Rule{{/translate}}</small></h2>
|
||||||
|
|
||||||
<hr>
|
<hr>
|
||||||
|
|
||||||
|
@ -16,7 +16,7 @@
|
||||||
<input type="hidden" name="column" value="{{column.column}}">
|
<input type="hidden" name="column" value="{{column.column}}">
|
||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="column" class="col-sm-2 control-label">Rule</label>
|
<label for="column" class="col-sm-2 control-label">{{#translate}}Rule{{/translate}}</label>
|
||||||
<div class="col-sm-10">
|
<div class="col-sm-10">
|
||||||
<p class="form-control-static"><strong>{{column.name}}</strong></p>
|
<p class="form-control-static"><strong>{{column.name}}</strong></p>
|
||||||
</div>
|
</div>
|
||||||
|
@ -24,20 +24,20 @@
|
||||||
|
|
||||||
{{#if columnTypeString}}
|
{{#if columnTypeString}}
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="value" class="col-sm-2 control-label">Value</label>
|
<label for="value" class="col-sm-2 control-label">{{#translate}}Value{{/translate}}</label>
|
||||||
<div class="col-sm-10">
|
<div class="col-sm-10">
|
||||||
<input type="text" class="form-control" name="value" id="value" value="{{value.value}}" placeholder="Value">
|
<input type="text" class="form-control" name="value" id="value" value="{{value.value}}" placeholder="{{#translate}}Value{{/translate}}">
|
||||||
<span class="help-block">Use % for wildcard character, e.g. "%test" to match all values that end with "test"</span>
|
<span class="help-block">{{#translate}}Use % for wildcard character, e.g. "%test" to match all values that end with "test"{{/translate}}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
|
|
||||||
{{#if columnTypeNumber}}
|
{{#if columnTypeNumber}}
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="value" class="col-sm-2 control-label">Value</label>
|
<label for="value" class="col-sm-2 control-label">{{#translate}}Value{{/translate}}</label>
|
||||||
<div class="col-sm-10 radio">
|
<div class="col-sm-10 radio">
|
||||||
<label>
|
<label>
|
||||||
<input type="radio" name="range" value="" {{#unless value.range}} checked {{/unless}}> Use exact match
|
<input type="radio" name="range" value="" {{#unless value.range}} checked {{/unless}}> {{#translate}}Use exact match{{/translate}}
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -50,7 +50,7 @@
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<div class="col-sm-offset-2 col-sm-10 radio">
|
<div class="col-sm-offset-2 col-sm-10 radio">
|
||||||
<label>
|
<label>
|
||||||
<input type="radio" name="range" value="yes" {{#if value.range}} checked {{/if}}> Use range match
|
<input type="radio" name="range" value="yes" {{#if value.range}} checked {{/if}}> {{#translate}}Use range match{{/translate}}
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -78,10 +78,10 @@
|
||||||
|
|
||||||
{{#if columnTypeDate}}
|
{{#if columnTypeDate}}
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="value" class="col-sm-2 control-label">Value</label>
|
<label for="value" class="col-sm-2 control-label">{{#translate}}Value{{/translate}}</label>
|
||||||
<div class="col-sm-10 radio">
|
<div class="col-sm-10 radio">
|
||||||
<label>
|
<label>
|
||||||
<input type="radio" name="range" value="" {{#unless value.range}} {{#unless value.relativeRange}} checked {{/unless}} {{/unless}}> Use exact match
|
<input type="radio" name="range" value="" {{#unless value.range}} {{#unless value.relativeRange}} checked {{/unless}} {{/unless}}> {{#translate}}Use exact match{{/translate}}
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -96,7 +96,7 @@
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<div class="col-sm-offset-2 col-sm-10 radio">
|
<div class="col-sm-offset-2 col-sm-10 radio">
|
||||||
<label>
|
<label>
|
||||||
<input type="radio" name="range" value="yes" {{#if value.range}} checked {{/if}}> Use range match
|
<input type="radio" name="range" value="yes" {{#if value.range}} checked {{/if}}> {{#translate}}Use range match{{/translate}}
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -127,7 +127,7 @@
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<div class="col-sm-offset-2 col-sm-10 radio">
|
<div class="col-sm-offset-2 col-sm-10 radio">
|
||||||
<label>
|
<label>
|
||||||
<input type="radio" name="range" value="relative" {{#if value.relativeRange}} checked {{/if}}> Use relative range match
|
<input type="radio" name="range" value="relative" {{#if value.relativeRange}} checked {{/if}}> {{#translate}}Use relative range match{{/translate}}
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -136,38 +136,38 @@
|
||||||
<div class="col-sm-offset-2 col-sm-10 radio">
|
<div class="col-sm-offset-2 col-sm-10 radio">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-md-1">
|
<div class="col-md-1">
|
||||||
<p class="form-control-static">From</p>
|
<p class="form-control-static">{{#translate}}From{{/translate}}</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-md-4 form-inline">
|
<div class="col-md-4 form-inline">
|
||||||
<div class="input-group">
|
<div class="input-group">
|
||||||
<input type="number" class="form-control" name="start-relative" placeholder="0" {{#if value.relativeRange}} value="{{value.start}}" {{/if}}>
|
<input type="number" class="form-control" name="start-relative" placeholder="0" {{#if value.relativeRange}} value="{{value.start}}" {{/if}}>
|
||||||
<div class="input-group-addon">
|
<div class="input-group-addon">
|
||||||
days
|
{{#translate}}days{{/translate}}
|
||||||
<select name="start-direction">
|
<select name="start-direction">
|
||||||
<option value="0">
|
<option value="0">
|
||||||
before today
|
{{#translate}}before today{{/translate}}
|
||||||
</option>
|
</option>
|
||||||
<option value="1" {{#if value.startDirection}} selected {{/if}}>
|
<option value="1" {{#if value.startDirection}} selected {{/if}}>
|
||||||
after today
|
{{#translate}}after today{{/translate}}
|
||||||
</option>
|
</option>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-md-1">
|
<div class="col-md-1">
|
||||||
<p class="form-control-static text-center">to</p>
|
<p class="form-control-static text-center">{{#translate}}to{{/translate}}</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-md-4 form-inline">
|
<div class="col-md-4 form-inline">
|
||||||
<div class="input-group">
|
<div class="input-group">
|
||||||
<input type="number" class="form-control" name="end-relative" placeholder="0" {{#if value.relativeRange}} value="{{value.end}}" {{/if}}>
|
<input type="number" class="form-control" name="end-relative" placeholder="0" {{#if value.relativeRange}} value="{{value.end}}" {{/if}}>
|
||||||
<div class="input-group-addon">
|
<div class="input-group-addon">
|
||||||
days
|
{{#translate}}days{{/translate}}
|
||||||
<select name="end-direction">
|
<select name="end-direction">
|
||||||
<option value="0">
|
<option value="0">
|
||||||
before today
|
{{#translate}}before today{{/translate}}
|
||||||
</option>
|
</option>
|
||||||
<option value="1" {{#if value.endDirection}} selected {{/if}}>
|
<option value="1" {{#if value.endDirection}} selected {{/if}}>
|
||||||
after today
|
{{#translate}}after today{{/translate}}
|
||||||
</option>
|
</option>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
|
@ -180,10 +180,10 @@
|
||||||
|
|
||||||
{{#if columnTypeBirthday}}
|
{{#if columnTypeBirthday}}
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="value" class="col-sm-2 control-label">Value</label>
|
<label for="value" class="col-sm-2 control-label">{{#translate}}Value{{/translate}}</label>
|
||||||
<div class="col-sm-10 radio">
|
<div class="col-sm-10 radio">
|
||||||
<label>
|
<label>
|
||||||
<input type="radio" name="range" value="" {{#unless isRange}} checked {{/unless}}> Use exact match
|
<input type="radio" name="range" value="" {{#unless isRange}} checked {{/unless}}> {{#translate}}Use exact match{{/translate}}
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -198,7 +198,7 @@
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<div class="col-sm-offset-2 col-sm-10 radio">
|
<div class="col-sm-offset-2 col-sm-10 radio">
|
||||||
<label>
|
<label>
|
||||||
<input type="radio" name="range" value="yes" {{#if isRange}} checked {{/if}}> Use range match
|
<input type="radio" name="range" value="yes" {{#if isRange}} checked {{/if}}> {{#translate}}Use range match{{/translate}}
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -207,7 +207,7 @@
|
||||||
<div class="col-sm-offset-2 col-sm-10 radio">
|
<div class="col-sm-offset-2 col-sm-10 radio">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-md-1">
|
<div class="col-md-1">
|
||||||
<p class="form-control-static">From</p>
|
<p class="form-control-static">{{#translate}}From{{/translate}}</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-md-3">
|
<div class="col-md-3">
|
||||||
<div class="input-group date fm-birthday-generic">
|
<div class="input-group date fm-birthday-generic">
|
||||||
|
@ -215,7 +215,7 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-md-1">
|
<div class="col-md-1">
|
||||||
<p class="form-control-static text-center">to</p>
|
<p class="form-control-static text-center">{{#translate}}to{{/translate}}</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-md-3">
|
<div class="col-md-3">
|
||||||
<div class="input-group date fm-birthday-generic">
|
<div class="input-group date fm-birthday-generic">
|
||||||
|
@ -229,16 +229,16 @@
|
||||||
|
|
||||||
{{#if columnTypeBoolean}}
|
{{#if columnTypeBoolean}}
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="value" class="col-sm-2 control-label">Value</label>
|
<label for="value" class="col-sm-2 control-label">{{#translate}}Value{{/translate}}</label>
|
||||||
<div class="col-sm-10">
|
<div class="col-sm-10">
|
||||||
<div class="radio">
|
<div class="radio">
|
||||||
<label>
|
<label>
|
||||||
<input type="radio" name="value" value="yes" {{#if value.value}} checked {{/if}}> Selected
|
<input type="radio" name="value" value="yes" {{#if value.value}} checked {{/if}}> {{#translate}}Selected{{/translate}}
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
<div class="radio">
|
<div class="radio">
|
||||||
<label>
|
<label>
|
||||||
<input type="radio" name="value" value="" {{#unless value.value}} checked {{/unless}}> Not selected
|
<input type="radio" name="value" value="" {{#unless value.value}} checked {{/unless}}> {{#translate}}Not selected{{/translate}}
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -248,7 +248,7 @@
|
||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<div class="col-sm-offset-2 col-sm-10">
|
<div class="col-sm-offset-2 col-sm-10">
|
||||||
<button type="submit" class="btn btn-primary"><i class="glyphicon glyphicon-plus"></i> Add Rule</button>
|
<button type="submit" class="btn btn-primary"><i class="glyphicon glyphicon-plus"></i> {{#translate}}Add Rule{{/translate}}</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
|
|
|
@ -1,13 +1,13 @@
|
||||||
<ol class="breadcrumb">
|
<ol class="breadcrumb">
|
||||||
<li><a href="/">Home</a></li>
|
<li><a href="/">{{#translate}}Home{{/translate}}</a></li>
|
||||||
<li><a href="/lists/">Lists</a></li>
|
<li><a href="/lists/">{{#translate}}Lists{{/translate}}</a></li>
|
||||||
<li><a href="/lists/view/{{list.id}}">{{list.name}}</a></li>
|
<li><a href="/lists/view/{{list.id}}">{{list.name}}</a></li>
|
||||||
<li><a href="/segments/{{list.id}}">Segments</a></li>
|
<li><a href="/segments/{{list.id}}">{{#translate}}Segments{{/translate}}</a></li>
|
||||||
<li><a href="/segments/{{list.id}}/view/{{id}}">{{name}}</a></li>
|
<li><a href="/segments/{{list.id}}/view/{{id}}">{{name}}</a></li>
|
||||||
<li class="active">Create Segment</li>
|
<li class="active">{{#translate}}Create Segment{{/translate}}</li>
|
||||||
</ol>
|
</ol>
|
||||||
|
|
||||||
<h2>{{list.name}} <small>Create Rule</small></h2>
|
<h2>{{list.name}} <small>{{#translate}}Create Rule{{/translate}}</small></h2>
|
||||||
|
|
||||||
<hr>
|
<hr>
|
||||||
|
|
||||||
|
@ -15,10 +15,10 @@
|
||||||
<input type="hidden" name="_csrf" value="{{csrfToken}}">
|
<input type="hidden" name="_csrf" value="{{csrfToken}}">
|
||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="column" class="col-sm-2 control-label">Rule</label>
|
<label for="column" class="col-sm-2 control-label">{{#translate}}Rule{{/translate}}</label>
|
||||||
<div class="col-sm-10">
|
<div class="col-sm-10">
|
||||||
<select id="column" class="form-control" name="column" required>
|
<select id="column" class="form-control" name="column" required>
|
||||||
<option value=""> –– Select ––</option>
|
<option value=""> –– {{#translate}}Select{{/translate}} ––</option>
|
||||||
{{#each columns}}
|
{{#each columns}}
|
||||||
<option value="{{column}}" {{#if selected}} selected {{/if}}>{{name}}</option>
|
<option value="{{column}}" {{#if selected}} selected {{/if}}>{{name}}</option>
|
||||||
{{/each}}
|
{{/each}}
|
||||||
|
@ -28,7 +28,7 @@
|
||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<div class="col-sm-offset-2 col-sm-10">
|
<div class="col-sm-offset-2 col-sm-10">
|
||||||
<button type="submit" class="btn btn-primary"><span class="glyphicon glyphicon-chevron-right" aria-hidden="true"></span> Next</button>
|
<button type="submit" class="btn btn-primary"><span class="glyphicon glyphicon-chevron-right" aria-hidden="true"></span> {{#translate}}Next{{/translate}}</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
|
|
|
@ -1,13 +1,13 @@
|
||||||
<ol class="breadcrumb">
|
<ol class="breadcrumb">
|
||||||
<li><a href="/">Home</a></li>
|
<li><a href="/">{{#translate}}Home{{/translate}}</a></li>
|
||||||
<li><a href="/lists/">Lists</a></li>
|
<li><a href="/lists/">{{#translate}}Lists{{/translate}}</a></li>
|
||||||
<li><a href="/lists/view/{{list.id}}">{{list.name}}</a></li>
|
<li><a href="/lists/view/{{list.id}}">{{list.name}}</a></li>
|
||||||
<li><a href="/segments/{{list.id}}">Segments</a></li>
|
<li><a href="/segments/{{list.id}}">{{#translate}}Segments{{/translate}}</a></li>
|
||||||
<li><a href="/segments/{{list.id}}/view/{{segment.id}}">{{name}}</a></li>
|
<li><a href="/segments/{{list.id}}/view/{{segment.id}}">{{name}}</a></li>
|
||||||
<li class="active">Create Segment</li>
|
<li class="active">{{#translate}}Create Segment{{/translate}}</li>
|
||||||
</ol>
|
</ol>
|
||||||
|
|
||||||
<h2>{{list.name}} <small>Create Rule</small></h2>
|
<h2>{{list.name}} <small>{{#translate}}Create Rule{{/translate}}</small></h2>
|
||||||
|
|
||||||
<hr>
|
<hr>
|
||||||
|
|
||||||
|
@ -21,7 +21,7 @@
|
||||||
<input type="hidden" name="id" value="{{id}}">
|
<input type="hidden" name="id" value="{{id}}">
|
||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="column" class="col-sm-2 control-label">Rule</label>
|
<label for="column" class="col-sm-2 control-label">{{#translate}}Rule{{/translate}}</label>
|
||||||
<div class="col-sm-10">
|
<div class="col-sm-10">
|
||||||
<p class="form-control-static"><strong>{{column.name}}</strong></p>
|
<p class="form-control-static"><strong>{{column.name}}</strong></p>
|
||||||
</div>
|
</div>
|
||||||
|
@ -29,20 +29,20 @@
|
||||||
|
|
||||||
{{#if columnTypeString}}
|
{{#if columnTypeString}}
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="value" class="col-sm-2 control-label">Value</label>
|
<label for="value" class="col-sm-2 control-label">{{#translate}}Value{{/translate}}</label>
|
||||||
<div class="col-sm-10">
|
<div class="col-sm-10">
|
||||||
<input type="text" class="form-control" name="value" id="value" value="{{value.value}}" placeholder="Value">
|
<input type="text" class="form-control" name="value" id="value" value="{{value.value}}" placeholder="{{#translate}}Value{{/translate}}">
|
||||||
<span class="help-block">Use % for wildcard character, e.g. "%test" to match all values that end with "test"</span>
|
<span class="help-block">{{#translate}}Use % for wildcard character, e.g. "%test" to match all values that end with "test"{{/translate}}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
|
|
||||||
{{#if columnTypeNumber}}
|
{{#if columnTypeNumber}}
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="value" class="col-sm-2 control-label">Value</label>
|
<label for="value" class="col-sm-2 control-label">{{#translate}}Value{{/translate}}</label>
|
||||||
<div class="col-sm-10 radio">
|
<div class="col-sm-10 radio">
|
||||||
<label>
|
<label>
|
||||||
<input type="radio" name="range" value="" {{#unless value.range}} checked {{/unless}}> Use exact match
|
<input type="radio" name="range" value="" {{#unless value.range}} checked {{/unless}}> {{#translate}}Use exact match{{/translate}}
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -55,7 +55,7 @@
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<div class="col-sm-offset-2 col-sm-10 radio">
|
<div class="col-sm-offset-2 col-sm-10 radio">
|
||||||
<label>
|
<label>
|
||||||
<input type="radio" name="range" value="yes" {{#if value.range}} checked {{/if}}> Use range match
|
<input type="radio" name="range" value="yes" {{#if value.range}} checked {{/if}}> {{#translate}}Use range match{{/translate}}
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -64,13 +64,13 @@
|
||||||
<div class="col-sm-offset-2 col-sm-10 radio">
|
<div class="col-sm-offset-2 col-sm-10 radio">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-md-1">
|
<div class="col-md-1">
|
||||||
<p class="form-control-static">From</p>
|
<p class="form-control-static">{{#translate}}From{{/translate}}</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-md-2">
|
<div class="col-md-2">
|
||||||
<input type="number" class="form-control" name="start" value="{{value.start}}" placeholder="0">
|
<input type="number" class="form-control" name="start" value="{{value.start}}" placeholder="0">
|
||||||
</div>
|
</div>
|
||||||
<div class="col-md-1">
|
<div class="col-md-1">
|
||||||
<p class="form-control-static text-center">to</p>
|
<p class="form-control-static text-center">{{#translate}}to{{/translate}}</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-md-2">
|
<div class="col-md-2">
|
||||||
<input type="number" class="form-control" name="end" value="{{value.end}}" placeholder="0">
|
<input type="number" class="form-control" name="end" value="{{value.end}}" placeholder="0">
|
||||||
|
@ -83,10 +83,10 @@
|
||||||
|
|
||||||
{{#if columnTypeDate}}
|
{{#if columnTypeDate}}
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="value" class="col-sm-2 control-label">Value</label>
|
<label for="value" class="col-sm-2 control-label">{{#translate}}Value{{/translate}}</label>
|
||||||
<div class="col-sm-10 radio">
|
<div class="col-sm-10 radio">
|
||||||
<label>
|
<label>
|
||||||
<input type="radio" name="range" value="" {{#unless value.range}} {{#unless value.relativeRange}} checked {{/unless}} {{/unless}}> Use exact match
|
<input type="radio" name="range" value="" {{#unless value.range}} {{#unless value.relativeRange}} checked {{/unless}} {{/unless}}> {{#translate}}Use exact match{{/translate}}
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -101,7 +101,7 @@
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<div class="col-sm-offset-2 col-sm-10 radio">
|
<div class="col-sm-offset-2 col-sm-10 radio">
|
||||||
<label>
|
<label>
|
||||||
<input type="radio" name="range" value="yes" {{#if value.range}} checked {{/if}}> Use range match
|
<input type="radio" name="range" value="yes" {{#if value.range}} checked {{/if}}> {{#translate}}Use range match{{/translate}}
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -110,7 +110,7 @@
|
||||||
<div class="col-sm-offset-2 col-sm-10 radio">
|
<div class="col-sm-offset-2 col-sm-10 radio">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-md-1">
|
<div class="col-md-1">
|
||||||
<p class="form-control-static">From</p>
|
<p class="form-control-static">{{#translate}}From{{/translate}}</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-md-3">
|
<div class="col-md-3">
|
||||||
<div class="input-group date fm-date-generic">
|
<div class="input-group date fm-date-generic">
|
||||||
|
@ -118,7 +118,7 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-md-1">
|
<div class="col-md-1">
|
||||||
<p class="form-control-static text-center">to</p>
|
<p class="form-control-static text-center">{{#translate}}to{{/translate}}</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-md-3">
|
<div class="col-md-3">
|
||||||
<div class="input-group date fm-date-generic">
|
<div class="input-group date fm-date-generic">
|
||||||
|
@ -132,7 +132,7 @@
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<div class="col-sm-offset-2 col-sm-10 radio">
|
<div class="col-sm-offset-2 col-sm-10 radio">
|
||||||
<label>
|
<label>
|
||||||
<input type="radio" name="range" value="relative" {{#if value.relativeRange}} checked {{/if}}> Use relative range match
|
<input type="radio" name="range" value="relative" {{#if value.relativeRange}} checked {{/if}}> {{#translate}}Use relative range match{{/translate}}
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -141,38 +141,38 @@
|
||||||
<div class="col-sm-offset-2 col-sm-10 radio">
|
<div class="col-sm-offset-2 col-sm-10 radio">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-md-1">
|
<div class="col-md-1">
|
||||||
<p class="form-control-static">From</p>
|
<p class="form-control-static">{{#translate}}From{{/translate}}</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-md-4 form-inline">
|
<div class="col-md-4 form-inline">
|
||||||
<div class="input-group">
|
<div class="input-group">
|
||||||
<input type="number" class="form-control" name="start-relative" placeholder="0" {{#if value.relativeRange}} value="{{value.start}}" {{/if}}>
|
<input type="number" class="form-control" name="start-relative" placeholder="0" {{#if value.relativeRange}} value="{{value.start}}" {{/if}}>
|
||||||
<div class="input-group-addon">
|
<div class="input-group-addon">
|
||||||
days
|
{{#translate}}days{{/translate}}
|
||||||
<select name="start-direction">
|
<select name="start-direction">
|
||||||
<option value="0">
|
<option value="0">
|
||||||
before today
|
{{#translate}}before today{{/translate}}
|
||||||
</option>
|
</option>
|
||||||
<option value="1" {{#if value.startDirection}} selected {{/if}}>
|
<option value="1" {{#if value.startDirection}} selected {{/if}}>
|
||||||
after today
|
{{#translate}}after today{{/translate}}
|
||||||
</option>
|
</option>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-md-1">
|
<div class="col-md-1">
|
||||||
<p class="form-control-static text-center">to</p>
|
<p class="form-control-static text-center">{{#translate}}to{{/translate}}</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-md-4 form-inline">
|
<div class="col-md-4 form-inline">
|
||||||
<div class="input-group">
|
<div class="input-group">
|
||||||
<input type="number" class="form-control" name="end-relative" placeholder="0" {{#if value.relativeRange}} value="{{value.end}}" {{/if}}>
|
<input type="number" class="form-control" name="end-relative" placeholder="0" {{#if value.relativeRange}} value="{{value.end}}" {{/if}}>
|
||||||
<div class="input-group-addon">
|
<div class="input-group-addon">
|
||||||
days
|
{{#translate}}days{{/translate}}
|
||||||
<select name="end-direction">
|
<select name="end-direction">
|
||||||
<option value="0">
|
<option value="0">
|
||||||
before today
|
{{#translate}}before today{{/translate}}
|
||||||
</option>
|
</option>
|
||||||
<option value="1" {{#if value.endDirection}} selected {{/if}}>
|
<option value="1" {{#if value.endDirection}} selected {{/if}}>
|
||||||
after today
|
{{#translate}}after today{{/translate}}
|
||||||
</option>
|
</option>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
|
@ -185,10 +185,10 @@
|
||||||
|
|
||||||
{{#if columnTypeBirthday}}
|
{{#if columnTypeBirthday}}
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="value" class="col-sm-2 control-label">Value</label>
|
<label for="value" class="col-sm-2 control-label">{{#translate}}Value{{/translate}}</label>
|
||||||
<div class="col-sm-10 radio">
|
<div class="col-sm-10 radio">
|
||||||
<label>
|
<label>
|
||||||
<input type="radio" name="range" value="" {{#unless value.range}} checked {{/unless}}> Use exact match
|
<input type="radio" name="range" value="" {{#unless value.range}} checked {{/unless}}> {{#translate}}Use exact match{{/translate}}
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -203,7 +203,7 @@
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<div class="col-sm-offset-2 col-sm-10 radio">
|
<div class="col-sm-offset-2 col-sm-10 radio">
|
||||||
<label>
|
<label>
|
||||||
<input type="radio" name="range" value="yes" {{#if value.range}} checked {{/if}}> Use range match
|
<input type="radio" name="range" value="yes" {{#if value.range}} checked {{/if}}> {{#translate}}Use range match{{/translate}}
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -212,7 +212,7 @@
|
||||||
<div class="col-sm-offset-2 col-sm-10 radio">
|
<div class="col-sm-offset-2 col-sm-10 radio">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-md-1">
|
<div class="col-md-1">
|
||||||
<p class="form-control-static">From</p>
|
<p class="form-control-static">{{#translate}}From{{/translate}}</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-md-3">
|
<div class="col-md-3">
|
||||||
<div class="input-group date fm-birthday-generic">
|
<div class="input-group date fm-birthday-generic">
|
||||||
|
@ -220,7 +220,7 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-md-1">
|
<div class="col-md-1">
|
||||||
<p class="form-control-static text-center">To</p>
|
<p class="form-control-static text-center">{{#translate}}to{{/translate}}</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-md-3">
|
<div class="col-md-3">
|
||||||
<div class="input-group date fm-birthday-generic">
|
<div class="input-group date fm-birthday-generic">
|
||||||
|
@ -234,16 +234,16 @@
|
||||||
|
|
||||||
{{#if columnTypeBoolean}}
|
{{#if columnTypeBoolean}}
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="value" class="col-sm-2 control-label">Value</label>
|
<label for="value" class="col-sm-2 control-label">{{#translate}}Value{{/translate}}</label>
|
||||||
<div class="col-sm-10">
|
<div class="col-sm-10">
|
||||||
<div class="radio">
|
<div class="radio">
|
||||||
<label>
|
<label>
|
||||||
<input type="radio" name="value" value="yes" {{#if value.value}} checked {{/if}}> Selected
|
<input type="radio" name="value" value="yes" {{#if value.value}} checked {{/if}}> {{#translate}}Selected{{/translate}}
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
<div class="radio">
|
<div class="radio">
|
||||||
<label>
|
<label>
|
||||||
<input type="radio" name="value" value="" {{#unless value.value}} checked {{/unless}}> Not selected
|
<input type="radio" name="value" value="" {{#unless value.value}} checked {{/unless}}> {{#translate}}Not selected{{/translate}}
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -254,9 +254,9 @@
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<div class="col-sm-offset-2 col-sm-10">
|
<div class="col-sm-offset-2 col-sm-10">
|
||||||
<div class="pull-right">
|
<div class="pull-right">
|
||||||
<button type="submit" form="rule-delete" class="btn btn-danger"><i class="glyphicon glyphicon-remove"></i> Delete Rule</button>
|
<button type="submit" form="rule-delete" class="btn btn-danger"><i class="glyphicon glyphicon-remove"></i> {{#translate}}Delete Rule{{/translate}}</button>
|
||||||
</div>
|
</div>
|
||||||
<button type="submit" class="btn btn-primary"><i class="glyphicon glyphicon-ok"></i> Update</button>
|
<button type="submit" class="btn btn-primary"><i class="glyphicon glyphicon-ok"></i> {{#translate}}Update{{/translate}}</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
|
|
|
@ -1,15 +1,15 @@
|
||||||
<ol class="breadcrumb">
|
<ol class="breadcrumb">
|
||||||
<li><a href="/">Home</a></li>
|
<li><a href="/">{{#translate}}Home{{/translate}}</a></li>
|
||||||
<li><a href="/lists/">Lists</a></li>
|
<li><a href="/lists/">{{#translate}}Lists{{/translate}}</a></li>
|
||||||
<li><a href="/lists/view/{{list.id}}">{{list.name}}</a></li>
|
<li><a href="/lists/view/{{list.id}}">{{list.name}}</a></li>
|
||||||
<li class="active">Segments</li>
|
<li class="active">{{#translate}}Segments{{/translate}}</li>
|
||||||
</ol>
|
</ol>
|
||||||
|
|
||||||
<div class="pull-right">
|
<div class="pull-right">
|
||||||
<a class="btn btn-primary" href="/segments/{{list.id}}/create" role="button"><i class="glyphicon glyphicon-plus"></i> Create Segment</a>
|
<a class="btn btn-primary" href="/segments/{{list.id}}/create" role="button"><i class="glyphicon glyphicon-plus"></i> {{#translate}}Create Segment{{/translate}}</a>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<h2>{{list.name}} <small>Segments</small></h2>
|
<h2>{{list.name}} <small>{{#translate}}Segments{{/translate}}</small></h2>
|
||||||
|
|
||||||
<hr>
|
<hr>
|
||||||
|
|
||||||
|
@ -20,10 +20,10 @@
|
||||||
#
|
#
|
||||||
</th>
|
</th>
|
||||||
<th>
|
<th>
|
||||||
Name
|
{{#translate}}Name{{/translate}}
|
||||||
</th>
|
</th>
|
||||||
<th class="col-md-2">
|
<th class="col-md-2">
|
||||||
Match
|
{{#translate}}Match{{/translate}}
|
||||||
</th>
|
</th>
|
||||||
<th class="col-md-1">
|
<th class="col-md-1">
|
||||||
|
|
||||||
|
@ -46,7 +46,7 @@
|
||||||
<td>
|
<td>
|
||||||
<span class="glyphicon glyphicon-wrench" aria-hidden="true"></span>
|
<span class="glyphicon glyphicon-wrench" aria-hidden="true"></span>
|
||||||
<a href="/segments/{{../list.id}}/edit/{{id}}">
|
<a href="/segments/{{../list.id}}/edit/{{id}}">
|
||||||
Edit
|
{{#translate}}Edit{{/translate}}
|
||||||
</a>
|
</a>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
|
|
@ -1,26 +1,26 @@
|
||||||
<ol class="breadcrumb">
|
<ol class="breadcrumb">
|
||||||
<li><a href="/">Home</a></li>
|
<li><a href="/">{{#translate}}Home{{/translate}}</a></li>
|
||||||
<li><a href="/lists/">Lists</a></li>
|
<li><a href="/lists/">{{#translate}}Lists{{/translate}}</a></li>
|
||||||
<li><a href="/lists/view/{{list.id}}">{{list.name}}</a></li>
|
<li><a href="/lists/view/{{list.id}}">{{list.name}}</a></li>
|
||||||
<li><a href="/segments/{{list.id}}">Segments</a></li>
|
<li><a href="/segments/{{list.id}}">{{#translate}}Segments{{/translate}}</a></li>
|
||||||
<li class="active">{{name}}</li>
|
<li class="active">{{name}}</li>
|
||||||
</ol>
|
</ol>
|
||||||
|
|
||||||
<div class="pull-right">
|
<div class="pull-right">
|
||||||
<a class="btn btn-primary" href="/segments/{{list.id}}/rules/{{id}}/create" role="button"><i class="glyphicon glyphicon-plus"></i> Create Rule</a>
|
<a class="btn btn-primary" href="/segments/{{list.id}}/rules/{{id}}/create" role="button"><i class="glyphicon glyphicon-plus"></i> {{#translate}}Create Rule{{/translate}}</a>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<h2>{{list.name}} <small><span class="glyphicon glyphicon-filter" aria-hidden="true"></span> Segment {{name}}</small></h2>
|
<h2>{{list.name}} <small><span class="glyphicon glyphicon-filter" aria-hidden="true"></span> {{#translate}}Segment{{/translate}} {{name}}</small></h2>
|
||||||
|
|
||||||
<hr>
|
<hr>
|
||||||
|
|
||||||
<div class="well well-sm">
|
<div class="well well-sm">
|
||||||
<div class="pull-right">
|
<div class="pull-right">
|
||||||
<a class="btn btn-primary btn-sm" href="/segments/{{list.id}}/edit/{{id}}" role="button"><i class="glyphicon glyphicon-wrench"></i> Edit Segment</a>
|
<a class="btn btn-primary btn-sm" href="/segments/{{list.id}}/edit/{{id}}" role="button"><i class="glyphicon glyphicon-wrench"></i> {{#translate}}Edit Segment{{/translate}}</a>
|
||||||
</div>
|
</div>
|
||||||
Match rules: <span class="label label-default">{{type}}</span>
|
{{#translate}}Match rules{{/translate}}: <span class="label label-default">{{type}}</span>
|
||||||
<br /> Matching subscribers: <span class="label label-default">{{subscribers}}</span> (
|
<br /> {{#translate}}Matching subscribers{{/translate}}: <span class="label label-default">{{subscribers}}</span> (
|
||||||
<a href="/lists/view/{{list.id}}?segment={{id}}">show</a>)
|
<a href="/lists/view/{{list.id}}?segment={{id}}">{{#translate}}show{{/translate}}</a>)
|
||||||
<br />
|
<br />
|
||||||
<div class="clearfix"></div>
|
<div class="clearfix"></div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -32,10 +32,10 @@
|
||||||
#
|
#
|
||||||
</th>
|
</th>
|
||||||
<th>
|
<th>
|
||||||
Rule
|
{{#translate}}Rule{{/translate}}
|
||||||
</th>
|
</th>
|
||||||
<th class="col-md-2">
|
<th class="col-md-2">
|
||||||
Value
|
{{#translate}}Value{{/translate}}
|
||||||
</th>
|
</th>
|
||||||
<th class="col-md-1">
|
<th class="col-md-1">
|
||||||
|
|
||||||
|
@ -57,7 +57,7 @@
|
||||||
<td>
|
<td>
|
||||||
<span class="glyphicon glyphicon-wrench" aria-hidden="true"></span>
|
<span class="glyphicon glyphicon-wrench" aria-hidden="true"></span>
|
||||||
<a href="/segments/{{../list.id}}/rules/{{../id}}/edit/{{id}}">
|
<a href="/segments/{{../list.id}}/rules/{{../id}}/edit/{{id}}">
|
||||||
Edit
|
{{#translate}}Edit{{/translate}}
|
||||||
</a>
|
</a>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
|
|
@ -1,11 +1,13 @@
|
||||||
<ol class="breadcrumb">
|
<ol class="breadcrumb">
|
||||||
<li><a href="/">Home</a></li>
|
<li><a href="/">{{#translate}}Home{{/translate}}</a></li>
|
||||||
<li><a href="/lists/">Lists</a></li>
|
<li><a href="/lists/">{{#translate}}Lists{{/translate}}</a></li>
|
||||||
<li><a href="/lists/view/{{list.id}}">{{list.name}}</a></li>
|
<li><a href="/lists/view/{{list.id}}">{{list.name}}</a></li>
|
||||||
<li class="active">Add subscriber</li>
|
<li class="active">
|
||||||
|
{{#translate}}Add subscriber{{/translate}}
|
||||||
|
</li>
|
||||||
</ol>
|
</ol>
|
||||||
|
|
||||||
<h2>{{list.name}} <small>Add subscriber</small></h2>
|
<h2>{{list.name}} <small>{{#translate}}Add subscriber{{/translate}}</small></h2>
|
||||||
|
|
||||||
<hr>
|
<hr>
|
||||||
|
|
||||||
|
@ -14,21 +16,21 @@
|
||||||
<input type="hidden" name="list" value="{{list.id}}">
|
<input type="hidden" name="list" value="{{list.id}}">
|
||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="email" class="col-sm-2 control-label">Email Address</label>
|
<label for="email" class="col-sm-2 control-label">{{#translate}}Email Address{{/translate}}</label>
|
||||||
<div class="col-sm-10">
|
<div class="col-sm-10">
|
||||||
<input type="email" class="form-control input-lg" name="email" id="email" placeholder="" value="{{email}}" required>
|
<input type="email" class="form-control input-lg" name="email" id="email" placeholder="" value="{{email}}" required>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="first-name" class="col-sm-2 control-label">First Name</label>
|
<label for="first-name" class="col-sm-2 control-label">{{#translate}}First Name{{/translate}}</label>
|
||||||
<div class="col-sm-10">
|
<div class="col-sm-10">
|
||||||
<input type="text" class="form-control" name="first-name" id="first-name" placeholder="" value="{{firstName}}">
|
<input type="text" class="form-control" name="first-name" id="first-name" placeholder="" value="{{firstName}}">
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="last-name" class="col-sm-2 control-label">Last Name</label>
|
<label for="last-name" class="col-sm-2 control-label">{{#translate}}Last Name{{/translate}}</label>
|
||||||
<div class="col-sm-10">
|
<div class="col-sm-10">
|
||||||
<input type="text" class="form-control" name="last-name" id="last-name" placeholder="" value="{{lastName}}">
|
<input type="text" class="form-control" name="last-name" id="last-name" placeholder="" value="{{lastName}}">
|
||||||
</div>
|
</div>
|
||||||
|
@ -59,8 +61,8 @@
|
||||||
{{/if}}
|
{{/if}}
|
||||||
|
|
||||||
{{#if typeGpg}}
|
{{#if typeGpg}}
|
||||||
<textarea class="form-control gpg-text" rows="3" name="{{column}}" placeholder="Begins with '-----BEGIN PGP PUBLIC KEY BLOCK-----'">{{value}}</textarea>
|
<textarea class="form-control gpg-text" rows="3" name="{{column}}" placeholder="{{#translate}}Begins with{{/translate}} '-----BEGIN PGP PUBLIC KEY BLOCK-----'">{{value}}</textarea>
|
||||||
<span class="help-block">Insert a GPG public key that will be used to encrypt messages sent this subscriber</span>
|
<span class="help-block">{{#translate}}Insert a GPG public key that will be used to encrypt messages sent this subscriber{{/translate}}</span>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
|
|
||||||
{{#if typeDateUs}}
|
{{#if typeDateUs}}
|
||||||
|
@ -90,7 +92,7 @@
|
||||||
{{#if typeDropdown}}
|
{{#if typeDropdown}}
|
||||||
<select name="{{key}}" class="form-control">
|
<select name="{{key}}" class="form-control">
|
||||||
<option value="">
|
<option value="">
|
||||||
–– Select ––
|
–– {{#translate}}Select{{/translate}} ––
|
||||||
</option>
|
</option>
|
||||||
{{#each options}}
|
{{#each options}}
|
||||||
<option value="{{column}}" {{#if value}} selected {{/if}}>{{name}}</option>
|
<option value="{{column}}" {{#if value}} selected {{/if}}>{{name}}</option>
|
||||||
|
@ -122,11 +124,11 @@
|
||||||
{{/each}}
|
{{/each}}
|
||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="tz" class="col-sm-2 control-label">Timezone</label>
|
<label for="tz" class="col-sm-2 control-label">{{#translate}}Timezone{{/translate}}</label>
|
||||||
<div class="col-sm-10">
|
<div class="col-sm-10">
|
||||||
<select name="tz" class="form-control">
|
<select name="tz" class="form-control">
|
||||||
<option value="">
|
<option value="">
|
||||||
–– Select ––
|
–– {{#translate}}Select{{/translate}} ––
|
||||||
</option>
|
</option>
|
||||||
{{#each timezones}}
|
{{#each timezones}}
|
||||||
<option value="{{key}}" {{#if selected}} selected {{/if}}>{{value}}</option>
|
<option value="{{key}}" {{#if selected}} selected {{/if}}>{{value}}</option>
|
||||||
|
@ -139,8 +141,8 @@
|
||||||
<div class="col-sm-offset-2 col-sm-10">
|
<div class="col-sm-offset-2 col-sm-10">
|
||||||
<div class="checkbox">
|
<div class="checkbox">
|
||||||
<label>
|
<label>
|
||||||
<input type="checkbox" name="is-test" {{#if isTest}} checked {{/if}}> Test user?
|
<input type="checkbox" name="is-test" {{#if isTest}} checked {{/if}}> {{#translate}}Test user?{{/translate}}
|
||||||
<span class="help-block">If checked then this subscription can be used for previewing campaign messages</span>
|
<span class="help-block">{{#translate}}If checked then this subscription can be used for previewing campaign messages{{/translate}}</span>
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -151,10 +153,10 @@
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<div class="col-sm-offset-2 col-sm-10">
|
<div class="col-sm-offset-2 col-sm-10">
|
||||||
<p class="text-warning">
|
<p class="text-warning">
|
||||||
This person will not receive a confirmation email so make sure that you have permission to email them.
|
{{#translate}}This person will not receive a confirmation email so make sure that you have permission to email them.{{/translate}}
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<button type="submit" class="btn btn-primary">Subscribe</button>
|
<button type="submit" class="btn btn-primary">{{#translate}}Subscribe{{/translate}}</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
|
|
|
@ -1,11 +1,13 @@
|
||||||
<ol class="breadcrumb">
|
<ol class="breadcrumb">
|
||||||
<li><a href="/">Home</a></li>
|
<li><a href="/">{{#translate}}Home{{/translate}}</a></li>
|
||||||
<li><a href="/lists/">Lists</a></li>
|
<li><a href="/lists/">{{#translate}}Lists{{/translate}}</a></li>
|
||||||
<li><a href="/lists/view/{{list.id}}">{{list.name}}</a></li>
|
<li><a href="/lists/view/{{list.id}}">{{list.name}}</a></li>
|
||||||
<li class="active">Edit subscriber</li>
|
<li class="active">
|
||||||
|
{{#translate}}Edit subscriber{{/translate}}
|
||||||
|
</li>
|
||||||
</ol>
|
</ol>
|
||||||
|
|
||||||
<h2>{{list.name}} <small>Edit subscriber</small> <a class="btn btn-default btn-xs" href="/lists/view/{{list.id}}" role="button"><span class="glyphicon glyphicon-arrow-left" aria-hidden="true"></span> Back to list</a></h2>
|
<h2>{{list.name}} <small>{{#translate}}Edit subscriber{{/translate}}</small> <a class="btn btn-default btn-xs" href="/lists/view/{{list.id}}" role="button"><span class="glyphicon glyphicon-arrow-left" aria-hidden="true"></span> {{#translate}}Back to list{{/translate}}</a></h2>
|
||||||
|
|
||||||
<hr>
|
<hr>
|
||||||
|
|
||||||
|
@ -27,21 +29,21 @@
|
||||||
<input type="hidden" name="cid" value="{{cid}}">
|
<input type="hidden" name="cid" value="{{cid}}">
|
||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="email" class="col-sm-2 control-label">Email address</label>
|
<label for="email" class="col-sm-2 control-label">{{#translate}}Email address{{/translate}}</label>
|
||||||
<div class="col-sm-10">
|
<div class="col-sm-10">
|
||||||
<input type="email" class="form-control input-lg" name="email" id="email" placeholder="" value="{{email}}" required>
|
<input type="email" class="form-control input-lg" name="email" id="email" placeholder="" value="{{email}}" required>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="first-name" class="col-sm-2 control-label">First Name</label>
|
<label for="first-name" class="col-sm-2 control-label">{{#translate}}First Name{{/translate}}</label>
|
||||||
<div class="col-sm-10">
|
<div class="col-sm-10">
|
||||||
<input type="text" class="form-control" name="first-name" id="first-name" placeholder="" value="{{firstName}}">
|
<input type="text" class="form-control" name="first-name" id="first-name" placeholder="" value="{{firstName}}">
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="last-name" class="col-sm-2 control-label">Last Name</label>
|
<label for="last-name" class="col-sm-2 control-label">{{#translate}}Last Name{{/translate}}</label>
|
||||||
<div class="col-sm-10">
|
<div class="col-sm-10">
|
||||||
<input type="text" class="form-control" name="last-name" id="last-name" placeholder="" value="{{lastName}}">
|
<input type="text" class="form-control" name="last-name" id="last-name" placeholder="" value="{{lastName}}">
|
||||||
</div>
|
</div>
|
||||||
|
@ -72,8 +74,8 @@
|
||||||
{{/if}}
|
{{/if}}
|
||||||
|
|
||||||
{{#if typeGpg}}
|
{{#if typeGpg}}
|
||||||
<textarea class="form-control gpg-text" rows="3" name="{{column}}" placeholder="Begins with '-----BEGIN PGP PUBLIC KEY BLOCK-----'">{{value}}</textarea>
|
<textarea class="form-control gpg-text" rows="3" name="{{column}}" placeholder="{{#translate}}Begins with{{/translate}} '-----BEGIN PGP PUBLIC KEY BLOCK-----'">{{value}}</textarea>
|
||||||
<span class="help-block">Insert a GPG public key that will be used to encrypt messages sent this subscriber</span>
|
<span class="help-block">{{#translate}}Insert a GPG public key that will be used to encrypt messages sent this subscriber{{/translate}}</span>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
|
|
||||||
{{#if typeDateUs}}
|
{{#if typeDateUs}}
|
||||||
|
@ -103,7 +105,7 @@
|
||||||
{{#if typeDropdown}}
|
{{#if typeDropdown}}
|
||||||
<select name="{{key}}" class="form-control">
|
<select name="{{key}}" class="form-control">
|
||||||
<option value="">
|
<option value="">
|
||||||
–– Select ––
|
–– {{#translate}}Select{{/translate}} ––
|
||||||
</option>
|
</option>
|
||||||
{{#each options}}
|
{{#each options}}
|
||||||
<option value="{{column}}" {{#if value}} selected {{/if}}>{{name}}</option>
|
<option value="{{column}}" {{#if value}} selected {{/if}}>{{name}}</option>
|
||||||
|
@ -135,7 +137,7 @@
|
||||||
{{/each}}
|
{{/each}}
|
||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="tz" class="col-sm-2 control-label">Timezone</label>
|
<label for="tz" class="col-sm-2 control-label">{{#translate}}Timezone{{/translate}}</label>
|
||||||
<div class="col-sm-10">
|
<div class="col-sm-10">
|
||||||
<select name="tz" class="form-control">
|
<select name="tz" class="form-control">
|
||||||
<option value="">
|
<option value="">
|
||||||
|
@ -152,8 +154,8 @@
|
||||||
<div class="col-sm-offset-2 col-sm-10">
|
<div class="col-sm-offset-2 col-sm-10">
|
||||||
<div class="checkbox">
|
<div class="checkbox">
|
||||||
<label>
|
<label>
|
||||||
<input type="checkbox" name="is-test" {{#if isTest}} checked {{/if}}> Test user?
|
<input type="checkbox" name="is-test" {{#if isTest}} checked {{/if}}> {{#translate}}Test user?{{/translate}}
|
||||||
<span class="help-block">If checked then this subscription can be used for previewing campaign messages</span>
|
<span class="help-block">{{#translate}}If checked then this subscription can be used for previewing campaign messages{{/translate}}</span>
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -165,12 +167,12 @@
|
||||||
<div class="col-sm-offset-2 col-sm-10">
|
<div class="col-sm-offset-2 col-sm-10">
|
||||||
<div class="pull-right">
|
<div class="pull-right">
|
||||||
{{#if isSubscribed}}
|
{{#if isSubscribed}}
|
||||||
<button type="submit" form="subscriber-unsubscribe" class="btn btn-default"><i class="glyphicon glyphicon-ban-circle"></i> Unsubscribe</button>
|
<button type="submit" form="subscriber-unsubscribe" class="btn btn-default"><i class="glyphicon glyphicon-ban-circle"></i> {{#translate}}Unsubscribe{{/translate}}</button>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
<button type="submit" form="subscriber-delete" class="btn btn-danger"><i class="glyphicon glyphicon-remove"></i> Delete Subscription</button>
|
<button type="submit" form="subscriber-delete" class="btn btn-danger"><i class="glyphicon glyphicon-remove"></i> {{#translate}}Delete Subscription{{/translate}}</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<button type="submit" class="btn btn-primary"><i class="glyphicon glyphicon-ok"></i> Update</button>
|
<button type="submit" class="btn btn-primary"><i class="glyphicon glyphicon-ok"></i> {{#translate}}Update{{/translate}}</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
|
|
|
@ -1,16 +1,16 @@
|
||||||
<ol class="breadcrumb">
|
<ol class="breadcrumb">
|
||||||
<li><a href="/">Home</a></li>
|
<li><a href="/">{{#translate}}Home{{/translate}}</a></li>
|
||||||
<li><a href="/lists/">Lists</a></li>
|
<li><a href="/lists/">{{#translate}}Lists{{/translate}}</a></li>
|
||||||
<li><a href="/lists/view/{{list.id}}">{{list.name}}</a></li>
|
<li><a href="/lists/view/{{list.id}}">{{list.name}}</a></li>
|
||||||
<li class="active">Import status</li>
|
<li class="active">{{#translate}}Import status{{/translate}}</li>
|
||||||
</ol>
|
</ol>
|
||||||
|
|
||||||
<h2>{{list.name}} <small>Failed addresses</small> <a class="btn btn-default btn-xs" href="/lists/view/{{list.id}}" role="button"><span class="glyphicon glyphicon-arrow-left" aria-hidden="true"></span> Back to list</a></h2>
|
<h2>{{list.name}} <small>{{#translate}}Failed addresses{{/translate}}</small> <a class="btn btn-default btn-xs" href="/lists/view/{{list.id}}" role="button"><span class="glyphicon glyphicon-arrow-left" aria-hidden="true"></span> {{#translate}}Back to list{{/translate}}</a></h2>
|
||||||
|
|
||||||
<hr>
|
<hr>
|
||||||
|
|
||||||
<div class="well">
|
<div class="well">
|
||||||
Role-based addresses like <em><strong>postmaster</strong>@example.com</em> are blocked when importing. Subscribers with role-based email addresses can join your list using the <a href="/subscription/{{list.cid}}">subscription form</a>.
|
{{#translate}}Role-based addresses like postmaster@example.com are blocked when importing. Subscribers with role-based email addresses can join your list using the subscription form{{#translate}} (<a href="/subscription/{{list.cid}}">{{#translate}}see here{{/translate}}</a>).
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="table-responsive">
|
<div class="table-responsive">
|
||||||
|
@ -20,10 +20,10 @@
|
||||||
#
|
#
|
||||||
</th>
|
</th>
|
||||||
<th>
|
<th>
|
||||||
Address
|
{{#translate}}Address{{/translate}}
|
||||||
</th>
|
</th>
|
||||||
<th>
|
<th>
|
||||||
Fail reason
|
{{#translate}}Fail reason{{/translate}}
|
||||||
</th>
|
</th>
|
||||||
</thead>
|
</thead>
|
||||||
{{#if rows}}
|
{{#if rows}}
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
<ol class="breadcrumb">
|
<ol class="breadcrumb">
|
||||||
<li><a href="/">Home</a></li>
|
<li><a href="/">{{#translate}}Home{{/translate}}</a></li>
|
||||||
<li><a href="/lists/">Lists</a></li>
|
<li><a href="/lists/">{{#translate}}Lists{{/translate}}</a></li>
|
||||||
<li><a href="/lists/view/{{list.id}}">{{list.name}}</a></li>
|
<li><a href="/lists/view/{{list.id}}">{{list.name}}</a></li>
|
||||||
<li class="active">Import subscribers</li>
|
<li class="active">{{#translate}}Import subscribers{{/translate}}</li>
|
||||||
</ol>
|
</ol>
|
||||||
|
|
||||||
<h2>{{list.name}} <small>Import subscribers</small></h2>
|
<h2>{{list.name}} <small>{{#translate}}Import subscribers{{/translate}}</small></h2>
|
||||||
|
|
||||||
<hr>
|
<hr>
|
||||||
|
|
||||||
|
@ -20,11 +20,11 @@
|
||||||
<label for="column-{{@index}}" class="col-sm-2 control-label">{{this}}</label>
|
<label for="column-{{@index}}" class="col-sm-2 control-label">{{this}}</label>
|
||||||
<div class="col-sm-10">
|
<div class="col-sm-10">
|
||||||
<select class="form-control" id="column-{{@index}}" name="column-{{@index}}">
|
<select class="form-control" id="column-{{@index}}" name="column-{{@index}}">
|
||||||
<option value=""> –– Select –– </option>
|
<option value=""> –– {{#translate}}Select{{/translate}} –– </option>
|
||||||
<option value="email">Email address</option>
|
<option value="email">{{#translate}}Email address{{/translate}}</option>
|
||||||
<option value="first_name">First Name</option>
|
<option value="first_name">{{#translate}}First Name{{/translate}}</option>
|
||||||
<option value="last_name">Last Name</option>
|
<option value="last_name">{{#translate}}Last Name{{/translate}}</option>
|
||||||
<option value="tz">Timezone</option>
|
<option value="tz">{{#translate}}Timezone{{/translate}}</option>
|
||||||
{{#each ../customFields}}
|
{{#each ../customFields}}
|
||||||
{{#if column}}
|
{{#if column}}
|
||||||
<option value="{{column}}">{{name}}</option>
|
<option value="{{column}}">{{name}}</option>
|
||||||
|
@ -39,7 +39,7 @@
|
||||||
{{/each}}
|
{{/each}}
|
||||||
</select>
|
</select>
|
||||||
|
|
||||||
<span id="helpBlock" class="help-block">Example: "{{lookup ../mapping.example @index}}"</span>
|
<span id="helpBlock" class="help-block">{{#translate}}Example{{/translate}}: "{{lookup ../mapping.example @index}}"</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@ -49,7 +49,7 @@
|
||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<div class="col-sm-offset-2 col-sm-10">
|
<div class="col-sm-offset-2 col-sm-10">
|
||||||
<button type="submit" class="btn btn-primary">Start import</button>
|
<button type="submit" class="btn btn-primary">{{#translate}}Start import{{/translate}}</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
<ol class="breadcrumb">
|
<ol class="breadcrumb">
|
||||||
<li><a href="/">Home</a></li>
|
<li><a href="/">{{#translate}}Home{{/translate}}</a></li>
|
||||||
<li><a href="/lists/">Lists</a></li>
|
<li><a href="/lists/">{{#translate}}Lists{{/translate}}</a></li>
|
||||||
<li><a href="/lists/view/{{list.id}}">{{list.name}}</a></li>
|
<li><a href="/lists/view/{{list.id}}">{{list.name}}</a></li>
|
||||||
<li class="active">Import subscribers</li>
|
<li class="active">{{#translate}}Import subscribers{{/translate}}</li>
|
||||||
</ol>
|
</ol>
|
||||||
|
|
||||||
<h2>{{list.name}} <small>Import subscribers</small></h2>
|
<h2>{{list.name}} <small>{{#translate}}Import subscribers{{/translate}}</small></h2>
|
||||||
|
|
||||||
<hr>
|
<hr>
|
||||||
|
|
||||||
|
@ -14,30 +14,30 @@
|
||||||
<input type="hidden" name="list" value="{{list.id}}">
|
<input type="hidden" name="list" value="{{list.id}}">
|
||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="listimport" class="col-sm-2 control-label">CSV File</label>
|
<label for="listimport" class="col-sm-2 control-label">{{#translate}}CSV File{{/translate}}</label>
|
||||||
<div class="col-sm-6">
|
<div class="col-sm-6">
|
||||||
<input type="file" class="form-control" name="listimport" id="listimport" required>
|
<input type="file" class="form-control" name="listimport" id="listimport" required>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="delimiter" class="col-sm-2 control-label">CSV delimiter</label>
|
<label for="delimiter" class="col-sm-2 control-label">{{#translate}}CSV delimiter{{/translate}}</label>
|
||||||
<div class="col-sm-1">
|
<div class="col-sm-1">
|
||||||
<input type="text" class="form-control" name="delimiter" id="delimiter" placeholder="" value="{{delimiter}}">
|
<input type="text" class="form-control" name="delimiter" id="delimiter" placeholder="" value="{{delimiter}}">
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label class="col-sm-2 control-label">Categorize the imported subscribers as:</label>
|
<label class="col-sm-2 control-label">{{#translate}}Categorize the imported subscribers as{{/translate}}:</label>
|
||||||
<div class="col-sm-6">
|
<div class="col-sm-6">
|
||||||
<div class="radio">
|
<div class="radio">
|
||||||
<label>
|
<label>
|
||||||
<input type="radio" name="type" id="type" value="subscribed" checked> Subscribed – <span class="text-muted">Regular subscriber addresses</span>
|
<input type="radio" name="type" id="type" value="subscribed" checked> {{#translate}}Subscribed{{/translate}} – <span class="text-muted">{{#translate}}Regular subscriber addresses{{/translate}}</span>
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
<div class="radio">
|
<div class="radio">
|
||||||
<label>
|
<label>
|
||||||
<input type="radio" name="type" id="type" value="unsubscribed"> Unsubscribed – <span class="text-muted">Suppressed emails that will be unsubscribed from your list</span>
|
<input type="radio" name="type" id="type" value="unsubscribed"> {{#translate}}Unsubscribed{{/translate}} – <span class="text-muted">{{#translate}}Suppressed emails that will be unsubscribed from your list{{/translate}}</span>
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -47,7 +47,7 @@
|
||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<div class="col-sm-offset-2 col-sm-10">
|
<div class="col-sm-offset-2 col-sm-10">
|
||||||
<button type="submit" class="btn btn-primary"><span class="glyphicon glyphicon-chevron-right" aria-hidden="true"></span> Next</button>
|
<button type="submit" class="btn btn-primary"><span class="glyphicon glyphicon-chevron-right" aria-hidden="true"></span> {{#translate}}Next{{/translate}}</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
|
|
|
@ -1,25 +1,25 @@
|
||||||
<ol class="breadcrumb">
|
<ol class="breadcrumb">
|
||||||
<li><a href="/">Home</a></li>
|
<li><a href="/">{{#translate}}Home{{/translate}}</a></li>
|
||||||
<li><a href="/lists/">Lists</a></li>
|
<li><a href="/lists/">{{#translate}}Lists{{/translate}}</a></li>
|
||||||
<li class="active">{{name}}</li>
|
<li class="active">{{name}}</li>
|
||||||
</ol>
|
</ol>
|
||||||
|
|
||||||
<div class="pull-right">
|
<div class="pull-right">
|
||||||
|
|
||||||
<a class="btn btn-default" href="/subscription/{{cid}}" target="_blank" role="button"><span class="glyphicon glyphicon-share-alt" aria-hidden="true"></span> Subscription Form</a>
|
<a class="btn btn-default" href="/subscription/{{cid}}" target="_blank" role="button"><span class="glyphicon glyphicon-share-alt" aria-hidden="true"></span> {{#translate}}Subscription Form{{/translate}}</a>
|
||||||
|
|
||||||
<div class="btn-group">
|
<div class="btn-group">
|
||||||
<button type="button" class="btn btn-primary dropdown-toggle" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
|
<button type="button" class="btn btn-primary dropdown-toggle" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
|
||||||
List Actions <span class="caret"></span>
|
{{#translate}}List Actions{{/translate}} <span class="caret"></span>
|
||||||
</button>
|
</button>
|
||||||
<ul class="dropdown-menu">
|
<ul class="dropdown-menu">
|
||||||
<li><a href="/fields/{{id}}" role="button"><span class="glyphicon glyphicon-tasks" aria-hidden="true"></span> Custom Fields</a></li>
|
<li><a href="/fields/{{id}}" role="button"><span class="glyphicon glyphicon-tasks" aria-hidden="true"></span> {{#translate}}Custom Fields{{/translate}}</a></li>
|
||||||
<li><a href="/segments/{{id}}" role="button"><span class="glyphicon glyphicon-filter" aria-hidden="true"></span> Segments</a></li>
|
<li><a href="/segments/{{id}}" role="button"><span class="glyphicon glyphicon-filter" aria-hidden="true"></span> {{#translate}}Segments{{/translate}}</a></li>
|
||||||
<li><a href="/lists/edit/{{id}}" role="button"><span class="glyphicon glyphicon-wrench" aria-hidden="true"></span> Edit List</a></li>
|
<li><a href="/lists/edit/{{id}}" role="button"><span class="glyphicon glyphicon-wrench" aria-hidden="true"></span> {{#translate}}Edit List{{/translate}}</a></li>
|
||||||
<li><a href="/triggers/{{id}}/create" role="button"><span class="glyphicon glyphicon-console" aria-hidden="true"></span> Create Trigger</a></li>
|
<li><a href="/triggers/{{id}}/create" role="button"><span class="glyphicon glyphicon-console" aria-hidden="true"></span> {{#translate}}Create Trigger{{/translate}}</a></li>
|
||||||
<li role="separator" class="divider"></li>
|
<li role="separator" class="divider"></li>
|
||||||
<li><a href="/lists/subscription/{{id}}/add" role="button"><span class="glyphicon glyphicon-plus-sign" aria-hidden="true"></span> Add Subscriber</a></li>
|
<li><a href="/lists/subscription/{{id}}/add" role="button"><span class="glyphicon glyphicon-plus-sign" aria-hidden="true"></span> {{#translate}}Add Subscriber{{/translate}}</a></li>
|
||||||
<li><a href="/lists/subscription/{{id}}/import" role="button"><span class="glyphicon glyphicon-cloud-upload" aria-hidden="true"></span> Import Subscribers</a></li>
|
<li><a href="/lists/subscription/{{id}}/import" role="button"><span class="glyphicon glyphicon-cloud-upload" aria-hidden="true"></span> {{#translate}}Import Subscribers{{/translate}}</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -35,35 +35,35 @@
|
||||||
<div class="well well-sm">
|
<div class="well well-sm">
|
||||||
{{#if useSegment}}
|
{{#if useSegment}}
|
||||||
<div class="pull-right">
|
<div class="pull-right">
|
||||||
<a class="btn btn-primary btn-sm" href="/segments/{{id}}/edit/{{segment}}" role="button"><span class="glyphicon glyphicon-wrench" aria-hidden="true"></span> Edit Segment</a>
|
<a class="btn btn-primary btn-sm" href="/segments/{{id}}/edit/{{segment}}" role="button"><span class="glyphicon glyphicon-wrench" aria-hidden="true"></span> {{#translate}}Edit Segment{{/translate}}</a>
|
||||||
</div>
|
</div>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
<form class="form-inline" method="get">
|
<form class="form-inline" method="get">
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="exampleInputName2">Segment</label>
|
<label for="exampleInputName2">{{#translate}}Segment{{/translate}}</label>
|
||||||
<select name="segment" class="form-control">
|
<select name="segment" class="form-control">
|
||||||
<option value="0">All Subscriptions</option>
|
<option value="0">All Subscriptions</option>
|
||||||
{{#if segments}}
|
{{#if segments}}
|
||||||
<optgroup label="Segments">
|
<optgroup label="{{#translate}}Segments{{/translate}}">
|
||||||
{{#each segments}}
|
{{#each segments}}
|
||||||
<option value="{{id}}" {{#if selected}} selected {{/if}}>{{name}}</option>
|
<option value="{{id}}" {{#if selected}} selected {{/if}}>{{name}}</option>
|
||||||
{{/each}}
|
{{/each}}
|
||||||
</optgroup>
|
</optgroup>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
<optgroup label="Actions">
|
<optgroup label="Actions">
|
||||||
<option value="-1">Create New Segment…</option>
|
<option value="-1">{{#translate}}Create New Segment{{/translate}}…</option>
|
||||||
</optgroup>
|
</optgroup>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
<button type="submit" class="btn btn-default"><span class="glyphicon glyphicon-filter" aria-hidden="true"></span> Filter</button>
|
<button type="submit" class="btn btn-default"><span class="glyphicon glyphicon-filter" aria-hidden="true"></span> {{#translate}}Filter{{/translate}}</button>
|
||||||
</form>
|
</form>
|
||||||
<div class="clearfix"></div>
|
<div class="clearfix"></div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Nav tabs -->
|
<!-- Nav tabs -->
|
||||||
<ul class="nav nav-tabs" role="tablist">
|
<ul class="nav nav-tabs" role="tablist">
|
||||||
<li role="presentation" class="{{#if showSubscriptions}}active{{/if}}"><a href="/lists/view/{{id}}" aria-controls="subscriptions" role="tab">Subscriptions</a></li>
|
<li role="presentation" class="{{#if showSubscriptions}}active{{/if}}"><a href="/lists/view/{{id}}" aria-controls="subscriptions" role="tab">{{#translate}}Subscriptions{{/translate}}</a></li>
|
||||||
<li role="presentation" class="{{#if showImports}}active{{/if}}"><a href="/lists/view/{{id}}?tab=imports" aria-controls="imports" role="tab">Imports</a></li>
|
<li role="presentation" class="{{#if showImports}}active{{/if}}"><a href="/lists/view/{{id}}?tab=imports" aria-controls="imports" role="tab">{{#translate}}Imports{{/translate}}</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
<div class="tab-content">
|
<div class="tab-content">
|
||||||
|
@ -79,13 +79,13 @@
|
||||||
#
|
#
|
||||||
</th>
|
</th>
|
||||||
<th>
|
<th>
|
||||||
Address
|
{{#translate}}Address{{/translate}}
|
||||||
</th>
|
</th>
|
||||||
<th>
|
<th>
|
||||||
First Name
|
{{#translate}}First Name{{/translate}}
|
||||||
</th>
|
</th>
|
||||||
<th>
|
<th>
|
||||||
Last Name
|
{{#translate}}Last Name{{/translate}}
|
||||||
</th>
|
</th>
|
||||||
{{#each customFields}}
|
{{#each customFields}}
|
||||||
<th>
|
<th>
|
||||||
|
@ -93,10 +93,10 @@
|
||||||
</th>
|
</th>
|
||||||
{{/each}}
|
{{/each}}
|
||||||
<th>
|
<th>
|
||||||
Status
|
{{#translate}}Status{{/translate}}
|
||||||
</th>
|
</th>
|
||||||
<th>
|
<th>
|
||||||
Created
|
{{#translate}}Created{{/translate}}
|
||||||
</th>
|
</th>
|
||||||
<th></th>
|
<th></th>
|
||||||
</tr>
|
</tr>
|
||||||
|
@ -117,25 +117,25 @@
|
||||||
#
|
#
|
||||||
</th>
|
</th>
|
||||||
<th>
|
<th>
|
||||||
Created
|
{{#translate}}Created{{/translate}}
|
||||||
</th>
|
</th>
|
||||||
<th>
|
<th>
|
||||||
Finished
|
{{#translate}}Finished{{/translate}}
|
||||||
</th>
|
</th>
|
||||||
<th>
|
<th>
|
||||||
Type
|
{{#translate}}Type{{/translate}}
|
||||||
</th>
|
</th>
|
||||||
<th>
|
<th>
|
||||||
Added
|
{{#translate}}Added{{/translate}}
|
||||||
</th>
|
</th>
|
||||||
<th>
|
<th>
|
||||||
Updated
|
{{#translate}}Updated{{/translate}}
|
||||||
</th>
|
</th>
|
||||||
<th>
|
<th>
|
||||||
Failed
|
{{#translate}}Failed{{/translate}}
|
||||||
</th>
|
</th>
|
||||||
<th>
|
<th>
|
||||||
Status
|
{{#translate}}Status{{/translate}}
|
||||||
</th>
|
</th>
|
||||||
<th>
|
<th>
|
||||||
|
|
||||||
|
@ -181,11 +181,11 @@
|
||||||
{{/if}}
|
{{/if}}
|
||||||
</td>
|
</td>
|
||||||
<td class="text-center">
|
<td class="text-center">
|
||||||
<form method="post" class="confirm-submit" data-confirm-message="Are you sure? This action should only be called to resolve stalled imports" action="/lists/subscription/import-restart">
|
<form method="post" class="confirm-submit" data-confirm-message="{{#translate}}Are you sure? This action should only be called to resolve stalled imports{{/translate}}" action="/lists/subscription/import-restart">
|
||||||
<input type="hidden" name="_csrf" value="{{../csrfToken}}">
|
<input type="hidden" name="_csrf" value="{{../csrfToken}}">
|
||||||
<input type="hidden" name="list" value="{{list}}">
|
<input type="hidden" name="list" value="{{list}}">
|
||||||
<input type="hidden" name="import" value="{{id}}">
|
<input type="hidden" name="import" value="{{id}}">
|
||||||
<button type="submit" class="btn btn-info btn-xs"><span class="glyphicon glyphicon-repeat" aria-hidden="true"></span> Restart</button>
|
<button type="submit" class="btn btn-info btn-xs"><span class="glyphicon glyphicon-repeat" aria-hidden="true"></span> {{#translate}}Restart{{/translate}}</button>
|
||||||
</form>
|
</form>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
@ -193,7 +193,7 @@
|
||||||
{{else}}
|
{{else}}
|
||||||
<tr>
|
<tr>
|
||||||
<td colspan="9">
|
<td colspan="9">
|
||||||
No data available in table
|
{{#translate}}No data available in table{{/translate}}
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="template-html" class="col-sm-2 control-label">Template content (HTML)</label>
|
<label for="template-html" class="col-sm-2 control-label">{{#translate}}Template content (HTML){{/translate}}</label>
|
||||||
<div class="col-sm-10">
|
<div class="col-sm-10">
|
||||||
<div class="code-editor" id="template-html">{{html}}</div>
|
<div class="code-editor" id="template-html">{{html}}</div>
|
||||||
<input type="hidden" name="html">
|
<input type="hidden" name="html">
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
<div class="form-group" id="html-preview" {{#unless html}}style="display: none;"{{/unless}}>
|
<div class="form-group" id="html-preview" {{#unless html}}style="display: none;"{{/unless}}>
|
||||||
<div class="col-sm-offset-2 col-sm-10">
|
<div class="col-sm-offset-2 col-sm-10">
|
||||||
<a role="button" data-toggle="collapse" href="#html-preview-toggle" aria-expanded="false" aria-controls="html-preview-toggle">Toggle HTML preview</a>
|
<a role="button" data-toggle="collapse" href="#html-preview-toggle" aria-expanded="false" aria-controls="html-preview-toggle">{{#translate}}Toggle HTML preview{{/translate}}</a>
|
||||||
<div class="collapse" id="html-preview-toggle">
|
<div class="collapse" id="html-preview-toggle">
|
||||||
<h6 class="small text-muted">320x480px</h6>
|
<h6 class="small text-muted">320x480px</h6>
|
||||||
<iframe
|
<iframe
|
||||||
|
|
|
@ -1,21 +1,20 @@
|
||||||
{{#if mergeTags}}
|
{{#if mergeTags}}
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<div class="col-sm-offset-2 col-sm-10">
|
<div class="col-sm-offset-2 col-sm-10">
|
||||||
<a class="btn btn-default" role="button" data-toggle="collapse" href="#mergeReference" aria-expanded="false" aria-controls="mergeReference">Merge tag reference</a>
|
<a class="btn btn-default" role="button" data-toggle="collapse" href="#mergeReference" aria-expanded="false" aria-controls="mergeReference">{{#translate}}Merge tag reference{{/translate}}</a>
|
||||||
<div class="collapse" id="mergeReference">
|
<div class="collapse" id="mergeReference">
|
||||||
<p style="margin-top: .8em;">
|
<p style="margin-top: .8em;">
|
||||||
Merge tags are tags that are replaced before sending out the message. The format of the merge tag is the following: <code>[TAG_NAME]</code> or <code>[TAG_NAME/fallback]</code> where <code>fallback</code> is an optional text value
|
{{#translate}}Merge tags are tags that are replaced before sending out the message. The format of the merge tag is the following: <code>[TAG_NAME]</code> or <code>[TAG_NAME/fallback]</code> where <code>fallback</code> is an optional text value used when <code>TAG_NAME</code> is empty.{{/translate}}
|
||||||
used when <code>TAG_NAME</code> is empty.
|
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<table class="table table-bordered table-condensed table-striped">
|
<table class="table table-bordered table-condensed table-striped">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th>
|
<th>
|
||||||
Merge tag
|
{{#translate}}Merge tag{{/translate}}
|
||||||
</th>
|
</th>
|
||||||
<th>
|
<th>
|
||||||
Description
|
{{#translate}}Description{{/translate}}
|
||||||
</th>
|
</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="template-text" class="col-sm-2 control-label">Template content (plaintext)</label>
|
<label for="template-text" class="col-sm-2 control-label">{{#translate}}Template content (plaintext){{/translate}}</label>
|
||||||
<div class="col-sm-10">
|
<div class="col-sm-10">
|
||||||
<textarea class="form-control" id="template-text" name="text" rows="10">{{text}}</textarea>
|
<textarea class="form-control" id="template-text" name="text" rows="10">{{text}}</textarea>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="template-html" class="col-sm-2 control-label">Template content (HTML)</label>
|
<label for="template-html" class="col-sm-2 control-label">{{#translate}}Template content (HTML){{/translate}}</label>
|
||||||
<div class="col-sm-10">
|
<div class="col-sm-10">
|
||||||
<textarea class="form-control summernote" id="template-html" name="html" rows="8">{{html}}</textarea>
|
<textarea class="form-control summernote" id="template-html" name="html" rows="8">{{html}}</textarea>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
<ol class="breadcrumb">
|
<ol class="breadcrumb">
|
||||||
<li><a href="/">Home</a></li>
|
<li><a href="/">{{#translate}}Home{{/translate}}</a></li>
|
||||||
<li class="active">Settings</li>
|
<li class="active">{{#translate}}Settings{{/translate}}</li>
|
||||||
</ol>
|
</ol>
|
||||||
|
|
||||||
<h2>Settings</h2>
|
<h2>{{#translate}}Settings{{/translate}}</h2>
|
||||||
|
|
||||||
<hr>
|
<hr>
|
||||||
|
|
||||||
|
@ -17,22 +17,22 @@
|
||||||
|
|
||||||
<fieldset>
|
<fieldset>
|
||||||
<legend>
|
<legend>
|
||||||
General Settings
|
{{#translate}}General Settings{{/translate}}
|
||||||
</legend>
|
</legend>
|
||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="service-url" class="col-sm-2 control-label">Service Address (URL)</label>
|
<label for="service-url" class="col-sm-2 control-label">{{#translate}}Service Address (URL){{/translate}}</label>
|
||||||
<div class="col-sm-10">
|
<div class="col-sm-10">
|
||||||
<input type="url" class="form-control" name="service-url" id="service-url" placeholder="http://example.com/" value="{{serviceUrl}}" required>
|
<input type="url" class="form-control" name="service-url" id="service-url" placeholder="http://example.com/" value="{{serviceUrl}}" required>
|
||||||
<span class="help-block">Enter the URL this service can be reached from</span>
|
<span class="help-block">{{#translate}}Enter the URL this service can be reached from{{/translate}}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="admin-email" class="col-sm-2 control-label">Admin Email</label>
|
<label for="admin-email" class="col-sm-2 control-label">{{#translate}}Admin Email{{/translate}}</label>
|
||||||
<div class="col-sm-10">
|
<div class="col-sm-10">
|
||||||
<input type="email" class="form-control" name="admin-email" id="admin-email" placeholder="admin@example.com" value="{{adminEmail}}" required>
|
<input type="email" class="form-control" name="admin-email" id="admin-email" placeholder="admin@example.com" value="{{adminEmail}}" required>
|
||||||
<span class="help-block">Enter the email address that will be used as "from" for system messages</span>
|
<span class="help-block">{{#translate}}Enter the email address that will be used as "from" for system messages{{/translate}}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@ -40,8 +40,8 @@
|
||||||
<div class="col-sm-offset-2 col-sm-10">
|
<div class="col-sm-offset-2 col-sm-10">
|
||||||
<div class="checkbox">
|
<div class="checkbox">
|
||||||
<label>
|
<label>
|
||||||
<input type="checkbox" name="disable-wysiwyg" {{#if disableWysiwyg}} checked {{/if}}> Disable WYSIWYG editor
|
<input type="checkbox" name="disable-wysiwyg" {{#if disableWysiwyg}} checked {{/if}}> {{#translate}}Disable WYSIWYG editor{{/translate}}
|
||||||
<span class="help-block">If checked then message editor displays HTML code without the preview</span>
|
<span class="help-block">{{#translate}}If checked then message editor displays HTML code without the preview{{/translate}}</span>
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -51,26 +51,26 @@
|
||||||
<div class="col-sm-offset-2 col-sm-10">
|
<div class="col-sm-offset-2 col-sm-10">
|
||||||
<div class="checkbox">
|
<div class="checkbox">
|
||||||
<label>
|
<label>
|
||||||
<input type="checkbox" name="disable-confirmations" {{#if disableConfirmations}} checked {{/if}}> Disable subscription confirmation messages
|
<input type="checkbox" name="disable-confirmations" {{#if disableConfirmations}} checked {{/if}}> {{#translate}}Disable subscription confirmation messages{{/translate}}
|
||||||
<span class="help-block">If checked then do not send a confirmation message that states the subscriber is now subscribed or unsubscribed. This does not disable double opt-in messages.</span>
|
<span class="help-block">{{#translate}}If checked then do not send a confirmation message that states the subscriber is now subscribed or unsubscribed. This does not disable double opt-in messages.{{/translate}}</span>
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="ua-code" class="col-sm-2 control-label">Tracking ID</label>
|
<label for="ua-code" class="col-sm-2 control-label">{{#translate}}Tracking ID{{/translate}}</label>
|
||||||
<div class="col-sm-10">
|
<div class="col-sm-10">
|
||||||
<input type="text" class="form-control" name="ua-code" id="ua-code-url" placeholder="UA-XXXXX-XX" value="{{uaCode}}">
|
<input type="text" class="form-control" name="ua-code" id="ua-code-url" placeholder="UA-XXXXX-XX" value="{{uaCode}}">
|
||||||
<span class="help-block">Enter Google Analytics tracking code</span>
|
<span class="help-block">{{#translate}}Enter Google Analytics tracking code{{/translate}}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="shoutout" class="col-sm-2 control-label">Frontpage shout out</label>
|
<label for="shoutout" class="col-sm-2 control-label">{{#translate}}Frontpage shout out{{/translate}}</label>
|
||||||
<div class="col-sm-10">
|
<div class="col-sm-10">
|
||||||
<textarea class="form-control gpg-text" rows="3" id="shoutout" name="shoutout" placeholder="">{{shoutout}}</textarea>
|
<textarea class="form-control gpg-text" rows="3" id="shoutout" name="shoutout" placeholder="">{{shoutout}}</textarea>
|
||||||
<span class="help-block">HTML code shown in the front page header section</span>
|
<span class="help-block">{{#translate}}HTML code shown in the front page header section{{/translate}}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@ -78,47 +78,47 @@
|
||||||
|
|
||||||
<fieldset>
|
<fieldset>
|
||||||
<legend>
|
<legend>
|
||||||
Campaign defaults
|
{{#translate}}Campaign defaults{{/translate}}
|
||||||
</legend>
|
</legend>
|
||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="default-sender" class="col-sm-2 control-label">Sender name</label>
|
<label for="default-sender" class="col-sm-2 control-label">{{#translate}}Sender name{{/translate}}</label>
|
||||||
<div class="col-sm-10">
|
<div class="col-sm-10">
|
||||||
<input type="text" class="form-control" name="default-sender" id="default-sender" value="{{defaultSender}}" placeholder="Sender name, eg. "My Awesome Company Ltd."">
|
<input type="text" class="form-control" name="default-sender" id="default-sender" value="{{defaultSender}}" placeholder="{{#translate}}Sender name, eg. My Awesome Company Ltd.{{/translate}}">
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="default-postaddress" class="col-sm-2 control-label">Default address</label>
|
<label for="default-postaddress" class="col-sm-2 control-label">{{#translate}}Default address{{/translate}}</label>
|
||||||
<div class="col-sm-10">
|
<div class="col-sm-10">
|
||||||
<input type="text" class="form-control" name="default-postaddress" id="default-postaddress" value="{{defaultPostaddress}}" placeholder="Contact address to provide, eg. "1234 Main Street, Anywhere, MA 01234, USA"">
|
<input type="text" class="form-control" name="default-postaddress" id="default-postaddress" value="{{defaultPostaddress}}" placeholder="{{#translate}}Contact address to provide, eg. 1234 Main Street, Anywhere, MA 01234, USA{{/translate}}">
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<hr />
|
<hr />
|
||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="default-from" class="col-sm-2 control-label">Default "from name"</label>
|
<label for="default-from" class="col-sm-2 control-label">{{#translate}}Default "from name"{{/translate}}</label>
|
||||||
<div class="col-sm-10">
|
<div class="col-sm-10">
|
||||||
<input type="text" class="form-control" name="default-from" id="default-from" value="{{defaultFrom}}" placeholder="This is the name your emails will come from" required>
|
<input type="text" class="form-control" name="default-from" id="default-from" value="{{defaultFrom}}" placeholder="{{#translate}}This is the name your emails will come from{{/translate}}" required>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="default-address" class="col-sm-2 control-label">Default "from" email</label>
|
<label for="default-address" class="col-sm-2 control-label">{{#translate}}Default "from" email{{/translate}}</label>
|
||||||
<div class="col-sm-10">
|
<div class="col-sm-10">
|
||||||
<input type="email" class="form-control" name="default-address" id="default-address" value="{{defaultAddress}}" placeholder="This is the address people will send replies to" required>
|
<input type="email" class="form-control" name="default-address" id="default-address" value="{{defaultAddress}}" placeholder="{{#translate}}This is the address people will send replies to{{/translate}}" required>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="default-subject" class="col-sm-2 control-label">Default "subject line"</label>
|
<label for="default-subject" class="col-sm-2 control-label">{{#translate}}Default "subject line"{{/translate}}</label>
|
||||||
<div class="col-sm-10">
|
<div class="col-sm-10">
|
||||||
<input type="text" class="form-control" name="default-subject" id="default-subject" value="{{defaultSubject}}" placeholder="Keep it relevant and non-spammy">
|
<input type="text" class="form-control" name="default-subject" id="default-subject" value="{{defaultSubject}}" placeholder="{{#translate}}Keep it relevant and non-spammy{{/translate}}">
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="default-homepage" class="col-sm-2 control-label">Default homepage (URL)</label>
|
<label for="default-homepage" class="col-sm-2 control-label">{{#translate}}Default homepage (URL){{/translate}}</label>
|
||||||
<div class="col-sm-10">
|
<div class="col-sm-10">
|
||||||
<input type="url" class="form-control" name="default-homepage" id="default-homepage" value="{{defaultHomepage}}" placeholder="URL to redirect the subscribed users to, eg. http://example.com/">
|
<input type="url" class="form-control" name="default-homepage" id="default-homepage" value="{{defaultHomepage}}" placeholder="{{#translate}}URL to redirect the subscribed users to, eg. http://example.com/{{/translate}}">
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@ -127,15 +127,15 @@
|
||||||
|
|
||||||
<fieldset>
|
<fieldset>
|
||||||
<legend>
|
<legend>
|
||||||
Mailer Settings
|
{{#translate}}Mailer Settings{{/translate}}
|
||||||
</legend>
|
</legend>
|
||||||
|
|
||||||
<p class="text-info">These settings are required to send out e-mail messages</p>
|
<p class="text-info">{{#translate}}These settings are required to send out e-mail messages{{/translate}}</p>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<ul class="nav nav-tabs" role="tablist">
|
<ul class="nav nav-tabs" role="tablist">
|
||||||
<li role="presentation" class="{{#if useSMTP}} active {{/if}}"><a href="#smtp-settings" aria-controls="smtp-settings" role="tab" data-toggle="tab">SMTP</a></li>
|
<li role="presentation" class="{{#if useSMTP}} active {{/if}}"><a href="#smtp-settings" aria-controls="smtp-settings" role="tab" data-toggle="tab">{{#translate}}SMTP{{/translate}}</a></li>
|
||||||
<li role="presentation" class="{{#if useSES}} active {{/if}}"><a href="#aws-ses" aria-controls="aws-ses" role="tab" data-toggle="tab">AWS SES</a></li>
|
<li role="presentation" class="{{#if useSES}} active {{/if}}"><a href="#aws-ses" aria-controls="aws-ses" role="tab" data-toggle="tab">{{#translate}}AWS SES{{/translate}}</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
<div class="tab-content">
|
<div class="tab-content">
|
||||||
<div role="tabpanel" class="tab-pane {{#if useSMTP}} active {{/if}}" id="smtp-settings">
|
<div role="tabpanel" class="tab-pane {{#if useSMTP}} active {{/if}}" id="smtp-settings">
|
||||||
|
@ -146,28 +146,28 @@
|
||||||
<div class="radio">
|
<div class="radio">
|
||||||
<label>
|
<label>
|
||||||
<input type="radio" name="mail-transport" id="transport-smtp" value="smtp" {{#if useSMTP}} checked {{/if}}>
|
<input type="radio" name="mail-transport" id="transport-smtp" value="smtp" {{#if useSMTP}} checked {{/if}}>
|
||||||
Use SMTP for sending mail
|
{{#translate}}Use SMTP for sending mail{{/translate}}
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="smtp-hostname" class="col-sm-2 control-label">Hostname</label>
|
<label for="smtp-hostname" class="col-sm-2 control-label">{{#translate}}Hostname{{/translate}}</label>
|
||||||
<div class="col-sm-10">
|
<div class="col-sm-10">
|
||||||
<input type="text" class="form-control" name="smtp-hostname" id="smtp-hostname" placeholder="Hostname, eg. smtp.example.com" value="{{smtpHostname}}" required>
|
<input type="text" class="form-control" name="smtp-hostname" id="smtp-hostname" placeholder="Hostname, eg. smtp.example.com" value="{{smtpHostname}}" required>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="smtp-port" class="col-sm-2 control-label">Port</label>
|
<label for="smtp-port" class="col-sm-2 control-label">{{#translate}}Port{{/translate}}</label>
|
||||||
<div class="col-sm-10">
|
<div class="col-sm-10">
|
||||||
<input type="number" class="form-control" name="smtp-port" id="smtp-port" placeholder="Port, eg. 465. Autodetected if left blank" value="{{smtpPort}}">
|
<input type="number" class="form-control" name="smtp-port" id="smtp-port" placeholder="{{#translate}}Port, eg. 465. Autodetected if left blank{{/translate}}" value="{{smtpPort}}">
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="smtp-encryption" class="col-sm-2 control-label">Encryption</label>
|
<label for="smtp-encryption" class="col-sm-2 control-label">{{#translate}}Encryption{{/translate}}</label>
|
||||||
<div class="col-sm-10">
|
<div class="col-sm-10">
|
||||||
<select class="form-control" id="smtp-encryption" name="smtp-encryption">
|
<select class="form-control" id="smtp-encryption" name="smtp-encryption">
|
||||||
{{#each smtpEncryption}}
|
{{#each smtpEncryption}}
|
||||||
|
@ -184,23 +184,23 @@
|
||||||
<div class="col-sm-offset-2 col-xs-4">
|
<div class="col-sm-offset-2 col-xs-4">
|
||||||
<div class="checkbox">
|
<div class="checkbox">
|
||||||
<label>
|
<label>
|
||||||
<input type="checkbox" name="smtp-disable-auth" {{#if smtpDisableAuth}} checked {{/if}}> Disable SMTP authentication
|
<input type="checkbox" name="smtp-disable-auth" {{#if smtpDisableAuth}} checked {{/if}}> {{#translate}}Disable SMTP authentication{{/translate}}
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="smtp-user" class="col-sm-2 control-label">Username</label>
|
<label for="smtp-user" class="col-sm-2 control-label">{{#translate}}Username{{/translate}}</label>
|
||||||
<div class="col-sm-10">
|
<div class="col-sm-10">
|
||||||
<input type="text" class="form-control" name="smtp-user" id="smtp-user" placeholder="Username, eg. myaccount@example.com" value="{{smtpUser}}">
|
<input type="text" class="form-control" name="smtp-user" id="smtp-user" placeholder="{{#translate}}Username, eg. myaccount@example.com{{/translate}}" value="{{smtpUser}}">
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="smtp-pass" class="col-sm-2 control-label">Password</label>
|
<label for="smtp-pass" class="col-sm-2 control-label">{{#translate}}Password{{/translate}}</label>
|
||||||
<div class="col-sm-10">
|
<div class="col-sm-10">
|
||||||
<input type="password" class="form-control" name="smtp-pass" id="smtp-pass" placeholder="Password" value="{{smtpPass}}">
|
<input type="password" class="form-control" name="smtp-pass" id="smtp-pass" placeholder="{{#translate}}Password{{/translate}}" value="{{smtpPass}}">
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@ -213,28 +213,28 @@
|
||||||
<div class="radio">
|
<div class="radio">
|
||||||
<label>
|
<label>
|
||||||
<input type="radio" name="mail-transport" id="transport-ses" value="ses" {{#if useSES}} checked {{/if}}>
|
<input type="radio" name="mail-transport" id="transport-ses" value="ses" {{#if useSES}} checked {{/if}}>
|
||||||
Use SES API for sending mail
|
{{#translate}}Use SES API for sending mail{{/translate}}
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="ses-key" class="col-sm-2 control-label">Access Key</label>
|
<label for="ses-key" class="col-sm-2 control-label">{{#translate}}Access Key{{/translate}}</label>
|
||||||
<div class="col-sm-10">
|
<div class="col-sm-10">
|
||||||
<input type="text" class="form-control" name="ses-key" id="ses-key" placeholder="AWS Access Key Id" value="{{sesKey}}">
|
<input type="text" class="form-control" name="ses-key" id="ses-key" placeholder="{{#translate}}AWS Access Key Id{{/translate}}" value="{{sesKey}}">
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="ses-secret" class="col-sm-2 control-label">Secret Key</label>
|
<label for="ses-secret" class="col-sm-2 control-label">{{#translate}}Secret Key{{/translate}}</label>
|
||||||
<div class="col-sm-10">
|
<div class="col-sm-10">
|
||||||
<input type="password" class="form-control" name="ses-secret" id="ses-secret" placeholder="AES Secret Access Key" value="{{sesSecret}}">
|
<input type="password" class="form-control" name="ses-secret" id="ses-secret" placeholder="{{#translate}}AWS Secret Access Key{{/translate}}" value="{{sesSecret}}">
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="ses-region" class="col-sm-2 control-label">Region</label>
|
<label for="ses-region" class="col-sm-2 control-label">{{#translate}}Region{{/translate}}</label>
|
||||||
<div class="col-sm-10">
|
<div class="col-sm-10">
|
||||||
<select class="form-control" id="ses-region" name="ses-region">
|
<select class="form-control" id="ses-region" name="ses-region">
|
||||||
{{#each sesRegion}}
|
{{#each sesRegion}}
|
||||||
|
@ -252,24 +252,24 @@
|
||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<div class="pull-right">
|
<div class="pull-right">
|
||||||
<button type="submit" id="verify-button" form="smtp-verify" class="btn btn-info" data-loading-text="Checking..."><span class="glyphicon glyphicon-refresh" aria-hidden="true"></span> Check Mailer config</button>
|
<button type="submit" id="verify-button" form="smtp-verify" class="btn btn-info" data-loading-text="{{#translate}}Checking{{/translate}}…"><span class="glyphicon glyphicon-refresh" aria-hidden="true"></span> {{#translate}}Check Mailer config{{/translate}}</button>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-sm-offset-2 col-xs-6">
|
<div class="col-sm-offset-2 col-xs-6">
|
||||||
<p class="form-control-static">Don't have an SMTP account yet? Create a free SendPulse account <a href="https://sendpulse.com/?utm_source=mailtrain&utm_medium=settings">here</a></p>
|
<p class="form-control-static">{{#translate}}Don't have an SMTP account yet? Create a free SendPulse account{{/translate}} <a href="https://sendpulse.com/?utm_source=mailtrain&utm_medium=settings">{{#translate}}here{{/translate}}</a></p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</fieldset>
|
</fieldset>
|
||||||
|
|
||||||
<fieldset>
|
<fieldset>
|
||||||
<legend>
|
<legend>
|
||||||
Advanced Mailer settings
|
{{#translate}}Advanced Mailer settings{{/translate}}
|
||||||
</legend>
|
</legend>
|
||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<div class="col-sm-offset-2 col-xs-4">
|
<div class="col-sm-offset-2 col-xs-4">
|
||||||
<div class="checkbox">
|
<div class="checkbox">
|
||||||
<label>
|
<label>
|
||||||
<input type="checkbox" name="smtp-log" {{#if smtpLog}} checked {{/if}}> Log SMTP transactions
|
<input type="checkbox" name="smtp-log" {{#if smtpLog}} checked {{/if}}> {{#translate}}Log SMTP transactions{{/translate}}
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -279,54 +279,52 @@
|
||||||
<div class="col-sm-offset-2 col-xs-4">
|
<div class="col-sm-offset-2 col-xs-4">
|
||||||
<div class="checkbox">
|
<div class="checkbox">
|
||||||
<label>
|
<label>
|
||||||
<input type="checkbox" name="smtp-self-signed" {{#if smtpSelfSigned}} checked {{/if}}> Allow self-signed certificates
|
<input type="checkbox" name="smtp-self-signed" {{#if smtpSelfSigned}} checked {{/if}}> {{#translate}}Allow self-signed certificates{{/translate}}
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="smtp-max-connections" class="col-sm-2 control-label">Max connections</label>
|
<label for="smtp-max-connections" class="col-sm-2 control-label">{{#translate}}Max connections{{/translate}}</label>
|
||||||
<div class="col-sm-6">
|
<div class="col-sm-6">
|
||||||
<input type="number" class="form-control" name="smtp-max-connections" id="smtp-max-connections" placeholder="The count of max connections, eg. 10" value="{{smtpMaxConnections}}">
|
<input type="number" class="form-control" name="smtp-max-connections" id="smtp-max-connections" placeholder="{{#translate}}The count of max connections, eg. 10{{/translate}}" value="{{smtpMaxConnections}}">
|
||||||
<span class="help-block">The count of maximum simultaneous connections to make against the SMTP server (defaults to 5). This limit is per sending process.</span>
|
<span class="help-block">{{#translate}}The count of maximum simultaneous connections to make against the SMTP server (defaults to 5). This limit is per sending process.{{/translate}}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="smtp-max-messages" class="col-sm-2 control-label">Max messages</label>
|
<label for="smtp-max-messages" class="col-sm-2 control-label">{{#translate}}Max messages{{/translate}}</label>
|
||||||
<div class="col-sm-6">
|
<div class="col-sm-6">
|
||||||
<input type="number" class="form-control" name="smtp-max-messages" id="smtp-max-messages" placeholder="The count of max messages, eg. 100" value="{{smtpMaxMessages}}">
|
<input type="number" class="form-control" name="smtp-max-messages" id="smtp-max-messages" placeholder="{{#translate}}The count of max messages, eg. 100{{/translate}}" value="{{smtpMaxMessages}}">
|
||||||
<span class="help-block">The number of messages to send through a single connection before the connection is closed and reopened (defaults to 100)</span>
|
<span class="help-block">T{{#translate}}he number of messages to send through a single connection before the connection is closed and reopened (defaults to 100){{/translate}}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="smtp-throttling" class="col-sm-2 control-label">Throttling</label>
|
<label for="smtp-throttling" class="col-sm-2 control-label">{{#translate}}Throttling{{/translate}}</label>
|
||||||
<div class="col-sm-6">
|
<div class="col-sm-6">
|
||||||
<input type="number" class="form-control" name="smtp-throttling" id="smtp-throttling" placeholder="Messages per hour eg. 1000" value="{{smtpThrottling}}">
|
<input type="number" class="form-control" name="smtp-throttling" id="smtp-throttling" placeholder="{{#translate}}Messages per hour eg. 1000{{/translate}}" value="{{smtpThrottling}}">
|
||||||
<span class="help-block">Maximum number of messages to send in an hour. Leave empty or zero for no throttling. If your provider uses a different speed limit (<em>messages/minute</em> or <em>messages/second</em>) then convert this limit into <em>messages/hour</em> (1m/s => 3600m/h). This limit is per sending process.</span>
|
<span class="help-block">{{#translate}}Maximum number of messages to send in an hour. Leave empty or zero for no throttling. If your provider uses a different speed limit (messages/minute or messages/second) then convert this limit into messages/hour (1m/s => 3600m/h). This limit is per sending process.{{/translate}}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</fieldset>
|
</fieldset>
|
||||||
|
|
||||||
<fieldset>
|
<fieldset>
|
||||||
<legend>
|
<legend>
|
||||||
VERP bounce handling
|
{{#translate}}VERP bounce handling{{/translate}}
|
||||||
</legend>
|
</legend>
|
||||||
|
|
||||||
<p class="text-info">
|
<p class="text-info">
|
||||||
Mailtrain is able to use <a href="https://en.wikipedia.org/wiki/Variable_envelope_return_path">VERP</a> based routing to detect bounces. In this case the message is sent to the recipient using a custom VERP address as the return path of the
|
{{#translate}}Mailtrain is able to use VERP based routing to detect bounces. In this case the message is sent to the recipient using a custom VERP address as the return path of the message. If the message is not accepted a bounce email is sent to this special VERP address and thus a bounce is detected.{{/translate}}
|
||||||
message. If the message is not accepted a bounce email is sent to this special VERP address and thus a bounce is detected.
|
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<p class="text-info">
|
<p class="text-info">
|
||||||
To get VERP working you need to set up a DNS MX record that points to your Mailtrain hostname. You must also ensure that Mailtrain VERP interface is available from port 25 of your server (port 25 usually requires root user privileges). This way if anyone
|
{{#translate}}To get VERP working you need to set up a DNS MX record that points to your Mailtrain hostname. You must also ensure that Mailtrain VERP interface is available from port 25 of your server (port 25 usually requires root user privileges). This way if anyone tries to send email to someuser@verp-hostname then the email should end up to this server.{{/translate}}
|
||||||
tries to send email to <code>someuser@{{verpHostname}}</code> then the email should end up to this server.
|
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<p class="text-warning">
|
<p class="text-warning">
|
||||||
VERP usually only works if you are using your own SMTP server. Regular relay services (SES, SparkPost, Gmail etc.) tend to remove the VERP address from the message.
|
{{#translate}}VERP usually only works if you are using your own SMTP server. Regular relay services (SES, SparkPost, Gmail etc.) tend to remove the VERP address from the message.{{/translate}}
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
{{#if verpEnabled}}
|
{{#if verpEnabled}}
|
||||||
|
@ -335,24 +333,24 @@
|
||||||
<div class="col-sm-offset-2 col-xs-4">
|
<div class="col-sm-offset-2 col-xs-4">
|
||||||
<div class="checkbox">
|
<div class="checkbox">
|
||||||
<label>
|
<label>
|
||||||
<input type="checkbox" name="verp-use" {{#if verpUse}} checked {{/if}}> Use VERP to catch bounces
|
<input type="checkbox" name="verp-use" {{#if verpUse}} checked {{/if}}> {{#translate}}Use VERP to catch bounces{{/translate}}
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="verp-hostname" class="col-sm-2 control-label">Server hostname</label>
|
<label for="verp-hostname" class="col-sm-2 control-label">{{#translate}}Server hostname{{/translate}}</label>
|
||||||
<div class="col-sm-10">
|
<div class="col-sm-10">
|
||||||
<input type="text" class="form-control" name="verp-hostname" id="verp-hostname" placeholder="The VERP server hostname, eg. bounces.example.com" value="{{verpHostname}}">
|
<input type="text" class="form-control" name="verp-hostname" id="verp-hostname" placeholder="{{#translate}}The VERP server hostname, eg. bounces.example.com{{/translate}}" value="{{verpHostname}}">
|
||||||
<span class="help-block">VERP bounce handling server hostname. This hostname is used in the SMTP envelope FROM address and the MX DNS records should point to this server</span>
|
<span class="help-block">{{#translate}}VERP bounce handling server hostname. This hostname is used in the SMTP envelope FROM address and the MX DNS records should point to this server{{/translate}}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{{else}}
|
{{else}}
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<div class="col-sm-10">
|
<div class="col-sm-10">
|
||||||
<p class="form-control-static">VERP bounce handling server is not enabled. Modify your server configuration file and restart server to enable it</p>
|
<p class="form-control-static">{{#translate}}VERP bounce handling server is not enabled. Modify your server configuration file and restart server to enable it{{/translate}}</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
|
@ -360,30 +358,29 @@
|
||||||
|
|
||||||
<fieldset>
|
<fieldset>
|
||||||
<legend>
|
<legend>
|
||||||
GPG Signing
|
{{#translate}}GPG Signing{{/translate}}
|
||||||
</legend>
|
</legend>
|
||||||
|
|
||||||
<p>
|
<p>
|
||||||
Only messages that are encrypted can be signed. Subsribers who have not set up a GPG public key in their profile receive normal email messages. Users with GPG key set receive encrypted messages and if you have signing key also set, the messages are signed
|
{{#translate}}Only messages that are encrypted can be signed. Subsribers who have not set up a GPG public key in their profile receive normal email messages. Users with GPG key set receive encrypted messages and if you have signing key also set, the messages are signed with this key.{{/translate}}
|
||||||
with this key.
|
|
||||||
</p>
|
</p>
|
||||||
<p class="text-warning">
|
<p class="text-warning">
|
||||||
Do not use sensitive keys here. The private key and passphrase are not encrypted in the database.
|
{{#translate}}Do not use sensitive keys here. The private key and passphrase are not encrypted in the database.{{/translate}}
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="pgp-passphrase" class="col-sm-2 control-label">Private Key Passphrase</label>
|
<label for="pgp-passphrase" class="col-sm-2 control-label">{{#translate}}Private Key Passphrase{{/translate}}</label>
|
||||||
<div class="col-sm-10">
|
<div class="col-sm-10">
|
||||||
<input type="password" class="form-control" name="pgp-passphrase" id="pgp-passphrase" placeholder="Passphrase for the key if set" value="{{pgpPassphrase}}">
|
<input type="password" class="form-control" name="pgp-passphrase" id="pgp-passphrase" placeholder="{{#translate}}Passphrase for the key if set{{/translate}}" value="{{pgpPassphrase}}">
|
||||||
<span class="help-block">Only fill this if your private key is encrypted with a passphrase</span>
|
<span class="help-block">{{#translate}}Only fill this if your private key is encrypted with a passphrase{{/translate}}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="pgp-private-key" class="col-sm-2 control-label">GPG Private Key</label>
|
<label for="pgp-private-key" class="col-sm-2 control-label">{{#translate}}GPG Private Key{{/translate}}</label>
|
||||||
<div class="col-sm-10">
|
<div class="col-sm-10">
|
||||||
<textarea class="form-control gpg-text" rows="3" id="pgp-private-key" name="pgp-private-key" placeholder="Begins with '-----BEGIN PGP PRIVATE KEY BLOCK-----'">{{pgpPrivateKey}}</textarea>
|
<textarea class="form-control gpg-text" rows="3" id="pgp-private-key" name="pgp-private-key" placeholder="{{#translate}}Begins with{{/translate}} '-----BEGIN PGP PRIVATE KEY BLOCK-----'">{{pgpPrivateKey}}</textarea>
|
||||||
<span class="help-block">This value is optional. If you do not provide a private key GPG encrypted messages are sent without signing.</span>
|
<span class="help-block">{{#translate}}This value is optional. If you do not provide a private key GPG encrypted messages are sent without signing.{{/translate}}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@ -391,45 +388,45 @@
|
||||||
|
|
||||||
<fieldset>
|
<fieldset>
|
||||||
<legend>
|
<legend>
|
||||||
DKIM Signing by ZoneMTA
|
{{#translate}}DKIM Signing by ZoneMTA{{/translate}}
|
||||||
</legend>
|
</legend>
|
||||||
|
|
||||||
<p>
|
<p>
|
||||||
If you are using <a href="https://github.com/zone-eu/zone-mta">ZoneMTA</a> then Mailtrain can provide a DKIM key for signing all outgoing messages. Other services usually provide their own means to DKIM sign your messages
|
{{#translate}}If you are using ZoneMTA then Mailtrain can provide a DKIM key for signing all outgoing messages. Other services usually provide their own means to DKIM sign your messages{{/translate}}
|
||||||
</p>
|
</p>
|
||||||
<p class="text-warning">
|
<p class="text-warning">
|
||||||
Do not use sensitive keys here. The private key is not encrypted in the database.
|
{{#translate}}Do not use sensitive keys here. The private key is not encrypted in the database.{{/translate}}
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="pgp-passphrase" class="col-sm-2 control-label">ZoneMTA DKIM API Key</label>
|
<label for="pgp-passphrase" class="col-sm-2 control-label">{{#translate}}ZoneMTA DKIM API Key{{/translate}}</label>
|
||||||
<div class="col-sm-10">
|
<div class="col-sm-10">
|
||||||
<input type="password" class="form-control" name="dkim-api-key" id="dkim-api-key" placeholder="Some secret value" value="{{dkimApiKey}}">
|
<input type="password" class="form-control" name="dkim-api-key" id="dkim-api-key" placeholder="{{#translate}}Some secret value{{/translate}}" value="{{dkimApiKey}}">
|
||||||
<span class="help-block">Secret value known to ZoneMTA for requesting DKIM key information. If this value was generated by the Mailtrain installation script then you can keep it as it is</span>
|
<span class="help-block">{{#translate}}Secret value known to ZoneMTA for requesting DKIM key information. If this value was generated by the Mailtrain installation script then you can keep it as it is{{/translate}}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="pgp-passphrase" class="col-sm-2 control-label">DKIM domain</label>
|
<label for="pgp-passphrase" class="col-sm-2 control-label">{{#translate}}DKIM domain{{/translate}}</label>
|
||||||
<div class="col-sm-10">
|
<div class="col-sm-10">
|
||||||
<input type="text" class="form-control" name="dkim-domain" id="dkim-domain" placeholder="Domain name for the DKIM key" value="{{dkimDomain}}">
|
<input type="text" class="form-control" name="dkim-domain" id="dkim-domain" placeholder="{{#translate}}Domain name for the DKIM key{{/translate}}" value="{{dkimDomain}}">
|
||||||
<span class="help-block">Leave blank to use the sender email address domain</span>
|
<span class="help-block">{{#translate}}Leave blank to use the sender email address domain{{/translate}}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="pgp-passphrase" class="col-sm-2 control-label">DKIM key selector</label>
|
<label for="pgp-passphrase" class="col-sm-2 control-label">{{#translate}}DKIM key selector{{/translate}}</label>
|
||||||
<div class="col-sm-10">
|
<div class="col-sm-10">
|
||||||
<input type="text" class="form-control" name="dkim-selector" id="dkim-selector" placeholder="DKIM key selector" value="{{dkimSelector}}">
|
<input type="text" class="form-control" name="dkim-selector" id="dkim-selector" placeholder="{{#translate}}DKIM key selector{{/translate}}" value="{{dkimSelector}}">
|
||||||
<span class="help-block">Signing is disabled without a valid selector value</span>
|
<span class="help-block">{{#translate}}Signing is disabled without a valid selector value{{/translate}}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="dkim-private-key" class="col-sm-2 control-label">DKIM Private Key</label>
|
<label for="dkim-private-key" class="col-sm-2 control-label">{{#translate}}DKIM Private Key{{/translate}}</label>
|
||||||
<div class="col-sm-10">
|
<div class="col-sm-10">
|
||||||
<textarea class="form-control gpg-text" rows="3" id="dkim-private-key" name="dkim-private-key" placeholder="Begins with '-----BEGIN RSA PRIVATE KEY-----'">{{dkimPrivateKey}}</textarea>
|
<textarea class="form-control gpg-text" rows="3" id="dkim-private-key" name="dkim-private-key" placeholder="{{#translate}}Begins with{{/translate}} '-----BEGIN RSA PRIVATE KEY-----'">{{dkimPrivateKey}}</textarea>
|
||||||
<span class="help-block">This value is optional. If you do not provide a private key then messages are not signed.</span>
|
<span class="help-block">{{#translate}}This value is optional. If you do not provide a private key then messages are not signed.{{/translate}}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@ -439,7 +436,7 @@
|
||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<div class="col-sm-offset-2 col-sm-10">
|
<div class="col-sm-offset-2 col-sm-10">
|
||||||
<button type="submit" class="btn btn-primary"><i class="glyphicon glyphicon-ok"></i> Update</button>
|
<button type="submit" class="btn btn-primary"><i class="glyphicon glyphicon-ok"></i> {{#translate}}Update{{/translate}}</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
|
@ -1,17 +1,17 @@
|
||||||
<div class="alert alert-warning alert-dismissible" role="alert" id="js-warning">
|
<div class="alert alert-warning alert-dismissible" role="alert" id="js-warning">
|
||||||
<button type="button" class="close" data-dismiss="alert" aria-label="Close"><span aria-hidden="true">×</span></button>
|
<button type="button" class="close" data-dismiss="alert" aria-label="Close"><span aria-hidden="true">×</span></button>
|
||||||
<strong>Warning!</strong> If JavaScript was not enabled then no confirmation message was sent
|
<strong>{{#translate}}Warning!{{/translate}}</strong> {{#translate}}If JavaScript was not enabled then no confirmation message was sent{{/translate}}
|
||||||
</div>
|
</div>
|
||||||
<script>
|
<script>
|
||||||
document.getElementById('js-warning').style.display = 'none';
|
document.getElementById('js-warning').style.display = 'none';
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<h2>Almost finished.</h2>
|
<h2>{{#translate}}Almost finished.{{/translate}}</h2>
|
||||||
|
|
||||||
<p>We need to confirm your email address. To complete the subscription process, please click the link in the email we just sent you.</p>
|
<p>{{#translate}}We need to confirm your email address. To complete the subscription process, please click the link in the email we just sent you.{{/translate}}</p>
|
||||||
|
|
||||||
<p>
|
<p>
|
||||||
<a class="btn btn-primary" href="{{homepage}}" role="button">
|
<a class="btn btn-primary" href="{{homepage}}" role="button">
|
||||||
<span class="glyphicon glyphicon-arrow-left" aria-hidden="true"></span> return to our website
|
<span class="glyphicon glyphicon-arrow-left" aria-hidden="true"></span> {{#translate}}return to our website{{/translate}}
|
||||||
</a>
|
</a>
|
||||||
</p>
|
</p>
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
<!-- The above 3 meta tags *must* come first in the head; any other head content must come *after* these tags -->
|
<!-- The above 3 meta tags *must* come first in the head; any other head content must come *after* these tags -->
|
||||||
<meta name="description" content="Self hosted email newsletter app">
|
<meta name="description" content="{{#translate}}Self hosted email newsletter app{{/translate}}">
|
||||||
<meta name="author" content="Andris Reinman">
|
<meta name="author" content="Andris Reinman">
|
||||||
<link rel="icon" href="/favicon.ico">
|
<link rel="icon" href="/favicon.ico">
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
<h2>Update your Email Address</h2>
|
<h2>{{#translate}}Update your Email Address{{/translate}}</h2>
|
||||||
|
|
||||||
<form method="post" action="/subscription/{{lcid}}/manage-address">
|
<form method="post" action="/subscription/{{lcid}}/manage-address">
|
||||||
|
|
||||||
|
@ -6,20 +6,20 @@
|
||||||
<input type="hidden" name="cid" value="{{cid}}">
|
<input type="hidden" name="cid" value="{{cid}}">
|
||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="email">Existing Email Address</label>
|
<label for="email">{{#translate}}Existing Email Address{{/translate}}</label>
|
||||||
<input type="email" class="form-control" name="email" id="email" placeholder="" value="{{email}}" readonly>
|
<input type="email" class="form-control" name="email" id="email" placeholder="" value="{{email}}" readonly>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="email-new">New Email Address</label>
|
<label for="email-new">{{#translate}}New Email Address{{/translate}}</label>
|
||||||
<input type="email" class="form-control" name="email-new" id="email-new" placeholder="Your new email address" value="{{email}}">
|
<input type="email" class="form-control" name="email-new" id="email-new" placeholder="{{#translate}}Your new email address{{/translate}}" value="{{email}}">
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<p>
|
<p>
|
||||||
<small>You will receive a confirmation request to your new email address that you need to accept before your email is actually changed</small>
|
<small>{{#translate}}You will receive a confirmation request to your new email address that you need to accept before your email is actually changed{{/translate}}</small>
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<button type="submit" class="btn btn-primary"><i class="glyphicon glyphicon-ok"></i> Update Email Address</button>
|
<button type="submit" class="btn btn-primary"><i class="glyphicon glyphicon-ok"></i> {{#translate}}Update Email Address{{/translate}}</button>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
<h2>Update your preferences</h2>
|
<h2>{{#translate}}Update your preferences{{/translate}}</h2>
|
||||||
|
|
||||||
{{#if hasPubkey}}
|
{{#if hasPubkey}}
|
||||||
<form method="post" id="download-pubkey" action="/subscription/publickey">
|
<form method="post" id="download-pubkey" action="/subscription/publickey">
|
||||||
|
@ -14,20 +14,20 @@
|
||||||
<input type="hidden" class="tz-detect" name="tz" id="tz" value="{{tz}}">
|
<input type="hidden" class="tz-detect" name="tz" id="tz" value="{{tz}}">
|
||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="email">Email Address</label>
|
<label for="email">{{#translate}}Email Address{{/translate}}</label>
|
||||||
<div class="input-group">
|
<div class="input-group">
|
||||||
<input type="email" class="form-control" name="email" id="email" placeholder="" value="{{email}}" readonly>
|
<input type="email" class="form-control" name="email" id="email" placeholder="" value="{{email}}" readonly>
|
||||||
<div class="input-group-addon"><a href="/subscription/{{lcid}}/manage-address/{{cid}}">want to change it?</a></div>
|
<div class="input-group-addon"><a href="/subscription/{{lcid}}/manage-address/{{cid}}">{{#translate}}want to change it?{{/translate}}</a></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="first-name">First Name</label>
|
<label for="first-name">{{#translate}}First Name{{/translate}}</label>
|
||||||
<input type="text" class="form-control" name="first-name" id="first-name" placeholder="" value="{{firstName}}">
|
<input type="text" class="form-control" name="first-name" id="first-name" placeholder="" value="{{firstName}}">
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="last-name">Last Name</label>
|
<label for="last-name">{{#translate}}Last Name{{/translate}}</label>
|
||||||
<input type="text" class="form-control" name="last-name" id="last-name" placeholder="" value="{{lastName}}">
|
<input type="text" class="form-control" name="last-name" id="last-name" placeholder="" value="{{lastName}}">
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@ -58,11 +58,11 @@
|
||||||
{{#if typeGpg}}
|
{{#if typeGpg}}
|
||||||
{{#if ../hasPubkey}}
|
{{#if ../hasPubkey}}
|
||||||
<div class="pull-right">
|
<div class="pull-right">
|
||||||
<button type="submit" class="btn btn-link btn-xs" form="download-pubkey"><span class="glyphicon glyphicon-cloud-download" aria-hidden="true"></span> Download signature verification key</button>
|
<button type="submit" class="btn btn-link btn-xs" form="download-pubkey"><span class="glyphicon glyphicon-cloud-download" aria-hidden="true"></span> {{#translate}}Download signature verification key{{/translate}}</button>
|
||||||
</div>
|
</div>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
<textarea class="form-control gpg-text" rows="3" name="{{column}}" placeholder="Begins with '-----BEGIN PGP PUBLIC KEY BLOCK-----'">{{value}}</textarea>
|
<textarea class="form-control gpg-text" rows="3" name="{{column}}" placeholder="{{#translate}}Begins with{{/translate}} '-----BEGIN PGP PUBLIC KEY BLOCK-----'">{{value}}</textarea>
|
||||||
<span class="help-block">Insert your GPG public key here to encrypt messages sent to your address <em>(optional)</em></span>
|
<span class="help-block">{{#translate}}Insert your GPG public key here to encrypt messages sent to your address{{/translate}} <em>({{#translate}}optional{{/translate}})</em></span>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
|
|
||||||
{{#if typeDateUs}}
|
{{#if typeDateUs}}
|
||||||
|
@ -92,7 +92,7 @@
|
||||||
{{#if typeDropdown}}
|
{{#if typeDropdown}}
|
||||||
<select name="{{key}}" class="form-control">
|
<select name="{{key}}" class="form-control">
|
||||||
<option value="">
|
<option value="">
|
||||||
–– Select ––
|
–– {{#translate}}Select{{/translate}} ––
|
||||||
</option>
|
</option>
|
||||||
{{#each options}}
|
{{#each options}}
|
||||||
<option value="{{column}}" {{#if value}} selected {{/if}}>{{name}}</option>
|
<option value="{{column}}" {{#if value}} selected {{/if}}>{{name}}</option>
|
||||||
|
@ -124,6 +124,6 @@
|
||||||
{{/each}}
|
{{/each}}
|
||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<button type="submit" class="btn btn-primary"><i class="glyphicon glyphicon-ok"></i> Update Profile</button> or <a href="/subscription/{{lcid}}/unsubscribe/{{cid}}">Unsubscribe</a>
|
<button type="submit" class="btn btn-primary"><i class="glyphicon glyphicon-ok"></i> {{#translate}}Update Profile{{/translate}}</button> or <a href="/subscription/{{lcid}}/unsubscribe/{{cid}}">{{#translate}}Unsubscribe{{/translate}}</a>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
|
|
|
@ -7,7 +7,8 @@
|
||||||
|
|
||||||
<div class="alert alert-warning alert-dismissible" role="alert" id="js-warning">
|
<div class="alert alert-warning alert-dismissible" role="alert" id="js-warning">
|
||||||
<button type="button" class="close" data-dismiss="alert" aria-label="Close"><span aria-hidden="true">×</span></button>
|
<button type="button" class="close" data-dismiss="alert" aria-label="Close"><span aria-hidden="true">×</span></button>
|
||||||
<strong>Warning!</strong> JavaScript must be enabled in order for the subscription form to work
|
<strong>{{#translate}}Warning!{{/translate}}</strong>
|
||||||
|
{{#translate}}JavaScript must be enabled in order for the subscription form to work{{/translate}}
|
||||||
</div>
|
</div>
|
||||||
<script>
|
<script>
|
||||||
document.getElementById('js-warning').style.display = 'none';
|
document.getElementById('js-warning').style.display = 'none';
|
||||||
|
@ -22,17 +23,17 @@
|
||||||
<input type="hidden" name="sub" id="sub" value="">
|
<input type="hidden" name="sub" id="sub" value="">
|
||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="email">Email Address</label>
|
<label for="email">{{#translate}}Email Address{{/translate}}</label>
|
||||||
<input type="email" class="form-control" name="email" id="email" placeholder="" value="{{email}}" required>
|
<input type="email" class="form-control" name="email" id="email" placeholder="" value="{{email}}" required>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="first-name">First Name</label>
|
<label for="first-name">{{#translate}}First Name{{/translate}}</label>
|
||||||
<input type="text" class="form-control" name="first-name" id="first-name" placeholder="" value="{{firstName}}">
|
<input type="text" class="form-control" name="first-name" id="first-name" placeholder="" value="{{firstName}}">
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="last-name">Last Name</label>
|
<label for="last-name">{{#translate}}Last Name{{/translate}}</label>
|
||||||
<input type="text" class="form-control" name="last-name" id="last-name" placeholder="" value="{{lastName}}">
|
<input type="text" class="form-control" name="last-name" id="last-name" placeholder="" value="{{lastName}}">
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@ -63,11 +64,11 @@
|
||||||
{{#if typeGpg}}
|
{{#if typeGpg}}
|
||||||
{{#if ../hasPubkey}}
|
{{#if ../hasPubkey}}
|
||||||
<div class="pull-right">
|
<div class="pull-right">
|
||||||
<button type="submit" class="btn btn-link btn-xs" form="download-pubkey"><span class="glyphicon glyphicon-cloud-download" aria-hidden="true"></span> Download signature verification key</button>
|
<button type="submit" class="btn btn-link btn-xs" form="download-pubkey"><span class="glyphicon glyphicon-cloud-download" aria-hidden="true"></span> {{#translate}}Download signature verification key{{/translate}}</button>
|
||||||
</div>
|
</div>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
<textarea class="form-control gpg-text" rows="3" name="{{column}}" placeholder="Begins with '-----BEGIN PGP PUBLIC KEY BLOCK-----'">{{value}}</textarea>
|
<textarea class="form-control gpg-text" rows="3" name="{{column}}" placeholder="{{#translate}}Begins with{{/translate}} '-----BEGIN PGP PUBLIC KEY BLOCK-----'">{{value}}</textarea>
|
||||||
<span class="help-block">Insert your GPG public key here to encrypt messages sent to your address <em>(optional)</em></span>
|
<span class="help-block">{{#translate}}Insert your GPG public key here to encrypt messages sent to your address{{/translate}} <em>({{#translate}}optional{{/translate}})</em></span>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
|
|
||||||
{{#if typeDateUs}}
|
{{#if typeDateUs}}
|
||||||
|
@ -97,7 +98,7 @@
|
||||||
{{#if typeDropdown}}
|
{{#if typeDropdown}}
|
||||||
<select name="{{key}}" class="form-control">
|
<select name="{{key}}" class="form-control">
|
||||||
<option value="">
|
<option value="">
|
||||||
–– Select ––
|
–– {{#translate}}Select{{/translate}} ––
|
||||||
</option>
|
</option>
|
||||||
{{#each options}}
|
{{#each options}}
|
||||||
<option value="{{column}}" {{#if value}} selected {{/if}}>{{name}}</option>
|
<option value="{{column}}" {{#if value}} selected {{/if}}>{{name}}</option>
|
||||||
|
@ -129,7 +130,7 @@
|
||||||
{{/each}}
|
{{/each}}
|
||||||
|
|
||||||
<div class="form-group" id="js-subscribe" style="display: none">
|
<div class="form-group" id="js-subscribe" style="display: none">
|
||||||
<button type="submit" class="btn btn-primary">Subscribe to list</button>
|
<button type="submit" class="btn btn-primary">{{#translate}}Subscribe to list{{/translate}}</button>
|
||||||
</div>
|
</div>
|
||||||
<script>
|
<script>
|
||||||
document.getElementById('js-subscribe').style.display = 'block';
|
document.getElementById('js-subscribe').style.display = 'block';
|
||||||
|
|
|
@ -1,15 +1,21 @@
|
||||||
<h2>Subscription Confirmed</h2>
|
<h2>{{#translate}}Subscription Confirmed{{/translate}}</h2>
|
||||||
|
|
||||||
<p>Your subscription to our list has been confirmed.</p>
|
<p>
|
||||||
|
{{#translate}}Your subscription to our list has been confirmed.{{/translate}}
|
||||||
|
</p>
|
||||||
|
|
||||||
<p>Thank you for subscribing!</p>
|
<p>
|
||||||
|
{{#translate}}Thank you for subscribing!{{/translate}}
|
||||||
|
</p>
|
||||||
|
|
||||||
<p>
|
<p>
|
||||||
<a class="btn btn-primary" href="{{homepage}}" role="button">
|
<a class="btn btn-primary" href="{{homepage}}" role="button">
|
||||||
<span class="glyphicon glyphicon-arrow-left" aria-hidden="true"></span> continue to our website
|
<span class="glyphicon glyphicon-arrow-left" aria-hidden="true"></span>
|
||||||
|
{{#translate}}continue to our website{{/translate}}
|
||||||
</a>
|
</a>
|
||||||
or
|
{{#translate}}or{{/translate}}
|
||||||
<a class="btn btn-primary" href="{{preferences}}" role="button">
|
<a class="btn btn-primary" href="{{preferences}}" role="button">
|
||||||
<span class="glyphicon glyphicon-wrench" aria-hidden="true"></span> manage your preferences
|
<span class="glyphicon glyphicon-wrench" aria-hidden="true"></span>
|
||||||
|
{{#translate}}manage your preferences{{/translate}}
|
||||||
</a>
|
</a>
|
||||||
</p>
|
</p>
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
<h2>Unsubscribe Successful</h2>
|
<h2>{{#translate}}Unsubscribe Successful{{/translate}}</h2>
|
||||||
|
|
||||||
<p>You have been removed from {{title}}.</p>
|
<p>{{#translate}}You have been removed from:{{/translate}} {{title}}.</p>
|
||||||
|
|
||||||
<p>
|
<p>
|
||||||
<a class="btn btn-primary" href="{{homepage}}" role="button">
|
<a class="btn btn-primary" href="{{homepage}}" role="button">
|
||||||
<span class="glyphicon glyphicon-arrow-left" aria-hidden="true"></span> return to our website
|
<span class="glyphicon glyphicon-arrow-left" aria-hidden="true"></span> {{#translate}}return to our website{{/translate}}
|
||||||
</a>
|
</a>
|
||||||
</p>
|
</p>
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
<h2>Unsubscribe</h2>
|
<h2>{{#translate}}Unsubscribe{{/translate}}</h2>
|
||||||
|
|
||||||
<p>
|
<p>
|
||||||
Enter your email address to unsubscribe from {{title}}
|
{{#translate}}Enter your email address to unsubscribe from:{{/translate}} {{title}}
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<form method="post" id="unsubscribe-form" action="/subscription/{{lcid}}/unsubscribe">
|
<form method="post" id="unsubscribe-form" action="/subscription/{{lcid}}/unsubscribe">
|
||||||
|
@ -11,12 +11,12 @@
|
||||||
<input type="hidden" name="cid" value="{{cid}}">
|
<input type="hidden" name="cid" value="{{cid}}">
|
||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="email">Email address</label>
|
<label for="email">{{#translate}}Email address{{/translate}}</label>
|
||||||
<input type="email" class="form-control" name="email" id="email" placeholder="" value="{{email}}" autofocus required>
|
<input type="email" class="form-control" name="email" id="email" placeholder="" value="{{email}}" autofocus required>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<button type="submit" id="unsubscribe-button" class="btn btn-primary">Unsubscribe</button>
|
<button type="submit" id="unsubscribe-button" class="btn btn-primary">{{#translate}}Unsubscribe{{/translate}}</button>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue