Browse Source

[MIG] privacy_consent: Migrate to v12

- Partner's `opt_out` no longer exists. Using `mail.blacklist` now.
- Tests updated to support that change.
- Test workarounds removed.
- Duplicated-field-name-in-model warning removed.
- Use create multi where possible.
pull/56/head
Jairo Llopis 5 years ago
committed by Valtteri Lattu
parent
commit
83eb6367de
  1. 10
      privacy_consent/README.rst
  2. 2
      privacy_consent/__manifest__.py
  3. 19
      privacy_consent/data/ir_actions_server.xml
  4. 10
      privacy_consent/migrations/12.0.1.0.0/pre-migrate.py
  5. 10
      privacy_consent/models/mail_mail.py
  6. 2
      privacy_consent/models/mail_template.py
  7. 11
      privacy_consent/models/privacy_activity.py
  8. 13
      privacy_consent/models/privacy_consent.py
  9. 6
      privacy_consent/static/description/index.html
  10. 80
      privacy_consent/tests/test_consent.py

10
privacy_consent/README.rst

@ -14,13 +14,13 @@ Privacy - Consent
:target: http://www.gnu.org/licenses/agpl-3.0-standalone.html
:alt: License: AGPL-3
.. |badge3| image:: https://img.shields.io/badge/github-OCA%2Fdata--protection-lightgray.png?logo=github
:target: https://github.com/OCA/data-protection/tree/11.0/privacy_consent
:target: https://github.com/OCA/data-protection/tree/12.0/privacy_consent
:alt: OCA/data-protection
.. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png
:target: https://translation.odoo-community.org/projects/data-protection-11-0/data-protection-11-0-privacy_consent
:target: https://translation.odoo-community.org/projects/data-protection-12-0/data-protection-12-0-privacy_consent
:alt: Translate me on Weblate
.. |badge5| image:: https://img.shields.io/badge/runbot-Try%20me-875A7B.png
:target: https://runbot.odoo-community.org/runbot/263/11.0
:target: https://runbot.odoo-community.org/runbot/263/12.0
:alt: Try me on Runbot
|badge1| |badge2| |badge3| |badge4| |badge5|
@ -136,7 +136,7 @@ Bug Tracker
Bugs are tracked on `GitHub Issues <https://github.com/OCA/data-protection/issues>`_.
In case of trouble, please check there if your issue has already been reported.
If you spotted it first, help us smashing it by providing a detailed and welcomed
`feedback <https://github.com/OCA/data-protection/issues/new?body=module:%20privacy_consent%0Aversion:%2011.0%0A%0A**Steps%20to%20reproduce**%0A-%20...%0A%0A**Current%20behavior**%0A%0A**Expected%20behavior**>`_.
`feedback <https://github.com/OCA/data-protection/issues/new?body=module:%20privacy_consent%0Aversion:%2012.0%0A%0A**Steps%20to%20reproduce**%0A-%20...%0A%0A**Current%20behavior**%0A%0A**Expected%20behavior**>`_.
Do not contact contributors directly about support or help with technical issues.
@ -173,6 +173,6 @@ OCA, or the Odoo Community Association, is a nonprofit organization whose
mission is to support the collaborative development of Odoo features and
promote its widespread use.
This module is part of the `OCA/data-protection <https://github.com/OCA/data-protection/tree/11.0/privacy_consent>`_ project on GitHub.
This module is part of the `OCA/data-protection <https://github.com/OCA/data-protection/tree/12.0/privacy_consent>`_ project on GitHub.
You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute.

2
privacy_consent/__manifest__.py

@ -4,7 +4,7 @@
"name": "Privacy - Consent",
"summary": "Allow people to explicitly accept or reject inclusion "
"in some activity, GDPR compliant",
"version": "11.0.1.0.0",
"version": "12.0.1.0.0",
"development_status": "Production/Stable",
"category": "Privacy",
"website": "https://github.com/OCA/management-activity",

19
privacy_consent/data/ir_actions_server.xml

@ -5,12 +5,25 @@
<data>
<record id="update_opt_out" model="ir.actions.server">
<field name="name">Update partner's opt out</field>
<record id="sync_blacklist" model="ir.actions.server">
<field name="name">Sync partner's email blacklist status</field>
<field name="model_id" ref="model_privacy_consent"/>
<field name="crud_model_id" ref="base.model_res_partner"/>
<field name="state">code</field>
<field name="code">records.mapped('partner_id').write({'opt_out': not record.accepted})</field>
<field name="code">
for consent in records:
email = consent.partner_id.email
# Skip records without email, although highly improbable
if not email:
continue
# Choose method to sync acceptance and blacklisting
if consent.accepted:
method = env["mail.blacklist"]._remove
else:
method = env["mail.blacklist"]._add
# Apply user desire
method(email)
</field>
</record>
</data>

10
privacy_consent/migrations/12.0.1.0.0/pre-migrate.py

@ -0,0 +1,10 @@
# Copyright 2019 Tecnativa - Jairo Llopis
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
from openupgradelib.openupgrade import rename_xmlids
def migrate(cr, version):
"""Use a better xmlid for the provided server action."""
rename_xmlids(cr, [
("privacy_consent.update_opt_out", "privacy_consent.sync_blacklist"),
])

10
privacy_consent/models/mail_mail.py

@ -7,8 +7,10 @@ from odoo import models
class MailMail(models.Model):
_inherit = "mail.mail"
def _postprocess_sent_message(self, mail_sent=True):
def _postprocess_sent_message(self, success_pids, failure_reason=False,
failure_type=None):
"""Write consent status after sending message."""
mail_sent = not failure_type
if mail_sent:
# Get all mails sent to consents
consent_mails = self.filtered(
@ -23,7 +25,11 @@ class MailMail(models.Model):
consents.write({
"state": "sent",
})
return super(MailMail, self)._postprocess_sent_message(mail_sent)
return super()._postprocess_sent_message(
success_pids=success_pids,
failure_reason=False,
failure_type=None,
)
def send_get_mail_body(self, partner=None):
"""Replace privacy consent magic links.

2
privacy_consent/models/mail_template.py

@ -13,7 +13,7 @@ class MailTemplate(models.Model):
@api.constrains("body_html", "model")
def _check_consent_links_in_body_html(self):
"""Body for ``privacy.consent`` templates needs placeholder links."""
links = [u"//a[@href='/privacy/consent/{}/']".format(action)
links = ["//a[@href='/privacy/consent/{}/']".format(action)
for action in ("accept", "reject")]
for one in self:
if one.model != "privacy.consent":

11
privacy_consent/models/privacy_activity.py

@ -24,7 +24,7 @@ class PrivacyActivity(models.Model):
"Consents",
)
consent_count = fields.Integer(
"Consents",
"Consents count",
compute="_compute_consent_count",
)
consent_required = fields.Selection(
@ -115,7 +115,7 @@ class PrivacyActivity(models.Model):
def action_new_consents(self):
"""Generate new consent requests."""
consents = self.env["privacy.consent"]
consents_vals = []
# Skip activitys where consent is not required
for one in self.with_context(active_test=False) \
.filtered("consent_required"):
@ -123,14 +123,15 @@ class PrivacyActivity(models.Model):
("id", "not in", one.mapped("consent_ids.partner_id").ids),
("email", "!=", False),
] + safe_eval(one.subject_domain)
# Create missing consent requests
# Store values for creating missing consent requests
for missing in self.env["res.partner"].search(domain):
consents |= consents.create({
consents_vals.append({
"partner_id": missing.id,
"accepted": one.default_consent,
"activity_id": one.id,
})
# Send consent request emails for automatic activitys
# Create and send consent request emails for automatic activitys
consents = self.env["privacy.consent"].create(consents_vals)
consents.action_auto_ask()
# Redirect user to new consent requests generated
return {

13
privacy_consent/models/privacy_consent.py

@ -118,14 +118,15 @@ class PrivacyConsent(models.Model):
)
action.run()
@api.model
def create(self, vals):
@api.model_create_multi
def create(self, vals_list):
"""Run server action on create."""
result = super(PrivacyConsent,
self.with_context(mail_create_nolog=True)).create(vals)
super_ = super(PrivacyConsent,
self.with_context(mail_create_nolog=True))
results = super_.create(vals_list)
# Sync the default acceptance status
result.sudo()._run_action()
return result
results.sudo()._run_action()
return results
def write(self, vals):
"""Run server action on update."""

6
privacy_consent/static/description/index.html

@ -367,7 +367,7 @@ ul.auto-toc {
!! This file is generated by oca-gen-addon-readme !!
!! changes will be overwritten. !!
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -->
<p><a class="reference external" href="https://odoo-community.org/page/development-status"><img alt="Production/Stable" src="https://img.shields.io/badge/maturity-Production%2FStable-green.png" /></a> <a class="reference external" href="http://www.gnu.org/licenses/agpl-3.0-standalone.html"><img alt="License: AGPL-3" src="https://img.shields.io/badge/licence-AGPL--3-blue.png" /></a> <a class="reference external" href="https://github.com/OCA/data-protection/tree/11.0/privacy_consent"><img alt="OCA/data-protection" src="https://img.shields.io/badge/github-OCA%2Fdata--protection-lightgray.png?logo=github" /></a> <a class="reference external" href="https://translation.odoo-community.org/projects/data-protection-11-0/data-protection-11-0-privacy_consent"><img alt="Translate me on Weblate" src="https://img.shields.io/badge/weblate-Translate%20me-F47D42.png" /></a> <a class="reference external" href="https://runbot.odoo-community.org/runbot/263/11.0"><img alt="Try me on Runbot" src="https://img.shields.io/badge/runbot-Try%20me-875A7B.png" /></a></p>
<p><a class="reference external" href="https://odoo-community.org/page/development-status"><img alt="Production/Stable" src="https://img.shields.io/badge/maturity-Production%2FStable-green.png" /></a> <a class="reference external" href="http://www.gnu.org/licenses/agpl-3.0-standalone.html"><img alt="License: AGPL-3" src="https://img.shields.io/badge/licence-AGPL--3-blue.png" /></a> <a class="reference external" href="https://github.com/OCA/data-protection/tree/12.0/privacy_consent"><img alt="OCA/data-protection" src="https://img.shields.io/badge/github-OCA%2Fdata--protection-lightgray.png?logo=github" /></a> <a class="reference external" href="https://translation.odoo-community.org/projects/data-protection-12-0/data-protection-12-0-privacy_consent"><img alt="Translate me on Weblate" src="https://img.shields.io/badge/weblate-Translate%20me-F47D42.png" /></a> <a class="reference external" href="https://runbot.odoo-community.org/runbot/263/12.0"><img alt="Try me on Runbot" src="https://img.shields.io/badge/runbot-Try%20me-875A7B.png" /></a></p>
<p>This module allows the user to define a set of subjects (partners)
affected by any data processing activity, and establish
a process to ask them for consent to include them in that activity.</p>
@ -484,7 +484,7 @@ and the request state.</li>
<p>Bugs are tracked on <a class="reference external" href="https://github.com/OCA/data-protection/issues">GitHub Issues</a>.
In case of trouble, please check there if your issue has already been reported.
If you spotted it first, help us smashing it by providing a detailed and welcomed
<a class="reference external" href="https://github.com/OCA/data-protection/issues/new?body=module:%20privacy_consent%0Aversion:%2011.0%0A%0A**Steps%20to%20reproduce**%0A-%20...%0A%0A**Current%20behavior**%0A%0A**Expected%20behavior**">feedback</a>.</p>
<a class="reference external" href="https://github.com/OCA/data-protection/issues/new?body=module:%20privacy_consent%0Aversion:%2012.0%0A%0A**Steps%20to%20reproduce**%0A-%20...%0A%0A**Current%20behavior**%0A%0A**Expected%20behavior**">feedback</a>.</p>
<p>Do not contact contributors directly about support or help with technical issues.</p>
</div>
<div class="section" id="credits">
@ -516,7 +516,7 @@ If you spotted it first, help us smashing it by providing a detailed and welcome
<p>OCA, or the Odoo Community Association, is a nonprofit organization whose
mission is to support the collaborative development of Odoo features and
promote its widespread use.</p>
<p>This module is part of the <a class="reference external" href="https://github.com/OCA/data-protection/tree/11.0/privacy_consent">OCA/data-protection</a> project on GitHub.</p>
<p>This module is part of the <a class="reference external" href="https://github.com/OCA/data-protection/tree/12.0/privacy_consent">OCA/data-protection</a> project on GitHub.</p>
<p>You are welcome to contribute. To learn how please visit <a class="reference external" href="https://odoo-community.org/page/Contribute">https://odoo-community.org/page/Contribute</a>.</p>
</div>
</div>

80
privacy_consent/tests/test_consent.py

@ -1,8 +1,6 @@
# 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
@ -10,15 +8,10 @@ from odoo.tests.common import HttpCase
class ActivityCase(HttpCase):
def setUp(self):
super(ActivityCase, self).setUp()
# HACK https://github.com/odoo/odoo/issues/12237
# TODO Remove hack in v12
self._oldenv = self.env
self.env = self._oldenv(self.cursor())
# HACK end
self.cron = self.env.ref("privacy_consent.cron_auto_consent")
self.cron_mail_queue = self.env.ref(
"mail.ir_cron_mail_scheduler_action")
self.update_opt_out = self.env.ref("privacy_consent.update_opt_out")
self.sync_blacklist = self.env.ref("privacy_consent.sync_blacklist")
self.mt_consent_consent_new = self.env.ref(
"privacy_consent.mt_consent_consent_new")
self.mt_consent_acceptance_changed = self.env.ref(
@ -30,25 +23,22 @@ class ActivityCase(HttpCase):
self.partners += self.partners.create({
"name": "consent-partner-0",
"email": "partner0@example.com",
"notify_email": "none",
"opt_out": False,
})
self.partners += self.partners.create({
"name": "consent-partner-1",
"email": "partner1@example.com",
"notify_email": "always",
"opt_out": True,
})
self.partners += self.partners.create({
"name": "consent-partner-2",
"email": "partner2@example.com",
"opt_out": False,
})
# Partner without email, on purpose
self.partners += self.partners.create({
"name": "consent-partner-3",
"opt_out": True,
})
# Blacklist some partners
self.blacklists = self.env["mail.blacklist"]
self.blacklists += self.blacklists._add("partner1@example.com")
# Activity without consent
self.activity_noconsent = self.env["privacy.activity"].create({
"name": "activity_noconsent",
@ -62,7 +52,7 @@ class ActivityCase(HttpCase):
"subject_domain": repr([("id", "in", self.partners.ids)]),
"consent_required": "auto",
"default_consent": True,
"server_action_id": self.update_opt_out.id,
"server_action_id": self.sync_blacklist.id,
})
# Activity with manual consent, skipping partner 0
self.activity_manual = self.env["privacy.activity"].create({
@ -72,23 +62,9 @@ class ActivityCase(HttpCase):
"subject_domain": repr([("id", "in", self.partners[1:].ids)]),
"consent_required": "manual",
"default_consent": False,
"server_action_id": self.update_opt_out.id,
"server_action_id": self.sync_blacklist.id,
})
# HACK https://github.com/odoo/odoo/issues/12237
# TODO Remove hack in v12
def tearDown(self):
self.env = self._oldenv
super(ActivityCase, self).tearDown()
# HACK https://github.com/odoo/odoo/issues/12237
# TODO Remove hack in v12
@contextmanager
def release_cr(self):
self.env.cr.release()
yield
self.env.cr.acquire()
def check_activity_auto_properly_sent(self):
"""Check emails sent by ``self.activity_auto``."""
consents = self.env["privacy.consent"].search([
@ -122,8 +98,8 @@ class ActivityCase(HttpCase):
messages[0].subtype_id,
self.mt_consent_state_changed,
)
# Partner's opt_out should be synced with default consent
self.assertFalse(consent.partner_id.opt_out)
# Partner's is_blacklisted should be synced with default consent
self.assertFalse(consent.partner_id.is_blacklisted)
def test_default_template(self):
"""We have a good mail template by default."""
@ -164,12 +140,17 @@ class ActivityCase(HttpCase):
def test_generate_manually(self):
"""Manually-generated consents work as expected."""
self.partners.write({"opt_out": False})
for partner in self.partners:
if partner.email:
self.blacklists._remove(partner.email)
result = self.activity_manual.action_new_consents()
self.assertEqual(result["res_model"], "privacy.consent")
consents = self.env[result["res_model"]].search(result["domain"])
self.assertEqual(consents.mapped("state"), ["draft"] * 2)
self.assertEqual(consents.mapped("partner_id.opt_out"), [False] * 2)
self.assertEqual(
consents.mapped("partner_id.is_blacklisted"),
[False] * 2,
)
self.assertEqual(consents.mapped("accepted"), [False] * 2)
self.assertEqual(consents.mapped("last_metadata"), [False] * 2)
# Check sent mails
@ -192,7 +173,10 @@ class ActivityCase(HttpCase):
self.assertEqual(len(messages), 2)
self.assertEqual(messages[0].subtype_id, self.mt_consent_state_changed)
self.assertEqual(consents.mapped("state"), ["sent", "draft"])
self.assertEqual(consents.mapped("partner_id.opt_out"), [True, False])
self.assertEqual(
consents.mapped("partner_id.is_blacklisted"),
[False, False],
)
# Placeholder links should be logged
self.assertTrue("/privacy/consent/accept/" in messages[1].body)
self.assertTrue("/privacy/consent/reject/" in messages[1].body)
@ -202,32 +186,30 @@ class ActivityCase(HttpCase):
self.assertNotIn(accept_url, messages[1].body)
self.assertNotIn(reject_url, messages[1].body)
# Visit tokenized accept URL
with self.release_cr():
result = self.url_open(accept_url).text
self.assertIn("accepted", result)
self.assertIn(reject_url, result)
self.assertIn(self.activity_manual.name, result)
self.assertIn(self.activity_manual.description, result)
result = self.url_open(accept_url).text
self.assertIn("accepted", result)
self.assertIn(reject_url, result)
self.assertIn(self.activity_manual.name, result)
self.assertIn(self.activity_manual.description, result)
consents.invalidate_cache()
self.assertEqual(consents.mapped("accepted"), [True, False])
self.assertTrue(consents[0].last_metadata)
self.assertFalse(consents[0].partner_id.opt_out)
self.assertFalse(consents[0].partner_id.is_blacklisted)
self.assertEqual(consents.mapped("state"), ["answered", "draft"])
self.assertEqual(
consents[0].message_ids[0].subtype_id,
self.mt_consent_acceptance_changed,
)
# Visit tokenized reject URL
with self.release_cr():
result = self.url_open(reject_url).text
self.assertIn("rejected", result)
self.assertIn(accept_url, result)
self.assertIn(self.activity_manual.name, result)
self.assertIn(self.activity_manual.description, result)
result = self.url_open(reject_url).text
self.assertIn("rejected", result)
self.assertIn(accept_url, result)
self.assertIn(self.activity_manual.name, result)
self.assertIn(self.activity_manual.description, result)
consents.invalidate_cache()
self.assertEqual(consents.mapped("accepted"), [False, False])
self.assertTrue(consents[0].last_metadata)
self.assertTrue(consents[0].partner_id.opt_out)
self.assertTrue(consents[0].partner_id.is_blacklisted)
self.assertEqual(consents.mapped("state"), ["answered", "draft"])
self.assertEqual(
consents[0].message_ids[0].subtype_id,

Loading…
Cancel
Save