diff --git a/addons/web_editor/static/src/js/editor/odoo-editor/src/utils/sanitize.js b/addons/web_editor/static/src/js/editor/odoo-editor/src/utils/sanitize.js index 3c75bd18b16..73020c23a07 100644 --- a/addons/web_editor/static/src/js/editor/odoo-editor/src/utils/sanitize.js +++ b/addons/web_editor/static/src/js/editor/odoo-editor/src/utils/sanitize.js @@ -16,9 +16,10 @@ import { isEditorTab, isProtected, isZWS, - getUrlsInfosInString, isArtificialVoidElement, ancestors, + EMAIL_REGEX, + URL_REGEX_WITH_INFOS, } from './utils.js'; const NOT_A_NUMBER = /[^\d]/g; @@ -74,6 +75,39 @@ export function areSimilarElements(node, node2) { ); } +/** +* Returns a URL if link's label is a valid email of http URL, null otherwise. +* +* @param {HTMLAnchorElement} link +* @returns {String|null} +*/ +function deduceURLfromLabel(link) { + const label = link.innerText.trim(); + // Check first for e-mail. + let match = label.match(EMAIL_REGEX); + if (match) { + return match[1] ? match[0] : 'mailto:' + match[0]; + } + // Check for http link. + // Regex with 'g' flag is stateful, reset lastIndex before and after using + // exec. + URL_REGEX_WITH_INFOS.lastIndex = 0; + match = URL_REGEX_WITH_INFOS.exec(label); + URL_REGEX_WITH_INFOS.lastIndex = 0; + if (match && match[0] === label) { + const currentHttpProtocol = (link.href.match(/^http(s)?:\/\//gi) || [])[0]; + if (match[2]) { + return match[0]; + } else if (currentHttpProtocol) { + // Avoid converting a http link to https. + return currentHttpProtocol + match[0]; + } else { + return 'https://' + match[0]; + } + } + return null; +} + /** * Sanitize the given node and return it. * @@ -208,10 +242,9 @@ export function sanitize(nodeToSanitize, root = nodeToSanitize) { // Update link URL if label is a new valid link. const startEl = start && closestElement(start, 'a'); if (startEl && root.contains(startEl)) { - const linkLabel = startEl.innerText; - const urlInfo = getUrlsInfosInString(linkLabel); - if (urlInfo.length && urlInfo[0].label === linkLabel && !startEl.href.startsWith('mailto:')) { - startEl.setAttribute('href', urlInfo[0].url); + const url = deduceURLfromLabel(startEl); + if (url) { + startEl.setAttribute('href', url); } } } diff --git a/addons/web_editor/static/src/js/editor/odoo-editor/test/spec/copyPaste.test.js b/addons/web_editor/static/src/js/editor/odoo-editor/test/spec/copyPaste.test.js index b321bd5f132..87cab3f1df9 100644 --- a/addons/web_editor/static/src/js/editor/odoo-editor/test/spec/copyPaste.test.js +++ b/addons/web_editor/static/src/js/editor/odoo-editor/test/spec/copyPaste.test.js @@ -1417,7 +1417,7 @@ describe('Paste', () => { stepFunction: async editor => { await pasteText(editor, 'oom'); }, - contentAfter: '

aboom[].comd

', + contentAfter: '

aboom[].comd

', }); }); it('should replace link for new content when pasting in an empty link', async () => { diff --git a/addons/web_editor/static/src/js/editor/odoo-editor/test/spec/link.test.js b/addons/web_editor/static/src/js/editor/odoo-editor/test/spec/link.test.js index a3e157f6323..52346843fec 100644 --- a/addons/web_editor/static/src/js/editor/odoo-editor/test/spec/link.test.js +++ b/addons/web_editor/static/src/js/editor/odoo-editor/test/spec/link.test.js @@ -275,6 +275,45 @@ describe('Link', () => { }, contentAfter: '

ahttp://goo[]gle.comb

', }); + await testEditor(BasicEditor, { + contentBefore: '

ahello@moto[].com

', + stepFunction: async editor => { + await insertText(editor, 'r'); + }, + contentAfter: '

ahello@motor[].com

', + }); + }); + it('should change the url when the label change, without changing the protocol', async () => { + await testEditor(BasicEditor, { + contentBefore: '

agoogle.co[]b

', + stepFunction: async editor => { + await insertText(editor, 'm'); + }, + contentAfter: '

agoogle.com[]b

', + }); + await testEditor(BasicEditor, { + contentBefore: '

agoogle.co[]b

', + stepFunction: async editor => { + await insertText(editor, 'm'); + }, + contentAfter: '

agoogle.com[]b

', + }); + }); + it('should change the url when the label change, changing to the suitable protocol', async () => { + await testEditor(BasicEditor, { + contentBefore: '

ahello[]moto.com

', + stepFunction: async editor => { + await insertText(editor, '@'); + }, + contentAfter: '

ahello@[]moto.com

', + }); + await testEditor(BasicEditor, { + contentBefore: '

ahello@[]moto.com

', + stepFunction: async editor => { + await deleteBackward(editor); + }, + contentAfter: '

ahello[]moto.com

', + }); }); it('should change the url in one step', async () => { await testEditor(BasicEditor, {