diff --git a/addons/mail/mail_message.py b/addons/mail/mail_message.py index 793345ffb32..e64efa95fa7 100644 --- a/addons/mail/mail_message.py +++ b/addons/mail/mail_message.py @@ -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 #------------------------------------------------------ diff --git a/addons/mail/res_partner.py b/addons/mail/res_partner.py index 2988cee3d9c..4a6f98fa31d 100644 --- a/addons/mail/res_partner.py +++ b/addons/mail/res_partner.py @@ -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: diff --git a/addons/mail/static/src/css/mail.css b/addons/mail/static/src/css/mail.css index 717d0439486..e6890c8554f 100644 --- a/addons/mail/static/src/css/mail.css +++ b/addons/mail/static/src/css/mail.css @@ -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 { diff --git a/addons/mail/static/src/js/mail.js b/addons/mail/static/src/js/mail.js index 57c777441e7..eb25084f530 100644 --- a/addons/mail/static/src/js/mail.js +++ b/addons/mail/static/src/js/mail.js @@ -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; - // }, }); diff --git a/addons/mail/static/src/xml/mail.xml b/addons/mail/static/src/xml/mail.xml index e3586e723e4..cddccedbf6a 100644 --- a/addons/mail/static/src/xml/mail.xml +++ b/addons/mail/static/src/xml/mail.xml @@ -101,9 +101,9 @@
-
+ @@ -161,6 +161,21 @@
+ +
  • +
    +
    + + +
    +
  • +