summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLauri Ojansivu <x@xet7.org>2019-08-13 01:08:22 +0300
committerGitHub <noreply@github.com>2019-08-13 01:08:22 +0300
commitb9a7bd3503464e808e1b4a3385128b97df52e915 (patch)
tree901264cf0aab46d1b95ba3ea897c191468f51a24
parent861868fa295fb549c64484eb79707e93ab79bc0c (diff)
parent8d76db91b883e7142cb7cddcfd25fa230663be34 (diff)
downloadwekan-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-xclient/components/main/editor.js43
-rw-r--r--models/activities.js8
-rw-r--r--server/notifications/email.js4
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;