Browse Source

[FIX] privacy_consent: make it work, basically

Some little nasty details were overlooked in the v12 migration, which rendered the module basically useless:

- The wizard used to ask for consent in manual activities did not fill the placeholders, resulting in an awkward UX.
- The sent mails **did not have the token** to authenticate and actually allow accepting or rejecting. These buttons returned a 500 error.
- Once the token is added, the form was ugly.
- Sadly, some tests were testing the how and not the what. It is understandable due to the complexity of testing the what, even more without the `Form` utility, new on v12. These are fixed now too.

@Tecnativa TT25868
pull/50/head
Jairo Llopis 4 years ago
parent
commit
0ebde567f2
No known key found for this signature in database GPG Key ID: 8B8A6900E4831A9B
  1. 1
      privacy_consent/__manifest__.py
  2. 4
      privacy_consent/models/mail_mail.py
  3. 2
      privacy_consent/models/privacy_consent.py
  4. 7
      privacy_consent/static/src/css/privacy_consent.scss
  5. 13
      privacy_consent/templates/assets.xml
  6. 1
      privacy_consent/templates/form.xml
  7. 66
      privacy_consent/tests/test_consent.py

1
privacy_consent/__manifest__.py

@ -20,6 +20,7 @@
"data/ir_actions_server.xml",
"data/ir_cron.xml",
"data/mail.xml",
"templates/assets.xml",
"templates/form.xml",
"views/privacy_consent.xml",
"views/privacy_activity.xml",

4
privacy_consent/models/mail_mail.py

@ -37,7 +37,7 @@ class MailMail(models.Model):
failure_type=failure_type,
)
def send_get_mail_body(self, partner=None):
def _send_prepare_body(self):
"""Replace privacy consent magic links.
This replacement is done here instead of directly writing it into
@ -46,7 +46,7 @@ class MailMail(models.Model):
which would enable any reader of such thread to impersonate the
subject and choose in its behalf.
"""
result = super(MailMail, self).send_get_mail_body(partner=partner)
result = super(MailMail, self)._send_prepare_body()
# Avoid polluting other model mails
if self.model != "privacy.consent":
return result

2
privacy_consent/models/privacy_consent.py

@ -150,7 +150,7 @@ class PrivacyConsent(models.Model):
"""Let user manually ask for consent."""
return {
"context": {
"default_composition_mode": "mass_mail",
"default_composition_mode": "comment",
"default_model": self._name,
"default_res_id": self.id,
"default_template_id": self.activity_id.consent_template_id.id,

7
privacy_consent/static/src/css/privacy_consent.scss

@ -0,0 +1,7 @@
/* Copyright 2020 Tecnativa - Jairo Llopis
License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). */
.o_consent_form {
// Need !important to override an inline style of max-width: 300px
max-width: 100% !important;
}

13
privacy_consent/templates/assets.xml

@ -0,0 +1,13 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright 2020 Tecnativa - Jairo Llopis
License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). -->
<data>
<template id="assets_frontend" inherit_id="web.assets_frontend">
<xpath expr=".">
<link rel="stylesheet" href="/privacy_consent/static/src/css/privacy_consent.scss" />
</xpath>
</template>
</data>

1
privacy_consent/templates/form.xml

@ -9,6 +9,7 @@
by website layout if website is installed, and otherwise includes
all possibly needed assets -->
<t t-call="web.login_layout">
<t t-set="login_card_classes" t-value="'o_consent_form'" />
<div class="container readable">
<div class="jumbotron">
<h1>Thank you!</h1>

66
privacy_consent/tests/test_consent.py

@ -1,8 +1,10 @@
# Copyright 2018 Tecnativa - Jairo Llopis
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
from contextlib import contextmanager
from odoo.exceptions import ValidationError
from odoo.tests.common import HttpCase
from odoo.tests.common import HttpCase, Form
class ActivityCase(HttpCase):
@ -70,6 +72,37 @@ class ActivityCase(HttpCase):
"server_action_id": self.sync_blacklist.id,
})
@contextmanager
def _patch_build(self):
self._built_messages = []
IMS = self.env['ir.mail_server']
def _build_email(
_self,
email_from,
email_to,
subject,
body,
*args,
**kwargs
):
self._built_messages.append(body)
return _build_email.origin(
_self,
email_from,
email_to,
subject,
body,
*args,
**kwargs,
)
try:
IMS._patch_method('build_email', _build_email)
yield
finally:
IMS._revert_method('build_email')
def check_activity_auto_properly_sent(self):
"""Check emails sent by ``self.activity_auto``."""
consents = self.env["privacy.consent"].search([
@ -81,8 +114,9 @@ class ActivityCase(HttpCase):
messages = consent.message_ids
self.assertEqual(len(messages), 2)
# Check sent mails
with self._patch_build():
self.cron_mail_queue.method_direct_trigger()
for consent in consents:
for index, consent in enumerate(consents):
good_email = "@" in (consent.partner_id.email or "")
expected_messages = 3 if good_email else 2
self.assertEqual(
@ -119,6 +153,14 @@ class ActivityCase(HttpCase):
)
# Partner's is_blacklisted should be synced with default consent
self.assertFalse(consent.partner_id.is_blacklisted)
# Check the sent message was built properly tokenized
accept_url, reject_url = map(consent._url, (True, False))
for body in self._built_messages:
if accept_url in body and reject_url in body:
self._built_messages.remove(body)
break
else:
raise AssertionError("Some message body should have these urls")
def test_default_template(self):
"""We have a good mail template by default."""
@ -182,19 +224,27 @@ class ActivityCase(HttpCase):
# Send one manual request
action = consents[0].action_manual_ask()
self.assertEqual(action["res_model"], "mail.compose.message")
composer = self.env[action["res_model"]] \
.with_context(active_ids=consents[0].ids,
Composer = self.env[action["res_model"]].with_context(
active_ids=consents[0].ids,
active_model=consents._name,
**action["context"]).create({})
composer.onchange_template_id_wrapper()
composer.send_mail()
**action["context"],
)
composer_wizard = Form(Composer)
self.assertIn(consents[0].partner_id.name, composer_wizard.body)
composer_record = composer_wizard.save()
with self._patch_build():
composer_record.send_mail()
# Check the sent message was built properly tokenized
body = self._built_messages[0]
self.assertIn(consents[0]._url(True), body)
self.assertIn(consents[0]._url(False), body)
messages = consents.mapped("message_ids") - messages
self.assertEqual(len(messages), 2)
self.assertEqual(messages[0].subtype_id, self.mt_consent_state_changed)
self.assertEqual(consents.mapped("state"), ["sent", "draft", "draft"])
self.assertEqual(
consents.mapped("partner_id.is_blacklisted"),
[False, False, False],
[True, False, False],
)
# Placeholder links should be logged
self.assertTrue("/privacy/consent/accept/" in messages[1].body)

Loading…
Cancel
Save