From 3657e4bd821e60544880985a5d48a5bc4419d803 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thibault=20Delavall=C3=A9e?= Date: Tue, 31 Jan 2012 16:50:00 +0100 Subject: [PATCH 0001/1333] [ADD] Added empty files for mail_group and mail_group views. Added css file for kanban view. Modified init and openerp files accordingly. bzr revid: tde@openerp.com-20120131155000-cmzd88d0rkso8sl1 --- addons/mail/__init__.py | 1 + addons/mail/__openerp__.py | 2 ++ 2 files changed, 3 insertions(+) diff --git a/addons/mail/__init__.py b/addons/mail/__init__.py index 4787883ef9c..c7fffb3a338 100644 --- a/addons/mail/__init__.py +++ b/addons/mail/__init__.py @@ -21,6 +21,7 @@ import mail_message import mail_thread +import mail_group import res_partner import wizard diff --git a/addons/mail/__openerp__.py b/addons/mail/__openerp__.py index 8085d301364..5e52e103852 100644 --- a/addons/mail/__openerp__.py +++ b/addons/mail/__openerp__.py @@ -58,6 +58,7 @@ The main features are: 'data': [ "wizard/mail_compose_message_view.xml", "mail_message_view.xml", + "mail_group_view.xml", "mail_thread_view.xml", "res_partner_view.xml", 'security/ir.model.access.csv', @@ -67,5 +68,6 @@ The main features are: 'auto_install': False, 'certificate': '001056784984222247309', 'images': ['images/customer_history.jpeg','images/messages_form.jpeg','images/messages_list.jpeg'], + 'css': ['static/src/css/mail_group.css'], } # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: From a0a89fd37b24553776fa451466a922e727915571 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thibault=20Delavall=C3=A9e?= Date: Tue, 31 Jan 2012 16:52:57 +0100 Subject: [PATCH 0002/1333] [ADD] Really added files I was pretending to add in previous revision bzr revid: tde@openerp.com-20120131155257-skq6lu8g0mptm19h --- addons/mail/mail_group.py | 20 +++++++ addons/mail/mail_group_view.xml | 4 ++ addons/mail/static/src/css/mail_group.css | 68 +++++++++++++++++++++++ 3 files changed, 92 insertions(+) create mode 100644 addons/mail/mail_group.py create mode 100644 addons/mail/mail_group_view.xml create mode 100644 addons/mail/static/src/css/mail_group.css diff --git a/addons/mail/mail_group.py b/addons/mail/mail_group.py new file mode 100644 index 00000000000..5dfeb073de6 --- /dev/null +++ b/addons/mail/mail_group.py @@ -0,0 +1,20 @@ +# -*- coding: utf-8 -*- +############################################################################## +# +# OpenERP, Open Source Management Solution +# Copyright (C) 2010-2011 OpenERP SA () +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see +# +############################################################################## \ No newline at end of file diff --git a/addons/mail/mail_group_view.xml b/addons/mail/mail_group_view.xml new file mode 100644 index 00000000000..b2839b2cc0a --- /dev/null +++ b/addons/mail/mail_group_view.xml @@ -0,0 +1,4 @@ + + + + diff --git a/addons/mail/static/src/css/mail_group.css b/addons/mail/static/src/css/mail_group.css new file mode 100644 index 00000000000..a65f766210e --- /dev/null +++ b/addons/mail/static/src/css/mail_group.css @@ -0,0 +1,68 @@ +.oe_group_vignette { + padding: 8px 0; + min-height: 100px; +} + +.oe_group_image, .oe_group_details { + display: inline-block; + vertical-align: top; +} + +.oe_group_image { + width: 100px; + height: 100px; + text-align: center; + overflow: hidden; + -moz-border-radius: 3px; + -webkit-border-radius: 3px; + -o-border-radius: 3px; + -ms-border-radius: 3px; + border-radius: 3px; + -moz-box-shadow: 0 1px 4px rgba(0, 0, 0, 0.4); + -webkit-box-shadow: 0 1px 4px rgba(0, 0, 0, 0.4); + -o-box-shadow: 0 1px 4px rgba(0, 0, 0, 0.4); + -box-shadow: 0 1px 4px rgba(0, 0, 0, 0.4); +} + +.oe_group_picture { + width: 100px; + height: auto; + clip: rect(10px, 100px, 110px, 0px); +} + +.oe_group_picture_wide { + height: 100px; + width: auto; + clip: rect(0px, 115px, 100px, 15px); +} + +.oe_group_details { + width: 220px; + font-size: 13px; + padding: 2px 5px; + color: #4c4c4c; + min-height: 120px; +} + +.oe_group_details h4 { + margin: 0; + font-size: 13px; +} + +.oe_group_details h4 a { + color: #4c4c4c; +} + +.oe_group_details h4 a:hover { + text-decoration: underline; +} + +.oe_group_details ul { + margin: 3px 0 5px; + padding: 0; + list-style: none; +} + +.oe_group_details li { + margin: 2px 0; +} From f4551851fd7a3905782b4e81b81878d867cb2ff0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thibault=20Delavall=C3=A9e?= Date: Tue, 31 Jan 2012 16:54:12 +0100 Subject: [PATCH 0003/1333] [IMP] Added group and subscription models. Added subscription mechanism when searching for tweets. Added views. bzr revid: tde@openerp.com-20120131155412-r16jr1nhwd9k4srv --- addons/mail/mail_group.py | 132 +++++++++++++++++++++++- addons/mail/mail_group_view.xml | 176 +++++++++++++++++++++++++++++++- addons/mail/mail_message.py | 81 ++++++++++++++- 3 files changed, 384 insertions(+), 5 deletions(-) diff --git a/addons/mail/mail_group.py b/addons/mail/mail_group.py index 5dfeb073de6..996f9f174ae 100644 --- a/addons/mail/mail_group.py +++ b/addons/mail/mail_group.py @@ -17,4 +17,134 @@ # You should have received a copy of the GNU Affero General Public License # along with this program. If not, see # -############################################################################## \ No newline at end of file +############################################################################## + +import tools +from osv import osv +from osv import fields +from tools.translate import _ + +class mail_group(osv.osv): + """ + A mail group is a collection of users sharing messages. Mail groups are different from user groups + because they don't have a specific field holding users. Group users are users that follow + the mail group, using the subscription/follow mechanism of Chatter. + """ + + _name = 'mail.group' + _inherits = {'res.groups': 'group_id'} + + def action_group_join(self, cr, uid, ids, context={}): + sub_obj = self.pool.get('mail.subscription') + menu_values = {'res_model': 'mail.group', 'user_id': uid} + for id in ids: + menu_values['res_id'] = id + sub_id = sub_obj.create(cr, uid, menu_values, context=context) + + for group in self.browse(cr, uid, ids, context): + self.write(cr, uid, group.id, { + 'users': [(4, uid)] + }) + + return True + + _columns = { + 'group_id': fields.many2one('res.groups', required=True, ondelete='cascade', + string='Group', + help='The group extended by this portal'), + #'name': fields.char('Name', size=64, required=True), + 'description': fields.text('Description'), + 'responsible_id': fields.many2one('res.users', string='Responsible', + ondelete='set null', required=True), + 'public': fields.boolean('Public', help='This group is visible by non members') + } + + _defaults = { + 'public': True, + } + + + def create(self, cr, uid, values, context=None): + """ extend create() to automatically create a menu for the group """ + if context is None: context = {} + # create group + group_id = super(mail_group, self).create(cr, uid, values, context) + # create menu + self._create_menu(cr, uid, [group_id], context) + return group_id + + def _create_menu(self, cr, uid, ids, context=None): + """ create a menu for the given groups """ + menu_obj = self.pool.get('ir.ui.menu') + ir_data = self.pool.get('ir.model.data') + act_win_obj = self.pool.get('ir.actions.act_window') + menu_root = self._get_res_xml_id(cr, uid, 'mail', 'mg_groups') + + for group in self.browse(cr, uid, ids, context): + # create an ir.action.act_window action + act_values = { + 'name': '%s' % group.name, + 'res_model': 'mail.message', + 'domain': '["&", ("res_model", "=", "mail.group"), ("res_id", "=", %s)]' % group.id, + } + act_id = act_win_obj.create(cr, uid, act_values, context) + # create a menuitem under 'mail.mg_groups' + menu_values = { + 'name': _('%s') % group.name, + 'parent_id': menu_root, + 'action': 'ir.actions.act_window,%s' % (act_id), + 'groups_id': [(6, 0, [group.group_id.id])], + } + menu_id = menu_obj.create(cr, uid, menu_values, context) + # create data + data_values = { + 'name': _('%s') % group.name, + 'model': 'ir.ui.menu', + 'module': 'portal', + 'res_id': menu_id, + 'noupdate': 'True'} + data_id = ir_data.create(cr, uid, data_values, context) + return True + + def _assign_menu(self, cr, uid, ids, context=None): + """ assign groups (ids) menu to the users joigning the groups""" + user_obj = self.pool.get('res.users') + for p in self.browse(cr, uid, ids, context): + # user menu action = portal menu action if set in portal + if p.menu_action_id: + user_ids = [u.id for u in p.users if u.id != 1] + user_values = {'menu_id': p.menu_action_id.id} + user_obj.write(cr, uid, user_ids, user_values, context) + + def _get_res_xml_id(self, cr, uid, module, xml_id): + """ return the resource id associated to the given xml_id """ + data_obj = self.pool.get('ir.model.data') + data_id = data_obj._get_id(cr, uid, module, xml_id) + return data_obj.browse(cr, uid, data_id).res_id + + +mail_group() + + +class mail_subscription(osv.osv): + """ + mail_subscription holds the data related to the follow mechanism inside OpenERP. + A subscription can be of following: + - res_model: model of the followed objects + - res_id: ID of resource OR + - res_domain: a domain filtering followed objects + """ + + _name = 'mail.subscription' + _rec_name = 'id' + _columns = { + 'res_model': fields.char('Related Document model', size=128, select=1), + 'res_id': fields.integer('Related Document ID', select=1), + 'res_domain': fields.char('res_domain', size=256), + 'user_id': fields.integer('Related User ID', select=1), + } + + _defaults = { + } + +mail_subscription() diff --git a/addons/mail/mail_group_view.xml b/addons/mail/mail_group_view.xml index b2839b2cc0a..3532eed5871 100644 --- a/addons/mail/mail_group_view.xml +++ b/addons/mail/mail_group_view.xml @@ -1,4 +1,178 @@ + - + + + mail.message.tree_tweet + mail.message + tree + 15 + + + + + + + + + + + + mail.message.form_tweet + mail.message + form + 15 + +
+ + + + + + + + + + + + +
+ + + + mail.message.search_tweet + mail.message + search + + + + + + + + + + mail.group.kanban + mail.group + kanban + + + + + +
+
+ + prout +
+
+

+
    +
  • +
  • Join
  • +
  • Joined
  • +
+
+ +
+
+
+
+
+
+ + + + mail.group.form + mail.group + form + + +
+ + + + + + + + + + + + + +
+ + + + Groups + mail.group + form + kanban,tree,form + + + + Messages + mail.message + form + tree,form + {'filter_search': True, 'tree_view_ref': 'mail.view_tweet_tree', 'form_view_ref': 'mail.view_tweet_form'} + + + + + Messages + mail.message + form + tree,form + {'filter_search': True, 'tree_view_ref': 'mail.view_tweet_tree', 'form_view_ref': 'mail.view_tweet_form'} + + [('user_id', '=', uid)] + + + + Subscriptions + mail.subscription + form + tree,form + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/addons/mail/mail_message.py b/addons/mail/mail_message.py index 1693397f6fa..825e3f3c7c6 100644 --- a/addons/mail/mail_message.py +++ b/addons/mail/mail_message.py @@ -71,8 +71,8 @@ class mail_message_common(osv.osv_memory): _rec_name = 'subject' _columns = { 'subject': fields.char('Subject', size=512, required=True), - 'model': fields.char('Related Document model', size=128, select=1, readonly=1), - 'res_id': fields.integer('Related Document ID', select=1, readonly=1), + 'model': fields.char('Related Document model', size=128, select=1), # was rfeadonly + 'res_id': fields.integer('Related Document ID', select=1), # was rfeadonly 'date': fields.datetime('Date'), 'email_from': fields.char('From', size=128, help='Message sender, taken from user preferences. If empty, this is not a mail but a message.'), 'email_to': fields.char('To', size=256, help='Message recipients'), @@ -163,7 +163,7 @@ class mail_message(osv.osv): _columns = { 'partner_id': fields.many2one('res.partner', 'Related partner'), - 'user_id': fields.many2one('res.users', 'Related user', readonly=1), + #'user_id': fields.many2one('res.users', 'Related user', readonly=1), 'attachment_ids': fields.many2many('ir.attachment', 'message_attachment_rel', 'message_id', 'attachment_id', 'Attachments'), 'display_text': fields.function(_get_display_text, method=True, type='text', size="512", string='Display Text'), 'mail_server_id': fields.many2one('ir.mail_server', 'Outgoing mail server', readonly=1), @@ -176,12 +176,87 @@ class mail_message(osv.osv): ], 'State', readonly=True), 'auto_delete': fields.boolean('Auto Delete', help="Permanently delete this email after sending it, to save space"), 'original': fields.binary('Original', help="Original version of the message, as it was sent on the network", readonly=1), + # tde add/modif + 'user_id': fields.many2one('res.users', 'Related user', readonly=1), + 'type': fields.selection([ + ('tweet', 'Tweet'), + ('status', 'Status'), + ], 'Type'), + 'need_action': fields.boolean('Need action'), } _defaults = { 'state': 'received', } + # thib add + def create(self, cr, uid, vals, context=None): + print vals + return super(mail_message, self).create(cr, uid, vals, context) + + def search(self, cr, uid, args, offset=0, limit=None, order=None, context=None, count=False): + if not context or not context.has_key('filter_search'): + return super(mail_message, self).search(cr, uid, args, offset=offset, limit=limit, order=order, context=context, count=count) + + # get subscriptions + sub_obj = self.pool.get('mail.subscription') + sub_ids = sub_obj.search(cr, uid, [('user_id', '=', uid)]) + subs = sub_obj.browse(cr, uid, sub_ids) + + # stock tweets to find + res_model_ids_dict = {} + res_model_all_list = [] + + # check all subscriptions + for sub in subs: + if sub.res_model and sub.res_id == 0 and sub.res_domain == False: + print "s-1" + if sub.res_model not in res_model_all_list: + res_model_all_list.append(sub.res_model) + elif sub.res_model and sub.res_id: + print "s-2" + if res_model_ids_dict.has_key(sub.res_model): + res_model_ids_dict[sub.res_model].append(sub.res_id) + else: + res_model_ids_dict[sub.res_model] = [sub.res_id] + elif sub.res_model and sub.res_domain: + print "s-3" + res_obj = self.pool.get(sub.res_model) + print sub.res_domain + #res_ids = res_obj.search(cr, uid, [('id', 'in', [1,2])]) + res_ids = res_obj.search(cr, uid, eval(sub.res_domain)) + if res_model_ids_dict.has_key(sub.res_model): + res_model_ids_dict[sub.res_model] += res_ids + else: + res_model_ids_dict[sub.res_model] = res_ids + print 'cacaprout' + else: + print 'erreur !!!' + print sub + + # add fully-followed domains + args.append('|') + args.append(['model', 'in', res_model_all_list]) + + # add partially-followed domains + for x in range(0, len(res_model_ids_dict.keys())-1): + args.append('|') + + for res_model in res_model_ids_dict.keys(): + if res_model not in res_model_all_list: + args.append('&') + args.append(['model', '=', res_model]) + args.append(['res_id', 'in', res_model_ids_dict[res_model]]) + + if context and context.has_key('filter_search'): + pass + else: + args = [] + print args + return super(mail_message, self).search(cr, uid, args, offset=offset, limit=limit,order=order, context=context, count=count) + + # end thib add + def init(self, cr): cr.execute("""SELECT indexname FROM pg_indexes WHERE indexname = 'mail_message_model_res_id_idx'""") if not cr.fetchone(): From b15ec8334bc641877e96957a064ca3b435248429 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thibault=20Delavall=C3=A9e?= Date: Tue, 31 Jan 2012 16:57:48 +0100 Subject: [PATCH 0004/1333] [FIX] Fixed missing fields for search. bzr revid: tde@openerp.com-20120131155748-1ossao3j32wd8bal --- addons/mail/mail_group_view.xml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/addons/mail/mail_group_view.xml b/addons/mail/mail_group_view.xml index 3532eed5871..755583cc136 100644 --- a/addons/mail/mail_group_view.xml +++ b/addons/mail/mail_group_view.xml @@ -13,6 +13,8 @@ + + From 2107ef2017ffa97b2029b2fb682895be4ff9d08b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thibault=20Delavall=C3=A9e?= Date: Wed, 1 Feb 2012 12:02:34 +0100 Subject: [PATCH 0005/1333] [FIX] Wrong field name in message tree view bzr revid: tde@openerp.com-20120201110234-a3vkrrtp70bc7wob --- addons/mail/mail_group_view.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addons/mail/mail_group_view.xml b/addons/mail/mail_group_view.xml index 755583cc136..d6b852a2cbe 100644 --- a/addons/mail/mail_group_view.xml +++ b/addons/mail/mail_group_view.xml @@ -13,7 +13,7 @@ - + From 991d9b784399ab0efa602dfba5f6e5beee445356 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thibault=20Delavall=C3=A9e?= Date: Wed, 1 Feb 2012 16:42:13 +0100 Subject: [PATCH 0006/1333] [FIX] spelling error bzr revid: tde@openerp.com-20120201154213-utvgy12zt49c7jcq --- addons/mail/mail_group_view.xml | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/addons/mail/mail_group_view.xml b/addons/mail/mail_group_view.xml index d6b852a2cbe..ee92758d5f9 100644 --- a/addons/mail/mail_group_view.xml +++ b/addons/mail/mail_group_view.xml @@ -116,7 +116,7 @@ - + Groups mail.group form @@ -150,22 +150,24 @@ - + - + - + + + - - + - + + + - + - - - + - + + + + + \ No newline at end of file diff --git a/addons/mail/mail_thread.py b/addons/mail/mail_thread.py index 23c131ec66b..c1e8f7aa098 100644 --- a/addons/mail/mail_thread.py +++ b/addons/mail/mail_thread.py @@ -82,7 +82,7 @@ class mail_thread(osv.osv): }) return super(mail_thread, self).copy(cr, uid, id, default, context=context) - def message_append(self, cr, uid, threads, subject, body_text=None, email_to=False, + def message_append(self, cr, uid, threads, subject, body_text=None, type='email', email_to=False, email_from=False, email_cc=None, email_bcc=None, reply_to=None, email_date=None, message_id=False, references=None, attachments=None, body_html=None, subtype=None, headers=None, @@ -166,7 +166,8 @@ class mail_thread(osv.osv): 'message_id': message_id, 'body_text': body_text or (hasattr(thread, 'description') and thread.description or False), 'attachment_ids': [(6, 0, to_attach)], - 'state' : 'received', + 'state': 'received', + 'type': type, } if email_from: @@ -215,9 +216,12 @@ class mail_thread(osv.osv): to determine the model of the thread to update (instead of the current model). """ + # 6.2 Social feature: add default email type for old API + if not 'type' in msg_dict: msg_dict['type'] = 'email' return self.message_append(cr, uid, ids, subject = msg_dict.get('subject'), body_text = msg_dict.get('body_text'), + type = msg_dict.get('type'), email_to = msg_dict.get('to'), email_from = msg_dict.get('from'), email_cc = msg_dict.get('cc'), @@ -235,8 +239,15 @@ class mail_thread(osv.osv): context = context) # Message loading - def message_load(self): - pass + def message_load(self, cr, uid, ids, context=None): + """ Social feature added this method + loading message: search in mail.messages where res_id = ids, (res_)model = current model """ + msg_obj = self.pool.get('mail.message') + msg_ids = [] + for id in ids: + msg_ids += msg_obj.search(cr, uid, ['&', ('res_id', '=', id), ('model', '=', self._name)], context=context) + msgs = msg_obj.browse(cr, uid, ids) + return msgs #------------------------------------------------------ # Email specific @@ -484,8 +495,8 @@ class mail_thread(osv.osv): #------------------------------------------------------ # Note specific #------------------------------------------------------ - def message_append_note(self, context, type='notification'): - pass + def message_append_note(self, cr, uid, ids, subject, body, type='notification', context=None): + return self.message_append(cr, uid, ids, subject, body_text=body, type=type, context=context) #------------------------------------------------------ # Subscription mechanism From a83847d6f74f832fa9c49a70cf9560f51c281007 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thibault=20Delavall=C3=A9e?= Date: Thu, 2 Feb 2012 11:35:01 +0100 Subject: [PATCH 0012/1333] [ADD] Implementation of subscription mechanism methods bzr revid: tde@openerp.com-20120202103501-0tt9epmzkfcuf8xz --- addons/mail/mail_thread.py | 29 ++++++++++++++++++++--------- 1 file changed, 20 insertions(+), 9 deletions(-) diff --git a/addons/mail/mail_thread.py b/addons/mail/mail_thread.py index c1e8f7aa098..8d5a05fb2bb 100644 --- a/addons/mail/mail_thread.py +++ b/addons/mail/mail_thread.py @@ -501,16 +501,27 @@ class mail_thread(osv.osv): #------------------------------------------------------ # Subscription mechanism #------------------------------------------------------ - def message_get_subscribers(self): - pass - - def message_subscribe(self): - pass - - def message_unsubscribe(self): - pass - + def message_get_subscribers(self, cr, uid, ids, context=None): + subscription_obj = self.pool.get('mail.subscription') + for id in ids: + sub_ids = subscription_obj.search(cr, uid, ['res_model': self._name, 'res_id': id], context=context) + subs = subscription_obj.browse(cr, uid, sub_ids, context=context) + return subs + def message_subscribe(self, cr, uid, ids, context=None): + subscription_obj = self.pool.get('mail.subscription') + subscriber_id = uid # TODO + for id in ids: + subscription_obj.create(cr, uid, {'res_model': self._name, 'res_id': id, 'user_id': subscriber_id}, context=context) + return True + def message_unsubscribe(self, cr, uid, ids, context=None): + subscription_obj = self.pool.get('mail.subscription') + subscriber_id = uid # TODO + sub_ids = [] + for id in ids: + sub_ids += subscription_obj.search(cr, uid, ['res_model': self._name, 'res_id': id, 'user_id': subscriber_id], context=context) + subscription_obj.unlink(cr, uid, ids, context=context) + return True # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: From 109647a109de0f0c45329570ff1b5e863359717e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thibault=20Delavall=C3=A9e?= Date: Thu, 2 Feb 2012 12:26:57 +0100 Subject: [PATCH 0013/1333] [IMP] Added push of messages when creating one. Fixed bugs in mail_thread subscription mechanism. Added list views for subscriptions and notifications. Subscription is now unread by default. bzr revid: tde@openerp.com-20120202112657-zj406ck98n06imo9 --- addons/mail/mail_message.py | 20 +++++++++++--- addons/mail/mail_subscription.py | 5 ++-- addons/mail/mail_subscription_view.xml | 36 ++++++++++++++++++++++++++ addons/mail/mail_thread.py | 4 +-- 4 files changed, 57 insertions(+), 8 deletions(-) diff --git a/addons/mail/mail_message.py b/addons/mail/mail_message.py index 877ffd972b6..dfd2fe00b92 100644 --- a/addons/mail/mail_message.py +++ b/addons/mail/mail_message.py @@ -71,8 +71,8 @@ class mail_message_common(osv.osv_memory): _rec_name = 'subject' _columns = { 'subject': fields.char('Subject', size=512, required=True), - 'model': fields.char('Related Document model', size=128, select=1), # was rfeadonly - 'res_id': fields.integer('Related Document ID', select=1), # was rfeadonly + 'model': fields.char('Related Document model', size=128, select=1), # was readonly + 'res_id': fields.integer('Related Document ID', select=1), # was readonly 'date': fields.datetime('Date'), 'email_from': fields.char('From', size=128, help='Message sender, taken from user preferences. If empty, this is not a mail but a message.'), 'email_to': fields.char('To', size=256, help='Message recipients'), @@ -191,11 +191,23 @@ class mail_message(osv.osv): } #------------------------------------------------------ - # Note specific api + # Generic api #------------------------------------------------------ def create(self, cr, uid, vals, context=None): - return super(mail_message, self).create(cr, uid, vals, context) + msg_id = super(mail_message, self).create(cr, uid, vals, context) + # push the message to suscribed users + subscription_obj = self.pool.get('mail.subscription') + notification_obj = self.pool.get('mail.notification') + sub_ids = subscription_obj.search(cr, uid, ['&', ('res_model', '=', vals['model']), ('user_id', '=', uid)], context=context) + subs = subscription_obj.browse(cr, uid, sub_ids, context=context) + for sub in subs: + notification_obj.create(cr, uid, {'user_id': sub.user_id, 'message_id': msg_id}, context=context) + return msg_id + + #------------------------------------------------------ + # Note specific api + #------------------------------------------------------ def search(self, cr, uid, args, offset=0, limit=None, order=None, context=None, count=False): if not context or not context.has_key('filter_search'): diff --git a/addons/mail/mail_subscription.py b/addons/mail/mail_subscription.py index f250ddc42e6..89750c47f5f 100644 --- a/addons/mail/mail_subscription.py +++ b/addons/mail/mail_subscription.py @@ -30,7 +30,7 @@ class mail_subscription(osv.osv): A subscription can be of following: - res_model: model of the followed objects - res_id: ID of resource OR - - res_domain: a domain filtering followed objects + - res_domain: a domain filtering followed objects - currently removed """ _name = 'mail.subscription' @@ -38,7 +38,7 @@ class mail_subscription(osv.osv): _columns = { 'res_model': fields.char('Related Document model', size=128, select=1), 'res_id': fields.integer('Related Document ID', select=1), - 'res_domain': fields.char('res_domain', size=256), + #'res_domain': fields.char('res_domain', size=256), 'user_id': fields.integer('Related User ID', select=1), } @@ -63,6 +63,7 @@ class mail_notification(osv.osv): } _defaults = { + 'read': False, } mail_notification() diff --git a/addons/mail/mail_subscription_view.xml b/addons/mail/mail_subscription_view.xml index 75f0408f341..2850cd206a1 100644 --- a/addons/mail/mail_subscription_view.xml +++ b/addons/mail/mail_subscription_view.xml @@ -2,6 +2,42 @@ + + + + mail.subscription.tree + mail.subscription + tree + 10 + + + + + + + + + + + + + mail.notification.tree + mail.notification + tree + 10 + + + + + + + + + Subscriptions mail.subscription diff --git a/addons/mail/mail_thread.py b/addons/mail/mail_thread.py index 8d5a05fb2bb..e9df98ee955 100644 --- a/addons/mail/mail_thread.py +++ b/addons/mail/mail_thread.py @@ -504,7 +504,7 @@ class mail_thread(osv.osv): def message_get_subscribers(self, cr, uid, ids, context=None): subscription_obj = self.pool.get('mail.subscription') for id in ids: - sub_ids = subscription_obj.search(cr, uid, ['res_model': self._name, 'res_id': id], context=context) + sub_ids = subscription_obj.search(cr, uid, ['&', ('res_model', '=', self._name), ('res_id', '=', id)], context=context) subs = subscription_obj.browse(cr, uid, sub_ids, context=context) return subs @@ -520,7 +520,7 @@ class mail_thread(osv.osv): subscriber_id = uid # TODO sub_ids = [] for id in ids: - sub_ids += subscription_obj.search(cr, uid, ['res_model': self._name, 'res_id': id, 'user_id': subscriber_id], context=context) + sub_ids += subscription_obj.search(cr, uid, ['&', '&', ('res_model', '=', self._name), ('res_id', '=', id), ('user_id', '=', subscriber_id)], context=context) subscription_obj.unlink(cr, uid, ids, context=context) return True From 1b9b26693ccc932c820df7cc1ccbe56dfc490fef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thibault=20Delavall=C3=A9e?= Date: Thu, 2 Feb 2012 15:18:43 +0100 Subject: [PATCH 0014/1333] [ADD] Added first not-working draft for widget. Cleaned message search function. bzr revid: tde@openerp.com-20120202141843-4jq49mbd7a6ecq4b --- addons/mail/__openerp__.py | 16 +++- addons/mail/mail_message.py | 117 +++++++++++++++------------- addons/mail/static/src/js/mail.js | 53 +++++++------ addons/mail/static/src/xml/mail.xml | 17 ++++ 4 files changed, 126 insertions(+), 77 deletions(-) create mode 100644 addons/mail/static/src/xml/mail.xml diff --git a/addons/mail/__openerp__.py b/addons/mail/__openerp__.py index ed28c5be365..ec079463230 100644 --- a/addons/mail/__openerp__.py +++ b/addons/mail/__openerp__.py @@ -68,7 +68,19 @@ The main features are: 'installable': True, 'auto_install': False, 'certificate': '001056784984222247309', - 'images': ['images/customer_history.jpeg','images/messages_form.jpeg','images/messages_list.jpeg'], - 'css': ['static/src/css/mail_group.css'], + 'images': [ + 'images/customer_history.jpeg', + 'images/messages_form.jpeg', + 'images/messages_list.jpeg', + ], + 'css': [ + 'static/src/css/mail_group.css', + ], + 'js': [ + 'static/src/js/mail.js', + ], + 'qweb': [ + 'static/src/xml/mail.xml', + ], } # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/addons/mail/mail_message.py b/addons/mail/mail_message.py index dfd2fe00b92..b13d9a3d389 100644 --- a/addons/mail/mail_message.py +++ b/addons/mail/mail_message.py @@ -205,70 +205,81 @@ class mail_message(osv.osv): notification_obj.create(cr, uid, {'user_id': sub.user_id, 'message_id': msg_id}, context=context) return msg_id + def get_pushed_messages(self, cr, uid, context=None): + """Wall: get messages to display""" + notification_obj = self.pool.get('mail.notification') + notification_ids = notification_obj.search(cr, uid, [('user_id', '=', uid)], context=context) + notifications = notification_obj.browse(cr, uid, notification_ids, context=context) + + # TODO / REMARK: classify based on res_model / res_id to have a 1_level hierarchy ? + + return notifications + + #------------------------------------------------------ # Note specific api #------------------------------------------------------ - def search(self, cr, uid, args, offset=0, limit=None, order=None, context=None, count=False): - if not context or not context.has_key('filter_search'): - return super(mail_message, self).search(cr, uid, args, offset=offset, limit=limit, order=order, context=context, count=count) + #def tmp_backup(self, cr, uid, args, offset=0, limit=None, order=None, context=None, count=False): + #if not context or not context.has_key('filter_search'): + #return super(mail_message, self).search(cr, uid, args, offset=offset, limit=limit, order=order, context=context, count=count) - # get subscriptions - sub_obj = self.pool.get('mail.subscription') - sub_ids = sub_obj.search(cr, uid, [('user_id', '=', uid)]) - subs = sub_obj.browse(cr, uid, sub_ids) + ## get subscriptions + #sub_obj = self.pool.get('mail.subscription') + #sub_ids = sub_obj.search(cr, uid, [('user_id', '=', uid)]) + #subs = sub_obj.browse(cr, uid, sub_ids) - # stock tweets to find - res_model_ids_dict = {} - res_model_all_list = [] + ## stock tweets to find + #res_model_ids_dict = {} + #res_model_all_list = [] - # check all subscriptions - for sub in subs: - if sub.res_model and sub.res_id == 0 and sub.res_domain == False: - print "s-1" - if sub.res_model not in res_model_all_list: - res_model_all_list.append(sub.res_model) - elif sub.res_model and sub.res_id: - print "s-2" - if res_model_ids_dict.has_key(sub.res_model): - res_model_ids_dict[sub.res_model].append(sub.res_id) - else: - res_model_ids_dict[sub.res_model] = [sub.res_id] - elif sub.res_model and sub.res_domain: - print "s-3" - res_obj = self.pool.get(sub.res_model) - print sub.res_domain - #res_ids = res_obj.search(cr, uid, [('id', 'in', [1,2])]) - res_ids = res_obj.search(cr, uid, eval(sub.res_domain)) - if res_model_ids_dict.has_key(sub.res_model): - res_model_ids_dict[sub.res_model] += res_ids - else: - res_model_ids_dict[sub.res_model] = res_ids - print 'cacaprout' - else: - print 'erreur !!!' - print sub + ## check all subscriptions + #for sub in subs: + #if sub.res_model and sub.res_id == 0 and sub.res_domain == False: + #print "s-1" + #if sub.res_model not in res_model_all_list: + #res_model_all_list.append(sub.res_model) + #elif sub.res_model and sub.res_id: + #print "s-2" + #if res_model_ids_dict.has_key(sub.res_model): + #res_model_ids_dict[sub.res_model].append(sub.res_id) + #else: + #res_model_ids_dict[sub.res_model] = [sub.res_id] + #elif sub.res_model and sub.res_domain: + #print "s-3" + #res_obj = self.pool.get(sub.res_model) + #print sub.res_domain + ##res_ids = res_obj.search(cr, uid, [('id', 'in', [1,2])]) + #res_ids = res_obj.search(cr, uid, eval(sub.res_domain)) + #if res_model_ids_dict.has_key(sub.res_model): + #res_model_ids_dict[sub.res_model] += res_ids + #else: + #res_model_ids_dict[sub.res_model] = res_ids + #print 'cacaprout' + #else: + #print 'erreur !!!' + #print sub - # add fully-followed domains - args.append('|') - args.append(['model', 'in', res_model_all_list]) + ## add fully-followed domains + #args.append('|') + #args.append(['model', 'in', res_model_all_list]) - # add partially-followed domains - for x in range(0, len(res_model_ids_dict.keys())-1): - args.append('|') + ## add partially-followed domains + #for x in range(0, len(res_model_ids_dict.keys())-1): + #args.append('|') - for res_model in res_model_ids_dict.keys(): - if res_model not in res_model_all_list: - args.append('&') - args.append(['model', '=', res_model]) - args.append(['res_id', 'in', res_model_ids_dict[res_model]]) + #for res_model in res_model_ids_dict.keys(): + #if res_model not in res_model_all_list: + #args.append('&') + #args.append(['model', '=', res_model]) + #args.append(['res_id', 'in', res_model_ids_dict[res_model]]) - if context and context.has_key('filter_search'): - pass - else: - args = [] - print args - return super(mail_message, self).search(cr, uid, args, offset=offset, limit=limit,order=order, context=context, count=count) + #if context and context.has_key('filter_search'): + #pass + #else: + #args = [] + #print args + #return super(mail_message, self).search(cr, uid, args, offset=offset, limit=limit,order=order, context=context, count=count) #------------------------------------------------------ # E-Mail api diff --git a/addons/mail/static/src/js/mail.js b/addons/mail/static/src/js/mail.js index 2e81741f62a..6e23d813702 100644 --- a/addons/mail/static/src/js/mail.js +++ b/addons/mail/static/src/js/mail.js @@ -1,25 +1,34 @@ openerp.mail = function(session) { - -var mail = session.mail = {}; - -mail.Wall = session.web.Widget.extend({ - init: function(parent) { - }, - start: function() { - this.$element - }, -}); - -session.ThreadView - -session.MessgageInput - - - -var w = new session.mail.ThreadView(this); - -w.appendTo($("div.wall")); - - + + var mail = session.mail = {}; + + mail.Wall = session.web.Widget.extend({ + init: function(parent) { + }, + start: function() { +// this.$element + }, + }); + + mail.ThreadView = session.web.Widget.extend({ + template: 'MailTest', + + init: function(parent) { + }, + + start: function() { +// this.$element + }, + }); + + mail.MessgageInput = session.web.Widget.extend({ + }); + + + var tv = new mail.ThreadView(this); +// tv.appendTo($("div.wall")); +// tv.appendTo($("body")); + }; + // vim:et fdc=0 fdl=0 foldnestmax=3 fdm=syntax: diff --git a/addons/mail/static/src/xml/mail.xml b/addons/mail/static/src/xml/mail.xml new file mode 100644 index 00000000000..005b4b23a79 --- /dev/null +++ b/addons/mail/static/src/xml/mail.xml @@ -0,0 +1,17 @@ + + \ No newline at end of file From babb03a00fcf01534c3b8bde56710c3799c6af2c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thibault=20Delavall=C3=A9e?= Date: Fri, 3 Feb 2012 09:48:25 +0100 Subject: [PATCH 0015/1333] First draft of javascript work for threadview bzr revid: tde@openerp.com-20120203084825-nyskuc8kxlljox0z --- addons/crm/crm_lead_view.xml | 43 +++++++++++++++++-------------- addons/mail/mail_thread.py | 1 + addons/mail/mail_thread_view.xml | 1 + addons/mail/static/src/js/mail.js | 5 ++-- 4 files changed, 28 insertions(+), 22 deletions(-) diff --git a/addons/crm/crm_lead_view.xml b/addons/crm/crm_lead_view.xml index 44d83f47634..6218d00f044 100644 --- a/addons/crm/crm_lead_view.xml +++ b/addons/crm/crm_lead_view.xml @@ -547,27 +547,30 @@ - + - - - - - -
- +
@@ -25,12 +25,14 @@
-
+
Image
-
-
-
- +
+

+
+
+ +

From b6739df2973dd7743a38165ce6e69c217b50d9ee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thibault=20Delavall=C3=A9e?= Date: Tue, 7 Feb 2012 11:16:57 +0100 Subject: [PATCH 0025/1333] Tmp action button add for res_log testing purpose bzr revid: tde@openerp.com-20120207101657-phoxiwpa5q4g35sk --- addons/mail/mail_group.py | 11 +++++++---- addons/mail/mail_group_view.xml | 3 +++ 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/addons/mail/mail_group.py b/addons/mail/mail_group.py index ff634e58d70..50141bf27a0 100644 --- a/addons/mail/mail_group.py +++ b/addons/mail/mail_group.py @@ -49,11 +49,14 @@ class mail_group(osv.osv): return True - def action_follow(self, cr, uid, ids, context={}): - return self.message_subscribe(cr, uid, ids, context=context) + def action_test(self, cr, uid, ids, context={}): + for o in self.browse(cr, uid, ids): + message = _('You are doing things with the group %s, wooooh !') % (o.name,) + self.log(cr, uid, o.id, message) + return True - def action_unfollow(self, cr, uid, ids, context={}): - return self.message_unsubscribe(cr, uid, ids, context=context) + def action_null(self, cr, uid, ids, context={}): + return True _columns = { #'group_id': fields.many2one('res.groups', required=True, ondelete='cascade', diff --git a/addons/mail/mail_group_view.xml b/addons/mail/mail_group_view.xml index ff489b09734..7ec4e26b25b 100644 --- a/addons/mail/mail_group_view.xml +++ b/addons/mail/mail_group_view.xml @@ -103,6 +103,9 @@
-
-

- 00000000 - 11111111 - 00000000 - 11111111 - 00000000 - 11111111 - 00000000 - 11111111 -

-
+ + zmeofizepofinzepfoiznepfoizpfozeinfpzoeinfpoznf zfe zef zef ze fz efz efz ef zef ze fz efznf zfe zef zef ze fz efz efz ef zef ze fz efznf zfe zef zef ze fz efz efz ef zef ze fz ef +
Image

-
-
- + zmeofizepofinzepfoiznepfoizpfozeinfpzoeinfpoznf zfe zef zef ze fz efz efz ef zef ze fz efznf zfe zef zef ze fz efz efz ef zef ze fz efznf zfe zef zef ze fz efz efz ef zef ze fz ef + + + +

From cff98a3aeb4b2308d00dc4b91b283ab79e322c45 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thibault=20Delavall=C3=A9e?= Date: Wed, 8 Feb 2012 16:25:21 +0100 Subject: [PATCH 0027/1333] [IMP] Improved ThreadView widget display; cleaned ThreadView widget js code; added basic access rules; added message_mark_done method to clean a mail.thread of its need_action_user_id; added auto-push of notifications if user requested to perform an action is not following the object; added first draft of messaging mechanism in hr_holidays bzr revid: tde@openerp.com-20120208152521-68i4nekjj8mnuyc8 --- addons/hr_holidays/hr_holidays.py | 21 ++++++++++- addons/hr_holidays/hr_holidays_view.xml | 1 + addons/mail/mail_group.py | 6 --- addons/mail/mail_group_view.xml | 12 ------ addons/mail/mail_message.py | 10 ++++- addons/mail/mail_thread.py | 17 ++++++--- addons/mail/mail_thread_view.xml | 10 +++++ addons/mail/security/ir.model.access.csv | 2 + addons/mail/static/src/css/mail.css | 47 ++++++++++++++++-------- addons/mail/static/src/js/mail.js | 38 +++++++------------ addons/mail/static/src/xml/mail.xml | 44 ++++++++++++---------- 11 files changed, 123 insertions(+), 85 deletions(-) diff --git a/addons/hr_holidays/hr_holidays.py b/addons/hr_holidays/hr_holidays.py index 3bb7a8e4982..2ddb6e87a3a 100644 --- a/addons/hr_holidays/hr_holidays.py +++ b/addons/hr_holidays/hr_holidays.py @@ -93,6 +93,7 @@ class hr_holidays(osv.osv): _name = "hr.holidays" _description = "Leave" _order = "type desc, date_from asc" + _inherit = ['mail.thread'] def _employee_get(self, cr, uid, context=None): ids = self.pool.get('hr.employee').search(cr, uid, [('user_id', '=', uid)], context=context) @@ -250,8 +251,16 @@ class hr_holidays(osv.osv): obj_emp = self.pool.get('hr.employee') ids2 = obj_emp.search(cr, uid, [('user_id', '=', uid)]) manager = ids2 and ids2[0] or False + self.holidays_validate_notificate(cr, uid, ids, context=context) return self.write(cr, uid, ids, {'state':'validate1', 'manager_id': manager}) + def holidays_validate_notificate(self, cr, uid, ids, context=None): + for obj in self.browse(cr, uid, ids): + self.message_mark_done(cr, uid, [obj.id], context=context) + self.message_append_note(cr, uid, [obj.id], 'System notification', _("The %s request '%s' has been validated.") % ('leave' if obj.type == 'remove' else 'allocation', obj.name,), type='notification', context=context) + if obj.holiday_status_id.double_validation: + self.message_append_note(cr, uid, [obj.id], 'System notification', _("The %s request '%s' is waiting for second validation.") % ('leave' if obj.type == 'remove' else 'allocation', obj.name,), type='notification', context=context) + def holidays_validate2(self, cr, uid, ids, context=None): self.check_holidays(cr, uid, ids, context=context) obj_emp = self.pool.get('hr.employee') @@ -301,13 +310,23 @@ class hr_holidays(osv.osv): wf_service.trg_validate(uid, 'hr.holidays', leave_id, 'validate', cr) wf_service.trg_validate(uid, 'hr.holidays', leave_id, 'second_validate', cr) if holiday_ids: + self.holidays_valid2_notificate(self, cr, uid, [holiday_ids], context=context) self.write(cr, uid, holiday_ids, {'manager_id2': manager}) return True + + def holidays_valid2_notificate(self, cr, uid, ids, context=None): + for obj in self.browse(cr, uid, ids): + self.message_append_note(cr, uid, [obj.id], 'System notification', _("The %s request '%s' has been double validated.") % ('leave' if obj.type == 'remove' else 'allocation', obj.name,), type='notification', context=context) def holidays_confirm(self, cr, uid, ids, context=None): self.check_holidays(cr, uid, ids, context=context) + self.holidays_confirm_notificate(cr, uid, ids, context=context) return self.write(cr, uid, ids, {'state':'confirm'}) - + + def holidays_confirm_notificate(self, cr, uid, ids, context=None): + for obj in self.browse(cr, uid, ids): + self.message_append_note(cr, uid, [obj.id], 'System notification', _("The %s request '%s' has been confirmed and is waiting for validation.") % ('leave' if obj.type == 'remove' else 'allocation', obj.name,), type='notification', need_action_user_id=obj.employee_id.parent_id.user_id.id) + def holidays_refuse(self, cr, uid, ids, approval, context=None): obj_emp = self.pool.get('hr.employee') ids2 = obj_emp.search(cr, uid, [('user_id', '=', uid)]) diff --git a/addons/hr_holidays/hr_holidays_view.xml b/addons/hr_holidays/hr_holidays_view.xml index 22d4d6e567d..2096ae2bc61 100644 --- a/addons/hr_holidays/hr_holidays_view.xml +++ b/addons/hr_holidays/hr_holidays_view.xml @@ -95,6 +95,7 @@ +
+
+
\ No newline at end of file From 9efc4714f26757a127861119e549f7718af3e136 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thibault=20Delavall=C3=A9e?= Date: Thu, 9 Feb 2012 11:04:47 +0100 Subject: [PATCH 0030/1333] [IMP] Wall: added parameters passing (filtering, message number to display, ..). Added My Wall (all feeds versus my feeds). bzr revid: tde@openerp.com-20120209100447-p8csuqq85jsexrdn --- addons/hr_holidays/hr_holidays.py | 8 ++++---- addons/mail/mail_message.py | 2 +- addons/mail/mail_message_view.xml | 7 +++++++ addons/mail/mail_thread_view.xml | 19 ++++++++++++++----- addons/mail/static/src/js/mail.js | 9 ++++++--- 5 files changed, 32 insertions(+), 13 deletions(-) diff --git a/addons/hr_holidays/hr_holidays.py b/addons/hr_holidays/hr_holidays.py index 2ddb6e87a3a..8fd91c30581 100644 --- a/addons/hr_holidays/hr_holidays.py +++ b/addons/hr_holidays/hr_holidays.py @@ -257,9 +257,9 @@ class hr_holidays(osv.osv): def holidays_validate_notificate(self, cr, uid, ids, context=None): for obj in self.browse(cr, uid, ids): self.message_mark_done(cr, uid, [obj.id], context=context) - self.message_append_note(cr, uid, [obj.id], 'System notification', _("The %s request '%s' has been validated.") % ('leave' if obj.type == 'remove' else 'allocation', obj.name,), type='notification', context=context) + self.message_append_note(cr, uid, [obj.id], _('System notification'), _("The %s request '%s' has been validated.") % ('leave' if obj.type == 'remove' else 'allocation', obj.name,), type='notification', context=context) if obj.holiday_status_id.double_validation: - self.message_append_note(cr, uid, [obj.id], 'System notification', _("The %s request '%s' is waiting for second validation.") % ('leave' if obj.type == 'remove' else 'allocation', obj.name,), type='notification', context=context) + self.message_append_note(cr, uid, [obj.id], _('System notification'), _("The %s request '%s' is waiting for second validation.") % ('leave' if obj.type == 'remove' else 'allocation', obj.name,), type='notification', context=context) def holidays_validate2(self, cr, uid, ids, context=None): self.check_holidays(cr, uid, ids, context=context) @@ -316,7 +316,7 @@ class hr_holidays(osv.osv): def holidays_valid2_notificate(self, cr, uid, ids, context=None): for obj in self.browse(cr, uid, ids): - self.message_append_note(cr, uid, [obj.id], 'System notification', _("The %s request '%s' has been double validated.") % ('leave' if obj.type == 'remove' else 'allocation', obj.name,), type='notification', context=context) + self.message_append_note(cr, uid, [obj.id], _('System notification'), _("The %s request '%s' has been double validated.") % ('leave' if obj.type == 'remove' else 'allocation', obj.name,), type='notification', context=context) def holidays_confirm(self, cr, uid, ids, context=None): self.check_holidays(cr, uid, ids, context=context) @@ -325,7 +325,7 @@ class hr_holidays(osv.osv): def holidays_confirm_notificate(self, cr, uid, ids, context=None): for obj in self.browse(cr, uid, ids): - self.message_append_note(cr, uid, [obj.id], 'System notification', _("The %s request '%s' has been confirmed and is waiting for validation.") % ('leave' if obj.type == 'remove' else 'allocation', obj.name,), type='notification', need_action_user_id=obj.employee_id.parent_id.user_id.id) + self.message_append_note(cr, uid, [obj.id], _('System notification'), _("The %s request '%s' has been confirmed and is waiting for validation.") % ('leave' if obj.type == 'remove' else 'allocation', obj.name,), type='notification', need_action_user_id=obj.employee_id.parent_id.user_id.id) def holidays_refuse(self, cr, uid, ids, approval, context=None): obj_emp = self.pool.get('hr.employee') diff --git a/addons/mail/mail_message.py b/addons/mail/mail_message.py index 2ecdad5ceca..28e02401244 100644 --- a/addons/mail/mail_message.py +++ b/addons/mail/mail_message.py @@ -216,7 +216,7 @@ class mail_message(osv.osv): notification_obj.create(cr, uid, {'user_id': vals['need_action_user_id'], 'message_id': msg_id}, context=context) return msg_id - def get_pushed_messages(self, cr, uid, context=None): + def get_pushed_messages(self, cr, uid, ids, filter_search=False, context=None): """Wall: get messages to display""" notification_obj = self.pool.get('mail.notification') notification_ids = notification_obj.search(cr, uid, [('user_id', '=', uid)], context=context) diff --git a/addons/mail/mail_message_view.xml b/addons/mail/mail_message_view.xml index 4ccf7d44323..0036897d411 100644 --- a/addons/mail/mail_message_view.xml +++ b/addons/mail/mail_message_view.xml @@ -230,6 +230,13 @@ (w)All Feeds mail.all_feeds + + + + + My Feeds + mail.all_feeds +
diff --git a/addons/mail/mail_thread_view.xml b/addons/mail/mail_thread_view.xml index 7bd503b0546..d2e557d06d8 100644 --- a/addons/mail/mail_thread_view.xml +++ b/addons/mail/mail_thread_view.xml @@ -51,26 +51,35 @@
- + + Feeds + + + + - - - + + My Feeds + + + + (w)All Feeds - + + diff --git a/addons/mail/static/src/js/mail.js b/addons/mail/static/src/js/mail.js index bd82399b88e..747276121fa 100644 --- a/addons/mail/static/src/js/mail.js +++ b/addons/mail/static/src/js/mail.js @@ -95,8 +95,9 @@ openerp.mail = function(session) { // QWeb template to use when rendering the object template: 'WallView', - init: function() { - this._super.apply(this, arguments); + init: function (parent, params) { + this._super(parent); + this.filter_search = params['filter_search']; /* DataSets */ this.ds_msg = new session.web.DataSet(this, 'mail.message'); }, @@ -104,8 +105,9 @@ openerp.mail = function(session) { start: function() { var self = this; this._super.apply(this, arguments); + console.log(this); self.$element.find('button.oe_mail_action_comment').bind('click', function () { self.do_comment(); }); - this.ds_msg.call('get_pushed_messages', []).then( + this.ds_msg.call('get_pushed_messages', [[], self.filter_search]).then( this.proxy('display_records')); }, @@ -114,6 +116,7 @@ openerp.mail = function(session) { }, fetch_messages: function () { + console.log('debug--fetch_messages'); return this.ds_msg.call('get_pushed_messages', []).then( this.proxy('display_records')); }, From 1d09919c1810c575d3c2bd74ef76b54ceca44d8c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thibault=20Delavall=C3=A9e?= Date: Fri, 10 Feb 2012 10:53:48 +0100 Subject: [PATCH 0031/1333] [FIX] Fixed bug with message dict not necessarily containing a 'type' entry bzr revid: tde@openerp.com-20120210095348-z8m7gqpse6hw9byr --- addons/mail/mail_message.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addons/mail/mail_message.py b/addons/mail/mail_message.py index 28e02401244..58e5833fa49 100644 --- a/addons/mail/mail_message.py +++ b/addons/mail/mail_message.py @@ -198,7 +198,7 @@ class mail_message(osv.osv): def create(self, cr, uid, vals, context=None): # OpenSocial: notifications do not come from any user but from system - if vals['type'] == 'notification': vals['user_id'] = False + if vals.get('type') == 'notification': vals['user_id'] = False need_action_pushed = False msg_id = super(mail_message, self).create(cr, uid, vals, context) # push the message to suscribed users From 62a33c175a16471db9e9b2ad5555beaca27ddd7a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thibault=20Delavall=C3=A9e?= Date: Fri, 10 Feb 2012 11:25:37 +0100 Subject: [PATCH 0032/1333] [FIX] Fixed crash when assigning need_action_user_id to unexistent manager_id for holidays validation bzr revid: tde@openerp.com-20120210102537-rrvk58ttasnfmoqq --- addons/hr_holidays/hr_holidays.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addons/hr_holidays/hr_holidays.py b/addons/hr_holidays/hr_holidays.py index 8fd91c30581..a2acea4792d 100644 --- a/addons/hr_holidays/hr_holidays.py +++ b/addons/hr_holidays/hr_holidays.py @@ -325,7 +325,7 @@ class hr_holidays(osv.osv): def holidays_confirm_notificate(self, cr, uid, ids, context=None): for obj in self.browse(cr, uid, ids): - self.message_append_note(cr, uid, [obj.id], _('System notification'), _("The %s request '%s' has been confirmed and is waiting for validation.") % ('leave' if obj.type == 'remove' else 'allocation', obj.name,), type='notification', need_action_user_id=obj.employee_id.parent_id.user_id.id) + self.message_append_note(cr, uid, [obj.id], _('System notification'), _("The %s request '%s' has been confirmed and is waiting for validation.") % ('leave' if obj.type == 'remove' else 'allocation', obj.name,), type='notification', need_action_user_id = obj.employee_id.parent_id.user_id.id if obj.employee_id.parent_id else False) def holidays_refuse(self, cr, uid, ids, approval, context=None): obj_emp = self.pool.get('hr.employee') From 5042ba24658d48ffbd500484b6e01c0712f0ada5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thibault=20Delavall=C3=A9e?= Date: Fri, 10 Feb 2012 11:52:23 +0100 Subject: [PATCH 0033/1333] [IMP] Cleaned xml, css and js for ThreadView. bzr revid: tde@openerp.com-20120210105223-fv24nic79ycldc2i --- addons/mail/static/src/css/mail.css | 75 ++++++++++++++++++++++------- addons/mail/static/src/js/mail.js | 26 +++++----- addons/mail/static/src/xml/mail.xml | 33 ++++++++----- 3 files changed, 93 insertions(+), 41 deletions(-) diff --git a/addons/mail/static/src/css/mail.css b/addons/mail/static/src/css/mail.css index 906bdbff6cf..baf7a98d71f 100644 --- a/addons/mail/static/src/css/mail.css +++ b/addons/mail/static/src/css/mail.css @@ -1,43 +1,74 @@ /** ThreadView widget **/ .oe_mail_main { + overflow: auto; + padding: 5px 5px 5px 5px; } +/* 2 columns view */ +.oe_mail_main_left { + float: left; + width: 65%; +} + +.oe_mail_main_right { + float: right; + width: 34%; +} + +/* Left-side CSS */ .oe_mail_actions { - padding-bottom: 10px; + margin-bottom: 10px; } .oe_mail_post_comment { - width: 65%; - padding-bottom: 10px; + margin-bottom: 10px; } -.oe_mail_comments { - width: 65%; +.oe_mail_button_follow, .oe_mail_button_unfollow, .oe_mail_button_getfollowers, .oe_mail_button_hidefollowers { + width: 100px; +} + +.oe_mail_button_comment { + width: 150px; +} + +.oe_mail_action_textarea { + width: 60%; + height: 50px; + padding: 5px; +} + +.oe_mail_action_comment { } .oe_mail_msg { - width: 65%; -/* border: 1px solid #998877; */ - padding: 10px 0px 10px 0px; } -.oe_mail_msg:after { +/*.oe_mail_msg:after { content: ""; display: block; clear: both; +}*/ + +.oe_mail_comment { + white-space: normal; + margin-bottom: 5px; } .oe_mail_msg_image { float: left; - width: 9%; + width: 14%; +} + +.oe_mail_msg_image img { + width: 80%; + margin-left: 10%; } .oe_mail_msg_content { - width: 89%; - margin-left: 10%; -/* border: 1px solid #557722; */ - overflow: hidden; + width: 85%; + margin-left: 15%; } .oe_mail_msg_p { @@ -48,14 +79,24 @@ .oe_mail_msg_body { } -.oe_mail_msg_author { +.oe_mail_msg_author, .oe_mail_msg_author a { color: #4E43E7; } -.oe_mail_msg_need_action { +.oe_mail_msg_need_action, .oe_mail_msg_need_action a { color: #C03000; } -.oe_mail_msg_date { +.oe_mail_msg_date, .oe_mail_msg_date a { color: #4E43E7; } + +/* Right-side CSS */ +.oe_mail_followers_vignette { + float: left; + width: 20%%; +} + +.oe_mail_followers_vignette img { + width: 80%; +} diff --git a/addons/mail/static/src/js/mail.js b/addons/mail/static/src/js/mail.js index 747276121fa..93a333335a8 100644 --- a/addons/mail/static/src/js/mail.js +++ b/addons/mail/static/src/js/mail.js @@ -27,15 +27,15 @@ openerp.mail = function(session) { var self = this; this._super.apply(this, arguments); /* bind follow and unfollow buttons */ - self.$element.find('button.oe_mail_action_follow').bind('click', function () { self.do_follow(); }); - self.$element.find('button.oe_mail_action_follow').hide(); - self.$element.find('button.oe_mail_action_unfollow').bind('click', function () { self.do_unfollow(); }); - self.$element.find('button.oe_mail_action_unfollow').hide(); - self.$element.find('button.oe_mail_action_comment').bind('click', function () { self.do_comment(); }); + self.$element.find('button.oe_mail_button_follow').bind('click', function () { self.do_follow(); }); + self.$element.find('button.oe_mail_button_follow').hide(); + self.$element.find('button.oe_mail_button_unfollow').bind('click', function () { self.do_unfollow(); }); + self.$element.find('button.oe_mail_button_unfollow').hide(); + self.$element.find('button.oe_mail_button_comment').bind('click', function () { self.do_comment(); }); /* find wich (un)follow buttons to show */ var call_res = this.ds.call('message_is_subscriber', [[this.session.uid]]).then(function (records) { - if (records == true) { self.follow_state = 1; self.$element.find('button.oe_mail_action_unfollow').show(); } - else { self.follow_state = 0; self.$element.find('button.oe_mail_action_follow').show(); } + if (records == true) { self.follow_state = 1; self.$element.find('button.oe_mail_button_unfollow').show(); } + else { self.follow_state = 0; self.$element.find('button.oe_mail_button_follow').show(); } }); }, @@ -56,27 +56,27 @@ openerp.mail = function(session) { }, display_records: function (records) { - this.$element.find('div.oe_mail_comments').empty(); + this.$element.find('div.oe_mail_msg').empty(); var self = this; _(records).each(function (record) { var template = 'ThreadMsgView'; var render_res = session.web.qweb.render(template, { 'record': record, }); - $('
').html(render_res).appendTo(self.$element.find('div.oe_mail_comments')); + $('
').html(render_res).appendTo(self.$element.find('div.oe_mail_msg')); }); // this.timeout = setTimeout(this.proxy('fetch_messages'), 5000); }, do_follow: function () { - this.$element.find('button.oe_mail_action_unfollow').show(); - this.$element.find('button.oe_mail_action_follow').hide(); + this.$element.find('button.oe_mail_button_unfollow').show(); + this.$element.find('button.oe_mail_button_follow').hide(); return this.ds_sub.create({'res_model': this.view.model, 'user_id': this.session.uid, 'res_id': this.view.datarecord.id}).then(); }, do_unfollow: function () { - this.$element.find('button.oe_mail_action_follow').show(); - this.$element.find('button.oe_mail_action_unfollow').hide(); + this.$element.find('button.oe_mail_button_follow').show(); + this.$element.find('button.oe_mail_button_unfollow').hide(); return this.ds.call('message_unsubscribe', [[this.view.datarecord.id]]).then(); }, diff --git a/addons/mail/static/src/xml/mail.xml b/addons/mail/static/src/xml/mail.xml index 58e242829b6..72fbf8d0f88 100644 --- a/addons/mail/static/src/xml/mail.xml +++ b/addons/mail/static/src/xml/mail.xml @@ -3,17 +3,28 @@
OpenSocial
-
- - +
+
+ + +
+
+
+ +
+
+
+
+
-
-
- -
-
+
+ + +
+
+
Image
@@ -23,15 +34,15 @@ OpenERP System Notification
- + - - Need action + - Need action by

- +

From 628989451d3f4ceab62dfdfce110d126b8f0b96b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thibault=20Delavall=C3=A9e?= Date: Fri, 10 Feb 2012 13:42:53 +0100 Subject: [PATCH 0034/1333] [IMP] Changed mail.subscription.user_id field from integer to many2one bzr revid: tde@openerp.com-20120210124253-mx7vgxhmzpfant62 --- addons/mail/mail_subscription.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/addons/mail/mail_subscription.py b/addons/mail/mail_subscription.py index c5b5407190b..eaf53bc1c17 100644 --- a/addons/mail/mail_subscription.py +++ b/addons/mail/mail_subscription.py @@ -39,7 +39,8 @@ class mail_subscription(osv.osv): select=1, required=True), 'res_id': fields.integer('Related Document ID', select=1), #'res_domain': fields.char('res_domain', size=256), - 'user_id': fields.integer('Related User ID', select=1, required=True), + 'user_id': fields.many2one('res.users', string='Related User ID', + ondelete='cascade', required=True, select=1), } _defaults = { } From a123480d2c123f011265eff253d787cca27fb57f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thibault=20Delavall=C3=A9e?= Date: Fri, 10 Feb 2012 13:43:48 +0100 Subject: [PATCH 0035/1333] [IMP] Added a 'get followers' button on ThreadWidget. Basic displaying of results added. bzr revid: tde@openerp.com-20120210124348-8s73xmswnfypqhx6 --- addons/mail/mail_thread.py | 7 +++++++ addons/mail/static/src/css/mail.css | 2 +- addons/mail/static/src/js/mail.js | 28 +++++++++++++++++++++++----- addons/mail/static/src/xml/mail.xml | 10 +++++----- 4 files changed, 36 insertions(+), 11 deletions(-) diff --git a/addons/mail/mail_thread.py b/addons/mail/mail_thread.py index f582bc50062..68bf4429d18 100644 --- a/addons/mail/mail_thread.py +++ b/addons/mail/mail_thread.py @@ -543,6 +543,13 @@ class mail_thread(osv.osv): subs = subscription_obj.browse(cr, uid, sub_ids, context=context) return subs + def message_get_subscribers_web(self, cr, uid, ids, context=None): + subscription_obj = self.pool.get('mail.subscription') + for id in ids: + sub_ids = subscription_obj.search(cr, uid, ['&', ('res_model', '=', self._name), ('res_id', '=', id)], context=context) + subs = subscription_obj.read(cr, uid, sub_ids, context=context) + return subs + def message_is_subscriber(self, cr, uid, ids, context=None): for subscription in self.message_get_subscribers(cr, uid, ids, context=context): if subscription.user_id == uid: return True diff --git a/addons/mail/static/src/css/mail.css b/addons/mail/static/src/css/mail.css index baf7a98d71f..0d153ce0c06 100644 --- a/addons/mail/static/src/css/mail.css +++ b/addons/mail/static/src/css/mail.css @@ -25,7 +25,7 @@ margin-bottom: 10px; } -.oe_mail_button_follow, .oe_mail_button_unfollow, .oe_mail_button_getfollowers, .oe_mail_button_hidefollowers { +.oe_mail_button_follow, .oe_mail_button_unfollow, .oe_mail_button_followers { width: 100px; } diff --git a/addons/mail/static/src/js/mail.js b/addons/mail/static/src/js/mail.js index 93a333335a8..fa7037bcacb 100644 --- a/addons/mail/static/src/js/mail.js +++ b/addons/mail/static/src/js/mail.js @@ -32,6 +32,7 @@ openerp.mail = function(session) { self.$element.find('button.oe_mail_button_unfollow').bind('click', function () { self.do_unfollow(); }); self.$element.find('button.oe_mail_button_unfollow').hide(); self.$element.find('button.oe_mail_button_comment').bind('click', function () { self.do_comment(); }); + self.$element.find('button.oe_mail_button_followers').bind('click', function () { self.do_toggle_followers(); }); /* find wich (un)follow buttons to show */ var call_res = this.ds.call('message_is_subscriber', [[this.session.uid]]).then(function (records) { if (records == true) { self.follow_state = 1; self.$element.find('button.oe_mail_button_unfollow').show(); } @@ -47,15 +48,18 @@ openerp.mail = function(session) { set_value: function() { this._super.apply(this, arguments); if (! this.view.datarecord.id) { return; } - return this.fetch_messages(); + return this.fetch_data(); }, - fetch_messages: function () { - return this.ds.call('message_load', [[this.view.datarecord.id]]).then( - this.proxy('display_records')); + fetch_data: function () { + var load_res = this.ds.call('message_load', [[this.view.datarecord.id]]).then( + this.proxy('display_comments')); + var follow_res = this.ds.call('message_get_subscribers_web', [[this.view.datarecord.id]]).then( + this.proxy('display_followers')); + return follow_res; }, - display_records: function (records) { + display_comments: function (records) { this.$element.find('div.oe_mail_msg').empty(); var self = this; _(records).each(function (record) { @@ -68,6 +72,16 @@ openerp.mail = function(session) { // this.timeout = setTimeout(this.proxy('fetch_messages'), 5000); }, + display_followers: function (records) { + this.$element.find('div.oe_mail_followers').empty(); + var self = this; + _(records).each(function (record) { + console.log(record); +//
+ $('
').text(record.user_id[1]).appendTo(self.$element.find('div.oe_mail_followers')); + }); + }, + do_follow: function () { this.$element.find('button.oe_mail_button_unfollow').show(); this.$element.find('button.oe_mail_button_follow').hide(); @@ -85,6 +99,10 @@ openerp.mail = function(session) { return this.ds.call('message_append_note', [[this.view.datarecord.id], 'Reply comment', body_text, type='comment']).then( this.proxy('fetch_messages')); }, + + do_toggle_followers: function () { + this.$element.find('div.oe_mail_followers').toggle(); + }, }); /* Add WallView widget to registry */ diff --git a/addons/mail/static/src/xml/mail.xml b/addons/mail/static/src/xml/mail.xml index 72fbf8d0f88..ff8386f32b9 100644 --- a/addons/mail/static/src/xml/mail.xml +++ b/addons/mail/static/src/xml/mail.xml @@ -18,13 +18,13 @@
- - -
-
+
+ +
+
+
-
Image
From fcbd3d26dca82b0c265abd45504b083e54f9ad9d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thibault=20Delavall=C3=A9e?= Date: Thu, 16 Feb 2012 14:34:44 +0100 Subject: [PATCH 0036/1333] [IMP] hr: added parent_id in demo data for employees, allowing to test the need_action_user_id feature with hr.holidays with demo data bzr revid: tde@openerp.com-20120216133444-byv4m8vkh8g68uoe --- addons/hr/hr_demo.xml | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/addons/hr/hr_demo.xml b/addons/hr/hr_demo.xml index b6a11f0b111..00b2ab325e4 100644 --- a/addons/hr/hr_demo.xml +++ b/addons/hr/hr_demo.xml @@ -165,6 +165,7 @@ Fabien Pinckaers + Grand-Rosière @@ -177,6 +178,7 @@ Antony Lesuisse + Grand-Rosière @@ -189,6 +191,7 @@ Minh Tran + Grand-Rosière @@ -202,6 +205,7 @@ Nicolas Vanhoren + Grand-Rosière @@ -214,6 +218,7 @@ Stéphane Wirtel + Grand-Rosière @@ -226,6 +231,7 @@ Christophe Simonis + Grand-Rosière @@ -238,6 +244,7 @@ Quentin De Paoli + Grand-Rosière @@ -249,6 +256,7 @@ Fabien Meghazi + Grand-Rosière @@ -260,6 +268,7 @@ Francois Pietquin + Grand-Rosière @@ -272,6 +281,7 @@ Julien Thewys + Grand-Rosière @@ -296,6 +306,7 @@ Valérie Descamps + Grand-Rosière From 8e14b6a70661dc367e20357afb3550bf12798465 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thibault=20Delavall=C3=A9e?= Date: Thu, 16 Feb 2012 14:43:19 +0100 Subject: [PATCH 0037/1333] [FIX] hr: updated demo data for employee_fp referencing itself as manager bzr revid: tde@openerp.com-20120216134319-8r8ty5h3wbnrkd35 --- addons/hr/hr_demo.xml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/addons/hr/hr_demo.xml b/addons/hr/hr_demo.xml index 00b2ab325e4..f82e2dac39e 100644 --- a/addons/hr/hr_demo.xml +++ b/addons/hr/hr_demo.xml @@ -165,7 +165,6 @@ Fabien Pinckaers - Grand-Rosière @@ -174,6 +173,10 @@ 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 + + + + Antony Lesuisse From 67bb1396605c4e145cfd8b0c397a4232e5626de4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thibault=20Delavall=C3=A9e?= Date: Thu, 16 Feb 2012 17:03:02 +0100 Subject: [PATCH 0038/1333] [REF] mail.thread: fixed an error in is_subscriber, cleaned code; mail.js: threadview widget: cleaned code bzr revid: tde@openerp.com-20120216160302-g5knaca3vie1ekot --- addons/mail/mail_thread.py | 53 ++++++++++-------------- addons/mail/static/src/js/mail.js | 62 +++++++++++++++++------------ addons/mail/static/src/xml/mail.xml | 2 +- 3 files changed, 59 insertions(+), 58 deletions(-) diff --git a/addons/mail/mail_thread.py b/addons/mail/mail_thread.py index 68bf4429d18..f63ebde836c 100644 --- a/addons/mail/mail_thread.py +++ b/addons/mail/mail_thread.py @@ -241,23 +241,19 @@ class mail_thread(osv.osv): # Message loading def message_load_ids(self, cr, uid, ids, context=None): - """ OpenSocial feature added this method - get ids of thread messages + """ OpenSocial feature: return thread messages ids (for web compatibility) + loading messages: search in mail.messages where res_id = ids, (res_)model = current model """ msg_obj = self.pool.get('mail.message') - msg_ids = [msg_obj.search(cr, uid, ['&', ('res_id', '=', id), ('model', '=', self._name)], context=context) for id in ids] - return msg_ids[0] + msg_ids = msg_obj.search(cr, uid, ['&', ('res_id', 'in', ids), ('model', '=', self._name)], context=context) + return msg_ids def message_load(self, cr, uid, ids, context=None): - """ OpenSocial feature added this method - loading message: search in mail.messages where res_id = ids, (res_)model = current model + """ OpenSocial feature: return thread messages + loading messages: search in mail.messages where res_id = ids, (res_)model = current model """ - msg_ids = [] - msg_obj = self.pool.get('mail.message') - for id in ids: - msg_ids += msg_obj.search(cr, uid, ['&', ('res_id', '=', id), ('model', '=', self._name)], context=context) - msgs = msg_obj.read(cr, uid, msg_ids, context=context) - return msgs + msg_ids = self.message_load_ids(cr, uid, ids, context=context) + return self.pool.get('mail.message').read(cr, uid, msg_ids, context=context) #------------------------------------------------------ # Email specific @@ -528,9 +524,10 @@ class mail_thread(osv.osv): Find by: res_id (thread id), model (self._name), need_action_user_id != false """ msg_obj = self.pool.get('mail.message') - for id in ids: - msg_ids = msg_obj.search(cr, uid, ['&', ('res_id', '=', id), ('model', '=', self._name)], context=context) - msg_obj.write(cr, uid, msg_ids, {'need_action_user_id': False}, context=context) + msg_ids = msg_obj.search(cr, uid, + ['&', '&', ('res_id', 'in', ids), ('model', '=', self._name), ('need_action_user_id', '!=', False)], context=context) + msg_obj.write(cr, uid, msg_ids, {'need_action_user_id': False}, context=context) + return True #------------------------------------------------------ # Subscription mechanism @@ -538,22 +535,17 @@ class mail_thread(osv.osv): def message_get_subscribers(self, cr, uid, ids, context=None): subscription_obj = self.pool.get('mail.subscription') - for id in ids: - sub_ids = subscription_obj.search(cr, uid, ['&', ('res_model', '=', self._name), ('res_id', '=', id)], context=context) - subs = subscription_obj.browse(cr, uid, sub_ids, context=context) - return subs - - def message_get_subscribers_web(self, cr, uid, ids, context=None): - subscription_obj = self.pool.get('mail.subscription') - for id in ids: - sub_ids = subscription_obj.search(cr, uid, ['&', ('res_model', '=', self._name), ('res_id', '=', id)], context=context) - subs = subscription_obj.read(cr, uid, sub_ids, context=context) + sub_ids = subscription_obj.search(cr, uid, ['&', ('res_model', '=', self._name), ('res_id', 'in', ids)], context=context) + subs = subscription_obj.read(cr, uid, sub_ids, context=context) return subs def message_is_subscriber(self, cr, uid, ids, context=None): - for subscription in self.message_get_subscribers(cr, uid, ids, context=context): - if subscription.user_id == uid: return True - return False + 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: + print 'cacaprout error !' + return True if sub_ids else False def message_subscribe(self, cr, uid, ids, context=None): subscription_obj = self.pool.get('mail.subscription') @@ -565,9 +557,8 @@ class mail_thread(osv.osv): def message_unsubscribe(self, cr, uid, ids, context=None): subscription_obj = self.pool.get('mail.subscription') subscriber_id = uid # TODO - sub_ids = [] - for id in ids: - sub_ids += subscription_obj.search(cr, uid, ['&', '&', ('res_model', '=', self._name), ('res_id', '=', id), ('user_id', '=', subscriber_id)], context=context) + sub_ids = subscription_obj.search(cr, uid, + ['&', '&', ('res_model', '=', self._name), ('res_id', 'in', ids), ('user_id', '=', subscriber_id)], context=context) subscription_obj.unlink(cr, uid, sub_ids, context=context) return True diff --git a/addons/mail/static/src/js/mail.js b/addons/mail/static/src/js/mail.js index fa7037bcacb..aa1ed33fb6d 100644 --- a/addons/mail/static/src/js/mail.js +++ b/addons/mail/static/src/js/mail.js @@ -14,47 +14,55 @@ openerp.mail = function(session) { template: 'ThreadView', init: function() { -// this.timeout; this.follow_state = 0; this._super.apply(this, arguments); /* DataSets */ this.ds = new session.web.DataSet(this, this.view.model); - this.ds_sub = new session.web.DataSet(this, 'mail.subscription'); -// this.ds_msg = new session.web.DataSet(this, 'mail.message'); }, start: function() { var self = this; this._super.apply(this, arguments); - /* bind follow and unfollow buttons */ - self.$element.find('button.oe_mail_button_follow').bind('click', function () { self.do_follow(); }); - self.$element.find('button.oe_mail_button_follow').hide(); - self.$element.find('button.oe_mail_button_unfollow').bind('click', function () { self.do_unfollow(); }); - self.$element.find('button.oe_mail_button_unfollow').hide(); + /* bind buttons */ self.$element.find('button.oe_mail_button_comment').bind('click', function () { self.do_comment(); }); self.$element.find('button.oe_mail_button_followers').bind('click', function () { self.do_toggle_followers(); }); - /* find wich (un)follow buttons to show */ - var call_res = this.ds.call('message_is_subscriber', [[this.session.uid]]).then(function (records) { - if (records == true) { self.follow_state = 1; self.$element.find('button.oe_mail_button_unfollow').show(); } - else { self.follow_state = 0; self.$element.find('button.oe_mail_button_follow').show(); } - }); + self.$element.find('button.oe_mail_button_follow').bind('click', function () { self.do_follow(); }); + self.$element.find('button.oe_mail_button_unfollow').bind('click', function () { self.do_unfollow(); }); + /* hide follow/unfollow buttons */ + self.$element.find('button.oe_mail_button_follow').hide(); + self.$element.find('button.oe_mail_button_unfollow').hide(); }, stop: function () { -// clearTimeout(this.timeout); - this._super(); + console.log('stop'); + this._super.apply(this, arguments); }, set_value: function() { + var self = this; this._super.apply(this, arguments); + /* hide follow/unfollow buttons */ + self.$element.find('button.oe_mail_button_follow').hide(); + self.$element.find('button.oe_mail_button_unfollow').hide(); if (! this.view.datarecord.id) { return; } - return this.fetch_data(); + /* find wich (un)follow buttons to show */ + var call_res = this.ds.call('message_is_subscriber', [[this.view.datarecord.id]]).then(function (records) { + if (records == true) { self.follow_state = 1; self.$element.find('button.oe_mail_button_unfollow').show(); } + else { self.follow_state = 0; self.$element.find('button.oe_mail_button_follow').show(); } + }); + /* fetch comments and subscribers */ + this.fetch_subscribers(); + return this.fetch_comments(); }, - fetch_data: function () { + fetch_comments: function () { var load_res = this.ds.call('message_load', [[this.view.datarecord.id]]).then( this.proxy('display_comments')); - var follow_res = this.ds.call('message_get_subscribers_web', [[this.view.datarecord.id]]).then( + return load_res; + }, + + fetch_subscribers: function () { + var follow_res = this.ds.call('message_get_subscribers', [[this.view.datarecord.id]]).then( this.proxy('display_followers')); return follow_res; }, @@ -69,35 +77,37 @@ openerp.mail = function(session) { }); $('
').html(render_res).appendTo(self.$element.find('div.oe_mail_msg')); }); -// this.timeout = setTimeout(this.proxy('fetch_messages'), 5000); }, display_followers: function (records) { this.$element.find('div.oe_mail_followers').empty(); var self = this; _(records).each(function (record) { - console.log(record); //
$('
').text(record.user_id[1]).appendTo(self.$element.find('div.oe_mail_followers')); }); }, do_follow: function () { - this.$element.find('button.oe_mail_button_unfollow').show(); - this.$element.find('button.oe_mail_button_follow').hide(); - return this.ds_sub.create({'res_model': this.view.model, 'user_id': this.session.uid, 'res_id': this.view.datarecord.id}).then(); + this.do_toggle_follow(); + return this.ds.call('message_subscribe', [[this.view.datarecord.id]]).then(); }, do_unfollow: function () { - this.$element.find('button.oe_mail_button_follow').show(); - this.$element.find('button.oe_mail_button_unfollow').hide(); + this.do_toggle_follow(); return this.ds.call('message_unsubscribe', [[this.view.datarecord.id]]).then(); }, do_comment: function () { var body_text = this.$element.find('textarea').val(); return this.ds.call('message_append_note', [[this.view.datarecord.id], 'Reply comment', body_text, type='comment']).then( - this.proxy('fetch_messages')); + this.proxy('fetch_comments')); + }, + + do_toggle_follow: function () { + this.follow_state = 1 - this.follow_state; + this.$element.find('button.oe_mail_button_unfollow').toggle(); + this.$element.find('button.oe_mail_button_follow').toggle(); }, do_toggle_followers: function () { diff --git a/addons/mail/static/src/xml/mail.xml b/addons/mail/static/src/xml/mail.xml index ff8386f32b9..50e73f10326 100644 --- a/addons/mail/static/src/xml/mail.xml +++ b/addons/mail/static/src/xml/mail.xml @@ -56,4 +56,4 @@
- \ No newline at end of file + From 64164155d23453a7087dd0740da1783e6332300b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thibault=20Delavall=C3=A9e?= Date: Thu, 16 Feb 2012 17:48:37 +0100 Subject: [PATCH 0039/1333] [FIX] mail_message: fixed notification creation, propagating a subscription model modification (from int to many2one); fixed unprotected accesses to 'need_action_user_id' key in vals dictionnary bzr revid: tde@openerp.com-20120216164837-jscxxu52sybvy7db --- addons/mail/mail_message.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/addons/mail/mail_message.py b/addons/mail/mail_message.py index 58e5833fa49..9ada617a177 100644 --- a/addons/mail/mail_message.py +++ b/addons/mail/mail_message.py @@ -209,10 +209,10 @@ class mail_message(osv.osv): ('res_id', '=', vals['res_id'])], context=context) subs = subscription_obj.browse(cr, uid, sub_ids, context=context) for sub in subs: - notification_obj.create(cr, uid, {'user_id': sub.user_id, 'message_id': msg_id}, context=context) - if vals['need_action_user_id'] == sub.user_id: need_action_pushed = True + notification_obj.create(cr, uid, {'user_id': sub.user_id.id, 'message_id': msg_id}, context=context) + if vals.get('need_action_user_id', False) == sub.user_id: need_action_pushed = True # push to need_action_user_id if user does not follow the object - if vals['need_action_user_id'] != False and not need_action_pushed: + if vals.get('need_action_user_id', False) and not need_action_pushed: notification_obj.create(cr, uid, {'user_id': vals['need_action_user_id'], 'message_id': msg_id}, context=context) return msg_id From 12514823e69781fdf6509c7e07dda2f9d6d67311 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thibault=20Delavall=C3=A9e?= Date: Thu, 16 Feb 2012 18:04:46 +0100 Subject: [PATCH 0040/1333] [FIX] Cleaned create method for mail_message; added protection against unknown keys in vals parameter bzr revid: tde@openerp.com-20120216170446-h4u8t3znp7p4lerb --- addons/mail/mail_message.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/addons/mail/mail_message.py b/addons/mail/mail_message.py index 9ada617a177..03eb57bfd0f 100644 --- a/addons/mail/mail_message.py +++ b/addons/mail/mail_message.py @@ -204,15 +204,19 @@ class mail_message(osv.osv): # push the message to suscribed users subscription_obj = self.pool.get('mail.subscription') notification_obj = self.pool.get('mail.notification') + # not pure-email: check for subscriptions + if not 'need_action_user_id' in vals: vals['need_action_user_id'] = False + if not 'model' in vals: vals['model'] = False + if not 'res_id' in vals: vals['res_id'] = 0 sub_ids = subscription_obj.search(cr, uid, ['&', ('res_model', '=', vals['model']), ('res_id', '=', vals['res_id'])], context=context) subs = subscription_obj.browse(cr, uid, sub_ids, context=context) for sub in subs: notification_obj.create(cr, uid, {'user_id': sub.user_id.id, 'message_id': msg_id}, context=context) - if vals.get('need_action_user_id', False) == sub.user_id: need_action_pushed = True + if vals['need_action_user_id'] == sub.user_id: need_action_pushed = True # push to need_action_user_id if user does not follow the object - if vals.get('need_action_user_id', False) and not need_action_pushed: + if vals['need_action_user_id'] and not need_action_pushed: notification_obj.create(cr, uid, {'user_id': vals['need_action_user_id'], 'message_id': msg_id}, context=context) return msg_id From b23dd504c4d572fad8478f040a36b3571066f2e1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thibault=20Delavall=C3=A9e?= Date: Mon, 20 Feb 2012 10:22:39 +0100 Subject: [PATCH 0041/1333] [FIX] Fixed template var name to match rev 2225 of web client bzr revid: tde@openerp.com-20120220092239-l1e35sv2l7qfzegx --- addons/mail/static/src/js/mail.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addons/mail/static/src/js/mail.js b/addons/mail/static/src/js/mail.js index aa1ed33fb6d..ae98891eda9 100644 --- a/addons/mail/static/src/js/mail.js +++ b/addons/mail/static/src/js/mail.js @@ -11,7 +11,7 @@ openerp.mail = function(session) { /* ThreadView widget: thread of comments */ mail.ThreadView = session.web.form.Field.extend({ // QWeb template to use when rendering the object - template: 'ThreadView', + form_template: 'ThreadView', init: function() { this.follow_state = 0; From b8612374d7f4b202a1f5c092bc7bd695864848a0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thibault=20Delavall=C3=A9e?= Date: Mon, 20 Feb 2012 12:17:05 +0100 Subject: [PATCH 0042/1333] [IMP] crm_case: removed generic notification when changing state. Messages are now delegated to upper modules, that will have to write consistent and useful notifications. bzr revid: tde@openerp.com-20120220111705-4q7x3ukgvnz3vhe1 --- addons/crm/crm.py | 6 ------ 1 file changed, 6 deletions(-) diff --git a/addons/crm/crm.py b/addons/crm/crm.py index 4ebb0b4796d..7775e442df7 100644 --- a/addons/crm/crm.py +++ b/addons/crm/crm.py @@ -393,7 +393,6 @@ class crm_case(crm_base): def case_open(self, cr, uid, ids, *args): """Opens Case""" cases = self.browse(cr, uid, ids) - self.message_append(cr, uid, cases, _('Open')) for case in cases: data = {'state': 'open', 'active': True } if not case.user_id: @@ -406,7 +405,6 @@ class crm_case(crm_base): """Closes Case""" cases = self.browse(cr, uid, ids) cases[0].state # to fill the browse record cache - self.message_append(cr, uid, cases, _('Close')) self.write(cr, uid, ids, {'state': 'done', 'date_closed': time.strftime('%Y-%m-%d %H:%M:%S'), }) @@ -430,7 +428,6 @@ class crm_case(crm_base): raise osv.except_osv(_('Error !'), _('You can not escalate, you are already at the top level regarding your sales-team category.')) self.write(cr, uid, [case.id], data) cases = self.browse(cr, uid, ids) - self.message_append(cr, uid, cases, _('Escalate')) self._action(cr, uid, cases, 'escalate') return True @@ -438,7 +435,6 @@ class crm_case(crm_base): """Cancels Case""" cases = self.browse(cr, uid, ids) cases[0].state # to fill the browse record cache - self.message_append(cr, uid, cases, _('Cancel')) self.write(cr, uid, ids, {'state': 'cancel', 'active': True}) self._action(cr, uid, cases, 'cancel') @@ -451,7 +447,6 @@ class crm_case(crm_base): """Marks case as pending""" cases = self.browse(cr, uid, ids) cases[0].state # to fill the browse record cache - self.message_append(cr, uid, cases, _('Pending')) self.write(cr, uid, ids, {'state': 'pending', 'active': True}) self._action(cr, uid, cases, 'pending') return True @@ -463,7 +458,6 @@ class crm_case(crm_base): state = 'open' cases = self.browse(cr, uid, ids) cases[0].state # to fill the browse record cache - self.message_append(cr, uid, cases, _('Draft')) self.write(cr, uid, ids, {'state': state, 'active': True}) self._action(cr, uid, cases, state) return True From 674680484179c93d424461011ce4e1503747b369 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thibault=20Delavall=C3=A9e?= Date: Mon, 20 Feb 2012 17:11:57 +0100 Subject: [PATCH 0043/1333] [REF] mail_thread: deleted unused mail_thread views, now unecessary since content is not managed by a message_ids o2m field anymore bzr revid: tde@openerp.com-20120220161157-b9m23nq1czt4uoy3 --- addons/mail/mail_thread_view.xml | 60 -------------------------------- 1 file changed, 60 deletions(-) diff --git a/addons/mail/mail_thread_view.xml b/addons/mail/mail_thread_view.xml index d2e557d06d8..a71ae040328 100644 --- a/addons/mail/mail_thread_view.xml +++ b/addons/mail/mail_thread_view.xml @@ -1,62 +1,12 @@ - - mail.thread.form - mail.thread - form - -
- - - - - - - - - -
- - - mail.thread.tree - mail.thread - tree - - - - - - - - - - Email Threads - mail.thread - tree,form - form - - - - - tree - - - - - - form - - - Feeds - - @@ -83,16 +33,6 @@ - -
From 1469deff854c9bf0369ece9c7f639d2fe0257b69 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thibault=20Delavall=C3=A9e?= Date: Mon, 20 Feb 2012 18:21:06 +0100 Subject: [PATCH 0044/1333] [IMP] mail_thread API: message_get_subscribers now returns res.users read results instead of mail.subscription results. message_subscribe has now an optional parameter user_ids to subscribe ids in user_ids instead of uid. bzr revid: tde@openerp.com-20120220172106-fpjafkkvea0m1k93 --- addons/mail/mail_thread.py | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/addons/mail/mail_thread.py b/addons/mail/mail_thread.py index f63ebde836c..e0a42f3cfe7 100644 --- a/addons/mail/mail_thread.py +++ b/addons/mail/mail_thread.py @@ -533,11 +533,16 @@ class mail_thread(osv.osv): # Subscription mechanism #------------------------------------------------------ - def message_get_subscribers(self, cr, uid, ids, context=None): + def message_get_subscribers_ids(self, cr, uid, ids, context=None): subscription_obj = self.pool.get('mail.subscription') sub_ids = subscription_obj.search(cr, uid, ['&', ('res_model', '=', self._name), ('res_id', 'in', ids)], context=context) subs = subscription_obj.read(cr, uid, sub_ids, context=context) - return subs + return [sub['user_id'] for sub in subs] + + def message_get_subscribers(self, cr, uid, ids, context=None): + user_ids = self.message_get_subscribers_ids(cr, uid, ids, context=context) + users = self.pool.get('res.users').read(cr, uid, user_ids, context=context) + return users def message_is_subscriber(self, cr, uid, ids, context=None): subscription_obj = self.pool.get('mail.subscription') @@ -547,12 +552,13 @@ class mail_thread(osv.osv): print 'cacaprout error !' return True if sub_ids else False - def message_subscribe(self, cr, uid, ids, context=None): + def message_subscribe(self, cr, uid, ids, user_ids = None, context=None): subscription_obj = self.pool.get('mail.subscription') - subscriber_id = uid # TODO + sub_user_ids = [uid] if user_ids is None else user_ids for id in ids: - subscription_obj.create(cr, uid, {'res_model': self._name, 'res_id': id, 'user_id': subscriber_id}, context=context) - return True + 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] + return create_ids def message_unsubscribe(self, cr, uid, ids, context=None): subscription_obj = self.pool.get('mail.subscription') From 3a53f777dfc5e3ce640f78d36acf94524e002d43 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thibault=20Delavall=C3=A9e?= Date: Mon, 20 Feb 2012 18:22:30 +0100 Subject: [PATCH 0045/1333] [FIX] Propagated API modification: message_get_subscribers result are now read records from res.users bzr revid: tde@openerp.com-20120220172230-mj7vx5akh0xrmzs6 --- addons/mail/static/src/js/mail.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/addons/mail/static/src/js/mail.js b/addons/mail/static/src/js/mail.js index ae98891eda9..b9404885b6a 100644 --- a/addons/mail/static/src/js/mail.js +++ b/addons/mail/static/src/js/mail.js @@ -84,7 +84,8 @@ openerp.mail = function(session) { var self = this; _(records).each(function (record) { //
- $('
').text(record.user_id[1]).appendTo(self.$element.find('div.oe_mail_followers')); + //$('
').text(record.user_id[1]).appendTo(self.$element.find('div.oe_mail_followers')); + $('
').text(record.name).appendTo(self.$element.find('div.oe_mail_followers')); }); }, From a2ef45aaebe2d75a7fb00c23276170bffbac3d58 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thibault=20Delavall=C3=A9e?= Date: Mon, 20 Feb 2012 18:23:23 +0100 Subject: [PATCH 0046/1333] [IMP] hr_holidays: improved notifications, added notifications when creating or refusing a leave requet/allocation. bzr revid: tde@openerp.com-20120220172323-4kqkhxntcgx6g178 --- addons/hr_holidays/hr_holidays.py | 80 ++++++++++++++++++++++++------- 1 file changed, 64 insertions(+), 16 deletions(-) diff --git a/addons/hr_holidays/hr_holidays.py b/addons/hr_holidays/hr_holidays.py index a2acea4792d..a8573b9bf7c 100644 --- a/addons/hr_holidays/hr_holidays.py +++ b/addons/hr_holidays/hr_holidays.py @@ -151,7 +151,12 @@ class hr_holidays(osv.osv): ('date_check2', "CHECK ( (type='add') OR (date_from <= date_to))", "The start date must be before the end date !"), ('date_check', "CHECK ( number_of_days_temp >= 0 )", "The number of days must be greater than 0 !"), ] - + + def create(self, cr, uid, vals, context=None): + obj_id = super(hr_holidays, self).create(cr, uid, vals, context=context) + self.create_notificate(cr, uid, [obj_id], context=context) + return obj_id + def _create_resource_leave(self, cr, uid, leaves, context=None): '''This method will create entry in resource calendar leave object at the time of holidays validated ''' obj_res_leave = self.pool.get('resource.calendar.leaves') @@ -253,13 +258,6 @@ class hr_holidays(osv.osv): manager = ids2 and ids2[0] or False self.holidays_validate_notificate(cr, uid, ids, context=context) return self.write(cr, uid, ids, {'state':'validate1', 'manager_id': manager}) - - def holidays_validate_notificate(self, cr, uid, ids, context=None): - for obj in self.browse(cr, uid, ids): - self.message_mark_done(cr, uid, [obj.id], context=context) - self.message_append_note(cr, uid, [obj.id], _('System notification'), _("The %s request '%s' has been validated.") % ('leave' if obj.type == 'remove' else 'allocation', obj.name,), type='notification', context=context) - if obj.holiday_status_id.double_validation: - self.message_append_note(cr, uid, [obj.id], _('System notification'), _("The %s request '%s' is waiting for second validation.") % ('leave' if obj.type == 'remove' else 'allocation', obj.name,), type='notification', context=context) def holidays_validate2(self, cr, uid, ids, context=None): self.check_holidays(cr, uid, ids, context=context) @@ -313,20 +311,12 @@ class hr_holidays(osv.osv): self.holidays_valid2_notificate(self, cr, uid, [holiday_ids], context=context) self.write(cr, uid, holiday_ids, {'manager_id2': manager}) return True - - def holidays_valid2_notificate(self, cr, uid, ids, context=None): - for obj in self.browse(cr, uid, ids): - self.message_append_note(cr, uid, [obj.id], _('System notification'), _("The %s request '%s' has been double validated.") % ('leave' if obj.type == 'remove' else 'allocation', obj.name,), type='notification', context=context) def holidays_confirm(self, cr, uid, ids, context=None): self.check_holidays(cr, uid, ids, context=context) self.holidays_confirm_notificate(cr, uid, ids, context=context) return self.write(cr, uid, ids, {'state':'confirm'}) - def holidays_confirm_notificate(self, cr, uid, ids, context=None): - for obj in self.browse(cr, uid, ids): - self.message_append_note(cr, uid, [obj.id], _('System notification'), _("The %s request '%s' has been confirmed and is waiting for validation.") % ('leave' if obj.type == 'remove' else 'allocation', obj.name,), type='notification', need_action_user_id = obj.employee_id.parent_id.user_id.id if obj.employee_id.parent_id else False) - def holidays_refuse(self, cr, uid, ids, approval, context=None): obj_emp = self.pool.get('hr.employee') ids2 = obj_emp.search(cr, uid, [('user_id', '=', uid)]) @@ -335,6 +325,7 @@ class hr_holidays(osv.osv): self.write(cr, uid, ids, {'state': 'refuse', 'manager_id': manager}) else: self.write(cr, uid, ids, {'state': 'refuse', 'manager_id2': manager}) + self.holidays_refuse_notificate(cr, uid, ids, approval, context=context) self.holidays_cancel(cr, uid, ids, context=context) return True @@ -362,6 +353,63 @@ class hr_holidays(osv.osv): if leaves_rest < record.number_of_days_temp: raise osv.except_osv(_('Warning!'),_('You cannot validate leaves for employee %s: too few remaining days (%s).') % (record.employee_id.name, leaves_rest)) return True + + # ----------------------------- + # OpenChatter and notifications + # ----------------------------- + + def create_notificate(self, cr, uid, ids, context=None): + obj = self.browse(cr, uid, ids, context=context)[0] + self.message_append_note(cr, uid, ids, _('System notification'), + _("""The %s request '%s' has been created and is in draft mode. In this mode, it can be modified freely. + Click on Confirm to confirm your request and ask for its validation by the managers.""") + % ('leave' if obj.type == 'remove' else 'allocation', obj.name), type='notification', context=context) + return True + + def message_get_subscribers(self, cr, uid, ids, context=None): + """OpenChatter: override message_get_subscribers to add : + - employee related user + - employee manager user, if it exists""" + users = super(hr_holidays, self).message_get_subscribers(cr, uid, ids, context=context) + user_ids = [] + for holidays in self.browse(cr, uid, ids, context=context): + user_ids.append(holidays.user_id.id) + if holidays.employee_id.parent_id: + user_ids.append(holidays.employee_id.parent_id.user_id.id) + users += self.pool.get('res.users').read(cr, uid, user_ids, context=context) + return users + + def holidays_confirm_notificate(self, cr, uid, ids, context=None): + for obj in self.browse(cr, uid, ids): + self.message_append_note(cr, uid, [obj.id], _('System notification'), + _("The %s request '%s' has been confirmed. It is now waiting for validation by the manager.") + % ('leave' if obj.type == 'remove' else 'allocation', obj.name,), type='notification', + need_action_user_id = obj.employee_id.parent_id.user_id.id if obj.employee_id.parent_id else False) + + def holidays_validate_notificate(self, cr, uid, ids, context=None): + for obj in self.browse(cr, uid, ids): + self.message_mark_done(cr, uid, [obj.id], context=context) + if obj.holiday_status_id.double_validation: + self.message_append_note(cr, uid, [obj.id], _('System notification'), + _("The %s request '%s' has been validated. A second validation of your request is necessary and is now pending.") + % ('leave' if obj.type == 'remove' else 'allocation', obj.name), type='notification', context=context) + else: + self.message_append_note(cr, uid, [obj.id], _('System notification'), + _("The %s request '%s' has been validated. The validation process is now over.") + % ('leave' if obj.type == 'remove' else 'allocation', obj.name), type='notification', context=context) + + def holidays_valid2_notificate(self, cr, uid, ids, context=None): + for obj in self.browse(cr, uid, ids): + self.message_append_note(cr, uid, [obj.id], _('System notification'), + _("The %s request '%s' has been double validated. The validation process is now over.") + % ('leave' if obj.type == 'remove' else 'allocation', obj.name,), type='notification', context=context) + + def holidays_refuse_notificate(self, cr, uid, ids, approval, context=None): + for obj in self.browse(cr, uid, ids): + self.message_append_note(cr, uid, [obj.id], _('System notification'), + _("The %s request '%s' has been refused. The validation process is now over.") + % ('leave' if obj.type == 'remove' else 'allocation', obj.name,), type='notification', context=context) + hr_holidays() class resource_calendar_leaves(osv.osv): From 592a41fa3e493ca6de87f91201b574063a3ab43b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thibault=20Delavall=C3=A9e?= Date: Tue, 21 Feb 2012 15:21:26 +0100 Subject: [PATCH 0047/1333] [FIX] mail.thread: fixed message_get_subscribers returning a tuple list, not an id list (RGA) bzr revid: tde@openerp.com-20120221142126-dzr4um7tfq1knxcv --- addons/mail/mail_thread.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addons/mail/mail_thread.py b/addons/mail/mail_thread.py index e0a42f3cfe7..b3592e0954e 100644 --- a/addons/mail/mail_thread.py +++ b/addons/mail/mail_thread.py @@ -537,7 +537,7 @@ class mail_thread(osv.osv): subscription_obj = self.pool.get('mail.subscription') sub_ids = subscription_obj.search(cr, uid, ['&', ('res_model', '=', self._name), ('res_id', 'in', ids)], context=context) subs = subscription_obj.read(cr, uid, sub_ids, context=context) - return [sub['user_id'] for sub in subs] + return [sub['user_id'][0] for sub in subs] def message_get_subscribers(self, cr, uid, ids, context=None): user_ids = self.message_get_subscribers_ids(cr, uid, ids, context=context) From 413df331044c61f8015cb9f580caaf424346dbdb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thibault=20Delavall=C3=A9e?= Date: Tue, 21 Feb 2012 16:39:03 +0100 Subject: [PATCH 0048/1333] [IMP] Removed space in a note. bzr revid: tde@openerp.com-20120221153903-px8wbaia3y9h4rvg --- addons/hr_holidays/hr_holidays.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addons/hr_holidays/hr_holidays.py b/addons/hr_holidays/hr_holidays.py index a8573b9bf7c..82d3d7d51cb 100644 --- a/addons/hr_holidays/hr_holidays.py +++ b/addons/hr_holidays/hr_holidays.py @@ -361,7 +361,7 @@ class hr_holidays(osv.osv): def create_notificate(self, cr, uid, ids, context=None): obj = self.browse(cr, uid, ids, context=context)[0] self.message_append_note(cr, uid, ids, _('System notification'), - _("""The %s request '%s' has been created and is in draft mode. In this mode, it can be modified freely. + _("""The %s request '%s' has been created and is in draft mode. In this mode, it can be modified freely. \ Click on Confirm to confirm your request and ask for its validation by the managers.""") % ('leave' if obj.type == 'remove' else 'allocation', obj.name), type='notification', context=context) return True From 7429f3bfb6797b1bdd97e05afdd5992a3bd320ac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thibault=20Delavall=C3=A9e?= Date: Tue, 21 Feb 2012 16:39:42 +0100 Subject: [PATCH 0049/1333] [IMP] Slighty improved wall widget. bzr revid: tde@openerp.com-20120221153942-mue1h18f2fjluf27 --- addons/mail/static/src/css/mail.css | 5 +++++ addons/mail/static/src/js/mail.js | 27 ++++++++++++--------------- addons/mail/static/src/xml/mail.xml | 10 ++++++---- 3 files changed, 23 insertions(+), 19 deletions(-) diff --git a/addons/mail/static/src/css/mail.css b/addons/mail/static/src/css/mail.css index 0d153ce0c06..be5e8425630 100644 --- a/addons/mail/static/src/css/mail.css +++ b/addons/mail/static/src/css/mail.css @@ -5,6 +5,11 @@ padding: 5px 5px 5px 5px; } +.oe_mail_wall_main { + overflow: auto; + padding: 5px 5px 5px 5px; +} + /* 2 columns view */ .oe_mail_main_left { float: left; diff --git a/addons/mail/static/src/js/mail.js b/addons/mail/static/src/js/mail.js index b9404885b6a..cd2e1f2ac5d 100644 --- a/addons/mail/static/src/js/mail.js +++ b/addons/mail/static/src/js/mail.js @@ -34,7 +34,6 @@ openerp.mail = function(session) { }, stop: function () { - console.log('stop'); this._super.apply(this, arguments); }, @@ -134,39 +133,37 @@ openerp.mail = function(session) { start: function() { var self = this; this._super.apply(this, arguments); - console.log(this); - self.$element.find('button.oe_mail_action_comment').bind('click', function () { self.do_comment(); }); - this.ds_msg.call('get_pushed_messages', [[], self.filter_search]).then( - this.proxy('display_records')); + self.$element.find('button.oe_mail_button_comment').bind('click', function () { self.do_comment(); }); + return this.fetch_comments(); }, stop: function () { this._super(); }, - fetch_messages: function () { - console.log('debug--fetch_messages'); - return this.ds_msg.call('get_pushed_messages', []).then( - this.proxy('display_records')); + fetch_comments: function () { + var load_res = this.ds_msg.call('get_pushed_messages', [[this.session.uid]]).then( + this.proxy('display_comments')); + return load_res; }, - display_records: function (records) { - this.$element.find('div.oe_mail_comments').empty(); + display_comments: function (records) { + this.$element.find('div.oe_mail_msg').empty(); var self = this; _(records).each(function (record) { + console.log(record); var template = 'ThreadMsgView'; var render_res = session.web.qweb.render(template, { 'record': record, }); - $('
').html(render_res).appendTo(self.$element.find('div.oe_mail_comments')); + $('
').html(render_res).appendTo(self.$element.find('div.oe_mail_msg')); }); -// this.timeout = setTimeout(this.proxy('fetch_messages'), 5000); }, do_comment: function () { var body_text = this.$element.find('textarea').val(); - return this.ds.call('message_append_note', [[this.view.datarecord.id], 'Reply comment', body_text, type='comment']).then( - this.proxy('fetch_messages')); + return this.ds_msg.call('create', [{'subject': 'Status tweet', 'model': 'res.users', 'body_text': body_text, 'type': 'comment'}]).then( + this.proxy('fetch_comments')); }, }); }; diff --git a/addons/mail/static/src/xml/mail.xml b/addons/mail/static/src/xml/mail.xml index 50e73f10326..ff69ef4c29f 100644 --- a/addons/mail/static/src/xml/mail.xml +++ b/addons/mail/static/src/xml/mail.xml @@ -47,12 +47,14 @@
-
+
-
- +
+
-
+
+
+
From 9f6af7c9747d8f85e3296d3044f13b735834dc70 Mon Sep 17 00:00:00 2001 From: Xavier Morel Date: Tue, 21 Feb 2012 18:04:55 +0100 Subject: [PATCH 0050/1333] [ADD] asynchronous javascript guide, as it'll be needed by the networking/RPC guide bzr revid: xmo@openerp.com-20120221170455-rcrbyvz1ozj7mfib --- doc/source/async.rst | 353 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 353 insertions(+) create mode 100644 doc/source/async.rst diff --git a/doc/source/async.rst b/doc/source/async.rst new file mode 100644 index 00000000000..23b3409bd8f --- /dev/null +++ b/doc/source/async.rst @@ -0,0 +1,353 @@ +Don't stop the world now: asynchronous development and Javascript +================================================================= + +As a language (and runtime), javascript is fundamentally +single-threaded. This means any blocking request or computation will +blocks the whole page (and, in older browsers, the software itself +even preventing users from switching to an other tab): a javascript +environment can be seen as an event-based runloop where application +developers have no control over the runloop itself. + +As a result, performing long-running synchronous network requests or +other types of complex and expensive accesses is frowned upon and +asynchronous APIs are used instead. + +Asynchronous code rarely comes naturally, especially for developers +used to synchronous server-side code (in Python, Java or C#) where the +code will just block until the deed is gone. This is increased further +when asynchronous programming is not a first-class concept and is +instead implemented on top of callbacks-based programming, which is +the case in javascript. + +The goal of this guide is to provide some tools to deal with +asynchronous systems, and warn against systematic issues or dangers. + +Deferreds +--------- + +Deferreds are a form of `promises`_. OpenERP Web currently uses +`jQuery's deferred`_, but any `CommonJS Promises/A`_ implementation +should work. + +The core idea of deferreds is that potentially asynchronous methods +will return a :js:class:`Deferred` object instead of an arbitrary +value or (most commonly) nothing. + +This object can then be used to track the end of the asynchronous +operation by adding callbacks onto it, either success callbacks or +error callbacks. + +A great advantage of deferreds over simply passing callback functions +directly to asynchronous methods is the ability to :ref:`compose them +`. + +Using deferreds +~~~~~~~~~~~~~~~ + +`CommonJS Promises/A`_ deferreds have only one method of importance: +:js:func:`Deferred.then`. This method is used to attach new callbacks +to the deferred object. + +* the first parameter attaches a success callback, called when the + deferred object is successfully resolved and provided with the + resolved value(s) for the asynchronous operation. + +* the second parameter attaches a failure callback, called when the + deferred object is rejected and provided with rejection values + (often some sort of error message). + +Callbacks attached to deferreds are never "lost": if a callback is +attached to an already resolved or rejected deferred, the callback +will be called (or ignored) immediately. A deferred is also only ever +resolved or rejected once, and is either resolved or rejected: a given +deferred can not call a single success callback twice, or call both a +success and a failure callbacks. + +:js:func:`~Deferred.then` should be the method you'll use most often +when interacting with deferred objects (and thus asynchronous APIs). + +Building deferreds +~~~~~~~~~~~~~~~~~~ + +After using asynchronous APIs may come the time to build them: for +`mocks`_, to compose deferreds from multiple source in a complex +manner, in order to let the current operations repaint the screen or +give other events the time to unfold, ... + +This is easy using jQuery's deferred objects. + +.. note:: this section is an implementation detail of jQuery Deferred + objects, the creation of promises is not part of any + standard (even tentative) that I know of. If you are using + deferred objects which are not jQuery's, their API may (and + often will) be completely different. + +Deferreds are created by invoking their constructor [#]_ without any +argument. This creates a :js:class:`Deferred` instance object with the +following methods: + +:js:func:`Deferred.resolve` + + As its name indicates, this method moves the deferred to the + "Resolved" state. It can be provided as many arguments as + necessary, these arguments will be provided to any pending success + callback. + +:js:func:`Deferred.reject` + + Similar to :js:func:`~Deferred.resolve`, but moves the deferred to + the "Rejected" state and calls pending failure handlers. + +:js:func:`Deferred.promise` + + Creates a readonly view of the deferred object. It is generally a + good idea to return a promise view of the deferred to prevent + callers from resolving or rejecting the deferred in your stead. + +:js:func:`~Deferred.reject` and :js:func:`~Deferred.resolve` are used +to inform callers that the asynchronous operation has failed (or +succeeded). These methods should simply be called when the +asynchronous operation has ended, to notify anybody interested in its +result(s). + +.. _deferred-composition: + +Composing deferreds +~~~~~~~~~~~~~~~~~~~ + +What we've seen so far is pretty nice, but mostly doable by passing +functions to other functions (well adding functions post-facto would +probably be a chore... still, doable). + +Deferreds truly shine when code needs to compose asynchronous +operations in some way or other, as they can be used as a basis for +such composition. + +There are two main forms of compositions over deferred: multiplexing +and piping/cascading. + +Deferred multiplexing +````````````````````` + +The most common reason for multiplexing deferred is simply performing +2+ asynchronous operations and wanting to wait until all of them are +done before moving on (and executing more stuff). + +The jQuery multiplexing function for promises is :js:func:`when`. + +.. note:: the multiplexing behavior of jQuery's :js:func:`when` is an + (incompatible, mostly) extension of the behavior defined in + `CommonJS Promises/B`_. + +This function can take any number of promises [#]_ and will return a +promise. + +This returned promise will be resolved when *all* multiplexed promises +are resolved, and will be rejected as soon as one of the multiplexed +promises is rejected (it behaves like Python's ``all()``, but with +promise objects instead of boolean-ish). + +The resolved values of the various promises multiplexed via +:js:func:`when` are mapped to the arguments of :js:func:`when`'s +success callback, if they are needed. The resolved values of a promise +are at the same index in the callback's arguments as the promise in +the :js:func:`when` call so you will have: + +.. code-block:: javascript + + $.when(p0, p1, p2, p3).then( + function (results0, results1, results2, results3) { + // code + }); + +.. warning:: + + in a normal mapping, each parameter to the callback would be an + array: each promise is conceptually resolved with an array of 0..n + values and these values are passed to :js:func:`when`'s + callback. But jQuery treats deferreds resolving a single value + specially, and "unwraps" that value. + + For instance, in the code block above if the index of each promise + is the number of values it resolves (0 to 3), ``results0`` is an + empty array, ``results2`` is an array of 2 elements (a pair) but + ``results1`` is the actual value resolved by ``p1``, not an array. + +Deferred chaining +````````````````` + +A second useful composition is starting an asynchronous operation as +the result of an other asynchronous operation, and wanting the result +of both: :js:func:`Deferred.then` returns the deferred on which it was +called, so handle e.g. OpenERP's search/read sequence with this would +require something along the lines of: + +.. code-block:: javascript + + var result = $.Deferred(); + Model.search(condition).then(function (ids) { + Model.read(ids, fields).then(function (records) { + result.resolve(records); + }); + }); + return result.promise(); + +While it doesn't look too bad for trivial code, this quickly gets +unwieldy. + +Instead, jQuery provides a tool to handle this kind of chains: +:js:func:`Deferred.pipe`. + +:js:func:`~Deferred.pipe` has the same signature as +:js:func:`~Deferred.then` and could be used in the same manner +provided its return value was not used. + +It differs from :js:func:`~Deferred.then` in two ways: it returns a +new promise object, not the one it was called with, and the return +values of the callbacks is actually important to it: whichever +callback is called, + +* If the callback is not set (not provided or left to null), the + resolution or rejection value(s) is simply forwarded to + :js:func:`~Deferred.pipe`'s promise (it's essentially a noop) + +* If the callback is set and does not return an observable object (a + deferred or a promise), the value it returns (``undefined`` if it + does not return anything) will replace the value it was given, e.g. + + .. code-block:: javascript + + promise.pipe(function () { + console.log('called'); + }); + + will resolve with the sole value ``undefined``. + +* If the callback is set and returns an observable object, that object + will be the actual resolution (and result) of the pipe. This means a + resolved promise from the failure callback will resolve the pipe, + and a failure promise from the success callback will reject the + pipe. + + This provides an easy way to chain operation successes, and the + previous piece of code can now be rewritten: + + .. code-block:: javascript + + return Model.search(condition).pipe(function (ids) { + return Model.read(ids, fields); + }); + + the result of the whole expression will encode failure if either + ``search`` or ``read`` fails (with the right rejection values), and + will be resolved with ``read``'s resolution values if the chain + executes correctly. + +:js:func:`~Deferred.pipe` is also useful to adapt third-party +promise-based APIs, in order to filter their resolution value counts +for instance (to take advantage of :js:func:`when` 's special treatment +of single-value promises). + +jQuery.Deferred API +~~~~~~~~~~~~~~~~~~~ + +.. js:function:: when(deferreds…) + + :param deferreds: deferred objects to multiplex + :returns: a multiplexed deferred + :rtype: :js:class:`Deferred` + +.. js:class:: Deferred + + .. js:function:: Deferred.then(doneCallback[, failCallback]) + + Attaches new callbacks to the resolution or rejection of the + deferred object. Callbacks are executed in the order they are + attached to the deferred. + + To provide only a failure callback, pass ``null`` as the + ``doneCallback``, to provide only a success callback the + second argument can just be ignored (and not passed at all). + + :param doneCallback: function called when the deferred is resolved + :type doneCallback: Function + :param failCallback: function called when the deferred is rejected + :type failCallback: Function + :returns: the deferred object on which it was called + :rtype: :js:class:`Deferred` + + .. js:function:: Deferred.done(doneCallback) + + Attaches a new success callback to the deferred, shortcut for + ``deferred.then(doneCallback)``. + + This is a jQuery extension to `CommonJS Promises/A`_ providing + little value over calling :js:func:`~Deferred.then` directly, + it should be avoided. + + :param doneCallback: function called when the deferred is resolved + :type doneCallback: Function + :returns: the deferred object on which it was called + :rtype: :js:class:`Deferred` + + .. js:function:: Deferred.fail(failCallback) + + Attaches a new failure callback to the deferred, shortcut for + ``deferred.then(null, failCallback)``. + + A second jQuery extension to `Promises/A `_. Although it provides more value than + :js:func:`~Deferred.done`, it still is not much and should be + avoided as well. + + :param failCallback: function called when the deferred is rejected + :type failCallback: Function + :returns: the deferred object on which it was called + :rtype: :js:class:`Deferred` + + .. js:function:: Deferred.promise() + + Returns a read-only view of the deferred object, with all + mutators (resolve and reject) methods removed. + + .. js:function:: Deferred.resolve(value…) + + Called to resolve a deferred, any value provided will be + passed onto the success handlers of the deferred object. + + Resolving a deferred which has already been resolved or + rejected has no effect. + + .. js:function:: Deferred.reject(value…) + + Called to reject (fail) a deferred, any value provided will be + passed onto the failure handler of the deferred object. + + Rejecting a deferred which has already been resolved or + rejected has no effect. + + .. js:function:: Deferred.pipe(doneFilter[, failFilter]) + + Filters the result of a deferred, able to transform a success + into failure and a failure into success, or to delay + resolution further. + +.. [#] or simply calling :js:class:`Deferred` as a function, the + result is the same + +.. [#] or not-promises, the `CommonJS Promises/B`_ role of + :js:func:`when` is to be able to treat values and promises + uniformly: :js:func:`when` will pass promises through directly, + but non-promise values and objects will be transformed into a + resolved promise (resolving themselves with the value itself). + + jQuery's :js:func:`when` keeps this behavior making deferreds + easy to build from "static" values, or allowing defensive code + where expected promises are wrapped in :js:func:`when` just in + case. + +.. _promises: http://en.wikipedia.org/wiki/Promise_(programming) +.. _jQuery's deferred: http://api.jquery.com/category/deferred-object/ +.. _CommonJS Promises/A: http://wiki.commonjs.org/wiki/Promises/A +.. _CommonJS Promises/B: http://wiki.commonjs.org/wiki/Promises/B +.. _mocks: http://en.wikipedia.org/wiki/Mock_object From babd47b963aa421c5b92624a485eb509ea7e970e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thibault=20Delavall=C3=A9e?= Date: Wed, 22 Feb 2012 10:09:23 +0100 Subject: [PATCH 0051/1333] [IMP] Notification push moved from mail.message to mail.thread to use message_get_subscribers method. Also added a temporary debug log on message.create method, to help tracking messages not using mail.thread. bzr revid: tde@openerp.com-20120222090923-1h6wx3bkwr5ngt4d --- addons/mail/mail_message.py | 31 +++++++++---------------------- addons/mail/mail_thread.py | 35 ++++++++++++++++++++++++++++++++--- 2 files changed, 41 insertions(+), 25 deletions(-) diff --git a/addons/mail/mail_message.py b/addons/mail/mail_message.py index 03eb57bfd0f..87e73dbb471 100644 --- a/addons/mail/mail_message.py +++ b/addons/mail/mail_message.py @@ -197,31 +197,18 @@ class mail_message(osv.osv): #------------------------------------------------------ def create(self, cr, uid, vals, context=None): - # OpenSocial: notifications do not come from any user but from system - if vals.get('type') == 'notification': vals['user_id'] = False - need_action_pushed = False + # temporary log directly created messages (to debug OpenSocial) + if not 'mail.thread' in context: + _logger.warning('Creating message without using mail.thread API') + _logger.warning('Message details: %s', str(vals)) msg_id = super(mail_message, self).create(cr, uid, vals, context) - # push the message to suscribed users - subscription_obj = self.pool.get('mail.subscription') - notification_obj = self.pool.get('mail.notification') - # not pure-email: check for subscriptions - if not 'need_action_user_id' in vals: vals['need_action_user_id'] = False - if not 'model' in vals: vals['model'] = False - if not 'res_id' in vals: vals['res_id'] = 0 - sub_ids = subscription_obj.search(cr, uid, ['&', - ('res_model', '=', vals['model']), - ('res_id', '=', vals['res_id'])], context=context) - subs = subscription_obj.browse(cr, uid, sub_ids, context=context) - for sub in subs: - notification_obj.create(cr, uid, {'user_id': sub.user_id.id, 'message_id': msg_id}, context=context) - if vals['need_action_user_id'] == sub.user_id: need_action_pushed = True - # push to need_action_user_id if user does not follow the object - if vals['need_action_user_id'] and not need_action_pushed: - notification_obj.create(cr, uid, {'user_id': vals['need_action_user_id'], 'message_id': msg_id}, context=context) return msg_id def get_pushed_messages(self, cr, uid, ids, filter_search=False, context=None): - """Wall: get messages to display""" + """OpenSocial: wall: get messages to display (=pushed notifications) + :param filter_search: TODO + :return: dict { res_model: { res_id: [pushed message list] }} + """ notification_obj = self.pool.get('mail.notification') notification_ids = notification_obj.search(cr, uid, [('user_id', '=', uid)], context=context) notifications = notification_obj.browse(cr, uid, notification_ids, context=context) @@ -230,7 +217,7 @@ class mail_message(osv.osv): msgs = self.read(cr, uid, msg_ids, context=context) print msgs - # TODO / REMARK: classify based on res_model / res_id to have a 1_level hierarchy ? + # sort return msgs diff --git a/addons/mail/mail_thread.py b/addons/mail/mail_thread.py index b3592e0954e..89273389dd2 100644 --- a/addons/mail/mail_thread.py +++ b/addons/mail/mail_thread.py @@ -72,6 +72,33 @@ class mail_thread(osv.osv): #------------------------------------------------------ # Generic message api #------------------------------------------------------ + + def message_create(self, cr, uid, ids, vals, context=None): + """OpenSocial: wrapper of mail.message create method + - creates the mail.message + - push the message to subscribed users""" + subscription_obj = self.pool.get('mail.subscription') + notification_obj = self.pool.get('mail.notification') + + # notifications do not come from any user, but from system + if vals.get('type') == 'notification': vals['user_id'] = False + if not 'need_action_user_id' in vals: vals['need_action_user_id'] = False + if not 'model' in vals: vals['model'] = False + if not 'res_id' in vals: vals['res_id'] = 0 + need_action_pushed = False + + msg_id = self.pool.get('mail.message').create(cr, uid, vals, context=context) + + # push the message to suscribed users + users = self.message_get_subscribers(cr, uid, ids, context=context) + for user in users: + notification_obj.create(cr, uid, {'user_id': user['id'], 'message_id': msg_id}, context=context) + if vals['need_action_user_id'] == user['id']: need_action_pushed = True + # push to need_action_user_id if user does not follow the object + if vals['need_action_user_id'] and not need_action_pushed: + notification_obj.create(cr, uid, {'user_id': vals['need_action_user_id'], 'message_id': msg_id}, context=context) + return msg_id + def message_capable_models(self, cr, uid, context=None): ret_dict = {} for model_name in self.pool.obj_list(): @@ -197,7 +224,9 @@ class mail_thread(osv.osv): 'reply_to': reply_to, 'original': original, } - mail_message.create(cr, uid, data, context=context) + #mail_message.create(cr, uid, data, context=context) + context['mail.thread'] = True + self.message_create(cr, uid, [thread.id], data, context=context) return True def message_append_dict(self, cr, uid, ids, msg_dict, context=None): @@ -533,14 +562,14 @@ class mail_thread(osv.osv): # Subscription mechanism #------------------------------------------------------ - def message_get_subscribers_ids(self, cr, uid, ids, context=None): + def _message_get_subscribers_ids(self, cr, uid, ids, context=None): subscription_obj = self.pool.get('mail.subscription') sub_ids = subscription_obj.search(cr, uid, ['&', ('res_model', '=', self._name), ('res_id', 'in', ids)], context=context) subs = subscription_obj.read(cr, uid, sub_ids, context=context) return [sub['user_id'][0] for sub in subs] def message_get_subscribers(self, cr, uid, ids, context=None): - user_ids = self.message_get_subscribers_ids(cr, uid, ids, context=context) + user_ids = self._message_get_subscribers_ids(cr, uid, ids, context=context) users = self.pool.get('res.users').read(cr, uid, user_ids, context=context) return users From acb592d84b9710bd563df76c8c822031344bf22e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thibault=20Delavall=C3=A9e?= Date: Wed, 22 Feb 2012 10:58:12 +0100 Subject: [PATCH 0052/1333] [IMP] Automatically subscribe the message writer to the related object. bzr revid: tde@openerp.com-20120222095812-mor2qolqjqzm188q --- addons/mail/mail_thread.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/addons/mail/mail_thread.py b/addons/mail/mail_thread.py index 89273389dd2..634ac3525a0 100644 --- a/addons/mail/mail_thread.py +++ b/addons/mail/mail_thread.py @@ -76,6 +76,7 @@ class mail_thread(osv.osv): def message_create(self, cr, uid, ids, vals, context=None): """OpenSocial: wrapper of mail.message create method - creates the mail.message + - automatically subscribe the message writer if not already done - push the message to subscribed users""" subscription_obj = self.pool.get('mail.subscription') notification_obj = self.pool.get('mail.notification') @@ -89,6 +90,11 @@ class mail_thread(osv.osv): msg_id = self.pool.get('mail.message').create(cr, uid, vals, context=context) + # automatically subscribe the writer of the message if not subscribed + if vals['user_id']: + if not self.message_is_subscriber(cr, uid, ids, context=context): + self.message_subscribe(cr, uid, ids, context=context) + # push the message to suscribed users users = self.message_get_subscribers(cr, uid, ids, context=context) for user in users: @@ -574,11 +580,12 @@ class mail_thread(osv.osv): 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: - print 'cacaprout error !' + pass # TODO return True if sub_ids else False def message_subscribe(self, cr, uid, ids, user_ids = None, context=None): From bfc8e5e86671a67cf03d8d570058800ee2e29259 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thibault=20Delavall=C3=A9e?= Date: Wed, 22 Feb 2012 10:59:50 +0100 Subject: [PATCH 0053/1333] [IMP] hr_holidays: propagated changes in subscription mechanism. Also cleaned messages. bzr revid: tde@openerp.com-20120222095950-wnx9qfta5dh4had5 --- addons/hr_holidays/hr_holidays.py | 30 ++++++++++-------------------- 1 file changed, 10 insertions(+), 20 deletions(-) diff --git a/addons/hr_holidays/hr_holidays.py b/addons/hr_holidays/hr_holidays.py index 82d3d7d51cb..78db0dc7063 100644 --- a/addons/hr_holidays/hr_holidays.py +++ b/addons/hr_holidays/hr_holidays.py @@ -359,30 +359,20 @@ class hr_holidays(osv.osv): # ----------------------------- def create_notificate(self, cr, uid, ids, context=None): - obj = self.browse(cr, uid, ids, context=context)[0] - self.message_append_note(cr, uid, ids, _('System notification'), - _("""The %s request '%s' has been created and is in draft mode. In this mode, it can be modified freely. \ - Click on Confirm to confirm your request and ask for its validation by the managers.""") - % ('leave' if obj.type == 'remove' else 'allocation', obj.name), type='notification', context=context) + 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) return True - def message_get_subscribers(self, cr, uid, ids, context=None): - """OpenChatter: override message_get_subscribers to add : - - employee related user - - employee manager user, if it exists""" - users = super(hr_holidays, self).message_get_subscribers(cr, uid, ids, context=context) - user_ids = [] - for holidays in self.browse(cr, uid, ids, context=context): - user_ids.append(holidays.user_id.id) - if holidays.employee_id.parent_id: - user_ids.append(holidays.employee_id.parent_id.user_id.id) - users += self.pool.get('res.users').read(cr, uid, user_ids, context=context) - return users - def holidays_confirm_notificate(self, cr, uid, ids, context=None): for obj in self.browse(cr, uid, ids): self.message_append_note(cr, uid, [obj.id], _('System notification'), - _("The %s request '%s' has been confirmed. It is now waiting for validation by the manager.") + _("The %s request '%s' has been confirmed and is waiting for validation by the manager.") % ('leave' if obj.type == 'remove' else 'allocation', obj.name,), type='notification', need_action_user_id = obj.employee_id.parent_id.user_id.id if obj.employee_id.parent_id else False) @@ -391,7 +381,7 @@ class hr_holidays(osv.osv): self.message_mark_done(cr, uid, [obj.id], context=context) if obj.holiday_status_id.double_validation: self.message_append_note(cr, uid, [obj.id], _('System notification'), - _("The %s request '%s' has been validated. A second validation of your request is necessary and is now pending.") + _("The %s request '%s' has been validated. A second validation is necessary and is now pending.") % ('leave' if obj.type == 'remove' else 'allocation', obj.name), type='notification', context=context) else: self.message_append_note(cr, uid, [obj.id], _('System notification'), From b0a1a7e24ae70e1a0d71b9f36ff3a9e4f7f139c9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thibault=20Delavall=C3=A9e?= Date: Wed, 22 Feb 2012 11:12:16 +0100 Subject: [PATCH 0054/1333] [FIX] added user_id when posting a tweet from the wall bzr revid: tde@openerp.com-20120222101216-9xpo3c0gj1ickigl --- addons/mail/mail_message.py | 6 ++---- addons/mail/static/src/js/mail.js | 5 +++-- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/addons/mail/mail_message.py b/addons/mail/mail_message.py index 87e73dbb471..f6d7d2c863c 100644 --- a/addons/mail/mail_message.py +++ b/addons/mail/mail_message.py @@ -214,11 +214,9 @@ class mail_message(osv.osv): notifications = notification_obj.browse(cr, uid, notification_ids, context=context) msg_ids = [notification.message_id.id for notification in notifications] - msgs = self.read(cr, uid, msg_ids, context=context) - print msgs + msgs = self.read(cr, uid, msg_ids, context=context) + # sort: TODO - # sort - return msgs #------------------------------------------------------ diff --git a/addons/mail/static/src/js/mail.js b/addons/mail/static/src/js/mail.js index cd2e1f2ac5d..bd3021e9f1f 100644 --- a/addons/mail/static/src/js/mail.js +++ b/addons/mail/static/src/js/mail.js @@ -162,8 +162,9 @@ openerp.mail = function(session) { do_comment: function () { var body_text = this.$element.find('textarea').val(); - return this.ds_msg.call('create', [{'subject': 'Status tweet', 'model': 'res.users', 'body_text': body_text, 'type': 'comment'}]).then( - this.proxy('fetch_comments')); + return this.ds_msg.call('create', [ + {'subject': 'Status tweet', 'model': 'res.users', 'body_text': body_text, 'type': 'comment', 'res_id': this.session.uid, 'user_id': this.session.uid} + ]).then(this.proxy('fetch_comments')); }, }); }; From 9ced116caa379f2b876912a0a638f61b51d5becd Mon Sep 17 00:00:00 2001 From: "Bhumi Thakkar (Open ERP)" Date: Wed, 22 Feb 2012 16:36:16 +0530 Subject: [PATCH 0055/1333] [IMP] Threadview in crm. bzr revid: bth@tinyerp.com-20120222110616-i4sz2b1630ehzqel --- addons/crm/crm_lead_view.xml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/addons/crm/crm_lead_view.xml b/addons/crm/crm_lead_view.xml index 3fc6aff1536..01020ac213c 100644 --- a/addons/crm/crm_lead_view.xml +++ b/addons/crm/crm_lead_view.xml @@ -195,6 +195,7 @@ + @@ -589,6 +590,7 @@ + From bb1b6fe1766ac240e2d6e0154bd0d6dcd4e714e6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thibault=20Delavall=C3=A9e?= Date: Wed, 22 Feb 2012 12:59:24 +0100 Subject: [PATCH 0056/1333] [ADD] res_users: added res_users.py file, making res.users in heriting from mail.thread. This is necessary to use mail.thread API on users (for tweets). bzr revid: tde@openerp.com-20120222115924-i55twzupuuzuc3gl --- addons/mail/__init__.py | 1 + addons/mail/res_users.py | 33 +++++++++++++++++++++++++++++++++ 2 files changed, 34 insertions(+) create mode 100644 addons/mail/res_users.py diff --git a/addons/mail/__init__.py b/addons/mail/__init__.py index 64339fcb9c0..2474c321946 100644 --- a/addons/mail/__init__.py +++ b/addons/mail/__init__.py @@ -23,6 +23,7 @@ import mail_message import mail_thread import mail_group import mail_subscription +import res_users import res_partner import wizard diff --git a/addons/mail/res_users.py b/addons/mail/res_users.py new file mode 100644 index 00000000000..1c650ab1a34 --- /dev/null +++ b/addons/mail/res_users.py @@ -0,0 +1,33 @@ +# -*- coding: utf-8 -*- +############################################################################## +# +# OpenERP, Open Source Management Solution +# Copyright (C) 2009-Today OpenERP SA () +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see +# +############################################################################## + +from osv import osv + +class res_users(osv.osv): + _name = 'res.users' + _inherit = ['res.users', 'mail.thread'] + + def create(self, cr, uid, data, context=None): + user_id = super(res_users, self).create(cr, uid, data, context=context) + # make user follow itself + self.message_subscribe(cr, uid, [user_id], [user_id], context=context) + return user_id + From d9a3b4b2900a477810c850e80b5cfb6fcbb5407f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thibault=20Delavall=C3=A9e?= Date: Wed, 22 Feb 2012 14:35:01 +0100 Subject: [PATCH 0057/1333] [REF] Cleaned mail.message get_pushed_messages method bzr revid: tde@openerp.com-20120222133501-it9li0lh8fwvuk4e --- addons/mail/mail_message.py | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/addons/mail/mail_message.py b/addons/mail/mail_message.py index f6d7d2c863c..9391a931774 100644 --- a/addons/mail/mail_message.py +++ b/addons/mail/mail_message.py @@ -207,16 +207,13 @@ class mail_message(osv.osv): def get_pushed_messages(self, cr, uid, ids, filter_search=False, context=None): """OpenSocial: wall: get messages to display (=pushed notifications) :param filter_search: TODO - :return: dict { res_model: { res_id: [pushed message list] }} + :return: list of mail.messages, unsorted """ notification_obj = self.pool.get('mail.notification') notification_ids = notification_obj.search(cr, uid, [('user_id', '=', uid)], context=context) notifications = notification_obj.browse(cr, uid, notification_ids, context=context) - msg_ids = [notification.message_id.id for notification in notifications] - msgs = self.read(cr, uid, msg_ids, context=context) - # sort: TODO - + msgs = self.read(cr, uid, msg_ids, context=context) return msgs #------------------------------------------------------ From 167f39807cd0478ca042aa550eb80c80e4954450 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thibault=20Delavall=C3=A9e?= Date: Wed, 22 Feb 2012 15:03:18 +0100 Subject: [PATCH 0058/1333] [IMP] mail.thread: get_subscribed_users returns only necessary fields bzr revid: tde@openerp.com-20120222140318-8trixmwfvgus00x4 --- addons/mail/mail_thread.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addons/mail/mail_thread.py b/addons/mail/mail_thread.py index 634ac3525a0..ee3270ca872 100644 --- a/addons/mail/mail_thread.py +++ b/addons/mail/mail_thread.py @@ -576,7 +576,7 @@ class mail_thread(osv.osv): def message_get_subscribers(self, cr, uid, ids, context=None): user_ids = self._message_get_subscribers_ids(cr, uid, ids, context=context) - users = self.pool.get('res.users').read(cr, uid, user_ids, context=context) + 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): From 1bc3f0cec4353e558ef47cf29bcc7b974d4194b3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thibault=20Delavall=C3=A9e?= Date: Wed, 22 Feb 2012 15:04:03 +0100 Subject: [PATCH 0059/1333] [IMP] Wall: pushed message are now displayed in conversation, with a 1-level hierarchy based on source record. Wall and Thread: added support for user image. bzr revid: tde@openerp.com-20120222140403-fbjisr0u96jml5zv --- addons/mail/static/src/css/mail.css | 19 +++++---- addons/mail/static/src/js/mail.js | 61 +++++++++++++++++++++++------ addons/mail/static/src/xml/mail.xml | 45 +++++++++++++++------ 3 files changed, 92 insertions(+), 33 deletions(-) diff --git a/addons/mail/static/src/css/mail.css b/addons/mail/static/src/css/mail.css index be5e8425630..9f4d8440205 100644 --- a/addons/mail/static/src/css/mail.css +++ b/addons/mail/static/src/css/mail.css @@ -50,24 +50,23 @@ .oe_mail_msg { } -/*.oe_mail_msg:after { - content: ""; - display: block; - clear: both; -}*/ - .oe_mail_comment { white-space: normal; margin-bottom: 5px; } +.oe_mail_comment:after { + content: ""; + display: block; + clear: both; +} + .oe_mail_msg_image { float: left; width: 14%; } .oe_mail_msg_image img { - width: 80%; margin-left: 10%; } @@ -105,3 +104,9 @@ .oe_mail_followers_vignette img { width: 80%; } + +/* Wall CSS */ +.oe_mail_thread_content div.oe_mail_comment { + margin-left: 5%; +} + diff --git a/addons/mail/static/src/js/mail.js b/addons/mail/static/src/js/mail.js index bd3021e9f1f..daf15e41d35 100644 --- a/addons/mail/static/src/js/mail.js +++ b/addons/mail/static/src/js/mail.js @@ -70,6 +70,7 @@ openerp.mail = function(session) { this.$element.find('div.oe_mail_msg').empty(); var self = this; _(records).each(function (record) { + record.mini_url = self.thread_get_mini('res.users', 'avatar_mini', record.user_id[0]); var template = 'ThreadMsgView'; var render_res = session.web.qweb.render(template, { 'record': record, @@ -77,14 +78,14 @@ openerp.mail = function(session) { $('
').html(render_res).appendTo(self.$element.find('div.oe_mail_msg')); }); }, - + display_followers: function (records) { this.$element.find('div.oe_mail_followers').empty(); var self = this; _(records).each(function (record) { -//
- //$('
').text(record.user_id[1]).appendTo(self.$element.find('div.oe_mail_followers')); - $('
').text(record.name).appendTo(self.$element.find('div.oe_mail_followers')); + $('
').html( + '' + record.name + '' + ).appendTo(self.$element.find('div.oe_mail_followers')); }); }, @@ -113,6 +114,12 @@ openerp.mail = function(session) { do_toggle_followers: function () { this.$element.find('div.oe_mail_followers').toggle(); }, + + 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; + return url; + }, }); /* Add WallView widget to registry */ @@ -128,6 +135,7 @@ openerp.mail = function(session) { this.filter_search = params['filter_search']; /* DataSets */ this.ds_msg = new session.web.DataSet(this, 'mail.message'); + this.ds_users = new session.web.DataSet(this, 'res.users'); }, start: function() { @@ -149,22 +157,49 @@ openerp.mail = function(session) { display_comments: function (records) { this.$element.find('div.oe_mail_msg').empty(); + sorted_records = this.sort_comments(records); var self = this; - _(records).each(function (record) { - console.log(record); - var template = 'ThreadMsgView'; - var render_res = session.web.qweb.render(template, { - 'record': record, + _(sorted_records).each(function (rec_models, model) { // each model + _(rec_models).each(function (record_id, id) { // each record + var template = 'WallThreadView'; + var render_res = session.web.qweb.render(template, { + 'record_model': model, + 'record_id': id, }); - $('
').html(render_res).appendTo(self.$element.find('div.oe_mail_msg')); + $('
').html(render_res).appendTo(self.$element.find('div.oe_mail_msg')); + _(record_id).each(function (record) { // each record + record.mini_url = self.thread_get_mini('res.users', 'avatar_mini', record.user_id[0]); + var template = 'ThreadMsgView'; + var render_res = session.web.qweb.render(template, { + 'record': record, + }); + $('
').html(render_res).appendTo(self.$element.find('div.oe_mail_thread_content:last')); + }); + }); }); }, + sort_comments: function (records) { + 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])) { + sorted_comments[record.model][record.res_id] = []; } + sorted_comments[record.model][record.res_id].push(record); + }); + return sorted_comments; + }, + do_comment: function () { var body_text = this.$element.find('textarea').val(); - return this.ds_msg.call('create', [ - {'subject': 'Status tweet', 'model': 'res.users', 'body_text': body_text, 'type': 'comment', 'res_id': this.session.uid, 'user_id': this.session.uid} - ]).then(this.proxy('fetch_comments')); + return this.ds_users.call('message_append_note', [[this.session.uid], 'Tweet', body_text, type='comment']).then( + this.proxy('fetch_comments')); + }, + + 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; + return url; }, }); }; diff --git a/addons/mail/static/src/xml/mail.xml b/addons/mail/static/src/xml/mail.xml index ff69ef4c29f..755357b7da3 100644 --- a/addons/mail/static/src/xml/mail.xml +++ b/addons/mail/static/src/xml/mail.xml @@ -25,9 +25,39 @@
- + +
+
+
+
+ +
+
+
+
+
+
+
+
+ +
+
+
+
+
+ + +
+ Discussion about document (id: ) +
+
+
+
+ -
Image
+
+ +

@@ -46,16 +76,5 @@

- -
-
-
- -
-
-
-
-
-
From 6671d25523af5fdb9b9da08bacce7033772a1af4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thibault=20Delavall=C3=A9e?= Date: Wed, 22 Feb 2012 15:48:35 +0100 Subject: [PATCH 0060/1333] [IMP] ThreadView: moved follow/unfollow action on the right; see followers now has its value toggling when choosing to display or hide followers; comment textare now displays as a comment, i.e. it has user avatar on the left and is aligned with comments bzr revid: tde@openerp.com-20120222144835-w8jfscffigaggr6p --- addons/mail/static/src/css/mail.css | 2 +- addons/mail/static/src/js/mail.js | 26 ++++++++++++++++++++++---- addons/mail/static/src/xml/mail.xml | 22 ++++++++++++++-------- 3 files changed, 37 insertions(+), 13 deletions(-) diff --git a/addons/mail/static/src/css/mail.css b/addons/mail/static/src/css/mail.css index 9f4d8440205..ed824071f0f 100644 --- a/addons/mail/static/src/css/mail.css +++ b/addons/mail/static/src/css/mail.css @@ -31,7 +31,7 @@ } .oe_mail_button_follow, .oe_mail_button_unfollow, .oe_mail_button_followers { - width: 100px; + width: 120px; } .oe_mail_button_comment { diff --git a/addons/mail/static/src/js/mail.js b/addons/mail/static/src/js/mail.js index daf15e41d35..fe29fd8d139 100644 --- a/addons/mail/static/src/js/mail.js +++ b/addons/mail/static/src/js/mail.js @@ -14,10 +14,12 @@ openerp.mail = function(session) { form_template: 'ThreadView', init: function() { - this.follow_state = 0; + this.is_sub = 0; + this.see_sub = 1; this._super.apply(this, arguments); /* DataSets */ this.ds = new session.web.DataSet(this, this.view.model); + this.ds_users = new session.web.DataSet(this, 'res.users'); }, start: function() { @@ -46,14 +48,21 @@ openerp.mail = function(session) { if (! this.view.datarecord.id) { return; } /* find wich (un)follow buttons to show */ var call_res = this.ds.call('message_is_subscriber', [[this.view.datarecord.id]]).then(function (records) { - if (records == true) { self.follow_state = 1; self.$element.find('button.oe_mail_button_unfollow').show(); } - else { self.follow_state = 0; self.$element.find('button.oe_mail_button_follow').show(); } + if (records == true) { self.is_sub = 1; self.$element.find('button.oe_mail_button_unfollow').show(); } + else { self.is_sub = 0; self.$element.find('button.oe_mail_button_follow').show(); } }); + console.log(this); /* fetch comments and subscribers */ + this.fetch_current_user(); this.fetch_subscribers(); return this.fetch_comments(); }, + fetch_current_user: function () { + return this.ds_users.read_ids([this.session.uid], ['id', 'name', 'avatar_mini']).then( + this.proxy('display_current_user')); + }, + fetch_comments: function () { var load_res = this.ds.call('message_load', [[this.view.datarecord.id]]).then( this.proxy('display_comments')); @@ -66,6 +75,12 @@ openerp.mail = function(session) { return follow_res; }, + display_current_user: function (records) { + $('
').html( + '' + records[0].name + '' + ).appendTo(this.$element.find('div.oe_mail_msg_image')); + }, + display_comments: function (records) { this.$element.find('div.oe_mail_msg').empty(); var self = this; @@ -106,12 +121,15 @@ openerp.mail = function(session) { }, do_toggle_follow: function () { - this.follow_state = 1 - this.follow_state; + this.is_sub = 1 - this.is_sub; this.$element.find('button.oe_mail_button_unfollow').toggle(); this.$element.find('button.oe_mail_button_follow').toggle(); }, do_toggle_followers: function () { + this.see_sub = 1 - this.see_sub; + if (this.see_sub == 1) { this.$element.find('button.oe_mail_button_followers').html('Hide followers'); } + else { this.$element.find('button.oe_mail_button_followers').html('Display followers'); } this.$element.find('div.oe_mail_followers').toggle(); }, diff --git a/addons/mail/static/src/xml/mail.xml b/addons/mail/static/src/xml/mail.xml index 755357b7da3..79e421cc559 100644 --- a/addons/mail/static/src/xml/mail.xml +++ b/addons/mail/static/src/xml/mail.xml @@ -4,13 +4,15 @@
OpenSocial
-
- - -
-
-
- +
+
+
+
+
+
+ +
+
@@ -18,8 +20,12 @@
+
+ + +
- +
From bf50057d22d0771ac59f4d57f4c8a63ba4851ab5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thibault=20Delavall=C3=A9e?= Date: Wed, 22 Feb 2012 15:58:00 +0100 Subject: [PATCH 0061/1333] [IMP] ThreadView: image left of post comment area is not refreshed in each set_value, only initialized bzr revid: tde@openerp.com-20120222145800-rfqkznqxcf3a22ok --- addons/mail/static/src/js/mail.js | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/addons/mail/static/src/js/mail.js b/addons/mail/static/src/js/mail.js index fe29fd8d139..2ef72d0b816 100644 --- a/addons/mail/static/src/js/mail.js +++ b/addons/mail/static/src/js/mail.js @@ -16,6 +16,7 @@ openerp.mail = function(session) { init: function() { this.is_sub = 0; this.see_sub = 1; + this.user_init = 0; this._super.apply(this, arguments); /* DataSets */ this.ds = new session.web.DataSet(this, this.view.model); @@ -53,7 +54,10 @@ openerp.mail = function(session) { }); console.log(this); /* fetch comments and subscribers */ - this.fetch_current_user(); + if (this.user_init == 0) { + this.fetch_current_user(); + this.user_init = 1; + } this.fetch_subscribers(); return this.fetch_comments(); }, From 44e6b0f079edccf0ad7f21d84463a83a6df57589 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thibault=20Delavall=C3=A9e?= Date: Wed, 22 Feb 2012 16:26:43 +0100 Subject: [PATCH 0062/1333] [IMP] Improved display of avatars bzr revid: tde@openerp.com-20120222152643-cdiyr0klegnt9sxd --- addons/mail/static/src/css/mail.css | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/addons/mail/static/src/css/mail.css b/addons/mail/static/src/css/mail.css index ed824071f0f..3f9fe3149d3 100644 --- a/addons/mail/static/src/css/mail.css +++ b/addons/mail/static/src/css/mail.css @@ -63,11 +63,25 @@ .oe_mail_msg_image { float: left; - width: 14%; + width: 100px; + height: 100px; + text-align: center; + overflow: hidden; + -moz-border-radius: 3px; + -webkit-border-radius: 3px; + -o-border-radius: 3px; + -ms-border-radius: 3px; + border-radius: 3px; + -moz-box-shadow: 0 1px 4px rgba(0, 0, 0, 0.4); + -webkit-box-shadow: 0 1px 4px rgba(0, 0, 0, 0.4); + -o-box-shadow: 0 1px 4px rgba(0, 0, 0, 0.4); + -box-shadow: 0 1px 4px rgba(0, 0, 0, 0.4); } .oe_mail_msg_image img { - margin-left: 10%; + width: 100px; + height: auto; + clip: rect(10px, 100px, 110px, 0px); } .oe_mail_msg_content { From 19897724a07cb5aebcd2ebe2d84ad605e3681309 Mon Sep 17 00:00:00 2001 From: "Turkesh Patel (Open ERP)" Date: Thu, 23 Feb 2012 15:14:21 +0530 Subject: [PATCH 0063/1333] [ADD] add new cofiguration wizard view for sale bzr revid: tpa@tinyerp.com-20120223094421-1mdenu3k0752p79j --- openerp/addons/base/__openerp__.py | 1 + openerp/addons/base/res/__init__.py | 1 + openerp/addons/base/res/res_config_sale.py | 164 ++++++++++++++++++ .../addons/base/res/res_config_sale_view.xml | 79 +++++++++ 4 files changed, 245 insertions(+) create mode 100644 openerp/addons/base/res/res_config_sale.py create mode 100644 openerp/addons/base/res/res_config_sale_view.xml diff --git a/openerp/addons/base/__openerp__.py b/openerp/addons/base/__openerp__.py index 4e9687e4ea4..a6e4f4da897 100644 --- a/openerp/addons/base/__openerp__.py +++ b/openerp/addons/base/__openerp__.py @@ -78,6 +78,7 @@ 'res/res_widget_view.xml', 'res/res_widget_data.xml', 'publisher_warranty/publisher_warranty_data.xml', + 'res/res_config_sale_view.xml', ], 'demo_xml': [ 'base_demo.xml', diff --git a/openerp/addons/base/res/__init__.py b/openerp/addons/base/res/__init__.py index 2b4da739f54..7cd63bc485d 100644 --- a/openerp/addons/base/res/__init__.py +++ b/openerp/addons/base/res/__init__.py @@ -35,6 +35,7 @@ import res_lang import res_log import res_widget import ir_property +import res_config_sale import wizard import report diff --git a/openerp/addons/base/res/res_config_sale.py b/openerp/addons/base/res/res_config_sale.py new file mode 100644 index 00000000000..0700635bcc2 --- /dev/null +++ b/openerp/addons/base/res/res_config_sale.py @@ -0,0 +1,164 @@ +# -*- coding: utf-8 -*- +############################################################################## +# +# OpenERP, Open Source Management Solution +# Copyright (C) 2004-2010 Tiny SPRL (). +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# +############################################################################## + +from datetime import datetime, timedelta +from dateutil.relativedelta import relativedelta +import time +import pooler +from osv import fields, osv +from tools.translate import _ +from tools import DEFAULT_SERVER_DATE_FORMAT, DEFAULT_SERVER_DATETIME_FORMAT, float_compare +import decimal_precision as dp +import netsvc + +class sale_config_picking_policy(osv.osv_memory): + _name = 'sale.config.picking_policy' + _inherit = 'res.config' + + _columns = { + 'name': fields.char('Name', size=64), + 'sale_orders': fields.boolean('Based on Sales Orders',), + 'deli_orders': fields.boolean('Based on Delivery Orders'), + 'task_work': fields.boolean('Based on Tasks\' Work'), + 'timesheet': fields.boolean('Based on Timesheet'), + 'order_policy': fields.selection([ + ('manual', 'Invoice Based on Sales Orders'), + ('picking', 'Invoice Based on Deliveries'), + ], 'Main Method Based On', required=True, help="You can generate invoices based on sales orders or based on shippings."), + 'charge_delivery': fields.boolean('Do you charge the delivery?'), + #'time_unit': fields.many2one('product.uom','Main Working Time Unit'), + 'picking_policy' : fields.boolean("Deliver all products at once?"), + 'group_sale_pricelist_per_customer':fields.boolean("Activate pricelist to manage prices per customer "), + 'group_sale_uom_per_product':fields.boolean("Allow different unit of measure per product"), + 'group_sale_delivery_address':fields.boolean(" Allow delivery address different from invoice address"), + 'group_sale_disc_per_sale_order_line':fields.boolean("Allow to apply discounts per sale order lines "), + 'group_sale_notes_subtotal':fields.boolean("Allow notes and subtotals"), + 'group_sale_alerts_per_customer_products':fields.boolean("Allow to define alerts by products or customers"), + 'tax_value' : fields.float('Value'), + 'tax_policy': fields.selection([ + ('no_tax', 'No Tax'), + ('global_on_order', 'Global On Order'), + ('on_order_line', 'On Order Lines'), + ], 'Taxes'), + 'sale_margin' : fields.boolean("Display Margin For Users"), + 'sale_journal' : fields.boolean("Invoice_journal?"), + + } + _defaults = { + 'order_policy': 'manual', + 'tax_policy': 'no_tax', + #'time_unit': lambda self, cr, uid, c: self.pool.get('product.uom').search(cr, uid, [('name', '=', _('Hour'))], context=c) and self.pool.get('product.uom').search(cr, uid, [('name', '=', _('Hour'))], context=c)[0] or False, + } + + def onchange_order(self, cr, uid, ids, sale, deli, context=None): + res = {} + if sale: + res.update({'order_policy': 'manual'}) + elif deli: + res.update({'order_policy': 'picking'}) + return {'value':res} + + def execute(self, cr, uid, ids, context=None): + ir_values_obj = self.pool.get('ir.values') + data_obj = self.pool.get('ir.model.data') + menu_obj = self.pool.get('ir.ui.menu') + module_obj = self.pool.get('ir.module.module') + module_upgrade_obj = self.pool.get('base.module.upgrade') + module_name = [] + + group_id = data_obj.get_object(cr, uid, 'base', 'group_sale_salesman').id + + wizard = self.browse(cr, uid, ids)[0] + + if wizard.sale_orders: + menu_id = data_obj.get_object(cr, uid, 'sale', 'menu_invoicing_sales_order_lines').id + menu_obj.write(cr, uid, menu_id, {'groups_id':[(4,group_id)]}) + + if wizard.deli_orders: + menu_id = data_obj.get_object(cr, uid, 'sale', 'menu_action_picking_list_to_invoice').id + menu_obj.write(cr, uid, menu_id, {'groups_id':[(4,group_id)]}) + + if wizard.task_work: + module_name.append('project_timesheet') + module_name.append('project_mrp') + module_name.append('account_analytic_analysis') + + if wizard.timesheet: + module_name.append('account_analytic_analysis') + + if wizard.charge_delivery: + module_name.append('delivery') + + if len(module_name): + module_ids = [] + need_install = False + module_ids = [] + for module in module_name: + data_id = module_obj.name_search(cr, uid , module, [], '=') + module_ids.append(data_id[0][0]) + + for module in module_obj.browse(cr, uid, module_ids): + if module.state == 'uninstalled': + module_obj.state_update(cr, uid, [module.id], 'to install', ['uninstalled'], context) + need_install = True + cr.commit() + if need_install: + pooler.restart_pool(cr.dbname, update_module=True)[1] + +# if wizard.time_unit: +# prod_id = data_obj.get_object(cr, uid, 'product', 'product_consultant').id +# product_obj = self.pool.get('product.product') +# product_obj.write(cr, uid, prod_id, {'uom_id':wizard.time_unit.id, 'uom_po_id': wizard.time_unit.id}) + + ir_values_obj.set(cr, uid, 'default', False, 'order_policy', ['sale.order'], wizard.order_policy) + if wizard.task_work and wizard.time_unit: + company_id = self.pool.get('res.users').browse(cr, uid, uid).company_id.id + self.pool.get('res.company').write(cr, uid, [company_id], { + 'project_time_mode_id': wizard.time_unit.id + }, context=context) + + def apply_cb(self, cr, uid, ids, context=None): + ir_values_obj = self.pool.get('ir.values') + wizard = self.browse(cr, uid, ids, context=context)[0] + ir_values_obj.set(cr, uid, 'default', False, 'picking_policy', ['sale.order'], wizard.picking_policy) + return {'type' : 'ir.actions.act_window_close'} + +sale_config_picking_policy() + + + +#class define_delivery_steps(osv.osv_memory): +# _name = 'delivery.define.delivery.steps.wizard' +# +# _columns = { +# 'picking_policy' : fields.selection([('direct', 'Deliver each product when available'), ('one', 'Deliver all products at once')], 'Picking Policy'), +# } +# _defaults = { +# 'picking_policy': lambda s,c,u,ctx: s.pool.get('sale.order').default_get(c,u,['picking_policy'],context=ctx)['picking_policy'] +# } +# +# def apply_cb(self, cr, uid, ids, context=None): +# ir_values_obj = self.pool.get('ir.values') +# wizard = self.browse(cr, uid, ids, context=context)[0] +# ir_values_obj.set(cr, uid, 'default', False, 'picking_policy', ['sale.order'], wizard.picking_policy) +# return {'type' : 'ir.actions.act_window_close'} +# +#define_delivery_steps() \ No newline at end of file diff --git a/openerp/addons/base/res/res_config_sale_view.xml b/openerp/addons/base/res/res_config_sale_view.xml new file mode 100644 index 00000000000..492032c044d --- /dev/null +++ b/openerp/addons/base/res/res_config_sale_view.xml @@ -0,0 +1,79 @@ + + + + + Setup Your Invoicing Method + sale.config.picking_policy + form + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +