diff --git a/.gitignore b/.gitignore
index add4ce7..d00798d 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,3 +1,4 @@
node_modules
.idea
-secrets.json
\ No newline at end of file
+config.json
+secrets.json
diff --git a/Gruntfile.js b/Gruntfile.js
index 89c8ebb..a6ffb5f 100644
--- a/Gruntfile.js
+++ b/Gruntfile.js
@@ -3,35 +3,76 @@
* https://github.com/wildbit/grunt-postmark.git
*/
-module.exports = function(grunt) {
+module.exports = function (grunt) {
+ var secret = grunt.file.readJSON('secrets.json');
+ var config = grunt.file.readJSON('config.json');
grunt.initConfig({
- secrets: grunt.file.readJSON('secrets.json'),
+ secret: secret,
+ config: config,
/* Postmark
- ------------------------------------------------- */
+ ------------------------------------------------- */
postmark: {
options: {
- serverToken: '<%= secrets.serverToken %>'
+ serverToken: "<%= secret.postmark.server_token %>"
},
email: {
- from: 'you@youremail.com',
- to: 'you@youremail.com',
- subject: 'Yo',
- src: ['test/email.html']
+ from: "<%= config.postmark.from %>",
+ to: "<%= config.postmark.to %>",
+ subject: "<%= config.postmark.subject %>",
+ src: ["test/email.html"]
},
bulk: {
- from: 'you@youremail.com',
- to: 'you@youremail.com',
- subject: 'Hey',
- src: ['test/*.html']
+ from: "<%= config.postmark.from %>",
+ to: "<%= config.postmark.to %>",
+ subject: "<%= config.postmark.subject %>",
+ src: ["test/*.html"]
}
- }
+ },
+
+ "postmark-templates-upload": {
+ options: {
+ ephemeralUploadResultsProperty: "<%= config.templates && config.templates.ephemeralUploadResultsProperty %>",
+ serverToken: "<%= secret.postmark.server_token %>"
+ },
+ test_email: {
+ name: "testing-postmark-templates-js1-" + new Date().valueOf(),
+ subject: "Testing grunt-postmark-templates",
+ htmlSrc: "test/email.html",
+ textSrc: "test/email.txt"
+ },
+ test_email_again: {
+ name: "testing-postmark-templates-js2-" + new Date().valueOf(),
+ subject: "Testing grunt-postmark-templates (again)",
+ htmlSrc: "test/email.html",
+ textSrc: "test/email.txt"
+ },
+ test_email_inline_body: {
+ name: "testing-postmark-templates-js3-" + new Date().valueOf(),
+ subject: "Testing grunt-postmark-templates (inline body)",
+ htmlBody: "
Another email test
",
+ textBody: "Hello from grunt-postmark-templates\n"
+ }
+ },
+ "postmark-templates-output": {
+ options: {
+ cleanOutput: "<%= config.templates && config.templates.cleanOutput %>",
+ outputFile: "<%= config.templates && config.templates.outputFile || config.templates && config.templates.file %>",
+ ephemeralUploadResultsProperty: "<%= config.templates && config.templates.ephemeralUploadResultsProperty %>"
+ }
+ },
+
+ "postmark-templates-parse": {
+ options: {
+ inputFile: "<%= config.templates && config.templates.inputFile || config.templates && config.templates.file %>"
+ }
+ }
});
- grunt.loadTasks('tasks');
+ grunt.loadTasks("tasks");
- grunt.registerTask('default', ['postmark']);
+ grunt.registerTask("default", ["postmark", "postmark-templates"]);
};
diff --git a/README.md b/README.md
index 5239406..f99dca1 100644
--- a/README.md
+++ b/README.md
@@ -20,38 +20,39 @@ After the plugin is installed, it can be enabled in your Gruntfile:
grunt.loadNpmTasks('grunt-postmark');
```
+You'll need to add a [`config.json`](https://github.com/wildbit/mailmason/wiki/Getting-Started#create-configjson-required) and a [`secrets.json`](https://github.com/wildbit/mailmason/wiki/Getting-Started#create-secretsjson-optional) per the `mailmason` configuration.
+
## Postmark task
_Run this task with the `grunt postmark` command._
-## Options
-
+### Options
-### serverToken
+#### serverToken
Your server token can be found on your server’s credentials page on [Postmark’s](http://postmarkapp.com) app.
Type: `String`
-### from
+#### from
This is the from address you are using to send the email. This must be a confirmed address that's set up on [Postmark’s](http://postmarkapp.com) sender signatures.
Type: `String`
-### to
+#### to
The address you’re sending to.
Type: `String`
-### subject
+#### subject
Type: `String`
-## Examples
+### Examples
-### Options specified through target
+#### Options specified through target
```javascript
grunt.initConfig({
@@ -67,7 +68,7 @@ grunt.initConfig({
});
```
-### Specify options through targets or globally
+#### Specify options through targets or globally
Options specified through a target will always take precedence over global options.
```javascript
@@ -89,4 +90,181 @@ grunt.initConfig({
}
}
});
+```
+
+## Postmark templates task
+_Run this task with the `grunt postmark-templates` command._
+
+The `postmark-templates` task is an alias of the `postmark-templates-from-targets` task which is itself a two stepped task – `postmark-templates-upload` followed by `postmark-templates-output`.
+
+`postmark-templates` (`postmark-templates-from-targets`) is intended for programmatic usage from other grunt tasks.
+
+### `postmark-templates-upload` Targets
+
+#### name
+The name of your template.
+
+Type: `String`
+
+
+#### subject
+The subject line of your template.
+
+Type: `String`
+
+#### htmlSrc
+A path to the generated HTML for your template. *Not used if `htmlBody` is specified.*
+
+Type: `String`
+
+
+#### textSrc
+A path to the generated plain text for your template. *Not used if `textBody` is specified.*
+
+Type: `String`
+
+#### htmlBody
+The generated HTML content of your template. *Not required if `htmlSrc` is specified.*
+
+Type: `String`
+
+
+#### textBody
+The generated plain text content of your template. *Not required if `textSrc` is specified.*
+
+Type: `String`
+
+
+### `postmark-templates-upload` Options
+
+#### serverToken
+Your server token can be found on your server’s credentials page on [Postmark’s](http://postmarkapp.com) app.
+
+Type: `String`
+
+
+#### ephemeralUploadResultsProperty
+This is the name of a temporary grunt task configuration property used to communicate the upload results between `postmark-templates-upload` and `postmark-templates-output` without having to write a temporary file. **This should be the same value as `ephemeralUploadResultsProperty` for `postmark-templates-output`.**
+
+Type: `String`
+
+
+### `postmark-templates-output` Options
+
+#### outputFile
+The name of a file to output the results of the upload to Postmark.
+
+Type: `String`
+
+
+#### cleanOutput
+If `true`, do not export `htmlBody`, `htmlSrc`, `textBody` or `textSrc` in the specified `outputFile`.
+
+Type: `Boolean`
+
+
+#### ephemeralUploadResultsProperty
+This is the name of a temporary grunt task configuration property used to communicate the upload results between `postmark-templates-upload` and `postmark-templates-output` without having to write a temporary file. **This should be the same value as `ephemeralUploadResultsProperty` for `postmark-templates-upload`.**
+
+Type: `String`
+
+
+### Example
+
+```javascript
+grunt.initConfig({
+ 'postmark-templates-upload': {
+ options: {
+ serverToken: 'POSTMARK_API_TEST',
+ ephemeralUploadResultsProperty: 'temp'
+ },
+ test_email: {
+ name: 'testing-postmark-templates-js1',
+ subject: 'Testing grunt-postmark-templates',
+ htmlSrc: 'test/email.html',
+ textSrc: 'test/email.txt'
+ },
+ test_email_inline_body: {
+ name: 'testing-postmark-templates-js3',
+ subject: 'Testing grunt-postmark-templates (inline body)',
+ htmlBody: 'Another email test
',
+ textBody: 'Hello from grunt-postmark-templates\n'
+ }
+ },
+ 'postmark-templates-output': {
+ options: {
+ cleanOutput: true,
+ outputFile: 'templates.json',
+ ephemeralUploadResultsProperty: 'temp'
+ }
+ }
+});
+```
+
+## Postmark templates (from file) task
+_Run this task with the `grunt postmark-templates-from-file` command._
+
+The `postmark-templates-from-file` task invokes the `postmark-templates` task with targets read from a JSON file (via `postmark-templates-parse`).
+
+This task is intended for standalone, manual usage.
+
+
+### `postmark-templates-parse` Options
+
+#### inputFile
+The name of a file that specifies templates for uploading to Postmark. These templates take the same shape as defined by `postmark-templates-upload`. This should usually be the same value as `outputFile` for `postmark-templates-output`.
+
+Type: `String`
+
+
+### Example
+
+In your `Gruntfile`:
+
+```javascript
+grunt.initConfig({
+ 'postmark-templates-parse': {
+ options: {
+ inputFile: 'templates.json'
+ }
+ },
+ 'postmark-templates-upload': {
+ options: {
+ serverToken: 'POSTMARK_API_TEST',
+ ephemeralUploadResultsProperty: 'temp'
+ }
+ },
+ 'postmark-templates-output': {
+ options: {
+ cleanOutput: true,
+ outputFile: 'templates.json',
+ ephemeralUploadResultsProperty: 'temp'
+ }
+ }
+});
+```
+
+In the file specified by `inputFile`, in this case, `templates.json`:
+
+```json
+{
+ "test_email": {
+ "name": "testing-postmark-templates-js1",
+ "subject": "Testing grunt-postmark-templates",
+ "htmlSrc": "test/email.html",
+ "textSrc": "test/email.txt"
+ },
+ "test_email_again": {
+ "name": "testing-postmark-templates-js2",
+ "subject": "Testing grunt-postmark-templates (again)",
+ "htmlSrc": "test/email.html",
+ "textSrc": "test/email.txt"
+ },
+ "test_email_inline_body": {
+ "name": "testing-postmark-templates-js3",
+ "subject": "Testing grunt-postmark-templates (inline body)",
+ "htmlBody": "Another email test
",
+ "textBody": "Hello from grunt-postmark-templates\n"
+ }
+}
```
\ No newline at end of file
diff --git a/example_config.json b/example_config.json
new file mode 100644
index 0000000..04c79a0
--- /dev/null
+++ b/example_config.json
@@ -0,0 +1,16 @@
+// 1. Copy this file to "config.json" for your own version that won't be
+// tracked in source control.
+//
+// 2. Delete these comments from the configuration so Grunt doesn't get confused
+{
+ "postmark": {
+ "from": "jane@example.com",
+ "to": "jon@example.com",
+ "subject": "Test Email"
+ },
+ "templates": {
+ "ephemeralUploadResultsProperty": "postmark-templates-upload-results",
+ "cleanOutput": true,
+ "file": "templates.json"
+ }
+}
diff --git a/example_secrets.json b/example_secrets.json
new file mode 100644
index 0000000..57db34e
--- /dev/null
+++ b/example_secrets.json
@@ -0,0 +1,9 @@
+// 1. Copy this file to "secrets.json" for your own version that won't be
+// tracked in source control.
+//
+// 2. Delete these comments from the configuration so Grunt doesn't get confused
+{
+ "postmark": {
+ "server_token": "YOUR_POSTMARK_SERVER_TOKEN_HERE"
+ }
+}
diff --git a/example_templates.json b/example_templates.json
new file mode 100644
index 0000000..9ccda38
--- /dev/null
+++ b/example_templates.json
@@ -0,0 +1,20 @@
+{
+ "test_email": {
+ "name": "testing-postmark-templates-js1",
+ "subject": "Testing grunt-postmark-templates",
+ "htmlSrc": "test/email.html",
+ "textSrc": "test/email.txt"
+ },
+ "test_email_again": {
+ "name": "testing-postmark-templates-js2",
+ "subject": "Testing grunt-postmark-templates (again)",
+ "htmlSrc": "test/email.html",
+ "textSrc": "test/email.txt"
+ },
+ "test_email_inline_body": {
+ "name": "testing-postmark-templates-js3",
+ "subject": "Testing grunt-postmark-templates (inline body)",
+ "htmlBody": "Another email test
",
+ "textBody": "Hello from grunt-postmark-templates\n"
+ }
+}
\ No newline at end of file
diff --git a/package.json b/package.json
index caed3b0..f7bea9a 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "grunt-postmark",
- "version": "0.0.7",
+ "version": "0.0.8",
"description": "Send emails through Postmark using GruntJS",
"main": "Gruntfile.js",
"homepage": "https://github.com/wildbit/grunt-postmark.git",
@@ -18,7 +18,6 @@
"name": "Derek Rushforth",
"email": "derek@wildbit.com"
},
- "main": "Gruntfile.js",
"license": "MIT",
"devDependencies": {
"grunt": "^1.0.1"
diff --git a/secrets_example.json b/secrets_example.json
deleted file mode 100644
index a108905..0000000
--- a/secrets_example.json
+++ /dev/null
@@ -1,3 +0,0 @@
-{
- "server_token": "POSTMARK_API_TEST"
-}
\ No newline at end of file
diff --git a/tasks/grunt-postmark-templates.js b/tasks/grunt-postmark-templates.js
new file mode 100644
index 0000000..2003fd9
--- /dev/null
+++ b/tasks/grunt-postmark-templates.js
@@ -0,0 +1,154 @@
+/*
+ * grunt-postmark-templates
+ * push templates to a Postmark server for use with SendTemplatedEmail
+ *
+ * https://github.com/wildbit/grunt-postmark.git
+ */
+
+module.exports = function (grunt) {
+ var DEFAULT_EPHEMERAL_UPLOAD_RESULTS_PROPERTY = 'postmark-templates-upload-results';
+ var DEFAULT_OUTPUT_FILE_NAME = 'templates.json';
+ var DEFAULT_CLEAN_OUTPUT = false;
+
+ grunt.registerTask('postmark-templates-parse', 'Parse Postmark templates for update', function () {
+ var options = this.options({
+ inputFile: DEFAULT_OUTPUT_FILE_NAME
+ });
+ var templateObjects = grunt.file.readJSON(options.inputFile || DEFAULT_OUTPUT_FILE_NAME);
+
+ grunt.config.set('postmark-templates-upload', Object.assign(
+ templateObjects,
+ {
+ options: grunt.config.get('postmark-templates-upload.options')
+ }
+ ));
+ });
+
+ grunt.registerMultiTask('postmark-templates-upload', 'Create or update Postmark templates', function () {
+ var done = this.async();
+ var options = this.options({
+ ephemeralUploadResultsProperty: DEFAULT_EPHEMERAL_UPLOAD_RESULTS_PROPERTY
+ });
+ var ephemeralUploadResultsProperty = options.ephemeralUploadResultsProperty || DEFAULT_EPHEMERAL_UPLOAD_RESULTS_PROPERTY;
+ var template = this.data;
+
+ var serverToken = options.serverToken || grunt.config('secret.postmark.server_token');
+
+ if (!serverToken) {
+ grunt.fail.warn('Missing Postmark server token \n');
+ }
+
+ if (!template.name) {
+ grunt.fail.warn('Missing required template property "name" \n');
+ }
+
+ if (!template.subject) {
+ grunt.fail.warn('Missing required template property "subject" \n');
+ }
+
+ if (!template.htmlBody && !template.htmlSrc) {
+ grunt.log.error('Missing template property "htmlBody" or "htmlSrc"\n');
+ }
+
+ if (!template.textBody && !template.textSrc) {
+ grunt.log.error('Missing template property "textBody" or "textSrc"\n');
+ }
+
+ var postmark = require('postmark');
+ var client = new postmark.Client(serverToken);
+
+ // read the referenced files, but hold on to the original filenames
+ var expanded = {
+ Name: template.name,
+ Subject: template.subject,
+ HtmlBody: template.htmlBody || grunt.file.read(template.htmlSrc),
+ TextBody: template.textBody || grunt.file.read(template.textSrc),
+ TemplateId: template.templateId
+ };
+
+ if (expanded.TemplateId) {
+ client.editTemplate(expanded.TemplateId, expanded, function (err, response) {
+ if (err && err.code === 1101) {
+ grunt.log.warn('Template ' + expanded.TemplateId + ' not found, so attempting create');
+ delete template.templateId;
+ delete expanded.TemplateId;
+ client.createTemplate(expanded.TemplateId, function (err, response) {
+ grunt.log.writeln('Template ' + expanded.Name + ' created: ' + JSON.stringify(response.TemplateId));
+ handleResponse(err, done, response, template, ephemeralUploadResultsProperty);
+ });
+ } else {
+ grunt.log.writeln('Template ' + expanded.Name + ' updated: ' + JSON.stringify(response.TemplateId));
+ handleResponse(err, done, response, template, ephemeralUploadResultsProperty);
+ }
+ });
+ } else {
+ client.createTemplate(expanded, function (err, response) {
+ grunt.log.writeln('Template ' + expanded.Name + ' created: ' + JSON.stringify(response.TemplateId));
+ handleResponse(err, done, response, template, ephemeralUploadResultsProperty);
+ });
+ }
+ });
+
+ function handleResponse(err, done, response, template, ephemeralUploadResultsProperty) {
+ if (err){
+ errorMessage(err);
+ done();
+ } else {
+ template.templateId = response.TemplateId;
+ // compile the templates for use by the `postmark-templates-output` task
+ var updatedTemplates = grunt.config.get(ephemeralUploadResultsProperty) || {};
+ updatedTemplates[template.name] = template;
+ grunt.config.set(ephemeralUploadResultsProperty, updatedTemplates);
+
+ done();
+ }
+ }
+
+ function errorMessage(err) {
+ if (err.message) {
+ grunt.log.warn('Error: ' + err.message);
+ } else {
+ grunt.log.warn('Error: ' + JSON.stringify(err));
+ }
+ }
+
+ // invoke this task after postmark-templates to get an output file containing the resulting template IDs
+ // this is in the same format as the postmark-templates config.
+
+ grunt.registerTask('postmark-templates-output', 'Write out the resulting template IDs', function () {
+ var options = this.options({
+ cleanOutput: DEFAULT_CLEAN_OUTPUT,
+ outputFile: DEFAULT_OUTPUT_FILE_NAME,
+ ephemeralUploadResultsProperty: DEFAULT_EPHEMERAL_UPLOAD_RESULTS_PROPERTY
+ });
+ var ephemeralUploadResultsProperty = options.ephemeralUploadResultsProperty || DEFAULT_EPHEMERAL_UPLOAD_RESULTS_PROPERTY;
+ var outputFile = options.outputFile || DEFAULT_OUTPUT_FILE_NAME;
+ var cleanOutput = options.cleanOutput || DEFAULT_CLEAN_OUTPUT;
+ var updatedTemplates = grunt.config(ephemeralUploadResultsProperty);
+ var oldTemplates = grunt.file.exists(outputFile)
+ ? grunt.file.read(outputFile)
+ : {};
+
+ Object.keys(updatedTemplates).forEach(function (updatedTemplateKey) {
+ updatedTemplates[updatedTemplateKey] = Object.assign(
+ oldTemplates[updatedTemplateKey] || {},
+ updatedTemplates[updatedTemplateKey]
+ );
+
+ if (cleanOutput) {
+ delete updatedTemplates[updatedTemplateKey].htmlBody;
+ delete updatedTemplates[updatedTemplateKey].textBody;
+ delete updatedTemplates[updatedTemplateKey].htmlSrc;
+ delete updatedTemplates[updatedTemplateKey].textSrc;
+ }
+ });
+
+ grunt.file.write(outputFile, JSON.stringify(updatedTemplates, null, 2));
+ grunt.log.writeln("Updated template information written to " + outputFile);
+ });
+
+ // you can also get a JSON report of uploaded templates
+ grunt.registerTask('postmark-templates-from-targets', ['postmark-templates-upload', 'postmark-templates-output']);
+ grunt.registerTask('postmark-templates-from-file', ['postmark-templates-parse', 'postmark-templates-from-targets']);
+ grunt.registerTask('postmark-templates', ['postmark-templates-from-targets']);
+};
diff --git a/tasks/grunt-postmark.js b/tasks/grunt-postmark.js
index ccf7f8b..e076ac9 100644
--- a/tasks/grunt-postmark.js
+++ b/tasks/grunt-postmark.js
@@ -3,10 +3,10 @@
* https://github.com/wildbit/grunt-postmark.git
*/
-'use strict';
-
module.exports = function(grunt) {
+ 'use strict';
+
grunt.registerMultiTask('postmark', 'Send emails through Postmark', function() {
var done = this.async();
@@ -14,7 +14,7 @@ module.exports = function(grunt) {
var _data = this.data;
// Check for server token
- if (!options.serverToken && !_data.serverToken) {
+ if (!options.serverToken && !_data.serverToken) {
grunt.fail.warn('Missing Postmark server token \n');
}
@@ -25,8 +25,8 @@ module.exports = function(grunt) {
if (this.files.length > 0) {
var message = {
- 'From': _data.from || options.from,
- 'To': _data.to || options.to,
+ 'From': _data.from || options.from,
+ 'To': _data.to || options.to,
'Subject': _data.subject || options.subject
};
@@ -56,7 +56,7 @@ module.exports = function(grunt) {
handleResponse(err, response, done);
});
}
-
+
} else {
// Warn about no files being passed to task
grunt.log.warn('No src file found \n');
@@ -66,7 +66,13 @@ module.exports = function(grunt) {
function handleResponse(err, response, done) {
- err ? errorMessage(err) : successMessage(response);
+ if (err) {
+ errorMessage(err);
+
+ } else {
+ successMessage(response);
+ }
+
done();
}
diff --git a/test/email.txt b/test/email.txt
new file mode 100644
index 0000000..e5494a8
--- /dev/null
+++ b/test/email.txt
@@ -0,0 +1 @@
+Hello from grunt-postmark-templates