diff --git a/addons/crm/crm.py b/addons/crm/crm.py index 27f50a0adb8..4c603777820 100644 --- a/addons/crm/crm.py +++ b/addons/crm/crm.py @@ -288,8 +288,7 @@ class crm_base(object): data = {'state': 'open', 'active': True} if not case.user_id: data['user_id'] = uid - self.write(cr, uid, case.id, data) - self._case_open_notification(case, context=context) + self.write(cr, uid, [case.id], data) self._action(cr, uid, cases, 'open') return True @@ -418,7 +417,7 @@ class crm_case(crm_base): data = {'state': 'open', 'active': True } if not case.user_id: data['user_id'] = uid - self.write(cr, uid, case.id, data) + self.write(cr, uid, [case.id], data) self._case_open_notification(case, context=context) self._action(cr, uid, cases, 'open') return True diff --git a/addons/crm_partner_assign/wizard/crm_forward_to_partner.py b/addons/crm_partner_assign/wizard/crm_forward_to_partner.py index 565f99b9db1..97d9ecac787 100644 --- a/addons/crm_partner_assign/wizard/crm_forward_to_partner.py +++ b/addons/crm_partner_assign/wizard/crm_forward_to_partner.py @@ -138,7 +138,7 @@ class crm_lead_forward_to_partner(osv.osv_memory): if email_to not in new_cc: new_cc.append(to) update_vals = {'email_cc' : ', '.join(new_cc) } - lead.write(cr, uid, case.id, update_vals, context=context) + lead.write(cr, uid, [case.id], update_vals, context=context) return res def _get_info_body_text(self, cr, uid, lead, context=None): diff --git a/addons/hr_holidays/hr_holidays.py b/addons/hr_holidays/hr_holidays.py index 78db0dc7063..f63dcf9632d 100644 --- a/addons/hr_holidays/hr_holidays.py +++ b/addons/hr_holidays/hr_holidays.py @@ -358,12 +358,16 @@ class hr_holidays(osv.osv): # OpenChatter and notifications # ----------------------------- + def message_get_subscribers(self, cr, uid, ids, context=None): + sub_ids = self._message_get_subscribers_ids(cr, uid, ids, context=context); + # add the employee and its manager if specified to the subscribed users + for obj in self.browse(cr, uid, ids, context=context): + if obj.employee_id.parent_id: + sub_ids.append(obj.employee_id.parent_id.user_id.id) + return self.pool.get('res.users').read(cr, uid, sub_ids, context=context) + def create_notificate(self, cr, uid, ids, context=None): for obj in self.browse(cr, uid, ids, context=context): - # add the employee and its manager if specified to the subscribed users - self.message_subscribe(cr, uid, ids, [obj.employee_id.user_id.id], context=context) - if obj.employee_id.parent_id: - self.message_subscribe(cr, uid, ids, [obj.employee_id.parent_id.user_id.id], context=context) self.message_append_note(cr, uid, ids, _('System notification'), _("The %s request '%s' has been created and is waiting confirmation") % ('leave' if obj.type == 'remove' else 'allocation', obj.name), type='notification', context=context) diff --git a/addons/hr_recruitment/wizard/hr_recruitment_create_partner_job.py b/addons/hr_recruitment/wizard/hr_recruitment_create_partner_job.py index dd1667aecef..ade9ce3deed 100644 --- a/addons/hr_recruitment/wizard/hr_recruitment_create_partner_job.py +++ b/addons/hr_recruitment/wizard/hr_recruitment_create_partner_job.py @@ -68,7 +68,7 @@ class hr_recruitment_partner_create(osv.osv_memory): 'email': case.email_from }, context=context) - case_obj.write(cr, uid, case.id, { + case_obj.write(cr, uid, [case.id], { 'partner_id': partner_id, 'partner_address_id': contact_id }, context=context) @@ -88,4 +88,4 @@ class hr_recruitment_partner_create(osv.osv_memory): hr_recruitment_partner_create() -# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: \ No newline at end of file +# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/addons/mail/__openerp__.py b/addons/mail/__openerp__.py index f8e7c74e5f8..a644caea5e7 100644 --- a/addons/mail/__openerp__.py +++ b/addons/mail/__openerp__.py @@ -72,6 +72,7 @@ The main features are: 'images/customer_history.jpeg', 'images/messages_form.jpeg', 'images/messages_list.jpeg', + 'static/src/img/email_icong.png', ], 'css': [ 'static/src/css/mail.css', diff --git a/addons/mail/mail_thread.py b/addons/mail/mail_thread.py index 1e8d3060313..85c297fb07f 100644 --- a/addons/mail/mail_thread.py +++ b/addons/mail/mail_thread.py @@ -68,6 +68,21 @@ class mail_thread(osv.osv): ), #widget='mail.ThreadView'), } + + #------------------------------------------------------ + # Automatic subscription when creating/reading + #------------------------------------------------------ + + def create(self, cr, uid, vals, context=None): + thread_id = super(mail_thread, self).create(cr, uid, vals, context=context); + self.message_subscribe(cr, uid, [thread_id], [uid], context=context) + return thread_id; + + def write(self, cr, uid, ids, vals, context=None): + write_res = super(mail_thread, self).write(cr, uid, ids, vals, context=context); + if write_res: + self.message_subscribe(cr, uid, ids, [uid], context=context) + return write_res; #------------------------------------------------------ # Generic message api @@ -597,21 +612,21 @@ class mail_thread(osv.osv): users = self.pool.get('res.users').read(cr, uid, user_ids, fields=['id', 'name', 'avatar_mini'], context=context) return users - def message_is_subscriber(self, cr, uid, ids, context=None): - # TODO: use message_get_subscribers ? as all subscriptions are in mail.subscription table, not necessary - subscription_obj = self.pool.get('mail.subscription') - sub_ids = subscription_obj.search(cr, uid, - ['&', '&', ('res_model', '=', self._name), ('res_id', 'in', ids), ('user_id', '=', uid)], context=context) - if len(sub_ids) > 1: - pass # TODO - return True if sub_ids else False + def message_is_subscriber(self, cr, uid, ids, user_id = None, context=None): + users = self.message_get_subscribers(cr, uid, ids, context=context) + sub_user_id = uid if user_id is None else user_id + if sub_user_id in [user['id'] for user in users]: + return True + return False def message_subscribe(self, cr, uid, ids, user_ids = None, context=None): subscription_obj = self.pool.get('mail.subscription') sub_user_ids = [uid] if user_ids is None else user_ids + create_ids = [] for id in ids: - create_ids = [subscription_obj.create(cr, uid, {'res_model': self._name, 'res_id': id, 'user_id': user_id}, context=context) - for user_id in sub_user_ids] + for user_id in sub_user_ids: + if self.message_is_subscriber(cr, uid, [id], user_id=user_id, context=context): continue + create_ids.append(subscription_obj.create(cr, uid, {'res_model': self._name, 'res_id': id, 'user_id': user_id}, context=context)) return create_ids def message_unsubscribe(self, cr, uid, ids, context=None): diff --git a/addons/mail/static/src/css/mail.css b/addons/mail/static/src/css/mail.css index d4eb2373b66..43c9c54afd0 100644 --- a/addons/mail/static/src/css/mail.css +++ b/addons/mail/static/src/css/mail.css @@ -20,6 +20,11 @@ margin-left: 5%; } +.oe_mail_wall_thread_content div.oe_mail_thread_act .oe_mail_action_textarea { + width: 100%; + height: 20px; + padding: 2px; +} /* Thread */ @@ -77,8 +82,26 @@ } .oe_mail_thread_msg, .oe_mail_thread_act { + width: 80%; white-space: normal; margin-bottom: 5px; + border-bottom: 1px solid #D2D9E7; +} + +.notification { + background: #E0E0E0; +} + +.email { + background: #E0E0E0; +} + +.oe_mail_thread_msg_more { + margin-left: 20%; + margin-right: 40%; + border: 1px solid #D2D9E7; + background: #E0E0E0; + text-align: center; } .oe_mail_thread_msg:after, .oe_mail_thread_act:after { @@ -92,15 +115,15 @@ } .oe_mail_action_textarea { - width: 60%; + width: 80%; height: 50px; padding: 5px; } .oe_mail_msg_image { float: left; - width: 60px; - height: 60px; + width: 45px; + height: 40px; text-align: center; overflow: hidden; -moz-border-radius: 3px; @@ -115,14 +138,13 @@ } .oe_mail_msg_image img { - width: 60px; + width: 40px; height: auto; clip: rect(10px, 100px, 110px, 0px); } .oe_mail_msg_content { - width: 89%; - margin-left: 11%; + margin-left: 50px; } .oe_mail_msg_p { @@ -130,10 +152,19 @@ margin: 0; } -.oe_mail_msg_body { +.oe_mail_msg_p_email_header { + border-bottom: 1px solid #D2D9E7; } -.oe_mail_msg_author, .oe_mail_msg_author a { +.oe_mail_msg_body a.reduce, .oe_mail_msg_body_short a.expand { + color: #4E43E7; +} + +.oe_mail_msg_author { + color: #747474; +} + +.oe_mail_msg_author a { color: #4E43E7; } @@ -141,6 +172,6 @@ color: #C03000; } -.oe_mail_msg_date, .oe_mail_msg_date a { - color: #4E43E7; +.oe_mail_msg_date { + color: #747474; } diff --git a/addons/mail/static/src/img/email_icon.png b/addons/mail/static/src/img/email_icon.png new file mode 100644 index 00000000000..6390d9e6e69 Binary files /dev/null and b/addons/mail/static/src/img/email_icon.png differ diff --git a/addons/mail/static/src/js/mail.js b/addons/mail/static/src/js/mail.js index 84800358ada..d49b7a0768c 100644 --- a/addons/mail/static/src/js/mail.js +++ b/addons/mail/static/src/js/mail.js @@ -10,18 +10,30 @@ openerp.mail = function(session) { /* ThreadDisplay widget: display a thread of comments */ mail.ThreadDisplay = session.web.Widget.extend({ - // QWeb template to use when rendering the object template: 'ThreadDisplay', - + + /** + * + * @params {Object} parent parent + * @params {Object} [params] + * @param {String} [params.res_model] res_model of mail.thread object + * @param {Number} [params.res_id] res_id of record + * @param {Number} [params.uid] user id + * @param {Number} [params.char_show_more=100] number of character to display before adding a "show more" + * @param {Number} [params.limit=10] maximum number of messages to fetch + * @param {Number} [params.offset=0] offset for fetchign messages + * @param {Number} [params.records=null] records to show instead of fetching messages + */ init: function(parent, params) { this._super(parent); - this.res_model = params['res_model']; - this.res_id = params['res_id']; - this.uid = params['uid']; - this.records = params['records'] || false; - this.map_hash = {'res.users': []}; - /* DataSets */ - this.ds = new session.web.DataSet(this, this.res_model); + this.params = params; + this.params.limit = this.params.limit || 10; + this.params.offset = this.params.offset || 0; + this.params.records = this.params.records || null; + this.params.char_show_more = this.params.char_show_more || 100; + this.map_hash = {'res.users': {'login': [] }}; + /* define DataSets */ + this.ds = new session.web.DataSet(this, this.params.res_model); this.ds_users = new session.web.DataSet(this, 'res.users'); }, @@ -31,116 +43,147 @@ openerp.mail = function(session) { /* bind buttons */ this.$element.find('button.oe_mail_button_comment').bind('click', function () { self.do_comment(); }); /* delegate links */ - self.$element.find('div.oe_mail_thread_display').delegate('a', 'click', function (event) { - //console.log(event); - var res_model = event.srcElement.dataset.resModel; - var res_id = event.srcElement.dataset.resId; + self.$element.find('div.oe_mail_thread_display').delegate('a.intlink', 'click', function (event) { self.do_action({ type: 'ir.actions.act_window', - res_model: res_model, - res_id: parseInt(res_id), - //context: context, + res_model: event.srcElement.dataset.resModel, + res_id: parseInt(event.srcElement.dataset.resId), views: [[false, 'form']] }); }); /* display user, fetch comments */ this.display_current_user(); - if (this.records == false) this.fetch_comments(); - else this.display_comments(this.records); + if (this.params.records) return this.display_comments(this.params.records); + else return this.init_comments(); }, stop: function () { this._super.apply(this, arguments); }, - fetch_comments: function () { - var load_res = this.ds.call('message_load', [[this.res_id]]).then( + init_comments: function() { + var self = this; + this.params.offset = 0; + this.$element.find('div.oe_mail_thread_display').empty(); + return this.fetch_comments().then(function () { + self.$element.find('button.oe_mail_button_more').bind('click', function () { self.do_more(); }); + }); + }, + + fetch_comments: function (limit, offset) { + return this.ds.call('message_load', [[this.params.res_id], limit=(limit||this.params.limit), offset=(offset||this.params.offset)]).then( this.proxy('display_comments')); - return load_res; + }, + + display_comments: function (records) { + var self = this; + /* WIP: map matched regexp -> records to browse with name */ + //_(records).each(function (record) { + //self.do_check_internal_links(record.body_text); + //}); + _(records).each(function (record) { + if (record.type == 'email') { record.mini_url = ('/mail/static/src/img/email_icon.png'); } + else { record.mini_url = self.thread_get_avatar_mini('res.users', 'avatar_mini', record.user_id[0]); } + + // body text manipulation + record.body_text = self.do_clean_text(record.body_text); + record.tr_body_text = self.do_truncate_string(record.body_text, self.params.char_show_more); + record.body_text = self.do_replace_internal_links(record.body_text); + if (record.tr_body_text) record.tr_body_text = self.do_replace_internal_links(record.tr_body_text); + + // render + $(session.web.qweb.render('ThreadMsg', {'record': record})).appendTo(self.$element.find('div.oe_mail_thread_display')); + + // truncated: hide full-text, show summary, add buttons + if (record.tr_body_text) { + var node = self.$element.find('span.oe_mail_msg_body:last').append(' [ ... Show less]'); + self.$element.find('p.oe_mail_msg_p:last').append($('' + record.tr_body_text + ' [ ... Show more]')); + var new_node = self.$element.find('span.oe_mail_msg_body_short:last'); + node.hide(); + node.find('a:last').click(function() { node.hide(); new_node.show(); return false; }); + new_node.find('a:last').click(function() { new_node.hide(); node.show(); return false; }); + } + }); + // update offset for "More" buttons + this.params.offset += records.length; }, display_current_user: function () { $('
').html( - '' + '' ).appendTo(this.$element.find('div.oe_mail_msg_image')); }, - display_comments: function (records) { - this.$element.find('div.oe_mail_thread_display').empty(); - var self = this; - /* WIP: map matched regexp -> records to browse with name */ - _(records).each(function (record) { - self.check_internal_links(record.body_text); - }); - //console.log(this.map_hash); - _(records).each(function (record) { - record.mini_url = self.thread_get_avatar_mini('res.users', 'avatar_mini', record.user_id[0]); - record.body_text = self.do_replace_internal_links(record.body_text); - var render_res = session.web.qweb.render('ThreadMsg', { - 'record': record, - }); - $('
').html(render_res).appendTo(self.$element.find('div.oe_mail_thread_display')); - }); - }, - do_comment: function () { var body_text = this.$element.find('textarea').val(); - return this.ds.call('message_append_note', [[this.res_id], 'Reply comment', body_text, type='comment']).then( - this.proxy('fetch_comments')); + return this.ds.call('message_append_note', [[this.params.res_id], 'Reply comment', body_text, type='comment']).then( + this.proxy('init_comments')); }, - /* check for internal links, and map them to limitate number of queries -- WIP, probably not useful */ - check_internal_links: function(string) { - /* shortcut to user: @login */ - var regex_login = new RegExp(/(^|\s)@(\w*[a-zA-Z_.]+\w*\s)/g); - var regex_res = regex_login.exec(string); - while (regex_res != null) { - var login = regex_res[2]; - this.map_hash['res.users'].push(login); - regex_res = regex_login.exec(string); - } - /* internal links: @res.model,name */ - var regex_intlink = new RegExp(/(^|\s)@(\w*[a-zA-Z_]+\w*)\.(\w+[a-zA-Z_]+\w*),(\w+)/g); - regex_res = regex_intlink.exec(string); - while (regex_res != null) { - var res_model = regex_res[2] + '.' + regex_res[3]; - var res_name = regex_res[4]; - if (! (res_model in this.map_hash)) { this.map_hash[res_model] = []; } - this.map_hash[res_model].push(res_name); - regex_res = regex_intlink.exec(string); - } + do_more: function () { + return this.fetch_comments(this.limit, this.offset); }, do_replace_internal_links: function (string) { var self = this; - /* internal links: @sale.order,32 */ - var regex_intlink = new RegExp(/(^|\s)@(\w*[a-zA-Z_]+\w*)\.(\w+[a-zA-Z_]+\w*),(\d+)/g); - var regex_res = regex_intlink.exec(string); - while (regex_res != null) { - var res_model = regex_res[2] + '.' + regex_res[3]; - var res_id = regex_res[4]; - string = string.replace(regex_res[0], '' + regex_res[1] + '' - + res_model + '(' + res_id + ')'); - regex_res = regex_intlink.exec(string); - } /* shortcut to user: @login */ - //var regex_login = new RegExp(/(^|\s)@(\w*[a-zA-Z_.]+\w*)/g); - //var regex_res = regex_login.exec(string); - //while (regex_res != null) { - //var res_model = regex_res[2] + '.' + regex_res[3]; - //var res_id = regex_res[4]; - //string = string.replace(regex_res[0], '' + regex_res[1] + '' - //+ res_model + '(' + res_id + ')'); - //regex_res = regex_intlink.exec(string); - //} + var regex_login = new RegExp(/(^|\s)@(\w*)/g); + var regex_res = regex_login.exec(string); + while (regex_res != null) { + var login = regex_res[2]; + var res_id = 1; + //var user_loaded = this.ds_users.call('search', [[['login', 'in', [login]]]]).then(function (records) { + //console.log(records); + //if (records) string = string.replace(regex_res[0], '@' + login + ''); + //}); + //$.when(user_loaded).then(function() { + //}); + regex_res = regex_login.exec(string); + } return string; }, thread_get_avatar_mini: function(model, field, id) { - id = id || ''; - var url = this.session.prefix + '/web/binary/image?session_id=' + this.session.session_id + '&model=' + model + '&field=' + field + '&id=' + id; - return url; + return this.session.prefix + '/web/binary/image?session_id=' + this.session.session_id + '&model=' + model + '&field=' + field + '&id=' + (id || ''); }, + + do_truncate_string: function(string, max_length) { + if (string.length <= max_length) return false; + else return string.slice(0, max_length); + }, + + do_clean_text: function (string) { + var html = $('
').text(string.replace(/\s+/g, ' ')).html().replace(new RegExp('<(/)?b\\s*>', 'gi'), '<$1b>'); + return html; + }, + + /** + * + * var regex_login = new RegExp(/(^|\s)@(\w*[a-zA-Z_.]+\w*\s)/g); + * var regex_login = new RegExp(/(^|\s)@(\w*)/g); + * var regex_intlink = new RegExp(/(^|\s)#(\w*[a-zA-Z_]+\w*)\.(\w+[a-zA-Z_]+\w*),(\w+)/g); + */ + do_check_internal_links: function(string) { + /* shortcut to user: @login */ + var regex_login = new RegExp(/(^|\s)@(\w*)/g); + var regex_res = regex_login.exec(string); + while (regex_res != null) { + var login = regex_res[2]; + this.map_hash['res.users']['login'].push(login); + regex_res = regex_login.exec(string); + } + /* internal links: #res.model,name */ + var regex_intlink = new RegExp(/(^|\s)#(\w*[a-zA-Z_]+\w*)\.(\w+[a-zA-Z_]+\w*),(\w+)/g); + regex_res = regex_intlink.exec(string); + while (regex_res != null) { + var res_model = regex_res[2] + '.' + regex_res[3]; + var res_name = regex_res[4]; + if (! (res_model in this.map_hash)) { this.map_hash[res_model]['name'] = []; } + this.map_hash[res_model]['name'].push(res_name); + regex_res = regex_intlink.exec(string); + } + }, + }); @@ -251,13 +294,23 @@ openerp.mail = function(session) { /* WallView widget: a wall of messages */ mail.WallView = session.web.Widget.extend({ - // QWeb template to use when rendering the object template: 'Wall', + /** + * @param {Object} parent parent + * @param {Object} [params] + * @param {Number} [params.limit=20] number of messages to show and fetch + * @var {Array} sorted_comments records sorted by res_model and res_id + * records.res_model = {res_ids} + * records.res_model.res_id = [records] + */ init: function (parent, params) { this._super(parent); - this.filter_search = params['filter_search']; - this.search = {} + this.params = params || {}; + this.params.limit = params.limit || 20; + this.params.search = {}; + this.params.domain = []; + this.sorted_comments = {}; /* DataSets */ this.ds_msg = new session.web.DataSet(this, 'mail.message'); this.ds_thread = new session.web.DataSet(this, 'mail.thread'); @@ -274,19 +327,33 @@ openerp.mail = function(session) { self.searchview.on_search.add(self.do_searchview_search); }); /* fetch comments */ - var comments_ready = this.fetch_comments(); + var comments_ready = this.init_comments(); return (search_view_ready && comments_ready); }, stop: function () { this._super.apply(this, arguments); }, - + + /** + * Loads the mail.message search view + * @param {Number} view_id id of the search view to load + * @param {??} defaults ?? + * @param {Boolean} hidden ?? + */ load_search_view: function (view_id, defaults, hidden) { this.searchview = new session.web.SearchView(this, this.ds_msg, view_id || false, defaults || {}, hidden || false); return this.searchview.appendTo(this.$element.find('div.oe_mail_wall_search')); }, - + + /** + * Aggregate the domains, contexts and groupbys in parameter + * with those from search form, and then calls fetch_comments + * to actually fetch comments + * @param {Array} domains + * @param {Array} contexts + * @param {Array} groupbys + */ do_searchview_search: function(domains, contexts, groupbys) { var self = this; this.rpc('/web/session/eval_domain_and_context', { @@ -294,25 +361,51 @@ openerp.mail = function(session) { contexts: contexts || [], group_by_seq: groupbys || [] }, function (results) { - self.search['context'] = results.context; - self.search['domain'] = results.domain; - self.search['groupby'] = results.group_by; - self.fetch_comments(self.search['domain'], self.search['context']); + self.params.search['context'] = results.context; + self.params.search['domain'] = results.domain; + self.params.search['groupby'] = results.group_by; + self.init_comments(self.params.search['domain'], self.params.search['context']); }); }, - + + /** + * Initializes Wall and calls fetch_comments + * @param {Array} domains + * @param {Array} contexts + * @param {Array} groupbys + * @param {Number} limit number of messages to fetch + */ + init_comments: function(domain, context, offset, limit) { + var self = this; + this.params.domain = []; + this.sorted_comments = {}; + this.$element.find('div.oe_mail_wall_threads').empty(); + return this.fetch_comments(domain, context, offset, limit).then(function () { + self.$element.find('button.oe_mail_wall_button_more').bind('click', function () { self.do_more(); }); + }); + }, + + /** + * Fetches Wall comments (mail.thread.get_pushed_messages) + * @param {Array} domains + * @param {Array} contexts + * @param {Array} groupbys + * @param {Number} limit number of messages to fetch + */ fetch_comments: function (domain, context, offset, limit) { var load_res = this.ds_thread.call('get_pushed_messages', [[this.session.uid], limit = (limit || 100), offset = (offset || 0), domain = (domain || null), context = (context || null) ]).then( this.proxy('display_comments')); return load_res; }, - + + /** + * @param {Array} records records to show in threads + */ display_comments: function (records) { - this.$element.find('div.oe_mail_wall_threads').empty(); - sorted_records = this.sort_comments(records); + var sorted_comments = this.sort_comments(records, this.sorted_comments); var self = this; - _(sorted_records).each(function (rec_models, model) { // each model + _(sorted_comments).each(function (rec_models, model) { // each model _(rec_models).each(function (record_id, id) { // each record var template = 'WallThreadContainer'; var render_res = session.web.qweb.render(template, { @@ -320,16 +413,24 @@ openerp.mail = function(session) { 'record_id': id, }); $('
').html(render_res).appendTo(self.$element.find('div.oe_mail_wall_threads')); - self.thread_display = new mail.ThreadDisplay(self, + var thread_display = new mail.ThreadDisplay(self, {'res_model': model, 'res_id': parseInt(id), 'uid': self.session.uid, 'records': record_id} ); - self.thread_display.appendTo(self.$element.find('div.oe_mail_wall_thread_content:last')); + thread_display.appendTo(self.$element.find('div.oe_mail_wall_thread_content:last')); }); }); + $.extend(true, this.sorted_comments, sorted_comments); }, + /** + * Add records to sorted_comments array + * @param {Array} records records from mail.message + * @returns {Object} sorted_comments: dict + * sorted_comments.res_model = {res_ids} + * sorted_comments.res_model.res_id = [records] + */ sort_comments: function (records) { - sorted_comments = {}; + sorted_comments = {} _(records).each(function (record) { if (! (record.model in sorted_comments)) { sorted_comments[record.model] = {}; } if (! (record.res_id in sorted_comments[record.model])) { @@ -339,12 +440,45 @@ openerp.mail = function(session) { return sorted_comments; }, + /** + * Create a domain to fetch new comments according to + * comment already present in sorted_comments + * @param {Object} sorted_comments (see sort_comments) + * @returns {Array} fetch_domain (OpenERP domain style) + */ + get_fetch_domain: function (sorted_comments) { + var domain = []; + _(sorted_comments).each(function (rec_models, model) { //each model + var ids = []; + _(rec_models).each(function (record_id, id) { // each record + ids.push(id); + }); + domain.push('|', ['model', '!=', model], ['res_id', 'not in', ids]); + }); + return domain; + }, + + /** + * Action: Posts a comment + */ do_comment: function () { var body_text = this.$element.find('textarea').val(); return this.ds_users.call('message_append_note', [[this.session.uid], 'Tweet', body_text, type='comment']).then( - this.proxy('fetch_comments')); + //this.proxy('fetch_comments')); + this.init_comments()); }, + /** + * Action: Shows more discussions + */ + do_more: function () { + var domain = this.get_fetch_domain(this.sorted_comments); + return this.fetch_comments(domain); + }, + + /** + * Tools: get avatar mini (TODO: should be moved in some tools ?) + */ thread_get_mini: function(model, field, id) { id = id || ''; var url = this.session.prefix + '/web/binary/image?session_id=' + this.session.session_id + '&model=' + model + '&field=' + field + '&id=' + id; diff --git a/addons/mail/static/src/xml/mail.xml b/addons/mail/static/src/xml/mail.xml index 393ab9d49b3..b2cc1e8e0c7 100644 --- a/addons/mail/static/src/xml/mail.xml +++ b/addons/mail/static/src/xml/mail.xml @@ -13,6 +13,11 @@
+
+ +
@@ -57,44 +62,67 @@
-
- +
+
+
+
+ +
+
- +
-

- - OpenERP System Notification - - - - - - - - - - Need action by - -
-
- -

+ + + + + +
+
+ + +

+ + OpenERP System Notification + + + + + + + + - Need action by + + wrote on +
+ +

- - -
- Discussion about document (id: ) -
-
+ + +

+ From: on
+ To:
+

+

+ +

+
+ + +
+