Merge PR #1475 into 17.0

Signed-off-by hbrunn
pull/1509/head
OCA-git-bot 2024-11-22 16:14:25 +00:00
commit ebe778fa59
13 changed files with 244 additions and 9 deletions

View File

@ -0,0 +1,6 @@
{
"pull_requests": {
"792": "(auto) Nothing to port from PR #792",
"1244": "(auto) Nothing to port from PR #1244"
}
}

View File

@ -23,6 +23,7 @@
"views/mail_tracking_event_view.xml",
"views/mail_message_view.xml",
"views/res_partner_view.xml",
"views/res_config_settings.xml",
],
"assets": {
"web.assets_backend": [

View File

@ -1,5 +1,7 @@
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
from . import res_company
from . import res_config_settings
from . import ir_mail_server
from . import mail_bounced_mixin
from . import mail_guest

View File

@ -226,6 +226,12 @@ class MailMessage(models.Model):
@api.model
def _drop_aliases(self, mail_list):
aliases = self.env["mail.alias"].get_aliases()
if self.env.company.mail_tracking_show_aliases:
IrConfigParamObj = self.env["ir.config_parameter"].sudo()
aliases = "{}@{}".format(
IrConfigParamObj.get_param("mail.catchall.alias"),
IrConfigParamObj.get_param("mail.catchall.domain"),
)
def _filter_alias(email):
email_wn = getaddresses([email])[0][1]

View File

@ -94,6 +94,7 @@ class MailThread(models.AbstractModel):
)
else:
partner = ResPartnerObj.browse(partner_id)
if partner.email not in aliases:
self._message_add_suggested_recipient(
suggestions, partner=partner, reason=reason
)

View File

@ -118,8 +118,7 @@ class MailTrackingEmail(models.Model):
@api.depends("mail_message_id")
def _compute_message_id(self):
"""This helper field will allow us to map the message_id from either the linked
mail.message or a mass.mailing mail.trace.
"""
mail.message or a mass.mailing mail.trace."""
self.message_id = False
for tracking in self.filtered("mail_message_id"):
tracking.message_id = tracking.mail_message_id.message_id
@ -462,3 +461,40 @@ class MailTrackingEmail(models.Model):
# - return 'NONE' if this request is not for you
# - return 'ERROR' if any error
return "NONE" # pragma: no cover
def _get_old_mail_tracking_email_domain(self, max_age_days):
target_write_date = fields.Datetime.subtract(
fields.Datetime.now(), days=max_age_days
)
return [("write_date", "<", target_write_date)]
@api.autovacuum
def _gc_mail_tracking_email(self, limit=5000):
config_max_age_days = (
self.env["ir.config_parameter"]
.sudo()
.get_param("mail_tracking.mail_tracking_email_max_age_days")
)
try:
max_age_days = int(config_max_age_days)
except ValueError:
max_age_days = 0
if not max_age_days > 0:
return False
domain = self._get_old_mail_tracking_email_domain(max_age_days)
records_to_delete = self.search(domain, limit=limit).exists()
if records_to_delete:
_logger.info(
"Deleting %s mail.tracking.email records", len(records_to_delete)
)
records_to_delete.flush_recordset()
# Using a direct query to avoid ORM as it causes an issue with
# a related field mass_mailing_id in customer DB when deleting
# the records. This might be 14.0 specific, so changing to
# .unlink() should be tested when forward porting.
query = "DELETE FROM mail_tracking_email WHERE id IN %s"
args = (tuple(records_to_delete.ids),)
self.env.cr.execute(query, args)
self.invalidate_model()

View File

@ -0,0 +1,10 @@
from odoo import fields, models
class ResCompany(models.Model):
_inherit = "res.company"
mail_tracking_show_aliases = fields.Boolean(
string="Show Aliases in Mail Tracking",
default=False,
)

View File

@ -0,0 +1,16 @@
from odoo import fields, models
class ResConfigSettings(models.TransientModel):
_inherit = "res.config.settings"
mail_tracking_show_aliases = fields.Boolean(
related="company_id.mail_tracking_show_aliases",
readonly=False,
)
mail_tracking_email_max_age_days = fields.Integer(
"Max age in days of mail tracking email records",
config_parameter="mail_tracking.mail_tracking_email_max_age_days",
help="If set as positive integer enables the deletion of "
"old mail tracking records to reduce the database size.",
)

View File

@ -8,10 +8,11 @@
/*
:Author: David Goodger (goodger@python.org)
:Id: $Id: html4css1.css 8954 2022-01-20 10:10:25Z milde $
:Id: $Id: html4css1.css 9511 2024-01-13 09:50:07Z milde $
:Copyright: This stylesheet has been placed in the public domain.
Default cascading style sheet for the HTML output of Docutils.
Despite the name, some widely supported CSS2 features are used.
See https://docutils.sourceforge.io/docs/howto/html-stylesheets.html for how to
customize this style sheet.
@ -274,7 +275,7 @@ pre.literal-block, pre.doctest-block, pre.math, pre.code {
margin-left: 2em ;
margin-right: 2em }
pre.code .ln { color: grey; } /* line numbers */
pre.code .ln { color: gray; } /* line numbers */
pre.code, code { background-color: #eeeeee }
pre.code .comment, code .comment { color: #5C6576 }
pre.code .keyword, code .keyword { color: #3B0D06; font-weight: bold }
@ -300,7 +301,7 @@ span.option {
span.pre {
white-space: pre }
span.problematic {
span.problematic, pre.problematic {
color: red }
span.section-subtitle {
@ -495,7 +496,9 @@ If you spotted it first, help us to smash it by providing a detailed and welcome
<div class="section" id="maintainers">
<h2><a class="toc-backref" href="#toc-entry-9">Maintainers</a></h2>
<p>This module is maintained by the OCA.</p>
<a class="reference external image-reference" href="https://odoo-community.org"><img alt="Odoo Community Association" src="https://odoo-community.org/logo.png" /></a>
<a class="reference external image-reference" href="https://odoo-community.org">
<img alt="Odoo Community Association" src="https://odoo-community.org/logo.png" />
</a>
<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>

View File

@ -2,3 +2,4 @@
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
from . import test_mail_tracking
from . import test_gc_mail_tracking_email

View File

@ -0,0 +1,88 @@
# Copyright 2024 Camptocamp SA
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl)
from odoo import fields
from odoo.addons.base.tests.common import SavepointCaseWithUserDemo
class TestMailTrackingEmailCleanUp(SavepointCaseWithUserDemo):
@classmethod
def setUpClass(cls):
super().setUpClass()
cls.env = cls.env(context=dict(cls.env.context, tracking_disable=True))
cls.settings = cls.env["res.config.settings"].create(
{"mail_tracking_email_max_age_days": 365}
)
cls.settings.set_values()
cls.partner = cls.env.ref("base.res_partner_address_28")
cls.message = cls.env["mail.message"].create(
{
"model": "res.partner",
"res_id": cls.partner.id,
"body": "TEST",
"message_type": "email",
"subtype_id": cls.env.ref("mail.mt_comment").id,
"author_id": cls.partner.id,
"date": "2024-03-26",
}
)
cls.recent_mail_tracking_email = cls.env["mail.tracking.email"].create(
{"mail_message_id": cls.message.id}
)
# Can't set the write_date directly as it gets overwritten by the ORM
cls.old_mail_tracking_email = cls.env["mail.tracking.email"].create(
{"mail_message_id": cls.message.id}
)
cls.total_count = 2
cls.recent_count = 1
cls.domain = [
("mail_message_id", "=", cls.message.id),
]
def _set_write_date(self):
# Set the write_date of the old record to be older than the max_age_days
# Update DB directly to avoid ORM overwriting the write_date
old_write_date = fields.Datetime.subtract(fields.Datetime.now(), days=400)
self.env.cr.execute(
"UPDATE mail_tracking_email SET write_date = %s WHERE id = %s",
(old_write_date, self.old_mail_tracking_email.id),
)
def test_deletion_of_mail_tracking_email(self):
self._set_write_date()
self.assertEqual(
len(self.env["mail.tracking.email"].search(self.domain)), self.total_count
)
self.env["mail.tracking.email"]._gc_mail_tracking_email()
self.assertEqual(
len(self.env["mail.tracking.email"].search(self.domain)), self.recent_count
)
self.assertTrue(self.recent_mail_tracking_email.exists())
def test_deletion_follows_configuration_variable(self):
self._set_write_date()
self.assertEqual(
len(self.env["mail.tracking.email"].search(self.domain)), self.total_count
)
# when disabled, no deletions should happen
self.settings.mail_tracking_email_max_age_days = 0
self.settings.set_values()
self.env["mail.tracking.email"]._gc_mail_tracking_email()
self.assertEqual(
len(self.env["mail.tracking.email"].search(self.domain)), self.total_count
)
# when disabled, no deletions should happen
self.settings.mail_tracking_email_max_age_days = -1
self.settings.set_values()
self.env["mail.tracking.email"]._gc_mail_tracking_email()
self.assertEqual(
len(self.env["mail.tracking.email"].search(self.domain)), self.total_count
)
# when enabled, deletions should happen
self.settings.mail_tracking_email_max_age_days = 365
self.settings.set_values()
self.env["mail.tracking.email"]._gc_mail_tracking_email()
self.assertEqual(
len(self.env["mail.tracking.email"].search(self.domain)), self.recent_count
)

View File

@ -158,6 +158,36 @@ class TestMailTracking(TransactionCase):
self.assertEqual(tracking_email.error_type, "no_recipient")
self.assertFalse(self.recipient.email_bounced)
def test_message_post_show_aliases(self):
# Create message with show aliases setup
self.env.company.mail_tracking_show_aliases = True
# Setup catchall domain
IrConfigParamObj = self.env["ir.config_parameter"].sudo()
IrConfigParamObj.set_param("mail.catchall.domain", "test.com")
# pylint: disable=C8107
message = self.env["mail.message"].create(
{
"subject": "Message test",
"author_id": self.sender.id,
"email_from": self.sender.email,
"message_type": "comment",
"model": "res.partner",
"res_id": self.recipient.id,
"partner_ids": [(4, self.recipient.id)],
"email_cc": "Dominique Pinon <unnamed@test.com>, customer-invoices@test.com", # noqa E501
"body": "<p>This is another test message</p>",
}
)
message_dict, *_ = message.message_format()
self.assertTrue(
any(
[
tracking["recipient"] == "customer-invoices@test.com"
for tracking in message_dict["partner_trackings"]
]
)
)
def _check_partner_trackings_cc(self, message):
message_dict = message.message_format()[0]
self.assertEqual(len(message_dict["partner_trackings"]), 3)

View File

@ -0,0 +1,35 @@
<?xml version="1.0" encoding="utf-8" ?>
<odoo>
<record id="res_config_settings_view_form" model="ir.ui.view">
<field name="name">res.config.settings.view.form.inherit.mail.tracking</field>
<field name="model">res.config.settings</field>
<field name="inherit_id" ref="mail.res_config_settings_view_form" />
<field name="arch" type="xml">
<xpath expr="//block[@id='emails']" position="inside">
<setting>
<field name="mail_tracking_show_aliases" />
<div class="content-group">
<label for="mail_tracking_show_aliases" />
<div
class="text-muted"
id="mail_tracking_show_aliases"
>Show Aliases in Mail Tracking</div>
</div>
</setting>
<setting>
<field name="mail_tracking_email_max_age_days" />
<div class="content-group">
<label
for="mail_tracking_email_max_age_days"
string="Max age in days of mail tracking email records"
/>
<div
class="text-muted"
id="mail_tracking_email_max_age_days"
>If set as positive integer enables the deletion of old mail tracking records to reduce the database size.</div>
</div>
</setting>
</xpath>
</field>
</record>
</odoo>