diff --git a/addons/event/data/mail_template_data.xml b/addons/event/data/mail_template_data.xml index 03d541a588d..392b637d14d 100644 --- a/addons/event/data/mail_template_data.xml +++ b/addons/event/data/mail_template_data.xml @@ -225,7 +225,10 @@
- See location on Google Maps + Google Maps + See location on Google Maps
@@ -442,11 +445,14 @@ -
+
diff --git a/addons/event/models/res_partner.py b/addons/event/models/res_partner.py index f82b58ae502..58a59efc063 100644 --- a/addons/event/models/res_partner.py +++ b/addons/event/models/res_partner.py @@ -1,7 +1,14 @@ # -*- coding: utf-8 -*- # Part of Odoo. See LICENSE file for full copyright and licensing details. -from odoo import fields, models +import base64 +import binascii +import hmac + +import requests +import werkzeug.urls + +from odoo import api, fields, models class ResPartner(models.Model): @@ -9,14 +16,71 @@ class ResPartner(models.Model): event_count = fields.Integer( '# Events', compute='_compute_event_count', groups='event.group_event_registration_desk') + static_map_url = fields.Char(compute="_compute_static_map_url") + static_map_url_is_valid = fields.Boolean(compute="_compute_static_map_url_is_valid") def _compute_event_count(self): self.event_count = 0 for partner in self: partner.event_count = self.env['event.event'].search_count([('registration_ids.partner_id', 'child_of', partner.ids)]) + @api.depends('zip', 'city', 'country_id', 'street') + def _compute_static_map_url(self): + for partner in self: + partner.static_map_url = partner._google_map_signed_img(zoom=13, width=598, height=200) + + def _compute_static_map_url_is_valid(self): + """Compute whether the link is valid. + + This should only remain valid for a relatively short time. + Here, for the duration it is in cache. + """ + session = requests.Session() + for partner in self: + url = self.static_map_url + if not url: + partner.static_map_url_is_valid = False + continue + + is_valid = False + # If the response isn't strictly successful, assume invalid url + try: + res = session.get(url, timeout=2) + if res.ok and not res.headers.get('X-Staticmap-API-Warning'): + is_valid = True + except requests.exceptions.RequestException: + pass + + partner.static_map_url_is_valid = is_valid + def action_event_view(self): action = self.env["ir.actions.actions"]._for_xml_id("event.action_event_view") action['context'] = {} action['domain'] = [('registration_ids.partner_id', 'child_of', self.ids)] return action + + def _google_map_signed_img(self, zoom=13, width=298, height=298): + """Create a signed static image URL for the location of this partner.""" + GOOGLE_MAPS_STATIC_API_KEY = self.env['ir.config_parameter'].sudo().get_param('google_maps.signed_static_api_key') + GOOGLE_MAPS_STATIC_API_SECRET = self.env['ir.config_parameter'].sudo().get_param('google_maps.signed_static_api_secret') + if not GOOGLE_MAPS_STATIC_API_KEY or not GOOGLE_MAPS_STATIC_API_SECRET: + return None + # generate signature as per https://developers.google.com/maps/documentation/maps-static/digital-signature#server-side-signing + location_string = f"{self.street}, {self.city} {self.zip}, {self.country_id and self.country_id.display_name or ''}" + params = { + 'center': location_string, + 'markers': f'size:mid|{location_string}', + 'size': f"{width}x{height}", + 'zoom': zoom, + 'sensor': "false", + 'key': GOOGLE_MAPS_STATIC_API_KEY, + } + unsigned_path = '/maps/api/staticmap?' + werkzeug.urls.url_encode(params) + try: + api_secret_bytes = base64.urlsafe_b64decode(GOOGLE_MAPS_STATIC_API_SECRET + "====") + except binascii.Error: + return None + url_signature_bytes = hmac.digest(api_secret_bytes, unsigned_path.encode(), 'sha1') + params['signature'] = base64.urlsafe_b64encode(url_signature_bytes) + + return 'https://maps.googleapis.com/maps/api/staticmap?' + werkzeug.urls.url_encode(params)