Browse Source

[MIG] mail_tracking: Migration to 12.0

pull/318/head
ernesto 6 years ago
parent
commit
9f6d72034f
  1. 99
      mail_tracking/README.rst
  2. 3
      mail_tracking/__manifest__.py
  3. 1
      mail_tracking/models/__init__.py
  4. 1
      mail_tracking/models/ir_mail_server.py
  5. 43
      mail_tracking/models/mail_bounced_mixin.py
  6. 4
      mail_tracking/models/mail_mail.py
  7. 16
      mail_tracking/models/mail_tracking_email.py
  8. 21
      mail_tracking/models/res_partner.py
  9. 7
      mail_tracking/readme/CONTRIBUTORS.rst
  10. 7
      mail_tracking/readme/CREDITS.rst
  11. 3
      mail_tracking/readme/DESCRIPTION.rst
  12. 5
      mail_tracking/readme/INSTALL.rst
  13. 41
      mail_tracking/readme/USAGE.rst
  14. 470
      mail_tracking/static/description/index.html
  15. 65
      mail_tracking/static/src/js/mail_tracking.js
  16. 54
      mail_tracking/static/src/xml/mail_tracking.xml
  17. 2
      mail_tracking/tests/__init__.py
  18. 18
      mail_tracking/tests/test_mail_tracking.py
  19. 8
      mail_tracking/views/mail_tracking_email_view.xml
  20. 14
      mail_tracking/views/mail_tracking_event_view.xml
  21. 6
      mail_tracking/views/res_partner_view.xml

99
mail_tracking/README.rst

@ -1,15 +1,39 @@
.. image:: https://img.shields.io/badge/licence-AGPL--3-blue.svg
==============
Email tracking
==============
.. !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!! This file is generated by oca-gen-addon-readme !!
!! changes will be overwritten. !!
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
.. |badge1| image:: https://img.shields.io/badge/maturity-Beta-yellow.png
:target: https://odoo-community.org/page/development-status
:alt: Beta
.. |badge2| image:: https://img.shields.io/badge/licence-AGPL--3-blue.png
:target: http://www.gnu.org/licenses/agpl-3.0-standalone.html :target: http://www.gnu.org/licenses/agpl-3.0-standalone.html
:alt: License: AGPL-3 :alt: License: AGPL-3
=============
Mail tracking
=============
.. |badge3| image:: https://img.shields.io/badge/github-OCA%2Fsocial-lightgray.png?logo=github
:target: https://github.com/OCA/social/tree/12.0/mail_tracking
:alt: OCA/social
.. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png
:target: https://translation.odoo-community.org/projects/social-12-0/social-12-0-mail_tracking
:alt: Translate me on Weblate
.. |badge5| image:: https://img.shields.io/badge/runbot-Try%20me-875A7B.png
:target: https://runbot.odoo-community.org/runbot/205/12.0
:alt: Try me on Runbot
|badge1| |badge2| |badge3| |badge4| |badge5|
This module shows email notification tracking status for any messages in This module shows email notification tracking status for any messages in
mail thread (chatter). Each notified partner will have an intuitive icon just mail thread (chatter). Each notified partner will have an intuitive icon just
right to his name. right to his name.
**Table of contents**
.. contents::
:local:
Installation Installation
============ ============
@ -28,22 +52,22 @@ status icon will appear just right to name of notified partner.
These are all available status icons: These are all available status icons:
.. |sent| image:: static/src/img/sent.png
.. |sent| image:: mail_tracking/static/src/img/sent.png
:width: 10px :width: 10px
.. |delivered| image:: static/src/img/delivered.png
.. |delivered| image:: mail_tracking/static/src/img/delivered.png
:width: 15px :width: 15px
.. |opened| image:: static/src/img/opened.png
.. |opened| image:: mail_tracking/static/src/img/opened.png
:width: 15px :width: 15px
.. |error| image:: static/src/img/error.png
.. |error| image:: mail_tracking/static/src/img/error.png
:width: 10px :width: 10px
.. |waiting| image:: static/src/img/waiting.png
.. |waiting| image:: mail_tracking/static/src/img/waiting.png
:width: 10px :width: 10px
.. |unknown| image:: static/src/img/unknown.png
.. |unknown| image:: mail_tracking/static/src/img/unknown.png
:width: 10px :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'
@ -58,11 +82,6 @@ These are all available status icons:
|opened| **Opened**: Opened by partner |opened| **Opened**: Opened by partner
.. image:: https://odoo-community.org/website/image/ir.attachment/5784_f2813bd/datas
:alt: Try me on Runbot
:target: https://runbot.odoo-community.org/runbot/205/11.0
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
* Settings > Technical > Email > Tracking emails * Settings > Technical > Email > Tracking emails
@ -72,14 +91,35 @@ If you want to see all tracking emails and events you can go to
Bug Tracker Bug Tracker
=========== ===========
Bugs are tracked on `GitHub Issues
<https://github.com/OCA/social/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.
Bugs are tracked on `GitHub Issues <https://github.com/OCA/social/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/social/issues/new?body=module:%20mail_tracking%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.
Credits Credits
======= =======
Authors
~~~~~~~
* Tecnativa
Contributors
~~~~~~~~~~~~
* `Tecnativa <https://www.tecnativa.com>`_:
* Pedro M. Baeza
* Antonio Espinosa
* David Vidal
* Ernesto Tejeda
* Rafael Blasco
Other credits
~~~~~~~~~~~~~
Images Images
------ ------
@ -88,26 +128,19 @@ Images
<https://openclipart.org>`_ for `the icon <https://openclipart.org>`_ for `the icon
<https://openclipart.org/detail/19342/open-envelope>`_. <https://openclipart.org/detail/19342/open-envelope>`_.
Contributors
------------
* `Tecnativa <https://www.tecnativa.com>`_:
Maintainers
~~~~~~~~~~~
* Pedro M. Baeza <pedro.baeza@tecnativa.com>
* Antonio Espinosa <antonio.espinosa@tecnativa.com>
* David Vidal <david.vidal@tecnativa.com>
Maintainer
----------
This module is maintained by the OCA.
.. image:: https://odoo-community.org/logo.png .. image:: https://odoo-community.org/logo.png
:alt: Odoo Community Association :alt: Odoo Community Association
:target: https://odoo-community.org :target: https://odoo-community.org
This module is maintained by the OCA.
OCA, or the Odoo Community Association, is a nonprofit organization whose 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. promote its widespread use.
To contribute to this module, please visit https://odoo-community.org.
This module is part of the `OCA/social <https://github.com/OCA/social/tree/12.0/mail_tracking>`_ project on GitHub.
You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute.

3
mail_tracking/__manifest__.py

@ -1,11 +1,12 @@
# Copyright 2016 Antonio Espinosa - <antonio.espinosa@tecnativa.com> # Copyright 2016 Antonio Espinosa - <antonio.espinosa@tecnativa.com>
# Copyright 2018 David Vidal - <david.vidal@tecnativa.com> # Copyright 2018 David Vidal - <david.vidal@tecnativa.com>
# Copyright 2018 Tecnativa - Ernesto Tejeda
# 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).
{ {
"name": "Email tracking", "name": "Email tracking",
"summary": "Email tracking system for all mails sent", "summary": "Email tracking system for all mails sent",
"version": "11.0.1.1.0",
"version": "12.0.1.0.0",
"category": "Social Network", "category": "Social Network",
"website": "http://github.com/OCA/social", "website": "http://github.com/OCA/social",
"author": "Tecnativa, " "author": "Tecnativa, "

1
mail_tracking/models/__init__.py

@ -1,6 +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 ir_mail_server from . import ir_mail_server
from . import mail_bounced_mixin
from . import mail_mail from . import mail_mail
from . import mail_message from . import mail_message
from . import mail_tracking_email from . import mail_tracking_email

1
mail_tracking/models/ir_mail_server.py

@ -80,7 +80,6 @@ class IrMailServer(models.Model):
except Exception as e: except Exception as e:
if tracking_email: if tracking_email:
tracking_email.smtp_error(self, smtp_server_used, e) tracking_email.smtp_error(self, smtp_server_used, e)
raise
if message_id and tracking_email: if message_id and tracking_email:
vals = tracking_email._tracking_sent_prepare( vals = tracking_email._tracking_sent_prepare(
self, smtp_server_used, message, message_id) self, smtp_server_used, message, message_id)

43
mail_tracking/models/mail_bounced_mixin.py

@ -0,0 +1,43 @@
# Copyright 2018 Tecnativa - Ernesto Tejeda
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
import logging
from odoo import api, fields, models
_logger = logging.getLogger(__name__)
class MailBouncedMixin(models.AbstractModel):
""" A mixin class to use if you want to add is_bounced flag on a model.
The field '_primary_email' must be overridden in the model that inherit
the mixin and must contain the email field of the model.
"""
_name = 'mail.bounced.mixin'
_description = 'Mail bounced mixin'
_primary_email = ['email']
email_bounced = fields.Boolean(index=True)
@api.multi
def email_bounced_set(self, tracking_emails, reason, event=None):
"""Inherit this method to make any other actions to the model that
inherit the mixin"""
partners = self.filtered(lambda r: not r.email_bounced)
return partners.write({'email_bounced': True})
def write(self, vals):
[email_field] = self._primary_email
if email_field not in vals:
return super(MailBouncedMixin, self).write(vals)
email = vals[email_field].lower() if vals[email_field] else False
mte_obj = self.env['mail.tracking.email']
if not mte_obj.email_is_bounced(email):
vals['email_bounced'] = False
return super(MailBouncedMixin, self).write(vals)
res = mte_obj._email_last_tracking_state(email)
tracking = mte_obj.browse(res[0].get('id'))
event = tracking.tracking_event_ids[:1]
self.email_bounced_set(tracking, event.error_details, event)
return super(MailBouncedMixin, self).write(vals)

4
mail_tracking/models/mail_mail.py

@ -27,8 +27,8 @@ class MailMail(models.Model):
'sender': self.email_from, 'sender': self.email_from,
} }
def send_get_email_dict(self, partner=None):
email = super(MailMail, self).send_get_email_dict(partner=partner)
def _send_prepare_values(self, partner=None):
email = super(MailMail, self)._send_prepare_values(partner=partner)
vals = self._tracking_email_prepare(partner, email) vals = self._tracking_email_prepare(partner, email)
tracking_email = self.env['mail.tracking.email'].sudo().create(vals) tracking_email = self.env['mail.tracking.email'].sudo().create(vals)
return tracking_email.tracking_img_add(email) return tracking_email.tracking_img_add(email)

16
mail_tracking/models/mail_tracking_email.py

@ -94,12 +94,16 @@ class MailTrackingEmail(models.Model):
@api.model @api.model
def email_is_bounced(self, email): def email_is_bounced(self, email):
if email:
return self.search_count([
('recipient_address', '=', email.lower()),
('state', 'in', ('error', 'rejected', 'spam', 'bounced')),
]) > 0
return False
if not email:
return False
res = self._email_last_tracking_state(email)
return res and res[0].get('state', '') in ['rejected', 'error',
'spam', 'bounced']
@api.model
def _email_last_tracking_state(self, email):
return self.search_read([('recipient_address', '=', email.lower())],
['state'], limit=1, order='time DESC')
@api.model @api.model
def email_score_from_email(self, email): def email_score_from_email(self, email):

21
mail_tracking/models/res_partner.py

@ -5,15 +5,13 @@ from odoo import models, api, fields
class ResPartner(models.Model): class ResPartner(models.Model):
_inherit = 'res.partner'
_name = 'res.partner'
_inherit = ['res.partner', 'mail.bounced.mixin']
# tracking_emails_count and email_score are non-store fields in order # tracking_emails_count and email_score are non-store fields in order
# to improve performance # to improve performance
# email_bounced is store=True and index=True field in order to filter
# in tree view for processing bounces easier
tracking_emails_count = fields.Integer( tracking_emails_count = fields.Integer(
compute='_compute_tracking_emails_count', readonly=True) compute='_compute_tracking_emails_count', readonly=True)
email_bounced = fields.Boolean(index=True)
email_score = fields.Float(compute='_compute_email_score', readonly=True) email_score = fields.Float(compute='_compute_email_score', readonly=True)
@api.depends('email') @api.depends('email')
@ -32,18 +30,3 @@ class ResPartner(models.Model):
('recipient_address', '=', partner.email.lower()) ('recipient_address', '=', partner.email.lower())
]) ])
partner.tracking_emails_count = count partner.tracking_emails_count = count
@api.multi
def email_bounced_set(self, tracking_emails, reason, event=None):
"""Inherit this method to make any other actions to partners"""
partners = self.filtered(lambda r: not r.email_bounced)
return partners.write({'email_bounced': True})
def write(self, vals):
email = vals.get('email')
if email is not None:
vals['email'] = email.lower() if email else False
vals['email_bounced'] = (
bool(email) and
self.env['mail.tracking.email'].email_is_bounced(email))
return super(ResPartner, self).write(vals)

7
mail_tracking/readme/CONTRIBUTORS.rst

@ -0,0 +1,7 @@
* `Tecnativa <https://www.tecnativa.com>`_:
* Pedro M. Baeza
* Antonio Espinosa
* David Vidal
* Ernesto Tejeda
* Rafael Blasco

7
mail_tracking/readme/CREDITS.rst

@ -0,0 +1,7 @@
Images
------
* Odoo Community Association: `Icon <https://github.com/OCA/maintainer-tools/blob/master/template/module/static/description/icon.svg>`_.
* Thanks to `LlubNek <https://openclipart.org/user-detail/LlubNek>`_ and `Openclipart
<https://openclipart.org>`_ for `the icon
<https://openclipart.org/detail/19342/open-envelope>`_.

3
mail_tracking/readme/DESCRIPTION.rst

@ -0,0 +1,3 @@
This module shows email notification tracking status for any messages in
mail thread (chatter). Each notified partner will have an intuitive icon just
right to his name.

5
mail_tracking/readme/INSTALL.rst

@ -0,0 +1,5 @@
If you're using a multi-database installation (with or without dbfilter option)
where /web/databse/selector returns a list of more than one database, then
you need to add ``mail_tracking`` addon to wide load addons list
(by default, only ``web`` addon), setting ``--load`` option.
For example, ``--load=web,mail_tracking``

41
mail_tracking/readme/USAGE.rst

@ -0,0 +1,41 @@
When user sends a message in mail_thread (chatter), for instance in partner
form, then an email tracking is created for each email notification. Then a
status icon will appear just right to name of notified partner.
These are all available status icons:
.. |sent| image:: mail_tracking/static/src/img/sent.png
:width: 10px
.. |delivered| image:: mail_tracking/static/src/img/delivered.png
:width: 15px
.. |opened| image:: mail_tracking/static/src/img/opened.png
:width: 15px
.. |error| image:: mail_tracking/static/src/img/error.png
:width: 10px
.. |waiting| image:: mail_tracking/static/src/img/waiting.png
:width: 10px
.. |unknown| image:: mail_tracking/static/src/img/unknown.png
:width: 10px
|unknown| **Unknown**: No email tracking info available. Maybe this notified partner has 'Receive Inbox Notifications by Email' == 'Never'
|waiting| **Waiting**: Waiting to be sent
|error| **Error**: Error while sending
|sent| **Sent**: Sent to SMTP server configured
|delivered| **Delivered**: Delivered to final MX server
|opened| **Opened**: Opened by partner
If you want to see all tracking emails and events you can go to
* Settings > Technical > Email > Tracking emails
* Settings > Technical > Email > Tracking events

470
mail_tracking/static/description/index.html

@ -0,0 +1,470 @@
<?xml version="1.0" encoding="utf-8" ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta name="generator" content="Docutils 0.14: http://docutils.sourceforge.net/" />
<title>Email tracking</title>
<style type="text/css">
/*
:Author: David Goodger (goodger@python.org)
:Id: $Id: html4css1.css 7952 2016-07-26 18:15:59Z milde $
:Copyright: This stylesheet has been placed in the public domain.
Default cascading style sheet for the HTML output of Docutils.
See http://docutils.sf.net/docs/howto/html-stylesheets.html for how to
customize this style sheet.
*/
/* used to remove borders from tables and images */
.borderless, table.borderless td, table.borderless th {
border: 0 }
table.borderless td, table.borderless th {
/* Override padding for "table.docutils td" with "! important".
The right padding separates the table cells. */
padding: 0 0.5em 0 0 ! important }
.first {
/* Override more specific margin styles with "! important". */
margin-top: 0 ! important }
.last, .with-subtitle {
margin-bottom: 0 ! important }
.hidden {
display: none }
.subscript {
vertical-align: sub;
font-size: smaller }
.superscript {
vertical-align: super;
font-size: smaller }
a.toc-backref {
text-decoration: none ;
color: black }
blockquote.epigraph {
margin: 2em 5em ; }
dl.docutils dd {
margin-bottom: 0.5em }
object[type="image/svg+xml"], object[type="application/x-shockwave-flash"] {
overflow: hidden;
}
/* Uncomment (and remove this text!) to get bold-faced definition list terms
dl.docutils dt {
font-weight: bold }
*/
div.abstract {
margin: 2em 5em }
div.abstract p.topic-title {
font-weight: bold ;
text-align: center }
div.admonition, div.attention, div.caution, div.danger, div.error,
div.hint, div.important, div.note, div.tip, div.warning {
margin: 2em ;
border: medium outset ;
padding: 1em }
div.admonition p.admonition-title, div.hint p.admonition-title,
div.important p.admonition-title, div.note p.admonition-title,
div.tip p.admonition-title {
font-weight: bold ;
font-family: sans-serif }
div.attention p.admonition-title, div.caution p.admonition-title,
div.danger p.admonition-title, div.error p.admonition-title,
div.warning p.admonition-title, .code .error {
color: red ;
font-weight: bold ;
font-family: sans-serif }
/* Uncomment (and remove this text!) to get reduced vertical space in
compound paragraphs.
div.compound .compound-first, div.compound .compound-middle {
margin-bottom: 0.5em }
div.compound .compound-last, div.compound .compound-middle {
margin-top: 0.5em }
*/
div.dedication {
margin: 2em 5em ;
text-align: center ;
font-style: italic }
div.dedication p.topic-title {
font-weight: bold ;
font-style: normal }
div.figure {
margin-left: 2em ;
margin-right: 2em }
div.footer, div.header {
clear: both;
font-size: smaller }
div.line-block {
display: block ;
margin-top: 1em ;
margin-bottom: 1em }
div.line-block div.line-block {
margin-top: 0 ;
margin-bottom: 0 ;
margin-left: 1.5em }
div.sidebar {
margin: 0 0 0.5em 1em ;
border: medium outset ;
padding: 1em ;
background-color: #ffffee ;
width: 40% ;
float: right ;
clear: right }
div.sidebar p.rubric {
font-family: sans-serif ;
font-size: medium }
div.system-messages {
margin: 5em }
div.system-messages h1 {
color: red }
div.system-message {
border: medium outset ;
padding: 1em }
div.system-message p.system-message-title {
color: red ;
font-weight: bold }
div.topic {
margin: 2em }
h1.section-subtitle, h2.section-subtitle, h3.section-subtitle,
h4.section-subtitle, h5.section-subtitle, h6.section-subtitle {
margin-top: 0.4em }
h1.title {
text-align: center }
h2.subtitle {
text-align: center }
hr.docutils {
width: 75% }
img.align-left, .figure.align-left, object.align-left, table.align-left {
clear: left ;
float: left ;
margin-right: 1em }
img.align-right, .figure.align-right, object.align-right, table.align-right {
clear: right ;
float: right ;
margin-left: 1em }
img.align-center, .figure.align-center, object.align-center {
display: block;
margin-left: auto;
margin-right: auto;
}
table.align-center {
margin-left: auto;
margin-right: auto;
}
.align-left {
text-align: left }
.align-center {
clear: both ;
text-align: center }
.align-right {
text-align: right }
/* reset inner alignment in figures */
div.align-right {
text-align: inherit }
/* div.align-center * { */
/* text-align: left } */
.align-top {
vertical-align: top }
.align-middle {
vertical-align: middle }
.align-bottom {
vertical-align: bottom }
ol.simple, ul.simple {
margin-bottom: 1em }
ol.arabic {
list-style: decimal }
ol.loweralpha {
list-style: lower-alpha }
ol.upperalpha {
list-style: upper-alpha }
ol.lowerroman {
list-style: lower-roman }
ol.upperroman {
list-style: upper-roman }
p.attribution {
text-align: right ;
margin-left: 50% }
p.caption {
font-style: italic }
p.credits {
font-style: italic ;
font-size: smaller }
p.label {
white-space: nowrap }
p.rubric {
font-weight: bold ;
font-size: larger ;
color: maroon ;
text-align: center }
p.sidebar-title {
font-family: sans-serif ;
font-weight: bold ;
font-size: larger }
p.sidebar-subtitle {
font-family: sans-serif ;
font-weight: bold }
p.topic-title {
font-weight: bold }
pre.address {
margin-bottom: 0 ;
margin-top: 0 ;
font: inherit }
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, code { background-color: #eeeeee }
pre.code .comment, code .comment { color: #5C6576 }
pre.code .keyword, code .keyword { color: #3B0D06; font-weight: bold }
pre.code .literal.string, code .literal.string { color: #0C5404 }
pre.code .name.builtin, code .name.builtin { color: #352B84 }
pre.code .deleted, code .deleted { background-color: #DEB0A1}
pre.code .inserted, code .inserted { background-color: #A3D289}
span.classifier {
font-family: sans-serif ;
font-style: oblique }
span.classifier-delimiter {
font-family: sans-serif ;
font-weight: bold }
span.interpreted {
font-family: sans-serif }
span.option {
white-space: nowrap }
span.pre {
white-space: pre }
span.problematic {
color: red }
span.section-subtitle {
/* font-size relative to parent (h1..h6 element) */
font-size: 80% }
table.citation {
border-left: solid 1px gray;
margin-left: 1px }
table.docinfo {
margin: 2em 4em }
table.docutils {
margin-top: 0.5em ;
margin-bottom: 0.5em }
table.footnote {
border-left: solid 1px black;
margin-left: 1px }
table.docutils td, table.docutils th,
table.docinfo td, table.docinfo th {
padding-left: 0.5em ;
padding-right: 0.5em ;
vertical-align: top }
table.docutils th.field-name, table.docinfo th.docinfo-name {
font-weight: bold ;
text-align: left ;
white-space: nowrap ;
padding-left: 0 }
/* "booktabs" style (no vertical lines) */
table.docutils.booktabs {
border: 0px;
border-top: 2px solid;
border-bottom: 2px solid;
border-collapse: collapse;
}
table.docutils.booktabs * {
border: 0px;
}
table.docutils.booktabs th {
border-bottom: thin solid;
text-align: left;
}
h1 tt.docutils, h2 tt.docutils, h3 tt.docutils,
h4 tt.docutils, h5 tt.docutils, h6 tt.docutils {
font-size: 100% }
ul.auto-toc {
list-style-type: none }
</style>
</head>
<body>
<div class="document" id="email-tracking">
<h1 class="title">Email tracking</h1>
<!-- !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!! 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="Beta" src="https://img.shields.io/badge/maturity-Beta-yellow.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/social/tree/12.0/mail_tracking"><img alt="OCA/social" src="https://img.shields.io/badge/github-OCA%2Fsocial-lightgray.png?logo=github" /></a> <a class="reference external" href="https://translation.odoo-community.org/projects/social-12-0/social-12-0-mail_tracking"><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/205/12.0"><img alt="Try me on Runbot" src="https://img.shields.io/badge/runbot-Try%20me-875A7B.png" /></a></p>
<p>This module shows email notification tracking status for any messages in
mail thread (chatter). Each notified partner will have an intuitive icon just
right to his name.</p>
<p><strong>Table of contents</strong></p>
<div class="contents local topic" id="contents">
<ul class="simple">
<li><a class="reference internal" href="#installation" id="id1">Installation</a></li>
<li><a class="reference internal" href="#usage" id="id2">Usage</a></li>
<li><a class="reference internal" href="#bug-tracker" id="id3">Bug Tracker</a></li>
<li><a class="reference internal" href="#credits" id="id4">Credits</a><ul>
<li><a class="reference internal" href="#authors" id="id5">Authors</a></li>
<li><a class="reference internal" href="#contributors" id="id6">Contributors</a></li>
<li><a class="reference internal" href="#other-credits" id="id7">Other credits</a><ul>
<li><a class="reference internal" href="#images" id="id8">Images</a></li>
</ul>
</li>
<li><a class="reference internal" href="#maintainers" id="id9">Maintainers</a></li>
</ul>
</li>
</ul>
</div>
<div class="section" id="installation">
<h1><a class="toc-backref" href="#id1">Installation</a></h1>
<p>If you’re using a multi-database installation (with or without dbfilter option)
where /web/databse/selector returns a list of more than one database, then
you need to add <tt class="docutils literal">mail_tracking</tt> addon to wide load addons list
(by default, only <tt class="docutils literal">web</tt> addon), setting <tt class="docutils literal"><span class="pre">--load</span></tt> option.
For example, <tt class="docutils literal"><span class="pre">--load=web,mail_tracking</span></tt></p>
</div>
<div class="section" id="usage">
<h1><a class="toc-backref" href="#id2">Usage</a></h1>
<p>When user sends a message in mail_thread (chatter), for instance in partner
form, then an email tracking is created for each email notification. Then a
status icon will appear just right to name of notified partner.</p>
<p>These are all available status icons:</p>
<p><img alt="unknown" src="mail_tracking/static/src/img/unknown.png" style="width: 10px;" /> <strong>Unknown</strong>: No email tracking info available. Maybe this notified partner has ‘Receive Inbox Notifications by Email’ == ‘Never’</p>
<p><img alt="waiting" src="mail_tracking/static/src/img/waiting.png" style="width: 10px;" /> <strong>Waiting</strong>: Waiting to be sent</p>
<p><img alt="error" src="mail_tracking/static/src/img/error.png" style="width: 10px;" /> <strong>Error</strong>: Error while sending</p>
<p><img alt="sent" src="mail_tracking/static/src/img/sent.png" style="width: 10px;" /> <strong>Sent</strong>: Sent to SMTP server configured</p>
<p><img alt="delivered" src="mail_tracking/static/src/img/delivered.png" style="width: 15px;" /> <strong>Delivered</strong>: Delivered to final MX server</p>
<p><img alt="opened" src="mail_tracking/static/src/img/opened.png" style="width: 15px;" /> <strong>Opened</strong>: Opened by partner</p>
<p>If you want to see all tracking emails and events you can go to</p>
<ul class="simple">
<li>Settings &gt; Technical &gt; Email &gt; Tracking emails</li>
<li>Settings &gt; Technical &gt; Email &gt; Tracking events</li>
</ul>
</div>
<div class="section" id="bug-tracker">
<h1><a class="toc-backref" href="#id3">Bug Tracker</a></h1>
<p>Bugs are tracked on <a class="reference external" href="https://github.com/OCA/social/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/social/issues/new?body=module:%20mail_tracking%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">
<h1><a class="toc-backref" href="#id4">Credits</a></h1>
<div class="section" id="authors">
<h2><a class="toc-backref" href="#id5">Authors</a></h2>
<ul class="simple">
<li>Tecnativa</li>
</ul>
</div>
<div class="section" id="contributors">
<h2><a class="toc-backref" href="#id6">Contributors</a></h2>
<ul class="simple">
<li><a class="reference external" href="https://www.tecnativa.com">Tecnativa</a>:<ul>
<li>Pedro M. Baeza</li>
<li>Antonio Espinosa</li>
<li>David Vidal</li>
<li>Ernesto Tejeda</li>
<li>Rafael Blasco</li>
</ul>
</li>
</ul>
</div>
<div class="section" id="other-credits">
<h2><a class="toc-backref" href="#id7">Other credits</a></h2>
<div class="section" id="images">
<h3><a class="toc-backref" href="#id8">Images</a></h3>
<ul class="simple">
<li>Odoo Community Association: <a class="reference external" href="https://github.com/OCA/maintainer-tools/blob/master/template/module/static/description/icon.svg">Icon</a>.</li>
<li>Thanks to <a class="reference external" href="https://openclipart.org/user-detail/LlubNek">LlubNek</a> and <a class="reference external" href="https://openclipart.org">Openclipart</a> for <a class="reference external" href="https://openclipart.org/detail/19342/open-envelope">the icon</a>.</li>
</ul>
</div>
</div>
<div class="section" id="maintainers">
<h2><a class="toc-backref" href="#id9">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>
<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/social/tree/12.0/mail_tracking">OCA/social</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>
</div>
</body>
</html>

65
mail_tracking/static/src/js/mail_tracking.js

@ -6,33 +6,60 @@ odoo.define('mail_tracking.partner_tracking', function(require){
"use strict"; "use strict";
var core = require('web.core'); var core = require('web.core');
var session = require('web.session');
var data = require('web.data');
var ActionManager = require('web.ActionManager'); var ActionManager = require('web.ActionManager');
var chat_manager = require('mail.chat_manager');
var ChatThread = require('mail.ChatThread');
var Chatter = require('mail.Chatter');
var AbstractMessage = require('mail.model.AbstractMessage');
var Message = require('mail.model.Message');
var ThreadWidget = require('mail.widget.Thread');
var _t = core._t; var _t = core._t;
// chat_manager is a simple dictionary, not an OdooClass
chat_manager._make_message_super = chat_manager.make_message;
chat_manager.make_message = function(data) {
var msg = this._make_message_super(data);
msg.partner_trackings = data.partner_trackings || [];
return msg;
};
AbstractMessage.include({
/**
* Messages do not have any PartnerTrackings.
*
* @return {boolean}
*/
hasPartnerTrackings: function () {
return false;
},
});
Message.include({
init: function (parent, data, emojis) {
this._super.apply(this, arguments);
this._partnerTrackings = data.partner_trackings || [];
},
ChatThread.include({
events: _.extend(ChatThread.prototype.events, {
/**
* State whether this message contains some PartnerTrackings values
*
* @override
* @return {boolean}
*/
hasPartnerTrackings: function () {
return !!(this._partnerTrackings && (this._partnerTrackings.length > 0));
},
/**
* Get the PartnerTrackings values of this message
* If this message has no PartnerTrackings values, returns []
*
* @override
* @return {Object[]}
*/
getPartnerTrackings: function () {
if (!this.hasPartnerTrackings()) {
return [];
}
return this._partnerTrackings;
},
});
ThreadWidget.include({
events: _.extend(ThreadWidget.prototype.events, {
'click .o_mail_action_tracking_partner': 'on_tracking_partner_click', 'click .o_mail_action_tracking_partner': 'on_tracking_partner_click',
'click .o_mail_action_tracking_status': 'on_tracking_status_click', 'click .o_mail_action_tracking_status': 'on_tracking_status_click',
}), }),
_preprocess_message: function (message) {
var msg = this._super.apply(this, arguments);
msg.partner_trackings = msg.partner_trackings || [];
return msg;
},
on_tracking_partner_click: function (event) { on_tracking_partner_click: function (event) {
var partner_id = this.$el.find(event.currentTarget).data('partner'); var partner_id = this.$el.find(event.currentTarget).data('partner');
var state = { var state = {

54
mail_tracking/static/src/xml/mail_tracking.xml

@ -40,33 +40,35 @@
</t> </t>
</t> </t>
<t t-extend="mail.ChatThread.Message">
<t t-jquery="p[class='o_mail_info']" t-operation="after">
<p class="o_mail_tracking">
<strong>To:</strong>
<t t-set="first_tracking" t-value="true"/>
<t t-foreach="message.partner_trackings" t-as="tracking">
<t t-if="!first_tracking">
-
<t t-extend="mail.widget.Thread.Message">
<t t-jquery="p.o_mail_info" t-operation="after">
<t t-if="message.hasPartnerTrackings()" >
<p class="o_mail_tracking">
<strong>To:</strong>
<t t-set="first_tracking" t-value="true"/>
<t t-foreach="message.getPartnerTrackings()" t-as="tracking">
<t t-if="!first_tracking">
-
</t>
<t t-if="tracking[3]">
<a class="o_mail_action_tracking_partner"
t-att-data-partner="tracking[3]"
t-attf-href="#model=res.partner&amp;id=#{tracking[3]}">
<t t-esc="tracking[2]"/>
</a>
</t>
<t t-if="!tracking[3]">
<span><t t-esc="tracking[2]"/></span>
</t>
<span class="mail_tracking o_mail_action_tracking_status"
t-att-data-tracking="tracking[1]"
t-attf-title="Status: #{tracking[0]}">
<t t-call="mail.tracking.status"/>
</span>
<t t-set="first_tracking" t-value="false"/>
</t> </t>
<t t-if="tracking[3]">
<a class="o_mail_action_tracking_partner"
t-att-data-partner="tracking[3]"
t-attf-href="#model=res.partner&amp;id=#{tracking[3]}">
<t t-esc="tracking[2]"/>
</a>
</t>
<t t-if="!tracking[3]">
<span><t t-esc="tracking[2]"/></span>
</t>
<span class="mail_tracking o_mail_action_tracking_status"
t-att-data-tracking="tracking[1]"
t-attf-title="Status: #{tracking[0]}">
<t t-call="mail.tracking.status"/>
</span>
<t t-set="first_tracking" t-value="false"/>
</t>
</p>
</p>
</t>
</t> </t>
</t> </t>

2
mail_tracking/tests/__init__.py

@ -1,6 +1,4 @@
# -*- coding: utf-8 -*-
# Copyright 2016 Antonio Espinosa - <antonio.espinosa@tecnativa.com> # Copyright 2016 Antonio Espinosa - <antonio.espinosa@tecnativa.com>
# 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).
# flake8: noqa
from . import test_mail_tracking from . import test_mail_tracking

18
mail_tracking/tests/test_mail_tracking.py

@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
# Copyright 2016 Antonio Espinosa - <antonio.espinosa@tecnativa.com> # Copyright 2016 Antonio Espinosa - <antonio.espinosa@tecnativa.com>
# 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).
@ -9,7 +8,7 @@ from odoo import http
from odoo.tests.common import TransactionCase from odoo.tests.common import TransactionCase
from ..controllers.main import MailTrackingController, BLANK from ..controllers.main import MailTrackingController, BLANK
mock_send_email = ('odoo.addons.base.ir.ir_mail_server.'
mock_send_email = ('odoo.addons.base.models.ir_mail_server.'
'IrMailServer.send_email') 'IrMailServer.send_email')
@ -50,10 +49,6 @@ class TestMailTracking(TransactionCase):
http.request = self.last_request http.request = self.last_request
return super(TestMailTracking, self).tearDown(*args, **kwargs) return super(TestMailTracking, self).tearDown(*args, **kwargs)
def test_email_lower(self):
self.recipient.write({'email': 'UPPER@example.com'})
self.assertEqual('upper@example.com', self.recipient.email)
def test_empty_email(self): def test_empty_email(self):
self.recipient.write({'email_bounced': True}) self.recipient.write({'email_bounced': True})
self.recipient.write({'email': False}) self.recipient.write({'email': False})
@ -61,7 +56,6 @@ class TestMailTracking(TransactionCase):
self.assertEqual(False, self.recipient.email_bounced) self.assertEqual(False, self.recipient.email_bounced)
self.recipient.write({'email_bounced': True}) self.recipient.write({'email_bounced': True})
self.recipient.write({'email': ''}) self.recipient.write({'email': ''})
self.assertEqual(False, self.recipient.email)
self.assertEqual(False, self.recipient.email_bounced) self.assertEqual(False, self.recipient.email_bounced)
self.assertEqual( self.assertEqual(
False, False,
@ -87,6 +81,7 @@ class TestMailTracking(TransactionCase):
'partner_ids': [(4, self.recipient.id)], 'partner_ids': [(4, self.recipient.id)],
'body': '<p>This is a test message</p>', 'body': '<p>This is a test message</p>',
}) })
message._notify(message, {}, force_send=True)
# Search tracking created # Search tracking created
tracking_email = self.env['mail.tracking.email'].search([ tracking_email = self.env['mail.tracking.email'].search([
('mail_message_id', '=', message.id), ('mail_message_id', '=', message.id),
@ -303,6 +298,15 @@ class TestMailTracking(TransactionCase):
self.assertEqual('bounced', tracking.state) self.assertEqual('bounced', tracking.state)
self.assertEqual(0.0, self.recipient.email_score) self.assertEqual(0.0, self.recipient.email_score)
def test_bounce_new_partner(self):
mail, tracking = self.mail_send(self.recipient.email)
tracking.event_create('hard_bounce', {})
new_partner = self.env['res.partner'].create({
'name': 'Test New Partner',
})
new_partner.email = self.recipient.email
self.assertTrue(new_partner.email_bounced)
def test_recordset_email_score(self): def test_recordset_email_score(self):
"""For backwords compatibility sake""" """For backwords compatibility sake"""
trackings = self.env['mail.tracking.email'] trackings = self.env['mail.tracking.email']

8
mail_tracking/views/mail_tracking_email_view.xml

@ -94,10 +94,10 @@
domain="[('state', 'in', ('error', 'rejected', 'spam', 'bounced', 'soft-bounced'))]"/> domain="[('state', 'in', ('error', 'rejected', 'spam', 'bounced', 'soft-bounced'))]"/>
<separator/> <separator/>
<group expand="0" string="Group By"> <group expand="0" string="Group By">
<filter string="State" domain="[]" context="{'group_by': 'state'}"/>
<filter string="Subject" domain="[]" context="{'group_by': 'name'}"/>
<filter string="Sender" domain="[]" context="{'group_by': 'sender'}"/>
<filter string="Month" domain="[]" context="{'group_by': 'date'}"/>
<filter string="State" name="group_by_state" domain="[]" context="{'group_by': 'state'}"/>
<filter string="Subject" name="group_by_subject" domain="[]" context="{'group_by': 'name'}"/>
<filter string="Sender" name="group_by_sender" domain="[]" context="{'group_by': 'sender'}"/>
<filter string="Month" name="group_by_month" domain="[]" context="{'group_by': 'date'}"/>
</group> </group>
</search> </search>
</field> </field>

14
mail_tracking/views/mail_tracking_event_view.xml

@ -96,13 +96,13 @@
context="{'event_error_filter': True}"/> context="{'event_error_filter': True}"/>
<separator/> <separator/>
<group expand="0" string="Group By"> <group expand="0" string="Group By">
<filter string="Type" domain="[]" context="{'group_by': 'event_type'}"/>
<filter string="Message" domain="[]" context="{'group_by': 'tracking_email_id'}"/>
<filter string="OS" domain="[('os_family', '!=', False)]" context="{'group_by': 'os_family'}"/>
<filter string="User agent" domain="[('ua_family', '!=', False)]" context="{'group_by': 'ua_family'}"/>
<filter string="User agent type" domain="[('ua_type', '!=', False)]" context="{'group_by': 'ua_type'}"/>
<filter string="Country" domain="[('user_country_id', '!=', False)]" context="{'group_by': 'user_country_id'}"/>
<filter string="Month" domain="[]" context="{'group_by': 'date'}"/>
<filter string="Type" name="group_by_type" domain="[]" context="{'group_by': 'event_type'}"/>
<filter string="Message" name="group_by_message" domain="[]" context="{'group_by': 'tracking_email_id'}"/>
<filter string="OS" name="group_by_os" domain="[('os_family', '!=', False)]" context="{'group_by': 'os_family'}"/>
<filter string="User agent" name="group_by_user_agent" domain="[('ua_family', '!=', False)]" context="{'group_by': 'ua_family'}"/>
<filter string="User agent type" name="group_by_user_agent_type" domain="[('ua_type', '!=', False)]" context="{'group_by': 'ua_type'}"/>
<filter string="Country" name="group_by_country" domain="[('user_country_id', '!=', False)]" context="{'group_by': 'user_country_id'}"/>
<filter string="Month" name="group_by_date" domain="[]" context="{'group_by': 'date'}"/>
</group> </group>
</search> </search>
</field> </field>

6
mail_tracking/views/res_partner_view.xml

@ -6,7 +6,7 @@
<record model="ir.ui.view" id="view_partner_form"> <record model="ir.ui.view" id="view_partner_form">
<field name="name">Partner Form with tracking emails</field> <field name="name">Partner Form with tracking emails</field>
<field name="model">res.partner</field> <field name="model">res.partner</field>
<field name="sequence">46</field>
<field name="priority">46</field>
<field name="inherit_id" ref="base.view_partner_form"/> <field name="inherit_id" ref="base.view_partner_form"/>
<field name="arch" type="xml"> <field name="arch" type="xml">
<div name="button_box" position="inside"> <div name="button_box" position="inside">
@ -22,12 +22,12 @@
string="Tracking emails"/> string="Tracking emails"/>
</button> </button>
</div> </div>
<field name="email" position="after">
<xpath expr="//field[@name='email']/.." position="after">
<field name="email_score" widget="progressbar" <field name="email_score" widget="progressbar"
attrs="{'invisible': [('email', '=', False)]}"/> attrs="{'invisible': [('email', '=', False)]}"/>
<field name="email_bounced" <field name="email_bounced"
attrs="{'invisible': [('email', '=', False)]}"/> attrs="{'invisible': [('email', '=', False)]}"/>
</field>
</xpath>
</field> </field>
</record> </record>

Loading…
Cancel
Save