mirror of https://github.com/OCA/social.git
Merge pull request #77 from Tecnativa/8.0-mail_tracking_email_stars
[IMP][mail_tracking] Email stars and generic webhookpull/79/head
commit
b8f2336d28
|
@ -5,3 +5,4 @@
|
||||||
|
|
||||||
from . import models
|
from . import models
|
||||||
from . import controllers
|
from . import controllers
|
||||||
|
from .hooks import post_init_hook
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
{
|
{
|
||||||
"name": "Email tracking",
|
"name": "Email tracking",
|
||||||
"summary": "Email tracking system for all mails sent",
|
"summary": "Email tracking system for all mails sent",
|
||||||
"version": "8.0.1.0.0",
|
"version": "8.0.2.0.0",
|
||||||
"category": "Social Network",
|
"category": "Social Network",
|
||||||
"website": "http://www.tecnativa.com",
|
"website": "http://www.tecnativa.com",
|
||||||
"author": "Tecnativa, "
|
"author": "Tecnativa, "
|
||||||
|
@ -23,8 +23,10 @@
|
||||||
"views/assets.xml",
|
"views/assets.xml",
|
||||||
"views/mail_tracking_email_view.xml",
|
"views/mail_tracking_email_view.xml",
|
||||||
"views/mail_tracking_event_view.xml",
|
"views/mail_tracking_event_view.xml",
|
||||||
|
"views/res_partner_view.xml",
|
||||||
],
|
],
|
||||||
"qweb": [
|
"qweb": [
|
||||||
"static/src/xml/mail_tracking.xml",
|
"static/src/xml/mail_tracking.xml",
|
||||||
]
|
],
|
||||||
|
"post_init_hook": "post_init_hook",
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,6 +11,19 @@ _logger = logging.getLogger(__name__)
|
||||||
BLANK = 'R0lGODlhAQABAIAAANvf7wAAACH5BAEAAAAALAAAAAABAAEAAAICRAEAOw=='
|
BLANK = 'R0lGODlhAQABAIAAANvf7wAAACH5BAEAAAAALAAAAAABAAEAAAICRAEAOw=='
|
||||||
|
|
||||||
|
|
||||||
|
def _env_get(db):
|
||||||
|
reg = False
|
||||||
|
try:
|
||||||
|
reg = registry(db)
|
||||||
|
except OperationalError:
|
||||||
|
_logger.warning("Selected BD '%s' not found", db)
|
||||||
|
except:
|
||||||
|
_logger.warning("Selected BD '%s' connection error", db)
|
||||||
|
if reg:
|
||||||
|
return api.Environment(reg.cursor(), SUPERUSER_ID, {})
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
class MailTrackingController(http.Controller):
|
class MailTrackingController(http.Controller):
|
||||||
|
|
||||||
def _request_metadata(self):
|
def _request_metadata(self):
|
||||||
|
@ -22,29 +35,49 @@ class MailTrackingController(http.Controller):
|
||||||
'ua_family': request.user_agent.browser,
|
'ua_family': request.user_agent.browser,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@http.route('/mail/tracking/all/<string:db>',
|
||||||
|
type='http', auth='none')
|
||||||
|
def mail_tracking_all(self, db, **kw):
|
||||||
|
env = _env_get(db)
|
||||||
|
if not env:
|
||||||
|
return 'NOT FOUND'
|
||||||
|
metadata = self._request_metadata()
|
||||||
|
response = env['mail.tracking.email'].event_process(
|
||||||
|
http.request, kw, metadata)
|
||||||
|
env.cr.commit()
|
||||||
|
env.cr.close()
|
||||||
|
return response
|
||||||
|
|
||||||
|
@http.route('/mail/tracking/event/<string:db>/<string:event_type>',
|
||||||
|
type='http', auth='none')
|
||||||
|
def mail_tracking_event(self, db, event_type, **kw):
|
||||||
|
env = _env_get(db)
|
||||||
|
if not env:
|
||||||
|
return 'NOT FOUND'
|
||||||
|
metadata = self._request_metadata()
|
||||||
|
response = env['mail.tracking.email'].event_process(
|
||||||
|
http.request, kw, metadata, event_type=event_type)
|
||||||
|
env.cr.commit()
|
||||||
|
env.cr.close()
|
||||||
|
return response
|
||||||
|
|
||||||
@http.route('/mail/tracking/open/<string:db>'
|
@http.route('/mail/tracking/open/<string:db>'
|
||||||
'/<int:tracking_email_id>/blank.gif',
|
'/<int:tracking_email_id>/blank.gif',
|
||||||
type='http', auth='none')
|
type='http', auth='none')
|
||||||
def mail_tracking_open(self, db, tracking_email_id, **kw):
|
def mail_tracking_open(self, db, tracking_email_id, **kw):
|
||||||
reg = False
|
env = _env_get(db)
|
||||||
try:
|
if env:
|
||||||
reg = registry(db)
|
tracking_email = env['mail.tracking.email'].search([
|
||||||
except OperationalError:
|
('id', '=', tracking_email_id),
|
||||||
_logger.warning("Selected BD '%s' not found", db)
|
])
|
||||||
except:
|
if tracking_email:
|
||||||
_logger.warning("Selected BD '%s' connection error", db)
|
metadata = self._request_metadata()
|
||||||
if reg:
|
tracking_email.event_create('open', metadata)
|
||||||
with reg.cursor() as cr:
|
else:
|
||||||
env = api.Environment(cr, SUPERUSER_ID, {})
|
_logger.warning(
|
||||||
tracking_email = env['mail.tracking.email'].search([
|
"MailTracking email '%s' not found", tracking_email_id)
|
||||||
('id', '=', tracking_email_id),
|
env.cr.commit()
|
||||||
])
|
env.cr.close()
|
||||||
if tracking_email:
|
|
||||||
metadata = self._request_metadata()
|
|
||||||
tracking_email.event_process('open', metadata)
|
|
||||||
else:
|
|
||||||
_logger.warning(
|
|
||||||
"MailTracking email '%s' not found", tracking_email_id)
|
|
||||||
|
|
||||||
# Always return GIF blank image
|
# Always return GIF blank image
|
||||||
response = werkzeug.wrappers.Response()
|
response = werkzeug.wrappers.Response()
|
||||||
|
|
|
@ -0,0 +1,24 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# © 2016 Antonio Espinosa - <antonio.espinosa@tecnativa.com>
|
||||||
|
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
|
||||||
|
|
||||||
|
import logging
|
||||||
|
from openerp import api, SUPERUSER_ID
|
||||||
|
|
||||||
|
_logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
def post_init_hook(cr, registry):
|
||||||
|
with api.Environment.manage():
|
||||||
|
env = api.Environment(cr, SUPERUSER_ID, {})
|
||||||
|
# Recalculate all partner tracking_email_ids
|
||||||
|
partners = env['res.partner'].search([
|
||||||
|
('email', '!=', False),
|
||||||
|
])
|
||||||
|
emails = partners.mapped('email')
|
||||||
|
_logger.info(
|
||||||
|
"Recalculating 'tracking_email_ids' in 'res.partner' "
|
||||||
|
"model for %d email addresses", len(emails))
|
||||||
|
for email in emails:
|
||||||
|
env['mail.tracking.email'].tracking_ids_recalculate(
|
||||||
|
'res.partner', 'email', 'tracking_email_ids', email)
|
|
@ -8,3 +8,4 @@ from . import mail_mail
|
||||||
from . import mail_message
|
from . import mail_message
|
||||||
from . import mail_tracking_email
|
from . import mail_tracking_email
|
||||||
from . import mail_tracking_event
|
from . import mail_tracking_event
|
||||||
|
from . import res_partner
|
||||||
|
|
|
@ -24,7 +24,7 @@ class IrMailServer(models.Model):
|
||||||
if match:
|
if match:
|
||||||
try:
|
try:
|
||||||
tracking_email_id = int(match.group(1))
|
tracking_email_id = int(match.group(1))
|
||||||
except:
|
except: # pragma: no cover
|
||||||
pass
|
pass
|
||||||
return tracking_email_id
|
return tracking_email_id
|
||||||
|
|
||||||
|
@ -61,7 +61,7 @@ class IrMailServer(models.Model):
|
||||||
mail_server = mail_server_ids[0] if mail_server_ids else None
|
mail_server = mail_server_ids[0] if mail_server_ids else None
|
||||||
if mail_server:
|
if mail_server:
|
||||||
smtp_server_used = mail_server.smtp_host
|
smtp_server_used = mail_server.smtp_host
|
||||||
else:
|
else: # pragma: no cover
|
||||||
smtp_server_used = smtp_server or tools.config.get('smtp_server')
|
smtp_server_used = smtp_server or tools.config.get('smtp_server')
|
||||||
return smtp_server_used
|
return smtp_server_used
|
||||||
|
|
||||||
|
|
|
@ -5,6 +5,7 @@
|
||||||
import logging
|
import logging
|
||||||
import urlparse
|
import urlparse
|
||||||
import time
|
import time
|
||||||
|
import re
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
|
|
||||||
from openerp import models, api, fields, tools
|
from openerp import models, api, fields, tools
|
||||||
|
@ -16,10 +17,13 @@ _logger = logging.getLogger(__name__)
|
||||||
class MailTrackingEmail(models.Model):
|
class MailTrackingEmail(models.Model):
|
||||||
_name = "mail.tracking.email"
|
_name = "mail.tracking.email"
|
||||||
_order = 'time desc'
|
_order = 'time desc'
|
||||||
_rec_name = 'name'
|
_rec_name = 'display_name'
|
||||||
_description = 'MailTracking email'
|
_description = 'MailTracking email'
|
||||||
|
|
||||||
name = fields.Char(string="Subject", readonly=True, index=True)
|
name = fields.Char(string="Subject", readonly=True, index=True)
|
||||||
|
display_name = fields.Char(
|
||||||
|
string="Display name", readonly=True, store=True,
|
||||||
|
compute="_compute_display_name")
|
||||||
timestamp = fields.Float(
|
timestamp = fields.Float(
|
||||||
string='UTC timestamp', readonly=True,
|
string='UTC timestamp', readonly=True,
|
||||||
digits=dp.get_precision('MailTracking Timestamp'))
|
digits=dp.get_precision('MailTracking Timestamp'))
|
||||||
|
@ -33,6 +37,9 @@ class MailTrackingEmail(models.Model):
|
||||||
partner_id = fields.Many2one(
|
partner_id = fields.Many2one(
|
||||||
string="Partner", comodel_name='res.partner', readonly=True)
|
string="Partner", comodel_name='res.partner', readonly=True)
|
||||||
recipient = fields.Char(string='Recipient email', readonly=True)
|
recipient = fields.Char(string='Recipient email', readonly=True)
|
||||||
|
recipient_address = fields.Char(
|
||||||
|
string='Recipient email address', readonly=True, store=True,
|
||||||
|
compute='_compute_recipient_address')
|
||||||
sender = fields.Char(string='Sender email', readonly=True)
|
sender = fields.Char(string='Sender email', readonly=True)
|
||||||
state = fields.Selection([
|
state = fields.Selection([
|
||||||
('error', 'Error'),
|
('error', 'Error'),
|
||||||
|
@ -77,6 +84,84 @@ class MailTrackingEmail(models.Model):
|
||||||
string="Tracking events", comodel_name='mail.tracking.event',
|
string="Tracking events", comodel_name='mail.tracking.event',
|
||||||
inverse_name='tracking_email_id', readonly=True)
|
inverse_name='tracking_email_id', readonly=True)
|
||||||
|
|
||||||
|
@api.model
|
||||||
|
def tracking_ids_recalculate(self, model, email_field, tracking_field,
|
||||||
|
email, new_tracking=None):
|
||||||
|
objects = self.env[model].search([
|
||||||
|
(email_field, '=ilike', email),
|
||||||
|
])
|
||||||
|
for obj in objects:
|
||||||
|
trackings = obj[tracking_field]
|
||||||
|
if new_tracking:
|
||||||
|
trackings |= new_tracking
|
||||||
|
trackings = trackings._email_score_tracking_filter()
|
||||||
|
if set(obj[tracking_field].ids) != set(trackings.ids):
|
||||||
|
if trackings:
|
||||||
|
obj.write({
|
||||||
|
tracking_field: [(6, False, trackings.ids)]
|
||||||
|
})
|
||||||
|
else:
|
||||||
|
obj.write({
|
||||||
|
tracking_field: [(5, False, False)]
|
||||||
|
})
|
||||||
|
return True
|
||||||
|
|
||||||
|
@api.model
|
||||||
|
def _tracking_ids_to_write(self, email):
|
||||||
|
trackings = self.env['mail.tracking.email'].search([
|
||||||
|
('recipient_address', '=ilike', email)
|
||||||
|
])
|
||||||
|
trackings = trackings._email_score_tracking_filter()
|
||||||
|
if trackings:
|
||||||
|
return [(6, False, trackings.ids)]
|
||||||
|
else:
|
||||||
|
return [(5, False, False)]
|
||||||
|
|
||||||
|
@api.multi
|
||||||
|
def _email_score_tracking_filter(self):
|
||||||
|
"""Default email score filter for tracking emails"""
|
||||||
|
# Consider only last 10 tracking emails
|
||||||
|
return self.sorted(key=lambda r: r.time, reverse=True)[:10]
|
||||||
|
|
||||||
|
@api.multi
|
||||||
|
def email_score(self):
|
||||||
|
"""Default email score algorimth"""
|
||||||
|
score = 50.0
|
||||||
|
trackings = self._email_score_tracking_filter()
|
||||||
|
for tracking in trackings:
|
||||||
|
if tracking.state in ('error',):
|
||||||
|
score -= 50.0
|
||||||
|
elif tracking.state in ('rejected', 'spam', 'bounced'):
|
||||||
|
score -= 25.0
|
||||||
|
elif tracking.state in ('soft-bounced', 'unsub'):
|
||||||
|
score -= 10.0
|
||||||
|
elif tracking.state in ('delivered',):
|
||||||
|
score += 5.0
|
||||||
|
elif tracking.state in ('opened',):
|
||||||
|
score += 10.0
|
||||||
|
if score > 100.0:
|
||||||
|
score = 100.0
|
||||||
|
return score
|
||||||
|
|
||||||
|
@api.multi
|
||||||
|
@api.depends('recipient')
|
||||||
|
def _compute_recipient_address(self):
|
||||||
|
for email in self:
|
||||||
|
matches = re.search(r'<(.*@.*)>', email.recipient)
|
||||||
|
if matches:
|
||||||
|
email.recipient_address = matches.group(1)
|
||||||
|
else:
|
||||||
|
email.recipient_address = email.recipient
|
||||||
|
|
||||||
|
@api.multi
|
||||||
|
@api.depends('name', 'recipient')
|
||||||
|
def _compute_display_name(self):
|
||||||
|
for email in self:
|
||||||
|
parts = [email.name]
|
||||||
|
if email.recipient:
|
||||||
|
parts.append(email.recipient)
|
||||||
|
email.display_name = ' - '.join(parts)
|
||||||
|
|
||||||
@api.multi
|
@api.multi
|
||||||
@api.depends('time')
|
@api.depends('time')
|
||||||
def _compute_date(self):
|
def _compute_date(self):
|
||||||
|
@ -84,6 +169,14 @@ class MailTrackingEmail(models.Model):
|
||||||
email.date = fields.Date.to_string(
|
email.date = fields.Date.to_string(
|
||||||
fields.Date.from_string(email.time))
|
fields.Date.from_string(email.time))
|
||||||
|
|
||||||
|
@api.model
|
||||||
|
def create(self, vals):
|
||||||
|
tracking = super(MailTrackingEmail, self).create(vals)
|
||||||
|
self.tracking_ids_recalculate(
|
||||||
|
'res.partner', 'email', 'tracking_email_ids',
|
||||||
|
tracking.recipient_address, new_tracking=tracking)
|
||||||
|
return tracking
|
||||||
|
|
||||||
def _get_mail_tracking_img(self):
|
def _get_mail_tracking_img(self):
|
||||||
base_url = self.env['ir.config_parameter'].get_param('web.base.url')
|
base_url = self.env['ir.config_parameter'].get_param('web.base.url')
|
||||||
path_url = (
|
path_url = (
|
||||||
|
@ -159,15 +252,23 @@ class MailTrackingEmail(models.Model):
|
||||||
method = getattr(m_event, 'process_' + event_type, None)
|
method = getattr(m_event, 'process_' + event_type, None)
|
||||||
if method and hasattr(method, '__call__'):
|
if method and hasattr(method, '__call__'):
|
||||||
return method(self, metadata)
|
return method(self, metadata)
|
||||||
else:
|
else: # pragma: no cover
|
||||||
_logger.info('Unknown event type: %s' % event_type)
|
_logger.info('Unknown event type: %s' % event_type)
|
||||||
return False
|
return False
|
||||||
|
|
||||||
@api.multi
|
@api.multi
|
||||||
def event_process(self, event_type, metadata):
|
def event_create(self, event_type, metadata):
|
||||||
event_ids = self.env['mail.tracking.event']
|
event_ids = self.env['mail.tracking.event']
|
||||||
for tracking_email in self:
|
for tracking_email in self:
|
||||||
vals = tracking_email._event_prepare(event_type, metadata)
|
vals = tracking_email._event_prepare(event_type, metadata)
|
||||||
if vals:
|
if vals:
|
||||||
event_ids += event_ids.sudo().create(vals)
|
event_ids += event_ids.sudo().create(vals)
|
||||||
return event_ids
|
return event_ids
|
||||||
|
|
||||||
|
@api.model
|
||||||
|
def event_process(self, request, post, metadata, event_type=None):
|
||||||
|
# Generic event process hook, inherit it and
|
||||||
|
# - return 'OK' if processed
|
||||||
|
# - return 'NONE' if this request is not for you
|
||||||
|
# - return 'ERROR' if any error
|
||||||
|
return 'NONE' # pragma: no cover
|
||||||
|
|
|
@ -0,0 +1,40 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# © 2016 Antonio Espinosa - <antonio.espinosa@tecnativa.com>
|
||||||
|
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
|
||||||
|
|
||||||
|
from openerp import models, api, fields
|
||||||
|
|
||||||
|
|
||||||
|
class ResPartner(models.Model):
|
||||||
|
_inherit = 'res.partner'
|
||||||
|
|
||||||
|
tracking_email_ids = fields.Many2many(
|
||||||
|
string="Tracking emails", comodel_name="mail.tracking.email",
|
||||||
|
readonly=True)
|
||||||
|
tracking_emails_count = fields.Integer(
|
||||||
|
string="Tracking emails count", store=True, readonly=True,
|
||||||
|
compute="_compute_tracking_emails_count")
|
||||||
|
email_score = fields.Float(
|
||||||
|
string="Email score",
|
||||||
|
compute="_compute_email_score", store=True, readonly=True)
|
||||||
|
|
||||||
|
@api.one
|
||||||
|
@api.depends('tracking_email_ids.state')
|
||||||
|
def _compute_email_score(self):
|
||||||
|
self.email_score = self.tracking_email_ids.email_score()
|
||||||
|
|
||||||
|
@api.one
|
||||||
|
@api.depends('tracking_email_ids')
|
||||||
|
def _compute_tracking_emails_count(self):
|
||||||
|
self.tracking_emails_count = self.env['mail.tracking.email'].\
|
||||||
|
search_count([
|
||||||
|
('recipient_address', '=ilike', self.email)
|
||||||
|
])
|
||||||
|
|
||||||
|
@api.multi
|
||||||
|
def write(self, vals):
|
||||||
|
email = vals.get('email')
|
||||||
|
if email is not None:
|
||||||
|
vals['tracking_email_ids'] = \
|
||||||
|
self.env['mail.tracking.email']._tracking_ids_to_write(email)
|
||||||
|
return super(ResPartner, self).write(vals)
|
|
@ -2,7 +2,24 @@
|
||||||
# © 2016 Antonio Espinosa - <antonio.espinosa@tecnativa.com>
|
# © 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).
|
||||||
|
|
||||||
|
import mock
|
||||||
|
import base64
|
||||||
from openerp.tests.common import TransactionCase
|
from openerp.tests.common import TransactionCase
|
||||||
|
from openerp.addons.mail_tracking.controllers.main import \
|
||||||
|
MailTrackingController, BLANK
|
||||||
|
|
||||||
|
mock_request = 'openerp.http.request'
|
||||||
|
mock_send_email = ('openerp.addons.base.ir.ir_mail_server.'
|
||||||
|
'ir_mail_server.send_email')
|
||||||
|
|
||||||
|
|
||||||
|
class FakeUserAgent(object):
|
||||||
|
browser = 'Test browser'
|
||||||
|
platform = 'Test platform'
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
"""Return name"""
|
||||||
|
return 'Test suite'
|
||||||
|
|
||||||
|
|
||||||
# One test case per method
|
# One test case per method
|
||||||
|
@ -20,6 +37,12 @@ class TestMailTracking(TransactionCase):
|
||||||
'email': 'recipient@example.com',
|
'email': 'recipient@example.com',
|
||||||
'notify_email': 'always',
|
'notify_email': 'always',
|
||||||
})
|
})
|
||||||
|
self.request = {
|
||||||
|
'httprequest': type('obj', (object,), {
|
||||||
|
'remote_addr': '123.123.123.123',
|
||||||
|
'user_agent': FakeUserAgent(),
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
|
||||||
def test_message_post(self):
|
def test_message_post(self):
|
||||||
# This message will generate a notification for recipient
|
# This message will generate a notification for recipient
|
||||||
|
@ -62,5 +85,51 @@ class TestMailTracking(TransactionCase):
|
||||||
'os_family': 'linux',
|
'os_family': 'linux',
|
||||||
'ua_family': 'odoo',
|
'ua_family': 'odoo',
|
||||||
}
|
}
|
||||||
tracking_email.event_process('open', metadata)
|
tracking_email.event_create('open', metadata)
|
||||||
self.assertEqual(tracking_email.state, 'opened')
|
self.assertEqual(tracking_email.state, 'opened')
|
||||||
|
|
||||||
|
def mail_send(self):
|
||||||
|
mail = self.env['mail.mail'].create({
|
||||||
|
'subject': 'Test subject',
|
||||||
|
'email_from': 'from@domain.com',
|
||||||
|
'email_to': 'to@domain.com',
|
||||||
|
'body_html': '<p>This is a test message</p>',
|
||||||
|
})
|
||||||
|
mail.send()
|
||||||
|
# Search tracking created
|
||||||
|
tracking_email = self.env['mail.tracking.email'].search([
|
||||||
|
('mail_id', '=', mail.id),
|
||||||
|
])
|
||||||
|
return mail, tracking_email
|
||||||
|
|
||||||
|
def test_mail_send(self):
|
||||||
|
controller = MailTrackingController()
|
||||||
|
db = self.env.cr.dbname
|
||||||
|
image = base64.decodestring(BLANK)
|
||||||
|
mail, tracking = self.mail_send()
|
||||||
|
self.assertEqual(mail.email_to, tracking.recipient)
|
||||||
|
self.assertEqual(mail.email_from, tracking.sender)
|
||||||
|
with mock.patch(mock_request) as mock_func:
|
||||||
|
mock_func.return_value = type('obj', (object,), self.request)
|
||||||
|
res = controller.mail_tracking_open(db, tracking.id)
|
||||||
|
self.assertEqual(image, res.response[0])
|
||||||
|
|
||||||
|
def test_smtp_error(self):
|
||||||
|
with mock.patch(mock_send_email) as mock_func:
|
||||||
|
mock_func.side_effect = Warning('Test error')
|
||||||
|
mail, tracking = self.mail_send()
|
||||||
|
self.assertEqual('error', tracking.state)
|
||||||
|
self.assertEqual('Warning', tracking.error_type)
|
||||||
|
self.assertEqual('Test error', tracking.error_description)
|
||||||
|
|
||||||
|
def test_db(self):
|
||||||
|
db = self.env.cr.dbname
|
||||||
|
controller = MailTrackingController()
|
||||||
|
with mock.patch(mock_request) as mock_func:
|
||||||
|
mock_func.return_value = type('obj', (object,), self.request)
|
||||||
|
not_found = controller.mail_tracking_all('not_found_db')
|
||||||
|
self.assertEqual('NOT FOUND', not_found.response[0])
|
||||||
|
none = controller.mail_tracking_all(db)
|
||||||
|
self.assertEqual('NONE', none.response[0])
|
||||||
|
none = controller.mail_tracking_event(db, 'open')
|
||||||
|
self.assertEqual('NONE', none.response[0])
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
<field name="name">mail.tracking.email.form</field>
|
<field name="name">mail.tracking.email.form</field>
|
||||||
<field name="model">mail.tracking.email</field>
|
<field name="model">mail.tracking.email</field>
|
||||||
<field name="arch" type="xml">
|
<field name="arch" type="xml">
|
||||||
<form string="MailTracking event">
|
<form string="MailTracking event" create="false" edit="false" delete="false">
|
||||||
<header>
|
<header>
|
||||||
<field name="state" widget="statusbar"/>
|
<field name="state" widget="statusbar"/>
|
||||||
</header>
|
</header>
|
||||||
|
@ -63,13 +63,14 @@
|
||||||
<field name="name">mail.tracking.email.tree</field>
|
<field name="name">mail.tracking.email.tree</field>
|
||||||
<field name="model">mail.tracking.email</field>
|
<field name="model">mail.tracking.email</field>
|
||||||
<field name="arch" type="xml">
|
<field name="arch" type="xml">
|
||||||
<tree string="MailTracking emails" colors="grey:state in (False, 'deferred');black:state in ('sent', 'delivered');green:state in ('opened');red:state in ('rejected', 'spam', 'bounced', 'soft-bounced');blue:state in ('unsub')">
|
<tree string="MailTracking emails" create="false" edit="false" delete="false"
|
||||||
<field name="state" invisible="1"/>
|
colors="grey:state in (False, 'deferred');black:state in ('sent', 'delivered');green:state in ('opened');red:state in ('rejected', 'spam', 'bounced', 'soft-bounced');blue:state in ('unsub')">
|
||||||
<field name="time"/>
|
<field name="time"/>
|
||||||
<field name="date" invisible="1"/>
|
<field name="date" invisible="1"/>
|
||||||
<field name="name"/>
|
<field name="name"/>
|
||||||
<field name="sender" string="Sender"/>
|
<field name="sender" string="Sender"/>
|
||||||
<field name="recipient" string="Recipient"/>
|
<field name="recipient" string="Recipient"/>
|
||||||
|
<field name="state"/>
|
||||||
</tree>
|
</tree>
|
||||||
</field>
|
</field>
|
||||||
</record>
|
</record>
|
||||||
|
@ -79,8 +80,10 @@
|
||||||
<field name="model">mail.tracking.email</field>
|
<field name="model">mail.tracking.email</field>
|
||||||
<field name="arch" type="xml">
|
<field name="arch" type="xml">
|
||||||
<search string="MailTracking email search">
|
<search string="MailTracking email search">
|
||||||
<field name="sender" string="Email"
|
<field name="display_name" string="Email"
|
||||||
filter_domain="['|', ('sender', 'ilike', self), ('recipient', 'ilike', self)]"/>
|
filter_domain="['|', ('sender', 'ilike', self), ('recipient', 'ilike', self)]"/>
|
||||||
|
<field name="sender" string="Sender"/>
|
||||||
|
<field name="recipient" string="Sender"/>
|
||||||
<field name="name" string="Subject"/>
|
<field name="name" string="Subject"/>
|
||||||
<field name="time" string="Time"/>
|
<field name="time" string="Time"/>
|
||||||
<field name="date" string="Date"/>
|
<field name="date" string="Date"/>
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
<field name="name">mail.tracking.event.form</field>
|
<field name="name">mail.tracking.event.form</field>
|
||||||
<field name="model">mail.tracking.event</field>
|
<field name="model">mail.tracking.event</field>
|
||||||
<field name="arch" type="xml">
|
<field name="arch" type="xml">
|
||||||
<form string="MailTracking event">
|
<form string="MailTracking event" create="false" edit="false" delete="false">
|
||||||
<sheet>
|
<sheet>
|
||||||
<group>
|
<group>
|
||||||
<group>
|
<group>
|
||||||
|
@ -50,7 +50,8 @@
|
||||||
<field name="name">mail.tracking.event.tree</field>
|
<field name="name">mail.tracking.event.tree</field>
|
||||||
<field name="model">mail.tracking.event</field>
|
<field name="model">mail.tracking.event</field>
|
||||||
<field name="arch" type="xml">
|
<field name="arch" type="xml">
|
||||||
<tree string="MailTracking events" colors="grey:event_type in ('deferral',);black:event_type in ('sent', 'delivered');red:event_type in ('hard_bounce', 'soft_bounce', 'spam', 'reject');blue:event_type in ('unsub', 'click', 'open')">
|
<tree string="MailTracking events" create="false" edit="false" delete="false"
|
||||||
|
colors="grey:event_type in ('deferral',);black:event_type in ('sent', 'delivered');red:event_type in ('hard_bounce', 'soft_bounce', 'spam', 'reject');blue:event_type in ('unsub', 'click', 'open')">
|
||||||
<field name="time"/>
|
<field name="time"/>
|
||||||
<field name="tracking_email_id"/>
|
<field name="tracking_email_id"/>
|
||||||
<field name="recipient"/>
|
<field name="recipient"/>
|
||||||
|
|
|
@ -0,0 +1,33 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!-- © 2016 Antonio Espinosa - <antonio.espinosa@tecnativa.com>
|
||||||
|
License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). -->
|
||||||
|
<openerp>
|
||||||
|
<data>
|
||||||
|
|
||||||
|
<record model="ir.ui.view" id="view_partner_form">
|
||||||
|
<field name="name">Partner Form with tracking emails</field>
|
||||||
|
<field name="model">res.partner</field>
|
||||||
|
<field name="inherit_id" ref="base.view_partner_form"/>
|
||||||
|
<field name="arch" type="xml">
|
||||||
|
<div class="oe_right oe_button_box" position="inside">
|
||||||
|
<button name="%(mail_tracking.action_view_mail_tracking_email)d"
|
||||||
|
context="{'search_default_recipient': email,
|
||||||
|
'default_recipient': email}"
|
||||||
|
type="action"
|
||||||
|
class="oe_stat_button oe_inline"
|
||||||
|
icon="fa-envelope-o"
|
||||||
|
attrs="{'invisible': [('email', '=', False)]}">
|
||||||
|
<field name="tracking_emails_count"
|
||||||
|
widget="statinfo"
|
||||||
|
string="Tracking emails"/>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<field name="email" position="after">
|
||||||
|
<field name="email_score" widget="progressbar"
|
||||||
|
attrs="{'invisible': [('email', '=', False)]}"/>
|
||||||
|
</field>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
</data>
|
||||||
|
</openerp>
|
Loading…
Reference in New Issue