Repository: OCA/social

Issue:
[14.0] mail_debrand: TypeError: cannot use a string pattern on a bytes-like object
This error occurs in the app email marketing(mass_mailing) by clicking on "Test" in the Mailings tab

 File "/opt/odoo/odoo/addons/base/models/ir_http.py", line 237, in _dispatch
    result = request.dispatch()
  File "/opt/odoo/odoo/http.py", line 683, in dispatch
    result = self._call_function(**self.params)
  File "/opt/odoo/odoo/http.py", line 359, in _call_function
    return checked_call(self.db, *args, **kwargs)
  File "/opt/odoo/odoo/service/model.py", line 94, in wrapper
    return f(dbname, *args, **kwargs)
  File "/opt/odoo/odoo/http.py", line 347, in checked_call
    result = self.endpoint(*a, **kw)
  File "/opt/odoo/odoo/http.py", line 912, in __call__
    return self.method(*args, **kw)
  File "/opt/odoo/odoo/http.py", line 531, in response_wrap
    response = f(*args, **kw)
  File "/opt/odoo/odoo/addons/web/controllers/main.py", line 1368, in call_button
    action = self._call_kw(model, method, args, kwargs)
  File "/opt/odoo/odoo/addons/web/controllers/main.py", line 1356, in _call_kw
    return call_kw(request.env[model], method, args, kwargs)
  File "/opt/odoo/odoo/api.py", line 396, in call_kw
    result = _call_kw_multi(method, model, args, kwargs)
  File "/opt/odoo/odoo/api.py", line 383, in _call_kw_multi
    result = method(recs, *args, **kwargs)
  File "/opt/odoo/odoo/addons/mass_mailing/wizard/mailing_mailing_test.py", line 54, in send_mail_test
    mail = self.env['mail.mail'].sudo().create(mail_values)
  File "<decorator-gen-210>", line 2, in create
  File "/opt/odoo/odoo/api.py", line 344, in _model_create_multi
    return create(self, [arg])
  File "/opt/odoo/odoo/addons/mass_mailing/models/mail_mail.py", line 21, in create
    mails = super(MailMail, self).create(values_list)
  File "<decorator-gen-155>", line 2, in create
  File "/opt/odoo/odoo/api.py", line 345, in _model_create_multi
    return create(self, arg)
  File "/opt/odoo/custom-addons/mail_debrand/models/mail_render_mixinANDmail_mail.py", line 98, in create
    values_list[index]["body_html"] = self.env[
  File "/opt/odoo/custom-addons/mail_debrand/models/mail_render_mixinANDmail_mail.py", line 18, in remove_href_odoo
    has_odoo_link = re.search(r"<a\s(.*)odoo\.com", value, flags=re.IGNORECASE)
  File "/usr/lib/python3.8/re.py", line 201, in search
    return _compile(pattern, flags).search(string)
Exception
 
The above exception was the direct cause of the following exception:
 
Traceback (most recent call last):
  File "/opt/odoo/odoo/http.py", line 639, in _handle_exception
    return super(JsonRequest, self)._handle_exception(exception)
  File "/opt/odoo/odoo/http.py", line 315, in _handle_exception
    raise exception.with_traceback(None) from new_cause
TypeError: cannot use a string pattern on a bytes-like object


Discussion:

Resolving patch:
diff --git a/mail_debrand/models/mail_render_mixinANDmail_mail.py b/mail_debrand/models/mail_render_mixinANDmail_mail.py
--- a/mail_debrand/models/mail_render_mixinANDmail_mail.py
+++ b/mail_debrand/models/mail_render_mixinANDmail_mail.py
@@ -15,11 +15,14 @@ class MailRenderMixin(models.AbstractModel):
     def remove_href_odoo(self, value, remove_parent=True, remove_before=False):
         if len(value) < 20:
             return value
+        # value can be bytes type; ensure we get a proper string
+        if type(value) is bytes:
+            value = value.decode()
         has_odoo_link = re.search(r"<a\s(.*)odoo\.com", value, flags=re.IGNORECASE)
         if has_odoo_link:
             tree = etree.HTML(
                 value
-            )  # html with brlken links   tree = etree.fromstring(value) just xml
+            )  # html with broken links   tree = etree.fromstring(value) just xml
             odoo_achors = tree.xpath('//a[contains(@href,"odoo.com")]')
             for elem in odoo_achors:
                 parent = elem.getparent()
@@ -33,7 +36,7 @@ def remove_href_odoo(self, value, remove_parent=True, remove_before=False):
                     only_what_is_in_tags = bytes_text[: bytes_text.rfind(b">") + 1]
                     data_formatted = html.fromstring(only_what_is_in_tags)
                     parent.replace(previous, data_formatted)
-                if len(parent.getparent()) and remove_parent:
+                if remove_parent and len(parent.getparent()):
                     # anchor <a href odoo has a parent powered by that must be removed
                     parent.getparent().remove(parent)
                 else:


Test patch:
diff --git a/mail_debrand/tests/test_mail_debrand.py b/mail_debrand/tests/test_mail_debrand.py
--- a/mail_debrand/tests/test_mail_debrand.py
+++ b/mail_debrand/tests/test_mail_debrand.py
@@ -13,6 +13,17 @@ def setUp(self):
         self.default_template = self.env.ref("mail.message_notification_email")
         self.paynow_template = self.env.ref("mail.mail_notification_paynow")
 
+    def test_debrand_binary_value(self):
+        """
+        Regression test: ensure binary input is gracefully handled
+        """
+        try:
+            self.env["mail.template"].remove_href_odoo(
+                b"Binary value with more than 20 characters"
+            )
+        except TypeError:
+            self.fail("Debranding binary string raised TypeError")
+
     def test_default_debrand(self):
         self.assertIn("using", self.default_template.arch)
         res = self.env["mail.template"]._render_template(