Antonio Espinosa
9 years ago
16 changed files with 1343 additions and 0 deletions
-
110mail_mandrill/README.rst
-
6mail_mandrill/__init__.py
-
25mail_mandrill/__openerp__.py
-
5mail_mandrill/controllers/__init__.py
-
130mail_mandrill/controllers/main.py
-
0mail_mandrill/i18n/.gitkeep
-
7mail_mandrill/models/__init__.py
-
38mail_mandrill/models/mail_mail.py
-
167mail_mandrill/models/mail_mandrill_event.py
-
102mail_mandrill/models/mail_mandrill_message.py
-
5mail_mandrill/security/ir.model.access.csv
-
BINmail_mandrill/static/description/icon.png
-
5mail_mandrill/tests/__init__.py
-
520mail_mandrill/tests/test_mail_mandrill.py
-
109mail_mandrill/views/mail_mandrill_event_view.xml
-
114mail_mandrill/views/mail_mandrill_message_view.xml
@ -0,0 +1,110 @@ |
|||
.. image:: https://img.shields.io/badge/licence-AGPL--3-blue.svg |
|||
:target: http://www.gnu.org/licenses/agpl-3.0-standalone.html |
|||
:alt: License: AGPL-3 |
|||
|
|||
================================ |
|||
Mandrill mail events integration |
|||
================================ |
|||
|
|||
This module logs Mandrill email messages events. |
|||
|
|||
|
|||
Configuration |
|||
============= |
|||
|
|||
To configure this module, you need to: |
|||
|
|||
* Define an STMP server in Odoo at Settings > Technical > Email > Outgoing Mail Servers |
|||
using Mandrill credentials from Mandrill settings panel (Settings > SMTP & API Credentials) |
|||
* Define a webhook in Mandrill settings panel (Settings > Webhooks) with |
|||
several triggers (Message Is Sent, Message Is Delayed, ...) and 'Post to URL' |
|||
like https://your_odoodomain.com/mandrill/event |
|||
* Copy Webhook key and paste in your Odoo configuration file, in 'options' |
|||
section, using 'mandrill_webhook_key' variable. This is optional, but |
|||
recommended because it is used to validate Mandrill POST requests |
|||
|
|||
|
|||
Usage |
|||
===== |
|||
|
|||
When any email message is sent via Mandrill SMTP server, Odoo will add |
|||
some metadata to email (Odoo DB, Odoo Model and Odoo Model record ID) using an |
|||
special SMTP header (X-MC-Metadata). More info at `Mandrill doc: Using Custom Message Metadata <https://mandrill.zendesk.com/hc/en-us/articles/205582417-Using-Custom-Message-Metadata>`_ |
|||
|
|||
Then when an event occurs related with that message (sent, open, click, bounce, ...) |
|||
Mandrill will trigger webhook configured and Odoo will log the message and the event. |
|||
|
|||
In 'Setting > Technical > Email > Mandrill emails' you can see all messages sent |
|||
using Mandrill. When clicking in one of them you'll see message details and events |
|||
related with it |
|||
|
|||
In 'Setting > Technical > Email > Mandrill events' you can see all Mandrill events |
|||
received |
|||
|
|||
.. 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/8.0 |
|||
|
|||
For further information, please visit: |
|||
|
|||
* https://www.odoo.com/forum/help-1 |
|||
|
|||
|
|||
Known issues / Roadmap |
|||
====================== |
|||
|
|||
* Define actions associated with events like open/click or bounce |
|||
(via configuration or via other addon) |
|||
* Create another addon 'mass_mailing_mandrill' (inheriting from mass_mailing |
|||
and this addon) to process bounces like mass_mailing addon does |
|||
|
|||
|
|||
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 |
|||
`here <https://github.com/OCA/social/issues/new?body=module:%20mail_mandrill%0Aversion:%208.0%0A%0A**Steps%20to%20reproduce**%0A-%20...%0A%0A**Current%20behavior**%0A%0A**Expected%20behavior**>`_. |
|||
|
|||
|
|||
License |
|||
======= |
|||
|
|||
This program is free software: you can redistribute it and/or modify |
|||
it under the terms of the GNU Affero General Public License as published |
|||
by the Free Software Foundation, either version 3 of the License, or |
|||
(at your option) any later version. |
|||
|
|||
This program is distributed in the hope that it will be useful, |
|||
but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
GNU Affero General Public License for more details. |
|||
|
|||
You should have received a copy of the GNU Affero General Public License |
|||
along with this program. If not, see <http://www.gnu.org/licenses/agpl-3.0-standalone.html>. |
|||
|
|||
|
|||
Credits |
|||
======= |
|||
|
|||
Contributors |
|||
------------ |
|||
|
|||
* Rafael Blasco <rafabn@antiun.com> |
|||
* Antonio Espinosa <antonioea@antiun.com> |
|||
|
|||
Maintainer |
|||
---------- |
|||
|
|||
.. image:: https://odoo-community.org/logo.png |
|||
:alt: Odoo Community Association |
|||
:target: https://odoo-community.org |
|||
|
|||
This module is maintained by the OCA. |
|||
|
|||
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. |
|||
|
|||
To contribute to this module, please visit http://odoo-community.org. |
@ -0,0 +1,6 @@ |
|||
# -*- coding: utf-8 -*- |
|||
# License AGPL-3: Antiun Ingenieria S.L. - Antonio Espinosa |
|||
# See README.rst file on addon root folder for more details |
|||
|
|||
from . import models |
|||
from . import controllers |
@ -0,0 +1,25 @@ |
|||
# -*- coding: utf-8 -*- |
|||
# License AGPL-3: Antiun Ingenieria S.L. - Antonio Espinosa |
|||
# See README.rst file on addon root folder for more details |
|||
|
|||
{ |
|||
'name': "Mandrill mail events integration", |
|||
'category': 'Social Network', |
|||
'version': '8.0.1.0.0', |
|||
'depends': [ |
|||
'mail', |
|||
], |
|||
'external_dependencies': {}, |
|||
'data': [ |
|||
'security/ir.model.access.csv', |
|||
'views/mail_mandrill_message_view.xml', |
|||
'views/mail_mandrill_event_view.xml', |
|||
], |
|||
'author': 'Antiun Ingeniería S.L., ' |
|||
'Odoo Community Association (OCA)', |
|||
'website': 'http://www.antiun.com', |
|||
'license': 'AGPL-3', |
|||
'demo': [], |
|||
'test': [], |
|||
'installable': True, |
|||
} |
@ -0,0 +1,5 @@ |
|||
# -*- coding: utf-8 -*- |
|||
# License AGPL-3: Antiun Ingenieria S.L. - Antonio Espinosa |
|||
# See README.rst file on addon root folder for more details |
|||
|
|||
from . import main |
@ -0,0 +1,130 @@ |
|||
# -*- coding: utf-8 -*- |
|||
# License AGPL-3: Antiun Ingenieria S.L. - Antonio Espinosa |
|||
# See README.rst file on addon root folder for more details |
|||
|
|||
import json |
|||
from hashlib import sha1 |
|||
import hmac |
|||
import logging |
|||
from psycopg2 import OperationalError |
|||
|
|||
import openerp |
|||
from openerp import api, http, SUPERUSER_ID, tools |
|||
from openerp.http import request |
|||
|
|||
_logger = logging.getLogger(__name__) |
|||
|
|||
|
|||
class MailController(http.Controller): |
|||
|
|||
def _mandrill_validation(self, **kw): |
|||
""" |
|||
Validate Mandrill POST reques using |
|||
https://mandrill.zendesk.com/hc/en-us/articles/ |
|||
205583257-Authenticating-webhook-requests |
|||
""" |
|||
headers = request.httprequest.headers |
|||
signature = headers.get('X-Mandrill-Signature', False) |
|||
key = tools.config.options.get('mandrill_webhook_key', False) |
|||
if not key: |
|||
_logger.info("No Mandrill validation key configured. " |
|||
"Please add 'mandrill_webhook_key' to [options] " |
|||
"section in odoo configuration file to enable " |
|||
"Mandrill authentication webhoook requests. " |
|||
"More info at: " |
|||
"https://mandrill.zendesk.com/hc/en-us/articles/" |
|||
"205583257-Authenticating-webhook-requests") |
|||
return True |
|||
if not signature: |
|||
return False |
|||
url = tools.config.options.get('mandrill_webhook_url', False) |
|||
if not url: |
|||
url = request.httprequest.url_root.rstrip('/') + '/mandrill/event' |
|||
data = url |
|||
kw_keys = kw.keys() |
|||
if kw_keys: |
|||
kw_keys.sort() |
|||
for kw_key in kw_keys: |
|||
data += kw_key + kw.get(kw_key) |
|||
hashed = hmac.new(key, data, sha1) |
|||
hash_text = hashed.digest().encode("base64").rstrip('\n') |
|||
if hash_text == signature: |
|||
return True |
|||
_logger.info("HASH[%s] != SIGNATURE[%s]" % (hash_text, signature)) |
|||
return False |
|||
|
|||
def _event_process(self, event): |
|||
message_id = event.get('_id') |
|||
event_type = event.get('event') |
|||
message = event.get('msg') |
|||
if not (message_id and event_type and message): |
|||
return False |
|||
|
|||
info = "%s event for Message ID '%s'" % (event_type, message_id) |
|||
metadata = message.get('metadata') |
|||
db = None |
|||
if metadata: |
|||
db = metadata.get('odoo_db', None) |
|||
|
|||
# Check database selected by mandrill event |
|||
if not db: |
|||
_logger.info('%s: No DB selected', info) |
|||
return False |
|||
try: |
|||
registry = openerp.registry(db) |
|||
except OperationalError: |
|||
_logger.info("%s: Selected BD '%s' not found", info, db) |
|||
return False |
|||
except: |
|||
_logger.info("%s: Selected BD '%s' connection error", info, db) |
|||
return False |
|||
|
|||
# Database has been selected, process event |
|||
with registry.cursor() as cr: |
|||
env = api.Environment(cr, SUPERUSER_ID, {}) |
|||
res = env['mail.mandrill.message'].process( |
|||
message_id, event_type, event) |
|||
if res: |
|||
_logger.info('%s: OK', info) |
|||
else: |
|||
_logger.info('%s: FAILED', info) |
|||
return res |
|||
|
|||
@http.route('/mandrill/event', type='http', auth='none') |
|||
def event(self, **kw): |
|||
""" |
|||
End-point to receive Mandrill event |
|||
Configuration in Mandrill app > Settings > Webhooks |
|||
(https://mandrillapp.com/settings/webhooks) |
|||
Add a webhook, selecting this type of events: |
|||
- Message Is Sent |
|||
- Message Is Bounced |
|||
- Message Is Opened |
|||
- Message Is Marked As Spam |
|||
- Message Is Rejected |
|||
- Message Is Delayed |
|||
- Message Is Soft-Bounced |
|||
- Message Is Clicked |
|||
- Message Recipient Unsubscribes |
|||
and setting this Post to URL: |
|||
https://your_odoodomain.com/mandrill/event |
|||
""" |
|||
if not self._mandrill_validation(**kw): |
|||
_logger.info('Validation error, ignoring this request') |
|||
return 'NO_AUTH' |
|||
events = [] |
|||
try: |
|||
events = json.loads(kw.get('mandrill_events', '[]')) |
|||
except: |
|||
pass |
|||
if not events: |
|||
return 'NO_EVENTS' |
|||
res = [] |
|||
for event in events: |
|||
res.append(self._event_process(event)) |
|||
msg = 'ALL_EVENTS_FAILED' |
|||
if all(res): |
|||
msg = 'OK' |
|||
elif any(res): |
|||
msg = 'SOME_EVENTS_FAILED' |
|||
return msg |
@ -0,0 +1,7 @@ |
|||
# -*- coding: utf-8 -*- |
|||
# License AGPL-3: Antiun Ingenieria S.L. - Antonio Espinosa |
|||
# See README.rst file on addon root folder for more details |
|||
|
|||
from . import mail_mail |
|||
from . import mail_mandrill_message |
|||
from . import mail_mandrill_event |
@ -0,0 +1,38 @@ |
|||
# -*- coding: utf-8 -*- |
|||
# License AGPL-3: Antiun Ingenieria S.L. - Antonio Espinosa |
|||
# See README.rst file on addon root folder for more details |
|||
|
|||
import json |
|||
import threading |
|||
|
|||
from openerp import models, api |
|||
|
|||
|
|||
class MailMail(models.Model): |
|||
_inherit = 'mail.mail' |
|||
|
|||
def _mandrill_headers_add(self): |
|||
for mail in self.sudo(): |
|||
headers = {} |
|||
if mail.headers: |
|||
try: |
|||
headers.update(eval(mail.headers)) |
|||
except Exception: |
|||
pass |
|||
|
|||
metadata = { |
|||
'odoo_db': getattr(threading.currentThread(), 'dbname', None), |
|||
'odoo_model': mail.model, |
|||
'odoo_id': mail.res_id, |
|||
} |
|||
headers['X-MC-Metadata'] = json.dumps(metadata) |
|||
mail.headers = repr(headers) |
|||
return True |
|||
|
|||
@api.multi |
|||
def send(self, auto_commit=False, raise_exception=False): |
|||
self._mandrill_headers_add() |
|||
super(MailMail, self).send( |
|||
auto_commit=auto_commit, |
|||
raise_exception=raise_exception) |
|||
return True |
@ -0,0 +1,167 @@ |
|||
# -*- coding: utf-8 -*- |
|||
# License AGPL-3: Antiun Ingenieria S.L. - Antonio Espinosa |
|||
# See README.rst file on addon root folder for more details |
|||
|
|||
import datetime |
|||
|
|||
from openerp import models, fields, api |
|||
|
|||
|
|||
class MailMandrillEvent(models.Model): |
|||
_name = 'mail.mandrill.event' |
|||
_order = 'timestamp desc' |
|||
_rec_name = 'event_type' |
|||
|
|||
timestamp = fields.Integer(string='Mandrill UTC timestamp', readonly=True) |
|||
time = fields.Datetime(string='Mandrill time', readonly=True) |
|||
date = fields.Date(string='Mandrill date', readonly=True) |
|||
event_type = fields.Selection(string='Event type', selection=[ |
|||
('send', 'Sent'), |
|||
('deferral', 'Deferral'), |
|||
('hard_bounce', 'Hard bounce'), |
|||
('soft_bounce', 'Soft bounce'), |
|||
('open', 'Opened'), |
|||
('click', 'Clicked'), |
|||
('spam', 'Spam'), |
|||
('unsub', 'Unsubscribed'), |
|||
('reject', 'Rejected'), |
|||
], readonly=True) |
|||
url = fields.Char(string='Clicked URL', readonly=True) |
|||
ip = fields.Char(string='User IP', readonly=True) |
|||
user_agent = fields.Char(string='User agent', readonly=True) |
|||
mobile = fields.Boolean(string='Is mobile?', readonly=True) |
|||
os_family = fields.Char(string='Operating system family', readonly=True) |
|||
ua_family = fields.Char(string='User agent family', readonly=True) |
|||
ua_type = fields.Char(string='User agent type', readonly=True) |
|||
user_country_id = fields.Many2one(string='User country', readonly=True, |
|||
comodel_name='res.country') |
|||
message_id = fields.Many2one(string='Message', readonly=True, |
|||
comodel_name='mail.mandrill.message') |
|||
|
|||
def _country_search(self, country_code, state_name): |
|||
country = False |
|||
if country_code: |
|||
country = self.env['res.country'].search([ |
|||
('code', 'ilike', country_code), |
|||
]) |
|||
if not country and state_name: |
|||
state = self.env['res.country.state'].search([ |
|||
('name', 'ilike', state_name), |
|||
]) |
|||
if state: |
|||
country = state.country_id |
|||
|
|||
if country: |
|||
return country.id |
|||
return False |
|||
|
|||
def _process_bounce(self, message, event, event_type): |
|||
msg = event.get('msg') |
|||
bounce_type = msg.get('bounce_description', False) if msg else False |
|||
bounce_description = msg.get('diag', False) if msg else False |
|||
message.write({ |
|||
'state': 'bounced', |
|||
'bounce_type': bounce_type, |
|||
'bounce_description': bounce_description, |
|||
}) |
|||
ts = event.get('ts', 0) |
|||
time = datetime.datetime.fromtimestamp(ts) |
|||
return { |
|||
'message_id': message.id, |
|||
'event_type': event_type, |
|||
'timestamp': ts, |
|||
'time': time.strftime('%Y-%m-%d %H:%M:%S') if ts else False, |
|||
'date': time.strftime('%Y-%m-%d') if ts else False, |
|||
} |
|||
|
|||
def _process_status(self, message, event, event_type, state): |
|||
message.write({ |
|||
'state': state, |
|||
}) |
|||
ts = event.get('ts', 0) |
|||
time = datetime.datetime.fromtimestamp(ts) |
|||
return { |
|||
'message_id': message.id, |
|||
'event_type': event_type, |
|||
'timestamp': ts, |
|||
'time': time.strftime('%Y-%m-%d %H:%M:%S') if ts else False, |
|||
'date': time.strftime('%Y-%m-%d') if ts else False, |
|||
} |
|||
|
|||
def _process_action(self, message, event, event_type, state): |
|||
message.write({ |
|||
'state': state, |
|||
}) |
|||
ts = event.get('ts', 0) |
|||
url = event.get('url', False) |
|||
ip = event.get('ip', False) |
|||
user_agent = event.get('user_agent', False) |
|||
os_family = False |
|||
ua_family = False |
|||
ua_type = False |
|||
mobile = False |
|||
country_code = False |
|||
state = False |
|||
location = event.get('location') |
|||
if location: |
|||
country_code = location.get('country_short', False) |
|||
state = location.get('region', False) |
|||
ua_parsed = event.get('user_agent_parsed') |
|||
if ua_parsed: |
|||
os_family = ua_parsed.get('os_family', False) |
|||
ua_family = ua_parsed.get('ua_family', False) |
|||
ua_type = ua_parsed.get('type', False) |
|||
mobile = ua_parsed.get('mobile', False) |
|||
country_id = self._country_search(country_code, state) |
|||
time = datetime.datetime.fromtimestamp(ts) |
|||
return { |
|||
'message_id': message.id, |
|||
'event_type': event_type, |
|||
'timestamp': ts, |
|||
'time': time.strftime('%Y-%m-%d %H:%M:%S') if ts else False, |
|||
'date': time.strftime('%Y-%m-%d') if ts else False, |
|||
'user_country_id': country_id, |
|||
'ip': ip, |
|||
'url': url, |
|||
'mobile': mobile, |
|||
'user_agent': user_agent, |
|||
'os_family': os_family, |
|||
'ua_family': ua_family, |
|||
'ua_type': ua_type, |
|||
} |
|||
|
|||
@api.model |
|||
def process_send(self, message, event): |
|||
return self._process_status(message, event, 'send', 'sent') |
|||
|
|||
@api.model |
|||
def process_deferral(self, message, event): |
|||
return self._process_status(message, event, 'deferral', 'deferred') |
|||
|
|||
@api.model |
|||
def process_hard_bounce(self, message, event): |
|||
return self._process_bounce(message, event, 'hard_bounce') |
|||
|
|||
@api.model |
|||
def process_soft_bounce(self, message, event): |
|||
return self._process_bounce(message, event, 'soft_bounce') |
|||
|
|||
@api.model |
|||
def process_open(self, message, event): |
|||
return self._process_action(message, event, 'open', 'opened') |
|||
|
|||
@api.model |
|||
def process_click(self, message, event): |
|||
return self._process_action(message, event, 'click', 'opened') |
|||
|
|||
@api.model |
|||
def process_spam(self, message, event): |
|||
return self._process_status(message, event, 'spam', 'spam') |
|||
|
|||
@api.model |
|||
def process_unsub(self, message, event): |
|||
return self._process_status(message, event, 'unsub', 'unsub') |
|||
|
|||
@api.model |
|||
def process_reject(self, message, event): |
|||
return self._process_status(message, event, 'reject', 'rejected') |
@ -0,0 +1,102 @@ |
|||
# -*- coding: utf-8 -*- |
|||
# License AGPL-3: Antiun Ingenieria S.L. - Antonio Espinosa |
|||
# See README.rst file on addon root folder for more details |
|||
|
|||
import datetime |
|||
import json |
|||
import logging |
|||
|
|||
from openerp import models, fields, api |
|||
|
|||
_logger = logging.getLogger(__name__) |
|||
|
|||
|
|||
class MailMandrillMessage(models.Model): |
|||
_name = 'mail.mandrill.message' |
|||
_order = 'timestamp desc' |
|||
_rec_name = 'name' |
|||
|
|||
name = fields.Char(string='Subject', readonly=True) |
|||
mandrill_id = fields.Char(string='Mandrill message ID', required=True, |
|||
readonly=True) |
|||
timestamp = fields.Integer(string='Mandrill UTC timestamp', readonly=True) |
|||
time = fields.Datetime(string='Mandrill time', readonly=True) |
|||
date = fields.Date(string='Mandrill date', readonly=True) |
|||
recipient = fields.Char(string='Recipient email', readonly=True) |
|||
sender = fields.Char(string='Sender email', readonly=True) |
|||
state = fields.Selection([ |
|||
('deferred', 'Deferred'), |
|||
('sent', 'Sent'), |
|||
('opened', 'Opened'), |
|||
('rejected', 'Rejected'), |
|||
('spam', 'Spam'), |
|||
('unsub', 'Unsubscribed'), |
|||
('bounced', 'Bounced'), |
|||
('soft-bounced', 'Soft bounced'), |
|||
], string='State', index=True, readonly=True, |
|||
help=" * The 'Sent' status indicates that message was succesfully " |
|||
"delivered to recipient Mail Exchange (MX) server.\n" |
|||
" * The 'Opened' status indicates that message was opened or " |
|||
"clicked by recipient.\n" |
|||
" * The 'Rejected' status indicates that recipient email " |
|||
"address is blacklisted by Mandrill. It is recomended to " |
|||
"delete this email address.\n" |
|||
" * The 'Spam' status indicates that Mandrill consider this " |
|||
"message as spam.\n" |
|||
" * The 'Unsubscribed' status indicates that recipient has " |
|||
"requested to be unsubscribed from this message.\n" |
|||
" * The 'Bounced' status indicates that message was bounced " |
|||
"by recipient Mail Exchange (MX) server.\n" |
|||
" * The 'Soft bounced' status indicates that message was soft " |
|||
"bounced by recipient Mail Exchange (MX) server.\n") |
|||
bounce_type = fields.Char(string='Bounce type', readonly=True) |
|||
bounce_description = fields.Char(string='Bounce description', |
|||
readonly=True) |
|||
tags = fields.Char(string='Tags', readonly=True) |
|||
metadata = fields.Text(string='Metadata', readonly=True) |
|||
event_ids = fields.One2many( |
|||
string='Mandrill events', |
|||
comodel_name='mail.mandrill.event', inverse_name='message_id') |
|||
|
|||
def _message_prepare(self, message_id, event_type, event): |
|||
msg = event.get('msg') |
|||
ts = msg.get('ts', 0) |
|||
time = datetime.datetime.fromtimestamp(ts) |
|||
tags = msg.get('tags', []) |
|||
metadata = msg.get('metadata', {}) |
|||
metatext = json.dumps(metadata, indent=4) if metadata else False |
|||
return { |
|||
'mandrill_id': message_id, |
|||
'timestamp': ts, |
|||
'time': time.strftime('%Y-%m-%d %H:%M:%S') if ts else False, |
|||
'date': time.strftime('%Y-%m-%d') if ts else False, |
|||
'recipient': msg.get('email', False), |
|||
'sender': msg.get('sender', False), |
|||
'name': msg.get('subject', False), |
|||
'tags': ', '.join(tags) if tags else False, |
|||
'metadata': metatext, |
|||
} |
|||
|
|||
def _event_prepare(self, message, event_type, event): |
|||
m_event = self.env['mail.mandrill.event'] |
|||
method = getattr(m_event, 'process_' + event_type, None) |
|||
if method and hasattr(method, '__call__'): |
|||
return method(message, event) |
|||
else: |
|||
_logger.info('Unknown event type: %s' % event_type) |
|||
return False |
|||
|
|||
@api.model |
|||
def process(self, message_id, event_type, event): |
|||
if not (message_id and event_type and event): |
|||
return False |
|||
msg = event.get('msg') |
|||
message = self.search([('mandrill_id', '=', message_id)]) |
|||
if msg and not message: |
|||
data = self._message_prepare(message_id, event_type, event) |
|||
message = self.create(data) if data else False |
|||
if message: |
|||
m_event = self.env['mail.mandrill.event'] |
|||
data = self._event_prepare(message, event_type, event) |
|||
return m_event.create(data) if data else False |
|||
return False |
@ -0,0 +1,5 @@ |
|||
"id","name","model_id:id","group_id:id","perm_read","perm_write","perm_create","perm_unlink" |
|||
"access_mail_mandrill_message_group_user","mail_mandrill_message group_user","model_mail_mandrill_message","base.group_user",1,0,0,0 |
|||
"access_mail_mandrill_event_group_user","mail_mandrill_event group_user","model_mail_mandrill_event","base.group_user",1,0,0,0 |
|||
"access_mail_mandrill_message_group_system","mail_mandrill_message group_system","model_mail_mandrill_message","base.group_system",1,1,1,1 |
|||
"access_mail_mandrill_event_group_system","mail_mandrill_event group_system","model_mail_mandrill_event","base.group_system",1,1,1,1 |
After Width: 128 | Height: 128 | Size: 5.1 KiB |
@ -0,0 +1,5 @@ |
|||
# -*- coding: utf-8 -*- |
|||
# License AGPL-3: Antiun Ingenieria S.L. - Antonio Espinosa |
|||
# See README.rst file on addon root folder for more details |
|||
|
|||
from . import test_mail_mandrill |
@ -0,0 +1,520 @@ |
|||
# -*- coding: utf-8 -*- |
|||
# License AGPL-3: Antiun Ingenieria S.L. - Antonio Espinosa |
|||
# See README.rst file on addon root folder for more details |
|||
|
|||
import json |
|||
from openerp.tests.common import TransactionCase |
|||
from openerp.tools.safe_eval import safe_eval |
|||
|
|||
|
|||
class TestMailMandrill(TransactionCase): |
|||
def setUp(self): |
|||
super(TestMailMandrill, self).setUp() |
|||
message_obj = self.env['mail.mandrill.message'] |
|||
self.partner_01 = self.env.ref('base.res_partner_1') |
|||
self.partner_02 = self.env.ref('base.res_partner_2') |
|||
self.model = 'res.partner' |
|||
self.res_id = self.partner_02.id |
|||
self.mandrill_message_id = '0123456789abcdef0123456789abcdef' |
|||
self.event_deferral = { |
|||
'msg': { |
|||
'sender': 'username01@example.com', |
|||
'tags': [], |
|||
'smtp_events': [ |
|||
{ |
|||
'destination_ip': '123.123.123.123', |
|||
'diag': 'Event description', |
|||
'source_ip': '145.145.145.145', |
|||
'ts': 1455192896, |
|||
'type': 'deferred', |
|||
'size': 19513 |
|||
}, |
|||
], |
|||
'ts': 1455008558, |
|||
'clicks': [], |
|||
'resends': [], |
|||
'state': 'deferred', |
|||
'_version': '1abcdefghijkABCDEFGHIJ', |
|||
'template': None, |
|||
'_id': self.mandrill_message_id, |
|||
'email': 'username02@example.com', |
|||
'metadata': { |
|||
'odoo_id': self.res_id, |
|||
'odoo_db': 'test', |
|||
'odoo_model': self.model, |
|||
}, |
|||
'opens': [], |
|||
'subject': 'My favorite subject' |
|||
}, |
|||
'diag': '454 4.7.1 <username02@example.com>: Relay access denied', |
|||
'_id': self.mandrill_message_id, |
|||
'event': 'deferral', |
|||
'ts': 1455201028, |
|||
} |
|||
self.event_send = { |
|||
'msg': { |
|||
'_id': self.mandrill_message_id, |
|||
'subaccount': None, |
|||
'tags': [], |
|||
'smtp_events': [], |
|||
'ts': 1455201157, |
|||
'email': 'username02@example.com', |
|||
'metadata': { |
|||
'odoo_id': self.res_id, |
|||
'odoo_db': 'test', |
|||
'odoo_model': self.model, |
|||
}, |
|||
'state': 'sent', |
|||
'sender': 'username01@example.com', |
|||
'template': None, |
|||
'reject': None, |
|||
'resends': [], |
|||
'clicks': [], |
|||
'opens': [], |
|||
'subject': 'My favorite subject', |
|||
}, |
|||
'_id': self.mandrill_message_id, |
|||
'event': 'send', |
|||
'ts': 1455201159, |
|||
} |
|||
self.event_hard_bounce = { |
|||
'msg': { |
|||
'bounce_description': 'bad_mailbox', |
|||
'sender': 'username01@example.com', |
|||
'tags': [], |
|||
'diag': 'smtp;550 5.4.1 [username02@example.com]: ' |
|||
'Recipient address rejected: Access denied', |
|||
'smtp_events': [], |
|||
'ts': 1455194565, |
|||
'template': None, |
|||
'_version': 'abcdefghi123456ABCDEFG', |
|||
'metadata': { |
|||
'odoo_id': self.res_id, |
|||
'odoo_db': 'test', |
|||
'odoo_model': self.model, |
|||
}, |
|||
'resends': [], |
|||
'state': 'bounced', |
|||
'bgtools_code': 10, |
|||
'_id': self.mandrill_message_id, |
|||
'email': 'username02@example.com', |
|||
'subject': 'My favorite subject', |
|||
}, |
|||
'_id': self.mandrill_message_id, |
|||
'event': 'hard_bounce', |
|||
'ts': 1455195340 |
|||
} |
|||
self.event_soft_bounce = { |
|||
'msg': { |
|||
'bounce_description': 'general', |
|||
'sender': 'username01@example.com', |
|||
'tags': [], |
|||
'diag': 'X-Notes; Error transferring to FQDN.EXAMPLE.COM\n ; ' |
|||
'SMTP Protocol Returned a Permanent Error 550 5.7.1 ' |
|||
'Unable to relay\n\n--==ABCDEFGHIJK12345678ABCDEFGH', |
|||
'smtp_events': [], |
|||
'ts': 1455194562, |
|||
'template': None, |
|||
'_version': 'abcdefghi123456ABCDEFG', |
|||
'metadata': { |
|||
'odoo_id': self.res_id, |
|||
'odoo_db': 'test', |
|||
'odoo_model': self.model, |
|||
}, |
|||
'resends': [], |
|||
'state': 'soft-bounced', |
|||
'bgtools_code': 40, |
|||
'_id': self.mandrill_message_id, |
|||
'email': 'username02@example.com', |
|||
'subject': 'My favorite subject', |
|||
}, |
|||
'_id': self.mandrill_message_id, |
|||
'event': 'soft_bounce', |
|||
'ts': 1455195622 |
|||
} |
|||
self.event_open = { |
|||
'ip': '111.111.111.111', |
|||
'ts': 1455189075, |
|||
'location': { |
|||
'country_short': 'PT', |
|||
'city': 'Porto', |
|||
'country': 'Portugal', |
|||
'region': 'Porto', |
|||
'longitude': -8.61098957062, |
|||
'postal_code': '-', |
|||
'latitude': 41.1496086121, |
|||
'timezone': '+01:00', |
|||
}, |
|||
'msg': { |
|||
'sender': 'username01@example.com', |
|||
'tags': [], |
|||
'smtp_events': [ |
|||
{ |
|||
'destination_ip': '222.222.222.222', |
|||
'diag': '250 2.0.0 ABCDEFGHIJK123456ABCDE mail ' |
|||
'accepted for delivery', |
|||
'source_ip': '111.1.1.1', |
|||
'ts': 1455185877, |
|||
'type': 'sent', |
|||
'size': 30276, |
|||
}, |
|||
], |
|||
'ts': 1455185876, |
|||
'clicks': [], |
|||
'metadata': { |
|||
'odoo_id': self.res_id, |
|||
'odoo_db': 'test', |
|||
'odoo_model': self.model, |
|||
}, |
|||
'resends': [], |
|||
'state': 'sent', |
|||
'_version': 'abcdefghi123456ABCDEFG', |
|||
'template': None, |
|||
'_id': self.mandrill_message_id, |
|||
'email': 'username02@example.com', |
|||
'opens': [ |
|||
{ |
|||
'ip': '111.111.111.111', |
|||
'ua': 'Windows/Windows 7/Outlook 2010/Outlook 2010', |
|||
'ts': 1455186247, |
|||
'location': |
|||
'Porto, PT' |
|||
}, { |
|||
'ip': '111.111.111.111', |
|||
'ua': 'Windows/Windows 7/Outlook 2010/Outlook 2010', |
|||
'ts': 1455189075, |
|||
'location': 'Porto, PT' |
|||
}, |
|||
], |
|||
'subject': 'My favorite subject', |
|||
}, |
|||
'_id': self.mandrill_message_id, |
|||
'user_agent_parsed': { |
|||
'ua_name': 'Outlook 2010', |
|||
'mobile': False, |
|||
'ua_company_url': 'http://www.microsoft.com/', |
|||
'os_icon': 'http://cdn.mandrill.com/img/email-client-icons/' |
|||
'windows-7.png', |
|||
'os_company': 'Microsoft Corporation.', |
|||
'ua_version': None, |
|||
'os_name': 'Windows 7', |
|||
'ua_family': 'Outlook 2010', |
|||
'os_url': 'http://en.wikipedia.org/wiki/Windows_7', |
|||
'os_company_url': 'http://www.microsoft.com/', |
|||
'ua_company': 'Microsoft Corporation.', |
|||
'os_family': 'Windows', |
|||
'type': 'Email Client', |
|||
'ua_icon': 'http://cdn.mandrill.com/img/email-client-icons/' |
|||
'outlook-2010.png', |
|||
'ua_url': 'http://en.wikipedia.org/wiki/Microsoft_Outlook', |
|||
}, |
|||
'event': 'open', |
|||
'user_agent': 'Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.1; ' |
|||
'Trident/7.0; SLCC2; .NET CLR 2.0.50727; ' |
|||
'.NET CLR 3.5.30729; .NET CLR 3.0.30729; ' |
|||
'Media Center PC 6.0; .NET4.0C; .NET4.0E; BRI/2; ' |
|||
'Tablet PC 2.0; GWX:DOWNLOADED; ' |
|||
'Microsoft Outlook 14.0.7166; ms-office; ' |
|||
'MSOffice 14)', |
|||
} |
|||
self.event_click = { |
|||
'url': 'http://www.example.com/index.php', |
|||
'ip': '111.111.111.111', |
|||
'ts': 1455186402, |
|||
'user_agent': 'Mozilla/5.0 (Windows NT 6.1) ' |
|||
'AppleWebKit/537.36 (KHTML, like Gecko) ' |
|||
'Chrome/48.0.2564.103 Safari/537.36', |
|||
'msg': { |
|||
'sender': 'username01@example.com', |
|||
'tags': [], |
|||
'smtp_events': [ |
|||
{ |
|||
'destination_ip': '222.222.222.222', |
|||
'diag': '250 2.0.0 Ok: queued as 12345678', |
|||
'source_ip': '111.1.1.1', |
|||
'ts': 1455186065, |
|||
'type': 'sent', |
|||
'size': 30994, |
|||
}, |
|||
], |
|||
'ts': 1455186063, |
|||
'clicks': [ |
|||
{ |
|||
'url': 'http://www.example.com/index.php', |
|||
'ip': '111.111.111.111', |
|||
'ua': 'Windows/Windows 7/Chrome/Chrome 48.0.2564.103', |
|||
'ts': 1455186402, |
|||
'location': 'Madrid, ES', |
|||
}, |
|||
], |
|||
'metadata': { |
|||
'odoo_id': self.res_id, |
|||
'odoo_db': 'test', |
|||
'odoo_model': self.model, |
|||
}, |
|||
'resends': [], |
|||
'state': 'sent', |
|||
'_version': 'abcdefghi123456ABCDEFG', |
|||
'template': None, |
|||
'_id': self.mandrill_message_id, |
|||
'email': 'username02@example.com', |
|||
'opens': [ |
|||
{ |
|||
'ip': '111.111.111.111', |
|||
'ua': 'Windows/Windows 7/Chrome/Chrome 48.0.2564.103', |
|||
'ts': 1455186402, |
|||
'location': 'Madrid, ES', |
|||
}, |
|||
], |
|||
'subject': 'My favorite subject', |
|||
}, |
|||
'_id': self.mandrill_message_id, |
|||
'user_agent_parsed': { |
|||
'ua_name': 'Chrome 48.0.2564.103', |
|||
'mobile': False, |
|||
'ua_company_url': 'http://www.google.com/', |
|||
'os_icon': 'http://cdn.mandrill.com/img/email-client-icons/' |
|||
'windows-7.png', |
|||
'os_company': 'Microsoft Corporation.', |
|||
'ua_version': '48.0.2564.103', |
|||
'os_name': 'Windows 7', |
|||
'ua_family': 'Chrome', |
|||
'os_url': 'http://en.wikipedia.org/wiki/Windows_7', |
|||
'os_company_url': 'http://www.microsoft.com/', |
|||
'ua_company': 'Google Inc.', |
|||
'os_family': 'Windows', |
|||
'type': 'Browser', |
|||
'ua_icon': 'http://cdn.mandrill.com/img/email-client-icons/' |
|||
'chrome.png', |
|||
'ua_url': 'http://www.google.com/chrome', |
|||
}, |
|||
'event': 'click', |
|||
'location': { |
|||
'country_short': 'ES', |
|||
'city': 'Madrid', |
|||
'country': 'Spain', |
|||
'region': 'Madrid', |
|||
'longitude': -3.70255994797, |
|||
'postal_code': '-', |
|||
'latitude': 40.4165000916, |
|||
'timezone': '+02:00', |
|||
}, |
|||
} |
|||
self.event_spam = { |
|||
'msg': { |
|||
'sender': 'username01@example.com', |
|||
'tags': [], |
|||
'smtp_events': [], |
|||
'ts': 1455186007, |
|||
'clicks': [], |
|||
'metadata': { |
|||
'odoo_id': self.res_id, |
|||
'odoo_db': 'test', |
|||
'odoo_model': self.model, |
|||
}, |
|||
'resends': [], |
|||
'state': 'spam', |
|||
'_version': 'abcdefghi123456ABCDEFG', |
|||
'template': None, |
|||
'_id': self.mandrill_message_id, |
|||
'email': 'username02@example.com', |
|||
'opens': [], |
|||
'subject': 'My favorite subject', |
|||
}, |
|||
'_id': self.mandrill_message_id, |
|||
'event': 'spam', |
|||
'ts': 1455186366 |
|||
} |
|||
self.event_reject = { |
|||
'msg': { |
|||
'_id': self.mandrill_message_id, |
|||
'subaccount': None, |
|||
'tags': [], |
|||
'smtp_events': [], |
|||
'ts': 1455194291, |
|||
'email': 'username02@example.com', |
|||
'metadata': { |
|||
'odoo_id': self.res_id, |
|||
'odoo_db': 'test', |
|||
'odoo_model': self.model, |
|||
}, |
|||
'state': 'rejected', |
|||
'sender': 'username01@example.com', |
|||
'template': None, |
|||
'reject': None, |
|||
'resends': [], |
|||
'clicks': [], |
|||
'opens': [], |
|||
'subject': 'My favorite subject', |
|||
}, |
|||
'_id': self.mandrill_message_id, |
|||
'event': 'reject', |
|||
'ts': 1455194291, |
|||
} |
|||
self.event_unsub = { |
|||
'msg': { |
|||
'_id': self.mandrill_message_id, |
|||
'subaccount': None, |
|||
'tags': [], |
|||
'smtp_events': [], |
|||
'ts': 1455194291, |
|||
'email': 'username02@example.com', |
|||
'metadata': { |
|||
'odoo_id': self.res_id, |
|||
'odoo_db': 'test', |
|||
'odoo_model': self.model, |
|||
}, |
|||
'state': 'unsub', |
|||
'sender': 'username01@example.com', |
|||
'template': None, |
|||
'reject': None, |
|||
'resends': [], |
|||
'clicks': [], |
|||
'opens': [], |
|||
'subject': 'My favorite subject', |
|||
}, |
|||
'_id': self.mandrill_message_id, |
|||
'event': 'unsub', |
|||
'ts': 1455194291, |
|||
} |
|||
self.message = message_obj.create( |
|||
message_obj._message_prepare( |
|||
self.mandrill_message_id, 'deferral', self.event_deferral)) |
|||
|
|||
# Test Unit: mail_mail.py |
|||
def test_mandrill_headers_add(self): |
|||
mail_obj = self.env['mail.mail'] |
|||
message = self.env['mail.message'].create({ |
|||
'author_id': self.partner_01.id, |
|||
'subject': 'Test subject', |
|||
'body': 'Test body', |
|||
'partner_ids': [(4, self.partner_02.id)], |
|||
'model': self.model, |
|||
'res_id': self.res_id, |
|||
}) |
|||
mail = mail_obj.create({ |
|||
'mail_message_id': message.id, |
|||
}) |
|||
mail._mandrill_headers_add() |
|||
headers = safe_eval(mail.headers) |
|||
self.assertIn('X-MC-Metadata', headers) |
|||
metadata = json.loads(headers.get('X-MC-Metadata', '[]')) |
|||
self.assertIn('odoo_db', metadata) |
|||
self.assertIn('odoo_model', metadata) |
|||
self.assertIn('odoo_id', metadata) |
|||
self.assertEqual(metadata['odoo_model'], self.model) |
|||
self.assertEqual(metadata['odoo_id'], self.res_id) |
|||
|
|||
# Test Unit: mail_mandrill_message.py |
|||
def test_message_prepare(self): |
|||
data = self.env['mail.mandrill.message']._message_prepare( |
|||
self.mandrill_message_id, 'deferral', self.event_deferral) |
|||
self.assertEqual(data['mandrill_id'], self.mandrill_message_id) |
|||
self.assertEqual(data['timestamp'], |
|||
self.event_deferral['msg']['ts']) |
|||
self.assertEqual(data['recipient'], |
|||
self.event_deferral['msg']['email']) |
|||
self.assertEqual(data['sender'], |
|||
self.event_deferral['msg']['sender']) |
|||
self.assertEqual(data['name'], |
|||
self.event_deferral['msg']['subject']) |
|||
|
|||
def test_event_prepare(self): |
|||
data = self.env['mail.mandrill.message']._event_prepare( |
|||
self.message, 'deferral', self.event_deferral) |
|||
self.assertEqual(self.message.state, 'deferred') |
|||
self.assertEqual(data['message_id'], self.message.id) |
|||
self.assertEqual(data['event_type'], 'deferral') |
|||
self.assertEqual(data['timestamp'], self.event_deferral['ts']) |
|||
|
|||
def test_process(self): |
|||
event = self.env['mail.mandrill.message'].process( |
|||
self.mandrill_message_id, 'deferral', self.event_deferral) |
|||
self.assertEqual(event.message_id.mandrill_id, |
|||
self.mandrill_message_id) |
|||
self.assertEqual(event.message_id.state, 'deferred') |
|||
self.assertEqual(event.event_type, 'deferral') |
|||
self.assertEqual(event.timestamp, self.event_deferral['ts']) |
|||
|
|||
# Test Unit: mail_mandrill_event.py |
|||
def test_process_send(self): |
|||
data = self.env['mail.mandrill.event'].process_send( |
|||
self.message, self.event_send) |
|||
self.assertEqual(self.message.state, 'sent') |
|||
self.assertEqual(data['message_id'], self.message.id) |
|||
self.assertEqual(data['event_type'], 'send') |
|||
self.assertEqual(data['timestamp'], self.event_send['ts']) |
|||
|
|||
def test_process_deferral(self): |
|||
data = self.env['mail.mandrill.event'].process_deferral( |
|||
self.message, self.event_deferral) |
|||
self.assertEqual(self.message.state, 'deferred') |
|||
self.assertEqual(data['message_id'], self.message.id) |
|||
self.assertEqual(data['event_type'], 'deferral') |
|||
self.assertEqual(data['timestamp'], self.event_deferral['ts']) |
|||
|
|||
def test_process_hard_bounce(self): |
|||
data = self.env['mail.mandrill.event'].process_hard_bounce( |
|||
self.message, self.event_hard_bounce) |
|||
self.assertEqual(self.message.state, 'bounced') |
|||
self.assertEqual(self.message.bounce_type, |
|||
self.event_hard_bounce['msg']['bounce_description']) |
|||
self.assertEqual(self.message.bounce_description, |
|||
self.event_hard_bounce['msg']['diag']) |
|||
self.assertEqual(data['message_id'], self.message.id) |
|||
self.assertEqual(data['event_type'], 'hard_bounce') |
|||
self.assertEqual(data['timestamp'], self.event_hard_bounce['ts']) |
|||
|
|||
def test_process_soft_bounce(self): |
|||
data = self.env['mail.mandrill.event'].process_soft_bounce( |
|||
self.message, self.event_soft_bounce) |
|||
self.assertEqual(self.message.state, 'bounced') |
|||
self.assertEqual(self.message.bounce_type, |
|||
self.event_soft_bounce['msg']['bounce_description']) |
|||
self.assertEqual(self.message.bounce_description, |
|||
self.event_soft_bounce['msg']['diag']) |
|||
self.assertEqual(data['message_id'], self.message.id) |
|||
self.assertEqual(data['event_type'], 'soft_bounce') |
|||
self.assertEqual(data['timestamp'], self.event_soft_bounce['ts']) |
|||
|
|||
def test_process_open(self): |
|||
data = self.env['mail.mandrill.event'].process_open( |
|||
self.message, self.event_open) |
|||
self.assertEqual(self.message.state, 'opened') |
|||
self.assertEqual(data['message_id'], self.message.id) |
|||
self.assertEqual(data['event_type'], 'open') |
|||
self.assertEqual(data['timestamp'], self.event_open['ts']) |
|||
self.assertEqual(data['ip'], self.event_open['ip']) |
|||
|
|||
def test_process_click(self): |
|||
data = self.env['mail.mandrill.event'].process_click( |
|||
self.message, self.event_open) |
|||
self.assertEqual(self.message.state, 'opened') |
|||
self.assertEqual(data['message_id'], self.message.id) |
|||
self.assertEqual(data['event_type'], 'click') |
|||
self.assertEqual(data['timestamp'], self.event_open['ts']) |
|||
self.assertEqual(data['ip'], self.event_open['ip']) |
|||
|
|||
def test_process_spam(self): |
|||
data = self.env['mail.mandrill.event'].process_spam( |
|||
self.message, self.event_spam) |
|||
self.assertEqual(self.message.state, 'spam') |
|||
self.assertEqual(data['message_id'], self.message.id) |
|||
self.assertEqual(data['event_type'], 'spam') |
|||
self.assertEqual(data['timestamp'], self.event_spam['ts']) |
|||
|
|||
def test_process_reject(self): |
|||
data = self.env['mail.mandrill.event'].process_reject( |
|||
self.message, self.event_reject) |
|||
self.assertEqual(self.message.state, 'rejected') |
|||
self.assertEqual(data['message_id'], self.message.id) |
|||
self.assertEqual(data['event_type'], 'reject') |
|||
self.assertEqual(data['timestamp'], self.event_reject['ts']) |
|||
|
|||
def test_process_unsub(self): |
|||
data = self.env['mail.mandrill.event'].process_unsub( |
|||
self.message, self.event_unsub) |
|||
self.assertEqual(self.message.state, 'unsub') |
|||
self.assertEqual(data['message_id'], self.message.id) |
|||
self.assertEqual(data['event_type'], 'unsub') |
|||
self.assertEqual(data['timestamp'], self.event_unsub['ts']) |
@ -0,0 +1,109 @@ |
|||
<?xml version="1.0" encoding="utf-8"?> |
|||
<openerp> |
|||
<data> |
|||
|
|||
<record model="ir.ui.view" id="view_mail_mandrill_event_form"> |
|||
<field name="name">mail.mandrill.event.form</field> |
|||
<field name="model">mail.mandrill.event</field> |
|||
<field name="arch" type="xml"> |
|||
<form string="Mandrill email event"> |
|||
<sheet> |
|||
<group> |
|||
<group> |
|||
<field name="message_id"/> |
|||
<field name="event_type"/> |
|||
</group> |
|||
<group> |
|||
<field name="timestamp"/> |
|||
<field name="time"/> |
|||
<field name="date"/> |
|||
</group> |
|||
</group> |
|||
<group attrs="{'invisible': [('event_type', 'not in', ('open', 'click'))]}"> |
|||
<field name="url"/> |
|||
</group> |
|||
<group attrs="{'invisible': [('event_type', 'not in', ('open', 'click'))]}"> |
|||
<group> |
|||
<field name="mobile"/> |
|||
<field name="ip"/> |
|||
<field name="user_country_id"/> |
|||
</group> |
|||
<group> |
|||
<field name="user_agent"/> |
|||
<field name="ua_family"/> |
|||
<field name="ua_type"/> |
|||
<field name="os_family"/> |
|||
</group> |
|||
</group> |
|||
</sheet> |
|||
</form> |
|||
</field> |
|||
</record> |
|||
|
|||
<record model="ir.ui.view" id="view_mail_mandrill_event_tree"> |
|||
<field name="name">mail.mandrill.event.tree</field> |
|||
<field name="model">mail.mandrill.event</field> |
|||
<field name="arch" type="xml"> |
|||
<tree string="Mandrill email events" colors="grey:event_type in ('deferral');black:event_type in ('send');red:event_type in ('hard_bounce', 'soft_bounce', 'spam', 'reject');blue:event_type in ('unsub', 'click', 'open')"> |
|||
<field name="time"/> |
|||
<field name="message_id" string="Message subject"/> |
|||
<field name="event_type"/> |
|||
<field name="date" invisible="1"/> |
|||
<field name="ip"/> |
|||
<field name="url"/> |
|||
<field name="user_country_id" string="Country"/> |
|||
<field name="os_family" string="OS"/> |
|||
<field name="ua_family" string="User agent"/> |
|||
</tree> |
|||
</field> |
|||
</record> |
|||
|
|||
<record model="ir.ui.view" id="view_mail_mandrill_event_search"> |
|||
<field name="name">mail.mandrill.event.search</field> |
|||
<field name="model">mail.mandrill.event</field> |
|||
<field name="arch" type="xml"> |
|||
<search string="Mandrill event Search"> |
|||
<field name="message_id" string="Message subject" |
|||
filter_domain="[('message_id','ilike',self)]"/> |
|||
<field name="message_id" string="Subject"/> |
|||
<field name="time" string="Time"/> |
|||
<field name="date" string="Date"/> |
|||
<field name="ip" string="IP"/> |
|||
<field name="url" string="URL"/> |
|||
<filter name="send" string="Send" domain="[('event_type', '=', 'send')]"/> |
|||
<filter name="click" string="Click" domain="[('event_type', '=', 'click')]"/> |
|||
<filter name="open" string="Open" domain="[('event_type', '=', 'open')]"/> |
|||
<filter name="unsub" string="Unsubscribe" domain="[('event_type', '=', 'unsub')]"/> |
|||
<filter name="bounce" string="Bounce" |
|||
domain="[('event_type', 'in', ('hard_bounce', 'soft_bounce'))]"/> |
|||
<filter name="exception" string="Failed" |
|||
domain="[('event_type', 'in', ('reject', 'spam'))]"/> |
|||
<separator/> |
|||
<group expand="0" string="Group By"> |
|||
<filter string="Type" domain="[]" context="{'group_by': 'event_type'}"/> |
|||
<filter string="Message subject" domain="[]" context="{'group_by': 'message_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'}"/> |
|||
</group> |
|||
</search> |
|||
</field> |
|||
</record> |
|||
|
|||
<record id="action_view_mail_mandrill_event" model="ir.actions.act_window"> |
|||
<field name="name">Mandrill events</field> |
|||
<field name="res_model">mail.mandrill.event</field> |
|||
<field name="view_type">form</field> |
|||
<field name="view_mode">tree,form</field> |
|||
<field name="search_view_id" ref="view_mail_mandrill_event_search"/> |
|||
</record> |
|||
|
|||
<!-- Add menu entry in Settings/Email --> |
|||
<menuitem name="Mandrill events" id="menu_mail_mandrill_event" |
|||
parent="base.menu_email" |
|||
action="action_view_mail_mandrill_event"/> |
|||
|
|||
</data> |
|||
</openerp> |
@ -0,0 +1,114 @@ |
|||
<?xml version="1.0" encoding="utf-8"?> |
|||
<openerp> |
|||
<data> |
|||
|
|||
<record model="ir.ui.view" id="view_mail_mandrill_message_form"> |
|||
<field name="name">mail.mandrill.message.form</field> |
|||
<field name="model">mail.mandrill.message</field> |
|||
<field name="arch" type="xml"> |
|||
<form string="Mandrill email message"> |
|||
<header> |
|||
<field name="state" widget="statusbar"/> |
|||
</header> |
|||
<sheet> |
|||
<group> |
|||
<field name="name"/> |
|||
<field name="tags"/> |
|||
</group> |
|||
<group> |
|||
<group> |
|||
<field name="mandrill_id"/> |
|||
<field name="recipient"/> |
|||
<field name="sender"/> |
|||
</group> |
|||
<group> |
|||
<field name="timestamp"/> |
|||
<field name="time"/> |
|||
<field name="date"/> |
|||
</group> |
|||
</group> |
|||
<group attrs="{'invisible': [('bounce_type', '=', False)]}"> |
|||
<field name="bounce_type"/> |
|||
<field name="bounce_description"/> |
|||
</group> |
|||
<label for="metadata"/> |
|||
<div> |
|||
<field name="metadata"/> |
|||
</div> |
|||
<label for="event_ids"/> |
|||
<div> |
|||
<field name="event_ids"> |
|||
<tree string="Mandrill email events" colors="grey:event_type in ('deferral');black:event_type in ('send');red:event_type in ('hard_bounce', 'soft_bounce', 'spam', 'reject');blue:event_type in ('unsub', 'click', 'open')"> |
|||
<field name="time"/> |
|||
<field name="event_type"/> |
|||
<field name="ip"/> |
|||
<field name="url"/> |
|||
<field name="user_country_id" string="Country"/> |
|||
<field name="os_family" string="OS"/> |
|||
<field name="ua_family" string="User agent"/> |
|||
</tree> |
|||
</field> |
|||
</div> |
|||
</sheet> |
|||
</form> |
|||
</field> |
|||
</record> |
|||
|
|||
<record model="ir.ui.view" id="view_mail_mandrill_message_tree"> |
|||
<field name="name">mail.mandrill.message.tree</field> |
|||
<field name="model">mail.mandrill.message</field> |
|||
<field name="arch" type="xml"> |
|||
<tree string="Mandrill emails" colors="grey:state in (False, 'deferred');black:state in ('sent');green:state in ('opened');red:state in ('rejected', 'spam', 'bounced', 'soft-bounced');blue:state in ('unsub')"> |
|||
<field name="state" invisible="1"/> |
|||
<field name="time"/> |
|||
<field name="date" invisible="1"/> |
|||
<field name="name"/> |
|||
<field name="sender" string="Sender"/> |
|||
<field name="recipient" string="Recipient"/> |
|||
<field name="tags" string="Tags"/> |
|||
</tree> |
|||
</field> |
|||
</record> |
|||
|
|||
<record model="ir.ui.view" id="view_mail_mandrill_message_search"> |
|||
<field name="name">mail.mandrill.message.search</field> |
|||
<field name="model">mail.mandrill.message</field> |
|||
<field name="arch" type="xml"> |
|||
<search string="Mandrill email Search"> |
|||
<field name="sender" string="Email" |
|||
filter_domain="['|', ('sender','ilike',self), ('recipient','ilike',self)]"/> |
|||
<field name="name" string="Subject"/> |
|||
<field name="time" string="Time"/> |
|||
<field name="date" string="Date"/> |
|||
<filter name="deferred" string="Deferred" domain="[('state', '=', 'deferred')]"/> |
|||
<filter name="sent" string="Sent" domain="[('state', 'in', ('sent', 'opened'))]"/> |
|||
<filter name="unsub" string="Unsubscribed" domain="[('state', '=', 'unsub')]"/> |
|||
<filter name="exception" string="Failed" |
|||
domain="[('state', 'in', ('rejected', 'spam', 'bounced', 'soft-bounced'))]"/> |
|||
<separator/> |
|||
<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'}"/> |
|||
</group> |
|||
</search> |
|||
</field> |
|||
</record> |
|||
|
|||
|
|||
<record id="action_view_mail_mandrill_message" model="ir.actions.act_window"> |
|||
<field name="name">Mandrill emails</field> |
|||
<field name="res_model">mail.mandrill.message</field> |
|||
<field name="view_type">form</field> |
|||
<field name="view_mode">tree,form</field> |
|||
<field name="search_view_id" ref="view_mail_mandrill_message_search"/> |
|||
</record> |
|||
|
|||
<!-- Add menu entry in Settings/Email --> |
|||
<menuitem name="Mandrill emails" id="menu_mail_mandrill_message" |
|||
parent="base.menu_email" |
|||
action="action_view_mail_mandrill_message"/> |
|||
|
|||
</data> |
|||
</openerp> |
Write
Preview
Loading…
Cancel
Save
Reference in new issue