diff options
| author | Pedro M. Baeza <pedro.baeza@gmail.com> | 2018-09-20 13:08:50 +0200 |
|---|---|---|
| committer | Olivier Dony <odo@odoo.com> | 2018-10-03 17:48:14 +0200 |
| commit | 1ad4d42f0b6da1677a5114c0b79a86b7be35577a (patch) | |
| tree | 92f266fc1f196de1c2aa97666ca3dec97954146e | |
| parent | 1be50fdeafcd2b94f7b2f47d6e5bc4b6ebaceadd (diff) | |
[ADD] *: replace hardcoded data URIs with helper
- Add a method for generating an image data URI, and expose it in QWeb context
- Fix reports, website templates or mail templates with data hardcoded
data URIs, to use the helper (Python cases), or the existing
kanban_image helper (for JS cases)
This will gracefully handle SVG support in addition to classical image
formats.
Closes #26635
| -rw-r--r-- | addons/account/views/account_portal_templates.xml | 2 | ||||
| -rw-r--r-- | addons/event/report/event_event_templates.xml | 4 | ||||
| -rw-r--r-- | addons/gamification/data/goal_base.xml | 5 | ||||
| -rw-r--r-- | addons/hr_attendance/report/hr_employee_badge.xml | 4 | ||||
| -rw-r--r-- | addons/l10n_de/report/din5008_report.xml | 2 | ||||
| -rw-r--r-- | addons/project/views/project_portal_templates.xml | 8 | ||||
| -rw-r--r-- | addons/project/views/project_rating_templates.xml | 4 | ||||
| -rw-r--r-- | addons/sale/report/sale_report_templates.xml | 2 | ||||
| -rw-r--r-- | addons/sale/views/sale_portal_templates.xml | 4 | ||||
| -rw-r--r-- | addons/web/views/report_templates.xml | 8 | ||||
| -rw-r--r-- | addons/website_livechat/views/website_livechat.xml | 4 | ||||
| -rw-r--r-- | addons/website_sale/views/product_views.xml | 3 | ||||
| -rw-r--r-- | odoo/addons/base/models/ir_ui_view.py | 2 | ||||
| -rw-r--r-- | odoo/addons/base/views/res_partner_views.xml | 3 | ||||
| -rw-r--r-- | odoo/tools/image.py | 18 |
15 files changed, 47 insertions, 26 deletions
diff --git a/addons/account/views/account_portal_templates.xml b/addons/account/views/account_portal_templates.xml index 07654ee275a..858a7cf58df 100644 --- a/addons/account/views/account_portal_templates.xml +++ b/addons/account/views/account_portal_templates.xml @@ -108,7 +108,7 @@ <div class="small mb-1"><strong class="text-muted">Your Contact</strong></div> <div class="row"> <div class="col flex-grow-0 pr-2"> - <img t-if="invoice.user_id.image" class="rounded-circle mt-1 o_portal_contact_img" t-attf-src="data:image/png;base64,#{invoice.user_id.image}" alt="Contact"/> + <img t-if="invoice.user_id.image" class="rounded-circle mt-1 o_portal_contact_img" t-att-src="image_data_uri(invoice.user_id.image)" alt="Contact"/> <img t-else="" class="rounded-circle mt-1 o_portal_contact_img" src="/web/static/src/img/user_menu_avatar.png" alt="Contact"/> </div> <div class="col pl-0"> diff --git a/addons/event/report/event_event_templates.xml b/addons/event/report/event_event_templates.xml index 23664ec8d94..67ac8267bc7 100644 --- a/addons/event/report/event_event_templates.xml +++ b/addons/event/report/event_event_templates.xml @@ -15,7 +15,7 @@ <div t-field="o.event_id.event_logo"/> </t> <span t-if="not o.event_id.event_logo and o.event_id.organizer_id.company_id.logo_web and o.event_id.organizer_id.is_company"> - <img t-att-src="'data:image/png;base64,%s' % to_text(o.event_id.organizer_id.company_id.logo_web)" style="max-height:1cm; max-width:4cm;" alt="Logo"/> + <img t-att-src="image_data_uri(o.event_id.organizer_id.company_id.logo_web)" style="max-height:1cm; max-width:4cm;" alt="Logo"/> </span> <h5 t-field="o.event_id.name"/> <h5>( <i class="fa fa-clock-o" aria-label="Period" role="img" title="Period"></i> <span itemprop="startDate" t-field="o.event_id.with_context(tz=o.event_id.date_tz).date_begin" t-options='{"hide_seconds": True}'> </span> <i>to</i> <span itemprop="endDate" t-field="o.event_id.with_context(tz=o.event_id.date_tz).date_end" t-options='{"hide_seconds": True}'> </span> )</h5> @@ -65,7 +65,7 @@ <div class="col-12"> <span t-if="event.organizer_id.is_company and event.organizer_id.company_id.logo_web"> <div t-field="event.event_logo"> - <img t-att-src="'data:image/png;base64,%s' % to_text(event.organizer_id.company_id.logo_web)" style="max-height:1cm; max-width:4cm;" alt="Logo"/> + <img t-att-src="image_data_uri(event.organizer_id.company_id.logo_web)" style="max-height:1cm; max-width:4cm;" alt="Logo"/> </div> </span> <h4 t-field="event.name"/> diff --git a/addons/gamification/data/goal_base.xml b/addons/gamification/data/goal_base.xml index 3552be22061..80018820414 100644 --- a/addons/gamification/data/goal_base.xml +++ b/addons/gamification/data/goal_base.xml @@ -142,9 +142,8 @@ Thank you, <div style="height:${heightA}px;"> ${extra_div | safe} <div style="height:55px;"> - % set path = object.env['res.users'].browse(goal['user_id']).partner_id.image_small - <img style="margin-bottom:5px;width:50px;height:50px;border-radius:50%;" src="data:image/jpeg;base64,${path}" alt="${goal['name']}"/> - </div> + <img style="margin-bottom:5px;width:50px;height:50px;border-radius:50%;" t-att-src="image_data_uri(object.env['res.users'].browse(goal['user_id']).partner_id.image_small)" alt="${goal['name']}"/> + </div> <div align="center" style ="color:${bgColor};height:20px"> ${goal['name']} </div> diff --git a/addons/hr_attendance/report/hr_employee_badge.xml b/addons/hr_attendance/report/hr_employee_badge.xml index b121b9c34f3..708a4932cbc 100644 --- a/addons/hr_attendance/report/hr_employee_badge.xml +++ b/addons/hr_attendance/report/hr_employee_badge.xml @@ -20,12 +20,12 @@ <table style="width:77pt; height:150pt"> <tr style="height:30%"> <td align="center" valign="center"> - <img t-if="employee.company_id and employee.company_id.logo" t-att-src="'data:image/png;base64,%s' % to_text(employee.company_id.logo)" style="max-height:45pt;max-width:90%" alt="Company Logo"/> + <img t-if="employee.company_id.logo" t-att-src="image_data_uri(employee.company_id.logo)" style="max-height:45pt;max-width:90%" alt="Company Logo"/> </td> </tr> <tr style="height:70%;"> <td align="center" valign="center"> - <img t-if="employee.image" t-att-src="'data:image/png;base64,%s' % to_text(employee.image)" style="max-height:85pt;max-width:90%" alt="Employee Image"/> + <img t-if="employee.image" t-att-src="image_data_uri(employee.image)" style="max-height:85pt;max-width:90%" alt="Employee Image"/> </td> </tr> </table> diff --git a/addons/l10n_de/report/din5008_report.xml b/addons/l10n_de/report/din5008_report.xml index ffd7ea97f83..84c03e9ccd8 100644 --- a/addons/l10n_de/report/din5008_report.xml +++ b/addons/l10n_de/report/din5008_report.xml @@ -39,7 +39,7 @@ <div class="float-right"> <h3 class="mt0 text-right" t-field="company.report_header"/> </div> - <img t-if="company.logo" t-att-src="'data:image/png;base64,%s' % to_text(company.logo)" class="float-left"/> + <img t-if="company.logo" t-att-src="image_data_uri(company.logo)" class="float-left"/> </div> <div class="article din" t-att-data-oe-model="o and o._name" t-att-data-oe-id="o and o.id"> diff --git a/addons/project/views/project_portal_templates.xml b/addons/project/views/project_portal_templates.xml index a093a741378..db82de63fe8 100644 --- a/addons/project/views/project_portal_templates.xml +++ b/addons/project/views/project_portal_templates.xml @@ -91,7 +91,7 @@ <h6>Customer</h6> <div class="row"> <div class="col flex-grow-0 pr-3"> - <img t-if="project.partner_id.image" class="rounded-circle mt-1 o_portal_contact_img" t-attf-src="data:image/png;base64,#{project.partner_id.image}" alt="Contact"/> + <img t-if="project.partner_id.image" class="rounded-circle mt-1 o_portal_contact_img" t-att-src="image_data_uri(project.partner_id.image)" alt="Contact"/> <img t-else="" class="rounded-circle mt-1 o_portal_contact_img" src="/web/static/src/img/user_menu_avatar.png" alt="Contact"/> </div> <div class="col pl-sm-0"> @@ -103,7 +103,7 @@ <h6>Project Manager</h6> <div class="row"> <div class="col flex-grow-0 pr-3"> - <img t-if="project.user_id.image" class="rounded-circle mt-1 o_portal_contact_img" t-attf-src="data:image/png;base64,#{project.user_id.image}" alt="Contact"/> + <img t-if="project.user_id.image" class="rounded-circle mt-1 o_portal_contact_img" t-att-src="image_data_uri(project.user_id.image)" alt="Contact"/> <img t-else="" class="rounded-circle mt-1 o_portal_contact_img" src="/web/static/src/img/user_menu_avatar.png" alt="Contact"/> </div> <div class="col pl-sm-0"> @@ -205,7 +205,7 @@ <strong>Assigned to</strong> <div class="row"> <div class="col flex-grow-0 pr-3"> - <img t-if="task.user_id.image" class="rounded-circle mt-1 o_portal_contact_img" t-attf-src="data:image/png;base64,#{task.user_id.image}" alt="Contact"/> + <img t-if="task.user_id.image" class="rounded-circle mt-1 o_portal_contact_img" t-att-src="image_data_uri(task.user_id.image)" alt="Contact"/> <img t-else="" class="rounded-circle mt-1 o_portal_contact_img" src="/web/static/src/img/user_menu_avatar.png" alt="Contact"/> </div> <div class="col pl-md-0"> @@ -217,7 +217,7 @@ <strong>Reported by</strong> <div class="row"> <div class="col flex-grow-0 pr-3"> - <img t-if="task.partner_id.image" class="rounded-circle mt-1 o_portal_contact_img" t-attf-src="data:image/png;base64,#{task.partner_id.image}" alt="Contact"/> + <img t-if="task.partner_id.image" class="rounded-circle mt-1 o_portal_contact_img" t-att-src="image_data_uri(task.partner_id.image)" alt="Contact"/> <img t-else="" class="rounded-circle mt-1 o_portal_contact_img" src="/web/static/src/img/user_menu_avatar.png" alt="Contact"/> </div> <div class="col pl-md-0"> diff --git a/addons/project/views/project_rating_templates.xml b/addons/project/views/project_rating_templates.xml index acc17cef767..0bed3db0219 100644 --- a/addons/project/views/project_rating_templates.xml +++ b/addons/project/views/project_rating_templates.xml @@ -77,7 +77,7 @@ <t t-foreach="partner_stats.values()" t-as="partner_stat"> <div class="row mt8" t-if="partner_stat['total']" title="Top 5 partner ratings of last 15 days."> <div class="col-md-1"> - <img t-if="partner_stat['rated_partner'].image" class="o_top_partner_image" t-attf-src="data:image/png;base64,#{partner_stat['rated_partner'].image}" alt="Rated partner"/> + <img t-if="partner_stat['rated_partner'].image" class="o_top_partner_image" t-att-src="image_data_uri(partner_stat['rated_partner'].image)" alt="Rated partner"/> <img t-if="not partner_stat['rated_partner'].image" class="o_top_partner_image" src='/web/static/src/img/placeholder.png' alt="Placeholder"/> </div> <div class="col-md-4 o_smiley_no_padding_right"> @@ -126,7 +126,7 @@ <div t-attf-id="rating_#{rating.id}" class="container d-none"> <div class="row"> <div class="col-lg-2"> - <img t-if="rating.partner_id.image" class="o_top_partner_image" t-attf-src="data:image/png;base64,#{rating.partner_id.image}" alt="Rated partner"/> + <img t-if="rating.partner_id.image" class="o_top_partner_image" t-att-src="image_data_uri(rating.partner_id.image)" alt="Rated partner"/> <img t-if="not rating.partner_id.image" class="o_top_partner_image" src='/web/static/src/img/placeholder.png' alt="Placeholder"/> </div> <div class="col-lg-10"> diff --git a/addons/sale/report/sale_report_templates.xml b/addons/sale/report/sale_report_templates.xml index 9a7647180d3..cf9019d7e3b 100644 --- a/addons/sale/report/sale_report_templates.xml +++ b/addons/sale/report/sale_report_templates.xml @@ -202,7 +202,7 @@ <strong>Signature</strong> </div> <div class="offset-8 col-4"> - <img t-att-src="'data:image/png;base64,%s' % to_text(doc.signature)" style="max-height: 4cm; max-width: 8cm;"/> + <img t-att-src="image_data_uri(doc.signature)" style="max-height: 4cm; max-width: 8cm;"/> </div> <div class="offset-8 col-4 text-center"> <p t-field="doc.signed_by"/> diff --git a/addons/sale/views/sale_portal_templates.xml b/addons/sale/views/sale_portal_templates.xml index ca1fde9bc49..1466714587b 100644 --- a/addons/sale/views/sale_portal_templates.xml +++ b/addons/sale/views/sale_portal_templates.xml @@ -183,7 +183,7 @@ <div class="small mb-1"><strong class="text-muted">Your Contact</strong></div> <div class="row flex-nowrap"> <div class="col flex-grow-0 pr-2"> - <img class="rounded-circle mr4 float-left o_portal_contact_img" t-if="sale_order.user_id.image" t-attf-src="data:image/png;base64,#{sale_order.user_id.image}" alt="Contact"/> + <img class="rounded-circle mr4 float-left o_portal_contact_img" t-if="sale_order.user_id.image" t-att-src="image_data_uri(sale_order.user_id.image)" alt="Contact"/> <img class="rounded-circle mr4 float-left o_portal_contact_img" t-if="not sale_order.user_id.image" src="/web/static/src/img/placeholder.png" alt="Contact"/> </div> <div class="col pl-0" style="min-width: 150px"> @@ -579,7 +579,7 @@ <div class="row mt-4" name="signature"> <div t-attf-class="#{'col-3' if report_type != 'html' else 'col-sm-7 col-md-4'} ml-auto text-center"> <h5>Signature</h5> - <img t-att-src="'data:image/png;base64,%s' % to_text(sale_order.signature)" style="max-height: 6rem; max-width: 100%;"/> + <img t-att-src="image_data_uri(sale_order.signature)" style="max-height: 6rem; max-width: 100%;"/> <p t-field="sale_order.signed_by"/> </div> </div> diff --git a/addons/web/views/report_templates.xml b/addons/web/views/report_templates.xml index a410aad17a0..3250167b54b 100644 --- a/addons/web/views/report_templates.xml +++ b/addons/web/views/report_templates.xml @@ -168,7 +168,7 @@ <div class="float-right"> <h3 class="mt0 text-right" t-field="company.report_header"/> </div> - <img t-if="company.logo" t-att-src="'data:image/png;base64,%s' % to_text(company.logo)" class="float-left" alt="Logo"/> + <img t-if="company.logo" t-att-src="image_data_uri(company.logo)" class="float-left" alt="Logo"/> <div class="float-left company_address"> <div> <strong t-field="company.partner_id.name"/> @@ -209,7 +209,7 @@ <div class="o_boxed_header"> <div class="row mb8"> <div class="col-6"> - <img t-if="company.logo" t-att-src="'data:image/png;base64,%s' % to_text(company.logo)" alt="Logo"/> + <img t-if="company.logo" t-att-src="image_data_uri(company.logo)" alt="Logo"/> </div> <div class="col-6 text-right mb4"> <h4 class="mt0" t-field="company.report_header"/> @@ -248,7 +248,7 @@ <div class="o_clean_header"> <div class="row"> <div class="col-6"> - <img t-if="company.logo" t-att-src="'data:image/png;base64,%s' % to_text(company.logo)" alt="Logo"/> + <img t-if="company.logo" t-att-src="image_data_uri(company.logo)" alt="Logo"/> </div> <div class="col-5 offset-1" name="company_address"> <ul class="list-unstyled"> @@ -293,7 +293,7 @@ <div class="header"> <div class="row"> <div class="col-3 mb4"> - <img t-if="company.logo" t-att-src="'data:image/png;base64,%s' % to_text(company.logo)" style="max-height: 45px;" alt="Logo"/> + <img t-if="company.logo" t-att-src="image_data_uri(company.logo)" style="max-height: 45px;" alt="Logo"/> </div> <div class="col-9 text-right" style="margin-top:22px;" t-field="company.report_header" name="moto"/> </div> diff --git a/addons/website_livechat/views/website_livechat.xml b/addons/website_livechat/views/website_livechat.xml index a7673243222..6afcda0cd0e 100644 --- a/addons/website_livechat/views/website_livechat.xml +++ b/addons/website_livechat/views/website_livechat.xml @@ -147,7 +147,7 @@ <t t-foreach="team" t-as="user"> <div class="media mt-3"> <t t-if="user.image_small"> - <img t-att-src="user.image_small and ('data:image/png;base64,' + to_text(user.image_small)) or '/web/static/src/img/placeholder.png'" + <img t-att-src="user.image_small and image_data_uri(user.image_small) or '/web/static/src/img/placeholder.png'" class="rounded" style="padding-right: 8px;" t-att-alt="user.name"/> </t> <div class="media-body"> @@ -180,7 +180,7 @@ <t t-foreach="channels" t-as="channel"> <div t-attf-class="media#{' mt-3' if channel_index else ''}"> <a t-attf-href="/livechat/channel/#{ slug(channel)}"> - <img t-att-src="channel.image_small and ('data:image/png;base64,' + to_text(channel.image_small)) or '/web/static/src/img/placeholder.png'" t-att-alt="channel.name" /> + <img t-att-src="channel.image_small and image_data_uri(channel.image_small) or '/web/static/src/img/placeholder.png'" t-att-alt="channel.name" /> </a> <div class="media-body"> <h4><t t-esc="channel.name"/></h4> diff --git a/addons/website_sale/views/product_views.xml b/addons/website_sale/views/product_views.xml index 72452ce93b7..3f541d31943 100644 --- a/addons/website_sale/views/product_views.xml +++ b/addons/website_sale/views/product_views.xml @@ -88,6 +88,7 @@ <group name="extra_images" string="Extra Images"> <field name="product_image_ids" class="o_website_sale_image_list" context="{'default_name': name, 'default_product_tmpl_id': active_id}" nolabel="1"> <kanban string="Product Images" class="o_attachments_previews"> + <field name="id"/> <field name="name"/> <field name="image"/> <templates> @@ -95,7 +96,7 @@ <div class="o_attachment oe_kanban_global_click o_website_sale_image_card"> <div class="o_attachment_wrap"> <div class="o_image_box"> - <div class="o_attachment_image" t-att-style="'background-image:url(data:image/png;base64,'+record.image.raw_value +')'"/> + <div class="o_attachment_image" t-attf-style="background-image:url({{kanban_image('product.image', 'image', record.id.raw_value)}});"/> <div t-attf-class="o_image_overlay" t-att-title="record.name.value"/> <div class="o_website_sale_image_card_title" t-att-title="record.name.value"> <field name="name"/> diff --git a/odoo/addons/base/models/ir_ui_view.py b/odoo/addons/base/models/ir_ui_view.py index 96e0593cbc1..407bec75af1 100644 --- a/odoo/addons/base/models/ir_ui_view.py +++ b/odoo/addons/base/models/ir_ui_view.py @@ -33,6 +33,7 @@ from odoo.tools.parse_version import parse_version from odoo.tools.safe_eval import safe_eval from odoo.tools.view_validation import valid_view from odoo.tools.translate import xml_translate, TRANSLATED_ATTRS +from odoo.tools.image import image_data_uri _logger = logging.getLogger(__name__) @@ -1296,6 +1297,7 @@ actual arch. xmlid=self.key, viewid=self.id, to_text=pycompat.to_text, + image_data_uri=image_data_uri, ) return qcontext diff --git a/odoo/addons/base/views/res_partner_views.xml b/odoo/addons/base/views/res_partner_views.xml index a6ba287dcff..9a86939782b 100644 --- a/odoo/addons/base/views/res_partner_views.xml +++ b/odoo/addons/base/views/res_partner_views.xml @@ -270,6 +270,7 @@ <page string="Contacts & Addresses" autofocus="autofocus"> <field name="child_ids" mode="kanban" context="{'default_parent_id': active_id, 'default_street': street, 'default_street2': street2, 'default_city': city, 'default_state_id': state_id, 'default_zip': zip, 'default_country_id': country_id, 'default_supplier': supplier, 'default_customer': customer, 'default_lang': lang, 'default_user_id': user_id}"> <kanban> + <field name="id"/> <field name="color"/> <field name="name"/> <field name="title"/> @@ -298,7 +299,7 @@ <t t-set="color" t-value="kanban_color(record.color.raw_value)"/> <div t-att-class="color + (record.title.raw_value == 1 ? ' oe_kanban_color_alert' : '') + ' oe_kanban_global_click'"> <div class="o_kanban_image"> - <img alt="" t-if="record.image_small.raw_value" t-att-src="'data:image/png;base64,'+record.image_small.raw_value"/> + <img alt="" t-if="record.image_small.raw_value" t-att-src="kanban_image('res.partner', 'image_small', record.id.raw_value)"/> <t t-if="!record.image_small.raw_value"> <img alt="Delivery" t-if="record.type.raw_value === 'delivery'" t-att-src='_s + "/base/static/img/truck.png"'/> <img alt="Invoice" t-if="record.type.raw_value === 'invoice'" t-att-src='_s + "/base/static/img/money.png"'/> diff --git a/odoo/tools/image.py b/odoo/tools/image.py index 24aa56ced17..2814aaaaa3f 100644 --- a/odoo/tools/image.py +++ b/odoo/tools/image.py @@ -14,6 +14,15 @@ from odoo.tools import pycompat Image.preinit() Image._initialized = 2 +# Maps only the 6 first bits of the base64 data, accurate enough +# for our purpose and faster than decoding the full blob first +FILETYPE_BASE64_MAGICWORD = { + b'/': 'jpg', + b'R': 'gif', + b'i': 'png', + b'P': 'svg+xml', +} + # ---------------------------------------- # Image resizing # ---------------------------------------- @@ -312,6 +321,15 @@ def image_resize_images(vals, big_name='image', medium_name='image_medium', smal elif big_name in vals or medium_name in vals or small_name in vals: vals[big_name] = vals[medium_name] = vals[small_name] = False +def image_data_uri(base64_source): + """This returns data URL scheme according RFC 2397 + (https://tools.ietf.org/html/rfc2397) for all kind of supported images + (PNG, GIF, JPG and SVG), defaulting on PNG type if not mimetype detected. + """ + return 'data:image/%s;base64,%s' % ( + FILETYPE_BASE64_MAGICWORD.get(base64_source[:1], 'png'), + base64_source.decode(), + ) if __name__=="__main__": import sys |
