[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) <aab@odoo.com>
This commit is contained in:
Dhruv Patel
2020-01-30 14:03:36 +00:00
committed by Aaron Bohy
parent eec0b80756
commit d5d2c5524c
4 changed files with 48 additions and 19 deletions
@@ -1528,6 +1528,7 @@ var UrlWidget = InputField.extend({
init: function () { init: function () {
this._super.apply(this, arguments); this._super.apply(this, arguments);
this.tagName = this.mode === 'readonly' ? 'a' : 'input'; this.tagName = this.mode === 'readonly' ? 'a' : 'input';
this.websitePath = this.nodeOptions.website_path || false;
}, },
//-------------------------------------------------------------------------- //--------------------------------------------------------------------------
@@ -1555,10 +1556,15 @@ var UrlWidget = InputField.extend({
* @private * @private
*/ */
_renderReadonly: function () { _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) this.$el.text(this.attrs.text || this.value)
.addClass('o_form_uri o_text_overflow') .addClass('o_form_uri o_text_overflow')
.attr('target', '_blank') .attr('target', '_blank')
.attr('href', this.value); .attr('href', href);
}, },
//-------------------------------------------------------------------------- //--------------------------------------------------------------------------
@@ -1629,7 +1629,7 @@ QUnit.module('basic_fields', {
assert.containsOnce(form, 'a.o_form_uri.o_field_widget.o_text_overflow', assert.containsOnce(form, 'a.o_form_uri.o_field_widget.o_text_overflow',
"should have a anchor with correct classes"); "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"); "should have proper href link");
assert.hasAttrValue(form.$('a.o_form_uri.o_field_widget.o_text_overflow'), 'target', '_blank', assert.hasAttrValue(form.$('a.o_form_uri.o_field_widget.o_text_overflow'), 'target', '_blank',
"should have target attribute set to _blank"); "should have target attribute set to _blank");
@@ -1650,7 +1650,7 @@ QUnit.module('basic_fields', {
await testUtils.form.clickSave(form); await testUtils.form.clickSave(form);
assert.containsOnce(form, 'a.o_form_uri.o_field_widget.o_text_overflow', assert.containsOnce(form, 'a.o_form_uri.o_field_widget.o_text_overflow',
"should still have a anchor with correct classes"); "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"); "should have proper new href link");
assert.strictEqual(form.$('a.o_form_uri.o_field_widget.o_text_overflow').text(), 'limbo', assert.strictEqual(form.$('a.o_form_uri.o_field_widget.o_text_overflow').text(), 'limbo',
'the new value should be displayed'); 'the new value should be displayed');
@@ -1676,6 +1676,36 @@ QUnit.module('basic_fields', {
form.destroy(); 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: `
<form>
<field name="url1" widget="url"/>
<field name="url2" widget="url" options="{'website_path': True}"/>
<field name="url3" widget="url"/>
<field name="url4" widget="url"/>
</form>`,
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) { QUnit.test('char field in editable list view', async function (assert) {
assert.expect(10); assert.expect(10);
@@ -1690,7 +1720,7 @@ QUnit.module('basic_fields', {
"should have 5 cells"); "should have 5 cells");
assert.containsN(list, 'a.o_form_uri.o_field_widget.o_text_overflow', 5, assert.containsN(list, 'a.o_form_uri.o_field_widget.o_text_overflow', 5,
"should have 5 anchors with correct classes"); "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"); "should have proper href link");
assert.strictEqual(list.$('tbody td:not(.o_list_record_selector)').first().text(), 'yop', assert.strictEqual(list.$('tbody td:not(.o_list_record_selector)').first().text(), 'yop',
"value should be displayed properly as text"); "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.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, assert.containsN(list, 'a.o_form_uri.o_field_widget.o_text_overflow', 5,
"should still have 5 anchors with correct classes"); "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"); "should have proper new href link");
assert.strictEqual(list.$('a.o_form_uri.o_field_widget.o_text_overflow').first().text(), 'brolo', assert.strictEqual(list.$('a.o_form_uri.o_field_widget.o_text_overflow').first().text(), 'brolo',
"value should be properly updated"); "value should be properly updated");
+7 -2
View File
@@ -1733,8 +1733,6 @@ order.
This field displays an url (in readonly mode). The main reason to use it is 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. 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 Also, the text of the anchor tag can be customized with the *text* attribute
(it won't change the href value). (it won't change the href value).
@@ -1742,6 +1740,13 @@ order.
<field name="foo" widget="url" text="Some URL"/> <field name="foo" widget="url" text="Some URL"/>
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) - domain (FieldDomain)
The "Domain" field allows the user to construct a technical-prefix domain The "Domain" field allows the user to construct a technical-prefix domain
-12
View File
@@ -476,14 +476,6 @@ class Partner(models.Model):
addr_vals = self._update_fields_values(address_fields) addr_vals = self._update_fields_values(address_fields)
parent.update_address(addr_vals) 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): def write(self, vals):
if vals.get('active') is False: 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 # 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 # (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 # if setting the company_id to False (this is compatible with any user
# company) # company)
if vals.get('website'):
vals['website'] = self._clean_website(vals['website'])
if vals.get('parent_id'): if vals.get('parent_id'):
vals['company_name'] = False vals['company_name'] = False
if vals.get('company_id'): if vals.get('company_id'):
@@ -534,8 +524,6 @@ class Partner(models.Model):
if self.env.context.get('import_file'): if self.env.context.get('import_file'):
self._check_import_consistency(vals_list) self._check_import_consistency(vals_list)
for vals in vals_list: for vals in vals_list:
if vals.get('website'):
vals['website'] = self._clean_website(vals['website'])
if vals.get('parent_id'): if vals.get('parent_id'):
vals['company_name'] = False vals['company_name'] = False
partners = super(Partner, self).create(vals_list) partners = super(Partner, self).create(vals_list)