From 0dc68dcc94ab9bb2e47943967fa2bc31780d20fa Mon Sep 17 00:00:00 2001
From: tsm-odoo <tsm@odoo.com>
Date: Wed, 24 May 2023 06:18:12 +0000
Subject: [PATCH] [FIX] mail: invalid url regex
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Since [1], comma can be included in url by the discuss regex. This
should not be the case if the comma is at the end of the url/line
since it could be part of a sentance.

This PR applies the same logic to the dot and the semicolon character
since they could also be used as punctuation.

[1] https://github.com/odoo/odoo/pull/122093

closes odoo/odoo#122227

Signed-off-by: Sébastien Theys (seb) <seb@odoo.com>
---
 addons/mail/static/src/js/utils.js            |  2 +-
 .../utils/mail_utils_tests.js                 | 66 ++++++++++++++++++-
 2 files changed, 66 insertions(+), 2 deletions(-)

diff --git a/addons/mail/static/src/js/utils.js b/addons/mail/static/src/js/utils.js
index ee588948b14a..b93b115ebe91 100644
--- a/addons/mail/static/src/js/utils.js
+++ b/addons/mail/static/src/js/utils.js
@@ -65,7 +65,7 @@ var _escapeEntities = (function () {
 // Suggested URL Javascript regex of http://stackoverflow.com/questions/3809401/what-is-a-good-regular-expression-to-match-a-url
 // Adapted to make http(s):// not required if (and only if) www. is given. So `should.notmatch` does not match.
 // And further extended to include Latin-1 Supplement, Latin Extended-A, Latin Extended-B and Latin Extended Additional.
-var 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;
+var 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;
 /**
  * @param {string} text
  * @param {Object} [attrs={}]
diff --git a/addons/mail/static/tests/qunit_suite_tests/utils/mail_utils_tests.js b/addons/mail/static/tests/qunit_suite_tests/utils/mail_utils_tests.js
index cd86f0c225e5..11a63975ede9 100644
--- a/addons/mail/static/tests/qunit_suite_tests/utils/mail_utils_tests.js
+++ b/addons/mail/static/tests/qunit_suite_tests/utils/mail_utils_tests.js
@@ -152,6 +152,70 @@ QUnit.test("url", async (assert) => {
     await insertText(".o_ComposerTextInput_textarea", messageBody);
     await click("button:contains(Send)");
     assert.containsOnce($, `.o_Message a:contains(${messageBody})`);
-})
+});
+
+QUnit.test("url with comma at the end", async (assert) => {
+    const pyEnv = await startServer();
+    const channelId = pyEnv["mail.channel"].create({ name: "General" });
+    const { click, insertText, openDiscuss } = await start({
+        discuss: {
+            context: { active_id: channelId },
+        },
+    });
+    await openDiscuss();
+    const messageBody = "Go to https://odoo.com, it's great!";
+    await insertText(".o_ComposerTextInput_textarea", messageBody);
+    await click("button:contains(Send)");
+    assert.containsOnce($, `.o_Message a:contains(https://odoo.com)`);
+    assert.containsOnce($, `.o_Message:contains(${messageBody})`);
+});
+
+QUnit.test("url with dot at the end", async (assert) => {
+    const pyEnv = await startServer();
+    const channelId = pyEnv["mail.channel"].create({ name: "General" });
+    const { click, insertText, openDiscuss } = await start({
+        discuss: {
+            context: { active_id: channelId },
+        },
+    });
+    await openDiscuss();
+    const messageBody = "Go to https://odoo.com. It's great!";
+    await insertText(".o_ComposerTextInput_textarea", messageBody);
+    await click("button:contains(Send)");
+    assert.containsOnce($, `.o_Message a:contains(https://odoo.com)`);
+    assert.containsOnce($, `.o_Message:contains(${messageBody})`);
+});
+
+QUnit.test("url with semicolon at the end", async (assert) => {
+    const pyEnv = await startServer();
+    const channelId = pyEnv["mail.channel"].create({ name: "General" });
+    const { click, insertText, openDiscuss } = await start({
+        discuss: {
+            context: { active_id: channelId },
+        },
+    });
+    await openDiscuss();
+    const messageBody = "Go to https://odoo.com; it's great!";
+    await insertText(".o_ComposerTextInput_textarea", messageBody);
+    await click("button:contains(Send)");
+    assert.containsOnce($, `.o_Message a:contains(https://odoo.com)`);
+    assert.containsOnce($, `.o_Message:contains(${messageBody})`);
+});
+
+QUnit.test("url with ellipsis at the end", async (assert) => {
+    const pyEnv = await startServer();
+    const channelId = pyEnv["mail.channel"].create({ name: "General" });
+    const { click, insertText, openDiscuss } = await start({
+        discuss: {
+            context: { active_id: channelId },
+        },
+    });
+    await openDiscuss();
+    const messageBody = "Go to https://odoo.com... it's great!";
+    await insertText(".o_ComposerTextInput_textarea", messageBody);
+    await click("button:contains(Send)");
+    assert.containsOnce($, `.o_Message a:contains(https://odoo.com)`);
+    assert.containsOnce($, `.o_Message:contains(${messageBody})`);
+});
 
 });
-- 
GitLab