Merge PR #526 into 13.0

Signed-off-by pedrobaeza
pull/538/head
OCA-git-bot 2020-04-22 07:24:17 +00:00
commit e5a12fac65
11 changed files with 204 additions and 46 deletions

View File

@ -1,4 +1,5 @@
from odoo import models from odoo import models
from odoo.tools import config
from odoo.tools.safe_eval import safe_eval from odoo.tools.safe_eval import safe_eval
@ -11,6 +12,11 @@ class MailThread(models.AbstractModel):
result = super(MailThread, self)._message_add_suggested_recipient( result = super(MailThread, self)._message_add_suggested_recipient(
result, partner=partner, email=email, reason=reason result, partner=partner, email=email, reason=reason
) )
test_condition = config["test_enable"] and not self.env.context.get(
"test_restrict_follower"
)
if test_condition or self.env.context.get("no_restrict_follower"):
return result
domain = self.env[ domain = self.env[
"mail.wizard.invite" "mail.wizard.invite"
]._mail_restrict_follower_selection_get_domain() ]._mail_restrict_follower_selection_get_domain()

View File

@ -76,6 +76,9 @@ These are all available status icons:
.. |noemail| image:: https://raw.githubusercontent.com/OCA/social/13.0/mail_tracking/static/src/img/no_email.png .. |noemail| image:: https://raw.githubusercontent.com/OCA/social/13.0/mail_tracking/static/src/img/no_email.png
:width: 10px :width: 10px
.. |anonuser| image:: https://raw.githubusercontent.com/OCA/social/13.0/mail_tracking/static/src/img/anon_user.png
:width: 10px
|unknown| **Unknown**: No email tracking info available. Maybe this notified partner has 'Receive Inbox Notifications by Email' == 'Never' |unknown| **Unknown**: No email tracking info available. Maybe this notified partner has 'Receive Inbox Notifications by Email' == 'Never'
|waiting| **Waiting**: Waiting to be sent |waiting| **Waiting**: Waiting to be sent
@ -92,6 +95,8 @@ These are all available status icons:
|noemail| **No Email**: The partner doesn't have a defined email |noemail| **No Email**: The partner doesn't have a defined email
|anonuser| **No Partner**: The recipient doesn't have a defined partner
If you want to see all tracking emails and events you can go to If you want to see all tracking emails and events you can go to

View File

@ -9,3 +9,4 @@ from . import mail_tracking_event
from . import res_partner from . import res_partner
from . import mail_thread from . import mail_thread
from . import mail_resend_message from . import mail_resend_message
from . import mail_alias

View File

@ -0,0 +1,33 @@
# Copyright 2020 Alexandre Díaz
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
from odoo import api, models, tools
class MailAlias(models.Model):
_inherit = "mail.alias"
@api.model
@tools.ormcache()
def get_aliases(self):
return {
x["display_name"]
for x in self.search_read([("alias_name", "!=", False)], ["display_name"])
}
@api.model_create_multi
def create(self, vals_list):
res = super().create(vals_list)
self.clear_caches()
return res
def write(self, vals):
res = super().write(vals)
if "alias_name" in vals:
self.clear_caches()
return res
def unlink(self):
res = super().unlink()
self.clear_caches()
return res

View File

@ -2,6 +2,8 @@
# Copyright 2019 Alexandre Díaz # Copyright 2019 Alexandre Díaz
# 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 email.utils import getaddresses
from odoo import _, api, fields, models from odoo import _, api, fields, models
from odoo.tools import email_split from odoo.tools import email_split
@ -13,6 +15,7 @@ class MailMessage(models.Model):
email_cc = fields.Char( email_cc = fields.Char(
"Cc", help="Additional recipients that receive a " '"Carbon Copy" of the e-mail' "Cc", help="Additional recipients that receive a " '"Carbon Copy" of the e-mail'
) )
email_to = fields.Char("To", help="Raw TO recipients")
mail_tracking_ids = fields.One2many( mail_tracking_ids = fields.One2many(
comodel_name="mail.tracking.email", comodel_name="mail.tracking.email",
inverse_name="mail_message_id", inverse_name="mail_message_id",
@ -105,11 +108,16 @@ class MailMessage(models.Model):
.sudo() .sudo()
.search([("mail_message_id", "=", message.id)]) .search([("mail_message_id", "=", message.id)])
) )
# Get Cc recipients # String to List
email_cc_list = email_split(message.email_cc) email_cc_list = self._drop_aliases(email_split(message.email_cc))
if any(email_cc_list): email_to_list = self._drop_aliases(email_split(message.email_to))
partners |= partners.search([("email", "in", email_cc_list)]) # Search related partners recipients
partners |= partners.search(
[("email", "in", email_cc_list + email_to_list)]
)
# Operate over set's instead of lists
email_cc_list = set(email_cc_list) email_cc_list = set(email_cc_list)
email_to_list = set(email_to_list) - email_cc_list
# Search all trackings for this message # Search all trackings for this message
for tracking in trackings: for tracking in trackings:
status = self._partner_tracking_status_get(tracking) status = self._partner_tracking_status_get(tracking)
@ -127,45 +135,66 @@ class MailMessage(models.Model):
} }
) )
if tracking.partner_id: if tracking.partner_id:
# Discard mails with tracking
email_cc_list.discard(tracking.partner_id.email) email_cc_list.discard(tracking.partner_id.email)
email_to_list.discard(tracking.partner_id.email)
partners_already |= tracking.partner_id partners_already |= tracking.partner_id
# Search all recipients for this message # Search all partner recipients for this message
if message.partner_ids: if message.partner_ids:
partners |= message.partner_ids partners |= message.partner_ids
if message.notified_partner_ids: if message.notified_partner_ids:
partners |= message.notified_partner_ids partners |= message.notified_partner_ids
# Remove recipients already included # Discard partner recipients already included
partners -= partners_already partners -= partners_already
tracking_unkown_values = { # Default tracking values
tracking_unknown_values = {
"status": "unknown", "status": "unknown",
"status_human": self._partner_tracking_status_human_get("unknown"), "status_human": self._partner_tracking_status_human_get("unknown"),
"error_type": False, "error_type": False,
"error_description": False, "error_description": False,
"tracking_id": False, "tracking_id": False,
} }
# Process tracking status of partner recipients without tracking
for partner in partners: for partner in partners:
# Discard 'To' with partner
if partner.email in email_to_list:
email_to_list.discard(partner.email)
# If there is partners not included, then status is 'unknown' # If there is partners not included, then status is 'unknown'
# and perhaps a Cc recipient # and perhaps a Cc recipient
isCc = False isCc = False
if partner.email in email_cc_list: if partner.email in email_cc_list:
email_cc_list.discard(partner.email) email_cc_list.discard(partner.email)
isCc = True isCc = True
tracking_unkown_values.update( tracking_status = tracking_unknown_values.copy()
tracking_status.update(
{"recipient": partner.name, "partner_id": partner.id, "isCc": isCc} {"recipient": partner.name, "partner_id": partner.id, "isCc": isCc}
) )
partner_trackings.append(tracking_unkown_values.copy()) partner_trackings.append(tracking_status)
for email in email_cc_list: # Process Cc/To recipients without partner
# If there is Cc without partner for cc, lst in [(True, email_cc_list), (False, email_to_list)]:
tracking_unkown_values.update( for email in lst:
{"recipient": email, "partner_id": False, "isCc": True} tracking_status = tracking_unknown_values.copy()
) tracking_status.update(
partner_trackings.append(tracking_unkown_values.copy()) {"recipient": email, "partner_id": False, "isCc": cc}
)
partner_trackings.append(tracking_status)
res[message.id] = { res[message.id] = {
"partner_trackings": partner_trackings, "partner_trackings": partner_trackings,
"is_failed_message": message.is_failed_message, "is_failed_message": message.is_failed_message,
} }
return res return res
@api.model
def _drop_aliases(self, mail_list):
aliases = self.env["mail.alias"].get_aliases()
def _filter_alias(email):
email_wn = getaddresses([email])[0][1]
if email_wn not in aliases:
return email_wn
return list(filter(_filter_alias, mail_list))
@api.model @api.model
def _message_read_dict_postprocess(self, messages, message_tree): def _message_read_dict_postprocess(self, messages, message_tree):
"""Preare values to be used by the chatter widget""" """Preare values to be used by the chatter widget"""

View File

@ -33,41 +33,48 @@ class MailThread(models.AbstractModel):
def message_post(self, *args, **kwargs): def message_post(self, *args, **kwargs):
"""Adds CC recipient to the message. """Adds CC recipient to the message.
Because Odoo implementation avoid store cc recipients we ensure that Because Odoo implementation avoid store 'from, to, cc' recipients we
this information its written into the mail.message record. ensure that this information its written into the mail.message record.
""" """
new_message = super().message_post(*args, **kwargs) kwargs.update(
email_cc = kwargs.get("cc") {"email_cc": kwargs.get("cc", False), "email_to": kwargs.get("to", False)}
if email_cc: )
new_message.sudo().write({"email_cc": email_cc}) return super().message_post(*args, **kwargs)
return new_message
def _message_get_suggested_recipients(self): def _message_get_suggested_recipients(self):
"""Adds email Cc recipients as suggested recipients. """Adds email 'extra' recipients as suggested recipients.
If the recipient has a res.partner, use it. If the recipient has a res.partner, use it.
""" """
res = super()._message_get_suggested_recipients() res = super()._message_get_suggested_recipients()
self._add_extra_recipients_suggestions(res, "email_cc", _("Cc"))
self._add_extra_recipients_suggestions(res, "email_to", _("Anon. To"))
return res
def _add_extra_recipients_suggestions(self, suggestions, field_mail, reason):
ResPartnerObj = self.env["res.partner"] ResPartnerObj = self.env["res.partner"]
email_cc_formated_list = [] aliases = self.env["mail.alias"].get_aliases()
email_extra_formated_list = []
for record in self: for record in self:
emails_cc = record.message_ids.mapped("email_cc") emails_extra = record.message_ids.mapped(field_mail)
for email in emails_cc: for email in emails_extra:
email_cc_formated_list.extend(email_split_and_format(email)) email_extra_formated_list.extend(email_split_and_format(email))
email_cc_formated_list = set(email_cc_formated_list) email_extra_formated_list = set(email_extra_formated_list)
for cc in email_cc_formated_list: email_extra_list = [x[1] for x in getaddresses(email_extra_formated_list)]
email_parts = getaddresses([cc])[0] partners_info = self._message_partner_info_from_emails(email_extra_list)
partner_id = record._message_partner_info_from_emails([email_parts[1]])[ for pinfo in partners_info:
0 partner_id = pinfo["partner_id"]
].get("partner_id") email = pinfo["full_name"]
if not partner_id: if not partner_id:
record._message_add_suggested_recipient(res, email=cc, reason=_("Cc")) if email not in aliases:
self._message_add_suggested_recipient(
suggestions, email=email, reason=reason
)
else: else:
partner = ResPartnerObj.browse(partner_id) partner = ResPartnerObj.browse(partner_id)
record._message_add_suggested_recipient( self._message_add_suggested_recipient(
res, partner=partner, reason=_("Cc") suggestions, partner=partner, reason=reason
) )
return res
@api.model @api.model
def _fields_view_get( def _fields_view_get(

View File

@ -28,6 +28,9 @@ These are all available status icons:
.. |noemail| image:: ../static/src/img/no_email.png .. |noemail| image:: ../static/src/img/no_email.png
:width: 10px :width: 10px
.. |anonuser| image:: ../static/src/img/anon_user.png
:width: 10px
|unknown| **Unknown**: No email tracking info available. Maybe this notified partner has 'Receive Inbox Notifications by Email' == 'Never' |unknown| **Unknown**: No email tracking info available. Maybe this notified partner has 'Receive Inbox Notifications by Email' == 'Never'
|waiting| **Waiting**: Waiting to be sent |waiting| **Waiting**: Waiting to be sent
@ -44,6 +47,8 @@ These are all available status icons:
|noemail| **No Email**: The partner doesn't have a defined email |noemail| **No Email**: The partner doesn't have a defined email
|anonuser| **No Partner**: The recipient doesn't have a defined partner
If you want to see all tracking emails and events you can go to If you want to see all tracking emails and events you can go to

View File

@ -412,6 +412,7 @@ status icon will appear just right to name of notified partner.</p>
<p><img alt="opened" src="https://raw.githubusercontent.com/OCA/social/13.0/mail_tracking/static/src/img/opened.png" style="width: 15px;" /> <strong>Opened</strong>: Opened by partner</p> <p><img alt="opened" src="https://raw.githubusercontent.com/OCA/social/13.0/mail_tracking/static/src/img/opened.png" style="width: 15px;" /> <strong>Opened</strong>: Opened by partner</p>
<p><img alt="cc" src="https://raw.githubusercontent.com/OCA/social/13.0/mail_tracking/static/src/img/cc.png" style="width: 10px;" /> <strong>Cc</strong>: Its a Carbon-Copy recipient. Cant know the status so is Unknown</p> <p><img alt="cc" src="https://raw.githubusercontent.com/OCA/social/13.0/mail_tracking/static/src/img/cc.png" style="width: 10px;" /> <strong>Cc</strong>: Its a Carbon-Copy recipient. Cant know the status so is Unknown</p>
<p><img alt="noemail" src="https://raw.githubusercontent.com/OCA/social/13.0/mail_tracking/static/src/img/no_email.png" style="width: 10px;" /> <strong>No Email</strong>: The partner doesnt have a defined email</p> <p><img alt="noemail" src="https://raw.githubusercontent.com/OCA/social/13.0/mail_tracking/static/src/img/no_email.png" style="width: 10px;" /> <strong>No Email</strong>: The partner doesnt have a defined email</p>
<p><img alt="anonuser" src="https://raw.githubusercontent.com/OCA/social/13.0/mail_tracking/static/src/img/anon_user.png" style="width: 10px;" /> <strong>No Partner</strong>: The recipient doesnt have a defined partner</p>
<p>If you want to see all tracking emails and events you can go to</p> <p>If you want to see all tracking emails and events you can go to</p>
<ul class="simple"> <ul class="simple">
<li>Settings &gt; Technical &gt; Email &gt; Tracking emails</li> <li>Settings &gt; Technical &gt; Email &gt; Tracking emails</li>

Binary file not shown.

After

Width:  |  Height:  |  Size: 483 B

View File

@ -10,6 +10,11 @@
<i class="fa fa-cc"></i> <i class="fa fa-cc"></i>
</span> </span>
</t> </t>
<t t-elif="!tracking['isCc'] &amp;&amp; !tracking['partner_id']">
<span class="mail_anon_recipient">
<i class="fa fa-low-vision"></i>
</span>
</t>
<t t-elif="tracking['status'] === 'unknown'"> <t t-elif="tracking['status'] === 'unknown'">
<span class="mail_tracking_unknown"> <span class="mail_tracking_unknown">
<i class="fa fa-ban"></i> <i class="fa fa-ban"></i>

View File

@ -9,7 +9,7 @@ import psycopg2
import psycopg2.errorcodes import psycopg2.errorcodes
from lxml import etree from lxml import etree
from odoo import _, http from odoo import http
from odoo.tests.common import TransactionCase from odoo.tests.common import TransactionCase
from odoo.tools import mute_logger from odoo.tools import mute_logger
@ -152,7 +152,7 @@ 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 _check_partner_trackings(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)
# mail cc # mail cc
@ -179,15 +179,16 @@ class TestMailTracking(TransactionCase):
"login": "sender-test", "login": "sender-test",
} }
) )
# pylint: disable=C8107
message = self.recipient.with_user(sender_user).message_post( message = self.recipient.with_user(sender_user).message_post(
body=_("<p>This is a test message</p>"), body="<p>This is a test message</p>",
cc="unnamed@test.com, sender@example.com", cc="Dominique Pinon <unnamed@test.com>, sender@example.com",
) )
# suggested recipients # suggested recipients
recipients = self.recipient._message_get_suggested_recipients() recipients = self.recipient._message_get_suggested_recipients()
suggested_mails = {email[1] for email in recipients[self.recipient.id]} suggested_mails = {email[1] for email in recipients[self.recipient.id]}
self.assertTrue("unnamed@test.com" in suggested_mails) self.assertIn("unnamed@test.com", suggested_mails)
self.assertEqual(len(recipients[self.recipient.id][0]), 3) self.assertEqual(len(recipients[self.recipient.id]), 3)
# Repeated Cc recipients # Repeated Cc recipients
message = self.env["mail.message"].create( message = self.env["mail.message"].create(
{ {
@ -198,15 +199,80 @@ class TestMailTracking(TransactionCase):
"model": "res.partner", "model": "res.partner",
"res_id": self.recipient.id, "res_id": self.recipient.id,
"partner_ids": [(4, self.recipient.id)], "partner_ids": [(4, self.recipient.id)],
"email_cc": "unnamed@test.com, sender@example.com" "email_cc": "Dominique Pinon <unnamed@test.com>, sender@example.com"
", recipient@example.com", ", recipient@example.com",
"body": "<p>This is another test message</p>", "body": "<p>This is another test message</p>",
} }
) )
message._moderate_accept() message._moderate_accept()
recipients = self.recipient._message_get_suggested_recipients() recipients = self.recipient._message_get_suggested_recipients()
self.assertEqual(len(recipients[self.recipient.id][0]), 3) self.assertEqual(len(recipients[self.recipient.id]), 3)
self._check_partner_trackings(message) self._check_partner_trackings_cc(message)
def _check_partner_trackings_to(self, message):
message_dict = message.message_format()[0]
self.assertEqual(len(message_dict["partner_trackings"]), 3)
# mail cc
foundPartner = False
foundNoPartner = False
for tracking in message_dict["partner_trackings"]:
if tracking["partner_id"] == self.sender.id:
foundPartner = True
elif tracking["recipient"] == "support+unnamed@test.com":
foundNoPartner = True
self.assertFalse(tracking["partner_id"])
self.assertTrue(foundPartner)
self.assertTrue(foundNoPartner)
def test_email_to(self):
sender_user = self.env["res.users"].create(
{
"name": "Sender User Test",
"partner_id": self.sender.id,
"login": "sender-test",
}
)
# pylint: disable=C8107
message = self.recipient.with_user(sender_user).message_post(
body="<p>This is a test message</p>",
to="Dominique Pinon <support+unnamed@test.com>, sender@example.com",
)
# suggested recipients
recipients = self.recipient._message_get_suggested_recipients()
suggested_mails = {email[1] for email in recipients[self.recipient.id]}
self.assertIn("support+unnamed@test.com", suggested_mails)
self.assertEqual(len(recipients[self.recipient.id]), 3)
# Repeated To recipients
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_to": "Dominique Pinon <support+unnamed@test.com>"
", sender@example.com, recipient@example.com",
"body": "<p>This is another test message</p>",
}
)
message._moderate_accept()
recipients = self.recipient._message_get_suggested_recipients()
self.assertEqual(len(recipients[self.recipient.id]), 3)
self._check_partner_trackings_to(message)
# Catchall + Alias
self.env["ir.config_parameter"].set_param("mail.catchall.domain", "test.com")
self.env["mail.alias"].create(
{
"alias_model_id": self.env["ir.model"]._get("res.partner").id,
"alias_name": "support+unnamed",
}
)
recipients = self.recipient._message_get_suggested_recipients()
self.assertEqual(len(recipients[self.recipient.id]), 2)
suggested_mails = {email[1] for email in recipients[self.recipient.id]}
self.assertNotIn("support+unnamed@test.com", suggested_mails)
def test_failed_message(self): def test_failed_message(self):
MailMessageObj = self.env["mail.message"] MailMessageObj = self.env["mail.message"]