diff --git a/addons/mail/mail_followers.py b/addons/mail/mail_followers.py index 5d2a8966bb8..fe2362eecc0 100644 --- a/addons/mail/mail_followers.py +++ b/addons/mail/mail_followers.py @@ -161,11 +161,16 @@ class mail_notification(osv.Model): else: email_from = msg.email_from + references = False + if msg.parent_id: + references = msg.parent_id.message_id + mail_values = { 'mail_message_id': msg.id, 'auto_delete': True, 'body_html': body_html, 'email_from': email_from, + 'references': references, } email_notif_id = mail_mail.create(cr, uid, mail_values, context=context) try: diff --git a/addons/mail/mail_mail.py b/addons/mail/mail_mail.py index 8bca0f85b94..c6584b1e0cb 100644 --- a/addons/mail/mail_mail.py +++ b/addons/mail/mail_mail.py @@ -228,7 +228,7 @@ class mail_mail(osv.Model): 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) + email_to = ['%s <%s>' % (partner.name, partner.email)] if partner else tools.email_split(mail.email_to) return { 'body': body, 'body_alternative': body_alternative, diff --git a/addons/mail/tests/__init__.py b/addons/mail/tests/__init__.py index b945da54655..ff1080b0580 100644 --- a/addons/mail/tests/__init__.py +++ b/addons/mail/tests/__init__.py @@ -19,11 +19,12 @@ # ############################################################################## -from . import test_mail_message, test_mail_features, test_message_read, test_invite +from . import test_mail_message, test_mail_features, test_mail_gateway, test_message_read, test_invite checks = [ test_mail_message, test_mail_features, + test_mail_gateway, test_message_read, test_invite, ] diff --git a/addons/mail/tests/test_mail_base.py b/addons/mail/tests/test_mail_base.py index fb53220ef1f..a3b90288f0b 100644 --- a/addons/mail/tests/test_mail_base.py +++ b/addons/mail/tests/test_mail_base.py @@ -70,9 +70,9 @@ class TestMailBase(common.TransactionCase): # Test users to use through the various tests self.user_raoul_id = self.res_users.create(cr, uid, - {'name': 'Raoul Grosbedon', 'signature': 'Raoul', 'email': 'raoul@raoul.fr', 'login': 'raoul', 'groups_id': [(6, 0, [self.group_employee_id])]}) + {'name': 'Raoul Grosbedon', 'signature': 'SignRaoul', 'email': 'raoul@raoul.fr', 'login': 'raoul', 'groups_id': [(6, 0, [self.group_employee_id])]}) self.user_bert_id = self.res_users.create(cr, uid, - {'name': 'Bert Tartignole', 'signature': 'Bert', 'email': 'bert@bert.fr', 'login': 'bert', 'groups_id': [(6, 0, [])]}) + {'name': 'Bert Tartignole', 'signature': 'SignBert', 'email': 'bert@bert.fr', 'login': 'bert', 'groups_id': [(6, 0, [])]}) self.user_raoul = self.res_users.browse(cr, uid, self.user_raoul_id) self.user_bert = self.res_users.browse(cr, uid, self.user_bert_id) self.user_admin = self.res_users.browse(cr, uid, uid) diff --git a/addons/mail/tests/test_mail_features.py b/addons/mail/tests/test_mail_features.py index ed2ff74fc5d..4d7e28fda70 100644 --- a/addons/mail/tests/test_mail_features.py +++ b/addons/mail/tests/test_mail_features.py @@ -20,183 +20,12 @@ ############################################################################## from openerp.addons.mail.tests.test_mail_base import TestMailBase -from openerp.tools.mail import html_sanitize, append_content_to_html - -MAIL_TEMPLATE = """Return-Path: -To: {to} -Received: by mail1.openerp.com (Postfix, from userid 10002) - id 5DF9ABFB2A; Fri, 10 Aug 2012 16:16:39 +0200 (CEST) -From: Sylvie Lelitre -Subject: {subject} -MIME-Version: 1.0 -Content-Type: multipart/alternative; - boundary="----=_Part_4200734_24778174.1344608186754" -Date: Fri, 10 Aug 2012 14:16:26 +0000 -Message-ID: <1198923581.41972151344608186760.JavaMail@agrolait.com> -{extra} -------=_Part_4200734_24778174.1344608186754 -Content-Type: text/plain; charset=utf-8 -Content-Transfer-Encoding: quoted-printable - -Please call me as soon as possible this afternoon! - --- -Sylvie -------=_Part_4200734_24778174.1344608186754 -Content-Type: text/html; charset=utf-8 -Content-Transfer-Encoding: quoted-printable - - - - =20 - - =20 - =20 - -

Please call me as soon as possible this afternoon!

- -

--
- Sylvie -

- - -------=_Part_4200734_24778174.1344608186754-- -""" - -MAIL_TEMPLATE_PLAINTEXT = """Return-Path: -To: {to} -Received: by mail1.openerp.com (Postfix, from userid 10002) - id 5DF9ABFB2A; Fri, 10 Aug 2012 16:16:39 +0200 (CEST) -From: Sylvie Lelitre -Subject: {subject} -MIME-Version: 1.0 -Content-Type: text/plain -Date: Fri, 10 Aug 2012 14:16:26 +0000 -Message-ID: {msg_id} -{extra} - -Please call me as soon as possible this afternoon! - --- -Sylvie -""" +from openerp.tools.mail import html_sanitize class test_mail(TestMailBase): - def _mock_send_get_mail_body(self, *args, **kwargs): - # def _send_get_mail_body(self, cr, uid, mail, partner=None, context=None) - body = append_content_to_html(args[2].body_html, kwargs.get('partner').name if kwargs.get('partner') else 'No specific partner', plaintext=False) - return body - - def setUp(self): - super(test_mail, self).setUp() - - # Mock send_get_mail_body to test its functionality without other addons override - self._send_get_mail_body = self.registry('mail.mail').send_get_mail_body - self.registry('mail.mail').send_get_mail_body = self._mock_send_get_mail_body - - def tearDown(self): - # Remove mocks - self.registry('mail.mail').send_get_mail_body = self._send_get_mail_body - super(test_mail, self).tearDown() - - def test_00_message_process(self): - """ Testing incoming emails processing. """ - cr, uid, user_raoul = self.cr, self.uid, self.user_raoul - - # groups@.. will cause the creation of new mail groups - self.mail_group_model_id = self.ir_model.search(cr, uid, [('model', '=', 'mail.group')])[0] - self.mail_alias.create(cr, uid, {'alias_name': 'groups', 'alias_model_id': self.mail_group_model_id}) - - # Incoming mail creates a new mail_group "frogs" - self.assertEqual(self.mail_group.search(cr, uid, [('name', '=', 'frogs')]), []) - mail_frogs = MAIL_TEMPLATE.format(to='groups@example.com, other@gmail.com', subject='frogs', extra='') - self.mail_thread.message_process(cr, uid, None, mail_frogs) - frog_groups = self.mail_group.search(cr, uid, [('name', '=', 'frogs')]) - self.assertTrue(len(frog_groups) == 1) - - # Previously-created group can be emailed now - it should have an implicit alias group+frogs@... - frog_group = self.mail_group.browse(cr, uid, frog_groups[0]) - group_messages = frog_group.message_ids - self.assertTrue(len(group_messages) == 1, 'New group should only have the original message') - mail_frog_news = MAIL_TEMPLATE.format(to='Friendly Frogs ', subject='news', extra='') - self.mail_thread.message_process(cr, uid, None, mail_frog_news) - frog_group.refresh() - self.assertTrue(len(frog_group.message_ids) == 2, 'Group should contain 2 messages now') - - # Even with a wrong destination, a reply should end up in the correct thread - mail_reply = MAIL_TEMPLATE.format(to='erroneous@example.com>', subject='Re: news', - extra='In-Reply-To: <12321321-openerp-%d-mail.group@example.com>\n' % frog_group.id) - self.mail_thread.message_process(cr, uid, None, mail_reply) - frog_group.refresh() - self.assertTrue(len(frog_group.message_ids) == 3, 'Group should contain 3 messages now') - - # No model passed and no matching alias must raise - mail_spam = MAIL_TEMPLATE.format(to='noone@example.com', subject='spam', extra='') - self.assertRaises(Exception, - self.mail_thread.message_process, - cr, uid, None, mail_spam) - - # plain text content should be wrapped and stored as html - test_msg_id = '' - mail_text = MAIL_TEMPLATE_PLAINTEXT.format(to='groups@example.com', subject='frogs', extra='', msg_id=test_msg_id) - self.mail_thread.message_process(cr, uid, None, mail_text) - new_mail = self.mail_message.browse(cr, uid, self.mail_message.search(cr, uid, [('message_id', '=', test_msg_id)])[0]) - self.assertEqual(new_mail.body, '

\nPlease call me as soon as possible this afternoon!\n\n--\nSylvie\n
', - 'plaintext mail incorrectly parsed') - - # Do: post a new message, with a known partner - test_msg_id = '' - TEMPLATE_MOD = MAIL_TEMPLATE_PLAINTEXT.replace('Sylvie Lelitre ', user_raoul.email) - mail_new = TEMPLATE_MOD.format(to='Friendly Frogs ', subject='extra news', extra='', msg_id=test_msg_id) - self.mail_thread.message_process(cr, uid, None, mail_new) - new_mail = self.mail_message.browse(cr, uid, self.mail_message.search(cr, uid, [('message_id', '=', test_msg_id)])[0]) - # Test: author_id set, not email_from - self.assertEqual(new_mail.author_id, user_raoul.partner_id, 'message process wrong author found') - self.assertEqual(new_mail.email_from, user_raoul.email, 'message process wrong email_from') - - # Do: post a new message, with a unknown partner - test_msg_id = '' - TEMPLATE_MOD = MAIL_TEMPLATE_PLAINTEXT.replace('Sylvie Lelitre ', '_abcd_') - mail_new = TEMPLATE_MOD.format(to='Friendly Frogs ', subject='super news', extra='', msg_id=test_msg_id) - self.mail_thread.message_process(cr, uid, None, mail_new) - new_mail = self.mail_message.browse(cr, uid, self.mail_message.search(cr, uid, [('message_id', '=', test_msg_id)])[0]) - # Test: author_id set, not email_from - self.assertFalse(new_mail.author_id, 'message process shnould not have found a partner for _abcd_ email address') - self.assertIn('_abcd_', new_mail.email_from, 'message process should set en email_from when not finding a partner_id') - - def test_05_thread_parent_resolution(self): - """Verify parent/child relationships are correctly established when processing incoming mails""" - cr, uid = self.cr, self.uid - group_pigs = self.mail_group.browse(cr, uid, self.group_pigs_id) - msg1 = group_pigs.message_post(body='My Body', subject='1') - msg2 = group_pigs.message_post(body='My Body', subject='2') - msg1, msg2 = self.mail_message.browse(cr, uid, [msg1, msg2]) - self.assertTrue(msg1.message_id, "New message should have a proper message_id") - - # Reply to msg1, make sure the reply is properly attached using the various reply identification mechanisms - # 1. In-Reply-To header - reply_msg = MAIL_TEMPLATE.format(to='Pretty Pigs , other@gmail.com', subject='Re: 1', - extra='In-Reply-To: %s' % msg1.message_id) - self.mail_group.message_process(cr, uid, None, reply_msg) - - # 2. References header - reply_msg2 = MAIL_TEMPLATE.format(to='Pretty Pigs , other@gmail.com', subject='Re: Re: 1', - extra='References: <2233@a.com>\r\n\t<3edss_dsa@b.com> %s' % msg1.message_id) - self.mail_group.message_process(cr, uid, None, reply_msg2) - - # 3. Subject contains [] + model passed to message+process -> only attached to group, not to mail - reply_msg3 = MAIL_TEMPLATE.format(to='Pretty Pigs , other@gmail.com', - extra='', subject='Re: [%s] 1' % self.group_pigs_id) - self.mail_group.message_process(cr, uid, 'mail.group', reply_msg3) - - group_pigs.refresh() - 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') - - def test_10_followers_function_field(self): + def test_00_followers_function_field(self): """ Tests designed for the many2many function field 'follower_ids'. We will test to perform writes using the many2many commands 0, 3, 4, 5 and 6. """ @@ -253,7 +82,7 @@ class test_mail(TestMailBase): follower_ids = set([follower.partner_id.id for follower in self.mail_followers.browse(cr, uid, fol_obj_ids)]) self.assertEqual(follower_ids, set([partner_bert_id, user_admin.partner_id.id]), 'Bert and Admin should be the followers of dummy mail.group data') - def test_11_message_followers_and_subtypes(self): + def test_05_message_followers_and_subtypes(self): """ Tests designed for the subscriber API as well as message subtypes """ cr, uid, user_admin, user_raoul, group_pigs = self.cr, self.uid, self.user_admin, self.user_raoul, self.group_pigs # Data: message subtypes @@ -305,7 +134,7 @@ class test_mail(TestMailBase): self.assertTrue(subtype_data['mt_mg_nodef']['followed'], 'Admin should follow mt_mg_nodef in pigs') self.assertTrue(subtype_data['mt_all_nodef']['followed'], 'Admin should follow mt_all_nodef in pigs') - def test_20_message_quote_context(self): + def test_10_message_quote_context(self): """ Tests designed for message_post. """ cr, uid, user_admin, group_pigs = self.cr, self.uid, self.user_admin, self.group_pigs @@ -323,236 +152,391 @@ class test_mail(TestMailBase): self.assertNotIn('First answer, should not be displayed', result, 'Old answer should not be in quote.') self.assertNotIn('My answer I am propagating', result, 'Thread header content should be in quote.') - def test_21_message_post(self): + def test_20_message_post(self): """ Tests designed for message_post. """ cr, uid, user_raoul, group_pigs = self.cr, self.uid, self.user_raoul, self.group_pigs - self.res_users.write(cr, uid, [uid], {'signature': 'Admin', 'email': 'a@a'}) + + # -------------------------------------------------- + # Data creation + # -------------------------------------------------- + # 0 - Update existing users-partners + self.res_users.write(cr, uid, [uid], {'email': 'a@a'}) + self.res_users.write(cr, uid, [self.user_raoul_id], {'email': 'r@r'}) # 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'}) - - # Subscribe Raoul, #1, #2 - group_pigs.message_subscribe([self.partner_raoul_id, p_b_id, p_c_id]) - - # Mail data + p_d_id = self.res_partner.create(cr, uid, {'name': 'Dédé Grosbedon', 'email': 'd@d', 'notification_email_send': 'all'}) + # 4 - Attachments + attach1_id = self.ir_attachment.create(cr, user_raoul.id, { + 'name': 'Attach1', 'datas_fname': 'Attach1', + 'datas': 'bWlncmF0aW9uIHRlc3Q=', + 'res_model': 'mail.compose.message', 'res_id': 0}) + attach2_id = self.ir_attachment.create(cr, user_raoul.id, { + 'name': 'Attach2', 'datas_fname': 'Attach2', + 'datas': 'bWlncmF0aW9uIHRlc3Q=', + 'res_model': 'mail.compose.message', 'res_id': 0}) + attach3_id = self.ir_attachment.create(cr, user_raoul.id, { + 'name': 'Attach3', 'datas_fname': 'Attach3', + 'datas': 'bWlncmF0aW9uIHRlc3Q=', + 'res_model': 'mail.compose.message', 'res_id': 0}) + # 5 - Mail data _subject = 'Pigs' _mail_subject = 'Re: %s' % (group_pigs.name) _body1 = '

Pigs rules

' - _mail_body1 = '

Pigs rules

\n

Raoul

\n' - _mail_bodyalt1 = 'Pigs rules\nRaoul\n' - _body2 = 'Pigs rules' - _mail_body2 = '

Pigs rules

\n

Raoul

' - _mail_bodyalt2 = 'Pigs rules\nRaoul' - _attachments = [('First', 'My first attachment'), ('Second', 'My second attachment')] + _body2 = 'Pigs rocks' + _attachments = [ + ('List1', 'My first attachment'), + ('List2', 'My second attachment') + ] - # ---------------------------------------- - # CASE1: post comment, body and subject specified - # ---------------------------------------- + # -------------------------------------------------- + # CASE1: post comment + partners + attachments + # -------------------------------------------------- - # 1. Post a new comment on Pigs + # Data: set alias_domain to see emails with alias + self.registry('ir.config_parameter').set_param(self.cr, self.uid, 'mail.catchall.domain', 'schlouby.fr') + + # Do: subscribe Raoul + new_follower_ids = [self.partner_raoul_id] + group_pigs.message_subscribe(new_follower_ids) + # Test: group followers = Raoul + uid + group_fids = [follower.id for follower in group_pigs.message_follower_ids] + test_fids = new_follower_ids + [self.partner_admin_id] + self.assertEqual(set(test_fids), set(group_fids), + 'message_subscribe: incorrect followers after subscribe') + + # Do: Raoul message_post on Pigs self._init_mock_build_email() - msg1_id = self.mail_group.message_post(cr, user_raoul.id, self.group_pigs_id, body=_body1, subject=_subject, type='comment', subtype='mt_comment') - message1 = self.mail_message.browse(cr, uid, msg1_id) + msg1_id = self.mail_group.message_post(cr, user_raoul.id, self.group_pigs_id, + body=_body1, subject=_subject, partner_ids=[p_b_id, p_c_id], + attachment_ids=[attach1_id, attach2_id], attachments=_attachments, + type='comment', subtype='mt_comment') + msg = self.mail_message.browse(cr, uid, msg1_id) + msg_message_id = msg.message_id + msg_pids = [partner.id for partner in msg.notified_partner_ids] + msg_aids = [attach.id for attach in msg.attachment_ids] sent_emails = self._build_email_kwargs_list - # Test: mail.mail notifications have been deleted - self.assertFalse(self.mail_mail.search(cr, uid, [('mail_message_id', '=', msg1_id)]), 'mail.mail notifications should have been auto-deleted!') - # Test: mail_message: subject is _subject, body is _body1 (no formatting done) - self.assertEqual(message1.subject, _subject, 'mail.message subject incorrect') - self.assertEqual(message1.body, _body1, 'mail.message body incorrect') - # Test: sent_email: email send by server: correct subject, body, body_alternative - self.assertEqual(len(sent_emails), 2, 'sent_email number of sent emails incorrect') - for sent_email in sent_emails: - self.assertEqual(sent_email['subject'], _subject, 'sent_email subject incorrect') - self.assertTrue(sent_email['body'] in [_mail_body1 + '\nBert Tartopoils\n', _mail_body1 + '\nAdministrator\n'], - 'sent_email body incorrect') - # the html2plaintext uses etree or beautiful soup, so the result may be slighly different - # depending if you have installed beautiful soup. - self.assertTrue(sent_email['body_alternative'] in [_mail_bodyalt1 + '\nBert Tartopoils\n', _mail_bodyalt1 + '\nAdministrator\n'], - 'sent_email body_alternative is incorrect') - # Test: mail_message: notified_partner_ids = group followers - message_pids = set([partner.id for partner in message1.notified_partner_ids]) + + # Test: mail_message: subject and body not modified + self.assertEqual(_subject, msg.subject, 'message_post: mail.message subject incorrect') + self.assertEqual(_body1, msg.body, 'message_post: mail.message body incorrect') + # Test: mail_message: notified_partner_ids = group followers + partner_ids - author test_pids = set([self.partner_admin_id, p_b_id, p_c_id]) - self.assertEqual(test_pids, message_pids, 'mail.message notified partners incorrect') + self.assertEqual(test_pids, set(msg_pids), 'message_post: mail.message notified partners incorrect') + # Test: mail_message: attachments (4, attachment_ids + attachments) + test_aids = set([attach1_id, attach2_id]) + msg_attach_names = set([attach.name for attach in msg.attachment_ids]) + test_attach_names = set(['Attach1', 'Attach2', 'List1', 'List2']) + self.assertEqual(len(msg_aids), 4, + 'message_post: mail.message wrong number of attachments') + self.assertEqual(msg_attach_names, test_attach_names, + 'message_post: mail.message attachments incorrectly added') + self.assertTrue(test_aids.issubset(set(msg_aids)), + 'message_post: mail.message attachments duplicated') + for attach in msg.attachment_ids: + self.assertEqual(attach.res_model, 'mail.group', + 'message_post: mail.message attachments were not linked to the document') + self.assertEqual(attach.res_id, group_pigs.id, + 'message_post: mail.message attachments were not linked to the document') + if 'List' in attach.name: + self.assertIn((attach.name, attach.datas.decode('base64')), _attachments, + 'message_post: mail.message attachment name / data incorrect') + dl_attach = self.mail_message.download_attachment(cr, user_raoul.id, id_message=msg.id, attachment_id=attach.id) + self.assertIn((dl_attach['filename'], dl_attach['base64'].decode('base64')), _attachments, + 'message_post: mail.message download_attachment is incorrect') + + # Test: followers: same as before (author was already subscribed) + group_pigs.refresh() + group_fids = [follower.id for follower in group_pigs.message_follower_ids] + test_fids = new_follower_ids + [self.partner_admin_id] + self.assertEqual(set(test_fids), set(group_fids), + 'message_post: wrong followers after posting') + + # Test: mail_mail: notifications have been deleted + self.assertFalse(self.mail_mail.search(cr, uid, [('mail_message_id', '=', msg1_id)]), + '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 ', 'Bert Tartopoils '] + 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 emails wrong recipients (email_to)') + for sent_email in sent_emails: + self.assertEqual(sent_email['email_from'], 'Raoul Grosbedon ', + 'message_post: notification email wrong email_from: should use alias of sender') + self.assertEqual(len(sent_email['email_to']), 1, + '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 ', + 'message_post: notification email reply_to incorrect') + self.assertEqual(_subject, sent_email['subject'], + 'message_post: notification email subject incorrect') + self.assertIn(_body1, sent_email['body'], + 'message_post: notification email body incorrect') + self.assertIn(user_raoul.signature, sent_email['body'], + 'message_post: notification email body should contain the sender signature') + self.assertIn('Pigs rules', sent_email['body_alternative'], + 'message_post: notification email body alternative should contain the body') + self.assertNotIn('

', sent_email['body_alternative'], + 'message_post: notification email body alternative still contains html') + self.assertIn(user_raoul.signature, sent_email['body_alternative'], + 'message_post: notification email body alternative should contain the sender signature') + self.assertFalse(sent_email['references'], + 'message_post: references should be False when sending a message that is not a reply') + # Test: notification linked to this message = group followers = notified_partner_ids notif_ids = self.mail_notification.search(cr, uid, [('message_id', '=', msg1_id)]) notif_pids = set([notif.partner_id.id for notif in self.mail_notification.browse(cr, uid, notif_ids)]) - self.assertEqual(notif_pids, test_pids, 'mail.message notification partners incorrect') - # Test: sent_email: email_to should contain b@b, not c@c (pref email), not a@a (writer) - for sent_email in sent_emails: - self.assertTrue(set(sent_email['email_to']).issubset(set(['a@a', 'b@b'])), 'sent_email email_to is incorrect') + self.assertEqual(notif_pids, test_pids, + 'message_post: mail.message created mail.notification incorrect') - # ---------------------------------------- - # CASE2: post an email with attachments, parent_id, partner_ids, parent notification - # ---------------------------------------- + # -------------------------------------------------- + # CASE2: reply + parent_id + parent notification + # -------------------------------------------------- - # 1. Post a new email comment on Pigs + # Data: remove alias_domain to see emails with alias + param_ids = self.registry('ir.config_parameter').search(cr, uid, [('key', '=', 'mail.catchall.domain')]) + self.registry('ir.config_parameter').unlink(cr, uid, param_ids) + + # Do: Raoul message_post on Pigs self._init_mock_build_email() - msg2_id = self.mail_group.message_post(cr, user_raoul.id, self.group_pigs_id, body=_body2, type='email', subtype='mt_comment', - partner_ids=[p_d_id], parent_id=msg1_id, attachments=_attachments, - context={'mail_post_autofollow': True}) - message2 = self.mail_message.browse(cr, uid, msg2_id) + msg2_id = self.mail_group.message_post(cr, user_raoul.id, self.group_pigs_id, + body=_body2, type='email', subtype='mt_comment', + partner_ids=[p_d_id], parent_id=msg1_id, attachment_ids=[attach3_id], + context={'mail_post_autofollow': True}) + msg = self.mail_message.browse(cr, uid, msg2_id) + msg_pids = [partner.id for partner in msg.notified_partner_ids] + msg_aids = [attach.id for attach in msg.attachment_ids] sent_emails = self._build_email_kwargs_list - self.assertFalse(self.mail_mail.search(cr, uid, [('mail_message_id', '=', msg2_id)]), 'mail.mail notifications should have been auto-deleted!') - # Test: mail_message: subject is False, body is _body2 (no formatting done), parent_id is msg_id - self.assertEqual(message2.subject, False, 'mail.message subject incorrect') - self.assertEqual(message2.body, html_sanitize(_body2), 'mail.message body incorrect') - self.assertEqual(message2.parent_id.id, msg1_id, 'mail.message parent_id incorrect') - # Test: sent_email: email send by server: correct automatic subject, body, body_alternative - self.assertEqual(len(sent_emails), 3, 'sent_email number of sent emails incorrect') - for sent_email in sent_emails: - self.assertEqual(sent_email['subject'], _mail_subject, 'sent_email subject incorrect') - self.assertIn(_mail_body2, sent_email['body'], 'sent_email body incorrect') - self.assertIn(_mail_bodyalt2, sent_email['body_alternative'], 'sent_email body_alternative incorrect') + + # Test: mail_message: subject is False, body, parent_id is msg_id + self.assertEqual(msg.subject, False, 'message_post: mail.message subject incorrect') + self.assertEqual(msg.body, html_sanitize(_body2), 'message_post: mail.message body incorrect') + self.assertEqual(msg.parent_id.id, msg1_id, 'message_post: mail.message parent_id incorrect') # Test: mail_message: notified_partner_ids = group followers - message_pids = set([partner.id for partner in message2.notified_partner_ids]) - test_pids = set([self.partner_admin_id, p_b_id, p_c_id, p_d_id]) - self.assertEqual(message_pids, test_pids, 'mail.message partners incorrect') - # Test: notifications linked to this message = group followers = notified_partner_ids + test_pids = [self.partner_admin_id, p_d_id] + self.assertEqual(set(test_pids), set(msg_pids), 'message_post: mail.message partners incorrect') + # Test: mail_message: notifications linked to this message = group followers = notified_partner_ids notif_ids = self.mail_notification.search(cr, uid, [('message_id', '=', msg2_id)]) - notif_pids = set([notif.partner_id.id for notif in self.mail_notification.browse(cr, uid, notif_ids)]) - self.assertEqual(notif_pids, test_pids, 'mail.message notification partners incorrect') - # Test: sent_email: email_to should contain b@b, c@c, not a@a (writer) + notif_pids = [notif.partner_id.id for notif in self.mail_notification.browse(cr, uid, notif_ids)] + self.assertEqual(set(test_pids), set(notif_pids), 'message_post: mail.message notification partners incorrect') + + # Test: mail_mail: notifications deleted + 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 ', u'Bert Tartopoils ', u'Carine Poilvache ', u'D\xe9d\xe9 Grosbedon '] + # self.assertEqual(len(sent_emails), 3, 'sent_email number of sent emails incorrect') for sent_email in sent_emails: - self.assertTrue(set(sent_email['email_to']).issubset(set(['a@a', 'b@b', 'c@c'])), 'sent_email email_to incorrect') - # Test: attachments - for attach in message2.attachment_ids: - self.assertEqual(attach.res_model, 'mail.group', 'mail.message attachment res_model incorrect') - self.assertEqual(attach.res_id, self.group_pigs_id, 'mail.message attachment res_id incorrect') - self.assertIn((attach.name, attach.datas.decode('base64')), _attachments, - 'mail.message attachment name / data incorrect') - # Test: download attachments - for attach in message2.attachment_ids: - dl_attach = self.mail_message.download_attachment(cr, user_raoul.id, id_message=message2.id, attachment_id=attach.id) - self.assertIn((dl_attach['filename'], dl_attach['base64'].decode('base64')), _attachments, 'mail.message download_attachment is incorrect') + self.assertEqual(sent_email['email_from'], 'Raoul Grosbedon ', + 'message_post: notification email wrong email_from: should use email of sender when no alias domain set') + self.assertEqual(len(sent_email['email_to']), 1, + '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 ', + 'message_post: notification email reply_to incorrect: should name Followers of Pigs, and have raoul email') + self.assertEqual(_mail_subject, sent_email['subject'], + 'message_post: notification email subject incorrect') + self.assertIn(html_sanitize(_body2), sent_email['body'], + 'message_post: notification email does not contain the body') + self.assertIn(user_raoul.signature, sent_email['body'], + 'message_post: notification email body should contain the sender signature') + self.assertIn('Pigs rocks', sent_email['body_alternative'], + 'message_post: notification email body alternative should contain the body') + self.assertNotIn('

', sent_email['body_alternative'], + 'message_post: notification email body alternative still contains html') + self.assertIn(user_raoul.signature, sent_email['body_alternative'], + 'message_post: notification email body alternative should contain the sender signature') + self.assertIn(msg_message_id, sent_email['references'], + 'message_post: notification email references lacks parent message message_id') + # Test: attachments + download + for attach in msg.attachment_ids: + self.assertEqual(attach.res_model, 'mail.group', + 'message_post: mail.message attachment res_model incorrect') + self.assertEqual(attach.res_id, self.group_pigs_id, + 'message_post: mail.message attachment res_id incorrect') - # 2. Dédé has been notified -> should also have been notified of the parent message - message1.refresh() - message_pids = set([partner.id for partner in message1.notified_partner_ids]) + # Test: Dédé has been notified -> should also have been notified of the parent message + msg = self.mail_message.browse(cr, uid, msg1_id) + msg_pids = set([partner.id for partner in msg.notified_partner_ids]) test_pids = set([self.partner_admin_id, p_b_id, p_c_id, p_d_id]) - self.assertEqual(test_pids, message_pids, 'mail.message parent notification not created') + self.assertEqual(test_pids, msg_pids, 'message_post: mail.message parent notification not created') - # 3. Reply to the last message, check that its parent will be the first message + # Do: reply to last message msg3_id = self.mail_group.message_post(cr, user_raoul.id, self.group_pigs_id, body='Test', parent_id=msg2_id) - message = self.mail_message.browse(cr, uid, msg3_id) - self.assertEqual(message.parent_id.id, msg1_id, 'message_post did not flatten the thread structure') + msg = self.mail_message.browse(cr, uid, msg3_id) + # Test: check that its parent will be the first message + self.assertEqual(msg.parent_id.id, msg1_id, 'message_post did not flatten the thread structure') def test_25_message_compose_wizard(self): """ Tests designed for the mail.compose.message wizard. """ - cr, uid, user_admin, group_pigs = self.cr, self.uid, self.user_admin, self.group_pigs + cr, uid, user_raoul, group_pigs = self.cr, self.uid, self.user_raoul, self.group_pigs mail_compose = self.registry('mail.compose.message') - self.res_users.write(cr, uid, [uid], {'signature': 'Admin', 'email': 'a@a'}) - group_bird_id = self.mail_group.create(cr, uid, {'name': 'Bird', 'description': 'Bird resistance'}, {'mail_create_nolog': True}) - group_bird = self.mail_group.browse(cr, uid, group_bird_id) - # Mail data + # -------------------------------------------------- + # Data creation + # -------------------------------------------------- + # 0 - Update existing users-partners + self.res_users.write(cr, uid, [uid], {'email': 'a@a'}) + self.res_users.write(cr, uid, [self.user_raoul_id], {'email': 'r@r'}) + # 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 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', 'email': 'd@d', 'notification_email_send': 'all'}) + # 4 - Create a Bird mail.group, that will be used to test mass mailing + group_bird_id = self.mail_group.create(cr, uid, + { + 'name': 'Bird', + 'description': 'Bird resistance', + }, context={'mail_create_nolog': True}) + group_bird = self.mail_group.browse(cr, uid, group_bird_id) + # 5 - Mail data _subject = 'Pigs' _body = 'Pigs rule' - _reply_subject = 'Re: Pigs' + _reply_subject = 'Re: %s' % _subject _attachments = [ {'name': 'First', 'datas_fname': 'first.txt', 'datas': 'My first attachment'.encode('base64')}, {'name': 'Second', 'datas_fname': 'second.txt', 'datas': 'My second attachment'.encode('base64')} ] _attachments_test = [('first.txt', 'My first attachment'), ('second.txt', 'My second attachment')] - - # 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 + # 6 - Subscribe Bert to Pigs group_pigs.message_subscribe([p_b_id]) - # ---------------------------------------- - # CASE1: comment on group_pigs - # ---------------------------------------- + # -------------------------------------------------- + # CASE1: wizard + partners + context keys + # -------------------------------------------------- - # 1. Comment group_pigs with body_text and subject - compose_id = mail_compose.create(cr, uid, - {'subject': _subject, 'body': _body, 'partner_ids': [(4, p_c_id), (4, p_d_id)]}, - {'default_composition_mode': 'comment', 'default_model': 'mail.group', 'default_res_id': self.group_pigs_id, - 'default_content_subtype': 'plaintext'}) + # Do: Raoul wizard-composes on Pigs with auto-follow for partners, not for author + compose_id = mail_compose.create(cr, user_raoul.id, + { + 'subject': _subject, + 'body': _body, + 'partner_ids': [(4, p_c_id), (4, p_d_id)], + }, context={ + 'default_composition_mode': 'comment', + 'default_model': 'mail.group', + 'default_res_id': self.group_pigs_id, + }) compose = mail_compose.browse(cr, uid, compose_id) - # Test: mail.compose.message: composition_mode, model, res_id - self.assertEqual(compose.composition_mode, 'comment', 'mail.compose.message incorrect composition_mode') - self.assertEqual(compose.model, 'mail.group', 'mail.compose.message incorrect model') - self.assertEqual(compose.res_id, self.group_pigs_id, 'mail.compose.message incorrect res_id') - # 2. Post the comment, get created message - mail_compose.send_mail(cr, uid, [compose_id], {'mail_post_autofollow': True}) + # Test: mail.compose.message: composition_mode, model, res_id + self.assertEqual(compose.composition_mode, 'comment', 'compose wizard: mail.compose.message incorrect composition_mode') + self.assertEqual(compose.model, 'mail.group', 'compose wizard: mail.compose.message incorrect model') + self.assertEqual(compose.res_id, self.group_pigs_id, 'compose wizard: mail.compose.message incorrect res_id') + + # Do: Post the comment + mail_compose.send_mail(cr, user_raoul.id, [compose_id], {'mail_post_autofollow': True, 'mail_create_nosubscribe': True}) group_pigs.refresh() message = group_pigs.message_ids[0] - # Test: mail.message: subject, body inside pre - self.assertEqual(message.subject, _subject, 'mail.message incorrect subject') - self.assertEqual(message.body, '

%s

' % _body, 'mail.message incorrect body') - # Test: mail.message: notified_partner_ids = entries in mail.notification: group_pigs fans (a, b) + mail.compose.message partner_ids (c, d) + + # Test: mail.group: followers (c and d added by auto follow key; raoul not added by nosubscribe key) + pigs_pids = [p.id for p in group_pigs.message_follower_ids] + test_pids = [self.partner_admin_id, p_b_id, p_c_id, p_d_id] + self.assertEqual(set(pigs_pids), set(test_pids), + 'compose wizard: mail_post_autofollow and mail_create_nosubscribe context keys not correctly taken into account') + + # Test: mail.message: subject, body inside p + self.assertEqual(message.subject, _subject, 'compose wizard: mail.message incorrect subject') + self.assertEqual(message.body, '

%s

' % _body, 'compose wizard: mail.message incorrect body') + # Test: mail.message: notified_partner_ids = admin + bert (followers) + c + d (recipients) msg_pids = [partner.id for partner in message.notified_partner_ids] - test_pids = [p_b_id, p_c_id, p_d_id] - notif_ids = self.mail_notification.search(cr, uid, [('message_id', '=', message.id)]) - self.assertEqual(len(notif_ids), 3, 'mail.message: too much notifications created') - self.assertEqual(set(msg_pids), set(test_pids), 'mail.message notified_partner_ids incorrect') + test_pids = [self.partner_admin_id, p_b_id, p_c_id, p_d_id] + self.assertEqual(set(msg_pids), set(test_pids), + 'compose wizard: mail.message notified_partner_ids incorrect') - # ---------------------------------------- - # CASE2: reply to last comment with attachments - # ---------------------------------------- + # -------------------------------------------------- + # CASE2: reply + attachments + # -------------------------------------------------- - # 1. Update last comment subject, reply with attachments - message.write({'subject': _subject}) - compose_id = mail_compose.create(cr, uid, - {'attachment_ids': [(0, 0, _attachments[0]), (0, 0, _attachments[1])]}, - {'default_composition_mode': 'reply', 'default_model': 'mail.thread', 'default_res_id': self.group_pigs_id, 'default_parent_id': message.id}) + # Do: Reply with attachments + compose_id = mail_compose.create(cr, user_raoul.id, + { + 'attachment_ids': [(0, 0, _attachments[0]), (0, 0, _attachments[1])] + }, context={ + 'default_composition_mode': 'reply', + 'default_model': 'mail.thread', + 'default_res_id': self.group_pigs_id, + 'default_parent_id': message.id + }) compose = mail_compose.browse(cr, uid, compose_id) - # Test: model, res_id, parent_id - self.assertEqual(compose.model, 'mail.group', 'mail.compose.message incorrect model') - self.assertEqual(compose.res_id, self.group_pigs_id, 'mail.compose.message incorrect res_id') - self.assertEqual(compose.parent_id.id, message.id, 'mail.compose.message incorrect parent_id') - # Test: mail.message: subject as Re:.., body in html, parent_id - self.assertEqual(compose.subject, _reply_subject, 'mail.message incorrect subject') - # self.assertIn('Administrator wrote:
Pigs rules
', compose.body, 'mail.message body is incorrect') - self.assertEqual(compose.parent_id and compose.parent_id.id, message.id, 'mail.message parent_id incorrect') - # Test: mail.message: attachments + + # Test: mail.compose.message: model, res_id, parent_id + self.assertEqual(compose.model, 'mail.group', 'compose wizard: mail.compose.message incorrect model') + self.assertEqual(compose.res_id, self.group_pigs_id, 'compose wizard: mail.compose.message incorrect res_id') + self.assertEqual(compose.parent_id.id, message.id, 'compose wizard: mail.compose.message incorrect parent_id') + + # Test: mail.compose.message: subject as Re:.., body, parent_id + self.assertEqual(compose.subject, _reply_subject, 'compose wizard: mail.compose.message incorrect subject') + self.assertFalse(compose.body, 'compose wizard: mail.compose.message body should not contain parent message body') + self.assertEqual(compose.parent_id and compose.parent_id.id, message.id, 'compose wizard: mail.compose.message parent_id incorrect') + # Test: mail.compose.message: attachments for attach in compose.attachment_ids: - self.assertIn((attach.datas_fname, attach.datas.decode('base64')), _attachments_test, 'mail.message attachment name / data incorrect') + self.assertIn((attach.datas_fname, attach.datas.decode('base64')), _attachments_test, + 'compose wizard: mail.message attachment name / data incorrect') - # ---------------------------------------- + # -------------------------------------------------- # CASE3: mass_mail on Pigs and Bird - # ---------------------------------------- + # -------------------------------------------------- - # 1. mass_mail on pigs and bird - compose_id = mail_compose.create(cr, uid, - {'subject': _subject, 'body': '${object.description}', 'partner_ids': [(4, p_c_id), (4, p_d_id)]}, - {'default_composition_mode': 'mass_mail', 'default_model': 'mail.group', 'default_res_id': False, - 'active_ids': [self.group_pigs_id, group_bird_id]}) + # Do: Compose in mass_mail_mode on pigs and bird + compose_id = mail_compose.create(cr, user_raoul.id, + { + 'subject': _subject, + 'body': '${object.description}', + 'partner_ids': [(4, p_c_id), (4, p_d_id)], + }, context={ + 'default_composition_mode': 'mass_mail', + 'default_model': 'mail.group', + 'default_res_id': False, + 'active_ids': [self.group_pigs_id, group_bird_id], + }) compose = mail_compose.browse(cr, uid, compose_id) - # 2. Post the comment, get created message for each group - mail_compose.send_mail(cr, uid, [compose_id], - context={'default_res_id': -1, 'active_ids': [self.group_pigs_id, group_bird_id]}) + # D: Post the comment, get created message for each group + mail_compose.send_mail(cr, user_raoul.id, [compose_id], context={ + 'default_res_id': -1, + 'active_ids': [self.group_pigs_id, group_bird_id] + }) group_pigs.refresh() group_bird.refresh() message1 = group_pigs.message_ids[0] message2 = group_bird.message_ids[0] + # Test: Pigs and Bird did receive their message test_msg_ids = self.mail_message.search(cr, uid, [], limit=2) - self.assertIn(message1.id, test_msg_ids, 'Pigs did not receive its mass mailing message') - self.assertIn(message2.id, test_msg_ids, 'Bird did not receive its mass mailing message') + self.assertIn(message1.id, test_msg_ids, 'compose wizard: Pigs did not receive its mass mailing message') + self.assertIn(message2.id, test_msg_ids, 'compose wizard: Bird did not receive its mass mailing message') + # Test: mail.message: subject, body, subtype, notified partners (nobody + specific recipients) self.assertEqual(message1.subject, _subject, - 'message_post: mail.message in mass mail subject incorrect') + 'compose wizard: message_post: mail.message in mass mail subject incorrect') self.assertEqual(message1.body, '

%s

' % group_pigs.description, - 'message_post: mail.message in mass mail body incorrect') + 'compose wizard: message_post: mail.message in mass mail body incorrect') self.assertEqual(set([p.id for p in message1.notified_partner_ids]), set([p_c_id, p_d_id]), - 'message_post: mail.message in mass mail incorrect notified partners') + 'compose wizard: message_post: mail.message in mass mail incorrect notified partners') self.assertEqual(message2.subject, _subject, - 'message_post: mail.message in mass mail subject incorrect') + 'compose wizard: message_post: mail.message in mass mail subject incorrect') self.assertEqual(message2.body, '

%s

' % group_bird.description, - 'message_post: mail.message in mass mail body incorrect') + 'compose wizard: message_post: mail.message in mass mail body incorrect') self.assertEqual(set([p.id for p in message2.notified_partner_ids]), set([p_c_id, p_d_id]), - 'message_post: mail.message in mass mail incorrect notified partners') + 'compose wizard: message_post: mail.message in mass mail incorrect notified partners') + + # Test: mail.group followers: author not added as follower in mass mail mode + pigs_pids = [p.id for p in group_pigs.message_follower_ids] + test_pids = [self.partner_admin_id, p_b_id, p_c_id, p_d_id] + self.assertEqual(set(pigs_pids), set(test_pids), + 'compose wizard: mail_post_autofollow and mail_create_nosubscribe context keys not correctly taken into account') + bird_pids = [p.id for p in group_bird.message_follower_ids] + test_pids = [self.partner_admin_id] + self.assertEqual(set(bird_pids), set(test_pids), + 'compose wizard: mail_post_autofollow and mail_create_nosubscribe context keys not correctly taken into account') def test_30_needaction(self): """ Tests for mail.message needaction. """ diff --git a/addons/mail/tests/test_mail_gateway.py b/addons/mail/tests/test_mail_gateway.py new file mode 100644 index 00000000000..ea52876d1f5 --- /dev/null +++ b/addons/mail/tests/test_mail_gateway.py @@ -0,0 +1,282 @@ +# -*- coding: utf-8 -*- +############################################################################## +# +# OpenERP, Open Source Business Applications +# Copyright (c) 2012-TODAY OpenERP S.A. +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# +############################################################################## + +from openerp.addons.mail.tests.test_mail_base import TestMailBase + +MAIL_TEMPLATE = """Return-Path: +To: {to} +Received: by mail1.openerp.com (Postfix, from userid 10002) + id 5DF9ABFB2A; Fri, 10 Aug 2012 16:16:39 +0200 (CEST) +From: {email_from} +Subject: {subject} +MIME-Version: 1.0 +Content-Type: multipart/alternative; + boundary="----=_Part_4200734_24778174.1344608186754" +Date: Fri, 10 Aug 2012 14:16:26 +0000 +Message-ID: {msg_id} +{extra} +------=_Part_4200734_24778174.1344608186754 +Content-Type: text/plain; charset=utf-8 +Content-Transfer-Encoding: quoted-printable + +Please call me as soon as possible this afternoon! + +-- +Sylvie +------=_Part_4200734_24778174.1344608186754 +Content-Type: text/html; charset=utf-8 +Content-Transfer-Encoding: quoted-printable + + + + =20 + + =20 + =20 + +

Please call me as soon as possible this afternoon!

+ +

--
+ Sylvie +

+ + +------=_Part_4200734_24778174.1344608186754-- +""" + +MAIL_TEMPLATE_PLAINTEXT = """Return-Path: +To: {to} +Received: by mail1.openerp.com (Postfix, from userid 10002) + id 5DF9ABFB2A; Fri, 10 Aug 2012 16:16:39 +0200 (CEST) +From: Sylvie Lelitre +Subject: {subject} +MIME-Version: 1.0 +Content-Type: text/plain +Date: Fri, 10 Aug 2012 14:16:26 +0000 +Message-ID: {msg_id} +{extra} + +Please call me as soon as possible this afternoon! + +-- +Sylvie +""" + + +class TestMailgateway(TestMailBase): + + def test_00_message_process(self): + """ Testing incoming emails processing. """ + cr, uid, user_raoul = self.cr, self.uid, self.user_raoul + + def format_and_process(template, to='groups@example.com, other@gmail.com', subject='Frogs', + extra='', email_from='Sylvie Lelitre ', + msg_id='<1198923581.41972151344608186760.JavaMail@agrolait.com>', + model=None): + self.assertEqual(self.mail_group.search(cr, uid, [('name', '=', subject)]), []) + mail = template.format(to=to, subject=subject, extra=extra, email_from=email_from, msg_id=msg_id) + self.mail_thread.message_process(cr, uid, model, mail) + return self.mail_group.search(cr, uid, [('name', '=', subject)]) + + # -------------------------------------------------- + # Data creation + # -------------------------------------------------- + + # groups@.. will cause the creation of new mail groups + self.mail_group_model_id = self.ir_model.search(cr, uid, [('model', '=', 'mail.group')])[0] + alias_id = self.mail_alias.create(cr, uid, { + 'alias_name': 'groups', + 'alias_user_id': False, + 'alias_model_id': self.mail_group_model_id}) + + # -------------------------------------------------- + # Test1: new record creation + # -------------------------------------------------- + + # Do: incoming mail from an unknown partner on an alias creates a new mail_group "frogs" + self._init_mock_build_email() + frog_groups = format_and_process(MAIL_TEMPLATE, to='groups@example.com, other@gmail.com') + sent_emails = self._build_email_kwargs_list + # Test: one group created by mailgateway administrator + self.assertTrue(len(frog_groups) == 1) + frog_group = self.mail_group.browse(cr, uid, frog_groups[0]) + # Test: one message that is the incoming email + self.assertEqual(len(frog_group.message_ids), 1, + 'message_process: newly created group should have the incoming email in message_ids') + msg = frog_group.message_ids[0] + self.assertEqual('Frogs', msg.subject, + 'message_process: newly created group should have the incoming email as first message') + self.assertIn('Please call me as soon as possible this afternoon!', msg.body, + 'message_process: newly created group should have the incoming email as first message') + self.assertEqual('email', msg.type, + 'message_process: newly created group should have an email as first message') + self.assertEqual('Discussions', msg.subtype_id.name, + 'message_process: newly created group should not have a log first message but an email') + # Test: message: unknown email address -> message has email_from, not author_id + self.assertFalse(msg.author_id, + 'message_process: message on created group should not have an author_id') + self.assertIn('test.sylvie.lelitre@agrolait.com', msg.email_from, + 'message_process: message on created group should have an email_from') + # Test: followers: nobody + self.assertEqual(len(frog_group.message_follower_ids), 0, 'message_process: newly create group should not have any follower') + # Test: sent emails: no-one + self.assertEqual(len(sent_emails), 0, + 'message_process: should create emails without any follower added') + # Data: unlink group + frog_group.unlink() + + # Do: incoming email from a known partner on an alias with known recipients, alias is owned by user that can create a group + self.mail_alias.write(cr, uid, [alias_id], {'alias_user_id': self.user_raoul_id}) + p1id = self.res_partner.create(cr, uid, {'name': 'Sylvie Lelitre', 'email': 'test.sylvie.lelitre@agrolait.com'}) + p2id = self.res_partner.create(cr, uid, {'name': 'Other Poilvache', 'email': 'other@gmail.com'}) + self._init_mock_build_email() + frog_groups = format_and_process(MAIL_TEMPLATE, to='groups@example.com, other@gmail.com') + sent_emails = self._build_email_kwargs_list + # Test: one group created by raoul + self.assertTrue(len(frog_groups) == 1) + frog_group = self.mail_group.browse(cr, uid, frog_groups[0]) + # Test: one message that is the incoming email + self.assertEqual(len(frog_group.message_ids), 1, + 'message_process: newly created group should have the incoming email in message_ids') + msg = frog_group.message_ids[0] + # Test: message: unknown email address -> message has email_from, not author_id + self.assertEqual(p1id, msg.author_id.id, + 'message_process: message on created group should have Sylvie as author_id') + self.assertIn('Sylvie Lelitre ', msg.email_from, + 'message_process: message on created group should have have an email_from') + # Test: author (not recipient and and not raoul (as alias owner)) added as follower + frog_follower_ids = set([p.id for p in frog_group.message_follower_ids]) + self.assertEqual(frog_follower_ids, set([p1id]), + 'message_process: newly created group should have 1 follower (author, not creator, not recipients)') + # Test: sent emails: no-one, no bounce effet + self.assertEqual(len(sent_emails), 0, + 'message_process: should not bounce incoming emails') + # Data: unlink group + frog_group.unlink() + + # Do: incoming email from a known partner that is also an user that can create a mail.group + self.res_users.create(cr, uid, {'partner_id': p1id, 'login': 'sylvie', 'groups_id': [(6, 0, [self.group_employee_id])]}) + frog_groups = format_and_process(MAIL_TEMPLATE, to='groups@example.com, other@gmail.com') + # Test: one group created by Sylvie + self.assertTrue(len(frog_groups) == 1) + frog_group = self.mail_group.browse(cr, uid, frog_groups[0]) + # Test: one message that is the incoming email + self.assertEqual(len(frog_group.message_ids), 1, + 'message_process: newly created group should have the incoming email in message_ids') + # Test: author (and not recipient) added as follower + frog_follower_ids = set([p.id for p in frog_group.message_follower_ids]) + self.assertEqual(frog_follower_ids, set([p1id]), + 'message_process: newly created group should have 1 follower (author, not creator, not recipients)') + # Test: sent emails: no-one, no bounce effet + self.assertEqual(len(sent_emails), 0, + 'message_process: should not bounce incoming emails') + + # -------------------------------------------------- + # Test2: discussion update + # -------------------------------------------------- + + # Do: even with a wrong destination, a reply should end up in the correct thread + frog_groups = format_and_process(MAIL_TEMPLATE, email_from='other@gmail.com', + to='erroneous@example.com>', subject='Re: news', + extra='In-Reply-To: <12321321-openerp-%d-mail.group@example.com>\n' % frog_group.id) + # Test: no group 'Re: news' created, still only 1 Frogs group + self.assertEqual(len(frog_groups), 0, + 'message_process: reply on Frogs should not have created a new group with new subject') + frog_groups = self.mail_group.search(cr, uid, [('name', '=', 'Frogs')]) + self.assertEqual(len(frog_groups), 1, + 'message_process: reply on Frogs should not have created a duplicate group with old subject') + frog_group = self.mail_group.browse(cr, uid, frog_groups[0]) + # Test: one new message + self.assertTrue(len(frog_group.message_ids) == 2, 'message_process: group should contain 2 messages after reply') + # Test: author (and not recipient) added as follower + frog_follower_ids = set([p.id for p in frog_group.message_follower_ids]) + self.assertEqual(frog_follower_ids, set([p1id, p2id]), + 'message_process: after reply, group should have 2 followers') + + # -------------------------------------------------- + # Test3: misc gateway features + # -------------------------------------------------- + + # Do: incoming email with model that does not accepts incoming emails must raise + self.assertRaises(AssertionError, + format_and_process, + MAIL_TEMPLATE, to='noone@example.com', subject='spam', extra='', model='res.country') + + # Do: incoming email without model and without alias must raise + self.assertRaises(AssertionError, + format_and_process, + MAIL_TEMPLATE, to='noone@example.com', subject='spam', extra='') + + # Do: incoming email with model that accepting incoming emails as fallback + frog_groups = format_and_process(MAIL_TEMPLATE, to='noone@example.com', subject='Spammy', extra='', model='mail.group') + self.assertEqual(len(frog_groups), 1, + 'message_process: erroneous email but with a fallback model should have created a new mail.group') + + # Do: incoming email in plaintext should be stored as html + frog_groups = format_and_process(MAIL_TEMPLATE_PLAINTEXT, to='groups@example.com', subject='Frogs Return', extra='', msg_id='') + # Test: one group created with one message + self.assertTrue(len(frog_groups) == 1) + frog_group = self.mail_group.browse(cr, uid, frog_groups[0]) + msg = frog_group.message_ids[0] + # Test: plain text content should be wrapped and stored as html + self.assertEqual(msg.body, '

\nPlease call me as soon as possible this afternoon!\n\n--\nSylvie\n
', + 'message_process: plaintext incoming email incorrectly parsed') + + def test_10_thread_parent_resolution(self): + """ Testing parent/child relationships are correctly established when processing incoming mails """ + cr, uid = self.cr, self.uid + + def format(template, to='Pretty Pigs , other@gmail.com', subject='Re: 1', + extra='', email_from='Sylvie Lelitre ', + msg_id='<1198923581.41972151344608186760.JavaMail@agrolait.com>'): + return template.format(to=to, subject=subject, extra=extra, email_from=email_from, msg_id=msg_id) + + group_pigs = self.mail_group.browse(cr, uid, self.group_pigs_id) + msg1 = group_pigs.message_post(body='My Body', subject='1') + msg2 = group_pigs.message_post(body='My Body', subject='2') + msg1, msg2 = self.mail_message.browse(cr, uid, [msg1, msg2]) + self.assertTrue(msg1.message_id, "message_process: new message should have a proper message_id") + + # Reply to msg1, make sure the reply is properly attached using the various reply identification mechanisms + # 0. Direct alias match + reply_msg1 = format(MAIL_TEMPLATE, to='Pretty Pigs ', extra='In-Reply-To: %s' % msg1.message_id) + self.mail_group.message_process(cr, uid, None, reply_msg1) + + # 1. In-Reply-To header + reply_msg2 = format(MAIL_TEMPLATE, to='erroneous@example.com', extra='In-Reply-To: %s' % msg1.message_id) + self.mail_group.message_process(cr, uid, None, reply_msg2) + + # 2. References header + reply_msg3 = format(MAIL_TEMPLATE, to='erroneous@example.com', extra='References: <2233@a.com>\r\n\t<3edss_dsa@b.com> %s' % msg1.message_id) + self.mail_group.message_process(cr, uid, None, reply_msg3) + + # 3. Subject contains [] + model passed to message+process -> only attached to group, but not to mail (not in msg1.child_ids) + reply_msg4 = format(MAIL_TEMPLATE, to='erroneous@example.com', extra='', subject='Re: [%s] 1' % self.group_pigs_id) + self.mail_group.message_process(cr, uid, 'mail.group', reply_msg4) + + group_pigs.refresh() + msg1.refresh() + self.assertEqual(6, len(group_pigs.message_ids), 'message_process: group should contain 6 messages') + self.assertEqual(3, len(msg1.child_ids), 'message_process: msg1 should have 3 children now') + + def test_20_private_discussion(self): + """ Testing private discussion between partners. """ + pass diff --git a/addons/mail/wizard/mail_compose_message.py b/addons/mail/wizard/mail_compose_message.py index 0591d7fd932..7e5b437623f 100644 --- a/addons/mail/wizard/mail_compose_message.py +++ b/addons/mail/wizard/mail_compose_message.py @@ -23,6 +23,7 @@ import base64 import re from openerp import tools +from openerp import SUPERUSER_ID from openerp.osv import osv from openerp.osv import fields from openerp.tools.safe_eval import safe_eval as eval @@ -126,6 +127,29 @@ class mail_compose_message(osv.TransientModel): 'partner_ids': lambda self, cr, uid, ctx={}: [], } + def check_access_rule(self, cr, uid, ids, operation, context=None): + """ Access rules of mail.compose.message: + - create: if + - model, no res_id, I create a message in mass mail mode + - then: fall back on mail.message acces rules + """ + if isinstance(ids, (int, long)): + ids = [ids] + + # Author condition (CREATE (mass_mail)) + if operation == 'create' and uid != SUPERUSER_ID: + # read mail_compose_message.ids to have their values + message_values = {} + cr.execute('SELECT DISTINCT id, model, res_id FROM "%s" WHERE id = ANY (%%s) AND res_id = 0' % self._table, (ids,)) + for id, rmod, rid in cr.fetchall(): + message_values[id] = {'model': rmod, 'res_id': rid} + # remove from the set to check the ids that mail_compose_message accepts + author_ids = [mid for mid, message in message_values.iteritems() + if message.get('model') and not message.get('res_id')] + ids = list(set(ids) - set(author_ids)) + + return super(mail_compose_message, self).check_access_rule(cr, uid, ids, operation, context=context) + def _notify(self, cr, uid, newid, context=None): """ Override specific notify method of mail.message, because we do not want that feature in the wizard. """ @@ -218,8 +242,11 @@ class mail_compose_message(osv.TransientModel): post_values.update(email_dict) # post the message subtype = 'mail.mt_comment' - if is_log or mass_mail_mode: + if is_log: # log a note: subtype is False subtype = False + elif mass_mail_mode: # mass mail: is a log pushed to recipients, author not added + subtype = False + context = dict(context, mail_create_nosubscribe=True) # add context key to avoid subscribing the author msg_id = active_model_pool.message_post(cr, uid, [res_id], type='comment', subtype=subtype, context=context, **post_values) # mass_mailing: notify specific partners, because subtype was False, and no-one was notified if mass_mail_mode and post_values['partner_ids']: