def test_mail_message_values_document_alias(self): msg = self.Message.create({ 'model': 'mail.test', 'res_id': self.alias_record.id }) self.assertIn('-openerp-%d-mail.test' % self.alias_record.id, msg.message_id.split('@')[0]) reply_to_name = '%s %s' % (self.env.user.company_id.name, self.alias_record.name) reply_to_email = '%s@%s' % (self.alias_record.alias_name, self.alias_domain) self.assertEqual(msg.reply_to, formataddr((reply_to_name, reply_to_email))) self.assertEqual(msg.email_from, '"%s" <%s>' % (self.user_employee.name, self.user_employee.email)) # no alias domain -> author self.env['ir.config_parameter'].search([('key', '=', 'mail.catchall.domain')]).unlink() msg = self.Message.create({ 'model': 'mail.test', 'res_id': self.alias_record.id }) self.assertIn('-openerp-%d-mail.test' % self.alias_record.id, msg.message_id.split('@')[0]) self.assertEqual(msg.reply_to, formataddr((self.user_employee.name, self.user_employee.email))) self.assertEqual(msg.email_from, formataddr((self.user_employee.name, self.user_employee.email))) # no catchall -> don't care, alias self.env['ir.config_parameter'].set_param('mail.catchall.domain', self.alias_domain) self.env['ir.config_parameter'].search([('key', '=', 'mail.catchall.alias')]).unlink() msg = self.Message.create({ 'model': 'mail.test', 'res_id': self.alias_record.id }) self.assertIn('-openerp-%d-mail.test' % self.alias_record.id, msg.message_id.split('@')[0]) reply_to_name = '%s %s' % (self.env.company.name, self.alias_record.name) reply_to_email = '%s@%s' % (self.alias_record.alias_name, self.alias_domain) self.assertEqual(msg.reply_to, formataddr((reply_to_name, reply_to_email))) self.assertEqual(msg.email_from, formataddr((self.user_employee.name, self.user_employee.email)))
def test_channel_mailing_list_recipients(self): """ Posting a message on a mailing list should send one email to all recipients """ self.env['ir.config_parameter'].set_param('mail.catchall.domain', 'schlouby.fr') self.test_channel.write({'email_send': True}) # Subscribe an user without email. We shouldn't try to send email to them. nomail = self.env['res.users'].create({ "login": "******", "name": "No Mail", "email": False, "notification_type": "email", }) self._join_channel( self.test_channel, self.user_employee.partner_id | self.test_partner | nomail.partner_id) self.test_channel.message_post(body="Test", message_type='comment', subtype='mt_comment') self.assertEqual(len(self._mails), 1) for email in self._mails: self.assertEqual( set(email['email_to']), set([ formataddr( (self.user_employee.name, self.user_employee.email)), formataddr( (self.test_partner.name, self.test_partner.email)) ]))
def test_mail_message_values_document_no_alias(self): test_record = self.env['mail.test.simple'].create({'name': 'Test', 'email_from': '*****@*****.**'}) msg = self.Message.create({ 'model': 'mail.test.simple', 'res_id': test_record.id }) self.assertIn('-openerp-%d-mail.test.simple' % test_record.id, msg.message_id.split('@')[0]) reply_to_name = '%s %s' % (self.env.user.company_id.name, test_record.name) reply_to_email = '%s@%s' % (self.alias_catchall, self.alias_domain) self.assertEqual(msg.reply_to, formataddr((reply_to_name, reply_to_email))) self.assertEqual(msg.email_from, formataddr((self.user_employee.name, self.user_employee.email)))
def _compute_email_formatted(self): for partner in self: if partner.email: partner.email_formatted = tools.formataddr( (partner.name or u"False", partner.email or u"False")) else: partner.email_formatted = ''
def _create_new_message(self, channel_id, status='pending_moderation', author=None, body='', message_type="email"): author = author if author else self.env.user.partner_id message = self.env['mail.message'].create({ 'model': 'mail.channel', 'res_id': channel_id, 'message_type': 'email', 'body': body, 'moderation_status': status, 'author_id': author.id, 'email_from': formataddr((author.name, author.email)), 'subtype_id': self.env['mail.message.subtype'].search([('name', '=', 'Discussions')]).id }) return message
def test_email_formataddr(self): email = '*****@*****.**' cases = [ # (name, address), charsets expected (('', email), ['ascii', 'utf-8'], '*****@*****.**'), (('joe', email), ['ascii', 'utf-8'], '"joe" <*****@*****.**>'), (('joe doe', email), ['ascii', 'utf-8'], '"joe doe" <*****@*****.**>'), (('joe"doe', email), ['ascii', 'utf-8'], '"joe\\"doe" <*****@*****.**>'), (('joé', email), ['ascii'], '=?utf-8?b?am/DqQ==?= <*****@*****.**>'), (('joé', email), ['utf-8'], '"joé" <*****@*****.**>'), (('', 'joé@example.com'), ['ascii', 'utf-8'], UnicodeEncodeError), # need SMTPUTF8 support (('', 'joe@examplé.com'), ['ascii', 'utf-8'], UnicodeEncodeError), # need IDNA support ] for pair, charsets, expected in cases: for charset in charsets: with self.subTest(pair=pair, charset=charset): if isinstance(expected, str): self.assertEqual(formataddr(pair, charset), expected) else: self.assertRaises(expected, formataddr, pair, charset)
def test_channel_blacklisted_recipients(self): """ Posting a message on a channel should send one email to all recipients, except the blacklisted ones """ self.test_channel = self.env['mail.channel'].create({ 'name': 'Test', 'description': 'Description', 'alias_name': 'test', 'public': 'public', }) self.test_partner = self.env['res.partner'].create({ 'name': 'Test Partner', 'email': '*****@*****.**', }) self.blacklisted_partner = self.env['res.partner'].create({ 'name': 'Blacklisted Partner', 'email': '*****@*****.**', }) # Set Blacklist self.env['mail.blacklist'].create({ 'email': '*****@*****.**', }) self.env['ir.config_parameter'].set_param('mail.catchall.domain', 'schlouby.fr') self.test_channel.write({'email_send': True}) self._join_channel(self.test_channel, self.test_partner) self.test_channel.message_post(body="Test", message_type='comment', subtype='mt_comment') self.assertEqual(len(self._mails), 1, 'Number of mail incorrect. Should be equal to 1.') for email in self._mails: self.assertEqual( set(email['email_to']), set([ formataddr( (self.test_partner.name, self.test_partner.email)) ]), 'email_to incorrect. Should be equal to "%s"' % (formataddr( (self.test_partner.name, self.test_partner.email))))
def test_channel_classic_recipients(self): """ Posting a message on a classic channel should work like classic post """ self.test_channel.write({'alias_name': False}) self.test_channel.message_subscribe( [self.user_employee.partner_id.id, self.test_partner.id]) self.test_channel.message_post(body="Test", message_type='comment', subtype='mt_comment') sent_emails = self._mails self.assertEqual(len(sent_emails), 2) for email in sent_emails: self.assertIn(email['email_to'][0], [ formataddr( (self.user_employee.name, self.user_employee.email)), formataddr((self.test_partner.name, self.test_partner.email)) ])
def _mail_cc_sanitized_raw_dict(self, cc_string): '''return a dict of sanitize_email:raw_email from a string of cc''' if not cc_string: return {} return { tools.email_normalize(email): tools.formataddr( (name, tools.email_normalize(email))) for (name, email) in tools.email_split_tuples(cc_string) }
def test_mail_message_values_document_manual_alias(self): test_record = self.env['mail.test.simple'].create({'name': 'Test', 'email_from': '*****@*****.**'}) alias = self.env['mail.alias'].create({ 'alias_name': 'MegaLias', 'alias_user_id': False, 'alias_model_id': self.env['ir.model']._get('mail.test.simple').id, 'alias_parent_model_id': self.env['ir.model']._get('mail.test.simple').id, 'alias_parent_thread_id': test_record.id, }) msg = self.Message.create({ 'model': 'mail.test.simple', 'res_id': test_record.id }) self.assertIn('-openerp-%d-mail.test.simple' % test_record.id, msg.message_id.split('@')[0]) reply_to_name = '%s %s' % (self.env.user.company_id.name, test_record.name) reply_to_email = '%s@%s' % (alias.alias_name, self.alias_domain) self.assertEqual(msg.reply_to, formataddr((reply_to_name, reply_to_email))) self.assertEqual(msg.email_from, formataddr((self.user_employee.name, self.user_employee.email)))
def test_post_log(self): new_note = self.test_record.with_user(self.user_employee)._message_log( body='<p>Labrador</p>', ) self.assertEqual(new_note.subtype_id, self.env.ref('mail.mt_note')) self.assertEqual(new_note.body, '<p>Labrador</p>') self.assertEqual(new_note.author_id, self.user_employee.partner_id) self.assertEqual( new_note.email_from, formataddr((self.user_employee.name, self.user_employee.email))) self.assertEqual(new_note.notified_partner_ids, self.env['res.partner'])
def test_post_notify(self): self.user_employee.write({'notification_type': 'inbox'}) new_notification = self.test_record.message_notify( subject='This should be a subject', body='<p>You have received a notification</p>', partner_ids=[self.partner_1.id, self.user_employee.partner_id.id], ) self.assertEqual(new_notification.subtype_id, self.env.ref('mail.mt_note')) self.assertEqual(new_notification.message_type, 'user_notification') self.assertEqual(new_notification.body, '<p>You have received a notification</p>') self.assertEqual(new_notification.author_id, self.env.user.partner_id) self.assertEqual(new_notification.email_from, formataddr((self.env.user.name, self.env.user.email))) self.assertEqual(new_notification.notified_partner_ids, self.partner_1 | self.user_employee.partner_id) self.assertNotIn(new_notification, self.test_record.message_ids)
def test_complex_mail_mail_send(self): message = self.env['mail.message'].sudo().create({ 'subject': 'Test', 'body': '<p>Test</p>', 'author_id': self.env.user.partner_id.id, 'email_from': self.env.user.partner_id.email, 'model': 'mail.test', 'res_id': self.umbrella.id, }) mail = self.env['mail.mail'].sudo().create({ 'body_html': '<p>Test</p>', 'mail_message_id': message.id, 'recipient_ids': [(4, pid) for pid in self.partners.ids], }) mail_ids = mail.ids with self.assertQueryCount(__system__=8, emp=9): self.env['mail.mail'].browse(mail_ids).send() self.assertEqual(mail.body_html, '<p>Test</p>') self.assertEqual(mail.reply_to, formataddr(('%s %s' % (self.env.company.name, self.umbrella.name), '*****@*****.**')))
def _send_prepare_values(self, partner=None): """Return a dictionary for specific email values, depending on a partner, or generic to the whole recipients given by mail.email_to. :param Model partner: specific recipient partner """ self.ensure_one() body = self._send_prepare_body() body_alternative = tools.html2plaintext(body) if partner: email_to = [ tools.formataddr((partner.name or 'False', partner.email or 'False')) ] else: email_to = tools.email_split_and_format(self.email_to) res = { 'body': body, 'body_alternative': body_alternative, 'email_to': email_to, } return res
def test_message_post(self): email1 = '*****@*****.**' email2 = '*****@*****.**' self.channel_1._update_moderation_email([email1], 'ban') self.channel_1._update_moderation_email([email2], 'allow') msg_admin = self.channel_1.message_post( message_type='email', subtype='mt_comment', author_id=self.partner_admin.id) msg_moderator = self.channel_1.message_post( message_type='comment', subtype='mt_comment', author_id=self.partner_employee.id) msg_email1 = self.channel_1.message_post(message_type='comment', subtype='mt_comment', email_from=formataddr( ("MyName", email1))) msg_email2 = self.channel_1.message_post(message_type='email', subtype='mt_comment', email_from=email2) msg_notif = self.channel_1.message_post() messages = self.env['mail.message'].search([ ('model', '=', 'mail.channel'), ('res_id', '=', self.channel_1.id) ]) pending_messages = messages.filtered( lambda m: m.moderation_status == 'pending_moderation') accepted_messages = messages.filtered( lambda m: m.moderation_status == 'accepted') self.assertFalse(msg_email1) self.assertEqual(msg_admin, pending_messages) self.assertEqual(accepted_messages, msg_moderator | msg_email2 | msg_notif) self.assertFalse(msg_admin.channel_ids) self.assertEqual(msg_email2.channel_ids, self.channel_1)
def test_message_track_template(self): """ Update some tracked fields linked to some template -> message with onchange """ self.record.write({ 'mail_template': self.env.ref('test_mail.mail_test_full_tracking_tpl').id }) self.assertEqual(self.record.message_ids, self.env['mail.message']) self.record.write({ 'name': 'Test2', 'customer_id': self.user_admin.partner_id.id, }) self.assertEqual( len(self.record.message_ids), 2, 'should have 2 new messages: one for tracking, one for template') # one new message containing the template linked to tracking self.assertEqual(self.record.message_ids[0].subject, 'Test Template') self.assertEqual(self.record.message_ids[0].body, '<p>Hello Test2</p>') # one email send due to template self.assertEqual(len(self._mails), 1) self.assertEqual( set(self._mails[0]['email_to']), set([formataddr((self.user_admin.name, self.user_admin.email))])) self.assertHtmlEqual(self._mails[0]['body'], '<p>Hello Test2</p>') # one new message containing tracking; without subtype linked to tracking self.assertEqual(self.record.message_ids[1].subtype_id, self.env.ref('mail.mt_note')) self.assertTracking( self.record.message_ids[1], [('customer_id', 'many2one', False, self.user_admin.partner_id ) # onchange tracked field ])
def test_mail_message_values_no_document(self): msg = self.Message.create({}) self.assertIn('-private', msg.message_id.split('@')[0], 'mail_message: message_id for a void message should be a "private" one') reply_to_name = self.env.user.company_id.name reply_to_email = '%s@%s' % (self.alias_catchall, self.alias_domain) self.assertEqual(msg.reply_to, formataddr((reply_to_name, reply_to_email))) self.assertEqual(msg.email_from, formataddr((self.user_employee.name, self.user_employee.email))) # no alias domain -> author self.env['ir.config_parameter'].search([('key', '=', 'mail.catchall.domain')]).unlink() msg = self.Message.create({}) self.assertIn('-private', msg.message_id.split('@')[0], 'mail_message: message_id for a void message should be a "private" one') self.assertEqual(msg.reply_to, formataddr((self.user_employee.name, self.user_employee.email))) self.assertEqual(msg.email_from, formataddr((self.user_employee.name, self.user_employee.email))) # no alias catchall, no alias -> author self.env['ir.config_parameter'].set_param('mail.catchall.domain', self.alias_domain) self.env['ir.config_parameter'].search([('key', '=', 'mail.catchall.alias')]).unlink() msg = self.Message.create({}) self.assertIn('-private', msg.message_id.split('@')[0], 'mail_message: message_id for a void message should be a "private" one') self.assertEqual(msg.reply_to, formataddr((self.user_employee.name, self.user_employee.email))) self.assertEqual(msg.email_from, formataddr((self.user_employee.name, self.user_employee.email)))
def default_get(self, fields): """ Handle composition mode. Some details about context keys: - comment: default mode, model and ID of a record the user comments - default_model or active_model - default_res_id or active_id - reply: active_id of a message the user replies to - default_parent_id or message_id or active_id: ID of the mail.message we reply to - message.res_model or default_model - message.res_id or default_res_id - mass_mail: model and IDs of records the user mass-mails - active_ids: record IDs - default_model or active_model """ result = super(MailComposer, self).default_get(fields) # author if 'author_id' not in result: result['author_id'] = self.env.user.partner_id.id if 'email_from' not in result and self.env.user.email: result['email_from'] = self.env.user.email_formatted elif 'email_from' not in result: author = self.env['res.partner'].browse(result['author_id']) if author.email: result['email_from'] = tools.formataddr( (author.name, author.email)) # v6.1 compatibility mode result['composition_mode'] = result.get( 'composition_mode', self._context.get('mail.compose.message.mode', 'comment')) result['model'] = result.get('model', self._context.get('active_model')) result['res_id'] = result.get('res_id', self._context.get('active_id')) result['parent_id'] = result.get('parent_id', self._context.get('message_id')) if 'no_auto_thread' not in result and ( result['model'] not in self.env or not hasattr(self.env[result['model']], 'message_post')): result['no_auto_thread'] = True # default values according to composition mode - NOTE: reply is deprecated, fall back on comment if result['composition_mode'] == 'reply': result['composition_mode'] = 'comment' vals = {} if 'active_domain' in self._context: # not context.get() because we want to keep global [] domains vals['active_domain'] = '%s' % self._context.get('active_domain') if result['composition_mode'] == 'comment': vals.update(self.get_record_data(result)) for field in vals: if field in fields: result[field] = vals[field] # TDE HACK: as mailboxes used default_model='res.users' and default_res_id=uid # (because of lack of an accessible pid), creating a message on its own # profile may crash (res_users does not allow writing on it) # Posting on its own profile works (res_users redirect to res_partner) # but when creating the mail.message to create the mail.compose.message # access rights issues may rise # We therefore directly change the model and res_id if result['model'] == 'res.users' and result['res_id'] == self._uid: result['model'] = 'res.partner' result['res_id'] = self.env.user.partner_id.id if fields is not None: [ result.pop(field, None) for field in list(result) if field not in fields ] return result
def assertEmails(self, partner_from, recipients, **values): """ Tools method to ease the check of send emails """ expected_email_values = [] for partners in recipients: if partner_from: email_from = formataddr( (partner_from.name, partner_from.email)) else: email_from = values['email_from'] expected = { 'email_from': email_from, 'email_to': [ formataddr((partner.name, partner.email)) for partner in partners ] } if 'reply_to' in values: expected['reply_to'] = values['reply_to'] if 'subject' in values: expected['subject'] = values['subject'] if 'attachments' in values: expected['attachments'] = values['attachments'] if 'body' in values: expected['body'] = values['body'] if 'body_content' in values: expected['body_content'] = values['body_content'] if 'body_alt_content' in values: expected['body_alternative_content'] = values[ 'body_alt_content'] if 'references' in values: expected['references'] = values['references'] if 'ref_content' in values: expected['references_content'] = values['ref_content'] expected_email_values.append(expected) self.assertEqual(len(self._mails), len(expected_email_values)) for expected in expected_email_values: sent_mail = next( (mail for mail in self._mails if set(mail['email_to']) == set(expected['email_to'])), False) self.assertTrue( bool(sent_mail), 'Expected mail to %s not found' % expected['email_to']) for val in [ 'email_from', 'reply_to', 'subject', 'body', 'references', 'attachments' ]: if val in expected: self.assertEqual( expected[val], sent_mail[val], 'Value for %s: expected %s, received %s' % (val, expected[val], sent_mail[val])) for val in [ 'body_content', 'body_alternative', 'references_content' ]: if val in expected: self.assertIn( expected[val], sent_mail[val[:-8]], 'Value for %s: %s does not contain %s' % (val, sent_mail[val[:-8]], expected[val]))
def _default_email_from(self): if self.env.user.email: return formataddr((self.env.user.name, self.env.user.email)) raise UserError( _("Unable to post message, please configure the sender's email address." ))