diff options
author | Lauri Ojansivu <x@xet7.org> | 2019-08-13 01:08:22 +0300 |
---|---|---|
committer | GitHub <noreply@github.com> | 2019-08-13 01:08:22 +0300 |
commit | b9a7bd3503464e808e1b4a3385128b97df52e915 (patch) | |
tree | 901264cf0aab46d1b95ba3ea897c191468f51a24 | |
parent | 861868fa295fb549c64484eb79707e93ab79bc0c (diff) | |
parent | 8d76db91b883e7142cb7cddcfd25fa230663be34 (diff) | |
download | wekan-b9a7bd3503464e808e1b4a3385128b97df52e915.tar.gz wekan-b9a7bd3503464e808e1b4a3385128b97df52e915.tar.bz2 wekan-b9a7bd3503464e808e1b4a3385128b97df52e915.zip |
Merge pull request #2611 from whowillcare/master
Addfeature: Enable HTML email content for richer comment
-rwxr-xr-x | client/components/main/editor.js | 43 | ||||
-rw-r--r-- | models/activities.js | 8 | ||||
-rw-r--r-- | server/notifications/email.js | 4 |
3 files changed, 37 insertions, 18 deletions
diff --git a/client/components/main/editor.js b/client/components/main/editor.js index 248f4588..82bda0a3 100755 --- a/client/components/main/editor.js +++ b/client/components/main/editor.js @@ -1,4 +1,5 @@ import _sanitizeXss from 'xss'; +const ASIS = 'asis'; const sanitizeXss = (input, options) => { const defaultAllowedIframeSrc = /^(https:){0,1}\/\/.*?(youtube|vimeo|dailymotion|youku)/i; const allowedIframeSrcRegex = (function() { @@ -17,28 +18,39 @@ const sanitizeXss = (input, options) => { return reg; })(); const targetWindow = '_blank'; + const getHtmlDOM = html => { + const i = document.createElement('i'); + i.innerHTML = html; + return i.firstChild; + }; options = { onTag(tag, html, options) { + const htmlDOM = getHtmlDOM(html); + const getAttr = attr => { + return htmlDOM && attr && htmlDOM.getAttribute(attr); + }; if (tag === 'iframe') { const clipCls = 'note-vide-clip'; if (!options.isClosing) { - const srcp = /src=(['"]{0,1})(\S*)(\1)/; - let safe = html.indexOf(`class="${clipCls}"`) > -1; - if (srcp.exec(html)) { - const src = RegExp.$2; - if (allowedIframeSrcRegex.exec(src)) { - safe = true; - } - if (safe) - return `<iframe src='${src}' class="${clipCls}" width=100% height=auto allowfullscreen></iframe>`; + const iframeCls = getAttr('class'); + let safe = iframeCls.indexOf(clipCls) > -1; + const src = getAttr('src'); + if (allowedIframeSrcRegex.exec(src)) { + safe = true; } + if (safe) + return `<iframe src='${src}' class="${clipCls}" width=100% height=auto allowfullscreen></iframe>`; } else { + // remove </iframe> tag return ''; } } else if (tag === 'a') { if (!options.isClosing) { - if (/href=(['"]{0,1})(\S*)(\1)/.exec(html)) { - const href = RegExp.$2; + if (getAttr(ASIS) === 'true') { + // if has a ASIS attribute, don't do anything, it's a member id + return html; + } else { + const href = getAttr('href'); if (href.match(/^((http(s){0,1}:){0,1}\/\/|\/)/)) { // a valid url return `<a href=${href} target=${targetWindow}>`; @@ -47,8 +59,8 @@ const sanitizeXss = (input, options) => { } } else if (tag === 'img') { if (!options.isClosing) { - if (new RegExp('src=([\'"]{0,1})(\\S*)(\\1)').exec(html)) { - const src = RegExp.$2; + const src = getAttr('src'); + if (src) { return `<a href='${src}' class='swipebox'><img src='${src}' class="attachment-image-preview mCS_img_loaded"></a>`; } } @@ -203,7 +215,9 @@ Template.editor.onRendered(() => { // even though uploaded event fired, attachment.url() is still null somehow //TODO const url = attachment.url(); if (url) { - insertImage(url); + insertImage( + `${location.protocol}//${location.host}${url}`, + ); } else { retry++; if (retry < maxTry) { @@ -334,6 +348,7 @@ Blaze.Template.registerHelper( // `userId` to the popup as usual, and we need to store it in the DOM // using a data attribute. 'data-userId': knowedUser.userId, + [ASIS]: 'true', }, linkValue, ); diff --git a/models/activities.js b/models/activities.js index 168effd0..3ecd5c8c 100644 --- a/models/activities.js +++ b/models/activities.js @@ -110,7 +110,9 @@ if (Meteor.isServer) { if (activity.userId) { // No need send notification to user of activity // participants = _.union(participants, [activity.userId]); - params.user = activity.user().getName(); + const user = activity.user(); + params.user = user.getName(); + params.userEmails = user.emails; params.userId = activity.userId; } if (activity.boardId) { @@ -172,7 +174,7 @@ if (Meteor.isServer) { const comment = activity.comment(); params.comment = comment.text; if (board) { - const atUser = /(?:^|\s+)@(\S+)(?:\s+|$)/g; + const atUser = /(?:^|>|\b|\s)@(\S+)(?:\s|$|<|\b)/g; const comment = params.comment; if (comment.match(atUser)) { const commenter = params.user; @@ -184,6 +186,8 @@ if (Meteor.isServer) { } const user = Users.findOne(username) || Users.findOne({ username }); const uid = user && user._id; + params.atUsername = username; + params.atEmails = user.emails; if (board.hasMember(uid)) { title = 'act-atUserComment'; watchers = _.union(watchers, [uid]); diff --git a/server/notifications/email.js b/server/notifications/email.js index d198b8b5..deb140a6 100644 --- a/server/notifications/email.js +++ b/server/notifications/email.js @@ -32,14 +32,14 @@ Meteor.startup(() => { if (texts.length === 0) return; // merge the cached content into single email and flush - const text = texts.join('\n\n'); + const html = texts.join('<br/>\n\n'); user.clearEmailBuffer(); try { Email.send({ to: user.emails[0].address.toLowerCase(), from: Accounts.emailTemplates.from, subject, - text, + html, }); } catch (e) { return; |