[IMP] mail: added and cleaned the tests. Fixed various small bugs in composer. Fixed mail_message.message_read() and flattening algorithm. Cleaned tests for message_read to have a more precise test.
bzr revid: tde@openerp.com-20120905155319-wd168m5snb25ari9
This commit is contained in:
parent
b5b813162d
commit
9fb978ea81
|
@ -20,10 +20,12 @@
|
|||
##############################################################################
|
||||
|
||||
import logging
|
||||
from email.header import decode_header
|
||||
from osv import osv, fields
|
||||
import tools
|
||||
|
||||
from email.header import decode_header
|
||||
from operator import itemgetter
|
||||
from osv import osv, fields
|
||||
|
||||
_logger = logging.getLogger(__name__)
|
||||
|
||||
""" Some tools for parsing / creating email fields """
|
||||
|
@ -45,7 +47,7 @@ class mail_message(osv.Model):
|
|||
_message_record_name_length = 18
|
||||
|
||||
def _shorten_name(self, name):
|
||||
if len(name) <= (self._message_record_name_length+3):
|
||||
if len(name) <= (self._message_record_name_length + 3):
|
||||
return name
|
||||
return name[:self._message_record_name_length] + '...'
|
||||
|
||||
|
@ -73,7 +75,7 @@ class mail_message(osv.Model):
|
|||
return res
|
||||
|
||||
def _search_unread(self, cr, uid, obj, name, domain, context=None):
|
||||
""" Search for messages unread by the current user. Condition is
|
||||
""" Search for messages unread by the current user. Condition is
|
||||
inversed because we search unread message on a read column. """
|
||||
if domain[0][2]:
|
||||
read_cond = '(read = false or read is null)'
|
||||
|
@ -135,7 +137,7 @@ class mail_message(osv.Model):
|
|||
_defaults = {
|
||||
'type': 'email',
|
||||
'date': lambda *a: fields.datetime.now(),
|
||||
'author_id': lambda self,cr,uid,ctx: self._get_default_author(cr, uid, ctx),
|
||||
'author_id': lambda self, cr, uid, ctx={}: self._get_default_author(cr, uid, ctx),
|
||||
'body': '',
|
||||
}
|
||||
|
||||
|
@ -169,12 +171,13 @@ class mail_message(osv.Model):
|
|||
""" Given a tree with several roots of following structure :
|
||||
[ {'id': 1, 'child_ids': [
|
||||
{'id': 11, 'child_ids': [...] },],
|
||||
},
|
||||
{...} ]
|
||||
Flatten it to have a maximum number of level, with 0 being
|
||||
completely flat.
|
||||
Flatten it to have a maximum number of levels, 0 being flat and
|
||||
sort messages in a level according to a key of the messages.
|
||||
Perform the flattening at leafs if above the maximum depth, then get
|
||||
back in the tree.
|
||||
:param context: ``sort_key``: key for sorting (id by default)
|
||||
:param context: ``sort_reverse``: reverser order for sorting (True by default)
|
||||
"""
|
||||
def _flatten(msg_dict):
|
||||
""" from {'id': x, 'child_ids': [{child1}, {child2}]}
|
||||
|
@ -183,19 +186,22 @@ class mail_message(osv.Model):
|
|||
child_ids = msg_dict.pop('child_ids', [])
|
||||
msg_dict['child_ids'] = []
|
||||
return [msg_dict] + child_ids
|
||||
# return sorted([msg_dict] + child_ids, key=itemgetter('id'), reverse=True)
|
||||
context = context or {}
|
||||
# Depth-first flattening
|
||||
for message in messages:
|
||||
if message.get('type') == 'expandable':
|
||||
continue
|
||||
message['child_ids'] = self.message_read_tree_flatten(cr, uid, message['child_ids'], current_level+1, level, context=context)
|
||||
message['child_ids'] = self.message_read_tree_flatten(cr, uid, message['child_ids'], current_level + 1, level, context=context)
|
||||
# Flatten if above maximum depth
|
||||
if current_level < level:
|
||||
return messages
|
||||
new_list = []
|
||||
for message in messages:
|
||||
for flat_message in _flatten(message):
|
||||
new_list.append(flat_message)
|
||||
return new_list
|
||||
return_list = messages
|
||||
else:
|
||||
return_list = []
|
||||
for message in messages:
|
||||
for flat_message in _flatten(message):
|
||||
return_list.append(flat_message)
|
||||
return sorted(return_list, key=itemgetter(context.get('sort_key', 'id')), reverse=context.get('sort_reverse', True))
|
||||
|
||||
def message_read(self, cr, uid, ids=False, domain=[], thread_level=0, limit=None, context=None):
|
||||
""" If IDs are provided, fetch these records. Otherwise use the domain
|
||||
|
@ -213,7 +219,7 @@ class mail_message(osv.Model):
|
|||
result = []
|
||||
tree = {} # key: ID, value: record
|
||||
for msg in messages:
|
||||
if len(result)<(limit-1):
|
||||
if len(result) < (limit - 1):
|
||||
record = self._message_dict_get(cr, uid, msg, context=context)
|
||||
if thread_level and msg.parent_id:
|
||||
while msg.parent_id:
|
||||
|
@ -233,7 +239,7 @@ class mail_message(osv.Model):
|
|||
else:
|
||||
result.append({
|
||||
'type': 'expandable',
|
||||
'domain': [('id','<=', msg.id)]+domain,
|
||||
'domain': [('id', '<=', msg.id)] + domain,
|
||||
'context': context,
|
||||
'thread_level': thread_level # should be improve accodting to level of records
|
||||
})
|
||||
|
@ -279,7 +285,7 @@ class mail_message(osv.Model):
|
|||
if not (rmod and rid):
|
||||
continue
|
||||
document_ids.append(id)
|
||||
model_record_ids.setdefault(rmod,set()).add(rid)
|
||||
model_record_ids.setdefault(rmod, set()).add(rid)
|
||||
for model, mids in model_record_ids.items():
|
||||
model_obj = self.pool.get(model)
|
||||
mids = model_obj.exists(cr, uid, mids)
|
||||
|
@ -292,7 +298,7 @@ class mail_message(osv.Model):
|
|||
|
||||
def create(self, cr, uid, values, context=None):
|
||||
if not values.get('message_id') and values.get('res_id') and values.get('model'):
|
||||
values['message_id'] = tools.generate_tracking_message_id('%(model)s-%(res_id)s'% values)
|
||||
values['message_id'] = tools.generate_tracking_message_id('%(model)s-%(res_id)s' % values)
|
||||
newid = super(mail_message, self).create(cr, uid, values, context)
|
||||
self.notify(cr, uid, newid, context=context)
|
||||
return newid
|
||||
|
@ -333,4 +339,4 @@ class mail_message(osv.Model):
|
|||
if default is None:
|
||||
default = {}
|
||||
default.update(message_id=False, headers=False)
|
||||
return super(mail_message,self).copy(cr, uid, id, default=default, context=context)
|
||||
return super(mail_message, self).copy(cr, uid, id, default=default, context=context)
|
||||
|
|
|
@ -19,8 +19,6 @@
|
|||
#
|
||||
##############################################################################
|
||||
|
||||
import tools
|
||||
|
||||
from openerp.tests import common
|
||||
|
||||
MAIL_TEMPLATE = """Return-Path: <whatever-2a840@postmaster.twitter.com>
|
||||
|
@ -93,10 +91,9 @@ class test_mail(common.TransactionCase):
|
|||
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]
|
||||
self.mail_group_model_id = self.ir_model.search(self.cr, self.uid, [('model', '=', 'mail.group')])[0]
|
||||
self.mail_alias.create(self.cr, self.uid, {'alias_name': 'groups',
|
||||
'alias_model_id': self.mail_group_model_id})
|
||||
|
||||
# create a 'pigs' group that will be used through the various tests
|
||||
self.group_pigs_id = self.mail_group.create(self.cr, self.uid,
|
||||
{'name': 'Pigs', 'description': 'Fans of Pigs, unite !'})
|
||||
|
@ -104,10 +101,10 @@ class test_mail(common.TransactionCase):
|
|||
def test_00_message_process(self):
|
||||
cr, uid = self.cr, self.uid
|
||||
# Incoming mail creates a new mail_group "frogs"
|
||||
self.assertEqual(self.mail_group.search(cr, uid, [('name','=','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')])
|
||||
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@...
|
||||
|
@ -121,11 +118,11 @@ class test_mail(common.TransactionCase):
|
|||
|
||||
# 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)
|
||||
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,
|
||||
|
@ -192,7 +189,7 @@ class test_mail(common.TransactionCase):
|
|||
self.assertEqual(follower_ids, set([partner_bert_id]), 'Bert should be the follower of dummy mail.thread data')
|
||||
fol_obj_ids = self.mail_followers.search(cr, uid, [('res_model', '=', 'mail.group'), ('res_id', '=', group_dummy_id)])
|
||||
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')
|
||||
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(self):
|
||||
""" Tests designed for the subscriber API. """
|
||||
|
@ -252,9 +249,8 @@ class test_mail(common.TransactionCase):
|
|||
msg_id = self.mail_group.message_post(cr, uid, self.group_pigs_id, body=_body1, subject=_subject, type='comment')
|
||||
message = self.mail_message.browse(cr, uid, msg_id)
|
||||
sent_email = self._build_email_kwargs
|
||||
|
||||
self.assertFalse(self.mail_mail.search(cr, uid, [('mail_message_id','=',msg_id)]), 'mail.mail notifications should have been auto-deleted!')
|
||||
|
||||
# Test: notifications have been deleted
|
||||
self.assertFalse(self.mail_mail.search(cr, uid, [('mail_message_id', '=', msg_id)]), 'mail.mail notifications should have been auto-deleted!')
|
||||
# Test: mail_message: subject is _subject, body is _body1 (no formatting done)
|
||||
self.assertEqual(message.subject, _subject, 'mail.message subject incorrect')
|
||||
self.assertEqual(message.body, _body1, 'mail.message body incorrect')
|
||||
|
@ -279,7 +275,7 @@ class test_mail(common.TransactionCase):
|
|||
partner_ids=[(6, 0, [p_d_id])], parent_id=msg_id, attachments=_attachments)
|
||||
message = self.mail_message.browse(cr, uid, msg_id2)
|
||||
sent_email = self._build_email_kwargs
|
||||
self.assertFalse(self.mail_mail.search(cr, uid, [('mail_message_id','=',msg_id2)]), 'mail.mail notifications should have been auto-deleted!')
|
||||
self.assertFalse(self.mail_mail.search(cr, uid, [('mail_message_id', '=', msg_id2)]), '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(message.subject, False, 'mail.message subject incorrect')
|
||||
|
@ -300,11 +296,11 @@ class test_mail(common.TransactionCase):
|
|||
# Test: sent_email: email_to should contain b@b, c@c, not a@a (writer)
|
||||
self.assertEqual(set(sent_email['email_to']), set(['b@b', 'c@c']), 'sent_email email_to incorrect')
|
||||
# Test: attachments
|
||||
for i in range(len(message.attachment_ids)):
|
||||
self.assertEqual(message.attachment_ids[i].name, _attachments[i][0], 'mail.message attachment name incorrect')
|
||||
self.assertEqual(message.attachment_ids[i].res_model, 'mail.group', 'mail.message attachment res_model incorrect')
|
||||
self.assertEqual(message.attachment_ids[i].res_id, self.group_pigs_id, 'mail.message attachment res_id incorrect')
|
||||
self.assertEqual(message.attachment_ids[i].datas.decode('base64'), _attachments[i][1], 'mail.message attachment data incorrect')
|
||||
for attach in message.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')
|
||||
|
||||
def test_21_message_compose_wizard(self):
|
||||
""" Tests designed for the mail.compose.message wizard. """
|
||||
|
@ -313,19 +309,19 @@ class test_mail(common.TransactionCase):
|
|||
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)
|
||||
group_bird_id = self.mail_group.create(cr, uid, {'name': 'Bird', 'description': 'Bird resistance'})
|
||||
group_bird = self.mail_group.browse(cr, uid, group_bird_id)
|
||||
|
||||
# Mail data
|
||||
_subject = 'Pigs'
|
||||
_subject_reply = 'Re: Pigs'
|
||||
_mail_subject = '%s posted on %s' % (user_admin.name, group_pigs.name)
|
||||
_body_text = 'Pigs rules'
|
||||
_msg_body1 = '<pre>Pigs rules</pre>'
|
||||
_body_html = '<html>Pigs rules</html>'
|
||||
_msg_body2 = '<html>Pigs rules</html>'
|
||||
_msg_reply = 'Re: Pigs'
|
||||
_msg_body = '<pre>Pigs rules</pre>'
|
||||
_attachments = [
|
||||
{'name': 'First', 'datas': 'My first attachment'.encode('base64')},
|
||||
{'name': 'Second', 'datas': 'My second attachment'.encode('base64')}
|
||||
{'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')]
|
||||
|
||||
# Create partners
|
||||
# 0 - Admin
|
||||
|
@ -340,23 +336,27 @@ class test_mail(common.TransactionCase):
|
|||
# Subscribe #1
|
||||
group_pigs.message_subscribe([p_b_id])
|
||||
|
||||
# CASE1: comment group_pigs with body_text and subject
|
||||
# ----------------------------------------
|
||||
# CASE1: comment on group_pigs
|
||||
# ----------------------------------------
|
||||
|
||||
# 1. Comment group_pigs with body_text and subject
|
||||
compose_id = mail_compose.create(cr, uid,
|
||||
{'subject': _subject, 'body_text': _body_text, '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})
|
||||
compose = mail_compose.browse(cr, uid, compose_id)
|
||||
# Test: mail.compose.message: model, res_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')
|
||||
|
||||
# Post the comment, get created message
|
||||
# 2. Post the comment, get created message
|
||||
mail_compose.send_mail(cr, uid, [compose_id])
|
||||
group_pigs.refresh()
|
||||
message = group_pigs.message_ids[0]
|
||||
|
||||
# Test: mail.message: subject, body inside pre
|
||||
self.assertEqual(message.subject, False, 'mail.message incorrect subject')
|
||||
self.assertEqual(message.body, _msg_body1, 'mail.message incorrect body')
|
||||
self.assertEqual(message.body, _msg_body, 'mail.message incorrect body')
|
||||
# Test: mail.message: partner_ids = entries in mail.notification: group_pigs fans (a, b) + mail.compose.message partner_ids (c, d)
|
||||
msg_pids = [partner.id for partner in message.partner_ids]
|
||||
test_pids = [p_a_id, p_b_id, p_c_id, p_d_id]
|
||||
|
@ -364,114 +364,165 @@ class test_mail(common.TransactionCase):
|
|||
self.assertEqual(len(notif_ids), 4, 'mail.message: too much notifications created')
|
||||
self.assertEqual(set(msg_pids), set(test_pids), 'mail.message partner_ids incorrect')
|
||||
|
||||
# CASE2: reply to last comment (update its subject) with attachments
|
||||
# ----------------------------------------
|
||||
# CASE2: reply to last comment with 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})
|
||||
compose = mail_compose.browse(cr, uid, compose_id)
|
||||
|
||||
# Test: form view methods
|
||||
# Test: model, res_id, parent_id, content_subtype
|
||||
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')
|
||||
self.assertEqual(compose.content_subtype, 'html', 'mail.compose.message incorrect content_subtype')
|
||||
|
||||
# Post the comment, get created message
|
||||
# 2. Post the comment, get created message
|
||||
mail_compose.send_mail(cr, uid, [compose_id])
|
||||
group_pigs.refresh()
|
||||
message = group_pigs.message_ids[0]
|
||||
# Test: subject as Re:.., body in html
|
||||
self.assertEqual(message.subject, _subject_reply, 'mail.message incorrect subject')
|
||||
# Test: mail.message: subject as Re:.., body in html
|
||||
self.assertEqual(message.subject, _msg_reply, 'mail.message incorrect subject')
|
||||
self.assertIn('Administrator wrote:<blockquote><pre>Pigs rules</pre></blockquote></div>', message.body, 'mail.message body is incorrect')
|
||||
# Test: attachments
|
||||
for i in range(len(message.attachment_ids)):
|
||||
self.assertEqual(message.attachment_ids[i].name, _attachments[i]['name'], 'mail.message attachment name incorrect')
|
||||
self.assertEqual(message.attachment_ids[i].res_model, 'mail.group', 'mail.message attachment res_model incorrect')
|
||||
self.assertEqual(message.attachment_ids[i].res_id, self.group_pigs_id, 'mail.message attachment res_id incorrect')
|
||||
self.assertEqual(message.attachment_ids[i].datas.decode('base64'), _attachments[i]['datas'].decode('base64'), 'mail.message attachment data incorrect')
|
||||
# Test: mail.message: attachments
|
||||
for attach in message.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_test,
|
||||
'mail.message attachment name / data incorrect')
|
||||
|
||||
# CASE3 - Create in mass_mail composition mode that should work with or without email_template installed
|
||||
# ----------------------------------------
|
||||
# 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}'},
|
||||
{'default_composition_mode': 'mass_mail', 'default_model': 'mail.group', 'default_res_id': -1,
|
||||
'active_ids': [self.group_pigs_id]})
|
||||
'active_ids': [self.group_pigs_id, group_bird_id]})
|
||||
compose = mail_compose.browse(cr, uid, compose_id)
|
||||
# Test: content_subtype is html
|
||||
self.assertEqual(compose.content_subtype, 'html', 'mail.compose.message content_subtype incorrect')
|
||||
|
||||
# Post the comment, get created message
|
||||
mail_compose.send_mail(cr, uid, [compose_id], {'default_res_id': -1, 'active_ids': [self.group_pigs_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]})
|
||||
group_pigs.refresh()
|
||||
message = group_pigs.message_ids[0]
|
||||
# Test: last message on Pigs = last created message
|
||||
test_msg = self.mail_message.browse(cr, uid, self.mail_message.search(cr, uid, [], limit=1))[0]
|
||||
self.assertEqual(message.id, test_msg.id, 'Pigs did not receive its mass mailing message')
|
||||
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')
|
||||
# Test: mail.message: subject, body
|
||||
self.assertEqual(message.subject, _subject, 'mail.message subject is incorrect')
|
||||
self.assertEqual(message.body, group_pigs.description, 'mail.message body is incorrect')
|
||||
self.assertEqual(message1.subject, _subject, 'mail.message subject incorrect')
|
||||
self.assertEqual(message1.body, group_pigs.description, 'mail.message body incorrect')
|
||||
self.assertEqual(message2.subject, _subject, 'mail.message subject incorrect')
|
||||
self.assertEqual(message2.body, group_bird.description, 'mail.message body incorrect')
|
||||
|
||||
def test_30_message_read(self):
|
||||
""" Tests designed for message_read. """
|
||||
# TDE NOTE: this test is not finished, as the message_read method is not fully specified.
|
||||
# It wil be updated as soon as we have fixed specs !
|
||||
cr, uid = self.cr, self.uid
|
||||
# It will be updated as soon as we have fixed specs !
|
||||
cr, uid = self.cr, self.uid
|
||||
group_pigs = self.mail_group.browse(cr, uid, self.group_pigs_id)
|
||||
def _compare_structures(struct1, struct2, n=0):
|
||||
# print '%scompare structure' % ('\t' * n)
|
||||
self.assertEqual(len(struct1), len(struct2), 'message_read structure number of childs incorrect')
|
||||
for x in range(len(struct1)):
|
||||
# print '%s' % ('\t' * n), struct1[x]['id'], struct2[x]['id'], struct1[x].get('subject') or ''
|
||||
self.assertEqual(struct1[x]['id'], struct2[x]['id'], 'message_read failure %s' % struct1[x].get('subject'))
|
||||
_compare_structures(struct1[x]['child_ids'], struct2[x]['child_ids'], n + 1)
|
||||
# print '%send compare' % ('\t' * n)
|
||||
|
||||
# Test message_read_tree_flatten that flattens a thread according to a given thread_level
|
||||
# ----------------------------------------
|
||||
# CASE1: Flattening test
|
||||
# ----------------------------------------
|
||||
|
||||
# Create dummy message structure
|
||||
import copy
|
||||
tree = [{'id': 1, 'child_ids':[
|
||||
{'id': 3, 'child_ids': [] },
|
||||
{'id': 4, 'child_ids': [
|
||||
{'id': 5, 'child_ids': []},
|
||||
{'id': 12, 'child_ids': []},
|
||||
] },
|
||||
{'id': 8, 'child_ids': [
|
||||
{'id': 10, 'child_ids': []},
|
||||
] },
|
||||
] },
|
||||
{'id': 2, 'child_ids': [
|
||||
tree = [{'id': 2, 'child_ids': [
|
||||
{'id': 6, 'child_ids': [
|
||||
{'id': 8, 'child_ids': []},
|
||||
]},
|
||||
]},
|
||||
{'id': 1, 'child_ids':[
|
||||
{'id': 7, 'child_ids': [
|
||||
{'id': 9, 'child_ids': []},
|
||||
] },
|
||||
] },
|
||||
{'id': 6, 'child_ids': [
|
||||
{'id': 11, 'child_ids': [] },
|
||||
] },
|
||||
]},
|
||||
{'id': 4, 'child_ids': [
|
||||
{'id': 10, 'child_ids': []},
|
||||
{'id': 5, 'child_ids': []},
|
||||
]},
|
||||
{'id': 3, 'child_ids': []},
|
||||
]},
|
||||
]
|
||||
# Test: completely flat
|
||||
new_tree = self.mail_message.message_read_tree_flatten(cr, uid, copy.deepcopy(tree), 0, 0)
|
||||
self.assertTrue(len(new_tree) == 12, 'Flattening wrongly produced')
|
||||
self.assertEqual(len(new_tree), 10, 'message_read_tree_flatten wrong in flat')
|
||||
# Test: 1 thread level
|
||||
tree_test = [{'id': 2, 'child_ids': [
|
||||
{'id': 8, 'child_ids': []}, {'id': 6, 'child_ids': []},
|
||||
]},
|
||||
{'id': 1, 'child_ids': [
|
||||
{'id': 10, 'child_ids': []}, {'id': 9, 'child_ids': []},
|
||||
{'id': 7, 'child_ids': []}, {'id': 5, 'child_ids': []},
|
||||
{'id': 4, 'child_ids': []}, {'id': 3, 'child_ids': []},
|
||||
]},
|
||||
]
|
||||
new_tree = self.mail_message.message_read_tree_flatten(cr, uid, copy.deepcopy(tree), 0, 1)
|
||||
self.assertTrue(len(new_tree) == 3 and len(new_tree[0]['child_ids']) == 6 and len(new_tree[1]['child_ids']) == 2 and len(new_tree[2]['child_ids']) == 1,
|
||||
'Flattening wrongly produced')
|
||||
_compare_structures(new_tree, tree_test)
|
||||
# Test: 2 thread levels
|
||||
new_tree = self.mail_message.message_read_tree_flatten(cr, uid, copy.deepcopy(tree), 0, 2)
|
||||
self.assertTrue(len(new_tree) == 3 and len(new_tree[0]['child_ids']) == 3 and len(new_tree[0]['child_ids'][1]) == 2,
|
||||
'Flattening wrongly produced')
|
||||
_compare_structures(new_tree, tree)
|
||||
|
||||
# Add a few messages to pigs group
|
||||
msgid1 = group_pigs.message_post(body='My Body', subject='1', parent_id=False)
|
||||
msgid2 = group_pigs.message_post(body='My Body', subject='1-1', parent_id=msgid1)
|
||||
msgid3 = group_pigs.message_post(body='My Body', subject='1-2', parent_id=msgid1)
|
||||
msgid4 = group_pigs.message_post(body='My Body', subject='2', parent_id=False)
|
||||
msgid5 = group_pigs.message_post(body='My Body', subject='1-1-1', parent_id=msgid2)
|
||||
msgid6 = group_pigs.message_post(body='My Body', subject='2-1', parent_id=msgid4)
|
||||
# ----------------------------------------
|
||||
# CASE2: message_read test
|
||||
# ----------------------------------------
|
||||
|
||||
# First try: read flat
|
||||
first_try_ids = [msgid6, msgid5, msgid4, msgid3, msgid2, msgid1]
|
||||
tree = self.mail_message.message_read(cr, uid, ids=False, domain=[('model', '=', 'mail.group'), ('res_id', '=', self.group_pigs_id)], thread_level=0)
|
||||
self.assertTrue(all(elem['id'] in first_try_ids for elem in tree) and len(tree) == 6,
|
||||
'Incorrect structure and/or number of childs in purely flat message_read')
|
||||
# 1. Add a few messages to pigs group
|
||||
msgid1 = group_pigs.message_post(body='1', subject='1', parent_id=False)
|
||||
msgid2 = group_pigs.message_post(body='2', subject='1-1', parent_id=msgid1)
|
||||
msgid3 = group_pigs.message_post(body='3', subject='1-2', parent_id=msgid1)
|
||||
msgid4 = group_pigs.message_post(body='4', subject='2', parent_id=False)
|
||||
msgid5 = group_pigs.message_post(body='5', subject='1-1-1', parent_id=msgid2)
|
||||
msgid6 = group_pigs.message_post(body='6', subject='2-1', parent_id=msgid4)
|
||||
|
||||
# Second try: read with thread_level 1
|
||||
tree = self.mail_message.message_read(cr, uid, ids=False, domain=[('model', '=', 'mail.group'), ('res_id', '=', self.group_pigs_id)], thread_level=1)
|
||||
self.assertTrue(len(tree) == 2 and len(tree[1]['child_ids']) == 3, 'Incorrect number of child in message_read')
|
||||
# Test: read all messages flat
|
||||
tree_test = [{'id': msgid6, 'child_ids': []}, {'id': msgid5, 'child_ids': []},
|
||||
{'id': msgid4, 'child_ids': []}, {'id': msgid3, 'child_ids': []},
|
||||
{'id': msgid2, 'child_ids': []}, {'id': msgid1, 'child_ids': []}]
|
||||
tree = self.mail_message.message_read(cr, uid, ids=False, domain=[('model', '=', 'mail.group'), ('res_id', '=', self.group_pigs_id)], thread_level=0, limit=10)
|
||||
_compare_structures(tree, tree_test)
|
||||
# Test: read with 1 level of thread
|
||||
tree_test = [{'id': msgid4, 'child_ids': [{'id': msgid6, 'child_ids': []}, ]},
|
||||
{'id': msgid1, 'child_ids': [
|
||||
{'id': msgid5, 'child_ids': []}, {'id': msgid3, 'child_ids': []},
|
||||
{'id': msgid2, 'child_ids': []},
|
||||
]},
|
||||
]
|
||||
tree = self.mail_message.message_read(cr, uid, ids=False, domain=[('model', '=', 'mail.group'), ('res_id', '=', self.group_pigs_id)], thread_level=1, limit=10)
|
||||
_compare_structures(tree, tree_test)
|
||||
# Test: read with 2 levels of thread
|
||||
tree_test = [{'id': msgid4, 'child_ids': [{'id': msgid6, 'child_ids': []}, ]},
|
||||
{'id': msgid1, 'child_ids': [
|
||||
{'id': msgid3, 'child_ids': []},
|
||||
{'id': msgid2, 'child_ids': [{'id': msgid5, 'child_ids': []}, ]},
|
||||
]},
|
||||
]
|
||||
tree = self.mail_message.message_read(cr, uid, ids=False, domain=[('model', '=', 'mail.group'), ('res_id', '=', self.group_pigs_id)], thread_level=2, limit=10)
|
||||
_compare_structures(tree, tree_test)
|
||||
|
||||
# Third try: read with thread_level 2
|
||||
tree = self.mail_message.message_read(cr, uid, ids=False, domain=[('model', '=', 'mail.group'), ('res_id', '=', self.group_pigs_id)], thread_level=2)
|
||||
self.assertTrue(len(tree) == 2 and len(tree[1]['child_ids']) == 2 and len(tree[1]['child_ids'][0]['child_ids']) == 1, 'Incorrect number of child in message_read')
|
||||
# 2. Test expandables
|
||||
# TDE FIXME: add those tests when expandables are specified and implemented
|
||||
|
||||
def test_40_needaction(self):
|
||||
""" Tests for mail.message needaction. """
|
||||
cr, uid = self.cr, self.uid
|
||||
cr, uid = self.cr, self.uid
|
||||
group_pigs = self.mail_group.browse(cr, uid, self.group_pigs_id)
|
||||
user_admin = self.res_users.browse(cr, uid, uid)
|
||||
|
||||
|
@ -480,7 +531,7 @@ class test_mail(common.TransactionCase):
|
|||
('partner_id', '=', user_admin.partner_id.id),
|
||||
('read', '=', False)
|
||||
])
|
||||
na_count = self.mail_message._needaction_count(cr, uid, domain = [])
|
||||
na_count = self.mail_message._needaction_count(cr, uid, domain=[])
|
||||
self.assertEqual(len(notif_ids), na_count, 'unread notifications count does not match needaction count')
|
||||
|
||||
# Post 4 message on group_pigs
|
||||
|
@ -492,11 +543,11 @@ class test_mail(common.TransactionCase):
|
|||
('partner_id', '=', user_admin.partner_id.id),
|
||||
('read', '=', False)
|
||||
])
|
||||
na_count = self.mail_message._needaction_count(cr, uid, domain = [])
|
||||
na_count = self.mail_message._needaction_count(cr, uid, domain=[])
|
||||
self.assertEqual(len(notif_ids), na_count, 'unread notifications count does not match needaction count')
|
||||
|
||||
# Check there are 4 needaction on mail.message with particular domain
|
||||
na_count = self.mail_message._needaction_count(cr, uid, domain = [('model', '=', 'mail.group'), ('res_id', '=', self.group_pigs_id)])
|
||||
na_count = self.mail_message._needaction_count(cr, uid, domain=[('model', '=', 'mail.group'), ('res_id', '=', self.group_pigs_id)])
|
||||
self.assertEqual(na_count, 4, 'posted message count does not match needaction count')
|
||||
|
||||
def test_50_thread_parent_resolution(self):
|
||||
|
@ -505,7 +556,7 @@ class test_mail(common.TransactionCase):
|
|||
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])
|
||||
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
|
||||
|
|
|
@ -36,7 +36,7 @@ class mail_compose_message(osv.TransientModel):
|
|||
at model and view levels to provide specific features.
|
||||
|
||||
The behavior of the wizard depends on the composition_mode field:
|
||||
- 'reply': reply to a previous message. The wizard is pre-populated
|
||||
- 'reply': reply to a previous message. The wizard is pre-populated
|
||||
via ``get_message_data``.
|
||||
- 'comment': new post on a record. The wizard is pre-populated via
|
||||
``get_record_data``
|
||||
|
@ -63,7 +63,6 @@ class mail_compose_message(osv.TransientModel):
|
|||
- active_ids: record IDs
|
||||
- default_model or active_model
|
||||
"""
|
||||
# get some important values from context
|
||||
if context is None:
|
||||
context = {}
|
||||
result = super(mail_compose_message, self).default_get(cr, uid, fields, context=context)
|
||||
|
@ -81,7 +80,7 @@ class mail_compose_message(osv.TransientModel):
|
|||
elif composition_mode == 'comment' and model and res_id:
|
||||
vals = self.get_record_data(cr, uid, model, res_id, context=context)
|
||||
elif composition_mode == 'mass_mail' and model and active_ids:
|
||||
vals = {'model': model, 'res_id': res_id}
|
||||
vals = {'model': model, 'res_id': res_id, 'content_subtype': 'html'}
|
||||
else:
|
||||
vals = {'model': model, 'res_id': res_id}
|
||||
if composition_mode:
|
||||
|
@ -114,10 +113,10 @@ class mail_compose_message(osv.TransientModel):
|
|||
|
||||
_defaults = {
|
||||
'composition_mode': 'comment',
|
||||
'content_subtype': lambda self,cr, uid, context={}: 'plain',
|
||||
'body_text': lambda self,cr, uid, context={}: False,
|
||||
'body': lambda self,cr, uid, context={}: '',
|
||||
'subject': lambda self,cr, uid, context={}: False,
|
||||
'content_subtype': lambda self, cr, uid, ctx={}: 'plain',
|
||||
'body_text': lambda self, cr, uid, ctx={}: False,
|
||||
'body': lambda self, cr, uid, ctx={}: '',
|
||||
'subject': lambda self, cr, uid, ctx={}: False,
|
||||
}
|
||||
|
||||
def notify(self, cr, uid, newid, context=None):
|
||||
|
@ -144,13 +143,10 @@ class mail_compose_message(osv.TransientModel):
|
|||
:param int message_id: id of the mail.message to which the user
|
||||
is replying.
|
||||
"""
|
||||
if not message_id:
|
||||
return {}
|
||||
if context is None:
|
||||
context = {}
|
||||
result = {}
|
||||
if not message_id:
|
||||
return result
|
||||
|
||||
current_user = self.pool.get('res.users').browse(cr, uid, uid, context=context)
|
||||
message_data = self.pool.get('mail.message').browse(cr, uid, message_id, context=context)
|
||||
|
||||
# create subject
|
||||
|
@ -159,15 +155,16 @@ class mail_compose_message(osv.TransientModel):
|
|||
if not (reply_subject.startswith('Re:') or reply_subject.startswith(re_prefix)):
|
||||
reply_subject = "%s %s" % (re_prefix, reply_subject)
|
||||
# create the reply in the body
|
||||
reply_header = _('On %(date)s, %(sender_name)s wrote:') % {
|
||||
reply_body = _('<div>On %(date)s, %(sender_name)s wrote:<blockquote>%(body)s</blockquote></div>') % {
|
||||
'date': message_data.date if message_data.date else '',
|
||||
'sender_name': message_data.author_id.name }
|
||||
reply_body = '<div>%s<blockquote>%s</blockquote></div>' % (reply_header, message_data.body)
|
||||
'sender_name': message_data.author_id.name,
|
||||
'body': message_data.body,
|
||||
}
|
||||
# get partner_ids from original message
|
||||
partner_ids = [partner.id for partner in message_data.partner_ids] if message_data.partner_ids else []
|
||||
|
||||
# update the result
|
||||
result.update({
|
||||
result = {
|
||||
'model': message_data.model,
|
||||
'res_id': message_data.res_id,
|
||||
'parent_id': message_data.id,
|
||||
|
@ -175,11 +172,11 @@ class mail_compose_message(osv.TransientModel):
|
|||
'subject': reply_subject,
|
||||
'partner_ids': partner_ids,
|
||||
'content_subtype': 'html',
|
||||
})
|
||||
}
|
||||
return result
|
||||
|
||||
def toggle_content_subtype(self, cr, uid, ids, context=None):
|
||||
""" hit toggle formatting mode button: calls onchange_formatting to
|
||||
""" hit toggle formatting mode button: calls onchange_formatting to
|
||||
emulate an on_change, then writes the value to update the form. """
|
||||
for record in self.browse(cr, uid, ids, context=context):
|
||||
content_st_new_value = 'plain' if record.content_subtype == 'html' else 'html'
|
||||
|
@ -209,17 +206,18 @@ class mail_compose_message(osv.TransientModel):
|
|||
warning_msg += '\n- %s' % (partner.name)
|
||||
return {'warning': {
|
||||
'title': _('Partners email addresses not found'),
|
||||
'message': warning_msg }
|
||||
'message': warning_msg,
|
||||
}
|
||||
}
|
||||
|
||||
def onchange_partner_ids(self, cr, uid, ids, value, context=None):
|
||||
""" onchange_partner_ids (value format: [[6, False, [3, 4]]]). The
|
||||
""" onchange_partner_ids (value format: [[6, 0, [3, 4]]]). The
|
||||
basic purpose of this method is to check that destination partners
|
||||
effectively have email addresses. Otherwise a warning is thrown.
|
||||
"""
|
||||
res = {'value': {}}
|
||||
if not value or not value[0] or not value[0][0] == 6:
|
||||
return
|
||||
return
|
||||
res.update(self._verify_partner_email(cr, uid, value[0][2], context=context))
|
||||
return res
|
||||
|
||||
|
@ -227,7 +225,7 @@ class mail_compose_message(osv.TransientModel):
|
|||
# Cascade delete all attachments, as they are owned by the composition wizard
|
||||
for wizard in self.read(cr, uid, ids, ['attachment_ids'], context=context):
|
||||
self.pool.get('ir.attachment').unlink(cr, uid, wizard['attachment_ids'], context=context)
|
||||
return super(mail_compose_message,self).unlink(cr, uid, ids, context=context)
|
||||
return super(mail_compose_message, self).unlink(cr, uid, ids, context=context)
|
||||
|
||||
def dummy(self, cr, uid, ids, context=None):
|
||||
""" TDE: defined to have buttons that do basically nothing. It is
|
||||
|
@ -258,13 +256,13 @@ class mail_compose_message(osv.TransientModel):
|
|||
'subject': wizard.subject if wizard.content_subtype == 'html' else False,
|
||||
'body': wizard.body if wizard.content_subtype == 'html' else '<pre>%s</pre>' % tools.ustr(wizard.body_text),
|
||||
'partner_ids': [(4, partner.id) for partner in wizard.partner_ids],
|
||||
'attachments': [(attach.name or attach.datas_fname, base64.b64decode(attach.datas)) for attach in wizard.attachment_ids],
|
||||
'attachments': [(attach.datas_fname or attach.name, base64.b64decode(attach.datas)) for attach in wizard.attachment_ids],
|
||||
}
|
||||
# mass mailing: render and override default values
|
||||
if mass_mail_mode and wizard.model:
|
||||
email_dict = self.render_message(cr, uid, wizard, wizard.model, res_id, context=context)
|
||||
email_dict = self.render_message(cr, uid, wizard, res_id, context=context)
|
||||
new_partner_ids = email_dict.pop('partner_ids', [])
|
||||
post_values['partner_ids'] += new_partner_ids
|
||||
post_values['partner_ids'] += [(4, partner_id) for partner_id in new_partner_ids]
|
||||
new_attachments = email_dict.pop('attachments', [])
|
||||
post_values['attachments'] += new_attachments
|
||||
post_values.update(email_dict)
|
||||
|
@ -273,14 +271,13 @@ class mail_compose_message(osv.TransientModel):
|
|||
|
||||
return {'type': 'ir.actions.act_window_close'}
|
||||
|
||||
def render_message(self, cr, uid, wizard, model, res_id, context=None):
|
||||
""" Generate an email from the template for given (model, res_id) pair.
|
||||
This method is meant to be inherited by email_template that will
|
||||
produce a more complete dictionary, with email_to, ...
|
||||
"""
|
||||
def render_message(self, cr, uid, wizard, res_id, context=None):
|
||||
""" Generate an email from the template for given (wizard.model, res_id)
|
||||
pair. This method is meant to be inherited by email_template that
|
||||
will produce a more complete dictionary. """
|
||||
return {
|
||||
'subject': self.render_template(cr, uid, wizard.subject, model, res_id, context),
|
||||
'body': self.render_template(cr, uid, wizard.body, model, res_id, context),
|
||||
'subject': self.render_template(cr, uid, wizard.subject, wizard.model, res_id, context),
|
||||
'body': self.render_template(cr, uid, wizard.body, wizard.model, res_id, context),
|
||||
}
|
||||
|
||||
def render_template(self, cr, uid, template, model, res_id, context=None):
|
||||
|
@ -302,8 +299,8 @@ class mail_compose_message(osv.TransientModel):
|
|||
def merge(match):
|
||||
exp = str(match.group()[2:-1]).strip()
|
||||
result = eval(exp, {
|
||||
'user' : self.pool.get('res.users').browse(cr, uid, uid, context=context),
|
||||
'object' : self.pool.get(model).browse(cr, uid, res_id, context=context),
|
||||
'user': self.pool.get('res.users').browse(cr, uid, uid, context=context),
|
||||
'object': self.pool.get(model).browse(cr, uid, res_id, context=context),
|
||||
'context': dict(context), # copy context to prevent side-effects of eval
|
||||
})
|
||||
return result and tools.ustr(result) or ''
|
||||
|
|
Loading…
Reference in New Issue