Merge branch 'master' into master

This commit is contained in:
Tomas Bures 2019-03-09 09:06:06 +01:00 committed by GitHub
commit 33f94034e2
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
13 changed files with 78 additions and 7 deletions

View file

@ -1,3 +1,5 @@
## Mailtrain v2 beta is available. Check it out here https://github.com/Mailtrain-org/mailtrain/tree/development
# Mailtrain # Mailtrain
[Mailtrain](http://mailtrain.org) is a self hosted newsletter application built on Node.js (v7+) and MySQL (v5.5+ or MariaDB). [Mailtrain](http://mailtrain.org) is a self hosted newsletter application built on Node.js (v7+) and MySQL (v5.5+ or MariaDB).

View file

@ -1,4 +1,4 @@
# Mailtrain # Mailtrain (v1)
[Mailtrain](http://mailtrain.org) is a self hosted newsletter application built on Node.js (v7+) and MySQL (v5.5+ or MariaDB). [Mailtrain](http://mailtrain.org) is a self hosted newsletter application built on Node.js (v7+) and MySQL (v5.5+ or MariaDB).

View file

@ -365,6 +365,17 @@ module.exports.getLinks = (id, linkId, callback) => {
}); });
}; };
module.exports.duplicate = (id, callback) => module.exports.get(id, true, (err, campaign) => {
if (err) {
return callback(err);
}
if (!campaign) {
return callback(new Error(_('Campaign does not exist')));
}
campaign.name = campaign.name + ' Copy';
return module.exports.create(campaign, false, callback);
});
module.exports.create = (campaign, opts, callback) => { module.exports.create = (campaign, opts, callback) => {
campaign = tools.convertKeys(campaign); campaign = tools.convertKeys(campaign);

View file

@ -9,7 +9,7 @@ let Handlebars = require('handlebars');
let _ = require('../translate')._; let _ = require('../translate')._;
let util = require('util'); let util = require('util');
let allowedKeys = ['name', 'key', 'default_value', 'group', 'group_template', 'visible']; let allowedKeys = ['name', 'description', 'key', 'default_value', 'group', 'group_template', 'visible'];
let allowedTypes; let allowedTypes;
module.exports.grouped = ['radio', 'checkbox', 'dropdown']; module.exports.grouped = ['radio', 'checkbox', 'dropdown'];
@ -133,10 +133,11 @@ module.exports.create = (listId, field, callback) => {
field.group = null; field.group = null;
} }
field.description = (field.description || '').toString().trim() || null;
field.defaultValue = (field.defaultValue || '').toString().trim() || null; field.defaultValue = (field.defaultValue || '').toString().trim() || null;
field.groupTemplate = (field.groupTemplate || '').toString().trim() || null; field.groupTemplate = (field.groupTemplate || '').toString().trim() || null;
addCustomField(listId, field.name, field.defaultValue, field.type, field.group, field.groupTemplate, field.visible, callback); addCustomField(listId, field.name, field.description, field.defaultValue, field.type, field.group, field.groupTemplate, field.visible, callback);
}; };
module.exports.update = (id, updates, callback) => { module.exports.update = (id, updates, callback) => {
@ -157,6 +158,7 @@ module.exports.update = (id, updates, callback) => {
updates.key = slugify(updates.key, '_').toUpperCase(); updates.key = slugify(updates.key, '_').toUpperCase();
} }
updates.description = (updates.description || '').toString().trim() || null;
updates.defaultValue = (updates.defaultValue || '').toString().trim() || null; updates.defaultValue = (updates.defaultValue || '').toString().trim() || null;
updates.groupTemplate = (updates.groupTemplate || '').toString().trim() || null; updates.groupTemplate = (updates.groupTemplate || '').toString().trim() || null;
@ -277,7 +279,7 @@ module.exports.delete = (fieldId, callback) => {
}); });
}; };
function addCustomField(listId, name, defaultValue, type, group, groupTemplate, visible, callback) { function addCustomField(listId, name, description, defaultValue, type, group, groupTemplate, visible, callback) {
type = (type || '').toString().trim().toLowerCase(); type = (type || '').toString().trim().toLowerCase();
group = Number(group) || null; group = Number(group) || null;
listId = Number(listId) || 0; listId = Number(listId) || 0;
@ -314,8 +316,8 @@ function addCustomField(listId, name, defaultValue, type, group, groupTemplate,
column = ('custom_' + slugify(name, '_') + '_' + shortid.generate()).toLowerCase().replace(/[^a-z0-9_]/g, ''); column = ('custom_' + slugify(name, '_') + '_' + shortid.generate()).toLowerCase().replace(/[^a-z0-9_]/g, '');
} }
let query = 'INSERT INTO custom_fields (`list`, `name`, `key`,`default_value`, `type`, `group`, `group_template`, `column`, `visible`) VALUES(?,?,?,?,?,?,?,?,?)'; let query = 'INSERT INTO custom_fields (`list`, `name`, `description`, `key`,`default_value`, `type`, `group`, `group_template`, `column`, `visible`) VALUES(?,?,?,?,?,?,?,?,?,?)';
connection.query(query, [listId, name, key, defaultValue, type, group, groupTemplate, column, visible ? 1 : 0], (err, result) => { connection.query(query, [listId, name, description, key, defaultValue, type, group, groupTemplate, column, visible ? 1 : 0], (err, result) => {
if (err) { if (err) {
connection.release(); connection.release();
@ -409,6 +411,7 @@ module.exports.getRow = (fieldList, values, useDate, showAll, onlyExisting) => {
id: field.id, id: field.id,
type: field.type, type: field.type,
name: field.name, name: field.name,
description: field.description,
column: field.column, column: field.column,
value: (valueList[field.column] || '').toString().trim(), value: (valueList[field.column] || '').toString().trim(),
visible: !!field.visible, visible: !!field.visible,
@ -439,6 +442,7 @@ module.exports.getRow = (fieldList, values, useDate, showAll, onlyExisting) => {
id: field.id, id: field.id,
type: field.type, type: field.type,
name: field.name, name: field.name,
description: field.description,
column: field.column, column: field.column,
value: (valueList[field.column] || '').toString().trim(), value: (valueList[field.column] || '').toString().trim(),
visible: !!field.visible, visible: !!field.visible,
@ -455,6 +459,7 @@ module.exports.getRow = (fieldList, values, useDate, showAll, onlyExisting) => {
id: field.id, id: field.id,
type: field.type, type: field.type,
name: field.name, name: field.name,
description: field.description,
column: field.column, column: field.column,
value: Number(valueList[field.column]) || 0, value: Number(valueList[field.column]) || 0,
visible: !!field.visible, visible: !!field.visible,
@ -474,6 +479,7 @@ module.exports.getRow = (fieldList, values, useDate, showAll, onlyExisting) => {
id: field.id, id: field.id,
type: field.type, type: field.type,
name: field.name, name: field.name,
description: field.description,
visible: !!field.visible, visible: !!field.visible,
key: 'group-g' + field.id, key: 'group-g' + field.id,
mergeTag: field.key, mergeTag: field.key,
@ -495,6 +501,7 @@ module.exports.getRow = (fieldList, values, useDate, showAll, onlyExisting) => {
return { return {
type: subField.type, type: subField.type,
name: subField.name, name: subField.name,
description: subField.description,
column: subField.column, column: subField.column,
value: valueList[subField.column] ? 1 : 0, value: valueList[subField.column] ? 1 : 0,
visible: !!subField.visible, visible: !!subField.visible,
@ -572,6 +579,7 @@ module.exports.getRow = (fieldList, values, useDate, showAll, onlyExisting) => {
id: field.id, id: field.id,
type: field.type, type: field.type,
name: field.name, name: field.name,
description: field.description,
column: field.column, column: field.column,
value: useDate ? value : formatted, value: useDate ? value : formatted,
visible: !!field.visible, visible: !!field.visible,

View file

@ -1,3 +1,3 @@
{ {
"schemaVersion": 33 "schemaVersion": 34
} }

View file

@ -419,6 +419,7 @@ router.post('/field/:listId', (req, res) => {
let field = { let field = {
name: (input.NAME || '').toString().trim(), name: (input.NAME || '').toString().trim(),
description: (input.DESCRIPTION || '').toString().trim(),
defaultValue: (input.DEFAULT || '').toString().trim() || null, defaultValue: (input.DEFAULT || '').toString().trim() || null,
type: (input.TYPE || '').toString().toLowerCase().trim(), type: (input.TYPE || '').toString().toLowerCase().trim(),
group: Number(input.GROUP) || null, group: Number(input.GROUP) || null,

View file

@ -126,6 +126,19 @@ router.post('/create', passport.parseForm, passport.csrfProtection, (req, res) =
}); });
}); });
router.post('/duplicate', passport.parseForm, passport.csrfProtection, (req, res) => {
campaigns.duplicate(req.body.id, (err, duplicated) => {
if (err) {
req.flash('danger', err && err.message || err);
} else if (duplicated) {
req.flash('success', _('Campaign duplicated'));
} else {
req.flash('info', _('Could not duplicate specified campaign'));
}
return res.redirect('/campaigns/edit/' + duplicated);
});
});
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) {

View file

@ -0,0 +1,11 @@
# Header section
# Define incrementing schema version number
SET @schema_version = '34';
# Add template field for group elements
ALTER TABLE `custom_fields` ADD COLUMN `description` text AFTER `name`;
# 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;

View file

@ -17,6 +17,11 @@
<input type="hidden" name="id" value="{{id}}" /> <input type="hidden" name="id" value="{{id}}" />
</form> </form>
<form method="post" class="duplicate-form" id="campaigns-duplicate" action="/campaigns/duplicate">
<input type="hidden" name="_csrf" value="{{csrfToken}}">
<input type="hidden" name="id" value="{{id}}" />
</form>
{{#each attachments}} {{#each attachments}}
<form method="post" id="attachment-download-{{id}}" action="/campaigns/attachment/download"> <form method="post" id="attachment-download-{{id}}" action="/campaigns/attachment/download">
<input type="hidden" name="_csrf" value="{{../csrfToken}}"> <input type="hidden" name="_csrf" value="{{../csrfToken}}">
@ -240,6 +245,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">
<div class="pull-right"> <div class="pull-right">
<button type="submit" form="campaigns-duplicate" class="btn btn-default"> {{#translate}}Duplicate{{/translate}}</button>
<button type="submit" form="campaigns-delete" class="btn btn-danger"><i class="glyphicon glyphicon-remove"></i> {{#translate}}Delete Campaign{{/translate}}</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> {{#translate}}Update{{/translate}}</button> <button type="submit" class="btn btn-primary"><i class="glyphicon glyphicon-ok"></i> {{#translate}}Update{{/translate}}</button>

View file

@ -19,6 +19,13 @@
</div> </div>
</div> </div>
<div class="form-group">
<label for="description" class="col-sm-2 control-label">{{#translate}}Field Description{{/translate}}</label>
<div class="col-sm-10">
<textarea class="form-control gpg-text" rows="3" name="description" id="description">{{field.description}}</textarea>
</div>
</div>
<div class="form-group"> <div class="form-group">
<label for="type" class="col-sm-2 control-label">{{#translate}}Field Type{{/translate}}</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">

View file

@ -26,6 +26,13 @@
</div> </div>
</div> </div>
<div class="form-group">
<label for="description" class="col-sm-2 control-label">{{#translate}}Field Description{{/translate}}</label>
<div class="col-sm-10">
<textarea class="form-control gpg-text" rows="3" name="description" id="description">{{field.description}}</textarea>
</div>
</div>
<div class="form-group"> <div class="form-group">
<label for="type" class="col-sm-2 control-label">{{#translate}}Field Type{{/translate}}</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">

View file

@ -134,9 +134,12 @@
<label>{{name}}</label> <label>{{name}}</label>
<input type="hidden" name="origin_{{key}}" value="webform"> <input type="hidden" name="origin_{{key}}" value="webform">
{{#each options}} {{#each options}}
<p>
<label class="label-checkbox"> <label class="label-checkbox">
<input type="checkbox" name="{{column}}" value="1" {{#if value}} checked {{/if}}> {{name}} <input type="checkbox" name="{{column}}" value="1" {{#if value}} checked {{/if}}> {{name}}
</label> </label>
<span class="help-block">{{description}}</span>
</p>
{{/each}} {{/each}}
</div> </div>
{{/if}} {{/if}}

View file

@ -181,6 +181,7 @@
</p> </p>
<ul> <ul>
<li><strong>NAME</strong> {{#translate}}field name{{/translate}} (<em>{{#translate}}required{{/translate}}</em>)</li> <li><strong>NAME</strong> {{#translate}}field name{{/translate}} (<em>{{#translate}}required{{/translate}}</em>)</li>
<li><strong>DESCRIPTION</strong> {{#translate}}field description{{/translate}}</li>
<li><strong>TYPE</strong> {{#translate}}one of the following types:{{/translate}} <li><strong>TYPE</strong> {{#translate}}one of the following types:{{/translate}}
<ul> <ul>
{{#each allowedTypes}} {{#each allowedTypes}}
@ -362,3 +363,4 @@
<pre>curl -XPOST {{serviceUrl}}api/changeemail/B16uVTdW?access_token={{accessToken}} \ <pre>curl -XPOST {{serviceUrl}}api/changeemail/B16uVTdW?access_token={{accessToken}} \
--data 'EMAILOLD=test@example.com&EMAILNEW=foo@bar.com'</pre> --data 'EMAILOLD=test@example.com&EMAILNEW=foo@bar.com'</pre>