[FIX] mail: linkify message content

closes odoo/odoo#125307

X-original-commit: 406ff4727e361588bded08c6719f932a8b800b35
Signed-off-by: Sébastien Theys (seb) <seb@odoo.com>
Signed-off-by: Alexandre Kühn (aku) <aku@odoo.com>
This commit is contained in:
Alexandre Kühn
2023-06-16 01:40:32 +02:00
parent e0de62b5a3
commit 9b55c4565b
2 changed files with 39 additions and 24 deletions
+34 -19
View File
@@ -8,6 +8,25 @@ import { url } from "@web/core/utils/urls";
const urlRegexp =
/\b(?:https?:\/\/\d{1,3}(?:\.\d{1,3}){3}|(?:https?:\/\/|(?:www\.))[-a-z0-9@:%._+~#=\u00C0-\u024F\u1E00-\u1EFF]{2,256}\.[a-z]{2,13})\b(?:[-a-z0-9@:%_+~#?&[\]^|{}`\\'$//=\u00C0-\u024F\u1E00-\u1EFF]|,(?!$| )|\.(?!$| |\.)|;(?!$| ))*/gi;
/**
* Escape < > & as html entities
*
* @param {string}
* @return {string}
*/
const _escapeEntities = (function () {
const map = { "&": "&amp;", "<": "&lt;", ">": "&gt;" };
const escaper = function (match) {
return map[match];
};
const testRegexp = RegExp("(?:&|<|>)");
const replaceRegexp = RegExp("(?:&|<|>)", "g");
return function (string) {
string = string == null ? "" : "" + string;
return testRegexp.test(string) ? string.replace(replaceRegexp, escaper) : string;
};
})();
/**
* @param rawBody {string}
* @param validRecords {Object}
@@ -40,7 +59,7 @@ export async function prettifyMessageContent(rawBody, validRecords = []) {
* @param {function} transformFunction
* @returns {string}
*/
function parseAndTransform(htmlString, transformFunction) {
export function parseAndTransform(htmlString, transformFunction) {
const openToken = "OPEN" + Date.now();
const string = htmlString.replace(/&lt;/g, openToken);
let children;
@@ -80,29 +99,25 @@ function _parseAndTransform(nodes, transformFunction) {
/**
* @param {string} text
* @param {Object} [attrs={}]
* @return {string} linkified text
*/
function linkify(text, attrs) {
attrs = attrs || {};
if (attrs.target === undefined) {
attrs.target = "_blank";
function linkify(text) {
let curIndex = 0;
let result = "";
let match;
while ((match = urlRegexp.exec(text)) !== null) {
result += _escapeEntities(text.slice(curIndex, match.index));
const url = match[0];
const href = encodeURI(!/^https?:\/\//i.test(url) ? "http://" + url : url);
result += `<a target="_blank" rel="noreferrer noopener" href="${href}">${_escapeEntities(
url
)}</a>`;
curIndex = match.index + match[0].length;
}
if (attrs.target === "_blank") {
attrs.rel = "noreferrer noopener";
}
attrs = Object.keys(attrs)
.map(function (key) {
return key + '="' + escape(attrs[key]) + '"';
})
.join(" ");
return text.replace(urlRegexp, function (url) {
var href = !/^https?:\/\//i.test(url) ? "http://" + url : url;
return "<a " + attrs + ' href="' + href + '">' + url + "</a>";
});
return result + _escapeEntities(text.slice(curIndex));
}
function addLink(node, transformChildren) {
export function addLink(node, transformChildren) {
if (node.nodeType === 3) {
// text node
const linkified = linkify(node.data);
+5 -5
View File
@@ -1,6 +1,6 @@
/* @odoo-module */
import * as utils from "@mail/js/utils";
import { addLink, parseAndTransform } from "@mail/utils/common/format";
import { start, startServer } from "@mail/../tests/helpers/test_utils";
QUnit.module("Mail utils");
@@ -21,7 +21,7 @@ QUnit.test("add_link utility function", function (assert) {
};
for (const [content, willLinkify] of Object.entries(testInputs)) {
const output = utils.parseAndTransform(content, utils.addLink);
const output = parseAndTransform(content, addLink);
if (willLinkify) {
assert.strictEqual(output.indexOf("<a "), 0);
assert.strictEqual(output.indexOf("</a>"), output.length - 4);
@@ -55,14 +55,14 @@ QUnit.test("addLink: utility function and special entities", function (assert) {
};
for (const [content, result] of Object.entries(testInputs)) {
const output = utils.parseAndTransform(content, utils.addLink);
const output = parseAndTransform(content, addLink);
assert.strictEqual(output, result);
}
});
QUnit.test("addLink: linkify inside text node (1 occurrence)", function (assert) {
const content = "<p>some text https://somelink.com</p>";
const linkified = utils.parseAndTransform(content, utils.addLink);
const linkified = parseAndTransform(content, addLink);
assert.ok(linkified.startsWith("<p>some text <a"));
assert.ok(linkified.endsWith("</a></p>"));
@@ -83,7 +83,7 @@ QUnit.test("addLink: linkify inside text node (2 occurrences)", function (assert
// stringified representation, we continue deeper assertion with query
// selectors.
const content = "<p>some text https://somelink.com and again https://somelink2.com ...</p>";
const linkified = utils.parseAndTransform(content, utils.addLink);
const linkified = parseAndTransform(content, addLink);
const fragment = document.createDocumentFragment();
const div = document.createElement("div");
fragment.appendChild(div);