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_actions_server.xml",
"data/ir_cron.xml", "data/ir_cron.xml",
"data/mail.xml", "data/mail.xml",
"templates/assets.xml",
"templates/form.xml", "templates/form.xml",
"views/privacy_consent.xml", "views/privacy_consent.xml",
"views/privacy_activity.xml", "views/privacy_activity.xml",

4
privacy_consent/models/mail_mail.py

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

2
privacy_consent/models/privacy_consent.py

@ -150,7 +150,7 @@ class PrivacyConsent(models.Model):
"""Let user manually ask for consent.""" """Let user manually ask for consent."""
return { return {
"context": { "context": {
"default_composition_mode": "mass_mail",
"default_composition_mode": "comment",
"default_model": self._name, "default_model": self._name,
"default_res_id": self.id, "default_res_id": self.id,
"default_template_id": self.activity_id.consent_template_id.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 by website layout if website is installed, and otherwise includes
all possibly needed assets --> all possibly needed assets -->
<t t-call="web.login_layout"> <t t-call="web.login_layout">
<t t-set="login_card_classes" t-value="'o_consent_form'" />
<div class="container readable"> <div class="container readable">
<div class="jumbotron"> <div class="jumbotron">
<h1>Thank you!</h1> <h1>Thank you!</h1>

66
privacy_consent/tests/test_consent.py

@ -1,8 +1,10 @@
# Copyright 2018 Tecnativa - Jairo Llopis # Copyright 2018 Tecnativa - Jairo Llopis
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
from contextlib import contextmanager
from odoo.exceptions import ValidationError from odoo.exceptions import ValidationError
from odoo.tests.common import HttpCase
from odoo.tests.common import HttpCase, Form
class ActivityCase(HttpCase): class ActivityCase(HttpCase):
@ -70,6 +72,37 @@ class ActivityCase(HttpCase):
"server_action_id": self.sync_blacklist.id, "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): def check_activity_auto_properly_sent(self):
"""Check emails sent by ``self.activity_auto``.""" """Check emails sent by ``self.activity_auto``."""
consents = self.env["privacy.consent"].search([ consents = self.env["privacy.consent"].search([
@ -81,8 +114,9 @@ class ActivityCase(HttpCase):
messages = consent.message_ids messages = consent.message_ids
self.assertEqual(len(messages), 2) self.assertEqual(len(messages), 2)
# Check sent mails # Check sent mails
with self._patch_build():
self.cron_mail_queue.method_direct_trigger() 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 "") good_email = "@" in (consent.partner_id.email or "")
expected_messages = 3 if good_email else 2 expected_messages = 3 if good_email else 2
self.assertEqual( self.assertEqual(
@ -119,6 +153,14 @@ class ActivityCase(HttpCase):
) )
# Partner's is_blacklisted should be synced with default consent # Partner's is_blacklisted should be synced with default consent
self.assertFalse(consent.partner_id.is_blacklisted) 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): def test_default_template(self):
"""We have a good mail template by default.""" """We have a good mail template by default."""
@ -182,19 +224,27 @@ class ActivityCase(HttpCase):
# Send one manual request # Send one manual request
action = consents[0].action_manual_ask() action = consents[0].action_manual_ask()
self.assertEqual(action["res_model"], "mail.compose.message") 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, 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 messages = consents.mapped("message_ids") - messages
self.assertEqual(len(messages), 2) self.assertEqual(len(messages), 2)
self.assertEqual(messages[0].subtype_id, self.mt_consent_state_changed) self.assertEqual(messages[0].subtype_id, self.mt_consent_state_changed)
self.assertEqual(consents.mapped("state"), ["sent", "draft", "draft"]) self.assertEqual(consents.mapped("state"), ["sent", "draft", "draft"])
self.assertEqual( self.assertEqual(
consents.mapped("partner_id.is_blacklisted"), consents.mapped("partner_id.is_blacklisted"),
[False, False, False],
[True, False, False],
) )
# Placeholder links should be logged # Placeholder links should be logged
self.assertTrue("/privacy/consent/accept/" in messages[1].body) self.assertTrue("/privacy/consent/accept/" in messages[1].body)

Loading…
Cancel
Save