[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:
@@ -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");
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
Reference in New Issue
Block a user