summaryrefslogtreecommitdiff
path: root/addons
diff options
context:
space:
mode:
authorThibault Delavallée <tde@openerp.com>2013-09-13 13:54:08 +0200
committerThibault Delavallée <tde@openerp.com>2013-09-13 13:54:08 +0200
commited62d1dac714398c58b5cc149f144ebc70b56438 (patch)
tree0260354278df9a493585953bd3bde3debc61e6e1 /addons
parent23f9324b94bea72bddb8d76bfe291e7636c0954f (diff)
[REF] mass_mailing: first refactor
Mail statistics are now stored onto a separated object (mail.mail.statistics), allowing to handle emails separately from statistics (among other removing mail.mail entries while keeping statistics). Everything linnked to opened/replied/bounce is not managed by mass_mailing, removed added code in mail module. bzr revid: tde@openerp.com-20130913115408-322cyjipdg680as6
Diffstat (limited to 'addons')
-rw-r--r--addons/mail/controllers/main.py8
-rw-r--r--addons/mail/data/mail_data.xml6
-rw-r--r--addons/mail/mail_mail.py47
-rw-r--r--addons/mail/mail_mail_view.xml5
-rw-r--r--addons/mail/mail_thread.py101
-rw-r--r--addons/mail/res_config.py26
-rw-r--r--addons/mail/res_config_view.xml8
-rw-r--r--addons/mail/tests/__init__.py3
-rw-r--r--addons/mass_mailing/__init__.py1
-rw-r--r--addons/mass_mailing/__openerp__.py4
-rw-r--r--addons/mass_mailing/controllers/__init__.py3
-rw-r--r--addons/mass_mailing/controllers/main.py12
-rw-r--r--addons/mass_mailing/mail_data.xml12
-rw-r--r--addons/mass_mailing/mail_mail.py54
-rw-r--r--addons/mass_mailing/mail_mail_view.xml20
-rw-r--r--addons/mass_mailing/mail_thread.py82
-rw-r--r--addons/mass_mailing/mass_mailing.py184
-rw-r--r--addons/mass_mailing/mass_mailing_demo.xml45
-rw-r--r--addons/mass_mailing/mass_mailing_view.xml68
-rw-r--r--addons/mass_mailing/security/ir.model.access.csv5
-rw-r--r--addons/mass_mailing/wizard/mail_compose_message.py18
-rw-r--r--addons/mass_mailing/wizard/mail_compose_message_view.xml6
-rw-r--r--addons/mass_mailing/wizard/mail_mass_mailing_create_segment.py34
-rw-r--r--addons/mass_mailing/wizard/mail_mass_mailing_create_segment.xml26
24 files changed, 431 insertions, 347 deletions
diff --git a/addons/mail/controllers/main.py b/addons/mail/controllers/main.py
index 27373c34add..959877ad098 100644
--- a/addons/mail/controllers/main.py
+++ b/addons/mail/controllers/main.py
@@ -5,7 +5,6 @@ import openerp
from openerp import SUPERUSER_ID
import openerp.addons.web.http as http
from openerp.addons.web.controllers.main import content_disposition
-from openerp.addons.web.http import request
class MailController(http.Controller):
@@ -38,10 +37,3 @@ class MailController(http.Controller):
except psycopg2.Error:
pass
return True
-
- @http.route('/mail/track/<int:mail_id>/blank.gif', type='http', auth='admin')
- def track_read_email(self, mail_id):
- """ Email tracking. """
- mail_mail = request.registry.get('mail.mail')
- mail_mail.set_opened(request.cr, request.uid, [mail_id])
- return False
diff --git a/addons/mail/data/mail_data.xml b/addons/mail/data/mail_data.xml
index 79754f5f5a4..e1ca797670b 100644
--- a/addons/mail/data/mail_data.xml
+++ b/addons/mail/data/mail_data.xml
@@ -51,12 +51,6 @@
<field name="value">catchall</field>
</record>
- <!-- Bounce Email Alias -->
- <record id="icp_mail_bounce_alias" model="ir.config_parameter">
- <field name="key">mail.bounce.alias</field>
- <field name="value">bounce</field>
- </record>
-
<!-- Discussion subtype for messaging / Chatter -->
<record id="mt_comment" model="mail.message.subtype">
<field name="name">Discussions</field>
diff --git a/addons/mail/mail_mail.py b/addons/mail/mail_mail.py
index 127d1502028..91799b0ac63 100644
--- a/addons/mail/mail_mail.py
+++ b/addons/mail/mail_mail.py
@@ -62,17 +62,6 @@ class mail_mail(osv.Model):
# and during unlink() we will not cascade delete the parent and its attachments
'notification': fields.boolean('Is Notification',
help='Mail has been created to notify people of an existing mail.message'),
- # Bounce and tracking
- 'opened': fields.datetime(
- 'Opened',
- help='Date when this email has been opened for the first time.'),
- 'replied': fields.datetime(
- 'Replied',
- help='Date when this email has been replied for the first time.'),
- 'bounced': fields.datetime(
- 'Bounced',
- help='Date when this email has bounced.'
- ),
}
_defaults = {
@@ -106,30 +95,6 @@ class mail_mail(osv.Model):
def cancel(self, cr, uid, ids, context=None):
return self.write(cr, uid, ids, {'state': 'cancel'}, context=context)
- def set_opened(self, cr, uid, ids, context=None):
- """ Set as opened """
- existing_ids = self.exists(cr, uid, ids, context=context)
- for mail in self.browse(cr, uid, existing_ids, context=context):
- if not mail.opened:
- self.write(cr, uid, [mail.id], {'opened': fields.datetime.now()}, context=context)
- return True
-
- def set_replied(self, cr, uid, ids, context=None):
- """ Set as replied """
- existing_ids = self.exists(cr, uid, ids, context=context)
- for mail in self.browse(cr, uid, existing_ids, context=context):
- if not mail.replied:
- self.write(cr, uid, [mail.id], {'replied': fields.datetime.now()}, context=context)
- return True
-
- def set_bounced(self, cr, uid, ids, context=None):
- """ Set as bounced """
- existing_ids = self.exists(cr, uid, ids, context=context)
- for mail in self.browse(cr, uid, existing_ids, context=context):
- if not mail.bounced:
- self.write(cr, uid, [mail.id], {'bounced': fields.datetime.now()}, context=context)
- return True
-
def process_email_queue(self, cr, uid, ids=None, context=None):
"""Send immediately queued messages, committing after each
message is sent - this is not transactional and should
@@ -200,15 +165,6 @@ class mail_mail(osv.Model):
else:
return None
- def _get_tracking_url(self, cr, uid, mail, partner=None, context=None):
- if not mail.auto_delete:
- base_url = self.pool.get('ir.config_parameter').get_param(cr, uid, 'web.base.url')
- track_url = urljoin(base_url, 'mail/track/%d/blank.gif' % mail.id)
- print base_url, track_url
- return '<img src="%s" alt=""/>' % track_url
- else:
- return ''
-
def send_get_mail_subject(self, cr, uid, mail, force=False, partner=None, context=None):
""" If subject is void and record_name defined: '<Author> posted on <Resource>'
@@ -233,11 +189,8 @@ class mail_mail(osv.Model):
# generate footer
link = self._get_partner_access_link(cr, uid, mail, partner, context=context)
- tracking_url = self._get_tracking_url(cr, uid, mail, partner, context=context)
if link:
body = tools.append_content_to_html(body, link, plaintext=False, container_tag='div')
- if tracking_url:
- body = tools.append_content_to_html(body, tracking_url, plaintext=False, container_tag='div')
return body
def send_get_email_dict(self, cr, uid, mail, partner=None, context=None):
diff --git a/addons/mail/mail_mail_view.xml b/addons/mail/mail_mail_view.xml
index 9a2a178d61c..dd6a78fd8a4 100644
--- a/addons/mail/mail_mail_view.xml
+++ b/addons/mail/mail_mail_view.xml
@@ -41,11 +41,6 @@
<field name="model"/>
<field name="res_id"/>
</group>
- <group string="Tracking">
- <field name="opened"/>
- <field name="replied"/>
- <field name="bounced"/>
- </group>
</div>
<div>
<group string="Headers">
diff --git a/addons/mail/mail_thread.py b/addons/mail/mail_thread.py
index 8c51e99a94c..2bfd17fdb59 100644
--- a/addons/mail/mail_thread.py
+++ b/addons/mail/mail_thread.py
@@ -778,7 +778,6 @@ class mail_thread(osv.AbstractModel):
"""
assert isinstance(message, Message), 'message must be an email.message.Message at this point'
fallback_model = model
- bounce_alias = self.pool['ir.config_parameter'].get_param(cr, uid, "mail.bounce.alias", context=context)
# Get email.message.Message variables for future processing
message_id = message.get('Message-Id')
@@ -787,25 +786,6 @@ class mail_thread(osv.AbstractModel):
references = decode_header(message, 'References')
in_reply_to = decode_header(message, 'In-Reply-To')
- # 0. Verify whether this is a bounced email (wrong destination,...) -> use it to collect data, such as dead leads
- if bounce_alias in email_to:
- bounce_match = tools.bounce_re.search(email_to)
- if bounce_match:
- bounced_mail_id = bounce_match.group(1)
- self.pool['mail.mail'].set_bounced(cr, uid, [bounced_mail_id], context=context)
- if self.pool['mail.mail'].exists(cr, uid, bounced_mail_id):
- mail = self.pool['mail.mail'].browse(cr, uid, bounced_mail_id, context=context)
- bounced_model = mail.model
- bounced_thread_id = mail.res_id
- else:
- bounced_model = bounce_match.group(2)
- bounced_thread_id = int(bounce_match.group(3)) if bounce_match.group(3) else 0
- _logger.info('Routing mail from %s to %s with Message-Id %s: bounced mail from mail %s, model: %s, thread_id: %s',
- email_from, email_to, message_id, bounced_mail_id, bounced_model, bounced_thread_id)
- if bounced_model and bounced_model in self.pool and hasattr(self.pool[bounced_model], 'message_receive_bounce'):
- self.pool[bounced_model].message_receive_bounce(cr, uid, [bounced_thread_id], mail_id=bounced_mail_id, context=context)
- return []
-
# 1. Verify if this is a reply to an existing thread
thread_references = references or in_reply_to
ref_match = thread_references and tools.reference_re.search(thread_references)
@@ -894,6 +874,40 @@ class mail_thread(osv.AbstractModel):
"No possible route found for incoming message from %s to %s (Message-Id %s:)." \
"Create an appropriate mail.alias or force the destination model." % (email_from, email_to, message_id)
+ def message_route_process(self, cr, uid, msg, routes, context=None):
+ # postpone setting msg.partner_ids after message_post, to avoid double notifications
+ partner_ids = msg.pop('partner_ids', [])
+ thread_id = False
+ for model, thread_id, custom_values, user_id, alias in routes:
+ if self._name == 'mail.thread':
+ context.update({'thread_model': model})
+ if model:
+ model_pool = self.pool[model]
+ assert thread_id and hasattr(model_pool, 'message_update') or hasattr(model_pool, 'message_new'), \
+ "Undeliverable mail with Message-Id %s, model %s does not accept incoming emails" % \
+ (msg['message_id'], model)
+
+ # disabled subscriptions during message_new/update to avoid having the system user running the
+ # email gateway become a follower of all inbound messages
+ nosub_ctx = dict(context, mail_create_nosubscribe=True, mail_create_nolog=True)
+ if thread_id and hasattr(model_pool, 'message_update'):
+ model_pool.message_update(cr, user_id, [thread_id], msg, context=nosub_ctx)
+ else:
+ thread_id = model_pool.message_new(cr, user_id, msg, custom_values, context=nosub_ctx)
+ else:
+ assert thread_id == 0, "Posting a message without model should be with a null res_id, to create a private message."
+ model_pool = self.pool.get('mail.thread')
+ if not hasattr(model_pool, 'message_post'):
+ context['thread_model'] = model
+ model_pool = self.pool['mail.thread']
+ new_msg_id = model_pool.message_post(cr, uid, [thread_id], context=context, subtype='mail.mt_comment', **msg)
+
+ if partner_ids:
+ # postponed after message_post, because this is an external message and we don't want to create
+ # duplicate emails due to notifications
+ self.pool.get('mail.message').write(cr, uid, [new_msg_id], {'partner_ids': partner_ids}, context=context)
+ return thread_id
+
def message_process(self, cr, uid, model, message, custom_values=None,
save_original=False, strip_attachments=False,
thread_id=None, context=None):
@@ -946,8 +960,7 @@ class mail_thread(osv.AbstractModel):
msg = self.message_parse(cr, uid, msg_txt, save_original=save_original, context=context)
if strip_attachments:
msg.pop('attachments', None)
- # postpone setting msg.partner_ids after message_post, to avoid double notifications
- partner_ids = msg.pop('partner_ids', [])
+
if msg.get('message_id'): # should always be True as message_parse generate one if missing
existing_msg_ids = self.pool.get('mail.message').search(cr, SUPERUSER_ID, [
('message_id', '=', msg.get('message_id')),
@@ -959,36 +972,7 @@ class mail_thread(osv.AbstractModel):
# find possible routes for the message
routes = self.message_route(cr, uid, msg_txt, msg, model, thread_id, custom_values, context=context)
- thread_id = False
- for model, thread_id, custom_values, user_id, alias in routes:
- if self._name == 'mail.thread':
- context.update({'thread_model': model})
- if model:
- model_pool = self.pool[model]
- assert thread_id and hasattr(model_pool, 'message_update') or hasattr(model_pool, 'message_new'), \
- "Undeliverable mail with Message-Id %s, model %s does not accept incoming emails" % \
- (msg['message_id'], model)
-
- # disabled subscriptions during message_new/update to avoid having the system user running the
- # email gateway become a follower of all inbound messages
- nosub_ctx = dict(context, mail_create_nosubscribe=True, mail_create_nolog=True)
- if thread_id and hasattr(model_pool, 'message_update'):
- model_pool.message_update(cr, user_id, [thread_id], msg, context=nosub_ctx)
- else:
- thread_id = model_pool.message_new(cr, user_id, msg, custom_values, context=nosub_ctx)
- else:
- assert thread_id == 0, "Posting a message without model should be with a null res_id, to create a private message."
- model_pool = self.pool.get('mail.thread')
- if not hasattr(model_pool, 'message_post'):
- context['thread_model'] = model
- model_pool = self.pool['mail.thread']
- new_msg_id = model_pool.message_post(cr, uid, [thread_id], context=context, subtype='mail.mt_comment', **msg)
-
- if partner_ids:
- # postponed after message_post, because this is an external message and we don't want to create
- # duplicate emails due to notifications
- self.pool.get('mail.message').write(cr, uid, [new_msg_id], {'partner_ids': partner_ids}, context=context)
-
+ thread_id = self.message_route_process(cr, uid, msg, routes, context=context)
return thread_id
def message_new(self, cr, uid, msg_dict, custom_values=None, context=None):
@@ -1044,15 +1028,6 @@ class mail_thread(osv.AbstractModel):
self.write(cr, uid, ids, update_vals, context=context)
return True
- def message_receive_bounce(self, cr, uid, ids, mail_id=None, context=None):
- """Called by ``message_process`` when a bounce email (such as Undelivered
- Mail Returned to Sender) is received for an existing thread. The default
- behavior is to check is an integer ``message_bounce`` column exists.
- If it is the case, its content is incremented. """
- if self._all_columns.get('message_bounce'):
- for obj in self.browse(cr, uid, ids, context=context):
- self.write(cr, uid, [obj.id], {'message_bounce': obj.message_bounce + 1}, context=context)
-
def _message_extract_payload(self, message, save_original=False):
"""Extract body as HTML and attachments from the mail message"""
attachments = []
@@ -1303,8 +1278,8 @@ class mail_thread(osv.AbstractModel):
return result
def message_post(self, cr, uid, thread_id, body='', subject=None, type='notification',
- subtype=None, parent_id=False, attachments=None, context=None,
- content_subtype='html', **kwargs):
+ subtype=None, parent_id=False, attachments=None, context=None,
+ content_subtype='html', **kwargs):
""" Post a new message in an existing thread, returning the new
mail.message ID.
diff --git a/addons/mail/res_config.py b/addons/mail/res_config.py
index 750c82d54ba..a0cc7ef5c53 100644
--- a/addons/mail/res_config.py
+++ b/addons/mail/res_config.py
@@ -32,14 +32,6 @@ class project_configuration(osv.TransientModel):
'Alias Domain',
help="If you have setup a catch-all email domain redirected to the OpenERP server, enter the domain name here."
),
- 'alias_bounce': fields.char(
- 'Return-Path for Emails',
- help="Return-Path of send Emails. Used to compute bounced emails.",
- ),
- 'alias_catchall': fields.char(
- 'Default Alias',
- help='Default email alias',
- ),
}
def get_default_alias_domain(self, cr, uid, ids, context=None):
@@ -56,21 +48,3 @@ class project_configuration(osv.TransientModel):
config_parameters = self.pool.get("ir.config_parameter")
for record in self.browse(cr, uid, ids, context=context):
config_parameters.set_param(cr, uid, "mail.catchall.domain", record.alias_domain or '', context=context)
-
- def get_default_alias_bounce(self, cr, uid, ids, context=None):
- alias_bounce = self.pool.get("ir.config_parameter").get_param(cr, uid, "mail.bounce.alias", context=context)
- return {'alias_bounce': alias_bounce}
-
- def set_alias_bounce(self, cr, uid, ids, context=None):
- config_parameters = self.pool.get("ir.config_parameter")
- for record in self.browse(cr, uid, ids, context=context):
- config_parameters.set_param(cr, uid, "mail.bounce.alias", record.alias_bounce or '', context=context)
-
- def get_default_alias_catchall(self, cr, uid, ids, context=None):
- alias_catchall = self.pool.get("ir.config_parameter").get_param(cr, uid, "mail.catchall.alias", context=context)
- return {'alias_catchall': alias_catchall}
-
- def set_alias_catchall(self, cr, uid, ids, context=None):
- config_parameters = self.pool.get("ir.config_parameter")
- for record in self.browse(cr, uid, ids, context=context):
- config_parameters.set_param(cr, uid, "mail.catchall.alias", record.alias_catchall or '', context=context)
diff --git a/addons/mail/res_config_view.xml b/addons/mail/res_config_view.xml
index 440cf358e47..d3797ba2dcc 100644
--- a/addons/mail/res_config_view.xml
+++ b/addons/mail/res_config_view.xml
@@ -11,14 +11,6 @@
<label for="alias_domain" class="oe_inline"/>
<field name="alias_domain" placeholder="mycompany.my.openerp.com" class="oe_inline"/>
</div>
- <div>
- <label for="alias_bounce" class="oe_inline"/>
- <field name="alias_bounce" placeholder="bounce" class="oe_inline"/>
- </div>
- <div>
- <label for="alias_catchall" class="oe_inline"/>
- <field name="alias_catchall" placeholder="catchall" class="oe_inline"/>
- </div>
</xpath>
</field>
</record>
diff --git a/addons/mail/tests/__init__.py b/addons/mail/tests/__init__.py
index ff075d73d73..242beb60bf1 100644
--- a/addons/mail/tests/__init__.py
+++ b/addons/mail/tests/__init__.py
@@ -19,10 +19,9 @@
#
##############################################################################
-from . import test_mail_mail, test_mail_group, test_mail_message, test_mail_features, test_mail_gateway, test_message_read, test_invite
+from . import test_mail_group, test_mail_message, test_mail_features, test_mail_gateway, test_message_read, test_invite
checks = [
- # test_mail_mail,
test_mail_group,
test_mail_message,
test_mail_features,
diff --git a/addons/mass_mailing/__init__.py b/addons/mass_mailing/__init__.py
index 37f3850306a..f7b250bd2ed 100644
--- a/addons/mass_mailing/__init__.py
+++ b/addons/mass_mailing/__init__.py
@@ -22,3 +22,4 @@
import mass_mailing
import mail_mail
import wizard
+import controllers
diff --git a/addons/mass_mailing/__openerp__.py b/addons/mass_mailing/__openerp__.py
index 7f2c26e4a28..0301484afec 100644
--- a/addons/mass_mailing/__openerp__.py
+++ b/addons/mass_mailing/__openerp__.py
@@ -21,6 +21,7 @@
{
'name': 'Mass Mailing Campaigns',
+ 'description': """TODO""",
'version': '1.0',
'author': 'OpenERP',
'website': 'http://www.openerp.com',
@@ -31,11 +32,10 @@
'web_kanban_gauge',
'web_kanban_sparkline',
],
- 'description': """TODO""",
'data': [
+ 'mail_data.xml',
'mass_mailing_view.xml',
'mass_mailing_demo.xml',
- 'mail_mail_view.xml',
'wizard/mail_compose_message_view.xml',
'wizard/mail_mass_mailing_create_segment.xml',
'security/ir.model.access.csv',
diff --git a/addons/mass_mailing/controllers/__init__.py b/addons/mass_mailing/controllers/__init__.py
new file mode 100644
index 00000000000..e11f9ba81bb
--- /dev/null
+++ b/addons/mass_mailing/controllers/__init__.py
@@ -0,0 +1,3 @@
+import main
+
+# vim:expandtab:tabstop=4:softtabstop=4:shiftwidth=4:
diff --git a/addons/mass_mailing/controllers/main.py b/addons/mass_mailing/controllers/main.py
new file mode 100644
index 00000000000..9997d854cf3
--- /dev/null
+++ b/addons/mass_mailing/controllers/main.py
@@ -0,0 +1,12 @@
+
+import openerp.addons.web.http as http
+from openerp.addons.web.http import request
+
+
+class MassMailController(http.Controller):
+ @http.route('/mail/track/<int:mail_id>/blank.gif', type='http', auth='admin')
+ def track_mail_open(self, mail_id):
+ """ Email tracking. """
+ mail_mail_stats = request.registry.get('mail.mail.statistics')
+ mail_mail_stats.set_opened(request.cr, request.uid, mail_ids=[mail_id])
+ return False
diff --git a/addons/mass_mailing/mail_data.xml b/addons/mass_mailing/mail_data.xml
new file mode 100644
index 00000000000..a1a0e404fe3
--- /dev/null
+++ b/addons/mass_mailing/mail_data.xml
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="utf-8"?>
+<openerp>
+ <data noupdate="1">
+
+ <!-- Bounce Email Alias -->
+ <record id="icp_mail_bounce_alias" model="ir.config_parameter">
+ <field name="key">mail.bounce.alias</field>
+ <field name="value">bounce</field>
+ </record>
+
+ </data>
+</openerp>
diff --git a/addons/mass_mailing/mail_mail.py b/addons/mass_mailing/mail_mail.py
index 2ecf38fd31e..5d576c158a9 100644
--- a/addons/mass_mailing/mail_mail.py
+++ b/addons/mass_mailing/mail_mail.py
@@ -2,7 +2,7 @@
##############################################################################
#
# OpenERP, Open Source Management Solution
-# Copyright (C) 2013-today OpenERP SA (<http://www.openerp.com>)
+# Copyright (C) 2013-Today OpenERP SA (<http://www.openerp.com>)
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
@@ -19,7 +19,11 @@
#
##############################################################################
-from openerp.osv import osv, fields
+from urlparse import urljoin
+
+from openerp import tools
+from openerp import SUPERUSER_ID
+from openerp.osv import osv
class MailMail(osv.Model):
@@ -27,23 +31,29 @@ class MailMail(osv.Model):
_name = 'mail.mail'
_inherit = ['mail.mail']
- _columns = {
- 'mass_mailing_segment_id': fields.many2one(
- 'mail.mass_mailing.segment', 'Mass Mailing Segment',
- ondelete='set null',
- ),
- 'mass_mailing_campaign_id': fields.related(
- 'mass_mailing_segment_id', 'mass_mailing_campaign_id',
- type='many2one', ondelete='set null',
- relation='mail.mass_mailing.campaign',
- string='Mass Mailing Campaign',
- store=True, readonly=True,
- ),
- 'template_id': fields.related(
- 'mass_mailing_segment_id', 'template_id',
- type='many2one', ondelete='set null',
- relation='email.template',
- string='Email Template',
- store=True, readonly=True,
- ),
- }
+ def create(self, cr, uid, values, context=None):
+ """ Override mail_mail creation to create an entry in mail.mail.statistics """
+ # TDE note: should be after 'all values computed', to have values (FIXME after merging other branch holding create refactoring)
+ mail_id = super(MailMail, self).create(cr, uid, values, context=context)
+ message_id = self.browse(cr, SUPERUSER_ID, mail_id).message_id
+ self.pool['mail.mail.statistics'].create(
+ cr, uid, {
+ 'mail_mail_id': mail_id,
+ 'message_id': message_id,
+ }, context=context)
+ return mail_id
+
+ def _get_tracking_url(self, cr, uid, mail, partner=None, context=None):
+ base_url = self.pool.get('ir.config_parameter').get_param(cr, uid, 'web.base.url')
+ track_url = urljoin(base_url, 'mail/track/%d/blank.gif' % mail.id)
+ return '<img src="%s" alt=""/>' % track_url
+
+ def send_get_mail_body(self, cr, uid, mail, partner=None, context=None):
+ """ Override to add the tracking URL to the body. """
+ body = super(MailMail, self).send_get_mail_body(cr, uid, mail, partner=partner, context=context)
+
+ # generate tracking URL
+ tracking_url = self._get_tracking_url(cr, uid, mail, partner, context=context)
+ if tracking_url:
+ body = tools.append_content_to_html(body, tracking_url, plaintext=False, container_tag='div')
+ return body
diff --git a/addons/mass_mailing/mail_mail_view.xml b/addons/mass_mailing/mail_mail_view.xml
deleted file mode 100644
index 11fa08776f7..00000000000
--- a/addons/mass_mailing/mail_mail_view.xml
+++ /dev/null
@@ -1,20 +0,0 @@
-<?xml version="1.0"?>
-<openerp>
- <data>
-
- <!-- FOLLOWERS !-->
- <record model="ir.ui.view" id="mail_mail_form_mass_mailing">
- <field name="name">mail.mail.form.mass_mailing</field>
- <field name="model">mail.mail</field>
- <field name="inherit_id" ref="mail.view_mail_form"/>
- <field name="arch" type="xml">
- <xpath expr="//field[@name='opened']" position="before">
- <field name="mass_mailing_campaign_id"/>
- <field name="mass_mailing_segment_id"/>
- <field name="template_id"/>
- </xpath>
- </field>
- </record>
-
- </data>
-</openerp>
diff --git a/addons/mass_mailing/mail_thread.py b/addons/mass_mailing/mail_thread.py
new file mode 100644
index 00000000000..292e13eab3a
--- /dev/null
+++ b/addons/mass_mailing/mail_thread.py
@@ -0,0 +1,82 @@
+# -*- coding: utf-8 -*-
+##############################################################################
+#
+# OpenERP, Open Source Management Solution
+# Copyright (C) 2013-Today OpenERP SA (<http://www.openerp.com>)
+#
+# 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/>
+#
+##############################################################################
+
+import logging
+
+from openerp import tools
+from openerp.addons.mail.mail_thread import decode_header
+from openerp.osv import osv
+
+_logger = logging.getLogger(__name__)
+
+
+class MailThread(osv.Model):
+ """ Update MailThread to add the feature of bounced emails and replied emails
+ in message_process. """
+ _name = 'mail.thread'
+ _inherit = ['mail.thread']
+
+ def message_route_check_bounce(self, cr, uid, message, context=None):
+ bounce_alias = self.pool['ir.config_parameter'].get_param(cr, uid, "mail.bounce.alias", context=context)
+ message_id = message.get('Message-Id')
+ email_from = decode_header(message, 'From')
+ email_to = decode_header(message, 'To')
+
+ # 0. Verify whether this is a bounced email (wrong destination,...) -> use it to collect data, such as dead leads
+ if bounce_alias in email_to:
+ bounce_match = tools.bounce_re.search(email_to)
+ if bounce_match:
+ bounced_mail_id = bounce_match.group(1)
+ self.pool['mail.mail'].set_bounced(cr, uid, [bounced_mail_id], context=context)
+ if self.pool['mail.mail'].exists(cr, uid, bounced_mail_id):
+ mail = self.pool['mail.mail'].browse(cr, uid, bounced_mail_id, context=context)
+ bounced_model = mail.model
+ bounced_thread_id = mail.res_id
+ else:
+ bounced_model = bounce_match.group(2)
+ bounced_thread_id = int(bounce_match.group(3)) if bounce_match.group(3) else 0
+ _logger.info('Routing mail from %s to %s with Message-Id %s: bounced mail from mail %s, model: %s, thread_id: %s',
+ email_from, email_to, message_id, bounced_mail_id, bounced_model, bounced_thread_id)
+ if bounced_model and bounced_model in self.pool and hasattr(self.pool[bounced_model], 'message_receive_bounce'):
+ self.pool[bounced_model].message_receive_bounce(cr, uid, [bounced_thread_id], mail_id=bounced_mail_id, context=context)
+ return False
+
+ return True
+
+ def message_route(self, cr, uid, message, message_dict, model=None, thread_id=None,
+ custom_values=None, context=None):
+ if not self.message_route_check_bounce(cr, uid, message, context=context):
+ return []
+ return super(MailThread, self).message_route(cr, uid, message, message_dict, model, thread_id, custom_values, context)
+
+ def message_receive_bounce(self, cr, uid, ids, mail_id=None, context=None):
+ """Called by ``message_process`` when a bounce email (such as Undelivered
+ Mail Returned to Sender) is received for an existing thread. The default
+ behavior is to check is an integer ``message_bounce`` column exists.
+ If it is the case, its content is incremented. """
+ if self._all_columns.get('message_bounce'):
+ for obj in self.browse(cr, uid, ids, context=context):
+ self.write(cr, uid, [obj.id], {'message_bounce': obj.message_bounce + 1}, context=context)
+
+ def message_route_process(self, cr, uid, msg, routes, context=None):
+ if msg.get('message_id'):
+ self.pool['mail.mail.statistics'].set_replied(cr, uid, mail_message_ids=[msg.get('message_id')], context=context)
+ return super(MailThread, self).message_route_process(cr, uid, msg, routes, context=context)
diff --git a/addons/mass_mailing/mass_mailing.py b/addons/mass_mailing/mass_mailing.py
index b255f3163c9..61fea847486 100644
--- a/addons/mass_mailing/mass_mailing.py
+++ b/addons/mass_mailing/mass_mailing.py
@@ -38,42 +38,46 @@ class MassMailingCampaign(osv.Model):
results = dict.fromkeys(ids, False)
for campaign in self.browse(cr, uid, ids, context=context):
results[campaign.id] = {
- 'sent': len(campaign.mail_ids),
- 'opened': len([mail for mail in campaign.mail_ids if mail.opened]),
- 'replied': len([mail for mail in campaign.mail_ids if mail.replied]),
- 'bounced': len([mail for mail in campaign.mail_ids if mail.bounced]),
+ 'sent': len(campaign.statistics_ids),
# delivered: shouldn't be: all mails - (failed + bounced) ?
- 'delivered': len([mail for mail in campaign.mail_ids if mail.state == 'sent' and not mail.bounced]),
+ 'delivered': len([stat for stat in campaign.statistics_ids if not stat.bounced]), # stat.state == 'sent' and
+ 'opened': len([stat for stat in campaign.statistics_ids if stat.opened]),
+ 'replied': len([stat for stat in campaign.statistics_ids if stat.replied]),
+ 'bounced': len([stat for stat in campaign.statistics_ids if stat.bounced]),
}
return results
- def _get_segment_kanban_ids(self, cr, uid, ids, name, arg, context=None):
+ def _get_mass_mailing_kanban_ids(self, cr, uid, ids, name, arg, context=None):
results = dict.fromkeys(ids, '')
for campaign in self.browse(cr, uid, ids, context=context):
- segment_results = []
- for segment in campaign.segment_ids:
- segment_object = {}
+ mass_mailing_results = []
+ for mass_mailing in campaign.mass_mailing_ids:
+ mass_mailing_object = {}
for attr in ['name', 'sent', 'delivered', 'opened', 'replied', 'bounced']:
- segment_object[attr] = getattr(segment, attr)
- segment_results.append(segment_object)
- results[campaign.id] = segment_results
+ mass_mailing_object[attr] = getattr(mass_mailing, attr)
+ mass_mailing_results.append(mass_mailing_object)
+ results[campaign.id] = mass_mailing_results
return results
_columns = {
'name': fields.char(
'Campaign Name', required=True,
),
- 'segment_ids': fields.one2many(
- 'mail.mass_mailing.segment', 'mass_mailing_campaign_id',
- 'Segments',
+ 'user_id': fields.many2one(
+ 'res.users', 'Responsible',
+ required=True,
),
- 'segment_kanban_ids': fields.function(
- _get_segment_kanban_ids,
- type='text', string='Segments (kanban data)',
- help='This field has for purpose to gather data about segment to display them in kanban view as nested kanban views is not possible currently',
+ 'mass_mailing_ids': fields.one2many(
+ 'mail.mass_mailing', 'mass_mailing_campaign_id',
+ 'Mass Mailings',
),
- 'mail_ids': fields.one2many(
- 'mail.mail', 'mass_mailing_campaign_id',
+ 'mass_mailing_kanban_ids': fields.function(
+ _get_mass_mailing_kanban_ids,
+ type='text', string='Mass Mailings (kanban data)',
+ help='This field has for purpose to gather data about mass mailings to display them in kanban view as nested kanban views is not possible currently',
+ ),
+ 'statistics_ids': fields.one2many(
+ 'mail.mail.statistics', 'mass_mailing_campaign_id',
'Sent Emails',
),
'color': fields.integer('Color Index'),
@@ -105,17 +109,21 @@ class MassMailingCampaign(osv.Model):
),
}
- def launch_segment_create_wizard(self, cr, uid, ids, context=None):
+ # _defaults = {
+ # 'user_id': lambda self, cr, uid, ctx=None: uid,
+ # },
+
+ def launch_mass_mailing_create_wizard(self, cr, uid, ids, context=None):
ctx = dict(context)
ctx.update({
'default_mass_mailing_campaign_id': ids[0],
})
return {
- 'name': _('Create a Segment for the Campaign'),
+ 'name': _('Create a Mass Mailing for the Campaign'),
'type': 'ir.actions.act_window',
'view_type': 'form',
'view_mode': 'form',
- 'res_model': 'mail.mass_mailing.segment.create',
+ 'res_model': 'mail.mass_mailing.create',
'views': [(False, 'form')],
'view_id': False,
'target': 'new',
@@ -123,12 +131,12 @@ class MassMailingCampaign(osv.Model):
}
-class MassMailingSegment(osv.Model):
- """ MassMailingSegment models a segment for a mass mailign campaign. A segment
- is an occurence of sending emails. """
+class MassMailing(osv.Model):
+ """ MassMailing models a wave of emails for a mass mailign campaign.
+ A mass mailing is an occurence of sending emails. """
- _name = 'mail.mass_mailing.segment'
- _description = 'Segment of a mass mailing campaign'
+ _name = 'mail.mass_mailing'
+ _description = 'Wave of sending emails'
# number of periods for tracking mail_mail statistics
_period_number = 6
@@ -162,7 +170,7 @@ class MassMailingSegment(osv.Model):
def _get_monthly_statistics(self, cr, uid, ids, field_name, arg, context=None):
""" TODO
"""
- obj = self.pool['mail.mail']
+ obj = self.pool['mail.mail.statistics']
res = {}
context['datetime_format'] = {
'opened': {
@@ -179,22 +187,22 @@ class MassMailingSegment(osv.Model):
for id in ids:
res[id] = {}
date_begin = self.browse(cr, uid, id, context=context).date
- domain = [('mass_mailing_segment_id', '=', id), ('opened', '>=', date_begin)]
+ domain = [('mass_mailing_id', '=', id), ('opened', '>=', date_begin)]
res[id]['opened_monthly'] = self.__get_bar_values(cr, uid, id, obj, domain, ['opened'], 'opened_count', 'opened', context=context)
- domain = [('mass_mailing_segment_id', '=', id), ('replied', '>=', date_begin)]
+ domain = [('mass_mailing_id', '=', id), ('replied', '>=', date_begin)]
res[id]['replied_monthly'] = self.__get_bar_values(cr, uid, id, obj, domain, ['replied'], 'replied_count', 'replied', context=context)
return res
def _get_statistics(self, cr, uid, ids, name, arg, context=None):
""" Compute statistics of the mass mailing campaign """
results = dict.fromkeys(ids, False)
- for segment in self.browse(cr, uid, ids, context=context):
- results[segment.id] = {
- 'sent': len(segment.mail_ids),
- 'delivered': len([mail for mail in segment.mail_ids if mail.state == 'sent' and not mail.bounced]),
- 'opened': len([mail for mail in segment.mail_ids if mail.opened]),
- 'replied': len([mail for mail in segment.mail_ids if mail.replied]),
- 'bounced': len([mail for mail in segment.mail_ids if mail.bounced]),
+ for mass_mailing in self.browse(cr, uid, ids, context=context):
+ results[mass_mailing.id] = {
+ 'sent': len(mass_mailing.statistics_ids),
+ 'delivered': len([stat for stat in mass_mailing.statistics_ids if not stat.bounced]), # mail.state == 'sent' and
+ 'opened': len([stat for stat in mass_mailing.statistics_ids if stat.opened]),
+ 'replied': len([stat for stat in mass_mailing.statistics_ids if stat.replied]),
+ 'bounced': len([stat for stat in mass_mailing.statistics_ids if stat.bounced]),
}
return results
@@ -214,9 +222,9 @@ class MassMailingSegment(osv.Model):
'mass_mailing_campaign_id', 'color',
type='integer', string='Color Index',
),
- # mail_mail data
- 'mail_ids': fields.one2many(
- 'mail.mail', 'mass_mailing_segment_id',
+ # statistics data
+ 'statistics_ids': fields.one2many(
+ 'mail.mail.statistics', 'mass_mailing_id',
'Send Emails',
),
'sent': fields.function(
@@ -260,3 +268,95 @@ class MassMailingSegment(osv.Model):
_defaults = {
'date': fields.datetime.now(),
}
+
+
+class MailMailStats(osv.Model):
+ """ MailMailStats models the statistics collected about emails. Those statistics
+ are stored in a separated model and table to avoid bloating the mail_mail table
+ with statistics values. This also allows to delete emails send with mass mailing
+ without loosing the statistics about them. """
+
+ _name = 'mail.mail.statistics'
+ _description = 'Email Statistics'
+ _rec_name = 'message_id'
+ _order = 'message_id'
+
+ _columns = {
+ 'mail_mail_id': fields.integer(
+ 'Mail ID',
+ help='ID of the related mail_mail. This field is an integer field because'
+ 'the related mail_mail can be deleted separately from its statistics.'
+ ),
+ 'message_id': fields.char(
+ 'Message-ID', required=True,
+ ),
+ # campaign / wave data
+ 'mass_mailing_id': fields.many2one(
+ 'mail.mass_mailing', 'Mass Mailing',
+ ondelete='set null',
+ ),
+ 'mass_mailing_campaign_id': fields.related(
+ 'mass_mailing_id', 'mass_mailing_campaign_id',
+ type='many2one', ondelete='set null',
+ relation='mail.mass_mailing.campaign',
+ string='Mass Mailing Campaign',
+ store=True, readonly=True,
+ ),
+ 'template_id': fields.related(
+ 'mass_mailing_id', 'template_id',
+ type='many2one', ondelete='set null',
+ relation='email.template',
+ string='Email Template',
+ store=True, readonly=True,
+ ),
+ # Bounce and tracking
+ 'opened': fields.datetime(
+ 'Opened',
+ help='Date when this email has been opened for the first time.'),
+ 'replied': fields.datetime(
+ 'Replied',
+ help='Date when this email has been replied for the first time.'),
+ 'bounced': fields.datetime(
+ 'Bounced',
+ help='Date when this email has bounced.'
+ ),
+ }
+
+ def set_opened(self, cr, uid, ids=None, mail_mail_ids=None, mail_message_ids=None, context=None):
+ """ Set as opened """
+ if not ids and mail_mail_ids:
+ ids = self.search(cr, uid, [('mail_mail_id', 'in', mail_mail_ids)], context=context)
+ elif not ids and mail_message_ids:
+ ids = self.search(cr, uid, [('message_id', 'in', mail_message_ids)], context=context)
+ else:
+ ids = []
+ for stat in self.browse(cr, uid, ids, context=context):
+ if not stat.opened:
+ self.write(cr, uid, [stat.id], {'opened': fields.datetime.now()}, context=context)
+ return True
+
+ def set_replied(self, cr, uid, ids=None, mail_mail_ids=None, mail_message_ids=None, context=None):
+ """ Set as replied """
+ if not ids and mail_mail_ids:
+ ids = self.search(cr, uid, [('mail_mail_id', 'in', mail_mail_ids)], context=context)
+ elif not ids and mail_message_ids:
+ ids = self.search(cr, uid, [('message_id', 'in', mail_message_ids)], context=context)
+ else:
+ ids = []
+ for stat in self.browse(cr, uid, ids, context=context):
+ if not stat.replied:
+ self.write(cr, uid, [stat.id], {'replied': fields.datetime.now()}, context=context)
+ return True
+
+ def set_bounced(self, cr, uid, ids=None, mail_mail_ids=None, mail_message_ids=None, context=None):
+ """ Set as bounced """
+ if not ids and mail_mail_ids:
+ ids = self.search(cr, uid, [('mail_mail_id', 'in', mail_mail_ids)], context=context)
+ elif not ids and mail_message_ids:
+ ids = self.search(cr, uid, [('message_id', 'in', mail_message_ids)], context=context)
+ else:
+ ids = []
+ for stat in self.browse(cr, uid, ids, context=context):
+ if not stat.bounced:
+ self.write(cr, uid, [stat.id], {'bounced': fields.datetime.now()}, context=context)
+ return True
diff --git a/addons/mass_mailing/mass_mailing_demo.xml b/addons/mass_mailing/mass_mailing_demo.xml
index b2278064d36..d75351e1166 100644
--- a/addons/mass_mailing/mass_mailing_demo.xml
+++ b/addons/mass_mailing/mass_mailing_demo.xml
@@ -20,60 +20,69 @@
<record id="mass_mail_campaign_1" model="mail.mass_mailing.campaign">
<field name="name">Partners Newsletter</field>
+ <field name="user_id" eval="ref('base.user_root')"/>
</record>
- <record id="mass_mail_segment_1" model="mail.mass_mailing.segment">
+ <record id="mass_mail_1" model="mail.mass_mailing">
<field name="name">First Newsletter</field>
<field name="template_id" eval="ref('mass_mail_template_1')"/>
<field name="date" eval="(DateTime.today() - relativedelta(days=3)).strftime('%Y-%m-%d %H:%M:%S')"/>
<field name="mass_mailing_campaign_id" eval="ref('mass_mail_campaign_1')"/>
</record>
- <record id="mass_mail_segment_2" model="mail.mass_mailing.segment">
+ <record id="mass_mail_2" model="mail.mass_mailing">
<field name="name">Second Newsletter</field>
<field name="template_id" eval="ref('mass_mail_template_2')"/>
<field name="date" eval="(DateTime.today() - relativedelta(days=3)).strftime('%Y-%m-%d %H:%M:%S')"/>
<field name="mass_mailing_campaign_id" eval="ref('mass_mail_campaign_1')"/>
</record>
- <record id="mass_mail_email_1" model="mail.mail">
- <field name="mass_mailing_segment_id" eval="ref('mass_mail_segment_1')"/>
+ <record id="mass_mail_email_1" model="mail.mail.statistics">
+ <field name="mass_mailing_id" eval="ref('mass_mail_1')"/>
+ <field name="message_id">1111000@OpenERP.com</field>
<field name="opened" eval="(DateTime.today() - relativedelta(days=2)).strftime('%Y-%m-%d %H:%M:%S')"/>
<field name="replied" eval="(DateTime.today() - relativedelta(days=1)).strftime('%Y-%m-%d %H:%M:%S')"/>
<field name="state">sent</field>
</record>
- <record id="mass_mail_email_2" model="mail.mail">
- <field name="mass_mailing_segment_id" eval="ref('mass_mail_segment_1')"/>
+ <record id="mass_mail_email_2" model="mail.mail.statistics">
+ <field name="mass_mailing_id" eval="ref('mass_mail_1')"/>
+ <field name="message_id">1111001@OpenERP.com</field>
<field name="opened" eval="(DateTime.today() - relativedelta(days=2)).strftime('%Y-%m-%d %H:%M:%S')"/>
<field name="replied" eval="(DateTime.today() - relativedelta(days=0)).strftime('%Y-%m-%d %H:%M:%S')"/>
<field name="state">sent</field>
</record>
- <record id="mass_mail_email_3" model="mail.mail">
- <field name="mass_mailing_segment_id" eval="ref('mass_mail_segment_1')"/>
+ <record id="mass_mail_email_3" model="mail.mail.statistics">
+ <field name="mass_mailing_id" eval="ref('mass_mail_1')"/>
+ <field name="message_id">1111002@OpenERP.com</field>
<field name="opened" eval="(DateTime.today() - relativedelta(days=1)).strftime('%Y-%m-%d %H:%M:%S')"/>
<field name="state">sent</field>
</record>
- <record id="mass_mail_email_4" model="mail.mail">
- <field name="mass_mailing_segment_id" eval="ref('mass_mail_segment_1')"/>
+ <record id="mass_mail_email_4" model="mail.mail.statistics">
+ <field name="mass_mailing_id" eval="ref('mass_mail_1')"/>
+ <field name="message_id">1111003@OpenERP.com</field>
<field name="state">sent</field>
</record>
- <record id="mass_mail_email_5" model="mail.mail">
- <field name="mass_mailing_segment_id" eval="ref('mass_mail_segment_1')"/>
+ <record id="mass_mail_email_5" model="mail.mail.statistics">
+ <field name="mass_mailing_id" eval="ref('mass_mail_1')"/>
+ <field name="message_id">1111004@OpenERP.com</field>
<field name="bounced" eval="(DateTime.today() - relativedelta(days=3)).strftime('%Y-%m-%d %H:%M:%S')"/>
<field name="state">sent</field>
</record>
- <record id="mass_mail_email_2_1" model="mail.mail">
- <field name="mass_mailing_segment_id" eval="ref('mass_mail_segment_2')"/>
+ <record id="mass_mail_email_2_1" model="mail.mail.statistics">
+ <field name="mass_mailing_id" eval="ref('mass_mail_2')"/>
+ <field name="message_id">1111005@OpenERP.com</field>
<field name="opened" eval="(DateTime.today() - relativedelta(days=3)).strftime('%Y-%m-%d %H:%M:%S')"/>
<field name="state">sent</field>
</record>
- <record id="mass_mail_email_2_2" model="mail.mail">
- <field name="mass_mailing_segment_id" eval="ref('mass_mail_segment_2')"/>
+ <record id="mass_mail_email_2_2" model="mail.mail.statistics">
+ <field name="mass_mailing_id" eval="ref('mass_mail_2')"/>
+ <field name="message_id">1111006@OpenERP.com</field>
<field name="opened" eval="(DateTime.today() - relativedelta(days=2)).strftime('%Y-%m-%d %H:%M:%S')"/>
<field name="state">sent</field>
</record>
- <record id="mass_mail_email_2_3" model="mail.mail">
- <field name="mass_mailing_segment_id" eval="ref('mass_mail_segment_2')"/>
+ <record id="mass_mail_email_2_3" model="mail.mail.statistics">
+ <field name="mass_mailing_id" eval="ref('mass_mail_2')"/>
+ <field name="message_id">1111007@OpenERP.com</field>
<field name="state">sent</field>
</record>
diff --git a/addons/mass_mailing/mass_mailing_view.xml b/addons/mass_mailing/mass_mailing_view.xml
index cc2ffc9cb25..2dc95220f6f 100644
--- a/addons/mass_mailing/mass_mailing_view.xml
+++ b/addons/mass_mailing/mass_mailing_view.xml
@@ -20,8 +20,8 @@
<field name="arch" type="xml">
<form string="Mass Mailing Campaign" version="7.0">
<header>
- <button name="launch_segment_create_wizard" type="object"
- class="oe_highlight" string="Create a New Segment"/>
+ <button name="launch_mass_mailing_create_wizard" type="object"
+ class="oe_highlight" string="Create a New Mailing"/>
</header>
<sheet>
<group>
@@ -32,8 +32,8 @@
<field name="replied"/>
<field name="bounced"/>
</group>
- <label for="segment_ids"/>
- <field name="segment_ids"/>
+ <label for="mass_mailing_ids"/>
+ <field name="mass_mailing_ids"/>
</sheet>
</form>
</field>
@@ -46,29 +46,29 @@
<field name="model">mail.mass_mailing.campaign</field>
<field name="arch" type="xml">
<kanban>
- <field name="segment_kanban_ids"/>
+ <field name="mass_mailing_kanban_ids"/>
<field name='sent'/>
<field name='color'/>
<templates>
- <t t-name="mass_mailing.segment">
+ <t t-name="mass_mailing.mass_mailing">
<div>
- <h4><t t-raw="segment.name"/></h4>
+ <h4><t t-raw="mass_mailing.name"/></h4>
</div>
<div>
<p class="oe_mail_stats">
- <span class="oe_mail_result"><t t-raw="segment.sent"/></span><br />
+ <span class="oe_mail_result"><t t-raw="mass_mailing.sent"/></span><br />
Sent
</p>
<p class="oe_mail_stats">
- <span class="oe_mail_result"><t t-raw="segment.delivered"/></span><br />
+ <span class="oe_mail_result"><t t-raw="mass_mailing.delivered"/></span><br />
Delivered
</p>
<p class="oe_mail_stats">
- <span class="oe_mail_result"><t t-raw="segment.opened"/></span><br />
+ <span class="oe_mail_result"><t t-raw="mass_mailing.opened"/></span><br />
Opened
</p>
<p class="oe_mail_stats">
- <span class="oe_mail_result"><t t-raw="segment.replied"/></span><br />
+ <span class="oe_mail_result"><t t-raw="mass_mailing.replied"/></span><br />
Replied
</p>
</div>
@@ -93,8 +93,8 @@
<field name="replied" widget="gauge" style="width:160px; height: 120px;"
options="{'max_field': 'sent'}"/>
</div>
- <t t-foreach='record.segment_kanban_ids.value' t-as='segment'>
- <t t-call="mass_mailing.segment"/>
+ <t t-foreach='record.mass_mailing_kanban_ids.value' t-as='mass_mailing'>
+ <t t-call="mass_mailing.mass_mailing"/>
</t>
</div>
<div class="oe_clear"></div>
@@ -112,13 +112,13 @@
<field name="view_mode">kanban,tree,form</field>
</record>
- <!-- MASS MAILING SEGMENTS !-->
- <record model="ir.ui.view" id="view_mail_mass_mailing_segment_tree">
- <field name="name">mail.mass_mailing.segment.tree</field>
- <field name="model">mail.mass_mailing.segment</field>
+ <!-- MASS MAILING !-->
+ <record model="ir.ui.view" id="view_mail_mass_mailing_tree">
+ <field name="name">mail.mass_mailing.tree</field>
+ <field name="model">mail.mass_mailing</field>
<field name="priority">10</field>
<field name="arch" type="xml">
- <tree string="Mass Mailing Segments">
+ <tree string="Mass Mailings">
<field name="name"/>
<field name="sent"/>
<field name="delivered"/>
@@ -128,11 +128,11 @@
</field>
</record>
- <record model="ir.ui.view" id="view_mail_mass_mailing_segment_form">
- <field name="name">mail.mass_mailing.segment.form</field>
- <field name="model">mail.mass_mailing.segment</field>
+ <record model="ir.ui.view" id="view_mail_mass_mailing_form">
+ <field name="name">mail.mass_mailing.form</field>
+ <field name="model">mail.mass_mailing</field>
<field name="arch" type="xml">
- <form string="Mass Mailing Segment" version="7.0">
+ <form string="Mass Mailing" version="7.0">
<sheet>
<group>
<group>
@@ -150,12 +150,12 @@
</field>
</record>
- <record model="ir.ui.view" id="view_mail_mass_mailing_segment_form_readonly">
- <field name="name">mail.mass_mailing.segment.form</field>
- <field name="model">mail.mass_mailing.segment</field>
+ <record model="ir.ui.view" id="view_mail_mass_mailing_form_readonly">
+ <field name="name">mail.mass_mailing.form</field>
+ <field name="model">mail.mass_mailing</field>
<field name="priority">18</field>
<field name="arch" type="xml">
- <form string="Mass Mailing Segment" version="7.0">
+ <form string="Mass Mailing" version="7.0">
<sheet>
<group>
<group>
@@ -173,9 +173,9 @@
</field>
</record>
- <record model="ir.ui.view" id="view_mail_mass_mailing_segment_kanban">
- <field name="name">mail.mass_mailing.segment.kanban</field>
- <field name="model">mail.mass_mailing.segment</field>
+ <record model="ir.ui.view" id="view_mail_mass_mailing_kanban">
+ <field name="name">mail.mass_mailing.kanban</field>
+ <field name="model">mail.mass_mailing</field>
<field name="arch" type="xml">
<kanban>
<field name='color'/>
@@ -229,9 +229,9 @@
</field>
</record>
- <record id="action_view_mass_mailing_segments" model="ir.actions.act_window">
- <field name="name">Mass Mailing Segments</field>
- <field name="res_model">mail.mass_mailing.segment</field>
+ <record id="action_view_mass_mailings" model="ir.actions.act_window">
+ <field name="name">Mass Mailings</field>
+ <field name="res_model">mail.mass_mailing</field>
<field name="view_type">form</field>
<field name="view_mode">kanban,tree,form</field>
</record>
@@ -245,9 +245,9 @@
<menuitem name="Campaigns" id="menu_email_campaigns"
parent="mass_mailing_campaign" sequence="1"
action="action_view_mass_mailing_campaigns"/>
- <menuitem name="Segments" id="menu_email_segments"
+ <menuitem name="Mass Mailings" id="menu_email_mass_mailings"
parent="mass_mailing_campaign" sequence="2"
- action="action_view_mass_mailing_segments"/>
+ action="action_view_mass_mailings"/>
</data>
</openerp>
diff --git a/addons/mass_mailing/security/ir.model.access.csv b/addons/mass_mailing/security/ir.model.access.csv
index 54f3413e9f1..6931ab0265b 100644
--- a/addons/mass_mailing/security/ir.model.access.csv
+++ b/addons/mass_mailing/security/ir.model.access.csv
@@ -1,5 +1,6 @@
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
access_mass_mailing_campaign,mail.mass_mailing.campaign,model_mail_mass_mailing_campaign,,1,1,1,0
access_mass_mailing_campaign_system,mail.mass_mailing.campaign.system,model_mail_mass_mailing_campaign,base.group_system,1,1,1,1
-access_mass_mailing_segment,mail.mass_mailing.segment,model_mail_mass_mailing_segment,,1,1,1,0
-access_mass_mailing_segment_system,mail.mass_mailing.segment.system,model_mail_mass_mailing_segment,base.group_system,1,1,1,1 \ No newline at end of file
+access_mass_mailing,mail.mass_mailing,model_mail_mass_mailing,,1,1,1,0
+access_mass_mailing_system,mail.mass_mailing.system,model_mail_mass_mailing,base.group_system,1,1,1,1
+access_mail_mail_statistics,mail.mail.statistics,model_mail_mail_statistics,,1,1,1,1 \ No newline at end of file
diff --git a/addons/mass_mailing/wizard/mail_compose_message.py b/addons/mass_mailing/wizard/mail_compose_message.py
index 4fe858734fe..7b22bc8f4aa 100644
--- a/addons/mass_mailing/wizard/mail_compose_message.py
+++ b/addons/mass_mailing/wizard/mail_compose_message.py
@@ -34,8 +34,8 @@ class MailComposeMessage(osv.TransientModel):
'mass_mailing_campaign_id': fields.many2one(
'mail.mass_mailing.campaign', 'Mass mailing campaign',
),
- 'mass_mailing_segment_id': fields.many2one(
- 'mail.mass_mailing.segment', 'Mass mailing segment',
+ 'mass_mailing_id': fields.many2one(
+ 'mail.mass_mailing', 'Mass mailing',
domain="[('mass_mailing_campaign_id', '=', mass_mailing_campaign_id)]",
),
}
@@ -44,18 +44,18 @@ class MailComposeMessage(osv.TransientModel):
'use_mass_mailing_campaign': False,
}
- def onchange_mass_mail_campaign_id(self, cr, uid, ids, mass_mailing_campaign_id, mass_mail_segment_id, context=None):
- if mass_mail_segment_id:
- segment = self.pool['mail.mass_mailing.segment'].browse(cr, uid, mass_mail_segment_id, context=context)
- if segment.mass_mailing_campaign_id.id == mass_mailing_campaign_id:
+ def onchange_mass_mail_campaign_id(self, cr, uid, ids, mass_mailing_campaign_id, mass_mailing_id, context=None):
+ if mass_mailing_id:
+ mass_mailing = self.pool['mail.mass_mailing'].browse(cr, uid, mass_mailing_id, context=context)
+ if mass_mailing.mass_mailing_campaign_id.id == mass_mailing_campaign_id:
return {}
- return {'value': {'mass_mailing_segment_id': False}}
+ return {'value': {'mass_mailing_id': False}}
def render_message_batch(self, cr, uid, wizard, res_ids, context=None):
""" Override method that generated the mail content by adding the mass
mailing campaign, when doing pure email mass mailing. """
res = super(MailComposeMessage, self).render_message_batch(cr, uid, wizard, res_ids, context=context)
- if wizard.composition_mode == 'mass_mail' and wizard.use_mass_mailing_campaign and wizard.mass_mailing_segment_id: # TODO: which kind of mass mailing ?
+ if wizard.composition_mode == 'mass_mail' and wizard.use_mass_mailing_campaign and wizard.mass_mailing_id: # TODO: which kind of mass mailing ?
for res_id in res_ids:
- res[res_id]['mass_mailing_segment_id'] = wizard.mass_mailing_segment_id.id
+ res[res_id]['mass_mailing_id'] = wizard.mass_mailing_id.id
return res
diff --git a/addons/mass_mailing/wizard/mail_compose_message_view.xml b/addons/mass_mailing/wizard/mail_compose_message_view.xml
index 9dcf57682f6..bdd4431a71d 100644
--- a/addons/mass_mailing/wizard/mail_compose_message_view.xml
+++ b/addons/mass_mailing/wizard/mail_compose_message_view.xml
@@ -15,16 +15,16 @@
<div>
<group>
<field name="mass_mailing_campaign_id"
- on_change="onchange_mass_mail_campaign_id(mass_mailing_campaign_id, mass_mailing_segment_id, context)"
+ on_change="onchange_mass_mail_campaign_id(mass_mailing_campaign_id, mass_mailing_id, context)"
attrs="{'invisible': ['|', ('composition_mode', '!=', 'mass_mail'), ('use_mass_mailing_campaign', '=', False)],
'required': [('composition_mode', '=', 'mass_mail'), ('use_mass_mailing_campaign', '=', True)]}"/>
- <field name="mass_mailing_segment_id"
+ <field name="mass_mailing_id"
attrs="{'invisible': ['|', ('composition_mode', '!=', 'mass_mail'), ('use_mass_mailing_campaign', '=', False)],
'required': [('composition_mode', '=', 'mass_mail'), ('use_mass_mailing_campaign', '=', True)]}"
context="{'default_mass_mailing_campaign_id': mass_mailing_campaign_id,
'default_template_id': template_id,
'default_domain': active_domain,
- 'form_view_ref': 'mass_mailing.view_mail_mass_mailing_segment_form_readonly'}"/>
+ 'form_view_ref': 'mass_mailing.view_mail_mass_mailing_form_readonly'}"/>
</group>
</div>
</xpath>
diff --git a/addons/mass_mailing/wizard/mail_mass_mailing_create_segment.py b/addons/mass_mailing/wizard/mail_mass_mailing_create_segment.py
index ffb7c004415..927c34ab5fd 100644
--- a/addons/mass_mailing/wizard/mail_mass_mailing_create_segment.py
+++ b/addons/mass_mailing/wizard/mail_mass_mailing_create_segment.py
@@ -24,11 +24,11 @@ from openerp.osv import osv, fields
from openerp.tools.translate import _
-class MailMassMailingSegmentCreate(osv.TransientModel):
- """Wizard to help creating mass mailing segments for a campaign. """
+class MailMassMailingCreate(osv.TransientModel):
+ """Wizard to help creating mass mailing waves for a campaign. """
- _name = 'mail.mass_mailing.segment.create'
- _description = 'Mass mailing segment creation'
+ _name = 'mail.mass_mailing.create'
+ _description = 'Mass mailing creation'
_columns = {
'mass_mailing_campaign_id': fields.many2one(
@@ -55,11 +55,11 @@ class MailMassMailingSegmentCreate(osv.TransientModel):
'email.template', 'Template', required=True,
domain="[('model_id', '=', model_id)]",
),
- 'segment_name': fields.char(
- 'Segment name', required=True,
+ 'name': fields.char(
+ 'Name', required=True,
),
- 'mass_mailing_segment_id': fields.many2one(
- 'mail.mass_mailing.segment', 'Mass Mailing Segment',
+ 'mass_mailing_id': fields.many2one(
+ 'mail.mass_mailing', 'Mass Mailing',
),
}
@@ -80,23 +80,23 @@ class MailMassMailingSegmentCreate(osv.TransientModel):
domain = False
return {'value': {'domain': domain}}
- def create_segment(self, cr, uid, ids, context=None):
- """ Create a segment based on wizard data, and update the wizard """
+ def create_mass_mailing(self, cr, uid, ids, context=None):
+ """ Create a mass mailing based on wizard data, and update the wizard """
for wizard in self.browse(cr, uid, ids, context=context):
- segment_values = {
- 'name': wizard.segment_name,
+ mass_mailing_values = {
+ 'name': wizard.name,
'mass_mailing_campaign_id': wizard.mass_mailing_campaign_id.id,
'domain': wizard.domain,
'template_id': wizard.template_id.id,
}
- segment_id = self.pool['mail.mass_mailing.segment'].create(cr, uid, segment_values, context=context)
- self.write(cr, uid, [wizard.id], {'mass_mailing_segment_id': segment_id}, context=context)
+ mass_mailing_id = self.pool['mail.mass_mailing'].create(cr, uid, mass_mailing_values, context=context)
+ self.write(cr, uid, [wizard.id], {'mass_mailing_id': mass_mailing_id}, context=context)
return True
def launch_composer(self, cr, uid, ids, context=None):
- """ Main wizard action: create a new segment and launch the mail.compose.message
+ """ Main wizard action: create a new mailing and launch the mail.compose.message
email composer with wizard data. """
- self.create_segment(cr, uid, ids, context=context)
+ self.create_mass_mailing(cr, uid, ids, context=context)
wizard = self.browse(cr, uid, ids[0], context=context)
ctx = dict(context)
@@ -107,7 +107,7 @@ class MailMassMailingSegmentCreate(osv.TransientModel):
'default_use_active_domain': True,
'default_active_domain': wizard.domain,
'default_mass_mailing_campaign_id': wizard.mass_mailing_campaign_id.id,
- 'default_mass_mailing_segment_id': wizard.mass_mailing_segment_id.id,
+ 'default_mass_mailing_id': wizard.mass_mailing_id.id,
})
return {
'name': _('Compose Email for Mass Mailing'),
diff --git a/addons/mass_mailing/wizard/mail_mass_mailing_create_segment.xml b/addons/mass_mailing/wizard/mail_mass_mailing_create_segment.xml
index 5d211bb9eb4..a10101e9446 100644
--- a/addons/mass_mailing/wizard/mail_mass_mailing_create_segment.xml
+++ b/addons/mass_mailing/wizard/mail_mass_mailing_create_segment.xml
@@ -3,17 +3,17 @@
<data>
<!-- Wizard form view -->
- <record model="ir.ui.view" id="view_mail_mass_mailing_segment_create_form">
- <field name="name">mail.mass_mailing.segment.create.form</field>
- <field name="model">mail.mass_mailing.segment.create</field>
+ <record model="ir.ui.view" id="view_mail_mass_mailing_create_form">
+ <field name="name">mail.mass_mailing.create.form</field>
+ <field name="model">mail.mass_mailing.create</field>
<field name="arch" type="xml">
- <form string="Create a Mass Mailing Segment" version="7.0">
+ <form string="Create a Mass Mailing" version="7.0">
<group>
<field name="model_model" invisible="1"/>
<field name="domain" invisible="1"/>
<p class="oe_grey" colspan="2"
attrs="{'invisible': [('mass_mailing_campaign_id', '!=', False)]}">
- Please choose a mass mailing campaign that will hold the new segment.
+ Please choose a mass mailing campaign that will hold the new mailing.
</p>
<field name="mass_mailing_campaign_id"/>
@@ -43,23 +43,23 @@
attrs="{'invisible': [('filter_id', '=', False)]}"/>
<p class="oe_grey" colspan="2"
- attrs="{'invisible': ['|', ('segment_name', '!=', False), ('template_id', '=', False)]}">
- Please choose the name of the campaign segment.
+ attrs="{'invisible': ['|', ('name', '!=', False), ('template_id', '=', False)]}">
+ Please choose the name of the mailing.
</p>
- <field name="segment_name"
+ <field name="name"
attrs="{'invisible': [('template_id', '=', False)]}"/>
<button name="launch_composer" type="object"
- string="Create segment and launch email composer"
- attrs="{'invisible': [('segment_name', '=', False)]}"/>
+ string="Create mailing and launch email composer"
+ attrs="{'invisible': [('name', '=', False)]}"/>
</group>
</form>
</field>
</record>
- <record model="ir.actions.act_window" id="action_mail_mass_mailing_segment_create">
- <field name="name">Create Mass Mailing Segment</field>
- <field name="res_model">mail.mass_mailing.segment.create</field>
+ <record model="ir.actions.act_window" id="action_mail_mass_mailing_create">
+ <field name="name">Create Mass Mailing</field>
+ <field name="res_model">mail.mass_mailing.create</field>
<field name="src_model">mail.mass_mailing.campaign</field>
<field name="type">ir.actions.act_window</field>
<field name="view_type">form</field>