[MERGE] from trunk-message_read-tde

bzr revid: chm@openerp.com-20120921084703-c4j4byp65tbx9i55
This commit is contained in:
Christophe Matthieu
2012-09-21 10:47:03 +02:00
6 changed files with 130 additions and 131 deletions
+70 -42
View File
@@ -173,6 +173,7 @@ class mail_message(osv.Model):
def _message_dict_get(self, cr, uid, msg, context=None):
""" Return a dict representation of the message browse record. """
child_nbr = len(msg.child_ids)
has_voted = False
vote_ids = self.pool.get('res.users').name_get(cr, uid, [user.id for user in msg.vote_user_ids], context=context)
for vote in vote_ids:
@@ -197,11 +198,29 @@ class mail_message(osv.Model):
'author_user_id': author_user_id,
'partner_ids': partner_ids,
'child_ids': [],
'child_nbr': child_nbr,
'vote_user_ids': vote_ids,
'has_voted': has_voted
}
def message_read_tree_flatten(self, cr, uid, messages, current_level, level, context=None):
def message_read_tree_get_expandable(self, cr, uid, parent_message, last_message, domain=[], current_level=0, level=0, context=None):
""" . """
base_domain = [('id', '<', last_message['id'])]
if parent_message and current_level < level:
base_domain += [('parent_id', '=', parent_message['id'])]
elif parent_message:
base_domain += [('id', 'child_of', parent_message['id']), ('id', '!=', parent_message['id'])]
if domain:
base_domain += domain
extension = { 'type': 'expandable',
'domain': base_domain,
'thread_level': current_level,
'context': context,
'id': -1,
}
return extension
def message_read_tree_flatten(self, cr, uid, parent_message, messages, domain=[], level=0, current_level=0, context=None, limit=None, add_expandable=True):
""" Given a tree with several roots of following structure :
[ {'id': 1, 'child_ids': [
{'id': 11, 'child_ids': [...] },],
@@ -220,28 +239,47 @@ 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 {}
limit = limit or self._message_read_limit
# 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, message['child_ids'], domain, level, current_level + 1, context=context, limit=limit)
for child in message['child_ids']:
if child.get('type') == 'expandable':
continue
message['child_nbr'] += child['child_nbr']
# Flatten if above maximum depth
if current_level < level:
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))
return_list = [flat_message for message in messages for flat_message in _flatten(message)]
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
to fetch the matching records.
After having fetched the records provided by IDs, it will fetch the
parents to have well-formed threads.
# Add expandable
return_list = sorted(return_list, key=itemgetter(context.get('sort_key', 'id')), reverse=context.get('sort_reverse', True))
if return_list and current_level == 0 and add_expandable:
expandable = self.message_read_tree_get_expandable(cr, uid, parent_message, return_list and return_list[-1] or parent_message, domain, current_level, level, context=context)
return_list.append(expandable)
elif return_list and current_level <= level and add_expandable:
expandable = self.message_read_tree_get_expandable(cr, uid, parent_message, return_list and return_list[-1] or parent_message, domain, current_level, level, context=context)
return_list.append(expandable)
return return_list
def message_read(self, cr, uid, ids=False, domain=[], level=0, context=None, parent_id=False, limit=None):
""" Read messages from mail.message, and get back a structured tree
of messages to be displayed as discussion threads. If IDs is set,
fetch these records. Otherwise use the domain to fetch messages.
After having fetch messages, their parents will be added to obtain
well formed threads.
:param domain: optional domain for searching ids
:param level: level of threads to display, 0 being flat
:param limit: number of messages to fetch
:param parent_id: if parent_id reached, stop searching for
further parents
:return list: list of trees of messages
"""
limit = limit or self._message_read_limit
@@ -249,40 +287,30 @@ class mail_message(osv.Model):
if not ids:
ids = self.search(cr, uid, domain, context=context, limit=limit)
messages = self.browse(cr, uid, ids, context=context)
add_expandable = (len(messages) >= limit)
# key: ID, value: record
tree = {}
result = []
tree = {} # key: ID, value: record
for msg in messages:
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:
if msg.parent_id.id in tree:
record_parent = tree[msg.parent_id.id]
else:
record_parent = self._message_dict_get(cr, uid, msg.parent_id, context=context)
if msg.parent_id.parent_id:
tree[msg.parent_id.id] = record_parent
if record['id'] not in [x['id'] for x in record_parent['child_ids']]:
record_parent['child_ids'].append(record)
record = record_parent
msg = msg.parent_id
if msg.id not in tree:
result.append(record)
tree[msg.id] = record
else:
result.append({
'type': 'expandable',
'domain': [('id', '<=', msg.id)] + domain,
'context': context,
'thread_level': thread_level, # should be improve accodting to level of records
'id': -1,
})
break
record = self._message_dict_get(cr, uid, msg, context=context)
while msg.parent_id and msg.parent_id.id != parent_id:
if msg.parent_id.id in tree:
record_parent = tree[msg.parent_id.id]
else:
record_parent = self._message_dict_get(cr, uid, msg.parent_id, context=context)
if msg.parent_id.parent_id:
tree[msg.parent_id.id] = record_parent
if record['id'] not in [x['id'] for x in record_parent['child_ids']]:
record_parent['child_ids'].append(record)
record = record_parent
msg = msg.parent_id
if msg.id not in tree:
result.append(record)
tree[msg.id] = record
# Flatten the result
if thread_level > 0:
result = self.message_read_tree_flatten(cr, uid, result, 0, thread_level, context=context)
result = self.message_read_tree_flatten(cr, uid, None, result, domain, level, context=context, limit=limit, add_expandable=add_expandable)
return result
#------------------------------------------------------
-1
View File
@@ -41,5 +41,4 @@ class res_partner_mail(osv.Model):
'notification_email_send': lambda *args: 'comment'
}
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
+12 -13
View File
@@ -55,16 +55,6 @@
height: 28px;
}
.openerp div.oe_mail_msg_content {
position: relative;
width: 486px;
}
.openerp div.oe_mail_msg_content > li {
float: left;
margin-right: 3px;
}
.openerp div.oe_mail_thread_subthread div.oe_mail_msg_content {
width: 440px;
}
@@ -194,14 +184,16 @@
margin: 0 0 4px 0;
}
.openerp .oe_mail_msg_notification,
.openerp .oe_mail_msg_comment,
.openerp .oe_mail_msg_notification,
.openerp .oe_mail_msg_expandable,
.openerp .oe_mail_msg_comment,
.openerp .oe_mail_msg_email {
padding: 8px;
background: white;
border-top: 1px solid #ebebeb;
}
.openerp div.oe_mail_thread_subthread .oe_mail_msg_expandable,
.openerp div.oe_mail_thread_subthread .oe_mail_msg_comment {
background: #eee;
}
@@ -214,8 +206,15 @@
clear: both;
}
.openerp .oe_mail_msg_content {
.openerp div.oe_mail_msg_content {
float: right;
position: relative;
width: 486px;
}
.openerp div.oe_mail_msg_content > li {
float: left;
margin-right: 3px;
}
.openerp .oe_mail_msg_content:after {
+10 -55
View File
@@ -287,7 +287,6 @@ openerp.mail = function(session) {
show_dd_reply_by_email:options.show_dd_reply_by_email != undefined ? options.show_dd_reply_by_email: true,
show_dd_delete: options.show_dd_delete || false,
show_dd_hide: options.show_dd_hide || false,
show_more: options.show_more || false,
truncate_limit: options.truncate_limit || 250,
}
// datasets and internal vars
@@ -325,7 +324,7 @@ openerp.mail = function(session) {
bind_events: function() {
var self = this;
// event: click on 'More' at bottom of thread
this.$el.on('click', 'button.oe_mail_button_more', this.do_message_fetch_more);
this.$el.on('click', 'a.oe_mail_fetch_more', this.do_message_fetch_more);
// event: writing in basic textarea of composition form (quick reply)
this.$el.find('textarea.oe_mail_compose_textarea').keyup(function (event) {
var charCode = (event.which) ? event.which : window.event.keyCode;
@@ -400,7 +399,6 @@ openerp.mail = function(session) {
'default_parent_id': this.context.default_parent_id,
'default_content_subtype': 'plain'} );
}
// return this._super(action, on_close);
},
/** Instantiate the composition form, with every parameters in context
@@ -438,14 +436,14 @@ openerp.mail = function(session) {
message_fetch: function (initial_mode, additional_domain, additional_context) {
var self = this;
// domain and context: options + additional
fetch_domain = _.flatten([this.domain, additional_domain || []], true)
fetch_context = _.extend(this.context, additional_context || {})
fetch_domain = _.flatten([this.domain, additional_domain || []], true);
fetch_context = _.extend({}, this.context, additional_context || {});
// initial mode: try to use message_data or message_ids
if (initial_mode && this.options.message_data) {
return this.message_display(this.options.message_data);
}
message_ids = initial_mode && this.options.message_ids != null && this.options.message_ids || false;
return this.ds_message.call('message_read', [message_ids, fetch_domain, this.options.thread_level, undefined, fetch_context]
return this.ds_message.call('message_read', [message_ids, fetch_domain, this.options.thread_level, fetch_context, this.context.default_parent_id || undefined]
).then(this.proxy('message_display'));
},
@@ -455,13 +453,12 @@ openerp.mail = function(session) {
*/
message_display: function (records) {
var self = this;
var _expendable = false;
_(records).each(function (record) {
if (record.type == 'expandable') {
_expendable = true;
self.update_fetch_more(true);
self.fetch_more_domain = record.domain;
self.fetch_more_context = record.context;
var rendered = session.web.qweb.render('mail.thread.message.expandable', {'record': record});
$(rendered).appendTo(self.$el.children('div.oe_mail_thread_display:first'));
}
else {
self.display_record(record);
@@ -480,9 +477,6 @@ openerp.mail = function(session) {
self.thread.appendTo(self.$el.find('div.oe_mail_thread_subthread:last'));
}
});
if (! _expendable) {
this.update_fetch_more(false);
}
},
/** Displays a record and performs some formatting on the record :
@@ -537,15 +531,6 @@ openerp.mail = function(session) {
vote_node.html(vote_element);
},
/** Display 'show more' button */
update_fetch_more: function (new_value) {
if (new_value) {
this.$el.find('div.oe_mail_thread_more:last').show();
} else {
this.$el.find('div.oe_mail_thread_more:last').hide();
}
},
display_user_avatar: function () {
var avatar = mail.ChatterUtils.get_image(this.session, 'res.users', 'image_small', this.session.uid);
return this.$el.find('img.oe_mail_icon').attr('src', avatar);
@@ -560,45 +545,15 @@ openerp.mail = function(session) {
}
return this.ds_thread.call('message_post', [
[this.context.default_res_id], body, false, 'comment', this.context.default_parent_id, undefined]
).then(self.message_fetch());
).pipe(self.message_clean()).pipe(self.message_fetch(false));
},
/** Action: 'shows more' to fetch new messages */
do_message_fetch_more: function () {
do_message_fetch_more: function (event) {
event.stopPropagation();
$(event.srcElement).parents('li').eq(0).remove();
return this.message_fetch(false, this.fetch_more_domain, this.fetch_more_context);
},
// TDE: keep currently because need something similar
// /**
// * Create a domain to fetch new comments according to
// * comment already present in comments_structure
// * @param {Object} comments_structure (see chatter utils)
// * @returns {Array} fetch_domain (OpenERP domain style)
// */
// get_fetch_domain: function (comments_structure) {
// var domain = [];
// var ids = comments_structure.root_ids.slice();
// var ids2 = [];
// // must be child of current parent
// if (this.options.parent_id) { domain.push(['id', 'child_of', this.options.parent_id]); }
// _(comments_structure.root_ids).each(function (id) { // each record
// ids.push(id);
// ids2.push(id);
// });
// if (this.options.parent_id != false) {
// ids2.push(this.options.parent_id);
// }
// // must not be children of already fetched messages
// if (ids.length > 0) {
// domain.push('&');
// domain.push('!');
// domain.push(['id', 'child_of', ids]);
// }
// if (ids2.length > 0) {
// domain.push(['id', 'not in', ids2]);
// }
// return domain;
// },
});
+17 -2
View File
@@ -101,9 +101,9 @@
<div class="oe_mail_thread_display">
<!-- contains the threads -->
</div>
<div class="oe_mail_thread_more">
<!-- <div class="oe_mail_thread_more">
<button class="oe_mail_button_more" type="button">Load more messages</button>
</div>
</div> -->
</ul>
<!-- default layout -->
@@ -161,6 +161,21 @@
</div>
</li>
<!-- expandable message layout -->
<li t-name="mail.thread.message.expandable" class="oe_mail oe_mail_thread_msg">
<div t-attf-class="oe_mail_msg_expandable oe_semantic_html_override">
<div class="oe_mail_msg_content">
<!-- message itself -->
<div class="oe_mail_msg">
<div class="oe_mail_msg_body">
<a class="oe_mail_fetch_more">Load more messages ...</a>
</div>
<div class="oe_clear"/>
</div>
</div>
</div>
</li>
<!--
mail.thread.message.attachments template
Template used to display attachments in a mail.message
+21 -18
View File
@@ -544,9 +544,11 @@ class test_mail(TestMailMockups):
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')
# 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 ''
if struct1[x].get('type') == 'expandable':
continue
# print '%s' % ('\t' * n), struct1[x]['id'], struct1[x]['child_nbr'], struct2[x]['id'], struct2[x].get('child_nbr', 'XX'), 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)
@@ -557,24 +559,25 @@ class test_mail(TestMailMockups):
# Create dummy message structure
import copy
tree = [{'id': 2, 'child_ids': [
{'id': 6, 'child_ids': [
{'id': 8, 'child_ids': []},
tree = [{'id': 2, 'child_nbr': 1, 'child_ids': [
{'id': 6, 'child_nbr': 1, 'child_ids': [
{'id': 8, 'child_nbr': 0, 'child_ids': []},
]},
]},
{'id': 1, 'child_ids':[
{'id': 7, 'child_ids': [
{'id': 9, 'child_ids': []},
{'id': 1, 'child_nbr': 3, 'child_ids':[
{'id': 7, 'child_nbr': 1, 'child_ids': [
{'id': 9, 'child_nbr': 0, 'child_ids': []},
]},
{'id': 4, 'child_ids': [
{'id': 10, 'child_ids': []},
{'id': 5, 'child_ids': []},
{'id': 4, 'child_nbr': 2, 'child_ids': [
{'id': 10, 'child_nbr': 0, 'child_ids': []},
{'id': 5, 'child_nbr': 0, 'child_ids': []},
]},
{'id': 3, 'child_ids': []},
{'id': 3, 'child_nbr': 0, 'child_ids': []},
]},
]
# Test: completely flat
new_tree = self.mail_message.message_read_tree_flatten(cr, uid, copy.deepcopy(tree), 0, 0)
new_tree = self.mail_message.message_read_tree_flatten(cr, uid, None, copy.deepcopy(tree), [('type', 'in', 'borderlands')], 0, limit=15, add_expandable=False)
_compare_structures(new_tree, new_tree)
self.assertEqual(len(new_tree), 10, 'message_read_tree_flatten wrong in flat')
# Test: 1 thread level
tree_test = [{'id': 2, 'child_ids': [
@@ -586,10 +589,10 @@ class test_mail(TestMailMockups):
{'id': 4, 'child_ids': []}, {'id': 3, 'child_ids': []},
]},
]
new_tree = self.mail_message.message_read_tree_flatten(cr, uid, copy.deepcopy(tree), 0, 1)
new_tree = self.mail_message.message_read_tree_flatten(cr, uid, None, copy.deepcopy(tree), [('type', 'in', 'borderlands')], 1, limit=15, add_expandable=False)
_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)
new_tree = self.mail_message.message_read_tree_flatten(cr, uid, None, copy.deepcopy(tree), [('type', 'in', 'borderlands')], 2, limit=15, add_expandable=False)
_compare_structures(new_tree, tree)
# ----------------------------------------
@@ -608,7 +611,7 @@ class test_mail(TestMailMockups):
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)
tree = self.mail_message.message_read(cr, uid, ids=False, domain=[('model', '=', 'mail.group'), ('res_id', '=', self.group_pigs_id)], level=0, limit=15)
_compare_structures(tree, tree_test)
# Test: read with 1 level of thread
tree_test = [{'id': msgid4, 'child_ids': [{'id': msgid6, 'child_ids': []}, ]},
@@ -617,7 +620,7 @@ class test_mail(TestMailMockups):
{'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)
tree = self.mail_message.message_read(cr, uid, ids=False, domain=[('model', '=', 'mail.group'), ('res_id', '=', self.group_pigs_id)], level=1, limit=15)
_compare_structures(tree, tree_test)
# Test: read with 2 levels of thread
tree_test = [{'id': msgid4, 'child_ids': [{'id': msgid6, 'child_ids': []}, ]},
@@ -626,7 +629,7 @@ class test_mail(TestMailMockups):
{'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)
tree = self.mail_message.message_read(cr, uid, ids=False, domain=[('model', '=', 'mail.group'), ('res_id', '=', self.group_pigs_id)], level=2, limit=15)
_compare_structures(tree, tree_test)
# 2. Test expandables