mail_render_mixin.py 3.8 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586
  1. # -*- coding: utf-8 -*-
  2. # Part of Odoo. See LICENSE file for full copyright and licensing details.
  3. import re
  4. import markupsafe
  5. from html import unescape
  6. from werkzeug import urls
  7. from odoo import api, models, tools
  8. class MailRenderMixin(models.AbstractModel):
  9. _inherit = "mail.render.mixin"
  10. # ------------------------------------------------------------
  11. # TOOLS
  12. # ------------------------------------------------------------
  13. @api.model
  14. def _shorten_links(self, html, link_tracker_vals, blacklist=None, base_url=None):
  15. """ Shorten links in an html content. It uses the '/r' short URL routing
  16. introduced in this module. Using the standard Odoo regex local links are
  17. found and replaced by global URLs (not including mailto, tel, sms).
  18. TDE FIXME: could be great to have a record to enable website-based URLs
  19. :param link_tracker_vals: values given to the created link.tracker, containing
  20. for example: campaign_id, medium_id, source_id, and any other relevant fields
  21. like mass_mailing_id in mass_mailing;
  22. :param list blacklist: list of (local) URLs to not shorten (e.g.
  23. '/unsubscribe_from_list')
  24. :param str base_url: either given, either based on config parameter
  25. :return: updated html
  26. """
  27. base_url = base_url or self.env['ir.config_parameter'].sudo().get_param('web.base.url')
  28. short_schema = base_url + '/r/'
  29. for match in set(re.findall(tools.HTML_TAG_URL_REGEX, html)):
  30. long_url = match[1]
  31. # Don't shorten already-shortened links
  32. if long_url.startswith(short_schema):
  33. continue
  34. # Don't shorten urls present in blacklist (aka to skip list)
  35. if blacklist and any(s in long_url for s in blacklist):
  36. continue
  37. label = (match[3] or '').strip()
  38. create_vals = dict(link_tracker_vals, url=unescape(long_url), label=unescape(label))
  39. link = self.env['link.tracker'].search_or_create(create_vals)
  40. if link.short_url:
  41. # `str` manipulation required to support replacing "&" characters, common in urls
  42. new_href = match[0].replace(long_url, link.short_url)
  43. html = html.replace(markupsafe.Markup(match[0]), markupsafe.Markup(new_href))
  44. return html
  45. @api.model
  46. def _shorten_links_text(self, content, link_tracker_vals, blacklist=None, base_url=None):
  47. """ Shorten links in a string content. Works like ``_shorten_links`` but
  48. targeting string content, not html.
  49. :return: updated content
  50. """
  51. if not content:
  52. return content
  53. base_url = base_url or self.env['ir.config_parameter'].sudo().get_param('web.base.url')
  54. shortened_schema = base_url + '/r/'
  55. unsubscribe_schema = base_url + '/sms/'
  56. for original_url in set(re.findall(tools.TEXT_URL_REGEX, content)):
  57. # don't shorten already-shortened links or links towards unsubscribe page
  58. if original_url.startswith(shortened_schema) or original_url.startswith(unsubscribe_schema):
  59. continue
  60. # support blacklist items in path, like /u/
  61. parsed = urls.url_parse(original_url, scheme='http')
  62. if blacklist and any(item in parsed.path for item in blacklist):
  63. continue
  64. create_vals = dict(link_tracker_vals, url=unescape(original_url))
  65. link = self.env['link.tracker'].search_or_create(create_vals)
  66. if link.short_url:
  67. # Ensures we only replace the same link and not a subpart of a longer one, multiple times if applicable
  68. content = re.sub(re.escape(original_url) + r'(?![\w@:%.+&~#=/-])', link.short_url, content)
  69. return content