diff --git a/addons/email_template/__openerp__.py b/addons/email_template/__openerp__.py
index b7977660134..298754c8b8c 100644
--- a/addons/email_template/__openerp__.py
+++ b/addons/email_template/__openerp__.py
@@ -63,7 +63,7 @@ campaigns on any OpenERP document.
'wizard/mail_compose_message_view.xml',
'security/ir.model.access.csv'
],
- 'demo': ['res_partner_demo.yml'],
+ 'demo': [],
'installable': True,
'auto_install': True,
'images': ['images/1_email_account.jpeg','images/2_email_template.jpeg','images/3_emails.jpeg'],
diff --git a/addons/email_template/res_partner_demo.yml b/addons/email_template/res_partner_demo.yml
deleted file mode 100644
index 8eaf48f9e87..00000000000
--- a/addons/email_template/res_partner_demo.yml
+++ /dev/null
@@ -1,9 +0,0 @@
--
- Set opt-out to True on all demo partners
--
- !python {model: res.partner}: |
- partner_ids = self.search(cr, uid, [])
- # assume partners with an external ID come from demo data
- ext_ids = self._get_external_ids(cr, uid, partner_ids)
- ids_to_update = [k for (k,v) in ext_ids.iteritems() if v]
- self.write(cr, uid, ids_to_update, {'opt_out': True})
\ No newline at end of file
diff --git a/addons/mass_mailing/__init__.py b/addons/mass_mailing/__init__.py
index 9dadc456842..b71d26831ac 100644
--- a/addons/mass_mailing/__init__.py
+++ b/addons/mass_mailing/__init__.py
@@ -2,7 +2,7 @@
##############################################################################
#
# OpenERP, Open Source Management Solution
-# Copyright (C) 2013-today OpenERP SA ()
+# Copyright (C) 2013-Today OpenERP SA ()
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
@@ -22,5 +22,7 @@
import mass_mailing
import mail_mail
import mail_thread
+import email_template
+import res_config
import wizard
-import controllers
+import controllers
\ No newline at end of file
diff --git a/addons/mass_mailing/__openerp__.py b/addons/mass_mailing/__openerp__.py
index 92f8b1fc3e8..b21e25bc8ba 100644
--- a/addons/mass_mailing/__openerp__.py
+++ b/addons/mass_mailing/__openerp__.py
@@ -33,14 +33,18 @@ professional emails and reuse templates in a few clicks.
'depends': [
'mail',
'email_template',
+ 'marketing',
'web_kanban_gauge',
'web_kanban_sparkline',
],
'data': [
'mail_data.xml',
+ 'mass_mailing_data.xml',
'wizard/mail_compose_message_view.xml',
'wizard/mail_mass_mailing_create_segment.xml',
'mass_mailing_view.xml',
+ 'res_config_view.xml',
+ 'email_template.xml',
'security/ir.model.access.csv',
],
'js': [
@@ -48,7 +52,8 @@ 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',
diff --git a/addons/mass_mailing/email_template.py b/addons/mass_mailing/email_template.py
new file mode 100644
index 00000000000..9fb2c22e0b1
--- /dev/null
+++ b/addons/mass_mailing/email_template.py
@@ -0,0 +1,31 @@
+# -*- coding: utf-8 -*-
+
+from openerp.tools.translate import _
+from openerp.osv import osv, fields
+
+
+class EmailTemplate(osv.Model):
+ """Add the mass mailing campaign data to mail"""
+ _name = 'email.template'
+ _inherit = ['email.template']
+
+ _columns = {
+ 'use_in_mass_mailing': fields.boolean('Available for mass mailing campaigns'),
+ }
+
+ def action_new_mailing(self, cr, uid, ids, context=None):
+ ctx = dict(context)
+ ctx.update({
+ 'default_template_id': ids[0],
+ })
+ return {
+ 'name': _('Create a Mass Mailing'),
+ 'type': 'ir.actions.act_window',
+ 'view_type': 'form',
+ 'view_mode': 'form',
+ 'res_model': 'mail.mass_mailing',
+ 'views': [(False, 'form')],
+ 'view_id': False,
+ # 'target': 'new',
+ 'context': ctx,
+ }
diff --git a/addons/mass_mailing/email_template.xml b/addons/mass_mailing/email_template.xml
new file mode 100644
index 00000000000..0278223290c
--- /dev/null
+++ b/addons/mass_mailing/email_template.xml
@@ -0,0 +1,83 @@
+
+
+
+
+
+
+ email.template.form.mass.mailing
+ email.template
+
+
+
+
+
+
+
+
+
+ email.template.search.mass.mailing
+ email.template
+
+
+
+
+
+
+
+
+
+
+ email.template.kanban
+ email.template
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Templates
+ email.template
+ form
+ kanban,tree,form
+ {'search_default_use_in_mass_mailing': 1}
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/addons/mass_mailing/mass_mailing.py b/addons/mass_mailing/mass_mailing.py
index e7583d8e739..79866a6b748 100644
--- a/addons/mass_mailing/mass_mailing.py
+++ b/addons/mass_mailing/mass_mailing.py
@@ -23,10 +23,151 @@ from datetime import datetime
from dateutil import relativedelta
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'
+
+ _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
+ database. """
+ _name = 'mail.mass_mailing.contact'
+ _description = 'Mass Mailing Contact'
+
+ _columns = {
+ 'name': fields.char('Name', required=True),
+ 'email': fields.char('Email', required=True),
+ 'list_id': fields.many2one(
+ 'mail.mass_mailing.list', string='Mailing List',
+ required=True, ondelete='cascade',
+ ),
+ 'opt_out': fields.boolean('Opt Out', help='The contact has chosen not to receive news anymore from this mailing list'),
+ }
+
+
+class MassMailingList(osv.Model):
+ """Model of a contact list. """
+ _name = 'mail.mass_mailing.list'
+ _description = 'Contact List'
+
+ def default_get(self, cr, uid, fields, context=None):
+ """Override default_get to handle active_domain coming from the list view. """
+ res = super(MassMailingList, self).default_get(cr, uid, fields, context=context)
+ if 'domain' in fields and 'active_domain' in context:
+ res['domain'] = '%s' % context['active_domain']
+ res['model'] = context.get('active_model', 'res.partner')
+ return res
+
+ def _get_contact_nbr(self, cr, uid, ids, name, arg, context=None):
+ """Compute the number of contacts linked to the mailing list. """
+ results = dict.fromkeys(ids, 0)
+ for contact_list in self.browse(cr, uid, ids, context=context):
+ if contact_list.model == 'mail.mass_mailing.contact':
+ results[contact_list.id] = len(contact_list.contact_ids)
+ else:
+ domain = self.compute_domain(cr, uid, [contact_list.id], context=context)
+ results[contact_list.id] = self.pool[contact_list.model].search(cr, uid, domain, count=True, context=context)
+ return results
+
+ def _get_model_list(self, cr, uid, context=None):
+ return [
+ ('res.partner', 'Customers'),
+ ('mail.mass_mailing.contact', 'Mailing Contacts')
+ ]
+
+ # indirections for inheritance
+ _model_list = lambda self, *args, **kwargs: self._get_model_list(*args, **kwargs)
+
+ _columns = {
+ 'name': fields.char('Name', required=True),
+ 'contact_nbr': fields.function(
+ _get_contact_nbr, type='integer',
+ string='Contact Number',
+ ),
+ # contact-based list
+ 'contact_ids': fields.one2many(
+ 'mail.mass_mailing.contact', 'list_id', string='Contacts',
+ domain=[('opt_out', '=', False)],
+ ),
+ # filter-based list
+ 'model': fields.selection(
+ _model_list, type='char', required=True,
+ string='Applies To'
+ ),
+ # 'model_id': fields.many2one(
+ # 'ir.model', string='Related Model',
+ # domain="[('model', '=', model')]",
+ # ),
+ 'filter_id': fields.many2one(
+ 'ir.filters', string='Custom Filter',
+ # domain="[('model_id', '=', model_id)]",
+ ),
+ 'domain': fields.text('Domain'),
+ }
+
+ def on_change_model(self, cr, uid, ids, model, context=None):
+ values = {}
+ if model == 'mail.mass_mailing.contact':
+ values.update(domain=False, filter_id=False)
+ else:
+ values.update(filter_id=False)
+ return {'value': values}
+
+ def on_change_filter_id(self, cr, uid, ids, filter_id, context=None):
+ values = {}
+ if filter_id:
+ ir_filter = self.pool['ir.filters'].browse(cr, uid, filter_id, context=context)
+ values['domain'] = ir_filter.domain
+ else:
+ values['domain'] = False
+ return {'value': values}
+
+ def action_see_records(self, cr, uid, ids, context=None):
+ contact_list = self.browse(cr, uid, ids[0], context=context)
+ ctx = dict(context)
+ ctx['search_default_not_opt_out'] = True
+ return {
+ 'name': _('See Contact List'),
+ 'type': 'ir.actions.act_window',
+ 'view_type': 'form',
+ 'view_mode': 'tree,form',
+ 'res_model': contact_list.model,
+ 'views': [(False, 'tree'), (False, 'form')],
+ 'view_id': False,
+ 'target': 'current',
+ 'context': ctx,
+ 'domain': contact_list.domain,
+ }
+
+ def compute_domain(self, cr, uid, ids, context=None):
+ domains = []
+ for contact_list in self.browse(cr, uid, ids, context=context):
+ domain = eval(contact_list.domain)
+ if domain:
+ domain = ['&', ('opt_out', '=', False)] + domain
+ else:
+ domain = [('opt_out', '=', False)]
+ if domain is not False:
+ domains.append(domain)
+ if domains:
+ final_domain = ['|'] * (len(domains) - 1) + [leaf for dom in domains for leaf in dom]
+ else:
+ final_domain = domains
+ return final_domain
+
+
class MassMailingCampaign(osv.Model):
"""Model of mass mailing campaigns.
"""
@@ -63,6 +204,12 @@ class MassMailingCampaign(osv.Model):
results[campaign.id] = mass_mailing_results
return results
+ def _get_state_list(self, cr, uid, context=None):
+ return [('draft', 'Schedule'), ('design', 'Design'), ('done', 'Sent')]
+
+ # indirections for inheritance
+ _state = lambda self, *args, **kwargs: self._get_state_list(*args, **kwargs)
+
_columns = {
'name': fields.char(
'Campaign Name', required=True,
@@ -71,6 +218,12 @@ class MassMailingCampaign(osv.Model):
'res.users', 'Responsible',
required=True,
),
+ 'state': fields.selection(
+ _state, string='Status', required=True,
+ ),
+ 'category_id': fields.many2one(
+ 'mail.mass_mailing.category', 'Category',
+ help='Category'),
'mass_mailing_ids': fields.one2many(
'mail.mass_mailing', 'mass_mailing_campaign_id',
'Mass Mailings',
@@ -117,8 +270,42 @@ class MassMailingCampaign(osv.Model):
_defaults = {
'user_id': lambda self, cr, uid, ctx=None: uid,
+ 'state': 'draft',
}
+ #------------------------------------------------------
+ # Technical stuff
+ #------------------------------------------------------
+
+ def read_group(self, cr, uid, domain, fields, groupby, offset=0, limit=None, context=None, orderby=False):
+ """ 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)
+ 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(MassMailingCampaign, 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(MassMailingCampaign, self).read_group(cr, uid, domain, fields, groupby, offset=offset, limit=limit, context=context, orderby=orderby)
+
+ #------------------------------------------------------
+ # Actions
+ #------------------------------------------------------
+
def launch_mass_mailing_create_wizard(self, cr, uid, ids, context=None):
ctx = dict(context)
ctx.update({
@@ -142,7 +329,7 @@ class MassMailing(osv.Model):
A mass mailing is an occurence of sending emails. """
_name = 'mail.mass_mailing'
- _description = 'Wave of sending emails'
+ _description = 'Mass Mailing'
# number of periods for tracking mail_mail statistics
_period_number = 6
_order = 'date DESC'
@@ -189,9 +376,9 @@ class MassMailing(osv.Model):
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)
+ res[id]['opened_dayly'] = 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)
+ res[id]['replied_dayly'] = 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):
@@ -207,22 +394,54 @@ class MassMailing(osv.Model):
}
return results
+ def _get_mailing_type(self, cr, uid, context=None):
+ return [
+ ('res.partner', 'Customers'),
+ ('mail.mass_mailing.contact', 'Contacts')
+ ]
+
+ def _get_state_list(self, cr, uid, context=None):
+ return [('draft', 'Schedule'), ('test', 'Tested'), ('done', 'Sent')]
+
+ # indirections for inheritance
+ _mailing_type = lambda self, *args, **kwargs: self._get_mailing_type(*args, **kwargs)
+ _state = lambda self, *args, **kwargs: self._get_state_list(*args, **kwargs)
+
_columns = {
- 'name': fields.char('Name', required=True),
- 'mass_mailing_campaign_id': fields.many2one(
- 'mail.mass_mailing.campaign', 'Mass Mailing Campaign',
- ondelete='cascade', required=True,
+ 'name': fields.char('Subject', required=True),
+ 'date': fields.datetime('Date'),
+ 'state': fields.selection(
+ _state, string='Status', required=True,
),
'template_id': fields.many2one(
'email.template', 'Email Template',
+ domain=[('use_in_mass_mailing', '=', True)],
+ ondelete='set null',
+ ),
+ 'body_html': fields.related(
+ 'template_id', 'body_html', type='html',
+ string='Body', readonly='True',
+ help='Technical field: used only to display a view of the template in the form view',
+ ),
+ 'mass_mailing_campaign_id': fields.many2one(
+ 'mail.mass_mailing.campaign', 'Mass Mailing Campaign',
ondelete='set null',
),
- 'domain': fields.char('Domain'),
- 'date': fields.datetime('Date'),
'color': fields.related(
'mass_mailing_campaign_id', 'color',
type='integer', string='Color Index',
),
+ # mailing options
+ 'email_from': fields.char('From'),
+ 'email_to': fields.char('Send to Emails'),
+ 'reply_to': fields.char('Reply To'),
+ 'mailing_type': fields.selection(_mailing_type, string='Type', required=True),
+ 'contact_list_ids': fields.many2many(
+ 'mail.mass_mailing.list', 'mail_mass_mailing_list_rel',
+ string='Mailing Lists',
+ domain="[('model', '=', mailing_type)]",
+ ),
+ 'contact_nbr': fields.integer('Contact Number'),
# statistics data
'statistics_ids': fields.one2many(
'mail.mail.statistics', 'mass_mailing_id',
@@ -254,22 +473,159 @@ class MassMailing(osv.Model):
type='integer', multi='_get_statistics'
),
# monthly ratio
- 'opened_monthly': fields.function(
+ 'opened_dayly': fields.function(
_get_daily_statistics,
string='Opened',
type='char', multi='_get_daily_statistics',
+ oldname='opened_monthly',
),
- 'replied_monthly': fields.function(
+ 'replied_dayly': fields.function(
_get_daily_statistics,
string='Replied',
type='char', multi='_get_daily_statistics',
+ oldname='replied_monthly',
),
}
_defaults = {
+ 'state': 'draft',
'date': fields.datetime.now,
+ 'email_from': lambda self, cr, uid, ctx=None: self.pool['mail.message']._get_default_from(cr, uid, context=ctx),
+ 'mailing_type': 'res.partner',
}
+ #------------------------------------------------------
+ # Technical stuff
+ #------------------------------------------------------
+
+ def read_group(self, cr, uid, domain, fields, groupby, offset=0, limit=None, context=None, orderby=False):
+ """ 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)
+ 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_mailing_type(self, cr, uid, ids, mailing_type, context=None):
+ return {'value': {'contact_list_ids': []}}
+
+ def on_change_template_id(self, cr, uid, ids, template_id, context=None):
+ values = {}
+ if template_id:
+ template = self.pool['email.template'].browse(cr, uid, template_id, context=context)
+ if template.email_from:
+ values['email_from'] = template.email_from
+ if template.reply_to:
+ values['reply_to'] = template.reply_to
+ values['body_html'] = template.body_html
+ else:
+ values['email_from'] = self.pool['mail.message']._get_default_from(cr, uid, context=context)
+ values['reply_to'] = False
+ values['body_html'] = False
+ return {'value': values}
+
+ def send_mail(self, cr, uid, ids, context=None):
+ Mail = self.pool['mail.mail']
+ for mailing in self.browse(cr, uid, ids, context=context):
+ contact_list_ids = [contact_list.id for contact_list in mailing.contact_list_ids]
+
+ # contact-based list: aggregate all contacts
+ if mailing.mailing_type == 'mail.mass_mailing.list':
+ res_ids = [contact.id for contact in contact_list.contact_ids for contact_list in mailing.contact_list_ids]
+ elif mailing.mailing_type == 'res.partner':
+ domain = self.pool['mail.mass_mailing.list'].compute_domain(cr, uid, contact_list_ids, context=context)
+ print domain
+ res_ids = self.pool[contact_list.model].search(cr, uid, domain, context=context)
+
+ all_mail_values = self.pool['mail.compose.message'].generate_email_for_composer_batch(
+ cr, uid, mailing.template_id.id, res_ids,
+ context=context,
+ fields=['body_html', 'attachment_ids', 'mail_server_id']
+ )
+ for res_id, mail_values in all_mail_values.iteritems():
+ mail_values.update({
+ 'email_from': mailing.email_from,
+ 'reply_to': mailing.reply_to,
+ 'subject': mailing.name,
+ 'body_html': mail_values.get('body'),
+ 'auto_delete': True,
+ 'statistics_ids': [(0, 0, {
+ 'model': mailing.mailing_type,
+ 'res_id': res_id,
+ 'mass_mailing_id': mailing.id,
+ })]
+ })
+ m2m_attachment_ids = self.pool['mail.thread']._message_preprocess_attachments(
+ cr, uid, mail_values.pop('attachments', []),
+ mail_values.pop('attachment_ids', []),
+ 'mail.message', 0,
+ context=context)
+ mail_values['attachment_ids'] = m2m_attachment_ids
+ if mailing.mailing_type == 'mail.mass_mailing.list':
+ contact = self.pool['mail.mass_mailing.contact'].browse(cr, uid, res_id, context=context)
+ mail_values['email_to'] = '"%s" <%s>' % (contact.name, contact.email)
+ elif mailing.mailing_type == 'res.partner':
+ mail_values['recipient_ids'] = [(4, res_id)]
+ Mail.create(cr, uid, mail_values, context=context)
+ # todo: handle email_to
+ return True
+
+ def send_mail_to_myself(self, cr, uid, ids, context=None):
+ Mail = self.pool['mail.mail']
+ for mailing in self.browse(cr, uid, ids, context=context):
+ mail_values = {
+ 'email_from': mailing.email_from,
+ 'reply_to': mailing.reply_to,
+ 'email_to': self.pool['res.users'].browse(cr, uid, uid, context=context).email,
+ 'subject': mailing.name,
+ 'body_html': mailing.template_id.body_html,
+ 'auto_delete': True,
+ }
+ mail_id = Mail.create(cr, uid, mail_values, context=context)
+ Mail.send(cr, uid, [mail_id], context=context)
+ return True
+
+ def select_customers(self, cr, uid, ids, context=None):
+ sid = self.pool['ir.model.data'].xmlid_to_res_id(cr, uid, 'base.view_res_partner_filter')
+
+ aid = self.pool['ir.model.data'].xmlid_to_res_id(cr, uid, 'mass_mailing.action_partner_to_mailing_list')
+ print sid, aid
+ ctx = dict(context)
+ ctx['view_manager_highlight'] = [aid]
+ return {
+ 'name': _('Choose Customers'),
+ 'type': 'ir.actions.act_window',
+ 'view_type': 'form',
+ 'view_mode': 'tree,form',
+ 'res_model': 'res.partner',
+ # 'views': [(False, 'tree'), (False, 'form')],
+ 'view_id': False,
+ 'search_view_id': sid,
+ # 'target': 'new',
+ 'context': ctx,
+ }
+
class MailMailStats(osv.Model):
""" MailMailStats models the statistics collected about emails. Those statistics
diff --git a/addons/mass_mailing/mass_mailing_data.xml b/addons/mass_mailing/mass_mailing_data.xml
new file mode 100644
index 00000000000..6982aac566f
--- /dev/null
+++ b/addons/mass_mailing/mass_mailing_data.xml
@@ -0,0 +1,23 @@
+
+
+
+
+
+
+
+
+
+
+ Manage Mass Mailing Campaigns
+
+
+
+
+
\ No newline at end of file
diff --git a/addons/mass_mailing/mass_mailing_demo.xml b/addons/mass_mailing/mass_mailing_demo.xml
index 5a2388846e4..3b440aab84c 100644
--- a/addons/mass_mailing/mass_mailing_demo.xml
+++ b/addons/mass_mailing/mass_mailing_demo.xml
@@ -8,16 +8,153 @@
${object.id}
- Hello
]]>
+ ]]>
+
+
+
+
+
+
+ A Punchy Headline
+ |
+
+
+ |
+
+
+
+
+
+ A Small Subtitle for ${object.name}
+
+
+
+ Choose a vibrant image and write an inspiring paragraph about it. It does not have to be long, but it should reinforce your image.
+ |
+
+
+
+
+
+
+
+
+ |
+ |
+
+
+
+ Feature One
+
+ Choose a vibrant image and write an inspiring paragraph about it. It does not have to be long, but it should reinforce your image.
+ |
+
+ Feature Two
+
+ Choose a vibrant image and write an inspiring paragraph about it. It does not have to be long, but it should reinforce your image.
+ |
+
+
+
+
]]>
+
Partner Newsletter 2
${object.id}
- Hello]]>
+ ]]>
+
+
+
+
+
+
+
+ Sell Online. Easily.
+
+ Write one sentence to convince visitor about your message.
+
+ |
+
+
+
+
+
+
+
+
+
+
+ |
+ |
+ |
+
+
+
+ Feature One
+
+ Choose a vibrant image and write an inspiring paragraph about it. It does not have to be long, but it should reinforce your image.
+ |
+
+ Feature Two
+
+ Choose a vibrant image and write an inspiring paragraph about it. It does not have to be long, but it should reinforce your image.
+ |
+
+ Feature Three
+
+ Choose a vibrant image and write an inspiring paragraph about it. It does not have to be long, but it should reinforce your image.
+ |
+
+
+
+
]]>
+
+
+ Imported Contacts
+ mail.mass_mailing.contact
+
+
+ Customers
+ res.partner
+
+ [('customer', '=', True)]
+
+
+
+
+ Aristide Antario
+ aa@example.com
+
+
+
+ Beverly Bridge
+ bb@example.com
+
+
+
+ Carol Cartridge
+ cc@example.com
+
+
+
+
+
Partners Newsletter
@@ -25,12 +162,14 @@
First Newsletter
+ done
-
+
Second Newsletter
+ test
@@ -63,20 +202,5 @@
-
-
- 1111005@OpenERP.com
-
-
-
-
- 1111006@OpenERP.com
-
-
-
-
- 1111007@OpenERP.com
-
-
diff --git a/addons/mass_mailing/mass_mailing_view.xml b/addons/mass_mailing/mass_mailing_view.xml
index e8848fba44f..b9549ebd176 100644
--- a/addons/mass_mailing/mass_mailing_view.xml
+++ b/addons/mass_mailing/mass_mailing_view.xml
@@ -2,6 +2,147 @@
+
+
+
+
+
+
+
+ mail.mass_mailing.contact.search
+ mail.mass_mailing.contact
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ mail.mass_mailing.contact.tree
+ mail.mass_mailing.contact
+ 10
+
+
+
+
+
+
+
+
+
+
+
+ mail.mass_mailing.contact.form
+ mail.mass_mailing.contact
+
+
+
+
+
+
+ Mass Mailing Contacts
+ mail.mass_mailing.contact
+ form
+ tree,form
+ {'search_default_not_opt_out': 1}
+
+
+
+
+
+
+ mail.mass_mailing.list.search
+ mail.mass_mailing.list
+
+
+
+
+
+
+
+
+
+ mail.mass_mailing.list.tree
+ mail.mass_mailing.list
+ 10
+
+
+
+
+
+
+
+
+
+
+ mail.mass_mailing.list.form
+ mail.mass_mailing.list
+
+
+
+
+
+
+ Contact Lists
+ mail.mass_mailing.list
+ form
+ tree,form
+
+
+
+
mail.mass_mailing.search
@@ -13,6 +154,7 @@
@@ -32,7 +174,8 @@
-
+
@@ -43,19 +186,51 @@
mail.mass_mailing
@@ -76,7 +251,7 @@
mail.mass_mailing.kanban
mail.mass_mailing
-
+
@@ -87,8 +262,9 @@
- Sent:
- Campaign:
+ Sent:
+ Campaign:
+
@@ -147,6 +323,10 @@
+
+
mail.mass_mailing.campaign.search
@@ -154,10 +334,13 @@
+
-
+
@@ -171,6 +354,8 @@
+
+
@@ -182,14 +367,16 @@
@@ -212,7 +410,7 @@
mail.mass_mailing.campaign.kanban
mail.mass_mailing.campaign
-
+
@@ -299,6 +497,11 @@
+
+
mail.mail.statistics.search
@@ -357,23 +560,25 @@
tree,form
-
-
-
-
-
-
-
-
+
+
+
+
+
diff --git a/addons/mass_mailing/res_config.py b/addons/mass_mailing/res_config.py
new file mode 100644
index 00000000000..4b3757b7dca
--- /dev/null
+++ b/addons/mass_mailing/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/res_config_view.xml b/addons/mass_mailing/res_config_view.xml
new file mode 100644
index 00000000000..5ebcdb1b3e2
--- /dev/null
+++ b/addons/mass_mailing/res_config_view.xml
@@ -0,0 +1,20 @@
+
+
+
+
+
+ marketing.config.settings.mass.mailing
+ marketing.config.settings
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/addons/mass_mailing/security/ir.model.access.csv b/addons/mass_mailing/security/ir.model.access.csv
index 59cf03c2c68..41539617d98 100644
--- a/addons/mass_mailing/security/ir.model.access.csv
+++ b/addons/mass_mailing/security/ir.model.access.csv
@@ -1,4 +1,7 @@
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_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..62766ac15b4
--- /dev/null
+++ b/addons/mass_mailing/static/src/css/email_template.css
@@ -0,0 +1,19 @@
+.openerp .oe_kanban_email_template {
+ width: 360px;
+ min-height: 270px !important;
+}
+
+
+.html_preview {
+ width: 600px;
+ height: 400px;
+ -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;
+ border: 2px solid gray;
+ margin: 0 0px -300px 0;
+ overflow: hidden !important;
+}