From d1eb6089fda3725a88af3b3eaf426d03e6855dc6 Mon Sep 17 00:00:00 2001 From: Victor Feyens Date: Wed, 10 Jun 2020 12:24:17 +0000 Subject: [PATCH 1/5] [IMP] website: define ir_attachment as type url when possible Storing attachment in database (when not specified as an url attachment) copies the given file in the filestore. When not necessary, this should be avoided to reduce the databases footprint (and their backups). On a given instance, the gain is minor, but on servers with hundreds of databases and/or backups, the impact is noticeable, leading to an economy of scale. --- addons/website/data/website_data.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/addons/website/data/website_data.xml b/addons/website/data/website_data.xml index b9e21640add..c7ed6fa16b8 100644 --- a/addons/website/data/website_data.xml +++ b/addons/website/data/website_data.xml @@ -250,8 +250,8 @@ business_conference.jpg ir.ui.view - binary - + url + /website/static/src/img/library/business_conference.jpg From 1aa874aab3579c8d3a93cb96df2476020e463dd3 Mon Sep 17 00:00:00 2001 From: Victor Feyens Date: Wed, 2 Sep 2020 08:48:49 +0000 Subject: [PATCH 2/5] [IMP] web: new widget for static image urls in char fields This new widget allows definition of Images through static urls stored in a Char field. It was implemented to provide a way to reduce databases footprint by using static files instead of duplicating them in the databases (& their backups). --- .../web/static/src/js/fields/basic_fields.js | 52 +++++++ .../static/src/js/fields/field_registry.js | 2 + .../static/tests/fields/basic_fields_tests.js | 128 ++++++++++++++++++ 3 files changed, 182 insertions(+) diff --git a/addons/web/static/src/js/fields/basic_fields.js b/addons/web/static/src/js/fields/basic_fields.js index 3e49fdbf250..8ebde6cbda9 100644 --- a/addons/web/static/src/js/fields/basic_fields.js +++ b/addons/web/static/src/js/fields/basic_fields.js @@ -1955,6 +1955,45 @@ var FieldBinaryImage = AbstractFieldBinary.extend({ }, }); +var CharImageUrl = AbstractField.extend({ + className: 'o_field_image', + description: _lt("Image"), + supportedFieldTypes: ['char'], + placeholder: "/web/static/src/img/placeholder.png", + + _renderReadonly: function () { + var self = this; + const url = this.value; + if (url) { + var $img = $(qweb.render("FieldBinaryImage-img", {widget: this, url: url})); + // override css size attributes (could have been defined in css files) + // if specified on the widget + const width = this.nodeOptions.size ? this.nodeOptions.size[0] : this.attrs.width; + const height = this.nodeOptions.size ? this.nodeOptions.size[1] : this.attrs.height; + if (width) { + $img.attr('width', width); + $img.css('max-width', width + 'px'); + } + if (height) { + $img.attr('height', height); + $img.css('max-height', height + 'px'); + } + this.$('> img').remove(); + this.$el.prepend($img); + + $img.one('error', function () { + $img.attr('src', self.placeholder); + self.displayNotification({ + type: 'info', + message: _t("Could not display the specified image url."), + }); + }); + } + + return this._super.apply(this, arguments); + }, +}); + var KanbanFieldBinaryImage = FieldBinaryImage.extend({ // In kanban views, there is a weird logic to determine whether or not a // click on a card should open the record in a form view. This logic checks @@ -1966,6 +2005,17 @@ var KanbanFieldBinaryImage = FieldBinaryImage.extend({ events: {}, }); +var KanbanCharImageUrl = CharImageUrl.extend({ + // In kanban views, there is a weird logic to determine whether or not a + // click on a card should open the record in a form view. This logic checks + // if the clicked element has click handlers bound on it, and if so, does + // not open the record (assuming that the click will be handled by someone + // else). In the case of this widget, there are clicks handler but they + // only apply in edit mode, which is never the case in kanban views, so we + // simply remove them. + events: {}, +}); + var FieldBinaryFile = AbstractFieldBinary.extend({ description: _lt("File"), template: 'FieldBinaryFile', @@ -3539,6 +3589,8 @@ return { AbstractFieldBinary: AbstractFieldBinary, FieldBinaryImage: FieldBinaryImage, KanbanFieldBinaryImage: KanbanFieldBinaryImage, + CharImageUrl: CharImageUrl, + KanbanCharImageUrl: KanbanCharImageUrl, FieldBoolean: FieldBoolean, BooleanToggle: BooleanToggle, FieldChar: FieldChar, diff --git a/addons/web/static/src/js/fields/field_registry.js b/addons/web/static/src/js/fields/field_registry.js index ef174702579..e96ee705b77 100644 --- a/addons/web/static/src/js/fields/field_registry.js +++ b/addons/web/static/src/js/fields/field_registry.js @@ -43,7 +43,9 @@ registry .add('CopyClipboardText', basic_fields.TextCopyClipboard) .add('CopyClipboardChar', basic_fields.CharCopyClipboard) .add('image', basic_fields.FieldBinaryImage) + .add('image_url', basic_fields.CharImageUrl) .add('kanban.image', basic_fields.KanbanFieldBinaryImage) + .add('kanban.image_url', basic_fields.KanbanCharImageUrl) .add('binary', basic_fields.FieldBinaryFile) .add('pdf_viewer', basic_fields.FieldPdfViewer) .add('monetary', basic_fields.FieldMonetary) diff --git a/addons/web/static/tests/fields/basic_fields_tests.js b/addons/web/static/tests/fields/basic_fields_tests.js index 76783a90d31..8debec81b3a 100644 --- a/addons/web/static/tests/fields/basic_fields_tests.js +++ b/addons/web/static/tests/fields/basic_fields_tests.js @@ -24,6 +24,9 @@ var _t = core._t; // Base64 images for testing purpose const MY_IMAGE = 'iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg=='; const PRODUCT_IMAGE = 'R0lGODlhDAAMAKIFAF5LAP/zxAAAANyuAP/gaP///wAAAAAAACH5BAEAAAUALAAAAAAMAAwAAAMlWLPcGjDKFYi9lxKBOaGcF35DhWHamZUW0K4mAbiwWtuf0uxFAgA7'; +const FR_FLAG_URL = '/base/static/img/country_flags/fr.png'; +const EN_FLAG_URL = '/base/static/img/country_flags/gb.png'; + QUnit.module('fields', {}, function () { @@ -2889,6 +2892,131 @@ QUnit.module('basic_fields', { form.destroy(); }); + /** + * Same tests than for Image fields, but for Char fields with image_url widget. + */ + QUnit.module('FieldChar-ImageUrlWidget', { + beforeEach: function () { + // specific sixth partner data for image_url widget tests + this.data.partner.records.push({id: 6, bar: false, foo: FR_FLAG_URL, int_field: 5, qux: 0.0, timmy: []}); + }, + }); + + QUnit.test('image fields are correctly rendered', async function (assert) { + assert.expect(6); + + const form = await createView({ + View: FormView, + model: 'partner', + data: this.data, + arch: '
' + + ' ' + + '', + res_id: 6, + async mockRPC(route, args) { + if (route === FR_FLAG_URL) { + assert.ok(true, "the correct route should have been called."); + } + return this._super.apply(this, arguments); + }, + }); + + assert.hasClass(form.$('div[name="foo"]'), 'o_field_image', + "the widget should have the correct class"); + assert.containsOnce(form, 'div[name="foo"] > img', + "the widget should contain an image"); + assert.hasClass(form.$('div[name="foo"] > img'), 'img-fluid', + "the image should have the correct class"); + assert.hasAttrValue(form.$('div[name="foo"] > img'), 'width', "90", + "the image should correctly set its attributes"); + assert.strictEqual(form.$('div[name="foo"] > img').css('max-width'), "90px", + "the image should correctly set its attributes"); + form.destroy(); + }); + + QUnit.test('image_url widget in subviews are loaded correctly', async function (assert) { + assert.expect(6); + + this.data.partner_type.fields.image = {name: 'image', type: 'char'}; + this.data.partner_type.records[0].image = EN_FLAG_URL; + this.data.partner.records[5].timmy = [12]; + + const form = await createView({ + View: FormView, + model: 'partner', + data: this.data, + arch: '
' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '', + res_id: 6, + async mockRPC(route) { + if (route === FR_FLAG_URL) { + assert.step("The view's image should have been fetched"); + return 'wow'; + } + if (route === EN_FLAG_URL) { + assert.step("The dialog's image should have been fetched"); + return; + } + return this._super.apply(this, arguments); + }, + }); + assert.verifySteps(["The view's image should have been fetched"]); + + assert.containsOnce(form, 'tr.o_data_row', + 'There should be one record in the many2many'); + + // Actual flow: click on an element of the m2m to get its form view + await testUtils.dom.click(form.$('tbody td:contains(gold)')); + assert.strictEqual($('.modal').length, 1, + 'The modal should have opened'); + assert.verifySteps(["The dialog's image should have been fetched"]); + + form.destroy(); + }); + + QUnit.test('image fields in x2many list are loaded correctly', async function (assert) { + assert.expect(2); + + this.data.partner_type.fields.image = {name: 'image', type: 'char'}; + this.data.partner_type.records[0].image = EN_FLAG_URL; + this.data.partner.records[5].timmy = [12]; + + const form = await createView({ + View: FormView, + model: 'partner', + data: this.data, + arch: '
' + + '' + + '' + + '' + + '' + + '' + + '
', + res_id: 6, + async mockRPC(route) { + if (route === EN_FLAG_URL) { + assert.ok(true, "The list's image should have been fetched"); + return; + } + return this._super.apply(this, arguments); + }, + }); + + assert.containsOnce(form, 'tr.o_data_row', + 'There should be one record in the many2many'); + + form.destroy(); + }); + QUnit.module('JournalDashboardGraph', { beforeEach: function () { _.extend(this.data.partner.fields, { From f8b901d04b6c5874b56576f9b89899b2812296f2 Mon Sep 17 00:00:00 2001 From: Victor Feyens Date: Wed, 2 Sep 2020 08:50:45 +0000 Subject: [PATCH 3/5] [IMP] base,web: add image_url widget support in qweb. The new "image_url" widget logic provides the ability to provide static image links in Char fields instead of duplicating the image file in database (+ filestore) through an Image field (& ir_attachment). This commits implements the frontend qweb logic for this widget, following the logic of the existing "image" qweb widget. --- addons/web/models/ir_qweb.py | 56 +++++++++++++++-------- odoo/addons/base/models/ir_qweb_fields.py | 11 +++++ 2 files changed, 48 insertions(+), 19 deletions(-) diff --git a/addons/web/models/ir_qweb.py b/addons/web/models/ir_qweb.py index 875899f7d57..51694a3bc42 100644 --- a/addons/web/models/ir_qweb.py +++ b/addons/web/models/ir_qweb.py @@ -21,20 +21,12 @@ class Image(models.AbstractModel): _description = 'Qweb Field Image' _inherit = 'ir.qweb.field.image' - @api.model - def record_to_html(self, record, field_name, options): - assert options['tagName'] != 'img',\ - "Oddly enough, the root tag of an image field can not be img. " \ - "That is because the image goes into the tag, or it gets the " \ - "hose again." - - if options.get('qweb_img_raw_data', False): - return super(Image, self).record_to_html(record, field_name, options) - - aclasses = ['img', 'img-fluid'] if options.get('qweb_img_responsive', True) else ['img'] - aclasses += options.get('class', '').split() - classes = ' '.join(map(escape, aclasses)) + def _get_src_urls(self, record, field_name, options): + """Considering the rendering options, returns the src and data-zoom-image urls. + :return: src, src_zoom urls + :rtype: tuple + """ max_size = None if options.get('resize'): max_size = options.get('resize') @@ -56,6 +48,30 @@ class Image(models.AbstractModel): src = '/web/image/%s/%s/%s%s/%s?unique=%s' % (record._name, record.id, options.get('preview_image', field_name), max_size, url_quote(filename), sha) + src_zoom = None + if options.get('zoom') and getattr(record, options['zoom'], None): + src_zoom = '/web/image/%s/%s/%s%s/%s?unique=%s' % (record._name, record.id, options['zoom'], max_size, url_quote(filename), sha) + elif options.get('zoom'): + src_zoom = options['zoom'] + + return src, src_zoom + + @api.model + def record_to_html(self, record, field_name, options): + assert options['tagName'] != 'img',\ + "Oddly enough, the root tag of an image field can not be img. " \ + "That is because the image goes into the tag, or it gets the " \ + "hose again." + + if options.get('qweb_img_raw_data', False): + return super(Image, self).record_to_html(record, field_name, options) + + aclasses = ['img', 'img-fluid'] if options.get('qweb_img_responsive', True) else ['img'] + aclasses += options.get('class', '').split() + classes = ' '.join(map(escape, aclasses)) + + src, src_zoom = self._get_src_urls(record, field_name, options) + if options.get('alt-field') and getattr(record, options['alt-field'], None): alt = escape(record[options['alt-field']]) elif options.get('alt'): @@ -63,12 +79,6 @@ class Image(models.AbstractModel): else: alt = escape(record.display_name) - src_zoom = None - if options.get('zoom') and getattr(record, options['zoom'], None): - src_zoom = '/web/image/%s/%s/%s%s/%s?unique=%s' % (record._name, record.id, options['zoom'], max_size, url_quote(filename), sha) - elif options.get('zoom'): - src_zoom = options['zoom'] - itemprop = None if options.get('itemprop'): itemprop = options['itemprop'] @@ -96,3 +106,11 @@ class Image(models.AbstractModel): img.append('/>') return u''.join(img) + +class ImageUrlConverter(models.AbstractModel): + _description = 'Qweb Field Image' + _inherit = 'ir.qweb.field.image_url' + + def _get_src_urls(self, record, field_name, options): + image_url = record[options.get('preview_image', field_name)] + return image_url, options.get("zoom", None) diff --git a/odoo/addons/base/models/ir_qweb_fields.py b/odoo/addons/base/models/ir_qweb_fields.py index 28591cb917c..7db65109aca 100644 --- a/odoo/addons/base/models/ir_qweb_fields.py +++ b/odoo/addons/base/models/ir_qweb_fields.py @@ -385,6 +385,17 @@ class ImageConverter(models.AbstractModel): return u'' % (Image.MIME[image.format], value.decode('ascii')) +class ImageUrlConverter(models.AbstractModel): + """ ``image_url`` widget rendering, inserts an image tag in the + document. + """ + _name = 'ir.qweb.field.image_url' + _description = 'Qweb Field Image' + _inherit = 'ir.qweb.field.image' + + @api.model + def value_to_html(self, value, options): + return u'' % (value) class MonetaryConverter(models.AbstractModel): """ ``monetary`` converter, has a mandatory option From 2f3dcb89472f79503ec5ff78188ecc79a50d55fd Mon Sep 17 00:00:00 2001 From: Victor Feyens Date: Wed, 2 Sep 2020 08:57:39 +0000 Subject: [PATCH 4/5] [IMP] base,website*: do not store country flags. 1) Avoid the storage of all country flags as ir_attachment (230+ ir_attachment in a new db) to reduce the filestore of databases. The country flags are nearly static and not expected to be modified on Odoo instances. This change is based on the new "image_url" widget logic (see previous commits). 2) Extend the flag coverage for countries Add the missing country flags & specify a mapping to provide flags for overseas administrated countries/territories. --- addons/website/models/website_visitor.py | 2 +- addons/website/tests/test_performance.py | 2 - .../website/views/website_visitor_views.xml | 18 +- .../models/event_sponsor.py | 4 +- addons/website_forum/views/website_forum.xml | 2 +- odoo/addons/base/data/res_country_data.xml | 235 ------------------ odoo/addons/base/models/res_country.py | 32 ++- .../base/static/img/country_flags/eh.png | Bin 0 -> 5121 bytes .../base/static/img/country_flags/ps.png | Bin 0 -> 2932 bytes .../base/static/img/country_flags/tw.png | Bin 0 -> 6529 bytes .../base/static/img/country_flags/wf.png | Bin 0 -> 3836 bytes .../base/views/ir_qweb_widget_templates.xml | 4 +- odoo/addons/base/views/res_country_views.xml | 2 +- 13 files changed, 49 insertions(+), 252 deletions(-) create mode 100644 odoo/addons/base/static/img/country_flags/eh.png create mode 100644 odoo/addons/base/static/img/country_flags/ps.png create mode 100644 odoo/addons/base/static/img/country_flags/tw.png create mode 100644 odoo/addons/base/static/img/country_flags/wf.png diff --git a/addons/website/models/website_visitor.py b/addons/website/models/website_visitor.py index ca54d20d883..b4eccd5264e 100644 --- a/addons/website/models/website_visitor.py +++ b/addons/website/models/website_visitor.py @@ -38,7 +38,7 @@ class WebsiteVisitor(models.Model): # localisation and info country_id = fields.Many2one('res.country', 'Country', readonly=True) - country_flag = fields.Binary(related="country_id.image", string="Country Flag") + country_flag = fields.Char(related="country_id.image_url", string="Country Flag") lang_id = fields.Many2one('res.lang', string='Language', help="Language from the website when visitor has been created") timezone = fields.Selection(_tz_get, string='Timezone') email = fields.Char(string='Email', compute='_compute_email_phone') diff --git a/addons/website/tests/test_performance.py b/addons/website/tests/test_performance.py index 466167b436f..48763dc75b9 100644 --- a/addons/website/tests/test_performance.py +++ b/addons/website/tests/test_performance.py @@ -33,8 +33,6 @@ class UtilPerf(HttpCase): class TestStandardPerformance(UtilPerf): def test_10_perf_sql_img_controller(self): self.authenticate('demo', 'demo') - url = '/web/image/res.country/4/image' - self.assertEqual(self._get_url_hot_query(url), 4) url = '/web/image/res.users/2/image_256' self.assertEqual(self._get_url_hot_query(url), 8) diff --git a/addons/website/views/website_visitor_views.xml b/addons/website/views/website_visitor_views.xml index 9ec67f4503f..6a07151fd66 100644 --- a/addons/website/views/website_visitor_views.xml +++ b/addons/website/views/website_visitor_views.xml @@ -82,6 +82,7 @@ + @@ -109,10 +110,10 @@ -
- Country + +
+ Country
@@ -147,7 +148,8 @@ Country + t-att-src="record.country_flag.raw_value" + class="o_country_flag" alt="Country"/>
Active
@@ -196,7 +198,9 @@ -
+
+ +

@@ -227,7 +231,7 @@ - + diff --git a/addons/website_event_track_exhibitor/models/event_sponsor.py b/addons/website_event_track_exhibitor/models/event_sponsor.py index 4f812034e3e..878938f739a 100644 --- a/addons/website_event_track_exhibitor/models/event_sponsor.py +++ b/addons/website_event_track_exhibitor/models/event_sponsor.py @@ -92,11 +92,11 @@ class EventSponsor(models.Model): sponsor.is_in_opening_hours = opening_from <= now_tz < opening_to - @api.depends('partner_id.country_id.image') + @api.depends('partner_id.country_id.image_url') def _compute_country_flag_url(self): for sponsor in self: if sponsor.partner_id.country_id: - sponsor.country_flag_url = self.env['website'].image_url(sponsor.partner_id.country_id, 'image', size=256) + sponsor.country_flag_url = sponsor.partner_id.country_id.image_url else: sponsor.country_flag_url = False diff --git a/addons/website_forum/views/website_forum.xml b/addons/website_forum/views/website_forum.xml index d24ea064a5f..3e8e8aea253 100644 --- a/addons/website_forum/views/website_forum.xml +++ b/addons/website_forum/views/website_forum.xml @@ -1641,7 +1641,7 @@
diff --git a/odoo/addons/base/data/res_country_data.xml b/odoo/addons/base/data/res_country_data.xml index 2f35c017426..d436eb684d2 100644 --- a/odoo/addons/base/data/res_country_data.xml +++ b/odoo/addons/base/data/res_country_data.xml @@ -5,49 +5,42 @@ United Arab Emirates ae - Afghanistan af - Antigua and Barbuda ag - Anguilla ai - Albania al - Armenia am - @@ -55,7 +48,6 @@ Angola ao 0 -
@@ -69,7 +61,6 @@ Argentina ar 1 - CUIT @@ -78,14 +69,12 @@ American Samoa as - Austria at - @@ -95,7 +84,6 @@ Australia au 1 - @@ -103,49 +91,42 @@ Aruba aw - Åland Islands ax - Azerbaijan az - Bosnia and Herzegovina ba - Barbados bb - Bangladesh bd - Belgium be - @@ -154,14 +135,12 @@ Burkina Faso bf - Bulgaria bg - VAT @@ -169,14 +148,12 @@ Bahrain bh - Burundi bi - @@ -184,35 +161,30 @@ Benin bj 0 - Saint Barthélémy bl - Bermuda bm - Brunei Darussalam bn - Bolivia bo - @@ -225,7 +197,6 @@ Brazil br - @@ -233,14 +204,12 @@ Bahamas bs - Bhutan bt - @@ -253,14 +222,12 @@ Botswana bw - Belarus by - @@ -268,7 +235,6 @@ Belize bz 0 - @@ -276,7 +242,6 @@ Canada ca 1 - @@ -285,35 +250,30 @@ Cocos (Keeling) Islands cc - Central African Republic cf - Democratic Republic of the Congo cd - Congo cg - Switzerland ch - @@ -321,35 +281,30 @@ Côte d'Ivoire ci - Cook Islands ck - Chile cl - Cameroon cm - China cn - @@ -358,7 +313,6 @@ Colombia co NIT - @@ -366,42 +320,36 @@ Costa Rica cr - Cuba cu - Cape Verde cv - Curaçao cw - Christmas Island cx - Cyprus cy - VAT @@ -409,7 +357,6 @@ Czech Republic cz - VAT @@ -417,7 +364,6 @@ Germany de - @@ -426,14 +372,12 @@ Djibouti dj - Denmark dk - @@ -442,14 +386,12 @@ Dominica dm - Dominican Republic do - @@ -458,21 +400,18 @@ Algeria dz - Ecuador ec - Estonia ee - VAT @@ -480,7 +419,6 @@ Egypt eg - @@ -493,14 +431,12 @@ Eritrea er - Spain es - @@ -509,14 +445,12 @@ Ethiopia et - Finland fi - @@ -525,35 +459,30 @@ Fiji fj - Falkland Islands fk - Micronesia fm - Faroe Islands fo - France fr - @@ -562,21 +491,18 @@ Gabon ga - Grenada gd - Georgia ge - @@ -589,42 +515,36 @@ Ghana gh - Gibraltar gi - Guernsey gg - Greenland gl - Gambia gm - Guinea gn - @@ -637,14 +557,12 @@ Equatorial Guinea gq - Greece gr - VAT @@ -652,14 +570,12 @@ South Georgia and the South Sandwich Islands gs - Guatemala gt - NIT @@ -667,21 +583,18 @@ Guam gu - Guinea-Bissau gw - Guyana gy - @@ -689,7 +602,6 @@ Hong Kong hk 0 - @@ -702,14 +614,12 @@ Honduras hn - Croatia hr - @@ -718,14 +628,12 @@ Haiti ht - Hungary hu - VAT @@ -734,7 +642,6 @@ Indonesia id 1 - NPWP @@ -743,7 +650,6 @@ Ireland ie 0 - VAT @@ -751,14 +657,12 @@ Israel il - Isle of Man im - @@ -766,7 +670,6 @@ India in 1 - @@ -775,28 +678,24 @@ British Indian Ocean Territory io - Iraq iq - Iran ir - Iceland is - @@ -804,7 +703,6 @@ Italy it 1 - VAT @@ -812,21 +710,18 @@ Jersey je - Jamaica jm - Jordan jo - @@ -834,7 +729,6 @@ Japan jp 1 - after @@ -843,105 +737,90 @@ Kenya ke - Kyrgyzstan kg - Cambodia kh - Kiribati ki - Comoros km - Saint Kitts and Nevis kn - North Korea kp - South Korea kr - Kuwait kw - Cayman Islands ky - Kazakhstan kz - Laos la - Lebanon lb - Saint Lucia lc - Liechtenstein li - @@ -949,28 +828,24 @@ Sri Lanka lk - Liberia lr - Lesotho ls - Lithuania lt - VAT @@ -978,7 +853,6 @@ Luxembourg lu - VAT @@ -986,7 +860,6 @@ Latvia lv - VAT @@ -994,14 +867,12 @@ Libya ly - Morocco ma - @@ -1009,20 +880,17 @@ Monaco mc - Moldova md - Montenegro me - @@ -1035,42 +903,36 @@ Madagascar mg - Marshall Islands mh - North Macedonia mk - Mali ml - Myanmar mm - Mongolia mn - @@ -1078,42 +940,36 @@ Macau mo 0 - Northern Mariana Islands mp - Martinique mq - Mauritania mr - Montserrat ms - Malta mt - VAT @@ -1121,7 +977,6 @@ Mauritius mu - @@ -1129,14 +984,12 @@ Maldives mv - Malawi mw - @@ -1144,7 +997,6 @@ Mexico mx 1 - RFC @@ -1153,63 +1005,54 @@ Malaysia my - Mozambique mz - Namibia na - New Caledonia nc - Niger ne - Norfolk Island nf - Nigeria ng - Nicaragua ni - Netherlands nl - @@ -1218,7 +1061,6 @@ Norway no - @@ -1226,42 +1068,36 @@ Nepal np - Nauru nr - Niue nu - New Zealand nz - Oman om - Panama pa - @@ -1269,42 +1105,36 @@ Peru pe - French Polynesia pf - Papua New Guinea pg - Philippines ph - Pakistan pk - Poland pl - @@ -1313,21 +1143,18 @@ Saint Pierre and Miquelon pm - Pitcairn Islands pn - Puerto Rico pr - @@ -1340,7 +1167,6 @@ Portugal pt - VAT @@ -1348,21 +1174,18 @@ Palau pw - Paraguay py - Qatar qa - @@ -1375,7 +1198,6 @@ Romania ro - VAT @@ -1383,56 +1205,48 @@ Serbia rs - Russian Federation ru - Rwanda rw - Saudi Arabia sa - Solomon Islands sb - Seychelles sc - Sudan sd - Sweden se - VAT @@ -1440,7 +1254,6 @@ Singapore sg - GST No. @@ -1448,14 +1261,12 @@ Saint Helena, Ascension and Tristan da Cunha sh - Slovenia si - VAT @@ -1469,7 +1280,6 @@ Slovakia sk - VAT @@ -1477,161 +1287,138 @@ Sierra Leone sl - San Marino sm - Senegal sn - Somalia so - Suriname sr - South Sudan ss - São Tomé and Príncipe st - El Salvador sv - Sint Maarten (Dutch part) sx - Syria sy - Swaziland sz - Turks and Caicos Islands tc - Chad td - French Southern Territories tf - Togo tg - Thailand th - Tajikistan tj - Tokelau tk - Turkmenistan tm - Tunisia tn - Tonga to - Timor-Leste tl - Turkey tr - @@ -1639,14 +1426,12 @@ Trinidad and Tobago tt - Tuvalu tv - @@ -1659,21 +1444,18 @@ Tanzania tz - Ukraine ua - Uganda ug - @@ -1681,7 +1463,6 @@ United Kingdom gb - VAT @@ -1696,7 +1477,6 @@ United States us 1 - @@ -1705,56 +1485,48 @@ Uruguay uy - Uzbekistan uz - Holy See (Vatican City State) va - Saint Vincent and the Grenadines vc - Venezuela ve - Virgin Islands (British) vg - Virgin Islands (USA) vi - Vietnam vn - @@ -1762,7 +1534,6 @@ Vanuatu vu - @@ -1775,14 +1546,12 @@ Samoa ws - Yemen ye - @@ -1795,28 +1564,24 @@ South Africa za - Zambia zm - Zimbabwe zw - Kosovo xk - diff --git a/odoo/addons/base/models/res_country.py b/odoo/addons/base/models/res_country.py index ad53687d981..dfcf758bdbd 100644 --- a/odoo/addons/base/models/res_country.py +++ b/odoo/addons/base/models/res_country.py @@ -10,6 +10,24 @@ from odoo.tools.translate import _ _logger = logging.getLogger(__name__) +FLAG_MAPPING = { + "GF": "fr", + "BV": "no", + "BQ": "nl", + "GP": "fr", + "HM": "au", + "YT": "fr", + "RE": "fr", + "MF": "fr", + "UM": "us", +} + +NO_FLAG_COUNTRIES = [ + "AQ", #Antarctica + "SJ", #Svalbard + Jan Mayen : separate jurisdictions : no dedicated flag +] + + class Country(models.Model): _name = 'res.country' _description = 'Country' @@ -37,7 +55,10 @@ class Country(models.Model): "(in reports for example), while this field is used to modify the input form for " "addresses.") currency_id = fields.Many2one('res.currency', string='Currency') - image = fields.Binary(attachment=True) + image_url = fields.Char( + compute="_compute_image_url", string="Flag", + help="Url of static flag image", + ) phone_code = fields.Integer(string='Country Calling Code') country_group_ids = fields.Many2many('res.country.group', 'res_country_res_country_group_rel', 'res_country_id', 'res_country_group_id', string='Country Groups') @@ -90,6 +111,15 @@ class Country(models.Model): self.ensure_one() return re.findall(r'\((.+?)\)', self.address_format) + @api.depends('code') + def _compute_image_url(self): + for country in self: + if not country.code or country.code in NO_FLAG_COUNTRIES: + country.image_url = False + else: + code = FLAG_MAPPING.get(country.code, country.code.lower()) + country.image_url = "/base/static/img/country_flags/%s.png" % code + class CountryGroup(models.Model): _description = "Country Group" diff --git a/odoo/addons/base/static/img/country_flags/eh.png b/odoo/addons/base/static/img/country_flags/eh.png new file mode 100644 index 0000000000000000000000000000000000000000..85a25c02920cffd89721f2381d47903fd53d81d0 GIT binary patch literal 5121 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 literal 0 HcmV?d00001 diff --git a/odoo/addons/base/static/img/country_flags/ps.png b/odoo/addons/base/static/img/country_flags/ps.png new file mode 100644 index 0000000000000000000000000000000000000000..1899b5a3d0ac1a3ff3718b19b0c8c16861faa3de GIT binary patch literal 2932 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 literal 0 HcmV?d00001 diff --git a/odoo/addons/base/static/img/country_flags/wf.png b/odoo/addons/base/static/img/country_flags/wf.png new file mode 100644 index 0000000000000000000000000000000000000000..497467d14b01a777eb2cd2f8df1b53bf89352b6b GIT binary patch literal 3836 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 literal 0 HcmV?d00001 diff --git a/odoo/addons/base/views/ir_qweb_widget_templates.xml b/odoo/addons/base/views/ir_qweb_widget_templates.xml index 0671c8a04a3..19dc914372b 100644 --- a/odoo/addons/base/views/ir_qweb_widget_templates.xml +++ b/odoo/addons/base/views/ir_qweb_widget_templates.xml @@ -7,8 +7,8 @@ - - + + diff --git a/odoo/addons/base/views/res_country_views.xml b/odoo/addons/base/views/res_country_views.xml index 02bd9c78d5e..4b9673a797b 100644 --- a/odoo/addons/base/views/res_country_views.xml +++ b/odoo/addons/base/views/res_country_views.xml @@ -23,7 +23,7 @@
- + From 633e2252f3795fd727ab0427b18670d4d73db827 Mon Sep 17 00:00:00 2001 From: Victor Feyens Date: Wed, 2 Sep 2020 10:04:54 +0000 Subject: [PATCH 5/5] [IMP] base: restrict countries creation/deletion The modification of countries data should be restricted to knowledgeable users. Indeed, by modifying, deleting and/or creating res.country records, the resulting behavior generally won't be what you expect, breaking existing flows (delivery, taxes, VAT validation, ...) in an open OR hidden way... By restricting those modifications, we reduce the risks of users breaking their database through 'minor' changes unexpectingly triggering 'heavy' problems. --- odoo/addons/base/security/ir.model.access.csv | 3 ++- odoo/addons/base/views/res_country_views.xml | 4 ++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/odoo/addons/base/security/ir.model.access.csv b/odoo/addons/base/security/ir.model.access.csv index 220116ea7c0..d56f7b55b79 100644 --- a/odoo/addons/base/security/ir.model.access.csv +++ b/odoo/addons/base/security/ir.model.access.csv @@ -43,7 +43,8 @@ "access_res_country_group_all","res_country group_user_all","model_res_country",,1,0,0,0 "access_res_country_state_group_all","res_country_state group_user_all","model_res_country_state",,1,0,0,0 "access_res_country_group_group_all","res_country_group group_user_all","model_res_country_group",,1,0,0,0 -"access_res_country_group_user","res_country group_user","model_res_country","group_partner_manager",1,1,1,1 +"access_res_country_group_user","res_country group_user","model_res_country","group_partner_manager",1,0,0,0 +"access_res_country_group_system","res_country_group_system","model_res_country","group_system",1,1,1,1 "access_res_country_state_group_user","res_country_state group_user","model_res_country_state","group_partner_manager",1,1,1,1 "access_res_country_group_group_user","res_country_group group_user","model_res_country_group","group_partner_manager",1,1,1,1 "access_res_currency_group_all","res_currency group_all","model_res_currency",,1,0,0,0 diff --git a/odoo/addons/base/views/res_country_views.xml b/odoo/addons/base/views/res_country_views.xml index 4b9673a797b..00fd602556b 100644 --- a/odoo/addons/base/views/res_country_views.xml +++ b/odoo/addons/base/views/res_country_views.xml @@ -8,7 +8,7 @@ res.country.tree res.country - + @@ -19,7 +19,7 @@ res.country.form res.country -
+