diff --git a/addons/account/edi/invoice_action_data.xml b/addons/account/edi/invoice_action_data.xml
index 18ac71f55ce..b3fe8362b8b 100644
--- a/addons/account/edi/invoice_action_data.xml
+++ b/addons/account/edi/invoice_action_data.xml
@@ -24,7 +24,7 @@
Invoice - Send by Email${object.user_id.email or object.company_id.email or 'noreply@localhost'}${object.company_id.name} Invoice (Ref ${object.number or 'n/a'})
- ${object.partner_id.id}
+ ${object.partner_id.id}
diff --git a/addons/crm/crm_data.xml b/addons/crm/crm_data.xml
index 613141b5818..3db7a457dfd 100644
--- a/addons/crm/crm_data.xml
+++ b/addons/crm/crm_data.xml
@@ -72,19 +72,4 @@
-
-
-
-
-
- Opportunity - Send Emails
- ${object.name}
-
-
- ${object.partner_id.id}
-
-
-
diff --git a/addons/crm/crm_lead_data.xml b/addons/crm/crm_lead_data.xml
index 724ec491efe..e335bd1b2ff 100644
--- a/addons/crm/crm_lead_data.xml
+++ b/addons/crm/crm_lead_data.xml
@@ -221,5 +221,17 @@
section_id
+
+
+ Lead/Opportunity Mass Mail
+
+
+ ${object.partner_id and object.partner_id.id}
+ ${not object.partner_id and object.email_from}
+
+
+
diff --git a/addons/crm/crm_lead_view.xml b/addons/crm/crm_lead_view.xml
index 2abd994d644..df28d240a0c 100644
--- a/addons/crm/crm_lead_view.xml
+++ b/addons/crm/crm_lead_view.xml
@@ -582,5 +582,29 @@
+
+
+
+
+
+
+
+
diff --git a/addons/email_template/email_template.py b/addons/email_template/email_template.py
index 08ef3ec6e98..147347182f5 100644
--- a/addons/email_template/email_template.py
+++ b/addons/email_template/email_template.py
@@ -146,7 +146,9 @@ class email_template(osv.osv):
help="Sender address (placeholders may be used here). If not set, the default "
"value will be the author's email alias if configured, or email address."),
'email_to': fields.char('To (Emails)', help="Comma-separated recipient addresses (placeholders may be used here)"),
- 'email_recipients': fields.char('To (Partners)', help="Comma-separated ids of recipient partners (placeholders may be used here)"),
+ 'partner_to': fields.char('To (Partners)',
+ help="Comma-separated ids of recipient partners (placeholders may be used here)",
+ oldname='email_recipients'),
'email_cc': fields.char('Cc', help="Carbon copy recipients (placeholders may be used here)"),
'reply_to': fields.char('Reply-To', help="Preferred response address (placeholders may be used here)"),
'mail_server_id': fields.many2one('ir.mail_server', 'Outgoing Mail Server', readonly=False,
@@ -314,7 +316,7 @@ class email_template(osv.osv):
template = self.get_email_template(cr, uid, template_id, res_id, context)
values = {}
for field in ['subject', 'body_html', 'email_from',
- 'email_to', 'email_recipients', 'email_cc', 'reply_to']:
+ 'email_to', 'partner_to', 'email_cc', 'reply_to']:
values[field] = self.render_template(cr, uid, getattr(template, field),
template.model, res_id, context=context) \
or False
@@ -374,7 +376,7 @@ class email_template(osv.osv):
values = self.generate_email(cr, uid, template_id, res_id, context=context)
assert 'email_from' in values, 'email_from is missing or empty after template rendering, send_mail() cannot proceed'
attachments = values.pop('attachments') or {}
- del values['email_recipients'] # TODO Properly use them.
+ del values['partner_to'] # TODO Properly use them.
msg_id = mail_mail.create(cr, uid, values, context=context)
# link attachments
attachment_ids = []
diff --git a/addons/email_template/email_template_view.xml b/addons/email_template/email_template_view.xml
index 73e796394a8..8c63a06ebe0 100644
--- a/addons/email_template/email_template_view.xml
+++ b/addons/email_template/email_template_view.xml
@@ -30,7 +30,7 @@
-
+
@@ -78,7 +78,7 @@
-
+
diff --git a/addons/email_template/res_partner_view.xml b/addons/email_template/res_partner_view.xml
index 1eab8029cb2..fbcfa8ba6ea 100644
--- a/addons/email_template/res_partner_view.xml
+++ b/addons/email_template/res_partner_view.xml
@@ -25,5 +25,35 @@
+
+
+
+ Partner Mass Mail
+
+
+ ${object.id}
+
+
+
+
+
+
+
+
+
diff --git a/addons/email_template/tests/test_mail.py b/addons/email_template/tests/test_mail.py
index b87b6d63000..56abe6a0fef 100644
--- a/addons/email_template/tests/test_mail.py
+++ b/addons/email_template/tests/test_mail.py
@@ -130,6 +130,7 @@ class test_message_compose(TestMailBase):
# 1. Mass_mail on pigs and bird, with a default_partner_ids set to check he is correctly added
context = {
'default_composition_mode': 'mass_mail',
+ 'default_notify': True,
'default_model': 'mail.group',
'default_res_id': self.group_pigs_id,
'default_template_id': email_template_id,
@@ -170,20 +171,20 @@ class test_message_compose(TestMailBase):
self.assertEqual(set(message_bird_pids), set(partner_ids), 'mail.message on bird notified_partner_ids incorrect')
# ----------------------------------------
- # CASE4: test newly introduced email_recipients field
+ # CASE4: test newly introduced partner_to field
# ----------------------------------------
# get already-created partners back
p_b_id = self.res_partner.search(cr, uid, [('email', '=', 'b@b.b')])[0]
p_c_id = self.res_partner.search(cr, uid, [('email', '=', 'c@c.c')])[0]
p_d_id = self.res_partner.search(cr, uid, [('email', '=', 'd@d.d')])[0]
- # modify template: use email_recipients, use template and email address in email_to to test all features together
+ # modify template: use partner_to, use template and email address in email_to to test all features together
user_model_id = self.registry('ir.model').search(cr, uid, [('model', '=', 'res.users')])[0]
email_template.write(cr, uid, [email_template_id], {
'model_id': user_model_id,
'body_html': '${object.login}',
'email_to': '${object.email} c@c',
- 'email_recipients': '%i,%i' % (p_b_id, p_c_id),
+ 'partner_to': '%i,%i' % (p_b_id, p_c_id),
'email_cc': 'd@d',
})
# patner by email + partner by id (no double)
diff --git a/addons/email_template/wizard/email_template_preview_view.xml b/addons/email_template/wizard/email_template_preview_view.xml
index 8dd4bc92ba7..2d7abcb4eef 100644
--- a/addons/email_template/wizard/email_template_preview_view.xml
+++ b/addons/email_template/wizard/email_template_preview_view.xml
@@ -17,7 +17,7 @@
-
+
diff --git a/addons/email_template/wizard/mail_compose_message.py b/addons/email_template/wizard/mail_compose_message.py
index 255f93998a2..2215d0705f1 100644
--- a/addons/email_template/wizard/mail_compose_message.py
+++ b/addons/email_template/wizard/mail_compose_message.py
@@ -59,14 +59,21 @@ class mail_compose_message(osv.TransientModel):
_columns = {
# incredible hack of the day: size=-1 means we want an int db column instead of an str one
'template_id': fields.selection(_get_templates, 'Template', size=-1),
+ 'partner_to': fields.char('To (Partner IDs)',
+ help="Comma-separated list of recipient partners ids (placeholders may be used here)"),
+ 'email_to': fields.char('To (Emails)',
+ help="Comma-separated recipient addresses (placeholders may be used here)",),
+ 'email_cc': fields.char('Cc (Emails)',
+ help="Carbon copy recipients (placeholders may be used here)"),
}
def onchange_template_id(self, cr, uid, ids, template_id, composition_mode, model, res_id, context=None):
""" - mass_mailing: we cannot render, so return the template values
- normal mode: return rendered values """
if template_id and composition_mode == 'mass_mail':
- values = self.pool.get('email.template').read(cr, uid, template_id, ['subject', 'body_html'], context)
- values.pop('id')
+ fields = ['subject', 'body_html', 'email_from', 'email_to', 'partner_to', 'email_cc', 'reply_to']
+ template_values = self.pool.get('email.template').read(cr, uid, template_id, fields, context)
+ values = dict((field, template_values[field]) for field in fields if template_values.get(field))
elif template_id:
# FIXME odo: change the mail generation to avoid attachment duplication
values = self.generate_email_for_composer(cr, uid, template_id, res_id, context=context)
@@ -84,7 +91,7 @@ class mail_compose_message(osv.TransientModel):
}
values['attachment_ids'].append(ir_attach_obj.create(cr, uid, data_attach, context=context))
else:
- values = self.default_get(cr, uid, ['body', 'subject', 'partner_ids', 'attachment_ids'], context=context)
+ values = self.default_get(cr, uid, ['subject', 'body', 'email_from', 'email_to', 'email_cc', 'partner_to', 'reply_to', 'attachment_ids'], context=context)
if values.get('body_html'):
values['body'] = values.pop('body_html')
@@ -117,26 +124,31 @@ class mail_compose_message(osv.TransientModel):
# Wizard validation and send
#------------------------------------------------------
+ def _get_or_create_partners_from_values(self, cr, uid, rendered_values, context=None):
+ """ Check for email_to, email_cc, partner_to """
+ partner_ids = []
+ mails = tools.email_split(rendered_values.pop('email_to', '') + ' ' + rendered_values.pop('email_cc', ''))
+ for mail in mails:
+ partner_id = self.pool.get('res.partner').find_or_create(cr, uid, mail, context=context)
+ partner_ids.append(partner_id)
+ partner_to = rendered_values.pop('partner_to', '')
+ if partner_to:
+ for partner_id in partner_to.split(','):
+ if partner_id: # placeholders could generate '', 3, 2 due to some empty field values
+ partner_ids.append(int(partner_id))
+ return partner_ids
+
def generate_email_for_composer(self, cr, uid, template_id, res_id, context=None):
""" Call email_template.generate_email(), get fields relevant for
mail.compose.message, transform email_cc and email_to into partner_ids """
template_values = self.pool.get('email.template').generate_email(cr, uid, template_id, res_id, context=context)
# filter template values
- fields = ['body_html', 'subject', 'email_to', 'email_recipients', 'email_cc', 'attachments']
+ fields = ['subject', 'body_html', 'email_from', 'email_to', 'partner_to', 'email_cc', 'reply_to', 'attachments']
values = dict((field, template_values[field]) for field in fields if template_values.get(field))
values['body'] = values.pop('body_html', '')
# transform email_to, email_cc into partner_ids
- partner_ids = set()
- mails = tools.email_split(values.pop('email_to', '') + ' ' + values.pop('email_cc', ''))
- for mail in mails:
- partner_id = self.pool.get('res.partner').find_or_create(cr, uid, mail, context=context)
- partner_ids.add(partner_id)
- email_recipients = values.pop('email_recipients', '')
- if email_recipients:
- for partner_id in email_recipients.split(','):
- if partner_id: # placeholders could generate '', 3, 2 due to some empty field values
- partner_ids.add(int(partner_id))
+ partner_ids = self._get_or_create_partners_from_values(cr, uid, values, context=context)
# legacy template behavior: void values do not erase existing values and the
# related key is removed from the values dict
if partner_ids:
@@ -153,6 +165,11 @@ class mail_compose_message(osv.TransientModel):
values = {}
# get values to return
email_dict = super(mail_compose_message, self).render_message(cr, uid, wizard, res_id, context)
+ # those values are not managed; they are readonly
+ email_dict.pop('email_to', None)
+ email_dict.pop('email_cc', None)
+ email_dict.pop('partner_to', None)
+ # update template values by wizard values
values.update(email_dict)
return values
diff --git a/addons/email_template/wizard/mail_compose_message_view.xml b/addons/email_template/wizard/mail_compose_message_view.xml
index 34a2112e1ac..1b2be68a4a2 100644
--- a/addons/email_template/wizard/mail_compose_message_view.xml
+++ b/addons/email_template/wizard/mail_compose_message_view.xml
@@ -7,6 +7,22 @@
mail.compose.message
+
+
+
+
+
+
+
+
+
+
+
+
+
Use template
diff --git a/addons/mail/mail_followers.py b/addons/mail/mail_followers.py
index dea0afafd8e..aa23e418d35 100644
--- a/addons/mail/mail_followers.py
+++ b/addons/mail/mail_followers.py
@@ -186,23 +186,19 @@ class mail_notification(osv.Model):
signature_company = self.get_signature_footer(cr, uid, user_id, res_model=msg.model, res_id=msg.res_id, context=context)
body_html = tools.append_content_to_html(body_html, signature_company, plaintext=False, container_tag='div')
- # email_from: partner-user alias or partner email or mail.message email_from
- if msg.author_id and msg.author_id.user_ids and msg.author_id.user_ids[0].alias_domain and msg.author_id.user_ids[0].alias_name:
- email_from = '%s <%s@%s>' % (msg.author_id.name, msg.author_id.user_ids[0].alias_name, msg.author_id.user_ids[0].alias_domain)
- elif msg.author_id:
- email_from = '%s <%s>' % (msg.author_id.name, msg.author_id.email)
- else:
- email_from = msg.email_from
-
mail_values = {
'mail_message_id': msg.id,
'auto_delete': True,
'body_html': body_html,
- 'email_from': email_from,
+ 'recipient_ids': [(4, id) for id in notify_partner_ids]
}
+ if msg.email_from:
+ mail_values['email_from'] = msg.email_from
+ if msg.reply_to:
+ mail_values['reply_to'] = msg.reply_to
mail_mail = self.pool.get('mail.mail')
email_notif_id = mail_mail.create(cr, uid, mail_values, context=context)
try:
- return mail_mail.send(cr, uid, [email_notif_id], recipient_ids=notify_partner_ids, context=context)
+ return mail_mail.send(cr, uid, [email_notif_id], context=context)
except Exception:
return False
diff --git a/addons/mail/mail_mail.py b/addons/mail/mail_mail.py
index a1ee03f4ef4..43d0d707699 100644
--- a/addons/mail/mail_mail.py
+++ b/addons/mail/mail_mail.py
@@ -55,22 +55,25 @@ class mail_mail(osv.Model):
help="Permanently delete this email after sending it, to save space"),
'references': fields.text('References', help='Message references, such as identifiers of previous messages', readonly=1),
'email_from': fields.char('From', help='Message sender, taken from user preferences.'),
- 'email_to': fields.text('To', help='Message recipients'),
+ 'email_to': fields.text('To', help='Message recipients (emails)'),
+ 'recipient_ids': fields.many2many('res.partner', string='To (Partners)'),
'email_cc': fields.char('Cc', help='Carbon copy message recipients'),
- 'reply_to': fields.char('Reply-To', help='Preferred response address for the message'),
'body_html': fields.text('Rich-text Contents', help="Rich-text/HTML message"),
-
+ # If not set in create values, auto-detected based on create values (res_id, model, email_from)
+ 'reply_to': fields.char('Reply-To',
+ help='Preferred response address for the message'),
# Auto-detected based on create() - if 'mail_message_id' was passed then this mail is a notification
# and during unlink() we will not cascade delete the parent and its attachments
- 'notification': fields.boolean('Is Notification')
+ 'notification': fields.boolean('Is Notification',
+ help='Mail has been created to notify people of an existing mail.message')
}
def _get_default_from(self, cr, uid, context=None):
- this = self.pool.get('res.users').browse(cr, uid, uid, context=context)
+ this = self.pool.get('res.users').browse(cr, SUPERUSER_ID, uid, context=context)
if this.alias_domain:
- return '%s@%s' % (this.alias_name, this.alias_domain)
+ return '%s <%s@%s>' % (this.name, this.alias_name, this.alias_domain)
elif this.email:
- return this.email
+ return '%s <%s>' % (this.name, this.email)
raise osv.except_osv(_('Invalid Action!'), _("Unable to send email, please configure the sender's email address or alias."))
_defaults = {
@@ -82,12 +85,41 @@ class mail_mail(osv.Model):
# protection for `default_type` values leaking from menu action context (e.g. for invoices)
# To remove when automatic context propagation is removed in web client
if context and context.get('default_type') and context.get('default_type') not in self._all_columns['type'].column.selection:
- context = dict(context, default_type = None)
+ context = dict(context, default_type=None)
return super(mail_mail, self).default_get(cr, uid, fields, context=context)
+ def _get_reply_to(self, cr, uid, values, context=None):
+ """ Return a specific reply_to: alias of the document through message_get_reply_to
+ or take the email_from
+ """
+ if values.get('reply_to'):
+ return values.get('reply_to')
+ email_reply_to = False
+
+ # if model and res_id: try to use ``message_get_reply_to`` that returns the document alias
+ if values.get('model') and values.get('res_id') and hasattr(self.pool.get(values.get('model')), 'message_get_reply_to'):
+ email_reply_to = self.pool.get(values.get('model')).message_get_reply_to(cr, uid, [values.get('res_id')], context=context)[0]
+ # no alias reply_to -> reply_to will be the email_from, only the email part
+ if not email_reply_to and values.get('email_from'):
+ emails = tools.email_split(values.get('email_from'))
+ if emails:
+ email_reply_to = emails[0]
+
+ # format 'Document name '
+ if email_reply_to and values.get('model') and values.get('res_id'):
+ document_name = self.pool.get(values.get('model')).name_get(cr, SUPERUSER_ID, [values.get('res_id')], context=context)[0]
+ if document_name:
+ email_reply_to = _('Followers of %s <%s>') % (document_name[1], email_reply_to)
+
+ return email_reply_to
+
def create(self, cr, uid, values, context=None):
+ # notification field: if not set, set if mail comes from an existing mail.message
if 'notification' not in values and values.get('mail_message_id'):
values['notification'] = True
+ # reply_to: if not set, set with default values that require creation values
+ if not values.get('reply_to'):
+ values['reply_to'] = self._get_reply_to(cr, uid, values, context=context)
return super(mail_mail, self).create(cr, uid, values, context=context)
def unlink(self, cr, uid, ids, context=None):
@@ -200,33 +232,6 @@ class mail_mail(osv.Model):
body = tools.append_content_to_html(body, body_footer, plaintext=False, container_tag='div')
return body
- def send_get_mail_reply_to(self, cr, uid, mail, partner=None, context=None):
- """ Return a specific ir_email reply_to.
-
- :param browse_record mail: mail.mail browse_record
- :param browse_record partner: specific recipient partner
- """
- if mail.reply_to:
- return mail.reply_to
- email_reply_to = False
-
- # if model and res_id: try to use ``message_get_reply_to`` that returns the document alias
- if mail.model and mail.res_id and hasattr(self.pool.get(mail.model), 'message_get_reply_to'):
- email_reply_to = self.pool.get(mail.model).message_get_reply_to(cr, uid, [mail.res_id], context=context)[0]
- # no alias reply_to -> reply_to will be the email_from, only the email part
- if not email_reply_to and mail.email_from:
- emails = tools.email_split(mail.email_from)
- if emails:
- email_reply_to = emails[0]
-
- # format 'Document name '
- if email_reply_to and mail.model and mail.res_id:
- document_name = self.pool.get(mail.model).name_get(cr, SUPERUSER_ID, [mail.res_id], context=context)[0]
- if document_name:
- email_reply_to = _('Followers of %s <%s>') % (document_name[1], email_reply_to)
-
- return email_reply_to
-
def send_get_email_dict(self, cr, uid, mail, partner=None, context=None):
""" Return a dictionary for specific email values, depending on a
partner, or generic to the whole recipients given by mail.email_to.
@@ -236,7 +241,6 @@ class mail_mail(osv.Model):
"""
body = self.send_get_mail_body(cr, uid, mail, partner=partner, context=context)
subject = self.send_get_mail_subject(cr, uid, mail, partner=partner, context=context)
- reply_to = self.send_get_mail_reply_to(cr, uid, mail, partner=partner, context=context)
body_alternative = tools.html2plaintext(body)
email_to = [partner.email] if partner else tools.email_split(mail.email_to)
return {
@@ -244,10 +248,9 @@ class mail_mail(osv.Model):
'body_alternative': body_alternative,
'subject': subject,
'email_to': email_to,
- 'reply_to': reply_to,
}
- def send(self, cr, uid, ids, auto_commit=False, recipient_ids=None, context=None):
+ def send(self, cr, uid, ids, auto_commit=False, context=None):
""" Sends the selected emails immediately, ignoring their current
state (mails that have already been sent should not be passed
unless they should actually be re-sent).
@@ -258,14 +261,10 @@ class mail_mail(osv.Model):
:param bool auto_commit: whether to force a commit of the mail status
after sending each mail (meant only for scheduler processing);
should never be True during normal transactions (default: False)
- :param list recipient_ids: specific list of res.partner recipients.
- If set, one email is sent to each partner. Its is possible to
- tune the sent email through ``send_get_mail_body`` and ``send_get_mail_subject``.
- If not specified, one email is sent to mail_mail.email_to.
:return: True
"""
ir_mail_server = self.pool.get('ir.mail_server')
- for mail in self.browse(cr, uid, ids, context=context):
+ for mail in self.browse(cr, SUPERUSER_ID, ids, context=context):
try:
# handle attachments
attachments = []
@@ -273,11 +272,10 @@ class mail_mail(osv.Model):
attachments.append((attach.datas_fname, base64.b64decode(attach.datas)))
# specific behavior to customize the send email for notified partners
email_list = []
- if recipient_ids:
- for partner in self.pool.get('res.partner').browse(cr, SUPERUSER_ID, recipient_ids, context=context):
- email_list.append(self.send_get_email_dict(cr, uid, mail, partner=partner, context=context))
- else:
+ if mail.email_to:
email_list.append(self.send_get_email_dict(cr, uid, mail, context=context))
+ for partner in mail.recipient_ids:
+ email_list.append(self.send_get_email_dict(cr, uid, mail, partner=partner, context=context))
# build an RFC2822 email.message.Message object and send it without queuing
for email in email_list:
@@ -288,7 +286,7 @@ class mail_mail(osv.Model):
body = email.get('body'),
body_alternative = email.get('body_alternative'),
email_cc = tools.email_split(mail.email_cc),
- reply_to = email.get('reply_to'),
+ reply_to = mail.reply_to,
attachments = attachments,
message_id = mail.message_id,
references = mail.references,
diff --git a/addons/mail/mail_mail_view.xml b/addons/mail/mail_mail_view.xml
index fb2b66adb36..1ff9a2a292e 100644
--- a/addons/mail/mail_mail_view.xml
+++ b/addons/mail/mail_mail_view.xml
@@ -14,31 +14,23 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
-
+
@@ -46,6 +38,8 @@
+
+
@@ -67,7 +61,7 @@
-
+
@@ -97,7 +91,7 @@
-
+
diff --git a/addons/mail/mail_message.py b/addons/mail/mail_message.py
index 2f56e620543..8442891f3f9 100644
--- a/addons/mail/mail_message.py
+++ b/addons/mail/mail_message.py
@@ -139,6 +139,8 @@ class mail_message(osv.Model):
"message, comment for other messages such as user replies"),
'email_from': fields.char('From',
help="Email address of the sender. This field is set when no matching partner is found for incoming emails."),
+ 'reply_to': fields.char('Reply-To',
+ help='Reply email address. Setting the reply_to bypasses the automatic thread creation.'),
'author_id': fields.many2one('res.partner', 'Author', select=1,
ondelete='set null',
help="Author of the message. If not set, email_from may hold an email address that did not match any partner."),
@@ -185,8 +187,9 @@ class mail_message(osv.Model):
_defaults = {
'type': 'email',
'date': lambda *a: fields.datetime.now(),
- 'author_id': lambda self, cr, uid, ctx={}: self._get_default_author(cr, uid, ctx),
+ 'author_id': lambda self, cr, uid, ctx=None: self._get_default_author(cr, uid, ctx),
'body': '',
+ 'email_from': lambda self, cr, uid, ctx=None: self.pool.get('mail.mail')._get_default_from(cr, uid, ctx),
}
#------------------------------------------------------
@@ -731,7 +734,10 @@ class mail_message(osv.Model):
if context is None:
context = {}
default_starred = context.pop('default_starred', False)
- if not values.get('message_id') and values.get('res_id') and values.get('model'):
+ # generate message_id, to redirect answers to the right discussion thread
+ if not values.get('message_id') and values.get('reply_to'):
+ values['message_id'] = tools.generate_tracking_message_id('reply_to-%(model)s' % values)
+ elif not values.get('message_id') and values.get('res_id') and values.get('model'):
values['message_id'] = tools.generate_tracking_message_id('%(res_id)s-%(model)s' % values)
elif not values.get('message_id'):
values['message_id'] = tools.generate_tracking_message_id('private')
diff --git a/addons/mail/mail_thread.py b/addons/mail/mail_thread.py
index 1d1131faccd..b8bd028289f 100644
--- a/addons/mail/mail_thread.py
+++ b/addons/mail/mail_thread.py
@@ -468,6 +468,7 @@ class mail_thread(osv.AbstractModel):
# 1. Verify if this is a reply to an existing thread
thread_references = references or in_reply_to
ref_match = thread_references and tools.reference_re.search(thread_references)
+
if ref_match:
thread_id = int(ref_match.group(1))
model = ref_match.group(2) or model
@@ -480,7 +481,10 @@ class mail_thread(osv.AbstractModel):
# Verify whether this is a reply to a private message
if in_reply_to:
- message_ids = self.pool.get('mail.message').search(cr, uid, [('message_id', '=', in_reply_to)], limit=1, context=context)
+ message_ids = self.pool.get('mail.message').search(cr, uid, [
+ ('message_id', '=', in_reply_to),
+ '!', ('message_id', 'ilike', 'reply_to')
+ ], limit=1, context=context)
if message_ids:
message = self.pool.get('mail.message').browse(cr, uid, message_ids[0], context=context)
_logger.debug('Routing mail with Message-Id %s: direct reply to a private message: %s, custom_values: %s, uid: %s',
@@ -1026,7 +1030,7 @@ class mail_thread(osv.AbstractModel):
# Create and auto subscribe the author
msg_id = mail_message.create(cr, uid, values, context=context)
message = mail_message.browse(cr, uid, msg_id, context=context)
- if message.author_id and thread_id and type != 'notification':
+ if message.author_id and thread_id and type != 'notification' and not context.get('mail_create_nosubscribe'):
self.message_subscribe(cr, uid, [thread_id], [message.author_id.id], context=context)
return msg_id
diff --git a/addons/mail/tests/test_mail_features.py b/addons/mail/tests/test_mail_features.py
index e4f340fef98..da7f2ae351c 100644
--- a/addons/mail/tests/test_mail_features.py
+++ b/addons/mail/tests/test_mail_features.py
@@ -20,7 +20,7 @@
##############################################################################
from openerp.addons.mail.tests.test_mail_base import TestMailBase
-from openerp.tools.mail import html_sanitize, append_content_to_html
+from openerp.tools.mail import html_sanitize
MAIL_TEMPLATE = """Return-Path:
To: {to}
@@ -312,7 +312,7 @@ class test_mail(TestMailBase):
self.res_users.write(cr, uid, [uid], {'signature': 'Admin', 'email': 'a@a'})
# 1 - Bert Tartopoils, with email, should receive emails for comments and emails
p_b_id = self.res_partner.create(cr, uid, {'name': 'Bert Tartopoils', 'email': 'b@b'})
- # 2 - Carine Poilvache, with email, should never receive emails
+ # 2 - Carine Poilvache, with email, should receive emails for emails
p_c_id = self.res_partner.create(cr, uid, {'name': 'Carine Poilvache', 'email': 'c@c', 'notification_email_send': 'email'})
# 3 - Dédé Grosbedon, without email, to test email verification; should receive emails for every message
p_d_id = self.res_partner.create(cr, uid, {'name': 'Dédé Grosbedon', 'notification_email_send': 'all'})
@@ -456,7 +456,7 @@ class test_mail(TestMailBase):
# 2 - Carine Poilvache, with email, should never receive emails
p_c_id = self.res_partner.create(cr, uid, {'name': 'Carine Poilvache', 'email': 'c@c', 'notification_email_send': 'email'})
# 3 - Dédé Grosbedon, without email, to test email verification; should receive emails for every message
- p_d_id = self.res_partner.create(cr, uid, {'name': 'Dédé Grosbedon', 'notification_email_send': 'all'})
+ p_d_id = self.res_partner.create(cr, uid, {'name': 'Dédé Grosbedon', 'email': 'd@d', 'notification_email_send': 'all'})
# Subscribe #1
group_pigs.message_subscribe([p_b_id])
@@ -519,7 +519,7 @@ class test_mail(TestMailBase):
# 1. mass_mail on pigs and bird
compose_id = mail_compose.create(cr, uid,
{'subject': _subject, 'body': '${object.description}'},
- {'default_composition_mode': 'mass_mail', 'default_model': 'mail.group', 'default_res_id': False,
+ {'default_composition_mode': 'mass_mail', 'default_model': 'mail.group', 'default_res_id': False, 'default_notify': True,
'active_ids': [self.group_pigs_id, group_bird_id]})
compose = mail_compose.browse(cr, uid, compose_id)
diff --git a/addons/mail/wizard/invite.py b/addons/mail/wizard/invite.py
index d3ec9a1ec14..2eb48f3d998 100644
--- a/addons/mail/wizard/invite.py
+++ b/addons/mail/wizard/invite.py
@@ -82,15 +82,15 @@ class invite_wizard(osv.osv_memory):
wizard.message = tools.append_content_to_html(wizard.message, signature_company, plaintext=False, container_tag='div')
# send mail to new followers
- for follower_id in new_follower_ids:
- mail_mail = self.pool.get('mail.mail')
- # the invite wizard should create a private message not related to any object -> no model, no res_id
- mail_id = mail_mail.create(cr, uid, {
- 'model': wizard.res_model,
- 'res_id': wizard.res_id,
- 'subject': _('Invitation to follow %s: %s') % (model_name, document.name_get()[0][1]),
- 'body_html': '%s' % wizard.message,
- 'auto_delete': True,
- }, context=context)
- mail_mail.send(cr, uid, [mail_id], recipient_ids=[follower_id], context=context)
+ # the invite wizard should create a private message not related to any object -> no model, no res_id
+ mail_mail = self.pool.get('mail.mail')
+ mail_id = mail_mail.create(cr, uid, {
+ 'model': wizard.res_model,
+ 'res_id': wizard.res_id,
+ 'subject': _('Invitation to follow %s: %s') % (model_name, document.name_get()[0][1]),
+ 'body_html': '%s' % wizard.message,
+ 'auto_delete': True,
+ 'recipient_ids': [(4, id) for id in new_follower_ids]
+ }, context=context)
+ mail_mail.send(cr, uid, [mail_id], context=context)
return {'type': 'ir.actions.act_window_close'}
diff --git a/addons/mail/wizard/mail_compose_message.py b/addons/mail/wizard/mail_compose_message.py
index 5776a8e46c5..5622d723eb9 100644
--- a/addons/mail/wizard/mail_compose_message.py
+++ b/addons/mail/wizard/mail_compose_message.py
@@ -113,6 +113,12 @@ class mail_compose_message(osv.TransientModel):
'partner_ids': fields.many2many('res.partner',
'mail_compose_message_res_partner_rel',
'wizard_id', 'partner_id', 'Additional contacts'),
+ 'post': fields.boolean('Post a copy in the document',
+ help='Post a copy of the message on the document communication history.'),
+ 'notify': fields.boolean('Notify followers',
+ help='Notify followers of the document'),
+ 'same_thread': fields.boolean('Replies in the document',
+ help='Replies to the messages will go into the selected document.'),
'attachment_ids': fields.many2many('ir.attachment',
'mail_compose_message_ir_attachments_rel',
'wizard_id', 'attachment_id', 'Attachments'),
@@ -121,9 +127,13 @@ class mail_compose_message(osv.TransientModel):
_defaults = {
'composition_mode': 'comment',
+ 'email_from': lambda self, cr, uid, ctx={}: self.pool.get('mail.mail')._get_default_from(cr, uid, context=ctx),
'body': lambda self, cr, uid, ctx={}: '',
'subject': lambda self, cr, uid, ctx={}: False,
'partner_ids': lambda self, cr, uid, ctx={}: [],
+ 'notify': lambda self, cr, uid, ctx={}: False,
+ 'post': lambda self, cr, uid, ctx={}: True,
+ 'same_thread': lambda self, cr, uid, ctx={}: True,
}
def _notify(self, cr, uid, newid, context=None):
@@ -195,6 +205,8 @@ class mail_compose_message(osv.TransientModel):
for wizard in self.browse(cr, uid, ids, context=context):
mass_mail_mode = wizard.composition_mode == 'mass_mail'
+ if mass_mail_mode: # mass mail: avoid any auto subscription because this could lead to people being follower of plenty of documents
+ context['mail_create_nosubscribe'] = True
active_model_pool = self.pool.get(wizard.model if wizard.model else 'mail.thread')
# wizard works in batch mode: [res_id] or active_ids
@@ -216,11 +228,28 @@ class mail_compose_message(osv.TransientModel):
new_attachments = email_dict.pop('attachments', [])
post_values['attachments'] += new_attachments
post_values.update(email_dict)
+ # email_from: mass mailing only can specify another email_from
+ if email_dict.get('email_from'):
+ post_values['email_from'] = email_dict.pop('email_from')
+ # replies redirection: mass mailing only
+ if not wizard.same_thread:
+ post_values['reply_to'] = email_dict.pop('reply_to')
+ # clean the context (hint: mass mailing sets some default values that
+ # could be wrongly interpreted by mail_mail)
+ context.pop('default_email_to', None)
+ context.pop('default_partner_ids', None)
# post the message
- subtype = 'mail.mt_comment'
- if is_log:
- subtype = False
- active_model_pool.message_post(cr, uid, [res_id], type='comment', subtype=subtype, context=context, **post_values)
+ if mass_mail_mode and not wizard.post:
+ post_values['recipient_ids'] = [(4, id) for id in post_values.pop('partner_ids', [])]
+ self.pool.get('mail.mail').create(cr, uid, post_values, context=context)
+ else:
+ subtype = 'mail.mt_comment'
+ if is_log or (mass_mail_mode and not wizard.notify):
+ subtype = False
+ msg_id = active_model_pool.message_post(cr, uid, [res_id], type='comment', subtype=subtype, context=context, **post_values)
+ # mass_mailing, post without notify: notify specific partners
+ if mass_mail_mode and not wizard.notify and post_values['partner_ids']:
+ self.pool.get('mail.notification')._notify(cr, uid, msg_id, post_values['partner_ids'], context=context)
return {'type': 'ir.actions.act_window_close'}
@@ -231,6 +260,8 @@ class mail_compose_message(osv.TransientModel):
return {
'subject': self.render_template(cr, uid, wizard.subject, wizard.model, res_id, context),
'body': self.render_template(cr, uid, wizard.body, wizard.model, res_id, context),
+ 'email_from': self.render_template(cr, uid, wizard.email_from, wizard.model, res_id, context),
+ 'reply_to': self.render_template(cr, uid, wizard.reply_to, wizard.model, res_id, context),
}
def render_template(self, cr, uid, template, model, res_id, context=None):
diff --git a/addons/mail/wizard/mail_compose_message_view.xml b/addons/mail/wizard/mail_compose_message_view.xml
index 00ba33a6d6f..1ca07b6a94c 100644
--- a/addons/mail/wizard/mail_compose_message_view.xml
+++ b/addons/mail/wizard/mail_compose_message_view.xml
@@ -13,25 +13,33 @@
+
+
+
+
+
+
-
-
- Followers of selected items and
-
-
+ attrs="{'invisible':[('composition_mode', '=', 'mass_mail')]}"/>
+