diff --git a/addons/mail/mail_followers.py b/addons/mail/mail_followers.py index 0c9a1a65432..b18832fa9cd 100644 --- a/addons/mail/mail_followers.py +++ b/addons/mail/mail_followers.py @@ -21,6 +21,7 @@ from osv import osv from osv import fields +import tools class mail_followers(osv.Model): """ mail_followers holds the data related to the follow mechanism inside @@ -82,24 +83,39 @@ class mail_notification(osv.Model): msg_obj = self.pool.get('mail.message') msg = msg_obj.browse(cr, uid, msg_id, context=context) + # add signature + body_html = msg.body + signature = msg.author_id and msg.author_id.user_ids[0].signature or '' + insertion_point = body_html.find('') + if insertion_point > -1: + insertion_point = body_html.find('') + body_html = body_html[:insertion_point] + '
%s
' % tools.ustr(signature) + body_html[insertion_point:] + else: + body_html = body_html + '
%s
' % tools.ustr(signature) + towrite = { 'mail_message_id': msg.id, 'email_to': [], - 'user_signature': True, - 'auto_delete': True, + 'auto_delete': False, + 'body_html': body_html, } for partner in self.pool.get('res.partner').browse(cr, uid, partner_ids, context=context): # Do not send an email to the writer - if partner.user_id.id == uid: + if partner.user_ids and partner.user_ids[0].id == uid: + continue + # Do not send to partners without email address defined + if not partner.email: continue # Partner does not want to receive any emails - if partner.notification_email_send=='none' or not partner.email: + if partner.notification_email_send == 'none': continue - # Partner want to receive only emails and comments - if partner.notification_email_send=='comment' and msg.type not in ('email','comment'): + # Partner wants to receive only emails and comments + if partner.notification_email_send == 'comment' and msg.type not in ('email', 'comment'): + continue + # Partner wants to receive only emails + if partner.notification_email_send == 'email' and msg.type != 'email': continue - if partner.email not in towrite['email_to']: towrite['email_to'].append(partner.email) towrite['email_to'] = ', '.join(towrite['email_to']) diff --git a/addons/mail/mail_mail.py b/addons/mail/mail_mail.py index 50b5b4cfe84..f128662aac1 100644 --- a/addons/mail/mail_mail.py +++ b/addons/mail/mail_mail.py @@ -39,6 +39,8 @@ class mail_mail(osv.Model): _name = 'mail.mail' _description = 'Outgoing Mails' _inherits = {'mail.message': 'mail_message_id'} + _order = 'id desc' + _columns = { 'mail_message_id': fields.many2one('mail.message', 'Message', required=True, ondelete='cascade'), 'mail_server_id': fields.many2one('ir.mail_server', 'Outgoing mail server', readonly=1), @@ -51,13 +53,12 @@ class mail_mail(osv.Model): ], 'Status', readonly=True), 'auto_delete': fields.boolean('Auto Delete', help="Permanently delete this email after sending it, to save space"), - 'user_signature': fields.boolean('Add Signature', - help="If checked, the user's signature will be appended to the text version of the message"), 'references': fields.text('References', help='Message references, such as identifiers of previous messages', readonly=1), 'email_from': fields.char('From', size=128, help='Message sender, taken from user preferences.'), 'email_to': fields.text('To', help='Message recipients'), 'email_cc': fields.char('Cc', size=256, help='Carbon copy message recipients'), 'reply_to':fields.char('Reply-To', size=256, help='Preferred response address for the message'), + 'body_html': fields.text('Rich-text Contents', help="Rich-text/HTML message"), } def _get_default_from(self, cr, uid, context=None): @@ -71,7 +72,6 @@ class mail_mail(osv.Model): _defaults = { 'state': 'outgoing', 'email_from': lambda self, cr, uid, ctx=None: self._get_default_from(cr, uid, ctx), - 'user_signature': False, } def mark_outgoing(self, cr, uid, ids, context=None): @@ -145,19 +145,13 @@ class mail_mail(osv.Model): ir_mail_server = self.pool.get('ir.mail_server') for message in self.browse(cr, uid, ids, context=context): try: - body = message.body + body = message.body_html # handle attachments attachments = [] for attach in message.attachment_ids: attachments.append((attach.datas_fname, base64.b64decode(attach.datas))) - # add signature if flag set - if message.user_signature: - signature = message.author_id and message.author_id.user_ids[0].signature or '' - insertion_point = body.find('') - body = body[:insertion_point] + signature + body[:insertion_point] - # no subject, res_id, model: ' posted on ' if not message.subject and message.model and message.res_id: subject = '%s posted on %s' % (message.author_id.name, message.record_name) @@ -171,11 +165,11 @@ class mail_mail(osv.Model): # build an RFC2822 email.message.Message object and send it # without queuing msg = ir_mail_server.build_email( + email_from = message.email_from, + email_to = tools.email_split(message.email_to), subject = subject, body = body, body_alternative = body_alternative, - email_from = message.email_from, - email_to = tools.email_split(message.email_to), email_cc = tools.email_split(message.email_cc), reply_to = message.reply_to, attachments = attachments, diff --git a/addons/mail/mail_mail_view.xml b/addons/mail/mail_mail_view.xml index 9f8a3419498..dc4f68b22c7 100644 --- a/addons/mail/mail_mail_view.xml +++ b/addons/mail/mail_mail_view.xml @@ -29,7 +29,7 @@ - + diff --git a/addons/mail/tests/test_mail.py b/addons/mail/tests/test_mail.py index ef72810309b..858e5e868ad 100644 --- a/addons/mail/tests/test_mail.py +++ b/addons/mail/tests/test_mail.py @@ -63,9 +63,16 @@ Content-Transfer-Encoding: quoted-printable ------=_Part_4200734_24778174.1344608186754-- """ - class test_mail(common.TransactionCase): + def _mock_smtp_gateway(self, *args, **kwargs): + return True + + def _mock_build_email(self, *args, **kwargs): + self._build_email_args = args + self._build_email_kwargs = kwargs + return self.build_email_real(*args, **kwargs) + def setUp(self): super(test_mail, self).setUp() self.ir_model = self.registry('ir.model') @@ -80,7 +87,9 @@ class test_mail(common.TransactionCase): self.res_partner = self.registry('res.partner') # Install mock SMTP gateway - self.registry('ir.mail_server').send_email = lambda *a,**kwargs: True + self.build_email_real = self.registry('ir.mail_server').build_email + self.registry('ir.mail_server').build_email = self._mock_build_email + self.registry('ir.mail_server').send_email = self._mock_smtp_gateway # groups@.. will cause the creation of new mail groups self.mail_group_model_id = self.ir_model.search(self.cr, self.uid, [('model','=', 'mail.group')])[0] @@ -234,77 +243,191 @@ class test_mail(common.TransactionCase): self.assertTrue(all(id in follower_ids for id in [user_admin.partner_id.id]), 'Admin the only Pigs group followers') - def test_20_message_post_and_compose(self): - """ Tests designed for message_post and the mail.compose.message wizard. """ + def test_20_message_post(self): + """ Tests designed for message_post. """ + cr, uid = self.cr, self.uid + self.res_users.write(cr, uid, [uid], {'signature': 'Admin', 'email': 'a@a'}) + user_admin = self.res_users.browse(cr, uid, uid) + group_pigs = self.mail_group.browse(cr, uid, self.group_pigs_id) + + # 0 - Admin + p_a_id = user_admin.partner_id.id + # 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 + 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'}) + + # Subscribe #1, #2 + group_pigs.message_subscribe([p_b_id, p_c_id]) + + # Mail data + _subject = 'Pigs' + _mail_subject = '%s posted on %s' % (user_admin.name, group_pigs.name) + _body1 = 'Pigs rules' + _mail_body1 = 'Pigs rules
Admin
' + _mail_bodyalt1 = 'Pigs rules\nAdmin' + _body2 = 'Pigs rules' + _mail_body2 = 'Pigs rules
Admin
' + _mail_bodyalt2 = 'Pigs rules\nAdmin' + + # Post comment with body and subject, comment preference + msg_id = self.mail_group.message_post(cr, uid, self.group_pigs_id, body=_body1, subject=_subject, msg_type='comment') + # Fetch: created mail_message, mail_mail, send_email + message = self.mail_message.browse(cr, uid, msg_id) + mail_ids = self.mail_mail.search(cr, uid, [], limit=1) + mail = self.mail_mail.browse(cr, uid, mail_ids[0]) + send_email = self._build_email_kwargs + + # Test: mail_message: subject is _subject, body is _body1 (no formatting done) + self.assertEqual(message.subject, _subject, + 'mail.message subject is %s; should be %s' % (message.subject, _subject)) + self.assertEqual(message.body, _body1, + 'mail.message body is %s; should be %s' % (message.body, _body1)) + # Test: mail_mail: subject is _subject, body_html is _mail_body1 (signature appended) + self.assertEqual(mail.subject, _subject, + 'mail.mail subject is %s; should be %s' % (mail.subject, _subject)) + self.assertEqual(mail.body_html, _mail_body1, + 'mail.mail body_html is %s; should be %s' % (mail.body_html, _mail_body1)) + self.assertTrue(mail.mail_message_id and mail.mail_message_id.id == msg_id, + 'mail_mail.mail_message_id is not the id of its related mail_message; got %s, should be %s' % (mail.mail_message_id.id, msg_id)) + # Test: send_email: email send by server: correct subject and body + self.assertEqual(send_email['subject'], _subject, + 'send_email subject is %s; should be %s' % (send_email['subject'], _subject)) + self.assertEqual(send_email['body'], _mail_body1, + 'send_email body is %s; should be %s' % (send_email['body'], _mail_body1)) + self.assertEqual(send_email['body_alternative'], _mail_bodyalt1, + 'send_email body_alternative is %s; should be %s' % (send_email['body_alternative'], _mail_bodyalt1)) + # Test: mail_message: partner_ids = group followers + message_pids = [partner.id for partner in message.partner_ids] + test_pids = [p_a_id, p_b_id, p_c_id] + self.assertTrue(all(pid in message_pids for pid in test_pids) and len(message_pids) == len(test_pids), + 'mail.message partners are %s; should be %s' % (message_pids, test_pids)) + # Test: notification linked to this message = group followers = partner_ids + notif_ids = self.mail_notification.search(cr, uid, [('message_id', '=', message.id)]) + notif_pids = [notif.partner_id.id for notif in self.mail_notification.browse(cr, uid, notif_ids)] + self.assertTrue(all(pid in notif_pids for pid in test_pids) and len(notif_pids) == len(test_pids), + 'mail.message notification partners are %s; should be %s' % (notif_pids, test_pids)) + # Test: send_email: email_to should contain b@b, not c@c (pref email), not a@a (writer) + test_emails = ['b@b'] + self.assertTrue(all(email in send_email['email_to'] for email in test_emails) and len(send_email['email_to']) == len(test_emails), + 'send_email email_to is %s; should be %s' % (send_email['email_to'], test_emails)) + + # New post: test automatic subject, signature in html, add a partner, email preference, parent_id previous message + msg_id2 = self.mail_group.message_post(cr, uid, self.group_pigs_id, body=_body2, msg_type='email', partner_ids=[(6, 0, [p_d_id])], parent_id=msg_id) + # Fetch: created mail_message, mail_mail, send_email + message = self.mail_message.browse(cr, uid, msg_id2) + mail_ids = self.mail_mail.search(cr, uid, [], limit=1) + mail = self.mail_mail.browse(cr, uid, mail_ids[0]) + send_email = self._build_email_kwargs + + # Test: mail_message: subject is False, body is _body1 (no formatting done), parent_id is msg_id + self.assertEqual(message.subject, False, + 'mail.message subject is %s; should be %s' % (message.subject, False)) + self.assertEqual(message.body, _body2, + 'mail.message body is %s; should be %s' % (message.body, _body2)) + self.assertEqual(message.parent_id.id, msg_id, + 'mail.message parent_id is %s; should be %s' % (message.parent_id.id, msg_id)) + # Test: mail_mail: subject is False, body_html is _mail_body1 (signature appended) + self.assertEqual(mail.subject, False, + 'mail.mail subject is %s; should be %s' % (mail.subject, False)) + self.assertEqual(mail.body_html, _mail_body2, + 'mail.mail body_html is %s; should be %s' % (mail.body_html, _mail_body2)) + self.assertTrue(mail.mail_message_id and mail.mail_message_id.id == msg_id2, + 'mail_mail.mail_message_id is not the id of its related mail_message; got %s, should be %s' % (mail.mail_message_id.id, msg_id2)) + # Test: send_email: email send by server: correct subject and body + self.assertEqual(send_email['subject'], _mail_subject, + 'send_email subject is %s; should be %s' % (send_email['subject'], _mail_subject)) + self.assertEqual(send_email['body'], _mail_body2, + 'send_email body is %s; should be %s' % (send_email['body'], _mail_body2)) + self.assertEqual(send_email['body_alternative'], _mail_bodyalt2, + 'send_email body_alternative is %s; should be %s' % (send_email['body_alternative'], _mail_bodyalt2)) + # Test: mail_message: partner_ids = group followers + message_pids = [partner.id for partner in message.partner_ids] + test_pids = [p_a_id, p_b_id, p_c_id, p_d_id] + self.assertTrue(all(pid in message_pids for pid in test_pids) and len(message_pids) == len(test_pids), + 'mail.message partners are %s; should be %s' % (message_pids, test_pids)) + # Test: notification linked to this message = group followers = partner_ids + notif_ids = self.mail_notification.search(cr, uid, [('message_id', '=', message.id)]) + notif_pids = [notif.partner_id.id for notif in self.mail_notification.browse(cr, uid, notif_ids)] + self.assertTrue(all(pid in notif_pids for pid in test_pids) and len(notif_pids) == len(test_pids), + 'mail.message notification partners are %s; should be %s' % (notif_pids, test_pids)) + # Test: send_email: email_to should contain b@b, c@c, not a@a (writer) + test_emails = ['b@b', 'c@c'] + self.assertTrue(all(email in send_email['email_to'] for email in test_emails) and len(send_email['email_to']) == len(test_emails), + 'send_email email_to is %s; should be %s' % (send_email['email_to'], test_emails)) + + def test_21_message_post_attachments(self): + """ Tests designed for attachments. """ + + def test_22_message_compose_wizard(self): + """ Tests designed for the mail.compose.message wizard. """ cr, uid = self.cr, self.uid mail_compose = self.registry('mail.compose.message') + self.res_users.write(cr, uid, [uid], {'signature': 'Admin', 'email': 'a@a'}) user_admin = self.res_users.browse(cr, uid, uid) - self.res_users.write(cr, uid, [uid], {'signature': 'Admin'}) - user_admin.refresh() group_pigs = self.mail_group.browse(cr, uid, self.group_pigs_id) # Mail data _subject = 'Pigs' - _mail_subject = '%s posted a comment on %s' % (user_admin.name, group_pigs.name) + _mail_subject = '%s posted on %s' % (user_admin.name, group_pigs.name) _body_text = 'Pigs rules' - _body_html = 'Pigs rules' + _msg_body1 = '
Pigs rules
' + _body_html = 'Pigs rules' + _msg_body2 = 'Pigs rules' # Create partners - # 0 - Create an email address for Admin, to check email sending - self.res_users.write(cr, uid, [uid], {'email': 'a@a'}) + # 0 - Admin + p_a_id = user_admin.partner_id.id # 1 - Bert Tartopoils, with email, should receive emails for comments and emails - partner_bert_id = self.res_partner.create(cr, uid, {'name': 'Bert Tartopoils', 'email': 'b@b'}) - # 2 - Raoul Grosbedon, without email, to test email verification; should receive emails for every message - partner_raoul_id = self.res_partner.create(cr, uid, {'name': 'Raoul Grosbedon', 'notification_email_send': 'all'}) - # 3 - Roger Poilvache, with email, should never receive emails - partner_roger_id = self.res_partner.create(cr, uid, {'name': 'Roger Poilvache', 'email': 'r@r', 'notification_email_send': 'none'}) + p_b_id = self.res_partner.create(cr, uid, {'name': 'Bert Tartopoils', 'email': 'b@b'}) + # 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'}) - # Create a new comment on group_pigs + # Subscribe #1 + group_pigs.message_subscribe([p_b_id]) + + # Comment group_pigs: body_text compose_id = mail_compose.create(cr, uid, - {'subject': _subject, 'body_text': _body_text, 'partner_ids': [(4, partner_bert_id), (4, partner_raoul_id), (4, partner_roger_id)]}, - {'mail.compose.message': 'comment', 'default_model': 'mail.group', 'default_res_id': self.group_pigs_id}) + {'subject': _subject, 'body_text': _body_text, 'partner_ids': [(4, p_c_id), (4, p_d_id)]}, + {'mail.compose.message.mode': 'comment', 'default_model': 'mail.group', 'default_res_id': self.group_pigs_id}) compose = mail_compose.browse(cr, uid, compose_id) + + # Test: model, res_id self.assertTrue(compose.model == 'mail.group' and compose.res_id == self.group_pigs_id, - 'Wizard message has model %s and res_id %s; should be mail.group and %s' % (compose.model, compose.res_id, self.group_pigs_id)) + 'mail.compose.message has model %s and res_id %s; should be mail.group and %s' % (compose.model, compose.res_id, self.group_pigs_id)) # Post the comment, get created message mail_compose.send_mail(cr, uid, [compose_id]) group_pigs.refresh() - first_com = group_pigs.message_ids[0] + msg = group_pigs.message_ids[0] - # Check message content - self.assertTrue(first_com.subject == False and first_com.body == _body_text, - 'Posted comment subject is %s and body is %s; should be False and %s' % (first_com.subject, first_com.body, _body_text)) - - # Message partners = notified people = writer + partner_ids - first_com_pids = [partner.id for partner in first_com.partner_ids] - notif_ids = self.mail_notification.search(cr, uid, [('message_id', '=', first_com.id)]) - self.assertTrue(len(first_com_pids) == 4, 'There are %s partners linked to the newly posted comment; should be 4' % (len(first_com_pids))) + # Test: mail.message: subject, body inside pre + self.assertTrue(msg.subject == False and msg.body == _msg_body1, + 'mail.message subject is %s, body is %s; should be %s and %s' % (msg.subject, msg.body, False, _msg_body1)) + # Test: mail.message partners = notified people: group_pigs followers (a, b) + mail.compose.message partner_ids (c, d) + msg_pids = [partner.id for partner in msg.partner_ids] + test_pids = [p_a_id, p_b_id, p_c_id, p_d_id] + notif_ids = self.mail_notification.search(cr, uid, [('message_id', '=', msg.id)]) + self.assertTrue(len(msg_pids) == 4, 'There are %s partners linked to the newly posted comment; should be 4' % (len(msg_pids))) self.assertTrue(len(notif_ids) == 4, 'There are %s entries in mail_notification: should be 4' % (len(notif_ids))) - self.assertTrue(all(id in [user_admin.partner_id.id, partner_bert_id, partner_raoul_id, partner_roger_id] for id in first_com_pids), + self.assertTrue(all(id in msg_pids for id in test_pids) and len(msg_pids) == len(test_pids), 'Admin, Bert Raoul and Roger should be the 4 partners of the newly created message') - # Fetch latest created email, that should be the email send to partners - mail_ids = self.mail_mail.search(cr, uid, [], limit=1) - mail = self.mail_mail.browse(cr, uid, mail_ids[0]) - mail_emails = tools.email_split(mail.email_to) - - # Check email subject, body, and email_to - expected_emails = ['a@a', 'b@b'] - self.assertTrue(mail.subject == _mail_subject and _body_text in mail.body, - 'Send email subject is \'%s\' and should be %s; body is %s and should contain %s' % (mail.subject, _mail_subject, mail.body, _body_text)) - self.assertTrue(all(email in mail_emails for email in expected_emails) and len(mail_emails) == 2, - 'Send email emails are %s; should be %s' % (mail_emails, expected_emails)) - # Create a reply to the last comment compose_id = mail_compose.create(cr, uid, {}, {'mail.compose.message.mode': 'reply', 'default_model': 'mail.thread', 'default_res_id': self.group_pigs_id, - 'active_id': first_com.id}) + 'active_id': msg.id}) compose = mail_compose.browse(cr, uid, compose_id) + + # Test: model, res_id, parent_id self.assertTrue(compose.model == 'mail.group' and compose.res_id == self.group_pigs_id, 'Wizard message has model: %s and res_id:%s; should be mail.group and %s' % (compose.model, compose.res_id, self.group_pigs_id)) - self.assertTrue(compose.parent_id.id == first_com.id, - 'Wizard parent_id is %d; should be %d' % (compose.parent_id.id, first_com.id)) + self.assertEqual(compose.parent_id.id, msg.id, + 'Wizard parent_id is %d; should be %d' % (compose.parent_id.id, msg.id)) def test_30_message_read(self): """ Tests designed for message_read. """ @@ -442,5 +565,3 @@ class test_mail(common.TransactionCase): msg1.refresh() self.assertEqual(5, len(group_pigs.message_ids), 'group should contain 5 messages') self.assertEqual(2, len(msg1.child_ids), 'msg1 should have 2 children now') - - diff --git a/addons/mail/wizard/mail_compose_message.py b/addons/mail/wizard/mail_compose_message.py index 96ce253c2a8..b4d992c5d1b 100644 --- a/addons/mail/wizard/mail_compose_message.py +++ b/addons/mail/wizard/mail_compose_message.py @@ -20,8 +20,8 @@ ############################################################################## import re - import tools + from osv import osv from osv import fields from tools.safe_eval import safe_eval as eval @@ -232,7 +232,7 @@ class mail_compose_message(osv.TransientModel): # default values, according to the wizard options subject = wizard.subject if wizard.content_subtype == 'html' else False partner_ids = [(4, partner.id) for partner in wizard.partner_ids] - body = wizard.body if wizard.content_subtype == 'html' else wizard.body_text + body = wizard.body if wizard.content_subtype == 'html' else '
%s
' % tools.ustr(wizard.body_text) active_model_pool = self.pool.get(wizard.model if wizard.model else 'mail.thread')