From d5d2c5524c0890e0d89ff2029995bb4bed941421 Mon Sep 17 00:00:00 2001 From: Dhruv Patel Date: Tue, 17 Dec 2019 09:31:20 +0000 Subject: [PATCH] [FIX] base,web: url widget: add protocol in href With this commit, the url widget automatically prefixes the href url by http:// if no protocol is specified, so that clicking on the url will by default redirect to an external website. To prevent this behavior, one can still specify the new option 'website_path: true' on the url field node in the view arch. Thanks to this, we can remove a similar logic (only implemented on res_partner model) which automatically altered the value saved in database to force its 'http://' prefix. task-2153184 closes odoo/odoo#42118 Signed-off-by: Aaron Bohy (aab) --- .../web/static/src/js/fields/basic_fields.js | 8 +++- .../static/tests/fields/basic_fields_tests.js | 38 +++++++++++++++++-- doc/reference/javascript_reference.rst | 9 ++++- odoo/addons/base/models/res_partner.py | 12 ------ 4 files changed, 48 insertions(+), 19 deletions(-) diff --git a/addons/web/static/src/js/fields/basic_fields.js b/addons/web/static/src/js/fields/basic_fields.js index 0d1fc77737f..996e759a70a 100644 --- a/addons/web/static/src/js/fields/basic_fields.js +++ b/addons/web/static/src/js/fields/basic_fields.js @@ -1528,6 +1528,7 @@ var UrlWidget = InputField.extend({ init: function () { this._super.apply(this, arguments); this.tagName = this.mode === 'readonly' ? 'a' : 'input'; + this.websitePath = this.nodeOptions.website_path || false; }, //-------------------------------------------------------------------------- @@ -1555,10 +1556,15 @@ var UrlWidget = InputField.extend({ * @private */ _renderReadonly: function () { + let href = this.value; + if (this.value && !this.websitePath) { + const regex = /^(?:[fF]|[hH][tT])[tT][pP][sS]?:\/\//; + href = !regex.test(this.value) ? `http://${href}` : href; + } this.$el.text(this.attrs.text || this.value) .addClass('o_form_uri o_text_overflow') .attr('target', '_blank') - .attr('href', this.value); + .attr('href', href); }, //-------------------------------------------------------------------------- diff --git a/addons/web/static/tests/fields/basic_fields_tests.js b/addons/web/static/tests/fields/basic_fields_tests.js index ede054eb5a9..1cd7b35b6be 100644 --- a/addons/web/static/tests/fields/basic_fields_tests.js +++ b/addons/web/static/tests/fields/basic_fields_tests.js @@ -1629,7 +1629,7 @@ QUnit.module('basic_fields', { assert.containsOnce(form, 'a.o_form_uri.o_field_widget.o_text_overflow', "should have a anchor with correct classes"); - assert.hasAttrValue(form.$('a.o_form_uri.o_field_widget.o_text_overflow'), 'href', 'yop', + assert.hasAttrValue(form.$('a.o_form_uri.o_field_widget.o_text_overflow'), 'href', 'http://yop', "should have proper href link"); assert.hasAttrValue(form.$('a.o_form_uri.o_field_widget.o_text_overflow'), 'target', '_blank', "should have target attribute set to _blank"); @@ -1650,7 +1650,7 @@ QUnit.module('basic_fields', { await testUtils.form.clickSave(form); assert.containsOnce(form, 'a.o_form_uri.o_field_widget.o_text_overflow', "should still have a anchor with correct classes"); - assert.hasAttrValue(form.$('a.o_form_uri.o_field_widget.o_text_overflow'), 'href', 'limbo', + assert.hasAttrValue(form.$('a.o_form_uri.o_field_widget.o_text_overflow'), 'href', 'http://limbo', "should have proper new href link"); assert.strictEqual(form.$('a.o_form_uri.o_field_widget.o_text_overflow').text(), 'limbo', 'the new value should be displayed'); @@ -1676,6 +1676,36 @@ QUnit.module('basic_fields', { form.destroy(); }); + QUnit.test('url widget: href attribute and website_path option', async function (assert) { + assert.expect(4); + + this.data.partner.fields.url1 = { string: "Url 1", type: "char", default: "www.url1.com" }; + this.data.partner.fields.url2 = { string: "Url 2", type: "char", default: "www.url2.com" }; + this.data.partner.fields.url3 = { string: "Url 3", type: "char", default: "http://www.url3.com" }; + this.data.partner.fields.url4 = { string: "Url 4", type: "char", default: "https://url4.com" }; + + const form = await createView({ + View: FormView, + model: 'partner', + data: this.data, + arch: ` +
+ + + + + `, + res_id: 1, + }); + + assert.strictEqual(form.$('a[name="url1"]').attr('href'), 'http://www.url1.com'); + assert.strictEqual(form.$('a[name="url2"]').attr('href'), 'www.url2.com'); + assert.strictEqual(form.$('a[name="url3"]').attr('href'), 'http://www.url3.com'); + assert.strictEqual(form.$('a[name="url4"]').attr('href'), 'https://url4.com'); + + form.destroy(); + }); + QUnit.test('char field in editable list view', async function (assert) { assert.expect(10); @@ -1690,7 +1720,7 @@ QUnit.module('basic_fields', { "should have 5 cells"); assert.containsN(list, 'a.o_form_uri.o_field_widget.o_text_overflow', 5, "should have 5 anchors with correct classes"); - assert.hasAttrValue(list.$('a.o_form_uri.o_field_widget.o_text_overflow').first(), 'href', 'yop', + assert.hasAttrValue(list.$('a.o_form_uri.o_field_widget.o_text_overflow').first(), 'href', 'http://yop', "should have proper href link"); assert.strictEqual(list.$('tbody td:not(.o_list_record_selector)').first().text(), 'yop', "value should be displayed properly as text"); @@ -1709,7 +1739,7 @@ QUnit.module('basic_fields', { assert.doesNotHaveClass($cell.parent(), 'o_selected_row', 'should not be in edit mode anymore'); assert.containsN(list, 'a.o_form_uri.o_field_widget.o_text_overflow', 5, "should still have 5 anchors with correct classes"); - assert.hasAttrValue(list.$('a.o_form_uri.o_field_widget.o_text_overflow').first(), 'href', 'brolo', + assert.hasAttrValue(list.$('a.o_form_uri.o_field_widget.o_text_overflow').first(), 'href', 'http://brolo', "should have proper new href link"); assert.strictEqual(list.$('a.o_form_uri.o_field_widget.o_text_overflow').first().text(), 'brolo', "value should be properly updated"); diff --git a/doc/reference/javascript_reference.rst b/doc/reference/javascript_reference.rst index f0db5cd40b2..8b3df82c85e 100644 --- a/doc/reference/javascript_reference.rst +++ b/doc/reference/javascript_reference.rst @@ -1733,8 +1733,6 @@ order. This field displays an url (in readonly mode). The main reason to use it is that it is rendered as an anchor tag with the proper css classes and href. - - Supported field types: *char* - Also, the text of the anchor tag can be customized with the *text* attribute (it won't change the href value). @@ -1742,6 +1740,13 @@ order. + Options: + + - website_path: (default:false) by default, the widget forces (if not already + the case) the href value to begin with http:// except if this option is set + to true, thus allowing redirections to the database's own website. + + - Supported field types: *char* - domain (FieldDomain) The "Domain" field allows the user to construct a technical-prefix domain diff --git a/odoo/addons/base/models/res_partner.py b/odoo/addons/base/models/res_partner.py index d110cb3598f..e84570cf555 100644 --- a/odoo/addons/base/models/res_partner.py +++ b/odoo/addons/base/models/res_partner.py @@ -476,14 +476,6 @@ class Partner(models.Model): addr_vals = self._update_fields_values(address_fields) parent.update_address(addr_vals) - def _clean_website(self, website): - url = urls.url_parse(website) - if not url.scheme: - if not url.netloc: - url = url.replace(netloc=url.path, path='') - website = url.replace(scheme='http').to_url() - return website - def write(self, vals): if vals.get('active') is False: # DLE: It should not be necessary to modify this to make work the ORM. The problem was just the recompute @@ -503,8 +495,6 @@ class Partner(models.Model): # (this is to allow the code from res_users to write to the partner!) or # if setting the company_id to False (this is compatible with any user # company) - if vals.get('website'): - vals['website'] = self._clean_website(vals['website']) if vals.get('parent_id'): vals['company_name'] = False if vals.get('company_id'): @@ -534,8 +524,6 @@ class Partner(models.Model): if self.env.context.get('import_file'): self._check_import_consistency(vals_list) for vals in vals_list: - if vals.get('website'): - vals['website'] = self._clean_website(vals['website']) if vals.get('parent_id'): vals['company_name'] = False partners = super(Partner, self).create(vals_list)