Skip to content

Commit

Permalink
v1.16.0
Browse files Browse the repository at this point in the history
  • Loading branch information
andris9 committed Aug 29, 2016
1 parent 3fcd389 commit 896fc01
Show file tree
Hide file tree
Showing 14 changed files with 95 additions and 29 deletions.
6 changes: 6 additions & 0 deletions CHANGELOG.txt
@@ -1,5 +1,11 @@
# Changelog

## 1.16.0 2016-08-29

* Render list values using Handlebars templates
* Added new API method to create custom fields
* Added LDAP authentication support

## 1.15.0 2016-07-28

* Check SMTP settings using AJAX instead of posting entire form
Expand Down
2 changes: 1 addition & 1 deletion Gruntfile.js
Expand Up @@ -5,7 +5,7 @@ module.exports = function (grunt) {
// Project configuration.
grunt.initConfig({
eslint: {
all: ['lib/**/*.js', 'test/**/*.js', 'config/**/*.js', 'Gruntfile.js', 'app.js', 'index.js', '.eslintrc.js']
all: ['lib/**/*.js', 'test/**/*.js', 'config/**/*.js', 'Gruntfile.js', 'app.js', 'index.js']
},

nodeunit: {
Expand Down
35 changes: 27 additions & 8 deletions lib/models/fields.js
Expand Up @@ -5,8 +5,9 @@ let tools = require('../tools');
let slugify = require('slugify');
let lists = require('./lists');
let shortid = require('shortid');
let Handlebars = require('handlebars');

let allowedKeys = ['name', 'key', 'default_value', 'group', 'visible'];
let allowedKeys = ['name', 'key', 'default_value', 'group', 'group_template', 'visible'];
let allowedTypes;

module.exports.grouped = ['radio', 'checkbox', 'dropdown'];
Expand Down Expand Up @@ -104,7 +105,9 @@ module.exports.get = (id, callback) => {
if (err) {
return callback(err);
}
return callback(null, rows && rows[0] && tools.convertKeys(rows[0]) || false);
let field = rows && rows[0] && tools.convertKeys(rows[0]) || false;
field.isGroup = module.exports.grouped.includes(field.type);
return callback(null, field);
});
});
};
Expand All @@ -126,7 +129,10 @@ module.exports.create = (listId, field, callback) => {
field.group = null;
}

addCustomField(listId, field.name, field.defaultValue, field.type, field.group, field.visible, callback);
field.defaultValue = (field.defaultValue || '').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);
};

module.exports.update = (id, updates, callback) => {
Expand All @@ -147,6 +153,9 @@ module.exports.update = (id, updates, callback) => {
updates.key = slugify(updates.key, '_').toUpperCase();
}

updates.defaultValue = (updates.defaultValue || '').toString().trim() || null;
updates.groupTemplate = (updates.groupTemplate || '').toString().trim() || null;

updates.visible = updates.visible ? 1 : 0;

let name = (updates.name || '').toString().trim();
Expand Down Expand Up @@ -264,7 +273,7 @@ module.exports.delete = (fieldId, callback) => {
});
};

function addCustomField(listId, name, defaultValue, type, group, visible, callback) {
function addCustomField(listId, name, defaultValue, type, group, groupTemplate, visible, callback) {
type = (type || '').toString().trim().toLowerCase();
group = Number(group) || null;
listId = Number(listId) || 0;
Expand Down Expand Up @@ -301,8 +310,8 @@ function addCustomField(listId, name, defaultValue, type, group, visible, callba
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`, `column`, `visible`) VALUES(?,?,?,?,?,?,?,?)';
connection.query(query, [listId, name, key, defaultValue, type, group, column, visible ? 1 : 0], (err, result) => {
let query = 'INSERT INTO custom_fields (`list`, `name`, `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) => {

if (err) {
connection.release();
Expand Down Expand Up @@ -397,7 +406,7 @@ module.exports.getRow = (fieldList, values, useDate, showAll, onlyExisting) => {
value: Number(valueList[field.column]) || 0,
visible: !!field.visible,
mergeTag: field.key,
mergeValue: Number(valueList[field.column]) || field.defaultValue || 0,
mergeValue: Number(valueList[field.column]) || Number(field.defaultValue) || 0,
['type' + (field.type || '').toString().trim().replace(/(?:^|\-)([a-z])/g, (m, c) => c.toUpperCase())]: true
};
row.push(item);
Expand All @@ -415,6 +424,7 @@ module.exports.getRow = (fieldList, values, useDate, showAll, onlyExisting) => {
mergeTag: field.key,
mergeValue: field.defaultValue,
['type' + (field.type || '').toString().trim().replace(/(?:^|\-)([a-z])/g, (m, c) => c.toUpperCase())]: true,
groupTemplate: field.groupTemplate,
options: (field.options || []).map(subField => {
if (onlyExisting && subField.column && !valueList.hasOwnProperty(subField.column)) {
// ignore missing values
Expand All @@ -431,7 +441,10 @@ module.exports.getRow = (fieldList, values, useDate, showAll, onlyExisting) => {
};
}).filter(subField => subField)
};
item.value = item.options.filter(subField => (showAll || subField.visible) && subField.value).map(subField => subField.name).join(', ');
let subItems = item.options.filter(subField => (showAll || subField.visible) && subField.value).map(subField => subField.name);
item.value = field.groupTemplate ? render(field.groupTemplate, {
values: subItems
}) : subItems.join(', ');
item.mergeValue = item.value || field.defaultValue;
row.push(item);
break;
Expand Down Expand Up @@ -523,3 +536,9 @@ module.exports.getValues = (row, showAll) => {

return result;
};


function render(template, options) {
let renderer = Handlebars.compile(template);
return renderer(options);
}
19 changes: 13 additions & 6 deletions lib/passport.js
Expand Up @@ -5,11 +5,18 @@ let log = require('npmlog');

let passport = require('passport');
let LocalStrategy = require('passport-local').Strategy;
let LdapStrategy = require('passport-ldapjs').Strategy;

let csrf = require('csurf');
let bodyParser = require('body-parser');
let users = require('./models/users');

let LdapStrategy;
try {
LdapStrategy = require('passport-ldapjs').Strategy; // eslint-disable-line global-require
} catch (E) {
// ignore
}

module.exports.csrfProtection = csrf({
cookie: true
});
Expand Down Expand Up @@ -61,12 +68,12 @@ module.exports.login = (req, res, next) => {
})(req, res, next);
};

if (config.ldap.enabled) {
if (config.ldap.enabled && LdapStrategy) {
log.info('Using LDAP auth');

var opts = {
let opts = {
server: {
url: 'ldap://' + config.ldap.host + ':' + config.ldap.port,
url: 'ldap://' + config.ldap.host + ':' + config.ldap.port
},
base: config.ldap.baseDN,
search: {
Expand All @@ -76,7 +83,7 @@ if (config.ldap.enabled) {
}
};

passport.use(new LdapStrategy(opts, function (profile, done) {
passport.use(new LdapStrategy(opts, (profile, done) => {
users.findByUsername(profile.username, (err, user) => {
if (err) {
return done(err);
Expand All @@ -90,7 +97,7 @@ if (config.ldap.enabled) {
}

return done(null, {
id: id,
id,
username: profile.username
});
});
Expand Down
2 changes: 1 addition & 1 deletion meta.json
@@ -1,3 +1,3 @@
{
"schemaVersion": 16
"schemaVersion": 17
}
3 changes: 1 addition & 2 deletions package.json
@@ -1,7 +1,7 @@
{
"name": "mailtrain",
"private": true,
"version": "1.15.1",
"version": "1.16.0",
"description": "Self hosted email newsletter app",
"main": "index.js",
"scripts": {
Expand Down Expand Up @@ -65,7 +65,6 @@
"npmlog": "^4.0.0",
"openpgp": "^2.3.3",
"passport": "^0.3.2",
"passport-ldapjs": "^1.0.2",
"passport-local": "^1.0.0",
"request": "^2.74.0",
"serve-favicon": "^2.3.0",
Expand Down
1 change: 1 addition & 0 deletions routes/api.js
Expand Up @@ -303,6 +303,7 @@ router.post('/field/:listId', (req, res) => {
defaultValue: (input.DEFAULT || '').toString().trim() || null,
type: (input.TYPE || '').toString().toLowerCase().trim(),
group: Number(input.GROUP) || null,
groupTemplate: (input.GROUP_TEMPLATE || '').toString().toLowerCase().trim(),
visible: !['false', 'no', '0', ''].includes((input.VISIBLE || '').toString().toLowerCase().trim())
};

Expand Down
5 changes: 3 additions & 2 deletions setup/sql/mailtrain.sql
Expand Up @@ -87,6 +87,7 @@ CREATE TABLE `custom_fields` (
`default_value` varchar(255) DEFAULT NULL,
`type` varchar(255) CHARACTER SET ascii NOT NULL DEFAULT '',
`group` int(11) unsigned DEFAULT NULL,
`group_template` text,
`column` varchar(255) CHARACTER SET ascii DEFAULT NULL,
`visible` tinyint(4) unsigned NOT NULL DEFAULT '1',
`created` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
Expand Down Expand Up @@ -194,7 +195,7 @@ CREATE TABLE `settings` (
`value` text NOT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `key` (`key`)
) ENGINE=InnoDB AUTO_INCREMENT=33 DEFAULT CHARSET=utf8mb4;
) ENGINE=InnoDB AUTO_INCREMENT=34 DEFAULT CHARSET=utf8mb4;
INSERT INTO `settings` (`id`, `key`, `value`) VALUES (1,'smtp_hostname','localhost');
INSERT INTO `settings` (`id`, `key`, `value`) VALUES (2,'smtp_port','465');
INSERT INTO `settings` (`id`, `key`, `value`) VALUES (3,'smtp_encryption','TLS');
Expand All @@ -211,7 +212,7 @@ INSERT INTO `settings` (`id`, `key`, `value`) VALUES (13,'default_from','My Awes
INSERT INTO `settings` (`id`, `key`, `value`) VALUES (14,'default_address','admin@example.com');
INSERT INTO `settings` (`id`, `key`, `value`) VALUES (15,'default_subject','Test message');
INSERT INTO `settings` (`id`, `key`, `value`) VALUES (16,'default_homepage','http://localhost:3000/');
INSERT INTO `settings` (`id`, `key`, `value`) VALUES (17,'db_schema_version','16');
INSERT INTO `settings` (`id`, `key`, `value`) VALUES (17,'db_schema_version','17');
CREATE TABLE `subscription` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`cid` varchar(255) CHARACTER SET ascii NOT NULL,
Expand Down
11 changes: 11 additions & 0 deletions setup/sql/upgrade-00017.sql
@@ -0,0 +1,11 @@
# Header section
# Define incrementing schema version number
SET @schema_version = '17';

# Add template field for group elements
ALTER TABLE `custom_fields` ADD COLUMN `group_template` text AFTER `group`;

# 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;
8 changes: 8 additions & 0 deletions views/lists/fields/create.hbs
Expand Up @@ -66,6 +66,14 @@
</div>
</div>

<div class="form-group">
<label for="group-template" class="col-sm-2 control-label">Group template</label>
<div class="col-sm-10">
<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.</span>
</div>
</div>

<div class="form-group">
<div class="col-sm-offset-2 col-xs-4">
<div class="checkbox">
Expand Down
24 changes: 18 additions & 6 deletions views/lists/fields/edit.hbs
Expand Up @@ -64,7 +64,7 @@
<option value="{{id}}" {{#if selected}} selected {{/if}}>{{name}}</option>
{{/each}}
</select>
<span class="help-block">Required for group options)</span>
<span class="help-block">Required for group options</span>
</div>
</div>

Expand All @@ -78,12 +78,24 @@
</div>
</div>

<div class="form-group">
<label for="default-value" class="col-sm-2 control-label">Default merge tag value</label>
<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">
{{#if field.isGroup}}

<div class="form-group">
<label for="group-template" class="col-sm-2 control-label">Group template</label>
<div class="col-sm-10">
<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.</span>
</div>
</div>
</div>

{{else}}
<div class="form-group">
<label for="default-value" class="col-sm-2 control-label">Default merge tag value</label>
<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">
</div>
</div>
{{/if}}

<div class="form-group">
<div class="col-sm-offset-2 col-xs-4">
Expand Down
4 changes: 2 additions & 2 deletions views/users/account.hbs
Expand Up @@ -9,9 +9,9 @@

{{#if ldap.enabled}}
<p>
This account is managed through LDAP.<br/>
This account is managed through LDAP.
<br/>
Associated Email Address: <a href="mailto:{{email}}">{{email}}</a>
<br/> Associated Email Address: <a href="mailto:{{email}}">{{email}}</a>
</p>
{{else}}
<form class="form-horizontal" method="post" action="/users/account">
Expand Down
1 change: 1 addition & 0 deletions views/users/api.hbs
Expand Up @@ -170,6 +170,7 @@
</ul>
</li>
<li><strong>GROUP</strong> – If the type is 'option' then you also need to specify the parent element ID</li>
<li><strong>GROUP_TEMPLATE</strong> – Template for the group element. If not set, then values of the elements are joined with commas</li>
<li><strong>VISIBLE</strong> – yes/no, if not visible then the subscriber can not view or modify this value at the profile page</li>
</ul>

Expand Down
3 changes: 2 additions & 1 deletion views/users/forgot.hbs
Expand Up @@ -10,7 +10,8 @@

{{#if ldap.enabled}}
<p>
Accounts are managed through LDAP.<br/>
Accounts are managed through LDAP.
<br/>
<br/>
<a href="{{ldap.passwordresetlink}}">Reset Password</a>
</p>
Expand Down

0 comments on commit 896fc01

Please sign in to comment.