summaryrefslogtreecommitdiff
path: root/addons/mass_mailing
diff options
context:
space:
mode:
authorchm@openerp.com <>2014-04-23 17:48:52 +0200
committerchm@openerp.com <>2014-04-23 17:48:52 +0200
commitee7ff4bd07d8d84ed2ebb0e2e3f5eb437f158e5b (patch)
tree25577845139d390f9bf27e0237d18842aeace02b /addons/mass_mailing
parentb1bc68d8eb374f30173e03e8048d1c3588a00d4b (diff)
parent1657ffb8e7d95ef5f94e59121737abb068371b66 (diff)
[MERGE] from trunk
bzr revid: chm@openerp.com-20140423154852-gzegxqrzb2sj01zl
Diffstat (limited to 'addons/mass_mailing')
-rw-r--r--addons/mass_mailing/__init__.py23
-rw-r--r--addons/mass_mailing/__openerp__.py20
-rw-r--r--addons/mass_mailing/controllers/main.py35
-rw-r--r--addons/mass_mailing/data/mail_data.xml (renamed from addons/mass_mailing/mail_data.xml)0
-rw-r--r--addons/mass_mailing/data/mass_mailing_data.xml37
-rw-r--r--addons/mass_mailing/data/mass_mailing_demo.xml146
-rw-r--r--addons/mass_mailing/mass_mailing.py369
-rw-r--r--addons/mass_mailing/mass_mailing_demo.xml82
-rw-r--r--addons/mass_mailing/mass_mailing_view.xml379
-rw-r--r--addons/mass_mailing/models/__init__.py7
-rw-r--r--addons/mass_mailing/models/mail_mail.py (renamed from addons/mass_mailing/mail_mail.py)37
-rw-r--r--addons/mass_mailing/models/mail_thread.py (renamed from addons/mass_mailing/mail_thread.py)0
-rw-r--r--addons/mass_mailing/models/mass_mailing.py571
-rw-r--r--addons/mass_mailing/models/mass_mailing_stats.py109
-rw-r--r--addons/mass_mailing/models/res_config.py15
-rw-r--r--addons/mass_mailing/security/ir.model.access.csv4
-rw-r--r--addons/mass_mailing/static/src/css/email_template.css17
-rw-r--r--addons/mass_mailing/static/src/css/mass_mailing.css64
-rw-r--r--addons/mass_mailing/static/src/js/mass_mailing.js6
-rw-r--r--addons/mass_mailing/views/email_template.xml97
-rw-r--r--addons/mass_mailing/views/mass_mailing.xml626
-rw-r--r--addons/mass_mailing/views/res_config.xml20
-rw-r--r--addons/mass_mailing/views/res_partner.xml6
-rw-r--r--addons/mass_mailing/wizard/__init__.py21
-rw-r--r--addons/mass_mailing/wizard/mail_compose_message.py52
-rw-r--r--addons/mass_mailing/wizard/mail_compose_message_view.xml4
-rw-r--r--addons/mass_mailing/wizard/mail_mass_mailing_create_segment.py135
-rw-r--r--addons/mass_mailing/wizard/mail_mass_mailing_create_segment.xml82
-rw-r--r--addons/mass_mailing/wizard/test_mailing.py40
-rw-r--r--addons/mass_mailing/wizard/test_mailing.xml34
30 files changed, 1847 insertions, 1191 deletions
diff --git a/addons/mass_mailing/__init__.py b/addons/mass_mailing/__init__.py
index 9dadc456842..175383c6644 100644
--- a/addons/mass_mailing/__init__.py
+++ b/addons/mass_mailing/__init__.py
@@ -1,26 +1,5 @@
# -*- 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 mass_mailing
-import mail_mail
-import mail_thread
+import models
import wizard
import controllers
diff --git a/addons/mass_mailing/__openerp__.py b/addons/mass_mailing/__openerp__.py
index 92f8b1fc3e8..91b57ac5b5a 100644
--- a/addons/mass_mailing/__openerp__.py
+++ b/addons/mass_mailing/__openerp__.py
@@ -21,26 +21,33 @@
{
'name': 'Mass Mailing Campaigns',
+ 'summary': 'Design, send and track emails',
'description': """
Easily send mass mailing to your leads, opportunities or customers. Track
marketing campaigns performance to improve conversion rates. Design
professional emails and reuse templates in a few clicks.
""",
- 'version': '1.0',
+ 'version': '2.0',
'author': 'OpenERP',
'website': 'http://www.openerp.com',
'category': 'Marketing',
'depends': [
'mail',
'email_template',
+ 'marketing',
'web_kanban_gauge',
'web_kanban_sparkline',
+ 'website_mail',
],
'data': [
- 'mail_data.xml',
+ 'data/mail_data.xml',
+ 'data/mass_mailing_data.xml',
'wizard/mail_compose_message_view.xml',
- 'wizard/mail_mass_mailing_create_segment.xml',
- 'mass_mailing_view.xml',
+ 'wizard/test_mailing.xml',
+ 'views/mass_mailing.xml',
+ 'views/res_config.xml',
+ 'views/res_partner.xml',
+ 'views/email_template.xml',
'security/ir.model.access.csv',
],
'js': [
@@ -48,10 +55,11 @@ professional emails and reuse templates in a few clicks.
],
'qweb': [],
'css': [
- 'static/src/css/mass_mailing.css'
+ 'static/src/css/mass_mailing.css',
+ 'static/src/css/email_template.css'
],
'demo': [
- 'mass_mailing_demo.xml',
+ 'data/mass_mailing_demo.xml',
],
'installable': True,
'auto_install': False,
diff --git a/addons/mass_mailing/controllers/main.py b/addons/mass_mailing/controllers/main.py
index 046084621c7..7396c02d453 100644
--- a/addons/mass_mailing/controllers/main.py
+++ b/addons/mass_mailing/controllers/main.py
@@ -1,11 +1,42 @@
+import werkzeug
+
from openerp import http, SUPERUSER_ID
from openerp.http import request
+
class MassMailController(http.Controller):
+
@http.route('/mail/track/<int:mail_id>/blank.gif', type='http', auth='none')
- def track_mail_open(self, mail_id):
+ def track_mail_open(self, mail_id, **post):
""" Email tracking. """
mail_mail_stats = request.registry.get('mail.mail.statistics')
mail_mail_stats.set_opened(request.cr, SUPERUSER_ID, mail_mail_ids=[mail_id])
- return "data:image/gif;base64,R0lGODlhAQABAIAAANvf7wAAACH5BAEAAAAALAAAAAABAAEAAAICRAEAOw=="
+ response = werkzeug.wrappers.Response()
+ response.mimetype = 'image/gif'
+ response.set_data('R0lGODlhAQABAIAAANvf7wAAACH5BAEAAAAALAAAAAABAAEAAAICRAEAOw=='.decode('base64'))
+ return response
+
+ @http.route(['/mail/mailing/<int:mailing_id>/unsubscribe'], type='http', auth='none')
+ def mailing(self, mailing_id, email=None, res_id=None, **post):
+ cr, uid, context = request.cr, request.uid, request.context
+ MassMailing = request.registry['mail.mass_mailing']
+ mailing_ids = MassMailing.exists(cr, SUPERUSER_ID, [mailing_id], context=context)
+ if not mailing_ids:
+ return 'KO'
+ mailing = MassMailing.browse(cr, SUPERUSER_ID, mailing_ids[0], context=context)
+ if mailing.mailing_model == 'mail.mass_mailing.contact':
+ list_ids = [l.id for l in mailing.contact_list_ids]
+ record_ids = request.registry[mailing.mailing_model].search(cr, SUPERUSER_ID, [('list_id', 'in', list_ids), ('id', '=', res_id), ('email', 'ilike', email)], context=context)
+ request.registry[mailing.mailing_model].write(cr, SUPERUSER_ID, record_ids, {'opt_out': True}, context=context)
+ else:
+ email_fname = None
+ if 'email_from' in request.registry[mailing.mailing_model]._all_columns:
+ email_fname = 'email_from'
+ elif 'email' in request.registry[mailing.mailing_model]._all_columns:
+ email_fname = 'email'
+ if email_fname:
+ record_ids = request.registry[mailing.mailing_model].search(cr, SUPERUSER_ID, [('id', '=', res_id), (email_fname, 'ilike', email)], context=context)
+ if 'opt_out' in request.registry[mailing.mailing_model]._all_columns:
+ request.registry[mailing.mailing_model].write(cr, SUPERUSER_ID, record_ids, {'opt_out': True}, context=context)
+ return 'OK'
diff --git a/addons/mass_mailing/mail_data.xml b/addons/mass_mailing/data/mail_data.xml
index a1a0e404fe3..a1a0e404fe3 100644
--- a/addons/mass_mailing/mail_data.xml
+++ b/addons/mass_mailing/data/mail_data.xml
diff --git a/addons/mass_mailing/data/mass_mailing_data.xml b/addons/mass_mailing/data/mass_mailing_data.xml
new file mode 100644
index 00000000000..6bd1df79a72
--- /dev/null
+++ b/addons/mass_mailing/data/mass_mailing_data.xml
@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="utf-8"?>
+<openerp>
+ <data noupdate="1">
+
+ <!-- After installation of the module, open the related menu -->
+ <record id="action_client_marketing_menu" model="ir.actions.client">
+ <field name="name">Open Marketing Menu</field>
+ <field name="tag">reload</field>
+ <field name="params" eval="{'menu_id': ref('base.marketing_menu')}"/>
+ </record>
+ <record id="base.open_menu" model="ir.actions.todo">
+ <field name="action_id" ref="action_client_marketing_menu"/>
+ <field name="state">open</field>
+ </record>
+
+ <!-- Group to manage campaigns -->
+ <record id="group_mass_mailing_campaign" model="res.groups">
+ <field name="name">Manage Mass Mailing Campaigns</field>
+ <field name="category_id" ref="base.module_category_hidden"/>
+ </record>
+
+ <!-- Default stages of mass mailing campaigns -->
+ <record id="campaign_stage_1" model="mail.mass_mailing.stage">
+ <field name="name">Schedule</field>
+ <field name="sequence">10</field>
+ </record>
+ <record id="campaign_stage_2" model="mail.mass_mailing.stage">
+ <field name="name">Design</field>
+ <field name="sequence">20</field>
+ </record>
+ <record id="campaign_stage_3" model="mail.mass_mailing.stage">
+ <field name="name">Sent</field>
+ <field name="sequence">30</field>
+ </record>
+
+ </data>
+</openerp> \ No newline at end of file
diff --git a/addons/mass_mailing/data/mass_mailing_demo.xml b/addons/mass_mailing/data/mass_mailing_demo.xml
new file mode 100644
index 00000000000..569feec9c88
--- /dev/null
+++ b/addons/mass_mailing/data/mass_mailing_demo.xml
@@ -0,0 +1,146 @@
+<?xml version="1.0"?>
+<openerp>
+ <data noupdate="1">
+
+ <record id="mass_mail_attach_1" model="ir.attachment">
+ <field name="datas">bWlncmF0aW9uIHRlc3Q=</field>
+ <field name="datas_fname">SampleDoc.doc</field>
+ <field name="name">SampleDoc.doc</field>
+ </record>
+
+ <!-- Create mailing lists -->
+ <record id="mass_mail_list_1" model="mail.mass_mailing.list">
+ <field name="name">Imported Contacts</field>
+ </record>
+
+ <!-- Create Contacts -->
+ <record id="mass_mail_contact_1" model="mail.mass_mailing.contact">
+ <field name="name">Aristide Antario</field>
+ <field name="email">aa@example.com</field>
+ <field name="list_id" ref="mass_mailing.mass_mail_list_1"/>
+ </record>
+ <record id="mass_mail_contact_2" model="mail.mass_mailing.contact">
+ <field name="name">Beverly Bridge</field>
+ <field name="email">bb@example.com</field>
+ <field name="list_id" ref="mass_mailing.mass_mail_list_1"/>
+ </record>
+ <record id="mass_mail_contact_3" model="mail.mass_mailing.contact">
+ <field name="name">Carol Cartridge</field>
+ <field name="email">cc@example.com</field>
+ <field name="list_id" ref="mass_mailing.mass_mail_list_1"/>
+ <field name="opt_out" eval="True"/>
+ </record>
+
+ <!-- Create campaign and mailings -->
+ <record id="mass_mail_category_1" model="mail.mass_mailing.category">
+ <field name="name">Marketing</field>
+ </record>
+ <record id="mass_mail_campaign_1" model="mail.mass_mailing.campaign">
+ <field name="name">Newsletter</field>
+ <field name="stage_id" ref="mass_mailing.campaign_stage_1"/>
+ <field name="user_id" eval="ref('base.user_root')"/>
+ <field name="category_ids" eval="[(6,0,[ref('mass_mailing.mass_mail_category_1')])]"/>
+ </record>
+
+ <record id="mass_mail_1" model="mail.mass_mailing">
+ <field name="name">First Newsletter</field>
+ <field name="state">done</field>
+ <field name="sent_date" eval="(DateTime.today() - relativedelta(days=5)).strftime('%Y-%m-%d %H:%M:%S')"/>
+ <field name="mass_mailing_campaign_id" eval="ref('mass_mail_campaign_1')"/>
+ <field name="mailing_model">res.partner</field>
+ <field name="mailing_domain">[('customer', '=', True)]</field>
+ <field name="reply_to_mode">email</field>
+ <field name="reply_to"><![CDATA[Info <info@yourcompany.example.com>]]></field>
+ <field name="body_html"><![CDATA[<div data-snippet-id="big-picture" style="padding:0px; margin:0px">
+<table cellpadding="0" cellspacing="0" style="margin:10px 0px 0px;vertical-align:top;padding:0px;font-family:arial;font-size:12px;color:rgb(51,51,51)">
+ <tbody>
+ <tr>
+ <td style="width:600px" valign="top">
+ <h2 style="text-align: center; padding:0px 5px">A Punchy Headline</h2>
+ </td>
+ </tr>
+ <tr>
+ <td style="width:600px" valign="top"><img src="/website/static/src/img/big_picture.png" style="display:block;border:none;min-height:250px;margin:0 auto;" width="500"></td>
+ </tr>
+ <tr>
+ <td style="width:600px" valign="top">
+ <p style="text-align: center; overflow:hidden"></p>
+
+ <h3 style="text-align: center; padding:0px 5px">A Small Subtitle for ${object.name}</h3>
+
+ <p></p>
+
+ <p style="text-align: center; overflow:hidden">Choose a vibrant image and write an inspiring paragraph about it. It does not have to be long, but it should reinforce your image.</p>
+ </td>
+ </tr>
+ </tbody>
+</table>
+</div>
+<div data-snippet-id="three-columns" style="padding:0px; margin:0px">
+<table cellpadding="0" cellspacing="0" style="margin:10px 0px 0px;vertical-align:top;padding:0px;font-family:arial;font-size:12px;color:rgb(51,51,51)">
+ <tbody>
+ <tr>
+ <td style="width:300px" valign="top"><img src="/website/static/src/img/desert_thumb.jpg" style="display:block;border:none;min-height:50px" width="275"></td>
+ <td style="width:300px" valign="top"><img src="/website/static/src/img/deers_thumb.jpg" style="display:block;border:none;min-height:50px" width="275"></td>
+ </tr>
+ <tr>
+ <td style="width:300px" valign="top">
+ <h3 style="text-align: center; padding:0px 5px">Feature One</h3>
+
+ <p style="overflow:hidden">Choose a vibrant image and write an inspiring paragraph about it. It does not have to be long, but it should reinforce your image.</p>
+ </td>
+ <td style="width:300px" valign="top">
+ <h3 style="text-align: center; padding:0px 5px">Feature Two</h3>
+
+ <p style="overflow:hidden">Choose a vibrant image and write an inspiring paragraph about it. It does not have to be long, but it should reinforce your image.</p>
+ </td>
+ </tr>
+ </tbody>
+</table>
+</div>]]></field>
+ <field name="attachment_ids" eval="[(4, ref('mass_mail_attach_1'))]"/>
+ </record>
+ <record id="mass_mail_2" model="mail.mass_mailing">
+ <field name="name">Second Newsletter</field>
+ <field name="state">test</field>
+ <field name="mass_mailing_campaign_id" eval="ref('mass_mail_campaign_1')"/>
+ <field name="mailing_model">res.partner</field>
+ <field name="mailing_domain">[('customer', '=', True)]</field>
+ <field name="reply_to_mode">email</field>
+ <field name="reply_to"><![CDATA[Info <info@yourcompany.example.com>]]></field>
+ </record>
+
+ <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="sent" eval="(DateTime.today() - relativedelta(days=5)).strftime('%Y-%m-%d %H:%M:%S')"/>
+ <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')"/>
+ </record>
+ <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="sent" eval="(DateTime.today() - relativedelta(days=5)).strftime('%Y-%m-%d %H:%M:%S')"/>
+ <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')"/>
+ </record>
+ <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="sent" eval="(DateTime.today() - relativedelta(days=5)).strftime('%Y-%m-%d %H:%M:%S')"/>
+ <field name="opened" eval="(DateTime.today() - relativedelta(days=1)).strftime('%Y-%m-%d %H:%M:%S')"/>
+ </record>
+ <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="exception" eval="(DateTime.today() - relativedelta(days=5)).strftime('%Y-%m-%d %H:%M:%S')"/>
+ </record>
+ <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="sent" eval="(DateTime.today() - relativedelta(days=5)).strftime('%Y-%m-%d %H:%M:%S')"/>
+ <field name="bounced" eval="(DateTime.today() - relativedelta(days=3)).strftime('%Y-%m-%d %H:%M:%S')"/>
+ </record>
+
+ </data>
+</openerp>
diff --git a/addons/mass_mailing/mass_mailing.py b/addons/mass_mailing/mass_mailing.py
deleted file mode 100644
index e7583d8e739..00000000000
--- a/addons/mass_mailing/mass_mailing.py
+++ /dev/null
@@ -1,369 +0,0 @@
-# -*- 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/>
-#
-##############################################################################
-
-from datetime import datetime
-from dateutil import relativedelta
-
-from openerp import tools
-from openerp.tools.translate import _
-from openerp.osv import osv, fields
-
-
-class MassMailingCampaign(osv.Model):
- """Model of mass mailing campaigns.
- """
- _name = "mail.mass_mailing.campaign"
- _description = 'Mass Mailing Campaign'
- # number of embedded mailings in kanban view
- _kanban_mailing_nbr = 4
-
- def _get_statistics(self, cr, uid, ids, name, arg, context=None):
- """ Compute statistics of the mass mailing campaign """
- results = dict.fromkeys(ids, False)
- for campaign in self.browse(cr, uid, ids, context=context):
- results[campaign.id] = {
- 'sent': len(campaign.statistics_ids),
- # delivered: shouldn't be: all mails - (failed + 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_mass_mailing_kanban_ids(self, cr, uid, ids, name, arg, context=None):
- """ Gather data about mass mailings to display them in kanban view as
- nested kanban views is not possible currently. """
- results = dict.fromkeys(ids, '')
- for campaign in self.browse(cr, uid, ids, context=context):
- mass_mailing_results = []
- for mass_mailing in campaign.mass_mailing_ids[:self._kanban_mailing_nbr]:
- mass_mailing_object = {}
- for attr in ['name', 'sent', 'delivered', 'opened', 'replied', 'bounced']:
- 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,
- ),
- 'user_id': fields.many2one(
- 'res.users', 'Responsible',
- required=True,
- ),
- 'mass_mailing_ids': fields.one2many(
- 'mail.mass_mailing', 'mass_mailing_campaign_id',
- 'Mass Mailings',
- ),
- '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'),
- # stat fields
- 'sent': fields.function(
- _get_statistics,
- string='Sent Emails',
- type='integer', multi='_get_statistics'
- ),
- 'delivered': fields.function(
- _get_statistics,
- string='Delivered',
- type='integer', multi='_get_statistics',
- ),
- 'opened': fields.function(
- _get_statistics,
- string='Opened',
- type='integer', multi='_get_statistics',
- ),
- 'replied': fields.function(
- _get_statistics,
- string='Replied',
- type='integer', multi='_get_statistics'
- ),
- 'bounced': fields.function(
- _get_statistics,
- string='Bounced',
- type='integer', multi='_get_statistics'
- ),
- }
-
- _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 Mass Mailing for the Campaign'),
- 'type': 'ir.actions.act_window',
- 'view_type': 'form',
- 'view_mode': 'form',
- 'res_model': 'mail.mass_mailing.create',
- 'views': [(False, 'form')],
- 'view_id': False,
- 'target': 'new',
- 'context': ctx,
- }
-
-
-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'
- _description = 'Wave of sending emails'
- # number of periods for tracking mail_mail statistics
- _period_number = 6
- _order = 'date DESC'
-
- def __get_bar_values(self, cr, uid, id, obj, domain, read_fields, value_field, groupby_field, context=None):
- """ Generic method to generate data for bar chart values using SparklineBarWidget.
- This method performs obj.read_group(cr, uid, domain, read_fields, groupby_field).
-
- :param obj: the target model (i.e. crm_lead)
- :param domain: the domain applied to the read_group
- :param list read_fields: the list of fields to read in the read_group
- :param str value_field: the field used to compute the value of the bar slice
- :param str groupby_field: the fields used to group
-
- :return list section_result: a list of dicts: [
- { 'value': (int) bar_column_value,
- 'tootip': (str) bar_column_tooltip,
- }
- ]
- """
- date_begin = datetime.strptime(self.browse(cr, uid, id, context=context).date, tools.DEFAULT_SERVER_DATETIME_FORMAT).date()
- section_result = [{'value': 0,
- 'tooltip': (date_begin + relativedelta.relativedelta(days=i)).strftime('%d %B %Y'),
- } for i in range(0, self._period_number)]
- group_obj = obj.read_group(cr, uid, domain, read_fields, groupby_field, context=context)
- field_col_info = obj._all_columns.get(groupby_field.split(':')[0])
- pattern = tools.DEFAULT_SERVER_DATE_FORMAT if field_col_info.column._type == 'date' else tools.DEFAULT_SERVER_DATETIME_FORMAT
- for group in group_obj:
- group_begin_date = datetime.strptime(group['__domain'][0][2], pattern).date()
- timedelta = relativedelta.relativedelta(group_begin_date, date_begin)
- section_result[timedelta.days] = {'value': group.get(value_field, 0), 'tooltip': group.get(groupby_field)}
- return section_result
-
- def _get_daily_statistics(self, cr, uid, ids, field_name, arg, context=None):
- """ Get the daily statistics of the mass mailing. This is done by a grouping
- on opened and replied fields. Using custom format in context, we obtain
- results for the next 6 days following the mass mailing date. """
- obj = self.pool['mail.mail.statistics']
- res = {}
- for id in ids:
- res[id] = {}
- date_begin = datetime.strptime(self.browse(cr, uid, id, context=context).date, tools.DEFAULT_SERVER_DATETIME_FORMAT)
- date_end = date_begin + relativedelta.relativedelta(days=self._period_number - 1)
- date_begin_str = date_begin.strftime(tools.DEFAULT_SERVER_DATETIME_FORMAT)
- date_end_str = date_end.strftime(tools.DEFAULT_SERVER_DATETIME_FORMAT)
- domain = [('mass_mailing_id', '=', id), ('opened', '>=', date_begin_str), ('opened', '<=', date_end_str)]
- res[id]['opened_monthly'] = self.__get_bar_values(cr, uid, id, obj, domain, ['opened'], 'opened_count', 'opened:day', context=context)
- domain = [('mass_mailing_id', '=', id), ('replied', '>=', date_begin_str), ('replied', '<=', date_end_str)]
- res[id]['replied_monthly'] = self.__get_bar_values(cr, uid, id, obj, domain, ['replied'], 'replied_count', 'replied:day', 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 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
-
- _columns = {
- 'name': fields.char('Name', required=True),
- 'mass_mailing_campaign_id': fields.many2one(
- 'mail.mass_mailing.campaign', 'Mass Mailing Campaign',
- ondelete='cascade', required=True,
- ),
- 'template_id': fields.many2one(
- 'email.template', 'Email Template',
- ondelete='set null',
- ),
- 'domain': fields.char('Domain'),
- 'date': fields.datetime('Date'),
- 'color': fields.related(
- 'mass_mailing_campaign_id', 'color',
- type='integer', string='Color Index',
- ),
- # statistics data
- 'statistics_ids': fields.one2many(
- 'mail.mail.statistics', 'mass_mailing_id',
- 'Emails Statistics',
- ),
- 'sent': fields.function(
- _get_statistics,
- string='Sent Emails',
- type='integer', multi='_get_statistics'
- ),
- 'delivered': fields.function(
- _get_statistics,
- string='Delivered',
- type='integer', multi='_get_statistics',
- ),
- 'opened': fields.function(
- _get_statistics,
- string='Opened',
- type='integer', multi='_get_statistics',
- ),
- 'replied': fields.function(
- _get_statistics,
- string='Replied',
- type='integer', multi='_get_statistics'
- ),
- 'bounced': fields.function(
- _get_statistics,
- string='Bounce',
- type='integer', multi='_get_statistics'
- ),
- # monthly ratio
- 'opened_monthly': fields.function(
- _get_daily_statistics,
- string='Opened',
- type='char', multi='_get_daily_statistics',
- ),
- 'replied_monthly': fields.function(
- _get_daily_statistics,
- string='Replied',
- type='char', multi='_get_daily_statistics',
- ),
- }
-
- _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',
- ),
- 'model': fields.char(
- 'Document model',
- ),
- 'res_id': fields.integer(
- 'Document ID',
- ),
- # 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 ids
-
- 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 ids
-
- 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 ids
diff --git a/addons/mass_mailing/mass_mailing_demo.xml b/addons/mass_mailing/mass_mailing_demo.xml
deleted file mode 100644
index 5a2388846e4..00000000000
--- a/addons/mass_mailing/mass_mailing_demo.xml
+++ /dev/null
@@ -1,82 +0,0 @@
-<?xml version="1.0"?>
-<openerp>
- <!-- <data noupdate="1"> -->
- <data>
-
- <record id="mass_mail_template_1" model="email.template">
- <field name="name">Partner Newsletter 1</field>
- <field name="model_id" ref="base.model_res_partner"/>
- <field name="auto_delete" eval="False"/>
- <field name="partner_to">${object.id}</field>
- <field name="body_html"><![CDATA[<p>Hello</p>]]></field>
- </record>
- <record id="mass_mail_template_2" model="email.template">
- <field name="name">Partner Newsletter 2</field>
- <field name="model_id" ref="base.model_res_partner"/>
- <field name="auto_delete" eval="False"/>
- <field name="partner_to">${object.id}</field>
- <field name="body_html"><![CDATA[<p>Hello</p>]]></field>
- </record>
-
- <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_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_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.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')"/>
- </record>
- <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')"/>
- </record>
- <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')"/>
- </record>
- <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>
- </record>
- <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')"/>
- </record>
-
- <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')"/>
- </record>
- <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')"/>
- </record>
- <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>
- </record>
-
- </data>
-</openerp>
diff --git a/addons/mass_mailing/mass_mailing_view.xml b/addons/mass_mailing/mass_mailing_view.xml
deleted file mode 100644
index e8848fba44f..00000000000
--- a/addons/mass_mailing/mass_mailing_view.xml
+++ /dev/null
@@ -1,379 +0,0 @@
-<?xml version="1.0"?>
-<openerp>
- <data>
-
- <!-- MASS MAILING !-->
- <record model="ir.ui.view" id="view_mail_mass_mailing_search">
- <field name="name">mail.mass_mailing.search</field>
- <field name="model">mail.mass_mailing</field>
- <field name="arch" type="xml">
- <search string="Mass Mailings">
- <field name="name" string="Mailings"/>
- <field name="mass_mailing_campaign_id"/>
- <field name="template_id"/>
- <group expand="0" string="Group By...">
- <filter string="Campaign" name="group_mass_mailing_campaign_id"
- context="{'group_by': 'mass_mailing_campaign_id'}"/>
- <filter string="Template" name="group_template_id"
- context="{'group_by': 'template_id'}"/>
- </group>
- </search>
- </field>
- </record>
-
- <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 Mailings">
- <field name="name"/>
- <field name="sent"/>
- <field name="delivered"/>
- <field name="opened"/>
- <field name="replied"/>
- <field name="mass_mailing_campaign_id" invisible="1"/>
- <field name="template_id" invisible="1"/>
- </tree>
- </field>
- </record>
-
- <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" version="7.0">
- <sheet>
- <group>
- <group>
- <field name="name"/>
- <field name="mass_mailing_campaign_id" readonly="True"/>
- </group>
- <group>
- <field name="template_id"/>
- <field name="domain"/>
- <field name="date"/>
- </group>
- </group>
- <group string="Email Statistics">
- <field name="statistics_ids" nolabel="1" colspan="2"/>
- <group>
- <field name="sent"/>
- <field name="opened"/>
- <field name="bounced"/>
- </group>
- <group>
- <field name="delivered"/>
- <field name="replied"/>
- </group>
- </group>
- </sheet>
- </form>
- </field>
- </record>
-
- <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'/>
- <templates>
- <t t-name="kanban-box">
- <div t-attf-class="oe_kanban_color_#{kanban_getcolor(record.color.raw_value)} oe_kanban_card oe_kanban_global_click oe_kanban_mass_mailing oe_kanban_mass_mailing_segment">
- <div class="oe_kanban_content">
- <div>
- <h3>
- <field name="name"/>
- </h3>
- <p style="margin-left: 10px; margin-top: 8px;">
- Sent: <field name="date"/><br />
- Campaign: <field name="mass_mailing_campaign_id"/>
- </p>
- </div>
- <div>
- <p class="oe_mail_stats">
- <span class="oe_mail_result"><field name="sent"/></span><br />
- Sent
- </p>
- <p class="oe_mail_stats">
- <span class="oe_mail_result"><field name="delivered"/></span><br />
- Delivered
- </p>
- <p class="oe_mail_stats">
- <span class="oe_mail_result"><field name="opened"/></span><br />
- Opened
- </p>
- <p class="oe_mail_stats">
- <span class="oe_mail_result"><field name="replied"/></span><br />
- Replied
- </p>
- </div>
- <div>
- <div class="oe_sparkline_container">
- <h4 class="oe_sparkline_bar_title">Opened</h4><br />
- <field name="opened_monthly" widget="sparkline_bar" options="{'height': '50px', 'barWidth': 10, 'barSpacing': 5}"/>
- </div>
- <div class="oe_sparkline_container">
- <h4 class="oe_sparkline_bar_title">Replied</h4><br />
- <field name="replied_monthly" widget="sparkline_bar" options="{'height': '50px', 'barWidth': 10, 'barSpacing': 5}"/>
- </div>
- </div>
- </div>
- <div class="oe_clear"></div>
- </div>
- </t>
- </templates>
- </kanban>
- </field>
- </record>
-
- <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>
-
- <record id="action_view_mass_mailings_from_campaign" 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>
- <field name="context">{
- 'search_default_mass_mailing_campaign_id': [active_id],
- 'default_mass_mailing_campaign_id': active_id,
- }
- </field>
- </record>
-
- <!-- MASS MAILING CAMPAIGNS !-->
- <record model="ir.ui.view" id="view_mail_mass_mailing_campaign_search">
- <field name="name">mail.mass_mailing.campaign.search</field>
- <field name="model">mail.mass_mailing.campaign</field>
- <field name="arch" type="xml">
- <search string="Mass Mailing Campaigns">
- <field name="name" string="Campaigns"/>
- <field name="user_id"/>
- <group expand="0" string="Group By...">
- <filter string="Responsibles" name="group_user_id"
- context="{'group_by': 'user_id'}"/>
- </group>
- </search>
- </field>
- </record>
-
- <record model="ir.ui.view" id="view_mail_mass_mailing_campaign_tree">
- <field name="name">mail.mass_mailing.campaign.tree</field>
- <field name="model">mail.mass_mailing.campaign</field>
- <field name="priority">10</field>
- <field name="arch" type="xml">
- <tree string="Mass Mailing Campaigns">
- <field name="name"/>
- <field name="user_id"/>
- </tree>
- </field>
- </record>
-
- <record model="ir.ui.view" id="view_mail_mass_mailing_campaign_form">
- <field name="name">mail.mass_mailing.campaign.form</field>
- <field name="model">mail.mass_mailing.campaign</field>
- <field name="arch" type="xml">
- <form string="Mass Mailing Campaign" version="7.0">
- <header>
- <button name="launch_mass_mailing_create_wizard" type="object"
- class="oe_highlight" string="Create a New Mailing"/>
- </header>
- <sheet>
- <group>
- <field name="name"/>
- <field name="user_id"/>
- </group>
- <group>
- <group>
- <field name="sent"/>
- <field name="opened"/>
- <field name="bounced"/>
- </group>
- <group>
- <field name="delivered"/>
- <field name="replied"/>
- </group>
- </group>
- <group>
- <field name="mass_mailing_ids" readonly="1"/>
- </group>
- </sheet>
- </form>
- </field>
- </record>
-
- <record model="ir.ui.view" id="view_mail_mass_mailing_campaign_kanban">
- <field name="name">mail.mass_mailing.campaign.kanban</field>
- <field name="model">mail.mass_mailing.campaign</field>
- <field name="arch" type="xml">
- <kanban>
- <field name="mass_mailing_kanban_ids"/>
- <field name='sent'/>
- <field name='color'/>
- <templates>
- <t t-name="mass_mailing.mass_mailing">
- <div class="oe_mass_mailings">
- <div>
- <a name="%(action_view_mass_mailings_from_campaign)d" type="action">
- <h4><t t-raw="mass_mailing.name"/></h4>
- </a>
- </div>
- <div>
- <p class="oe_mail_stats">
- <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="mass_mailing.delivered"/></span><br />
- Delivered
- </p>
- <p class="oe_mail_stats">
- <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="mass_mailing.replied"/></span><br />
- Replied
- </p>
- </div>
- </div>
- </t>
- <t t-name="kanban-box">
- <div t-attf-class="oe_kanban_color_#{kanban_getcolor(record.color.raw_value)} oe_kanban_card oe_kanban_global_click oe_kanban_mass_mailing oe_kanban_mass_mailing_campaign">
- <div class="oe_dropdown_toggle oe_dropdown_kanban">
- <span class="oe_e">i</span>
- <ul class="oe_dropdown_menu">
- <t t-if="widget.view.is_action_enabled('edit')">
- <li><a type="edit">Settings</a></li>
- </t>
- <t t-if="widget.view.is_action_enabled('edit')">
- <li><a name="%(action_mail_mass_mailing_create)d" type="action">New Wave</a></li>
- </t>
- <t t-if="widget.view.is_action_enabled('delete')">
- <li><a type="delete">Delete</a></li>
- </t>
- <li><ul class="oe_kanban_colorpicker" data-field="color"/></li>
- </ul>
- </div>
- <div class="oe_kanban_content">
- <h3>
- <field name="name"/>
- </h3>
- <div>
- <field name="delivered" widget="gauge" style="width:160px; height: 120px;"
- options="{'max_field': 'sent'}"/>
- <field name="opened" widget="gauge" style="width:160px; height: 120px;"
- options="{'max_field': 'sent'}"/>
- <field name="replied" widget="gauge" style="width:160px; height: 120px;"
- options="{'max_field': 'sent'}"/>
- </div>
- <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>
- </div>
- </t>
- </templates>
- </kanban>
- </field>
- </record>
-
- <record id="action_view_mass_mailing_campaigns" model="ir.actions.act_window">
- <field name="name">Mass Mailing Campaigns</field>
- <field name="res_model">mail.mass_mailing.campaign</field>
- <field name="view_type">form</field>
- <field name="view_mode">kanban,tree,form</field>
- <field name="help" type="html">
- <p class="oe_view_nocontent_create">
- Click to define a new mass mailing campaign.
- </p><p>
- Create a campaign to structure mass mailing and get analysis from email status.
- </p>
- </field>
- </record>
-
- <!-- MAIL MAIL STATISTICS !-->
- <record model="ir.ui.view" id="view_mail_mail_statistics_search">
- <field name="name">mail.mail.statistics.search</field>
- <field name="model">mail.mail.statistics</field>
- <field name="arch" type="xml">
- <search string="Mail Statistics">
- <field name="mail_mail_id"/>
- <field name="message_id"/>
- </search>
- </field>
- </record>
-
- <record model="ir.ui.view" id="view_mail_mail_statistics_tree">
- <field name="name">mail.mail.statistics.tree</field>
- <field name="model">mail.mail.statistics</field>
- <field name="arch" type="xml">
- <tree string="Mail Statistics">
- <field name="mail_mail_id"/>
- <field name="message_id"/>
- <field name="opened"/>
- <field name="replied"/>
- <field name="bounced"/>
- </tree>
- </field>
- </record>
-
- <record model="ir.ui.view" id="view_mail_mail_statistics_form">
- <field name="name">mail.mail.statistics.form</field>
- <field name="model">mail.mail.statistics</field>
- <field name="arch" type="xml">
- <form string="Mail Statistics" version="7.0">
- <group>
- <group>
- <field name="mail_mail_id"/>
- <field name="message_id"/>
- <field name="opened"/>
- <field name="replied"/>
- <field name="bounced"/>
- </group>
- <group>
- <field name="mass_mailing_id"/>
- <field name="mass_mailing_campaign_id"/>
- <field name="template_id"/>
- <field name="model"/>
- <field name="res_id"/>
- </group>
- </group>
- </form>
- </field>
- </record>
-
- <record id="action_view_mail_mail_statistics" model="ir.actions.act_window">
- <field name="name">Mail Statistics</field>
- <field name="res_model">mail.mail.statistics</field>
- <field name="view_type">form</field>
- <field name="view_mode">tree,form</field>
- </record>
-
- <!-- Top menu item -->
- <menuitem name="Marketing" id="base.marketing_menu" sequence="85" groups="base.group_user"/>
-
- <!-- Add in marketing -->
- <menuitem name="Mass Mailing" id="mass_mailing_campaign"
- parent="base.marketing_menu" sequence="1"/>
- <menuitem name="Campaigns" id="menu_email_campaigns"
- parent="mass_mailing_campaign" sequence="1"
- action="action_view_mass_mailing_campaigns"/>
- <menuitem name="Mass Mailings" id="menu_email_mass_mailings"
- parent="mass_mailing_campaign" sequence="2"
- action="action_view_mass_mailings"/>
-
- <!-- Add in Technical/Email -->
- <menuitem name="Mail Statistics" id="menu_email_statistics"
- parent="base.menu_email" sequence="50"
- action="action_view_mail_mail_statistics"/>
-
- </data>
-</openerp>
diff --git a/addons/mass_mailing/models/__init__.py b/addons/mass_mailing/models/__init__.py
new file mode 100644
index 00000000000..2cffe48642b
--- /dev/null
+++ b/addons/mass_mailing/models/__init__.py
@@ -0,0 +1,7 @@
+# -*- coding: utf-8 -*-
+
+import mass_mailing
+import mass_mailing_stats
+import mail_mail
+import mail_thread
+import res_config
diff --git a/addons/mass_mailing/mail_mail.py b/addons/mass_mailing/models/mail_mail.py
index 917334bdda4..a67f88a2a28 100644
--- a/addons/mass_mailing/mail_mail.py
+++ b/addons/mass_mailing/models/mail_mail.py
@@ -19,7 +19,8 @@
#
##############################################################################
-from urlparse import urljoin
+import urllib
+import urlparse
from openerp import tools
from openerp import SUPERUSER_ID
@@ -32,6 +33,7 @@ class MailMail(osv.Model):
_inherit = ['mail.mail']
_columns = {
+ 'mailing_id': fields.many2one('mail.mass_mailing', 'Mass Mailing'),
'statistics_ids': fields.one2many(
'mail.mail.statistics', 'mail_mail_id',
string='Statistics',
@@ -50,9 +52,24 @@ class MailMail(osv.Model):
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)
+ track_url = urlparse.urljoin(
+ base_url, 'mail/track/%(mail_id)s/blank.gif?%(params)s' % {
+ 'mail_id': mail.id,
+ 'params': urllib.urlencode({'db': cr.dbname})
+ }
+ )
return '<img src="%s" alt=""/>' % track_url
+ def _get_unsubscribe_url(self, cr, uid, mail, email_to, msg=None, context=None):
+ base_url = self.pool.get('ir.config_parameter').get_param(cr, uid, 'web.base.url')
+ url = urlparse.urljoin(
+ base_url, 'mail/mailing/%(mailing_id)s/unsubscribe?%(params)s' % {
+ 'mailing_id': mail.mailing_id.id,
+ 'params': urllib.urlencode({'db': cr.dbname, 'res_id': mail.res_id, 'email': email_to})
+ }
+ )
+ return '<small><a href="%s">%s</a></small>' % (url, msg or 'Click to unsubscribe')
+
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)
@@ -63,3 +80,19 @@ class MailMail(osv.Model):
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):
+ res = super(MailMail, self).send_get_email_dict(cr, uid, mail, partner, context=context)
+ if mail.mailing_id and res.get('body') and res.get('email_to'):
+ email_to = tools.email_split(res.get('email_to')[0])
+ unsubscribe_url = self._get_unsubscribe_url(cr, uid, mail, email_to, context=context)
+ if unsubscribe_url:
+ res['body'] = tools.append_content_to_html(res['body'], unsubscribe_url, plaintext=False, container_tag='p')
+ return res
+
+ def _postprocess_sent_message(self, cr, uid, mail, context=None, mail_sent=True):
+ if mail_sent is True and mail.statistics_ids:
+ self.pool['mail.mail.statistics'].write(cr, uid, [s.id for s in mail.statistics_ids], {'sent': fields.datetime.now()}, context=context)
+ elif mail_sent is False and mail.statistics_ids:
+ self.pool['mail.mail.statistics'].write(cr, uid, [s.id for s in mail.statistics_ids], {'exception': fields.datetime.now()}, context=context)
+ return super(MailMail, self)._postprocess_sent_message(cr, uid, mail, context=context, mail_sent=mail_sent)
diff --git a/addons/mass_mailing/mail_thread.py b/addons/mass_mailing/models/mail_thread.py
index 524341f88d7..524341f88d7 100644
--- a/addons/mass_mailing/mail_thread.py
+++ b/addons/mass_mailing/models/mail_thread.py
diff --git a/addons/mass_mailing/models/mass_mailing.py b/addons/mass_mailing/models/mass_mailing.py
new file mode 100644
index 00000000000..bd314f03a3d
--- /dev/null
+++ b/addons/mass_mailing/models/mass_mailing.py
@@ -0,0 +1,571 @@
+# -*- coding: utf-8 -*-
+
+from datetime import datetime
+from dateutil import relativedelta
+import json
+import random
+import urllib
+import urlparse
+
+from openerp import tools
+from openerp.tools.safe_eval import safe_eval as eval
+from openerp.tools.translate import _
+from openerp.osv import osv, fields
+
+
+class MassMailingCategory(osv.Model):
+ """Model of categories of mass mailing, i.e. marketing, newsletter, ... """
+ _name = 'mail.mass_mailing.category'
+ _description = 'Mass Mailing Category'
+ _order = 'name'
+
+ _columns = {
+ 'name': fields.char('Name', required=True),
+ }
+
+
+class MassMailingContact(osv.Model):
+ """Model of a contact. This model is different from the partner model
+ because it holds only some basic information: name, email. The purpose is to
+ be able to deal with large contact list to email without bloating the partner
+ base."""
+ _name = 'mail.mass_mailing.contact'
+ _description = 'Mass Mailing Contact'
+ _order = 'email'
+ _rec_name = 'email'
+
+ _columns = {
+ 'name': fields.char('Name'),
+ 'email': fields.char('Email', required=True),
+ 'create_date': fields.datetime('Create Date'),
+ 'list_id': fields.many2one(
+ 'mail.mass_mailing.list', string='Mailing List',
+ ondelete='cascade', required=True,
+ ),
+ 'opt_out': fields.boolean('Opt Out', help='The contact has chosen not to receive mails anymore from this list'),
+ }
+
+ def _get_latest_list(self, cr, uid, context={}):
+ lid = self.pool.get('mail.mass_mailing.list').search(cr, uid, [], limit=1, order='id desc', context=context)
+ return lid and lid[0] or False
+
+ _defaults = {
+ 'list_id': _get_latest_list
+ }
+
+ def name_create(self, cr, uid, name, context=None):
+ name, email = self.pool['res.partner']._parse_partner_name(name, context=context)
+ if name and not email:
+ email = name
+ if email and not name:
+ name = email
+ rec_id = self.create(cr, uid, {'name': name, 'email': email}, context=context)
+ return self.name_get(cr, uid, [rec_id], context)[0]
+
+
+class MassMailingList(osv.Model):
+ """Model of a contact list. """
+ _name = 'mail.mass_mailing.list'
+ _order = 'name'
+ _description = 'Mailing List'
+
+ def _get_contact_nbr(self, cr, uid, ids, name, arg, context=None):
+ result = dict.fromkeys(ids, 0)
+ Contacts = self.pool.get('mail.mass_mailing.contact')
+ for group in Contacts.read_group(cr, uid, [('list_id', 'in', ids), ('opt_out', '!=', True)], ['list_id'], ['list_id'], context=context):
+ result[group['list_id'][0]] = group['list_id_count']
+ return result
+
+ _columns = {
+ 'name': fields.char('Mailing List', required=True),
+ 'contact_nbr': fields.function(
+ _get_contact_nbr, type='integer',
+ string='Number of Contacts',
+ ),
+ }
+
+
+class MassMailingStage(osv.Model):
+ """Stage for mass mailing campaigns. """
+ _name = 'mail.mass_mailing.stage'
+ _description = 'Mass Mailing Campaign Stage'
+ _order = 'sequence'
+
+ _columns = {
+ 'name': fields.char('Name', required=True, translate=True),
+ 'sequence': fields.integer('Sequence'),
+ }
+
+ _defaults = {
+ 'sequence': 0,
+ }
+
+
+class MassMailingCampaign(osv.Model):
+ """Model of mass mailing campaigns. """
+ _name = "mail.mass_mailing.campaign"
+ _description = 'Mass Mailing Campaign'
+
+ def _get_statistics(self, cr, uid, ids, name, arg, context=None):
+ """ Compute statistics of the mass mailing campaign """
+ Statistics = self.pool['mail.mail.statistics']
+ results = dict.fromkeys(ids, False)
+ for cid in ids:
+ stat_ids = Statistics.search(cr, uid, [('mass_mailing_campaign_id', '=', cid)], context=context)
+ stats = Statistics.browse(cr, uid, stat_ids, context=context)
+ results[cid] = {
+ 'total': len(stats),
+ 'failed': len([s for s in stats if not s.scheduled is False and s.sent is False and not s.exception is False]),
+ 'scheduled': len([s for s in stats if not s.scheduled is False and s.sent is False and s.exception is False]),
+ 'sent': len([s for s in stats if not s.sent is False]),
+ 'opened': len([s for s in stats if not s.opened is False]),
+ 'replied': len([s for s in stats if not s.replied is False]),
+ 'bounced': len([s for s in stats if not s.bounced is False]),
+ }
+ results[cid]['delivered'] = results[cid]['sent'] - results[cid]['bounced']
+ results[cid]['received_ratio'] = 100.0 * results[cid]['delivered'] / (results[cid]['total'] or 1)
+ results[cid]['opened_ratio'] = 100.0 * results[cid]['opened'] / (results[cid]['total'] or 1)
+ results[cid]['replied_ratio'] = 100.0 * results[cid]['replied'] / (results[cid]['total'] or 1)
+ return results
+
+ _columns = {
+ 'name': fields.char('Name', required=True),
+ 'stage_id': fields.many2one('mail.mass_mailing.stage', 'Stage', required=True),
+ 'user_id': fields.many2one(
+ 'res.users', 'Responsible',
+ required=True,
+ ),
+ 'category_ids': fields.many2many(
+ 'mail.mass_mailing.category', 'mail_mass_mailing_category_rel',
+ 'category_id', 'campaign_id', string='Categories'),
+ 'mass_mailing_ids': fields.one2many(
+ 'mail.mass_mailing', 'mass_mailing_campaign_id',
+ 'Mass Mailings',
+ ),
+ 'unique_ab_testing': fields.boolean(
+ 'AB Testing',
+ help='If checked, recipients will be mailed only once, allowing to send'
+ 'various mailings in a single campaign to test the effectiveness'
+ 'of the mailings.'),
+ 'color': fields.integer('Color Index'),
+ # stat fields
+ 'total': fields.function(
+ _get_statistics, string='Total',
+ type='integer', multi='_get_statistics'
+ ),
+ 'scheduled': fields.function(
+ _get_statistics, string='Scheduled',
+ type='integer', multi='_get_statistics'
+ ),
+ 'failed': fields.function(
+ _get_statistics, string='Failed',
+ type='integer', multi='_get_statistics'
+ ),
+ 'sent': fields.function(
+ _get_statistics, string='Sent Emails',
+ type='integer', multi='_get_statistics'
+ ),
+ 'delivered': fields.function(
+ _get_statistics, string='Delivered',
+ type='integer', multi='_get_statistics',
+ ),
+ 'opened': fields.function(
+ _get_statistics, string='Opened',
+ type='integer', multi='_get_statistics',
+ ),
+ 'replied': fields.function(
+ _get_statistics, string='Replied',
+ type='integer', multi='_get_statistics'
+ ),
+ 'bounced': fields.function(
+ _get_statistics, string='Bounced',
+ type='integer', multi='_get_statistics'
+ ),
+ 'received_ratio': fields.function(
+ _get_statistics, string='Received Ratio',
+ type='integer', multi='_get_statistics',
+ ),
+ 'opened_ratio': fields.function(
+ _get_statistics, string='Opened Ratio',
+ type='integer', multi='_get_statistics',
+ ),
+ 'replied_ratio': fields.function(
+ _get_statistics, string='Replied Ratio',
+ type='integer', multi='_get_statistics',
+ ),
+ }
+
+ def _get_default_stage_id(self, cr, uid, context=None):
+ stage_ids = self.pool['mail.mass_mailing.stage'].search(cr, uid, [], limit=1, context=context)
+ return stage_ids and stage_ids[0] or False
+
+ _defaults = {
+ 'user_id': lambda self, cr, uid, ctx=None: uid,
+ 'stage_id': lambda self, *args: self._get_default_stage_id(*args),
+ }
+
+ def get_recipients(self, cr, uid, ids, model=None, context=None):
+ """Return the recipients of a mailing campaign. This is based on the statistics
+ build for each mailing. """
+ Statistics = self.pool['mail.mail.statistics']
+ res = dict.fromkeys(ids, False)
+ for cid in ids:
+ domain = [('mass_mailing_campaign_id', '=', cid)]
+ if model:
+ domain += [('model', '=', model)]
+ stat_ids = Statistics.search(cr, uid, domain, context=context)
+ res[cid] = set(stat.res_id for stat in Statistics.browse(cr, uid, stat_ids, context=context))
+ return res
+
+
+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'
+ _description = 'Mass Mailing'
+ # number of periods for tracking mail_mail statistics
+ _period_number = 6
+ _order = 'sent_date DESC'
+
+ def __get_bar_values(self, cr, uid, obj, domain, read_fields, value_field, groupby_field, date_begin, context=None):
+ """ Generic method to generate data for bar chart values using SparklineBarWidget.
+ This method performs obj.read_group(cr, uid, domain, read_fields, groupby_field).
+
+ :param obj: the target model (i.e. crm_lead)
+ :param domain: the domain applied to the read_group
+ :param list read_fields: the list of fields to read in the read_group
+ :param str value_field: the field used to compute the value of the bar slice
+ :param str groupby_field: the fields used to group
+
+ :return list section_result: a list of dicts: [
+ { 'value': (int) bar_column_value,
+ 'tootip': (str) bar_column_tooltip,
+ }
+ ]
+ """
+ date_begin = date_begin.date()
+ section_result = [{'value': 0,
+ 'tooltip': (date_begin + relativedelta.relativedelta(days=i)).strftime('%d %B %Y'),
+ } for i in range(0, self._period_number)]
+ group_obj = obj.read_group(cr, uid, domain, read_fields, groupby_field, context=context)
+ field_col_info = obj._all_columns.get(groupby_field.split(':')[0])
+ pattern = tools.DEFAULT_SERVER_DATE_FORMAT if field_col_info.column._type == 'date' else tools.DEFAULT_SERVER_DATETIME_FORMAT
+ for group in group_obj:
+ group_begin_date = datetime.strptime(group['__domain'][0][2], pattern).date()
+ timedelta = relativedelta.relativedelta(group_begin_date, date_begin)
+ section_result[timedelta.days] = {'value': group.get(value_field, 0), 'tooltip': group.get(groupby_field)}
+ return section_result
+
+ def _get_daily_statistics(self, cr, uid, ids, field_name, arg, context=None):
+ """ Get the daily statistics of the mass mailing. This is done by a grouping
+ on opened and replied fields. Using custom format in context, we obtain
+ results for the next 6 days following the mass mailing date. """
+ obj = self.pool['mail.mail.statistics']
+ res = {}
+ for mailing in self.browse(cr, uid, ids, context=context):
+ res[mailing.id] = {}
+ date = mailing.sent_date if mailing.sent_date else mailing.create_date
+ date_begin = datetime.strptime(date, tools.DEFAULT_SERVER_DATETIME_FORMAT)
+ date_end = date_begin + relativedelta.relativedelta(days=self._period_number - 1)
+ date_begin_str = date_begin.strftime(tools.DEFAULT_SERVER_DATETIME_FORMAT)
+ date_end_str = date_end.strftime(tools.DEFAULT_SERVER_DATETIME_FORMAT)
+ domain = [('mass_mailing_id', '=', mailing.id), ('opened', '>=', date_begin_str), ('opened', '<=', date_end_str)]
+ res[mailing.id]['opened_dayly'] = json.dumps(self.__get_bar_values(cr, uid, obj, domain, ['opened'], 'opened_count', 'opened:day', date_begin, context=context))
+ domain = [('mass_mailing_id', '=', mailing.id), ('replied', '>=', date_begin_str), ('replied', '<=', date_end_str)]
+ res[mailing.id]['replied_dayly'] = json.dumps(self.__get_bar_values(cr, uid, obj, domain, ['replied'], 'replied_count', 'replied:day', date_begin, context=context))
+ return res
+
+ def _get_statistics(self, cr, uid, ids, name, arg, context=None):
+ """ Compute statistics of the mass mailing campaign """
+ Statistics = self.pool['mail.mail.statistics']
+ results = dict.fromkeys(ids, False)
+ for mid in ids:
+ stat_ids = Statistics.search(cr, uid, [('mass_mailing_id', '=', mid)], context=context)
+ stats = Statistics.browse(cr, uid, stat_ids, context=context)
+ results[mid] = {
+ 'total': len(stats),
+ 'failed': len([s for s in stats if not s.scheduled is False and s.sent is False and not s.exception is False]),
+ 'scheduled': len([s for s in stats if not s.scheduled is False and s.sent is False and s.exception is False]),
+ 'sent': len([s for s in stats if not s.sent is False]),
+ 'opened': len([s for s in stats if not s.opened is False]),
+ 'replied': len([s for s in stats if not s.replied is False]),
+ 'bounced': len([s for s in stats if not s.bounced is False]),
+ }
+ results[mid]['delivered'] = results[mid]['sent'] - results[mid]['bounced']
+ results[mid]['received_ratio'] = 100.0 * results[mid]['delivered'] / (results[mid]['total'] or 1)
+ results[mid]['opened_ratio'] = 100.0 * results[mid]['opened'] / (results[mid]['total'] or 1)
+ results[mid]['replied_ratio'] = 100.0 * results[mid]['replied'] / (results[mid]['total'] or 1)
+ return results
+
+ def _get_mailing_model(self, cr, uid, context=None):
+ res = []
+ for model_name in self.pool:
+ model = self.pool[model_name]
+ if hasattr(model, '_mail_mass_mailing') and getattr(model, '_mail_mass_mailing'):
+ res.append((model._name, getattr(model, '_mail_mass_mailing')))
+ res.append(('mail.mass_mailing.contact', _('Mailing List')))
+ return res
+
+ # indirections for inheritance
+ _mailing_model = lambda self, *args, **kwargs: self._get_mailing_model(*args, **kwargs)
+
+ _columns = {
+ 'name': fields.char('Subject', required=True),
+ 'email_from': fields.char('From', required=True),
+ 'create_date': fields.datetime('Creation Date'),
+ 'sent_date': fields.datetime('Sent Date'),
+ 'body_html': fields.html('Body'),
+ 'attachment_ids': fields.many2many(
+ 'ir.attachment', 'mass_mailing_ir_attachments_rel',
+ 'mass_mailing_id', 'attachment_id', 'Attachments'
+ ),
+ 'mass_mailing_campaign_id': fields.many2one(
+ 'mail.mass_mailing.campaign', 'Mass Mailing Campaign',
+ ondelete='set null',
+ ),
+ 'state': fields.selection(
+ [('draft', 'Draft'), ('test', 'Tested'), ('done', 'Sent')],
+ string='Status', required=True,
+ ),
+ 'color': fields.related(
+ 'mass_mailing_campaign_id', 'color',
+ type='integer', string='Color Index',
+ ),
+ # mailing options
+ 'reply_to_mode': fields.selection(
+ [('thread', 'In Document'), ('email', 'Specified Email Address')],
+ string='Reply-To Mode', required=True,
+ ),
+ 'reply_to': fields.char('Reply To', help='Preferred Reply-To Address'),
+ # recipients
+ 'mailing_model': fields.selection(_mailing_model, string='Recipients Model', required=True),
+ 'mailing_domain': fields.char('Domain'),
+ 'contact_list_ids': fields.many2many(
+ 'mail.mass_mailing.list', 'mail_mass_mailing_list_rel',
+ string='Mailing Lists',
+ ),
+ 'contact_ab_pc': fields.integer(
+ 'AB Testing percentage',
+ help='Percentage of the contacts that will be mailed. Recipients will be taken randomly.'
+ ),
+ # statistics data
+ 'statistics_ids': fields.one2many(
+ 'mail.mail.statistics', 'mass_mailing_id',
+ 'Emails Statistics',
+ ),
+ 'total': fields.function(
+ _get_statistics, string='Total',
+ type='integer', multi='_get_statistics',
+ ),
+ 'scheduled': fields.function(
+ _get_statistics, string='Scheduled',
+ type='integer', multi='_get_statistics',
+ ),
+ 'failed': fields.function(
+ _get_statistics, string='Failed',
+ type='integer', multi='_get_statistics',
+ ),
+ 'sent': fields.function(
+ _get_statistics, string='Sent',
+ type='integer', multi='_get_statistics',
+ ),
+ 'delivered': fields.function(
+ _get_statistics, string='Delivered',
+ type='integer', multi='_get_statistics',
+ ),
+ 'opened': fields.function(
+ _get_statistics, string='Opened',
+ type='integer', multi='_get_statistics',
+ ),
+ 'replied': fields.function(
+ _get_statistics, string='Replied',
+ type='integer', multi='_get_statistics',
+ ),
+ 'bounced': fields.function(
+ _get_statistics, string='Bounced',
+ type='integer', multi='_get_statistics',
+ ),
+ 'received_ratio': fields.function(
+ _get_statistics, string='Received Ratio',
+ type='integer', multi='_get_statistics',
+ ),
+ 'opened_ratio': fields.function(
+ _get_statistics, string='Opened Ratio',
+ type='integer', multi='_get_statistics',
+ ),
+ 'replied_ratio': fields.function(
+ _get_statistics, string='Replied Ratio',
+ type='integer', multi='_get_statistics',
+ ),
+ # dayly ratio
+ 'opened_dayly': fields.function(
+ _get_daily_statistics, string='Opened',
+ type='char', multi='_get_daily_statistics',
+ oldname='opened_monthly',
+ ),
+ 'replied_dayly': fields.function(
+ _get_daily_statistics, string='Replied',
+ type='char', multi='_get_daily_statistics',
+ oldname='replied_monthly',
+ )
+ }
+
+ def default_get(self, cr, uid, fields, context=None):
+ res = super(MassMailing, self).default_get(cr, uid, fields, context=context)
+ if 'reply_to_mode' in fields and not 'reply_to_mode' in res and res.get('mailing_model'):
+ if res['mailing_model'] in ['res.partner', 'mail.mass_mailing.contact']:
+ res['reply_to_mode'] = 'email'
+ else:
+ res['reply_to_mode'] = 'thread'
+ return res
+
+ _defaults = {
+ 'state': 'draft',
+ 'email_from': lambda self, cr, uid, ctx=None: self.pool['mail.message']._get_default_from(cr, uid, context=ctx),
+ 'reply_to': lambda self, cr, uid, ctx=None: self.pool['mail.message']._get_default_from(cr, uid, context=ctx),
+ 'mailing_model': 'mail.mass_mailing.contact',
+ 'contact_ab_pc': 100,
+ }
+
+ #------------------------------------------------------
+ # Technical stuff
+ #------------------------------------------------------
+
+ def copy_data(self, cr, uid, id, default=None, context=None):
+ if default is None:
+ default = {}
+ mailing = self.browse(cr, uid, id, context=context)
+ default.update({
+ 'state': 'draft',
+ 'statistics_ids': [],
+ 'name': _('%s (duplicate)') % mailing.name,
+ 'sent_date': False,
+ })
+ return super(MassMailing, self).copy_data(cr, uid, id, default, context=context)
+
+ def read_group(self, cr, uid, domain, fields, groupby, offset=0, limit=None, context=None, orderby=False, lazy=True):
+ """ Override read_group to always display all states. """
+ if groupby and groupby[0] == "state":
+ # Default result structure
+ # states = self._get_state_list(cr, uid, context=context)
+ states = [('draft', 'Draft'), ('test', 'Tested'), ('done', 'Sent')]
+ read_group_all_states = [{
+ '__context': {'group_by': groupby[1:]},
+ '__domain': domain + [('state', '=', state_value)],
+ 'state': state_value,
+ 'state_count': 0,
+ } for state_value, state_name in states]
+ # Get standard results
+ read_group_res = super(MassMailing, self).read_group(cr, uid, domain, fields, groupby, offset=offset, limit=limit, context=context, orderby=orderby)
+ # Update standard results with default results
+ result = []
+ for state_value, state_name in states:
+ res = filter(lambda x: x['state'] == state_value, read_group_res)
+ if not res:
+ res = filter(lambda x: x['state'] == state_value, read_group_all_states)
+ res[0]['state'] = [state_value, state_name]
+ result.append(res[0])
+ return result
+ else:
+ return super(MassMailing, self).read_group(cr, uid, domain, fields, groupby, offset=offset, limit=limit, context=context, orderby=orderby)
+
+ #------------------------------------------------------
+ # Views & Actions
+ #------------------------------------------------------
+
+ def on_change_model_and_list(self, cr, uid, ids, mailing_model, list_ids, context=None):
+ value = {}
+ if mailing_model == 'mail.mass_mailing.contact':
+ list_ids = map(lambda item: item if isinstance(item, (int, long)) else [lid for lid in item[2]], list_ids)
+ if list_ids:
+ value['mailing_domain'] = "[('list_id', 'in', %s)]" % list_ids
+ else:
+ value['mailing_domain'] = "[('list_id', '=', False)]"
+ else:
+ value['mailing_domain'] = False
+ return {'value': value}
+
+ def action_duplicate(self, cr, uid, ids, context=None):
+ copy_id = None
+ for mid in ids:
+ copy_id = self.copy(cr, uid, mid, context=context)
+ if copy_id:
+ return {
+ 'type': 'ir.actions.act_window',
+ 'view_type': 'form',
+ 'view_mode': 'form',
+ 'res_model': 'mail.mass_mailing',
+ 'res_id': copy_id,
+ 'context': context,
+ }
+ return False
+
+ def action_test_mailing(self, cr, uid, ids, context=None):
+ ctx = dict(context, default_mass_mailing_id=ids[0])
+ return {
+ 'name': _('Test Mailing'),
+ 'type': 'ir.actions.act_window',
+ 'view_mode': 'form',
+ 'res_model': 'mail.mass_mailing.test',
+ 'target': 'new',
+ 'context': ctx,
+ }
+
+ def action_edit_html(self, cr, uid, ids, context=None):
+ if not len(ids) == 1:
+ raise ValueError('One and only one ID allowed for this action')
+ mail = self.browse(cr, uid, ids[0], context=context)
+ url = '/website_mail/email_designer?model=mail.mass_mailing&res_id=%d&template_model=%s&enable_editor=1' % (ids[0], mail.mailing_model)
+ return {
+ 'name': _('Open with Visual Editor'),
+ 'type': 'ir.actions.act_url',
+ 'url': url,
+ 'target': 'self',
+ }
+
+ #------------------------------------------------------
+ # Email Sending
+ #------------------------------------------------------
+
+ def get_recipients(self, cr, uid, mailing, context=None):
+ domain = eval(mailing.mailing_domain)
+ res_ids = self.pool[mailing.mailing_model].search(cr, uid, domain, context=context)
+
+ # randomly choose a fragment
+ if mailing.contact_ab_pc < 100:
+ contact_nbr = self.pool[mailing.mailing_model].search(cr, uid, domain, count=True, context=context)
+ topick = int(contact_nbr / 100.0 * mailing.contact_ab_pc)
+ if mailing.mass_mailing_campaign_id and mailing.mass_mailing_campaign_id.unique_ab_testing:
+ already_mailed = self.pool['mail.mass_mailing.campaign'].get_recipients(cr, uid, [mailing.mass_mailing_campaign_id.id], context=context)[mailing.mass_mailing_campaign_id.id]
+ else:
+ already_mailed = set([])
+ remaining = set(res_ids).difference(already_mailed)
+ if topick > len(remaining):
+ topick = len(remaining)
+ res_ids = random.sample(remaining, topick)
+ return res_ids
+
+ def send_mail(self, cr, uid, ids, context=None):
+ author_id = self.pool['res.users'].browse(cr, uid, uid, context=context).partner_id.id
+ for mailing in self.browse(cr, uid, ids, context=context):
+ # instantiate an email composer + send emails
+ res_ids = self.get_recipients(cr, uid, mailing, context=context)
+ comp_ctx = dict(context, active_ids=res_ids)
+ composer_values = {
+ 'author_id': author_id,
+ 'body': mailing.body_html,
+ 'subject': mailing.name,
+ 'model': mailing.mailing_model,
+ 'email_from': mailing.email_from,
+ 'record_name': False,
+ 'composition_mode': 'mass_mail',
+ 'mass_mailing_id': mailing.id,
+ 'mailing_list_ids': [(4, l.id) for l in mailing.contact_list_ids],
+ }
+ if mailing.reply_to_mode == 'email':
+ composer_values['reply_to'] = mailing.reply_to
+ composer_id = self.pool['mail.compose.message'].create(cr, uid, composer_values, context=comp_ctx)
+ self.pool['mail.compose.message'].send_mail(cr, uid, [composer_id], context=comp_ctx)
+ self.write(cr, uid, [mailing.id], {'sent_date': fields.datetime.now(), 'state': 'done'}, context=context)
+ return True
diff --git a/addons/mass_mailing/models/mass_mailing_stats.py b/addons/mass_mailing/models/mass_mailing_stats.py
new file mode 100644
index 00000000000..319a6fde14d
--- /dev/null
+++ b/addons/mass_mailing/models/mass_mailing_stats.py
@@ -0,0 +1,109 @@
+# -*- 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/>
+#
+##############################################################################
+
+from datetime import datetime
+from dateutil import relativedelta
+import random
+try:
+ import simplejson as json
+except ImportError:
+ import json
+import urllib
+import urlparse
+
+from openerp import tools
+from openerp.exceptions import Warning
+from openerp.tools.safe_eval import safe_eval as eval
+from openerp.tools.translate import _
+from openerp.osv import osv, fields
+
+
+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'),
+ 'model': fields.char('Document model'),
+ 'res_id': fields.integer('Document ID'),
+ # 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,
+ ),
+ # Bounce and tracking
+ 'scheduled': fields.datetime('Scheduled', help='Date when the email has been created'),
+ 'sent': fields.datetime('Sent', help='Date when the email has been sent'),
+ 'exception': fields.datetime('Exception', help='Date of technical error leading to the email not being sent'),
+ 'opened': fields.datetime('Opened', help='Date when the email has been opened 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 = {
+ 'scheduled': fields.datetime.now,
+ }
+
+ def _get_ids(self, cr, uid, ids=None, mail_mail_ids=None, mail_message_ids=None, domain=None, context=None):
+ if not ids and mail_mail_ids:
+ base_domain = [('mail_mail_id', 'in', mail_mail_ids)]
+ elif not ids and mail_message_ids:
+ base_domain = [('message_id', 'in', mail_message_ids)]
+ else:
+ base_domain = [('id', 'in', ids or [])]
+ if domain:
+ base_domain = ['&'] + domain + base_domain
+ return self.search(cr, uid, base_domain, context=context)
+
+ def set_opened(self, cr, uid, ids=None, mail_mail_ids=None, mail_message_ids=None, context=None):
+ stat_ids = self._get_ids(cr, uid, ids, mail_mail_ids, mail_message_ids, [('opened', '=', False)], context)
+ self.write(cr, uid, stat_ids, {'opened': fields.datetime.now()}, context=context)
+ return stat_ids
+
+ def set_replied(self, cr, uid, ids=None, mail_mail_ids=None, mail_message_ids=None, context=None):
+ stat_ids = self._get_ids(cr, uid, ids, mail_mail_ids, mail_message_ids, [('replied', '=', False)], context)
+ self.write(cr, uid, stat_ids, {'replied': fields.datetime.now()}, context=context)
+ return stat_ids
+
+ def set_bounced(self, cr, uid, ids=None, mail_mail_ids=None, mail_message_ids=None, context=None):
+ stat_ids = self._get_ids(cr, uid, ids, mail_mail_ids, mail_message_ids, [('bounced', '=', False)], context)
+ self.write(cr, uid, stat_ids, {'bounced': fields.datetime.now()}, context=context)
+ return stat_ids
+
diff --git a/addons/mass_mailing/models/res_config.py b/addons/mass_mailing/models/res_config.py
new file mode 100644
index 00000000000..4b3757b7dca
--- /dev/null
+++ b/addons/mass_mailing/models/res_config.py
@@ -0,0 +1,15 @@
+# -*- coding: utf-8 -*-
+
+from openerp.osv import fields, osv
+
+
+class MassMailingConfiguration(osv.TransientModel):
+ _name = 'marketing.config.settings'
+ _inherit = 'marketing.config.settings'
+
+ _columns = {
+ 'group_mass_mailing_campaign': fields.boolean(
+ 'Manage Mass Mailing using Campaign',
+ implied_group='mass_mailing.group_mass_mailing_campaign',
+ help="""Manage mass mailign using Campaigns"""),
+ }
diff --git a/addons/mass_mailing/security/ir.model.access.csv b/addons/mass_mailing/security/ir.model.access.csv
index 59cf03c2c68..d5c76b9f69d 100644
--- a/addons/mass_mailing/security/ir.model.access.csv
+++ b/addons/mass_mailing/security/ir.model.access.csv
@@ -1,4 +1,8 @@
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
+access_mass_mailing_category,mail.mass_mailing.category,model_mail_mass_mailing_category,base.group_user,1,1,1,1
+access_mass_mailing_contact,mail.mass_mailing.contact,model_mail_mass_mailing_contact,base.group_user,1,1,1,1
+access_mass_mailing_list,mail.mass_mailing.list,model_mail_mass_mailing_list,base.group_user,1,1,1,1
+access_mass_mailing_stage,mail.mass_mailing.stage,model_mail_mass_mailing_stage,base.group_user,1,1,1,1
access_mass_mailing_campaign,mail.mass_mailing.campaign,model_mail_mass_mailing_campaign,base.group_user,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,mail.mass_mailing,model_mail_mass_mailing,base.group_user,1,1,1,0
diff --git a/addons/mass_mailing/static/src/css/email_template.css b/addons/mass_mailing/static/src/css/email_template.css
new file mode 100644
index 00000000000..495c201a172
--- /dev/null
+++ b/addons/mass_mailing/static/src/css/email_template.css
@@ -0,0 +1,17 @@
+.openerp .oe_kanban_email_template {
+ width: 360px;
+ min-height: 270px !important;
+}
+
+.kanban_html_preview {
+ width: 600px;
+ height: 600px;
+ -webkit-transform: scale(.50);
+ -ms-transform: scale(.50);
+ transform: scale(.50);
+ -webkit-transform-origin: 0 0;
+ -ms-transform-origin: 0 0;
+ transform-origin: 0 0;
+ margin: 0 0px -300px 0;
+ overflow: hidden !important;
+}
diff --git a/addons/mass_mailing/static/src/css/mass_mailing.css b/addons/mass_mailing/static/src/css/mass_mailing.css
index e54ce46c025..cc91e8acf58 100644
--- a/addons/mass_mailing/static/src/css/mass_mailing.css
+++ b/addons/mass_mailing/static/src/css/mass_mailing.css
@@ -1,61 +1,13 @@
-.openerp .oe_kanban_view .oe_kanban_mass_mailing.oe_kanban_mass_mailing_campaign {
- /* Customize to manage content */
- width: 552px;
- min-height: 278px !important;
- /* End of customize */
+.openerp .oe_kanban_view .oe_kanban_mass_mailing_campaign {
+ width: 280px;
+ min-height: 141px;
}
-.openerp .oe_kanban_view .oe_kanban_mass_mailing.oe_kanban_mass_mailing_segment {
- /* Customize to manage content */
- width: 282px;
- min-height: 246px !important;
- /* End of customize */
+.openerp .oe_kanban_view .oe_kanban_mass_mailing_campaign .oe_kanban_header_right {
+ float: right;
}
-.openerp .oe_kanban_view .oe_kanban_mass_mailing .oe_mail_stats {
- width: 122px; /* Manage space in between stats */
- display: inline-block;
- margin: 2px 5px 0px 5px;
- text-align: center;
- border: 1px solid rgba(0, 0, 0, 0.16);
- -webkit-border-radius: 2px;
- border-radius: 2px;
- background-color: #FFFFFF;
+.openerp .oe_kanban_view .oe_kanban_mass_mailing {
+ width: 280px;
+ min-height: 141px;
}
-
-.openerp .oe_kanban_view .oe_kanban_mass_mailing .oe_mail_result {
- font-weight: bold;
-}
-
-.openerp .oe_kanban_view .oe_kanban_mass_mailing .oe_gauge {
- width: 120px;
- height: 120px;
- display: inline-block;
-}
-
-.openerp .oe_kanban_view .oe_kanban_mass_mailing .oe_kanban_content div.oe_sparkline_container {
- height: 60px;
- width: 120px;
- display: inline-block;
- margin: 8px 5px 0px 5px;
-}
-
-.openerp .oe_kanban_view .oe_kanban_mass_mailing .oe_sparkline_bar_title {
- text-align: center;
- display: inline;
-}
-
-.openerp .oe_kanban_view .oe_kanban_mass_mailing .oe_sparkline_bar {
- width: 100px;
- height: 60px;
- display: inline-block;
-}
-
-/*
- * Campaign related CSS
- */
-
-
-/*
- * Segment related CSS
- */
diff --git a/addons/mass_mailing/static/src/js/mass_mailing.js b/addons/mass_mailing/static/src/js/mass_mailing.js
index 93f63f01ad1..ef456c40bbf 100644
--- a/addons/mass_mailing/static/src/js/mass_mailing.js
+++ b/addons/mass_mailing/static/src/js/mass_mailing.js
@@ -1,13 +1,13 @@
-openerp.mass_mailing = function(openerp) {
+openerp.mass_mailing = function (instance) {
+ var _t = instance.web._t;
openerp.web_kanban.KanbanRecord.include({
on_card_clicked: function (event) {
if (this.view.dataset.model === 'mail.mass_mailing.campaign') {
- this.$('.oe_mass_mailings a').first().click();
+ this.$('.oe_mailings').click();
} else {
this._super.apply(this, arguments);
}
},
});
-
};
diff --git a/addons/mass_mailing/views/email_template.xml b/addons/mass_mailing/views/email_template.xml
new file mode 100644
index 00000000000..cde5685abaf
--- /dev/null
+++ b/addons/mass_mailing/views/email_template.xml
@@ -0,0 +1,97 @@
+<?xml version="1.0"?>
+<openerp>
+ <data>
+
+ <!-- Email Templates -->
+ <record model="ir.ui.view" id="email_template_form_minimal">
+ <field name="name">email.template.form.minimal</field>
+ <field name="model">email.template</field>
+ <field name="priority">32</field>
+ <field name="arch" type="xml">
+ <form string="Templates" version="7.0">
+ <sheet>
+ <group>
+ <group>
+ <field name="name" required="True"/>
+ <field name="model_id" required="1" options="{'no_open': True, 'no_create': True}"
+ on_change="onchange_model_id(model_id)"
+ domain="[('model', 'in', ['res.partner', 'mail.mass_mailing.contact'])]"/>
+ <field name="model" invisible="True"/>
+ <field name="use_default_to" invisible="1"/>
+ </group>
+ <group>
+ <div class="oe_right oe_button_box" name="buttons">
+ <button name="%(email_template.wizard_email_template_preview)d" string="Preview"
+ type="action" target="new"
+ context="{'template_id':active_id}"/>
+ <br />
+ <!-- <field name="website_link" widget='html' radonly='1'
+ style='margin: 0px; padding: 0px;'/> -->
+ </div>
+ </group>
+ </group>
+ <notebook>
+ <page string="Body">
+ <field name="body_html" nolabel="1"/>
+ <field name="attachment_ids" widget="many2many_binary"/>
+ </page>
+ </notebook>
+ </sheet>
+ </form>
+ </field>
+ </record>
+
+ <record model="ir.ui.view" id="view_email_template_kanban">
+ <field name="name">email.template.kanban</field>
+ <field name="model">email.template</field>
+ <field name="arch" type="xml">
+ <kanban>
+ <field name="body_html"/>
+ <templates>
+ <t t-name="kanban-box">
+ <div t-attf-class="oe_kanban_card oe_kanban_global_click oe_kanban_email_template">
+ <div class="oe_dropdown_toggle oe_dropdown_kanban">
+ <span class="oe_e">i</span>
+ <ul class="oe_dropdown_menu">
+ <t t-if="widget.view.is_action_enabled('edit')">
+ <li><a type="edit">Edit</a></li>
+ </t>
+ <t t-if="widget.view.is_action_enabled('delete')">
+ <li><a type="delete">Delete</a></li>
+ </t>
+ </ul>
+ </div>
+ <div class="oe_kanban_content">
+ <h3>
+ <field name="name"/>
+ </h3>
+ <div class="kanban_html_preview">
+ <t t-raw="record.body_html.raw_value"/>
+ </div>
+ </div>
+ <div class="oe_clear"></div>
+ </div>
+ </t>
+ </templates>
+ </kanban>
+ </field>
+ </record>
+
+ <record model="ir.actions.act_window" id="action_email_template_marketing">
+ <field name="name">Templates</field>
+ <field name="res_model">email.template</field>
+ <field name="view_type">form</field>
+ <field name="view_mode">kanban,tree,form</field>
+ <field name="context">{
+ 'form_view_ref': 'mass_mailing.email_template_form_minimal',
+ 'default_use_default_to': True,
+}</field>
+ </record>
+
+ <!-- Add Templates in Marketing / Mass mailing menu -->
+ <menuitem name="Mail Templates" id="menu_email_template"
+ parent="mass_mailing_campaign" sequence="3"
+ action="action_email_template_marketing"/>
+
+ </data>
+</openerp>
diff --git a/addons/mass_mailing/views/mass_mailing.xml b/addons/mass_mailing/views/mass_mailing.xml
new file mode 100644
index 00000000000..7892feacacc
--- /dev/null
+++ b/addons/mass_mailing/views/mass_mailing.xml
@@ -0,0 +1,626 @@
+<?xml version="1.0"?>
+<openerp>
+ <data>
+
+ <!-- Marketing / Mass Mailing -->
+ <menuitem name="Mass Mailing" id="mass_mailing_campaign"
+ parent="base.marketing_menu" sequence="1"/>
+ <!-- Marketing / Mailing Lists -->
+ <menuitem name="Mailing Lists" id="mass_mailing_list"
+ parent="base.marketing_menu" sequence="2"/>
+ <!-- Marketing / Configuration -->
+ <menuitem name="Configuration" id="marketing_configuration"
+ parent="base.marketing_menu" sequence="99"/>
+
+ <!-- MASS MAILING CONTACT -->
+ <record model="ir.ui.view" id="view_mail_mass_mailing_contact_search">
+ <field name="name">mail.mass_mailing.contact.search</field>
+ <field name="model">mail.mass_mailing.contact</field>
+ <field name="arch" type="xml">
+ <search string="Mailing Lists Subscribers">
+ <field name="name"/>
+ <field name="email"/>
+ <field name="list_id"/>
+ <separator/>
+ <filter string="Exclude Opt Out" name="not_opt_out" domain="[('opt_out', '=', False)]"/>
+ <group expand="0" string="Group By...">
+ <filter string="Creation Date" name="group_create_date"
+ context="{'group_by': 'create_date'}"/>
+ <filter string="Mailing Lists" name="group_list_id"
+ context="{'group_by': 'list_id'}"/>
+ </group>
+ </search>
+ </field>
+ </record>
+
+ <record model="ir.ui.view" id="view_mail_mass_mailing_contact_tree">
+ <field name="name">mail.mass_mailing.contact.tree</field>
+ <field name="model">mail.mass_mailing.contact</field>
+ <field name="priority">10</field>
+ <field name="arch" type="xml">
+ <tree string="Mailing Lists Subscribers" editable="top">
+ <field name="email"/>
+ <field name="name"/>
+ <field name="list_id"/>
+ <field name="opt_out"/>
+ </tree>
+ </field>
+ </record>
+
+ <record model="ir.actions.act_window" id="action_view_mass_mailing_contacts">
+ <field name="name">Mailing List Subscribers</field>
+ <field name="res_model">mail.mass_mailing.contact</field>
+ <field name="view_type">form</field>
+ <field name="view_mode">tree</field>
+ <field name="context">{'search_default_not_opt_out': 1}</field>
+ </record>
+
+ <record model="ir.actions.act_window" id="action_view_mass_mailing_contacts_from_list">
+ <field name="name">Recipients</field>
+ <field name="res_model">mail.mass_mailing.contact</field>
+ <field name="view_type">form</field>
+ <field name="view_mode">tree</field>
+ <field name="context">{'search_default_list_id': active_id, 'search_default_not_opt_out': 1}</field>
+ <field name="help" type="html">
+ <p class="oe_view_nocontent_create">
+ Click to create a recipient.
+ </p>
+ </field>
+ </record>
+
+ <menuitem name="Contacts" id="menu_email_mass_mailing_contacts"
+ parent="mass_mailing_list" sequence="50"
+ action="action_view_mass_mailing_contacts"/>
+
+ <!-- MASS MAILING LIST -->
+ <record model="ir.ui.view" id="view_mail_mass_mailing_list_search">
+ <field name="name">mail.mass_mailing.list.search</field>
+ <field name="model">mail.mass_mailing.list</field>
+ <field name="arch" type="xml">
+ <search string="Mailing Lists">
+ <field name="name"/>
+ </search>
+ </field>
+ </record>
+
+ <record model="ir.ui.view" id="view_mail_mass_mailing_list_tree">
+ <field name="name">mail.mass_mailing.list.tree</field>
+ <field name="model">mail.mass_mailing.list</field>
+ <field name="priority">10</field>
+ <field name="arch" type="xml">
+ <tree string="Mailing Lists">
+ <field name="name"/>
+ <field name="contact_nbr"/>
+ </tree>
+ </field>
+ </record>
+
+ <record model="ir.ui.view" id="view_mail_mass_mailing_list_form">
+ <field name="name">mail.mass_mailing.list.form</field>
+ <field name="model">mail.mass_mailing.list</field>
+ <field name="arch" type="xml">
+ <form string="Contact List" version="7.0">
+ <sheet>
+ <div class="oe_right oe_button_box" name="buttons">
+ <button name="%(mass_mailing.action_view_mass_mailing_contacts_from_list)d"
+ type="action" icon="fa-user" class="oe_stat_button pull-right">
+ <field name="contact_nbr" string="Recipients" widget="statinfo"/>
+ </button>
+ </div>
+ <div class="oe_title">
+ <label for="name" class="oe_edit_only"/>
+ <h1>
+ <field name="name"/>
+ </h1>
+ </div>
+ </sheet>
+ </form>
+ </field>
+ </record>
+
+ <record model="ir.actions.act_window" id="action_view_mass_mailing_lists">
+ <field name="name">Contact Lists</field>
+ <field name="res_model">mail.mass_mailing.list</field>
+ <field name="view_type">form</field>
+ <field name="view_mode">tree,form</field>
+ <field name="help" type="html">
+<p class="oe_view_nocontent_create">
+ Click here to create a new mailing list.
+</p><p>
+ Mailing lists allows you to to manage customers and
+ contacts easily and to send to mailings in a single click.
+</p></field>
+ </record>
+
+ <menuitem name="Mailing Lists" id="menu_email_mass_mailing_lists"
+ parent="mass_mailing_list" sequence="40"
+ action="action_view_mass_mailing_lists"/>
+
+ <!-- MASS MAILING !-->
+ <record model="ir.ui.view" id="view_mail_mass_mailing_search">
+ <field name="name">mail.mass_mailing.search</field>
+ <field name="model">mail.mass_mailing</field>
+ <field name="arch" type="xml">
+ <search string="Mass Mailings">
+ <field name="name" string="Mailings"/>
+ <field name="mass_mailing_campaign_id"/>
+ <group expand="0" string="Group By...">
+ <filter string="State" name="group_state"
+ context="{'group_by': 'state'}"/>
+ <filter string="Campaign" name="group_mass_mailing_campaign_id"
+ groups="mass_mailing.group_mass_mailing_campaign"
+ context="{'group_by': 'mass_mailing_campaign_id'}"/>
+ </group>
+ </search>
+ </field>
+ </record>
+
+ <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 Mailings">
+ <field name="name"/>
+ <field name="sent"/>
+ <field name="delivered"/>
+ <field name="opened"/>
+ <field name="replied"/>
+ <field name="mass_mailing_campaign_id"
+ groups="mass_mailing.group_mass_mailing_campaign"/>
+ </tree>
+ </field>
+ </record>
+
+ <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" version="7.0">
+ <header>
+ <button name="action_test_mailing" type="object"
+ class="oe_highlight" string="Test Mailing" states="draft"/>
+ <button name="send_mail" type="object" states="draft,test"
+ class="oe_highlight" string="Send to All"/>
+ <button name="action_test_mailing" type="object" states="test,done"
+ string="Send Test Sample"/>
+ <field name="state" widget="statusbar"/>
+ </header>
+ <div class="oe_form_box_info oe_text_center" attrs="{'invisible': [('scheduled', '=', 0)]}">
+ <p><strong>
+ <field name="scheduled" class="oe_inline"/>
+ emails are in queue and will be sent soon.
+ </strong></p>
+ </div>
+ <sheet>
+ <div class="oe_button_box pull-right" attrs="{'invisible': [('state', 'in', ('draft','test'))]}">
+ <button name="%(action_view_mass_mailing_contacts)d"
+ type="action" class="oe_stat_button">
+ <field name="received_ratio" string="Received" widget="percentpie"/>
+ </button>
+ <button name="%(action_view_mass_mailing_contacts)d"
+ type="action" class="oe_stat_button">
+ <field name="opened_ratio" string="Opened" widget="percentpie"/>
+ </button>
+ <button name="%(action_view_mass_mailing_contacts)d"
+ type="action" class="oe_stat_button">
+ <field name="replied_ratio" string="Replied" widget="percentpie"/>
+ </button>
+ <button name="%(action_view_mass_mailing_contacts)d"
+ type="action" class="oe_stat_button oe_inline">
+ <field name="opened_dayly" string="Opened Daily" widget="barchart"/>
+ </button>
+ <button name="%(action_view_mass_mailing_contacts)d"
+ type="action" class="oe_stat_button oe_inline">
+ <field name="replied_dayly" string="Replied Daily" widget="barchart"/>
+ </button>
+ </div>
+ <div class="oe_button_box" attrs="{'invisible': [('total', '=', 0)]}" style="margin-bottom: 32px">
+ <button name="%(action_view_mass_mailing_contacts)d" type="action"
+ icon="fa-envelope-o" class="oe_stat_button">
+ <field name="total" string="Emails" widget="statinfo"/>
+ </button>
+ </div>
+
+ <group>
+ <field name="email_from"/>
+ <field name="name"/>
+ <label for="mailing_model" string="Recipients"/>
+ <div>
+ <field name="mailing_model" widget="radio" style="margin-bottom: 8px"
+ on_change="on_change_model_and_list(mailing_model, contact_list_ids)"/>
+
+ <field name="mailing_domain" widget="char_domain"
+ placeholder="Select recipients"
+ options="{'model_field': 'mailing_model'}"/>
+
+ <div attrs="{'invisible': [('mailing_model', '&lt;&gt;', 'mail.mass_mailing.contact')]}">
+ <label for="contact_list_ids" string="Select mailing lists:" class="oe_edit_only"/>
+ <field name="contact_list_ids" widget="many2many_tags"
+ placeholder="Select mailing lists..." class="oe_inline"
+ on_change="on_change_model_and_list(mailing_model, contact_list_ids)"/>
+ </div>
+ </div>
+ </group>
+ <notebook>
+ <page string="Mail Body">
+ <button name="action_edit_html" type="object" string="Design Email" class="oe_highlight" states="draft"/>
+ <button name="action_edit_html" type="object" string="Change Email Design" states="test"/>
+ <div attrs="{'invisible' : ['|', '|', ('state', '=', 'done'), ('body_html','!=',False), ('mailing_domain', '=', False)]}" class="oe_view_nocontent oe_clear">
+ <p class="oe_view_nocontent_create oe_edit_only">
+ Click to design your email.
+ </p>
+ </div>
+ <field name="body_html" readonly="1"/>
+ <field name="attachment_ids" widget="many2many_binary" string="Attach a file"/>
+ </page>
+ <page string="Options">
+ <group>
+ <group string="Mailing">
+ <label for="reply_to"/>
+ <div>
+ <p class="alert alert-danger"
+ attrs="{'invisible': ['|', ('reply_to_mode', '!=', 'thread'), ('mailing_model', 'not in', ['mail.mass_mailing.contact', 'res.partner'])]}">
+ This option is not available for the recipients you selected.
+ Please use a specific reply-to email address.
+ </p>
+ <field name="reply_to_mode" widget="radio"/>
+ <field name="reply_to" style="margin-left: 16px;"
+ attrs="{'required': [('reply_to_mode', '=', 'email')]}"/>
+ </div>
+ <field name="create_date" readonly="1"/>
+ <field name="sent_date" readonly="1"/>
+ </group>
+ <group string="Campaign">
+ <field name="mass_mailing_campaign_id" groups="mass_mailing.group_mass_mailing_campaign"/>
+ <label for="contact_ab_pc" groups="mass_mailing.group_mass_mailing_campaign"/>
+ <div>
+ <field name="contact_ab_pc" class="oe_inline"/> %
+ </div>
+ </group>
+ </group>
+ </page>
+ </notebook>
+ </sheet>
+ </form>
+ </field>
+ </record>
+
+ <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 default_group_by='state'>
+ <field name='color'/>
+ <field name='total'/>
+ <templates>
+ <t t-name="kanban-box">
+ <div t-attf-class="oe_kanban_color_#{kanban_getcolor(record.color.raw_value)} oe_kanban_card oe_kanban_global_click oe_kanban_mass_mailing">
+ <div class="oe_dropdown_toggle oe_dropdown_kanban">
+ <span class="oe_e">i</span>
+ <ul class="oe_dropdown_menu">
+ <t t-if="widget.view.is_action_enabled('delete')">
+ <li><a type="delete">Delete</a></li>
+ </t>
+ </ul>
+ </div>
+ <div class="oe_kanban_content">
+ <div>
+ <h3><field name="name"/></h3>
+ <h4 style="display: inline;"><field name="mass_mailing_campaign_id" groups="mass_mailing.group_mass_mailing_campaign"/></h4>
+ <t t-if="record.mass_mailing_campaign_id.raw_value" groups="mass_mailing.group_mass_mailing_campaign"> - </t><field name="sent_date"/>
+ </div>
+ <div>
+ <div style="display: inline-block">
+ <field name="delivered" widget="gauge" style="width:120px; height: 90px;"
+ options="{'max_field': 'total'}"/>
+ </div>
+ <div style="display: inline-block; vertical-align: top;">
+ <strong>Opened</strong> <field name="opened_ratio"/> %<br />
+ <strong>Replied</strong> <field name="replied_ratio"/> %
+ </div>
+ </div>
+ </div>
+ <div class="oe_clear"></div>
+ </div>
+ </t>
+ </templates>
+ </kanban>
+ </field>
+ </record>
+
+ <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>
+ <field name="help" type="html">
+<p class="oe_view_nocontent_create">
+ Click here to create a new mailing.
+</p><p>
+ Mass mailing allows you to to easily design and send mass mailings to your contacts, customers or leads using mailing lists.
+</p></field>
+ </record>
+
+ <record id="action_view_mass_mailings_from_campaign" 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>
+ <field name="context">{
+ 'search_default_mass_mailing_campaign_id': [active_id],
+ 'default_mass_mailing_campaign_id': active_id,
+ }
+ </field>
+ <field name="help" type="html">
+<p class="oe_view_nocontent_create">
+ Click here to create a new mailing.
+</p><p>
+ Mass mailing allows you to to easily design and send mass mailings to your contacts, customers or leads using mailing lists.
+</p></field>
+ </record>
+
+ <menuitem name="Mass Mailings" id="menu_email_mass_mailings"
+ parent="mass_mailing_campaign" sequence="2"
+ action="action_view_mass_mailings"/>
+
+ <!-- MASS MAILING CAMPAIGN STAGE !-->
+ <record model="ir.ui.view" id="view_mail_mass_mailing_stage_search">
+ <field name="name">mail.mass_mailing.stage.search</field>
+ <field name="model">mail.mass_mailing.stage</field>
+ <field name="arch" type="xml">
+ <search string="Mass Mailings">
+ <field name="name"/>
+ </search>
+ </field>
+ </record>
+
+ <record model="ir.ui.view" id="view_mail_mass_mailing_stage_tree">
+ <field name="name">mail.mass_mailing.stage.tree</field>
+ <field name="model">mail.mass_mailing.stage</field>
+ <field name="priority">10</field>
+ <field name="arch" type="xml">
+ <tree string="Mass Mailings" editable="top">
+ <field name="sequence" widget="handle"/>
+ <field name="name"/>
+ </tree>
+ </field>
+ </record>
+ <record id="action_view_mass_mailing_stages" model="ir.actions.act_window">
+ <field name="name">Mass Mailing Stages</field>
+ <field name="res_model">mail.mass_mailing.stage</field>
+ <field name="view_type">form</field>
+ <field name="view_mode">tree,form</field>
+ </record>
+
+ <menuitem name="Campaign Stages" id="menu_view_mass_mailing_stages"
+ parent="marketing_configuration" sequence="1"
+ groups="mass_mailing.group_mass_mailing_campaign"
+ action="action_view_mass_mailing_stages"/>
+
+ <!-- MASS MAILING CAMPAIGNS !-->
+ <record model="ir.ui.view" id="view_mail_mass_mailing_campaign_search">
+ <field name="name">mail.mass_mailing.campaign.search</field>
+ <field name="model">mail.mass_mailing.campaign</field>
+ <field name="arch" type="xml">
+ <search string="Mass Mailing Campaigns">
+ <field name="name" string="Campaigns"/>
+ <field name="category_ids"/>
+ <field name="user_id"/>
+ <group expand="0" string="Group By...">
+ <filter string="Stage" name="group_stage_id"
+ context="{'group_by': 'stage_id'}"/>
+ <filter string="Responsible" name="group_user_id"
+ context="{'group_by': 'user_id'}"/>
+ </group>
+ </search>
+ </field>
+ </record>
+
+ <record model="ir.ui.view" id="view_mail_mass_mailing_campaign_tree">
+ <field name="name">mail.mass_mailing.campaign.tree</field>
+ <field name="model">mail.mass_mailing.campaign</field>
+ <field name="priority">10</field>
+ <field name="arch" type="xml">
+ <tree string="Mass Mailing Campaigns">
+ <field name="name"/>
+ <field name="user_id"/>
+ <field name="stage_id"/>
+ <field name="category_ids"/>
+ </tree>
+ </field>
+ </record>
+
+ <record model="ir.ui.view" id="view_mail_mass_mailing_campaign_form">
+ <field name="name">mail.mass_mailing.campaign.form</field>
+ <field name="model">mail.mass_mailing.campaign</field>
+ <field name="arch" type="xml">
+ <form string="Mass Mailing Campaign" version="7.0">
+ <header>
+ <field name="stage_id" widget="statusbar" clickable="True"/>
+ </header>
+ <sheet>
+ <group>
+ <group>
+ <field name="name"/>
+ <field name="user_id"/>
+ <field name="category_ids" widget="many2many_tags"/>
+ </group>
+ <group>
+ <field name="total" invisible="1"/>
+ <div class="oe_right oe_button_box" name="buttons"
+ attrs="{'invisible': [('total', '=', 0)]}">
+ <button name="%(action_view_mass_mailing_contacts)d"
+ type="action" class="oe_stat_button oe_inline">
+ <field name="received_ratio" widget="percentpie" string="Received"/>
+ </button>
+ <button name="%(action_view_mass_mailing_contacts)d"
+ type="action" class="oe_stat_button oe_inline">
+ <field name="opened_ratio" widget="percentpie" string="Opened"/>
+ </button>
+ <button name="%(action_view_mass_mailing_contacts)d"
+ type="action" class="oe_stat_button oe_inline">
+ <field name="replied_ratio" widget="percentpie" string="Replied"/>
+ </button>
+ </div>
+ </group>
+ </group>
+ <strong>Related Mailing(s)</strong>
+ <field name="mass_mailing_ids" readonly="1" string="Related Mailing(s)">
+ <tree>
+ <field name="name"/>
+ <field name="sent_date"/>
+ <field name="state"/>
+ <field name="delivered"/>
+ <field name="opened"/>
+ <field name="replied"/>
+ <field name="bounced"/>
+ <button name="action_duplicate" type="object" string="Duplicate"/>
+ </tree>
+ </field>
+ </sheet>
+ </form>
+ </field>
+ </record>
+
+ <record model="ir.ui.view" id="view_mail_mass_mailing_campaign_kanban">
+ <field name="name">mail.mass_mailing.campaign.kanban</field>
+ <field name="model">mail.mass_mailing.campaign</field>
+ <field name="arch" type="xml">
+ <kanban default_group_by='stage_id'>
+ <field name='total'/>
+ <field name='color'/>
+ <field name='user_id'/>
+ <field name='mass_mailing_ids'/>
+ <templates>
+ <t t-name="kanban-box">
+ <div t-attf-class="oe_kanban_color_#{kanban_getcolor(record.color.raw_value)} oe_kanban_card oe_kanban_global_click oe_kanban_mass_mailing_campaign">
+ <div class="oe_dropdown_toggle oe_dropdown_kanban">
+ <span class="oe_e">i</span>
+ <ul class="oe_dropdown_menu">
+ <t t-if="widget.view.is_action_enabled('edit')">
+ <li><a type="edit">Settings</a></li>
+ </t>
+ <t t-if="widget.view.is_action_enabled('delete')">
+ <li><a type="delete">Delete</a></li>
+ </t>
+ <li><ul class="oe_kanban_colorpicker" data-field="color"/></li>
+ </ul>
+ </div>
+ <div class="oe_kanban_content">
+ <div>
+ <img t-att-src="kanban_image('res.users', 'image_small', record.user_id.raw_value)"
+ t-att-title="record.user_id.value" width="24" height="24" class="oe_kanban_avatar oe_kanban_header_right"/>
+ <h3 style="margin-bottom: 8px;"><field name="name"/></h3>
+ <field name="category_ids"/>
+ <a name="%(action_view_mass_mailings_from_campaign)d" type="action"
+ class="oe_mailings">
+ <h4 style="margin-top: 8px;"><t t-raw="record.mass_mailing_ids.raw_value.length"/> Mailings</h4>
+ </a>
+ </div>
+ <div class="oe_clear"></div>
+ <div>
+ <div style="display: inline-block">
+ <field name="delivered" widget="gauge" style="width:120px; height: 90px;"
+ options="{'max_field': 'total'}"/>
+ </div>
+ <div style="display: inline-block; vertical-align: top;">
+ <strong>Opened</strong> <field name="opened_ratio"/> %<br />
+ <strong>Replied</strong> <field name="replied_ratio"/> %
+ </div>
+ </div>
+ </div>
+ <div class="oe_clear"></div>
+ </div>
+ </t>
+ </templates>
+ </kanban>
+ </field>
+ </record>
+
+ <record id="action_view_mass_mailing_campaigns" model="ir.actions.act_window">
+ <field name="name">Mass Mailing Campaigns</field>
+ <field name="res_model">mail.mass_mailing.campaign</field>
+ <field name="view_type">form</field>
+ <field name="view_mode">kanban,tree,form</field>
+ <field name="help" type="html">
+ <p class="oe_view_nocontent_create">
+ Click to define a new mass mailing campaign.
+ </p><p>
+ Create a campaign to structure mass mailing and get analysis from email status.
+ </p>
+ </field>
+ </record>
+
+ <menuitem name="Campaigns" id="menu_email_campaigns"
+ parent="mass_mailing_campaign" sequence="1"
+ action="action_view_mass_mailing_campaigns"
+ groups="mass_mailing.group_mass_mailing_campaign"/>
+
+ <!-- MAIL MAIL STATISTICS !-->
+ <record model="ir.ui.view" id="view_mail_mail_statistics_search">
+ <field name="name">mail.mail.statistics.search</field>
+ <field name="model">mail.mail.statistics</field>
+ <field name="arch" type="xml">
+ <search string="Mail Statistics">
+ <field name="mail_mail_id"/>
+ <field name="message_id"/>
+ </search>
+ </field>
+ </record>
+
+ <record model="ir.ui.view" id="view_mail_mail_statistics_tree">
+ <field name="name">mail.mail.statistics.tree</field>
+ <field name="model">mail.mail.statistics</field>
+ <field name="arch" type="xml">
+ <tree string="Mail Statistics">
+ <field name="mail_mail_id"/>
+ <field name="message_id"/>
+ <field name="sent"/>
+ <field name="opened"/>
+ <field name="replied"/>
+ <field name="bounced"/>
+ </tree>
+ </field>
+ </record>
+
+ <record model="ir.ui.view" id="view_mail_mail_statistics_form">
+ <field name="name">mail.mail.statistics.form</field>
+ <field name="model">mail.mail.statistics</field>
+ <field name="arch" type="xml">
+ <form string="Mail Statistics" version="7.0">
+ <group>
+ <group>
+ <field name="mail_mail_id"/>
+ <field name="message_id"/>
+ <field name="exception"/>
+ <field name="sent"/>
+ <field name="opened"/>
+ <field name="replied"/>
+ <field name="bounced"/>
+ </group>
+ <group>
+ <field name="mass_mailing_id"/>
+ <field name="mass_mailing_campaign_id"/>
+ <field name="model"/>
+ <field name="res_id"/>
+ </group>
+ </group>
+ </form>
+ </field>
+ </record>
+
+ <record id="action_view_mail_mail_statistics" model="ir.actions.act_window">
+ <field name="name">Mail Statistics</field>
+ <field name="res_model">mail.mail.statistics</field>
+ <field name="view_type">form</field>
+ <field name="view_mode">tree,form</field>
+ </record>
+
+ <!-- Add in Technical/Email -->
+ <menuitem name="Mail Statistics" id="menu_email_statistics"
+ parent="base.menu_email" sequence="50"
+ action="action_view_mail_mail_statistics"/>
+
+ </data>
+</openerp>
diff --git a/addons/mass_mailing/views/res_config.xml b/addons/mass_mailing/views/res_config.xml
new file mode 100644
index 00000000000..0f61086e2cc
--- /dev/null
+++ b/addons/mass_mailing/views/res_config.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<openerp>
+ <data>
+
+ <record id="view_marketing_configuration_mass_mailing" model="ir.ui.view">
+ <field name="name">marketing.config.settings.mass.mailing</field>
+ <field name="model">marketing.config.settings</field>
+ <field name="inherit_id" ref="marketing.view_marketing_configuration"/>
+ <field name="arch" type="xml">
+ <xpath expr="//div[@name='module_mass_mailing']" position="after">
+ <div>
+ <field name="group_mass_mailing_campaign" class="oe_inline"/>
+ <label for="group_mass_mailing_campaign"/>
+ </div>
+ </xpath>
+ </field>
+ </record>
+
+ </data>
+</openerp>
diff --git a/addons/mass_mailing/views/res_partner.xml b/addons/mass_mailing/views/res_partner.xml
new file mode 100644
index 00000000000..14710cf8f37
--- /dev/null
+++ b/addons/mass_mailing/views/res_partner.xml
@@ -0,0 +1,6 @@
+<?xml version="1.0"?>
+<openerp>
+ <data>
+
+ </data>
+</openerp>
diff --git a/addons/mass_mailing/wizard/__init__.py b/addons/mass_mailing/wizard/__init__.py
index 669d12289c2..aa8074b7488 100644
--- a/addons/mass_mailing/wizard/__init__.py
+++ b/addons/mass_mailing/wizard/__init__.py
@@ -1,23 +1,4 @@
# -*- 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 test_mailing
import mail_compose_message
-import mail_mass_mailing_create_segment
diff --git a/addons/mass_mailing/wizard/mail_compose_message.py b/addons/mass_mailing/wizard/mail_compose_message.py
index 4b3f0d6b9bd..4aebff2d04d 100644
--- a/addons/mass_mailing/wizard/mail_compose_message.py
+++ b/addons/mass_mailing/wizard/mail_compose_message.py
@@ -1,23 +1,4 @@
# -*- 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/>
-#
-##############################################################################
from openerp.osv import osv, fields
@@ -29,11 +10,14 @@ class MailComposeMessage(osv.TransientModel):
_columns = {
'mass_mailing_campaign_id': fields.many2one(
- 'mail.mass_mailing.campaign', 'Mass mailing campaign',
+ 'mail.mass_mailing.campaign', 'Mass Mailing Campaign',
),
'mass_mailing_id': fields.many2one(
- 'mail.mass_mailing', 'Mass mailing',
- domain="[('mass_mailing_campaign_id', '=', mass_mailing_campaign_id)]",
+ 'mail.mass_mailing', 'Mass Mailing'
+ ),
+ 'mass_mailing_name': fields.char('Mass Mailing'),
+ 'mailing_list_ids': fields.many2many(
+ 'mail.mass_mailing.list', string='Mailing List'
),
}
@@ -42,23 +26,27 @@ class MailComposeMessage(osv.TransientModel):
mail.mail.statistics values in the o2m of mail_mail, when doing pure
email mass mailing. """
res = super(MailComposeMessage, self).get_mail_values(cr, uid, wizard, res_ids, context=context)
- if wizard.composition_mode == 'mass_mail' and wizard.mass_mailing_campaign_id: # TODO: which kind of mass mailing ?
- if wizard.mass_mailing_id:
- mass_mailing_id = wizard.mass_mailing_id.id
- else:
- current_date = fields.datetime.now()
+ # use only for allowed models in mass mailing
+ if wizard.composition_mode == 'mass_mail' and \
+ (wizard.mass_mailing_name or wizard.mass_mailing_id) and \
+ wizard.model in [item[0] for item in self.pool['mail.mass_mailing']._get_mailing_model(cr, uid, context=context)]:
+ mass_mailing = wizard.mass_mailing_id
+ if not mass_mailing:
mass_mailing_id = self.pool['mail.mass_mailing'].create(
cr, uid, {
- 'mass_mailing_campaign_id': wizard.mass_mailing_campaign_id.id,
- 'name': '%s-%s' % (wizard.mass_mailing_campaign_id.name, current_date),
- 'date': current_date,
- 'domain': wizard.active_domain,
+ 'mass_mailing_campaign_id': wizard.mass_mailing_campaign_id and wizard.mass_mailing_campaign_id.id or False,
+ 'name': wizard.mass_mailing_name,
'template_id': wizard.template_id and wizard.template_id.id or False,
+ 'state': 'done',
+ 'mailing_type': wizard.model,
+ 'mailing_domain': wizard.active_domain,
}, context=context)
+ mass_mailing = self.pool['mail.mass_mailing'].browse(cr, uid, mass_mailing_id, context=context)
for res_id in res_ids:
+ res[res_id]['mailing_id'] = mass_mailing.id
res[res_id]['statistics_ids'] = [(0, 0, {
'model': wizard.model,
'res_id': res_id,
- 'mass_mailing_id': mass_mailing_id,
+ 'mass_mailing_id': mass_mailing.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 f5dbf57a808..61cfe823006 100644
--- a/addons/mass_mailing/wizard/mail_compose_message_view.xml
+++ b/addons/mass_mailing/wizard/mail_compose_message_view.xml
@@ -9,7 +9,9 @@
<field name="inherit_id" ref="mail.email_compose_message_wizard_form"/>
<field name="arch" type="xml">
<xpath expr="//field[@name='notify']" position="after">
- <field name="mass_mailing_campaign_id"
+ <field name="mass_mailing_campaign_id" groups="mass_mailing.group_mass_mailing_campaign"
+ attrs="{'invisible': [('composition_mode', '!=', 'mass_mail')]}"/>
+ <field name="mass_mailing_name"
attrs="{'invisible': [('composition_mode', '!=', 'mass_mail')]}"/>
</xpath>
</field>
diff --git a/addons/mass_mailing/wizard/mail_mass_mailing_create_segment.py b/addons/mass_mailing/wizard/mail_mass_mailing_create_segment.py
deleted file mode 100644
index 0944e89f1de..00000000000
--- a/addons/mass_mailing/wizard/mail_mass_mailing_create_segment.py
+++ /dev/null
@@ -1,135 +0,0 @@
-# -*- 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/>
-#
-##############################################################################
-
-from openerp.osv import osv, fields
-
-from openerp.tools.translate import _
-
-
-class MailMassMailingCreate(osv.TransientModel):
- """Wizard to help creating mass mailing waves for a campaign. """
-
- _name = 'mail.mass_mailing.create'
- _description = 'Mass mailing creation'
-
- _columns = {
- 'mass_mailing_campaign_id': fields.many2one(
- 'mail.mass_mailing.campaign', 'Mass mailing campaign',
- required=True,
- ),
- 'model_id': fields.many2one(
- 'ir.model', 'Document Type',
- required=True,
- help='Document on which the mass mailing will run. This must be a '
- 'valid OpenERP model.',
- ),
- 'model_model': fields.related(
- 'model_id', 'name',
- type='char', string='Model Name'
- ),
- 'filter_id': fields.many2one(
- 'ir.filters', 'Filter',
- required=True,
- domain="[('model_id', '=', model_model)]",
- help='Filter to be applied on the document to find the records to be '
- 'mailed.',
- ),
- 'domain': fields.related(
- 'filter_id', 'domain',
- type='char', string='Domain',
- ),
- 'template_id': fields.many2one(
- 'email.template', 'Template', required=True,
- domain="[('model_id', '=', model_id)]",
- ),
- 'name': fields.char(
- 'Mailing Name', required=True,
- help='Name of the mass mailing.',
- ),
- 'mass_mailing_id': fields.many2one(
- 'mail.mass_mailing', 'Mass Mailing',
- ),
- }
-
- def _get_default_model_id(self, cr, uid, context=None):
- model_ids = self.pool['ir.model'].search(cr, uid, [('model', '=', 'res.partner')], context=context)
- return model_ids and model_ids[0] or False
-
- _defaults = {
- 'model_id': lambda self, cr, uid, ctx=None: self._get_default_model_id(cr, uid, context=ctx),
- }
-
- def on_change_model_id(self, cr, uid, ids, model_id, context=None):
- if model_id:
- model_model = self.pool['ir.model'].browse(cr, uid, model_id, context=context).model
- else:
- model_model = False
- return {'value': {'model_model': model_model}}
-
- def on_change_filter_id(self, cr, uid, ids, filter_id, context=None):
- if filter_id:
- domain = self.pool['ir.filters'].browse(cr, uid, filter_id, context=context).domain
- else:
- domain = False
- return {'value': {'domain': domain}}
-
- 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):
- 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,
- }
- 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 mailing and launch the mail.compose.message
- email composer with wizard data. """
- self.create_mass_mailing(cr, uid, ids, context=context)
-
- wizard = self.browse(cr, uid, ids[0], context=context)
- ctx = dict(context)
- ctx.update({
- 'default_composition_mode': 'mass_mail',
- 'default_template_id': wizard.template_id.id,
- 'default_use_mass_mailing_campaign': True,
- 'default_use_active_domain': True,
- 'default_model': wizard.model_id.model,
- 'default_res_id': False,
- 'default_active_domain': wizard.domain,
- 'default_mass_mailing_campaign_id': wizard.mass_mailing_campaign_id.id,
- 'default_mass_mailing_id': wizard.mass_mailing_id.id,
- })
- return {
- 'name': _('Compose Email for Mass Mailing'),
- 'type': 'ir.actions.act_window',
- 'view_type': 'form',
- 'view_mode': 'form',
- 'res_model': 'mail.compose.message',
- 'views': [(False, 'form')],
- 'view_id': False,
- 'target': 'new',
- 'context': ctx,
- }
diff --git a/addons/mass_mailing/wizard/mail_mass_mailing_create_segment.xml b/addons/mass_mailing/wizard/mail_mass_mailing_create_segment.xml
deleted file mode 100644
index b2b48fb8618..00000000000
--- a/addons/mass_mailing/wizard/mail_mass_mailing_create_segment.xml
+++ /dev/null
@@ -1,82 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<openerp>
- <data>
-
- <!-- Wizard form view -->
- <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" version="7.0">
- <group>
- <field name="model_model" invisible="1"/>
- <field name="domain" invisible="1"/>
-
- <label for="mass_mailing_campaign_id"/>
- <div>
- <field name="mass_mailing_campaign_id"/>
- <p class="oe_grey"
- attrs="{'invisible': [('mass_mailing_campaign_id', '!=', False)]}">
- Please choose a mass mailing campaign that will hold the new mailing.
- </p>
- </div>
-
- <label for="model_id"/>
- <div>
- <field name="model_id"
- on_change="on_change_model_id(model_id, context)"/>
- <p class="oe_grey"
- attrs="{'invisible': [('model_id', '!=', False)]}">
- Please choose a model on which you will run the mass mailing.
- </p>
- </div>
-
- <label for="filter_id"/>
- <div>
- <field name="filter_id"
- on_change="on_change_filter_id(filter_id, context)"/>
- <p class="oe_grey"
- attrs="{'invisible': [('filter_id', '!=', False)]}">
- Please choose a filter that will be applied on the model
- to find the records on which you will run the mass mailing.
- </p>
- </div>
-
- <label for="template_id"/>
- <div>
- <field name="template_id"/>
- <p class="oe_grey"
- attrs="{'invisible': [('template_id', '!=', False)]}">
- Please choose the template to use to render the emails
- to send.
- </p>
- </div>
-
- <label for="name"/>
- <div>
- <field name="name"/>
- <p class="oe_grey">
- Please choose the name of the mailing.
- </p>
- </div>
-
- <button name="launch_composer" type="object"
- string="Create mailing and launch email composer"/>
- </group>
- </form>
- </field>
- </record>
-
- <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>
- <field name="view_mode">form</field>
- <field name="target">new</field>
- <field name="context">{'default_mass_mailing_campaign_id': active_id}</field>
- </record>
-
- </data>
-</openerp>
diff --git a/addons/mass_mailing/wizard/test_mailing.py b/addons/mass_mailing/wizard/test_mailing.py
new file mode 100644
index 00000000000..bd1124a8cdc
--- /dev/null
+++ b/addons/mass_mailing/wizard/test_mailing.py
@@ -0,0 +1,40 @@
+# -*- coding: utf-8 -*-
+
+from openerp import tools
+from openerp.osv import osv, fields
+
+
+class TestMassMailing(osv.TransientModel):
+ _name = 'mail.mass_mailing.test'
+ _description = 'Sample Mail Wizard'
+
+ _columns = {
+ 'email_to': fields.char('Recipients', required=True,
+ help='Comma-separated list of email addresses.'),
+ 'mass_mailing_id': fields.many2one('mail.mass_mailing', 'Mailing', required=True),
+ }
+
+ _defaults = {
+ 'email_to': lambda self, cr, uid, ctx=None: self.pool['mail.message']._get_default_from(cr, uid, context=ctx),
+ }
+
+ def send_mail_test(self, cr, uid, ids, context=None):
+ Mail = self.pool['mail.mail']
+ for wizard in self.browse(cr, uid, ids, context=context):
+ mailing = wizard.mass_mailing_id
+ test_emails = tools.email_split(wizard.email_to)
+ mail_ids = []
+ for test_mail in test_emails:
+ mail_values = {
+ 'email_from': mailing.email_from,
+ 'reply_to': mailing.reply_to,
+ 'email_to': test_mail,
+ 'subject': mailing.name,
+ 'body_html': mailing.body_html,
+ 'auto_delete': True,
+ 'mailing_id': wizard.mass_mailing_id.id,
+ }
+ mail_ids.append(Mail.create(cr, uid, mail_values, context=context))
+ Mail.send(cr, uid, mail_ids, context=context)
+ self.pool['mail.mass_mailing'].write(cr, uid, [mailing.id], {'state': 'test'}, context=context)
+ return True
diff --git a/addons/mass_mailing/wizard/test_mailing.xml b/addons/mass_mailing/wizard/test_mailing.xml
new file mode 100644
index 00000000000..61ea8817bc4
--- /dev/null
+++ b/addons/mass_mailing/wizard/test_mailing.xml
@@ -0,0 +1,34 @@
+<?xml version="1.0"?>
+<openerp>
+ <data>
+
+ <record model="ir.ui.view" id="view_mail_mass_mailing_test_form">
+ <field name="name">mail.mass_mailing.test.form</field>
+ <field name="model">mail.mass_mailing.test</field>
+ <field name="arch" type="xml">
+ <form string="Send a Sample Mail" version="7.0">
+ <p class="text-muted">
+ Send a sample of this mailing to the above of email addresses for test purpose.
+ </p>
+ <group>
+ <field name="email_to"/>
+ </group>
+ <footer>
+ <button string="Send Sample Mail" name="send_mail_test" type="object" class="oe_highlight"/>
+ or
+ <button string="Cancel" class="oe_link" special="cancel" />
+ </footer>
+ </form>
+ </field>
+ </record>
+
+ <record id="action_mail_mass_mailing_test" model="ir.actions.act_window">
+ <field name="name">Mailing Test</field>
+ <field name="res_model">mail.mass_mailing.test</field>
+ <field name="view_type">form</field>
+ <field name="view_mode">form</field>
+ <field name="target">new</field>
+ </record>
+
+ </data>
+</openerp>