mirror of https://github.com/OCA/social.git
commit
ebe778fa59
|
@ -0,0 +1,6 @@
|
||||||
|
{
|
||||||
|
"pull_requests": {
|
||||||
|
"792": "(auto) Nothing to port from PR #792",
|
||||||
|
"1244": "(auto) Nothing to port from PR #1244"
|
||||||
|
}
|
||||||
|
}
|
|
@ -23,6 +23,7 @@
|
||||||
"views/mail_tracking_event_view.xml",
|
"views/mail_tracking_event_view.xml",
|
||||||
"views/mail_message_view.xml",
|
"views/mail_message_view.xml",
|
||||||
"views/res_partner_view.xml",
|
"views/res_partner_view.xml",
|
||||||
|
"views/res_config_settings.xml",
|
||||||
],
|
],
|
||||||
"assets": {
|
"assets": {
|
||||||
"web.assets_backend": [
|
"web.assets_backend": [
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
|
# 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 ir_mail_server
|
||||||
from . import mail_bounced_mixin
|
from . import mail_bounced_mixin
|
||||||
from . import mail_guest
|
from . import mail_guest
|
||||||
|
|
|
@ -226,6 +226,12 @@ class MailMessage(models.Model):
|
||||||
@api.model
|
@api.model
|
||||||
def _drop_aliases(self, mail_list):
|
def _drop_aliases(self, mail_list):
|
||||||
aliases = self.env["mail.alias"].get_aliases()
|
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):
|
def _filter_alias(email):
|
||||||
email_wn = getaddresses([email])[0][1]
|
email_wn = getaddresses([email])[0][1]
|
||||||
|
|
|
@ -94,6 +94,7 @@ class MailThread(models.AbstractModel):
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
partner = ResPartnerObj.browse(partner_id)
|
partner = ResPartnerObj.browse(partner_id)
|
||||||
|
if partner.email not in aliases:
|
||||||
self._message_add_suggested_recipient(
|
self._message_add_suggested_recipient(
|
||||||
suggestions, partner=partner, reason=reason
|
suggestions, partner=partner, reason=reason
|
||||||
)
|
)
|
||||||
|
|
|
@ -118,8 +118,7 @@ class MailTrackingEmail(models.Model):
|
||||||
@api.depends("mail_message_id")
|
@api.depends("mail_message_id")
|
||||||
def _compute_message_id(self):
|
def _compute_message_id(self):
|
||||||
"""This helper field will allow us to map the message_id from either the linked
|
"""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
|
self.message_id = False
|
||||||
for tracking in self.filtered("mail_message_id"):
|
for tracking in self.filtered("mail_message_id"):
|
||||||
tracking.message_id = tracking.mail_message_id.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 'NONE' if this request is not for you
|
||||||
# - return 'ERROR' if any error
|
# - return 'ERROR' if any error
|
||||||
return "NONE" # pragma: no cover
|
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()
|
||||||
|
|
|
@ -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,
|
||||||
|
)
|
|
@ -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.",
|
||||||
|
)
|
|
@ -8,10 +8,11 @@
|
||||||
|
|
||||||
/*
|
/*
|
||||||
:Author: David Goodger (goodger@python.org)
|
: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.
|
:Copyright: This stylesheet has been placed in the public domain.
|
||||||
|
|
||||||
Default cascading style sheet for the HTML output of Docutils.
|
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
|
See https://docutils.sourceforge.io/docs/howto/html-stylesheets.html for how to
|
||||||
customize this style sheet.
|
customize this style sheet.
|
||||||
|
@ -274,7 +275,7 @@ pre.literal-block, pre.doctest-block, pre.math, pre.code {
|
||||||
margin-left: 2em ;
|
margin-left: 2em ;
|
||||||
margin-right: 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, code { background-color: #eeeeee }
|
||||||
pre.code .comment, code .comment { color: #5C6576 }
|
pre.code .comment, code .comment { color: #5C6576 }
|
||||||
pre.code .keyword, code .keyword { color: #3B0D06; font-weight: bold }
|
pre.code .keyword, code .keyword { color: #3B0D06; font-weight: bold }
|
||||||
|
@ -300,7 +301,7 @@ span.option {
|
||||||
span.pre {
|
span.pre {
|
||||||
white-space: pre }
|
white-space: pre }
|
||||||
|
|
||||||
span.problematic {
|
span.problematic, pre.problematic {
|
||||||
color: red }
|
color: red }
|
||||||
|
|
||||||
span.section-subtitle {
|
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">
|
<div class="section" id="maintainers">
|
||||||
<h2><a class="toc-backref" href="#toc-entry-9">Maintainers</a></h2>
|
<h2><a class="toc-backref" href="#toc-entry-9">Maintainers</a></h2>
|
||||||
<p>This module is maintained by the OCA.</p>
|
<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
|
<p>OCA, or the Odoo Community Association, is a nonprofit organization whose
|
||||||
mission is to support the collaborative development of Odoo features and
|
mission is to support the collaborative development of Odoo features and
|
||||||
promote its widespread use.</p>
|
promote its widespread use.</p>
|
||||||
|
|
|
@ -2,3 +2,4 @@
|
||||||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
|
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
|
||||||
|
|
||||||
from . import test_mail_tracking
|
from . import test_mail_tracking
|
||||||
|
from . import test_gc_mail_tracking_email
|
||||||
|
|
|
@ -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
|
||||||
|
)
|
|
@ -158,6 +158,36 @@ class TestMailTracking(TransactionCase):
|
||||||
self.assertEqual(tracking_email.error_type, "no_recipient")
|
self.assertEqual(tracking_email.error_type, "no_recipient")
|
||||||
self.assertFalse(self.recipient.email_bounced)
|
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):
|
def _check_partner_trackings_cc(self, message):
|
||||||
message_dict = message.message_format()[0]
|
message_dict = message.message_format()[0]
|
||||||
self.assertEqual(len(message_dict["partner_trackings"]), 3)
|
self.assertEqual(len(message_dict["partner_trackings"]), 3)
|
||||||
|
|
|
@ -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>
|
Loading…
Reference in New Issue