From b5fdbcd3e148e6d2e6a72df81d8ded004e568b21 Mon Sep 17 00:00:00 2001 From: Andris Reinman Date: Fri, 4 Aug 2017 14:07:17 +0300 Subject: [PATCH] Allow using external resources for default messages --- .gitignore | 2 +- api.js | 21 +++++- emails/01.json.example | 17 ----- emails/README.md | 22 ++++-- emails/example.duck.png | Bin 0 -> 15754 bytes emails/example.html | 130 +++++++++++++++++++++++++++++++++++ emails/example.json.disabled | 12 ++++ emails/example.txt | 9 +++ lib/tools.js | 98 ++++++++++++++++++++++---- lib/user-handler.js | 5 +- package.json | 4 +- test/api-test.js | 6 -- 12 files changed, 280 insertions(+), 46 deletions(-) delete mode 100644 emails/01.json.example create mode 100644 emails/example.duck.png create mode 100644 emails/example.html create mode 100644 emails/example.json.disabled create mode 100644 emails/example.txt diff --git a/.gitignore b/.gitignore index 465b35b8..44be061b 100644 --- a/.gitignore +++ b/.gitignore @@ -4,4 +4,4 @@ npm-debug.log .npmrc config/production.* config/development.* -emails/*.json +emails/example.json diff --git a/api.js b/api.js index a08a9a92..1d39e4e9 100644 --- a/api.js +++ b/api.js @@ -100,9 +100,24 @@ module.exports = done => { database: db.database, redis: db.redis }); - messageHandler = new MessageHandler({ database: db.database, gridfs: db.gridfs, redis: db.redis }); - userHandler = new UserHandler({ database: db.database, users: db.users, redis: db.redis, messageHandler }); - mailboxHandler = new MailboxHandler({ database: db.database, users: db.users, redis: db.redis, notifier }); + messageHandler = new MessageHandler({ + database: db.database, + users: db.users, + gridfs: db.gridfs, + redis: db.redis + }); + userHandler = new UserHandler({ + database: db.database, + users: db.users, + redis: db.redis, + messageHandler + }); + mailboxHandler = new MailboxHandler({ + database: db.database, + users: db.users, + redis: db.redis, + notifier + }); usersRoutes(db, server, userHandler); addressesRoutes(db, server); diff --git a/emails/01.json.example b/emails/01.json.example deleted file mode 100644 index 21ec7dbb..00000000 --- a/emails/01.json.example +++ /dev/null @@ -1,17 +0,0 @@ -{ - "mailbox": "INBOX", - "seen": true, - "flag": true, - - "from": { - "name": "Support", - "address": "info@example.com" - }, - "to": { - "name": "[NAME]", - "address": "[EMAIL]" - }, - "subject": "[FNAME], welcome to our awesome service!", - "text": "[FNAME], your new email account [EMAIL] is now ready to be used!", - "html": "

[FNAME], your new email account [EMAIL] is now ready to be used!

" -} diff --git a/emails/README.md b/emails/README.md index fa3cccbf..10393cfd 100644 --- a/emails/README.md +++ b/emails/README.md @@ -1,14 +1,21 @@ # Default messages -Add here messages that should be inserted to new users INBOX. Messages are formatted according to [Nodemailer message structure](https://nodemailer.com/message/) and sorted by filename. Only files with .json extension are used. +Add here default email messages that should be inserted to new users account. To test it out, rename example.json.disabled to example.json, restart Wild Duck and create a new account. Your INBOX should include the message composed from the example. -All string values can take the following template tags (case sensitive): +## Creating default messages + +Messages are formatted according to the [Nodemailer message structure](https://nodemailer.com/message/) and stored to this folder as json files. + +All string values in the JSON structure can use the following template tags (case sensitive) that are replaced while compiling: - **[USERNAME]** will be replaced by the username of the user -- **[DOMAIN]** will be replaced by the service domain - **[EMAIL]** will be replaced by the email address of the user +- **[DOMAIN]** will be replaced by the domain part of the email address - **[NAME]** will be replaced by the registered name of the user -- **[FNAME]** will be replaced by the first part of the registered name of the user +- **[FNAME]** will be replaced by the first name of the registered user +- **[LNAME]** will be replaced by the last name of the registered user + +> NB! All values are replaced as is, except in the `html` field. For `html` the replaced values are html encoded. You can also specify some extra options with the mail data object @@ -21,3 +28,10 @@ You can also specify some extra options with the mail data object - **'Junk'** to store the message to the Spam folder - **'Drafts'** to store the message to the Drafts folder - **'Archive'** to store the message to the Archive folder + +You can include some resources as external files by using the same name prefix as the main json file. Name prefix can be anything, it is used to sort the messages (if you want to insert multiple messages at once) and also to group resources related to that message. + +- **name.json** is the main message file, this includes the general message structure +- **name.html** or **name.htm** is the HTML content of the message. If this file exists then it sets or overrides the `html` property in message json structure +- **name.text** or **name.txt** is the plaintext content of the message. If this file exists then it sets or overrides the `text` property in message json structure +- **name.filename.ext** is included in the message as an attachment diff --git a/emails/example.duck.png b/emails/example.duck.png new file mode 100644 index 0000000000000000000000000000000000000000..54c89c8110b21861821f943877902402ee8daf51 GIT binary patch literal 15754 zcmZ{LWmFtZ)Aj<36I>Qp+#|5~;u4&oAq01a;I4~9a1R6z+yr-rus8%*EVwOBa0zZN z&(H72ccy3hjP#j4HB((zT~!sWt}6cuhYAM(0K8IEkkNeUz5lmkVZ0oLECi-rI-s?b ziWC4)n}GXZivDs=Z?2%J0s#230sycu0O0QB5^N6u@PGgS`z8Q@@HYT}!a2K5L-gea zrn{nwEao}}4F(l1U7Y;O;{XgZWqBFE^Z&N|&XN=WKr~)aMoPmVH&2r3f4WwXgrCN~Zcb5M=C%PHR{7Y&i_W5aeJ zL}mj}b!hMgyXReoZ)@EKXJ;DtTkb=H>SHH($xNO$%GbfxA?+My?wnDEeXSxXxbvZ6Qh;{z~Q+U(4vd488%8H^3Pk(ha~*B&m_3KMIS|ClZcB zP1fdQVLCyny`KLQsLh^~r50#M*+|DG`t)Jt{6Aa5Xt4RQO7{1)C@uT^#A>F`c1*8} z)n#!r*`iRB-ol1O;qPIXFm-$b6h>F|q~PC_&Qd*~rr(r-DDbS=Hu)OcldLcULBfAhg&Cdr!qLUT%#)bCn?ig+bGm-Zj>A)of7!_;V{$|W*8C39DoMG3}Zfp zO9Sz_hajOV$Wp`PyU+(MvG4xy z0@YK0Yu{(KOh_FhEd%5fg~y_NAZ%fdf=dB`oHUYn+84?7{v>Ga37CITngZYK)efc; zJ!lwRTAy>y8OA8PU;=!J%#UxSb$&{|m!tZ?-51HfBQAm#;6+_6{=9t?qrVm($4l}v zz#Juq1m!~+5{zl7y^IbWvfsimBaP4-`XroKAQZ=aEy9UE0Jov+EWt6sK;j#S!zC%W zD4#)WzH6bMk zy9$kgc*H^DT}++>{SWlldaT9ob^lOM6Bc9fiQ>URxlwn411PL@6m0ZA-VZ_Mbt1H5 z7*ihb2Ylkv&CkXukq1>rvwGB^A9F6pWztkX>Cq{GODM@=F_2ei@?mJ01nWZcVRrR$e*h`*hO>A=C~A zJG0Mk5(@d`-ZKL+4uFXf$f;mO*sD*8?XdS47^Y;2EbG|uiSW>-g!)(>rt#m!j(0nN zJZ_Q}XTskwTr_wktE+jb;&t59#v7N1AI}Z3JL8M8YgcR(8`)|Jn@JgcX%Z45t#?_a z?iTE*Q9!VZ^A6#T@DPEQ`r;6zMkT)?v!G=lA6lG5HyQPsqo+_R(JiqcR2@Io8s%GJ ztK+lv0G5G{vuU^F`)u=fPe&IMh__6)^$p1ORS(W&sO2r{r=SBaYfqcOptHKPTk3MM zPoh_lbr}Z~LZBK^_?NI&($}tu4D=RO1v2@LlK)lM4uPV4>{;jH=75C3QIhl!tL|2G zNgqJ@=gxHxvs$C(5%niL!0P4Gh13=_qx-dfJg3sTc5Q3}u!7JhcA3MDWcUi3rYPS@ zez#zIl-T);2T@k?) zzMg-@CV!RP3u^z0>r$HNT4}%&)`rOcOaOi(>w*@TaS=H8f^vix{!IAYJ!$i(Jz#N>Wmms1HzMItI|T?K$

52fJ$aqGQKoG^NuI-jq$kV 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 F{|5;6&JX|q literal 0 HcmV?d00001 diff --git a/emails/example.html b/emails/example.html new file mode 100644 index 00000000..a853ab43 --- /dev/null +++ b/emails/example.html @@ -0,0 +1,130 @@ + + + + + + + Actionable emails e.g. reset password + + + + + + + + + + + + + +
+
+ + + + +
+ + + + + + + + + + + + + + + + +
+ [FNAME], welcome to Wild Duck email service! +
+ If you are seeing this message then it means you have reached the inbox of your new email address [EMAIL]. Be aware though that the service is in a constant change, so this address might disappear during the next database schema update. Don't start using it as your main email address! +
+ This message was generated and inserted to this mailbox from the /emails folder using the default template. +
+ — the Wild Duck team +
+
+ +
+
+ + + diff --git a/emails/example.json.disabled b/emails/example.json.disabled new file mode 100644 index 00000000..4601a288 --- /dev/null +++ b/emails/example.json.disabled @@ -0,0 +1,12 @@ +{ + "flag": true, + "from": { + "name": "Wild Duck Support", + "address": "info@[DOMAIN]" + }, + "to": { + "name": "[NAME]", + "address": "[EMAIL]" + }, + "subject": "[FNAME], welcome to our awesome service!" +} diff --git a/emails/example.txt b/emails/example.txt new file mode 100644 index 00000000..fc9eea47 --- /dev/null +++ b/emails/example.txt @@ -0,0 +1,9 @@ +[FNAME], welcome to Wild Duck email service! + +If you are seeing this message then it means you have reached the inbox of your new email address [EMAIL]. Be aware though that the service is in a constant change, so this address might disappear during the next database schema update. Don't start using it as your main email address! + +This message was generated and inserted to this mailbox from the /emails folder using the default template. + +Your friends from the Wild Duck team + +– Check out Wild Duck on Github: https://github.com/nodemailer/wildduck diff --git a/lib/tools.js b/lib/tools.js index ce3d3f83..5b7cc25a 100644 --- a/lib/tools.js +++ b/lib/tools.js @@ -170,7 +170,7 @@ function renderEmailTemplate(tags, template) { } Object.keys(node || {}).forEach(key => { - if (!node[key]) { + if (!node[key] || ['content'].includes(key)) { return; } @@ -213,30 +213,102 @@ function getEmailTemplates(tags, callback) { files = files.sort((a, b) => a.localeCompare(b)); let pos = 0; - let newTemplates = []; + let filesMap = new Map(); + let checkFiles = () => { if (pos >= files.length) { + let newTemplates = Array.from(filesMap) + .map(entry => { + entry = entry[1]; + if (!entry.message) { + return false; + } + + if (entry.html) { + entry.message.html = entry.html; + } + + if (entry.text) { + entry.message.text = entry.text; + } + + if (entry.attachments) { + entry.message.attachments = [].concat(entry.message.attachments || []).concat(entry.attachments); + } + + if (entry.text) { + entry.message.text = entry.text; + } + + return entry.message; + }) + .filter(entry => entry); + templates = newTemplates; return callback(null, templates.map(template => renderEmailTemplate(tags, template))); } let file = files[pos++]; - if (!/\.json$/i.test(file)) { - return checkFiles(); - } - fs.readFile(pathlib.join(templateFolder, file), 'utf-8', (err, email) => { + let fParts = pathlib.parse(file); + fs.readFile(pathlib.join(templateFolder, file), (err, value) => { if (err) { // ignore? return checkFiles(); } - let parsed; - try { - parsed = JSON.parse(email); - } catch (E) { - //ignore? + + let ext = fParts.ext.toLowerCase(); + let name = fParts.name.toLowerCase(); + if (name.indexOf('.') >= 0) { + name = name.substr(0, name.indexOf('.')); } - if (parsed) { - newTemplates.push(parsed); + + let type = false; + switch (ext) { + case '.json': { + try { + value = JSON.parse(value.toString('utf-8')); + type = 'message'; + } catch (E) { + //ignore? + } + break; + } + case '.html': + case '.htm': + value = value.toString('utf-8'); + type = 'html'; + break; + case '.text': + case '.txt': + value = value.toString('utf-8'); + type = 'text'; + break; + default: { + if (name.length < fParts.name.length) { + type = 'attachment'; + value = { + filename: fParts.base.substr(name.length + 1), + content: value.toString('base64'), + encoding: 'base64' + }; + } + } } + + if (type) { + if (!filesMap.has(name)) { + filesMap.set(name, {}); + } + if (type === 'attachment') { + if (!filesMap.get(name).attachments) { + filesMap.get(name).attachments = [value]; + } else { + filesMap.get(name).attachments.push(value); + } + } else { + filesMap.get(name)[type] = value; + } + } + return checkFiles(); }); }; diff --git a/lib/user-handler.js b/lib/user-handler.js index 09fd7436..cafbc917 100644 --- a/lib/user-handler.js +++ b/lib/user-handler.js @@ -14,6 +14,7 @@ const crypto = require('crypto'); const mailboxTranslations = require('./translations'); const base32 = require('base32.js'); const MailComposer = require('nodemailer/lib/mail-composer'); +const humanname = require('humanname'); class UserHandler { constructor(options) { @@ -480,11 +481,13 @@ class UserHandler { return callback(null, id); } + let parsedName = humanname.parse(userData.name); this.pushDefaultMessages( userData, { NAME: userData.name || address, - FNAME: (userData.name || '').trim().replace(/\s+/g, ' ').split(' ').shift() || address, + FNAME: parsedName.firstName, + LNAME: parsedName.lastName, DOMAIN: address.substr(address.indexOf('@') + 1), EMAIL: address }, diff --git a/package.json b/package.json index d678a9db..53e5e7fd 100644 --- a/package.json +++ b/package.json @@ -28,6 +28,8 @@ "generate-password": "^1.3.0", "he": "^1.1.1", "html-to-text": "^3.3.0", + "humanname": "^0.2.2", + "humanparser": "^1.4.0", "iconv-lite": "^0.4.18", "joi": "^10.6.0", "js-yaml": "^3.9.1", @@ -58,7 +60,7 @@ "url": "git://github.com/wildduck-email/wildduck.git" }, "optionalDependencies": { - "@ronomon/crypto-async": "^2.0.1", + "@ronomon/crypto-async": "^2.2.0", "modern-syslog": "^1.1.4" } } diff --git a/test/api-test.js b/test/api-test.js index c71a9a92..d982b76e 100644 --- a/test/api-test.js +++ b/test/api-test.js @@ -4,7 +4,6 @@ const chai = require('chai'); const frisby = require('icedfrisby'); -const Joi = require('joi'); const expect = chai.expect; chai.config.includeStack = true; @@ -26,11 +25,6 @@ frisby { json: true } ) .expectStatus(200) - .expectJSONTypes({ - success: Joi.boolean(), - id: Joi.string(), - error: Joi.string() - }) .afterJSON(response => { expect(response).to.exist; expect(response.success).to.be.true;