Merge branch 'master' into development
This commit is contained in:
commit
877e0a857d
44 changed files with 5260 additions and 366 deletions
|
@ -12,7 +12,7 @@
|
|||
* Email templates
|
||||
* Large CSV list import files
|
||||
|
||||
Subscribe to Mailtrain Newsletter [here](http://mailtrain.org/subscription/EysIv8sAx) (uses Mailtrain obviously)
|
||||
Subscribe to Mailtrain Newsletter [here](https://mailtrain.org/subscription/S18sew2wM) (uses Mailtrain obviously)
|
||||
|
||||
## Hardware Requirements
|
||||
* 1 vCPU
|
||||
|
@ -30,6 +30,7 @@ Depending on how you have configured your system and Docker you may need to prep
|
|||
* Download Mailtrain files using git: `git clone git://github.com/Mailtrain-org/mailtrain.git` (or download [zipped repo](https://github.com/Mailtrain-org/mailtrain/archive/master.zip)) and open Mailtrain folder `cd mailtrain`
|
||||
* Copy the file `docker-compose.override.yml.tmpl` to `docker-compose.override.yml` and modify it if you need to.
|
||||
* Bring up the stack with: `docker-compose up -d`
|
||||
* Start: `docker-compose start`
|
||||
* Open [http://localhost:3000/](http://localhost:3000/) (change the host name `localhost` to the name of the host where you are deploying the system).
|
||||
* Authenticate as user `admin` with password `test`
|
||||
* Navigate to [http://localhost:3000/settings](http://localhost:3000/settings) and update service configuration.
|
||||
|
@ -58,4 +59,4 @@ For more information, please [read the docs](http://docs.mailtrain.org/).
|
|||
* Versions 1.22.0 and up **GPL-V3.0**
|
||||
* Versions 1.21.0 and up: **EUPL-1.1**
|
||||
* Versions 1.19.0 and up: **MIT**
|
||||
* Up to versions 1.18.0 **GPL-V3.0**
|
||||
* Up to versions 1.18.0 **GPL-V3.0**
|
||||
|
|
Binary file not shown.
|
@ -3,14 +3,14 @@ msgstr ""
|
|||
"Project-Id-Version: \n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2017-05-04 00:45+0200\n"
|
||||
"PO-Revision-Date: 2017-05-04 00:46+0200\n"
|
||||
"PO-Revision-Date: 2018-03-07 14:12+0100\n"
|
||||
"Last-Translator: \n"
|
||||
"Language-Team: \n"
|
||||
"Language: de_DE\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Generator: Poedit 2.0.1\n"
|
||||
"X-Generator: Poedit 2.0.6\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||
|
||||
#: views/archive/layout.hbs:1 views/layout.hbs:1
|
||||
|
@ -872,7 +872,7 @@ msgstr "Hallo [FIRST_NAME/Customer],"
|
|||
|
||||
#: views/emails/stationery-html.hbs:2 views/emails/stationery-text.hbs:2
|
||||
msgid "Cheers,"
|
||||
msgstr "Viele Grüsse,"
|
||||
msgstr "Viele Grüße,"
|
||||
|
||||
#: views/index.hbs:1
|
||||
msgid "List Management"
|
||||
|
@ -918,16 +918,16 @@ msgstr "Mailtrain bietet auch benutzerdefinierte Formulare."
|
|||
|
||||
#: views/index.hbs:8
|
||||
msgid "List Segmentation"
|
||||
msgstr "Listen Segementierung"
|
||||
msgstr "Segmentierung"
|
||||
|
||||
#: views/index.hbs:9
|
||||
msgid ""
|
||||
"Send messages only to list subscribers that match predefined segmentation "
|
||||
"rules. No need to create separate lists with small differences."
|
||||
msgstr ""
|
||||
"Senden Sie nur Nachrichten an Abonnenten welche die vordefinierten "
|
||||
"Segmentierungsregeln erfüllen. Keine Notwendigkeit, separate Listen mit "
|
||||
"kleinen Unterschieden zu erstellen."
|
||||
"Senden Sie nur Nachrichten an Abonnenten, welche die vordefinierten "
|
||||
"Segmentierungsregeln erfüllen. Es besteht keine Notwendigkeit, separate "
|
||||
"Listen mit kleinen Unterschieden zu erstellen."
|
||||
|
||||
#: views/index.hbs:11
|
||||
msgid "RSS Campaigns"
|
||||
|
@ -954,7 +954,7 @@ msgid ""
|
|||
msgstr ""
|
||||
"Wenn für eine Liste ein benutzerdefiniertes Feld für den GPG-Public-Key "
|
||||
"vorhanden ist, können Abonnenten ihren GPG-Public-Key hochladen, um "
|
||||
"verschlüsselte E-Mails dieser der Liste zu empfangen."
|
||||
"verschlüsselte E-Mails der Liste zu empfangen."
|
||||
|
||||
#: views/index.hbs:17
|
||||
msgid "Click Stats"
|
||||
|
@ -984,7 +984,7 @@ msgstr ""
|
|||
|
||||
#: views/index.hbs:23
|
||||
msgid "Send via Any Provider"
|
||||
msgstr "Sende mit "
|
||||
msgstr "Sende mit"
|
||||
|
||||
#: views/index.hbs:24
|
||||
msgid ""
|
||||
|
@ -1121,7 +1121,7 @@ msgstr "Liste erstellen"
|
|||
|
||||
#: views/lists/create.hbs:6 views/lists/edit.hbs:7
|
||||
msgid "List Name"
|
||||
msgstr "Linstennamen"
|
||||
msgstr "Name der Liste"
|
||||
|
||||
#: views/lists/create.hbs:9 views/lists/edit.hbs:15
|
||||
msgid "Allow public users to subscribe themselves"
|
||||
|
@ -1472,7 +1472,7 @@ msgstr "ID"
|
|||
|
||||
#: views/lists/lists.hbs:7 views/reports/partials/report-fields.hbs:10
|
||||
msgid "Subscribers"
|
||||
msgstr "Abonnenten "
|
||||
msgstr "Abonnenten"
|
||||
|
||||
#: views/lists/segments/create.hbs:3 views/lists/segments/edit.hbs:3
|
||||
#: views/lists/segments/rule-configure.hbs:3
|
||||
|
@ -1966,7 +1966,7 @@ msgstr "Mosaico öffnen"
|
|||
|
||||
#: views/partials/plaintext.hbs:1
|
||||
msgid "Template content (plaintext)"
|
||||
msgstr "Vorlagen-Inhalt (Klartext) "
|
||||
msgstr "Vorlagen-Inhalt (Klartext)"
|
||||
|
||||
#: views/report-templates/create.hbs:2 views/report-templates/edit.hbs:2
|
||||
#: views/report-templates/report-templates.hbs:2
|
||||
|
@ -2386,7 +2386,7 @@ msgstr "VERP verwenden um bounces zu erfassen"
|
|||
|
||||
#: views/settings.hbs:71
|
||||
msgid "Server hostname"
|
||||
msgstr "Server Hostname"
|
||||
msgstr "Hostname"
|
||||
|
||||
#: views/settings.hbs:72
|
||||
msgid "The VERP server hostname, eg. bounces.example.com"
|
||||
|
@ -2462,7 +2462,7 @@ msgstr ""
|
|||
|
||||
#: views/settings.hbs:84
|
||||
msgid "DKIM Signing by ZoneMTA"
|
||||
msgstr "DKIM Signing by ZoneMTA"
|
||||
msgstr "DKIM Signing durch ZoneMTA"
|
||||
|
||||
#: views/settings.hbs:85
|
||||
msgid ""
|
||||
|
@ -2507,7 +2507,7 @@ msgstr "DKIM Domain"
|
|||
|
||||
#: views/settings.hbs:91
|
||||
msgid "Domain name for the DKIM key"
|
||||
msgstr "Domain-Name für den DKIM Key"
|
||||
msgstr "Domain-Name für den DKIM-Key"
|
||||
|
||||
#: views/settings.hbs:92
|
||||
msgid "Leave blank to use the sender email address domain"
|
||||
|
@ -2582,7 +2582,7 @@ msgstr "Ihr Abonnement für unsere Liste wurde bestätigt"
|
|||
|
||||
#: views/subscription/mail-subscription-confirmed-html.mjml.hbs:3
|
||||
msgid "If you want to modify your subscription then you can "
|
||||
msgstr "Wenn Sie Ihr Abonnement ändern möchten, dann können Sie"
|
||||
msgstr "Wenn Sie Ihr Abonnement ändern möchten, dann können Sie "
|
||||
|
||||
#: views/subscription/mail-subscription-confirmed-html.mjml.hbs:4
|
||||
#: views/subscription/mail-subscription-confirmed-text.hbs:4
|
||||
|
@ -2731,7 +2731,7 @@ msgstr "Einstellungen aktualisieren"
|
|||
|
||||
#: views/subscription/web-subscribe.mjml.hbs:1
|
||||
msgid "Subscribe to List"
|
||||
msgstr "Diese Liste Abonnieren"
|
||||
msgstr "Diese Liste abonnieren"
|
||||
|
||||
#: views/subscription/web-subscribed.mjml.hbs:3
|
||||
msgid "Thank you for subscribing!"
|
||||
|
@ -2900,7 +2900,7 @@ msgstr "Ihre E-Mail-Adresse"
|
|||
|
||||
#: views/users/account.hbs:9
|
||||
msgid ""
|
||||
"This address is used for account recovery in case you loose your password"
|
||||
"This address is used for account recovery in case you lose your password"
|
||||
msgstr ""
|
||||
"Diese Adresse wird für die Wiederherstellung des Kontos verwendet, falls Sie "
|
||||
"Ihr Passwort vergessen haben"
|
||||
|
@ -3185,7 +3185,7 @@ msgstr "Wähle Sie Ihr neues Passwort"
|
|||
|
||||
#: views/users/reset.hbs:5
|
||||
msgid "Please enter a new password."
|
||||
msgstr "Bitte geben Sie ein neues Passwort ein"
|
||||
msgstr "Bitte geben Sie ein neues Passwort ein."
|
||||
|
||||
#: lib/editor-helpers.js:17 routes/templates.js:95
|
||||
msgid "Could not find template with specified ID"
|
||||
|
@ -3488,11 +3488,11 @@ msgid "Could not save subscription"
|
|||
msgstr "Abonnement konnte nicht gespeichert werden"
|
||||
|
||||
#: lib/models/subscriptions.js:441 lib/models/subscriptions.js:471
|
||||
msgid "Missing Subbscription ID"
|
||||
msgid "Missing Subscription ID"
|
||||
msgstr "Abonnement-ID fehlt"
|
||||
|
||||
#: lib/models/subscriptions.js:499
|
||||
msgid "Missing Subbscription email address"
|
||||
msgid "Missing Subscription email address"
|
||||
msgstr "Abonnement-E-Mail-Adresse fehlt"
|
||||
|
||||
#: lib/models/subscriptions.js:578 lib/models/subscriptions.js:827
|
||||
|
@ -3650,6 +3650,22 @@ msgstr "Eingeloggt als %s"
|
|||
msgid "Incorrect username or password"
|
||||
msgstr "Falscher Benutzername oder Passwort"
|
||||
|
||||
#: lib/subscription-mail-helpers.js:39
|
||||
msgid "%s: Email Address Already Registered"
|
||||
msgstr "%s: Email-Adresse bereits registriert"
|
||||
|
||||
#: lib/subscription-mail-helpers.js:49
|
||||
msgid "%s: Please Confirm Email Change in Subscription"
|
||||
msgstr "%s: Bitte bestätigen Sie die Änderung der Email-Adresse"
|
||||
|
||||
#: lib/subscription-mail-helpers.js:69
|
||||
msgid "%s: Please Confirm Unsubscription"
|
||||
msgstr "%s: Bitte bestätigen Sie die Kündigung des Abonnements"
|
||||
|
||||
#: lib/subscription-mail-helpers.js:76
|
||||
msgid "%s: Unsubscription Confirmed"
|
||||
msgstr "%s: Kündigung des Abonnements bestätigt"
|
||||
|
||||
#: lib/tools.js:148
|
||||
msgid "Blocked email address \"%s\""
|
||||
msgstr "Gesperrte E-Mail-Adresse \"%s\""
|
||||
|
@ -4593,3 +4609,31 @@ msgstr "Unbekannter Trigger-Typ %s"
|
|||
|
||||
#~ msgid "Yes, subscribe me to this list:"
|
||||
#~ msgstr "Ja, tragen Sie mich in diese Liste ein:"
|
||||
|
||||
#: lib/models/subscriptions.js:910 routes/subscription.js:472
|
||||
#: routes/subscription.js:544 routes/subscription.js:580
|
||||
#: routes/subscription.js:696 routes/subscription.js:771
|
||||
msgid "Subscription not found in this list"
|
||||
msgstr "Das Abonnement wurde in dieser Liste nicht gefunden"
|
||||
|
||||
#: views/subscription/web-confirm-unsubscription-notice.mjml.hbs:2
|
||||
msgid ""
|
||||
"We need to confirm your email address. To complete the unsubscription "
|
||||
"process, please click the link in the email we just sent you."
|
||||
msgstr ""
|
||||
"Wir müssen Ihre E-Mail-Adresse bestätigen. Um die Kündigung Ihres Abonnements abzuschließen, "
|
||||
"klicken Sie bitte auf den Link in der E-Mail, die wir Ihnen gerade geschickt "
|
||||
"haben."
|
||||
|
||||
msgid "Please Confirm Unsubscription"
|
||||
msgstr "Bitte bestätigen Sie die Kündigung Ihres Abonnements"
|
||||
|
||||
msgid "Yes, unsubscribe me from this list"
|
||||
msgstr "Ja, ich möchte dieses Abonnement kündigen"
|
||||
|
||||
msgid ""
|
||||
"If you received this email by mistake, simply delete it. You won't be "
|
||||
"unsubscribed if you don't click the confirmation link above."
|
||||
msgstr ""
|
||||
"Wenn Sie diese E-Mail versehentlich erhalten haben, löschen Sie sie einfach. "
|
||||
"Ihr Abonnement wird nicht gekündigt, wenn Sie nicht auf den Bestätigungslink oben klicken."
|
||||
|
|
Binary file not shown.
|
@ -3027,7 +3027,7 @@ msgstr "Tu dirección email"
|
|||
|
||||
#: views/users/account.hbs:9
|
||||
msgid ""
|
||||
"This address is used for account recovery in case you loose your password"
|
||||
"This address is used for account recovery in case you lose your password"
|
||||
msgstr ""
|
||||
"Este email es usado para recuperar una cuenta en caso de que no recuerdes la "
|
||||
"contraseña"
|
||||
|
@ -3614,18 +3614,14 @@ msgstr "El segmento especificado no se encontra"
|
|||
msgid "Selected rule not found"
|
||||
msgstr "La regla seleccionada no se encontra"
|
||||
|
||||
#: lib/models/subscriptions.js:254 lib/models/subscriptions.js:284
|
||||
msgid "Missing Subbscription ID"
|
||||
#: lib/models/subscriptions.js:254 lib/models/subscriptions.js:284 lib/models/subscriptions.js:391
|
||||
msgid "Missing Subscription ID"
|
||||
msgstr "Falta el ID de Suscripción"
|
||||
|
||||
#: lib/models/subscriptions.js:312
|
||||
msgid "Missing Subbscription email address"
|
||||
msgid "Missing Subscription email address"
|
||||
msgstr "Falta el correo electrónico de suscripción"
|
||||
|
||||
#: lib/models/subscriptions.js:391
|
||||
msgid "Missing Subscription ID"
|
||||
msgstr "Falta el ID de suscripción"
|
||||
|
||||
#: lib/models/subscriptions.js:567 lib/models/subscriptions.js:817
|
||||
msgid "Missing subscription ID"
|
||||
msgstr "Falta el ID de suscripción"
|
||||
|
|
BIN
languages/et.mo
BIN
languages/et.mo
Binary file not shown.
|
@ -2300,7 +2300,7 @@ msgstr ""
|
|||
|
||||
#: views/users/account.hbs:9
|
||||
msgid ""
|
||||
"This address is used for account recovery in case you loose your password"
|
||||
"This address is used for account recovery in case you lose your password"
|
||||
msgstr ""
|
||||
|
||||
#: views/users/account.hbs:10
|
||||
|
@ -2770,11 +2770,11 @@ msgid "Could not save subscription"
|
|||
msgstr ""
|
||||
|
||||
#: lib/models/subscriptions.js:507 lib/models/subscriptions.js:537
|
||||
msgid "Missing Subbscription ID"
|
||||
msgid "Missing Subscription ID"
|
||||
msgstr ""
|
||||
|
||||
#: lib/models/subscriptions.js:565
|
||||
msgid "Missing Subbscription email address"
|
||||
msgid "Missing Subscription email address"
|
||||
msgstr ""
|
||||
|
||||
#: lib/models/subscriptions.js:644 lib/models/subscriptions.js:893
|
||||
|
|
BIN
languages/it_IT.mo
Normal file
BIN
languages/it_IT.mo
Normal file
Binary file not shown.
4661
languages/it_IT.po
Normal file
4661
languages/it_IT.po
Normal file
File diff suppressed because it is too large
Load diff
|
@ -16,7 +16,7 @@ let _ = require('../translate')._;
|
|||
let util = require('util');
|
||||
let tableHelpers = require('../table-helpers');
|
||||
|
||||
let allowedKeys = ['description', 'from', 'address', 'reply_to', 'subject', 'editor_name', 'editor_data', 'template', 'source_url', 'list', 'segment', 'html', 'text', 'click_tracking_disabled', 'open_tracking_disabled'];
|
||||
let allowedKeys = ['description', 'from', 'address', 'reply_to', 'subject', 'editor_name', 'editor_data', 'template', 'source_url', 'list', 'segment', 'html', 'text', 'click_tracking_disabled', 'open_tracking_disabled', 'unsubscribe'];
|
||||
|
||||
module.exports.list = (start, limit, callback) => {
|
||||
tableHelpers.list('campaigns', ['*'], 'scheduled', null, start, limit, callback);
|
||||
|
|
|
@ -11,7 +11,7 @@ const UnsubscriptionMode = require('../../shared/lists').UnsubscriptionMode;
|
|||
|
||||
module.exports.UnsubscriptionMode = UnsubscriptionMode;
|
||||
|
||||
let allowedKeys = ['description', 'default_form', 'public_subscribe', 'unsubscription_mode'];
|
||||
let allowedKeys = ['description', 'default_form', 'public_subscribe', 'unsubscription_mode', 'listunsubscribe_disabled'];
|
||||
|
||||
module.exports.list = (start, limit, callback) => {
|
||||
tableHelpers.list('lists', ['*'], 'name', null, start, limit, callback);
|
||||
|
@ -150,6 +150,7 @@ module.exports.update = (id, updates, callback) => {
|
|||
// The update can be only partial when executed from forms/:list
|
||||
if (!data.customFormChangeOnly) {
|
||||
data.publicSubscribe = data.publicSubscribe ? 1 : 0;
|
||||
data.listunsubscribeDisabled = data.listunsubscribeDisabled ? 1 : 0;
|
||||
data.unsubscriptionMode = Number(data.unsubscriptionMode);
|
||||
|
||||
let name = (data.name || '').toString().trim();
|
||||
|
|
|
@ -156,7 +156,7 @@ module.exports.get = (id, callback) => {
|
|||
rule.formatted = rule.value.value ? _('Selected') : _('Not selected');
|
||||
break;
|
||||
default:
|
||||
rule.formatted = rule.value.value || '';
|
||||
rule.formatted = (rule.value.negate ? '!= ' : '') + (rule.value.value || '');
|
||||
}
|
||||
|
||||
return rule;
|
||||
|
@ -327,6 +327,7 @@ module.exports.createRule = (segmentId, rule, callback) => {
|
|||
break;
|
||||
default:
|
||||
value = {
|
||||
negate: Number(rule.negate) ? 1 : 0,
|
||||
value: rule.value
|
||||
};
|
||||
}
|
||||
|
@ -418,7 +419,7 @@ module.exports.getRule = (id, callback) => {
|
|||
rule.formatted = rule.value.value ? _('Selected') : _('Not selected');
|
||||
break;
|
||||
default:
|
||||
rule.formatted = rule.value.value || '';
|
||||
rule.formatted = (rule.value.negate ? '!= ' : '') + (rule.value.value || '');
|
||||
}
|
||||
|
||||
return callback(null, rule);
|
||||
|
@ -491,6 +492,7 @@ module.exports.updateRule = (id, rule, callback) => {
|
|||
break;
|
||||
default:
|
||||
value = {
|
||||
negate: Number(rule.negate) ? 1 : 0,
|
||||
value: rule.value
|
||||
};
|
||||
}
|
||||
|
@ -573,7 +575,8 @@ module.exports.getQuery = (id, prefix, callback) => {
|
|||
segment.rules.forEach(rule => {
|
||||
switch (rule.columnType.type) {
|
||||
case 'string':
|
||||
query.push(prefix + '`' + rule.columnType.column + '` LIKE ?');
|
||||
let condition = rule.value.negate ? 'NOT LIKE' : 'LIKE';
|
||||
query.push(prefix + '`' + rule.columnType.column + '` ' + condition + ' ?');
|
||||
values.push(rule.value.value);
|
||||
break;
|
||||
case 'boolean':
|
||||
|
|
|
@ -247,7 +247,7 @@ module.exports.get = (listId, cid, callback) => {
|
|||
cid = (cid || '').toString().trim();
|
||||
|
||||
if (!cid) {
|
||||
return callback(new Error(_('Missing Subbscription ID')));
|
||||
return callback(new Error(_('Missing Subscription ID')));
|
||||
}
|
||||
|
||||
db.getConnection((err, connection) => {
|
||||
|
@ -277,7 +277,7 @@ module.exports.getById = (listId, id, callback) => {
|
|||
id = Number(id) || 0;
|
||||
|
||||
if (!id) {
|
||||
return callback(new Error(_('Missing Subbscription ID')));
|
||||
return callback(new Error(_('Missing Subscription ID')));
|
||||
}
|
||||
|
||||
db.getConnection((err, connection) => {
|
||||
|
@ -305,7 +305,7 @@ module.exports.getById = (listId, id, callback) => {
|
|||
|
||||
module.exports.getByEmail = (listId, email, callback) => {
|
||||
if (!email) {
|
||||
return callback(new Error(_('Missing Subbscription email address')));
|
||||
return callback(new Error(_('Missing Subscription email address')));
|
||||
}
|
||||
|
||||
db.getConnection((err, connection) => {
|
||||
|
|
|
@ -1,8 +1,10 @@
|
|||
'use strict';
|
||||
|
||||
let log = require('npmlog');
|
||||
let tools = require('../tools');
|
||||
let db = require('../db');
|
||||
let lists = require('./lists');
|
||||
let segments = require('./segments');
|
||||
let util = require('util');
|
||||
let _ = require('../translate')._;
|
||||
let tableHelpers = require('../table-helpers');
|
||||
|
@ -55,7 +57,9 @@ module.exports.list = callback => {
|
|||
'`triggers`.`description` AS `description`',
|
||||
'`triggers`.`enabled` AS `enabled`',
|
||||
'`triggers`.`list` AS `list`',
|
||||
'`triggers`.`segment` AS `segment`',
|
||||
'`lists`.`name` AS `list_name`',
|
||||
'`segments`.`name` AS `segment_name`',
|
||||
'`source`.`id` AS `source_campaign`',
|
||||
'`source`.`name` AS `source_campaign_name`',
|
||||
'`dest`.`id` AS `dest_campaign`',
|
||||
|
@ -69,7 +73,7 @@ module.exports.list = callback => {
|
|||
'`triggers`.`created` AS `created`'
|
||||
];
|
||||
|
||||
let query = 'SELECT ' + tableFields.join(', ') + ' FROM `triggers` LEFT JOIN `campaigns` `source` ON `source`.`id`=`triggers`.`source_campaign` LEFT JOIN `campaigns` `dest` ON `dest`.`id`=`triggers`.`dest_campaign` LEFT JOIN `lists` ON `lists`.`id`=`triggers`.`list` LEFT JOIN `custom_fields` ON `custom_fields`.`list` = `triggers`.`list` AND `custom_fields`.`column`=`triggers`.`column` ORDER BY `triggers`.`name`';
|
||||
let query = 'SELECT ' + tableFields.join(', ') + ' FROM `triggers` LEFT JOIN `campaigns` `source` ON `source`.`id`=`triggers`.`source_campaign` LEFT JOIN `campaigns` `dest` ON `dest`.`id`=`triggers`.`dest_campaign` LEFT JOIN `lists` ON `lists`.`id`=`triggers`.`list` LEFT JOIN `segments` ON `segments`.`id`=`triggers`.`segment` LEFT JOIN `custom_fields` ON `custom_fields`.`list` = `triggers`.`list` AND `custom_fields`.`column`=`triggers`.`column` ORDER BY `triggers`.`name`';
|
||||
connection.query(query, (err, rows) => {
|
||||
connection.release();
|
||||
if (err) {
|
||||
|
@ -105,32 +109,70 @@ module.exports.getQuery = (id, callback) => {
|
|||
|
||||
let intervalQuery = (column, seconds, treshold) => column + ' <= NOW() - INTERVAL ' + seconds + ' SECOND AND ' + column + ' >= NOW() - INTERVAL ' + (treshold + seconds) + ' SECOND';
|
||||
|
||||
let query = false;
|
||||
switch (trigger.rule) {
|
||||
case 'subscription':
|
||||
query = 'SELECT id FROM `subscription__' + trigger.list + '` subscription WHERE ' + intervalQuery('`' + trigger.column + '`', trigger.seconds, treshold) + ' AND id NOT IN (SELECT subscription FROM `trigger__' + id + '` triggertable WHERE triggertable.`list` = ' + trigger.list + ' AND triggertable.`subscription` = subscription.`id`) LIMIT ' + limit;
|
||||
break;
|
||||
case 'campaign':
|
||||
switch (trigger.column) {
|
||||
case 'delivered':
|
||||
query = 'SELECT subscription.id AS id FROM `subscription__' + trigger.list + '` subscription LEFT JOIN `campaign__' + trigger.sourceCampaign + '` campaign ON campaign.list=' + trigger.list + ' AND subscription.id=campaign.subscription WHERE campaign.status=1 AND ' + intervalQuery('`campaign`.`created`', trigger.seconds, treshold) + ' AND subscription.id NOT IN (SELECT subscription FROM `trigger__' + id + '` triggertable WHERE triggertable.`list` = ' + trigger.list + ' AND triggertable.`subscription` = subscription.`id`) LIMIT ' + limit;
|
||||
break;
|
||||
case 'not_clicked':
|
||||
query = 'SELECT subscription.id AS id FROM `subscription__' + trigger.list + '` subscription LEFT JOIN `campaign__' + trigger.sourceCampaign + '` campaign ON campaign.list=' + trigger.list + ' AND subscription.id=campaign.subscription LEFT JOIN `campaign_tracker__' + trigger.sourceCampaign + '` tracker ON tracker.list=campaign.list AND tracker.subscriber=subscription.id AND tracker.link=0 WHERE campaign.status=1 AND ' + intervalQuery('`campaign`.`created`', trigger.seconds, treshold) + ' AND tracker.created IS NULL AND subscription.id NOT IN (SELECT subscription FROM `trigger__' + id + '` triggertable WHERE triggertable.`list` = ' + trigger.list + ' AND triggertable.`subscription` = subscription.`id`) LIMIT ' + limit;
|
||||
break;
|
||||
case 'not_opened':
|
||||
query = 'SELECT subscription.id AS id FROM `subscription__' + trigger.list + '` subscription LEFT JOIN `campaign__' + trigger.sourceCampaign + '` campaign ON campaign.list=' + trigger.list + ' AND subscription.id=campaign.subscription LEFT JOIN `campaign_tracker__' + trigger.sourceCampaign + '` tracker ON tracker.list=campaign.list AND tracker.subscriber=subscription.id AND tracker.link=-1 WHERE campaign.status=1 AND ' + intervalQuery('`campaign`.`created`', trigger.seconds, treshold) + ' AND tracker.created IS NULL AND subscription.id NOT IN (SELECT subscription FROM `trigger__' + id + '` triggertable WHERE triggertable.`list` = ' + trigger.list + ' AND triggertable.`subscription` = subscription.`id`) LIMIT ' + limit;
|
||||
break;
|
||||
case 'clicked':
|
||||
query = 'SELECT subscription.id AS id FROM `subscription__' + trigger.list + '` subscription LEFT JOIN `campaign__' + trigger.sourceCampaign + '` campaign ON campaign.list=' + trigger.list + ' AND subscription.id=campaign.subscription LEFT JOIN `campaign_tracker__' + trigger.sourceCampaign + '` tracker ON tracker.list=campaign.list AND tracker.subscriber=subscription.id AND tracker.link=0 WHERE campaign.status=1 AND ' + intervalQuery('`tracker`.`created`', trigger.seconds, treshold) + ' AND subscription.id NOT IN (SELECT subscription FROM `trigger__' + id + '` triggertable WHERE triggertable.`list` = ' + trigger.list + ' AND triggertable.`subscription` = subscription.`id`) LIMIT ' + limit;
|
||||
break;
|
||||
case 'opened':
|
||||
query = 'SELECT subscription.id AS id FROM `subscription__' + trigger.list + '` subscription LEFT JOIN `campaign__' + trigger.sourceCampaign + '` campaign ON campaign.list=' + trigger.list + ' AND subscription.id=campaign.subscription LEFT JOIN `campaign_tracker__' + trigger.sourceCampaign + '` tracker ON tracker.list=campaign.list AND tracker.subscriber=subscription.id AND tracker.link=-1 WHERE campaign.status=1 AND ' + intervalQuery('`tracker`.`created`', trigger.seconds, treshold) + ' AND subscription.id NOT IN (SELECT subscription FROM `trigger__' + id + '` triggertable WHERE triggertable.`list` = ' + trigger.list + ' AND triggertable.`subscription` = subscription.`id`) LIMIT ' + limit;
|
||||
break;
|
||||
|
||||
let getSegmentQuery = (segmentId, next) => {
|
||||
segmentId = Number(segmentId);
|
||||
if (!segmentId) {
|
||||
return next(null, {
|
||||
where: '',
|
||||
values: []
|
||||
});
|
||||
}
|
||||
|
||||
segments.getQuery(segmentId, 'subscription', next);
|
||||
};
|
||||
|
||||
getSegmentQuery(trigger.segment, (err, queryData) => {
|
||||
if (err) {
|
||||
log.err('Triggers', err);
|
||||
return null;
|
||||
}
|
||||
|
||||
let query = false;
|
||||
let querySegmentSubscription = '';
|
||||
let querySegmentTriggertable = '';
|
||||
if (trigger.segment > 0)
|
||||
{
|
||||
querySegmentSubscription = (queryData.where ? ' AND (' + queryData.where + ')' : '');
|
||||
querySegmentTriggertable = ' AND triggertable.`segment` = ' + trigger.segment;
|
||||
}
|
||||
|
||||
switch (trigger.rule) {
|
||||
case 'subscription':
|
||||
query = 'SELECT id FROM `subscription__' + trigger.list + '` subscription WHERE status=1 AND ' + intervalQuery('`' + trigger.column + '`', trigger.seconds, treshold) + ' ' + querySegmentSubscription + ' AND id NOT IN (SELECT subscription FROM `trigger__' + id + '` triggertable WHERE triggertable.`list` = ' + trigger.list + ' ' + querySegmentTriggertable + ' AND triggertable.`subscription` = subscription.`id`) LIMIT ' + limit;
|
||||
break;
|
||||
case 'campaign':
|
||||
switch (trigger.column) {
|
||||
case 'delivered':
|
||||
query = 'SELECT subscription.id AS id FROM `subscription__' + trigger.list + '` subscription LEFT JOIN `campaign__' + trigger.sourceCampaign + '` campaign ON campaign.list=' + trigger.list + ' AND subscription.id=campaign.subscription WHERE campaign.status=1 AND ' + intervalQuery('`campaign`.`created`', trigger.seconds, treshold) + ' ' + querySegmentSubscription + ' AND subscription.id NOT IN (SELECT subscription FROM `trigger__' + id + '` triggertable WHERE triggertable.`list` = ' + trigger.list + ' ' + querySegmentTriggertable + ' AND triggertable.`subscription` = subscription.`id`) LIMIT ' + limit;
|
||||
break;
|
||||
case 'not_clicked':
|
||||
query = 'SELECT subscription.id AS id FROM `subscription__' + trigger.list + '` subscription LEFT JOIN `campaign__' + trigger.sourceCampaign + '` campaign ON campaign.list=' + trigger.list + ' AND subscription.id=campaign.subscription LEFT JOIN `campaign_tracker__' + trigger.sourceCampaign + '` tracker ON tracker.list=campaign.list AND tracker.subscriber=subscription.id AND tracker.link=0 WHERE campaign.status=1 AND ' + intervalQuery('`campaign`.`created`', trigger.seconds, treshold) + ' AND tracker.created IS NULL ' + querySegmentSubscription + ' AND subscription.id NOT IN (SELECT subscription FROM `trigger__' + id + '` triggertable WHERE triggertable.`list` = ' + trigger.list + ' ' + querySegmentTriggertable + ' AND triggertable.`subscription` = subscription.`id`) LIMIT ' + limit;
|
||||
break;
|
||||
case 'not_opened':
|
||||
query = 'SELECT subscription.id AS id FROM `subscription__' + trigger.list + '` subscription LEFT JOIN `campaign__' + trigger.sourceCampaign + '` campaign ON campaign.list=' + trigger.list + ' AND subscription.id=campaign.subscription LEFT JOIN `campaign_tracker__' + trigger.sourceCampaign + '` tracker ON tracker.list=campaign.list AND tracker.subscriber=subscription.id AND tracker.link=-1 WHERE campaign.status=1 AND ' + intervalQuery('`campaign`.`created`', trigger.seconds, treshold) + ' AND tracker.created IS NULL ' + querySegmentSubscription + ' AND subscription.id NOT IN (SELECT subscription FROM `trigger__' + id + '` triggertable WHERE triggertable.`list` = ' + trigger.list + ' ' + querySegmentTriggertable + ' AND triggertable.`subscription` = subscription.`id`) LIMIT ' + limit;
|
||||
break;
|
||||
case 'clicked':
|
||||
query = 'SELECT subscription.id AS id FROM `subscription__' + trigger.list + '` subscription LEFT JOIN `campaign__' + trigger.sourceCampaign + '` campaign ON campaign.list=' + trigger.list + ' AND subscription.id=campaign.subscription LEFT JOIN `campaign_tracker__' + trigger.sourceCampaign + '` tracker ON tracker.list=campaign.list AND tracker.subscriber=subscription.id AND tracker.link=0 WHERE campaign.status=1 AND ' + intervalQuery('`tracker`.`created`', trigger.seconds, treshold) + ' ' + querySegmentSubscription + ' AND subscription.id NOT IN (SELECT subscription FROM `trigger__' + id + '` triggertable WHERE triggertable.`list` = ' + trigger.list + ' ' + querySegmentTriggertable + ' AND triggertable.`subscription` = subscription.`id`) LIMIT ' + limit;
|
||||
break;
|
||||
case 'opened':
|
||||
query = 'SELECT subscription.id AS id FROM `subscription__' + trigger.list + '` subscription LEFT JOIN `campaign__' + trigger.sourceCampaign + '` campaign ON campaign.list=' + trigger.list + ' AND subscription.id=campaign.subscription LEFT JOIN `campaign_tracker__' + trigger.sourceCampaign + '` tracker ON tracker.list=campaign.list AND tracker.subscriber=subscription.id AND tracker.link=-1 WHERE campaign.status=1 AND ' + intervalQuery('`tracker`.`created`', trigger.seconds, treshold) + ' ' + querySegmentSubscription + ' AND subscription.id NOT IN (SELECT subscription FROM `trigger__' + id + '` triggertable WHERE triggertable.`list` = ' + trigger.list + ' ' + querySegmentTriggertable + ' AND triggertable.`subscription` = subscription.`id`) LIMIT ' + limit;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if (trigger.segment > 0) {
|
||||
let values = queryData.values.concat([trigger.list, trigger.segment]);
|
||||
for (let i = 0; i < values.length; i++) {
|
||||
query = query.replace('?', db.escape(values[i]));
|
||||
}
|
||||
break;
|
||||
}
|
||||
callback(null, query);
|
||||
}
|
||||
|
||||
callback(null, query);
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
};
|
||||
|
||||
|
@ -168,6 +210,7 @@ module.exports.create = (trigger, callback) => {
|
|||
let name = (trigger.name || '').toString().trim();
|
||||
let description = (trigger.description || '').toString().trim();
|
||||
let listId = Number(trigger.list) || 0;
|
||||
let segmentId = Number(trigger.segmentId) || 0;
|
||||
let seconds = (Number(trigger.days) || 0) * 24 * 3600;
|
||||
let rule = (trigger.rule || '').toString().toLowerCase().trim();
|
||||
let destCampaign = Number(trigger.destCampaign) || 0;
|
||||
|
@ -220,8 +263,8 @@ module.exports.create = (trigger, callback) => {
|
|||
return callback(err);
|
||||
}
|
||||
|
||||
let keys = ['name', 'description', 'list', 'source_campaign', 'rule', 'column', 'seconds', 'dest_campaign', 'last_check'];
|
||||
let values = [name, description, list.id, sourceCampaign, rule, column, seconds, destCampaign];
|
||||
let keys = ['name', 'description', 'list', 'segment', 'source_campaign', 'rule', 'column', 'seconds', 'dest_campaign', 'last_check'];
|
||||
let values = [name, description, list.id, segmentId, sourceCampaign, rule, column, seconds, destCampaign];
|
||||
|
||||
let query = 'INSERT INTO `triggers` (`' + keys.join('`, `') + '`) VALUES (' + values.map(() => '?').join(', ') + ', NOW())';
|
||||
|
||||
|
|
File diff suppressed because one or more lines are too long
|
@ -1,4 +1,5 @@
|
|||
/* #008f73 #4c9790 */
|
||||
/* Class names prefixes */
|
||||
/* Colors / Theme */
|
||||
.gjs-clm-tags .gjs-sm-title,
|
||||
.gjs-sm-sector .gjs-sm-title {
|
||||
border-top: none; }
|
||||
|
@ -42,8 +43,7 @@
|
|||
.gjs-sm-sector .gjs-sm-composite.gjs-clm-field,
|
||||
.gjs-sm-sector .gjs-sm-field.gjs-sm-composite,
|
||||
.gjs-sm-sector .gjs-sm-stack #gjs-sm-add {
|
||||
color: #a0aabf;
|
||||
/* #a0aabf #d0d6e2 */ }
|
||||
color: #a0aabf; }
|
||||
|
||||
#gjs-rte-toolbar,
|
||||
.gjs-bg-main,
|
||||
|
|
|
@ -50,4 +50,4 @@
|
|||
.cm-s-material .CodeMirror-matchingbracket {
|
||||
text-decoration: underline;
|
||||
color: white !important;
|
||||
}
|
||||
}
|
||||
|
|
File diff suppressed because one or more lines are too long
2
obsolete/public/grapejs/dist/css/tooltip.css
vendored
2
obsolete/public/grapejs/dist/css/tooltip.css
vendored
|
@ -85,4 +85,4 @@
|
|||
-webkit-transform: translate(-0.5rem, 50%);
|
||||
-ms-transform: translate(-0.5rem, 50%);
|
||||
transform: translate(-0.5rem, 50%);
|
||||
}
|
||||
}
|
||||
|
|
30
obsolete/public/grapejs/dist/js/grapes.min.js
vendored
30
obsolete/public/grapejs/dist/js/grapes.min.js
vendored
File diff suppressed because one or more lines are too long
177
obsolete/public/grapejs/dist/js/grapesjs-mjml.min.js
vendored
177
obsolete/public/grapejs/dist/js/grapesjs-mjml.min.js
vendored
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
@ -48,7 +48,7 @@ router.get('/create', passport.csrfProtection, (req, res) => {
|
|||
data.list = Number(data.list.split(':').shift());
|
||||
}
|
||||
|
||||
getSettings(['defaultFrom', 'defaultAddress', 'defaultSubject'], (err, configItems) => {
|
||||
settings.list(['defaultFrom', 'defaultAddress', 'defaultSubject', 'defaultUnsubscribe'], (err, configItems) => {
|
||||
if (err) {
|
||||
req.flash('danger', err.message || err);
|
||||
return res.redirect('/');
|
||||
|
@ -95,6 +95,7 @@ router.get('/create', passport.csrfProtection, (req, res) => {
|
|||
data.address = data.address || configItems.defaultAddress;
|
||||
data.replyTo = data.replyTo || '';
|
||||
data.subject = data.subject || configItems.defaultSubject;
|
||||
data.unsubscribe = data.unsubscribe || configItems.defaultUnsubscribe;
|
||||
|
||||
let view;
|
||||
switch (req.query.type) {
|
||||
|
|
|
@ -19,7 +19,7 @@ const senders = require('../lib/senders');
|
|||
|
||||
let settings = require('../lib/models/settings');
|
||||
|
||||
let allowedKeys = ['service_url', 'smtp_hostname', 'smtp_port', 'smtp_encryption', 'smtp_disable_auth', 'smtp_user', 'smtp_pass', 'admin_email', 'smtp_log', 'smtp_max_connections', 'smtp_max_messages', 'smtp_self_signed', 'default_from', 'default_address', 'default_subject', 'default_homepage', 'default_postaddress', 'default_sender', 'verp_hostname', 'verp_use', 'disable_wysiwyg', 'pgp_private_key', 'pgp_passphrase', 'ua_code', 'shoutout', 'disable_confirmations', 'smtp_throttling', 'dkim_api_key', 'dkim_private_key', 'dkim_selector', 'dkim_domain', 'mail_transport', 'ses_key', 'ses_secret', 'ses_region'];
|
||||
let allowedKeys = ['service_url', 'smtp_hostname', 'smtp_port', 'smtp_encryption', 'smtp_disable_auth', 'smtp_user', 'smtp_pass', 'admin_email', 'smtp_log', 'smtp_max_connections', 'smtp_max_messages', 'smtp_self_signed', 'default_from', 'default_address', 'default_subject', 'default_homepage', 'default_postaddress', 'default_sender', 'verp_hostname', 'verp_use', 'disable_wysiwyg', 'pgp_private_key', 'pgp_passphrase', 'ua_code', 'shoutout', 'disable_confirmations', 'smtp_throttling', 'dkim_api_key', 'dkim_private_key', 'dkim_selector', 'dkim_domain', 'mail_transport', 'ses_key', 'ses_secret', 'ses_region', 'x_mailer', 'default_unsubscribe'];
|
||||
|
||||
router.all('/*', (req, res, next) => {
|
||||
if (!req.user) {
|
||||
|
|
|
@ -5,6 +5,7 @@ let router = new express.Router();
|
|||
let triggers = require('../lib/models/triggers');
|
||||
let campaigns = require('../lib/models/campaigns');
|
||||
let lists = require('../lib/models/lists');
|
||||
let segments = require('../lib/models/segments');
|
||||
let fields = require('../lib/models/fields');
|
||||
let striptags = require('striptags');
|
||||
let passport = require('../lib/passport');
|
||||
|
@ -58,15 +59,23 @@ router.get('/create-select', passport.csrfProtection, (req, res, next) => {
|
|||
});
|
||||
|
||||
router.post('/create-select', passport.parseForm, passport.csrfProtection, (req, res) => {
|
||||
if (!req.body.list) {
|
||||
// Check if req.body.list is in correct format ("listId:segmentId")
|
||||
if (!req.body.list || !/([\d]+):([\d]+)/.test(req.body.list)) {
|
||||
req.flash('danger', _('Could not find selected list'));
|
||||
return res.redirect('/triggers/create-select');
|
||||
}
|
||||
res.redirect('/triggers/' + encodeURIComponent(req.body.list) + '/create');
|
||||
|
||||
let listId = parseInt(req.body.list.split(':')[0]);
|
||||
let segmentId = parseInt(req.body.list.split(':')[1]);
|
||||
|
||||
res.redirect('/triggers/' + encodeURIComponent(listId) + '/' + encodeURIComponent(segmentId) + '/create');
|
||||
});
|
||||
|
||||
|
||||
router.get('/:listId/create', passport.csrfProtection, (req, res, next) => {
|
||||
router.get('/:listId/:segmentId/create', passport.csrfProtection, (req, res, next) => {
|
||||
let listId = parseInt(req.params.listId);
|
||||
let segmentId = parseInt(req.params.segmentId);
|
||||
|
||||
let data = tools.convertKeys(req.query, {
|
||||
skip: ['layout']
|
||||
});
|
||||
|
@ -74,52 +83,69 @@ router.get('/:listId/create', passport.csrfProtection, (req, res, next) => {
|
|||
data.csrfToken = req.csrfToken();
|
||||
data.days = Math.max(Number(data.days) || 1, 1);
|
||||
|
||||
lists.get(req.params.listId, (err, list) => {
|
||||
lists.get(listId, (err, list) => {
|
||||
if (err || !list) {
|
||||
req.flash('danger', err && err.message || err || _('Could not find selected list'));
|
||||
return res.redirect('/triggers/create-select');
|
||||
}
|
||||
fields.list(list.id, (err, fieldList) => {
|
||||
if (err && !fieldList) {
|
||||
fieldList = [];
|
||||
|
||||
segments.get(segmentId, (err, segment) => {
|
||||
if (segmentId > 0 && err) {
|
||||
req.flash('danger', err && err.message || err || _('Error while finding selected segment'));
|
||||
return res.redirect('/triggers/create-select');
|
||||
}
|
||||
|
||||
data.columns = triggers.defaultColumns.concat(fieldList.filter(field => fields.genericTypes[field.type] === 'date')).map(field => ({
|
||||
column: field.column,
|
||||
name: field.name,
|
||||
selected: data.column === field.column
|
||||
}));
|
||||
|
||||
campaigns.list(0, 300, (err, campaignList) => {
|
||||
if (err) {
|
||||
return next(err);
|
||||
segments.subscribers(segmentId, true, (err, segmentSubscribers) => {
|
||||
if (segmentId > 0 && err) {
|
||||
req.flash('danger', err && err.message || err || _('Error while finding selected segment'));
|
||||
return res.redirect('/triggers/create-select');
|
||||
}
|
||||
|
||||
data.sourceCampaigns = (campaignList || []).filter(campaign => campaign.list === list.id).map(campaign => ({
|
||||
id: campaign.id,
|
||||
name: campaign.name,
|
||||
selected: Number(data.sourceCampaign) === campaign.id
|
||||
}));
|
||||
fields.list(list.id, (err, fieldList) => {
|
||||
if (err && !fieldList) {
|
||||
fieldList = [];
|
||||
}
|
||||
|
||||
data.destCampaigns = (campaignList || []).filter(campaign => campaign.list === list.id && campaign.type === 4).map(campaign => ({
|
||||
id: campaign.id,
|
||||
name: campaign.name,
|
||||
selected: Number(data.destCampaign) === campaign.id
|
||||
}));
|
||||
data.columns = triggers.defaultColumns.concat(fieldList.filter(field => fields.genericTypes[field.type] === 'date')).map(field => ({
|
||||
column: field.column,
|
||||
name: field.name,
|
||||
selected: data.column === field.column
|
||||
}));
|
||||
|
||||
data.list = list;
|
||||
data.isSubscription = data.rule === 'subscription' || !data.rule;
|
||||
data.isCampaign = data.rule === 'campaign';
|
||||
campaigns.list(0, 300, (err, campaignList) => {
|
||||
if (err) {
|
||||
return next(err);
|
||||
}
|
||||
|
||||
data.campaignOptions = triggers.defaultCampaignEvents.map(evt => ({
|
||||
option: evt.option,
|
||||
name: evt.name,
|
||||
selected: Number(data.sourceCampaign) === evt.option
|
||||
}));
|
||||
data.sourceCampaigns = (campaignList || []).filter(campaign => campaign.list === list.id).map(campaign => ({
|
||||
id: campaign.id,
|
||||
name: campaign.name,
|
||||
selected: Number(data.sourceCampaign) === campaign.id
|
||||
}));
|
||||
|
||||
data.isSend = true;
|
||||
data.destCampaigns = (campaignList || []).filter(campaign => campaign.list === list.id && (segmentId <= 0 || campaign.segment === segmentId) && campaign.type === 4).map(campaign => ({
|
||||
id: campaign.id,
|
||||
name: campaign.name,
|
||||
selected: Number(data.destCampaign) === campaign.id
|
||||
}));
|
||||
|
||||
res.render('triggers/create', data);
|
||||
data.list = list;
|
||||
data.segment = segment;
|
||||
data.segmentSubscribers = segmentSubscribers;
|
||||
data.isSubscription = data.rule === 'subscription' || !data.rule;
|
||||
data.isCampaign = data.rule === 'campaign';
|
||||
|
||||
data.campaignOptions = triggers.defaultCampaignEvents.map(evt => ({
|
||||
option: evt.option,
|
||||
name: evt.name,
|
||||
selected: Number(data.sourceCampaign) === evt.option
|
||||
}));
|
||||
|
||||
data.isSend = true;
|
||||
|
||||
res.render('triggers/create', data);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -154,51 +180,73 @@ router.get('/edit/:id', passport.csrfProtection, (req, res, next) => {
|
|||
req.flash('danger', err && err.message || err || _('Could not find selected list'));
|
||||
return res.redirect('/triggers');
|
||||
}
|
||||
fields.list(list.id, (err, fieldList) => {
|
||||
if (err && !fieldList) {
|
||||
fieldList = [];
|
||||
|
||||
segments.get(trigger.segment, (err, segment) => {
|
||||
if (trigger.segment > 0 && err) {
|
||||
req.flash('danger', err && err.message || err || _('Error while finding selected segment'));
|
||||
return res.redirect('/triggers');
|
||||
}
|
||||
|
||||
let segmentId = 0;
|
||||
if (trigger.segment > 0) {
|
||||
segmentId = segment.id;
|
||||
}
|
||||
|
||||
campaigns.list(0, 300, (err, campaignList) => {
|
||||
if (err) {
|
||||
return next(err);
|
||||
segments.subscribers(segmentId, true, (err, segmentSubscribers) => {
|
||||
if (trigger.segment > 0 && err) {
|
||||
req.flash('danger', err && err.message || err || _('Error while finding selected segment subscribers'));
|
||||
return res.redirect('/triggers');
|
||||
}
|
||||
|
||||
trigger.sourceCampaigns = (campaignList || []).filter(campaign => campaign.list === list.id).map(campaign => ({
|
||||
id: campaign.id,
|
||||
name: campaign.name,
|
||||
selected: Number(trigger.sourceCampaign) === campaign.id
|
||||
}));
|
||||
fields.list(list.id, (err, fieldList) => {
|
||||
if (err && !fieldList) {
|
||||
fieldList = [];
|
||||
}
|
||||
|
||||
trigger.destCampaigns = (campaignList || []).filter(campaign => campaign.list === list.id && campaign.type === 4).map(campaign => ({
|
||||
id: campaign.id,
|
||||
name: campaign.name,
|
||||
selected: Number(trigger.destCampaign) === campaign.id
|
||||
}));
|
||||
campaigns.list(0, 300, (err, campaignList) => {
|
||||
if (err) {
|
||||
return next(err);
|
||||
}
|
||||
|
||||
trigger.list = list;
|
||||
trigger.isSubscription = trigger.rule === 'subscription' || !trigger.rule;
|
||||
trigger.isCampaign = trigger.rule === 'campaign';
|
||||
trigger.sourceCampaigns = (campaignList || []).filter(campaign => campaign.list === list.id).map(campaign => ({
|
||||
id: campaign.id,
|
||||
name: campaign.name,
|
||||
selected: Number(trigger.sourceCampaign) === campaign.id
|
||||
}));
|
||||
|
||||
trigger.columns = triggers.defaultColumns.concat(fieldList.filter(field => fields.genericTypes[field.type] === 'date')).map(field => ({
|
||||
column: field.column,
|
||||
name: field.name,
|
||||
selected: trigger.isSubscription && trigger.column === field.column
|
||||
}));
|
||||
trigger.destCampaigns = (campaignList || []).filter(campaign => campaign.list === list.id && campaign.type === 4).map(campaign => ({
|
||||
id: campaign.id,
|
||||
name: campaign.name,
|
||||
selected: Number(trigger.destCampaign) === campaign.id
|
||||
}));
|
||||
|
||||
trigger.campaignOptions = triggers.defaultCampaignEvents.map(evt => ({
|
||||
option: evt.option,
|
||||
name: evt.name,
|
||||
selected: trigger.isCampaign && trigger.column === evt.option
|
||||
}));
|
||||
trigger.list = list;
|
||||
trigger.segment = segment;
|
||||
trigger.segmentSubscribers = segmentSubscribers;
|
||||
trigger.isSubscription = trigger.rule === 'subscription' || !trigger.rule;
|
||||
trigger.isCampaign = trigger.rule === 'campaign';
|
||||
|
||||
if (trigger.rule !== 'subscription') {
|
||||
trigger.column = null;
|
||||
}
|
||||
trigger.columns = triggers.defaultColumns.concat(fieldList.filter(field => fields.genericTypes[field.type] === 'date')).map(field => ({
|
||||
column: field.column,
|
||||
name: field.name,
|
||||
selected: trigger.isSubscription && trigger.column === field.column
|
||||
}));
|
||||
|
||||
trigger.isSend = true;
|
||||
trigger.campaignOptions = triggers.defaultCampaignEvents.map(evt => ({
|
||||
option: evt.option,
|
||||
name: evt.name,
|
||||
selected: trigger.isCampaign && trigger.column === evt.option
|
||||
}));
|
||||
|
||||
res.render('triggers/edit', trigger);
|
||||
if (trigger.rule !== 'subscription') {
|
||||
trigger.column = null;
|
||||
}
|
||||
|
||||
trigger.isSend = true;
|
||||
|
||||
res.render('triggers/edit', trigger);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -316,7 +316,7 @@ function formatMessage(message, callback) {
|
|||
return callback(new Error(_('List not found')));
|
||||
}
|
||||
|
||||
getSettings(['serviceUrl', 'verpUse', 'verpHostname'], (err, configItems) => {
|
||||
settings.list(['serviceUrl', 'verpUse', 'verpHostname', 'xMailer'], (err, configItems) => {
|
||||
if (err) {
|
||||
return callback(err);
|
||||
}
|
||||
|
@ -383,13 +383,18 @@ function formatMessage(message, callback) {
|
|||
wordwrap: 130
|
||||
});
|
||||
|
||||
let listUnsubscribe = null;
|
||||
if (!list.listunsubscribeDisabled) {
|
||||
listUnsubscribe = campaign.unsubscribe ? tools.formatMessage(configItems.serviceUrl, campaign, list, message.subscription, campaign.unsubscribe) : url.resolve(configItems.serviceUrl, '/subscription/' + list.cid + '/unsubscribe/' + message.subscription.cid);
|
||||
}
|
||||
|
||||
return callback(null, {
|
||||
from: {
|
||||
name: campaign.from,
|
||||
address: campaign.address
|
||||
},
|
||||
replyTo: campaign.replyTo,
|
||||
xMailer: 'Mailtrain Mailer (+https://mailtrain.org)',
|
||||
xMailer: configItems.xMailer ? configItems.xMailer : false,
|
||||
to: {
|
||||
name: [].concat(message.subscription.firstName || []).concat(message.subscription.lastName || []).join(' '),
|
||||
address: message.subscription.email
|
||||
|
@ -423,7 +428,7 @@ function formatMessage(message, callback) {
|
|||
}
|
||||
},
|
||||
list: {
|
||||
unsubscribe: url.resolve(configItems.serviceUrl, '/subscription/' + list.cid + '/unsubscribe/' + message.subscription.cid)
|
||||
unsubscribe: listUnsubscribe
|
||||
},
|
||||
subject: tools.formatMessage(configItems.serviceUrl, campaign, list, message.subscription, campaign.subject),
|
||||
html: renderedHtml,
|
||||
|
|
|
@ -84,8 +84,8 @@ function fireTrigger(trigger, callback) {
|
|||
}
|
||||
let subscriber = rows[pos++].id;
|
||||
|
||||
let query = 'INSERT INTO `trigger__' + trigger.id + '` (`list`, `subscription`) VALUES (?,?)';
|
||||
let values = [trigger.list, subscriber];
|
||||
let query = 'INSERT INTO `trigger__' + trigger.id + '` (`list`, `segment`, `subscription`) VALUES (?,?,?)';
|
||||
let values = [trigger.list, trigger.segment, subscriber];
|
||||
|
||||
connection.query(query, values, (err, result) => {
|
||||
if (err && err.code !== 'ER_DUP_ENTRY') {
|
||||
|
|
|
@ -106,6 +106,12 @@
|
|||
<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 class="form-group">
|
||||
<label for="unsubscribe" class="col-sm-2 control-label">{{#translate}}Custom unsubscribe (URL){{/translate}}</label>
|
||||
<div class="col-sm-10">
|
||||
<input type="text" class="form-control" name="unsubscribe" id="unsubscribe" value="{{unsubscribe}}" placeholder="{{#translate}}Set a custom unsubscribe url{{/translate}}">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-sm-offset-2">
|
||||
<div class="checkbox">
|
||||
|
|
|
@ -117,6 +117,12 @@
|
|||
<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 class="form-group">
|
||||
<label for="unsubscribe" class="col-sm-2 control-label">{{#translate}}Custom unsubscribe (URL){{/translate}}</label>
|
||||
<div class="col-sm-10">
|
||||
<input type="text" class="form-control" name="unsubscribe" id="unsubscribe" value="{{unsubscribe}}" placeholder="{{#translate}}Set a custom unsubscribe url{{/translate}}">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-sm-offset-2">
|
||||
<div class="checkbox">
|
||||
|
|
|
@ -40,18 +40,6 @@
|
|||
#merge-tag-reference-container table th {
|
||||
padding: 5px 20px 5px 0;
|
||||
}
|
||||
|
||||
/* Fixed width sidebar */
|
||||
#gjs > .gjs-editor > .gjs-cv-canvas { width: 100% !important; padding-right: 300px !important; }
|
||||
#gjs-pn-views, #gjs-pn-views-container { width: 300px !important; }
|
||||
#gjs-pn-options { right: 300px !important; }
|
||||
#gjs-pn-commands { width: 100% !important; padding-right: 310px !important; }
|
||||
|
||||
/* Hide the fullscreen button - doesn't work from within our iFrame */
|
||||
#gjs-pn-options .gjs-pn-btn.fa.fa-arrows-alt { display: none !important; }
|
||||
|
||||
/* Hide pencil icon on image toolbar, issue #195 */
|
||||
.gjs-toolbar > div > div.fa-pencil { display: none; }
|
||||
</style>
|
||||
|
||||
|
||||
|
@ -68,7 +56,7 @@
|
|||
<form id="test-form" class="test-form" action="/editorapi/test?editor=grapejs" method="post" style="display: none">
|
||||
<div class="putsmail-c">
|
||||
<div class="gjs-sm-property" style="font-size: 10px">
|
||||
Hello! I'm a placerholder message.
|
||||
Please enter the email address to send a test to:
|
||||
<span class="form-status" style="opacity: 0">
|
||||
<i class="fa fa-refresh anim-spin" aria-hidden="true"></i>
|
||||
</span>
|
||||
|
@ -143,6 +131,12 @@
|
|||
assets: [],
|
||||
upload: '/editorapi/upload?type={{type}}&id={{resource.id}}&editor={{editor.name}}',
|
||||
uploadText: 'Drop images here or click to upload',
|
||||
headers: {
|
||||
'X-CSRF-TOKEN': '{{csrfToken}}',
|
||||
},
|
||||
},
|
||||
styleManager: {
|
||||
clearProperties: true,
|
||||
},
|
||||
container : '#gjs',
|
||||
fromElement: false,
|
||||
|
@ -356,13 +350,60 @@
|
|||
// TODO: Show a spinner
|
||||
getPreparedHtml(function(html) {
|
||||
sender.set('active', 0);
|
||||
var modalContent = md.getContentEl();
|
||||
var mdlDialog = document.querySelector('.gjs-mdl-dialog');
|
||||
testContentEl.value = html;
|
||||
mdlDialog.className += ' ' + mdlClass;
|
||||
testContainer.style.display = 'block';
|
||||
md.setTitle('Test your Newsletter');
|
||||
md.setContent(testContainer);
|
||||
|
||||
var modalContent = $('<div>').append(testContainer).html();
|
||||
for(var i=0; i<100; i++) {
|
||||
try {
|
||||
md.setContent(modalContent);
|
||||
break;
|
||||
} catch(err) {}
|
||||
}
|
||||
|
||||
testContainerCopy = $(".gjs-mdl-dialog #test-form");
|
||||
|
||||
var statusFormElC = document.querySelector('.gjs-mdl-dialog .form-status');
|
||||
var statusFormEl = document.querySelector('.gjs-mdl-dialog .form-status i');
|
||||
|
||||
var ajaxTest = ajaxable(testContainerCopy, { headers: { 'X-CSRF-TOKEN': '{{csrfToken}}' } })
|
||||
.onStart(function() {
|
||||
statusFormEl.className = 'fa fa-refresh anim-spin';
|
||||
statusFormElC.style.opacity = '1';
|
||||
statusFormElC.className = 'form-status';
|
||||
})
|
||||
.onResponse(function(res) {
|
||||
if (res.data) {
|
||||
statusFormElC.style.opacity = '0';
|
||||
statusFormEl.removeAttribute('data-tooltip');
|
||||
md.close();
|
||||
toastr.success('Testmail sent');
|
||||
} else if (res.errors) {
|
||||
statusFormEl.className = 'fa fa-exclamation-circle';
|
||||
statusFormEl.setAttribute('data-tooltip', res.errors);
|
||||
statusFormElC.className = 'form-status text-danger';
|
||||
toastr.error(res.errors);
|
||||
}
|
||||
});
|
||||
|
||||
// Remember testemail address
|
||||
|
||||
var isValidEmail = function(email) {
|
||||
return /\S+@\S+\.\S+/.test(email);
|
||||
};
|
||||
|
||||
if (isValidEmail(localStorage.getItem('testemail'))) {
|
||||
$('.gjs-mdl-dialog #test-form input[name=email]').val(localStorage.getItem('testemail'));
|
||||
}
|
||||
|
||||
$('.gjs-mdl-dialog #test-form').on('submit', function() {
|
||||
var email = $('.gjs-mdl-dialog #test-form input[name=email]').val();
|
||||
isValidEmail(email) && localStorage.setItem('testemail', email);
|
||||
});
|
||||
|
||||
md.open();
|
||||
md.getModel().once('change:open', function() {
|
||||
mdlDialog.className = mdlDialog.className.replace(mdlClass, '');
|
||||
|
@ -382,43 +423,6 @@
|
|||
},
|
||||
});
|
||||
|
||||
var statusFormElC = document.querySelector('.form-status');
|
||||
var statusFormEl = document.querySelector('.form-status i');
|
||||
|
||||
var ajaxTest = ajaxable(testContainer, { headers: { 'X-CSRF-TOKEN': '{{csrfToken}}' } })
|
||||
.onStart(function() {
|
||||
statusFormEl.className = 'fa fa-refresh anim-spin';
|
||||
statusFormElC.style.opacity = '1';
|
||||
statusFormElC.className = 'form-status';
|
||||
})
|
||||
.onResponse(function(res) {
|
||||
if (res.data) {
|
||||
statusFormElC.style.opacity = '0';
|
||||
statusFormEl.removeAttribute('data-tooltip');
|
||||
md.close();
|
||||
toastr.success('Testmail sent');
|
||||
} else if (res.errors) {
|
||||
statusFormEl.className = 'fa fa-exclamation-circle';
|
||||
statusFormEl.setAttribute('data-tooltip', res.errors);
|
||||
statusFormElC.className = 'form-status text-danger';
|
||||
toastr.error(res.errors);
|
||||
}
|
||||
});
|
||||
|
||||
// Remember testemail address
|
||||
|
||||
var isValidEmail = function(email) {
|
||||
return /\S+@\S+\.\S+/.test(email);
|
||||
};
|
||||
|
||||
if (isValidEmail(localStorage.getItem('testemail'))) {
|
||||
$('#test-form input[name=email]').val(localStorage.getItem('testemail'));
|
||||
}
|
||||
|
||||
$('#test-form').on('submit', function() {
|
||||
var email = $('#test-form input[name=email]').val();
|
||||
isValidEmail(email) && localStorage.setItem('testemail', email);
|
||||
});
|
||||
|
||||
|
||||
// Merge Tag Reference command
|
||||
|
@ -431,7 +435,12 @@
|
|||
mdlDialog.className += ' gjs-mdl-dialog-lg';
|
||||
mergeTagReferenceContainer.style.display = 'block';
|
||||
md.setTitle('Merge tag reference');
|
||||
md.setContent(mergeTagReferenceContainer);
|
||||
for(var i=0; i<100; i++) {
|
||||
try {
|
||||
md.setContent(mergeTagReferenceContainer);
|
||||
break;
|
||||
} catch(err) {}
|
||||
}
|
||||
md.open();
|
||||
md.getModel().once('change:open', function() {
|
||||
mdlDialog.className = mdlDialog.className.replace('gjs-mdl-dialog-lg', '');
|
||||
|
|
|
@ -4,20 +4,20 @@
|
|||
<meta charset="utf-8">
|
||||
<title>GrapesJS Newsletter Editor</title>
|
||||
|
||||
<link rel="stylesheet" href="/grapejs/dist/css/grapes.min.css?v=0.5.41">
|
||||
<link rel="stylesheet" href="/grapejs/dist/css/grapes.min.css?v=0.14.25">
|
||||
<link rel="stylesheet" href="/grapejs/dist/css/toastr.min.css?v=2.1.3">
|
||||
<link rel="stylesheet" href="/grapejs/dist/css/material.css">
|
||||
<link rel="stylesheet" href="/grapejs/dist/css/tooltip.css">
|
||||
|
||||
<script src="/javascript/jquery-2.2.1.min.js"></script>
|
||||
<script src="/grapejs/dist/js/grapes.min.js?v=0.5.41"></script>
|
||||
<script src="/grapejs/dist/js/grapes.min.js?v=0.14.25"></script>
|
||||
<script src="/grapejs/dist/js/toastr.min.js?v=2.1.3"></script>
|
||||
<script src="/grapejs/dist/js/ajaxable.min.js?v=0.2.3"></script>
|
||||
|
||||
{{#switch editor.mode}}
|
||||
{{#case "mjml"}}
|
||||
<link rel="stylesheet" href="/grapejs/dist/css/grapesjs-mjml.css?v=0.0.7">
|
||||
<script src="/grapejs/dist/js/grapesjs-mjml.min.js?v=0.0.7"></script>
|
||||
<script src="/grapejs/dist/js/grapesjs-mjml.min.js?v=0.0.27"></script>
|
||||
<script src="/grapejs/dist/js/grapesjs-preset-mjml.js"></script>
|
||||
{{/case}}
|
||||
{{#case "html"}}
|
||||
|
|
|
@ -80,6 +80,17 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="default_form" class="col-sm-2 control-label">{{#translate}}Unsubscribe Header{{/translate}}</label>
|
||||
<div class="col-sm-10">
|
||||
<div class="checkbox">
|
||||
<label>
|
||||
<input type="checkbox" name="listunsubscribe_disabled" value="1" {{#if listunsubscribeDisabled}} checked {{/if}}> {{#translate}}Do not send List-Unsubscribe headers{{/translate}}
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<hr />
|
||||
|
||||
<div class="form-group">
|
||||
|
|
|
@ -23,6 +23,20 @@
|
|||
</div>
|
||||
|
||||
{{#if columnTypeString}}
|
||||
<div class="form-group">
|
||||
<label for="negate" class="col-sm-2 control-label">{{#translate}}Condition{{/translate}}</label>
|
||||
<div class="col-sm-10">
|
||||
<select name="negate" id="negate" class="form-control">
|
||||
<option value="0">
|
||||
{{#translate}}Equals{{/translate}}
|
||||
</option>
|
||||
<option value="1" {{#if value.negate}} selected {{/if}}>
|
||||
{{#translate}}Not squals{{/translate}}
|
||||
</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="value" class="col-sm-2 control-label">{{#translate}}Value{{/translate}}</label>
|
||||
<div class="col-sm-10">
|
||||
|
|
|
@ -28,6 +28,20 @@
|
|||
</div>
|
||||
|
||||
{{#if columnTypeString}}
|
||||
<div class="form-group">
|
||||
<label for="negate" class="col-sm-2 control-label">{{#translate}}Condition{{/translate}}</label>
|
||||
<div class="col-sm-10">
|
||||
<select name="negate" id="negate" class="form-control">
|
||||
<option value="0">
|
||||
{{#translate}}Equals{{/translate}}
|
||||
</option>
|
||||
<option value="1" {{#if value.negate}} selected {{/if}}>
|
||||
{{#translate}}Not squals{{/translate}}
|
||||
</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="value" class="col-sm-2 control-label">{{#translate}}Value{{/translate}}</label>
|
||||
<div class="col-sm-10">
|
||||
|
|
|
@ -74,6 +74,14 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="x-mailer" class="col-sm-2 control-label">{{#translate}}X-Mailer header{{/translate}}</label>
|
||||
<div class="col-sm-10">
|
||||
<input type="text" class="form-control" name="x-mailer" id="x-mailer" value="{{xMailer}}">
|
||||
<span class="help-block">{{#translate}}Set a custom X-Mailer header value or leave empty to disable it{{/translate}}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</fieldset>
|
||||
|
||||
<fieldset>
|
||||
|
@ -122,6 +130,13 @@
|
|||
<span class="help-block"> </span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="default-unsubscribe" class="col-sm-2 control-label">{{#translate}}Default custom unsubscribe (URL)){{/translate}}</label>
|
||||
<div class="col-sm-10">
|
||||
<input type="url" class="form-control" name="default-unsubscribe" id="default-unsubscribe" value="{{defaultUnsubscribe}}" placeholder="{{#translate}}Custom unsubscribe URL, eg. http://example.com/unsubscribe/[EMAIL]{{/translate}}">
|
||||
<span class="help-block">{{#translate}}Set a custom unsubscribe url.{{/translate}}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
</fieldset>
|
||||
|
|
|
@ -17,9 +17,19 @@
|
|||
<select class="form-control" name="list" required>
|
||||
<option value=""> –– {{#translate}}Select{{/translate}} ––</option>
|
||||
{{#each listItems}}
|
||||
<option value="{{id}}">
|
||||
<option value="{{id}}:0">
|
||||
{{name}} <span class="text-muted"> — {{subscribers}} {{#translate}}subscribers{{/translate}}</span>
|
||||
</option>
|
||||
|
||||
{{#if segments}}
|
||||
<optgroup label="{{name}} segments">
|
||||
{{#each segments}}
|
||||
<option value="{{../id}}:{{id}}" {{#if selected}} selected {{/if}}>
|
||||
{{../name}}: {{name}}
|
||||
</option>
|
||||
{{/each}}
|
||||
</optgroup>
|
||||
{{/if}}
|
||||
{{/each}}
|
||||
</select>
|
||||
</div>
|
||||
|
|
|
@ -30,7 +30,7 @@
|
|||
<div class="form-group">
|
||||
<label class="col-sm-2 control-label">{{#translate}}List{{/translate}}</label>
|
||||
<div class="col-sm-10">
|
||||
<p class="form-control-static"><a href="/lists/view/{{list.id}}">{{list.name}}</a> <span class="text-muted"> – {{list.subscribers}} {{#translate}}subscribers{{/translate}}</span></p>
|
||||
<p class="form-control-static"><a href="/lists/view/{{list.id}}">{{list.name}}</a> <span class="text-muted"> {{#if segment.id}}– <a href="/lists/view/{{list.id}}?segment={{segment.id}}">{{segment.name}}</a>{{/if}} <span class="text-muted"> – {{#if segment.id}} {{segmentSubscribers}} {{else}} {{list.subscribers}} {{/if}} {{#translate}}subscribers{{/translate}}</span><input id="segmentId" name="segmentId" type="hidden" value="{{segment.id}}"></p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
|
|
@ -43,7 +43,7 @@
|
|||
<div class="form-group">
|
||||
<label class="col-sm-2 control-label">{{#translate}}List{{/translate}}</label>
|
||||
<div class="col-sm-10">
|
||||
<p class="form-control-static"><a href="/lists/view/{{list.id}}">{{list.name}}</a> <span class="text-muted"> – {{list.subscribers}} {{#translate}}subscribers{{/translate}}</span></p>
|
||||
<p class="form-control-static"><a href="/lists/view/{{list.id}}">{{list.name}}</a> <span class="text-muted"> {{#if segment.id}}– <a href="/lists/view/{{list.id}}?segment={{segment.id}}">{{segment.name}}</a>{{/if}} <span class="text-muted"> – {{#if segment.id}} {{segmentSubscribers}} {{else}} {{list.subscribers}} {{/if}} {{#translate}}subscribers{{/translate}}</span><input id="segmentId" name="segmentId" type="hidden" value="{{segment.id}}"></p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
|
|
@ -64,7 +64,7 @@
|
|||
{{description}}
|
||||
</td>
|
||||
<td class="text-info">
|
||||
<a href="/lists/view/{{list}}">{{listName}}</a>
|
||||
<a href="/lists/view/{{list}}">{{listName}}</a>{{#if segment}} - <a href="/lists/view/{{list}}?segment={{segment}}">{{segmentName}}</a>{{/if}}
|
||||
</td>
|
||||
<td class="text-info">
|
||||
{{{formatted}}}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
{
|
||||
"name": "mailtrain",
|
||||
"version": "1.23.2",
|
||||
"private": true,
|
||||
"version": "1.24.0",
|
||||
"description": "Self hosted email newsletter app",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
|
|
17
setup/sql/upgrade-00030.sql
Normal file
17
setup/sql/upgrade-00030.sql
Normal file
|
@ -0,0 +1,17 @@
|
|||
# Header section
|
||||
# Define incrementing schema version number
|
||||
SET @schema_version = '30';
|
||||
|
||||
# Upgrade script section
|
||||
#### INSERT YOUR UPGRADE SCRIPT BELOW THIS LINE ######
|
||||
|
||||
ALTER TABLE `lists` ADD COLUMN `listunsubscribe_disabled` tinyint(4) unsigned DEFAULT 0 NOT NULL;
|
||||
|
||||
#### INSERT YOUR UPGRADE SCRIPT ABOVE THIS LINE ######
|
||||
|
||||
# Footer section. Updates schema version in settings
|
||||
LOCK TABLES `settings` WRITE;
|
||||
/*!40000 ALTER TABLE `settings` DISABLE KEYS */;
|
||||
INSERT INTO `settings` (`key`, `value`) VALUES('db_schema_version', @schema_version) ON DUPLICATE KEY UPDATE `value`=@schema_version;
|
||||
/*!40000 ALTER TABLE `settings` ENABLE KEYS */;
|
||||
UNLOCK TABLES;
|
12
setup/sql/upgrade-00031.sql
Normal file
12
setup/sql/upgrade-00031.sql
Normal file
|
@ -0,0 +1,12 @@
|
|||
# Header section
|
||||
# Define incrementing schema version number
|
||||
SET @schema_version = '31';
|
||||
|
||||
# Add segment support for triggers
|
||||
ALTER TABLE `triggers` ADD `segment` INT(11) UNSIGNED NOT NULL AFTER `list`;
|
||||
ALTER TABLE `trigger` ADD `segment` INT(11) UNSIGNED NOT NULL AFTER `list`;
|
||||
|
||||
# Footer section
|
||||
LOCK TABLES `settings` WRITE;
|
||||
INSERT INTO `settings` (`key`, `value`) VALUES('db_schema_version', @schema_version) ON DUPLICATE KEY UPDATE `value`=@schema_version;
|
||||
UNLOCK TABLES;
|
15
setup/sql/upgrade-00032.sql
Normal file
15
setup/sql/upgrade-00032.sql
Normal file
|
@ -0,0 +1,15 @@
|
|||
# Header section
|
||||
# Define incrementing schema version number
|
||||
SET @schema_version = '32';
|
||||
|
||||
# Set default X-Mailer header value
|
||||
LOCK TABLES `settings` WRITE;
|
||||
INSERT INTO `settings` (`key`, `value`) VALUES ('x_mailer','Mailtrain Mailer (+https://mailtrain.org)') ON DUPLICATE KEY UPDATE `value`='Mailtrain Mailer (+https://mailtrain.org)';
|
||||
UNLOCK TABLES;
|
||||
|
||||
# Footer section. Updates schema version in settings
|
||||
LOCK TABLES `settings` WRITE;
|
||||
/*!40000 ALTER TABLE `settings` DISABLE KEYS */;
|
||||
INSERT INTO `settings` (`key`, `value`) VALUES('db_schema_version', @schema_version) ON DUPLICATE KEY UPDATE `value`=@schema_version;
|
||||
/*!40000 ALTER TABLE `settings` ENABLE KEYS */;
|
||||
UNLOCK TABLES;
|
13
setup/sql/upgrade-00033.sql
Normal file
13
setup/sql/upgrade-00033.sql
Normal file
|
@ -0,0 +1,13 @@
|
|||
# Header section
|
||||
# Define incrementing schema version number
|
||||
SET @schema_version = '33';
|
||||
|
||||
# Adds new column 'unsubscribe' to campaign table.
|
||||
ALTER TABLE campaigns ADD COLUMN `unsubscribe` VARCHAR(255) NOT NULL DEFAULT '' AFTER `subject`;
|
||||
|
||||
# Footer section. Updates schema version in settings
|
||||
LOCK TABLES `settings` WRITE;
|
||||
/*!40000 ALTER TABLE `settings` DISABLE KEYS */;
|
||||
INSERT INTO `settings` (`key`, `value`) VALUES('db_schema_version', @schema_version) ON DUPLICATE KEY UPDATE `value`=@schema_version;
|
||||
/*!40000 ALTER TABLE `settings` ENABLE KEYS */;
|
||||
UNLOCK TABLES;
|
Loading…
Reference in a new issue