def message_get_reply_to(self, cr, uid, ids, context=None):
""" Override to get the reply_to of the parent project. """
- return [lead.section_id.message_get_reply_to()[0] if lead.section_id else False
- for lead in self.browse(cr, SUPERUSER_ID, ids, context=context)]
+ leads = self.browse(cr, SUPERUSER_ID, ids, context=context)
+ section_ids = set([lead.section_id.id for lead in leads if lead.section_id])
+ aliases = self.pool['crm.case.section'].message_get_reply_to(cr, uid, list(section_ids), context=context)
+ return dict((lead.id, aliases.get(lead.section_id and lead.section_id.id or 0, False)) for lead in leads)
def get_formview_id(self, cr, uid, id, context=None):
obj = self.browse(cr, uid, id, context=context)
email_template.send_mail(cr, uid, email_template_id, self.group_pigs_id, force_send=True, context=context)
sent_emails = self._build_email_kwargs_list
email_to_lst = [
- ['b@b.b', 'c@c.c'], ['Administrator <admin@yourcompany.example.com>'],
- ['Raoul Grosbedon <raoul@raoul.fr>'], ['Bert Tartignole <bert@bert.fr>']]
+ ['b@b.b', 'c@c.c'], ['"Administrator" <admin@yourcompany.example.com>'],
+ ['"Raoul Grosbedon" <raoul@raoul.fr>'], ['"Bert Tartignole" <bert@bert.fr>']]
self.assertEqual(len(sent_emails), 4, 'email_template: send_mail: 3 valid email recipients + email_to -> should send 4 emails')
for email in sent_emails:
self.assertIn(email['email_to'], email_to_lst, 'email_template: send_mail: wrong email_recipients')
##############################################################################
from datetime import datetime
+
+from openerp import SUPERUSER_ID
from openerp.osv import fields, osv
from openerp.tools.translate import _
action['domain'] = str(['&', ('res_model', '=', self._name), ('res_id', 'in', ids)])
return action
+ def message_get_reply_to(self, cr, uid, ids, context=None):
+ """ Override to get the reply_to of the parent project. """
+ applicants = self.browse(cr, SUPERUSER_ID, ids, context=context)
+ job_ids = set([applicant.job_id.id for applicant in applicants if applicant.job_id])
+ aliases = self.pool['project.project'].message_get_reply_to(cr, uid, list(job_ids), context=context)
+ return dict((applicant.id, aliases.get(applicant.job_id and applicant.job_id.id or 0, False)) for applicant in applicants)
+
def message_get_suggested_recipients(self, cr, uid, ids, context=None):
recipients = super(hr_applicant, self).message_get_suggested_recipients(cr, uid, ids, context=context)
for applicant in self.browse(cr, uid, ids, context=context):
- if 'partner' and mail is a notification on a document: followers (Followers of 'Doc' <email>)
- elif 'partner', no notificatoin or no doc: recipient specific (Partner Name <email>)
- else fallback on mail.email_to splitting """
- if partner and mail.notification and mail.record_name:
- sanitized_record_name = re.sub(r'[^\w+.]+', '-', mail.record_name)
- email_to = [_('"Followers of %s" <%s>') % (sanitized_record_name, partner.email)]
- elif partner:
- email_to = ['%s <%s>' % (partner.name, partner.email)]
+ if partner:
+ email_to = ['"%s" <%s>' % (partner.name, partner.email)]
else:
email_to = tools.email_split(mail.email_to)
return email_to
##############################################################################
import logging
-import re
from openerp import tools
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.'),
+ 'same_thread': fields.boolean('Same thread',
+ help='Redirect answers to the same discussion thread.'),
'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."),
'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._get_default_from(cr, uid, ctx),
+ 'same_thread': True,
}
#------------------------------------------------------
""" Return a specific reply_to: alias of the document through message_get_reply_to
or take the email_from
"""
- email_reply_to = None
-
- ir_config_parameter = self.pool.get("ir.config_parameter")
- catchall_domain = ir_config_parameter.get_param(cr, uid, "mail.catchall.domain", context=context)
-
- # model, res_id, email_from: comes from values OR related message
model, res_id, email_from = values.get('model'), values.get('res_id'), values.get('email_from')
-
- # if model and res_id: try to use ``message_get_reply_to`` that returns the document alias
- if not email_reply_to and model and res_id and catchall_domain and hasattr(self.pool[model], 'message_get_reply_to'):
- email_reply_to = self.pool[model].message_get_reply_to(cr, uid, [res_id], context=context)[0]
- # no alias reply_to -> catchall alias
- if not email_reply_to and catchall_domain:
- catchall_alias = ir_config_parameter.get_param(cr, uid, "mail.catchall.alias", context=context)
- if catchall_alias:
- email_reply_to = '%s@%s' % (catchall_alias, catchall_domain)
- # still no reply_to -> reply_to will be the email_from
- if not email_reply_to and email_from:
- email_reply_to = email_from
-
- # format 'Document name <email_address>'
- if email_reply_to and model and res_id:
- emails = tools.email_split(email_reply_to)
- if emails:
- email_reply_to = emails[0]
- document_name = self.pool[model].name_get(cr, SUPERUSER_ID, [res_id], context=context)[0]
- if document_name:
- # sanitize document name
- sanitized_doc_name = re.sub(r'[^\w+.]+', '-', document_name[1])
- # generate reply to
- email_reply_to = _('"Followers of %s" <%s>') % (sanitized_doc_name, email_reply_to)
-
- return email_reply_to
+ ctx = dict(context, thread_model=model)
+ return self.pool['mail.thread'].message_get_reply_to(cr, uid, [res_id], default=email_from, context=ctx)[res_id]
def _get_message_id(self, cr, uid, values, context=None):
- if values.get('reply_to'):
+ if values.get('same_thread', True) is False:
message_id = tools.generate_tracking_message_id('reply_to')
elif values.get('res_id') and values.get('model'):
message_id = tools.generate_tracking_message_id('%(res_id)s-%(model)s' % values)
from lxml import etree
import logging
import pytz
+import re
import socket
import time
import xmlrpclib
res[record.id] = {'partner_ids': list(recipient_ids), 'email_to': email_to, 'email_cc': email_cc}
return res
- def message_get_reply_to(self, cr, uid, ids, context=None):
+ def message_get_reply_to(self, cr, uid, ids, default=None, context=None):
""" Returns the preferred reply-to email address that is basically
the alias of the document, if it exists. """
- if not self._inherits.get('mail.alias'):
- return [False for id in ids]
- return ["%s@%s" % (record.alias_name, record.alias_domain)
- if record.alias_domain and record.alias_name else False
- for record in self.browse(cr, SUPERUSER_ID, ids, context=context)]
+ if context is None:
+ context = {}
+ model_name = context.get('thread_model') or self._name
+ alias_domain = self.pool['ir.config_parameter'].get_param(cr, uid, "mail.catchall.domain", context=context)
+ res = dict.fromkeys(ids, False)
+
+ # alias domain: check for aliases and catchall
+ aliases = {}
+ doc_names = {}
+ if alias_domain:
+ if model_name and model_name != 'mail.thread':
+ alias_ids = self.pool['mail.alias'].search(
+ cr, SUPERUSER_ID, [
+ ('alias_parent_model_id.model', '=', model_name),
+ ('alias_parent_thread_id', 'in', ids),
+ ('alias_name', '!=', False)
+ ], context=context)
+ aliases.update(
+ dict((alias.alias_parent_thread_id, '%s@%s' % (alias.alias_name, alias_domain))
+ for alias in self.pool['mail.alias'].browse(cr, SUPERUSER_ID, alias_ids, context=context)))
+ doc_names.update(
+ dict((ng_res[0], ng_res[1])
+ for ng_res in self.pool[model_name].name_get(cr, SUPERUSER_ID, aliases.keys(), context=context)))
+ # left ids: use catchall
+ left_ids = set(ids).difference(set(aliases.keys()))
+ if left_ids:
+ catchall_alias = self.pool['ir.config_parameter'].get_param(cr, uid, "mail.catchall.alias", context=context)
+ if catchall_alias:
+ aliases.update(dict((res_id, '%s@%s' % (catchall_alias, alias_domain)) for res_id in left_ids))
+ # compute name of reply-to
+ company_name = self.pool['res.users'].browse(cr, SUPERUSER_ID, uid, context=context).company_id.name
+ res.update(
+ dict((res_id, '"%(company_name)s%(document_name)s" <%(email)s>' %
+ {'company_name': company_name,
+ 'document_name': doc_names.get(res_id) and ' ' + re.sub(r'[^\w+.]+', '-', doc_names[res_id]) or '',
+ 'email': aliases[res_id]
+ } or False) for res_id in aliases.keys()))
+ left_ids = set(ids).difference(set(aliases.keys()))
+ if left_ids and default:
+ res.update(dict((res_id, default) for res_id in left_ids))
+ return res
def message_get_email_values(self, cr, uid, id, notif_mail=None, context=None):
""" Get specific notification email values to store on the notification
'message_post: mail.mail notifications should have been auto-deleted!')
# Test: notifications emails: to a and b, c is email only, r is author
- # test_emailto = ['Administrator <a@a>', 'Bert Tartopoils <b@b>']
- test_emailto = ['"Followers of -Pigs-" <a@a>', '"Followers of -Pigs-" <b@b>']
+ test_emailto = ['"Administrator" <a@a>', '"Bert Tartopoils" <b@b>']
+ # test_emailto = ['"Followers of -Pigs-" <a@a>', '"Followers of -Pigs-" <b@b>']
self.assertEqual(len(sent_emails), 2,
'message_post: notification emails wrong number of send emails')
self.assertEqual(set([m['email_to'][0] for m in sent_emails]), set(test_emailto),
'message_post: notification email sent to more than one email address instead of a precise partner')
self.assertIn(sent_email['email_to'][0], test_emailto,
'message_post: notification email email_to incorrect')
- self.assertEqual(sent_email['reply_to'], '"Followers of -Pigs-" <group+pigs@schlouby.fr>',
+ self.assertEqual(sent_email['reply_to'], '"YourCompany -Pigs-" <group+pigs@schlouby.fr>',
'message_post: notification email reply_to incorrect')
self.assertEqual(_subject, sent_email['subject'],
'message_post: notification email subject incorrect')
self.assertFalse(self.mail_mail.search(cr, uid, [('mail_message_id', '=', msg2_id)]), 'mail.mail notifications should have been auto-deleted!')
# Test: emails send by server (to a, b, c, d)
- # test_emailto = [u'Administrator <a@a>', u'Bert Tartopoils <b@b>', u'Carine Poilvache <c@c>', u'D\xe9d\xe9 Grosbedon <d@d>']
- test_emailto = [u'"Followers of Pigs" <a@a>', u'"Followers of Pigs" <b@b>', u'"Followers of Pigs" <c@c>', u'"Followers of Pigs" <d@d>']
+ test_emailto = [u'"Administrator" <a@a>', u'"Bert Tartopoils" <b@b>', u'"Carine Poilvache" <c@c>', u'"D\xe9d\xe9 Grosbedon" <d@d>']
+ # test_emailto = [u'"Followers of Pigs" <a@a>', u'"Followers of Pigs" <b@b>', u'"Followers of Pigs" <c@c>', u'"Followers of Pigs" <d@d>']
# self.assertEqual(len(sent_emails), 3, 'sent_email number of sent emails incorrect')
for sent_email in sent_emails:
self.assertEqual(sent_email['email_from'], 'Raoul Grosbedon <r@r>',
alias_domain = 'schlouby.fr'
raoul_from = 'Raoul Grosbedon <raoul@raoul.fr>'
raoul_from_alias = 'Raoul Grosbedon <raoul@schlouby.fr>'
- raoul_reply = '"Followers of Pigs" <raoul@raoul.fr>'
- raoul_reply_alias = '"Followers of Pigs" <group+pigs@schlouby.fr>'
+ raoul_reply_alias = '"YourCompany Pigs" <group+pigs@schlouby.fr>'
# --------------------------------------------------
# Case1: without alias_domain
self.registry('ir.config_parameter').unlink(cr, uid, param_ids)
# Do: free message; specified values > default values
- msg_id = self.mail_message.create(cr, user_raoul_id, {'reply_to': reply_to1, 'email_from': email_from1})
+ msg_id = self.mail_message.create(cr, user_raoul_id, {'same_thread': False, 'reply_to': reply_to1, 'email_from': email_from1})
msg = self.mail_message.browse(cr, user_raoul_id, msg_id)
# Test: message content
self.assertIn('reply_to', msg.message_id,
'mail_message: message_id should contain model')
self.assertIn('%s' % self.group_pigs_id, msg.message_id,
'mail_message: message_id should contain res_id')
- self.assertEqual(msg.reply_to, raoul_reply,
+ self.assertEqual(msg.reply_to, raoul_from,
'mail_message: incorrect reply_to: should be Raoul')
self.assertEqual(msg.email_from, raoul_from,
'mail_message: incorrect email_from: should be Raoul')
msg_id = self.mail_message.create(cr, user_raoul_id, {})
msg = self.mail_message.browse(cr, user_raoul_id, msg_id)
# Test: generated reply_to
- self.assertEqual(msg.reply_to, 'gateway@schlouby.fr',
+ self.assertEqual(msg.reply_to, '"YourCompany" <gateway@schlouby.fr>',
'mail_mail: reply_to should equal the catchall email alias')
# Do: create a mail_mail
# mass mode options
'notify': fields.boolean('Notify followers',
help='Notify followers of the document (mass post only)'),
- 'same_thread': fields.boolean('Replies in the document',
- help='Replies to the messages will go into the selected document (mass mail only)'),
}
- #TODO change same_thread to False in trunk (Require view update)
_defaults = {
'composition_mode': 'comment',
'body': lambda self, cr, uid, ctx={}: '',
'subject': lambda self, cr, uid, ctx={}: False,
'partner_ids': lambda self, cr, uid, ctx={}: [],
- 'same_thread': True,
}
def check_access_rule(self, cr, uid, ids, operation, context=None):
# render all template-based value at once
if mass_mail_mode and wizard.model:
rendered_values = self.render_message_batch(cr, uid, wizard, res_ids, context=context)
+ # compute alias-based reply-to in batch
+ reply_to_value = dict.fromkeys(res_ids, None)
+ if mass_mail_mode and wizard.same_thread:
+ reply_to_value = self.pool['mail.thread'].message_get_reply_to(cr, uid, res_ids, default=wizard.email_from, context=dict(context, thread_model=wizard.model))
for res_id in res_ids:
# static wizard (mail.message) values
mail_values.update(email_dict)
if wizard.same_thread:
mail_values.pop('reply_to')
- elif not mail_values.get('reply_to'):
+ if reply_to_value.get(res_id):
+ mail_values['reply_to'] = reply_to_value[res_id]
+ if not wizard.same_thread and not mail_values.get('reply_to'):
mail_values['reply_to'] = mail_values['email_from']
# mail_mail values: body -> body_html, partner_ids -> recipient_ids
mail_values['body_html'] = mail_values.get('body', '')
def message_get_reply_to(self, cr, uid, ids, context=None):
""" Override to get the reply_to of the parent project. """
- return [task.project_id.message_get_reply_to()[0] if task.project_id else False
- for task in self.browse(cr, uid, ids, context=context)]
+ tasks = self.browse(cr, SUPERUSER_ID, ids, context=context)
+ project_ids = set([task.project_id.id for task in tasks if task.project_id])
+ aliases = self.pool['project.project'].message_get_reply_to(cr, uid, list(project_ids), context=context)
+ return dict((task.id, aliases.get(task.project_id and task.project_id.id or 0, False)) for task in tasks)
def message_new(self, cr, uid, msg, custom_values=None, context=None):
""" Override to updates the document according to the email. """
def message_get_reply_to(self, cr, uid, ids, context=None):
""" Override to get the reply_to of the parent project. """
- return [issue.project_id.message_get_reply_to()[0] if issue.project_id else False
- for issue in self.browse(cr, uid, ids, context=context)]
+ issues = self.browse(cr, SUPERUSER_ID, ids, context=context)
+ project_ids = set([issue.project_id.id for issue in issues if issue.project_id])
+ aliases = self.pool['project.project'].message_get_reply_to(cr, uid, list(project_ids), context=context)
+ return dict((issue.id, aliases.get(issue.project_id and issue.project_id.id or 0, False)) for issue in issues)
def message_get_suggested_recipients(self, cr, uid, ids, context=None):
recipients = super(project_issue, self).message_get_suggested_recipients(cr, uid, ids, context=context)