From 4661fb71d202390f0bf55c1f92073d3fce24878f Mon Sep 17 00:00:00 2001 From: "Turkesh Patel (Open ERP)" Date: Fri, 18 May 2012 12:03:48 +0530 Subject: [PATCH 001/436] [FIX] account: set ondelete='cascade' on payment_id for allow to delete payment term. bzr revid: tpa@tinyerp.com-20120518063348-1lm9oyy0kw5pkwdy --- addons/account/account.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addons/account/account.py b/addons/account/account.py index 26af7a5f9bd..8d0fb8eb818 100644 --- a/addons/account/account.py +++ b/addons/account/account.py @@ -106,7 +106,7 @@ class account_payment_term_line(osv.osv): 'days': fields.integer('Number of Days', required=True, help="Number of days to add before computation of the day of month." \ "If Date=15/01, Number of Days=22, Day of Month=-1, then the due date is 28/02."), 'days2': fields.integer('Day of the Month', required=True, help="Day of the month, set -1 for the last day of the current month. If it's positive, it gives the day of the next month. Set 0 for net days (otherwise it's based on the beginning of the month)."), - 'payment_id': fields.many2one('account.payment.term', 'Payment Term', required=True, select=True), + 'payment_id': fields.many2one('account.payment.term', 'Payment Term', required=True, select=True, ondelete='cascade'), } _defaults = { 'value': 'balance', From da13d90912fb12bd8f82a12300325f6f57dfee17 Mon Sep 17 00:00:00 2001 From: "Turkesh Patel (Open ERP)" Date: Mon, 11 Jun 2012 14:38:49 +0530 Subject: [PATCH 002/436] [FIX] stock:'product level forecast' report does not support accented char. bzr revid: tpa@tinyerp.com-20120611090849-cmir9qms3blrjjw4 --- addons/stock/report/stock_graph.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/addons/stock/report/stock_graph.py b/addons/stock/report/stock_graph.py index 5ee58bb9d2c..b5d08f93d3f 100644 --- a/addons/stock/report/stock_graph.py +++ b/addons/stock/report/stock_graph.py @@ -22,6 +22,7 @@ from pychart import * import pychart.legend import time from report.misc import choice_colors +import base64 # # Draw a graph for stocks @@ -41,7 +42,7 @@ class stock_graph(object): product_name=product_name.replace('/', '//') if product_id not in self._datas: self._datas[product_id] = {} - self._names[product_id] = product_name + self._names[product_id] = product_name.decode('utf-8') for (dt,stock) in datas: if not dt in self._datas[product_id]: self._datas[product_id][dt]=0 From 61a3238d978ab40e661974dc2b75fbda700f7c70 Mon Sep 17 00:00:00 2001 From: "Mayur Maheshwari (OpenERP)" Date: Mon, 11 Jun 2012 16:20:38 +0530 Subject: [PATCH 003/436] [FIX]stock: fix the issue of counting of Deliveries in kanban view and improve a action string lp bug: https://launchpad.net/bugs/1011541 fixed bzr revid: mma@tinyerp.com-20120611105038-wrospxpjr2wbna6h --- addons/stock/product.py | 2 +- addons/stock/product_view.xml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/addons/stock/product.py b/addons/stock/product.py index 62fdbb24ca2..9f9faa1cc24 100644 --- a/addons/stock/product.py +++ b/addons/stock/product.py @@ -44,7 +44,7 @@ class product_product(osv.osv): ], ['product_id'], ['product_id']) for move in moves: product_id = move['product_id'][0] - res[product_id]['reception_count'] = move['product_id_count'] + res[product_id]['delivery_count'] = move['product_id_count'] return res def get_product_accounts(self, cr, uid, product_id, context=None): diff --git a/addons/stock/product_view.xml b/addons/stock/product_view.xml index 49e0e9c1880..e1840ac7497 100644 --- a/addons/stock/product_view.xml +++ b/addons/stock/product_view.xml @@ -140,7 +140,7 @@ - Receive Products + Deliver Products stock.move ir.actions.act_window form From 344ddfbdd5fcf2ff6f22a76f5aa405e8ebeee185 Mon Sep 17 00:00:00 2001 From: "Mayur Maheshwari (OpenERP)" Date: Tue, 19 Jun 2012 11:54:26 +0530 Subject: [PATCH 004/436] [FIX]base:fix the issue of KeyError when changes in line of Journal Entry lp bug: https://launchpad.net/bugs/955910 fixed bzr revid: mma@tinyerp.com-20120619062426-x5ykv2thycvpvs4l --- addons/account/account.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addons/account/account.py b/addons/account/account.py index f0c16d8586c..a441a262003 100644 --- a/addons/account/account.py +++ b/addons/account/account.py @@ -1363,7 +1363,7 @@ class account_move(osv.osv): balance = 0.0 for line in line_ids: if line[2]: - balance += (line[2]['debit'] or 0.00)- (line[2]['credit'] or 0.00) + balance += (line[2].get('debit',0.00)- (line[2].get('credit',0.00))) return {'value': {'balance': balance}} def write(self, cr, uid, ids, vals, context=None): From 41c90e8755b5b12f8e6f4e3870eb3a7f8e2b7b5b Mon Sep 17 00:00:00 2001 From: "Ajay Chauhan (OpenERP)" Date: Thu, 5 Jul 2012 18:34:55 +0530 Subject: [PATCH 005/436] [FIX] account: add else condition in onchange_chart_id lp bug: https://launchpad.net/bugs/1021152 fixed bzr revid: cha@tinyerp.com-20120705130455-iy00wseff3lroj25 --- addons/account/wizard/account_report_common.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/addons/account/wizard/account_report_common.py b/addons/account/wizard/account_report_common.py index fa3c195cfb7..f84c4e909a2 100644 --- a/addons/account/wizard/account_report_common.py +++ b/addons/account/wizard/account_report_common.py @@ -32,6 +32,8 @@ class account_common_report(osv.osv_memory): def onchange_chart_id(self, cr, uid, ids, chart_account_id=False, context=None): if chart_account_id: company_id = self.pool.get('account.account').browse(cr, uid, chart_account_id, context=context).company_id.id + else: + return {'value': {'company_id': False}} return {'value': {'company_id': company_id}} _columns = { From 9c03b140ce4444479d64130a6b75f3b44db79701 Mon Sep 17 00:00:00 2001 From: "Kuldeep Joshi (OpenERP)" Date: Tue, 10 Jul 2012 14:51:35 +0530 Subject: [PATCH 006/436] [FIX]event: add oe_google_map class bzr revid: kjo@tinyerp.com-20120710092135-wskq7ga5da8jqdrc --- addons/event/event_view.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addons/event/event_view.xml b/addons/event/event_view.xml index 142ba2bb2a4..a3a7bff8107 100644 --- a/addons/event/event_view.xml +++ b/addons/event/event_view.xml @@ -138,7 +138,7 @@ - +
From 3bd4660e92f57d1bd1a8b985f2590e13db31f710 Mon Sep 17 00:00:00 2001 From: "Divyesh Makwana (Open ERP)" Date: Wed, 8 Aug 2012 12:36:12 +0530 Subject: [PATCH 007/436] [IMP] account : Convert invoice line to editable list and call the onchange method to set all the information. bzr revid: mdi@tinyerp.com-20120808070612-20q3wyqkevgtjnx6 --- addons/account/account_invoice.py | 6 ++++++ addons/account/account_invoice_view.xml | 2 +- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/addons/account/account_invoice.py b/addons/account/account_invoice.py index b1c5db23df6..7000ca189dc 100644 --- a/addons/account/account_invoice.py +++ b/addons/account/account_invoice.py @@ -1361,10 +1361,16 @@ class account_invoice_line(osv.osv): 'company_id': fields.related('invoice_id','company_id',type='many2one',relation='res.company',string='Company', store=True, readonly=True), 'partner_id': fields.related('invoice_id','partner_id',type='many2one',relation='res.partner',string='Partner',store=True) } + + def default_account_id(self, cr, uid, ids, context=None): + prop = self.pool.get('ir.property').get(cr, uid, 'property_account_income_categ', 'product.category', context=context) + return prop and prop.id or False + _defaults = { 'quantity': 1, 'discount': 0.0, 'price_unit': _price_unit_default, + 'account_id': default_account_id, } def fields_view_get(self, cr, uid, view_id=None, view_type='form', context=None, toolbar=False, submenu=False): diff --git a/addons/account/account_invoice_view.xml b/addons/account/account_invoice_view.xml index 501a50b96c2..8fbd85f2599 100644 --- a/addons/account/account_invoice_view.xml +++ b/addons/account/account_invoice_view.xml @@ -347,7 +347,7 @@ - + Date: Thu, 9 Aug 2012 10:57:30 +0530 Subject: [PATCH 008/436] [FIX]purchase :set a po line editable bzr revid: mma@tinyerp.com-20120809052730-4c0ykb7k54v6ju44 --- addons/purchase/purchase_view.xml | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/addons/purchase/purchase_view.xml b/addons/purchase/purchase_view.xml index 47216a11cac..9c8b508553d 100644 --- a/addons/purchase/purchase_view.xml +++ b/addons/purchase/purchase_view.xml @@ -218,11 +218,12 @@ - + + - - + + From 1a36733f5be49a3de6d45548cf5b4f41caad7ceb Mon Sep 17 00:00:00 2001 From: "Ajay Chauhan (OpenERP)" Date: Thu, 9 Aug 2012 14:14:30 +0530 Subject: [PATCH 009/436] [IMP]hr_expense : made expense line editable bzr revid: cha@tinyerp.com-20120809084430-fvhy9nfph9xiwwna --- addons/hr_expense/hr_expense.py | 29 ++++++++++++++++++++++----- addons/hr_expense/hr_expense_view.xml | 16 +++++++++++++-- 2 files changed, 38 insertions(+), 7 deletions(-) diff --git a/addons/hr_expense/hr_expense.py b/addons/hr_expense/hr_expense.py index b1192701dfb..74eac5d99c2 100644 --- a/addons/hr_expense/hr_expense.py +++ b/addons/hr_expense/hr_expense.py @@ -253,15 +253,23 @@ class hr_expense_line(osv.osv): res = dict(cr.fetchall()) return res + def _get_uom_id(self, cr, uid, context=None): + try: + proxy = self.pool.get('ir.model.data') + result = proxy.get_object_reference(cr, uid, 'product', 'product_uom_unit') + return result[1] + except Exception, ex: + return False + _columns = { 'name': fields.char('Expense Note', size=128, required=True), 'date_value': fields.date('Date', required=True), 'expense_id': fields.many2one('hr.expense.expense', 'Expense', ondelete='cascade', select=True), 'total_amount': fields.function(_amount, string='Total', digits_compute=dp.get_precision('Account')), 'unit_amount': fields.float('Unit Price', digits_compute=dp.get_precision('Account')), - 'unit_quantity': fields.float('Quantities' ), + 'unit_quantity': fields.float('Quantities'), 'product_id': fields.many2one('product.product', 'Product', domain=[('hr_expense_ok','=',True)]), - 'uom_id': fields.many2one('product.uom', 'Unit of Measure'), + 'uom_id': fields.many2one('product.uom', 'Unit of Measure', required=True), 'description': fields.text('Description'), 'analytic_account': fields.many2one('account.analytic.account','Analytic account'), 'ref': fields.char('Reference', size=32), @@ -270,20 +278,31 @@ class hr_expense_line(osv.osv): _defaults = { 'unit_quantity': 1, 'date_value': lambda *a: time.strftime('%Y-%m-%d'), + 'uom_id': _get_uom_id, } _order = "sequence, date_value desc" - def onchange_product_id(self, cr, uid, ids, product_id, uom_id, employee_id, context=None): + def onchange_product_id(self, cr, uid, ids, product_id, context=None): res = {} if product_id: product = self.pool.get('product.product').browse(cr, uid, product_id, context=context) res['name'] = product.name amount_unit = product.price_get('standard_price')[product.id] res['unit_amount'] = amount_unit - if not uom_id: - res['uom_id'] = product.uom_id.id + res['uom_id'] = product.uom_id.id return {'value': res} + def onchange_uom(self, cr, uid, ids, product_id, uom_id, context=None): + res = {'value':{}} + if product_id: + product = self.pool.get('product.product').browse(cr, uid, product_id, context=context) + uom = self.pool.get('product.uom').browse(cr, uid, uom_id, context=context) + if uom.category_id.id != product.uom_id.category_id.id: + res['warning'] = {'title': _('Warning'), 'message': _('Selected Unit of Measure does not belong to the same category as the product Unit of Measure')} + uom_id = product.uom_id.id + res['value'].update({'uom_id': uom_id}) + return res + hr_expense_line() # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/addons/hr_expense/hr_expense_view.xml b/addons/hr_expense/hr_expense_view.xml index 67c08ee8a65..57b7a4cc37f 100644 --- a/addons/hr_expense/hr_expense_view.xml +++ b/addons/hr_expense/hr_expense_view.xml @@ -92,7 +92,7 @@
- + @@ -102,12 +102,24 @@
+ + + + + + + + + + + +
From 4cc8dce4ec176928cf7d092f587f9c1b6ec9db75 Mon Sep 17 00:00:00 2001 From: "Ajay Chauhan (OpenERP)" Date: Thu, 9 Aug 2012 16:21:03 +0530 Subject: [PATCH 010/436] [IMP] hr_expense : added condition to function bzr revid: cha@tinyerp.com-20120809105103-tm0m7g854zglxfrg --- addons/hr_expense/hr_expense.py | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/addons/hr_expense/hr_expense.py b/addons/hr_expense/hr_expense.py index 74eac5d99c2..d822d466901 100644 --- a/addons/hr_expense/hr_expense.py +++ b/addons/hr_expense/hr_expense.py @@ -294,13 +294,14 @@ class hr_expense_line(osv.osv): def onchange_uom(self, cr, uid, ids, product_id, uom_id, context=None): res = {'value':{}} - if product_id: - product = self.pool.get('product.product').browse(cr, uid, product_id, context=context) - uom = self.pool.get('product.uom').browse(cr, uid, uom_id, context=context) - if uom.category_id.id != product.uom_id.category_id.id: - res['warning'] = {'title': _('Warning'), 'message': _('Selected Unit of Measure does not belong to the same category as the product Unit of Measure')} - uom_id = product.uom_id.id - res['value'].update({'uom_id': uom_id}) + if uom_id: + if product_id: + product = self.pool.get('product.product').browse(cr, uid, product_id, context=context) + uom = self.pool.get('product.uom').browse(cr, uid, uom_id, context=context) + if uom.category_id.id != product.uom_id.category_id.id: + res['warning'] = {'title': _('Warning'), 'message': _('Selected Unit of Measure does not belong to the same category as the product Unit of Measure')} + uom_id = product.uom_id.id + res['value'].update({'uom_id': uom_id}) return res hr_expense_line() From 5e0f7094105b8722f3d5b0007f46188be095d888 Mon Sep 17 00:00:00 2001 From: Vo Minh Thu Date: Mon, 13 Aug 2012 11:10:47 +0200 Subject: [PATCH 011/436] [IMP] views: allow create/delete/edit attribte on form/tree/kanban views, automatically set the attributes from access rights. bzr revid: vmt@openerp.com-20120813091047-vqjk3an52dfzbhqe --- openerp/addons/base/rng/view.rng | 8 ++++++++ openerp/osv/orm.py | 4 ++++ 2 files changed, 12 insertions(+) diff --git a/openerp/addons/base/rng/view.rng b/openerp/addons/base/rng/view.rng index c583040495b..c56892d7a07 100644 --- a/openerp/addons/base/rng/view.rng +++ b/openerp/addons/base/rng/view.rng @@ -172,6 +172,9 @@ + + + @@ -220,6 +223,8 @@ + + @@ -235,6 +240,9 @@ + + + diff --git a/openerp/osv/orm.py b/openerp/osv/orm.py index d8861a20c26..b359c135d00 100644 --- a/openerp/osv/orm.py +++ b/openerp/osv/orm.py @@ -1830,6 +1830,10 @@ class BaseModel(object): fields = self.fields_get(cr, user, None, context) fields_def = self.__view_look_dom(cr, user, node, view_id, False, fields, context=context) node = self._disable_workflow_buttons(cr, user, node) + if node.tag in ('kanban', 'tree', 'form'): + for a, b in (('create', 'check_create'), ('delete', 'check_unlink'), ('edit', 'check_write')): + if not node.get(a) and not getattr(self, b)(cr, user, raise_exception=False): + node.set(a, 'false') arch = etree.tostring(node, encoding="utf-8").replace('\t', '') for k in fields.keys(): if k not in fields_def: From 076fdb09940c57c385c3b3f7639bb893464afb69 Mon Sep 17 00:00:00 2001 From: Jigar Amin - OpenERP Date: Tue, 14 Aug 2012 18:38:21 +0530 Subject: [PATCH 012/436] [IMP] views should show the button create/edit/delete/duplicate on the view tag create/edit/delete attributes bzr revid: jam@tinyerp.com-20120814130821-ret2z62dsthle39p --- addons/web/static/src/js/view_form.js | 15 +++++++++++++-- addons/web/static/src/js/view_list.js | 11 +++++++++-- addons/web/static/src/xml/base.xml | 18 ++++++++++++------ 3 files changed, 34 insertions(+), 10 deletions(-) diff --git a/addons/web/static/src/js/view_form.js b/addons/web/static/src/js/view_form.js index baf8c151b83..2e800191749 100644 --- a/addons/web/static/src/js/view_form.js +++ b/addons/web/static/src/js/view_form.js @@ -148,9 +148,17 @@ instance.web.FormView = instance.web.View.extend(instance.web.form.FieldManagerM if(this.fields_view.toolbar) { this.sidebar.add_toolbar(this.fields_view.toolbar); } + if (self._is_action_enabled('delete')){ + this.sidebar.add_items('other', [ + { label: _t('Delete'), callback: self.on_button_delete }, + ]); + } + if (self._is_action_enabled('create')){ + this.sidebar.add_items('other', [ + { label: _t('Duplicate'), callback: self.on_button_duplicate } + ]); + } this.sidebar.add_items('other', [ - { label: _t('Delete'), callback: self.on_button_delete }, - { label: _t('Duplicate'), callback: self.on_button_duplicate }, { label: _t('Set Default'), callback: function (item) { self.open_defaults_dialog(); } } ]); } @@ -1042,6 +1050,9 @@ instance.web.FormView = instance.web.View.extend(instance.web.form.FieldManagerM open_translate_dialog: function(field) { return this._super(field); }, + _is_action_enabled: function(action) { + return (_.has(this.fields_view.arch.attrs, action))?JSON.parse(this.fields_view.arch.attrs[action]):true; + } }); /** diff --git a/addons/web/static/src/js/view_list.js b/addons/web/static/src/js/view_list.js index 986b0e49faf..3d6ec27d065 100644 --- a/addons/web/static/src/js/view_list.js +++ b/addons/web/static/src/js/view_list.js @@ -353,9 +353,13 @@ instance.web.ListView = instance.web.View.extend( /** @lends instance.web.ListVi this.sidebar.appendTo(this.options.$sidebar); this.sidebar.add_items('other', [ { label: _t("Import"), callback: this.on_sidebar_import }, - { label: _t("Export"), callback: this.on_sidebar_export }, - { label: _t('Delete'), callback: this.do_delete_selected } + { label: _t("Export"), callback: this.on_sidebar_export } ]); + if (self._is_action_enabled('delete')){ + this.sidebar.add_items('other', [ + { label: _t('Delete'), callback: this.do_delete_selected } + ]); + } this.sidebar.add_toolbar(this.fields_view.toolbar); this.sidebar.$element.hide(); } @@ -831,6 +835,9 @@ instance.web.ListView = instance.web.View.extend( /** @lends instance.web.ListVi this.$element.prepend( $('
').html(this.options.action.help) ); + }, + _is_action_enabled: function(action) { + return (_.has(this.fields_view.arch.attrs, action))?JSON.parse(this.fields_view.arch.attrs[action]):true; } }); instance.web.ListView.List = instance.web.Class.extend( /** @lends instance.web.ListView.List# */{ diff --git a/addons/web/static/src/xml/base.xml b/addons/web/static/src/xml/base.xml index 0eddd914f01..21d006e23ab 100644 --- a/addons/web/static/src/xml/base.xml +++ b/addons/web/static/src/xml/base.xml @@ -672,9 +672,11 @@
- + + + or Import @@ -747,10 +749,14 @@
-
- -
+ +
+ +
+
+ +
or Discard From 9c04845999c0c7176077781650332beb673e7a70 Mon Sep 17 00:00:00 2001 From: Jigar Amin - OpenERP Date: Tue, 14 Aug 2012 19:02:42 +0530 Subject: [PATCH 013/436] [IMP] Addded the common view attrib check method for the access ui support bzr revid: jam@tinyerp.com-20120814133242-y7ofa6oy59gj0uj4 --- addons/web/static/src/js/view_form.js | 3 --- addons/web/static/src/js/view_list.js | 3 --- addons/web/static/src/js/views.js | 8 ++++++++ 3 files changed, 8 insertions(+), 6 deletions(-) diff --git a/addons/web/static/src/js/view_form.js b/addons/web/static/src/js/view_form.js index 2e800191749..239460bbcb4 100644 --- a/addons/web/static/src/js/view_form.js +++ b/addons/web/static/src/js/view_form.js @@ -1050,9 +1050,6 @@ instance.web.FormView = instance.web.View.extend(instance.web.form.FieldManagerM open_translate_dialog: function(field) { return this._super(field); }, - _is_action_enabled: function(action) { - return (_.has(this.fields_view.arch.attrs, action))?JSON.parse(this.fields_view.arch.attrs[action]):true; - } }); /** diff --git a/addons/web/static/src/js/view_list.js b/addons/web/static/src/js/view_list.js index 3d6ec27d065..f420001669b 100644 --- a/addons/web/static/src/js/view_list.js +++ b/addons/web/static/src/js/view_list.js @@ -835,9 +835,6 @@ instance.web.ListView = instance.web.View.extend( /** @lends instance.web.ListVi this.$element.prepend( $('
').html(this.options.action.help) ); - }, - _is_action_enabled: function(action) { - return (_.has(this.fields_view.arch.attrs, action))?JSON.parse(this.fields_view.arch.attrs[action]):true; } }); instance.web.ListView.List = instance.web.Class.extend( /** @lends instance.web.ListView.List# */{ diff --git a/addons/web/static/src/js/views.js b/addons/web/static/src/js/views.js index 7c9dfe1d543..0fdd49633b1 100644 --- a/addons/web/static/src/js/views.js +++ b/addons/web/static/src/js/views.js @@ -1357,6 +1357,14 @@ instance.web.View = instance.web.Widget.extend({ */ reload: function () { return $.when(); + }, + _is_action_enabled: function(action) { + /** + * Takes action (e.g. create/edit/delete) and return the tag attribute from + * the view (e.g. ) + * will help to check access ui of the view. + */ + return (_.has(this.fields_view.arch.attrs, action))?JSON.parse(this.fields_view.arch.attrs[action]):true; } }); From c02116b41f9c500828c54725c11541ddcd1c5dfd Mon Sep 17 00:00:00 2001 From: Fabien Pinckaers Date: Tue, 14 Aug 2012 21:44:12 +0200 Subject: [PATCH 014/436] [IMP] cleaning of need action bzr revid: fp@openerp.com-20120814194412-2bcujivyd45buikx --- openerp/addons/base/ir/ir.xml | 24 --- openerp/addons/base/ir/ir_needaction.py | 182 +++--------------- openerp/addons/base/ir/ir_ui_menu.py | 18 +- .../addons/base/security/ir.model.access.csv | 1 - openerp/osv/orm.py | 33 +--- 5 files changed, 36 insertions(+), 222 deletions(-) diff --git a/openerp/addons/base/ir/ir.xml b/openerp/addons/base/ir/ir.xml index fac2946e204..c554a8896aa 100644 --- a/openerp/addons/base/ir/ir.xml +++ b/openerp/addons/base/ir/ir.xml @@ -556,30 +556,6 @@ - - - ir.needaction_users_rel.tree - ir.needaction_users_rel - 10 - - - - - - - - - - - Need action relationships - ir.needaction_users_rel - form - tree,form - - - - - ir.ui.view diff --git a/openerp/addons/base/ir/ir_needaction.py b/openerp/addons/base/ir/ir_needaction.py index 0f28c33ded9..95fc6d81b09 100644 --- a/openerp/addons/base/ir/ir_needaction.py +++ b/openerp/addons/base/ir/ir_needaction.py @@ -19,180 +19,48 @@ # ############################################################################## -import openerp.pooler as pooler -from operator import itemgetter from osv import osv, fields -from tools.translate import _ - -class ir_needaction_users_rel(osv.Model): - ''' ir_needaction_users_rel holds data related to the needaction - mechanism inside OpenERP. A row in this model is characterized - by: - - res_model: model of the record requiring an action - - res_id: ID of the record requiring an action - - user_id: foreign key to the res.users table, to the user that has to - perform the action - This model can be seen as a many2many, linking (res_model, res_id) to users - (those whose attention is required on the record). ''' - - _name = 'ir.needaction_users_rel' - _description = 'Needaction relationship table' - _rec_name = 'id' - _order = 'id desc' - _columns = { - 'res_model': fields.char('Related Document Model', size=128, - select=1, required=True), - 'res_id': fields.integer('Related Document ID', - select=1, required=True), - 'user_id': fields.many2one('res.users', string='Related User', - ondelete='cascade', select=1, required=True), - } - - def _get_users(self, cr, uid, res_ids, res_model, context=None): - """Given res_ids of res_model, get user_ids present in table""" - rel_ids = self.search(cr, uid, [('res_model', '=', res_model), ('res_id', 'in', res_ids)], context=context) - return list(set(map(itemgetter('user_id'), self.read(cr, uid, rel_ids, ['user_id'], context=context)))) - - def create_users(self, cr, uid, res_ids, res_model, user_ids, context=None): - """Given res_ids of res_model, add user_ids to the relationship table""" - for res_id in res_ids: - for user_id in user_ids: - self.create(cr, uid, {'res_model': res_model, 'res_id': res_id, 'user_id': user_id}, context=context) - return True - - def unlink_users(self, cr, uid, res_ids, res_model, context=None): - """Given res_ids of res_model, delete all entries in the relationship table""" - to_del_ids = self.search(cr, uid, [('res_model', '=', res_model), ('res_id', 'in', res_ids)], context=context) - return self.unlink(cr, uid, to_del_ids, context=context) - - def update_users(self, cr, uid, res_ids, res_model, user_ids, context=None): - """Given res_ids of res_model, update their entries in the relationship table to user_ids""" - # read current records - cur_users = self._get_users(cr, uid, res_ids, res_model, context=context) - if len(cur_users) == len(user_ids) and all(cur_user in user_ids for cur_user in cur_users): - return True - # unlink old records - self.unlink_users(cr, uid, res_ids, res_model, context=context) - # link new records - self.create_users(cr, uid, res_ids, res_model, user_ids, context=context) - return True - class ir_needaction_mixin(osv.Model): '''Mixin class for objects using the need action feature. - - Need action feature can be used by objects having to be able to - signal that an action is required on a particular record. If in - the business logic an action must be performed by somebody, for - instance validation by a manager, this mechanism allows to set a + + Need action feature can be used by objects having to be able to + signal that an action is required on a particular record. If in + the business logic an action must be performed by somebody, for + instance validation by a manager, this mechanism allows to set a list of users asked to perform an action. - - This class wraps a class (ir.ir_needaction_users_rel) that - behaves like a many2many field. This class handles the low-level - considerations of updating relationships. Every change made on - the record calls a method that updates the relationships. - - Objects using the 'need_action' feature should override the - ``get_needaction_user_ids`` method. This methods returns a - dictionary whose keys are record ids, and values a list of user - ids, like in a many2many relationship. Therefore by defining - only one method, you can specify if an action is required by - defining the users that have to do it, in every possible - situation. - - This class also offers several global services: - - ``needaction_get_record_ids``: for the current model and uid, get - all record ids that ask this user to perform an action. This - mechanism is used for instance to display the number of pending - actions in menus, such as Leads (12) + + Objects using the 'need_action' feature should override the + ``needaction_domain_get`` method. This methods returns a + domain to filter records requiring an action for a specific user. + + This class also offers several global services: - ``needaction_get_action_count``: as ``needaction_get_record_ids`` - but returns only the number of action, not the ids (performs a + but returns only the number of action, not the ids (performs a search with count=True) + ''' - The ``ir_needaction_mixin`` class adds a calculated field - ``needaction_pending``. This function field allows to state - whether a given record has a needaction for the current user. - This is usefull if you want to customize views according to the - needaction feature. For example, you may want to set records in - bold in a list view if the current user has an action to perform - on the record. ''' - _name = 'ir.needaction_mixin' - _description = 'Need action mixin' - - def get_needaction_pending(self, cr, uid, ids, name, arg, context=None): - res = {} - needaction_user_ids = self.get_needaction_user_ids(cr, uid, ids, context=context) - for id in ids: - res[id] = uid in needaction_user_ids[id] - return res + _description = 'Need Action Mixin' + _needaction = True - def search_needaction_pending(self, cr, uid, self_again, field_name, criterion, context=None): - ids = self.needaction_get_record_ids( - cr, uid, uid, limit=1024, context=context) - return [('id', 'in', ids)] - - _columns = { - 'needaction_pending': fields.function( - get_needaction_pending, type='boolean', - fnct_search=search_needaction_pending, - string='Need action pending', - help="If True, this field states that users have to perform an " \ - "action This field comes from the ir.needaction_mixin class."), - } - #------------------------------------------------------ # Addon API #------------------------------------------------------ - - def get_needaction_user_ids(self, cr, uid, ids, context=None): - """ Returns the user_ids that have to perform an action - :return: dict { record_id: [user_ids], } + + def _needaction_domain_get(self, cr, uid, context=None): + """ Returns the domain to filter records that require an action + :return: domain or False is no need action """ - return dict((id,list()) for id in ids) - - def create(self, cr, uid, values, context=None): - rel_obj = self.pool.get('ir.needaction_users_rel') - # perform create - obj_id = super(ir_needaction_mixin, self).create(cr, uid, values, context=context) - # link user_ids - needaction_user_ids = self.get_needaction_user_ids(cr, uid, [obj_id], context=context) - rel_obj.create_users(cr, uid, [obj_id], self._name, needaction_user_ids[obj_id], context=context) - return obj_id - - def write(self, cr, uid, ids, values, context=None): - rel_obj = self.pool.get('ir.needaction_users_rel') - # perform write - write_res = super(ir_needaction_mixin, self).write(cr, uid, ids, values, context=context) - # get and update user_ids - needaction_user_ids = self.get_needaction_user_ids(cr, uid, ids, context=context) - for id in ids: - rel_obj.update_users(cr, uid, [id], self._name, needaction_user_ids[id], context=context) - return write_res - - def unlink(self, cr, uid, ids, context=None): - # unlink user_ids - rel_obj = self.pool.get('ir.needaction_users_rel') - rel_obj.unlink_users(cr, uid, ids, self._name, context=context) - # perform unlink - return super(ir_needaction_mixin, self).unlink(cr, uid, ids, context=context) - + return False + #------------------------------------------------------ # "Need action" API #------------------------------------------------------ - - def needaction_get_record_ids(self, cr, uid, user_id, limit=80, context=None): - """Given the current model and a user_id - return the record ids that require the user to perform an - action""" - rel_obj = self.pool.get('ir.needaction_users_rel') - rel_ids = rel_obj.search(cr, uid, [('res_model', '=', self._name), ('user_id', '=', user_id)], limit=limit, context=context) - return map(itemgetter('res_id'), rel_obj.read(cr, uid, rel_ids, ['res_id'], context=context)) - - def needaction_get_action_count(self, cr, uid, user_id, limit=80, context=None): + + def _needaction_count(self, cr, uid, domain=[], context=None): """Given the current model and a user_id get the number of actions it has to perform""" - rel_obj = self.pool.get('ir.needaction_users_rel') - return rel_obj.search(cr, uid, [('res_model', '=', self._name), ('user_id', '=', user_id)], limit=limit, count=True, context=context) + dom = self._needaction_domain_get(cr, uid, context=context) + return self.search(cr, uid, domain+dom, context=context, count=True) -# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/openerp/addons/base/ir/ir_ui_menu.py b/openerp/addons/base/ir/ir_ui_menu.py index 77c0c4d738a..1bb517c570f 100644 --- a/openerp/addons/base/ir/ir_ui_menu.py +++ b/openerp/addons/base/ir/ir_ui_menu.py @@ -255,23 +255,23 @@ class ir_ui_menu(osv.osv): return res - def _get_needaction_info(self, cr, uid, id, domain=[], context={}): - return [False, 0] - def _get_needaction(self, cr, uid, ids, field_names, args, context=None): if context is None: context = {} res = {} for menu in self.browse(cr, uid, ids, context=context): + res[menu.id]['needaction_enabled'] = False + res[menu.id]['needaction_counter'] = False res[menu.id] = {} if menu.action and menu.action.type == 'ir.actions.act_window' and menu.action.res_model: - menu_needaction_res = self.pool.get(menu.action.res_model)._get_needaction_info(cr, uid, uid, domain=menu.action.domain, context=context) - else: - menu_needaction_res = [False, 0] - res[menu.id]['needaction_enabled'] = menu_needaction_res[0] - res[menu.id]['needaction_counter'] = menu_needaction_res[1] + obj = self.pool.get(menu.action.res_model) + if obj._needaction_active: + res[menu.id]['needaction_enabled'] = obj._needaction_active + # check domain and context: should we evaluate the domain ? + # and add context of the action ? + res[menu.id]['needaction_counter'] = obj._needaction_count(cr, uid, menu.action.domain, context=context) return res - + _columns = { 'name': fields.char('Menu', size=64, required=True, translate=True), 'sequence': fields.integer('Sequence'), diff --git a/openerp/addons/base/security/ir.model.access.csv b/openerp/addons/base/security/ir.model.access.csv index d49aa18db8c..b0218e977bc 100644 --- a/openerp/addons/base/security/ir.model.access.csv +++ b/openerp/addons/base/security/ir.model.access.csv @@ -118,6 +118,5 @@ "access_ir_config_parameter","ir_config_parameter","model_ir_config_parameter",,1,0,0,0 "access_ir_mail_server_all","ir_mail_server","model_ir_mail_server",,1,0,0,0 "access_ir_actions_client","ir_actions_client all","model_ir_actions_client",,1,0,0,0 -"access_ir_needaction_users_rel","ir_needaction_users_rel","model_ir_needaction_users_rel",,1,1,1,1 "access_ir_needaction_mixin","ir_needaction_mixin","model_ir_needaction_mixin",,1,1,1,1 diff --git a/openerp/osv/orm.py b/openerp/osv/orm.py index 7c8085ea8ed..368a37f455a 100644 --- a/openerp/osv/orm.py +++ b/openerp/osv/orm.py @@ -666,6 +666,7 @@ class BaseModel(object): _order = 'id' _sequence = None _description = None + _needaction = False # dict of {field:method}, with method returning the name_get of records # to include in the _read_group, if grouped on this field @@ -4960,37 +4961,7 @@ class BaseModel(object): # backwards compatibility get_xml_id = get_external_id _get_xml_ids = _get_external_ids - - def _get_needaction_info(self, cr, uid, user_id, limit=None, order=None, domain=False, context=None): - """Base method for needaction mechanism - - see ir.needaction for actual implementation - - if the model uses the need action mechanism - (hasattr(model_obj, 'needaction_get_record_ids')): - - get the record ids on which the user has actions to perform - - evaluate the menu domain - - compose a new domain: menu domain, limited to ids of - records requesting an action - - count the number of records maching that domain, that - is the number of actions the user has to perform - - this method returns default values - :param: model_name: the name of the model (ex: hr.holidays) - :param: user_id: the id of user - :return: [uses_needaction=True/False, needaction_uid_ctr=%d] - """ - if hasattr(self, 'needaction_get_record_ids'): - # Arbitrary limit, but still much lower thant infinity, to avoid - # getting too much data. - ids = self.needaction_get_record_ids(cr, uid, user_id, limit=8192, context=context) - if not ids: - return [True, 0] - if domain: - new_domain = eval(domain, locals_dict={'uid': user_id}) + [('id', 'in', ids)] - else: - new_domain = [('id', 'in', ids)] - return [True, self.search(cr, uid, new_domain, limit=limit, order=order, count=True, context=context)] - else: - return [False, 0] - + # Transience def is_transient(self): """ Return whether the model is transient. From 560e9e7fd259c4da0285d3730ec72c1f462b755a Mon Sep 17 00:00:00 2001 From: Fabien Pinckaers Date: Tue, 14 Aug 2012 21:58:40 +0200 Subject: [PATCH 015/436] [IMP] put needaction_pending again, new implementation bzr revid: fp@openerp.com-20120814195840-yaha9r3qvj8o13zr --- openerp/addons/base/ir/ir_needaction.py | 27 +++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/openerp/addons/base/ir/ir_needaction.py b/openerp/addons/base/ir/ir_needaction.py index 95fc6d81b09..5552b42052e 100644 --- a/openerp/addons/base/ir/ir_needaction.py +++ b/openerp/addons/base/ir/ir_needaction.py @@ -38,12 +38,39 @@ class ir_needaction_mixin(osv.Model): - ``needaction_get_action_count``: as ``needaction_get_record_ids`` but returns only the number of action, not the ids (performs a search with count=True) + + The ``ir_needaction_mixin`` class adds a calculated field + ``needaction_pending``. This function field allows to state + whether a given record has a needaction for the current user. + This is usefull if you want to customize views according to the + needaction feature. For example, you may want to set records in + bold in a list view if the current user has an action to perform + on the record. ''' _name = 'ir.needaction_mixin' _description = 'Need Action Mixin' _needaction = True + def get_needaction_pending(self, cr, uid, ids, name, arg, context=None): + dom = self._needaction_domain_get(cr, uid, context=context) + result = dict.fromkeys(ids, False) + ids2 = self.search(cr, uid, [('id','in',ids)+dom], context=context) + for idna in ids2: + result[idna] = True + return result + + def search_needaction_pending(self, cr, uid, field, field_name, criterion, context=None): + return self._needaction_domain_get(cr, uid, context=context) + + _columns = { + 'needaction_pending': fields.function( + get_needaction_pending, type='boolean', + fnct_search=search_needaction_pending, + string='Need an Action', + help="Do the current user requires to perform an action."), + } + #------------------------------------------------------ # Addon API #------------------------------------------------------ From 8b61ed737c1e5da85dc75091246cab89ca6b85b0 Mon Sep 17 00:00:00 2001 From: Fabien Pinckaers Date: Wed, 15 Aug 2012 12:29:19 +0200 Subject: [PATCH 016/436] [IMP] need action new implementation bzr revid: fp@openerp.com-20120815102919-w3iuspeoz5fmz97u --- addons/base_calendar/crm_meeting.py | 4 ++++ addons/crm/crm_phonecall.py | 2 +- addons/hr_holidays/hr_holidays.py | 28 +++++++++++++--------------- addons/purchase/purchase.py | 8 ++------ addons/sale/sale.py | 8 ++------ 5 files changed, 22 insertions(+), 28 deletions(-) diff --git a/addons/base_calendar/crm_meeting.py b/addons/base_calendar/crm_meeting.py index cb96ff2a4d0..a66449a8b5d 100644 --- a/addons/base_calendar/crm_meeting.py +++ b/addons/base_calendar/crm_meeting.py @@ -70,6 +70,10 @@ class crm_meeting(base_state, osv.Model): # OpenChatter # ---------------------------------------- + # shows events of the day for this user + def needaction_domain_get(self, cr, uid, domain=[], context={}): + return [('date','<=',time.strftime('%Y-%M-%D 23:59:59')), ('date_deadline','>=', time.strftime('%Y-%M-%D 00:00:00')), ('user_id','=',uid)] + def case_get_note_msg_prefix(self, cr, uid, id, context=None): return 'Meeting' diff --git a/addons/crm/crm_phonecall.py b/addons/crm/crm_phonecall.py index 8ffa8dd591e..7a544da5dc1 100644 --- a/addons/crm/crm_phonecall.py +++ b/addons/crm/crm_phonecall.py @@ -32,7 +32,7 @@ class crm_phonecall(base_state, osv.osv): _name = "crm.phonecall" _description = "Phonecall" _order = "id desc" - _inherit = ['ir.needaction_mixin', 'mail.thread'] + _inherit = ['mail.thread'] _columns = { # base_state required fields 'date_action_last': fields.datetime('Last Action', readonly=1), diff --git a/addons/hr_holidays/hr_holidays.py b/addons/hr_holidays/hr_holidays.py index 7eedfa54ce5..0134fc79fdd 100644 --- a/addons/hr_holidays/hr_holidays.py +++ b/addons/hr_holidays/hr_holidays.py @@ -347,24 +347,22 @@ class hr_holidays(osv.osv): if leaves_rest < record.number_of_days_temp: raise osv.except_osv(_('Warning!'), _('There are not enough %s allocated for employee %s; please create an allocation request for this leave type.') % (record.holiday_status_id.name, record.employee_id.name)) return True - + # ----------------------------- # OpenChatter and notifications # ----------------------------- - - def get_needaction_user_ids(self, cr, uid, ids, context=None): - result = super(hr_holidays, self).get_needaction_user_ids(cr, uid, ids, context=context) - for obj in self.browse(cr, uid, ids, context=context): - if obj.state == 'confirm' and obj.employee_id.parent_id: - result[obj.id] = [obj.employee_id.parent_id.user_id.id] - elif obj.state == 'validate1': - # get group_hr_manager: everyone will be warned of second validation - res = self.pool.get('ir.model.data').get_object_reference(cr, uid, 'base', 'group_hr_manager') or False - obj_id = res and res[1] or False - if obj_id: - hr_manager_group = self.pool.get('res.groups').read(cr, uid, [obj_id], ['users'], context=context)[0] - result[obj.id] = hr_manager_group['users'] - return result + + def needaction_domain_get(self, cr, uid, ids, context=None): + # to be tested, otherwise convert into employee_id in ... + emp_obj = self.pool.get('hr.employee') + empids = emp_obj.search(cr, uid, [('parent_id.user_id','=',uid)], context=context) + dom = [ + '&', ('state','=','confirm'),('employee_id', 'in', empids) + ] + # if this user is a hr.manager, he should do second validations + if self.pool.get('res.users').has_group(cr, uid, 'base.group_hr_manager'): + dom = ['|'] + dom + [ ('state','=','validate1') ] + return dom def message_get_subscribers(self, cr, uid, ids, context=None): """ Override to add employee and its manager. """ diff --git a/addons/purchase/purchase.py b/addons/purchase/purchase.py index b02e959301f..705f55f5d5c 100644 --- a/addons/purchase/purchase.py +++ b/addons/purchase/purchase.py @@ -732,12 +732,8 @@ class purchase_order(osv.osv): # OpenChatter methods and notifications # -------------------------------------- - def get_needaction_user_ids(self, cr, uid, ids, context=None): - result = super(purchase_order, self).get_needaction_user_ids(cr, uid, ids, context=context) - for obj in self.browse(cr, uid, ids, context=context): - if obj.state == 'approved': - result[obj.id].append(obj.validator.id) - return result + def needaction_domain_get(self, cr, uid, ids, context=None): + return [('state','=','draft')] def create_send_note(self, cr, uid, ids, context=None): return self.message_append_note(cr, uid, ids, body=_("Request for quotation created."), context=context) diff --git a/addons/sale/sale.py b/addons/sale/sale.py index 722b22ba602..2a9aa08d3ca 100644 --- a/addons/sale/sale.py +++ b/addons/sale/sale.py @@ -1022,12 +1022,8 @@ class sale_order(osv.osv): # OpenChatter methods and notifications # ------------------------------------------------ - def get_needaction_user_ids(self, cr, uid, ids, context=None): - result = super(sale_order, self).get_needaction_user_ids(cr, uid, ids, context=context) - for obj in self.browse(cr, uid, ids, context=context): - if (obj.state == 'manual' or obj.state == 'progress'): - result[obj.id].append(obj.user_id.id) - return result + def needaction_domain_get(self, cr, uid, ids, context=None): + return [('state', '=', 'draft'), ('user_id','=',uid)] def create_send_note(self, cr, uid, ids, context=None): for obj in self.browse(cr, uid, ids, context=context): From eb0669850d6f07d19db12291fe78a40eb3ca8e4e Mon Sep 17 00:00:00 2001 From: Fabien Pinckaers Date: Wed, 15 Aug 2012 15:36:43 +0200 Subject: [PATCH 017/436] [IMP] removed needaction_pending, some cleaning in messages mail bzr revid: fp@openerp.com-20120815133643-loyzmyp6azs5lr47 --- addons/base_calendar/crm_meeting_view.xml | 25 +- addons/crm/crm_lead_view.xml | 16 +- addons/crm/crm_phonecall_view.xml | 8 +- addons/event/event_view.xml | 12 +- addons/hr_holidays/hr_holidays_view.xml | 10 +- addons/hr_recruitment/hr_recruitment_view.xml | 6 +- addons/mail/mail_message.py | 90 ++--- addons/mail/mail_subscription.py | 40 ++ addons/mail/mail_thread.py | 342 ++++++------------ addons/mail/res_users.py | 3 +- addons/mrp/mrp_view.xml | 4 +- addons/project/project_view.xml | 14 +- addons/project_issue/project_issue_view.xml | 10 +- addons/purchase/purchase_view.xml | 6 +- .../purchase_requisition_view.xml | 4 +- addons/sale/sale_view.xml | 6 +- 16 files changed, 246 insertions(+), 350 deletions(-) diff --git a/addons/base_calendar/crm_meeting_view.xml b/addons/base_calendar/crm_meeting_view.xml index c6826786379..b57d7dcb2c7 100644 --- a/addons/base_calendar/crm_meeting_view.xml +++ b/addons/base_calendar/crm_meeting_view.xml @@ -170,7 +170,7 @@
@@ -363,7 +363,7 @@ - + @@ -542,7 +542,7 @@ Opportunities Tree crm.lead - + @@ -560,7 +560,7 @@ - + @@ -575,7 +575,7 @@ - + diff --git a/addons/crm/crm_phonecall_view.xml b/addons/crm/crm_phonecall_view.xml index 9081ea15637..1639d075f3e 100644 --- a/addons/crm/crm_phonecall_view.xml +++ b/addons/crm/crm_phonecall_view.xml @@ -66,8 +66,7 @@ CRM - Phone Calls Tree crm.phonecall - - + @@ -76,7 +75,6 @@ -
-
+

- +
diff --git a/addons/mail/mail_mail.py b/addons/mail/mail_mail.py new file mode 100644 index 00000000000..b960745ec68 --- /dev/null +++ b/addons/mail/mail_mail.py @@ -0,0 +1,268 @@ + +class mail_mail(osv.Model): + """ + Model holding RFC2822 email messages to send. This model also provides + facilities to queue and send new email messages. + """ + + _name = 'mail.mail' + _description = 'Outgoing Mails' + _inherits = {'mail.message': 'message_id'} + _columns = { + 'message_id': fields.many2one('mail.message', 'Message', required=True), + # why do we need this on the mail, why not on the company? + 'mail_server_id': fields.many2one('ir.mail_server', 'Outgoing mail server', readonly=1), + 'state': fields.selection([ + ('outgoing', 'Outgoing'), + ('sent', 'Sent'), + ('received', 'Received'), + ('exception', 'Delivery Failed'), + ('cancel', 'Cancelled'), + ], 'Status', readonly=True), + 'auto_delete': fields.boolean('Auto Delete', + help="Permanently delete this email after sending it, to save space"), + + # FP Note: to be removed, in attachment if needed + 'original': fields.binary('Original', readonly=1, + help="Original version of the message, as it was sent on the network"), + # End FP Note + + # FP Note: I propose to remove these fields and put email in attachment as an option + 'email_from': fields.char('From', size=128, help='Message sender, taken from user preferences.'), + 'email_to': fields.text('To', help='Message recipients'), + 'email_cc': fields.char('Cc', size=256, help='Carbon copy message recipients'), + 'email_bcc': fields.char('Bcc', size=256, help='Blind carbon copy message recipients'), + 'reply_to':fields.char('Reply-To', size=256, help='Preferred response address for the message'), + 'content_subtype': fields.char('Message content subtype', size=32, + oldname="subtype", readonly=1, + help="Type of message, usually 'html' or 'plain', used to select "\ + "plain-text or rich-text contents accordingly"), + 'body_html': fields.html('Rich-text Contents', help="Rich-text/HTML version of the message"), + + } + + _defaults = { + 'state': 'received', + 'content_subtype': 'plain', + } + + # FP Note: do we need this method ? + # We should just ask to create the message before scheduling it + def schedule_with_attach(self, cr, uid, email_from, email_to, subject, body, model=False, type='email', + email_cc=None, email_bcc=None, reply_to=False, partner_ids=None, attachments=None, + message_id=False, references=False, res_id=False, content_subtype='plain', + headers=None, mail_server_id=False, auto_delete=False, context=None): + """ Schedule sending a new email message, to be sent the next time the + mail scheduler runs, or the next time :meth:`process_email_queue` is + called explicitly. + + :param string email_from: sender email address + :param list email_to: list of recipient addresses (to be joined with commas) + :param string subject: email subject (no pre-encoding/quoting necessary) + :param string body: email body, according to the ``content_subtype`` + (by default, plaintext). If html content_subtype is used, the + message will be automatically converted to plaintext and wrapped + in multipart/alternative. + :param list email_cc: optional list of string values for CC header + (to be joined with commas) + :param list email_bcc: optional list of string values for BCC header + (to be joined with commas) + :param string model: optional model name of the document this mail + is related to (this will also be used to generate a tracking id, + used to match any response related to the same document) + :param int res_id: optional resource identifier this mail is related + to (this will also be used to generate a tracking id, used to + match any response related to the same document) + :param string reply_to: optional value of Reply-To header + :param partner_ids: destination partner_ids + :param string content_subtype: optional mime content_subtype for + the text body (usually 'plain' or 'html'), must match the format + of the ``body`` parameter. Default is 'plain', making the content + part of the mail "text/plain". + :param dict attachments: map of filename to filecontents, where + filecontents is a string containing the bytes of the attachment + :param dict headers: optional map of headers to set on the outgoing + mail (may override the other headers, including Subject, + Reply-To, Message-Id, etc.) + :param int mail_server_id: optional id of the preferred outgoing + mail server for this mail + :param bool auto_delete: optional flag to turn on auto-deletion of + the message after it has been successfully sent (default to False) + """ + if context is None: + context = {} + if attachments is None: + attachments = {} + if partner_ids is None: + partner_ids = [] + attachment_obj = self.pool.get('ir.attachment') + for param in (email_to, email_cc, email_bcc): + if param and not isinstance(param, list): + param = [param] + msg_vals = { + 'subject': subject, + 'date': fields.datetime.now(), + 'user_id': uid, + 'model': model, + 'res_id': res_id, + 'type': type, + 'body_text': body if content_subtype != 'html' else False, + 'body_html': body if content_subtype == 'html' else False, + 'email_from': email_from, + 'email_to': email_to and ','.join(email_to) or '', + 'email_cc': email_cc and ','.join(email_cc) or '', + 'email_bcc': email_bcc and ','.join(email_bcc) or '', + 'partner_ids': partner_ids, + 'reply_to': reply_to, + 'message_id': message_id, + 'references': references, + 'content_subtype': content_subtype, + 'headers': headers, # serialize the dict on the fly + 'mail_server_id': mail_server_id, + 'state': 'outgoing', + 'auto_delete': auto_delete + } + email_msg_id = self.create(cr, uid, msg_vals, context) + attachment_ids = [] + for fname, fcontent in attachments.iteritems(): + attachment_data = { + 'name': fname, + 'datas_fname': fname, + 'datas': fcontent and fcontent.encode('base64'), + 'res_model': self._name, + 'res_id': email_msg_id, + } + if context.has_key('default_type'): + del context['default_type'] + attachment_ids.append(attachment_obj.create(cr, uid, attachment_data, context)) + if attachment_ids: + self.write(cr, uid, email_msg_id, { 'attachment_ids': [(6, 0, attachment_ids)]}, context=context) + return email_msg_id + + def mark_outgoing(self, cr, uid, ids, context=None): + return self.write(cr, uid, ids, {'state':'outgoing'}, context=context) + + def cancel(self, cr, uid, ids, context=None): + return self.write(cr, uid, ids, {'state':'cancel'}, context=context) + + def process_email_queue(self, cr, uid, ids=None, context=None): + """Send immediately queued messages, committing after each + message is sent - this is not transactional and should + not be called during another transaction! + + :param list ids: optional list of emails ids to send. If passed + no search is performed, and these ids are used + instead. + :param dict context: if a 'filters' key is present in context, + this value will be used as an additional + filter to further restrict the outgoing + messages to send (by default all 'outgoing' + messages are sent). + """ + if context is None: + context = {} + if not ids: + filters = ['&', ('state', '=', 'outgoing'), ('type', '=', 'email')] + if 'filters' in context: + filters.extend(context['filters']) + ids = self.search(cr, uid, filters, context=context) + res = None + try: + # Force auto-commit - this is meant to be called by + # the scheduler, and we can't allow rolling back the status + # of previously sent emails! + res = self.send(cr, uid, ids, auto_commit=True, context=context) + except Exception: + _logger.exception("Failed processing mail queue") + return res + + def _postprocess_sent_message(self, cr, uid, message, context=None): + """Perform any post-processing necessary after sending ``message`` + successfully, including deleting it completely along with its + attachment if the ``auto_delete`` flag of the message was set. + Overridden by subclasses for extra post-processing behaviors. + + :param browse_record message: the message that was just sent + :return: True + """ + if message.auto_delete: + self.pool.get('ir.attachment').unlink(cr, uid, + [x.id for x in message.attachment_ids + if x.res_model == self._name and x.res_id == message.id], + context=context) + message.unlink() + return True + + def send(self, cr, uid, ids, auto_commit=False, context=None): + """Sends the selected emails immediately, ignoring their current + state (mails that have already been sent should not be passed + unless they should actually be re-sent). + Emails successfully delivered are marked as 'sent', and those + that fail to be deliver are marked as 'exception', and the + corresponding error message is output in the server logs. + + :param bool auto_commit: whether to force a commit of the message + status after sending each message (meant + only for processing by the scheduler), + should never be True during normal + transactions (default: False) + :return: True + """ + ir_mail_server = self.pool.get('ir.mail_server') + self.write(cr, uid, ids, {'state': 'outgoing'}, context=context) + for message in self.browse(cr, uid, ids, context=context): + try: + attachments = [] + for attach in message.attachment_ids: + attachments.append((attach.datas_fname, base64.b64decode(attach.datas))) + + body = message.body_html if message.content_subtype == 'html' else message.body_text + body_alternative = None + content_subtype_alternative = None + if message.content_subtype == 'html' and message.body_text: + # we have a plain text alternative prepared, pass it to + # build_message instead of letting it build one + body_alternative = message.body_text + content_subtype_alternative = 'plain' + + # handle destination_partners + partner_ids_email_to = '' + for partner in message.partner_ids: + partner_ids_email_to += '%s ' % (partner.email or '') + message_email_to = '%s %s' % (partner_ids_email_to, message.email_to or '') + + # build an RFC2822 email.message.Message object and send it + # without queuing + msg = ir_mail_server.build_email( + email_from=message.email_from, + email_to=mail_tools_to_email(message_email_to), + subject=message.subject, + body=body, + body_alternative=body_alternative, + email_cc=mail_tools_to_email(message.email_cc), + email_bcc=mail_tools_to_email(message.email_bcc), + reply_to=message.reply_to, + attachments=attachments, message_id=message.message_id, + references = message.references, + object_id=message.res_id and ('%s-%s' % (message.res_id,message.model)), + subtype=message.content_subtype, + subtype_alternative=content_subtype_alternative, + headers=message.headers and ast.literal_eval(message.headers)) + res = ir_mail_server.send_email(cr, uid, msg, + mail_server_id=message.mail_server_id.id, + context=context) + if res: + message.write({'state':'sent', 'message_id': res, 'email_to': message_email_to}) + else: + message.write({'state':'exception', 'email_to': message_email_to}) + message.refresh() + if message.state == 'sent': + self._postprocess_sent_message(cr, uid, message, context=context) + except Exception: + _logger.exception('failed sending mail.message %s', message.id) + message.write({'state':'exception'}) + + if auto_commit == True: + cr.commit() + return True + diff --git a/addons/mail/mail_message.py b/addons/mail/mail_message.py index ee137b635ce..227a9b81f5b 100644 --- a/addons/mail/mail_message.py +++ b/addons/mail/mail_message.py @@ -54,93 +54,15 @@ def mail_tools_to_email(text): if not text: return [] return re.findall(r'([^ ,<@]+@[^> ,]+)', text) -# TODO: remove that after cleaning -def to_email(text): - return mail_tools_to_email(text) - -# FP Note: I propose to merge mail.message and mail.message.common -# there is a conflict for the field parent_id -# I don't understand the usage of mail.message.common -class mail_message_common(osv.TransientModel): - """ Common abstract class for holding the main attributes of a - message object. It could be reused as parent model for any - database model or wizard screen that needs to hold a kind of - message. - All internal logic should be in another model while this - model holds the basics of a message. For example, a wizard for writing - emails should inherit from this class and not from mail.message.""" - - def get_record_name(self, cr, uid, ids, name, arg, context=None): - result = dict.fromkeys(ids, '') - for message in self.browse(cr, uid, ids, context=context): - if not message.model or not message.res_id: - continue - result[message.id] = self.pool.get(message.model).name_get(cr, uid, [message.res_id], context=context)[0][1] - return result - - def name_get(self, cr, uid, ids, context=None): - res = [] - for message in self.browse(cr, uid, ids, context=context): - name = '' - if message.subject: - name = '%s: ' % (message.subject) - if message.body_text: - name = name + message.body_text[0:20] - res.append((message.id, name)) - return res - - _name = 'mail.message.common' - _rec_name = 'subject' - _columns = { - 'model': fields.char('Related Document Model', size=128, select=1), - 'res_id': fields.integer('Related Document ID', select=1), - 'record_name': fields.function(get_record_name, type='string', - string='Message Record Name', - help="Name get of the related document."), - - 'subject': fields.char('Subject', size=128), - 'date': fields.datetime('Date'), - - # FP Note: I propose to remove these fields and put email in attachment as an option - 'email_from': fields.char('From', size=128, help='Message sender, taken from user preferences.'), - 'email_to': fields.text('To', help='Message recipients'), - 'email_cc': fields.char('Cc', size=256, help='Carbon copy message recipients'), - 'email_bcc': fields.char('Bcc', size=256, help='Blind carbon copy message recipients'), - 'reply_to':fields.char('Reply-To', size=256, help='Preferred response address for the message'), - 'headers': fields.text('Message Headers', readonly=1, - help="Full message headers, e.g. SMTP session headers (usually available on inbound messages only)"), - 'references': fields.text('References', help='Message references, such as identifiers of previous messages', readonly=1), - 'content_subtype': fields.char('Message content subtype', size=32, - oldname="subtype", readonly=1, - help="Type of message, usually 'html' or 'plain', used to select "\ - "plain-text or rich-text contents accordingly"), - 'body_html': fields.html('Rich-text Contents', help="Rich-text/HTML version of the message"), - # END FP Note - - 'message_id': fields.char('Message-Id', size=256, help='Message unique identifier', select=1, readonly=1), - 'body': fields.text('Text Contents', help="Plain-text version of the message", required=True), - - 'parent_id': fields.many2one('mail.message.common', 'Parent Message', - select=True, ondelete='set null', - help="Parent message, used for displaying as threads with hierarchy"), - } - - _defaults = { - 'content_subtype': 'plain', - 'date': (lambda *a: fields.datetime.now()), - } - class mail_message(osv.Model): """Model holding messages: system notification (replacing res.log - notifications), comments (for OpenChatter feature) and - RFC2822 email messages. This model also provides facilities to - parse, queue and send new email messages. Type of messages - are differentiated using the 'type' column. """ + notifications), comments (for OpenChatter feature). This model also + provides facilities to parse new email messages. Type of messages are + differentiated using the 'type' column. """ _name = 'mail.message' - _inherit = 'mail.message.common' - _description = 'Mail Message (email, comment, notification)' - _order = 'date desc' + _description = 'Message' + _order = 'id desc' # FP Note: can we remove these two methods ? def open_document(self, cr, uid, ids, context=None): @@ -183,7 +105,28 @@ class mail_message(osv.Model): return action_data # END FP Note + def get_record_name(self, cr, uid, ids, name, arg, context=None): + result = dict.fromkeys(ids, '') + for message in self.browse(cr, uid, ids, context=context): + if not message.model or not message.res_id: + continue + result[message.id] = self.pool.get(message.model).name_get(cr, uid, [message.res_id], context=context)[0][1] + return result + + def name_get(self, cr, uid, ids, context=None): + res = [] + for message in self.browse(cr, uid, ids, context=context): + name = '' + if message.subject: + name = '%s: ' % (message.subject) + if message.body_text: + name = name + message.body_text[0:20] + res.append((message.id, name)) + return res + + _columns = { + # should we keep a distinction between email and comment ? 'type': fields.selection([ ('email', 'email'), ('comment', 'Comment'), @@ -191,49 +134,55 @@ class mail_message(osv.Model): ], 'Type', help="Message type: email for email message, notification for system "\ "message, comment for other messages such as user replies"), - # FP Note: ro be removed - 'partner_id': fields.many2one('res.partner', 'Related partner', - help="Deprecated field. Use partner_ids instead."), - # END FP Note + # partner_id should be renamed into author_id + 'author_id': fields.many2one('res.partner', 'Author', required=True), + + # this is redundant with notifications ? 'partner_ids': fields.many2many('res.partner', 'mail_message_destination_partner_rel', 'message_id', 'partner_id', 'Destination partners', help="When sending emails through the social network composition wizard"\ "you may choose to send a copy of the mail to partners."), - 'user_id': fields.many2one('res.users', 'Author', readonly=1), - 'attachment_ids': fields.many2many('ir.attachment', 'message_attachment_rel', 'message_id', 'attachment_id', 'Attachments'), - 'mail_server_id': fields.many2one('ir.mail_server', 'Outgoing mail server', readonly=1), - 'state': fields.selection([ - ('outgoing', 'Outgoing'), - ('sent', 'Sent'), - ('received', 'Received'), - ('exception', 'Delivery Failed'), - ('cancel', 'Cancelled'), - ], 'Status', readonly=True), - 'auto_delete': fields.boolean('Auto Delete', - help="Permanently delete this email after sending it, to save space"), - # FP Note: to be removed, in attachment if needed - 'original': fields.binary('Original', readonly=1, - help="Original version of the message, as it was sent on the network"), - # End FP Note + # 'user_id': fields.many2one('res.users', 'Author', readonly=1), + + # why don't we attach the file to the message using res_id, res_model of the attachment ? + 'attachment_ids': fields.many2many('ir.attachment', 'message_attachment_rel', 'message_id', 'attachment_id', 'Attachments'), 'parent_id': fields.many2one('mail.message', 'Parent Message', select=True, ondelete='set null', help="Parent message, used for displaying as threads with hierarchy"), 'child_ids': fields.one2many('mail.message', 'parent_id', 'Child Messages'), + + + 'model': fields.char('Related Document Model', size=128, select=1), + 'res_id': fields.integer('Related Document ID', select=1), + 'record_name': fields.function(get_record_name, type='string', + string='Message Record Name', + help="Name get of the related document."), + + 'subject': fields.char('Subject', size=128), + 'date': fields.datetime('Date'), + + # FP Note: do we need this ? + 'references': fields.text('References', help='Message references, such as identifiers of previous messages', readonly=1), + + # END FP Note + + 'message_id': fields.char('Message-Id', size=256, help='Message unique identifier', select=1, readonly=1), + 'body': fields.text('Content', help="Content of Message", required=True), + } - _defaults = { 'type': 'email', - 'state': 'received', + 'date': (lambda *a: fields.datetime.now()), } - + #------------------------------------------------------ # Email api #------------------------------------------------------ - + def init(self, cr): cr.execute("""SELECT indexname FROM pg_indexes WHERE indexname = 'mail_message_model_res_id_idx'""") if not cr.fetchone(): @@ -260,7 +209,7 @@ class mail_message(osv.Model): mids = self.pool.get(model).exists(cr, uid, mids) ima_obj.check(cr, uid, model, mode) self.pool.get(model).check_access_rule(cr, uid, mids, mode, context=context) - + def create(self, cr, uid, values, context=None): newid = super(mail_message, self).create(cr, uid, values, context) self.check(cr, uid, [newid], mode='create', context=context) @@ -273,9 +222,7 @@ class mail_message(osv.Model): for follower in modobj.follower_ids: if follower.id <> uid: follower_notify.append(follower.id) - self.pool.get('mail.notification').notify(cr, uid, follower_notify, newid, context=context) - return newid def read(self, cr, uid, ids, fields_to_read=None, context=None, load='_classic_read'): @@ -289,7 +236,7 @@ class mail_message(osv.Model): self.check(cr, uid, [id], 'read', context=context) default.update(message_id=False, original=False, headers=False) return super(mail_message,self).copy(cr, uid, id, default=default, context=context) - + def write(self, cr, uid, ids, vals, context=None): result = super(mail_message, self).write(cr, uid, ids, vals, context) self.check(cr, uid, ids, 'write', context=context) @@ -299,136 +246,7 @@ class mail_message(osv.Model): self.check(cr, uid, ids, 'unlink', context=context) return super(mail_message, self).unlink(cr, uid, ids, context) - # FP Note: do we need this method ? - # We should just ask to create the message before scheduling it - def schedule_with_attach(self, cr, uid, email_from, email_to, subject, body, model=False, type='email', - email_cc=None, email_bcc=None, reply_to=False, partner_ids=None, attachments=None, - message_id=False, references=False, res_id=False, content_subtype='plain', - headers=None, mail_server_id=False, auto_delete=False, context=None): - """ Schedule sending a new email message, to be sent the next time the - mail scheduler runs, or the next time :meth:`process_email_queue` is - called explicitly. - - :param string email_from: sender email address - :param list email_to: list of recipient addresses (to be joined with commas) - :param string subject: email subject (no pre-encoding/quoting necessary) - :param string body: email body, according to the ``content_subtype`` - (by default, plaintext). If html content_subtype is used, the - message will be automatically converted to plaintext and wrapped - in multipart/alternative. - :param list email_cc: optional list of string values for CC header - (to be joined with commas) - :param list email_bcc: optional list of string values for BCC header - (to be joined with commas) - :param string model: optional model name of the document this mail - is related to (this will also be used to generate a tracking id, - used to match any response related to the same document) - :param int res_id: optional resource identifier this mail is related - to (this will also be used to generate a tracking id, used to - match any response related to the same document) - :param string reply_to: optional value of Reply-To header - :param partner_ids: destination partner_ids - :param string content_subtype: optional mime content_subtype for - the text body (usually 'plain' or 'html'), must match the format - of the ``body`` parameter. Default is 'plain', making the content - part of the mail "text/plain". - :param dict attachments: map of filename to filecontents, where - filecontents is a string containing the bytes of the attachment - :param dict headers: optional map of headers to set on the outgoing - mail (may override the other headers, including Subject, - Reply-To, Message-Id, etc.) - :param int mail_server_id: optional id of the preferred outgoing - mail server for this mail - :param bool auto_delete: optional flag to turn on auto-deletion of - the message after it has been successfully sent (default to False) - """ - if context is None: - context = {} - if attachments is None: - attachments = {} - if partner_ids is None: - partner_ids = [] - attachment_obj = self.pool.get('ir.attachment') - for param in (email_to, email_cc, email_bcc): - if param and not isinstance(param, list): - param = [param] - msg_vals = { - 'subject': subject, - 'date': fields.datetime.now(), - 'user_id': uid, - 'model': model, - 'res_id': res_id, - 'type': type, - 'body_text': body if content_subtype != 'html' else False, - 'body_html': body if content_subtype == 'html' else False, - 'email_from': email_from, - 'email_to': email_to and ','.join(email_to) or '', - 'email_cc': email_cc and ','.join(email_cc) or '', - 'email_bcc': email_bcc and ','.join(email_bcc) or '', - 'partner_ids': partner_ids, - 'reply_to': reply_to, - 'message_id': message_id, - 'references': references, - 'content_subtype': content_subtype, - 'headers': headers, # serialize the dict on the fly - 'mail_server_id': mail_server_id, - 'state': 'outgoing', - 'auto_delete': auto_delete - } - email_msg_id = self.create(cr, uid, msg_vals, context) - attachment_ids = [] - for fname, fcontent in attachments.iteritems(): - attachment_data = { - 'name': fname, - 'datas_fname': fname, - 'datas': fcontent and fcontent.encode('base64'), - 'res_model': self._name, - 'res_id': email_msg_id, - } - if context.has_key('default_type'): - del context['default_type'] - attachment_ids.append(attachment_obj.create(cr, uid, attachment_data, context)) - if attachment_ids: - self.write(cr, uid, email_msg_id, { 'attachment_ids': [(6, 0, attachment_ids)]}, context=context) - return email_msg_id - - def mark_outgoing(self, cr, uid, ids, context=None): - return self.write(cr, uid, ids, {'state':'outgoing'}, context=context) - - def cancel(self, cr, uid, ids, context=None): - return self.write(cr, uid, ids, {'state':'cancel'}, context=context) - - def process_email_queue(self, cr, uid, ids=None, context=None): - """Send immediately queued messages, committing after each - message is sent - this is not transactional and should - not be called during another transaction! - - :param list ids: optional list of emails ids to send. If passed - no search is performed, and these ids are used - instead. - :param dict context: if a 'filters' key is present in context, - this value will be used as an additional - filter to further restrict the outgoing - messages to send (by default all 'outgoing' - messages are sent). - """ - if context is None: - context = {} - if not ids: - filters = ['&', ('state', '=', 'outgoing'), ('type', '=', 'email')] - if 'filters' in context: - filters.extend(context['filters']) - ids = self.search(cr, uid, filters, context=context) - res = None - try: - # Force auto-commit - this is meant to be called by - # the scheduler, and we can't allow rolling back the status - # of previously sent emails! - res = self.send(cr, uid, ids, auto_commit=True, context=context) - except Exception: - _logger.exception("Failed processing mail queue") - return res - + # FP Note: to be simplified, mail.message fields only, not mail.mail def parse_message(self, message, save_original=False, context=None): """Parses a string or email.message.Message representing an RFC-2822 email, and returns a generic dict holding the @@ -585,96 +403,4 @@ class mail_message(osv.Model): msg['sub_type'] = msg['content_subtype'] or 'plain' return msg - def _postprocess_sent_message(self, cr, uid, message, context=None): - """Perform any post-processing necessary after sending ``message`` - successfully, including deleting it completely along with its - attachment if the ``auto_delete`` flag of the message was set. - Overridden by subclasses for extra post-processing behaviors. - :param browse_record message: the message that was just sent - :return: True - """ - if message.auto_delete: - self.pool.get('ir.attachment').unlink(cr, uid, - [x.id for x in message.attachment_ids - if x.res_model == self._name and x.res_id == message.id], - context=context) - message.unlink() - return True - - def send(self, cr, uid, ids, auto_commit=False, context=None): - """Sends the selected emails immediately, ignoring their current - state (mails that have already been sent should not be passed - unless they should actually be re-sent). - Emails successfully delivered are marked as 'sent', and those - that fail to be deliver are marked as 'exception', and the - corresponding error message is output in the server logs. - - :param bool auto_commit: whether to force a commit of the message - status after sending each message (meant - only for processing by the scheduler), - should never be True during normal - transactions (default: False) - :return: True - """ - ir_mail_server = self.pool.get('ir.mail_server') - self.write(cr, uid, ids, {'state': 'outgoing'}, context=context) - for message in self.browse(cr, uid, ids, context=context): - try: - attachments = [] - for attach in message.attachment_ids: - attachments.append((attach.datas_fname, base64.b64decode(attach.datas))) - - body = message.body_html if message.content_subtype == 'html' else message.body_text - body_alternative = None - content_subtype_alternative = None - if message.content_subtype == 'html' and message.body_text: - # we have a plain text alternative prepared, pass it to - # build_message instead of letting it build one - body_alternative = message.body_text - content_subtype_alternative = 'plain' - - # handle destination_partners - partner_ids_email_to = '' - for partner in message.partner_ids: - partner_ids_email_to += '%s ' % (partner.email or '') - message_email_to = '%s %s' % (partner_ids_email_to, message.email_to or '') - - # build an RFC2822 email.message.Message object and send it - # without queuing - msg = ir_mail_server.build_email( - email_from=message.email_from, - email_to=mail_tools_to_email(message_email_to), - subject=message.subject, - body=body, - body_alternative=body_alternative, - email_cc=mail_tools_to_email(message.email_cc), - email_bcc=mail_tools_to_email(message.email_bcc), - reply_to=message.reply_to, - attachments=attachments, message_id=message.message_id, - references = message.references, - object_id=message.res_id and ('%s-%s' % (message.res_id,message.model)), - subtype=message.content_subtype, - subtype_alternative=content_subtype_alternative, - headers=message.headers and ast.literal_eval(message.headers)) - res = ir_mail_server.send_email(cr, uid, msg, - mail_server_id=message.mail_server_id.id, - context=context) - if res: - message.write({'state':'sent', 'message_id': res, 'email_to': message_email_to}) - else: - message.write({'state':'exception', 'email_to': message_email_to}) - message.refresh() - if message.state == 'sent': - self._postprocess_sent_message(cr, uid, message, context=context) - except Exception: - _logger.exception('failed sending mail.message %s', message.id) - message.write({'state':'exception'}) - - if auto_commit == True: - cr.commit() - return True - - - -# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/addons/mail/mail_thread.py b/addons/mail/mail_thread.py index d6e33497d98..d3f80c08513 100644 --- a/addons/mail/mail_thread.py +++ b/addons/mail/mail_thread.py @@ -70,6 +70,19 @@ class mail_thread(osv.Model): _description = 'Email Thread' # TODO: may be we should make it _inherit ir.needaction + def _get_is_subscriber(self, cr, uid, ids, name, args, context=None): + subobj = self.pool.get('mail.subscription') + subids = subobj.search(cr, uid, [ + ('res_model','=',self._name), + ('res_id', 'in', ids), + ('user_id','=',uid)], context=context) + result = dict.fromkeys(ids, False) + for sub in subobj.browse(cr, uid, subids, context=context): + result[res_id] = True + return result + + + def _get_message_data(self, cr, uid, ids, name, args, context=None): res = {} for id in ids: @@ -98,6 +111,8 @@ class mail_thread(osv.Model): return [('id','in',[])] _columns = { + 'message_is_subscriber': fields.function(_get_is_subscriber, + type='boolean', string='Is a Follower'), 'message_follower_ids': fields.one2many('mail.subscription', 'res_id', domain=lambda self: [('res_model','=',self._name)], string='Followers') From 677024e09940a83fed23d7ef24e67206d060331f Mon Sep 17 00:00:00 2001 From: Fabien Pinckaers Date: Wed, 15 Aug 2012 20:44:03 +0200 Subject: [PATCH 020/436] [IMP] refactoring email bzr revid: fp@openerp.com-20120815184403-dtpep7ypwbc6hm5w --- addons/mail/mail_group.py | 13 ++-- addons/mail/mail_group_menu.py | 3 +- addons/mail/mail_group_view.xml | 5 -- addons/mail/mail_mail.py | 51 +++++++-------- addons/mail/mail_message.py | 62 +++---------------- addons/mail/mail_message_view.xml | 6 -- addons/mail/mail_subscription.py | 22 +++---- addons/mail/mail_thread.py | 48 +++----------- addons/mail/res_partner.py | 9 +++ addons/mail/res_users.py | 9 +-- addons/mail/wizard/mail_compose_message.py | 9 ++- .../mail/wizard/mail_compose_message_view.xml | 1 - 12 files changed, 70 insertions(+), 168 deletions(-) diff --git a/addons/mail/mail_group.py b/addons/mail/mail_group.py index e93306aed02..b93a7f814a1 100644 --- a/addons/mail/mail_group.py +++ b/addons/mail/mail_group.py @@ -35,10 +35,9 @@ class mail_group(osv.osv): in common with res.users.group. Additional information on fields: """ - _description = 'Discussion group' _name = 'mail.group' - _inherit = ['mail.thread'] + _inherit = ['mail.thread','ir.needaction'] _inherits = {'mail.alias': 'alias_id', 'ir.ui.menu': 'menu_id'} def _get_image(self, cr, uid, ids, name, args, context=None): @@ -57,9 +56,6 @@ class mail_group(osv.osv): _columns = { 'description': fields.text('Description'), 'menu_id': fields.many2one('ir.ui.menu', string='Related Menu', required=True, ondelete="cascade"), - 'responsible_id': fields.many2one('res.users', string='Responsible', - ondelete='set null', required=True, select=1, - help="Responsible of the group that has all rights on the record."), 'public': fields.selection([('public','Public'),('private','Private'),('groups','Selected Group Only')], 'Privacy', required=True, help='This group is visible by non members. \ Invisible groups can add members through the invite button.'), @@ -105,7 +101,6 @@ class mail_group(osv.osv): _defaults = { 'public': 'groups', 'group_public_id': _get_default_employee_group, - 'responsible_id': (lambda s, cr, uid, ctx: uid), 'image': _get_default_image, 'parent_id': _get_menu_parent, 'alias_domain': False, # always hide alias during creation @@ -116,12 +111,12 @@ class mail_group(osv.osv): user_group_ids = [command[1] for command in group_ids_command if command[0] == 4] user_group_ids += [id for command in group_ids_command if command[0] == 6 for id in command[2]] # retrieve the user member of those groups - user_ids = [] + partner_ids = [] res_groups_obj = self.pool.get('res.groups') for group in res_groups_obj.browse(cr, uid, user_group_ids, context=context): - user_ids += [user.id for user in group.users] + partner_ids += [user.partner_id.id for user in group.users] # subscribe the users - return self.message_subscribe(cr, uid, ids, user_ids, context=context) + return self.message_subscribe(cr, uid, ids, partner_ids, context=context) def create(self, cr, uid, vals, context=None): mail_alias = self.pool.get('mail.alias') diff --git a/addons/mail/mail_group_menu.py b/addons/mail/mail_group_menu.py index 38756f69928..a966118dba8 100644 --- a/addons/mail/mail_group_menu.py +++ b/addons/mail/mail_group_menu.py @@ -31,11 +31,12 @@ class ir_ui_menu(osv.osv): def search(self, cr, uid, args, offset=0, limit=None, order=None, context=None, count=False): ids = super(ir_ui_menu, self).search(cr, uid, args, offset=0, limit=None, order=order, context=context, count=False) + partner_id = self.pool.get('res.users').browse(cr, uid, uid, context=context).partner_id.id subs = self.pool.get('mail.subscription') for menu in self.browse(cr, uid, ids, context=context): if menu.mail_group_id: sub_ids = subs.search(cr, uid, [ - ('user_id','=',uid),('res_model','=','mail.group'), + ('partner_id','=',partner_id),('res_model','=','mail.group'), ('res_id','=',menu.mail_group_id.id) ], context=context) if not sub_ids: diff --git a/addons/mail/mail_group_view.xml b/addons/mail/mail_group_view.xml index dac5333024c..8f1430fbc56 100644 --- a/addons/mail/mail_group_view.xml +++ b/addons/mail/mail_group_view.xml @@ -75,9 +75,6 @@ /> - - -
@@ -96,7 +93,6 @@ - @@ -109,7 +105,6 @@ - diff --git a/addons/mail/mail_mail.py b/addons/mail/mail_mail.py index b960745ec68..b129597c3e8 100644 --- a/addons/mail/mail_mail.py +++ b/addons/mail/mail_mail.py @@ -1,4 +1,15 @@ +import ast +import base64 +import email +import logging +import re +import time +import datetime + +from osv import osv +from osv import fields + class mail_mail(osv.Model): """ Model holding RFC2822 email messages to send. This model also provides @@ -9,8 +20,7 @@ class mail_mail(osv.Model): _description = 'Outgoing Mails' _inherits = {'mail.message': 'message_id'} _columns = { - 'message_id': fields.many2one('mail.message', 'Message', required=True), - # why do we need this on the mail, why not on the company? + 'message_id': fields.many2one('mail.message', 'Message', required=True, ondelete='cascade'), 'mail_server_id': fields.many2one('ir.mail_server', 'Outgoing mail server', readonly=1), 'state': fields.selection([ ('outgoing', 'Outgoing'), @@ -22,34 +32,24 @@ class mail_mail(osv.Model): 'auto_delete': fields.boolean('Auto Delete', help="Permanently delete this email after sending it, to save space"), - # FP Note: to be removed, in attachment if needed - 'original': fields.binary('Original', readonly=1, - help="Original version of the message, as it was sent on the network"), - # End FP Note - - # FP Note: I propose to remove these fields and put email in attachment as an option 'email_from': fields.char('From', size=128, help='Message sender, taken from user preferences.'), 'email_to': fields.text('To', help='Message recipients'), 'email_cc': fields.char('Cc', size=256, help='Carbon copy message recipients'), - 'email_bcc': fields.char('Bcc', size=256, help='Blind carbon copy message recipients'), 'reply_to':fields.char('Reply-To', size=256, help='Preferred response address for the message'), 'content_subtype': fields.char('Message content subtype', size=32, oldname="subtype", readonly=1, help="Type of message, usually 'html' or 'plain', used to select "\ "plain-text or rich-text contents accordingly"), 'body_html': fields.html('Rich-text Contents', help="Rich-text/HTML version of the message"), - } _defaults = { - 'state': 'received', + 'state': 'outgoing', 'content_subtype': 'plain', } - # FP Note: do we need this method ? - # We should just ask to create the message before scheduling it def schedule_with_attach(self, cr, uid, email_from, email_to, subject, body, model=False, type='email', - email_cc=None, email_bcc=None, reply_to=False, partner_ids=None, attachments=None, + email_cc=None, reply_to=False, partner_ids=None, attachments=None, message_id=False, references=False, res_id=False, content_subtype='plain', headers=None, mail_server_id=False, auto_delete=False, context=None): """ Schedule sending a new email message, to be sent the next time the @@ -65,8 +65,6 @@ class mail_mail(osv.Model): in multipart/alternative. :param list email_cc: optional list of string values for CC header (to be joined with commas) - :param list email_bcc: optional list of string values for BCC header - (to be joined with commas) :param string model: optional model name of the document this mail is related to (this will also be used to generate a tracking id, used to match any response related to the same document) @@ -96,7 +94,7 @@ class mail_mail(osv.Model): if partner_ids is None: partner_ids = [] attachment_obj = self.pool.get('ir.attachment') - for param in (email_to, email_cc, email_bcc): + for param in (email_to, email_cc): if param and not isinstance(param, list): param = [param] msg_vals = { @@ -111,7 +109,6 @@ class mail_mail(osv.Model): 'email_from': email_from, 'email_to': email_to and ','.join(email_to) or '', 'email_cc': email_cc and ','.join(email_cc) or '', - 'email_bcc': email_bcc and ','.join(email_bcc) or '', 'partner_ids': partner_ids, 'reply_to': reply_to, 'message_id': message_id, @@ -123,20 +120,18 @@ class mail_mail(osv.Model): 'auto_delete': auto_delete } email_msg_id = self.create(cr, uid, msg_vals, context) - attachment_ids = [] + msg = self.browse(cr, uid, email_msg_id, context) for fname, fcontent in attachments.iteritems(): attachment_data = { 'name': fname, 'datas_fname': fname, 'datas': fcontent and fcontent.encode('base64'), - 'res_model': self._name, - 'res_id': email_msg_id, + 'res_model': 'mail.message', + 'res_id': msg.message_id.id, } - if context.has_key('default_type'): - del context['default_type'] - attachment_ids.append(attachment_obj.create(cr, uid, attachment_data, context)) - if attachment_ids: - self.write(cr, uid, email_msg_id, { 'attachment_ids': [(6, 0, attachment_ids)]}, context=context) + # FP Note: what's this ??? + # if context.has_key('default_type'): + # del context['default_type'] return email_msg_id def mark_outgoing(self, cr, uid, ids, context=None): @@ -187,8 +182,7 @@ class mail_mail(osv.Model): """ if message.auto_delete: self.pool.get('ir.attachment').unlink(cr, uid, - [x.id for x in message.attachment_ids - if x.res_model == self._name and x.res_id == message.id], + [x.id for x in message.attachment_ids], context=context) message.unlink() return True @@ -240,7 +234,6 @@ class mail_mail(osv.Model): body=body, body_alternative=body_alternative, email_cc=mail_tools_to_email(message.email_cc), - email_bcc=mail_tools_to_email(message.email_bcc), reply_to=message.reply_to, attachments=attachments, message_id=message.message_id, references = message.references, diff --git a/addons/mail/mail_message.py b/addons/mail/mail_message.py index 227a9b81f5b..1262cbb9fc7 100644 --- a/addons/mail/mail_message.py +++ b/addons/mail/mail_message.py @@ -21,7 +21,6 @@ # FP Note: can we remove some dependencies ? Use lint -import ast import base64 import dateutil.parser import email @@ -32,12 +31,10 @@ import datetime from email.header import decode_header from email.message import Message -from openerp import SUPERUSER_ID from osv import osv from osv import fields import pytz from tools import DEFAULT_SERVER_DATETIME_FORMAT -from tools.translate import _ import tools _logger = logging.getLogger(__name__) @@ -64,47 +61,6 @@ class mail_message(osv.Model): _description = 'Message' _order = 'id desc' - # FP Note: can we remove these two methods ? - def open_document(self, cr, uid, ids, context=None): - """ Open the message related document. Note that only the document of - ids[0] will be opened. - TODO: how to determine the action to use ? - """ - action_data = False - if not ids: - return action_data - msg = self.browse(cr, uid, ids[0], context=context) - ir_act_window = self.pool.get('ir.actions.act_window') - action_ids = ir_act_window.search(cr, uid, [('res_model', '=', msg.model)], context=context) - if action_ids: - action_data = ir_act_window.read(cr, uid, action_ids[0], context=context) - action_data.update({ - 'domain' : "[('id', '=', %d)]" % (msg.res_id), - 'nodestroy': True, - 'context': {} - }) - return action_data - - def open_attachment(self, cr, uid, ids, context=None): - """ Open the message related attachments. - TODO: how to determine the action to use ? - """ - action_data = False - if not ids: - return action_data - action_pool = self.pool.get('ir.actions.act_window') - messages = self.browse(cr, uid, ids, context=context) - att_ids = [x.id for message in messages for x in message.attachment_ids] - action_ids = action_pool.search(cr, uid, [('res_model', '=', 'ir.attachment')], context=context) - if action_ids: - action_data = action_pool.read(cr, uid, action_ids[0], context=context) - action_data.update({ - 'domain': [('id', 'in', att_ids)], - 'nodestroy': True - }) - return action_data - # END FP Note - def get_record_name(self, cr, uid, ids, name, arg, context=None): result = dict.fromkeys(ids, '') for message in self.browse(cr, uid, ids, context=context): @@ -135,20 +91,18 @@ class mail_message(osv.Model): help="Message type: email for email message, notification for system "\ "message, comment for other messages such as user replies"), - # partner_id should be renamed into author_id + # partner_id should be renamed into author_id, user_id removed 'author_id': fields.many2one('res.partner', 'Author', required=True), - # this is redundant with notifications ? + # this is redundant with notifications ? Yes 'partner_ids': fields.many2many('res.partner', 'mail_message_destination_partner_rel', 'message_id', 'partner_id', 'Destination partners', help="When sending emails through the social network composition wizard"\ "you may choose to send a copy of the mail to partners."), - # 'user_id': fields.many2one('res.users', 'Author', readonly=1), - - # why don't we attach the file to the message using res_id, res_model of the attachment ? - 'attachment_ids': fields.many2many('ir.attachment', 'message_attachment_rel', 'message_id', 'attachment_id', 'Attachments'), + 'attachment_ids': fields.one2many('ir.attachment', 'res_id', 'Attachments' + domain=[('res_model','=','mail.message')]), 'parent_id': fields.many2one('mail.message', 'Parent Message', select=True, ondelete='set null', @@ -189,7 +143,11 @@ class mail_message(osv.Model): cr.execute("""CREATE INDEX mail_message_model_res_id_idx ON mail_message (model, res_id)""") def check(self, cr, uid, ids, mode, context=None): - """Restricts the access to a mail.message, according to referred model + """ + You can access a message if: + - you received it (a notification exists) or + - you can read the related document (res_model, res_id) + If a message is not attached to a document, normal access rights apply. """ if not ids: return @@ -234,7 +192,7 @@ class mail_message(osv.Model): if default is None: default = {} self.check(cr, uid, [id], 'read', context=context) - default.update(message_id=False, original=False, headers=False) + default.update(message_id=False, headers=False) return super(mail_message,self).copy(cr, uid, id, default=default, context=context) def write(self, cr, uid, ids, vals, context=None): diff --git a/addons/mail/mail_message_view.xml b/addons/mail/mail_message_view.xml index 0aa5261d7e7..c50e53c8e75 100644 --- a/addons/mail/mail_message_view.xml +++ b/addons/mail/mail_message_view.xml @@ -123,7 +123,6 @@ - @@ -148,11 +147,8 @@ - - From 1b58af40cc5affbdde948ed323ba4d3e626c9d6d Mon Sep 17 00:00:00 2001 From: Fabien Pinckaers Date: Thu, 16 Aug 2012 09:26:42 +0200 Subject: [PATCH 026/436] [IMP] subscriber --> follower bzr revid: fp@tinyerp.com-20120816072642-614ukk9q5zvrdp6i --- addons/mail/mail_thread.py | 24 ++++++++++----------- addons/mail/static/src/js/mail_followers.js | 20 ++++++++--------- 2 files changed, 22 insertions(+), 22 deletions(-) diff --git a/addons/mail/mail_thread.py b/addons/mail/mail_thread.py index 7d233345315..689bbc2f27f 100644 --- a/addons/mail/mail_thread.py +++ b/addons/mail/mail_thread.py @@ -121,7 +121,7 @@ class mail_thread(osv.Model): _description = 'Email Thread' # TODO: may be we should make it _inherit ir.needaction - def _get_is_subscriber(self, cr, uid, ids, name, args, context=None): + def _get_is_follower(self, cr, uid, ids, name, args, context=None): subobj = self.pool.get('mail.subscription') subids = subobj.search(cr, uid, [ ('res_model','=',self._name), @@ -153,8 +153,8 @@ class mail_thread(osv.Model): for thread in self.browse(cr, uid, ids, context=context): message_ids = thread.message_ids - subscriber_ids = thread.message_follower_ids - res[id]['message_summary'] = "9 %d + %d" % (len(message_ids), len(subscriber_ids)), + follower_ids = thread.message_follower_ids + res[id]['message_summary'] = "9 %d + %d" % (len(message_ids), len(follower_ids)), return res # FP Note: todo @@ -229,7 +229,7 @@ class mail_thread(osv.Model): def message_get_automatic_followers(self, cr, uid, id, record_vals, add_uid=True, fetch_missing=False, context=None): """ Return the command for the many2many follower_ids field to manage - subscribers. Behavior : + followers. Behavior : - get the monitored fields (ex: ['user_id', 'responsible_id']); those fields should be relationships to res.users (#TODO: res.partner) - if this field is in the record_vals: it means it has been modified @@ -237,7 +237,7 @@ class mail_thread(osv.Model): - if this fields is not in record_vals, but fetch_missing paramter is set to True: fetch the value in the record (use: at creation for default values, not present in record_vals) - - if add_uid: add the current user (for example: writer is subscriber) + - if add_uid: add the current user (for example: writer is follower) - generate the command and return it This method has to be used on 1 id, because otherwise it would imply to track which user.id is used for which record.id. @@ -965,25 +965,25 @@ class mail_thread(osv.Model): #------------------------------------------------------ # FP Note: replaced by message_follower_ids - # def message_get_subscribers(self, cr, uid, ids, context=None): + # def message_get_followers(self, cr, uid, ids, context=None): - def message_read_subscribers(self, cr, uid, ids, fields=['id', 'name', 'image_small'], context=None): + def message_read_followers(self, cr, uid, ids, fields=['id', 'name', 'image_small'], context=None): """ Returns the current document followers as a read result. Used mainly for Chatter having only one method to call to have details about users. """ - user_ids = self.message_get_subscribers(cr, uid, ids, context=context) + user_ids = self.message_get_followers(cr, uid, ids, context=context) return self.pool.get('res.users').read(cr, uid, user_ids, fields=fields, context=context) - def message_is_subscriber(self, cr, uid, ids, user_id = None, context=None): - """ Check if uid or user_id (if set) is a subscriber to the current + def message_is_follower(self, cr, uid, ids, user_id = None, context=None): + """ Check if uid or user_id (if set) is a follower to the current document. :param user_id: if set, check is done on user_id; if not set check is done on uid """ sub_user_id = uid if user_id is None else user_id - if sub_user_id in self.message_get_subscribers(cr, uid, ids, context=context): + if sub_user_id in self.message_get_followers(cr, uid, ids, context=context): return True return False @@ -1002,7 +1002,7 @@ class mail_thread(osv.Model): subscription_obj = self.pool.get('mail.subscription') create_ids = [] for id in ids: - already_subscribed_user_ids = self.message_get_subscribers(cr, uid, [id], context=context) + already_subscribed_user_ids = self.message_get_followers(cr, uid, [id], context=context) for user_id in to_subscribe_uids: if user_id in already_subscribed_user_ids: continue create_ids.append(subscription_obj.create(cr, uid, {'res_model': self._name, 'res_id': id, 'user_id': user_id}, context=context)) diff --git a/addons/mail/static/src/js/mail_followers.js b/addons/mail/static/src/js/mail_followers.js index 0e920f73041..87d68a228a9 100644 --- a/addons/mail/static/src/js/mail_followers.js +++ b/addons/mail/static/src/js/mail_followers.js @@ -76,27 +76,27 @@ openerp_mail_followers = function(session, mail) { this.$element.find('div.oe_mail_recthread_aside').hide(); return; } - return this.fetch_subscribers(value_); + return this.fetch_followers(value_); }, - fetch_subscribers: function (value_) { - return this.ds_follow.call('read', [value_ || this.get_value(), ['name', this.params.image]]).then(this.proxy('display_subscribers')); + fetch_followers: function (value_) { + return this.ds_follow.call('read', [value_ || this.get_value(), ['name', this.params.image]]).then(this.proxy('display_followers')); }, /** * Display the followers. - * TODO: replace the is_subscriber check by fields read */ - display_subscribers: function (records) { + * TODO: replace the is_follower check by fields read */ + display_followers: function (records) { var self = this; - this.is_subscriber = false; + this.is_follower = false; var user_list = this.$element.find('ul.oe_mail_followers_display').empty(); this.$element.find('div.oe_mail_recthread_followers h4').html(this.params.title + ' (' + records.length + ')'); _(records).each(function (record) { - if (record.id == self.session.uid) { self.is_subscriber = true; } + if (record.id == self.session.uid) { self.is_follower = true; } record.avatar_url = mail.ChatterUtils.get_image(self.session.prefix, self.session.session_id, 'res.users', 'image_small', record.id); $(session.web.qweb.render('mail.followers.partner', {'record': record})).appendTo(user_list); }); - if (this.is_subscriber) { + if (this.is_follower) { this.$element.find('button.oe_mail_button_follow').hide(); this.$element.find('button.oe_mail_button_unfollow').show(); } else { @@ -113,8 +113,8 @@ openerp_mail_followers = function(session, mail) { }, do_toggle_followers: function () { - this.params.see_subscribers = ! this.params.see_subscribers; - if (this.params.see_subscribers) { this.$element.find('button.oe_mail_button_followers').html('Hide followers'); } + this.params.see_followers = ! this.params.see_followers; + if (this.params.see_followers) { this.$element.find('button.oe_mail_button_followers').html('Hide followers'); } else { this.$element.find('button.oe_mail_button_followers').html('Show followers'); } this.$element.find('div.oe_mail_recthread_followers').toggle(); }, From a2b7f82a81513d39e43b8e48c0c06903d14aef2e Mon Sep 17 00:00:00 2001 From: Fabien Pinckaers Date: Thu, 16 Aug 2012 11:02:43 +0200 Subject: [PATCH 027/436] [IMP] no auto_follow, we will use auto-compose instead on some actions bzr revid: fp@tinyerp.com-20120816090243-nzu6ounygswiceqi --- addons/crm/crm_lead.py | 5 -- addons/hr_holidays/hr_holidays.py | 6 --- addons/hr_recruitment/hr_recruitment.py | 5 -- addons/mail/__init__.py | 1 - addons/mail/mail_followers.py | 18 +++---- addons/mail/mail_group.py | 16 +----- addons/mail/mail_group_view.xml | 4 +- addons/mail/mail_mail.py | 1 + addons/mail/mail_thread.py | 72 ------------------------- addons/mrp/mrp.py | 5 -- addons/project/project.py | 5 -- addons/project_issue/project_issue.py | 5 -- addons/purchase/purchase.py | 5 -- 13 files changed, 12 insertions(+), 136 deletions(-) diff --git a/addons/crm/crm_lead.py b/addons/crm/crm_lead.py index 146417a7abb..9fd81712b92 100644 --- a/addons/crm/crm_lead.py +++ b/addons/crm/crm_lead.py @@ -859,11 +859,6 @@ class crm_lead(base_stage, osv.osv): # OpenChatter methods and notifications # ---------------------------------------- - def message_get_monitored_follower_fields(self, cr, uid, ids, context=None): - """ Add 'user_id' to the monitored fields """ - res = super(crm_lead, self).message_get_monitored_follower_fields(cr, uid, ids, context=context) - return res + ['user_id'] - def stage_set_send_note(self, cr, uid, ids, stage_id, context=None): """ Override of the (void) default notification method. """ stage_name = self.pool.get('crm.case.stage').name_get(cr, uid, [stage_id], context=context)[0][1] diff --git a/addons/hr_holidays/hr_holidays.py b/addons/hr_holidays/hr_holidays.py index e888f9b5c3b..47ca1246240 100644 --- a/addons/hr_holidays/hr_holidays.py +++ b/addons/hr_holidays/hr_holidays.py @@ -364,12 +364,6 @@ class hr_holidays(osv.osv): dom = ['|'] + dom + [ ('state','=','validate1') ] return dom - def message_get_monitored_follower_fields(self, cr, uid, ids, context=None): - """ Add 'user_id' and 'manager' to the monitored fields """ - res = super(hr_holidays, self).message_get_monitored_follower_fields(cr, uid, ids, context=context) - # TODO: add manager - return res + ['user_id'] - def create_notificate(self, cr, uid, ids, context=None): for obj in self.browse(cr, uid, ids, context=context): self.message_append_note(cr, uid, ids, _('System notification'), diff --git a/addons/hr_recruitment/hr_recruitment.py b/addons/hr_recruitment/hr_recruitment.py index 0ace2574d65..3d0b704b36d 100644 --- a/addons/hr_recruitment/hr_recruitment.py +++ b/addons/hr_recruitment/hr_recruitment.py @@ -461,11 +461,6 @@ class hr_applicant(base_stage, osv.Model): # OpenChatter methods and notifications # ------------------------------------------------------- - def message_get_monitored_follower_fields(self, cr, uid, ids, context=None): - """ Add 'user_id' to the monitored fields """ - res = super(hr_applicant, self).message_get_monitored_follower_fields(cr, uid, ids, context=context) - return res + ['user_id'] - def stage_set_send_note(self, cr, uid, ids, stage_id, context=None): """ Override of the (void) default notification method. """ if not stage_id: return True diff --git a/addons/mail/__init__.py b/addons/mail/__init__.py index 49ee9e18995..cba9cfe1fe7 100644 --- a/addons/mail/__init__.py +++ b/addons/mail/__init__.py @@ -25,7 +25,6 @@ import mail_mail import mail_followers import mail_thread import mail_group -import ir_needaction import res_partner import res_users import report diff --git a/addons/mail/mail_followers.py b/addons/mail/mail_followers.py index 24ab4a56b4d..0073ee81e41 100644 --- a/addons/mail/mail_followers.py +++ b/addons/mail/mail_followers.py @@ -24,7 +24,7 @@ from osv import fields class mail_followers(osv.Model): """ mail_followers holds the data related to the follow mechanism inside - OpenERP. Users can choose to follow documents (records) of any kind that + OpenERP. Partners can choose to follow documents (records) of any kind that inherits from mail.thread. Following documents allow to receive notifications for new messages. A subscription is characterized by: @@ -32,28 +32,26 @@ class mail_followers(osv.Model): :param: res_id: ID of resource (may be 0 for every objects) """ _name = 'mail.followers' - _rec_name = 'id' + _rec_name = 'partner_id' _log_access = False - _order = 'res_model asc' - _description = 'Mail Document Followers' + _description = 'Document Followers' _columns = { 'res_model': fields.char('Related Document Model', size=128, required=True, select=1, help='Model of the followed resource'), 'res_id': fields.integer('Related Document ID', select=1, help='Id of the followed resource'), - 'partner_id': fields.many2one('res.partner', string='Related User', + 'partner_id': fields.many2one('res.partner', string='Related Partner', ondelete='cascade', required=True, select=1), } class mail_notification(osv.Model): - """ mail_notification is a relational table modeling messages pushed to users. + """ mail_notification is a relational table modeling messages pushed to partners. """ _name = 'mail.notification' - _rec_name = 'id' + _rec_name = 'partner_id' _log_access = False - _order = 'message_id desc' - _description = 'Mail notification' + _description = 'Notifications' _columns = { 'partner_id': fields.many2one('res.partner', string='Contact', ondelete='cascade', required=True, select=1), @@ -103,9 +101,9 @@ class mail_notification(osv.Model): if email_to not in towrite['email_to']: towrite['email_to'] = towrite['email_to'] + ', ' + email_to + if towrite.get('state', False): if towrite.get('subject', False): towrite['subject'] = msg.name_get(cr, uid, [msg.id], context=context)[0][1] - if towrite.get('state', False): towrite['message_id'] = msg.id self.pool.get('mail.mail').create(cr, uid, towrite, context=context) return True diff --git a/addons/mail/mail_group.py b/addons/mail/mail_group.py index 6db6308d040..7de74db27c3 100644 --- a/addons/mail/mail_group.py +++ b/addons/mail/mail_group.py @@ -67,7 +67,7 @@ class mail_group(osv.Model): 'image': fields.binary("Photo", help="This field holds the image used as photo for the "\ "user. The image is base64 encoded, and PIL-supported. "\ - "It is limited to a 12024x1024 px image."), + "It is limited to a 1024x1024 px image."), 'image_medium': fields.function(_get_image, fnct_inv=_set_image, string="Medium-sized photo", type="binary", multi="_get_image", store = { @@ -165,17 +165,3 @@ class mail_group(osv.Model): self._subscribe_user_with_group_m2m_command(cr, uid, ids, vals.get('group_ids'), context=context) return super(mail_group, self).write(cr, uid, ids, vals, context=context) - def action_group_join(self, cr, uid, ids, context=None): - return self.message_subscribe(cr, uid, ids, context=context) - - def action_group_leave(self, cr, uid, ids, context=None): - return self.message_unsubscribe(cr, uid, ids, context=context) - - # ---------------------------------------- - # OpenChatter methods and notifications - # ---------------------------------------- - - def message_get_monitored_follower_fields(self, cr, uid, ids, context=None): - """ Add 'responsible_id' to the monitored fields """ - res = super(mail_group, self).message_get_monitored_follower_fields(cr, uid, ids, context=context) - return res + ['responsible_id'] diff --git a/addons/mail/mail_group_view.xml b/addons/mail/mail_group_view.xml index 1dac0ab9408..b1d7971e2ac 100644 --- a/addons/mail/mail_group_view.xml +++ b/addons/mail/mail_group_view.xml @@ -31,8 +31,8 @@

diff --git a/addons/mail/mail_mail.py b/addons/mail/mail_mail.py index 1112bf8d76a..4599209c305 100644 --- a/addons/mail/mail_mail.py +++ b/addons/mail/mail_mail.py @@ -22,6 +22,7 @@ class mail_mail(osv.Model): _columns = { 'message_id': fields.many2one('mail.message', 'Message', required=True, ondelete='cascade'), 'mail_server_id': fields.many2one('ir.mail_server', 'Outgoing mail server', readonly=1), + 'subject': fields.char('Subject', size=128), 'state': fields.selection([ ('outgoing', 'Outgoing'), ('sent', 'Sent'), diff --git a/addons/mail/mail_thread.py b/addons/mail/mail_thread.py index 689bbc2f27f..a86eaec2896 100644 --- a/addons/mail/mail_thread.py +++ b/addons/mail/mail_thread.py @@ -194,27 +194,6 @@ class mail_thread(osv.Model): self.message_subscribe(cr, uid, [thread_id], [uid], context=context) return thread_id - # FP Note: not sure we need this? - #def write(self, cr, uid, ids, vals, context=None): - # """ Override of write to subscribe : - # - the writer - # - followers given by the monitored fields - # """ - # if isinstance(ids, (int, long)): - # ids = [ids] - # for id in ids: - # # copy original vals because we are going to modify it - # specific_vals = dict(vals) - # # we modify followers: do not subscribe the uid - # if specific_vals.get('message_follower_ids'): - # followers_command = self.message_get_automatic_followers(cr, uid, id, specific_vals, add_uid=False, context=context) - # specific_vals['message_follower_ids'] += followers_command - # else: - # followers_command = self.message_get_automatic_followers(cr, uid, id, specific_vals, context=context) - # specific_vals['message_follower_ids'] = followers_command - # write_res = super(mail_thread, self).write(cr, uid, ids, specific_vals, context=context) - # return True - def unlink(self, cr, uid, ids, context=None): """Override unlink, to automatically delete messages that are linked with res_model and res_id, not through @@ -227,51 +206,6 @@ class mail_thread(osv.Model): msg_obj.unlink(cr, uid, msg_to_del_ids, context=context) return super(mail_thread, self).unlink(cr, uid, ids, context=context) - def message_get_automatic_followers(self, cr, uid, id, record_vals, add_uid=True, fetch_missing=False, context=None): - """ Return the command for the many2many follower_ids field to manage - followers. Behavior : - - get the monitored fields (ex: ['user_id', 'responsible_id']); those - fields should be relationships to res.users (#TODO: res.partner) - - if this field is in the record_vals: it means it has been modified - thus add its value to the followers - - if this fields is not in record_vals, but fetch_missing paramter - is set to True: fetch the value in the record (use: at creation - for default values, not present in record_vals) - - if add_uid: add the current user (for example: writer is follower) - - generate the command and return it - This method has to be used on 1 id, because otherwise it would imply - to track which user.id is used for which record.id. - - :param record_vals: values given to the create method of the new - record, or values updated in a write. - :param monitored_fields: a list of fields that are monitored. Those - fields must be many2one fields to the res.users model. - :param fetch_missing: is set to True, the method will read the - record to find values that are not present in record_vals. - - #TODO : UPDATE WHEN MERGING TO PARTNERS - """ - # get monitored fields - monitored_fields = self.message_get_monitored_follower_fields(cr, uid, [id], context=context) - modified_fields = [field for field in monitored_fields if field in record_vals.iterkeys()] - other_fields = [field for field in monitored_fields if field not in record_vals.iterkeys()] if fetch_missing else [] - # for each monitored field: if in record_vals, it has been modified/added - follower_ids = [] - for field in modified_fields: - # do not add 'False' - if record_vals.get(fields): - follower_ids.append(record_vals.get(field)) - # for other fields: read in record if fetch_missing (otherwise list is void) - for field in other_fields: - record = self.browse(cr, uid, id, context=context) - value = getattr(record, field) - if value: - follower_ids.append(value) - # add uid if asked and not already present - if add_uid and uid not in follower_ids: - follower_ids.append(uid) - return self.message_subscribe_get_command(cr, uid, follower_ids, context=context) - #------------------------------------------------------ # mail.message wrappers and tools #------------------------------------------------------ @@ -987,12 +921,6 @@ class mail_thread(osv.Model): return True return False - def message_get_monitored_follower_fields(self, cr, uid, ids, context=None): - """ Returns a list of fields containing a res.user.id. Those fields - will be checked to automatically subscribe those users. - """ - return [] - def message_subscribe(self, cr, uid, ids, partner_ids=None, context=None): """ :param user_ids: a list of user_ids; if not set, subscribe diff --git a/addons/mrp/mrp.py b/addons/mrp/mrp.py index 7b8442b4c46..16c66655b3c 100644 --- a/addons/mrp/mrp.py +++ b/addons/mrp/mrp.py @@ -1046,11 +1046,6 @@ class mrp_production(osv.osv): # OpenChatter methods and notifications # --------------------------------------------------- - def message_get_monitored_follower_fields(self, cr, uid, ids, context=None): - """ Add 'user_id' to the monitored fields """ - res = super(mrp_production, self).message_get_monitored_follower_fields(cr, uid, ids, context=context) - return res + ['user_id'] - def create_send_note(self, cr, uid, ids, context=None): self.message_append_note(cr, uid, ids, body=_("Manufacturing order has been created."), context=context) return True diff --git a/addons/project/project.py b/addons/project/project.py index 1a8f8c8bfe5..caea3c6f23b 100644 --- a/addons/project/project.py +++ b/addons/project/project.py @@ -514,11 +514,6 @@ def Project(): # OpenChatter methods and notifications # ------------------------------------------------ - def message_get_monitored_follower_fields(self, cr, uid, ids, context=None): - """ Add 'user_id' to the monitored fields """ - res = super(project, self).message_get_monitored_follower_fields(cr, uid, ids, context=context) - return res + ['user_id'] - def create(self, cr, uid, vals, context=None): if context is None: context = {} # Prevent double project creation when 'use_tasks' is checked! diff --git a/addons/project_issue/project_issue.py b/addons/project_issue/project_issue.py index 5c440976b5c..a8a1e617ecb 100644 --- a/addons/project_issue/project_issue.py +++ b/addons/project_issue/project_issue.py @@ -499,11 +499,6 @@ class project_issue(base_stage, osv.osv): # OpenChatter methods and notifications # ------------------------------------------------------- - def message_get_monitored_follower_fields(self, cr, uid, ids, context=None): - """ Add 'user_id' to the monitored fields """ - res = super(project_issue, self).message_get_monitored_follower_fields(cr, uid, ids, context=context) - return res + ['user_id'] - def stage_set_send_note(self, cr, uid, ids, stage_id, context=None): """ Override of the (void) default notification method. """ stage_name = self.pool.get('project.task.type').name_get(cr, uid, [stage_id], context=context)[0][1] diff --git a/addons/purchase/purchase.py b/addons/purchase/purchase.py index 1d904383718..350d40526ab 100644 --- a/addons/purchase/purchase.py +++ b/addons/purchase/purchase.py @@ -734,11 +734,6 @@ class purchase_order(osv.osv): def needaction_domain_get(self, cr, uid, ids, context=None): return [('state','=','draft')] - def message_get_monitored_follower_fields(self, cr, uid, ids, context=None): - """ Add 'validator' to the monitored fields """ - res = super(purchase_order, self).message_get_monitored_follower_fields(cr, uid, ids, context=context) - return res + ['validator'] - def create_send_note(self, cr, uid, ids, context=None): return self.message_append_note(cr, uid, ids, body=_("Request for quotation created."), context=context) From c7c1cb1037b755a52b8c00a200bc51cc6b29a644 Mon Sep 17 00:00:00 2001 From: Fabien Pinckaers Date: Thu, 16 Aug 2012 11:26:16 +0200 Subject: [PATCH 028/436] [IMP] renaming message_state to message_unread bzr revid: fp@tinyerp.com-20120816092616-jnoj0d18rw1ifqth --- addons/base_calendar/crm_meeting_view.xml | 6 +- addons/crm/crm_lead_view.xml | 16 ++--- addons/event/event_view.xml | 12 ++-- addons/hr_recruitment/hr_recruitment_view.xml | 6 +- addons/mail/doc/mail_state.rst | 8 +-- addons/mail/mail_group.py | 2 + addons/mail/mail_mail.py | 8 +-- addons/mail/mail_message.py | 10 +-- addons/mail/mail_thread.py | 65 ++----------------- addons/mrp/mrp_view.xml | 4 +- addons/project/project_view.xml | 14 ++-- addons/project_issue/project_issue_view.xml | 10 +-- addons/purchase/purchase_view.xml | 6 +- .../purchase_requisition_view.xml | 4 +- addons/sale/sale_view.xml | 6 +- 15 files changed, 59 insertions(+), 118 deletions(-) diff --git a/addons/base_calendar/crm_meeting_view.xml b/addons/base_calendar/crm_meeting_view.xml index 9776d66b839..3dffcd61520 100644 --- a/addons/base_calendar/crm_meeting_view.xml +++ b/addons/base_calendar/crm_meeting_view.xml @@ -244,14 +244,14 @@ CRM - Meetings Tree crm.meeting - - + @@ -288,7 +288,7 @@ - + diff --git a/addons/crm/crm_lead_view.xml b/addons/crm/crm_lead_view.xml index e47e0cafcad..4109bca5b17 100644 --- a/addons/crm/crm_lead_view.xml +++ b/addons/crm/crm_lead_view.xml @@ -246,7 +246,7 @@ Leads crm.lead - + @@ -262,7 +262,7 @@ - + @@ -295,7 +295,7 @@ - +
    @@ -341,7 +341,7 @@
@@ -364,7 +364,7 @@ - + @@ -544,7 +544,7 @@ Opportunities Tree crm.lead - + @@ -562,7 +562,7 @@ - +
@@ -577,7 +577,7 @@ - + diff --git a/addons/event/event_view.xml b/addons/event/event_view.xml index bfed55b17cf..29e3afb04b4 100644 --- a/addons/event/event_view.xml +++ b/addons/event/event_view.xml @@ -208,7 +208,7 @@ event.event.tree event.event - + @@ -219,7 +219,7 @@ - + @@ -326,7 +326,7 @@ - + @@ -421,7 +421,7 @@ event.registration.tree event.registration - + @@ -431,7 +431,7 @@ - + @@ -516,7 +516,7 @@ - + diff --git a/addons/hr_recruitment/hr_recruitment_view.xml b/addons/hr_recruitment/hr_recruitment_view.xml index 548b4f93ead..59d4993bca3 100644 --- a/addons/hr_recruitment/hr_recruitment_view.xml +++ b/addons/hr_recruitment/hr_recruitment_view.xml @@ -76,8 +76,8 @@ Applicants hr.applicant - - + + @@ -205,7 +205,7 @@ - + diff --git a/addons/mail/doc/mail_state.rst b/addons/mail/doc/mail_state.rst index 527a31ba835..fc2772bf094 100644 --- a/addons/mail/doc/mail_state.rst +++ b/addons/mail/doc/mail_state.rst @@ -1,15 +1,15 @@ .. _mail_state: -message_state +message_unread ============= -``message_state`` is a boolean field that states whether the document +``message_unread`` is a boolean field that states whether the document has unread messages. In previous versions, some documents were going back to ``pending`` state when receiving an email through the mail gateway. Now the state related to messages differs from the state or stage of the document itself. -message_state and need action mechanism +message_unread and need action mechanism +++++++++++++++++++++++++++++++++++++++ The ``mail`` module introduces a default behavior for the need_action @@ -23,6 +23,6 @@ mechanism [REF]. """ result = super(ir_needaction_mixin, self).get_needaction_user_ids(cr, uid, ids, context=context) for obj in self.browse(cr, uid, ids, context=context): - if obj.message_state == False and obj.user_id: + if obj.message_unread == False and obj.user_id: result[obj.id].append(obj.user_id.id) return result diff --git a/addons/mail/mail_group.py b/addons/mail/mail_group.py index 7de74db27c3..0ff6c3f798d 100644 --- a/addons/mail/mail_group.py +++ b/addons/mail/mail_group.py @@ -105,6 +105,8 @@ class mail_group(osv.Model): 'alias_domain': False, # always hide alias during creation } + # FP Note: code to be improved. Check we have a code for res.users + # when we give them a new group. def _subscribe_user_with_group_m2m_command(self, cr, uid, ids, group_ids_command, context=None): # form: {'group_ids': [(3, 10), (3, 3), (4, 10), (4, 3)]} or {'group_ids': [(6, 0, [ids]} user_group_ids = [command[1] for command in group_ids_command if command[0] == 4] diff --git a/addons/mail/mail_mail.py b/addons/mail/mail_mail.py index 4599209c305..8d53793341b 100644 --- a/addons/mail/mail_mail.py +++ b/addons/mail/mail_mail.py @@ -18,9 +18,9 @@ class mail_mail(osv.Model): _name = 'mail.mail' _description = 'Outgoing Mails' - _inherits = {'mail.message': 'message_id'} + _inherits = {'mail.message': 'mail_message_id'} _columns = { - 'message_id': fields.many2one('mail.message', 'Message', required=True, ondelete='cascade'), + 'mail_message_id': fields.many2one('mail.message', 'Message', required=True, ondelete='cascade'), 'mail_server_id': fields.many2one('ir.mail_server', 'Outgoing mail server', readonly=1), 'subject': fields.char('Subject', size=128), 'state': fields.selection([ @@ -32,7 +32,7 @@ class mail_mail(osv.Model): ], 'Status', readonly=True), 'auto_delete': fields.boolean('Auto Delete', help="Permanently delete this email after sending it, to save space"), - + 'references': fields.text('References', help='Message references, such as identifiers of previous messages', readonly=1), 'email_from': fields.char('From', size=128, help='Message sender, taken from user preferences.'), 'email_to': fields.text('To', help='Message recipients'), 'email_cc': fields.char('Cc', size=256, help='Carbon copy message recipients'), @@ -129,7 +129,7 @@ class mail_mail(osv.Model): 'datas_fname': fname, 'datas': fcontent and fcontent.encode('base64'), 'res_model': 'mail.message', - 'res_id': msg.message_id.id, + 'res_id': msg.mail_message_id.id, } # FP Note: what's this ??? # if context.has_key('default_type'): diff --git a/addons/mail/mail_message.py b/addons/mail/mail_message.py index c6e901d98e7..3a942bf53a7 100644 --- a/addons/mail/mail_message.py +++ b/addons/mail/mail_message.py @@ -83,7 +83,6 @@ class mail_message(osv.Model): res.append((message.id, name)) return res - _columns = { # should we keep a distinction between email and comment ? 'type': fields.selection([ @@ -105,26 +104,20 @@ class mail_message(osv.Model): 'parent_id': fields.many2one('mail.message', 'Parent Message', select=True, ondelete='set null', help="Initial thread message."), - 'child_ids': fields.one2many('mail.message', 'parent_id', 'Child Messages'), - 'model': fields.char('Related Document Model', size=128, select=1), 'res_id': fields.integer('Related Document ID', select=1), 'record_name': fields.function(get_record_name, type='string', string='Message Record Name', help="Name get of the related document."), - 'subject': fields.char('Subject', size=128), 'date': fields.datetime('Date'), - - 'references': fields.text('References', help='Message references, such as identifiers of previous messages', readonly=1), 'message_id': fields.char('Message-Id', size=256, help='Message unique identifier', select=1, readonly=1), - 'body': fields.text('Content', required=True), } _defaults = { 'type': 'email', - 'date': (lambda *a: fields.datetime.now()), + 'date': lambda *a: fields.datetime.now(), } #------------------------------------------------------ @@ -211,6 +204,7 @@ class mail_message(osv.Model): self.check(cr, uid, ids, 'unlink', context=context) return super(mail_message, self).unlink(cr, uid, ids, context) + # FP Note: to review def parse_message(self, message, save_original=False, context=None): """Parses a string or email.message.Message representing an RFC-2822 email, and returns a generic dict holding the diff --git a/addons/mail/mail_thread.py b/addons/mail/mail_thread.py index a86eaec2896..7cdaa70f5de 100644 --- a/addons/mail/mail_thread.py +++ b/addons/mail/mail_thread.py @@ -39,59 +39,6 @@ _logger = logging.getLogger(__name__) def decode_header(message, header, separator=' '): return separator.join(map(decode,message.get_all(header, []))) -class many2many_reference(fields.many2many): - """ many2many_reference is an override of fields.many2many. It manages - many2many-like table where one id is given by two fields, res_model - and res_id. - """ - - def _get_query_and_where_params(self, cr, model, ids, values, where_params): - """ Add in where: - - mail_followers.res_model = 'crm.lead' - """ - query = 'SELECT %(rel)s.%(id2)s, %(rel)s.%(id1)s \ - FROM %(rel)s, %(from_c)s \ - WHERE %(rel)s.%(id1)s IN %%s \ - AND %(rel)s.%(id2)s = %(tbl)s.id \ - AND %(rel)s.res_model = %%s \ - %(where_c)s \ - %(order_by)s \ - %(limit)s \ - OFFSET %(offset)d' \ - % values - where_params = [model._name] + where_params - return query, where_params - - def set(self, cr, model, id, name, values, user=None, context=None): - """ Override to add the res_model field in queries. """ - if not values: return - rel, id1, id2 = self._sql_names(model) - obj = model.pool.get(self._obj) - for act in values: - if not (isinstance(act, list) or isinstance(act, tuple)) or not act: - continue - if act[0] == 0: - idnew = obj.create(cr, user, act[2], context=context) - cr.execute('INSERT INTO '+rel+' ('+id1+','+id2+',res_model) VALUES (%s,%s,%s)', (id, idnew, model._name)) - elif act[0] == 3: - cr.execute('DELETE FROM '+rel+' WHERE '+id1+'=%s AND '+id2+'=%s AND res_model=%s', (id, act[1], model._name)) - elif act[0] == 4: - # following queries are in the same transaction - so should be relatively safe - cr.execute('SELECT 1 FROM '+rel+' WHERE '+id1+'=%s AND '+id2+'=%s AND res_model=%s', (id, act[1], model._name)) - if not cr.fetchone(): - cr.execute('INSERT INTO '+rel+' ('+id1+','+id2+',res_model) VALUES (%s,%s,%s)', (id, act[1], model._name)) - elif act[0] == 6: - d1, d2,tables = obj.pool.get('ir.rule').domain_get(cr, user, obj._name, context=context) - if d1: - d1 = ' and ' + ' and '.join(d1) - else: - d1 = '' - cr.execute('DELETE FROM '+rel+' WHERE '+id1+'=%s AND res_model=%s AND '+id2+' IN (SELECT '+rel+'.'+id2+' FROM '+rel+', '+','.join(tables)+' WHERE '+rel+'.'+id1+'=%s AND '+rel+'.'+id2+' = '+obj._table+'.id '+ d1 +')', [id, model._name, id]+d2) - for act_nbr in act[2]: - cr.execute('INSERT INTO '+rel+' ('+id1+','+id2+',res_model) VALUES (%s,%s,%s)', (id, act_nbr, model._name)) - else: - return super(many2many_reference, self).set(cr, model, id, name, values, user, context) - class mail_thread(osv.Model): '''Mixin model, meant to be inherited by any model that needs to act as a discussion topic on which messages can be attached. @@ -132,13 +79,11 @@ class mail_thread(osv.Model): result[res_id] = True return result - - def _get_message_data(self, cr, uid, ids, name, args, context=None): res = {} for id in ids: res[id] = { - 'message_state': False, + 'message_unread': False, 'message_Summary': '' } nobj = self.pool.get('mail.notification') @@ -149,7 +94,7 @@ class mail_thread(osv.Model): ('read','=',False) ], context=context) for notif in nobj.browse(cr, uid, nids, context=context): - res[notif.message_id.id]['message_state'] = True + res[notif.message_id.id]['message_unread'] = True for thread in self.browse(cr, uid, ids, context=context): message_ids = thread.message_ids @@ -171,7 +116,7 @@ class mail_thread(osv.Model): domain=lambda self: [('model','=',self._name)], string='Related Messages', help="All messages related to the current document."), - 'message_state': fields.function(_get_message_data, fnct_search=_search_state, 'Message Read', + 'message_unread': fields.function(_get_message_data, fnct_search=_search_state, 'Message Read', help="When checked, new messages require your attention.", multi="_get_message_data"), 'message_summary': fields.function(_get_message_data, method=True, @@ -225,7 +170,7 @@ class mail_thread(osv.Model): def _needaction_domain_get(self, cr, uid, context={}): if self._needaction: - return [('message_state','=',True)] + return [('message_unread','=',True)] return [] #------------------------------------------------------ @@ -969,7 +914,7 @@ class mail_thread(osv.Model): # Thread_state #------------------------------------------------------ - # FP Note: this should be a invert function on message_state field + # FP Note: this should be a invert function on message_unread field def message_mark_as_read(self, cr, uid, ids, context=None): """ Set as read. """ notobj = self.pool.get('mail.notification') diff --git a/addons/mrp/mrp_view.xml b/addons/mrp/mrp_view.xml index 02cf1f19149..1a63a3dab14 100644 --- a/addons/mrp/mrp_view.xml +++ b/addons/mrp/mrp_view.xml @@ -574,8 +574,8 @@ mrp.production.tree mrp.production - - + + diff --git a/addons/project/project_view.xml b/addons/project/project_view.xml index 395a3c63219..3a1d21324f3 100644 --- a/addons/project/project_view.xml +++ b/addons/project/project_view.xml @@ -160,7 +160,7 @@ - + @@ -185,9 +185,9 @@ project.project child_ids - + - + @@ -506,7 +506,7 @@ - + @@ -571,8 +571,8 @@ project.task - - + + @@ -632,7 +632,7 @@ - + diff --git a/addons/project_issue/project_issue_view.xml b/addons/project_issue/project_issue_view.xml index 6dc5ed946f1..be732c8c9a6 100644 --- a/addons/project_issue/project_issue_view.xml +++ b/addons/project_issue/project_issue_view.xml @@ -170,8 +170,8 @@ Project Issue Tracker Tree project.issue - - + + @@ -196,7 +196,7 @@ - + @@ -310,9 +310,9 @@ Project Issue- Feature Tracker Tree project.issue - + - + diff --git a/addons/purchase/purchase_view.xml b/addons/purchase/purchase_view.xml index 0cdc8d2d206..158ef53f559 100644 --- a/addons/purchase/purchase_view.xml +++ b/addons/purchase/purchase_view.xml @@ -298,7 +298,7 @@ - + @@ -323,8 +323,8 @@ purchase.order.tree purchase.order - - + + diff --git a/addons/purchase_requisition/purchase_requisition_view.xml b/addons/purchase_requisition/purchase_requisition_view.xml index 2e9f4104342..34fc8dcb540 100644 --- a/addons/purchase_requisition/purchase_requisition_view.xml +++ b/addons/purchase_requisition/purchase_requisition_view.xml @@ -112,8 +112,8 @@ purchase.requisition.tree purchase.requisition - - + + diff --git a/addons/sale/sale_view.xml b/addons/sale/sale_view.xml index 07abe9c4318..1e7e3fc1581 100644 --- a/addons/sale/sale_view.xml +++ b/addons/sale/sale_view.xml @@ -133,8 +133,8 @@ sale.order 2 - - + + @@ -363,7 +363,7 @@ - + From a1182d97ed749ddf175d1c6f9f88ca3dc8ebdb1b Mon Sep 17 00:00:00 2001 From: "Twinkle Christian (OpenERP)" Date: Thu, 16 Aug 2012 15:11:23 +0530 Subject: [PATCH 029/436] [IMP]Remove or bzr revid: tch@tinyerp.com-20120816094123-f1yiealw1p59j95n --- addons/web/static/src/xml/base.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addons/web/static/src/xml/base.xml b/addons/web/static/src/xml/base.xml index 1728bfb584b..96adbd42d2e 100644 --- a/addons/web/static/src/xml/base.xml +++ b/addons/web/static/src/xml/base.xml @@ -678,7 +678,7 @@ - or Import + Import From 73827c55259f81223f9629f8f1925042043dabce Mon Sep 17 00:00:00 2001 From: Fabien Pinckaers Date: Thu, 16 Aug 2012 12:18:48 +0200 Subject: [PATCH 030/436] [IMP] mail cleaning bzr revid: fp@tinyerp.com-20120816101848-0l3wyu98zjmtkfks --- addons/mail/mail_group.py | 25 ++++++--------- addons/mail/mail_mail.py | 1 + addons/mail/mail_message.py | 2 -- addons/mail/mail_thread.py | 58 ++++++++++++++++++++-------------- addons/mail/res_partner.py | 13 ++------ addons/mail/res_users.py | 62 ++++++++++++++++++------------------- 6 files changed, 80 insertions(+), 81 deletions(-) diff --git a/addons/mail/mail_group.py b/addons/mail/mail_group.py index 0ff6c3f798d..2c8b9450439 100644 --- a/addons/mail/mail_group.py +++ b/addons/mail/mail_group.py @@ -107,17 +107,12 @@ class mail_group(osv.Model): # FP Note: code to be improved. Check we have a code for res.users # when we give them a new group. - def _subscribe_user_with_group_m2m_command(self, cr, uid, ids, group_ids_command, context=None): - # form: {'group_ids': [(3, 10), (3, 3), (4, 10), (4, 3)]} or {'group_ids': [(6, 0, [ids]} - user_group_ids = [command[1] for command in group_ids_command if command[0] == 4] - user_group_ids += [id for command in group_ids_command if command[0] == 6 for id in command[2]] - # retrieve the user member of those groups - partner_ids = [] - res_groups_obj = self.pool.get('res.groups') - for group in res_groups_obj.browse(cr, uid, user_group_ids, context=context): - partner_ids += [user.partner_id.id for user in group.users] - # subscribe the users - return self.message_subscribe(cr, uid, ids, partner_ids, context=context) + def _subscribe_users(self, cr, uid, ids, context=None): + for mail_group in self.browse(cr, uid, ids, context=context): + partner_ids = [] + for group in mail_group.group_ids: + partner_ids += [user.partner_id.id for user in group.users] + self.message_subscribe(cr, uid, ids, partner_ids, context=context) def create(self, cr, uid, vals, context=None): mail_alias = self.pool.get('mail.alias') @@ -150,8 +145,7 @@ class mail_group(osv.Model): mail_alias.write(cr, uid, [vals['alias_id']], {"alias_force_thread_id": mail_group_id}, context) if vals.get('group_ids'): - self._subscribe_user_with_group_m2m_command(cr, uid, [mail_group_id], vals.get('group_ids'), context=context) - + self._subscribe_users(cr, uid, [mail_group_id], context=context) return mail_group_id def unlink(self, cr, uid, ids, context=None): @@ -163,7 +157,8 @@ class mail_group(osv.Model): return res def write(self, cr, uid, ids, vals, context=None): + result = super(mail_group, self).write(cr, uid, ids, vals, context=context) if vals.get('group_ids'): - self._subscribe_user_with_group_m2m_command(cr, uid, ids, vals.get('group_ids'), context=context) - return super(mail_group, self).write(cr, uid, ids, vals, context=context) + self._subscribe_users(cr, uid, ids, vals.get('group_ids'), context=context) + return result diff --git a/addons/mail/mail_mail.py b/addons/mail/mail_mail.py index 8d53793341b..9aae5f9a8b7 100644 --- a/addons/mail/mail_mail.py +++ b/addons/mail/mail_mail.py @@ -49,6 +49,7 @@ class mail_mail(osv.Model): 'content_subtype': 'plain', } + # FP Note: should we use a dict instead ? def schedule_with_attach(self, cr, uid, email_from, email_to, subject, body, model=False, type='email', email_cc=None, reply_to=False, partner_ids=None, attachments=None, message_id=False, references=False, res_id=False, content_subtype='plain', diff --git a/addons/mail/mail_message.py b/addons/mail/mail_message.py index 3a942bf53a7..43628f217f1 100644 --- a/addons/mail/mail_message.py +++ b/addons/mail/mail_message.py @@ -360,5 +360,3 @@ class mail_message(osv.Model): msg['body'] = msg['body_text'] msg['sub_type'] = msg['content_subtype'] or 'plain' return msg - - diff --git a/addons/mail/mail_thread.py b/addons/mail/mail_thread.py index 7cdaa70f5de..74a69ac88e5 100644 --- a/addons/mail/mail_thread.py +++ b/addons/mail/mail_thread.py @@ -103,8 +103,8 @@ class mail_thread(osv.Model): return res # FP Note: todo - def _search_state(self, tobj, cr, uid, obj=None, name=None, domain=None, context=None): - return [('id','in',[])] + def _search_unread(self, tobj, cr, uid, obj=None, name=None, domain=None, context=None): + return [] _columns = { 'message_is_follower': fields.function(_get_is_follower, @@ -116,7 +116,7 @@ class mail_thread(osv.Model): domain=lambda self: [('model','=',self._name)], string='Related Messages', help="All messages related to the current document."), - 'message_unread': fields.function(_get_message_data, fnct_search=_search_state, 'Message Read', + 'message_unread': fields.function(_get_message_data, fnct_search=_search_unread, 'Has Unread Messages', help="When checked, new messages require your attention.", multi="_get_message_data"), 'message_summary': fields.function(_get_message_data, method=True, @@ -131,12 +131,10 @@ class mail_thread(osv.Model): #------------------------------------------------------ def create(self, cr, uid, vals, context=None): - """ Override of create to subscribe : - - the writer - - followers given by the monitored fields + """ Override of create to subscribe the current user """ thread_id = super(mail_thread, self).create(cr, uid, vals, context=context) - self.message_subscribe(cr, uid, [thread_id], [uid], context=context) + self.message_subscribe_users(cr, uid, [thread_id], [uid], context=context) return thread_id def unlink(self, cr, uid, ids, context=None): @@ -155,6 +153,8 @@ class mail_thread(osv.Model): # mail.message wrappers and tools #------------------------------------------------------ + # FP Note: should we support attachment ? Also, this method must be on + # the mail.message object, not on the thread. def message_create(self, cr, uid, thread_id, vals, context=None): """ OpenChatter: wrapper of mail.message create method - creates the mail.message @@ -177,14 +177,7 @@ class mail_thread(osv.Model): # Generic message api #------------------------------------------------------ - def message_capable_models(self, cr, uid, context=None): - ret_dict = {} - for model_name in self.pool.obj_list(): - model = self.pool.get(model_name) - if 'mail.thread' in getattr(model, '_inherit', []): - ret_dict[model_name] = model._description - return ret_dict - + # I propose to remove this. Everyone should use message_create instead. def message_append(self, cr, uid, threads, subject, body_text=None, body_html=None, type='email', email_date=None, parent_id=False, content_subtype='plain', state=None, @@ -313,6 +306,7 @@ class mail_thread(osv.Model): new_msg_ids.append(self.message_create(cr, uid, thread.id, data, context=context)) return new_msg_ids + # to be removed completly def message_append_dict(self, cr, uid, ids, msg_dict, context=None): """Creates a new mail.message attached to the given threads (``ids``), with the contents of ``msg_dict``, by calling ``message_append`` @@ -866,19 +860,37 @@ class mail_thread(osv.Model): return True return False - def message_subscribe(self, cr, uid, ids, partner_ids=None, context=None): + def message_subscribe_users(self, cr, uid, ids, user_ids=None, context=None): + if not user_ids: user_ids = [uid] + partners = {} + for user in self.pool.get('res.users').browse(cr, uid, user_ids, context=context): + partners[user.partner_id.id] = True + return self.message_subscribe(cr, uid, ids, partners.keys(), context=context) + + def message_subscribe(self, cr, uid, ids, partner_ids, context=None): """ - :param user_ids: a list of user_ids; if not set, subscribe + :param partner_ids: a list of user_ids; if not set, subscribe uid instead :param return: new value of followers, for Chatter """ - subscription_obj = self.pool.get('mail.subscription') + obj = self.pool.get('mail.followers') + objids = obj.search(cr, uid, [ + ('res_id', 'in', ids), + ('res_model', '=', self._name), + ('partner_id', 'in', partner_ids), + ], context=context) + followers = {} + for follow in obj.browse(cr, uid, objids, context=context) + followers.setdefault(follow.partner_id.id, {})[follow.res_id] = True create_ids = [] - for id in ids: - already_subscribed_user_ids = self.message_get_followers(cr, uid, [id], context=context) - for user_id in to_subscribe_uids: - if user_id in already_subscribed_user_ids: continue - create_ids.append(subscription_obj.create(cr, uid, {'res_model': self._name, 'res_id': id, 'user_id': user_id}, context=context)) + for res_id in ids: + for partner_id in partner_ids: + if followers.get(partner_id, {}).get(res_id, False): + continue + create_ids.append(obj.create(cr, uid, { + 'res_model': self._name, + 'res_id': res_id, 'partner_id': partner_id + }, context=context)) return create_ids def message_unsubscribe(self, cr, uid, ids, user_ids = None, context=None): diff --git a/addons/mail/res_partner.py b/addons/mail/res_partner.py index 062c557b325..c6ec7712fe1 100644 --- a/addons/mail/res_partner.py +++ b/addons/mail/res_partner.py @@ -31,18 +31,11 @@ class res_partner_mail(osv.Model): The purpose is to add messages directly sent to the partner. It also adds messages pushed to the related user, if any, using @login. """ - initial_domain = super(res_partner_mail, self).message_search_get_domain(cr, uid, ids, context=context) - # to avoid models inheriting from res.partner if self._name != 'res.partner': - return initial_domain + return super(res_partner_mail, self).message_search_get_domain(cr, uid, ids, context=context) # add message linked to the partner - search_domain = ['|'] + initial_domain + ['|', ('partner_id', 'in', ids), ('partner_ids', 'in', ids)] - # if partner is linked to a user: find @login - res_users_obj = self.pool.get('res.users') - user_ids = res_users_obj.search(cr, uid, [('partner_id', 'in', ids)], context=context) - for user in res_users_obj.browse(cr, uid, user_ids, context=context): - search_domain = ['|'] + search_domain + ['|', ('body_text', 'like', '@%s' % (user.login)), ('body_html', 'like', '@%s' % (user.login))] - return search_domain + return [('partner_ids', 'in', ids)] + _columns = { 'notification_email_pref': fields.selection([ ('all', 'All feeds'), diff --git a/addons/mail/res_users.py b/addons/mail/res_users.py index e57b046622e..1a39ed64bd9 100644 --- a/addons/mail/res_users.py +++ b/addons/mail/res_users.py @@ -98,17 +98,16 @@ class res_users(osv.Model): alias_id = mail_alias.create_unique_alias(cr, uid, {'alias_name': data['login']}, model_name=self._name, context=context) data['alias_id'] = alias_id data.pop('alias_name', None) # prevent errors during copy() + # create user that follows its related partner - user_id = super(res_users, self).create(cr, uid, data, context=context) - user = self.browse(cr, uid, user_id, context=context) - self.pool.get('res.partner').message_subscribe(cr, uid, [user.partner_id.id], [user_id], context=context) + self.pool.get('res.partner').message_subscribe_users(cr, uid, [user_id], [user_id], context=context) # alias mail_alias.write(cr, SUPERUSER_ID, [alias_id], {"alias_force_thread_id": user_id}, context) # create a welcome message - self.create_welcome_message(cr, uid, user, context=context) + self._create_welcome_message(cr, uid, user, context=context) return user_id - def create_welcome_message(self, cr, uid, user, context=None): + def _create_welcome_message(self, cr, uid, user, context=None): company_name = user.company_id.name if user.company_id else _('the company') subject = '''%s has joined %s.''' % (user.name, company_name) body = '''Welcome to OpenERP !''' @@ -135,35 +134,35 @@ class res_users(osv.Model): # that should help cleaning those wrappers # -------------------------------------------------- - def message_append(self, cr, uid, threads, subject, body_text=None, body_html=None, - type='email', email_date=None, parent_id=False, - content_subtype='plain', state=None, - partner_ids=None, email_from=False, email_to=False, - email_cc=None, email_bcc=None, reply_to=None, - headers=None, message_id=False, references=None, - attachments=None, original=None, context=None): - for user in self.browse(cr, uid, threads, context=context): - user.partner_id.message_append(subject, body_text, body_html, type, email_date, parent_id, - content_subtype, state, partner_ids, email_from, email_to, email_cc, email_bcc, reply_to, - headers, message_id, references, attachments, original) + #def message_append(self, cr, uid, threads, subject, body_text=None, body_html=None, + # type='email', email_date=None, parent_id=False, + # content_subtype='plain', state=None, + # partner_ids=None, email_from=False, email_to=False, + # email_cc=None, email_bcc=None, reply_to=None, + # headers=None, message_id=False, references=None, + # attachments=None, original=None, context=None): + # for user in self.browse(cr, uid, threads, context=context): + # user.partner_id.message_append(subject, body_text, body_html, type, email_date, parent_id, + # content_subtype, state, partner_ids, email_from, email_to, email_cc, email_bcc, reply_to, + # headers, message_id, references, attachments, original) - def message_read(self, cr, uid, ids, fetch_ancestors=False, ancestor_ids=None, - limit=100, offset=0, domain=None, context=None): - for user in self.browse(cr, uid, ids, context=context): - return user.partner_id.message_read(fetch_ancestors, ancestor_ids, limit, offset, domain) + #def message_read(self, cr, uid, ids, fetch_ancestors=False, ancestor_ids=None, + # limit=100, offset=0, domain=None, context=None): + # for user in self.browse(cr, uid, ids, context=context): + # return user.partner_id.message_read(fetch_ancestors, ancestor_ids, limit, offset, domain) - def message_search(self, cr, uid, ids, fetch_ancestors=False, ancestor_ids=None, - limit=100, offset=0, domain=None, count=False, context=None): - for user in self.browse(cr, uid, ids, context=context): - return user.partner_id.message_search(fetch_ancestors, ancestor_ids, limit, offset, domain, count) + #def message_search(self, cr, uid, ids, fetch_ancestors=False, ancestor_ids=None, + # limit=100, offset=0, domain=None, count=False, context=None): + # for user in self.browse(cr, uid, ids, context=context): + # return user.partner_id.message_search(fetch_ancestors, ancestor_ids, limit, offset, domain, count) - def message_subscribe(self, cr, uid, ids, user_ids = None, context=None): - for user in self.browse(cr, uid, ids, context=context): - return user.partner_id.message_subscribe(user_ids) + #def message_subscribe(self, cr, uid, ids, user_ids = None, context=None): + # for user in self.browse(cr, uid, ids, context=context): + # return user.partner_id.message_subscribe(user_ids) - def message_unsubscribe(self, cr, uid, ids, user_ids = None, context=None): - for user in self.browse(cr, uid, ids, context=context): - return user.partner_id.message_unsubscribe(user_ids) + #def message_unsubscribe(self, cr, uid, ids, user_ids = None, context=None): + # for user in self.browse(cr, uid, ids, context=context): + # return user.partner_id.message_unsubscribe(user_ids) class res_users_mail_group(osv.Model): @@ -175,6 +174,7 @@ class res_users_mail_group(osv.Model): _name = 'res.users' _inherit = ['res.users'] + # FP Note: to improve def write(self, cr, uid, ids, vals, context=None): write_res = super(res_users_mail_group, self).write(cr, uid, ids, vals, context=context) if vals.get('groups_id'): @@ -195,6 +195,7 @@ class res_groups_mail_group(osv.Model): _name = 'res.groups' _inherit = 'res.groups' + # FP Note: to improve def write(self, cr, uid, ids, vals, context=None): if vals.get('users'): # form: {'group_ids': [(3, 10), (3, 3), (4, 10), (4, 3)]} or {'group_ids': [(6, 0, [ids]} @@ -205,4 +206,3 @@ class res_groups_mail_group(osv.Model): mail_group_obj.message_subscribe(cr, uid, mail_group_ids, user_ids, context=context) return super(res_groups_mail_group, self).write(cr, uid, ids, vals, context=context) -# vim:et: From 4c9205ff4aa8590ec49cc9131e312c6158a8819c Mon Sep 17 00:00:00 2001 From: Jigar Amin - OpenERP Date: Thu, 16 Aug 2012 15:57:58 +0530 Subject: [PATCH 031/436] [IMP] Kanan quick create action should also repect the create tag on view bzr revid: jam@tinyerp.com-20120816102758-2swimfa9dxu3uotv --- addons/web_kanban/static/src/js/kanban.js | 7 ++++++- addons/web_kanban/static/src/xml/web_kanban.xml | 2 +- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/addons/web_kanban/static/src/js/kanban.js b/addons/web_kanban/static/src/js/kanban.js index 155f03b8aae..27b3184977c 100644 --- a/addons/web_kanban/static/src/js/kanban.js +++ b/addons/web_kanban/static/src/js/kanban.js @@ -514,7 +514,12 @@ instance.web_kanban.KanbanGroup = instance.web.Widget.extend({ self.view.dataset.ids.push(id); self.do_add_records(records, true); }); - } + }, + _is_action_enabled: function(action) { + if (_.has(this.fields_view.arch.attrs, action)) + return JSON.parse(this.fields_view.arch.attrs[action]); + return true; + } }); instance.web_kanban.KanbanRecord = instance.web.Widget.extend({ diff --git a/addons/web_kanban/static/src/xml/web_kanban.xml b/addons/web_kanban/static/src/xml/web_kanban.xml index 7aba4d6ee87..7e316a2b47e 100644 --- a/addons/web_kanban/static/src/xml/web_kanban.xml +++ b/addons/web_kanban/static/src/xml/web_kanban.xml @@ -25,7 +25,7 @@
- +
]
From d42261907c2bb2f2e3c690b2d88720e6b7ecc838 Mon Sep 17 00:00:00 2001 From: Jigar Amin - OpenERP Date: Thu, 16 Aug 2012 16:32:43 +0530 Subject: [PATCH 032/436] [FIX] Gantt View Create button Shouls repect the view create teg bzr revid: jam@tinyerp.com-20120816110243-blbjzmb2xdemoupg --- addons/web_gantt/static/src/js/gantt.js | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/addons/web_gantt/static/src/js/gantt.js b/addons/web_gantt/static/src/js/gantt.js index 202fb2aecc6..05488fcdca9 100644 --- a/addons/web_gantt/static/src/js/gantt.js +++ b/addons/web_gantt/static/src/js/gantt.js @@ -183,12 +183,14 @@ instance.web_gantt.GanttView = instance.web.View.extend({ self.on_task_display(task_info.internal_task); } }); - - // insertion of create button - var td = $($("table td", self.$element)[0]); - var rendered = QWeb.render("GanttView-create-button"); - $(rendered).prependTo(td); - $(".oe_gantt_button_create", this.$element).click(this.on_task_create); + + // insertion of create button if gantt has crate false do not render create button + if (this._is_action_enabled('create')){ + var td = $($("table td", self.$element)[0]); + var rendered = QWeb.render("GanttView-create-button"); + $(rendered).prependTo(td); + $(".oe_gantt_button_create", this.$element).click(this.on_task_create); + } }, on_task_changed: function(task_obj) { var self = this; From 9ec6f82e382984c9e0fc415090d8c5c56892b6c4 Mon Sep 17 00:00:00 2001 From: "Twinkle Christian (OpenERP)" Date: Thu, 16 Aug 2012 16:59:20 +0530 Subject: [PATCH 033/436] [IMP]Remove or bzr revid: tch@tinyerp.com-20120816112920-i4gnwzn7z7n3v0gs --- addons/web/static/src/xml/base.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addons/web/static/src/xml/base.xml b/addons/web/static/src/xml/base.xml index 96adbd42d2e..614c2a91d79 100644 --- a/addons/web/static/src/xml/base.xml +++ b/addons/web/static/src/xml/base.xml @@ -678,7 +678,7 @@ - Import + or Import
From cfa66e42f8e99609aaddd09a612884a4c373c1fe Mon Sep 17 00:00:00 2001 From: "Ajay Chauhan (OpenERP)" Date: Thu, 16 Aug 2012 18:06:17 +0530 Subject: [PATCH 034/436] [IMP] hr_expense: improve code as per suggestion bzr revid: cha@tinyerp.com-20120816123617-afade73y5wqune2r --- addons/hr_expense/hr_expense.py | 25 +++++++++++-------------- 1 file changed, 11 insertions(+), 14 deletions(-) diff --git a/addons/hr_expense/hr_expense.py b/addons/hr_expense/hr_expense.py index 3683eff9f1d..fca62261814 100644 --- a/addons/hr_expense/hr_expense.py +++ b/addons/hr_expense/hr_expense.py @@ -254,12 +254,8 @@ class hr_expense_line(osv.osv): return res def _get_uom_id(self, cr, uid, context=None): - try: - proxy = self.pool.get('ir.model.data') - result = proxy.get_object_reference(cr, uid, 'product', 'product_uom_unit') - return result[1] - except Exception, ex: - return False + result = self.pool.get('ir.model.data').get_object_reference(cr, uid, 'product', 'product_uom_unit') + return result and result[1] or False _columns = { 'name': fields.char('Expense Note', size=128, required=True), @@ -294,14 +290,15 @@ class hr_expense_line(osv.osv): def onchange_uom(self, cr, uid, ids, product_id, uom_id, context=None): res = {'value':{}} - if uom_id: - if product_id: - product = self.pool.get('product.product').browse(cr, uid, product_id, context=context) - uom = self.pool.get('product.uom').browse(cr, uid, uom_id, context=context) - if uom.category_id.id != product.uom_id.category_id.id: - res['warning'] = {'title': _('Warning'), 'message': _('Selected Unit of Measure does not belong to the same category as the product Unit of Measure')} - uom_id = product.uom_id.id - res['value'].update({'uom_id': uom_id}) + if not uom_id: + return res + if product_id: + product = self.pool.get('product.product').browse(cr, uid, product_id, context=context) + uom = self.pool.get('product.uom').browse(cr, uid, uom_id, context=context) + if uom.category_id.id != product.uom_id.category_id.id: + res['warning'] = {'title': _('Warning'), 'message': _('Selected Unit of Measure does not belong to the same category as the product Unit of Measure')} + uom_id = product.uom_id.id + res['value'].update({'uom_id': uom_id}) return res hr_expense_line() From 9aca3bac8bcff422f1f7354f97cf43f1efbbd6e9 Mon Sep 17 00:00:00 2001 From: Jigar Amin - OpenERP Date: Thu, 16 Aug 2012 18:27:06 +0530 Subject: [PATCH 035/436] [REF/IMP] refectorec the varibale names and added the gantt view, added create ndoe button diable condition bzr revid: jam@tinyerp.com-20120816125706-h92owusp47t6chl1 --- openerp/addons/base/rng/view.rng | 4 ++++ openerp/osv/orm.py | 12 ++++++++---- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/openerp/addons/base/rng/view.rng b/openerp/addons/base/rng/view.rng index c56892d7a07..b82c96fdb60 100644 --- a/openerp/addons/base/rng/view.rng +++ b/openerp/addons/base/rng/view.rng @@ -184,6 +184,7 @@ + @@ -381,6 +382,9 @@ 5years + + + diff --git a/openerp/osv/orm.py b/openerp/osv/orm.py index 0a8a05e7706..ea9f1c094e3 100644 --- a/openerp/osv/orm.py +++ b/openerp/osv/orm.py @@ -1824,6 +1824,10 @@ class BaseModel(object): if node.getchildren()[0].tag == 'node': node_fields = self.pool.get(node.getchildren()[0].get('object')).fields_get(cr, user, None, context) fields.update(node_fields) + if not node.get("create"): + fn = getattr(self.pool.get(node.getchildren()[1].get('object')), 'check_create') + if not fn(cr, user, raise_exception=False): + node.set("create", 'false') if node.getchildren()[1].tag == 'arrow': arrow_fields = self.pool.get(node.getchildren()[1].get('object')).fields_get(cr, user, None, context) fields.update(arrow_fields) @@ -1831,10 +1835,10 @@ class BaseModel(object): fields = self.fields_get(cr, user, None, context) fields_def = self.__view_look_dom(cr, user, node, view_id, False, fields, context=context) node = self._disable_workflow_buttons(cr, user, node) - if node.tag in ('kanban', 'tree', 'form'): - for a, b in (('create', 'check_create'), ('delete', 'check_unlink'), ('edit', 'check_write')): - if not node.get(a) and not getattr(self, b)(cr, user, raise_exception=False): - node.set(a, 'false') + if node.tag in ('kanban', 'tree', 'form', 'gantt'): + for action, fn in (('create', 'check_create'), ('delete', 'check_unlink'), ('edit', 'check_write')): + if not node.get(action) and not getattr(self, fn)(cr, user, raise_exception=False): + node.set(action, 'false') arch = etree.tostring(node, encoding="utf-8").replace('\t', '') for k in fields.keys(): if k not in fields_def: From 41964c4b9d9b53358b3fc01700bd2306f13916c6 Mon Sep 17 00:00:00 2001 From: Jigar Amin - OpenERP Date: Thu, 16 Aug 2012 18:30:12 +0530 Subject: [PATCH 036/436] [IMP] View Button Constrains: Diagrem view add node button, kanban record slidebar widget mnethod support bzr revid: jam@tinyerp.com-20120816130012-ibtv0ipbbnpznqb1 --- addons/web_diagram/static/src/js/diagram.js | 2 +- addons/web_diagram/static/src/xml/base_diagram.xml | 8 +++++--- addons/web_kanban/static/src/js/kanban.js | 3 +++ 3 files changed, 9 insertions(+), 4 deletions(-) diff --git a/addons/web_diagram/static/src/js/diagram.js b/addons/web_diagram/static/src/js/diagram.js index f06e757f7f1..02216052c69 100644 --- a/addons/web_diagram/static/src/js/diagram.js +++ b/addons/web_diagram/static/src/js/diagram.js @@ -44,7 +44,7 @@ instance.web.DiagramView = instance.web.View.extend({ this.node = this.nodes.attrs.object, this.connector = this.connectors.attrs.object; - this.$element.html(QWeb.render("DiagramView", this)); + this.$element.html(QWeb.render("DiagramView", {'widget':self})); this.$element.addClass(this.fields_view.arch.attrs['class']); this.$element.find('div.oe_diagram_pager button[data-pager-action]').click(function() { diff --git a/addons/web_diagram/static/src/xml/base_diagram.xml b/addons/web_diagram/static/src/xml/base_diagram.xml index 96e32f59648..f883f949457 100644 --- a/addons/web_diagram/static/src/xml/base_diagram.xml +++ b/addons/web_diagram/static/src/xml/base_diagram.xml @@ -2,9 +2,11 @@

-
- -
+ +
+ +
+
0 / 0 diff --git a/addons/web_kanban/static/src/js/kanban.js b/addons/web_kanban/static/src/js/kanban.js index 27b3184977c..84a0a76ba0d 100644 --- a/addons/web_kanban/static/src/js/kanban.js +++ b/addons/web_kanban/static/src/js/kanban.js @@ -785,6 +785,9 @@ instance.web_kanban.KanbanRecord = instance.web.Widget.extend({ }, kanban_compute_domain: function(domain) { return instance.web.form.compute_domain(domain, this.values); + }, + _is_action_enabled: function(action) { + return (_.has(this.fields_view.arch.attrs, action))?JSON.parse(this.fields_view.arch.attrs[action]):true; } }); From b478264bded10e59ef4ff83997fe03757dcd9d75 Mon Sep 17 00:00:00 2001 From: "Ajay Chauhan (OpenERP)" Date: Thu, 16 Aug 2012 19:11:49 +0530 Subject: [PATCH 037/436] [IMP] hr_expense: made change in condition bzr revid: cha@tinyerp.com-20120816134149-wjumyv73z72sh6w5 --- addons/hr_expense/hr_expense.py | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/addons/hr_expense/hr_expense.py b/addons/hr_expense/hr_expense.py index fca62261814..44ecef2ebba 100644 --- a/addons/hr_expense/hr_expense.py +++ b/addons/hr_expense/hr_expense.py @@ -290,15 +290,14 @@ class hr_expense_line(osv.osv): def onchange_uom(self, cr, uid, ids, product_id, uom_id, context=None): res = {'value':{}} - if not uom_id: + if not uom_id or not product_id: return res - if product_id: - product = self.pool.get('product.product').browse(cr, uid, product_id, context=context) - uom = self.pool.get('product.uom').browse(cr, uid, uom_id, context=context) - if uom.category_id.id != product.uom_id.category_id.id: - res['warning'] = {'title': _('Warning'), 'message': _('Selected Unit of Measure does not belong to the same category as the product Unit of Measure')} - uom_id = product.uom_id.id - res['value'].update({'uom_id': uom_id}) + product = self.pool.get('product.product').browse(cr, uid, product_id, context=context) + uom = self.pool.get('product.uom').browse(cr, uid, uom_id, context=context) + if uom.category_id.id != product.uom_id.category_id.id: + res['warning'] = {'title': _('Warning'), 'message': _('Selected Unit of Measure does not belong to the same category as the product Unit of Measure')} + uom_id = product.uom_id.id + res['value'].update({'uom_id': uom_id}) return res hr_expense_line() From 282f2535bc59742ff7ab5bac9a321c4bdf1eb5c7 Mon Sep 17 00:00:00 2001 From: Fabien Pinckaers Date: Thu, 16 Aug 2012 17:48:23 +0200 Subject: [PATCH 038/436] [IMP] mail cleaning bzr revid: fp@tinyerp.com-20120816154823-84cnif2cw99vn0qu --- addons/crm/crm_lead.py | 1 - .../process/communication_with_customer.yml | 6 - addons/crm/wizard/crm_lead_to_partner.py | 4 - addons/crm_claim/crm_claim.py | 1 - addons/crm_helpdesk/crm_helpdesk.py | 1 - addons/hr_recruitment/hr_recruitment.py | 1 - addons/mail/mail_mail.py | 3 +- addons/mail/mail_message.py | 159 +------------ addons/mail/mail_thread.py | 221 +++++++++++++++--- addons/mail/res_users.py | 4 +- addons/project_issue/project_issue.py | 1 - addons/project_mailgate/project_mailgate.py | 1 - 12 files changed, 196 insertions(+), 207 deletions(-) diff --git a/addons/crm/crm_lead.py b/addons/crm/crm_lead.py index 9fd81712b92..daecdf86ef4 100644 --- a/addons/crm/crm_lead.py +++ b/addons/crm/crm_lead.py @@ -827,7 +827,6 @@ class crm_lead(base_stage, osv.osv): }) if msg.get('priority') in dict(crm.AVAILABLE_PRIORITIES): custom_values['priority'] = msg.get('priority') - custom_values.update(self.message_partner_by_email(cr, uid, msg.get('from', False), context=context)) return super(crm_lead, self).message_new(cr, uid, msg, custom_values=custom_values, context=context) def message_update(self, cr, uid, ids, msg, update_vals=None, context=None): diff --git a/addons/crm/test/process/communication_with_customer.yml b/addons/crm/test/process/communication_with_customer.yml index 1653be10763..c958889bdc1 100644 --- a/addons/crm/test/process/communication_with_customer.yml +++ b/addons/crm/test/process/communication_with_customer.yml @@ -34,12 +34,6 @@ !python {model: crm.lead}: | lead_ids = self.search(cr, uid, [('email_from','=', 'Mr. John Right ')]) self.convert_partner(cr, uid, lead_ids, context=context) -- - Now, I search customer in regular customer list. -- - !python {model: crm.lead}: | - partner_ids = self.message_partner_by_email(cr, uid, 'Mr. John Right ') - assert partner_ids.get('partner_id'), "Customer is not found in regular customer list." - I convert one phonecall request as a customer and put into regular customer list. - diff --git a/addons/crm/wizard/crm_lead_to_partner.py b/addons/crm/wizard/crm_lead_to_partner.py index 98f114b4088..430ec3c1543 100644 --- a/addons/crm/wizard/crm_lead_to_partner.py +++ b/addons/crm/wizard/crm_lead_to_partner.py @@ -56,10 +56,6 @@ class crm_lead2partner(osv.osv_memory): if not len(lead_ids): return False this = lead.browse(cr, uid, lead_ids[0], context=context) - # Find partner address matches the email_from of the lead - res = lead.message_partner_by_email(cr, uid, this.email_from, context=context) - partner_id = res.get('partner_id', False) - # Find partner name that matches the name of the lead if not partner_id and this.partner_name: partner_ids = partner.search(cr, uid, [('name', '=', this.partner_name)], context=context) if partner_ids and len(partner_ids): diff --git a/addons/crm_claim/crm_claim.py b/addons/crm_claim/crm_claim.py index ec6252bcdca..73d5af68b52 100644 --- a/addons/crm_claim/crm_claim.py +++ b/addons/crm_claim/crm_claim.py @@ -200,7 +200,6 @@ class crm_claim(base_stage, osv.osv): }) if msg.get('priority'): custom_values['priority'] = msg.get('priority') - custom_values.update(self.message_partner_by_email(cr, uid, msg.get('from'), context=context)) return super(crm_claim,self).message_new(cr, uid, msg, custom_values=custom_values, context=context) def message_update(self, cr, uid, ids, msg, update_vals=None, context=None): diff --git a/addons/crm_helpdesk/crm_helpdesk.py b/addons/crm_helpdesk/crm_helpdesk.py index 4b0cb2e3ffa..bb3b0891479 100644 --- a/addons/crm_helpdesk/crm_helpdesk.py +++ b/addons/crm_helpdesk/crm_helpdesk.py @@ -110,7 +110,6 @@ class crm_helpdesk(base_state, osv.osv): 'email_cc': msg.get('cc'), 'user_id': False, }) - custom_values.update(self.message_partner_by_email(cr, uid, msg.get('from'), context=context)) return super(crm_helpdesk,self).message_new(cr, uid, msg, custom_values=custom_values, context=context) def message_update(self, cr, uid, ids, msg, update_vals=None, context=None): diff --git a/addons/hr_recruitment/hr_recruitment.py b/addons/hr_recruitment/hr_recruitment.py index 3d0b704b36d..721b5a8dc6d 100644 --- a/addons/hr_recruitment/hr_recruitment.py +++ b/addons/hr_recruitment/hr_recruitment.py @@ -339,7 +339,6 @@ class hr_applicant(base_stage, osv.Model): }) if msg.get('priority'): custom_values['priority'] = msg.get('priority') - custom_values.update(self.message_partner_by_email(cr, uid, msg.get('from', False), context=context)) return super(hr_applicant,self).message_new(cr, uid, msg, custom_values=custom_values, context=context) def message_update(self, cr, uid, ids, msg, update_vals=None, context=None): diff --git a/addons/mail/mail_mail.py b/addons/mail/mail_mail.py index 9aae5f9a8b7..0380e9084a3 100644 --- a/addons/mail/mail_mail.py +++ b/addons/mail/mail_mail.py @@ -37,11 +37,12 @@ class mail_mail(osv.Model): 'email_to': fields.text('To', help='Message recipients'), 'email_cc': fields.char('Cc', size=256, help='Carbon copy message recipients'), 'reply_to':fields.char('Reply-To', size=256, help='Preferred response address for the message'), + 'content_subtype': fields.char('Message content subtype', size=32, oldname="subtype", readonly=1, help="Type of message, usually 'html' or 'plain', used to select "\ "plain-text or rich-text contents accordingly"), - 'body_html': fields.html('Rich-text Contents', help="Rich-text/HTML version of the message"), + 'body_html': fields.text('Rich-text Contents', help="Rich-text/HTML version of the message"), } _defaults = { diff --git a/addons/mail/mail_message.py b/addons/mail/mail_message.py index 43628f217f1..2fe65600389 100644 --- a/addons/mail/mail_message.py +++ b/addons/mail/mail_message.py @@ -105,15 +105,17 @@ class mail_message(osv.Model): select=True, ondelete='set null', help="Initial thread message."), 'child_ids': fields.one2many('mail.message', 'parent_id', 'Child Messages'), + 'model': fields.char('Related Document Model', size=128, select=1), 'res_id': fields.integer('Related Document ID', select=1), 'record_name': fields.function(get_record_name, type='string', string='Message Record Name', help="Name get of the related document."), + 'subject': fields.char('Subject', size=128), 'date': fields.datetime('Date'), 'message_id': fields.char('Message-Id', size=256, help='Message unique identifier', select=1, readonly=1), - 'body': fields.text('Content', required=True), + 'body': fields.html('Content', required=True), } _defaults = { 'type': 'email', @@ -204,159 +206,4 @@ class mail_message(osv.Model): self.check(cr, uid, ids, 'unlink', context=context) return super(mail_message, self).unlink(cr, uid, ids, context) - # FP Note: to review - def parse_message(self, message, save_original=False, context=None): - """Parses a string or email.message.Message representing an - RFC-2822 email, and returns a generic dict holding the - message details. - :param message: the message to parse - :type message: email.message.Message | string | unicode - :param bool save_original: whether the returned dict - should include an ``original`` entry with the base64 - encoded source of the message. - :rtype: dict - :return: A dict with the following structure, where each - field may not be present if missing in original - message:: - - { 'message-id': msg_id, - 'subject': subject, - 'from': from, - 'to': to, - 'cc': cc, - 'headers' : { 'X-Mailer': mailer, - #.. all X- headers... - }, - 'content_subtype': msg_mime_subtype, - 'body_text': plaintext_body - 'body_html': html_body, - 'attachments': [('file1', 'bytes'), - ('file2', 'bytes') } - # ... - 'original': source_of_email, - } - """ - msg_txt = message - if isinstance(message, str): - msg_txt = email.message_from_string(message) - - # Warning: message_from_string doesn't always work correctly on unicode, - # we must use utf-8 strings here :-( - if isinstance(message, unicode): - message = message.encode('utf-8') - msg_txt = email.message_from_string(message) - - message_id = msg_txt.get('message-id', False) - msg = {} - - if save_original: - # save original, we need to be able to read the original email sometimes - msg['original'] = message.as_string() if isinstance(message, Message) \ - else message - msg['original'] = base64.b64encode(msg['original']) # binary fields are b64 - - if not message_id: - # Very unusual situation, be we should be fault-tolerant here - message_id = time.time() - msg_txt['message-id'] = message_id - _logger.info('Parsing Message without message-id, generating a random one: %s', message_id) - - msg_fields = msg_txt.keys() - msg['id'] = message_id - msg['message-id'] = message_id - - if 'Subject' in msg_fields: - msg['subject'] = decode(msg_txt.get('Subject')) - - if 'Content-Type' in msg_fields: - msg['content-type'] = msg_txt.get('Content-Type') - - if 'From' in msg_fields: - msg['from'] = decode(msg_txt.get('From') or msg_txt.get_unixfrom()) - - if 'To' in msg_fields: - msg['to'] = decode(msg_txt.get('To')) - - if 'Delivered-To' in msg_fields: - msg['to'] = decode(msg_txt.get('Delivered-To')) - - if 'CC' in msg_fields: - msg['cc'] = decode(msg_txt.get('CC')) - - if 'Cc' in msg_fields: - msg['cc'] = decode(msg_txt.get('Cc')) - - if 'Reply-To' in msg_fields: - msg['reply'] = decode(msg_txt.get('Reply-To')) - - if 'Date' in msg_fields: - date_hdr = decode(msg_txt.get('Date')) - # convert from email timezone to server timezone - date_server_datetime = dateutil.parser.parse(date_hdr).astimezone(pytz.timezone(tools.get_server_timezone())) - date_server_datetime_str = date_server_datetime.strftime(DEFAULT_SERVER_DATETIME_FORMAT) - msg['date'] = date_server_datetime_str - - if 'Content-Transfer-Encoding' in msg_fields: - msg['encoding'] = msg_txt.get('Content-Transfer-Encoding') - - if 'References' in msg_fields: - msg['references'] = msg_txt.get('References') - - if 'In-Reply-To' in msg_fields: - msg['in-reply-to'] = msg_txt.get('In-Reply-To') - - msg['headers'] = {} - msg['content_subtype'] = 'plain' - for item in msg_txt.items(): - if item[0].startswith('X-'): - msg['headers'].update({item[0]: item[1]}) - if not msg_txt.is_multipart() or 'text/plain' in msg.get('content-type', ''): - encoding = msg_txt.get_content_charset() - body = msg_txt.get_payload(decode=True) - if 'text/html' in msg.get('content-type', ''): - msg['body_html'] = body - msg['content_subtype'] = 'html' - if body: - body = tools.html2plaintext(body) - msg['body_text'] = tools.ustr(body, encoding) - - attachments = [] - if msg_txt.is_multipart() or 'multipart/alternative' in msg.get('content-type', ''): - body = "" - if 'multipart/alternative' in msg.get('content-type', ''): - msg['content_subtype'] = 'alternative' - else: - msg['content_subtype'] = 'mixed' - for part in msg_txt.walk(): - if part.get_content_maintype() == 'multipart': - continue - - encoding = part.get_content_charset() - filename = part.get_filename() - if part.get_content_maintype()=='text': - content = part.get_payload(decode=True) - if filename: - attachments.append((filename, content)) - content = tools.ustr(content, encoding) - if part.get_content_subtype() == 'html': - msg['body_html'] = content - msg['content_subtype'] = 'html' # html version prevails - body = tools.ustr(tools.html2plaintext(content)) - body = body.replace(' ', '') - elif part.get_content_subtype() == 'plain': - body = content - elif part.get_content_maintype() in ('application', 'image'): - if filename : - attachments.append((filename,part.get_payload(decode=True))) - else: - res = part.get_payload(decode=True) - body += tools.ustr(res, encoding) - - msg['body_text'] = body - msg['attachments'] = attachments - - # for backwards compatibility: - msg['body'] = msg['body_text'] - msg['sub_type'] = msg['content_subtype'] or 'plain' - return msg diff --git a/addons/mail/mail_thread.py b/addons/mail/mail_thread.py index 74a69ac88e5..394ef32ca1e 100644 --- a/addons/mail/mail_thread.py +++ b/addons/mail/mail_thread.py @@ -29,7 +29,7 @@ from email.utils import parsedate from email.message import Message from osv import osv, fields -from mail_message import decode, to_email +from mail_message import decode, mail_tools_to_email import tools from tools.translate import _ from tools.safe_eval import safe_eval as eval @@ -497,8 +497,16 @@ class mail_thread(osv.Model): msgs = msg_obj.read(cr, uid, msg_ids, context=context) return msgs + def _message_find_partners(self, cr, uid, message, headers=['From'], context=None): + s = ', '.join([decode(message.get(h)) for h in headers) + mails = mail_tools_to_email(s) + result = [] + for m in mails: + result += self.pool.get('res.partner').search(cr, uid, [('email','ilike',m)], context=context) + return result + def _message_find_user_id(self, cr, uid, message, context=None): - from_local_part = to_email(decode(message.get('From')))[0] + from_local_part = mail_tools_to_email(decode(message.get('From')))[0] user_ids = self.pool.get('res.users').search(cr, uid, [('login', '=', from_local_part)], context=context) return user_ids[0] if user_ids else uid @@ -595,7 +603,6 @@ class mail_thread(osv.Model): message_id, model, thread_id, custom_values, uid) return [(model, thread_id, custom_values, uid)] - def message_process(self, cr, uid, model, message, custom_values=None, save_original=False, strip_attachments=False, thread_id=None, context=None): @@ -753,8 +760,8 @@ class mail_thread(osv.Model): followers = model_pool.message_thread_followers(cr, uid, [res.id])[res.id] else: followers = self.message_thread_followers(cr, uid, [res.id])[res.id] - message_followers_emails = to_email(','.join(filter(None, followers))) - message_recipients = to_email(','.join(filter(None, + message_followers_emails = mail_tools_to_email(','.join(filter(None, followers))) + message_recipients = mail_tools_to_email(','.join(filter(None, [decode(msg['from']), decode(msg['to']), decode(msg['cc'])]))) @@ -765,7 +772,7 @@ class mail_thread(osv.Model): del msg['reply-to'] msg['reply-to'] = res.section_id.reply_to - smtp_from, = to_email(msg['from']) + smtp_from, = mail_tools_to_email(msg['from']) msg['from'] = smtp_from msg['to'] = ", ".join(forward_to) msg['message-id'] = tools.generate_tracking_message_id(res.id) @@ -777,37 +784,187 @@ class mail_thread(osv.Model): smtp_server_obj.send_email(cr, uid, msg) return True - def message_partner_by_email(self, cr, uid, email, context=None): - """Attempts to return the id of a partner address matching - the given ``email``, and the corresponding partner id. - Can be used by classes using the ``mail.thread`` mixin - to lookup the partner and use it in their implementation - of ``message_new`` to link the new record with a - corresponding partner. - The keys used in the returned dict are meant to map - to usual names for relationships towards a partner - and one of its addresses. + def parse_message(self, message, save_original=False, context=None): + """Parses a string or email.message.Message representing an + RFC-2822 email, and returns a generic dict holding the + message details. - :param email: email address for which a partner - should be searched for. + :param message: the message to parse + :type message: email.message.Message | string | unicode + :param bool save_original: whether the returned dict + should include an ``original`` entry with the base64 + encoded source of the message. :rtype: dict - :return: a map of the following form:: + :return: A dict with the following structure, where each + field may not be present if missing in original + message:: - { 'partner_address_id': id or False, - 'partner_id': pid or False } + { 'message-id': msg_id, + 'subject': subject, + 'from': from, --> author_id + 'to': to, --> partner_ids + 'cc': cc, --> partner_ids + 'headers' : { 'X-Mailer': mailer, --> to remove + #.. all X- headers... + }, + 'content_subtype': msg_mime_subtype, --> to remove + 'body_text': plaintext_body --> keep body + 'body_html': html_body, --> to remove + 'attachments': [('file1', 'bytes'), + ('file2', 'bytes') } + # ... + 'original': source_of_email, --> attachment document + } """ - partner_pool = self.pool.get('res.partner') - res = {'partner_id': False} - if email: - email = to_email(email)[0] - contact_ids = partner_pool.search(cr, uid, [('email', '=', email)]) - if contact_ids: - contact = partner_pool.browse(cr, uid, contact_ids[0]) - res['partner_id'] = contact.id - return res + msg_txt = message + attachments = [] + if isinstance(message, str): + msg_txt = email.message_from_string(message) - # for backwards-compatibility with old scripts - process_email = message_process + # Warning: message_from_string doesn't always work correctly on unicode, + # we must use utf-8 strings here :-( + if isinstance(message, unicode): + message = message.encode('utf-8') + msg_txt = email.message_from_string(message) + + message_id = msg_txt.get('message-id', False) + msg = {} + + if save_original: + msg_original = message.as_string() if isinstance(message, Message) \ + else message + attachments.append((0, 0, { + 'name':'email.msg', + 'datas': base64.b64encode(msg_original), + 'datas_fname': 'email.msg', + 'res_model': 'mail.message', + 'description': _('original email'), + }) + + if not message_id: + # Very unusual situation, be we should be fault-tolerant here + message_id = time.time() + msg_txt['message-id'] = message_id + _logger.info('Parsing Message without message-id, generating a random one: %s', message_id) + + msg_fields = msg_txt.keys() + + msg['message_id'] = message_id + + if 'Subject' in msg_fields: + msg['subject'] = decode(msg_txt.get('Subject')) + + #if 'Content-Type' in msg_fields: + # msg['content-type'] = msg_txt.get('Content-Type') + + # find author_id + + if 'From' in msg_fields: + author_ids = self._message_find_partners(cr, uid, msg_text, ['From'], context=context) + #decode(msg_txt.get('From') or msg_txt.get_unixfrom()) ) + if author_ids: + msg['author_id'] = author_ids[0] + + partner_ids = self._message_find_partners(cr, uid, msg_text, ['From','To','Delivered-To','CC','Cc'], context=context) + msg['partner_ids'] = partner_ids + + #if 'To' in msg_fields: + # msg['to'] = decode(msg_txt.get('To')) + + #if 'Delivered-To' in msg_fields: + # msg['to'] = decode(msg_txt.get('Delivered-To')) + + #if 'CC' in msg_fields: + # msg['cc'] = decode(msg_txt.get('CC')) + + #if 'Cc' in msg_fields: + # msg['cc'] = decode(msg_txt.get('Cc')) + + #if 'Reply-To' in msg_fields: + # msg['reply'] = decode(msg_txt.get('Reply-To')) + + # FP Note: I propose to store the current datetime rather than the email date + #if 'Date' in msg_fields: + # date_hdr = decode(msg_txt.get('Date')) + # # convert from email timezone to server timezone + # date_server_datetime = dateutil.parser.parse(date_hdr).astimezone(pytz.timezone(tools.get_server_timezone())) + # date_server_datetime_str = date_server_datetime.strftime(DEFAULT_SERVER_DATETIME_FORMAT) + # msg['date'] = date_server_datetime_str + + #if 'Content-Transfer-Encoding' in msg_fields: + # msg['encoding'] = msg_txt.get('Content-Transfer-Encoding') + + #if 'References' in msg_fields: + # msg['references'] = msg_txt.get('References') + + # FP Note: todo - find parent_id + if 'In-Reply-To' in msg_fields: + pass + + #msg['headers'] = {} + #msg['content_subtype'] = 'plain' + #for item in msg_txt.items(): + # if item[0].startswith('X-'): + # msg['headers'].update({item[0]: item[1]}) + if not msg_txt.is_multipart() or 'text/plain' in msg.get('content-type', ''): + encoding = msg_txt.get_content_charset() + body = msg_txt.get_payload(decode=True) + if 'text/html' in msg.get('content-type', ''): + msg['body'] = body + # msg['content_subtype'] = 'html' + # if body: + # body = tools.html2plaintext(body) + # msg['body_text'] = tools.ustr(body, encoding) + + if msg_txt.is_multipart() or 'multipart/alternative' in msg.get('content-type', ''): + body = "" + if 'multipart/alternative' in msg.get('content-type', ''): + msg['content_subtype'] = 'alternative' + else: + msg['content_subtype'] = 'mixed' + for part in msg_txt.walk(): + if part.get_content_maintype() == 'multipart': + continue + + encoding = part.get_content_charset() + filename = part.get_filename() + if part.get_content_maintype()=='text': + content = part.get_payload(decode=True) + if filename: + attachments.append((0, 0, { + 'name': filename, + 'datas': base64.b64encode(msg_original), + 'datas_fname': filename, + 'res_model': 'mail.message', + 'description': _('email attachment'), + })) + content = tools.ustr(content, encoding) + if part.get_content_subtype() == 'html': + msg['body'] = content + # msg['content_subtype'] = 'html' # html version prevails + # body = tools.ustr(tools.html2plaintext(content)) + # body = body.replace(' ', '') + elif part.get_content_subtype() == 'plain': + msg['body'] = content + elif part.get_content_maintype() in ('application', 'image'): + if filename: + attachments.append((0, 0, { + 'name': filename, + 'datas': part.get_payload(decode=True), + 'datas_fname': filename, + 'res_model': 'mail.message', + 'description': _('email attachment'), + })) + else: + res = part.get_payload(decode=True) + msg['body'] += tools.ustr(res, encoding) + + msg['attachments'] = attachments + + # for backwards compatibility: + # msg['body'] = msg['body_text'] + # msg['sub_type'] = msg['content_subtype'] or 'plain' + return msg #------------------------------------------------------ # Note specific diff --git a/addons/mail/res_users.py b/addons/mail/res_users.py index 1a39ed64bd9..5a8c1b0412e 100644 --- a/addons/mail/res_users.py +++ b/addons/mail/res_users.py @@ -183,7 +183,7 @@ class res_users_mail_group(osv.Model): user_group_ids += [id for command in vals['groups_id'] if command[0] == 6 for id in command[2]] mail_group_obj = self.pool.get('mail.group') mail_group_ids = mail_group_obj.search(cr, uid, [('group_ids', 'in', user_group_ids)], context=context) - mail_group_obj.message_subscribe(cr, uid, mail_group_ids, ids, context=context) + mail_group_obj.message_subscribe_users(cr, uid, mail_group_ids, ids, context=context) return write_res class res_groups_mail_group(osv.Model): @@ -203,6 +203,6 @@ class res_groups_mail_group(osv.Model): user_ids += [id for command in vals['users'] if command[0] == 6 for id in command[2]] mail_group_obj = self.pool.get('mail.group') mail_group_ids = mail_group_obj.search(cr, uid, [('group_ids', 'in', ids)], context=context) - mail_group_obj.message_subscribe(cr, uid, mail_group_ids, user_ids, context=context) + mail_group_obj.message_subscribe_users(cr, uid, mail_group_ids, user_ids, context=context) return super(res_groups_mail_group, self).write(cr, uid, ids, vals, context=context) diff --git a/addons/project_issue/project_issue.py b/addons/project_issue/project_issue.py index a8a1e617ecb..db9d02bfd1e 100644 --- a/addons/project_issue/project_issue.py +++ b/addons/project_issue/project_issue.py @@ -461,7 +461,6 @@ class project_issue(base_stage, osv.osv): }) if msg.get('priority'): custom_values['priority'] = msg.get('priority') - custom_values.update(self.message_partner_by_email(cr, uid, msg.get('from'), context=context)) res_id = super(project_issue, self).message_new(cr, uid, msg, custom_values=custom_values, context=context) # self.convert_to_bug(cr, uid, [res_id], context=context) diff --git a/addons/project_mailgate/project_mailgate.py b/addons/project_mailgate/project_mailgate.py index ba3cd702760..cf2bc120fbf 100644 --- a/addons/project_mailgate/project_mailgate.py +++ b/addons/project_mailgate/project_mailgate.py @@ -38,7 +38,6 @@ class project_tasks(osv.osv): 'planned_hours': 0.0, 'subject': msg.get('subject'), }) - custom_values.update(self.message_partner_by_email(cr, uid, msg.get('from', False), context=context)) return super(project_tasks,self).message_new(cr, uid, msg, custom_values=custom_values, context=context) def message_update(self, cr, uid, ids, msg, update_vals=None, context=None): From b9bdf2c29a048116304f18a95eb2e8c912f3100a Mon Sep 17 00:00:00 2001 From: Fabien Pinckaers Date: Thu, 16 Aug 2012 18:42:39 +0200 Subject: [PATCH 039/436] [IMP] tools.email_split bzr revid: fp@tinyerp.com-20120816164239-yl1vzp83w6z5xi5i --- openerp/tools/misc.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/openerp/tools/misc.py b/openerp/tools/misc.py index e90fadf0492..7f2dade30d8 100644 --- a/openerp/tools/misc.py +++ b/openerp/tools/misc.py @@ -1113,6 +1113,12 @@ class unquote(str): def __repr__(self): return self +def emails_split(text): + """Return a list of the email addresses found in ``text``""" + if not text: return [] + return re.findall(r'([^ ,<@]+@[^> ,]+)', text) + + class UnquoteEvalContext(defaultdict): """Defaultdict-based evaluation context that returns an ``unquote`` string for any missing name used during From 960e4ba5412ae0646667803b4b97de9a2e10039a Mon Sep 17 00:00:00 2001 From: Fabien Pinckaers Date: Thu, 16 Aug 2012 18:43:11 +0200 Subject: [PATCH 040/436] [IMP] email cleaning bzr revid: fp@tinyerp.com-20120816164311-o7r05mewrvjoldsg --- addons/base_action_rule/base_action_rule.py | 7 ++---- addons/crm/crm_lead.py | 3 +-- addons/mail/mail_followers.py | 5 ++-- addons/mail/mail_mail.py | 4 +-- addons/mail/mail_message.py | 7 +----- addons/mail/mail_thread.py | 27 +++++++++++---------- addons/mail/res_partner.py | 2 +- addons/mail/wizard/mail_compose_message.py | 16 +++++++----- 8 files changed, 34 insertions(+), 37 deletions(-) diff --git a/addons/base_action_rule/base_action_rule.py b/addons/base_action_rule/base_action_rule.py index b6eacda63c0..8c115baed23 100644 --- a/addons/base_action_rule/base_action_rule.py +++ b/addons/base_action_rule/base_action_rule.py @@ -438,11 +438,8 @@ the rule to mark CC(mail to any other person defined in actions)."), if len(emails) and action.act_mail_body: emails = list(set(emails)) email_from = safe_eval(action.act_email_from, {}, locals_for_emails) - - def to_email(text): - return re.findall(r'([^ ,<@]+@[^> ,]+)', text or '') - emails = to_email(','.join(filter(None, emails))) - email_froms = to_email(email_from) + emails = tools.email_split(','.join(filter(None, emails))) + email_froms = tools.email_split(email_from) if email_froms: self.email_send(cr, uid, obj, emails, action.act_mail_body, emailfrom=email_froms[0]) return True diff --git a/addons/crm/crm_lead.py b/addons/crm/crm_lead.py index daecdf86ef4..3569480ba9c 100644 --- a/addons/crm/crm_lead.py +++ b/addons/crm/crm_lead.py @@ -23,7 +23,6 @@ import binascii from base_status.base_stage import base_stage import crm from datetime import datetime -from mail.mail_message import to_email from osv import fields, osv import time import tools @@ -629,7 +628,7 @@ class crm_lead(base_stage, osv.osv): 'parent_id': parent_id, 'phone': lead.phone, 'mobile': lead.mobile, - 'email': lead.email_from and to_email(lead.email_from)[0], + 'email': lead.email_from and tools.email_split(lead.email_from)[0], 'fax': lead.fax, 'title': lead.title and lead.title.id or False, 'function': lead.function, diff --git a/addons/mail/mail_followers.py b/addons/mail/mail_followers.py index 0073ee81e41..dcace5c0344 100644 --- a/addons/mail/mail_followers.py +++ b/addons/mail/mail_followers.py @@ -70,7 +70,8 @@ class mail_notification(osv.Model): # Create notification in the wall of each user # Send by email the notification depending on the user preferences - def notify(self, cr, uid, partner_ids, msg_id, context=context): + def notify(self, cr, uid, partner_ids, msg_id, context=None): + context = context or {} partner_obj = self.pool.get('res.partner') msg_obj = self.pool.get('mail.message') msg = msg_obj.browse(cr, uid, msg_id, context=context) @@ -101,7 +102,7 @@ class mail_notification(osv.Model): if email_to not in towrite['email_to']: towrite['email_to'] = towrite['email_to'] + ', ' + email_to - if towrite.get('state', False): + if towrite.get('state', False) and not context.get('noemail', False): if towrite.get('subject', False): towrite['subject'] = msg.name_get(cr, uid, [msg.id], context=context)[0][1] towrite['message_id'] = msg.id diff --git a/addons/mail/mail_mail.py b/addons/mail/mail_mail.py index 0380e9084a3..13e0638c299 100644 --- a/addons/mail/mail_mail.py +++ b/addons/mail/mail_mail.py @@ -233,11 +233,11 @@ class mail_mail(osv.Model): # without queuing msg = ir_mail_server.build_email( email_from=message.email_from, - email_to=mail_tools_to_email(message_email_to), + email_to=tools.email_split(message_email_to), subject=message.subject, body=body, body_alternative=body_alternative, - email_cc=mail_tools_to_email(message.email_cc), + email_cc=tools.email_split(message.email_cc), reply_to=message.reply_to, attachments=attachments, message_id=message.message_id, references = message.references, diff --git a/addons/mail/mail_message.py b/addons/mail/mail_message.py index 2fe65600389..846ba739843 100644 --- a/addons/mail/mail_message.py +++ b/addons/mail/mail_message.py @@ -46,11 +46,6 @@ def decode(text): text = decode_header(text.replace('\r', '')) return ''.join([tools.ustr(x[0], x[1]) for x in text]) -def mail_tools_to_email(text): - """Return a list of the email addresses found in ``text``""" - if not text: return [] - return re.findall(r'([^ ,<@]+@[^> ,]+)', text) - class mail_message(osv.Model): """Model holding messages: system notification (replacing res.log notifications), comments (for OpenChatter feature). This model also @@ -99,7 +94,7 @@ class mail_message(osv.Model): 'Recipients'), 'attachment_ids': fields.one2many('ir.attachment', 'res_id', - 'Attachments' domain=[('res_model','=','mail.message')]), + 'Attachments', domain=[('res_model','=','mail.message')]), 'parent_id': fields.many2one('mail.message', 'Parent Message', select=True, ondelete='set null', diff --git a/addons/mail/mail_thread.py b/addons/mail/mail_thread.py index 394ef32ca1e..ab6ad324125 100644 --- a/addons/mail/mail_thread.py +++ b/addons/mail/mail_thread.py @@ -29,7 +29,7 @@ from email.utils import parsedate from email.message import Message from osv import osv, fields -from mail_message import decode, mail_tools_to_email +from mail_message import decode import tools from tools.translate import _ from tools.safe_eval import safe_eval as eval @@ -111,12 +111,13 @@ class mail_thread(osv.Model): type='boolean', string='Is a Follower'), 'message_follower_ids': fields.one2many('mail.subscription', 'res_id', domain=lambda self: [('res_model','=',self._name)], - string='Followers') + string='Followers'), 'message_ids': fields.one2many('mail.message', 'res_id', domain=lambda self: [('model','=',self._name)], string='Related Messages', help="All messages related to the current document."), - 'message_unread': fields.function(_get_message_data, fnct_search=_search_unread, 'Has Unread Messages', + 'message_unread': fields.function(_get_message_data, fnct_search=_search_unread, + string='Has Unread Messages', help="When checked, new messages require your attention.", multi="_get_message_data"), 'message_summary': fields.function(_get_message_data, method=True, @@ -498,15 +499,15 @@ class mail_thread(osv.Model): return msgs def _message_find_partners(self, cr, uid, message, headers=['From'], context=None): - s = ', '.join([decode(message.get(h)) for h in headers) - mails = mail_tools_to_email(s) + s = ', '.join([decode(message.get(h)) for h in headers]) + mails = tools.email_split(s) result = [] for m in mails: result += self.pool.get('res.partner').search(cr, uid, [('email','ilike',m)], context=context) return result def _message_find_user_id(self, cr, uid, message, context=None): - from_local_part = mail_tools_to_email(decode(message.get('From')))[0] + from_local_part = tools.email_split(decode(message.get('From')))[0] user_ids = self.pool.get('res.users').search(cr, uid, [('login', '=', from_local_part)], context=context) return user_ids[0] if user_ids else uid @@ -571,7 +572,7 @@ class mail_thread(osv.Model): decode_header(message, 'Cc'), decode_header(message, 'Resent-To'), decode_header(message, 'Resent-Cc')]) - local_parts = [e.split('@')[0] for e in to_email(rcpt_tos)] + local_parts = [e.split('@')[0] for e in tools.email_split(rcpt_tos)] if local_parts: mail_alias = self.pool.get('mail.alias') alias_ids = mail_alias.search(cr, uid, [('alias_name', 'in', local_parts)]) @@ -760,8 +761,8 @@ class mail_thread(osv.Model): followers = model_pool.message_thread_followers(cr, uid, [res.id])[res.id] else: followers = self.message_thread_followers(cr, uid, [res.id])[res.id] - message_followers_emails = mail_tools_to_email(','.join(filter(None, followers))) - message_recipients = mail_tools_to_email(','.join(filter(None, + message_followers_emails = tools.email_split(','.join(filter(None, followers))) + message_recipients = tools.email_split(','.join(filter(None, [decode(msg['from']), decode(msg['to']), decode(msg['cc'])]))) @@ -772,7 +773,7 @@ class mail_thread(osv.Model): del msg['reply-to'] msg['reply-to'] = res.section_id.reply_to - smtp_from, = mail_tools_to_email(msg['from']) + smtp_from, = tools.email_split(msg['from']) msg['from'] = smtp_from msg['to'] = ", ".join(forward_to) msg['message-id'] = tools.generate_tracking_message_id(res.id) @@ -839,7 +840,7 @@ class mail_thread(osv.Model): 'datas_fname': 'email.msg', 'res_model': 'mail.message', 'description': _('original email'), - }) + })) if not message_id: # Very unusual situation, be we should be fault-tolerant here @@ -1037,7 +1038,7 @@ class mail_thread(osv.Model): ('partner_id', 'in', partner_ids), ], context=context) followers = {} - for follow in obj.browse(cr, uid, objids, context=context) + for follow in obj.browse(cr, uid, objids, context=context): followers.setdefault(follow.partner_id.id, {})[follow.res_id] = True create_ids = [] for res_id in ids: @@ -1093,6 +1094,6 @@ class mail_thread(osv.Model): where message_id in (select id from mail_message where res_id in %s and model=%s) user_id = %s - ''', (ids, self._name, uid) + ''', (ids, self._name, uid)) return True diff --git a/addons/mail/res_partner.py b/addons/mail/res_partner.py index c6ec7712fe1..7ca33e94f01 100644 --- a/addons/mail/res_partner.py +++ b/addons/mail/res_partner.py @@ -19,7 +19,7 @@ # ############################################################################## -from osv import osv +from osv import osv, fields class res_partner_mail(osv.Model): """ Inherits partner and adds CRM information in the partner form """ diff --git a/addons/mail/wizard/mail_compose_message.py b/addons/mail/wizard/mail_compose_message.py index fafc866b895..86cf34df04c 100644 --- a/addons/mail/wizard/mail_compose_message.py +++ b/addons/mail/wizard/mail_compose_message.py @@ -28,7 +28,11 @@ from osv import fields from tools.safe_eval import safe_eval as eval from tools.translate import _ -from ..mail_message import to_email +# FP Note: refactor in tools ? +def mail_tools_to_email(text): + """Return a list of the email addresses found in ``text``""" + if not text: return [] + return re.findall(r'([^ ,<@]+@[^> ,]+)', text) # main mako-like expression pattern EXPRESSION_PATTERN = re.compile('(\$\{.+?\})') @@ -56,7 +60,7 @@ class mail_compose_message(osv.TransientModel): related, in case ``mail.compose.message.mode == 'mass_mail'``. """ _name = 'mail.compose.message' - _inherit = 'mail.message.common' + _inherit = 'mail.message' _description = 'Email composition wizard' def default_get(self, cr, uid, fields, context=None): @@ -304,8 +308,8 @@ class mail_compose_message(osv.TransientModel): email_from=email_from, email_to=email_to, email_cc=email_cc, reply_to=reply_to, references=references, attachments=attachment, headers=headers, context=context) else: - mail_message_obj.schedule_with_attach(cr, uid, email_from, to_email(email_to), subject, rendered_body_text, - model=mail_wiz.model, email_cc=to_email(email_cc), reply_to=reply_to, + mail_message_obj.schedule_with_attach(cr, uid, email_from, mail_tools_to_email(email_to), subject, rendered_body_text, + model=mail_wiz.model, email_cc=mail_tools_to_email(email_cc), reply_to=reply_to, attachments=attachment, references=references, res_id=active_id, partner_ids=partner_ids, content_subtype=mail_wiz.content_subtype, headers=headers, context=context) else: @@ -316,8 +320,8 @@ class mail_compose_message(osv.TransientModel): email_from=mail_wiz.email_from, email_to=mail_wiz.email_to, email_cc=mail_wiz.email_cc, reply_to=mail_wiz.reply_to, references=references, attachments=attachment, headers=headers, context=context) else: - msg_ids = [mail_message_obj.schedule_with_attach(cr, uid, mail_wiz.email_from, to_email(mail_wiz.email_to), subject, mail_wiz.body_text, - type=type, model=mail_wiz.model, email_cc=to_email(mail_wiz.email_cc), reply_to=mail_wiz.reply_to, + msg_ids = [mail_message_obj.schedule_with_attach(cr, uid, mail_wiz.email_from, mail_tools_to_email(mail_wiz.email_to), subject, mail_wiz.body_text, + type=type, model=mail_wiz.model, email_cc=mail_tools_to_email(mail_wiz.email_cc), reply_to=mail_wiz.reply_to, attachments=attachment, references=references, res_id=int(mail_wiz.res_id), partner_ids=partner_ids, content_subtype=mail_wiz.content_subtype, headers=headers, context=context)] # in normal mode, we send the email immediately, as the user expects us to (delay should be sufficiently small) From 811466be2faef29b9a0f53f4ae5126a8a8134bba Mon Sep 17 00:00:00 2001 From: "Divyesh Makwana (Open ERP)" Date: Fri, 17 Aug 2012 11:21:34 +0530 Subject: [PATCH 041/436] [IMP] account : Improved the onchange uos_id method. bzr revid: mdi@tinyerp.com-20120817055134-4p4gcsgusmyk34wd --- addons/account/account_invoice.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/addons/account/account_invoice.py b/addons/account/account_invoice.py index 225904f28b7..f0d2f2d8360 100644 --- a/addons/account/account_invoice.py +++ b/addons/account/account_invoice.py @@ -1479,10 +1479,11 @@ class account_invoice_line(osv.osv): prod = self.pool.get('product.product').browse(cr, uid, product, context=context) prod_uom = self.pool.get('product.uom').browse(cr, uid, uom, context=context) if prod.uom_id.category_id.id != prod_uom.category_id.id: - warning = { + warning = { 'title': _('Warning!'), 'message': _('The selected unit of measure is not compatible with the unit of measure of the product.') - } + } + res['value'].update({'uos_id': prod.uom_id.id}) return {'value': res['value'], 'warning': warning} return res From 32201e28604f119a0c59f6aee8fe4b511f637c6f Mon Sep 17 00:00:00 2001 From: "Divyesh Makwana (Open ERP)" Date: Fri, 17 Aug 2012 11:26:39 +0530 Subject: [PATCH 042/436] [IMP] account : Improved the code. bzr revid: mdi@tinyerp.com-20120817055639-sc5c9awb2h8d59zl --- addons/account/account_invoice.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/addons/account/account_invoice.py b/addons/account/account_invoice.py index f0d2f2d8360..44531a1a266 100644 --- a/addons/account/account_invoice.py +++ b/addons/account/account_invoice.py @@ -1362,7 +1362,7 @@ class account_invoice_line(osv.osv): 'partner_id': fields.related('invoice_id','partner_id',type='many2one',relation='res.partner',string='Partner',store=True) } - def default_account_id(self, cr, uid, ids, context=None): + def _default_account_id(self, cr, uid, ids, context=None): prop = self.pool.get('ir.property').get(cr, uid, 'property_account_income_categ', 'product.category', context=context) return prop and prop.id or False @@ -1370,7 +1370,7 @@ class account_invoice_line(osv.osv): 'quantity': 1, 'discount': 0.0, 'price_unit': _price_unit_default, - 'account_id': default_account_id, + 'account_id': _default_account_id, } def fields_view_get(self, cr, uid, view_id=None, view_type='form', context=None, toolbar=False, submenu=False): From cb944d66ea941a3f80025f56d576677fa9d1c59c Mon Sep 17 00:00:00 2001 From: Fabien Pinckaers Date: Fri, 17 Aug 2012 09:02:18 +0200 Subject: [PATCH 043/436] [IMP] mail message bzr revid: fp@tinyerp.com-20120817070218-rp1ssuhwnf8raitf --- addons/event/event.py | 6 +- addons/mail/mail_followers_view.xml | 4 +- addons/mail/mail_group.py | 4 +- addons/mail/mail_message_view.xml | 62 ++++++------------- addons/mail/mail_thread.py | 4 +- addons/mail/res_partner.py | 3 + addons/mail/res_users.py | 38 ------------ addons/mail/wizard/mail_compose_message.py | 1 + .../mail/wizard/mail_compose_message_view.xml | 12 ++-- addons/plugin/plugin_handler.py | 3 +- 10 files changed, 36 insertions(+), 101 deletions(-) diff --git a/addons/event/event.py b/addons/event/event.py index 7f0c77661e4..48946959551 100644 --- a/addons/event/event.py +++ b/addons/event/event.py @@ -334,7 +334,7 @@ class event_registration(osv.osv): return self.write(cr, uid, ids, {'state': 'draft'}, context=context) def confirm_registration(self, cr, uid, ids, context=None): - self.message_append(cr, uid, ids,_('State set to open'),body_text= _('Open')) + self.message_append_note(cr, uid, ids, body=_('State set to open')) return self.write(cr, uid, ids, {'state': 'open'}, context=context) def create(self, cr, uid, vals, context=None): @@ -364,13 +364,13 @@ class event_registration(osv.osv): if today >= registration.event_id.date_begin: values = {'state': 'done', 'date_closed': today} self.write(cr, uid, ids, values) - self.message_append(cr, uid, ids, _('State set to Done'), body_text=_('Done')) + self.message_append_note(cr, uid, ids, body=_('State set to Done')) else: raise osv.except_osv(_('Error!'),_("You must wait for the starting day of the event to do this action.") ) return True def button_reg_cancel(self, cr, uid, ids, context=None, *args): - self.message_append(cr, uid, ids,_('State set to Cancel'),body_text= _('Cancel')) + self.message_append_note(cr, uid, ids,body = _('State set to Cancel')) return self.write(cr, uid, ids, {'state': 'cancel'}) def mail_user(self, cr, uid, ids, context=None): diff --git a/addons/mail/mail_followers_view.xml b/addons/mail/mail_followers_view.xml index 62e93430675..0637d4dc11e 100644 --- a/addons/mail/mail_followers_view.xml +++ b/addons/mail/mail_followers_view.xml @@ -12,7 +12,7 @@ - + @@ -24,7 +24,7 @@ 10 - + diff --git a/addons/mail/mail_group.py b/addons/mail/mail_group.py index 2c8b9450439..ba3ed292bf8 100644 --- a/addons/mail/mail_group.py +++ b/addons/mail/mail_group.py @@ -36,7 +36,7 @@ class mail_group(osv.Model): """ _description = 'Discussion group' _name = 'mail.group' - _inherit = ['mail.thread','ir.needaction'] + _inherit = ['mail.thread','ir.needaction_mixin'] _inherits = {'mail.alias': 'alias_id', 'ir.ui.menu': 'menu_id'} def _get_image(self, cr, uid, ids, name, args, context=None): @@ -84,7 +84,7 @@ class mail_group(osv.Model): help="Small-sized photo of the group. It is automatically "\ "resized as a 50x50px image, with aspect ratio preserved. "\ "Use this field anywhere a small image is required."), - 'alias_id': fields.many2one('mail.alias', 'Alias', ondelete="cascade", + 'alias_id': fields.many2one('mail.alias', 'Alias', ondelete="cascade", required=True, help="The email address associated with this group. New emails received will automatically " "create new topics."), } diff --git a/addons/mail/mail_message_view.xml b/addons/mail/mail_message_view.xml index c50e53c8e75..e7c4f9df494 100644 --- a/addons/mail/mail_message_view.xml +++ b/addons/mail/mail_message_view.xml @@ -10,7 +10,7 @@ - + @@ -28,10 +28,9 @@ - + - @@ -40,14 +39,7 @@ - - - - - - - - + @@ -60,9 +52,8 @@ 20 - - - + + @@ -76,9 +67,6 @@ - @@ -88,31 +76,21 @@ - - - + - mail.message.form - mail.message + mail.mail.form + mail.mail
]]> -

]]> - Date: Fri, 17 Aug 2012 14:50:04 +0200 Subject: [PATCH 054/436] [IMP] mails bzr revid: fp@tinyerp.com-20120817125004-wcchhac2cvxuf93i --- addons/email_template/email_template.py | 22 +++++++++++----------- addons/mail/mail_message.py | 1 + addons/mail/mail_thread.py | 2 +- addons/procurement/schedulers.py | 14 -------------- 4 files changed, 13 insertions(+), 26 deletions(-) diff --git a/addons/email_template/email_template.py b/addons/email_template/email_template.py index ef01533adaa..84d48e1387a 100644 --- a/addons/email_template/email_template.py +++ b/addons/email_template/email_template.py @@ -137,13 +137,13 @@ class email_template(osv.osv): 'model': fields.related('model_id','model', type='char', string='Related Document Model', size=128, select=True, store=True, readonly=True), # we need a separate m2m table to avoid ID collisions with the original mail.message entries - 'attachment_ids': fields.many2many('ir.attachment', 'email_template_attachment_rel', 'email_template_id', - 'attachment_id', 'Files to attach', - help="You may attach files to this template, to be added to all " - "emails created from this template"), + #'attachment_ids': fields.many2many('ir.attachment', 'email_template_attachment_rel', 'email_template_id', + # 'attachment_id', 'Files to attach', + # help="You may attach files to this template, to be added to all " + # "emails created from this template"), # Overridden mail.message.common fields to make tooltips more appropriate: - 'subject':fields.char('Subject', size=512, translate=True, help="Subject (placeholders may be used here)",), + #'subject':fields.char('Subject', size=512, translate=True, help="Subject (placeholders may be used here)",), 'email_from': fields.char('From', size=128, help="Sender address (placeholders may be used here)"), 'email_to': fields.char('To', size=256, help="Comma-separated recipient addresses (placeholders may be used here)"), 'email_cc': fields.char('Cc', size=256, help="Carbon copy recipients (placeholders may be used here)"), @@ -151,13 +151,13 @@ class email_template(osv.osv): 'mail_server_id': fields.many2one('ir.mail_server', 'Outgoing Mail Server', readonly=False, help="Optional preferred server for outgoing mails. If not set, the highest " "priority one will be used."), - 'body': fields.text('Text Contents', translate=True, help="Plaintext version of the message (placeholders may be used here)"), + #'body': fields.text('Text Contents', translate=True, help="Plaintext version of the message (placeholders may be used here)"), 'body_html': fields.text('Rich-text Contents', translate=True, help="Rich-text/HTML version of the message (placeholders may be used here)"), - 'message_id': fields.char('Message-Id', size=256, help="Message-ID SMTP header to use in outgoing messages based on this template. " - "Please note that this overrides the 'Resource Tracking' option, " - "so if you simply need to track replies to outgoing emails, enable " - "that option instead.\n" - "Placeholders must be used here, as this value always needs to be unique!"), + #'message_id': fields.char('Message-Id', size=256, help="Message-ID SMTP header to use in outgoing messages based on this template. " + # "Please note that this overrides the 'Resource Tracking' option, " + # "so if you simply need to track replies to outgoing emails, enable " + # "that option instead.\n" + # "Placeholders must be used here, as this value always needs to be unique!"), # Fake fields used to implement the placeholder assistant 'model_object_field': fields.many2one('ir.model.fields', string="Field", diff --git a/addons/mail/mail_message.py b/addons/mail/mail_message.py index a722dca3797..5de34d18f42 100644 --- a/addons/mail/mail_message.py +++ b/addons/mail/mail_message.py @@ -169,6 +169,7 @@ class mail_message(osv.Model): self.pool.get(model).check_access_rule(cr, uid, mids, mode, context=context) def create(self, cr, uid, values, context=None): + print values newid = super(mail_message, self).create(cr, uid, values, context) self.check(cr, uid, [newid], mode='create', context=context) diff --git a/addons/mail/mail_thread.py b/addons/mail/mail_thread.py index 833bba056ec..1def02bd5fa 100644 --- a/addons/mail/mail_thread.py +++ b/addons/mail/mail_thread.py @@ -757,7 +757,7 @@ class mail_thread(osv.Model): context = context or {} attachments = attachments or {} if type(res_id) in (list, tuple): - res_id = res_id[0] + res_id = res_id and res_id[0] or False to_attach = [] for fname, fcontent in attachments: diff --git a/addons/procurement/schedulers.py b/addons/procurement/schedulers.py index a8bff6213d7..1c8ac5934fc 100644 --- a/addons/procurement/schedulers.py +++ b/addons/procurement/schedulers.py @@ -54,7 +54,6 @@ class procurement_order(osv.osv): ''' if context is None: context = {} - try: if use_new_cursor: cr = pooler.get_db(use_new_cursor).cursor() @@ -119,19 +118,6 @@ class procurement_order(osv.osv): offset += len(ids) if not ids: break end_date = fields.datetime.now() - if uid: - # Chatter: old res.request is now a chatter on res.users, id=uid - summary = _("""Here is the procurement scheduling report. - - Start Time: %s - End Time: %s - Total Procurements processed: %d - Procurements with exceptions: %d - Skipped Procurements (scheduled date outside of scheduler range) %d - - Exceptions:\n""") % (start_date, end_date, report_total, report_except, report_later) - summary += '\n'.join(report) - procurement_obj.message_post(cr, uid, ids, body=summary, context=context) if use_new_cursor: cr.commit() From a2961cbdc3bc345e91057434026e804b260a41be Mon Sep 17 00:00:00 2001 From: Fabien Pinckaers Date: Fri, 17 Aug 2012 15:01:26 +0200 Subject: [PATCH 055/436] mail_refactor bzr revid: fp@tinyerp.com-20120817130126-ldiw28gs13rvf2ka --- addons/fetchmail/fetchmail_view.xml | 11 ----------- addons/mail/mail_message.py | 1 - 2 files changed, 12 deletions(-) diff --git a/addons/fetchmail/fetchmail_view.xml b/addons/fetchmail/fetchmail_view.xml index cf651b5fd3d..7c049dedbb8 100644 --- a/addons/fetchmail/fetchmail_view.xml +++ b/addons/fetchmail/fetchmail_view.xml @@ -114,17 +114,6 @@ - - mail.message.inherit.search - mail.message - - - - - - - - Date: Fri, 17 Aug 2012 15:34:49 +0200 Subject: [PATCH 056/436] [IMP] FIX in MRP demo data + mail stuff bzr revid: fp@tinyerp.com-20120817133449-y50n3iyh49f6w8j1 --- addons/crm/crm_lead_demo.xml | 21 +++++---------------- addons/mail/mail_thread.py | 25 ++++++++++++++++++++----- addons/mrp/mrp_demo.xml | 2 +- 3 files changed, 26 insertions(+), 22 deletions(-) diff --git a/addons/crm/crm_lead_demo.xml b/addons/crm/crm_lead_demo.xml index ed51c525335..79aa1676a7e 100644 --- a/addons/crm/crm_lead_demo.xml +++ b/addons/crm/crm_lead_demo.xml @@ -553,50 +553,39 @@ Andrew Kitchen design crm.lead - html - + email sent - Reply crm.lead - html - + comment - Reply crm.lead - html - + comment - Reply crm.lead - html - + comment - Your inquiry crm.lead - html - + email - received - diff --git a/addons/mail/mail_thread.py b/addons/mail/mail_thread.py index 1def02bd5fa..de4759dd835 100644 --- a/addons/mail/mail_thread.py +++ b/addons/mail/mail_thread.py @@ -877,15 +877,30 @@ class mail_thread(osv.Model): #------------------------------------------------------ # FP Note: this should be a invert function on message_unread field + # not sure because if not readonly, it may often write to this field? + def message_mark_as_unread(self, cr, uid, ids, context=None): + """ Set as read. """ + notobj = self.pool.get('mail.notification') + partner_id = self.pool.get('res.users').browse(cr, uid, uid, context=context).partner_id.id + cr.execute(''' + UPDATE mail_notification SET + read=false + WHERE + message_id IN (SELECT id from mail_message where res_id=any(%s) and model=%s limit 1) and + partner_id = %s + ''', (ids, self._name, partner_id)) + return True + def message_mark_as_read(self, cr, uid, ids, context=None): """ Set as read. """ notobj = self.pool.get('mail.notification') + partner_id = self.pool.get('res.users').browse(cr, uid, uid, context=context).partner_id.id cr.execute(''' - update mail_notification set + UPDATE mail_notification SET read=true - where - message_id in (select id from mail_message where res_id in %s and model=%s) - user_id = %s - ''', (ids, self._name, uid)) + WHERE + message_id IN (SELECT id FROM mail_message WHERE res_id=ANY(%s) AND model=%s) AND + partner_id = %s + ''', (ids, self._name, partner_id)) return True diff --git a/addons/mrp/mrp_demo.xml b/addons/mrp/mrp_demo.xml index d0f05a2aaff..952a5e0e3de 100644 --- a/addons/mrp/mrp_demo.xml +++ b/addons/mrp/mrp_demo.xml @@ -49,7 +49,7 @@ 0.05 - + From 07c5ee86d67a573d220803640c73b273456a233d Mon Sep 17 00:00:00 2001 From: Fabien Pinckaers Date: Fri, 17 Aug 2012 16:49:49 +0200 Subject: [PATCH 057/436] fix bzr revid: fp@tinyerp.com-20120817144949-wrqo086h3dx6f3kx --- openerp/addons/base/ir/ir_needaction.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/openerp/addons/base/ir/ir_needaction.py b/openerp/addons/base/ir/ir_needaction.py index 9112d60c1f4..e40a3030eb8 100644 --- a/openerp/addons/base/ir/ir_needaction.py +++ b/openerp/addons/base/ir/ir_needaction.py @@ -63,5 +63,6 @@ class ir_needaction_mixin(osv.Model): dom = self._needaction_domain_get(cr, uid, context=context) if dom is False: return 0 - return self.search(cr, uid, domain+dom, context=context, count=True) + print domain, dom + return self.search(cr, uid, (domain or []) +dom, context=context, count=True) From 2756495b4f68d3a62b400397e46c535ef057ea1b Mon Sep 17 00:00:00 2001 From: Fabien Pinckaers Date: Fri, 17 Aug 2012 16:50:10 +0200 Subject: [PATCH 058/436] fix bzr revid: fp@tinyerp.com-20120817145010-lnh2496hi1o90eu5 --- addons/mail/mail_group_menu.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addons/mail/mail_group_menu.py b/addons/mail/mail_group_menu.py index 72c3ecdba4b..25b85bb7b22 100644 --- a/addons/mail/mail_group_menu.py +++ b/addons/mail/mail_group_menu.py @@ -45,7 +45,7 @@ class ir_ui_menu(osv.osv): for menu in self.browse(cr, uid, ids, context=context): if menu.mail_group_id: sub_ids = follower_obj.search(cr, uid, [ - ('user_id', '=', uid), ('res_model', '=', 'mail.group'), + ('partner_id', '=', partner_id), ('res_model', '=', 'mail.group'), ('res_id', '=', menu.mail_group_id.id) ], context=context) if not sub_ids: From 03f4b992ad57d5a52d4ac8229bab95ba94808969 Mon Sep 17 00:00:00 2001 From: Fabien Pinckaers Date: Sun, 19 Aug 2012 16:59:28 +0200 Subject: [PATCH 059/436] [IMP] need action & mails bzr revid: fp@openerp.com-20120819145928-5bw5lo6dn4ni4gl7 --- openerp/addons/base/ir/ir.xml | 24 --- openerp/addons/base/ir/ir_needaction.py | 183 +++--------------- openerp/addons/base/ir/ir_ui_menu.py | 19 +- .../addons/base/security/ir.model.access.csv | 1 - openerp/osv/fields.py | 15 +- openerp/osv/orm.py | 33 +--- 6 files changed, 50 insertions(+), 225 deletions(-) diff --git a/openerp/addons/base/ir/ir.xml b/openerp/addons/base/ir/ir.xml index fac2946e204..c554a8896aa 100644 --- a/openerp/addons/base/ir/ir.xml +++ b/openerp/addons/base/ir/ir.xml @@ -556,30 +556,6 @@ - - - ir.needaction_users_rel.tree - ir.needaction_users_rel - 10 - - - - - - - - - - - Need action relationships - ir.needaction_users_rel - form - tree,form - - - - - ir.ui.view diff --git a/openerp/addons/base/ir/ir_needaction.py b/openerp/addons/base/ir/ir_needaction.py index 0f28c33ded9..9112d60c1f4 100644 --- a/openerp/addons/base/ir/ir_needaction.py +++ b/openerp/addons/base/ir/ir_needaction.py @@ -19,180 +19,49 @@ # ############################################################################## -import openerp.pooler as pooler -from operator import itemgetter from osv import osv, fields -from tools.translate import _ - -class ir_needaction_users_rel(osv.Model): - ''' ir_needaction_users_rel holds data related to the needaction - mechanism inside OpenERP. A row in this model is characterized - by: - - res_model: model of the record requiring an action - - res_id: ID of the record requiring an action - - user_id: foreign key to the res.users table, to the user that has to - perform the action - This model can be seen as a many2many, linking (res_model, res_id) to users - (those whose attention is required on the record). ''' - - _name = 'ir.needaction_users_rel' - _description = 'Needaction relationship table' - _rec_name = 'id' - _order = 'id desc' - _columns = { - 'res_model': fields.char('Related Document Model', size=128, - select=1, required=True), - 'res_id': fields.integer('Related Document ID', - select=1, required=True), - 'user_id': fields.many2one('res.users', string='Related User', - ondelete='cascade', select=1, required=True), - } - - def _get_users(self, cr, uid, res_ids, res_model, context=None): - """Given res_ids of res_model, get user_ids present in table""" - rel_ids = self.search(cr, uid, [('res_model', '=', res_model), ('res_id', 'in', res_ids)], context=context) - return list(set(map(itemgetter('user_id'), self.read(cr, uid, rel_ids, ['user_id'], context=context)))) - - def create_users(self, cr, uid, res_ids, res_model, user_ids, context=None): - """Given res_ids of res_model, add user_ids to the relationship table""" - for res_id in res_ids: - for user_id in user_ids: - self.create(cr, uid, {'res_model': res_model, 'res_id': res_id, 'user_id': user_id}, context=context) - return True - - def unlink_users(self, cr, uid, res_ids, res_model, context=None): - """Given res_ids of res_model, delete all entries in the relationship table""" - to_del_ids = self.search(cr, uid, [('res_model', '=', res_model), ('res_id', 'in', res_ids)], context=context) - return self.unlink(cr, uid, to_del_ids, context=context) - - def update_users(self, cr, uid, res_ids, res_model, user_ids, context=None): - """Given res_ids of res_model, update their entries in the relationship table to user_ids""" - # read current records - cur_users = self._get_users(cr, uid, res_ids, res_model, context=context) - if len(cur_users) == len(user_ids) and all(cur_user in user_ids for cur_user in cur_users): - return True - # unlink old records - self.unlink_users(cr, uid, res_ids, res_model, context=context) - # link new records - self.create_users(cr, uid, res_ids, res_model, user_ids, context=context) - return True - class ir_needaction_mixin(osv.Model): '''Mixin class for objects using the need action feature. - - Need action feature can be used by objects having to be able to - signal that an action is required on a particular record. If in - the business logic an action must be performed by somebody, for - instance validation by a manager, this mechanism allows to set a + + Need action feature can be used by objects having to be able to + signal that an action is required on a particular record. If in + the business logic an action must be performed by somebody, for + instance validation by a manager, this mechanism allows to set a list of users asked to perform an action. - - This class wraps a class (ir.ir_needaction_users_rel) that - behaves like a many2many field. This class handles the low-level - considerations of updating relationships. Every change made on - the record calls a method that updates the relationships. - - Objects using the 'need_action' feature should override the - ``get_needaction_user_ids`` method. This methods returns a - dictionary whose keys are record ids, and values a list of user - ids, like in a many2many relationship. Therefore by defining - only one method, you can specify if an action is required by - defining the users that have to do it, in every possible - situation. - - This class also offers several global services: - - ``needaction_get_record_ids``: for the current model and uid, get - all record ids that ask this user to perform an action. This - mechanism is used for instance to display the number of pending - actions in menus, such as Leads (12) + + Objects using the 'need_action' feature should override the + ``needaction_domain_get`` method. This methods returns a + domain to filter records requiring an action for a specific user. + + This class also offers several global services: - ``needaction_get_action_count``: as ``needaction_get_record_ids`` - but returns only the number of action, not the ids (performs a + but returns only the number of action, not the ids (performs a search with count=True) + ''' - The ``ir_needaction_mixin`` class adds a calculated field - ``needaction_pending``. This function field allows to state - whether a given record has a needaction for the current user. - This is usefull if you want to customize views according to the - needaction feature. For example, you may want to set records in - bold in a list view if the current user has an action to perform - on the record. ''' - _name = 'ir.needaction_mixin' - _description = 'Need action mixin' - - def get_needaction_pending(self, cr, uid, ids, name, arg, context=None): - res = {} - needaction_user_ids = self.get_needaction_user_ids(cr, uid, ids, context=context) - for id in ids: - res[id] = uid in needaction_user_ids[id] - return res + _needaction = True - def search_needaction_pending(self, cr, uid, self_again, field_name, criterion, context=None): - ids = self.needaction_get_record_ids( - cr, uid, uid, limit=1024, context=context) - return [('id', 'in', ids)] - - _columns = { - 'needaction_pending': fields.function( - get_needaction_pending, type='boolean', - fnct_search=search_needaction_pending, - string='Need action pending', - help="If True, this field states that users have to perform an " \ - "action This field comes from the ir.needaction_mixin class."), - } - #------------------------------------------------------ # Addon API #------------------------------------------------------ - - def get_needaction_user_ids(self, cr, uid, ids, context=None): - """ Returns the user_ids that have to perform an action - :return: dict { record_id: [user_ids], } + + def _needaction_domain_get(self, cr, uid, context=None): + """ Returns the domain to filter records that require an action + :return: domain or False is no need action """ - return dict((id,list()) for id in ids) - - def create(self, cr, uid, values, context=None): - rel_obj = self.pool.get('ir.needaction_users_rel') - # perform create - obj_id = super(ir_needaction_mixin, self).create(cr, uid, values, context=context) - # link user_ids - needaction_user_ids = self.get_needaction_user_ids(cr, uid, [obj_id], context=context) - rel_obj.create_users(cr, uid, [obj_id], self._name, needaction_user_ids[obj_id], context=context) - return obj_id - - def write(self, cr, uid, ids, values, context=None): - rel_obj = self.pool.get('ir.needaction_users_rel') - # perform write - write_res = super(ir_needaction_mixin, self).write(cr, uid, ids, values, context=context) - # get and update user_ids - needaction_user_ids = self.get_needaction_user_ids(cr, uid, ids, context=context) - for id in ids: - rel_obj.update_users(cr, uid, [id], self._name, needaction_user_ids[id], context=context) - return write_res - - def unlink(self, cr, uid, ids, context=None): - # unlink user_ids - rel_obj = self.pool.get('ir.needaction_users_rel') - rel_obj.unlink_users(cr, uid, ids, self._name, context=context) - # perform unlink - return super(ir_needaction_mixin, self).unlink(cr, uid, ids, context=context) - + return False + #------------------------------------------------------ # "Need action" API #------------------------------------------------------ - - def needaction_get_record_ids(self, cr, uid, user_id, limit=80, context=None): - """Given the current model and a user_id - return the record ids that require the user to perform an - action""" - rel_obj = self.pool.get('ir.needaction_users_rel') - rel_ids = rel_obj.search(cr, uid, [('res_model', '=', self._name), ('user_id', '=', user_id)], limit=limit, context=context) - return map(itemgetter('res_id'), rel_obj.read(cr, uid, rel_ids, ['res_id'], context=context)) - - def needaction_get_action_count(self, cr, uid, user_id, limit=80, context=None): + + def _needaction_count(self, cr, uid, domain=[], context=None): """Given the current model and a user_id get the number of actions it has to perform""" - rel_obj = self.pool.get('ir.needaction_users_rel') - return rel_obj.search(cr, uid, [('res_model', '=', self._name), ('user_id', '=', user_id)], limit=limit, count=True, context=context) + dom = self._needaction_domain_get(cr, uid, context=context) + if dom is False: + return 0 + return self.search(cr, uid, domain+dom, context=context, count=True) -# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/openerp/addons/base/ir/ir_ui_menu.py b/openerp/addons/base/ir/ir_ui_menu.py index 77c0c4d738a..f5e4ee2ba05 100644 --- a/openerp/addons/base/ir/ir_ui_menu.py +++ b/openerp/addons/base/ir/ir_ui_menu.py @@ -255,23 +255,24 @@ class ir_ui_menu(osv.osv): return res - def _get_needaction_info(self, cr, uid, id, domain=[], context={}): - return [False, 0] - def _get_needaction(self, cr, uid, ids, field_names, args, context=None): if context is None: context = {} res = {} for menu in self.browse(cr, uid, ids, context=context): + res[menu.id] = {} + res[menu.id]['needaction_enabled'] = False + res[menu.id]['needaction_counter'] = False res[menu.id] = {} if menu.action and menu.action.type == 'ir.actions.act_window' and menu.action.res_model: - menu_needaction_res = self.pool.get(menu.action.res_model)._get_needaction_info(cr, uid, uid, domain=menu.action.domain, context=context) - else: - menu_needaction_res = [False, 0] - res[menu.id]['needaction_enabled'] = menu_needaction_res[0] - res[menu.id]['needaction_counter'] = menu_needaction_res[1] + obj = self.pool.get(menu.action.res_model) + if obj._needaction: + res[menu.id]['needaction_enabled'] = obj._needaction + # check domain and context: should we evaluate the domain ? + # and add context of the action ? + res[menu.id]['needaction_counter'] = obj._needaction_count(cr, uid, menu.action.domain, context=context) return res - + _columns = { 'name': fields.char('Menu', size=64, required=True, translate=True), 'sequence': fields.integer('Sequence'), diff --git a/openerp/addons/base/security/ir.model.access.csv b/openerp/addons/base/security/ir.model.access.csv index d49aa18db8c..b0218e977bc 100644 --- a/openerp/addons/base/security/ir.model.access.csv +++ b/openerp/addons/base/security/ir.model.access.csv @@ -118,6 +118,5 @@ "access_ir_config_parameter","ir_config_parameter","model_ir_config_parameter",,1,0,0,0 "access_ir_mail_server_all","ir_mail_server","model_ir_mail_server",,1,0,0,0 "access_ir_actions_client","ir_actions_client all","model_ir_actions_client",,1,0,0,0 -"access_ir_needaction_users_rel","ir_needaction_users_rel","model_ir_needaction_users_rel",,1,1,1,1 "access_ir_needaction_mixin","ir_needaction_mixin","model_ir_needaction_mixin",,1,1,1,1 diff --git a/openerp/osv/fields.py b/openerp/osv/fields.py index 5a47601f8a5..da025845759 100644 --- a/openerp/osv/fields.py +++ b/openerp/osv/fields.py @@ -514,7 +514,10 @@ class one2many(_column): for id in ids: res[id] = [] - ids2 = obj.pool.get(self._obj).search(cr, user, self._domain + [(self._fields_id, 'in', ids)], limit=self._limit, context=context) + dom = self._domain + if isinstance(self._domain, type(lambda: None)): + dom = self._domain(obj) + ids2 = obj.pool.get(self._obj).search(cr, user, dom + [(self._fields_id, 'in', ids)], limit=self._limit, context=context) for r in obj.pool.get(self._obj)._read_flat(cr, user, ids2, [self._fields_id], context=context, load='_classic_write'): if r[self._fields_id] in res: res[r[self._fields_id]].append(r['id']) @@ -556,7 +559,10 @@ class one2many(_column): reverse_rel = obj._all_columns.get(self._fields_id) assert reverse_rel, 'Trying to unlink the content of a o2m but the pointed model does not have a m2o' # if the o2m has a static domain we must respect it when unlinking - extra_domain = self._domain if isinstance(getattr(self, '_domain', None), list) else [] + dom = self._domain + if isinstance(self._domain, type(lambda: None)): + dom = self._domain(obj) + extra_domain = dom or [] ids_to_unlink = obj.search(cr, user, [(self._fields_id,'=',id)] + extra_domain, context=context) # If the model has cascade deletion, we delete the rows because it is the intended behavior, # otherwise we only nullify the reverse foreign key column. @@ -574,7 +580,10 @@ class one2many(_column): return result def search(self, cr, obj, args, name, value, offset=0, limit=None, uid=None, operator='like', context=None): - return obj.pool.get(self._obj).name_search(cr, uid, value, self._domain, operator, context=context,limit=limit) + dom = self._domain + if isinstance(self._domain, type(lambda: None)): + dom = self._domain(obj) + return obj.pool.get(self._obj).name_search(cr, uid, value, dom, operator, context=context,limit=limit) @classmethod diff --git a/openerp/osv/orm.py b/openerp/osv/orm.py index 7c8085ea8ed..368a37f455a 100644 --- a/openerp/osv/orm.py +++ b/openerp/osv/orm.py @@ -666,6 +666,7 @@ class BaseModel(object): _order = 'id' _sequence = None _description = None + _needaction = False # dict of {field:method}, with method returning the name_get of records # to include in the _read_group, if grouped on this field @@ -4960,37 +4961,7 @@ class BaseModel(object): # backwards compatibility get_xml_id = get_external_id _get_xml_ids = _get_external_ids - - def _get_needaction_info(self, cr, uid, user_id, limit=None, order=None, domain=False, context=None): - """Base method for needaction mechanism - - see ir.needaction for actual implementation - - if the model uses the need action mechanism - (hasattr(model_obj, 'needaction_get_record_ids')): - - get the record ids on which the user has actions to perform - - evaluate the menu domain - - compose a new domain: menu domain, limited to ids of - records requesting an action - - count the number of records maching that domain, that - is the number of actions the user has to perform - - this method returns default values - :param: model_name: the name of the model (ex: hr.holidays) - :param: user_id: the id of user - :return: [uses_needaction=True/False, needaction_uid_ctr=%d] - """ - if hasattr(self, 'needaction_get_record_ids'): - # Arbitrary limit, but still much lower thant infinity, to avoid - # getting too much data. - ids = self.needaction_get_record_ids(cr, uid, user_id, limit=8192, context=context) - if not ids: - return [True, 0] - if domain: - new_domain = eval(domain, locals_dict={'uid': user_id}) + [('id', 'in', ids)] - else: - new_domain = [('id', 'in', ids)] - return [True, self.search(cr, uid, new_domain, limit=limit, order=order, count=True, context=context)] - else: - return [False, 0] - + # Transience def is_transient(self): """ Return whether the model is transient. From 1b8d1908293300f2b2475df028b48d3a522fae90 Mon Sep 17 00:00:00 2001 From: Fabien Pinckaers Date: Sun, 19 Aug 2012 18:28:32 +0200 Subject: [PATCH 060/436] [IMP] merging _get_pushed_messages and message_read, still have to add a domain on message_read based on notifications bzr revid: fp@openerp.com-20120819162832-ffzlm7zej7qrn11l --- addons/mail/mail_thread.py | 34 ------------------------------- addons/mail/static/src/js/mail.js | 12 ++++++----- 2 files changed, 7 insertions(+), 39 deletions(-) diff --git a/addons/mail/mail_thread.py b/addons/mail/mail_thread.py index de4759dd835..6c9d4dc3904 100644 --- a/addons/mail/mail_thread.py +++ b/addons/mail/mail_thread.py @@ -278,40 +278,6 @@ class mail_thread(osv.Model): messages = sorted(messages, key=lambda d: (-d['id'])) return messages - def message_get_pushed_messages(self, cr, uid, ids, fetch_ancestors=False, ancestor_ids=None, - limit=100, offset=0, msg_search_domain=[], context=None): - """ OpenChatter: wall: get the pushed notifications and used them - to fetch messages to display on the wall. - - :param fetch_ancestors: performs an ascended search; will add - to fetched msgs all their parents until - ancestor_ids - :param ancestor_ids: used when fetching ancestors - :param domain: domain to add to the search; especially child_of - is interesting when dealing with threaded display - :param ascent: performs an ascended search; will add to fetched msgs - all their parents until root_ids - :param root_ids: for ascent search - :return: list of mail.messages sorted by date - """ - notification_obj = self.pool.get('mail.notification') - msg_obj = self.pool.get('mail.message') - # update message search - for arg in msg_search_domain: - if isinstance(arg, (tuple, list)): - arg[0] = 'message_id.' + arg[0] - # compose final domain - domain = [('user_id', '=', uid)] + msg_search_domain - # get notifications - notification_ids = notification_obj.search(cr, uid, domain, limit=limit, offset=offset, context=context) - notifications = notification_obj.browse(cr, uid, notification_ids, context=context) - msg_ids = [notification.message_id.id for notification in notifications] - # get messages - msg_ids = msg_obj.search(cr, uid, [('id', 'in', msg_ids)], context=context) - if (fetch_ancestors): msg_ids = self._message_search_ancestor_ids(cr, uid, ids, msg_ids, ancestor_ids, context=context) - msgs = msg_obj.read(cr, uid, msg_ids, context=context) - return msgs - def _message_find_partners(self, cr, uid, message, headers=['From'], context=None): s = ', '.join([decode(message.get(h)) for h in headers]) mails = tools.email_split(s) diff --git a/addons/mail/static/src/js/mail.js b/addons/mail/static/src/js/mail.js index f619480aaf4..9612f73a6b8 100644 --- a/addons/mail/static/src/js/mail.js +++ b/addons/mail/static/src/js/mail.js @@ -529,6 +529,7 @@ openerp.mail = function(session) { // this.params.limit = 3; // tmp for testing this.params.offset = this.params.offset || 0; this.params.records = this.params.records || null; + // datasets and internal vars this.ds = new session.web.DataSetSearch(this, this.params.res_model); this.ds_users = new session.web.DataSetSearch(this, 'res.users'); @@ -550,6 +551,7 @@ openerp.mail = function(session) { this.bind_events(); // display user, fetch comments this.display_current_user(); + if (this.params.records) var display_done = this.display_comments_from_parameters(this.params.records); else var display_done = this.init_comments(); // customize display @@ -747,10 +749,10 @@ openerp.mail = function(session) { record.mini_url = mail.ChatterUtils.get_image(this.session.prefix, this.session.session_id, 'res.users', 'image_small', record.user_id[0]); } // body text manipulation - if (record.subtype == 'plain') { - record.body = mail.ChatterUtils.do_text_remove_html_tags(record.body); - } - record.body = mail.ChatterUtils.do_replace_expressions(record.body); + // if (record.subtype == 'plain') { + // record.body = mail.ChatterUtils.do_text_remove_html_tags(record.body); + // } + // record.body = mail.ChatterUtils.do_replace_expressions(record.body); // format date according to the user timezone record.date = session.web.format_value(record.date, {type:"datetime"}); // is the user the author ? @@ -1041,7 +1043,7 @@ openerp.mail = function(session) { else var fetch_domain = this.search['domain']; if (additional_context) var fetch_context = _.extend(this.search['context'], additional_context); else var fetch_context = this.search['context']; - return this.ds_thread.call('message_get_pushed_messages', + return this.ds_thread.call('message_read', [[this.session.uid], true, [], (limit || 0), (offset || 0), fetch_domain, fetch_context]).then(this.proxy('display_comments')); }, From 229096453c6fed151ab5edebffa4fcee42263110 Mon Sep 17 00:00:00 2001 From: Fabien Pinckaers Date: Sun, 19 Aug 2012 18:50:39 +0200 Subject: [PATCH 061/436] [IMP] remove formatting code and unused method bzr revid: fp@openerp.com-20120819165039-kgs71kkn436753tz --- addons/mail/mail_thread.py | 12 +--------- addons/mail/static/src/js/mail.js | 39 ------------------------------- 2 files changed, 1 insertion(+), 50 deletions(-) diff --git a/addons/mail/mail_thread.py b/addons/mail/mail_thread.py index 6c9d4dc3904..615dcf9c306 100644 --- a/addons/mail/mail_thread.py +++ b/addons/mail/mail_thread.py @@ -251,6 +251,7 @@ class mail_thread(osv.Model): Please see message_search for more information about the parameters. """ + print 'MSG READ', uid, ids, fetch_ancestors, ancestor_ids, limit, offset, domain, context message_ids = self.message_search(cr, uid, ids, fetch_ancestors, ancestor_ids, limit, offset, domain, context=context) messages = self.pool.get('mail.message').read(cr, uid, message_ids, context=context) @@ -753,17 +754,6 @@ class mail_thread(osv.Model): # Subscription mechanism #------------------------------------------------------ - # FP Note: replaced by message_follower_ids - # def message_get_followers(self, cr, uid, ids, context=None): - - def message_read_followers(self, cr, uid, ids, fields=['id', 'name', 'image_small'], context=None): - """ Returns the current document followers as a read result. Used - mainly for Chatter having only one method to call to have - details about users. - """ - user_ids = self.message_get_followers(cr, uid, ids, context=context) - return self.pool.get('res.users').read(cr, uid, user_ids, fields=fields, context=context) - def message_is_follower(self, cr, uid, ids, user_id = None, context=None): """ Check if uid or user_id (if set) is a follower to the current document. diff --git a/addons/mail/static/src/js/mail.js b/addons/mail/static/src/js/mail.js index 9612f73a6b8..a6d6f17958e 100644 --- a/addons/mail/static/src/js/mail.js +++ b/addons/mail/static/src/js/mail.js @@ -164,18 +164,6 @@ openerp.mail = function(session) { * 5. 'My Label' */ - /** Removes html tags, except b, em, br, ul, li */ - do_text_remove_html_tags: function (string) { - var html = $('
').text(string.replace(/\s+/g, ' ')).html().replace(new RegExp('<(/)?(b|em|br|br /|ul|li|div)\\s*>', 'gi'), '<$1$2>'); - return html; - }, - - /** Replaces line breaks by html line breaks (br) */ - do_text_nl2br: function (str, is_xhtml) { - var break_tag = (is_xhtml || typeof is_xhtml === 'undefined') ? '
' : '
'; - return (str + '').replace(/([^>\r\n]?)(\r\n|\n\r|\r|\n)/g, '$1'+ break_tag +'$2'); - }, - /* Add a prefix before each new line of the original string */ do_text_quote: function (str, prefix) { return str.replace(/([^>\r\n]?)(\r\n|\n\r|\r|\n)/g, '$1'+ break_tag +'$2' + prefix || '> '); @@ -191,28 +179,6 @@ openerp.mail = function(session) { do_replace_expressions: function (string) { var self = this; var icon_list = ['al', 'pinky'] - /* shortcut to user: @login */ - var regex_login = new RegExp(/(^|\s)@((\w|@|\.)*)/g); - var regex_res = regex_login.exec(string); - while (regex_res != null) { - var login = regex_res[2]; - string = string.replace(regex_res[0], regex_res[1] + '@' + login + ''); - regex_res = regex_login.exec(string); - } - /* shortcut for internal document */ - var regex_login = new RegExp(/(^|\s)\[(\w+).(\w+),(\d)\|*((\w|[@ .,])*)\]/g); - var regex_res = regex_login.exec(string); - while (regex_res != null) { - var res_model = regex_res[2] + '.' + regex_res[3]; - var res_id = regex_res[4]; - if (! regex_res[5]) { - var label = res_model + ':' + res_id } - else { - var label = regex_res[5]; - } - string = string.replace(regex_res[0], regex_res[1] + ' Date: Sun, 19 Aug 2012 19:53:09 +0200 Subject: [PATCH 062/436] [IMP] removing some options and requirement of ds_thread bzr revid: fp@openerp.com-20120819175309-9c3bl70yg10mmkqr --- addons/mail/mail_group_view.xml | 1 + addons/mail/mail_message_view.xml | 4 +-- addons/mail/static/src/js/mail.js | 45 ++++++++++++++++++++----------- 3 files changed, 33 insertions(+), 17 deletions(-) diff --git a/addons/mail/mail_group_view.xml b/addons/mail/mail_group_view.xml index b1d7971e2ac..bc8624cc0ba 100644 --- a/addons/mail/mail_group_view.xml +++ b/addons/mail/mail_group_view.xml @@ -1,6 +1,7 @@ + Discussion Group mail.wall diff --git a/addons/mail/mail_message_view.xml b/addons/mail/mail_message_view.xml index e7c4f9df494..b31b94eaa61 100644 --- a/addons/mail/mail_message_view.xml +++ b/addons/mail/mail_message_view.xml @@ -227,13 +227,13 @@ News Feed mail.wall - + My Feeds mail.wall - + diff --git a/addons/mail/static/src/js/mail.js b/addons/mail/static/src/js/mail.js index a6d6f17958e..3eb5018b9fd 100644 --- a/addons/mail/static/src/js/mail.js +++ b/addons/mail/static/src/js/mail.js @@ -498,7 +498,6 @@ openerp.mail = function(session) { // datasets and internal vars this.ds = new session.web.DataSetSearch(this, this.params.res_model); - this.ds_users = new session.web.DataSetSearch(this, 'res.users'); this.ds_msg = new session.web.DataSetSearch(this, 'mail.message'); this.comments_structure = {'root_ids': [], 'new_root_ids': [], 'msgs': {}, 'tree_struct': {}, 'model_to_root_ids': {}}; // display customization vars @@ -805,7 +804,6 @@ openerp.mail = function(session) { this.params.thread_level = this.params.thread_level || 0; this.thread = null; this.ds = new session.web.DataSet(this, this.view.model); - this.ds_users = new session.web.DataSet(this, 'res.users'); }, start: function() { @@ -866,23 +864,38 @@ openerp.mail = function(session) { */ init: function (parent, params) { this._super(parent); + this.params = {}; - this.params.limit = params.limit || 25; + // this.params.limit = params.limit || 25; + this.params.domain = params.domain || []; this.params.context = params.context || {}; + + // FP Note: do we need this ? + // Should be in domain/context instead ? this.params.res_model = params.res_model || false; this.params.res_id = params.res_id || false; - this.params.search_view_id = params.search_view_id || false; + + // Do we need this ? + // this.params.search_view_id = params.search_view_id || false; + this.params.thread_level = params.thread_level || 1; + + // FP Note: looks strange, this is not the way to build a tree structure this.comments_structure = {'root_ids': [], 'new_root_ids': [], 'msgs': {}, 'tree_struct': {}, 'model_to_root_ids': {}}; - this.display_show_more = true; + + // FP Note: I changed: always true + // this.display_show_more = true; + this.thread_list = []; this.search = {'domain': [], 'context': {}, 'groupby': {}} this.search_results = {'domain': [], 'context': {}, 'groupby': {}} // datasets this.ds_msg = new session.web.DataSet(this, 'mail.message'); - this.ds_thread = new session.web.DataSet(this, 'mail.thread'); - this.ds_users = new session.web.DataSet(this, 'res.users'); + + // I don't think we need this ? + // message_read and others methods must be on mail.message + // this.ds_thread = new session.web.DataSet(this, 'mail.thread'); }, start: function () { @@ -891,7 +904,7 @@ openerp.mail = function(session) { // add events this.add_event_handlers(); // load mail.message search view - var search_view_ready = this.load_search_view(this.params.search_view_id, {}, false); + var search_view_ready = this.load_search_view({}, false); // load composition form var compose_done = this.instantiate_composition_form(); // fetch first threads @@ -940,9 +953,9 @@ openerp.mail = function(session) { * @param {Object} defaults ?? * @param {Boolean} hidden some kind of trick we do not care here */ - load_search_view: function (view_id, defaults, hidden) { + load_search_view: function (defaults, hidden) { var self = this; - this.searchview = new session.web.SearchView(this, this.ds_msg, view_id || false, defaults || {}, hidden || false); + this.searchview = new session.web.SearchView(this, this.ds_msg, false, defaults || {}, hidden || false); var search_view_loaded = this.searchview.appendTo(this.$element.find('.oe_view_manager_view_search')); return $.when(search_view_loaded).then(function () { self.searchview.on_search.add(self.do_searchview_search); @@ -985,7 +998,6 @@ openerp.mail = function(session) { init_and_fetch_comments: function() { this.search['domain'] = _.union(this.params.domain, this.search_results.domain); this.search['context'] = _.extend(this.params.context, this.search_results.context); - this.display_show_more = true; this.comments_structure = {'root_ids': [], 'new_root_ids': [], 'msgs': {}, 'tree_struct': {}, 'model_to_root_ids': {}}; this.$element.find('ul.oe_mail_wall_threads').empty(); return this.fetch_comments(this.params.limit, 0); @@ -1004,7 +1016,8 @@ openerp.mail = function(session) { else var fetch_domain = this.search['domain']; if (additional_context) var fetch_context = _.extend(this.search['context'], additional_context); else var fetch_context = this.search['context']; - return this.ds_thread.call('message_read', + // FP Note: call to review + return this.ds_msg.call('message_read', [[this.session.uid], true, [], (limit || 0), (offset || 0), fetch_domain, fetch_context]).then(this.proxy('display_comments')); }, @@ -1052,9 +1065,11 @@ openerp.mail = function(session) { /** Display update: show more button */ do_update_show_more: function (new_value) { - if (new_value != undefined) this.display_show_more = new_value; - if (this.display_show_more) this.$element.find('div.oe_mail_wall_more:last').show(); - else this.$element.find('div.oe_mail_wall_more:last').hide(); + if (new_value) { + this.$element.find('div.oe_mail_wall_more:last').show(); + } else { + this.$element.find('div.oe_mail_wall_more:last').hide(); + } }, /** Action: Shows more discussions */ From 432740bf6057af488360426344878badfccc47db Mon Sep 17 00:00:00 2001 From: Fabien Pinckaers Date: Sun, 19 Aug 2012 22:39:41 +0200 Subject: [PATCH 063/436] [DUMMY] temporary add TODO.txt to keep track of remaining cleanings bzr revid: fp@openerp.com-20120819203941-8yllg5g3i9mmorsl --- addons/mail/TODO.txt | 23 ++++++++++++++++ addons/mail/static/src/js/mail.js | 44 ------------------------------- 2 files changed, 23 insertions(+), 44 deletions(-) create mode 100644 addons/mail/TODO.txt diff --git a/addons/mail/TODO.txt b/addons/mail/TODO.txt new file mode 100644 index 00000000000..96c12dc17b8 --- /dev/null +++ b/addons/mail/TODO.txt @@ -0,0 +1,23 @@ +Web Stuff to Do: +* replace comments_structure by a real tree structure +* rewrite completly records_struct_add_records +* remove records_struct_update_after_display: +* remove all is_wall stuff and pass real arguments +* remove init_and_fetch_comments +* rename fetch_comments into message_read +* implement display_current_user --> check why it's present two times + +Backend: +* remove _message_search_ancestor_ids and implement in message_read (must use browse records instead of IDS) +* remove message_search_get_domain +* remove message_search (object can overwrite message_ids if they want a custom list of messages) +* remove _message_find_partners +* remove _message_find_user_id +* remove message_thread_followers +* remove method message_is_follower (conflict with column of same name) +* remove message_subscribe_users and use message_subscribe instead +* remove message_unsubscribe_get_command + +To Check: +* message_forward for mail gateway + diff --git a/addons/mail/static/src/js/mail.js b/addons/mail/static/src/js/mail.js index 3eb5018b9fd..09e4d7dae3d 100644 --- a/addons/mail/static/src/js/mail.js +++ b/addons/mail/static/src/js/mail.js @@ -164,11 +164,6 @@ openerp.mail = function(session) { * 5. 'My Label' */ - /* Add a prefix before each new line of the original string */ - do_text_quote: function (str, prefix) { - return str.replace(/([^>\r\n]?)(\r\n|\n\r|\r|\n)/g, '$1'+ break_tag +'$2' + prefix || '> '); - }, - /** * Replaces some expressions * - @login - shorcut to link to a res.user, given its login @@ -191,45 +186,6 @@ openerp.mail = function(session) { return string; }, - /** - * Checks a string to find an expression that will be replaced - * by an internal link and requiring a name_get to replace - * the expression. - * :param mapping: structure to keep a trace of internal links mapping - * mapping['model'] = { - * name_get': [[id,label], [id,label], ...] - * 'to_fetch_ids': [id, id, ...] - * } - * CURRENTLY NOT IMPLEMENTED */ - do_check_for_name_get_mapping: function(string, mapping) { - /* shortcut to user: @login */ - //var regex_login = new RegExp(/(^|\s)@((\w|@|\.)*)/g); - //var regex_res = regex_login.exec(string); - //while (regex_res != null) { - //var login = regex_res[2]; - //if (! ('res.users' in this.map_hash)) { this.map_hash['res.users']['name'] = []; } - //this.map_hash['res.users']['login'].push(login); - //regex_res = regex_login.exec(string); - //} - /* document link with name_get: [res.model,name] */ - /* internal link with id: [res.model,id], or [res.model,id|display_name] */ - //var regex_intlink = new RegExp(/(^|\s)#(\w*[a-zA-Z_]+\w*)\.(\w+[a-zA-Z_]+\w*),(\w+)/g); - //regex_res = regex_intlink.exec(string); - //while (regex_res != null) { - //var res_model = regex_res[2] + '.' + regex_res[3]; - //var res_name = regex_res[4]; - //if (! (res_model in this.map_hash)) { this.map_hash[res_model]['name'] = []; } - //this.map_hash[res_model]['name'].push(res_name); - //regex_res = regex_intlink.exec(string); - //} - }, - - /** - * Updates the mapping; check for to_fetch_ids for each recorded - * model, and perform a name_get to update the mapping. - * CURRENTLY NOT IMPLEMENTED */ - do_update_name_get_mapping: function(mapping) { - }, }; From 26881fb462cdf655e621e1807f7abcc12d704aa1 Mon Sep 17 00:00:00 2001 From: Fabien Pinckaers Date: Sun, 19 Aug 2012 22:40:45 +0200 Subject: [PATCH 064/436] [DUMMY] temporary add TODO.txt to keep track of remaining cleanings bzr revid: fp@openerp.com-20120819204045-wz4r82kmh87j0s3b --- addons/mail/TODO.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addons/mail/TODO.txt b/addons/mail/TODO.txt index 96c12dc17b8..4d35702526b 100644 --- a/addons/mail/TODO.txt +++ b/addons/mail/TODO.txt @@ -19,5 +19,5 @@ Backend: * remove message_unsubscribe_get_command To Check: -* message_forward for mail gateway +* message_forward for mail gateway (redundant with notifications by emails ?) From 6741e7a653063d71fd2cbce5da101c631166de7d Mon Sep 17 00:00:00 2001 From: Fabien Pinckaers Date: Sun, 19 Aug 2012 22:44:21 +0200 Subject: [PATCH 065/436] imp bzr revid: fp@openerp.com-20120819204421-mbcxtc9uxx0zjcra --- addons/mail/TODO.txt | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/addons/mail/TODO.txt b/addons/mail/TODO.txt index 4d35702526b..96eafc203e7 100644 --- a/addons/mail/TODO.txt +++ b/addons/mail/TODO.txt @@ -1,12 +1,3 @@ -Web Stuff to Do: -* replace comments_structure by a real tree structure -* rewrite completly records_struct_add_records -* remove records_struct_update_after_display: -* remove all is_wall stuff and pass real arguments -* remove init_and_fetch_comments -* rename fetch_comments into message_read -* implement display_current_user --> check why it's present two times - Backend: * remove _message_search_ancestor_ids and implement in message_read (must use browse records instead of IDS) * remove message_search_get_domain @@ -18,6 +9,15 @@ Backend: * remove message_subscribe_users and use message_subscribe instead * remove message_unsubscribe_get_command +Web Stuff to Do: +* replace comments_structure by a real tree structure, provided by message_read directly +* rewrite completly records_struct_add_records, merge existing messages with those coming from another message_read +* remove records_struct_update_after_display: +* remove all is_wall stuff and pass real arguments +* remove init_and_fetch_comments +* rename fetch_comments into message_read +* implement display_current_user --> check why it's present two times + To Check: * message_forward for mail gateway (redundant with notifications by emails ?) - +* check if addons modules rely on some stuff changed in the aboce backend section From 18353be9e154c9488d01d988500910c78a99c26c Mon Sep 17 00:00:00 2001 From: Fabien Pinckaers Date: Mon, 20 Aug 2012 09:09:56 +0200 Subject: [PATCH 066/436] [IMP] todo.txt bzr revid: fp@tinyerp.com-20120820070956-el8ltgfqxwstvep1 --- addons/mail/TODO.txt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/addons/mail/TODO.txt b/addons/mail/TODO.txt index 96eafc203e7..764046da9d0 100644 --- a/addons/mail/TODO.txt +++ b/addons/mail/TODO.txt @@ -1,7 +1,8 @@ Backend: * remove _message_search_ancestor_ids and implement in message_read (must use browse records instead of IDS) +* I propose to move message_read in mail.message and rename to message_fetch * remove message_search_get_domain -* remove message_search (object can overwrite message_ids if they want a custom list of messages) +* remove message_search (objects can overwrite message_ids if they want a custom list of messages) * remove _message_find_partners * remove _message_find_user_id * remove message_thread_followers From d862e2daa604f54cc65185e91d015e9db9b77651 Mon Sep 17 00:00:00 2001 From: Fabien Pinckaers Date: Mon, 20 Aug 2012 09:26:03 +0200 Subject: [PATCH 067/436] [IMP] removing unused code bzr revid: fp@tinyerp.com-20120820072603-94bb7eki8ec01gap --- addons/mail/TODO.txt | 7 ++-- addons/mail/mail_thread.py | 86 +++----------------------------------- 2 files changed, 8 insertions(+), 85 deletions(-) diff --git a/addons/mail/TODO.txt b/addons/mail/TODO.txt index 764046da9d0..f5def9b7423 100644 --- a/addons/mail/TODO.txt +++ b/addons/mail/TODO.txt @@ -5,10 +5,9 @@ Backend: * remove message_search (objects can overwrite message_ids if they want a custom list of messages) * remove _message_find_partners * remove _message_find_user_id -* remove message_thread_followers -* remove method message_is_follower (conflict with column of same name) -* remove message_subscribe_users and use message_subscribe instead -* remove message_unsubscribe_get_command + +I was wrong: + * remove message_subscribe_users and use message_subscribe instead Web Stuff to Do: * replace comments_structure by a real tree structure, provided by message_read directly diff --git a/addons/mail/mail_thread.py b/addons/mail/mail_thread.py index 615dcf9c306..fe746dc6a33 100644 --- a/addons/mail/mail_thread.py +++ b/addons/mail/mail_thread.py @@ -394,9 +394,7 @@ class mail_thread(osv.Model): Once the target model is known, its ``message_new`` method is called with the new message (if the thread record did not exist) - or its ``message_update`` method (if it did). Finally, - ``message_forward`` is called to automatically notify other - people that should receive this message. + or its ``message_update`` method (if it did). :param string model: the fallback model to use if the message does not match any of the currently configured mail aliases @@ -505,66 +503,6 @@ class mail_thread(osv.Model): self.write(cr, uid, ids, update_vals, context=context) return True - def message_thread_followers(self, cr, uid, ids, context=None): - """ Returns a list of email addresses of the people following - this thread, including the sender of each mail, and the - people who were in CC of the messages, if any. - """ - res = {} - if isinstance(ids, (str, int, long)): - ids = [long(ids)] - for thread in self.browse(cr, uid, ids, context=context): - l = set() - for message in thread.message_ids: - l.add((message.user_id and message.user_id.email) or '') - l.add(message.email_from or '') - l.add(message.email_cc or '') - res[thread.id] = filter(None, l) - return res - - def message_forward(self, cr, uid, model, thread_ids, msg, email_error=False, context=None): - """Sends an email to all people following the given threads. - The emails are forwarded immediately, not queued for sending, - and not archived. - - :param str model: thread model - :param list thread_ids: ids of the thread records - :param msg: email.message.Message object to forward - :param email_error: optional email address to notify in case - of any delivery error during the forward. - :return: True - """ - model_pool = self.pool.get(model) - smtp_server_obj = self.pool.get('ir.mail_server') - for res in model_pool.browse(cr, uid, thread_ids, context=context): - if hasattr(model_pool, 'message_thread_followers'): - followers = model_pool.message_thread_followers(cr, uid, [res.id])[res.id] - else: - followers = self.message_thread_followers(cr, uid, [res.id])[res.id] - message_followers_emails = tools.email_split(','.join(filter(None, followers))) - message_recipients = tools.email_split(','.join(filter(None, - [decode(msg['from']), - decode(msg['to']), - decode(msg['cc'])]))) - forward_to = [i for i in message_followers_emails if (i and (i not in message_recipients))] - if forward_to: - # TODO: we need an interface for this for all types of objects, not just leads - if model_pool._columns.get('section_id'): - del msg['reply-to'] - msg['reply-to'] = res.section_id.reply_to - - smtp_from, = tools.email_split(msg['from']) - msg['from'] = smtp_from - msg['to'] = ", ".join(forward_to) - msg['message-id'] = tools.generate_tracking_message_id(res.id) - if not smtp_server_obj.send_email(cr, uid, msg) and email_error: - subj = msg['subject'] - del msg['subject'], msg['to'], msg['cc'], msg['bcc'] - msg['subject'] = _('[OpenERP-Forward-Failed] %s') % subj - msg['to'] = email_error - smtp_server_obj.send_email(cr, uid, msg) - return True - def parse_message(self, message, save_original=False, context=None): """Parses a string or email.message.Message representing an RFC-2822 email, and returns a generic dict holding the @@ -754,18 +692,6 @@ class mail_thread(osv.Model): # Subscription mechanism #------------------------------------------------------ - def message_is_follower(self, cr, uid, ids, user_id = None, context=None): - """ Check if uid or user_id (if set) is a follower to the current - document. - - :param user_id: if set, check is done on user_id; if not set - check is done on uid - """ - sub_user_id = uid if user_id is None else user_id - if sub_user_id in self.message_get_followers(cr, uid, ids, context=context): - return True - return False - def message_subscribe_users(self, cr, uid, ids, user_ids=None, context=None): if not user_ids: user_ids = [uid] partners = {} @@ -806,13 +732,11 @@ class mail_thread(osv.Model): uid instead :param return: new value of followers, for Chatter """ - to_unsubscribe_uids = [uid] if user_ids is None else user_ids - write_res = self.write(cr, uid, ids, {'message_follower_ids': self.message_unsubscribe_get_command(cr, uid, to_unsubscribe_uids, context)}, context=context) - return [follower.id for thread in self.browse(cr, uid, ids, context=context) for follower in thread.message_follower_ids] + partner_id = self.pool.get('res.users').browse(cr, uid, uid, context=context).partner_id.id + self.write(cr, uid, ids, [(3, partner_id)], context=context) - def message_unsubscribe_get_command(self, cr, uid, follower_ids, context=None): - """ Generate the many2many command to remove followers. """ - return [(3, id) for id in follower_ids] + # FP Note: do we need this ? + return [follower.id for thread in self.browse(cr, uid, ids, context=context) for follower in thread.message_follower_ids] #------------------------------------------------------ # Notification API From 6e9edd77c0ad026816f5a8aae958c70fe877be73 Mon Sep 17 00:00:00 2001 From: Fabien Pinckaers Date: Mon, 20 Aug 2012 09:26:42 +0200 Subject: [PATCH 068/436] imp bzr revid: fp@tinyerp.com-20120820072642-k1zx5o9lk0e26ikf --- addons/mail/TODO.txt | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/addons/mail/TODO.txt b/addons/mail/TODO.txt index f5def9b7423..e32f2b56027 100644 --- a/addons/mail/TODO.txt +++ b/addons/mail/TODO.txt @@ -19,5 +19,8 @@ Web Stuff to Do: * implement display_current_user --> check why it's present two times To Check: -* message_forward for mail gateway (redundant with notifications by emails ?) * check if addons modules rely on some stuff changed in the aboce backend section + +To ask ODO: +* message_forward for mail gateway (redundant with notifications by emails ?) + -> I removed it to rely on notifications From b386ee3c4a9f5457d34eace947e51acca73c152f Mon Sep 17 00:00:00 2001 From: Fabien Pinckaers Date: Mon, 20 Aug 2012 09:42:42 +0200 Subject: [PATCH 069/436] [IMP] remove unused code, I still have to rewrite message_read bzr revid: fp@tinyerp.com-20120820074242-w6n1nmwu315r2bhj --- addons/mail/TODO.txt | 11 ++--- addons/mail/mail_thread.py | 94 ++++---------------------------------- 2 files changed, 13 insertions(+), 92 deletions(-) diff --git a/addons/mail/TODO.txt b/addons/mail/TODO.txt index e32f2b56027..9dd9262888f 100644 --- a/addons/mail/TODO.txt +++ b/addons/mail/TODO.txt @@ -1,13 +1,12 @@ Backend: * remove _message_search_ancestor_ids and implement in message_read (must use browse records instead of IDS) * I propose to move message_read in mail.message and rename to message_fetch -* remove message_search_get_domain -* remove message_search (objects can overwrite message_ids if they want a custom list of messages) -* remove _message_find_partners -* remove _message_find_user_id -I was wrong: - * remove message_subscribe_users and use message_subscribe instead +* remove message_search (objects can overwrite message_ids if they want a custom list of messages) + - requires to reimplement message_read + +* remove _message_find_user_id and use _message_find_partners + - it requires to change the route code Web Stuff to Do: * replace comments_structure by a real tree structure, provided by message_read directly diff --git a/addons/mail/mail_thread.py b/addons/mail/mail_thread.py index fe746dc6a33..614adcf9697 100644 --- a/addons/mail/mail_thread.py +++ b/addons/mail/mail_thread.py @@ -163,86 +163,6 @@ class mail_thread(osv.Model): # Message loading #------------------------------------------------------ - def _message_search_ancestor_ids(self, cr, uid, ids, child_ids, ancestor_ids, context=None): - """ Given message child_ids ids, find their ancestors until ancestor_ids - using their parent_id relationship. - - :param child_ids: the first nodes of the search - :param ancestor_ids: list of ancestors. When the search reach an - ancestor, it stops. - """ - def _get_parent_ids(message_list, ancestor_ids, child_ids): - """ Tool function: return the list of parent_ids of messages - contained in message_list. Parents that are in ancestor_ids - or in child_ids are not returned. """ - return [message['parent_id'][0] for message in message_list - if message['parent_id'] - and message['parent_id'][0] not in ancestor_ids - and message['parent_id'][0] not in child_ids - ] - - message_obj = self.pool.get('mail.message') - messages_temp = message_obj.read(cr, uid, child_ids, ['id', 'parent_id'], context=context) - parent_ids = _get_parent_ids(messages_temp, ancestor_ids, child_ids) - child_ids += parent_ids - cur_iter = 0; max_iter = 100; # avoid infinite loop - while (parent_ids and (cur_iter < max_iter)): - cur_iter += 1 - messages_temp = message_obj.read(cr, uid, parent_ids, ['id', 'parent_id'], context=context) - parent_ids = _get_parent_ids(messages_temp, ancestor_ids, child_ids) - child_ids += parent_ids - if (cur_iter > max_iter): - _logger.warning("Possible infinite loop in _message_search_ancestor_ids. "\ - "Note that this algorithm is intended to check for cycle in "\ - "message graph, leading to a curious error. Have fun.") - return child_ids - - def message_search_get_domain(self, cr, uid, ids, context=None): - """ OpenChatter feature: get the domain to search the messages related - to a document. mail.thread defines the default behavior as - being messages with model = self._name, id in ids. - This method should be overridden if a model has to implement a - particular behavior. - """ - return ['&', ('res_id', 'in', ids), ('model', '=', self._name)] - - def message_search(self, cr, uid, ids, fetch_ancestors=False, ancestor_ids=None, - limit=100, offset=0, domain=None, count=False, context=None): - """ OpenChatter feature: return thread messages ids according to the - search domain given by ``message_search_get_domain``. - - It is possible to add in the search the parent of messages by - setting the fetch_ancestors flag to True. In that case, using - the parent_id relationship, the method returns the id list according - to the search domain, but then calls ``_message_search_ancestor_ids`` - that will add to the list the ancestors ids. The search is limited - to parent messages having an id in ancestor_ids or having - parent_id set to False. - - If ``count==True``, the number of ids is returned instead of the - id list. The count is done by hand instead of passing it as an - argument to the search call because we might want to perform - a research including parent messages until some ancestor_ids. - - :param fetch_ancestors: performs an ascended search; will add - to fetched msgs all their parents until - ancestor_ids - :param ancestor_ids: used when fetching ancestors - :param domain: domain to add to the search; especially child_of - is interesting when dealing with threaded display. - Note that the added domain is anded with the - default domain. - :param limit, offset, count, context: as usual - """ - search_domain = self.message_search_get_domain(cr, uid, ids, context=context) - if domain: - search_domain += domain - message_obj = self.pool.get('mail.message') - message_res = message_obj.search(cr, uid, search_domain, limit=limit, offset=offset, count=count, context=context) - if not count and fetch_ancestors: - message_res += self._message_search_ancestor_ids(cr, uid, ids, message_res, ancestor_ids, context=context) - return message_res - def message_read(self, cr, uid, ids, fetch_ancestors=False, ancestor_ids=None, limit=100, offset=0, domain=None, context=None): """ OpenChatter feature: read the messages related to some threads. @@ -279,6 +199,10 @@ class mail_thread(osv.Model): messages = sorted(messages, key=lambda d: (-d['id'])) return messages + #------------------------------------------------------ + # Mail gateway + #------------------------------------------------------ + def _message_find_partners(self, cr, uid, message, headers=['From'], context=None): s = ', '.join([decode(message.get(h)) for h in headers]) mails = tools.email_split(s) @@ -289,14 +213,12 @@ class mail_thread(osv.Model): def _message_find_user_id(self, cr, uid, message, context=None): from_local_part = tools.email_split(decode(message.get('From')))[0] - user_ids = self.pool.get('res.users').search(cr, uid, [('login', '=', from_local_part)], context=context) + # FP Note: canonification required, the minimu: .lower() + user_ids = self.pool.get('res.users').search(cr, uid, ['|', + ('login', '=', from_local_part), + ('email', '=', from_local_part)], context=context) return user_ids[0] if user_ids else uid - #------------------------------------------------------ - # Mail gateway - #------------------------------------------------------ - # message_process will call either message_new or message_update. - def message_route(self, cr, uid, message, model=None, thread_id=None, custom_values=None, context=None): """Attempt to figure out the correct target model, thread_id, From f73372e7a25cd480e309b31bb818ccb0e93e36cc Mon Sep 17 00:00:00 2001 From: Fabien Pinckaers Date: Mon, 20 Aug 2012 11:06:36 +0200 Subject: [PATCH 070/436] [IMP] better implementation of message read, no ancestror yet bzr revid: fp@tinyerp.com-20120820090636-gyyp3n47sjs0fbhc --- addons/mail/TODO.txt | 14 ++++----- addons/mail/mail_message.py | 59 +++++++++++++++++++++++++++++++++++++ addons/mail/mail_thread.py | 40 ------------------------- 3 files changed, 64 insertions(+), 49 deletions(-) diff --git a/addons/mail/TODO.txt b/addons/mail/TODO.txt index 9dd9262888f..eb6d0ccd761 100644 --- a/addons/mail/TODO.txt +++ b/addons/mail/TODO.txt @@ -1,25 +1,21 @@ Backend: -* remove _message_search_ancestor_ids and implement in message_read (must use browse records instead of IDS) * I propose to move message_read in mail.message and rename to message_fetch -* remove message_search (objects can overwrite message_ids if they want a custom list of messages) - - requires to reimplement message_read - * remove _message_find_user_id and use _message_find_partners - it requires to change the route code Web Stuff to Do: * replace comments_structure by a real tree structure, provided by message_read directly * rewrite completly records_struct_add_records, merge existing messages with those coming from another message_read -* remove records_struct_update_after_display: + -> false records with domain message_read +* remove records_struct_update_after_display * remove all is_wall stuff and pass real arguments * remove init_and_fetch_comments * rename fetch_comments into message_read -* implement display_current_user --> check why it's present two times +Not Sure: + * implement display_current_user --> check why it's present two times To Check: * check if addons modules rely on some stuff changed in the aboce backend section -To ask ODO: -* message_forward for mail gateway (redundant with notifications by emails ?) - -> I removed it to rely on notifications + diff --git a/addons/mail/mail_message.py b/addons/mail/mail_message.py index a722dca3797..15cb1218218 100644 --- a/addons/mail/mail_message.py +++ b/addons/mail/mail_message.py @@ -120,6 +120,65 @@ class mail_message(osv.Model): 'author_id': _get_default_author } + + #------------------------------------------------------ + # Message loading for web interface + #------------------------------------------------------ + + _limit = 10 + def _message_read(self, cr, uid, messages, domain=[], thread_level=0, fetch_ancestors=False, context=None): + result = [] + for msg in messages[:-1]: + if len(result)<(self._limit-1): + record = { + 'id': msg.id, + 'type': msg.type, + 'attachment_ids': msg.attachment_ids.name_get(context=context), + 'body': msg.body, + 'model': msg.model, + 'res_id': msg.res_id, + 'record_name': msg.record_name, + 'subject': msg.subject, + 'date': msg.date, + 'author_id': msg.author_id.id + } + if thread_level>0: + dom = [('parent_id','=', msg.id)] + if thread_level==1: + dom = [('parent_id','child_of', [msg.id])] + newids = self.search(cr, uid, domain+dom, context=context, limit=self._limit) + objs = self.browse(cr, uid, newids, context=context) + record['child_ids'] = self._message_read(cr, uid, objs, domain+dom, thread_level-1, context=context) + result.append(record) + else: + result.append({ + 'type': 'expandable', + 'domain': [('id','<=', msg.id)]+domain, + 'context': context + }) + break + + def message_read(self, cr, uid, ids=False, domain=[], thread_level=0, fetch_ancestors=False, context=None): + """ + If IDS are provided, fetch these records, otherwise use the domain to + fetch the matching records. After having fetched the records provided + by IDS, it will fetch children (according to thread_level) and/or the + parents if fetch_ancestrors is True. + + Return [ + + ] + """ + if ids is False: + dom = [] + if thread_level>0: + dom = [('parent_id', '=', False)] + ids = self.search(cr, uid, domain+dom, context=context, limit=10) + + messages = self.browse(cr, uid, ids, context=context) + return self._message_read(cr, uid, messages, thread_level, domain=domain, context=context) + + #------------------------------------------------------ # Email api #------------------------------------------------------ diff --git a/addons/mail/mail_thread.py b/addons/mail/mail_thread.py index 614adcf9697..c962e36fc2c 100644 --- a/addons/mail/mail_thread.py +++ b/addons/mail/mail_thread.py @@ -159,46 +159,6 @@ class mail_thread(osv.Model): return [('message_unread','=',True)] return [] - #------------------------------------------------------ - # Message loading - #------------------------------------------------------ - - def message_read(self, cr, uid, ids, fetch_ancestors=False, ancestor_ids=None, - limit=100, offset=0, domain=None, context=None): - """ OpenChatter feature: read the messages related to some threads. - This method is used mainly the Chatter widget, to directly have - read result instead of searching then reading. - - Please see message_search for more information about the parameters. - """ - print 'MSG READ', uid, ids, fetch_ancestors, ancestor_ids, limit, offset, domain, context - message_ids = self.message_search(cr, uid, ids, fetch_ancestors, ancestor_ids, - limit, offset, domain, context=context) - messages = self.pool.get('mail.message').read(cr, uid, message_ids, context=context) - - """ Retrieve all attachments names """ - map_id_to_name = dict((attachment_id, '') for message in messages for attachment_id in message['attachment_ids']) - - ids = map_id_to_name.keys() - names = self.pool.get('ir.attachment').name_get(cr, uid, ids, context=context) - - # convert the list of tuples into a dictionnary - for name in names: - map_id_to_name[name[0]] = name[1] - - # give corresponding ids and names to each message - for msg in messages: - msg["attachments"] = [] - - for attach_id in msg["attachment_ids"]: - msg["attachments"].append({'id': attach_id, 'name': map_id_to_name[attach_id]}) - - # Set the threads as read - self.message_mark_as_read(cr, uid, ids, context=context) - # Sort and return the messages - messages = sorted(messages, key=lambda d: (-d['id'])) - return messages - #------------------------------------------------------ # Mail gateway #------------------------------------------------------ From 7a44634a38ff5daacecd78e8362a95221c63ffae Mon Sep 17 00:00:00 2001 From: Fabien Pinckaers Date: Mon, 20 Aug 2012 11:39:22 +0200 Subject: [PATCH 071/436] [IMP] message_read with ancestrors and threads and expandable domains bzr revid: fp@tinyerp.com-20120820093922-b3aokqftfcptwmpp --- addons/mail/mail_message.py | 46 ++++++++++++++++++++++++++----------- 1 file changed, 32 insertions(+), 14 deletions(-) diff --git a/addons/mail/mail_message.py b/addons/mail/mail_message.py index 15cb1218218..4da9115620c 100644 --- a/addons/mail/mail_message.py +++ b/addons/mail/mail_message.py @@ -126,22 +126,27 @@ class mail_message(osv.Model): #------------------------------------------------------ _limit = 10 + def _message_dict_get(self, cr, uid, msg, context={}): + return { + 'id': msg.id, + 'type': msg.type, + 'attachment_ids': msg.attachment_ids.name_get(context=context), + 'body': msg.body, + 'model': msg.model, + 'res_id': msg.res_id, + 'record_name': msg.record_name, + 'subject': msg.subject, + 'date': msg.date, + 'author_id': msg.author_id.id, + 'child_ids': [] # will be filled after by _message_read + } + def _message_read(self, cr, uid, messages, domain=[], thread_level=0, fetch_ancestors=False, context=None): result = [] - for msg in messages[:-1]: + tree = {} # key: ID, value: record + for msg in messages: if len(result)<(self._limit-1): - record = { - 'id': msg.id, - 'type': msg.type, - 'attachment_ids': msg.attachment_ids.name_get(context=context), - 'body': msg.body, - 'model': msg.model, - 'res_id': msg.res_id, - 'record_name': msg.record_name, - 'subject': msg.subject, - 'date': msg.date, - 'author_id': msg.author_id.id - } + record = self._message_dict_get(msg) if thread_level>0: dom = [('parent_id','=', msg.id)] if thread_level==1: @@ -149,7 +154,20 @@ class mail_message(osv.Model): newids = self.search(cr, uid, domain+dom, context=context, limit=self._limit) objs = self.browse(cr, uid, newids, context=context) record['child_ids'] = self._message_read(cr, uid, objs, domain+dom, thread_level-1, context=context) - result.append(record) + + if fetch_ancestrors and record.parent_id: + while record.parent_id: + if record.parent_id.id in tree: + record_parent = tree[record.parent_id.id] + else: + record_parent = self._message_dict(record.parent_id) + if record.parent_id.parent_id: + tree[record.parent_id.id] = record.parent_id + record_parent['child_ids'].append(record) + record = record.parent_id + if record.id not in tree: + result.append(record) + tree[record.id] = record else: result.append({ 'type': 'expandable', From 3169475b3e4644c2ba5f4f2dace75ad0b397a624 Mon Sep 17 00:00:00 2001 From: Fabien Pinckaers Date: Mon, 20 Aug 2012 12:14:34 +0200 Subject: [PATCH 072/436] [IMP] message_read on mail.message bzr revid: fp@tinyerp.com-20120820101434-429mgbhbuk1wjqa2 --- addons/base_calendar/base_calendar.py | 60 +++++++++++++-------------- addons/mail/mail_message.py | 34 ++++++++------- 2 files changed, 49 insertions(+), 45 deletions(-) diff --git a/addons/base_calendar/base_calendar.py b/addons/base_calendar/base_calendar.py index be3244f8a5b..d1b8709e1e9 100644 --- a/addons/base_calendar/base_calendar.py +++ b/addons/base_calendar/base_calendar.py @@ -1616,36 +1616,36 @@ class calendar_todo(osv.osv): calendar_todo() -class ir_attachment(osv.osv): - _name = 'ir.attachment' - _inherit = 'ir.attachment' - - def search_count(self, cr, user, args, context=None): - new_args = [] - for domain_item in args: - if isinstance(domain_item, (list, tuple)) and len(domain_item) == 3 and domain_item[0] == 'res_id': - new_args.append((domain_item[0], domain_item[1], base_calendar_id2real_id(domain_item[2]))) - else: - new_args.append(domain_item) - return super(ir_attachment, self).search_count(cr, user, new_args, context) - - def create(self, cr, uid, vals, context=None): - if context: - id = context.get('default_res_id', False) - context.update({'default_res_id' : base_calendar_id2real_id(id)}) - return super(ir_attachment, self).create(cr, uid, vals, context=context) - - def search(self, cr, uid, args, offset=0, limit=None, order=None, - context=None, count=False): - new_args = [] - for domain_item in args: - if isinstance(domain_item, (list, tuple)) and len(domain_item) == 3 and domain_item[0] == 'res_id': - new_args.append((domain_item[0], domain_item[1], base_calendar_id2real_id(domain_item[2]))) - else: - new_args.append(domain_item) - return super(ir_attachment, self).search(cr, uid, new_args, offset=offset, - limit=limit, order=order, context=context, count=False) -ir_attachment() +#class ir_attachment(osv.osv): +# _name = 'ir.attachment' +# _inherit = 'ir.attachment' +# +# def search_count(self, cr, user, args, context=None): +# new_args = [] +# for domain_item in args: +# if isinstance(domain_item, (list, tuple)) and len(domain_item) == 3 and domain_item[0] == 'res_id': +# new_args.append((domain_item[0], domain_item[1], base_calendar_id2real_id(domain_item[2]))) +# else: +# new_args.append(domain_item) +# return super(ir_attachment, self).search_count(cr, user, new_args, context) +# +# def create(self, cr, uid, vals, context=None): +# if context: +# id = context.get('default_res_id', False) +# context.update({'default_res_id' : base_calendar_id2real_id(id)}) +# return super(ir_attachment, self).create(cr, uid, vals, context=context) +# +# def search(self, cr, uid, args, offset=0, limit=None, order=None, +# context=None, count=False): +# new_args = [] +# for domain_item in args: +# if isinstance(domain_item, (list, tuple)) and len(domain_item) == 3 and domain_item[0] == 'res_id': +# new_args.append((domain_item[0], domain_item[1], base_calendar_id2real_id(domain_item[2]))) +# else: +# new_args.append(domain_item) +# return super(ir_attachment, self).search(cr, uid, new_args, offset=offset, +# limit=limit, order=order, context=context, count=False) +#ir_attachment() class ir_values(osv.osv): _inherit = 'ir.values' diff --git a/addons/mail/mail_message.py b/addons/mail/mail_message.py index 4da9115620c..59750f9b34b 100644 --- a/addons/mail/mail_message.py +++ b/addons/mail/mail_message.py @@ -127,10 +127,11 @@ class mail_message(osv.Model): _limit = 10 def _message_dict_get(self, cr, uid, msg, context={}): + attachs = self.pool.get('ir.attachment').name_get(cr, uid, [x.id for x in msg.attachment_ids], context=context) return { 'id': msg.id, 'type': msg.type, - 'attachment_ids': msg.attachment_ids.name_get(context=context), + 'attachment_ids': attachs, 'body': msg.body, 'model': msg.model, 'res_id': msg.res_id, @@ -146,28 +147,29 @@ class mail_message(osv.Model): tree = {} # key: ID, value: record for msg in messages: if len(result)<(self._limit-1): - record = self._message_dict_get(msg) + record = self._message_dict_get(cr, uid, msg, context=context) if thread_level>0: dom = [('parent_id','=', msg.id)] if thread_level==1: - dom = [('parent_id','child_of', [msg.id])] + dom = [('parent_id','child_of', [x.id for x in msg.child_ids])] newids = self.search(cr, uid, domain+dom, context=context, limit=self._limit) objs = self.browse(cr, uid, newids, context=context) record['child_ids'] = self._message_read(cr, uid, objs, domain+dom, thread_level-1, context=context) - if fetch_ancestrors and record.parent_id: - while record.parent_id: - if record.parent_id.id in tree: - record_parent = tree[record.parent_id.id] + if fetch_ancestors and msg.parent_id: + while msg.parent_id: + if msg.parent_id.id in tree: + record_parent = tree[msg.parent_id.id] else: - record_parent = self._message_dict(record.parent_id) - if record.parent_id.parent_id: - tree[record.parent_id.id] = record.parent_id + record_parent = self._message_dict_get(cr, uid, msg.parent_id, context=context) + if msg.parent_id.parent_id: + tree[msg.parent_id.id] = record_parent record_parent['child_ids'].append(record) - record = record.parent_id - if record.id not in tree: + record = record_parent + msg = msg.parent_id + if msg.id not in tree: result.append(record) - tree[record.id] = record + tree[msg.id] = record else: result.append({ 'type': 'expandable', @@ -175,13 +177,14 @@ class mail_message(osv.Model): 'context': context }) break + return result def message_read(self, cr, uid, ids=False, domain=[], thread_level=0, fetch_ancestors=False, context=None): """ If IDS are provided, fetch these records, otherwise use the domain to fetch the matching records. After having fetched the records provided by IDS, it will fetch children (according to thread_level) and/or the - parents if fetch_ancestrors is True. + parents if fetch_ancestors is True. Return [ @@ -194,7 +197,8 @@ class mail_message(osv.Model): ids = self.search(cr, uid, domain+dom, context=context, limit=10) messages = self.browse(cr, uid, ids, context=context) - return self._message_read(cr, uid, messages, thread_level, domain=domain, context=context) + result = self._message_read(cr, uid, messages, thread_level=thread_level, domain=domain, fetch_ancestors=fetch_ancestors, context=context) + return result #------------------------------------------------------ From 0f364e1acd876183e5957db99e73d6869b684d08 Mon Sep 17 00:00:00 2001 From: Jigar Amin - OpenERP Date: Mon, 20 Aug 2012 16:10:55 +0530 Subject: [PATCH 073/436] [IMP] removed Import action if create = false bzr revid: jam@tinyerp.com-20120820104055-mgw4y5csa31poa33 --- addons/web/static/src/js/view_list.js | 6 +++++- addons/web/static/src/xml/base.xml | 12 +++++------- 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/addons/web/static/src/js/view_list.js b/addons/web/static/src/js/view_list.js index fa6f48b37fa..bf8ee05d925 100644 --- a/addons/web/static/src/js/view_list.js +++ b/addons/web/static/src/js/view_list.js @@ -351,8 +351,12 @@ instance.web.ListView = instance.web.View.extend( /** @lends instance.web.ListVi if (!this.sidebar && this.options.$sidebar) { this.sidebar = new instance.web.Sidebar(this); this.sidebar.appendTo(this.options.$sidebar); + if (self._is_action_enabled('create')){ + this.sidebar.add_items('other', [ + { label: _t("Import"), callback: this.on_sidebar_import } + ]); + } this.sidebar.add_items('other', [ - { label: _t("Import"), callback: this.on_sidebar_import }, { label: _t("Export"), callback: this.on_sidebar_export } ]); if (self._is_action_enabled('delete')){ diff --git a/addons/web/static/src/xml/base.xml b/addons/web/static/src/xml/base.xml index 2d86b40f44f..61a1a834d0d 100644 --- a/addons/web/static/src/xml/base.xml +++ b/addons/web/static/src/xml/base.xml @@ -671,14 +671,12 @@ From 7fc19eba9919fd34e408b3db56b896087769aa80 Mon Sep 17 00:00:00 2001 From: Fabien Pinckaers Date: Mon, 20 Aug 2012 12:45:00 +0200 Subject: [PATCH 074/436] [IMP] removed ancestror, merged with thread level bzr revid: fp@tinyerp.com-20120820104500-8m246hwoexejjobt --- addons/mail/mail_message.py | 27 +++++++++------------------ 1 file changed, 9 insertions(+), 18 deletions(-) diff --git a/addons/mail/mail_message.py b/addons/mail/mail_message.py index 59750f9b34b..6e6445dae82 100644 --- a/addons/mail/mail_message.py +++ b/addons/mail/mail_message.py @@ -142,21 +142,13 @@ class mail_message(osv.Model): 'child_ids': [] # will be filled after by _message_read } - def _message_read(self, cr, uid, messages, domain=[], thread_level=0, fetch_ancestors=False, context=None): + def _message_read(self, cr, uid, messages, domain=[], thread_level=0, context=None): result = [] tree = {} # key: ID, value: record for msg in messages: if len(result)<(self._limit-1): record = self._message_dict_get(cr, uid, msg, context=context) - if thread_level>0: - dom = [('parent_id','=', msg.id)] - if thread_level==1: - dom = [('parent_id','child_of', [x.id for x in msg.child_ids])] - newids = self.search(cr, uid, domain+dom, context=context, limit=self._limit) - objs = self.browse(cr, uid, newids, context=context) - record['child_ids'] = self._message_read(cr, uid, objs, domain+dom, thread_level-1, context=context) - - if fetch_ancestors and msg.parent_id: + if thread_level and msg.parent_id: while msg.parent_id: if msg.parent_id.id in tree: record_parent = tree[msg.parent_id.id] @@ -179,25 +171,24 @@ class mail_message(osv.Model): break return result - def message_read(self, cr, uid, ids=False, domain=[], thread_level=0, fetch_ancestors=False, context=None): + def message_read(self, cr, uid, ids=False, domain=[], thread_level=0, context=None): """ If IDS are provided, fetch these records, otherwise use the domain to fetch the matching records. After having fetched the records provided - by IDS, it will fetch children (according to thread_level) and/or the - parents if fetch_ancestors is True. + by IDS, it will fetch children (according to thread_level). Return [ ] """ if ids is False: - dom = [] - if thread_level>0: - dom = [('parent_id', '=', False)] - ids = self.search(cr, uid, domain+dom, context=context, limit=10) + ids = self.search(cr, uid, domain, context=context, limit=10) + + + # FP Todo: flatten to max X level of mail_thread messages = self.browse(cr, uid, ids, context=context) - result = self._message_read(cr, uid, messages, thread_level=thread_level, domain=domain, fetch_ancestors=fetch_ancestors, context=context) + result = self._message_read(cr, uid, messages, thread_level=thread_level, domain=domain, context=context) return result From e18c83106c45c58a0c4b1f59efbe50eaaa76b9d8 Mon Sep 17 00:00:00 2001 From: Fabien Pinckaers Date: Mon, 20 Aug 2012 12:45:56 +0200 Subject: [PATCH 075/436] imp bzr revid: fp@tinyerp.com-20120820104556-li9wfg2riqb85c8a --- addons/mail/tests/write_test.py | 79 +++++++++++++++++++++++++++++++++ 1 file changed, 79 insertions(+) create mode 100644 addons/mail/tests/write_test.py diff --git a/addons/mail/tests/write_test.py b/addons/mail/tests/write_test.py new file mode 100644 index 00000000000..920fbb92dbe --- /dev/null +++ b/addons/mail/tests/write_test.py @@ -0,0 +1,79 @@ +############################################################################## +# +# Copyright (c) 2004 TINY SPRL. (http://tiny.be) All Rights Reserved. +# Fabien Pinckaers +# +# WARNING: This program as such is intended to be used by professional +# programmers who take the whole responsability of assessing all potential +# consequences resulting from its eventual inadequacies and bugs +# End users who are looking for a ready-to-use solution with commercial +# garantees and support are strongly adviced to contract a Free Software +# Service Company +# +# This program is Free Software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# 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 General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +# +############################################################################## + +# +# This module test all RPC methods +# + +import xmlrpclib + +uid = 1 +passwd='admin' +server = 'localhost' +db = 'trunk' + +sock = xmlrpclib.ServerProxy('http://'+server+':8069/xmlrpc/object') + +def _print_data(data, level=0): + for d in data: + print ' '*level, d['id'] + _print_data(d['child_ids'], level+1) + +print 'With Domain', [('model','=','mail.group')], 'thread_level' +data = sock.execute(db, uid, passwd, 'mail.message', 'message_read', False, [('model','=','mail.group')], 1) +_print_data(data) + +print 'With Domain', [('model','=','mail.group')], 'no thread_level' +data = sock.execute(db, uid, passwd, 'mail.message', 'message_read', False, [('model','=','mail.group')], 0) +_print_data(data) + + +print 'With Domain', [('model','=','mail.group'), ('parent_id','=',False)], 'thread_level=0' +data = sock.execute(db, uid, passwd, 'mail.message', 'message_read', False, [('model','=','mail.group'), ('parent_id','=',False)], 0) +_print_data(data) + +print 'With Domain', [('model','=','mail.group'), ('parent_id','=',False)], 'thread_level=2' +data = sock.execute(db, uid, passwd, 'mail.message', 'message_read', False, [('model','=','mail.group'), ('parent_id','=',False)], 2) +_print_data(data) + +print 'Fixed IDS', [2,3,41,43], 'thread_level' +data = sock.execute(db, uid, passwd, 'mail.message', 'message_read', [2,3,41,43], [], 1) +_print_data(data) + +print 'Fixed IDS', [2,43], 'no thread_level' +data = sock.execute(db, uid, passwd, 'mail.message', 'message_read', [2,43], [], 0) +_print_data(data) + +print 'Fixed IDS', [2,43], 'thread_level' +data = sock.execute(db, uid, passwd, 'mail.message', 'message_read', [2,43], [], 1) +_print_data(data) + +print 'domain [id in 3,41]', 'thread_level' +data = sock.execute(db, uid, passwd, 'mail.message', 'message_read', False, [('id','in',[3,41])], 1) +_print_data(data) + From 6021b4473aa0b5cdf436e61320ffcba503de53f3 Mon Sep 17 00:00:00 2001 From: Fabien Pinckaers Date: Mon, 20 Aug 2012 12:52:52 +0200 Subject: [PATCH 076/436] [IMP] message_read on mail.message bzr revid: fp@tinyerp.com-20120820105252-o1eqy5yfqb66hga9 --- openerp/osv/fields.py | 1 + 1 file changed, 1 insertion(+) diff --git a/openerp/osv/fields.py b/openerp/osv/fields.py index da025845759..496aab5407b 100644 --- a/openerp/osv/fields.py +++ b/openerp/osv/fields.py @@ -517,6 +517,7 @@ class one2many(_column): dom = self._domain if isinstance(self._domain, type(lambda: None)): dom = self._domain(obj) + print self._obj, user, dom + [(self._fields_id, 'in', ids)] ids2 = obj.pool.get(self._obj).search(cr, user, dom + [(self._fields_id, 'in', ids)], limit=self._limit, context=context) for r in obj.pool.get(self._obj)._read_flat(cr, user, ids2, [self._fields_id], context=context, load='_classic_write'): if r[self._fields_id] in res: From 1d4dbc3440772893e58f77d5cd20f0202380d444 Mon Sep 17 00:00:00 2001 From: Fabien Pinckaers Date: Mon, 20 Aug 2012 12:57:49 +0200 Subject: [PATCH 077/436] [IMP] message_read bzr revid: fp@tinyerp.com-20120820105749-y5fqaztvj5put395 --- addons/mail/mail_message.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/addons/mail/mail_message.py b/addons/mail/mail_message.py index 6e6445dae82..b8746042664 100644 --- a/addons/mail/mail_message.py +++ b/addons/mail/mail_message.py @@ -128,6 +128,8 @@ class mail_message(osv.Model): _limit = 10 def _message_dict_get(self, cr, uid, msg, context={}): attachs = self.pool.get('ir.attachment').name_get(cr, uid, [x.id for x in msg.attachment_ids], context=context) + author = self.pool.get('res.partner').name_get(cr, uid, [msg.author_id.id,], context=context)[0] + partner_ids = self.pool.get('res.partner').name_get(cr, uid, [x.id for x in msg.partner_ids], context=context) return { 'id': msg.id, 'type': msg.type, @@ -138,11 +140,13 @@ class mail_message(osv.Model): 'record_name': msg.record_name, 'subject': msg.subject, 'date': msg.date, - 'author_id': msg.author_id.id, + 'author_id': author, + 'partner_ids': partner_ids, 'child_ids': [] # will be filled after by _message_read } def _message_read(self, cr, uid, messages, domain=[], thread_level=0, context=None): + context = context or {} result = [] tree = {} # key: ID, value: record for msg in messages: @@ -169,6 +173,8 @@ class mail_message(osv.Model): 'context': context }) break + for r in result: + print r return result def message_read(self, cr, uid, ids=False, domain=[], thread_level=0, context=None): From 20981ac1a30cf05f82f2467b65664931b5ebf579 Mon Sep 17 00:00:00 2001 From: "Ajay Chauhan (OpenERP)" Date: Mon, 20 Aug 2012 16:29:42 +0530 Subject: [PATCH 078/436] [IMP] account: make little change in onchange method. bzr revid: cha@tinyerp.com-20120820105942-zkyrqrebn3s3j9nc --- addons/account/wizard/account_report_common.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/addons/account/wizard/account_report_common.py b/addons/account/wizard/account_report_common.py index f84c4e909a2..604806f6e31 100644 --- a/addons/account/wizard/account_report_common.py +++ b/addons/account/wizard/account_report_common.py @@ -30,11 +30,11 @@ class account_common_report(osv.osv_memory): _description = "Account Common Report" def onchange_chart_id(self, cr, uid, ids, chart_account_id=False, context=None): + res = {} if chart_account_id: company_id = self.pool.get('account.account').browse(cr, uid, chart_account_id, context=context).company_id.id - else: - return {'value': {'company_id': False}} - return {'value': {'company_id': company_id}} + res['value'] = {'company_id': company_id} + return res _columns = { 'chart_account_id': fields.many2one('account.account', 'Chart of Account', help='Select Charts of Accounts', required=True, domain = [('parent_id','=',False)]), From a054299277113f1ae09f47b0cd52f9752ef10a82 Mon Sep 17 00:00:00 2001 From: Fabien Pinckaers Date: Mon, 20 Aug 2012 13:04:55 +0200 Subject: [PATCH 079/436] imp bzr revid: fp@tinyerp.com-20120820110455-jelu0xl7jil36q18 --- openerp/osv/fields.py | 1 - 1 file changed, 1 deletion(-) diff --git a/openerp/osv/fields.py b/openerp/osv/fields.py index 496aab5407b..da025845759 100644 --- a/openerp/osv/fields.py +++ b/openerp/osv/fields.py @@ -517,7 +517,6 @@ class one2many(_column): dom = self._domain if isinstance(self._domain, type(lambda: None)): dom = self._domain(obj) - print self._obj, user, dom + [(self._fields_id, 'in', ids)] ids2 = obj.pool.get(self._obj).search(cr, user, dom + [(self._fields_id, 'in', ids)], limit=self._limit, context=context) for r in obj.pool.get(self._obj)._read_flat(cr, user, ids2, [self._fields_id], context=context, load='_classic_write'): if r[self._fields_id] in res: From b7c078b651b2b51be779e704514896e818078488 Mon Sep 17 00:00:00 2001 From: Fabien Pinckaers Date: Mon, 20 Aug 2012 13:31:50 +0200 Subject: [PATCH 080/436] [IMP] some fixes bzr revid: fp@tinyerp.com-20120820113150-bgcsylj7k64s26iq --- addons/mail/mail_message.py | 53 ++++++++++++++++--------------- addons/mail/mail_message_view.xml | 2 +- 2 files changed, 28 insertions(+), 27 deletions(-) diff --git a/addons/mail/mail_message.py b/addons/mail/mail_message.py index b8746042664..20a14ce9203 100644 --- a/addons/mail/mail_message.py +++ b/addons/mail/mail_message.py @@ -45,6 +45,7 @@ def decode(text): if text: text = decode_header(text.replace('\r', '')) return ''.join([tools.ustr(x[0], x[1]) for x in text]) + class mail_message(osv.Model): """Model holding messages: system notification (replacing res.log notifications), comments (for OpenChatter feature). This model also @@ -106,6 +107,7 @@ class mail_message(osv.Model): string='Message Record Name', help="Name get of the related document."), + 'notification_ids': fields.one2many('mail.notification', 'message_id', 'Notifications'), 'subject': fields.char('Subject', size=128), 'date': fields.datetime('Date'), 'message_id': fields.char('Message-Id', size=256, help='Message unique identifier', select=1, readonly=1), @@ -127,6 +129,8 @@ class mail_message(osv.Model): _limit = 10 def _message_dict_get(self, cr, uid, msg, context={}): + print msg + print msg.attachment_ids attachs = self.pool.get('ir.attachment').name_get(cr, uid, [x.id for x in msg.attachment_ids], context=context) author = self.pool.get('res.partner').name_get(cr, uid, [msg.author_id.id,], context=context)[0] partner_ids = self.pool.get('res.partner').name_get(cr, uid, [x.id for x in msg.partner_ids], context=context) @@ -142,11 +146,26 @@ class mail_message(osv.Model): 'date': msg.date, 'author_id': author, 'partner_ids': partner_ids, - 'child_ids': [] # will be filled after by _message_read + 'child_ids': [] } - def _message_read(self, cr, uid, messages, domain=[], thread_level=0, context=None): + def message_read(self, cr, uid, ids=False, domain=[], thread_level=0, context=None): + """ + If IDS are provided, fetch these records, otherwise use the domain to + fetch the matching records. After having fetched the records provided + by IDS, it will fetch children (according to thread_level). + + Return [ + + ] + """ context = context or {} + if ids is False: + ids = self.search(cr, uid, domain, context=context, limit=10) + + # FP Todo: flatten to max X level of mail_thread + messages = self.browse(cr, uid, ids, context=context) + result = [] tree = {} # key: ID, value: record for msg in messages: @@ -170,31 +189,10 @@ class mail_message(osv.Model): result.append({ 'type': 'expandable', 'domain': [('id','<=', msg.id)]+domain, - 'context': context + 'context': context, + 'thread_level': thread_level # should be improve accodting to level of records }) break - for r in result: - print r - return result - - def message_read(self, cr, uid, ids=False, domain=[], thread_level=0, context=None): - """ - If IDS are provided, fetch these records, otherwise use the domain to - fetch the matching records. After having fetched the records provided - by IDS, it will fetch children (according to thread_level). - - Return [ - - ] - """ - if ids is False: - ids = self.search(cr, uid, domain, context=context, limit=10) - - - # FP Todo: flatten to max X level of mail_thread - - messages = self.browse(cr, uid, ids, context=context) - result = self._message_read(cr, uid, messages, thread_level=thread_level, domain=domain, context=context) return result @@ -230,7 +228,10 @@ class mail_message(osv.Model): ], context=context) notifications = {} for notification in not_obj.browse(cr, uid, not_ids, context=context): - ids.remove(notification.message_id.id) + if notification.message_id.id in ids: + pass + # FO Note: we should put this again !!! + #ids.remove(notification.message_id.id) # check messages according to related documents res_ids = {} diff --git a/addons/mail/mail_message_view.xml b/addons/mail/mail_message_view.xml index b31b94eaa61..9532bf6b17f 100644 --- a/addons/mail/mail_message_view.xml +++ b/addons/mail/mail_message_view.xml @@ -227,7 +227,7 @@ News Feed mail.wall - + From 7a2e5962abfce3548dcca1c0265b58b553d455e1 Mon Sep 17 00:00:00 2001 From: Fabien Pinckaers Date: Mon, 20 Aug 2012 13:32:10 +0200 Subject: [PATCH 081/436] fix bzr revid: fp@tinyerp.com-20120820113210-4lxap11yk9xxkv9d --- addons/mail/mail_message.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/addons/mail/mail_message.py b/addons/mail/mail_message.py index 20a14ce9203..4cb545c146c 100644 --- a/addons/mail/mail_message.py +++ b/addons/mail/mail_message.py @@ -129,8 +129,6 @@ class mail_message(osv.Model): _limit = 10 def _message_dict_get(self, cr, uid, msg, context={}): - print msg - print msg.attachment_ids attachs = self.pool.get('ir.attachment').name_get(cr, uid, [x.id for x in msg.attachment_ids], context=context) author = self.pool.get('res.partner').name_get(cr, uid, [msg.author_id.id,], context=context)[0] partner_ids = self.pool.get('res.partner').name_get(cr, uid, [x.id for x in msg.partner_ids], context=context) From 25233cd5137255f707f6830f720b2ca76f6567aa Mon Sep 17 00:00:00 2001 From: Fabien Pinckaers Date: Mon, 20 Aug 2012 13:32:27 +0200 Subject: [PATCH 082/436] [IMP] add user_ids on partners bzr revid: fp@tinyerp.com-20120820113227-5k9d7m41vjcof26c --- openerp/addons/base/res/res_partner.py | 1 + 1 file changed, 1 insertion(+) diff --git a/openerp/addons/base/res/res_partner.py b/openerp/addons/base/res/res_partner.py index 93d12228ebe..d9ae1d8595e 100644 --- a/openerp/addons/base/res/res_partner.py +++ b/openerp/addons/base/res/res_partner.py @@ -215,6 +215,7 @@ class res_partner(osv.osv): "Use this field anywhere a small image is required."), 'company_id': fields.many2one('res.company', 'Company', select=1), 'color': fields.integer('Color Index'), + 'user_ids': fields.one2many('res.users', 'partner_id', 'Users'), 'contact_address': fields.function(_address_display, type='char', string='Complete Address'), } From 264885ad77cf8321d26cf7793fe31a7c07a2056a Mon Sep 17 00:00:00 2001 From: Fabien Pinckaers Date: Mon, 20 Aug 2012 13:33:51 +0200 Subject: [PATCH 083/436] [IMP] domains bzr revid: fp@tinyerp.com-20120820113351-wo982pj7ii2e78k0 --- addons/mail/mail_message_view.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addons/mail/mail_message_view.xml b/addons/mail/mail_message_view.xml index 9532bf6b17f..e29027712ba 100644 --- a/addons/mail/mail_message_view.xml +++ b/addons/mail/mail_message_view.xml @@ -233,7 +233,7 @@ My Feeds mail.wall - + From c1b7de054cbc59fa93f367d46ab31e31eb794801 Mon Sep 17 00:00:00 2001 From: Jigar Amin - OpenERP Date: Mon, 20 Aug 2012 17:50:20 +0530 Subject: [PATCH 084/436] [IMP] Kanban type action edit and delete on edit and delete falase bzr revid: jam@tinyerp.com-20120820122020-8tszb43bivfgq72v --- addons/web_kanban/static/src/js/kanban.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/addons/web_kanban/static/src/js/kanban.js b/addons/web_kanban/static/src/js/kanban.js index 154df0f8b6b..96f28dff196 100644 --- a/addons/web_kanban/static/src/js/kanban.js +++ b/addons/web_kanban/static/src/js/kanban.js @@ -734,7 +734,9 @@ instance.web_kanban.KanbanRecord = instance.web.Widget.extend({ var $action = $(this), type = $action.data('type') || 'button', method = 'do_action_' + (type === 'action' ? 'object' : type); - if (_.str.startsWith(type, 'switch_')) { + if ((type === 'edit' || type === 'delete') && ! self.view._is_action_enabled(type)){ + self.view.open_record(self.id); + } else if (_.str.startsWith(type, 'switch_')) { self.view.do_switch_view(type.substr(7)); } else if (typeof self[method] === 'function') { self[method]($action); From 2be5f9424ae0784dd4219885242576f6e9b9b429 Mon Sep 17 00:00:00 2001 From: Fabien Pinckaers Date: Mon, 20 Aug 2012 14:42:05 +0200 Subject: [PATCH 085/436] fix bzr revid: fp@tinyerp.com-20120820124205-qomccfiwd6zjtc1w --- addons/mail/mail_thread.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/addons/mail/mail_thread.py b/addons/mail/mail_thread.py index c962e36fc2c..d77049226e2 100644 --- a/addons/mail/mail_thread.py +++ b/addons/mail/mail_thread.py @@ -69,7 +69,7 @@ class mail_thread(osv.Model): # TODO: may be we should make it _inherit ir.needaction def _get_is_follower(self, cr, uid, ids, name, args, context=None): - subobj = self.pool.get('mail.subscription') + subobj = self.pool.get('mail.followers') subids = subobj.search(cr, uid, [ ('res_model','=',self._name), ('res_id', 'in', ids), @@ -109,7 +109,7 @@ class mail_thread(osv.Model): _columns = { 'message_is_follower': fields.function(_get_is_follower, type='boolean', string='Is a Follower'), - 'message_follower_ids': fields.many2many('res.partner', 'mail_subscription', 'res_id', 'partner_id', + 'message_follower_ids': fields.many2many('res.partner', 'mail_followers', 'res_id', 'partner_id', # FP Note: implement this domain=lambda self: [('res_model','=',self._name)], string='Followers'), 'message_ids': fields.one2many('mail.message', 'res_id', From d6395938cebac6245910022572daea543c3910fb Mon Sep 17 00:00:00 2001 From: Fabien Pinckaers Date: Mon, 20 Aug 2012 14:55:25 +0200 Subject: [PATCH 086/436] [FIX] small fixes bzr revid: fp@tinyerp.com-20120820125525-daa8ghuqse6csgbg --- addons/mail/mail_thread.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/addons/mail/mail_thread.py b/addons/mail/mail_thread.py index d77049226e2..a0d2ae5065f 100644 --- a/addons/mail/mail_thread.py +++ b/addons/mail/mail_thread.py @@ -73,10 +73,10 @@ class mail_thread(osv.Model): subids = subobj.search(cr, uid, [ ('res_model','=',self._name), ('res_id', 'in', ids), - ('user_id','=',uid)], context=context) + ('partner_id.user_ids','in',[uid])], context=context) result = dict.fromkeys(ids, False) for sub in subobj.browse(cr, uid, subids, context=context): - result[res_id] = True + result[sub.res_id] = True return result def _get_message_data(self, cr, uid, ids, name, args, context=None): @@ -84,17 +84,17 @@ class mail_thread(osv.Model): for id in ids: res[id] = { 'message_unread': False, - 'message_Summary': '' + 'message_summary': '' } nobj = self.pool.get('mail.notification') - notifs = nobj.search(cr, uid, [ - ('user_id','=',uid), + nids = nobj.search(cr, uid, [ + ('partner_id.user_ids','in',[uid]), ('message_id.res_id','in', ids), ('message_id.model','=', self._name), ('read','=',False) ], context=context) for notif in nobj.browse(cr, uid, nids, context=context): - res[notif.message_id.id]['message_unread'] = True + res[notif.message_id.res_id]['message_unread'] = True for thread in self.browse(cr, uid, ids, context=context): message_ids = thread.message_ids From de096a967d30eedd64c603940edced3729f886fd Mon Sep 17 00:00:00 2001 From: Fabien Pinckaers Date: Mon, 20 Aug 2012 15:02:32 +0200 Subject: [PATCH 087/436] fix bzr revid: fp@tinyerp.com-20120820130232-qrw1zk5i2fhe22qk --- 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 a0d2ae5065f..544ebf20a8e 100644 --- a/addons/mail/mail_thread.py +++ b/addons/mail/mail_thread.py @@ -560,7 +560,7 @@ class mail_thread(osv.Model): value = kwargs value.update( { - 'model': self._name, + 'model': res_id and self._name or False, 'res_id': res_id, 'body': body, 'subject': subject, From ccb78bfdd9ebc528e423e1b563b19dfee701308c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thibault=20Delavall=C3=A9e?= Date: Mon, 20 Aug 2012 15:06:31 +0200 Subject: [PATCH 088/436] [FIX] fetchmail: fetchmail_server_id added to mail.mail form view. bzr revid: tde@openerp.com-20120820130631-3kn57wnvj4ssyrjt --- addons/fetchmail/fetchmail.py | 6 +++--- addons/fetchmail/fetchmail_view.xml | 8 ++++---- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/addons/fetchmail/fetchmail.py b/addons/fetchmail/fetchmail.py index 544a4f81b96..24c977bba1d 100644 --- a/addons/fetchmail/fetchmail.py +++ b/addons/fetchmail/fetchmail.py @@ -77,7 +77,7 @@ class fetchmail_server(osv.osv): "emails to the existing conversations (documents)."), 'priority': fields.integer('Server Priority', readonly=True, states={'draft':[('readonly', False)]}, help="Defines the order of processing, " "lower values mean higher priority"), - 'message_ids': fields.one2many('mail.message', 'fetchmail_server_id', 'Messages', readonly=True), + 'message_ids': fields.one2many('mail.mail', 'fetchmail_server_id', 'Messages', readonly=True), 'configuration' : fields.text('Configuration'), 'script' : fields.char('Script', readonly=True, size=64), } @@ -232,8 +232,8 @@ openerp_mailgate.py -u %(uid)d -p PASSWORD -o %(model)s -d %(dbname)s --host=HOS server.write({'date': time.strftime(tools.DEFAULT_SERVER_DATETIME_FORMAT)}) return True -class mail_message(osv.osv): - _inherit = "mail.message" +class mail_mail(osv.osv): + _inherit = "mail.mail" _columns = { 'fetchmail_server_id': fields.many2one('fetchmail.server', "Inbound Mail Server", readonly=True, diff --git a/addons/fetchmail/fetchmail_view.xml b/addons/fetchmail/fetchmail_view.xml index 7c049dedbb8..bdb1ebf2fa5 100644 --- a/addons/fetchmail/fetchmail_view.xml +++ b/addons/fetchmail/fetchmail_view.xml @@ -104,11 +104,11 @@ /> - mail.message.list.fetchmail - mail.message - + mail.mail.list.fetchmail + mail.mail + - + From daf0f7412ebda47b40106e3bbd4907c4f7221c44 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thibault=20Delavall=C3=A9e?= Date: Mon, 20 Aug 2012 15:07:30 +0200 Subject: [PATCH 089/436] [WIP] mail.js: first cleaning of Wall. bzr revid: tde@openerp.com-20120820130730-lpjt9wpj7ggqq4yv --- addons/mail/mail_message_view.xml | 4 +- addons/mail/static/src/js/mail.js | 214 ++++++++++++---------------- addons/mail/static/src/xml/mail.xml | 6 +- 3 files changed, 94 insertions(+), 130 deletions(-) diff --git a/addons/mail/mail_message_view.xml b/addons/mail/mail_message_view.xml index e29027712ba..8b3d21677d0 100644 --- a/addons/mail/mail_message_view.xml +++ b/addons/mail/mail_message_view.xml @@ -227,13 +227,13 @@ News Feed mail.wall - + My Feeds mail.wall - + diff --git a/addons/mail/static/src/js/mail.js b/addons/mail/static/src/js/mail.js index 09e4d7dae3d..a5633dcfc2a 100644 --- a/addons/mail/static/src/js/mail.js +++ b/addons/mail/static/src/js/mail.js @@ -42,18 +42,6 @@ openerp.mail = function(session) { mail.ChatterUtils = { - /** - * mail_int_mapping: structure to keep a trace of internal links mapping - * mail_int_mapping['model'] = { - * 'name_get': [[id,label], [id,label], ...] - * 'fetch_ids': [id, id, ...] } */ - //var mail_int_mapping = {}; - - /** - * mail_msg_struct: structure to orrganize chatter messages - */ - //var mail_msg_struct = {}; // TODO: USE IT OR NOT :) - /* generic chatter events binding */ bind_events: function(widget) { // event: click on an internal link to a document: model, login @@ -100,50 +88,50 @@ openerp.mail = function(session) { * 'for_thread_msgs': [records], * 'ancestors': [msg_ids], } } */ - records_struct_add_records: function(cs, records, parent_id) { - var cur_iter = 0; var max_iter = 10; var modif = true; - while ( modif && (cur_iter++) < max_iter) { - modif = false; - _(records).each(function (record) { - // root and not yet recorded - if ( (record.parent_id == false || record.parent_id[0] == parent_id) && ! cs['msgs'][record.id]) { - // add to model -> root_list ids - if (! cs['model_to_root_ids'][record.model]) cs['model_to_root_ids'][record.model] = [record.id]; - else cs['model_to_root_ids'][record.model].push(record.id); - // add root data - cs['new_root_ids'].push(record.id); - // add record - cs['tree_struct'][record.id] = {'level': 0, 'direct_childs': [], 'all_childs': [], 'for_thread_msgs': [record], 'msg_nbr': -1, 'ancestors': []}; - cs['msgs'][record.id] = record; - modif = true; - } - // not yet recorded, but parent is recorded - else if (! cs['msgs'][record.id] && cs['msgs'][record.parent_id[0]]) { - var parent_level = cs['tree_struct'][record.parent_id[0]]['level']; - // update parent structure - cs['tree_struct'][record.parent_id[0]]['direct_childs'].push(record.id); - cs['tree_struct'][record.parent_id[0]]['for_thread_msgs'].push(record); - // update ancestors structure - for (ancestor_id in cs['tree_struct'][record.parent_id[0]]['ancestors']) { - cs['tree_struct'][ancestor_id]['all_childs'].push(record.id); - } - // add record - cs['tree_struct'][record.id] = {'level': parent_level+1, 'direct_childs': [], 'all_childs': [], 'for_thread_msgs': [], 'msg_nbr': -1, 'ancestors': []}; - cs['msgs'][record.id] = record; - modif = true; - } - }); - } - return cs; - }, + // records_struct_add_records: function(cs, records, parent_id) { + // var cur_iter = 0; var max_iter = 10; var modif = true; + // while ( modif && (cur_iter++) < max_iter) { + // modif = false; + // _(records).each(function (record) { + // // root and not yet recorded + // if ( (record.parent_id == false || record.parent_id[0] == parent_id) && ! cs['msgs'][record.id]) { + // // add to model -> root_list ids + // if (! cs['model_to_root_ids'][record.model]) cs['model_to_root_ids'][record.model] = [record.id]; + // else cs['model_to_root_ids'][record.model].push(record.id); + // // add root data + // cs['new_root_ids'].push(record.id); + // // add record + // cs['tree_struct'][record.id] = {'level': 0, 'direct_childs': [], 'all_childs': [], 'for_thread_msgs': [record], 'msg_nbr': -1, 'ancestors': []}; + // cs['msgs'][record.id] = record; + // modif = true; + // } + // // not yet recorded, but parent is recorded + // else if (! cs['msgs'][record.id] && cs['msgs'][record.parent_id[0]]) { + // var parent_level = cs['tree_struct'][record.parent_id[0]]['level']; + // // update parent structure + // cs['tree_struct'][record.parent_id[0]]['direct_childs'].push(record.id); + // cs['tree_struct'][record.parent_id[0]]['for_thread_msgs'].push(record); + // // update ancestors structure + // for (ancestor_id in cs['tree_struct'][record.parent_id[0]]['ancestors']) { + // cs['tree_struct'][ancestor_id]['all_childs'].push(record.id); + // } + // // add record + // cs['tree_struct'][record.id] = {'level': parent_level+1, 'direct_childs': [], 'all_childs': [], 'for_thread_msgs': [], 'msg_nbr': -1, 'ancestors': []}; + // cs['msgs'][record.id] = record; + // modif = true; + // } + // }); + // } + // return cs; + // }, /* copy cs.new_root_ids into cs.root_ids */ - records_struct_update_after_display: function(cs) { - // update TODO - cs['root_ids'] = _.union(cs['root_ids'], cs['new_root_ids']); - cs['new_root_ids'] = []; - return cs; - }, + // records_struct_update_after_display: function(cs) { + // // update TODO + // cs['root_ids'] = _.union(cs['root_ids'], cs['new_root_ids']); + // cs['new_root_ids'] = []; + // return cs; + // }, /** * CONTENT MANIPULATION @@ -623,7 +611,7 @@ openerp.mail = function(session) { display_comments: function (records) { var self = this; // sort the records - mail.ChatterUtils.records_struct_add_records(this.comments_structure, records, this.params.parent_id); + // mail.ChatterUtils.records_struct_add_records(this.comments_structure, records, this.params.parent_id); //build attachments download urls and compute time-relative from dates for (var k in records) { records[k].timerelative = $.timeago(records[k].date); @@ -636,14 +624,14 @@ openerp.mail = function(session) { } _(records).each(function (record) { var sub_msgs = []; - if ((record.parent_id == false || record.parent_id[0] == self.params.parent_id) && self.params.thread_level > 0 ) { - var sub_list = self.comments_structure['tree_struct'][record.id]['direct_childs']; - _(records).each(function (record) { - //if (record.parent_id == false || record.parent_id[0] == self.params.parent_id) return; - if (_.indexOf(sub_list, record.id) != -1) { - sub_msgs.push(record); - } - }); + if ((record.parent_id == undefined || record.parent_id == false || record.parent_id[0] == self.params.parent_id) && self.params.thread_level > 0 ) { + // var sub_list = self.comments_structure['tree_struct'][record.id]['direct_childs']; + // _(records).each(function (record) { + // //if (record.parent_id == false || record.parent_id[0] == self.params.parent_id) return; + // if (_.indexOf(sub_list, record.id) != -1) { + // sub_msgs.push(record); + // } + // }); self.display_comment(record); self.thread = new mail.Thread(self, {'res_model': self.params.res_model, 'res_id': self.params.res_id, 'uid': self.params.uid, 'records': sub_msgs, 'thread_level': (self.params.thread_level-1), 'parent_id': record.id, @@ -655,7 +643,7 @@ openerp.mail = function(session) { self.display_comment(record); } }); - mail.ChatterUtils.records_struct_update_after_display(this.comments_structure); + // mail.ChatterUtils.records_struct_update_after_display(this.comments_structure); // update offset for "More" buttons if (this.params.thread_level == 0) this.params.offset += records.length; }, @@ -666,13 +654,13 @@ openerp.mail = function(session) { if (record.type == 'email') { record.mini_url = ('/mail/static/src/img/email_icon.png'); } else { - record.mini_url = mail.ChatterUtils.get_image(this.session.prefix, this.session.session_id, 'res.users', 'image_small', record.user_id[0]); + record.mini_url = mail.ChatterUtils.get_image(this.session.prefix, this.session.session_id, 'res.partner', 'image_small', record.author_id[0]); } // record.body = mail.ChatterUtils.do_replace_expressions(record.body); // format date according to the user timezone record.date = session.web.format_value(record.date, {type:"datetime"}); // is the user the author ? - record.is_author = mail.ChatterUtils.is_author(this, record.user_id[0]); + record.is_author = mail.ChatterUtils.is_author(this, record.author_id[0]); // render var rendered = session.web.qweb.render('mail.thread.message', {'record': record, 'thread': this, 'params': this.params, 'display': this.display}); // expand feature @@ -756,10 +744,9 @@ openerp.mail = function(session) { init: function() { this._super.apply(this, arguments); - this.params = this.get_definition_options(); + this.params = this.options; this.params.thread_level = this.params.thread_level || 0; this.thread = null; - this.ds = new session.web.DataSet(this, this.view.model); }, start: function() { @@ -785,6 +772,7 @@ openerp.mail = function(session) { this.$element.find('.oe_mail_thread').hide(); return; } + // debugger // create and render Thread widget this.$element.find('div.oe_mail_recthread_main').empty(); if (this.thread) this.thread.destroy(); @@ -815,48 +803,29 @@ openerp.mail = function(session) { * @param {Object} parent parent * @param {Object} [params] * @param {Number} [params.limit=20] number of messages to show and fetch - * @param {Number} [params.search_view_id=false] search view id for messages * @var {Array} comments_structure (see chatter utils) */ - init: function (parent, params) { + init: function (parent, options) { this._super(parent); - - this.params = {}; - // this.params.limit = params.limit || 25; - - this.params.domain = params.domain || []; - this.params.context = params.context || {}; - - // FP Note: do we need this ? + // console.log(options); + this.options = options || {}; + this.options.domain = options.domain || []; + this.options.context = options.context || {}; + // FP Note: do we need this ? --> not sure, use mail.message create instead ? // Should be in domain/context instead ? - this.params.res_model = params.res_model || false; - this.params.res_id = params.res_id || false; - - // Do we need this ? - // this.params.search_view_id = params.search_view_id || false; - - this.params.thread_level = params.thread_level || 1; - - // FP Note: looks strange, this is not the way to build a tree structure - this.comments_structure = {'root_ids': [], 'new_root_ids': [], 'msgs': {}, 'tree_struct': {}, 'model_to_root_ids': {}}; - - // FP Note: I changed: always true - // this.display_show_more = true; - + this.options.context.res_model = options.res_model || 'mail.thread'; + this.options.context.res_id = options.res_id || false; + this.options.thread_level = options.thread_level || 1; this.thread_list = []; this.search = {'domain': [], 'context': {}, 'groupby': {}} this.search_results = {'domain': [], 'context': {}, 'groupby': {}} // datasets this.ds_msg = new session.web.DataSet(this, 'mail.message'); - - // I don't think we need this ? - // message_read and others methods must be on mail.message - // this.ds_thread = new session.web.DataSet(this, 'mail.thread'); + // this.ds_post = new session.web.DataSet(this, this.options.context.res_model); }, start: function () { this._super.apply(this, arguments); - this.display_current_user(); // add events this.add_event_handlers(); // load mail.message search view @@ -864,7 +833,7 @@ openerp.mail = function(session) { // load composition form var compose_done = this.instantiate_composition_form(); // fetch first threads - var comments_ready = this.init_and_fetch_comments(this.params.limit, 0); + var comments_ready = this.init_and_fetch_comments(this.options.limit, 0); return (search_view_ready && comments_ready && compose_done); }, @@ -888,8 +857,8 @@ openerp.mail = function(session) { this.compose_message_widget.destroy(); } this.compose_message_widget = new mail.ComposeMessage(this, { - 'extended_mode': false, 'uid': this.session.uid, 'res_model': this.params.res_model, - 'res_id': this.params.res_id, 'mode': mode || 'comment', 'msg_id': msg_id }); + 'extended_mode': false, 'uid': this.session.uid, 'res_model': this.options.res_model, + 'res_id': this.options.res_id, 'mode': mode || 'comment', 'msg_id': msg_id }); var composition_node = this.$element.find('div.oe_mail_wall_action'); composition_node.empty(); var compose_done = this.compose_message_widget.appendTo(composition_node); @@ -940,10 +909,6 @@ openerp.mail = function(session) { }); }, - display_current_user: function () { - //return this.$element.find('img.oe_mail_msg_image').attr('src', this.thread_get_avatar('res.users', 'avatar', this.session.uid)); - }, - /** * Initializes the wall and calls fetch_comments * @param {Number} limit: number of notifications to fetch @@ -952,11 +917,11 @@ openerp.mail = function(session) { * @param {Array} context */ init_and_fetch_comments: function() { - this.search['domain'] = _.union(this.params.domain, this.search_results.domain); - this.search['context'] = _.extend(this.params.context, this.search_results.context); - this.comments_structure = {'root_ids': [], 'new_root_ids': [], 'msgs': {}, 'tree_struct': {}, 'model_to_root_ids': {}}; + this.search['domain'] = _.union(this.options.domain, this.search_results.domain); + this.search['context'] = _.extend(this.options.context, this.search_results.context); + // this.comments_structure = {'root_ids': [], 'new_root_ids': [], 'msgs': {}, 'tree_struct': {}, 'model_to_root_ids': {}}; this.$element.find('ul.oe_mail_wall_threads').empty(); - return this.fetch_comments(this.params.limit, 0); + return this.fetch_comments(this.options.limit); }, /** @@ -966,40 +931,39 @@ openerp.mail = function(session) { * @param {Array} domain * @param {Array} context */ - fetch_comments: function (limit, offset, additional_domain, additional_context) { + fetch_comments: function (limit, additional_domain, additional_context) { var self = this; if (additional_domain) var fetch_domain = this.search['domain'].concat(additional_domain); else var fetch_domain = this.search['domain']; if (additional_context) var fetch_context = _.extend(this.search['context'], additional_context); else var fetch_context = this.search['context']; - // FP Note: call to review + // FP Note: call to review return this.ds_msg.call('message_read', - [[this.session.uid], true, [], (limit || 0), (offset || 0), fetch_domain, fetch_context]).then(this.proxy('display_comments')); + [[this.session.uid], false, [] ]).then(this.proxy('display_comments')); }, /** * @param {Array} records records to show in threads */ display_comments: function (records) { + console.log(records); + // debugger var self = this; - this.do_update_show_more(records.length >= self.params.limit); - mail.ChatterUtils.records_struct_add_records(this.comments_structure, records, false); - _(this.comments_structure['new_root_ids']).each(function (root_id) { - var records = self.comments_structure.tree_struct[root_id]['for_thread_msgs']; - var model_name = self.comments_structure.msgs[root_id]['model']; - var res_id = self.comments_structure.msgs[root_id]['res_id']; + this.do_update_show_more(records.length >= self.options.limit); + + _(records).each(function (root_record) { + console.log(root_record); + var render_res = session.web.qweb.render('mail.wall_thread_container', {}); $('
  • ').html(render_res).appendTo(self.$element.find('ul.oe_mail_wall_threads')); var thread = new mail.Thread(self, { - 'res_model': model_name, 'res_id': res_id, 'uid': self.session.uid, 'records': records, - 'parent_id': false, 'thread_level': self.params.thread_level, 'show_hide': true, 'is_wall': true} + 'res_model': root_record.model, 'res_id': root_record.res_id, 'uid': self.session.uid, 'records': [root_record], + 'parent_id': false, 'thread_level': self.options.thread_level, 'show_hide': true, 'is_wall': true} ); self.thread_list.push(thread); return thread.appendTo(self.$element.find('li.oe_mail_wall_thread:last')); }); - // update TODO - this.comments_structure['root_ids'] = _.union(this.comments_structure['root_ids'], this.comments_structure['new_root_ids']); - this.comments_structure['new_root_ids'] = []; + }, /** @@ -1013,9 +977,9 @@ openerp.mail = function(session) { var self = this; var model_to_root = {}; var fetch_domain = []; - _(this.comments_structure['model_to_root_ids']).each(function (sc_model, model_name) { - fetch_domain.push('|', ['model', '!=', model_name], '!', ['id', 'child_of', sc_model]); - }); + // _(this.comments_structure['model_to_root_ids']).each(function (sc_model, model_name) { + // fetch_domain.push('|', ['model', '!=', model_name], '!', ['id', 'child_of', sc_model]); + // }); return fetch_domain; }, diff --git a/addons/mail/static/src/xml/mail.xml b/addons/mail/static/src/xml/mail.xml index 7f49b7f3943..8a0fc1d90e0 100644 --- a/addons/mail/static/src/xml/mail.xml +++ b/addons/mail/static/src/xml/mail.xml @@ -17,8 +17,8 @@

    - News Feeds - + News Feeds +

    @@ -136,7 +136,7 @@
    - - - Checklist - - - Attachment - - - Formatting - +
    diff --git a/addons/project_issue/project_issue.py b/addons/project_issue/project_issue.py index 106ea195571..3b0311d8f45 100644 --- a/addons/project_issue/project_issue.py +++ b/addons/project_issue/project_issue.py @@ -47,7 +47,6 @@ class project_issue(base_stage, osv.osv): _description = "Project Issue" _order = "priority, create_date desc" _inherit = ['ir.needaction_mixin', 'mail.thread'] - _mail_compose_message = True def _get_default_project_id(self, cr, uid, context=None): """ Gives default project by checking if present in the context """ From b1d80c734b9cad97d958424099cc0f8132096851 Mon Sep 17 00:00:00 2001 From: Fabien Pinckaers Date: Tue, 21 Aug 2012 16:36:43 +0200 Subject: [PATCH 122/436] fix bzr revid: fp@tinyerp.com-20120821143643-npc5a84ajzfcsl14 --- addons/mail/TODO.txt | 21 --------------------- addons/mail/static/src/js/mail.js | 1 - 2 files changed, 22 deletions(-) delete mode 100644 addons/mail/TODO.txt diff --git a/addons/mail/TODO.txt b/addons/mail/TODO.txt deleted file mode 100644 index eb6d0ccd761..00000000000 --- a/addons/mail/TODO.txt +++ /dev/null @@ -1,21 +0,0 @@ -Backend: -* I propose to move message_read in mail.message and rename to message_fetch - -* remove _message_find_user_id and use _message_find_partners - - it requires to change the route code - -Web Stuff to Do: -* replace comments_structure by a real tree structure, provided by message_read directly -* rewrite completly records_struct_add_records, merge existing messages with those coming from another message_read - -> false records with domain message_read -* remove records_struct_update_after_display -* remove all is_wall stuff and pass real arguments -* remove init_and_fetch_comments -* rename fetch_comments into message_read -Not Sure: - * implement display_current_user --> check why it's present two times - -To Check: -* check if addons modules rely on some stuff changed in the aboce backend section - - diff --git a/addons/mail/static/src/js/mail.js b/addons/mail/static/src/js/mail.js index e0cdec54299..f9d91e3acd6 100644 --- a/addons/mail/static/src/js/mail.js +++ b/addons/mail/static/src/js/mail.js @@ -30,7 +30,6 @@ openerp.mail = function(session) { }, }); - /** * ------------------------------------------------------------ * ChatterUtils From 24651a903b4ec977b191d94b85c4548391fbe3e1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thibault=20Delavall=C3=A9e?= Date: Tue, 21 Aug 2012 16:49:30 +0200 Subject: [PATCH 123/436] [IMP] mail.compose.message: added body_text filed, for plain-text; updated composer accordlingly, removed some dead code/css classes. bzr revid: tde@openerp.com-20120821144930-ucgmx8rwasamhpgp --- addons/mail/mail_message.py | 2 +- .../static/src/css/mail_compose_message.css | 4 --- addons/mail/static/src/js/mail.js | 5 ++-- addons/mail/wizard/mail_compose_message.py | 28 ++++++++----------- .../mail/wizard/mail_compose_message_view.xml | 18 ++++++------ 5 files changed, 24 insertions(+), 33 deletions(-) diff --git a/addons/mail/mail_message.py b/addons/mail/mail_message.py index 01086b57bb0..4cbcbea127f 100644 --- a/addons/mail/mail_message.py +++ b/addons/mail/mail_message.py @@ -112,7 +112,7 @@ class mail_message(osv.Model): 'subject': fields.char('Subject', size=128), 'date': fields.datetime('Date'), 'message_id': fields.char('Message-Id', size=256, help='Message unique identifier', select=1, readonly=1), - 'body': fields.html('Content', required=True), + 'body': fields.html('Content'), } def _get_default_author(self, cr, uid, context={}): diff --git a/addons/mail/static/src/css/mail_compose_message.css b/addons/mail/static/src/css/mail_compose_message.css index 4cd1339ded4..ba7ed36a695 100644 --- a/addons/mail/static/src/css/mail_compose_message.css +++ b/addons/mail/static/src/css/mail_compose_message.css @@ -2,10 +2,6 @@ /* Compose Message Wizard Form */ /* ------------------------------ */ -.openerp tr td .oe_form_field.oe_mail_compose_message_invisible { - display: none; -} - .openerp .oe_mail_compose_message_icons { text-align: right; } diff --git a/addons/mail/static/src/js/mail.js b/addons/mail/static/src/js/mail.js index f9d91e3acd6..90b3655a208 100644 --- a/addons/mail/static/src/js/mail.js +++ b/addons/mail/static/src/js/mail.js @@ -324,9 +324,8 @@ openerp.mail = function(session) { // check in parents, should not define multiple times this.options.context.res_model = options.context.res_model || 'mail.thread'; this.options.context.res_id = options.context.res_id || false; - // this.options.context.parent_id = options.context.parent_id || false; + this.options.context.parent_id = options.context.parent_id || false; this.options.thread_level = options.thread_level || 0; - // this.options.fetch_limit = options.fetch_limit || 100; this.options.composer = options.composer || false; // TDE: not sure, here for testing / compatibility this.options.records = options.records || null; @@ -337,7 +336,7 @@ openerp.mail = function(session) { // display customization vars this.display = {}; this.display.truncate_limit = options.truncate_limit || 250; - // this.display.show_header_compose = options.show_header_compose || false; + this.display.show_header_compose = options.show_header_compose || false; this.display.show_reply = options.show_reply || false; this.display.show_delete = options.show_delete || false; this.display.show_hide = options.show_hide || false; diff --git a/addons/mail/wizard/mail_compose_message.py b/addons/mail/wizard/mail_compose_message.py index 3000d2a2cdc..5da8d7c8e80 100644 --- a/addons/mail/wizard/mail_compose_message.py +++ b/addons/mail/wizard/mail_compose_message.py @@ -114,19 +114,16 @@ class mail_compose_message(osv.TransientModel): 'wizard_id', 'attachment_id', 'Attachments'), 'auto_delete': fields.boolean('Auto Delete', help="Permanently delete emails after sending"), 'filter_id': fields.many2one('ir.filters', 'Filters'), - 'body_html': fields.html('HTML Editor Body'), + 'body_text': fields.text('Plain-text editor body'), 'content_subtype': fields.char('Message content subtype', size=32, readonly=1, help="Type of message, usually 'html' or 'plain', used to select "\ "plain-text or rich-text contents accordingly"), - # boolean fields to control - 'formatting': fields.boolean('Toggle advanced formatting mode'), } _defaults = { 'content_subtype': lambda self,cr, uid, context={}: 'plain', - 'body_html': lambda self,cr, uid, context={}: '', + 'body_text': lambda self,cr, uid, context={}: '', 'body': lambda self,cr, uid, context={}: '', - 'formatting': False, } def get_value(self, cr, uid, model, res_id, context=None): @@ -159,24 +156,23 @@ class mail_compose_message(osv.TransientModel): """ hit toggle formatting mode button: calls onchange_formatting to emulate an on_change, then writes the value to update the form. """ for record in self.browse(cr, uid, ids, context=context): - onchange_res = self.onchange_formatting(cr, uid, ids, not record.formatting, record.model, record.res_id, context=context) + content_st_new_value = 'plain' if record.content_subtype == 'html' else 'html' + onchange_res = self.onchange_content_subtype(cr, uid, ids, content_st_new_value, record.model, record.res_id, context=context) self.write(cr, uid, [record.id], onchange_res['value'], context=context) return False - def onchange_formatting(self, cr, uid, ids, value, model, res_id, context=None): - """ onchange_formatting (values: True or False). This onchange on the - formatting allows to have some specific behavior when going in - formatting mode, or when going out of formatting. - Basically, subject is reset when going out of formatting mode. + def onchange_content_subtype(self, cr, uid, ids, value, model, res_id, context=None): + """ onchange_content_subtype (values: 'plain' or 'html'). This onchange + on the subtype allows to have some specific behavior when switching + between text or html mode. + Basically, subject is reset when going out of html mode. This method can be overridden for models that want to have their specific behavior. - Note that currently, this onchange is used in mail.js and called - manually on the form instantiated in the Chatter. """ - if not value: - return {'value': {'subject': False, 'formatting': value}} - return {'value': {'formatting': value}} + if value == 'plain': + return {'value': {'subject': False, 'content_subtype': value}} + return {'value': {'content_subtype': value}} def onchange_dest_partner_ids(self, cr, uid, ids, value, context=None): """ onchange_dest_partner_ids (value format: [[6, False, [3, 4]]]). The diff --git a/addons/mail/wizard/mail_compose_message_view.xml b/addons/mail/wizard/mail_compose_message_view.xml index d1f2f535e8a..82c96f9eb80 100644 --- a/addons/mail/wizard/mail_compose_message_view.xml +++ b/addons/mail/wizard/mail_compose_message_view.xml @@ -39,19 +39,19 @@
    - - + + class="oe_mail_compose_message_subject" + attrs="{'invisible':[('content_subtype', '=', 'plain')]}"/> + - + class="oe_mail_compose_message_body_html" + attrs="{'invisible':[('content_subtype', '=', 'plain')]}"/> + class="oe_mail_compose_message_partner_ids"/> From b919782ad91643603bc31ccb02916cb4ded93f3e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thibault=20Delavall=C3=A9e?= Date: Tue, 21 Aug 2012 16:55:27 +0200 Subject: [PATCH 124/436] [REM] mail.js: removed references to 'email_mode' of composer, now deleted. bzr revid: tde@openerp.com-20120821145527-vdvu00doz77mzx7n --- addons/mail/static/src/js/mail.js | 44 +++++-------------------------- 1 file changed, 6 insertions(+), 38 deletions(-) diff --git a/addons/mail/static/src/js/mail.js b/addons/mail/static/src/js/mail.js index 90b3655a208..3ff80a20b41 100644 --- a/addons/mail/static/src/js/mail.js +++ b/addons/mail/static/src/js/mail.js @@ -104,8 +104,6 @@ openerp.mail = function(session) { * @param {Object} [options] * @param {String} [options.res_model] res_model of document [REQUIRED] * @param {Number} [options.res_id] res_id of record [REQUIRED] - * @param {Number} [options.email_mode] true/false, tells whether - * we are in email sending mode * @param {Number} [options.formatting] true/false, tells whether * we are in advance formatting mode * @param {String} [options.model] mail.compose.message.mode (see @@ -119,7 +117,6 @@ openerp.mail = function(session) { // options this.options = options || {}; this.options.context = options.context || {}; - this.options.email_mode = options.email_mode || false; this.options.formatting = options.formatting || false; this.options.mode = options.mode || 'comment'; this.options.form_xml_id = options.form_xml_id || 'email_compose_message_wizard_form_chatter'; @@ -129,7 +126,6 @@ openerp.mail = function(session) { } else { this.options.active_id = this.options.res_id; } - this.email_mode = false; this.formatting = false; // debug console.groupCollapsed('New ComposeMessage: model', this.options.res_model, ', id', this.options.res_id); @@ -144,7 +140,7 @@ openerp.mail = function(session) { reinit: function() { var self = this; if (! this.form_view) return; - var call_defer = this.ds_compose.call('default_get', [['subject', 'body', 'body_html', 'dest_partner_ids'], this.ds_compose.get_context()]).then( + var call_defer = this.ds_compose.call('default_get', [['subject', 'body_text', 'body', 'attachment_ids', 'dest_partner_ids'], this.ds_compose.get_context()]).then( function (result) { self.form_view.on_processed_onchange({'value': result}, []); }); @@ -165,8 +161,7 @@ openerp.mail = function(session) { var self = this; this._super.apply(this, arguments); // customize display: add avatar, clean previous content - var user_avatar = mail.ChatterUtils.get_image(this.session.prefix, - this.session.session_id, 'res.users', 'image_small', this.session.uid); + var user_avatar = mail.ChatterUtils.get_image(this.session.prefix, this.session.session_id, 'res.users', 'image_small', this.session.uid); this.$element.find('img.oe_mail_icon').attr('src', user_avatar); this.$element.find('div.oe_mail_msg_content').empty(); // create a context for the default_get of the compose form @@ -179,7 +174,7 @@ openerp.mail = function(session) { this.ds_compose = new session.web.DataSetSearch(this, 'mail.compose.message', context); // find the id of the view to display in the chatter form var data_ds = new session.web.DataSetSearch(this, 'ir.model.data'); - var deferred_form_id =data_ds.call('get_object_reference', ['mail', this.options.form_xml_id]).then( function (result) { + var deferred_form_id = data_ds.call('get_object_reference', ['mail', this.options.form_xml_id]).then( function (result) { if (result) { self.options.form_view_id = result[1]; } @@ -205,7 +200,6 @@ openerp.mail = function(session) { return $.when(this.form_view.appendTo(msg_node)).pipe(function() { self.bind_events(); self.form_view.do_show(); - if (self.options.email_mode) { self.toggle_email_mode(); } if (self.options.formatting) { self.toggle_formatting_mode(); } }); }, @@ -222,13 +216,6 @@ openerp.mail = function(session) { this.$element.find('button.oe_form_button').click(function (event) { event.preventDefault(); }); - // event: click on 'Send an Email' link that toggles the form for - // sending an email (partner_ids) - this.$element.on('click', 'button.oe_mail_compose_message_email', function (event) { - event.preventDefault(); - event.stopPropagation(); - self.toggle_email_mode(event); - }); // event: click on 'Formatting' icon-link that toggles the advanced // formatting options for writing a message (subject, body_html) this.$element.on('click', 'button.oe_mail_compose_message_formatting', function (event) { @@ -251,23 +238,6 @@ openerp.mail = function(session) { this.ds_compose.context.formatting = this.formatting; }, - /** - * Toggle the email mode. */ - toggle_email_mode: function(event) { - this.email_mode = ! this.email_mode; - // update context of datasetsearch - this.ds_compose.context.email_mode = this.email_mode; - // update 'Post' button -> 'Send' - // update 'Send an Email' link -> 'Post a comment' - if (this.email_mode) { - this.$element.find('button.oe_mail_compose_message_button_send').html('Send'); - this.$element.find('button.oe_mail_compose_message_email_mode').html('Comment'); - } else { - this.$element.find('button.oe_mail_compose_message_button_send').html('Post'); - this.$element.find('button.oe_mail_compose_message_email_mode').html('Send an Email'); - } - }, - /** * Update the values of the composition form; with possible different * values for body and body_html. */ @@ -431,10 +401,9 @@ openerp.mail = function(session) { event.preventDefault(); event.stopPropagation(); var msg_id = event.srcElement.dataset.msg_id; - var email_mode = (event.srcElement.dataset.type == 'email'); var formatting = (event.srcElement.dataset.formatting == 'html'); if (! msg_id) return false; - self.instantiate_composition_form('reply', email_mode, formatting, msg_id); + self.instantiate_composition_form('reply', formatting, msg_id); }); }, @@ -451,15 +420,14 @@ openerp.mail = function(session) { }, /** Instantiate the composition form, with paramteres coming from thread parameters */ - instantiate_composition_form: function(mode, email_mode, formatting, msg_id, context) { + instantiate_composition_form: function(mode, formatting, msg_id, context) { if (this.compose_message_widget) { this.compose_message_widget.destroy(); } this.compose_message_widget = new mail.ComposeMessage(this, { 'extended_mode': false, 'uid': this.options.uid, 'res_model': this.options.context.res_model, 'res_id': this.options.context.res_id, 'mode': mode || 'comment', 'msg_id': msg_id, - 'email_mode': email_mode || false, 'formatting': formatting || false, - 'context': context || false } ); + 'formatting': formatting || false, 'context': context || false } ); var composition_node = this.$element.find('div.oe_mail_thread_action'); composition_node.empty(); var compose_done = this.compose_message_widget.appendTo(composition_node); From 392ac848b9c718469c93f1ac9034d2ba1e509571 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thibault=20Delavall=C3=A9e?= Date: Tue, 21 Aug 2012 17:06:22 +0200 Subject: [PATCH 125/436] [FIX] Wall: css fix in composer. bzr revid: tde@openerp.com-20120821150622-2h13mgtwk3h0uh3g --- addons/mail/static/src/css/mail.css | 2 +- addons/mail/static/src/js/mail.js | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/addons/mail/static/src/css/mail.css b/addons/mail/static/src/css/mail.css index 7a3d0e29710..b19e9abbe0a 100644 --- a/addons/mail/static/src/css/mail.css +++ b/addons/mail/static/src/css/mail.css @@ -26,7 +26,7 @@ /* Specific display of threads in the wall */ /* ------------------------------------------------------------ */ -.openerp ul.oe_mail_wall_threads .oe_mail_msg_content textarea { +.openerp ul.oe_mail_wall_threads .oe_mail_msg_content textarea.oe_mail_compose_textarea { width: 434px; height: 30px; padding: 4px; diff --git a/addons/mail/static/src/js/mail.js b/addons/mail/static/src/js/mail.js index 3ff80a20b41..864900bb729 100644 --- a/addons/mail/static/src/js/mail.js +++ b/addons/mail/static/src/js/mail.js @@ -413,7 +413,8 @@ openerp.mail = function(session) { * in the composition form. */ do_action: function(action, on_close) { console.log('thread do_action'); - this.init_comments(); + this.message_clean(); + this.message_fetch(); if (this.compose_message_widget) { this.compose_message_widget.reinit(); } return this._super(action, on_close); @@ -709,6 +710,7 @@ openerp.mail = function(session) { * in the composition form. */ //TDE: still useful ? TO CHECK do_action: function(action, on_close) { + console.log('wall do_action'); if (this.compose_message_widget) { this.compose_message_widget.reinit(); } this.message_clean(); From b772da168f7e8e6e6539ac4e867dc1a2e25b7a82 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thibault=20Delavall=C3=A9e?= Date: Tue, 21 Aug 2012 17:14:13 +0200 Subject: [PATCH 126/436] [FIX] tools: emails_split -> email-split. bzr revid: tde@openerp.com-20120821151413-9hxmsyp51q3ny32f --- openerp/tools/misc.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openerp/tools/misc.py b/openerp/tools/misc.py index 7f2dade30d8..8ca90e32f07 100644 --- a/openerp/tools/misc.py +++ b/openerp/tools/misc.py @@ -1113,7 +1113,7 @@ class unquote(str): def __repr__(self): return self -def emails_split(text): +def email_split(text): """Return a list of the email addresses found in ``text``""" if not text: return [] return re.findall(r'([^ ,<@]+@[^> ,]+)', text) From 3ae87708d4a36b500a4eda6f423c22c315571788 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thibault=20Delavall=C3=A9e?= Date: Tue, 21 Aug 2012 17:20:09 +0200 Subject: [PATCH 127/436] [CLEAN] mail_thread: removed print statements. bzr revid: tde@openerp.com-20120821152009-y50ju4q9u1catn2f --- addons/mail/mail_thread.py | 8 -------- 1 file changed, 8 deletions(-) diff --git a/addons/mail/mail_thread.py b/addons/mail/mail_thread.py index de7dc954f31..71f03e87f8f 100644 --- a/addons/mail/mail_thread.py +++ b/addons/mail/mail_thread.py @@ -540,14 +540,6 @@ class mail_thread(osv.Model): def message_post(self, cr, uid, res_id, body, subject=False, mtype='notification', parent_id=False, attachments=None, context=None, **kwargs): - print res_id - print body - print subject - print mtype - print parent_id - print attachments - print context - print kwargs context = context or {} attachments = attachments or {} if type(res_id) in (list, tuple): From 98f41b176b3dce481330b91d3f22e5083e281fdd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thibault=20Delavall=C3=A9e?= Date: Tue, 21 Aug 2012 17:35:42 +0200 Subject: [PATCH 128/436] [FIX] mail_thread: parse_message was on mail.message, but now on mail.thread. bzr revid: tde@openerp.com-20120821153542-87i32quupsmr1t2y --- 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 71f03e87f8f..0f6fc0ee9f3 100644 --- a/addons/mail/mail_thread.py +++ b/addons/mail/mail_thread.py @@ -311,7 +311,7 @@ class mail_thread(osv.Model): routes = self.message_route(cr, uid, msg_txt, model, thread_id, custom_values, context=context) - msg = self.pool.get('mail.message').parse_message(msg_txt, save_original=save_original, context=context) + msg = self.parse_message(msg_txt, save_original=save_original, context=context) msg['state'] = 'received' if strip_attachments and 'attachments' in msg: del msg['attachments'] From d876320e12b80a229bc97b3d2df35858f6812521 Mon Sep 17 00:00:00 2001 From: Fabien Pinckaers Date: Tue, 21 Aug 2012 17:45:55 +0200 Subject: [PATCH 129/436] [FIX] allows using uid in domains of params bzr revid: fp@tinyerp.com-20120821154555-gjsw2pr5ykbl0ed5 --- openerp/addons/base/ir/ir_actions.py | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/openerp/addons/base/ir/ir_actions.py b/openerp/addons/base/ir/ir_actions.py index a5d04d7f1bc..28a309f427a 100644 --- a/openerp/addons/base/ir/ir_actions.py +++ b/openerp/addons/base/ir/ir_actions.py @@ -860,11 +860,10 @@ class act_client(osv.osv): _order = 'name' def _get_params(self, cr, uid, ids, field_name, arg, context): - return dict([ - ((record.id, ast.literal_eval(record.params_store)) - if record.params_store else (record.id, False)) - for record in self.browse(cr, uid, ids, context=context) - ]) + result = {} + for record in self.browse(cr, uid, ids, context=context): + result[record.id] = record.params_store and eval(record.params_store, {'uid': uid}) or False + return result def _set_params(self, cr, uid, id, field_name, field_value, arg, context): if isinstance(field_value, dict): From 5fe1c4382826795d065eec721edaf4f472c54876 Mon Sep 17 00:00:00 2001 From: Fabien Pinckaers Date: Tue, 21 Aug 2012 17:46:21 +0200 Subject: [PATCH 130/436] [FIX] clean domains bzr revid: fp@tinyerp.com-20120821154621-r7datjbrnzjoeg36 --- addons/mail/mail_message_view.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/addons/mail/mail_message_view.xml b/addons/mail/mail_message_view.xml index 38181033a51..853326249ec 100644 --- a/addons/mail/mail_message_view.xml +++ b/addons/mail/mail_message_view.xml @@ -217,13 +217,13 @@ News Feed mail.wall - + My Feeds mail.wall - + From a7531c4814e731fc163af48ffc80d184a61e418c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thibault=20Delavall=C3=A9e?= Date: Tue, 21 Aug 2012 17:52:57 +0200 Subject: [PATCH 131/436] [FIX] mail_thread: pass cr arg to parse_message, because it is necessary for sub-calls. bzr revid: tde@openerp.com-20120821155257-0paa85fs6gla5slr --- addons/mail/mail_thread.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/addons/mail/mail_thread.py b/addons/mail/mail_thread.py index 0f6fc0ee9f3..78dfa4bb46a 100644 --- a/addons/mail/mail_thread.py +++ b/addons/mail/mail_thread.py @@ -311,7 +311,7 @@ class mail_thread(osv.Model): routes = self.message_route(cr, uid, msg_txt, model, thread_id, custom_values, context=context) - msg = self.parse_message(msg_txt, save_original=save_original, context=context) + msg = self.parse_message(cr, msg_txt, save_original=save_original, context=context) msg['state'] = 'received' if strip_attachments and 'attachments' in msg: del msg['attachments'] @@ -385,7 +385,7 @@ class mail_thread(osv.Model): self.write(cr, uid, ids, update_vals, context=context) return True - def parse_message(self, message, save_original=False, context=None): + def parse_message(self, cr, message, save_original=False, context=None): """Parses a string or email.message.Message representing an RFC-2822 email, and returns a generic dict holding the message details. From be208572cf75a5da802991acc367d69e2c9cce93 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thibault=20Delavall=C3=A9e?= Date: Tue, 21 Aug 2012 18:00:36 +0200 Subject: [PATCH 132/436] [FIX] mail.js: fixed some function names / mail_followers: fixed an email_to. bzr revid: tde@openerp.com-20120821160036-jruwclg54d67a21y --- addons/mail/mail_followers.py | 10 ++++------ addons/mail/static/src/js/mail.js | 6 +++--- 2 files changed, 7 insertions(+), 9 deletions(-) diff --git a/addons/mail/mail_followers.py b/addons/mail/mail_followers.py index d9dab750ecd..b94e03efd93 100644 --- a/addons/mail/mail_followers.py +++ b/addons/mail/mail_followers.py @@ -77,7 +77,7 @@ class mail_notification(osv.Model): msg = msg_obj.browse(cr, uid, msg_id, context=context) towrite = { - 'email_to': '', + 'email_to': [], 'subject': msg.subject } for partner in partner_obj.browse(cr, uid, partner_ids, context=context): @@ -92,16 +92,14 @@ class mail_notification(osv.Model): continue towrite['state'] = 'outgoing' - if not towrite.get('email_to', False): - towrite['email_to'] = email_to - else: - if email_to not in towrite['email_to']: - towrite['email_to'] = towrite['email_to'] + ', ' + email_to + if partner.email not in towrite['email_to']: + towrite['email_to'].append(partner.email) if towrite.get('state', False) and not context.get('noemail', False): if towrite.get('subject', False): towrite['subject'] = msg.name_get(cr, uid, [msg.id], context=context)[0][1] towrite['message_id'] = msg.id + towrite['email_to'] = ', '.join(towrite['email_to']) mail_message_obj = self.pool.get('mail.mail') newid = mail_message_obj.create(cr, uid, towrite, context=context) mail_message_obj.send(cr, uid, [newid], context=context) diff --git a/addons/mail/static/src/js/mail.js b/addons/mail/static/src/js/mail.js index 569ecde7200..e29988389df 100644 --- a/addons/mail/static/src/js/mail.js +++ b/addons/mail/static/src/js/mail.js @@ -358,7 +358,7 @@ openerp.mail = function(session) { this.$element.find('textarea.oe_mail_compose_textarea').keyup(function (event) { var charCode = (event.which) ? event.which : window.event.keyCode; if (event.shiftKey && charCode == 13) { this.value = this.value+"\n"; } - else if (charCode == 13) { return self.do_message_post(); } + else if (charCode == 13) { return self.message_post(); } }); // event: click on 'Reply' in msg this.$element.on('click', 'a.oe_mail_msg_reply', function (event) { @@ -437,7 +437,7 @@ openerp.mail = function(session) { /** Clean the thread */ message_clean: function() { - this.$el.find('div.oe_mail_thread_display').empty(); + this.$element.find('div.oe_mail_thread_display').empty(); }, /** Fetch messages @@ -552,7 +552,7 @@ openerp.mail = function(session) { } return this.ds_post.call('message_post', [ [this.options.context.res_id], body, false, 'comment', this.options.context.parent_id] - ).then(this.proxy('init_comments')); + ).then(this.proxy('message_fetch')); }, /** Action: 'shows more' to fetch new messages */ From 38acf7c1041e4a613d95f108d4c905e6342a84d5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thibault=20Delavall=C3=A9e?= Date: Tue, 21 Aug 2012 18:05:48 +0200 Subject: [PATCH 133/436] [REM] base_calendar: removed ir_attachment override, that was added to be able to manage virtual_ids for attachments (i.e., adding attachments to reccuring meetings). As this fix was causing issues, not having attachments on recurrent meetings seems more acceptable than having an ugly fix + other bigs. bzr revid: tde@openerp.com-20120821160548-qq0sjku3yf3c0ogq --- addons/base_calendar/base_calendar.py | 30 --------------------------- 1 file changed, 30 deletions(-) diff --git a/addons/base_calendar/base_calendar.py b/addons/base_calendar/base_calendar.py index d1b8709e1e9..d560eaacd66 100644 --- a/addons/base_calendar/base_calendar.py +++ b/addons/base_calendar/base_calendar.py @@ -1616,36 +1616,6 @@ class calendar_todo(osv.osv): calendar_todo() -#class ir_attachment(osv.osv): -# _name = 'ir.attachment' -# _inherit = 'ir.attachment' -# -# def search_count(self, cr, user, args, context=None): -# new_args = [] -# for domain_item in args: -# if isinstance(domain_item, (list, tuple)) and len(domain_item) == 3 and domain_item[0] == 'res_id': -# new_args.append((domain_item[0], domain_item[1], base_calendar_id2real_id(domain_item[2]))) -# else: -# new_args.append(domain_item) -# return super(ir_attachment, self).search_count(cr, user, new_args, context) -# -# def create(self, cr, uid, vals, context=None): -# if context: -# id = context.get('default_res_id', False) -# context.update({'default_res_id' : base_calendar_id2real_id(id)}) -# return super(ir_attachment, self).create(cr, uid, vals, context=context) -# -# def search(self, cr, uid, args, offset=0, limit=None, order=None, -# context=None, count=False): -# new_args = [] -# for domain_item in args: -# if isinstance(domain_item, (list, tuple)) and len(domain_item) == 3 and domain_item[0] == 'res_id': -# new_args.append((domain_item[0], domain_item[1], base_calendar_id2real_id(domain_item[2]))) -# else: -# new_args.append(domain_item) -# return super(ir_attachment, self).search(cr, uid, new_args, offset=offset, -# limit=limit, order=order, context=context, count=False) -#ir_attachment() class ir_values(osv.osv): _inherit = 'ir.values' From e88e21bb569d9ce086bf03285aa4141dc28f0c58 Mon Sep 17 00:00:00 2001 From: Fabien Pinckaers Date: Tue, 21 Aug 2012 19:24:14 +0200 Subject: [PATCH 134/436] fix bzr revid: fp@tinyerp.com-20120821172414-7kz7qcibjile1dfl --- openerp/addons/base/res/res_partner_view.xml | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/openerp/addons/base/res/res_partner_view.xml b/openerp/addons/base/res/res_partner_view.xml index 43a37d1a79c..6e2e8fac178 100644 --- a/openerp/addons/base/res/res_partner_view.xml +++ b/openerp/addons/base/res/res_partner_view.xml @@ -186,13 +186,10 @@

    -
    - - - - - - +
  • + From 20748495c61fa3cc6e8e07cf25c94cb0dca2a49f Mon Sep 17 00:00:00 2001 From: Fabien Pinckaers Date: Tue, 21 Aug 2012 20:03:51 +0200 Subject: [PATCH 135/436] [IMP] force email when quick create bzr revid: fp@tinyerp.com-20120821180351-t28m9svpdj8ois0k --- addons/mail/wizard/mail_compose_message_view.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/addons/mail/wizard/mail_compose_message_view.xml b/addons/mail/wizard/mail_compose_message_view.xml index 82c96f9eb80..a10dc6ccc86 100644 --- a/addons/mail/wizard/mail_compose_message_view.xml +++ b/addons/mail/wizard/mail_compose_message_view.xml @@ -50,6 +50,7 @@ class="oe_mail_compose_message_body_html" attrs="{'invisible':[('content_subtype', '=', 'plain')]}"/> Date: Tue, 21 Aug 2012 20:04:09 +0200 Subject: [PATCH 136/436] [IMP] better implementation of name_create bzr revid: fp@tinyerp.com-20120821180409-gk2c3odfve53epdp --- openerp/addons/base/res/res_partner.py | 24 ++++++++---------------- 1 file changed, 8 insertions(+), 16 deletions(-) diff --git a/openerp/addons/base/res/res_partner.py b/openerp/addons/base/res/res_partner.py index d9ae1d8595e..30255d93ec3 100644 --- a/openerp/addons/base/res/res_partner.py +++ b/openerp/addons/base/res/res_partner.py @@ -366,22 +366,14 @@ class res_partner(osv.osv): - ([\w\s.\\-]+)[\<]([a-zA-Z0-9._%-]+@[a-zA-Z0-9_-]+\.[a-zA-Z0-9._]{1,8})[\>]: Raoul Grosbedon, raoul@grosbedon.fr """ - contact_regex = re.compile('([\w\s.\\-]+)[\<]([a-zA-Z0-9._%-]+@[a-zA-Z0-9_-]+\.[a-zA-Z0-9._]{1,8})[\>]') - email_regex = re.compile('([a-zA-Z0-9._%-]+@[a-zA-Z0-9_-]+\.[a-zA-Z0-9._]{1,8})') - contact_regex_res = contact_regex.findall(name) - email_regex_res = email_regex.findall(name) - if contact_regex_res: - name = contact_regex_res[0][0].rstrip(' ') # remove extra spaces on the right - email = contact_regex_res[0][1] - rec_id = self.create(cr, uid, {self._rec_name: name, 'email': email}, context); - return self.name_get(cr, uid, [rec_id], context)[0] - elif email_regex_res: - email = '%s' % (email_regex_res[0]) - rec_id = self.create(cr, uid, {self._rec_name: email, 'email': email}, context); - return self.name_get(cr, uid, [rec_id], context)[0] - else: - rec_id = super(res_partner, self).create(cr, uid, {self._rec_name: name}, context) - return self.name_get(cr, uid, [rec_id], context)[0] + regex = re.compile('^(.*?)(?: ?[\<]?([a-zA-Z0-9._%-]+@[a-zA-Z0-9_-]+\.[a-zA-Z0-9._]{1,8})[\>]?)?$') + result = regex.match(name) + if not result or (context.get('force_create',False) and result.group(2) is None): + raise osv.except_osv(_('Warning'), _("Couldn't create contact without email address !")) + name = result.group(1).strip() + email = result.group(2) + rec_id = self.create(cr, uid, {self._rec_name: name or email, 'email': email or False}, context=context) + return self.name_get(cr, uid, [rec_id], context)[0] def name_search(self, cr, uid, name, args=None, operator='ilike', context=None, limit=100): if not args: From d3f493de07fb8afda2dabfd889ec93d9a1545edc Mon Sep 17 00:00:00 2001 From: Olivier Dony Date: Tue, 21 Aug 2012 23:09:53 +0200 Subject: [PATCH 137/436] [FIX] fetchmail: typos bzr revid: odo@openerp.com-20120821210953-olzgm14wsk7jzcia --- addons/fetchmail/fetchmail.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/addons/fetchmail/fetchmail.py b/addons/fetchmail/fetchmail.py index 24c977bba1d..70a05c6bd0e 100644 --- a/addons/fetchmail/fetchmail.py +++ b/addons/fetchmail/fetchmail.py @@ -247,7 +247,7 @@ class mail_mail(osv.osv): fetchmail_server_id = context.get('fetchmail_server_id') if fetchmail_server_id: values['fetchmail_server_id'] = fetchmail_server_id - res = super(mail_message,self).create(cr, uid, values, context=context) + res = super(mail_mail,self).create(cr, uid, values, context=context) return res def write(self, cr, uid, ids, values, context=None): @@ -256,7 +256,7 @@ class mail_mail(osv.osv): fetchmail_server_id = context.get('fetchmail_server_id') if fetchmail_server_id: values['fetchmail_server_id'] = server_id - res = super(mail_message,self).write(cr, uid, ids, values, context=context) + res = super(mail_mail,self).write(cr, uid, ids, values, context=context) return res From 5008e9cc84002797b2b4c8c43d10ab5e720bac8e Mon Sep 17 00:00:00 2001 From: Olivier Dony Date: Tue, 21 Aug 2012 23:15:28 +0200 Subject: [PATCH 138/436] [FIX] mail: fix parse_message calls for new signature - cr,uid needed bzr revid: odo@openerp.com-20120821211528-yyxyq1bqti5g3s5v --- addons/mail/mail_thread.py | 4 ++-- addons/plugin/plugin_handler.py | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/addons/mail/mail_thread.py b/addons/mail/mail_thread.py index 78dfa4bb46a..90b686d6693 100644 --- a/addons/mail/mail_thread.py +++ b/addons/mail/mail_thread.py @@ -311,7 +311,7 @@ class mail_thread(osv.Model): routes = self.message_route(cr, uid, msg_txt, model, thread_id, custom_values, context=context) - msg = self.parse_message(cr, msg_txt, save_original=save_original, context=context) + msg = self.parse_message(cr, uid, msg_txt, save_original=save_original, context=context) msg['state'] = 'received' if strip_attachments and 'attachments' in msg: del msg['attachments'] @@ -385,7 +385,7 @@ class mail_thread(osv.Model): self.write(cr, uid, ids, update_vals, context=context) return True - def parse_message(self, cr, message, save_original=False, context=None): + def parse_message(self, cr, uid, message, save_original=False, context=None): """Parses a string or email.message.Message representing an RFC-2822 email, and returns a generic dict holding the message details. diff --git a/addons/plugin/plugin_handler.py b/addons/plugin/plugin_handler.py index d493501e1cc..6d6262ddd22 100644 --- a/addons/plugin/plugin_handler.py +++ b/addons/plugin/plugin_handler.py @@ -44,7 +44,7 @@ class plugin_handler(osv.osv_memory): res_id = 0 url = "" name = "" - msg = mail_message_obj.parse_message(email) + msg = self.pool.get('mail.thread').parse_message(cr, uid, email) references = [msg.get('message-id')] refs = msg.get('references',False) if refs: @@ -91,7 +91,7 @@ class plugin_handler(osv.osv_memory): """ mail_message = self.pool.get('mail.message') model_obj = self.pool.get(model) - msg = mail_message.parse_message(email) + msg = self.pool.get('mail.thread').parse_message(cr, uid, email) message_id = msg.get('message-id') mail_ids = mail_message.search(cr, uid, [('message_id','=',message_id),('res_id','=',res_id),('model','=',model)]) @@ -146,7 +146,7 @@ class plugin_handler(osv.osv_memory): mail_message = self.pool.get('mail.message') ir_attachment_obj = self.pool.get('ir.attachment') attach_ids = [] - msg = mail_message.parse_message(headers) + msg = self.pool.get('mail.thread').parse_message(cr, uid, headers) message_id = msg.get('message-id') push_mail = self.push_message(cr, uid, model, headers, res_id) res_id = push_mail[1] From 95b13bdaa45d3bfbb178c679a6661b53b2f00205 Mon Sep 17 00:00:00 2001 From: Olivier Dony Date: Tue, 21 Aug 2012 23:24:19 +0200 Subject: [PATCH 139/436] [FIX] *mail: s/mail_message/mail_mail/ for cases where a full mail_mail is necessary bzr revid: odo@openerp.com-20120821212419-yu2criox6sayf2cv --- addons/email_template/email_template.py | 10 +++++----- addons/mail/mail_mail.py | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/addons/email_template/email_template.py b/addons/email_template/email_template.py index 84d48e1387a..291ddd6a0ef 100644 --- a/addons/email_template/email_template.py +++ b/addons/email_template/email_template.py @@ -389,12 +389,12 @@ class email_template(osv.osv): :returns: id of the mail.message that was created """ if context is None: context = {} - mail_message = self.pool.get('mail.message') + mail_mail = self.pool.get('mail.mail') ir_attachment = self.pool.get('ir.attachment') values = self.generate_email(cr, uid, template_id, res_id, context=context) assert 'email_from' in values, 'email_from is missing or empty after template rendering, send_mail() cannot proceed' attachments = values.pop('attachments') or {} - msg_id = mail_message.create(cr, uid, values, context=context) + msg_id = mail_mail.create(cr, uid, values, context=context) # link attachments attachment_ids = [] for fname, fcontent in attachments.iteritems(): @@ -402,15 +402,15 @@ class email_template(osv.osv): 'name': fname, 'datas_fname': fname, 'datas': fcontent, - 'res_model': mail_message._name, + 'res_model': mail_mail._name, 'res_id': msg_id, } context.pop('default_type', None) attachment_ids.append(ir_attachment.create(cr, uid, attachment_data, context=context)) if attachment_ids: - mail_message.write(cr, uid, msg_id, {'attachment_ids': [(6, 0, attachment_ids)]}, context=context) + mail_mail.write(cr, uid, msg_id, {'attachment_ids': [(6, 0, attachment_ids)]}, context=context) if force_send: - mail_message.send(cr, uid, [msg_id], context=context) + mail_mail.send(cr, uid, [msg_id], context=context) return msg_id # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/addons/mail/mail_mail.py b/addons/mail/mail_mail.py index 3d7c6fa2ec7..89f863ea2f8 100644 --- a/addons/mail/mail_mail.py +++ b/addons/mail/mail_mail.py @@ -174,7 +174,7 @@ class mail_mail(osv.Model): if message.state == 'sent': self._postprocess_sent_message(cr, uid, message, context=context) except Exception: - _logger.exception('failed sending mail.message %s', message.id) + _logger.exception('failed sending mail.mail %s', message.id) message.write({'state':'exception'}) if auto_commit == True: From caec481a351e1ddb939838d1fa51f2d2c08fdcb0 Mon Sep 17 00:00:00 2001 From: Olivier Dony Date: Wed, 22 Aug 2012 03:07:55 +0200 Subject: [PATCH 140/436] [FIX] mail: typos during refactoring bzr revid: odo@openerp.com-20120822010755-xjc53162ue4kxm2e --- addons/mail/mail_thread.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/addons/mail/mail_thread.py b/addons/mail/mail_thread.py index 90b686d6693..8ca2bc6e56c 100644 --- a/addons/mail/mail_thread.py +++ b/addons/mail/mail_thread.py @@ -455,12 +455,12 @@ class mail_thread(osv.Model): # find author_id if 'From' in msg_fields: - author_ids = self._message_find_partners(cr, uid, msg_text, ['From'], context=context) + author_ids = self._message_find_partners(cr, uid, msg_txt, ['From'], context=context) #decode(msg_txt.get('From') or msg_txt.get_unixfrom()) ) if author_ids: msg['author_id'] = author_ids[0] - partner_ids = self._message_find_partners(cr, uid, msg_text, ['From','To','Delivered-To','CC','Cc'], context=context) + partner_ids = self._message_find_partners(cr, uid, msg_txt, ['From','To','Delivered-To','CC','Cc'], context=context) msg['partner_ids'] = partner_ids #if 'To' in msg_fields: From 0113e9281a70eb034e58f471df3aba598a3eafe7 Mon Sep 17 00:00:00 2001 From: Olivier Dony Date: Wed, 22 Aug 2012 03:09:47 +0200 Subject: [PATCH 141/436] [FIX] mail: more refactoring errors: missing _logger bzr revid: odo@openerp.com-20120822010947-8avs21hi7ib30ix2 --- addons/mail/mail_mail.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/addons/mail/mail_mail.py b/addons/mail/mail_mail.py index 89f863ea2f8..07631cec992 100644 --- a/addons/mail/mail_mail.py +++ b/addons/mail/mail_mail.py @@ -9,6 +9,8 @@ import datetime from osv import osv from osv import fields +_logger = logging.getLogger(__name__) + class mail_mail(osv.Model): """ Model holding RFC2822 email messages to send. This model also provides From 8bf884d2b698dae61f00814768259d6e6fe6a3c7 Mon Sep 17 00:00:00 2001 From: "Nimesh (Open ERP)" Date: Wed, 22 Aug 2012 11:20:23 +0530 Subject: [PATCH 142/436] [FIX] set the menu for base.menu_definitions bzr revid: nco@tinyerp.com-20120822055023-4y39imx6dhwyjqsi --- addons/crm_claim/crm_claim_menu.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addons/crm_claim/crm_claim_menu.xml b/addons/crm_claim/crm_claim_menu.xml index 46f8760387a..bb881d62a58 100644 --- a/addons/crm_claim/crm_claim_menu.xml +++ b/addons/crm_claim/crm_claim_menu.xml @@ -50,9 +50,9 @@ parent="base.menu_aftersale" action="crm_case_categ_claim0" sequence="1"/> + - From 4d0937e9a642b31183dfbc0cd963b95411d536cb Mon Sep 17 00:00:00 2001 From: "Jignesh Rathod (Open ERP)" Date: Wed, 22 Aug 2012 12:39:28 +0530 Subject: [PATCH 143/436] [FIX] Fixes the problem of Iteration is not allowed on browse_record lp bug: https://launchpad.net/bugs/1039899 fixed bzr revid: jir@tinyerp.com-20120822070928-hfqz8s9du89ib03d --- addons/event/event.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/addons/event/event.py b/addons/event/event.py index 7f0c77661e4..004b370f48e 100644 --- a/addons/event/event.py +++ b/addons/event/event.py @@ -52,10 +52,13 @@ class event_event(osv.osv): _inherit = ['ir.needaction_mixin','mail.thread'] def name_get(self, cr, uid, ids, context=None): + event_obj = self.browse(cr, uid, ids, context=context) if not ids: return [] + if not isinstance(event_obj,list): + event_obj = [event_obj] res = [] - for record in self.browse(cr, uid, ids, context=context): + for record in event_obj: date = record.date_begin.split(" ")[0] date_end = record.date_end.split(" ")[0] if date != date_end: From 1bc63a91adfe37d8c284236f276fd4c0f791ea96 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thibault=20Delavall=C3=A9e?= Date: Wed, 22 Aug 2012 09:45:45 +0200 Subject: [PATCH 144/436] [FIX] mail_mail: added import of tools, to be able to use email_split. bzr revid: tde@openerp.com-20120822074545-7sx8xnsmf610gnxz --- addons/mail/mail_mail.py | 24 +++++++++++++++++++++++- addons/mail/static/src/js/mail.js | 1 + 2 files changed, 24 insertions(+), 1 deletion(-) diff --git a/addons/mail/mail_mail.py b/addons/mail/mail_mail.py index 07631cec992..a016586347f 100644 --- a/addons/mail/mail_mail.py +++ b/addons/mail/mail_mail.py @@ -1,10 +1,32 @@ +# -*- coding: utf-8 -*- +############################################################################## +# +# OpenERP, Open Source Management Solution +# Copyright (C) 2010-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 +# +############################################################################## + import ast import base64 +import datetime import email import logging import re import time -import datetime +import tools from osv import osv from osv import fields diff --git a/addons/mail/static/src/js/mail.js b/addons/mail/static/src/js/mail.js index e29988389df..d0c4ff55ee8 100644 --- a/addons/mail/static/src/js/mail.js +++ b/addons/mail/static/src/js/mail.js @@ -545,6 +545,7 @@ openerp.mail = function(session) { }, message_post: function (body) { + var self = this; if (! body) { var comment_node = this.$element.find('textarea'); var body = comment_node.val(); From 6c109e7d7eb3e3b4f1741e199ace40ded9fc7c3d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thibault=20Delavall=C3=A9e?= Date: Wed, 22 Aug 2012 09:49:24 +0200 Subject: [PATCH 145/436] [REM] project: removed custom 'followers' fields; security rule updated to use follower_ids. bzr revid: tde@openerp.com-20120822074924-bbb5lc1w48dtebeb --- addons/project/project.py | 19 ------------------- addons/project/security/project_security.xml | 2 +- 2 files changed, 1 insertion(+), 20 deletions(-) diff --git a/addons/project/project.py b/addons/project/project.py index 7088dccae23..5fe7f31fa57 100644 --- a/addons/project/project.py +++ b/addons/project/project.py @@ -186,23 +186,6 @@ class project(osv.osv): """Overriden in project_issue to offer more options""" return [('project.task', "Tasks")] - def _get_followers(self, cr, uid, ids, name, arg, context=None): - ''' - Functional field that computes the users that are 'following' a thread. - ''' - res = {} - for project in self.browse(cr, uid, ids, context=context): - l = set() - for message in project.message_ids: - l.add(message.user_id and message.user_id.id or False) - res[project.id] = list(filter(None, l)) - return res - - def _search_followers(self, cr, uid, obj, name, args, context=None): - project_obj = self.pool.get('project.project') - project_ids = project_obj.search(cr, uid, [('message_ids.user_id.id', 'in', args[0][2])], context=context) - return [('id', 'in', project_ids)] - # Lambda indirection method to avoid passing a copy of the overridable method when declaring the field _alias_models = lambda self, *args, **kwargs: self._get_alias_models(*args, **kwargs) @@ -246,8 +229,6 @@ class project(osv.osv): help="The kind of document created when an email is received on this project's email alias"), 'privacy_visibility': fields.selection([('public','Public'), ('followers','Followers Only')], 'Privacy / Visibility', required=True), 'state': fields.selection([('template', 'Template'),('draft','New'),('open','In Progress'), ('cancelled', 'Cancelled'),('pending','Pending'),('close','Closed')], 'Status', required=True,), - 'followers': fields.function(_get_followers, method=True, fnct_search=_search_followers, - type='many2many', relation='res.users', string='Followers'), } def dummy(self, cr, uid, ids, context): diff --git a/addons/project/security/project_security.xml b/addons/project/security/project_security.xml index f867285138a..3afe0f14e7a 100644 --- a/addons/project/security/project_security.xml +++ b/addons/project/security/project_security.xml @@ -45,7 +45,7 @@ public Members - ['|','|',('privacy_visibility','in',[False,'public']),('members','in',[user.id]),('followers','in',[user.id])] + ['|','|',('privacy_visibility','in',[False,'public']),('members','in',[user.id]),('follower_ids','in',[user.partner_id.id])]
    From 2f9f57cc469a8dcd4149dbe253dbbb8c6149169c Mon Sep 17 00:00:00 2001 From: Fabien Pinckaers Date: Wed, 22 Aug 2012 10:36:08 +0200 Subject: [PATCH 146/436] [IMP] removing some FP Note bzr revid: fp@tinyerp.com-20120822083608-2q98p1758erm8f7t --- addons/mail/mail_group.py | 2 -- addons/mail/mail_message.py | 6 ------ addons/mail/mail_thread.py | 2 -- addons/mail/res_users.py | 4 ++-- addons/mail/wizard/mail_compose_message.py | 6 ------ 5 files changed, 2 insertions(+), 18 deletions(-) diff --git a/addons/mail/mail_group.py b/addons/mail/mail_group.py index 145b9c9bdb4..faa4bb1c9b4 100644 --- a/addons/mail/mail_group.py +++ b/addons/mail/mail_group.py @@ -105,8 +105,6 @@ class mail_group(osv.Model): 'alias_domain': False, # always hide alias during creation } - # FP Note: code to be improved. Check we have a code for res.users - # when we give them a new group. def _subscribe_users(self, cr, uid, ids, context=None): for mail_group in self.browse(cr, uid, ids, context=context): partner_ids = [] diff --git a/addons/mail/mail_message.py b/addons/mail/mail_message.py index 4cbcbea127f..b74df2f8594 100644 --- a/addons/mail/mail_message.py +++ b/addons/mail/mail_message.py @@ -19,22 +19,16 @@ # ############################################################################## -# FP Note: can we remove some dependencies ? Use lint - -import base64 import dateutil.parser import email import logging import re import time -import datetime from email.header import decode_header from email.message import Message from osv import osv from osv import fields -import pytz -from tools import DEFAULT_SERVER_DATETIME_FORMAT import tools _logger = logging.getLogger(__name__) diff --git a/addons/mail/mail_thread.py b/addons/mail/mail_thread.py index 8ca2bc6e56c..f0bb90947e9 100644 --- a/addons/mail/mail_thread.py +++ b/addons/mail/mail_thread.py @@ -638,8 +638,6 @@ class mail_thread(osv.Model): # Thread_state #------------------------------------------------------ - # FP Note: this should be a invert function on message_unread field - # not sure because if not readonly, it may often write to this field? def message_mark_as_unread(self, cr, uid, ids, context=None): """ Set as read. """ notobj = self.pool.get('mail.notification') diff --git a/addons/mail/res_users.py b/addons/mail/res_users.py index 1ae72246e69..8eacd0580d7 100644 --- a/addons/mail/res_users.py +++ b/addons/mail/res_users.py @@ -137,7 +137,7 @@ class res_users_mail_group(osv.Model): _name = 'res.users' _inherit = ['res.users'] - # FP Note: to improve + # FP Note: to improve, post processing may be better ? def write(self, cr, uid, ids, vals, context=None): write_res = super(res_users_mail_group, self).write(cr, uid, ids, vals, context=context) if vals.get('groups_id'): @@ -158,7 +158,7 @@ class res_groups_mail_group(osv.Model): _name = 'res.groups' _inherit = 'res.groups' - # FP Note: to improve + # FP Note: to improve, post processeing, after the super may be better def write(self, cr, uid, ids, vals, context=None): if vals.get('users'): # form: {'group_ids': [(3, 10), (3, 3), (4, 10), (4, 3)]} or {'group_ids': [(6, 0, [ids]} diff --git a/addons/mail/wizard/mail_compose_message.py b/addons/mail/wizard/mail_compose_message.py index 5da8d7c8e80..50ffd6e440e 100644 --- a/addons/mail/wizard/mail_compose_message.py +++ b/addons/mail/wizard/mail_compose_message.py @@ -28,12 +28,6 @@ from osv import fields from tools.safe_eval import safe_eval as eval from tools.translate import _ -# FP Note: refactor in tools ? -def mail_tools_to_email(text): - """Return a list of the email addresses found in ``text``""" - if not text: return [] - return re.findall(r'([^ ,<@]+@[^> ,]+)', text) - # main mako-like expression pattern EXPRESSION_PATTERN = re.compile('(\$\{.+?\})') From 26cb67660226fb13120b7546c603dc2b508bd8b5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thibault=20Delavall=C3=A9e?= Date: Wed, 22 Aug 2012 10:38:13 +0200 Subject: [PATCH 147/436] [FIX] mail_thread: find_partners: fixed message_find_partners, message_txt did not always have all headers filled; removed some commented code that can be deleted; re-added computation of email date; added missing imports. bzr revid: tde@openerp.com-20120822083813-tt2sjx54iu6sbskt --- addons/mail/mail_thread.py | 46 +++++++++++++------------------------- 1 file changed, 16 insertions(+), 30 deletions(-) diff --git a/addons/mail/mail_thread.py b/addons/mail/mail_thread.py index f0bb90947e9..80cb7794e54 100644 --- a/addons/mail/mail_thread.py +++ b/addons/mail/mail_thread.py @@ -20,19 +20,20 @@ ############################################################################## import base64 +import dateutil import email import logging -import re -import time -import xmlrpclib from email.utils import parsedate from email.message import Message - from osv import osv, fields from mail_message import decode +import re +import time import tools from tools.translate import _ from tools.safe_eval import safe_eval as eval +import pytz +import xmlrpclib _logger = logging.getLogger(__name__) @@ -163,12 +164,13 @@ class mail_thread(osv.Model): # Mail gateway #------------------------------------------------------ - def _message_find_partners(self, cr, uid, message, headers=['From'], context=None): - s = ', '.join([decode(message.get(h)) for h in headers]) + def _message_find_partners(self, cr, uid, message, header_fields=['From'], context=None): + """ Find partners related to some header fields of the message. """ + s = ', '.join([decode(message.get(h)) for h in header_fields if message.get(h)]) mails = tools.email_split(s) result = [] - for m in mails: - result += self.pool.get('res.partner').search(cr, uid, [('email','ilike',m)], context=context) + for email in mails: + result += self.pool.get('res.partner').search(cr, uid, [('email', 'ilike', email)], context=context) return result def _message_find_user_id(self, cr, uid, message, context=None): @@ -463,28 +465,12 @@ class mail_thread(osv.Model): partner_ids = self._message_find_partners(cr, uid, msg_txt, ['From','To','Delivered-To','CC','Cc'], context=context) msg['partner_ids'] = partner_ids - #if 'To' in msg_fields: - # msg['to'] = decode(msg_txt.get('To')) - - #if 'Delivered-To' in msg_fields: - # msg['to'] = decode(msg_txt.get('Delivered-To')) - - #if 'CC' in msg_fields: - # msg['cc'] = decode(msg_txt.get('CC')) - - #if 'Cc' in msg_fields: - # msg['cc'] = decode(msg_txt.get('Cc')) - - #if 'Reply-To' in msg_fields: - # msg['reply'] = decode(msg_txt.get('Reply-To')) - - # FP Note: I propose to store the current datetime rather than the email date - #if 'Date' in msg_fields: - # date_hdr = decode(msg_txt.get('Date')) - # # convert from email timezone to server timezone - # date_server_datetime = dateutil.parser.parse(date_hdr).astimezone(pytz.timezone(tools.get_server_timezone())) - # date_server_datetime_str = date_server_datetime.strftime(DEFAULT_SERVER_DATETIME_FORMAT) - # msg['date'] = date_server_datetime_str + if 'Date' in msg_fields: + date_hdr = decode(msg_txt.get('Date')) + # convert from email timezone to server timezone + date_server_datetime = dateutil.parser.parse(date_hdr).astimezone(pytz.timezone(tools.get_server_timezone())) + date_server_datetime_str = date_server_datetime.strftime(tools.DEFAULT_SERVER_DATETIME_FORMAT) + msg['date'] = date_server_datetime_str #if 'Content-Transfer-Encoding' in msg_fields: # msg['encoding'] = msg_txt.get('Content-Transfer-Encoding') From 7867021f80b7716a373e70501ebbaab6b89d1f9c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thibault=20Delavall=C3=A9e?= Date: Wed, 22 Aug 2012 12:12:51 +0200 Subject: [PATCH 148/436] [CLEAN] mail.followers widget: removed unnecessary code about showing/hiding followers (always show); updated calls to subscription API, using .._users. Updated widget.params to widget.options. bzr revid: tde@openerp.com-20120822101251-sdnknsgfexkpljdh --- addons/mail/static/src/js/mail_followers.js | 43 ++++++------------- addons/mail/static/src/xml/mail_followers.xml | 7 ++- 2 files changed, 17 insertions(+), 33 deletions(-) diff --git a/addons/mail/static/src/js/mail_followers.js b/addons/mail/static/src/js/mail_followers.js index 1e420fbcbb9..d233235cd00 100644 --- a/addons/mail/static/src/js/mail_followers.js +++ b/addons/mail/static/src/js/mail_followers.js @@ -24,12 +24,8 @@ openerp_mail_followers = function(session, mail) { init: function() { this._super.apply(this, arguments); - this.params = {}; - this.params.image = this.node.attrs.image || 'image_small'; - this.params.title = this.node.attrs.title || 'Followers'; - this.params.display_followers = true; - this.params.display_control = this.node.attrs.display_control || false; - this.params.display_actions = this.node.attrs.display_actions || false; + this.options.image = this.node.attrs.image || 'image_small'; + this.options.title = this.node.attrs.title || 'Followers'; this.ds_model = new session.web.DataSetSearch(this, this.view.model); this.ds_follow = new session.web.DataSetSearch(this, this.field.relation); }, @@ -40,9 +36,6 @@ openerp_mail_followers = function(session, mail) { // any other method to know if the view is in create mode anymore this.view.on("change:actual_mode", this, this._check_visibility); this._check_visibility(); - this.$element.find('button.oe_mail_button_followers').click(function () { self.do_toggle_followers(); }); - if (! this.params.display_control) { - this.$element.find('button.oe_mail_button_followers').hide(); } this.$element.find('button.oe_mail_button_follow').click(function () { self.do_follow(); }) .mouseover(function () { $(this).html('Follow').removeClass('oe_mail_button_mouseout').addClass('oe_mail_button_mouseover'); }) .mouseleave(function () { $(this).html('Not following').removeClass('oe_mail_button_mouseover').addClass('oe_mail_button_mouseout'); }); @@ -61,10 +54,6 @@ openerp_mail_followers = function(session, mail) { }, reinit: function() { - this.params.display_followers = true; - this.params.display_control = this.node.attrs.display_control || false; - this.params.display_actions = this.node.attrs.display_actions || false; - this.$element.find('button.oe_mail_button_followers').html('Hide followers') this.$element.find('button.oe_mail_button_follow').hide(); this.$element.find('button.oe_mail_button_unfollow').hide(); }, @@ -76,11 +65,16 @@ openerp_mail_followers = function(session, mail) { this.$element.find('div.oe_mail_recthread_aside').hide(); return; } + if (this.getParent().fields.message_is_follower === undefined) { + // TDE: raise a warning + } + this.message_is_follower = this.getParent().fields.message_is_follower.get_value(); + console.log(this.message_is_follower); return this.fetch_followers(value_); }, fetch_followers: function (value_) { - return this.ds_follow.call('read', [value_ || this.get_value(), ['name', this.params.image]]).then(this.proxy('display_followers')); + return this.ds_follow.call('read', [value_ || this.get_value(), ['name']]).then(this.proxy('display_followers')); }, /** @@ -88,15 +82,13 @@ openerp_mail_followers = function(session, mail) { * TODO: replace the is_follower check by fields read */ display_followers: function (records) { var self = this; - this.is_follower = false; - var user_list = this.$element.find('ul.oe_mail_followers_display').empty(); - this.$element.find('div.oe_mail_recthread_followers h4').html(this.params.title + ' (' + records.length + ')'); + var node_user_list = this.$element.find('ul.oe_mail_followers_display').empty(); + this.$element.find('div.oe_mail_recthread_followers h4').html(this.options.title + ' (' + records.length + ')'); _(records).each(function (record) { - if (record.id == self.session.uid) { self.is_follower = true; } record.avatar_url = mail.ChatterUtils.get_image(self.session.prefix, self.session.session_id, 'res.partner', 'image_small', record.id); - $(session.web.qweb.render('mail.followers.partner', {'record': record})).appendTo(user_list); + $(session.web.qweb.render('mail.followers.partner', {'record': record})).appendTo(node_user_list); }); - if (this.is_follower) { + if (this.message_is_follower) { this.$element.find('button.oe_mail_button_follow').hide(); this.$element.find('button.oe_mail_button_unfollow').show(); } else { @@ -105,18 +97,11 @@ openerp_mail_followers = function(session, mail) { }, do_follow: function () { - return this.ds_model.call('message_subscribe', [[this.view.datarecord.id]]).pipe(this.proxy('set_value')); + return this.ds_model.call('message_subscribe_users', [[this.view.datarecord.id]]).pipe(this.proxy('set_value')); }, do_unfollow: function () { - return this.ds_model.call('message_unsubscribe', [[this.view.datarecord.id]]).pipe(this.proxy('set_value')); - }, - - do_toggle_followers: function () { - this.params.see_followers = ! this.params.see_followers; - if (this.params.see_followers) { this.$element.find('button.oe_mail_button_followers').html('Hide followers'); } - else { this.$element.find('button.oe_mail_button_followers').html('Show followers'); } - this.$element.find('div.oe_mail_recthread_followers').toggle(); + return this.ds_model.call('message_unsubscribe_users', [[this.view.datarecord.id]]).pipe(this.proxy('set_value')); }, }); }; diff --git a/addons/mail/static/src/xml/mail_followers.xml b/addons/mail/static/src/xml/mail_followers.xml index 53c899b9569..2776669fa70 100644 --- a/addons/mail/static/src/xml/mail_followers.xml +++ b/addons/mail/static/src/xml/mail_followers.xml @@ -9,11 +9,10 @@
    -
    - -

    + +

      @@ -25,7 +24,7 @@ -->
    • - +
    • From d572beb36e8827bda4a4501ca62430b75c664a66 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thibault=20Delavall=C3=A9e?= Date: Wed, 22 Aug 2012 12:13:33 +0200 Subject: [PATCH 149/436] [IMP] res_users in mail: use message_subscribe, as we already have a browse in create_welcome_message, that I moved to create. bzr revid: tde@openerp.com-20120822101333-clct0k4o0pu9r2ix --- addons/mail/res_users.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/addons/mail/res_users.py b/addons/mail/res_users.py index 8eacd0580d7..1e638251e40 100644 --- a/addons/mail/res_users.py +++ b/addons/mail/res_users.py @@ -100,15 +100,15 @@ class res_users(osv.Model): # create user that follows its related partner user_id = super(res_users, self).create(cr, uid, data, context=context) - self.pool.get('res.partner').message_subscribe_users(cr, uid, [user_id], [user_id], context=context) + user = self.browse(cr, uid, user_id, context=context) + self.pool.get('res.partner').message_subscribe(cr, uid, [user.partner_id.id], [user.partner_id.id], context=context) # alias mail_alias.write(cr, SUPERUSER_ID, [alias_id], {"alias_force_thread_id": user_id}, context) # create a welcome message self._create_welcome_message(cr, uid, user_id, context=context) return user_id - def _create_welcome_message(self, cr, uid, user_id, context=None): - user = self.browse(cr, uid, user_id, context=context) + def _create_welcome_message(self, cr, uid, user, context=None): company_name = user.company_id.name if user.company_id else _('the company') body = '''%s has joined %s.''' % (user.name, company_name) # TODO change 1 into user.id but catch errors From e46c8e43ff588ba4a7b0d902527cdf3011efed9d Mon Sep 17 00:00:00 2001 From: Fabien Pinckaers Date: Wed, 22 Aug 2012 12:37:25 +0200 Subject: [PATCH 150/436] [IMP] Removing a bad practice: - what happens if you overwrite check_read and modules calls check_access_rights ? - 5 methods to learn to understand the API insead of one bzr revid: fp@tinyerp.com-20120822103725-vdubhkzlake2jyvo --- openerp/osv/orm.py | 31 +++++++++---------------------- 1 file changed, 9 insertions(+), 22 deletions(-) diff --git a/openerp/osv/orm.py b/openerp/osv/orm.py index 368a37f455a..e8f59e50cbd 100644 --- a/openerp/osv/orm.py +++ b/openerp/osv/orm.py @@ -2346,7 +2346,7 @@ class BaseModel(object): def read_string(self, cr, uid, id, langs, fields=None, context=None): res = {} res2 = {} - self.pool.get('ir.translation').check_read(cr, uid) + self.pool.get('ir.translation').check_access_rights(cr, uid, 'read') if not fields: fields = self._columns.keys() + self._inherit_fields.keys() #FIXME: collect all calls to _get_source into one SQL call. @@ -2370,7 +2370,7 @@ class BaseModel(object): return res def write_string(self, cr, uid, id, langs, vals, context=None): - self.pool.get('ir.translation').check_write(cr, uid) + self.pool.get('ir.translation').check_access_rights(cr, uid, 'write') #FIXME: try to only call the translation in one SQL for lang in langs: for field in vals: @@ -2518,7 +2518,7 @@ class BaseModel(object): """ context = context or {} - self.check_read(cr, uid) + self.check_access_rights(cr, uid, 'read') if not fields: fields = self._columns.keys() @@ -3399,8 +3399,7 @@ class BaseModel(object): if context is None: context = {} - write_access = self.check_write(cr, user, False) or \ - self.check_create(cr, user, False) + write_access = self.check_access_rights(cr, user, 'write') or self.check_access_rights(cr, user, 'create') res = {} @@ -3464,7 +3463,7 @@ class BaseModel(object): if not context: context = {} - self.check_read(cr, user) + self.check_access_rights(cr, user, 'read') if not fields: fields = list(set(self._columns.keys() + self._inherit_fields.keys())) if isinstance(ids, (int, long)): @@ -3738,18 +3737,6 @@ class BaseModel(object): according to the access rights.""" return self.pool.get('ir.model.access').check(cr, uid, self._name, operation, raise_exception) - def check_create(self, cr, uid, raise_exception=True): - return self.check_access_rights(cr, uid, 'create', raise_exception) - - def check_read(self, cr, uid, raise_exception=True): - return self.check_access_rights(cr, uid, 'read', raise_exception) - - def check_unlink(self, cr, uid, raise_exception=True): - return self.check_access_rights(cr, uid, 'unlink', raise_exception) - - def check_write(self, cr, uid, raise_exception=True): - return self.check_access_rights(cr, uid, 'write', raise_exception) - def check_access_rule(self, cr, uid, ids, operation, context=None): """Verifies that the operation given by ``operation`` is allowed for the user according to ir.rules. @@ -3813,7 +3800,7 @@ class BaseModel(object): self._check_concurrency(cr, ids, context) - self.check_unlink(cr, uid) + self.check_access_rights(cr, uid, 'unlink') ir_property = self.pool.get('ir.property') @@ -3949,7 +3936,7 @@ class BaseModel(object): ids = [ids] self._check_concurrency(cr, ids, context) - self.check_write(cr, user) + self.check_access_rights(cr, user, 'write') result = self._store_get_values(cr, user, ids, vals.keys(), context) or [] @@ -4169,7 +4156,7 @@ class BaseModel(object): if self.is_transient(): self._transient_vacuum(cr, user) - self.check_create(cr, user) + self.check_access_rights(cr, user, 'create') if self._log_access: for f in LOG_ACCESS_COLUMNS: @@ -4661,7 +4648,7 @@ class BaseModel(object): """ if context is None: context = {} - self.check_read(cr, access_rights_uid or user) + self.check_access_rights(cr, access_rights_uid or user, 'read') # For transient models, restrict acces to the current user, except for the super-user if self.is_transient() and self._log_access and user != SUPERUSER_ID: From 40949f8052bf448ce9d67f73159da4a66baa8b17 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thibault=20Delavall=C3=A9e?= Date: Wed, 22 Aug 2012 13:00:57 +0200 Subject: [PATCH 151/436] [FIX] mail.js: fixed a wrong var name. bzr revid: tde@openerp.com-20120822110057-1dqo701k2lmpmoxo --- 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 d0c4ff55ee8..409ff41fcf5 100644 --- a/addons/mail/static/src/js/mail.js +++ b/addons/mail/static/src/js/mail.js @@ -639,7 +639,7 @@ openerp.mail = function(session) { set_value: function() { this._super.apply(this, arguments); if (! this.view.datarecord.id || session.web.BufferedDataSet.virtual_id_regex.test(this.view.datarecord.id)) { - this.$el.find('oe_mail_thread').hide(); + this.$element.find('oe_mail_thread').hide(); return; } // update context From 49596ebe91b11bfa759782eb6176c6a0a918279a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thibault=20Delavall=C3=A9e?= Date: Wed, 22 Aug 2012 13:03:13 +0200 Subject: [PATCH 152/436] [IMP] mail_thread: message_is_follower field is now calculated with other functional fields (message_summary and message_read). Cleaned message_subscribe and message_unsubscribe. Added deletion of followers when unlinking a record. Propagated change in mail_group view. bzr revid: tde@openerp.com-20120822110313-is3p1h4s2e8c4hbm --- addons/mail/mail_group_view.xml | 10 +- addons/mail/mail_thread.py | 143 ++++++++++---------- addons/mail/static/src/js/mail_followers.js | 8 +- 3 files changed, 80 insertions(+), 81 deletions(-) diff --git a/addons/mail/mail_group_view.xml b/addons/mail/mail_group_view.xml index 1aa1e1aa687..c303b1fd40b 100644 --- a/addons/mail/mail_group_view.xml +++ b/addons/mail/mail_group_view.xml @@ -14,8 +14,8 @@ - + @@ -32,8 +32,8 @@

      @@ -80,10 +80,10 @@
      + - +
      diff --git a/addons/mail/mail_thread.py b/addons/mail/mail_thread.py index 80cb7794e54..3bc45d3af26 100644 --- a/addons/mail/mail_thread.py +++ b/addons/mail/mail_thread.py @@ -69,38 +69,23 @@ class mail_thread(osv.Model): _description = 'Email Thread' # TODO: may be we should make it _inherit ir.needaction - def _get_is_follower(self, cr, uid, ids, name, args, context=None): - subobj = self.pool.get('mail.followers') - subids = subobj.search(cr, uid, [ - ('res_model','=',self._name), - ('res_id', 'in', ids), - ('partner_id.user_ids','in',[uid])], context=context) - result = dict.fromkeys(ids, False) - for sub in subobj.browse(cr, uid, subids, context=context): - result[sub.res_id] = True - return result - def _get_message_data(self, cr, uid, ids, name, args, context=None): - res = {} - for id in ids: - res[id] = { - 'message_unread': False, - 'message_summary': '' - } - nobj = self.pool.get('mail.notification') - nids = nobj.search(cr, uid, [ - ('partner_id.user_ids','in',[uid]), - ('message_id.res_id','in', ids), - ('message_id.model','=', self._name), - ('read','=',False) + res = dict( (id, dict(message_unread=False, message_summary='')) for id in ids) + user = self.pool.get('res.users').browse(cr, uid, uid, context=context) + + notif_obj = self.pool.get('mail.notification') + notif_ids = notif_obj.search(cr, uid, [ + ('partner_id.user_ids', 'in', [uid]), + ('message_id.res_id', 'in', ids), + ('message_id.model', '=', self._name), + ('read', '=', False) ], context=context) - for notif in nobj.browse(cr, uid, nids, context=context): + for notif in notif_obj.browse(cr, uid, notif_ids, context=context): res[notif.message_id.res_id]['message_unread'] = True for thread in self.browse(cr, uid, ids, context=context): - message_ids = thread.message_ids - follower_ids = thread.message_follower_ids - res[id]['message_summary'] = "9 %d + %d" % (len(message_ids), len(follower_ids)), + res[thread.id]['message_summary'] = "9 %d + %d" % (len(thread.message_ids), len(thread.message_follower_ids)) + res[thread.id]['message_is_follower'] = user.partner_id.id in [follower.id for follower in thread.message_follower_ids] return res # FP Note: todo @@ -108,10 +93,10 @@ class mail_thread(osv.Model): return [] _columns = { - 'message_is_follower': fields.function(_get_is_follower, - type='boolean', string='Is a Follower'), + 'message_is_follower': fields.function(_get_message_data, + type='boolean', string='Is a Follower', multi='_get_message_data'), 'message_follower_ids': fields.many2many('res.partner', 'mail_followers', 'res_id', 'partner_id', - # FP Note: implement this domain=lambda self: [('res_model','=',self._name)], + domain=lambda self: [('res_model','=',self._name)], string='Followers'), 'message_ids': fields.one2many('mail.message', 'res_id', domain=lambda self: [('model','=',self._name)], @@ -129,26 +114,26 @@ class mail_thread(osv.Model): } #------------------------------------------------------ - # Automatic subscription when creating/reading + # Automatic subscription when creating #------------------------------------------------------ def create(self, cr, uid, vals, context=None): - """ Override of create to subscribe the current user - """ + """ Override of create to subscribe the current user. """ thread_id = super(mail_thread, self).create(cr, uid, vals, context=context) self.message_subscribe_users(cr, uid, [thread_id], [uid], context=context) return thread_id def unlink(self, cr, uid, ids, context=None): - """Override unlink, to automatically delete messages - that are linked with res_model and res_id, not through - a foreign key with a 'cascade' ondelete attribute. - Notifications will be deleted with messages - """ + """ Override unlink to delete messages and followers. This cannot be + cascaded, because link is done through (res_model, res_id). """ msg_obj = self.pool.get('mail.message') + fol_obj = self.pool.get('mail.followers') # delete messages and notifications - msg_to_del_ids = msg_obj.search(cr, uid, [('model', '=', self._name), ('res_id', 'in', ids)], context=context) - msg_obj.unlink(cr, uid, msg_to_del_ids, context=context) + msg_ids = msg_obj.search(cr, uid, [('model', '=', self._name), ('res_id', 'in', ids)], context=context) + msg_obj.unlink(cr, uid, msg_ids, context=context) + # delete followers + fol_ids = fol_obj.search(cr, uid, [('res_model', '=', self._name), ('res_id', 'in', ids)], context=context) + fol_obj.unlink(cr, uid, fol_ids, context=context) return super(mail_thread, self).unlink(cr, uid, ids, context=context) #------------------------------------------------------ @@ -557,54 +542,66 @@ class mail_thread(osv.Model): #------------------------------------------------------ - # Subscription mechanism + # Followers API #------------------------------------------------------ def message_subscribe_users(self, cr, uid, ids, user_ids=None, context=None): - if not user_ids: user_ids = [uid] - partners = {} - for user in self.pool.get('res.users').browse(cr, uid, user_ids, context=context): - partners[user.partner_id.id] = True - return self.message_subscribe(cr, uid, ids, partners.keys(), context=context) + """ Wrapper on message_subscribe, using users. If user_ids is not + provided, subscribe uid instead. """ + # isinstance: because using message_subscribe_users called in a view set the context as user_ids + if not user_ids or isinstance(user_ids, dict): user_ids = [uid] + partner_ids = [user.partner_id.id for user in self.pool.get('res.users').browse(cr, uid, user_ids, context=context)] + return self.message_subscribe(cr, uid, ids, partner_ids, context=context) def message_subscribe(self, cr, uid, ids, partner_ids, context=None): - """ - :param partner_ids: a list of user_ids; if not set, subscribe - uid instead + """ Add partners to the records followers. This implementation cannot + directly use [(4, partner_id)] because of the res_model column of + mail.followers. We therefore check access rights and access rules + before directly creating a follower record. This way we simulate + a write in message_follower_ids, enabling a correct check of access + rights. + + :param partner_ids: a list of partner_ids to subscribe :param return: new value of followers, for Chatter """ - obj = self.pool.get('mail.followers') - objids = obj.search(cr, uid, [ - ('res_id', 'in', ids), - ('res_model', '=', self._name), - ('partner_id', 'in', partner_ids), + self.check_access_rights(cr, uid, 'write', raise_exception=True) + self.check_access_rule(cr, uid, ids, 'write', context=context) + + fol_obj = self.pool.get('mail.followers') + fol_ids = fol_obj.search(cr, uid, [ + ('res_id', 'in', ids), ('res_model', '=', self._name), ('partner_id', 'in', partner_ids) ], context=context) followers = {} - for follow in obj.browse(cr, uid, objids, context=context): - followers.setdefault(follow.partner_id.id, {})[follow.res_id] = True - create_ids = [] + for fol in fol_obj.browse(cr, uid, fol_ids, context=context): + followers.setdefault(fol.partner_id.id, []).append(fol.res_id) + for res_id in ids: for partner_id in partner_ids: - if followers.get(partner_id, {}).get(res_id, False): - continue - create_ids.append(obj.create(cr, uid, { - 'res_model': self._name, - 'res_id': res_id, 'partner_id': partner_id - }, context=context)) - return create_ids + if not res_id in followers.get(partner_id, []): + fol_obj.create(cr, uid, { + 'res_id': res_id, 'partner_id': partner_id, 'res_model': self._name + }, context=context) + # TDE: temp, must check followers widget + return [] - def message_unsubscribe(self, cr, uid, ids, user_ids = None, context=None): - """ Unsubscribe the user (or user_ids) from the current document. + def message_unsubscribe_users(self, cr, uid, ids, user_ids=None, context=None): + """ Wrapper on message_subscribe, using users. If user_ids is not + provided, unsubscribe uid instead. """ + # isinstance: because using message_subscribe_users called in a view set the context as user_ids + if not user_ids or isinstance(user_ids, dict): user_ids = [uid] + partner_ids = [user.partner_id.id for user in self.pool.get('res.users').browse(cr, uid, user_ids, context=context)] + return self.message_unsubscribe(cr, uid, ids, partner_ids, context=context) - :param user_ids: a list of user_ids; if not set, subscribe - uid instead + def message_unsubscribe(self, cr, uid, ids, partner_ids, context=None): + """ Remove partners from the records followers. + + :param partner_ids: a list of partner_ids to unsubscribe :param return: new value of followers, for Chatter """ - partner_id = self.pool.get('res.users').browse(cr, uid, uid, context=context).partner_id.id - self.write(cr, uid, ids, [(3, partner_id)], context=context) - - # FP Note: do we need this ? - return [follower.id for thread in self.browse(cr, uid, ids, context=context) for follower in thread.message_follower_ids] + self.write(cr, uid, ids, {'message_follower_ids': [(3, pid) for pid in partner_ids]}, context=context) + # return [follower.id for thread in self.browse(cr, uid, ids, context=context) for follower in thread.message_follower_ids] + # TDE: temp, must check followers widget + return [] #------------------------------------------------------ # Notification API diff --git a/addons/mail/static/src/js/mail_followers.js b/addons/mail/static/src/js/mail_followers.js index d233235cd00..7b6723489ed 100644 --- a/addons/mail/static/src/js/mail_followers.js +++ b/addons/mail/static/src/js/mail_followers.js @@ -66,10 +66,12 @@ openerp_mail_followers = function(session, mail) { return; } if (this.getParent().fields.message_is_follower === undefined) { - // TDE: raise a warning + // TDE: TMP, need to change all form views + this.message_is_follower = false; + } + else { + this.message_is_follower = this.getParent().fields.message_is_follower.get_value(); } - this.message_is_follower = this.getParent().fields.message_is_follower.get_value(); - console.log(this.message_is_follower); return this.fetch_followers(value_); }, From 9871b2dd370c61052844ed79bc3de20a12c03adb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thibault=20Delavall=C3=A9e?= Date: Wed, 22 Aug 2012 13:16:13 +0200 Subject: [PATCH 153/436] [FIX] Fixed last commit, about removing check_read/write/unlink/create: those methods were used, propagated the changes :) . bzr revid: tde@openerp.com-20120822111613-305j77rs4fphkjts --- openerp/addons/base/ir/ir_sequence.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/openerp/addons/base/ir/ir_sequence.py b/openerp/addons/base/ir/ir_sequence.py index e3136b92b6e..8b03cd3f3b3 100644 --- a/openerp/addons/base/ir/ir_sequence.py +++ b/openerp/addons/base/ir/ir_sequence.py @@ -210,7 +210,7 @@ class ir_sequence(openerp.osv.osv.osv): def next_by_id(self, cr, uid, sequence_id, context=None): """ Draw an interpolated string using the specified sequence.""" - self.check_read(cr, uid) + self.check_access_rights(cr, uid, 'read') company_ids = self.pool.get('res.company').search(cr, uid, [], order='company_id', context=context) + [False] ids = self.search(cr, uid, ['&',('id','=', sequence_id),('company_id','in',company_ids)]) return self._next(cr, uid, ids, context) @@ -227,7 +227,7 @@ class ir_sequence(openerp.osv.osv.osv): sequence selection. A matching sequence for that specific company will get higher priority. """ - self.check_read(cr, uid) + self.check_access_rights(cr, uid, 'read') company_ids = self.pool.get('res.company').search(cr, uid, [], order='company_id', context=context) + [False] ids = self.search(cr, uid, ['&',('code','=', sequence_code),('company_id','in',company_ids)]) return self._next(cr, uid, ids, context) From 406e58399fd1444cd5b83862d6493f59fcb08790 Mon Sep 17 00:00:00 2001 From: Olivier Dony Date: Wed, 22 Aug 2012 13:34:39 +0200 Subject: [PATCH 154/436] [FIX] mail: unify message_post calls, fix typos, various lint cleanup bzr revid: odo@openerp.com-20120822113439-aoy7v4njs6o34tu5 --- addons/account/account_bank_statement.py | 2 +- addons/account/account_invoice.py | 17 +++--- addons/base_calendar/crm_meeting.py | 4 +- addons/base_status/base_stage.py | 2 +- addons/base_status/base_state.py | 8 +-- addons/crm/crm_action_rule.py | 4 +- addons/crm/crm_lead.py | 3 +- addons/crm/crm_meeting.py | 2 +- addons/event/event.py | 14 ++--- addons/hr/hr.py | 2 +- addons/hr_holidays/hr_holidays.py | 4 +- addons/mail/mail_mail.py | 1 - addons/mail/mail_thread.py | 67 +++++++++++++--------- addons/mail/res_users.py | 6 +- addons/mail/wizard/mail_compose_message.py | 5 +- addons/plugin/plugin_handler.py | 6 +- addons/procurement/procurement.py | 2 +- addons/project_issue/project_issue.py | 12 ++-- addons/sale/sale.py | 4 +- 19 files changed, 87 insertions(+), 78 deletions(-) diff --git a/addons/account/account_bank_statement.py b/addons/account/account_bank_statement.py index 911086a83e4..095d461ff3b 100644 --- a/addons/account/account_bank_statement.py +++ b/addons/account/account_bank_statement.py @@ -430,7 +430,7 @@ class account_bank_statement(osv.osv): 'name': st_number, 'balance_end_real': st.balance_end }, context=context) - self.message_post(cr, uid, [st.id], body=_('Statement %s is confirmed, journal items are created.') % (st_number,), context=context) + self.message_post(cr, uid, [st.id], body=_('Statement %s confirmed, journal items were created.') % (st_number,), context=context) return self.write(cr, uid, ids, {'state':'confirm'}, context=context) def button_cancel(self, cr, uid, ids, context=None): diff --git a/addons/account/account_invoice.py b/addons/account/account_invoice.py index bca1530abae..8b86429ea1b 100644 --- a/addons/account/account_invoice.py +++ b/addons/account/account_invoice.py @@ -1288,24 +1288,25 @@ class account_invoice(osv.osv): def _get_document_type(self, type): type_dict = { - 'out_invoice': 'Customer invoice', - 'in_invoice': 'Supplier invoice', - 'out_refund': 'Customer Refund', - 'in_refund': 'Supplier Refund', + # Translation markers will have no effect at runtime, only used to properly flag export + 'out_invoice': _('Customer invoice'), + 'in_invoice': _('Supplier invoice'), + 'out_refund': _('Customer Refund'), + 'in_refund': _('Supplier Refund'), } return type_dict.get(type, 'Invoice') def create_send_note(self, cr, uid, ids, context=None): for obj in self.browse(cr, uid, ids, context=context): - self.message_post(cr, uid, [obj.id],body=_("%s created.") % (self._get_document_type(obj.type)), context=context) + self.message_post(cr, uid, [obj.id], body=_("%s created.") % (_(self._get_document_type(obj.type))), context=context) def confirm_paid_send_note(self, cr, uid, ids, context=None): - for obj in self.browse(cr, uid, ids, context=context): - self.message_post(cr, uid, [obj.id], body=_("%s paid.") % (self._get_document_type(obj.type)), context=context) + for obj in self.browse(cr, uid, ids, context=context): + self.message_post(cr, uid, [obj.id], body=_("%s paid.") % (_(self._get_document_type(obj.type))), context=context) def invoice_cancel_send_note(self, cr, uid, ids, context=None): for obj in self.browse(cr, uid, ids, context=context): - self.message_post(cr, uid, [obj.id], body=_("%s cancelled.") % (self._get_document_type(obj.type)), context=context) + self.message_post(cr, uid, [obj.id], body=_("%s cancelled.") % (_(self._get_document_type(obj.type))), context=context) account_invoice() diff --git a/addons/base_calendar/crm_meeting.py b/addons/base_calendar/crm_meeting.py index 4bf922a2797..5f58877a8a6 100644 --- a/addons/base_calendar/crm_meeting.py +++ b/addons/base_calendar/crm_meeting.py @@ -78,9 +78,9 @@ class crm_meeting(base_state, osv.Model): return 'Meeting' def case_open_send_note(self, cr, uid, ids, context=None): - return self.message_post(cr, uid, ids, body=_("Meeting has been confirmed."), context=context) + return self.message_post(cr, uid, ids, body=_("Meeting confirmed."), context=context) def case_close_send_note(self, cr, uid, ids, context=None): - return self.message_post(cr, uid, ids, body=_("Meeting has been done."), context=context) + return self.message_post(cr, uid, ids, body=_("Meeting completed."), context=context) diff --git a/addons/base_status/base_stage.py b/addons/base_status/base_stage.py index 6b834399dd9..26afe548217 100644 --- a/addons/base_status/base_stage.py +++ b/addons/base_status/base_stage.py @@ -428,5 +428,5 @@ class base_stage(object): msg = '%s has been escalated to %s.' % (self.case_get_note_msg_prefix(cr, uid, id, context=context), new_section.name) else: msg = '%s has been escalated.' % (self.case_get_note_msg_prefix(cr, uid, id, context=context)) - self.message_post(cr, uid, [id], 'System Notification', msg, context=context) + self.message_post(cr, uid, [id], body=msg, context=context) return True diff --git a/addons/base_status/base_state.py b/addons/base_status/base_state.py index 098e09210f9..cebf5537485 100644 --- a/addons/base_status/base_state.py +++ b/addons/base_status/base_state.py @@ -179,9 +179,9 @@ class base_state(object): # Notifications # ****************************** - def case_get_note_msg_prefix(self, cr, uid, id, context=None): - return '' - + def case_get_note_msg_prefix(self, cr, uid, id, context=None): + return '' + def case_open_send_note(self, cr, uid, ids, context=None): for id in ids: msg = _('%s has been opened.') % (self.case_get_note_msg_prefix(cr, uid, id, context=context)) @@ -194,7 +194,7 @@ class base_state(object): msg = '%s has been escalated to %s.' % (self.case_get_note_msg_prefix(cr, uid, id, context=context), new_section.name) else: msg = '%s has been escalated.' % (self.case_get_note_msg_prefix(cr, uid, id, context=context)) - self.message_post(cr, uid, [id], 'System Notification', msg, context=context) + self.message_post(cr, uid, [id], body=msg, context=context) return True def case_close_send_note(self, cr, uid, ids, context=None): diff --git a/addons/crm/crm_action_rule.py b/addons/crm/crm_action_rule.py index 6753484dae0..36700b5b6c2 100644 --- a/addons/crm/crm_action_rule.py +++ b/addons/crm/crm_action_rule.py @@ -105,8 +105,8 @@ class base_action_rule(osv.osv): write['email_cc'] = obj.act_email_cc # Put state change by rule in communication history - if hasattr(obj, 'state') and hasattr(obj, 'message_append') and action.act_state: - model_obj.message_post(cr, uid, [obj], _(action.act_state)) + if hasattr(obj, 'state') and hasattr(obj, 'message_post') and action.act_state: + model_obj.message_post(cr, uid, [obj], _(action.act_state), context=context) model_obj.write(cr, uid, [obj.id], write, context) super(base_action_rule, self).do_action(cr, uid, action, model_obj, obj, context=context) diff --git a/addons/crm/crm_lead.py b/addons/crm/crm_lead.py index b1335fd91d4..39ad56e007d 100644 --- a/addons/crm/crm_lead.py +++ b/addons/crm/crm_lead.py @@ -19,7 +19,6 @@ # ############################################################################## -import binascii from base_status.base_stage import base_stage import crm from datetime import datetime @@ -476,7 +475,7 @@ class crm_lead(base_stage, osv.osv): subject = subject[0] + ", ".join(subject[1:]) details = "\n\n".join(details) - return self.message_post(cr, uid, [opportunity_id], subject=subject, body=details) + return self.message_post(cr, uid, [opportunity_id], body=details, subject=subject, context=context) def _merge_opportunity_history(self, cr, uid, opportunity_id, opportunities, context=None): message = self.pool.get('mail.message') diff --git a/addons/crm/crm_meeting.py b/addons/crm/crm_meeting.py index de54d8e40ac..62fc53364ee 100644 --- a/addons/crm/crm_meeting.py +++ b/addons/crm/crm_meeting.py @@ -53,7 +53,7 @@ class crm_meeting(osv.Model): if meeting.opportunity_id: # meeting can be create from phonecalls or opportunities, therefore checking for the parent lead = meeting.opportunity_id message = _("Meeting linked to the opportunity %s has been created and scheduled on %s.") % (lead.name, meeting_date_tz) - lead.message_post(_('System Notification'), message) + lead.message_post(body=message) elif meeting.phonecall_id: phonecall = meeting.phonecall_id message = _("Meeting linked to the phonecall %s has been created and scheduled on %s.") % (phonecall.name, meeting_date_tz) diff --git a/addons/event/event.py b/addons/event/event.py index a057fe80ac4..d347c1547d8 100644 --- a/addons/event/event.py +++ b/addons/event/event.py @@ -19,10 +19,8 @@ # ############################################################################## -import time from osv import fields, osv from tools.translate import _ -import decimal_precision as dp from openerp import SUPERUSER_ID class event_type(osv.osv): @@ -53,7 +51,7 @@ class event_event(osv.osv): def name_get(self, cr, uid, ids, context=None): if not ids: - return [] + return [] res = [] for record in self.browse(cr, uid, ids, context=context): date = record.date_begin.split(" ")[0] @@ -99,7 +97,6 @@ class event_event(osv.osv): return self.write(cr, uid, ids, {'state': 'done'}, context=context) def check_registration_limits(self, cr, uid, ids, context=None): - register_pool = self.pool.get('event.registration') for self.event in self.browse(cr, uid, ids, context=context): total_confirmed = self.event.register_current if total_confirmed < self.event.register_min or total_confirmed > self.event.register_max and self.event.register_max!=0: @@ -109,7 +106,7 @@ class event_event(osv.osv): for event in self.browse(cr, uid, ids, context=context): available_seats = event.register_avail if available_seats and no_of_registration > available_seats: - raise osv.except_osv(_('Warning!'),_("Only %d Seats are Available!") % (available_seats)) + raise osv.except_osv(_('Warning!'),_("Only %d Seats are Available!") % (available_seats)) elif available_seats == 0: raise osv.except_osv(_('Warning!'),_("No Tickets Available!")) @@ -139,7 +136,6 @@ class event_event(osv.osv): @param context: A standard dictionary for contextual values @return: Dictionary of function fields value. """ - register_pool = self.pool.get('event.registration') res = {} for event in self.browse(cr, uid, ids, context=context): res[event.id] = {} @@ -334,7 +330,7 @@ class event_registration(osv.osv): return self.write(cr, uid, ids, {'state': 'draft'}, context=context) def confirm_registration(self, cr, uid, ids, context=None): - self.message_post(cr, uid, ids, body=_('State set to open')) + self.message_post(cr, uid, ids, body=_('State set to open'), context=context) return self.write(cr, uid, ids, {'state': 'open'}, context=context) def create(self, cr, uid, vals, context=None): @@ -364,13 +360,13 @@ class event_registration(osv.osv): if today >= registration.event_id.date_begin: values = {'state': 'done', 'date_closed': today} self.write(cr, uid, ids, values) - self.message_post(cr, uid, ids, body=_('State set to Done')) + self.message_post(cr, uid, ids, body=_('State set to Done'), context=context) else: raise osv.except_osv(_('Error!'),_("You must wait for the starting day of the event to do this action.") ) return True def button_reg_cancel(self, cr, uid, ids, context=None, *args): - self.message_post(cr, uid, ids,body = _('State set to Cancel')) + self.message_post(cr, uid, ids, body=_('State set to Cancel'), context=context) return self.write(cr, uid, ids, {'state': 'cancel'}) def mail_user(self, cr, uid, ids, context=None): diff --git a/addons/hr/hr.py b/addons/hr/hr.py index 54560863dd0..3c307b9fee8 100644 --- a/addons/hr/hr.py +++ b/addons/hr/hr.py @@ -212,7 +212,7 @@ class hr_employee(osv.osv): try: (model, mail_group_id) = self.pool.get('ir.model.data').get_object_reference(cr, uid, 'mail', 'group_all_employees') employee = self.browse(cr, uid, employee_id, context=context) - self.pool.get('mail.group').message_post(cr, uid, [mail_group_id], body='Welcome to %s! Please help him make its first steps in OpenERP!' % (employee.name), context=context) + self.pool.get('mail.group').message_post(cr, uid, [mail_group_id], body='Welcome to %s! Please help them take the first steps with OpenERP!' % (employee.name), context=context) except: pass # group deleted: do not push a message return employee_id diff --git a/addons/hr_holidays/hr_holidays.py b/addons/hr_holidays/hr_holidays.py index 5748da37c1e..2e26e72bcdc 100644 --- a/addons/hr_holidays/hr_holidays.py +++ b/addons/hr_holidays/hr_holidays.py @@ -367,14 +367,14 @@ class hr_holidays(osv.osv): def create_notificate(self, cr, uid, ids, context=None): for obj in self.browse(cr, uid, ids, context=context): self.message_post(cr, uid, ids, - _("The %s request has been created and is waiting confirmation.") + _("The %s request has been created and is waiting for confirmation.") % ('leave' if obj.type == 'remove' else 'allocation',), context=context) return True def holidays_confirm_notificate(self, cr, uid, ids, context=None): for obj in self.browse(cr, uid, ids): self.message_post(cr, uid, [obj.id], _("The %s request has been confirmed and is waiting for validation by the manager.") - % ('leave' if obj.type == 'remove' else 'allocation',)) + % ('leave' if obj.type == 'remove' else 'allocation',), context=context) def holidays_validate_notificate(self, cr, uid, ids, context=None): for obj in self.browse(cr, uid, ids): diff --git a/addons/mail/mail_mail.py b/addons/mail/mail_mail.py index a016586347f..bd0bfbf33c0 100644 --- a/addons/mail/mail_mail.py +++ b/addons/mail/mail_mail.py @@ -45,7 +45,6 @@ class mail_mail(osv.Model): _columns = { 'mail_message_id': fields.many2one('mail.message', 'Message', required=True, ondelete='cascade'), 'mail_server_id': fields.many2one('ir.mail_server', 'Outgoing mail server', readonly=1), - 'subject': fields.char('Subject', size=128), 'state': fields.selection([ ('outgoing', 'Outgoing'), ('sent', 'Sent'), diff --git a/addons/mail/mail_thread.py b/addons/mail/mail_thread.py index 3bc45d3af26..5795fa8b824 100644 --- a/addons/mail/mail_thread.py +++ b/addons/mail/mail_thread.py @@ -19,7 +19,6 @@ # ############################################################################## -import base64 import dateutil import email import logging @@ -27,7 +26,6 @@ from email.utils import parsedate from email.message import Message from osv import osv, fields from mail_message import decode -import re import time import tools from tools.translate import _ @@ -313,7 +311,7 @@ class mail_thread(osv.Model): model_pool.message_update(cr, user_id, [thread_id], msg, context=context) else: thread_id = model_pool.message_new(cr, user_id, msg, custom_values, context=context) - self.message_post(cr, uid, thread_id, context=context, **msg) + self.message_post(cr, uid, [thread_id], msg_txt['body'], context=context, **msg) return True def message_new(self, cr, uid, msg_dict, custom_values=None, context=None): @@ -421,7 +419,7 @@ class mail_thread(osv.Model): if save_original: msg_original = message.as_string() if isinstance(message, Message) \ else message - attachments.append(('email.eml', msg_original)) + attachments.append(('original_email.eml', msg_original)) if not message_id: # Very unusual situation, be we should be fault-tolerant here @@ -451,11 +449,11 @@ class mail_thread(osv.Model): msg['partner_ids'] = partner_ids if 'Date' in msg_fields: - date_hdr = decode(msg_txt.get('Date')) - # convert from email timezone to server timezone - date_server_datetime = dateutil.parser.parse(date_hdr).astimezone(pytz.timezone(tools.get_server_timezone())) - date_server_datetime_str = date_server_datetime.strftime(tools.DEFAULT_SERVER_DATETIME_FORMAT) - msg['date'] = date_server_datetime_str + date_hdr = decode(msg_txt.get('Date')) + # convert from email timezone to server timezone + date_server_datetime = dateutil.parser.parse(date_hdr).astimezone(pytz.timezone(tools.get_server_timezone())) + date_server_datetime_str = date_server_datetime.strftime(tools.DEFAULT_SERVER_DATETIME_FORMAT) + msg['date'] = date_server_datetime_str #if 'Content-Transfer-Encoding' in msg_fields: # msg['encoding'] = msg_txt.get('Content-Transfer-Encoding') @@ -507,38 +505,53 @@ class mail_thread(osv.Model): module instead of by the res.log mechanism. Please \ use the mail.thread OpenChatter API instead of the \ now deprecated res.log.") - self.message_post(cr, uid, id, message, context=context) + self.message_post(cr, uid, [id], message, context=context) - def message_post(self, cr, uid, res_id, body, subject=False, - mtype='notification', parent_id=False, attachments=None, context=None, **kwargs): + def message_post(self, cr, uid, thread_id, body, subject=False, + msg_type='notification', parent_id=False, attachments=None, context=None, **kwargs): + """Post a new message in an existing message thread, returning the new mail.message ID. + Extra keyword arguments will be used as default column values for the new + mail.message record. + + :param int thread_id: thread ID to post into, or list with one ID + :param str body: body of the message, usually raw HTML + :param str subject: optional subject + :param str msg_type: message type, out of the possible values for mail_message.type, + currently one of ``email, 'comment', 'notification'``. + :param int parent_id: optional ID of parent message in this thread + :param tuple(str,str) attachments: list of attachment tuples in the form ``(name,content)`` + :return: ID of newly created mail.message + """ context = context or {} attachments = attachments or {} - if type(res_id) in (list, tuple): - res_id = res_id and res_id[0] or False + assert (not thread_id) or isinstance(thread_id, (int,long)) or \ + (isinstance(thread_id, (list, tuple)) and len(thread_id) == 1), "Invalid thread_id" + if isinstance(thread_id, (list, tuple)): + thread_id = thread_id and thread_id[0] to_attach = [] - for fname, fcontent in attachments: - if isinstance(fcontent, unicode): - fcontent = fcontent.encode('utf-8') + for name, content in attachments: + if isinstance(content, unicode): + content = content.encode('utf-8') data_attach = { - 'name': fname, - 'datas': fcontent, - 'datas_fname': fname, - 'description': _('email attachment'), + 'name': name, + 'datas': content, + 'datas_fname': name, + 'description': name, } to_attach.append((0,0, data_attach)) - value = kwargs - value.update( { - 'model': res_id and self._name or False, - 'res_id': res_id, + values = kwargs + values.update( { + 'model': thread_id and self._name or False, + 'thread_id': thread_id or False, 'body': body, 'subject': subject, - 'type': mtype, + 'type': msg_type, 'parent_id': parent_id, 'attachment_ids': to_attach }) - return self.pool.get('mail.message').create(cr, uid, value, context=context) + return self.pool.get('mail.message').create(cr, uid, values, context=context) #------------------------------------------------------ diff --git a/addons/mail/res_users.py b/addons/mail/res_users.py index 1e638251e40..d863719db77 100644 --- a/addons/mail/res_users.py +++ b/addons/mail/res_users.py @@ -110,9 +110,9 @@ class res_users(osv.Model): def _create_welcome_message(self, cr, uid, user, context=None): company_name = user.company_id.name if user.company_id else _('the company') - body = '''%s has joined %s.''' % (user.name, company_name) - # TODO change 1 into user.id but catch errors - return self.pool.get('res.partner').message_post(cr, 1, [user.partner_id.id], + body = _('%s has joined %s.') % (user.name, company_name) + # TODO change SUPERUSER_ID into user.id but catch errors + return self.pool.get('res.partner').message_post(cr, SUPERUSER_ID, [user.partner_id.id], body=body, context=context) def write(self, cr, uid, ids, vals, context=None): diff --git a/addons/mail/wizard/mail_compose_message.py b/addons/mail/wizard/mail_compose_message.py index 50ffd6e440e..19faaad9578 100644 --- a/addons/mail/wizard/mail_compose_message.py +++ b/addons/mail/wizard/mail_compose_message.py @@ -254,9 +254,9 @@ class mail_compose_message(osv.TransientModel): context = {} formatting = context.get('formatting') + # FIXME TODO: mass_mail_mode unused? mass_mail_mode = context.get('mail.compose.message.mode') == 'mass_mail' - mail_message_obj = self.pool.get('mail.message') for mail_wiz in self.browse(cr, uid, ids, context=context): attachment = {} for attach in mail_wiz.attachment_ids: @@ -264,6 +264,7 @@ class mail_compose_message(osv.TransientModel): # default values, according to the wizard options subject = mail_wiz.subject if formatting else False + # FIXME TODO: partner_ids not used?? partner_ids = [partner.id for partner in mail_wiz.dest_partner_ids] body = mail_wiz.body_html if mail_wiz.content_subtype == 'html' else mail_wiz.body @@ -274,7 +275,7 @@ class mail_compose_message(osv.TransientModel): active_model_pool = self.pool.get(active_model) subject = self.render_template(cr, uid, subject, active_model, active_id) body = self.render_template(cr, uid, mail_wiz.body_html, active_model, active_id) - active_model_pool.message_post(cr, uid, active_id, body, subject, 'comment', + active_model_pool.message_post(cr, uid, [active_id], body=body, subject=subject, msg_type='comment', attachments=attachment, context=context) return {'type': 'ir.actions.act_window_close'} diff --git a/addons/plugin/plugin_handler.py b/addons/plugin/plugin_handler.py index 6d6262ddd22..9c582a8e3bf 100644 --- a/addons/plugin/plugin_handler.py +++ b/addons/plugin/plugin_handler.py @@ -101,15 +101,15 @@ class plugin_handler(osv.osv_memory): notify = "Email already pushed" elif res_id == 0: if model == 'res.partner': - notify = 'User the button Partner to create a new partner' + notify = 'User the Partner button to create a new partner' else: res_id = model_obj.message_new(cr, uid, msg) - notify = "Mail succefully pushed, a new %s has been created " % model + notify = "Mail succesfully pushed, a new %s has been created " % model else: if model == 'res.partner': model_obj = self.pool.get('mail.thread') model_obj.message_post(cr, uid, [res_id], body=msg) - notify = "Mail succefully pushed" + notify = "Mail succesfully pushed" url = self._make_url(cr, uid, res_id, model) return (model, res_id, url, notify) diff --git a/addons/procurement/procurement.py b/addons/procurement/procurement.py index 8efd09a1efc..db50ebd94fd 100644 --- a/addons/procurement/procurement.py +++ b/addons/procurement/procurement.py @@ -389,7 +389,7 @@ class procurement_order(osv.osv): ok = ok and self.pool.get('stock.move').action_assign(cr, uid, [id]) order_point_id = self.pool.get('stock.warehouse.orderpoint').search(cr, uid, [('product_id', '=', procurement.product_id.id)], context=context) if not order_point_id and not ok: - message = _("Not enough stock and no minimum orderpoint rule defined.") + message = _("Not enough stock and no minimum orderpoint rule defined.") elif not order_point_id: message = _("No minimum orderpoint rule defined.") elif not ok: diff --git a/addons/project_issue/project_issue.py b/addons/project_issue/project_issue.py index 3b0311d8f45..93040b20af5 100644 --- a/addons/project_issue/project_issue.py +++ b/addons/project_issue/project_issue.py @@ -507,21 +507,21 @@ class project_issue(base_stage, osv.osv): return 'Project issue' def convert_to_task_send_note(self, cr, uid, ids, context=None): - message = _("Project issue has been converted into task.") + message = _("Project issue converted to task.") return self.message_post(cr, uid, ids, body=message, context=context) def create_send_note(self, cr, uid, ids, context=None): - message = _("Project issue has been created.") + message = _("Project issue created.") return self.message_post(cr, uid, ids, body=message, context=context) def case_escalate_send_note(self, cr, uid, ids, context=None): for obj in self.browse(cr, uid, ids, context=context): if obj.project_id: - message = _("has been escalated to '%s'.") % (obj.project_id.name) - obj.message_post(body=message, context=context) + message = _("escalated to '%s'.") % (obj.project_id.name) + obj.message_post(body=message) else: - message = _("has been escalated.") - obj.message_post(body=message, context=context) + message = _("escalated.") + obj.message_post(body=message) return True project_issue() diff --git a/addons/sale/sale.py b/addons/sale/sale.py index 476ee6458f0..521d7dc504f 100644 --- a/addons/sale/sale.py +++ b/addons/sale/sale.py @@ -1027,7 +1027,7 @@ class sale_order(osv.osv): def create_send_note(self, cr, uid, ids, context=None): for obj in self.browse(cr, uid, ids, context=context): - self.message_post(cr, uid, [obj.id], body=_("Quotation for %s has been created.") % (obj.partner_id.name), context=context) + self.message_post(cr, uid, [obj.id], body=_("Quotation for %s created.") % (obj.partner_id.name), context=context) def confirm_send_note(self, cr, uid, ids, context=None): for obj in self.browse(cr, uid, ids, context=context): @@ -1058,7 +1058,7 @@ class sale_order(osv.osv): self.message_post(cr, uid, [order.id], body=_("Draft Invoice of %s %s waiting for validation.") % (invoice.amount_total, invoice.currency_id.symbol), context=context) def action_cancel_draft_send_note(self, cr, uid, ids, context=None): - return self.message_post(cr, uid, ids, body='Sale order has been set in draft.', context=context) + return self.message_post(cr, uid, ids, body=_('Sale order set to draft.'), context=context) sale_order() From 8b7812721078b0331c90081c77fdcd6f7d4d187e Mon Sep 17 00:00:00 2001 From: Olivier Dony Date: Wed, 22 Aug 2012 14:38:58 +0200 Subject: [PATCH 155/436] [FIX] mail: typo bzr revid: odo@openerp.com-20120822123858-e3alagahniwbdpgz --- 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 5795fa8b824..61c8f611b18 100644 --- a/addons/mail/mail_thread.py +++ b/addons/mail/mail_thread.py @@ -311,7 +311,7 @@ class mail_thread(osv.Model): model_pool.message_update(cr, user_id, [thread_id], msg, context=context) else: thread_id = model_pool.message_new(cr, user_id, msg, custom_values, context=context) - self.message_post(cr, uid, [thread_id], msg_txt['body'], context=context, **msg) + self.message_post(cr, uid, [thread_id], context=context, **msg) return True def message_new(self, cr, uid, msg_dict, custom_values=None, context=None): From b873d36b9098ad2e06b6b14db691871ddc8c1371 Mon Sep 17 00:00:00 2001 From: Olivier Dony Date: Wed, 22 Aug 2012 14:43:07 +0200 Subject: [PATCH 156/436] [FIX] mail: more typos bzr revid: odo@openerp.com-20120822124307-qnwx6nk66msz01jx --- addons/mail/res_users.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addons/mail/res_users.py b/addons/mail/res_users.py index d863719db77..86fd048479c 100644 --- a/addons/mail/res_users.py +++ b/addons/mail/res_users.py @@ -105,7 +105,7 @@ class res_users(osv.Model): # alias mail_alias.write(cr, SUPERUSER_ID, [alias_id], {"alias_force_thread_id": user_id}, context) # create a welcome message - self._create_welcome_message(cr, uid, user_id, context=context) + self._create_welcome_message(cr, uid, user, context=context) return user_id def _create_welcome_message(self, cr, uid, user, context=None): From a001d892431b8efabef6118e67e828aa1849c2da Mon Sep 17 00:00:00 2001 From: Olivier Dony Date: Wed, 22 Aug 2012 14:49:43 +0200 Subject: [PATCH 157/436] [FIX] mail: more typos bzr revid: odo@openerp.com-20120822124943-2ixd2vbbug38tfb2 --- 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 61c8f611b18..c8e5ba3ffa1 100644 --- a/addons/mail/mail_thread.py +++ b/addons/mail/mail_thread.py @@ -544,7 +544,7 @@ class mail_thread(osv.Model): values = kwargs values.update( { 'model': thread_id and self._name or False, - 'thread_id': thread_id or False, + 'res_id': thread_id or False, 'body': body, 'subject': subject, 'type': msg_type, From 786419565ab19523233856a0fcce26de58db3b3c Mon Sep 17 00:00:00 2001 From: Olivier Dony Date: Wed, 22 Aug 2012 15:30:28 +0200 Subject: [PATCH 158/436] [FIX] mail: make basic mail tests pass again, remove useless/dead test code bzr revid: odo@openerp.com-20120822133028-hms3xfhibhn1j38l --- addons/mail/mail_thread.py | 3 +- addons/mail/tests/test_mail.py | 15 ++++--- addons/mail/tests/write_test.py | 79 --------------------------------- 3 files changed, 9 insertions(+), 88 deletions(-) delete mode 100644 addons/mail/tests/write_test.py diff --git a/addons/mail/mail_thread.py b/addons/mail/mail_thread.py index c8e5ba3ffa1..1be8dfdbf0c 100644 --- a/addons/mail/mail_thread.py +++ b/addons/mail/mail_thread.py @@ -297,7 +297,6 @@ class mail_thread(osv.Model): thread_id, custom_values, context=context) msg = self.parse_message(cr, uid, msg_txt, save_original=save_original, context=context) - msg['state'] = 'received' if strip_attachments and 'attachments' in msg: del msg['attachments'] for model, thread_id, custom_values, user_id in routes: @@ -543,7 +542,7 @@ class mail_thread(osv.Model): values = kwargs values.update( { - 'model': thread_id and self._name or False, + 'model': thread_id and context.get('thread_model', self._name) or False, 'res_id': thread_id or False, 'body': body, 'subject': subject, diff --git a/addons/mail/tests/test_mail.py b/addons/mail/tests/test_mail.py index 0940d72c490..a9da19e90fa 100644 --- a/addons/mail/tests/test_mail.py +++ b/addons/mail/tests/test_mail.py @@ -82,26 +82,27 @@ class test_mail(common.TransactionCase): self.group_tech_id = self.mail_group.create(self.cr, self.uid, {'name': 'tech'}) def test_message_process(self): + cr, uid = self.cr, self.uid # Incoming mail creates a new mail_group "frogs" - self.assertEqual(self.mail_group.search(self.cr, self.uid, [('name','=','frogs')]), []) + self.assertEqual(self.mail_group.search(cr, uid, [('name','=','frogs')]), []) mail_frogs = MAIL_TEMPLATE.format(to='groups@example.com, other@gmail.com', subject='frogs', extra='') - self.mail_thread.message_process(self.cr, self.uid, None, mail_frogs) - frog_groups = self.mail_group.search(self.cr, self.uid, [('name','=','frogs')]) + self.mail_thread.message_process(cr, uid, None, mail_frogs) + frog_groups = self.mail_group.search(cr, uid, [('name','=','frogs')]) self.assertTrue(len(frog_groups) == 1) # Previously-created group can be emailed now - it should have an implicit alias group+frogs@... - frog_group = self.mail_group.browse(self.cr, self.uid, frog_groups[0]) + frog_group = self.mail_group.browse(cr, uid, frog_groups[0]) group_messages = frog_group.message_ids self.assertTrue(len(group_messages) == 1, 'New group should only have the original message') mail_frog_news = MAIL_TEMPLATE.format(to='Friendly Frogs ', subject='news', extra='') - self.mail_thread.message_process(self.cr, self.uid, None, mail_frog_news) + self.mail_thread.message_process(cr, uid, None, mail_frog_news) frog_group.refresh() self.assertTrue(len(frog_group.message_ids) == 2, 'Group should contain 2 messages now') # Even with a wrong destination, a reply should end up in the correct thread mail_reply = MAIL_TEMPLATE.format(to='erroneous@example.com>', subject='Re: news', extra='In-Reply-To: <12321321-openerp-%d-mail.group@example.com>\n'%frog_group.id) - self.mail_thread.message_process(self.cr, self.uid, None, mail_reply) + self.mail_thread.message_process(cr, uid, None, mail_reply) frog_group.refresh() self.assertTrue(len(frog_group.message_ids) == 3, 'Group should contain 3 messages now') @@ -109,4 +110,4 @@ class test_mail(common.TransactionCase): mail_spam = MAIL_TEMPLATE.format(to='noone@example.com', subject='spam', extra='') self.assertRaises(Exception, self.mail_thread.message_process, - self.cr, self.uid, None, mail_spam) + cr, uid, None, mail_spam) diff --git a/addons/mail/tests/write_test.py b/addons/mail/tests/write_test.py deleted file mode 100644 index 920fbb92dbe..00000000000 --- a/addons/mail/tests/write_test.py +++ /dev/null @@ -1,79 +0,0 @@ -############################################################################## -# -# Copyright (c) 2004 TINY SPRL. (http://tiny.be) All Rights Reserved. -# Fabien Pinckaers -# -# WARNING: This program as such is intended to be used by professional -# programmers who take the whole responsability of assessing all potential -# consequences resulting from its eventual inadequacies and bugs -# End users who are looking for a ready-to-use solution with commercial -# garantees and support are strongly adviced to contract a Free Software -# Service Company -# -# This program is Free Software; you can redistribute it and/or -# modify it under the terms of the GNU General Public License -# as published by the Free Software Foundation; either version 2 -# 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 General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -# -############################################################################## - -# -# This module test all RPC methods -# - -import xmlrpclib - -uid = 1 -passwd='admin' -server = 'localhost' -db = 'trunk' - -sock = xmlrpclib.ServerProxy('http://'+server+':8069/xmlrpc/object') - -def _print_data(data, level=0): - for d in data: - print ' '*level, d['id'] - _print_data(d['child_ids'], level+1) - -print 'With Domain', [('model','=','mail.group')], 'thread_level' -data = sock.execute(db, uid, passwd, 'mail.message', 'message_read', False, [('model','=','mail.group')], 1) -_print_data(data) - -print 'With Domain', [('model','=','mail.group')], 'no thread_level' -data = sock.execute(db, uid, passwd, 'mail.message', 'message_read', False, [('model','=','mail.group')], 0) -_print_data(data) - - -print 'With Domain', [('model','=','mail.group'), ('parent_id','=',False)], 'thread_level=0' -data = sock.execute(db, uid, passwd, 'mail.message', 'message_read', False, [('model','=','mail.group'), ('parent_id','=',False)], 0) -_print_data(data) - -print 'With Domain', [('model','=','mail.group'), ('parent_id','=',False)], 'thread_level=2' -data = sock.execute(db, uid, passwd, 'mail.message', 'message_read', False, [('model','=','mail.group'), ('parent_id','=',False)], 2) -_print_data(data) - -print 'Fixed IDS', [2,3,41,43], 'thread_level' -data = sock.execute(db, uid, passwd, 'mail.message', 'message_read', [2,3,41,43], [], 1) -_print_data(data) - -print 'Fixed IDS', [2,43], 'no thread_level' -data = sock.execute(db, uid, passwd, 'mail.message', 'message_read', [2,43], [], 0) -_print_data(data) - -print 'Fixed IDS', [2,43], 'thread_level' -data = sock.execute(db, uid, passwd, 'mail.message', 'message_read', [2,43], [], 1) -_print_data(data) - -print 'domain [id in 3,41]', 'thread_level' -data = sock.execute(db, uid, passwd, 'mail.message', 'message_read', False, [('id','in',[3,41])], 1) -_print_data(data) - From 0f63b4a1fb8a41971d1fd6a9810a75a7af5de759 Mon Sep 17 00:00:00 2001 From: Fabien Pinckaers Date: Wed, 22 Aug 2012 15:34:06 +0200 Subject: [PATCH 159/436] [IMP] need action bzr revid: fp@tinyerp.com-20120822133406-9ayg228mxfeg16dg --- openerp/addons/base/ir/ir_actions.py | 4 +++- openerp/addons/base/ir/ir_ui_menu.py | 4 +--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/openerp/addons/base/ir/ir_actions.py b/openerp/addons/base/ir/ir_actions.py index 28a309f427a..b70a9d89c06 100644 --- a/openerp/addons/base/ir/ir_actions.py +++ b/openerp/addons/base/ir/ir_actions.py @@ -43,7 +43,6 @@ class actions(osv.osv): _table = 'ir_actions' _order = 'name' _columns = { - 'name': fields.char('Action Name', required=True, size=64), 'type': fields.char('Action Type', required=True, size=32,readonly=True), 'usage': fields.char('Action Usage', size=32), } @@ -872,10 +871,13 @@ class act_client(osv.osv): self.write(cr, uid, id, {'params_store': field_value}, context=context) _columns = { + 'name': fields.char('Action Name', required=True, size=64, translate=True), 'tag': fields.char('Client action tag', size=64, required=True, help="An arbitrary string, interpreted by the client" " according to its own needs and wishes. There " "is no central tag repository across clients."), + 'res_model': fields.char('Destination Model', size=64, + help="Optional model, mostly used for needactions."), 'params': fields.function(_get_params, fnct_inv=_set_params, type='binary', string="Supplementary arguments", diff --git a/openerp/addons/base/ir/ir_ui_menu.py b/openerp/addons/base/ir/ir_ui_menu.py index f5e4ee2ba05..bc743d34fc0 100644 --- a/openerp/addons/base/ir/ir_ui_menu.py +++ b/openerp/addons/base/ir/ir_ui_menu.py @@ -264,12 +264,10 @@ class ir_ui_menu(osv.osv): res[menu.id]['needaction_enabled'] = False res[menu.id]['needaction_counter'] = False res[menu.id] = {} - if menu.action and menu.action.type == 'ir.actions.act_window' and menu.action.res_model: + if menu.action and menu.action.type in ('ir.actions.act_window','ir.actions.client') and menu.action.res_model: obj = self.pool.get(menu.action.res_model) if obj._needaction: res[menu.id]['needaction_enabled'] = obj._needaction - # check domain and context: should we evaluate the domain ? - # and add context of the action ? res[menu.id]['needaction_counter'] = obj._needaction_count(cr, uid, menu.action.domain, context=context) return res From 4fdb70d12ab10728034e8077e9bba12d4b8a3a8a Mon Sep 17 00:00:00 2001 From: Fabien Pinckaers Date: Wed, 22 Aug 2012 15:37:23 +0200 Subject: [PATCH 160/436] fix bzr revid: fp@tinyerp.com-20120822133723-k5r76cty7dej5kjh --- addons/mail/mail_group.py | 3 +-- addons/mail/mail_message.py | 7 +++++++ 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/addons/mail/mail_group.py b/addons/mail/mail_group.py index faa4bb1c9b4..1824f59e0b9 100644 --- a/addons/mail/mail_group.py +++ b/addons/mail/mail_group.py @@ -132,8 +132,7 @@ class mail_group(osv.Model): params = { 'search_view_id': search_ref and search_ref[1] or False, 'domain': [('model','=','mail.group'),('res_id','=',mail_group_id)], - 'res_model': 'mail.group', - 'res_id': mail_group_id, + 'res_model': 'mail.message', 'thread_level': 2 } cobj = self.pool.get('ir.actions.client') diff --git a/addons/mail/mail_message.py b/addons/mail/mail_message.py index b74df2f8594..b9cb0723a4f 100644 --- a/addons/mail/mail_message.py +++ b/addons/mail/mail_message.py @@ -109,6 +109,13 @@ class mail_message(osv.Model): 'body': fields.html('Content'), } + def _needaction_domain_get(self, cr, uid, context={}): + if self._needaction: + partner_id = self.pool.get('res.users').browse(cr, uid, uid, context=context).partner_id.id + return [('notification_ids.partner_id','=',partner_id),('notification_ids.read','=',False)] + return [] + + def _get_default_author(self, cr, uid, context={}): return self.pool.get('res.users').browse(cr, uid, uid, context=context).partner_id.id From 9d6ee746eb4e29ea7dca76ef8c836d71374d6186 Mon Sep 17 00:00:00 2001 From: Fabien Pinckaers Date: Wed, 22 Aug 2012 15:41:09 +0200 Subject: [PATCH 161/436] fix bzr revid: fp@tinyerp.com-20120822134109-97lbhcllylbe15ce --- openerp/addons/base/ir/ir_actions.py | 1 + 1 file changed, 1 insertion(+) diff --git a/openerp/addons/base/ir/ir_actions.py b/openerp/addons/base/ir/ir_actions.py index b70a9d89c06..cf67e6fcf88 100644 --- a/openerp/addons/base/ir/ir_actions.py +++ b/openerp/addons/base/ir/ir_actions.py @@ -43,6 +43,7 @@ class actions(osv.osv): _table = 'ir_actions' _order = 'name' _columns = { + 'name': fields.char('Name', size=64, required=True), 'type': fields.char('Action Type', required=True, size=32,readonly=True), 'usage': fields.char('Action Usage', size=32), } From b531d9cff1054165bf8cc5cfe37304187d0924e6 Mon Sep 17 00:00:00 2001 From: Fabien Pinckaers Date: Wed, 22 Aug 2012 17:31:45 +0200 Subject: [PATCH 162/436] [IMP] needaction after mail.thread bzr revid: fp@tinyerp.com-20120822153145-g7xopqq7oje7y60c --- addons/base_calendar/crm_meeting.py | 2 +- addons/crm/crm_lead.py | 2 +- addons/event/event.py | 2 +- addons/hr_holidays/hr_holidays.py | 2 +- addons/hr_recruitment/hr_recruitment.py | 2 +- addons/mrp/mrp.py | 2 +- addons/project/project.py | 4 ++-- addons/project_issue/project_issue.py | 2 +- addons/purchase/purchase.py | 2 +- addons/purchase_requisition/purchase_requisition.py | 2 +- addons/sale/sale.py | 2 +- 11 files changed, 12 insertions(+), 12 deletions(-) diff --git a/addons/base_calendar/crm_meeting.py b/addons/base_calendar/crm_meeting.py index 5f58877a8a6..e1c66148ae7 100644 --- a/addons/base_calendar/crm_meeting.py +++ b/addons/base_calendar/crm_meeting.py @@ -43,7 +43,7 @@ class crm_meeting(base_state, osv.Model): _name = 'crm.meeting' _description = "Meeting" _order = "id desc" - _inherit = ["calendar.event", 'ir.needaction_mixin', "mail.thread"] + _inherit = ["calendar.event", "mail.thread", 'ir.needaction_mixin'] _columns = { # base_state required fields 'create_date': fields.datetime('Creation Date', readonly=True), diff --git a/addons/crm/crm_lead.py b/addons/crm/crm_lead.py index 39ad56e007d..83ffb379950 100644 --- a/addons/crm/crm_lead.py +++ b/addons/crm/crm_lead.py @@ -38,7 +38,7 @@ class crm_lead(base_stage, osv.osv): _name = "crm.lead" _description = "Lead/Opportunity" _order = "priority,date_action,id desc" - _inherit = ['ir.needaction_mixin', 'mail.thread'] + _inherit = ['mail.thread','ir.needaction_mixin'] def _get_default_section_id(self, cr, uid, context=None): """ Gives default section by checking if present in the context """ diff --git a/addons/event/event.py b/addons/event/event.py index d347c1547d8..763aa423378 100644 --- a/addons/event/event.py +++ b/addons/event/event.py @@ -47,7 +47,7 @@ class event_event(osv.osv): _name = 'event.event' _description = __doc__ _order = 'date_begin' - _inherit = ['ir.needaction_mixin','mail.thread'] + _inherit = ['mail.thread','ir.needaction_mixin'] def name_get(self, cr, uid, ids, context=None): if not ids: diff --git a/addons/hr_holidays/hr_holidays.py b/addons/hr_holidays/hr_holidays.py index 2e26e72bcdc..4bc9dabba71 100644 --- a/addons/hr_holidays/hr_holidays.py +++ b/addons/hr_holidays/hr_holidays.py @@ -94,7 +94,7 @@ class hr_holidays(osv.osv): _name = "hr.holidays" _description = "Leave" _order = "type desc, date_from asc" - _inherit = ['ir.needaction_mixin', 'mail.thread'] + _inherit = [ 'mail.thread','ir.needaction_mixin'] def _employee_get(self, cr, uid, context=None): ids = self.pool.get('hr.employee').search(cr, uid, [('user_id', '=', uid)], context=context) diff --git a/addons/hr_recruitment/hr_recruitment.py b/addons/hr_recruitment/hr_recruitment.py index 910c4b89dd2..db5fd57b0a9 100644 --- a/addons/hr_recruitment/hr_recruitment.py +++ b/addons/hr_recruitment/hr_recruitment.py @@ -95,7 +95,7 @@ class hr_applicant(base_stage, osv.Model): _name = "hr.applicant" _description = "Applicant" _order = "id desc" - _inherit = ['ir.needaction_mixin', 'mail.thread'] + _inherit = ['mail.thread', 'ir.needaction_mixin'] def _get_default_department_id(self, cr, uid, context=None): """ Gives default department by checking if present in the context """ diff --git a/addons/mrp/mrp.py b/addons/mrp/mrp.py index 76f7c7b9f34..c667daadbec 100644 --- a/addons/mrp/mrp.py +++ b/addons/mrp/mrp.py @@ -407,7 +407,7 @@ class mrp_production(osv.osv): _name = 'mrp.production' _description = 'Manufacturing Order' _date_name = 'date_planned' - _inherit = ['ir.needaction_mixin', 'mail.thread'] + _inherit = ['mail.thread', 'ir.needaction_mixin'] def _production_calc(self, cr, uid, ids, prop, unknow_none, context=None): """ Calculates total hours and total no. of cycles for a production order. diff --git a/addons/project/project.py b/addons/project/project.py index 5fe7f31fa57..c998cd368d0 100644 --- a/addons/project/project.py +++ b/addons/project/project.py @@ -66,7 +66,7 @@ class project(osv.osv): _description = "Project" _inherits = {'account.analytic.account': "analytic_account_id", "mail.alias": "alias_id"} - _inherit = ['ir.needaction_mixin', 'mail.thread'] + _inherit = ['mail.thread', 'ir.needaction_mixin'] def search(self, cr, user, args, offset=0, limit=None, order=None, context=None, count=False): if user == 1: @@ -544,7 +544,7 @@ class task(base_stage, osv.osv): _name = "project.task" _description = "Task" _date_name = "date_start" - _inherit = ['ir.needaction_mixin', 'mail.thread'] + _inherit = ['mail.thread', 'ir.needaction_mixin'] def _get_default_project_id(self, cr, uid, context=None): """ Gives default section by checking if present in the context """ diff --git a/addons/project_issue/project_issue.py b/addons/project_issue/project_issue.py index 93040b20af5..49d62ae9ba7 100644 --- a/addons/project_issue/project_issue.py +++ b/addons/project_issue/project_issue.py @@ -46,7 +46,7 @@ class project_issue(base_stage, osv.osv): _name = "project.issue" _description = "Project Issue" _order = "priority, create_date desc" - _inherit = ['ir.needaction_mixin', 'mail.thread'] + _inherit = ['mail.thread', 'ir.needaction_mixin'] def _get_default_project_id(self, cr, uid, context=None): """ Gives default project by checking if present in the context """ diff --git a/addons/purchase/purchase.py b/addons/purchase/purchase.py index 0697ccb339a..b1a5631e59e 100644 --- a/addons/purchase/purchase.py +++ b/addons/purchase/purchase.py @@ -220,7 +220,7 @@ class purchase_order(osv.osv): ('name_uniq', 'unique(name, company_id)', 'Order Reference must be unique per Company!'), ] _name = "purchase.order" - _inherit = ['ir.needaction_mixin', 'mail.thread'] + _inherit = ['mail.thread', 'ir.needaction_mixin'] _description = "Purchase Order" _order = "name desc" diff --git a/addons/purchase_requisition/purchase_requisition.py b/addons/purchase_requisition/purchase_requisition.py index bbd011a53a8..f8d7741822e 100644 --- a/addons/purchase_requisition/purchase_requisition.py +++ b/addons/purchase_requisition/purchase_requisition.py @@ -31,7 +31,7 @@ import decimal_precision as dp class purchase_requisition(osv.osv): _name = "purchase.requisition" _description="Purchase Requisition" - _inherit = ['ir.needaction_mixin', 'mail.thread'] + _inherit = ['mail.thread', 'ir.needaction_mixin'] _columns = { 'name': fields.char('Requisition Reference', size=32,required=True), 'origin': fields.char('Source', size=32), diff --git a/addons/sale/sale.py b/addons/sale/sale.py index 521d7dc504f..6a6678d6163 100644 --- a/addons/sale/sale.py +++ b/addons/sale/sale.py @@ -48,7 +48,7 @@ sale_shop() class sale_order(osv.osv): _name = "sale.order" - _inherit = ['ir.needaction_mixin', 'mail.thread'] + _inherit = ['mail.thread', 'ir.needaction_mixin'] _description = "Sales Order" From f6b6fb4debcd02918c8a4b172227e6331d38fdc5 Mon Sep 17 00:00:00 2001 From: Fabien Pinckaers Date: Wed, 22 Aug 2012 17:59:54 +0200 Subject: [PATCH 163/436] [FIX] needaction ok for crm bzr revid: fp@tinyerp.com-20120822155954-8ix4op6hj5e5hzi5 --- addons/mail/mail_thread.py | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/addons/mail/mail_thread.py b/addons/mail/mail_thread.py index 1be8dfdbf0c..f7078348f08 100644 --- a/addons/mail/mail_thread.py +++ b/addons/mail/mail_thread.py @@ -87,8 +87,18 @@ class mail_thread(osv.Model): return res # FP Note: todo - def _search_unread(self, tobj, cr, uid, obj=None, name=None, domain=None, context=None): - return [] + def _search_unread(self, cr, uid, obj=None, name=None, domain=None, context=None): + partner_id = self.pool.get('res.users').browse(cr, uid, uid, context=context).partner_id.id + res = {} + notif_obj = self.pool.get('mail.notification') + notif_ids = notif_obj.search(cr, uid, [ + ('partner_id', '=', partner_id), + ('message_id.model', '=', self._name), + ('read', '=', False) + ], context=context) + for notif in notif_obj.browse(cr, uid, notif_ids, context=context): + res[notif.message_id.res_id] = True + return [('id','in',res.keys())] _columns = { 'message_is_follower': fields.function(_get_message_data, From 9c3e45579958d2dcfd02990d6cc6858c46f1bc1b Mon Sep 17 00:00:00 2001 From: Fabien Pinckaers Date: Wed, 22 Aug 2012 18:13:15 +0200 Subject: [PATCH 164/436] [FIX] bzr revid: fp@tinyerp.com-20120822161315-n1uwcxevdo18elkb --- addons/mail/mail_thread.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/addons/mail/mail_thread.py b/addons/mail/mail_thread.py index f7078348f08..6867c0e08b1 100644 --- a/addons/mail/mail_thread.py +++ b/addons/mail/mail_thread.py @@ -86,7 +86,6 @@ class mail_thread(osv.Model): res[thread.id]['message_is_follower'] = user.partner_id.id in [follower.id for follower in thread.message_follower_ids] return res - # FP Note: todo def _search_unread(self, cr, uid, obj=None, name=None, domain=None, context=None): partner_id = self.pool.get('res.users').browse(cr, uid, uid, context=context).partner_id.id res = {} @@ -103,8 +102,8 @@ class mail_thread(osv.Model): _columns = { 'message_is_follower': fields.function(_get_message_data, type='boolean', string='Is a Follower', multi='_get_message_data'), + # missing domain on model 'message_follower_ids': fields.many2many('res.partner', 'mail_followers', 'res_id', 'partner_id', - domain=lambda self: [('res_model','=',self._name)], string='Followers'), 'message_ids': fields.one2many('mail.message', 'res_id', domain=lambda self: [('model','=',self._name)], @@ -112,6 +111,7 @@ class mail_thread(osv.Model): help="All messages related to the current document."), 'message_unread': fields.function(_get_message_data, fnct_search=_search_unread, string='Has Unread Messages', + type='boolean', help="When checked, new messages require your attention.", multi="_get_message_data"), 'message_summary': fields.function(_get_message_data, method=True, From 552705494c11c53d81671766e608f830e773df96 Mon Sep 17 00:00:00 2001 From: Fabien Pinckaers Date: Wed, 22 Aug 2012 18:14:07 +0200 Subject: [PATCH 165/436] [IMP] needaction bzr revid: fp@tinyerp.com-20120822161407-aucl1m2jsa3pe6sv --- openerp/addons/base/ir/ir_needaction.py | 3 ++- openerp/addons/base/ir/ir_ui_menu.py | 4 +++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/openerp/addons/base/ir/ir_needaction.py b/openerp/addons/base/ir/ir_needaction.py index caf099c18cd..1e59a6fbb8e 100644 --- a/openerp/addons/base/ir/ir_needaction.py +++ b/openerp/addons/base/ir/ir_needaction.py @@ -63,5 +63,6 @@ class ir_needaction_mixin(osv.Model): dom = self._needaction_domain_get(cr, uid, context=context) if dom is False: return 0 - return self.search(cr, uid, (domain or []) +dom, context=context, count=True) + result = self.search(cr, uid, (domain or []) +dom, context=context, count=True) + return result diff --git a/openerp/addons/base/ir/ir_ui_menu.py b/openerp/addons/base/ir/ir_ui_menu.py index bc743d34fc0..eefa8ea4a50 100644 --- a/openerp/addons/base/ir/ir_ui_menu.py +++ b/openerp/addons/base/ir/ir_ui_menu.py @@ -24,6 +24,8 @@ import base64 import re import threading +from tools.safe_eval import safe_eval as eval + import tools import openerp.modules from osv import fields, osv @@ -268,7 +270,7 @@ class ir_ui_menu(osv.osv): obj = self.pool.get(menu.action.res_model) if obj._needaction: res[menu.id]['needaction_enabled'] = obj._needaction - res[menu.id]['needaction_counter'] = obj._needaction_count(cr, uid, menu.action.domain, context=context) + res[menu.id]['needaction_counter'] = obj._needaction_count(cr, uid, menu.action.domain and eval(menu.action.domain), context=context) return res _columns = { From 4e6e86fab535e217bbe548ee932bdcca1346e9f0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thibault=20Delavall=C3=A9e?= Date: Wed, 22 Aug 2012 18:21:51 +0200 Subject: [PATCH 166/436] [IMP] [CLEAN] [WIP] composer: cleaning of the server-side code. It now relies less on context keys and values; some fields have been added to manage the composition mode, model, res_id and message_id as a classic form. Updated form view, with invisible fields, to have those value accessible and modifiable through onchange or through JS. Updated JS-part of the composer to handle the new composer; less options, less logic client-side ! Still not finished, short in time today (have to check mass_mail, templates). bzr revid: tde@openerp.com-20120822162151-n9o23ik0v45h7v6j --- addons/mail/static/src/js/mail.js | 157 +++++----- addons/mail/static/src/xml/mail.xml | 8 +- addons/mail/wizard/mail_compose_message.py | 275 ++++++++---------- .../mail/wizard/mail_compose_message_view.xml | 11 +- 4 files changed, 201 insertions(+), 250 deletions(-) diff --git a/addons/mail/static/src/js/mail.js b/addons/mail/static/src/js/mail.js index 409ff41fcf5..fb788e305cd 100644 --- a/addons/mail/static/src/js/mail.js +++ b/addons/mail/static/src/js/mail.js @@ -104,87 +104,60 @@ openerp.mail = function(session) { * @param {Object} [options] * @param {String} [options.res_model] res_model of document [REQUIRED] * @param {Number} [options.res_id] res_id of record [REQUIRED] - * @param {Number} [options.formatting] true/false, tells whether - * we are in advance formatting mode - * @param {String} [options.model] mail.compose.message.mode (see - * composition wizard) + * @param {String} [options.composition_mode] mail.compose.message.mode + * (see composition wizard) * @param {Number} [options.msg_id] id of a message in case we are in * reply mode */ - init: function(parent, options) { + init: function (parent, options) { var self = this; this._super(parent); // options this.options = options || {}; - this.options.context = options.context || {}; - this.options.formatting = options.formatting || false; - this.options.mode = options.mode || 'comment'; + this.options.composition_mode = options.composition_mode || 'comment'; this.options.form_xml_id = options.form_xml_id || 'email_compose_message_wizard_form_chatter'; - this.options.form_view_id = false; - if (this.options.mode == 'reply') { - this.options.active_id = this.options.msg_id; - } else { - this.options.active_id = this.options.res_id; - } - this.formatting = false; + this.options.form_view_id = options.form_view_id || false; + this.options.context = options.context || {}; // debug console.groupCollapsed('New ComposeMessage: model', this.options.res_model, ', id', this.options.res_id); console.log('context:', this.options.context); console.groupEnd(); }, - /** - * Reinitialize the widget field values to the default values. The - * purpose is to avoid to destroy and re-build a form view. Default - * values are therefore given as for an onchange. */ - reinit: function() { - var self = this; - if (! this.form_view) return; - var call_defer = this.ds_compose.call('default_get', [['subject', 'body_text', 'body', 'attachment_ids', 'dest_partner_ids'], this.ds_compose.get_context()]).then( - function (result) { - self.form_view.on_processed_onchange({'value': result}, []); - }); - return call_defer; - }, - - /** - * Override-hack of do_action: clean the form */ - do_action: function(action, on_close) { - // this.init_comments(); - return this._super(action, on_close); - }, - - /** - * Widget start function - * - builds and initializes the form view */ - start: function() { - var self = this; + start: function () { this._super.apply(this, arguments); // customize display: add avatar, clean previous content var user_avatar = mail.ChatterUtils.get_image(this.session.prefix, this.session.session_id, 'res.users', 'image_small', this.session.uid); this.$element.find('img.oe_mail_icon').attr('src', user_avatar); this.$element.find('div.oe_mail_msg_content').empty(); - // create a context for the default_get of the compose form - var widget_context = { - 'active_model': this.options.res_model, - 'active_id': this.options.active_id, - 'mail.compose.message.mode': this.options.mode, - }; - var context = _.extend({}, this.options.context, widget_context); + // create a context for the dataset and default_get of the wizard + var context = this._update_context({}); + console.log(context); + // debugger this.ds_compose = new session.web.DataSetSearch(this, 'mail.compose.message', context); // find the id of the view to display in the chatter form var data_ds = new session.web.DataSetSearch(this, 'ir.model.data'); - var deferred_form_id = data_ds.call('get_object_reference', ['mail', this.options.form_xml_id]).then( function (result) { - if (result) { - self.options.form_view_id = result[1]; - } - }).pipe(this.proxy('create_form_view')); - return deferred_form_id; + return data_ds.call('get_object_reference', ['mail', this.options.form_xml_id]).pipe(this.proxy('create_form_view')); }, - /** - * Create a FormView, then append it to the to widget DOM. */ - create_form_view: function () { + /** Update the context of the compose wizard */ + _update_context: function (dest_context) { + _.extend(dest_context, this.options.context, { + 'default_model': this.options.res_model, + 'mail.compose.message.mode': this.options.composition_mode + }); + if (this.options.composition_mode == 'comment') { + _.extend(dest_context, {'default_res_id': this.options.res_id}); + } + else if (this.options.composition_mode == 'reply') { + _.extend(dest_context, {'active_id': this.options.msg_id}); + } + return dest_context + }, + + /** Create a FormView, then append it to the to widget DOM. */ + create_form_view: function (form_view_id) { + this.options.form_view_id = form_view_id[1] || false; var self = this; // destroy previous form_view if any if (this.form_view) { this.form_view.destroy(); } @@ -200,12 +173,31 @@ openerp.mail = function(session) { return $.when(this.form_view.appendTo(msg_node)).pipe(function() { self.bind_events(); self.form_view.do_show(); - if (self.options.formatting) { self.toggle_formatting_mode(); } }); }, - destroy: function() { - this._super.apply(this, arguments); + /** + * Reinitialize the widget field values to the default values. The + * purpose is to avoid to destroy and re-build a form view. Default + * values are therefore given as for an on_change. */ + refresh: function (options_update_values) { + var self = this; + // debugger + this.options = _.extend(this.options, options_update_values); + if (! this.form_view) return; + this.ds_compose.context = this._update_context(this.ds_compose.context); + return this.ds_compose.call('default_get', [ + ['subject', 'body_text', 'body', 'attachment_ids', 'partner_ids', 'composition_mode', + 'res_model', 'res_id', 'parent_id', 'content_subtype'], + this.ds_compose.get_context(), + ]).then( function (result) { self.form_view.on_processed_onchange({'value': result}, []); }); + }, + + /** + * Override-hack of do_action: clean the form */ + do_action: function(action, on_close) { + console.log('compose_message do_action', action, on_close); + return this._super(action, on_close); }, /** @@ -213,38 +205,23 @@ openerp.mail = function(session) { * in the function. */ bind_events: function() { var self = this; - this.$element.find('button.oe_form_button').click(function (event) { - event.preventDefault(); - }); + // this.$element.find('button.oe_form_button').click(function (event) { + // event.preventDefault(); + // event.stopPropagation(); + // }); // event: click on 'Formatting' icon-link that toggles the advanced // formatting options for writing a message (subject, body_html) - this.$element.on('click', 'button.oe_mail_compose_message_formatting', function (event) { - event.preventDefault(); - event.stopPropagation(); - self.toggle_formatting_mode(event); - }); + // this.$element.on('click', 'button.oe_mail_compose_message_formatting', function (event) { + // event.preventDefault(); + // event.stopPropagation(); + // self.toggle_formatting_mode(event); + // }); // event: click on 'Attachment' icon-link that opens the dialog to // add an attachment. this.$element.on('click', 'button.oe_mail_compose_message_attachment', function (event) { event.stopImmediatePropagation(); }); }, - - /** - * Toggle the formatting mode. */ - toggle_formatting_mode: function(event) { - this.formatting = ! this.formatting; - // update context of datasetsearch - this.ds_compose.context.formatting = this.formatting; - }, - - /** - * Update the values of the composition form; with possible different - * values for body and body_html. */ - set_body_value: function(body, body_html) { - this.form_view.fields.body.set_value(body); - this.form_view.fields.body_html.set_value(body_html); - }, }), /** @@ -398,12 +375,15 @@ openerp.mail = function(session) { }); // event: click on "Reply by email" in msg side menu (email style) this.$element.on('click', 'a.oe_mail_msg_reply_by_email', function (event) { + console.log('cacaprout'); event.preventDefault(); event.stopPropagation(); var msg_id = event.srcElement.dataset.msg_id; var formatting = (event.srcElement.dataset.formatting == 'html'); if (! msg_id) return false; - self.instantiate_composition_form('reply', formatting, msg_id); + // self.instantiate_composition_form('reply', formatting, msg_id); + console.log('cacaprout2'); + self.compose_message_widget.refresh({'composition_mode': 'reply', 'msg_id': parseInt(msg_id)}); }); }, @@ -420,15 +400,14 @@ openerp.mail = function(session) { return this._super(action, on_close); }, - /** Instantiate the composition form, with paramteres coming from thread parameters */ + /** Instantiate the composition form, with parameters coming from thread parameters */ instantiate_composition_form: function(mode, formatting, msg_id, context) { if (this.compose_message_widget) { this.compose_message_widget.destroy(); } this.compose_message_widget = new mail.ComposeMessage(this, { - 'extended_mode': false, 'uid': this.options.uid, 'res_model': this.options.context.res_model, - 'res_id': this.options.context.res_id, 'mode': mode || 'comment', 'msg_id': msg_id, - 'formatting': formatting || false, 'context': context || false } ); + 'res_model': this.options.context.res_model, 'res_id': this.options.context.res_id, + 'composition_mode': mode || 'comment', 'msg_id': msg_id, 'context': context || false } ); var composition_node = this.$element.find('div.oe_mail_thread_action'); composition_node.empty(); var compose_done = this.compose_message_widget.appendTo(composition_node); diff --git a/addons/mail/static/src/xml/mail.xml b/addons/mail/static/src/xml/mail.xml index 9a6d8b92a3c..d0ae83316e7 100644 --- a/addons/mail/static/src/xml/mail.xml +++ b/addons/mail/static/src/xml/mail.xml @@ -102,15 +102,15 @@ diff --git a/addons/mail/wizard/mail_compose_message.py b/addons/mail/wizard/mail_compose_message.py index 19faaad9578..140773981e5 100644 --- a/addons/mail/wizard/mail_compose_message.py +++ b/addons/mail/wizard/mail_compose_message.py @@ -32,81 +32,75 @@ from tools.translate import _ EXPRESSION_PATTERN = re.compile('(\$\{.+?\})') class mail_compose_message(osv.TransientModel): - """ Generic Email composition wizard. This wizard is meant to be inherited - at model and view levels to provide specific wizard features. + """ Generic message composition wizard. You may inherit from this wizard + at model and view levels to provide specific features. - The behavior of the wizard can be modified through the use of context - parameters, among which are: - - mail.compose.message.mode: - - if set to 'reply', the wizard is a reply to a previous message. - It is pre-populated with the original quote - - if set to 'comment', it means you are writing a new message to - be attached to a document. It is pre-populated with values - coming from ``get_value``, related to the document, and that - can be overridden to add specific model-related behavior. - - if set to 'mass_mail', the wizard is in mass mailing mode where - the mail details can contain template placeholders that will be - merged with actual data before being sent to each recipient. - - active_model: model name of the document to which the mail being - composed is related - - active_id: id of the document to which the mail being composed is - related, or id of the message to which user is replying, in case - ``mail.compose.message.mode == 'reply'`` - - active_ids: ids of the documents to which the mail being composed is - related, in case ``mail.compose.message.mode == 'mass_mail'``. + The behavior of the wizard can be modified through the context key + mail.compose.message.mode: + - 'reply': reply to a previous message. The wizard is pre-populated + via ``get_message_data``. + - 'comment': new post on a record. The wizard is pre-populated via + ``get_record_data`` + - 'mass_mail': wizard in mass mailing mode where the mail details can + contain template placeholders that will be merged with actual data + before being sent to each recipient. """ _name = 'mail.compose.message' _inherit = 'mail.message' _description = 'Email composition wizard' def default_get(self, cr, uid, fields, context=None): - """ Overridden to provide specific defaults depending on the context - parameters. - - Composition mode - - comment: default mode; active_model, active_id = model and ID of a - document we are commenting, - - mass_mailing mode: active_model, active_id = model and ID of a - document we are commenting, - - reply: active_id = ID of a mail.message to which we are replying. - From this message we can find the related model and res_id, - - :param dict context: several context values will modify the behavior - of the wizard, cfr. the class description. + """ Handle composition mode. Some details about context keys: + - comment: default mode, model and ID of a record the user comments + - default_model or active_model + - default_res_id or active_id + - reply: active_id of a message the user replies to + - active_id: ID of a mail.message to which we are replying + - message.res_model or default_model + - message.res_id or default_res_id + - mass_mailing mode: model and IDs of records the user mass-mails + - active_ids: record IDs + - default_model or active_model """ + # get some important values from context if context is None: context = {} - compose_mode = context.get('mail.compose.message.mode', 'comment') - active_model = context.get('active_model') - active_id = context.get('active_id') result = super(mail_compose_message, self).default_get(cr, uid, fields, context=context) + # get some important values from context + composition_mode = context.get('mail.compose.message.mode') + model = context.get('default_model', context.get('active_model')) + res_id = context.get('default_res_id', context.get('active_id')) + active_id = context.get('active_id') + active_ids = context.get('active_ids') + # get default values according to the composition mode - vals = {} - if compose_mode in ['reply']: - vals = self.get_message_data(cr, uid, int(context['active_id']), context=context) - elif compose_mode in ['comment', 'mass_mail'] and active_model and active_id: - vals = self.get_value(cr, uid, active_model, active_id, context) + if composition_mode in ['reply']: + vals = self.get_message_data(cr, uid, active_id, context=context) + elif composition_mode in ['comment', 'mass_mail'] and model and res_id: + vals = self.get_record_data(cr, uid, model, res_id, context=context) + else: + vals = {'model': model, 'res_id': res_id} + if composition_mode: + vals['composition_mode'] = composition_mode + for field in vals: if field in fields: result[field] = vals[field] - - # link to model and record if not done yet - if not result.get('model') and active_model: - result['model'] = active_model - if not result.get('res_id') and active_id: - result['res_id'] = active_id return result _columns = { - 'dest_partner_ids': fields.many2many('res.partner', + 'composition_mode': fields.selection([ + ('comment', 'Comment a document'), + ('reply', 'Reply to a message'), + ('mass_mail', 'Mass mailing') + ], string='Composition mode'), + 'partner_ids': fields.many2many('res.partner', 'mail_compose_message_res_partner_rel', - 'wizard_id', 'partner_id', 'Destination partners', - help="When sending emails through the social network composition wizard"\ - "you may choose to send a copy of the mail to partners."), - 'attachment_ids': fields.many2many('ir.attachment','mail_compose_message_ir_attachments_rel', + 'wizard_id', 'partner_id', 'Additional contacts'), + 'attachment_ids': fields.many2many('ir.attachment', + 'mail_compose_message_ir_attachments_rel', 'wizard_id', 'attachment_id', 'Attachments'), - 'auto_delete': fields.boolean('Auto Delete', help="Permanently delete emails after sending"), 'filter_id': fields.many2one('ir.filters', 'Filters'), 'body_text': fields.text('Plain-text editor body'), 'content_subtype': fields.char('Message content subtype', size=32, readonly=1, @@ -115,46 +109,75 @@ class mail_compose_message(osv.TransientModel): } _defaults = { + 'composition_mode': 'comment', 'content_subtype': lambda self,cr, uid, context={}: 'plain', - 'body_text': lambda self,cr, uid, context={}: '', + 'body_text': lambda self,cr, uid, context={}: False, 'body': lambda self,cr, uid, context={}: '', + 'subject': lambda self,cr, uid, context={}: False, + 'partner_ids': [], } - def get_value(self, cr, uid, model, res_id, context=None): + def get_record_data(self, cr, uid, model, res_id, context=None): """ Returns a defaults-like dict with initial values for the composition wizard when sending an email related to the document record identified by ``model`` and ``res_id``. - The default implementation returns an empty dictionary, and is meant - to be overridden by subclasses. - :param str model: model name of the document record this mail is related to. - :param int res_id: id of the document record this mail is related to. - :param dict context: several context values will modify the behavior - of the wizard, cfr. the class description. + :param int res_id: id of the document record this mail is related to """ + return {'model': model, 'res_id': res_id} + + def get_message_data(self, cr, uid, message_id, context=None): + """ Returns a defaults-like dict with initial values for the composition + wizard when replying to the given message (e.g. including the quote + of the initial message, and the correct recipients). + + :param int message_id: id of the mail.message to which the user + is replying. + """ + if context is None: + context = {} result = {} - user = self.pool.get('res.users').browse(cr, uid, uid, context=context) + if not message_id: + return result + + current_user = self.pool.get('res.users').browse(cr, uid, uid, context=context) + message_data = self.pool.get('mail.message').browse(cr, uid, message_id, context=context) + + # create subject + re_prefix = _("Re:") + reply_subject = tools.ustr(message_data.subject or '') + if not (reply_subject.startswith('Re:') or reply_subject.startswith(re_prefix)): + reply_subject = "%s %s" % (re_prefix, reply_subject) + # create the reply in the body + reply_header = _('On %(date)s, %(sender_name)s wrote:') % { + 'date': message_data.date if message_data.date else '', + 'sender_name': message_data.author_id.name } + reply_body = '
      %s
      %s
      %s' % (reply_header, message_data.body, current_user.signature) + # get partner_ids from original message + partner_ids = [partner.id for partner in message_data.partner_ids] if message_data.partner_ids else [] + + # update the result result.update({ - 'model': model, - 'res_id': res_id, - 'email_from': user.email or tools.config.get('email_from', False), - 'body': False, - 'subject': False, - 'dest_partner_ids': [], + 'model': message_data.model, + 'res_id': message_data.res_id, + 'parent_id': message_data.id, + 'body': reply_body, + 'subject': reply_subject, + 'partner_ids': partner_ids, + 'content_subtype': 'html', }) return result - def toggle_formatting(self, cr, uid, ids, context=None): + def toggle_content_subtype(self, cr, uid, ids, context=None): """ hit toggle formatting mode button: calls onchange_formatting to emulate an on_change, then writes the value to update the form. """ for record in self.browse(cr, uid, ids, context=context): content_st_new_value = 'plain' if record.content_subtype == 'html' else 'html' onchange_res = self.onchange_content_subtype(cr, uid, ids, content_st_new_value, record.model, record.res_id, context=context) self.write(cr, uid, [record.id], onchange_res['value'], context=context) - return False - + return True def onchange_content_subtype(self, cr, uid, ids, value, model, res_id, context=None): """ onchange_content_subtype (values: 'plain' or 'html'). This onchange @@ -164,12 +187,10 @@ class mail_compose_message(osv.TransientModel): This method can be overridden for models that want to have their specific behavior. """ - if value == 'plain': - return {'value': {'subject': False, 'content_subtype': value}} return {'value': {'content_subtype': value}} - def onchange_dest_partner_ids(self, cr, uid, ids, value, context=None): - """ onchange_dest_partner_ids (value format: [[6, False, [3, 4]]]). The + def onchange_partner_ids(self, cr, uid, ids, value, context=None): + """ onchange_partner_ids (value format: [[6, False, [3, 4]]]). The basic purpose of this method is to check that destination partners effectively have email addresses. Otherwise a warning is thrown. """ @@ -189,94 +210,40 @@ class mail_compose_message(osv.TransientModel): } return {'warning': warning, 'value': {}} - def get_message_data(self, cr, uid, message_id, context=None): - """ Returns a defaults-like dict with initial values for the composition - wizard when replying to the given message (e.g. including the quote - of the initial message, and the correct recipient). It should not be - called unless ``context['mail.compose.message.mode'] == 'reply'``. - - :param int message_id: id of the mail.message to which the user - is replying. - :param dict context: several context values will modify the behavior - of the wizard, cfr. the class description. - """ - if context is None: - context = {} - result = {} - if not message_id: - return result - - current_user = self.pool.get('res.users').browse(cr, uid, uid, context) - message_data = self.pool.get('mail.message').browse(cr, uid, message_id, context) - # Form the subject - re_prefix = _("Re:") - reply_subject = tools.ustr(message_data.subject or '') - if not (reply_subject.startswith('Re:') or reply_subject.startswith(re_prefix)): - reply_subject = "%s %s" % (re_prefix, reply_subject) - - # Form the bodies (text and html). We use the plain text version of the - # original mail, by default, as it is easier to quote than the HTML - # version. TODO: make it possible to switch to HTML on the fly - - sent_date = _('On %(date)s, ') % {'date': message_data.date} if message_data.date else '' - sender = _('%(sender_name)s wrote:') % {'sender_name': tools.ustr(message_data.email_from or _('You'))} - - body = message_data.body or '' - quoted_body = '
      %s
      ' % (tools.ustr(body)), - reply_body = '

      %s%s
      %s
      %s' % (sent_date, sender, quoted_body, current_user.signature) - - # form dest_partner_ids - dest_partner_ids = [partner.id for partner in message_data.partner_ids] - - # update the result - result.update({ - 'body': reply_body, - 'subject': reply_subject, - 'dest_partner_ids': dest_partner_ids, - 'model': message_data.model or False, - 'res_id': message_data.res_id or False, - }) - return result - def send_mail(self, cr, uid, ids, context=None): - '''Process the wizard contents and proceed with sending the corresponding - email(s), rendering any template patterns on the fly if needed. - If the wizard is in mass-mail mode (context['mail.compose.message.mode'] is - set to ``'mass_mail'``), the resulting email(s) are scheduled for being - sent the next time the mail.message scheduler runs, or the next time - ``mail.message.process_email_queue`` is called. - Otherwise the new message is sent immediately. - - :param dict context: several context values will modify the behavior - of the wizard, cfr. the class description. - ''' + """ Process the wizard content and proceed with sending the related + email(s), rendering any template patterns on the fly if needed. """ if context is None: context = {} - formatting = context.get('formatting') - # FIXME TODO: mass_mail_mode unused? - mass_mail_mode = context.get('mail.compose.message.mode') == 'mass_mail' + for wizard in self.browse(cr, uid, ids, context=context): + mass_mail_mode = wizard.composition_mode == 'mass_mail' - for mail_wiz in self.browse(cr, uid, ids, context=context): attachment = {} - for attach in mail_wiz.attachment_ids: + for attach in wizard.attachment_ids: attachment[attach.datas_fname] = attach.datas and attach.datas or False # default values, according to the wizard options - subject = mail_wiz.subject if formatting else False - # FIXME TODO: partner_ids not used?? - partner_ids = [partner.id for partner in mail_wiz.dest_partner_ids] - body = mail_wiz.body_html if mail_wiz.content_subtype == 'html' else mail_wiz.body + subject = wizard.subject if wizard.content_subtype == 'html' else False + partner_ids = [partner.id for partner in wizard.partner_ids] + body = wizard.body if wizard.content_subtype == 'html' else wizard.body_text - active_model_pool = self.pool.get('mail.thread') - active_id = context.get('default_res_id', False) + active_model_pool = self.pool.get(wizard.model if wizard.model else 'mail.thread') + + #TODO: TDE: WIP: have to check for mass mail and templates - no time anymore today if context.get('mail.compose.message.mode') == 'mass_mail' and context.get('default_model', False) and context.get('default_res_id', False): active_model = context.get('default_model', False) active_model_pool = self.pool.get(active_model) subject = self.render_template(cr, uid, subject, active_model, active_id) - body = self.render_template(cr, uid, mail_wiz.body_html, active_model, active_id) - active_model_pool.message_post(cr, uid, [active_id], body=body, subject=subject, msg_type='comment', - attachments=attachment, context=context) + body = self.render_template(cr, uid, wizard.body_html, active_model, active_id) + + # determine the ids we are commenting + if mass_mail_mode: + res_ids = context.get('active_ids', []) + else: + res_ids = [wizard.res_id] + active_model_pool.message_post(cr, uid, res_ids, body=body, subject=subject, msg_type='comment', + attachments=attachment, context=context, partner_ids=partner_ids) return {'type': 'ir.actions.act_window_close'} @@ -310,7 +277,7 @@ class mail_compose_message(osv.TransientModel): return template and EXPRESSION_PATTERN.sub(merge, template) def dummy(self, cr, uid, ids, context=None): - return False - - -#FIXME: check for models defining '_mail_compose_message' + """ TDE: defined to have buttons that do basically nothing. It is + currently impossible to have buttons that do nothing special + in views (if type not specified, considered as 'object'). """ + return True diff --git a/addons/mail/wizard/mail_compose_message_view.xml b/addons/mail/wizard/mail_compose_message_view.xml index a10dc6ccc86..8bf8df24f59 100644 --- a/addons/mail/wizard/mail_compose_message_view.xml +++ b/addons/mail/wizard/mail_compose_message_view.xml @@ -38,8 +38,13 @@
      + + + + + @@ -49,9 +54,9 @@ - From 51df98c72ad95edb466cc3a773c59dfc9155071b Mon Sep 17 00:00:00 2001 From: Jigar Amin - OpenERP Date: Thu, 23 Aug 2012 17:15:57 +0530 Subject: [PATCH 174/436] [FIX] the link 'Add a row' should be hidden when create='false' in the corresponding tree view bzr revid: jam@tinyerp.com-20120823114557-ibid3ob03k864o06 --- addons/web/static/src/js/view_form.js | 60 ++++++++++++++------------- 1 file changed, 31 insertions(+), 29 deletions(-) diff --git a/addons/web/static/src/js/view_form.js b/addons/web/static/src/js/view_form.js index 069c1840611..97e4b976c04 100644 --- a/addons/web/static/src/js/view_form.js +++ b/addons/web/static/src/js/view_form.js @@ -3578,36 +3578,38 @@ instance.web.form.One2ManyList = instance.web.ListView.List.extend({ }).length; if (this.options.selectable) { columns++; } if (this.options.deletable) { columns++; } - var $cell = $('', { - colspan: columns, - 'class': 'oe_form_field_one2many_list_row_add' - }).append( - $('', {href: '#'}).text(_t("Add a row")) - .mousedown(function () { - // FIXME: needs to be an official API somehow - if (self.view.editor.is_editing()) { - self.view.__ignore_blur = true; - } - }) - .click(function (e) { - e.preventDefault(); - e.stopPropagation(); - // FIXME: there should also be an API for that one - if (self.view.editor.form.__blur_timeout) { - clearTimeout(self.view.editor.form.__blur_timeout); - self.view.editor.form.__blur_timeout = false; - } - self.view.ensure_saved().then(function () { - self.view.do_add_record(); - }); - })); + if (this.view._is_action_enabled('create')){ + var $cell = $('', { + colspan: columns, + 'class': 'oe_form_field_one2many_list_row_add' + }).append( + $('', {href: '#'}).text(_t("Add a row")) + .mousedown(function () { + // FIXME: needs to be an official API somehow + if (self.view.editor.is_editing()) { + self.view.__ignore_blur = true; + } + }) + .click(function (e) { + e.preventDefault(); + e.stopPropagation(); + // FIXME: there should also be an API for that one + if (self.view.editor.form.__blur_timeout) { + clearTimeout(self.view.editor.form.__blur_timeout); + self.view.editor.form.__blur_timeout = false; + } + self.view.ensure_saved().then(function () { + self.view.do_add_record(); + }); + })); - var $padding = this.$current.find('tr:not([data-id]):first'); - var $newrow = $('').append($cell); - if ($padding.length) { - $padding.before($newrow); - } else { - this.$current.append($newrow) + var $padding = this.$current.find('tr:not([data-id]):first'); + var $newrow = $('').append($cell); + if ($padding.length) { + $padding.before($newrow); + } else { + this.$current.append($newrow) + } } } }); From 61a45e9268d149120e7d1e46a75fecafc8953005 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thibault=20Delavall=C3=A9e?= Date: Thu, 23 Aug 2012 14:24:26 +0200 Subject: [PATCH 175/436] [IMP] mail_thread: message_follower_ids: simplified message_subscribe code, now that we use the many2many modified field. Subscrubing is now a simple write on the field. bzr revid: tde@openerp.com-20120823122426-o08nh8gxx93kizjl --- addons/mail/mail_thread.py | 28 ++++------------------------ 1 file changed, 4 insertions(+), 24 deletions(-) diff --git a/addons/mail/mail_thread.py b/addons/mail/mail_thread.py index 9f43087cd6a..d21aae67c87 100644 --- a/addons/mail/mail_thread.py +++ b/addons/mail/mail_thread.py @@ -637,35 +637,15 @@ class mail_thread(osv.Model): return self.message_subscribe(cr, uid, ids, partner_ids, context=context) def message_subscribe(self, cr, uid, ids, partner_ids, context=None): - """ Add partners to the records followers. This implementation cannot - directly use [(4, partner_id)] because of the res_model column of - mail.followers. We therefore check access rights and access rules - before directly creating a follower record. This way we simulate - a write in message_follower_ids, enabling a correct check of access - rights. + """ Add partners to the records followers. :param partner_ids: a list of partner_ids to subscribe :param return: new value of followers, for Chatter """ - self.check_access_rights(cr, uid, 'write', raise_exception=True) - self.check_access_rule(cr, uid, ids, 'write', context=context) - - fol_obj = self.pool.get('mail.followers') - fol_ids = fol_obj.search(cr, uid, [ - ('res_id', 'in', ids), ('res_model', '=', self._name), ('partner_id', 'in', partner_ids) - ], context=context) - followers = {} - for fol in fol_obj.browse(cr, uid, fol_ids, context=context): - followers.setdefault(fol.partner_id.id, []).append(fol.res_id) - - for res_id in ids: - for partner_id in partner_ids: - if not res_id in followers.get(partner_id, []): - fol_obj.create(cr, uid, { - 'res_id': res_id, 'partner_id': partner_id, 'res_model': self._name - }, context=context) + self.write(cr, uid, ids, {'message_follower_ids': [(4, pid) for pid in partner_ids]}, context=context) # TDE: temp, must check followers widget return [] + # return [follower.id for thread in self.browse(cr, uid, ids, context=context) for follower in thread.message_follower_ids] def message_unsubscribe_users(self, cr, uid, ids, user_ids=None, context=None): """ Wrapper on message_subscribe, using users. If user_ids is not @@ -682,9 +662,9 @@ class mail_thread(osv.Model): :param return: new value of followers, for Chatter """ self.write(cr, uid, ids, {'message_follower_ids': [(3, pid) for pid in partner_ids]}, context=context) - # return [follower.id for thread in self.browse(cr, uid, ids, context=context) for follower in thread.message_follower_ids] # TDE: temp, must check followers widget return [] + # return [follower.id for thread in self.browse(cr, uid, ids, context=context) for follower in thread.message_follower_ids] #------------------------------------------------------ # Notification API From 690bc2ece98516c1ef756b2e3e9f5435db422c07 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thibault=20Delavall=C3=A9e?= Date: Thu, 23 Aug 2012 14:25:37 +0200 Subject: [PATCH 176/436] [TEST] mail: added tests for the many2many modified field used for follower_ids. Added tests for the subscriber API. bzr revid: tde@openerp.com-20120823122537-g0m5jtkq67jhuone --- addons/mail/tests/test_mail.py | 122 ++++++++++++++++++++++++++++++++- 1 file changed, 119 insertions(+), 3 deletions(-) diff --git a/addons/mail/tests/test_mail.py b/addons/mail/tests/test_mail.py index a9da19e90fa..a1249994ebd 100644 --- a/addons/mail/tests/test_mail.py +++ b/addons/mail/tests/test_mail.py @@ -71,17 +71,20 @@ class test_mail(common.TransactionCase): self.mail_alias = self.registry('mail.alias') self.mail_thread = self.registry('mail.thread') self.mail_group = self.registry('mail.group') + self.mail_notification = self.registry('mail.notification') + self.mail_followers = self.registry('mail.followers') self.res_users = self.registry('res.users') + self.res_partner = self.registry('res.partner') # groups@.. will cause the creation of new mail groups self.mail_group_model_id = self.ir_model.search(self.cr, self.uid, [('model','=', 'mail.group')])[0] self.mail_alias.create(self.cr, self.uid, {'alias_name': 'groups', 'alias_model_id': self.mail_group_model_id}) - # tech@... will append new messages to the 'tech' group - self.group_tech_id = self.mail_group.create(self.cr, self.uid, {'name': 'tech'}) + # create a 'pigs' group that will be used through the various tests + self.group_pigs_id = self.mail_group.create(self.cr, self.uid, {'name': 'pigs'}) - def test_message_process(self): + def test_00_message_process(self): cr, uid = self.cr, self.uid # Incoming mail creates a new mail_group "frogs" self.assertEqual(self.mail_group.search(cr, uid, [('name','=','frogs')]), []) @@ -111,3 +114,116 @@ class test_mail(common.TransactionCase): self.assertRaises(Exception, self.mail_thread.message_process, cr, uid, None, mail_spam) + + def test_01_many2many_reference_field(self): + """ Tests designed for the many2many_reference field (follower_ids). + We will test to perform write using the many2many commands 0, 3, 4, + 5 and 6. """ + cr, uid = self.cr, self.uid + user_admin = self.res_users.browse(cr, uid, uid) + group_pigs = self.mail_group.browse(cr, uid, self.group_pigs_id) + + # Create partner Bert Poilu partner + partner_bert_id = self.res_partner.create(cr, uid, {'name': 'Bert Poilu'}) + + # Create 'disturbing' values in mail.followers: same res_id, other res_model; same res_model, other res_id + group_dummy_id = self.mail_group.create(cr, uid, + {'name': 'Dummy group'}) + self.mail_followers.create(cr, uid, + {'res_model': 'mail.thread', 'res_id': self.group_pigs_id, 'partner_id': partner_bert_id}) + self.mail_followers.create(cr, uid, + {'res_model': 'mail.group', 'res_id': group_dummy_id, 'partner_id': partner_bert_id}) + + # Pigs just created: should be only Admin as follower + follower_ids = [follower.id for follower in group_pigs.message_follower_ids] + self.assertTrue(len(follower_ids) == 1, + 'Newly created group should have only 1 follower') + self.assertTrue(user_admin.partner_id.id in follower_ids, + 'Admin should be the only Pigs group follower') + + # Subscribe Bert through a '4' command + group_pigs.write({'message_follower_ids': [(4, partner_bert_id)]}) + group_pigs.refresh() + follower_ids = [follower.id for follower in group_pigs.message_follower_ids] + self.assertTrue(len(follower_ids) == 2, + 'Pigs group should have 2 followers after linking Bert') + self.assertTrue(all(id in follower_ids for id in [partner_bert_id, user_admin.partner_id.id]), + 'Bert and Admin should be the 2 Pigs group followers') + + # Unsubscribe Bert through a '3' command + group_pigs.write({'message_follower_ids': [(3, partner_bert_id)]}) + group_pigs.refresh() + follower_ids = [follower.id for follower in group_pigs.message_follower_ids] + self.assertTrue(len(follower_ids) == 1, + 'Pigs group should have 1 follower after unlinking Bert') + self.assertTrue(all(id in follower_ids for id in [user_admin.partner_id.id]), + 'Admin should be the only Pigs group follower') + + # Set followers through a '6' command + group_pigs.write({'message_follower_ids': [(6, 0, [partner_bert_id])]}) + group_pigs.refresh() + follower_ids = [follower.id for follower in group_pigs.message_follower_ids] + self.assertTrue(len(follower_ids) == 1, + 'Pigs group should have 1 follower after replacing followers') + self.assertTrue(follower_ids == [partner_bert_id], + 'Bert should be the only Pigs group follower') + + # Add a follower created on the fly through a '0' command + group_pigs.write({'message_follower_ids': [(0, 0, {'name': 'Patrick Fiori'})]}) + partner_patrick_id = self.res_partner.search(cr, uid, [('name', '=', 'Patrick Fiori')])[0] + group_pigs.refresh() + follower_ids = [follower.id for follower in group_pigs.message_follower_ids] + self.assertTrue(len(follower_ids) == 2, + 'Pigs group should have 2 followers after linking to new partner') + self.assertTrue(all(id in follower_ids for id in [partner_bert_id, partner_patrick_id]), + 'Bert and Patrick should be Pigs group followers') + + # Finally, unlink through a '5' command + group_pigs.write({'message_follower_ids': [(5, 0)]}) + group_pigs.refresh() + follower_ids = [follower.id for follower in group_pigs.message_follower_ids] + self.assertTrue(len(follower_ids) == 0, + 'Pigs group should not have followers anymore') + + # Test dummy data has not been altered + fol_obj_ids = self.mail_followers.search(cr, uid, [('res_model', '=', 'mail.thread'), ('res_id', '=', self.group_pigs_id)]) + follower_ids = [follower.partner_id.id for follower in self.mail_followers.browse(cr, uid, fol_obj_ids)] + self.assertTrue(len(follower_ids) == 1, + 'mail.thread dummy data should have 1 follower') + self.assertTrue(follower_ids[0] == partner_bert_id, + 'Bert should be the follower of dummy mail.thread data') + fol_obj_ids = self.mail_followers.search(cr, uid, [('res_model', '=', 'mail.group'), ('res_id', '=', group_dummy_id)]) + follower_ids = [follower.partner_id.id for follower in self.mail_followers.browse(cr, uid, fol_obj_ids)] + self.assertTrue(len(follower_ids) == 2, + 'mail.group dummy data should have 2 followers') + self.assertTrue(all(id in follower_ids for id in [partner_bert_id, user_admin.partner_id.id]), + 'Bert and Admin should be the followers of dummy mail.group data') + + def test_02_message_followers(self): + """ Tests designed for the subscriber API. """ + cr, uid = self.cr, self.uid + user_admin = self.res_users.browse(cr, uid, uid) + group_pigs = self.mail_group.browse(cr, uid, self.group_pigs_id) + + # Create user Raoul + user_raoul_id = self.res_users.create(cr, uid, {'name': 'Raoul Grosbedon', 'login': 'raoul'}) + user_raoul = self.res_users.browse(cr, uid, user_raoul_id) + + # Subscribe Raoul twice (niak niak) through message_subscribe_users + group_pigs.message_subscribe_users([user_raoul_id, user_raoul_id]) + group_pigs.refresh() + follower_ids = [follower.id for follower in group_pigs.message_follower_ids] + self.assertTrue(len(follower_ids) == 2, + 'Pigs group should have 2 followers after having subscribed Raoul. Subscribing twice '\ + 'the same user should create only one follower.') + self.assertTrue(all(id in follower_ids for id in [user_raoul.partner_id.id, user_admin.partner_id.id]), + 'Admin and Raoul should be the 2 Pigs group followers') + + # Unsubscribe Raoul twice through message_unsubscribe_users + group_pigs.message_unsubscribe_users([user_raoul_id, user_raoul_id]) + group_pigs.refresh() + follower_ids = [follower.id for follower in group_pigs.message_follower_ids] + self.assertTrue(len(follower_ids) == 1, + 'Pigs group should have 1 follower after unsubscribing Raoul') + self.assertTrue(all(id in follower_ids for id in [user_admin.partner_id.id]), + 'Admin the only Pigs group followers') From f3b43da73bebb93c04fb00d6800c6ef376a05a22 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thibault=20Delavall=C3=A9e?= Date: Thu, 23 Aug 2012 15:11:44 +0200 Subject: [PATCH 177/436] [CLEAN] mail: res_partner: removed dead code. Also fixe da typo in mail tests. bzr revid: tde@openerp.com-20120823131144-xbryqdesfw5ix8y9 --- addons/mail/res_partner.py | 14 +++----------- addons/mail/tests/test_mail.py | 2 +- 2 files changed, 4 insertions(+), 12 deletions(-) diff --git a/addons/mail/res_partner.py b/addons/mail/res_partner.py index 6f1808c1612..c8b1e9c9240 100644 --- a/addons/mail/res_partner.py +++ b/addons/mail/res_partner.py @@ -22,20 +22,10 @@ from osv import osv, fields class res_partner_mail(osv.Model): - """ Inherits partner and adds CRM information in the partner form """ + """ Update partner to add a field about notification preferences """ _name = "res.partner" _inherit = ['res.partner', 'mail.thread'] - def message_search_get_domain(self, cr, uid, ids, context=None): - """ Override of message_search_get_domain for partner discussion page. - The purpose is to add messages directly sent to the partner. It also - adds messages pushed to the related user, if any, using @login. - """ - if self._name != 'res.partner': - return super(res_partner_mail, self).message_search_get_domain(cr, uid, ids, context=context) - # add message linked to the partner - return [('partner_ids', 'in', ids)] - _columns = { 'notification_email_pref': fields.selection([ ('all', 'All feeds'), @@ -45,8 +35,10 @@ class res_partner_mail(osv.Model): help="Choose in which case you want to receive an email when you "\ "receive new feeds."), } + _defaults = { 'notification_email_pref': lambda *args: 'comment' } + # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/addons/mail/tests/test_mail.py b/addons/mail/tests/test_mail.py index a1249994ebd..a424a408067 100644 --- a/addons/mail/tests/test_mail.py +++ b/addons/mail/tests/test_mail.py @@ -117,7 +117,7 @@ class test_mail(common.TransactionCase): def test_01_many2many_reference_field(self): """ Tests designed for the many2many_reference field (follower_ids). - We will test to perform write using the many2many commands 0, 3, 4, + We will test to perform writes using the many2many commands 0, 3, 4, 5 and 6. """ cr, uid = self.cr, self.uid user_admin = self.res_users.browse(cr, uid, uid) From c7c93bf42f7a6fea1195d4120be69cab98a820d5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thibault=20Delavall=C3=A9e?= Date: Thu, 23 Aug 2012 15:45:08 +0200 Subject: [PATCH 178/436] [CLEAN] mail_group: small cleaning in comments; write before subscribing. bzr revid: tde@openerp.com-20120823134508-3d1yawov0lv87jx5 --- addons/mail/res_users.py | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/addons/mail/res_users.py b/addons/mail/res_users.py index 86fd048479c..babf2213bd5 100644 --- a/addons/mail/res_users.py +++ b/addons/mail/res_users.py @@ -129,10 +129,9 @@ class res_users(osv.Model): return res class res_users_mail_group(osv.Model): - """ Update of res.groups class - - if adding/removing users from a group, check mail.groups linked to - this user group, and subscribe / unsubscribe them from the discussion - group. This is done by overriding the write method. + """ Update of res.users class + - if adding groups to an user, check mail.groups linked to this user + group, and the user. This is done by overriding the write method. """ _name = 'res.users' _inherit = ['res.users'] @@ -151,15 +150,15 @@ class res_users_mail_group(osv.Model): class res_groups_mail_group(osv.Model): """ Update of res.groups class - - if adding/removing users from a group, check mail.groups linked to - this user group, and subscribe / unsubscribe them from the discussion - group. This is done by overriding the write method. + - if adding users from a group, check mail.groups linked to this user + group and subscribe them. This is done by overriding the write method. """ _name = 'res.groups' _inherit = 'res.groups' # FP Note: to improve, post processeing, after the super may be better def write(self, cr, uid, ids, vals, context=None): + write_res = super(res_groups_mail_group, self).write(cr, uid, ids, vals, context=context) if vals.get('users'): # form: {'group_ids': [(3, 10), (3, 3), (4, 10), (4, 3)]} or {'group_ids': [(6, 0, [ids]} user_ids = [command[1] for command in vals['users'] if command[0] == 4] @@ -167,5 +166,4 @@ class res_groups_mail_group(osv.Model): mail_group_obj = self.pool.get('mail.group') mail_group_ids = mail_group_obj.search(cr, uid, [('group_ids', 'in', ids)], context=context) mail_group_obj.message_subscribe_users(cr, uid, mail_group_ids, user_ids, context=context) - return super(res_groups_mail_group, self).write(cr, uid, ids, vals, context=context) - + return write_res From 08f22c7411f3b9efa9fd29c1d0ecf9e0e72728c9 Mon Sep 17 00:00:00 2001 From: Fabien Pinckaers Date: Thu, 23 Aug 2012 16:26:22 +0200 Subject: [PATCH 179/436] fix bzr revid: fp@tinyerp.com-20120823142622-8dsevh9eqarj1nht --- addons/mail/mail_group_view.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/addons/mail/mail_group_view.xml b/addons/mail/mail_group_view.xml index c303b1fd40b..8d7e229eccf 100644 --- a/addons/mail/mail_group_view.xml +++ b/addons/mail/mail_group_view.xml @@ -5,6 +5,7 @@ Discussion Group mail.wall + mail.message From 84625969c0a724e0374f21f527ce9a04339ebbee Mon Sep 17 00:00:00 2001 From: Fabien Pinckaers Date: Thu, 23 Aug 2012 16:35:25 +0200 Subject: [PATCH 180/436] [FIX] needaction on mail.groups bzr revid: fp@tinyerp.com-20120823143525-28mnbgd015jd8yyd --- openerp/addons/base/ir/ir_actions.py | 3 +++ openerp/addons/base/ir/ir_ui_menu.py | 7 ++++++- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/openerp/addons/base/ir/ir_actions.py b/openerp/addons/base/ir/ir_actions.py index cf67e6fcf88..6f906f5c416 100644 --- a/openerp/addons/base/ir/ir_actions.py +++ b/openerp/addons/base/ir/ir_actions.py @@ -879,6 +879,8 @@ class act_client(osv.osv): "is no central tag repository across clients."), 'res_model': fields.char('Destination Model', size=64, help="Optional model, mostly used for needactions."), + 'context': fields.char('Context Value', size=250, required=True, + help="Context dictionary as Python expression, empty by default (Default: {})"), 'params': fields.function(_get_params, fnct_inv=_set_params, type='binary', string="Supplementary arguments", @@ -888,6 +890,7 @@ class act_client(osv.osv): } _defaults = { 'type': 'ir.actions.client', + 'context': '{}', } act_client() diff --git a/openerp/addons/base/ir/ir_ui_menu.py b/openerp/addons/base/ir/ir_ui_menu.py index eefa8ea4a50..05523faa550 100644 --- a/openerp/addons/base/ir/ir_ui_menu.py +++ b/openerp/addons/base/ir/ir_ui_menu.py @@ -269,8 +269,13 @@ class ir_ui_menu(osv.osv): if menu.action and menu.action.type in ('ir.actions.act_window','ir.actions.client') and menu.action.res_model: obj = self.pool.get(menu.action.res_model) if obj._needaction: + dom = [] + if menu.action.type=='ir.actions.act_window': + dom = menu.action.domain and eval(menu.action.domain, {'uid': uid}) or [] + else: + dom = eval(menu.action.params_store or '{}', {'uid': uid}).get('domain') res[menu.id]['needaction_enabled'] = obj._needaction - res[menu.id]['needaction_counter'] = obj._needaction_count(cr, uid, menu.action.domain and eval(menu.action.domain), context=context) + res[menu.id]['needaction_counter'] = obj._needaction_count(cr, uid, dom, context=context) return res _columns = { From d0d14766fc5bda5eede3a3402f52b4e6ae11e01b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thibault=20Delavall=C3=A9e?= Date: Thu, 23 Aug 2012 17:02:29 +0200 Subject: [PATCH 181/436] [FIX] mail.js: fixed do_action of Wall. bzr revid: tde@openerp.com-20120823150229-7vp9hbfb80bo29xf --- addons/mail/static/src/js/mail.js | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/addons/mail/static/src/js/mail.js b/addons/mail/static/src/js/mail.js index 14183f13b1c..a3e53a9d948 100644 --- a/addons/mail/static/src/js/mail.js +++ b/addons/mail/static/src/js/mail.js @@ -251,6 +251,7 @@ openerp.mail = function(session) { * @param {Number} [options.records=null] records to show instead of fetching messages */ init: function(parent, options) { + console.log(parent); this._super(parent); // options this.options = options || {}; @@ -374,7 +375,7 @@ openerp.mail = function(session) { * Normally it should be called only when clicking on 'Post/Send' * in the composition form. */ do_action: function(action, on_close) { - console.log('thread do_action'); + console.log('thread do_action', action, on_close, this); this.message_clean(); this.message_fetch(); if (this.compose_message_widget) { @@ -600,6 +601,7 @@ openerp.mail = function(session) { }, set_value: function() { + var self = this; this._super.apply(this, arguments); if (! this.view.datarecord.id || session.web.BufferedDataSet.virtual_id_regex.test(this.view.datarecord.id)) { this.$element.find('oe_mail_thread').hide(); @@ -611,7 +613,8 @@ openerp.mail = function(session) { // create and render Thread widget this.$element.find('div.oe_mail_recthread_main').empty(); for (var i in this.thread_list) { this.thread_list[i].destroy(); } - var thread = new mail.Thread(this, { + console.log(this); + var thread = new mail.Thread(self, { 'context': this.options.context, 'uid': this.session.uid, 'thread_level': this.options.thread_level, 'show_header_compose': true, 'show_delete': true, 'composer': true }); @@ -678,7 +681,7 @@ openerp.mail = function(session) { if (this.compose_message_widget) { this.compose_message_widget.refresh(); } this.message_clean(); - this.message_fetch(); + this.display_thread(); return this._super(action, on_close); }, @@ -729,7 +732,7 @@ openerp.mail = function(session) { display_thread: function () { var render_res = session.web.qweb.render('mail.wall_thread_container', {}); $('
    • ').html(render_res).appendTo(this.$element.find('ul.oe_mail_wall_threads')); - var thread = new mail.Thread(self, { + var thread = new mail.Thread(this, { 'domain': this.options.domain, 'context': this.options.context, 'uid': this.session.uid, 'thread_level': this.options.thread_level, 'composer': true, // display options From 462fc03987b9678d4bd1adb16a63ecb4cae9a3ed Mon Sep 17 00:00:00 2001 From: Fabien Pinckaers Date: Thu, 23 Aug 2012 17:57:47 +0200 Subject: [PATCH 182/436] fix bzr revid: fp@tinyerp.com-20120823155747-0ysezcil7hcy1sv0 --- addons/mail/mail_followers.py | 2 ++ addons/mail/mail_message.py | 3 +-- addons/mail/mail_thread.py | 6 +++++- 3 files changed, 8 insertions(+), 3 deletions(-) diff --git a/addons/mail/mail_followers.py b/addons/mail/mail_followers.py index 7734f8a0ec8..e6ce4af6592 100644 --- a/addons/mail/mail_followers.py +++ b/addons/mail/mail_followers.py @@ -83,6 +83,8 @@ class mail_notification(osv.Model): 'subject': msg.subject } for partner in partner_obj.browse(cr, uid, partner_ids, context=context): + if partner.user_id.id == uid: + continue notification_obj.create(cr, uid, { 'partner_id': partner.id, 'message_id': msg_id diff --git a/addons/mail/mail_message.py b/addons/mail/mail_message.py index d8dbdcb5f49..79ecfdc9383 100644 --- a/addons/mail/mail_message.py +++ b/addons/mail/mail_message.py @@ -264,8 +264,7 @@ class mail_message(osv.Model): modobj = self.pool.get(values.get('model')) follower_notify = [] for follower in modobj.browse(cr, uid, values.get('res_id'), context=context).message_follower_ids: - if follower.id <> uid: - follower_notify.append(follower.id) + follower_notify.append(follower.id) self.pool.get('mail.notification').notify(cr, uid, follower_notify, newid, context=context) return newid diff --git a/addons/mail/mail_thread.py b/addons/mail/mail_thread.py index d21aae67c87..6a8f225234a 100644 --- a/addons/mail/mail_thread.py +++ b/addons/mail/mail_thread.py @@ -143,7 +143,7 @@ class mail_thread(osv.Model): res[notif.message_id.res_id]['message_unread'] = True for thread in self.browse(cr, uid, ids, context=context): - res[thread.id]['message_summary'] = "9 %d + %d" % (len(thread.message_ids), len(thread.message_follower_ids)) + res[thread.id]['message_summary'] = "9 %d + %d" % (len(thread.message_comment_ids), len(thread.message_follower_ids)) res[thread.id]['message_is_follower'] = user.partner_id.id in [follower.id for follower in thread.message_follower_ids] return res @@ -166,6 +166,10 @@ class mail_thread(osv.Model): 'message_follower_ids': many2many_reference('res.partner', 'mail_followers', 'res_id', 'partner_id', ref_col_name='res_model', string='Followers'), + 'message_comment_ids': fields.one2many('mail.message', 'res_id', + domain=lambda self: [('model','=',self._name),('type','in',('comment','email'))], + string='Related Messages', + help="All messages related to the current document."), 'message_ids': fields.one2many('mail.message', 'res_id', domain=lambda self: [('model','=',self._name)], string='Related Messages', From 2c29bd58f0b57587f5e67cf674f12a14e43587e3 Mon Sep 17 00:00:00 2001 From: Fabien Pinckaers Date: Thu, 23 Aug 2012 18:04:16 +0200 Subject: [PATCH 183/436] [IMP] blue color on unread messages bzr revid: fp@tinyerp.com-20120823160416-23a11k1ivu2md34v --- addons/crm/crm_lead_view.xml | 1 - addons/mail/mail_thread.py | 3 ++- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/addons/crm/crm_lead_view.xml b/addons/crm/crm_lead_view.xml index 77aaf95deb8..63de20b42cd 100644 --- a/addons/crm/crm_lead_view.xml +++ b/addons/crm/crm_lead_view.xml @@ -341,7 +341,6 @@ diff --git a/addons/mail/mail_thread.py b/addons/mail/mail_thread.py index 6a8f225234a..4493b226b18 100644 --- a/addons/mail/mail_thread.py +++ b/addons/mail/mail_thread.py @@ -143,7 +143,8 @@ class mail_thread(osv.Model): res[notif.message_id.res_id]['message_unread'] = True for thread in self.browse(cr, uid, ids, context=context): - res[thread.id]['message_summary'] = "9 %d + %d" % (len(thread.message_comment_ids), len(thread.message_follower_ids)) + cls = res[thread.id]['message_unread'] and ' class="oe_kanban_mail_new"' or '' + res[thread.id]['message_summary'] = "9 %d + %d" % (cls, len(thread.message_comment_ids), len(thread.message_follower_ids)) res[thread.id]['message_is_follower'] = user.partner_id.id in [follower.id for follower in thread.message_follower_ids] return res From d808f69390c8dff7ade2dacaeb1325144003c532 Mon Sep 17 00:00:00 2001 From: Fabien Pinckaers Date: Thu, 23 Aug 2012 18:16:55 +0200 Subject: [PATCH 184/436] imp_demo bzr revid: fp@tinyerp.com-20120823161655-z823jx02ymg4kmky --- addons/mail/data/mail_demo.xml | 1 - 1 file changed, 1 deletion(-) diff --git a/addons/mail/data/mail_demo.xml b/addons/mail/data/mail_demo.xml index f69fd1b5820..ed30d38b8d3 100644 --- a/addons/mail/data/mail_demo.xml +++ b/addons/mail/data/mail_demo.xml @@ -3,7 +3,6 @@ - Internal company announce mail.group Date: Thu, 23 Aug 2012 20:06:48 +0200 Subject: [PATCH 185/436] [REF] [TEST] mail: cleaned the way followers are added to a message partner_ids (done in notify in mail.message) and the way emails are send (done in notify in mail.followers). Composition wizard override mail_message.notify to avoid having partners and sending emails. Also added tests that helped finding some bugs, that will be cleaned tomorrow. bzr revid: tde@openerp.com-20120823180648-8xpu8qyf7qr6azc2 --- addons/mail/mail_followers.py | 18 ++++---- addons/mail/mail_message.py | 29 +++++++++---- addons/mail/tests/test_mail.py | 48 +++++++++++++++++++++- addons/mail/wizard/mail_compose_message.py | 7 +++- 4 files changed, 81 insertions(+), 21 deletions(-) diff --git a/addons/mail/mail_followers.py b/addons/mail/mail_followers.py index e6ce4af6592..671e2405b49 100644 --- a/addons/mail/mail_followers.py +++ b/addons/mail/mail_followers.py @@ -68,30 +68,27 @@ class mail_notification(osv.Model): # a message we can not read. # def create(self, ...) - - # Create notification in the wall of each user - # Send by email the notification depending on the user preferences def notify(self, cr, uid, partner_ids, msg_id, context=None): + """ Send by email the notification depending on the user preferences """ context = context or {} partner_obj = self.pool.get('res.partner') - msg_obj = self.pool.get('mail.message') notification_obj = self.pool.get('mail.notification') + msg_obj = self.pool.get('mail.message') msg = msg_obj.browse(cr, uid, msg_id, context=context) towrite = { 'email_to': [], 'subject': msg.subject } + for partner in partner_obj.browse(cr, uid, partner_ids, context=context): + # Do not send an email to the writer if partner.user_id.id == uid: continue - notification_obj.create(cr, uid, { - 'partner_id': partner.id, - 'message_id': msg_id - }, context=context) + # Partner does not want to receive any emails if partner.notification_email_pref=='none' or not partner.email: continue - + # Partners want to receive only emails and comments if partner.notification_email_pref=='comment' and msg.type in ('email','comment'): continue @@ -101,10 +98,11 @@ class mail_notification(osv.Model): if towrite.get('state', False) and not context.get('noemail', False): if towrite.get('subject', False): - towrite['subject'] = msg.name_get(cr, uid, [msg.id], context=context)[0][1] + towrite['subject'] = msg.name_get()[0][1] towrite['message_id'] = msg.id towrite['email_to'] = ', '.join(towrite['email_to']) mail_message_obj = self.pool.get('mail.mail') newid = mail_message_obj.create(cr, uid, towrite, context=context) mail_message_obj.send(cr, uid, [newid], context=context) + return True diff --git a/addons/mail/mail_message.py b/addons/mail/mail_message.py index 79ecfdc9383..46bfd25e1be 100644 --- a/addons/mail/mail_message.py +++ b/addons/mail/mail_message.py @@ -257,17 +257,28 @@ class mail_message(osv.Model): def create(self, cr, uid, values, context=None): newid = super(mail_message, self).create(cr, uid, values, context) self.check(cr, uid, [newid], mode='create', context=context) - - # notify all followers - if values.get('model') and values.get('res_id'): - notification_obj = self.pool.get('mail.notification') - modobj = self.pool.get(values.get('model')) - follower_notify = [] - for follower in modobj.browse(cr, uid, values.get('res_id'), context=context).message_follower_ids: - follower_notify.append(follower.id) - self.pool.get('mail.notification').notify(cr, uid, follower_notify, newid, context=context) + self.notify(cr, uid, newid, context=context) return newid + def notify(self, cr, uid, newid, context=None): + """ Add the related record followers to the destination partner_ids. + Call mail_notification.notify to manage the email sending + """ + message = self.browse(cr, uid, newid, context=context) + partners_to_notify = [] + # add all partner_ids of the message + if message.partner_ids: + for partner in message.partner_ids: + if partner.id not in partners_to_notify: + partners_to_notify.append(partner.id) + # add all followers and set them as partner_ids + if message.model and message.res_id: + modobj = self.pool.get(message.model) + for follower in modobj.browse(cr, uid, message.res_id, context=context).message_follower_ids: + partners_to_notify.append(follower.id) + self.write(cr, uid, [newid], {'partner_ids': [(4, follower.id)]}, context=context) + self.pool.get('mail.notification').notify(cr, uid, partners_to_notify, newid, context=context) + def read(self, cr, uid, ids, fields_to_read=None, context=None, load='_classic_read'): self.check(cr, uid, ids, 'read', context=context) return super(mail_message, self).read(cr, uid, ids, fields_to_read, context, load) diff --git a/addons/mail/tests/test_mail.py b/addons/mail/tests/test_mail.py index a424a408067..83c9f363c7f 100644 --- a/addons/mail/tests/test_mail.py +++ b/addons/mail/tests/test_mail.py @@ -71,6 +71,8 @@ class test_mail(common.TransactionCase): self.mail_alias = self.registry('mail.alias') self.mail_thread = self.registry('mail.thread') self.mail_group = self.registry('mail.group') + self.mail_mail = self.registry('mail.mail') + self.mail_message = self.registry('mail.message') self.mail_notification = self.registry('mail.notification') self.mail_followers = self.registry('mail.followers') self.res_users = self.registry('res.users') @@ -123,7 +125,7 @@ class test_mail(common.TransactionCase): user_admin = self.res_users.browse(cr, uid, uid) group_pigs = self.mail_group.browse(cr, uid, self.group_pigs_id) - # Create partner Bert Poilu partner + # Create partner Bert Poilu partner_bert_id = self.res_partner.create(cr, uid, {'name': 'Bert Poilu'}) # Create 'disturbing' values in mail.followers: same res_id, other res_model; same res_model, other res_id @@ -227,3 +229,47 @@ class test_mail(common.TransactionCase): 'Pigs group should have 1 follower after unsubscribing Raoul') self.assertTrue(all(id in follower_ids for id in [user_admin.partner_id.id]), 'Admin the only Pigs group followers') + + def test_10_mail_composer(self): + """ Tests designed for the mail.compose.message wizard. """ + cr, uid = self.cr, self.uid + mail_compose = self.registry('mail.compose.message') + user_admin = self.res_users.browse(cr, uid, uid) + group_pigs = self.mail_group.browse(cr, uid, self.group_pigs_id) + # print 'pigs message_ids: %s' % group_pigs.message_ids + # print 'pigs follower_ids: %s' % group_pigs.message_follower_ids + + # Create partner Bert Poilu + partner_bert_id = self.res_partner.create(cr, uid, {'name': 'Bert Poilu', 'email': 'bert@poil.poil'}) + partner_raoul_id = self.res_partner.create(cr, uid, {'name': 'Raoul Grosbedon'}) + + # Create a new comment on group_pigs + compose_id = mail_compose.create(cr, uid, + {'subject': 'Pigs', 'body_text': 'Pigs rules', 'partner_ids': [(4, partner_bert_id), (4, partner_raoul_id)]}, + {'mail.compose.message': 'comment', 'default_model': 'mail.group', 'default_res_id': self.group_pigs_id}) + # print 'wizard id: %s' % compose_id + compose = mail_compose.browse(cr, uid, compose_id) + # print 'wizard partner_ids: %s' % compose.partner_ids + self.assertTrue(compose.model == 'mail.group' and compose.res_id == self.group_pigs_id) + + # Send the comment + mail_compose.send_mail(cr, uid, [compose_id]) + + group_pigs.refresh() + # print 'pigs message_ids: %s' % group_pigs.message_ids[0] + new_message = group_pigs.message_ids[0] + new_partner_ids = [partner.id for partner in new_message.partner_ids] + + # print 'pigs new_message subject: %s' % new_message.subject + # print 'pigs new_message body: %s' % new_message.body + # print 'pigs new_message partner_ids: %s' % new_message.partner_ids + + notif_ids = self.mail_notification.search(cr, uid, [('message_id', '=', new_message.id)]) + # print notif_ids + + # Message partners = notified people = writer + partner_ids + self.assertTrue(len(new_partner_ids) == 3, 'There should be 3 partners linked to the newly posted comment.') + self.assertTrue(len(notif_ids) == 3, 'There should be only 3 entries in mail_notification') + self.assertTrue(all(id in [user_admin.partner_id.id, partner_bert_id, partner_raoul_id] for id in new_partner_ids), + 'Admin, Bert and Roaul should be the 3 partners of the newly created message') + \ No newline at end of file diff --git a/addons/mail/wizard/mail_compose_message.py b/addons/mail/wizard/mail_compose_message.py index 140773981e5..46ef8a4786f 100644 --- a/addons/mail/wizard/mail_compose_message.py +++ b/addons/mail/wizard/mail_compose_message.py @@ -117,6 +117,11 @@ class mail_compose_message(osv.TransientModel): 'partner_ids': [], } + def notify(self, cr, uid, newid, context=None): + """ Override specific notify method of mail.message, because we do + not want that feature in the wizard. """ + return + def get_record_data(self, cr, uid, model, res_id, context=None): """ Returns a defaults-like dict with initial values for the composition wizard when sending an email related to the document record @@ -225,7 +230,7 @@ class mail_compose_message(osv.TransientModel): # default values, according to the wizard options subject = wizard.subject if wizard.content_subtype == 'html' else False - partner_ids = [partner.id for partner in wizard.partner_ids] + partner_ids = [(4, partner.id) for partner in wizard.partner_ids] body = wizard.body if wizard.content_subtype == 'html' else wizard.body_text active_model_pool = self.pool.get(wizard.model if wizard.model else 'mail.thread') From 2c4ed841b915cef42fe1f65d27427b7d86d659cd Mon Sep 17 00:00:00 2001 From: Olivier Dony Date: Thu, 23 Aug 2012 20:54:43 +0200 Subject: [PATCH 186/436] [FIX] mail: make message_process/parse work, restore attachment_ids m2m on mail.message, various lint cleanup bzr revid: odo@openerp.com-20120823185443-zsnm6g14r6e42y3i --- addons/hr_recruitment/hr_recruitment.py | 2 - addons/mail/mail_message.py | 33 +--- addons/mail/mail_thread.py | 196 ++++++++++++------------ 3 files changed, 103 insertions(+), 128 deletions(-) diff --git a/addons/hr_recruitment/hr_recruitment.py b/addons/hr_recruitment/hr_recruitment.py index db5fd57b0a9..321aff950ad 100644 --- a/addons/hr_recruitment/hr_recruitment.py +++ b/addons/hr_recruitment/hr_recruitment.py @@ -165,13 +165,11 @@ class hr_applicant(base_stage, osv.Model): date_create = datetime.strptime(issue.create_date, "%Y-%m-%d %H:%M:%S") date_open = datetime.strptime(issue.date_open, "%Y-%m-%d %H:%M:%S") ans = date_open - date_create - date_until = issue.date_open elif field in ['day_close']: if issue.date_closed: date_create = datetime.strptime(issue.create_date, "%Y-%m-%d %H:%M:%S") date_close = datetime.strptime(issue.date_closed, "%Y-%m-%d %H:%M:%S") - date_until = issue.date_closed ans = date_close - date_create if ans: duration = float(ans.days) diff --git a/addons/mail/mail_message.py b/addons/mail/mail_message.py index 46bfd25e1be..79e7eb81456 100644 --- a/addons/mail/mail_message.py +++ b/addons/mail/mail_message.py @@ -19,16 +19,10 @@ # ############################################################################## -import dateutil.parser -import email import logging -import re -import time from email.header import decode_header -from email.message import Message -from osv import osv -from osv import fields +from osv import osv, fields import tools _logger = logging.getLogger(__name__) @@ -84,31 +78,22 @@ class mail_message(osv.Model): ('notification', 'System notification'), ], 'Type', help="Message type: email for email message, notification for system "\ - "message, comment for other messages such as user replies"), - + "message, comment for other messages such as user replies"), 'author_id': fields.many2one('res.partner', 'Author', required=True), - 'partner_ids': fields.many2many('res.partner', - 'mail_notification', 'message_id', 'partner_id', - 'Recipients'), - - 'attachment_ids': fields.one2many('ir.attachment', 'res_id', - 'Attachments', domain=[('res_model','=','mail.message')]), - - 'parent_id': fields.many2one('mail.message', 'Parent Message', - select=True, ondelete='set null', - help="Initial thread message."), + 'partner_ids': fields.many2many('res.partner', 'mail_notification', 'message_id', 'partner_id', 'Recipients'), + 'attachment_ids': fields.many2many('ir.attachment', 'message_attachment_rel', + 'message_id', 'attachment_id', 'Attachments'), + 'parent_id': fields.many2one('mail.message', 'Parent Message', select=True, ondelete='set null', help="Initial thread message."), 'child_ids': fields.one2many('mail.message', 'parent_id', 'Child Messages'), - 'model': fields.char('Related Document Model', size=128, select=1), 'res_id': fields.integer('Related Document ID', select=1), 'record_name': fields.function(get_record_name, type='string', string='Message Record Name', help="Name get of the related document."), - 'notification_ids': fields.one2many('mail.notification', 'message_id', 'Notifications'), - 'subject': fields.char('Subject', size=128), + 'subject': fields.char('Subject'), 'date': fields.datetime('Date'), - 'message_id': fields.char('Message-Id', size=256, help='Message unique identifier', select=1, readonly=1), + 'message_id': fields.char('Message-Id', help='Message unique identifier', select=1, readonly=1), 'body': fields.html('Content'), } @@ -118,7 +103,6 @@ class mail_message(osv.Model): return [('notification_ids.partner_id','=',partner_id),('notification_ids.read','=',False)] return [] - def _get_default_author(self, cr, uid, context={}): return self.pool.get('res.users').browse(cr, uid, uid, context=context).partner_id.id @@ -233,7 +217,6 @@ class mail_message(osv.Model): ('partner_id', '=', partner_id), ('message_id', 'in', ids), ], context=context) - notifications = {} for notification in not_obj.browse(cr, uid, not_ids, context=context): if notification.message_id.id in ids: pass diff --git a/addons/mail/mail_thread.py b/addons/mail/mail_thread.py index 4493b226b18..12dab7dfe92 100644 --- a/addons/mail/mail_thread.py +++ b/addons/mail/mail_thread.py @@ -19,19 +19,23 @@ # ############################################################################## +import base64 import dateutil import email import logging +import pytz +import re +import time +import xmlrpclib from email.utils import parsedate from email.message import Message -from osv import osv, fields -from mail_message import decode -import time + import tools +from mail_message import decode +from osv import osv, fields from tools.translate import _ from tools.safe_eval import safe_eval as eval -import pytz -import xmlrpclib + _logger = logging.getLogger(__name__) @@ -372,9 +376,8 @@ class mail_thread(osv.Model): routes = self.message_route(cr, uid, msg_txt, model, thread_id, custom_values, context=context) - msg = self.parse_message(cr, uid, msg_txt, save_original=save_original, context=context) - if strip_attachments and 'attachments' in msg: - del msg['attachments'] + msg = self.message_parse(cr, uid, msg_txt, save_original=save_original, context=context) + if strip_attachments: msg.pop('attachments', None) for model, thread_id, custom_values, user_id in routes: if self._name != model: context.update({'thread_model': model}) @@ -445,7 +448,50 @@ class mail_thread(osv.Model): self.write(cr, uid, ids, update_vals, context=context) return True - def parse_message(self, cr, uid, message, save_original=False, context=None): + + def _message_extract_payload(self, message, save_original=False): + """Extract body as HTML and attachments from the mail message""" + attachments = [] + body = u'' + if save_original: + attachments.append(('original_email.eml', message.as_string())) + if not message.is_multipart() or 'text/' in message.get('content-type', ''): + encoding = message.get_content_charset() + body = message.get_payload(decode=True) + body = tools.ustr(body, encoding, errors='replace') + else: + alternative = (message.get_content_type() == 'multipart/alternative') + for part in message.walk(): + if part.get_content_maintype() == 'multipart': + continue # skip container + filename = part.get_filename() # None if normal part + encoding = part.get_content_charset() # None if attachment + # 1) Explicit Attachments -> attachments + if filename or part.get('content-disposition','').strip().startswith('attachment'): + attachments.append((filename or 'attachment', part.get_payload(decode=True))) + continue + # 2) text/plain ->
      +                if part.get_content_type() == 'text/plain' and (not alternative or not body):
      +                    insertion_point = body.find('')
      +                    # plain text is wrapped in 
       to preserve formatting
      +                    text = u'
      %s
      ' % tools.ustr(part.get_payload(decode=True), + encoding, errors='replace') + body = body[:insertion_point] + text + body[:insertion_point] + # 3) text/html -> raw + elif part.get_content_type() == 'text/html': + html = tools.ustr(part.get_payload(decode=True), encoding, errors='replace') + if alternative: + body = html + else: + html = re.sub('(?i)', '', html) + insertion_point = body.find('') + body = body[:insertion_point] + text + body[:insertion_point] + # 4) Anything else -> attachment + else: + attachments.append((filename or 'attachment', part.get_payload(decode=True))) + return body, attachments + + def message_parse(self, cr, uid, message, save_original=False, context=None): """Parses a string or email.message.Message representing an RFC-2822 email, and returns a generic dict holding the message details. @@ -453,8 +499,8 @@ class mail_thread(osv.Model): :param message: the message to parse :type message: email.message.Message | string | unicode :param bool save_original: whether the returned dict - should include an ``original`` entry with the base64 - encoded source of the message. + should include an ``original`` attachment containing + the source of the message :rtype: dict :return: A dict with the following structure, where each field may not be present if missing in original @@ -462,113 +508,58 @@ class mail_thread(osv.Model): { 'message-id': msg_id, 'subject': subject, - 'from': from, --> author_id - 'to': to, --> partner_ids - 'cc': cc, --> partner_ids - 'headers' : { 'X-Mailer': mailer, --> to remove - #.. all X- headers... - }, - 'content_subtype': msg_mime_subtype, --> to remove - 'body': plaintext_body --> keep body + 'from': from, + 'to': to, + 'cc': cc, + 'body': unified_body, 'body_html': html_body, --> to remove 'attachments': [('file1', 'bytes'), - ('file2', 'bytes') } - # ... - 'original': source_of_email, --> attachment document + ('file2', 'bytes')} } """ - msg_txt = message - attachments = [] - if isinstance(message, str): - msg_txt = email.message_from_string(message) - - # Warning: message_from_string doesn't always work correctly on unicode, - # we must use utf-8 strings here :-( - if isinstance(message, unicode): - message = message.encode('utf-8') - msg_txt = email.message_from_string(message) - - message_id = msg_txt.get('message-id', False) - msg = {} - - if save_original: - msg_original = message.as_string() if isinstance(message, Message) \ - else message - attachments.append(('original_email.eml', msg_original)) + msg_dict = {} + if not isinstance(message, Message): + if isinstance(message, unicode): + # Warning: message_from_string doesn't always work correctly on unicode, + # we must use utf-8 strings here :-( + message = message.encode('utf-8') + message = email.message_from_string(message) + message_id = message['message-id'] if not message_id: # Very unusual situation, be we should be fault-tolerant here - message_id = time.time() - msg_txt['message-id'] = message_id - _logger.info('Parsing Message without message-id, generating a random one: %s', message_id) + message_id = "<%s@localhost>" % time.time() + _logger.debug('Parsing Message without message-id, generating a random one: %s', message_id) + msg_dict['message_id'] = message_id - msg_fields = msg_txt.keys() + if 'Subject' in message: + msg_dict['subject'] = decode(message.get('Subject')) - msg['message_id'] = message_id + # Envelope fields not stored in mail.message but made available for message_new() + msg_dict['from'] = decode(message.get('from')) + msg_dict['to'] = decode(message.get('to')) + msg_dict['cc'] = decode(message.get('cc')) - if 'Subject' in msg_fields: - msg['subject'] = decode(msg_txt.get('Subject')) - - #if 'Content-Type' in msg_fields: - # msg['content-type'] = msg_txt.get('Content-Type') - - # find author_id - - if 'From' in msg_fields: - author_ids = self._message_find_partners(cr, uid, msg_txt, ['From'], context=context) - #decode(msg_txt.get('From') or msg_txt.get_unixfrom()) ) + if 'From' in message: + author_ids = self._message_find_partners(cr, uid, message, ['From'], context=context) if author_ids: - msg['author_id'] = author_ids[0] + msg_dict['author_id'] = author_ids[0] + partner_ids = self._message_find_partners(cr, uid, message, ['From','To','Cc'], context=context) + msg_dict['partner_ids'] = partner_ids - partner_ids = self._message_find_partners(cr, uid, msg_txt, ['From','To','Delivered-To','CC','Cc'], context=context) - msg['partner_ids'] = partner_ids - - if 'Date' in msg_fields: - date_hdr = decode(msg_txt.get('Date')) + if 'Date' in message: + date_hdr = decode(message.get('Date')) # convert from email timezone to server timezone date_server_datetime = dateutil.parser.parse(date_hdr).astimezone(pytz.timezone(tools.get_server_timezone())) date_server_datetime_str = date_server_datetime.strftime(tools.DEFAULT_SERVER_DATETIME_FORMAT) - msg['date'] = date_server_datetime_str - - #if 'Content-Transfer-Encoding' in msg_fields: - # msg['encoding'] = msg_txt.get('Content-Transfer-Encoding') - - #if 'References' in msg_fields: - # msg['references'] = msg_txt.get('References') + msg_dict['date'] = date_server_datetime_str # FP Note: todo - find parent_id - if 'In-Reply-To' in msg_fields: + if 'In-Reply-To' in message: pass - - if not msg_txt.is_multipart() or 'text/plain' in msg.get('content-type', ''): - encoding = msg_txt.get_content_charset() - body = msg_txt.get_payload(decode=True) - if 'text/html' in msg.get('content-type', ''): - msg['body'] = body - - if msg_txt.is_multipart() or 'multipart/alternative' in msg.get('content-type', ''): - body = "" - for part in msg_txt.walk(): - if part.get_content_maintype() == 'multipart': - continue - - encoding = part.get_content_charset() - filename = part.get_filename() - if part.get_content_maintype()=='text': - content = part.get_payload(decode=True) - if filename: - attachments.append(( filename, msg_original)) - content = tools.ustr(content, encoding) - msg['body'] = content - elif part.get_content_maintype() in ('application', 'image'): - if filename: - attachments.append(( filename, part.get_payload(decode=True))) - else: - res = part.get_payload(decode=True) - msg['body'] += tools.ustr(res, encoding) - - msg['attachments'] = attachments - return msg + + msg_dict['body'], msg_dict['attachments'] = self._message_extract_payload(message) + return msg_dict #------------------------------------------------------ # Note specific @@ -610,9 +601,11 @@ class mail_thread(osv.Model): content = content.encode('utf-8') data_attach = { 'name': name, - 'datas': content, + 'datas': base64.b64encode(str(content)), 'datas_fname': name, 'description': name, + 'res_model': context.get('thread_model') or self._name, + 'res_id': thread_id, } to_attach.append((0,0, data_attach)) @@ -626,6 +619,7 @@ class mail_thread(osv.Model): 'parent_id': parent_id, 'attachment_ids': to_attach }) + for x in ('from','to','cc'): values.pop(x, None) # Avoid warnings return self.pool.get('mail.message').create(cr, uid, values, context=context) From a7193cb193e166c5a04a68c7a842ae926f7c07b2 Mon Sep 17 00:00:00 2001 From: Olivier Dony Date: Thu, 23 Aug 2012 21:31:30 +0200 Subject: [PATCH 187/436] [FIX] crm: fix some missing bits after the big refactoring bzr revid: odo@openerp.com-20120823193130-4u42mhyih43k32nb --- addons/crm/crm_lead.py | 2 +- addons/crm/wizard/crm_lead_to_partner.py | 19 +++++++++++-------- .../report/crm_helpdesk_report.py | 2 +- 3 files changed, 13 insertions(+), 10 deletions(-) diff --git a/addons/crm/crm_lead.py b/addons/crm/crm_lead.py index 83ffb379950..b9741d17c3a 100644 --- a/addons/crm/crm_lead.py +++ b/addons/crm/crm_lead.py @@ -635,7 +635,7 @@ class crm_lead(base_stage, osv.osv): partner_id = False if lead.partner_name and lead.contact_name: partner_id = self._lead_create_contact(cr, uid, lead, lead.partner_name, True, context=context) - self._lead_create_contact(cr, uid, lead, lead.contact_name, False, partner_id, context=context) + partner_id = self._lead_create_contact(cr, uid, lead, lead.contact_name, False, partner_id, context=context) elif lead.partner_name and not lead.contact_name: partner_id = self._lead_create_contact(cr, uid, lead, lead.partner_name, True, context=context) elif not lead.partner_name and lead.contact_name: diff --git a/addons/crm/wizard/crm_lead_to_partner.py b/addons/crm/wizard/crm_lead_to_partner.py index 430ec3c1543..60d358e034c 100644 --- a/addons/crm/wizard/crm_lead_to_partner.py +++ b/addons/crm/wizard/crm_lead_to_partner.py @@ -50,16 +50,19 @@ class crm_lead2partner(osv.osv_memory): def _select_partner(self, cr, uid, context=None): if context is None: context = {} - lead = self.pool.get('crm.lead') - partner = self.pool.get('res.partner') - lead_ids = list(context and context.get('active_ids', []) or []) - if not len(lead_ids): + if not context.get('active_model') == 'crm.lead' or not context.get('active_id'): return False - this = lead.browse(cr, uid, lead_ids[0], context=context) - if not partner_id and this.partner_name: + partner = self.pool.get('res.partner') + lead = self.pool.get('crm.lead') + this = lead.browse(cr, uid, context.get('active_id'), context=context) + if this.email_from: + partner_ids = partner.search(cr, uid, [('email', '=', this.email_from)], context=context) + if partner_ids: + partner_id = partner_ids[0] + if not this.partner_id and this.partner_name: partner_ids = partner.search(cr, uid, [('name', '=', this.partner_name)], context=context) - if partner_ids and len(partner_ids): - partner_id = partner_ids[0] + if partner_ids: + partner_id = partner_ids[0] return partner_id def default_get(self, cr, uid, fields, context=None): diff --git a/addons/crm_helpdesk/report/crm_helpdesk_report.py b/addons/crm_helpdesk/report/crm_helpdesk_report.py index 93a33d62819..6496935c2d2 100644 --- a/addons/crm_helpdesk/report/crm_helpdesk_report.py +++ b/addons/crm_helpdesk/report/crm_helpdesk_report.py @@ -97,7 +97,7 @@ class crm_helpdesk_report(osv.osv): c.planned_cost, count(*) as nbr, extract('epoch' from (c.date_closed-c.create_date))/(3600*24) as delay_close, - (SELECT count(id) FROM mail_message WHERE model='crm.helpdesk' AND res_id=c.id AND email_from IS NOT NULL) AS email, + (SELECT count(id) FROM mail_message WHERE model='crm.helpdesk' AND res_id=c.id AND type = 'email') AS email, abs(avg(extract('epoch' from (c.date_deadline - c.date_closed)))/(3600*24)) as delay_expected from crm_helpdesk c From cd67c50e295c65e9b7e4581163fa519f5813e94a Mon Sep 17 00:00:00 2001 From: Olivier Dony Date: Thu, 23 Aug 2012 22:06:29 +0200 Subject: [PATCH 188/436] [MERGE] crm: cherry-pick of mass conversion improvements from 6.1 This corresponds to rev.6762 from 6.1, which includes a lot of fixes related to mass conversions, and is completely related to the kind of fixes currently done for crm during the mail refactoring. This should not break crm more than it is already in this branch. bzr revid: odo@openerp.com-20120823200629-1m1eym62l7aabzgl --- addons/crm/crm_lead.py | 14 +++-- addons/crm/crm_phonecall.py | 6 +-- addons/crm/wizard/crm_lead_to_opportunity.py | 53 ++++++++++--------- .../wizard/crm_lead_to_opportunity_view.xml | 34 ++++++++---- addons/crm/wizard/crm_lead_to_partner.py | 10 ++-- addons/crm/wizard/crm_phonecall_to_partner.py | 4 +- 6 files changed, 71 insertions(+), 50 deletions(-) diff --git a/addons/crm/crm_lead.py b/addons/crm/crm_lead.py index b9741d17c3a..703a389ba93 100644 --- a/addons/crm/crm_lead.py +++ b/addons/crm/crm_lead.py @@ -531,7 +531,7 @@ class crm_lead(base_stage, osv.osv): oldest = self._merge_find_oldest(cr, uid, ids, context=context) if ctx_opportunities : first_opportunity = ctx_opportunities[0] - tail_opportunities = opportunities_list + tail_opportunities = opportunities_list + ctx_opportunities[1:] else: first_opportunity = opportunities_list[0] tail_opportunities = opportunities_list[1:] @@ -591,8 +591,6 @@ class crm_lead(base_stage, osv.osv): for lead in self.browse(cr, uid, ids, context=context): if lead.state in ('done', 'cancel'): continue - if user_ids or section_id: - self.allocate_salesman(cr, uid, [lead.id], user_ids, section_id, context=context) vals = self._convert_opportunity_data(cr, uid, lead, customer, section_id, context=context) self.write(cr, uid, [lead.id], vals, context=context) @@ -604,6 +602,10 @@ class crm_lead(base_stage, osv.osv): mail_message.write(cr, uid, msg_ids, { 'partner_id': lead.partner_id.id }, context=context) + + if user_ids or section_id: + self.allocate_salesman(cr, uid, ids, user_ids, section_id, context=context) + return True def _lead_create_contact(self, cr, uid, lead, name, is_company, parent_id=False, context=None): @@ -663,10 +665,12 @@ class crm_lead(base_stage, osv.osv): if context is None: context = {} partner_ids = {} + force_partner_id = partner_id for lead in self.browse(cr, uid, ids, context=context): if action == 'create': if not partner_id: partner_id = self._create_lead_partner(cr, uid, lead, context) + partner_id = force_partner_id or self._create_lead_partner(cr, uid, lead, context=context) self._lead_set_partner(cr, uid, lead, partner_id, context=context) partner_ids[lead.id] = partner_id return partner_ids @@ -825,7 +829,7 @@ class crm_lead(base_stage, osv.osv): if update_vals is None: update_vals = {} if msg.get('priority') in dict(crm.AVAILABLE_PRIORITIES): - vals['priority'] = msg.get('priority') + update_vals['priority'] = msg.get('priority') maps = { 'cost':'planned_cost', 'revenue': 'planned_revenue', @@ -836,7 +840,7 @@ class crm_lead(base_stage, osv.osv): res = tools.misc.command_re.match(line) if res and maps.get(res.group(1).lower()): key = maps.get(res.group(1).lower()) - vals[key] = res.group(2).lower() + update_vals[key] = res.group(2).lower() return super(crm_lead, self).message_update(cr, uid, ids, msg, update_vals=update_vals, context=context) diff --git a/addons/crm/crm_phonecall.py b/addons/crm/crm_phonecall.py index e613733d9ab..44894fc881d 100644 --- a/addons/crm/crm_phonecall.py +++ b/addons/crm/crm_phonecall.py @@ -177,11 +177,11 @@ class crm_phonecall(base_state, osv.osv): if context is None: context = {} partner_ids = {} + force_partner_id = partner_id for call in self.browse(cr, uid, ids, context=context): if action == 'create': - if not partner_id: - partner_id = self._call_create_partner(cr, uid, call, context=context) - self._call_create_partner_address(cr, uid, call, partner_id, context=context) + partner_id = force_partner_id or self._call_create_partner(cr, uid, call, context=context) + self._call_create_partner_address(cr, uid, call, partner_id, context=context) self._call_set_partner(cr, uid, [call.id], partner_id, context=context) partner_ids[call.id] = partner_id return partner_ids diff --git a/addons/crm/wizard/crm_lead_to_opportunity.py b/addons/crm/wizard/crm_lead_to_opportunity.py index 547d3ed299e..fc366768023 100644 --- a/addons/crm/wizard/crm_lead_to_opportunity.py +++ b/addons/crm/wizard/crm_lead_to_opportunity.py @@ -24,8 +24,6 @@ from tools.translate import _ import tools import re -import time - class crm_lead2opportunity_partner(osv.osv_memory): _name = 'crm.lead2opportunity.partner' _description = 'Lead To Opportunity Partner' @@ -35,8 +33,8 @@ class crm_lead2opportunity_partner(osv.osv_memory): 'action': fields.selection([('exist', 'Link to an existing partner'), \ ('create', 'Create a new partner'), \ ('nothing', 'Do not link to a partner')], \ - 'Action', required=True), - 'name': fields.selection([('convert', 'Convert to Opportunity'), ('merge', 'Merge with existing Opportunity')],'Select Action', required=True), + 'Related Partner', required=True), + 'name': fields.selection([('convert', 'Convert to Opportunities'), ('merge', 'Merge with existing Opportunities')], 'Conversion Action', required=True), 'opportunity_ids': fields.many2many('crm.lead', string='Opportunities', domain=[('type', '=', 'opportunity')]), } @@ -68,8 +66,6 @@ class crm_lead2opportunity_partner(osv.osv_memory): if ids: opportunities.append(ids[0]) if not partner_id: - label = False - opp_ids = [] if email: # Find email of existing opportunity matches the email_from of the lead cr.execute("""select id from crm_lead where type='opportunity' and @@ -106,23 +102,36 @@ class crm_lead2opportunity_partner(osv.osv_memory): if context is None: context = {} lead = self.pool.get('crm.lead') - partner_id = self._create_partner(cr, uid, ids, context=context) + res = False + partner_ids_map = self._create_partner(cr, uid, ids, context=context) lead_ids = vals.get('lead_ids', []) - user_ids = vals.get('user_ids', False) team_id = vals.get('section_id', False) - return lead.convert_opportunity(cr, uid, lead_ids, partner_id, user_ids, team_id, context=context) + for lead_id in lead_ids: + partner_id = partner_ids_map.get(lead_id, False) + # FIXME: cannot pass user_ids as the salesman allocation only works in batch + res = lead.convert_opportunity(cr, uid, [lead_id], partner_id, [], team_id, context=context) + # FIXME: must perform salesman allocation in batch separately here + user_ids = vals.get('user_ids', False) + if user_ids: + lead.allocate_salesman(cr, uid, lead_ids, user_ids, team_id=team_id, context=context) + return res def _merge_opportunity(self, cr, uid, ids, opportunity_ids, action='merge', context=None): - #TOFIX: is it usefully ? if context is None: context = {} - merge_opportunity = self.pool.get('crm.merge.opportunity') res = False - #If we convert in mass, don't merge if there is no other opportunity but no warning - if action == 'merge' and (len(opportunity_ids) > 1 or not context.get('mass_convert') ): - self.write(cr, uid, ids, {'opportunity_ids' : [(6,0, [opportunity_ids[0].id])]}, context=context) - context.update({'lead_ids' : record_id, "convert" : True}) - res = merge_opportunity.merge(cr, uid, data.opportunity_ids, context=context) + # Expected: all newly-converted leads (active_ids) will be merged with the opportunity(ies) + # that have been selected in the 'opportunity_ids' m2m, with all these records + # merged into the first opportunity (and the rest deleted) + opportunity_ids = [o.id for o in opportunity_ids] + lead_ids = context.get('active_ids', []) + if action == 'merge' and lead_ids and opportunity_ids: + # Add the leads in the to-merge list, next to other opps + # (the fact that they're passed in context['lead_ids'] means that + # they cannot be selected to contain the result of the merge. + opportunity_ids.extend(lead_ids) + context.update({'lead_ids': lead_ids, "convert" : True}) + res = self.pool.get('crm.lead').merge_opportunity(cr, uid, opportunity_ids, context=context) return res def action_apply(self, cr, uid, ids, context=None): @@ -131,15 +140,13 @@ class crm_lead2opportunity_partner(osv.osv_memory): """ if not context: context = {} - lead = self.pool.get('crm.lead') lead_ids = context.get('active_ids', []) data = self.browse(cr, uid, ids, context=context)[0] self._convert_opportunity(cr, uid, ids, {'lead_ids': lead_ids}, context=context) - self._merge_opportunity(cr, uid, ids, data.opportunity_ids, data.action, context=context) + self._merge_opportunity(cr, uid, ids, data.opportunity_ids, data.name, context=context) return lead.redirect_opportunity_view(cr, uid, lead_ids[0], context=context) -crm_lead2opportunity_partner() class crm_lead2opportunity_mass_convert(osv.osv_memory): _name = 'crm.lead2opportunity.partner.mass' @@ -148,9 +155,8 @@ class crm_lead2opportunity_mass_convert(osv.osv_memory): _columns = { - 'user_ids': fields.many2many('res.users', string='Salesmans'), + 'user_ids': fields.many2many('res.users', string='Salesmen'), 'section_id': fields.many2one('crm.case.section', 'Sales Team'), - } def _convert_opportunity(self, cr, uid, ids, vals, context=None): data = self.browse(cr, uid, ids, context=context)[0] @@ -162,9 +168,6 @@ class crm_lead2opportunity_mass_convert(osv.osv_memory): return super(crm_lead2opportunity_mass_convert, self)._convert_opportunity(cr, uid, ids, vals, context=context) def mass_convert(self, cr, uid, ids, context=None): - value = self.default_get(cr, uid, ['partner_id', 'opportunity_ids'], context=context) - value['opportunity_ids'] = [(6, 0, value['opportunity_ids'])] - self.write(cr, uid, ids, value, context=context) return self.action_apply(cr, uid, ids, context=context) -crm_lead2opportunity_mass_convert() + # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/addons/crm/wizard/crm_lead_to_opportunity_view.xml b/addons/crm/wizard/crm_lead_to_opportunity_view.xml index 8829749efa3..6a826f7bc0e 100644 --- a/addons/crm/wizard/crm_lead_to_opportunity_view.xml +++ b/addons/crm/wizard/crm_lead_to_opportunity_view.xml @@ -37,24 +37,38 @@ crm.lead2opportunity.partner.mass - - - - + + + + - - + + - + + + + + + + + + + + + + + +
      -
      + -
      +
      diff --git a/addons/crm/wizard/crm_lead_to_partner.py b/addons/crm/wizard/crm_lead_to_partner.py index 60d358e034c..9b239bab7b9 100644 --- a/addons/crm/wizard/crm_lead_to_partner.py +++ b/addons/crm/wizard/crm_lead_to_partner.py @@ -55,6 +55,7 @@ class crm_lead2partner(osv.osv_memory): partner = self.pool.get('res.partner') lead = self.pool.get('crm.lead') this = lead.browse(cr, uid, context.get('active_id'), context=context) + partner_id = False if this.email_from: partner_ids = partner.search(cr, uid, [('email', '=', this.email_from)], context=context) if partner_ids: @@ -106,15 +107,16 @@ class crm_lead2partner(osv.osv_memory): lead_ids = context and context.get('active_ids') or [] data = self.browse(cr, uid, ids, context=context)[0] partner_id = data.partner_id and data.partner_id.id or False - partner_ids = lead.convert_partner(cr, uid, lead_ids, data.action, partner_id, context=context) - return partner_ids[lead_ids[0]] + return lead.convert_partner(cr, uid, lead_ids, data.action, partner_id, context=context) def make_partner(self, cr, uid, ids, context=None): """ This function Makes partner based on action. """ - partner_id = self._create_partner(cr, uid, ids, context=context) - return self.pool.get('res.partner').redirect_partner_form(cr, uid, partner_id, context=context) + # Only called from Form view, so only meant to convert one Lead. + lead_id = context and context.get('active_id') or False + partner_ids_map = self._create_partner(cr, uid, ids, context=context) + return self.pool.get('res.partner').redirect_partner_form(cr, uid, partner_ids_map.get(lead_id, False), context=context) crm_lead2partner() diff --git a/addons/crm/wizard/crm_phonecall_to_partner.py b/addons/crm/wizard/crm_phonecall_to_partner.py index 422af02f99d..715b7396c66 100644 --- a/addons/crm/wizard/crm_phonecall_to_partner.py +++ b/addons/crm/wizard/crm_phonecall_to_partner.py @@ -57,12 +57,10 @@ class crm_phonecall2partner(osv.osv_memory): if context is None: context = {} phonecall = self.pool.get('crm.phonecall') - data = self.browse(cr, uid, ids, context=context)[0] call_ids = context and context.get('active_ids') or [] partner_id = data.partner_id and data.partner_id.id or False - partner_ids = phonecall.convert_partner(cr, uid, call_ids, data.action, partner_id, context=context) - return partner_ids[call_ids[0]] + return phonecall.convert_partner(cr, uid, call_ids, data.action, partner_id, context=context) crm_phonecall2partner() From 12f01f09a12548e7f984449d46206864eebed409 Mon Sep 17 00:00:00 2001 From: Jigar Amin - OpenERP Date: Fri, 24 Aug 2012 11:04:59 +0530 Subject: [PATCH 189/436] [IMP] 1] if create='false' no extra row in a editable embeded list. 2] If edit='false' o2mfrom pop should be on readony mode bzr revid: jam@tinyerp.com-20120824053459-91ejrex8ox4hfptz --- addons/web/static/src/js/view_form.js | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/addons/web/static/src/js/view_form.js b/addons/web/static/src/js/view_form.js index 97e4b976c04..e91d343a618 100644 --- a/addons/web/static/src/js/view_form.js +++ b/addons/web/static/src/js/view_form.js @@ -3461,7 +3461,8 @@ instance.web.form.One2ManyListView = instance.web.ListView.extend({ } }, do_activate_record: function(index, id) { - var self = this; + var self = this, + attr_readonly = (_.has(this.fields_view.arch.attrs, 'edit'))?JSON.parse(this.fields_view.arch.attrs.edit):true; var pop = new instance.web.form.FormOpenPopup(self.o2m.view); pop.show_element(self.o2m.field.relation, id, self.o2m.build_context(), { title: _t("Open: ") + self.o2m.string, @@ -3477,7 +3478,7 @@ instance.web.form.One2ManyListView = instance.web.ListView.extend({ return self.o2m.dataset.read_ids.apply(self.o2m.dataset, arguments); }, form_view_options: {'not_interactible_on_create':true}, - readonly: self.o2m.get("effective_readonly") + readonly: attr_readonly ? self.o2m.get("effective_readonly") : true, }); }, do_button_action: function (name, id, callback) { @@ -3565,7 +3566,11 @@ instance.web.form.One2ManyListView = instance.web.ListView.extend({ }); instance.web.form.One2ManyList = instance.web.ListView.List.extend({ pad_table_to: function (count) { - this._super(count > 0 ? count - 1 : 0); + count = count > 0 ? count - 1 : 0 + if (! this.view._is_action_enabled('create')){ + count = this.records.length + } + this._super(count); // magical invocation of wtf does that do if (this.view.o2m.get('effective_readonly')) { From 813db083ff7844d2ffcdcb8a2ad5bedf3a0bfaf4 Mon Sep 17 00:00:00 2001 From: Jigar Amin - OpenERP Date: Fri, 24 Aug 2012 11:15:47 +0530 Subject: [PATCH 190/436] [REF] refectroed the row count code bzr revid: jam@tinyerp.com-20120824054547-v3me14ca341rkj44 --- addons/web/static/src/js/view_form.js | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/addons/web/static/src/js/view_form.js b/addons/web/static/src/js/view_form.js index e91d343a618..de7b737ad34 100644 --- a/addons/web/static/src/js/view_form.js +++ b/addons/web/static/src/js/view_form.js @@ -3566,11 +3566,7 @@ instance.web.form.One2ManyListView = instance.web.ListView.extend({ }); instance.web.form.One2ManyList = instance.web.ListView.List.extend({ pad_table_to: function (count) { - count = count > 0 ? count - 1 : 0 - if (! this.view._is_action_enabled('create')){ - count = this.records.length - } - this._super(count); + this._super(this.view._is_action_enabled('create') ? (count > 0 ? count - 1 : 0) : this.records.length); // magical invocation of wtf does that do if (this.view.o2m.get('effective_readonly')) { From 4969c586b4898b1ca1e9cf112985ce89f6030ae6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thibault=20Delavall=C3=A9e?= Date: Fri, 24 Aug 2012 10:53:42 +0200 Subject: [PATCH 191/436] [FIX] mail_mail: temp fix, commented headers propagation; mail_followers: send emails accordign to email preferences was not correct. bzr revid: tde@openerp.com-20120824085342-aa9s1ojl2j3v85fk --- addons/mail/mail_followers.py | 13 +++++++------ addons/mail/mail_mail.py | 4 +++- 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/addons/mail/mail_followers.py b/addons/mail/mail_followers.py index 671e2405b49..f73478f3a6f 100644 --- a/addons/mail/mail_followers.py +++ b/addons/mail/mail_followers.py @@ -72,6 +72,7 @@ class mail_notification(osv.Model): """ Send by email the notification depending on the user preferences """ context = context or {} partner_obj = self.pool.get('res.partner') + mail_mail_obj = self.pool.get('mail.mail') notification_obj = self.pool.get('mail.notification') msg_obj = self.pool.get('mail.message') msg = msg_obj.browse(cr, uid, msg_id, context=context) @@ -89,20 +90,20 @@ class mail_notification(osv.Model): if partner.notification_email_pref=='none' or not partner.email: continue # Partners want to receive only emails and comments - if partner.notification_email_pref=='comment' and msg.type in ('email','comment'): + if partner.notification_email_pref=='comment' and msg.type not in ('email','comment'): continue towrite['state'] = 'outgoing' if partner.email not in towrite['email_to']: towrite['email_to'].append(partner.email) - if towrite.get('state', False) and not context.get('noemail', False): - if towrite.get('subject', False): + if towrite.get('state') and not context.get('noemail'): + if towrite.get('subject'): towrite['subject'] = msg.name_get()[0][1] towrite['message_id'] = msg.id towrite['email_to'] = ', '.join(towrite['email_to']) - mail_message_obj = self.pool.get('mail.mail') - newid = mail_message_obj.create(cr, uid, towrite, context=context) - mail_message_obj.send(cr, uid, [newid], context=context) + + email_notif_id = mail_mail_obj.create(cr, uid, towrite, context=context) + mail_mail_obj.send(cr, uid, [email_notif_id], context=context) return True diff --git a/addons/mail/mail_mail.py b/addons/mail/mail_mail.py index bd0bfbf33c0..c5a062054f3 100644 --- a/addons/mail/mail_mail.py +++ b/addons/mail/mail_mail.py @@ -185,7 +185,9 @@ class mail_mail(osv.Model): object_id=message.res_id and ('%s-%s' % (message.res_id,message.model)), subtype=message.content_subtype, subtype_alternative=content_subtype_alternative, - headers=message.headers and ast.literal_eval(message.headers)) + # TDE FIXME: what to do with headers ? currently ycommented to avoid bugs + # headers=message.headers and ast.literal_eval(message.headers)) + ) res = ir_mail_server.send_email(cr, uid, msg, mail_server_id=message.mail_server_id.id, context=context) From 6a386dc762929d6a7ce368a78e17b43890631b0a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thibault=20Delavall=C3=A9e?= Date: Fri, 24 Aug 2012 11:31:30 +0200 Subject: [PATCH 192/436] [FIX] crm_forward_to_partner: to_email is now tools.email_split. bzr revid: tde@openerp.com-20120824093130-e24hm16pq14x84zb --- .../wizard/crm_forward_to_partner.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/addons/crm_partner_assign/wizard/crm_forward_to_partner.py b/addons/crm_partner_assign/wizard/crm_forward_to_partner.py index ce6087cad69..361cdeaf1e4 100644 --- a/addons/crm_partner_assign/wizard/crm_forward_to_partner.py +++ b/addons/crm_partner_assign/wizard/crm_forward_to_partner.py @@ -20,11 +20,12 @@ # ############################################################################## -import time import re +import time +import tools + from osv import osv, fields -from tools.translate import _ -from mail.mail_message import to_email +from tools.translate import _, class crm_lead_forward_to_partner(osv.osv_memory): """Forwards lead history""" @@ -111,13 +112,13 @@ class crm_lead_forward_to_partner(osv.osv_memory): if this.send_to == 'user': lead.allocate_salesman(cr, uid, [case.id], [this.user_id.id], context=context) - email_cc = to_email(case.email_cc) + email_cc = tools.email_split(case.email_cc) email_cc = email_cc and email_cc[0] or '' new_cc = [] if email_cc: new_cc.append(email_cc) for to in this.email_to.split(','): - email_to = to_email(to) + email_to = tools.email_split(to) email_to = email_to and email_to[0] or '' if email_to not in new_cc: new_cc.append(to) From c5cfb049f8573226be572a7a742b5f5381f6d15d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thibault=20Delavall=C3=A9e?= Date: Fri, 24 Aug 2012 12:02:05 +0200 Subject: [PATCH 193/436] [FIX] Fixed last commit (forgotten a ','), fixed demo data about non-existing fields. bzr revid: tde@openerp.com-20120824100205-nm4vjw13qx363cb5 --- addons/crm/crm_lead_demo.xml | 1 - addons/crm_partner_assign/wizard/crm_forward_to_partner.py | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/addons/crm/crm_lead_demo.xml b/addons/crm/crm_lead_demo.xml index 79aa1676a7e..83617d394fb 100644 --- a/addons/crm/crm_lead_demo.xml +++ b/addons/crm/crm_lead_demo.xml @@ -555,7 +555,6 @@ Andrew
      email - sent Reply diff --git a/addons/crm_partner_assign/wizard/crm_forward_to_partner.py b/addons/crm_partner_assign/wizard/crm_forward_to_partner.py index 361cdeaf1e4..3f90bebff97 100644 --- a/addons/crm_partner_assign/wizard/crm_forward_to_partner.py +++ b/addons/crm_partner_assign/wizard/crm_forward_to_partner.py @@ -25,7 +25,7 @@ import time import tools from osv import osv, fields -from tools.translate import _, +from tools.translate import _ class crm_lead_forward_to_partner(osv.osv_memory): """Forwards lead history""" From 36c3a308e684411bea18ddeb1965a7ab0cf9f597 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thibault=20Delavall=C3=A9e?= Date: Fri, 24 Aug 2012 12:03:35 +0200 Subject: [PATCH 194/436] [TOCHECK] crm_lead: commented statemetns that make no sense abotu changing partner of emails when converting a lead into an oportunity. bzr revid: tde@openerp.com-20120824100335-1kso3qphdh8piq32 --- addons/crm/crm_lead.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/addons/crm/crm_lead.py b/addons/crm/crm_lead.py index 703a389ba93..167a180b53e 100644 --- a/addons/crm/crm_lead.py +++ b/addons/crm/crm_lead.py @@ -597,11 +597,12 @@ class crm_lead(base_stage, osv.osv): self.convert_opportunity_send_note(cr, uid, lead, context=context) #TOCHECK: why need to change partner details in all messages of lead ? - if lead.partner_id: - msg_ids = [ x.id for x in lead.message_ids] - mail_message.write(cr, uid, msg_ids, { - 'partner_id': lead.partner_id.id - }, context=context) + # TDE FIXME: I propose to delete this, makes no sense to update partner of an already-send email ... + # if lead.partner_id: + # msg_ids = [x.id for x in lead.message_ids] + # mail_message.write(cr, uid, msg_ids, { + # 'partner_id': lead.partner_id.id + # }, context=context) if user_ids or section_id: self.allocate_salesman(cr, uid, ids, user_ids, section_id, context=context) From 70b90a23439eeb5c82f19936ab8ce485600e9b6d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thibault=20Delavall=C3=A9e?= Date: Fri, 24 Aug 2012 12:04:51 +0200 Subject: [PATCH 195/436] [FIX] mail_followers: when sending the notification email, it could be interesting to have something in the body. Well, receiving void emails ensure that your spam is not a time-consuming one, but I am not sure this was the intended purpose. bzr revid: tde@openerp.com-20120824100451-82crb3j7i0ki2h9h --- addons/mail/mail_followers.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/addons/mail/mail_followers.py b/addons/mail/mail_followers.py index f73478f3a6f..72dd3519ace 100644 --- a/addons/mail/mail_followers.py +++ b/addons/mail/mail_followers.py @@ -79,7 +79,8 @@ class mail_notification(osv.Model): towrite = { 'email_to': [], - 'subject': msg.subject + 'subject': msg.subject, + 'body': msg.body, } for partner in partner_obj.browse(cr, uid, partner_ids, context=context): From 18f6fd89900b242124e138f5389f97f79a6c2dad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thibault=20Delavall=C3=A9e?= Date: Fri, 24 Aug 2012 12:38:45 +0200 Subject: [PATCH 196/436] [FIX] email_template: fixed inherited view. bzr revid: tde@openerp.com-20120824103845-7g1jzgtcctry6nod --- addons/email_template/wizard/mail_compose_message_view.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addons/email_template/wizard/mail_compose_message_view.xml b/addons/email_template/wizard/mail_compose_message_view.xml index d0e842fa950..dff6f4a3ad4 100644 --- a/addons/email_template/wizard/mail_compose_message_view.xml +++ b/addons/email_template/wizard/mail_compose_message_view.xml @@ -31,7 +31,7 @@ - + Date: Fri, 24 Aug 2012 13:23:12 +0200 Subject: [PATCH 197/436] [IMP] mail.compose.message: cascade delete attachments upon vacuum-clean bzr revid: odo@openerp.com-20120824112312-zs5raruc4qdprlnp --- addons/mail/mail_message.py | 2 +- addons/mail/wizard/mail_compose_message.py | 7 ++++++- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/addons/mail/mail_message.py b/addons/mail/mail_message.py index 79e7eb81456..4ce552d6b05 100644 --- a/addons/mail/mail_message.py +++ b/addons/mail/mail_message.py @@ -220,7 +220,7 @@ class mail_message(osv.Model): for notification in not_obj.browse(cr, uid, not_ids, context=context): if notification.message_id.id in ids: pass - # FO Note: we should put this again !!! + # FP Note: we should put this again !!! #ids.remove(notification.message_id.id) # check messages according to related documents diff --git a/addons/mail/wizard/mail_compose_message.py b/addons/mail/wizard/mail_compose_message.py index 46ef8a4786f..8a172bd62e4 100644 --- a/addons/mail/wizard/mail_compose_message.py +++ b/addons/mail/wizard/mail_compose_message.py @@ -19,7 +19,6 @@ # ############################################################################## -import ast import re import tools @@ -281,6 +280,12 @@ class mail_compose_message(osv.TransientModel): return tools.ustr(result) return template and EXPRESSION_PATTERN.sub(merge, template) + def unlink(self, cr, uid, ids, context=None): + # Cascade delete all attachments, as they are owned by the composition wizard + for wizard in self.read(cr, uid, ids, ['attachment_ids'], context=context): + self.pool.get('ir.attachment').unlink(cr, uid, wizard['attachment_ids'], context=context) + return super(mail_compose_message,self).unlink(cr, uid, ids, context=context) + def dummy(self, cr, uid, ids, context=None): """ TDE: defined to have buttons that do basically nothing. It is currently impossible to have buttons that do nothing special From 545d7843e36bbc6351abdd319eac3016f11daaa2 Mon Sep 17 00:00:00 2001 From: Olivier Dony Date: Fri, 24 Aug 2012 14:01:02 +0200 Subject: [PATCH 198/436] [FIX] marketing_campaign: make tests pass again, move partner resolution to mail_compose_message email_template's job is to generate a mail.mail record, but it does not care about linking it automatically to a partner (and certainly not creating new partners) as this must be usable for mass mailing and for marketing campaign, and we don't want to pollute the partners database with possibly thousands of partially fake emails bzr revid: odo@openerp.com-20120824120102-jycs8x4vkxdmrvge --- addons/email_template/email_template.py | 8 -------- addons/mail/wizard/mail_compose_message.py | 8 ++++++++ addons/marketing_campaign/test/marketing_campaign.yml | 8 +------- 3 files changed, 9 insertions(+), 15 deletions(-) diff --git a/addons/email_template/email_template.py b/addons/email_template/email_template.py index 291ddd6a0ef..9adab6d3976 100644 --- a/addons/email_template/email_template.py +++ b/addons/email_template/email_template.py @@ -315,7 +315,6 @@ class email_template(osv.osv): 'message_id': False, 'state': 'outgoing', 'content_subtype': 'plain', - 'partner_ids': [], } if not template_id: return values @@ -330,13 +329,6 @@ class email_template(osv.osv): template.model, res_id, context=context) \ or False - # if email_to: find or create a partner - if values['email_to']: - partner_id = self.pool.get('mail.thread').message_partner_by_email(cr, uid, values['email_to'], context=context)['partner_id'] - if not partner_id: - partner_id = self.pool.get('res.partner').name_create(cr, uid, values['email_to'], context=context) - values['partner_ids'] = [partner_id] - if values['body_html']: values.update(content_subtype='html') diff --git a/addons/mail/wizard/mail_compose_message.py b/addons/mail/wizard/mail_compose_message.py index 8a172bd62e4..44c30241c13 100644 --- a/addons/mail/wizard/mail_compose_message.py +++ b/addons/mail/wizard/mail_compose_message.py @@ -241,6 +241,14 @@ class mail_compose_message(osv.TransientModel): subject = self.render_template(cr, uid, subject, active_model, active_id) body = self.render_template(cr, uid, wizard.body_html, active_model, active_id) + # TODO TDE: find partner_ids + # if email_to: find or create a partner + if values['email_to']: + partner_id = self.pool.get('mail.thread').message_partner_by_email(cr, uid, values['email_to'], context=context)['partner_id'] + if not partner_id: + partner_id = self.pool.get('res.partner').name_create(cr, uid, values['email_to'], context=context) + values['partner_ids'] = [partner_id] + # determine the ids we are commenting if mass_mail_mode: res_ids = context.get('active_ids', []) diff --git a/addons/marketing_campaign/test/marketing_campaign.yml b/addons/marketing_campaign/test/marketing_campaign.yml index 04063a6fbc1..3d540b4ccb7 100644 --- a/addons/marketing_campaign/test/marketing_campaign.yml +++ b/addons/marketing_campaign/test/marketing_campaign.yml @@ -42,7 +42,7 @@ record = self.browse(cr, uid, ids[0]) assert record.state == 'todo' or record.state == 'done' , 'Marketing Workitem shoud be in draft state.' - - I check follow-up detail of first activity. + I process follow-up of first activity. - !python {model: marketing.campaign.workitem}: | ids = self.search(cr, uid, [('segment_id', '=', ref('marketing_campaign_segment0')), @@ -50,12 +50,6 @@ assert ids, 'Follow-up item is not created for first activity.' work_item_id = self.browse(cr ,uid ,ids[0] ,context) assert work_item_id.res_name, 'Resource Name is not defined.' -- - I process follow-up of first activity. -- - !python {model: marketing.campaign.workitem}: | - ids = self.search(cr, uid, [('segment_id', '=', ref('marketing_campaign_segment0')), - ('campaign_id', '=', ref('marketing_campaign_openerppartnerchannel')), ('activity_id', '=', ref('marketing_campaign_activity_0'))]) self.process(cr, uid, ids) record = self.browse(cr, uid, ids)[0] assert record.state == "done", "Follow-up item should be closed after process." From fb76f9f03792da709e0fefeef3735cf9107d999a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thibault=20Delavall=C3=A9e?= Date: Fri, 24 Aug 2012 14:29:28 +0200 Subject: [PATCH 199/436] [IMP] [FIX] crm_partner_assign: as it inherits from mail.compose.message, updated the form view to avoid crashe; updated mail.compose.message composition mode, that is now an updatable selection field. crm_partner_assign will habe to be massively cleaned with the new mail module. bzr revid: tde@openerp.com-20120824122928-dof1y1uxv3lkiwly --- .../wizard/crm_forward_to_partner.py | 29 +++++++++++-------- .../wizard/crm_forward_to_partner_view.xml | 7 +---- addons/mail/wizard/mail_compose_message.py | 11 +++---- 3 files changed, 24 insertions(+), 23 deletions(-) diff --git a/addons/crm_partner_assign/wizard/crm_forward_to_partner.py b/addons/crm_partner_assign/wizard/crm_forward_to_partner.py index 3f90bebff97..14013c76b44 100644 --- a/addons/crm_partner_assign/wizard/crm_forward_to_partner.py +++ b/addons/crm_partner_assign/wizard/crm_forward_to_partner.py @@ -32,6 +32,11 @@ class crm_lead_forward_to_partner(osv.osv_memory): _name = 'crm.lead.forward.to.partner' _inherit = "mail.compose.message" + def _get_composition_mode_selection(self, cr, uid, context=None): + composition_mode = super(crm_lead_forward_to_partner, self)._get_composition_mode_selection(cr, uid, context=context) + composition_mode.append(('forward', 'Forward')) + return composition_mode + _columns = { 'send_to': fields.selection([('user', 'User'), ('partner', 'Partner'), \ ('email', 'Email Address')], 'Send to', required=True), @@ -44,7 +49,6 @@ class crm_lead_forward_to_partner(osv.osv_memory): _defaults = { 'send_to' : 'email', 'history': 'latest', - 'email_from': lambda s, cr, uid, c: s.pool.get('res.users').browse(cr, uid, uid, c).email, } def on_change_email(self, cr, uid, ids, user, context=None): @@ -68,19 +72,20 @@ class crm_lead_forward_to_partner(osv.osv_memory): res = {'value': {'body' : body}} return res - def on_change_partner(self, cr, uid, ids, partner_id): + def on_change_partners(self, cr, uid, ids, partner_id): """This function fills address information based on partner/user selected """ - if not partner_id: - return {'value' : {'email_to' : False}} - partner_obj = self.pool.get('res.partner') - data = {} - partner = partner_obj.browse(cr, uid, [partner_id]) - user_id = partner and partner[0].user_id or False - data.update({'email_from': partner and partner[0].email or "", - 'email_cc' : user_id and user_id.user or '', - 'user_id': user_id and user_id.id or False}) - return {'value' : data} + return {} + # if not partner_id: + # return {'value' : {'email_to' : False}} + # partner_obj = self.pool.get('res.partner') + # data = {} + # partner = partner_obj.browse(cr, uid, [partner_id]) + # user_id = partner and partner[0].user_id or False + # data.update({'email_from': partner and partner[0].email or "", + # 'email_cc' : user_id and user_id.user or '', + # 'user_id': user_id and user_id.id or False}) + # return {'value' : data} def action_forward(self, cr, uid, ids, context=None): """ diff --git a/addons/crm_partner_assign/wizard/crm_forward_to_partner_view.xml b/addons/crm_partner_assign/wizard/crm_forward_to_partner_view.xml index f415a00c680..0613c0aacab 100644 --- a/addons/crm_partner_assign/wizard/crm_forward_to_partner_view.xml +++ b/addons/crm_partner_assign/wizard/crm_forward_to_partner_view.xml @@ -17,16 +17,11 @@ on_change="on_change_email(user_id)"/> - + - - - - - diff --git a/addons/mail/wizard/mail_compose_message.py b/addons/mail/wizard/mail_compose_message.py index 44c30241c13..e64b0cd23aa 100644 --- a/addons/mail/wizard/mail_compose_message.py +++ b/addons/mail/wizard/mail_compose_message.py @@ -88,12 +88,13 @@ class mail_compose_message(osv.TransientModel): result[field] = vals[field] return result + def _get_composition_mode_selection(self, cr, uid, context=None): + return [('comment', 'Comment a document'), ('reply', 'Reply to a message'), ('mass_mail', 'Mass mailing')] + _columns = { - 'composition_mode': fields.selection([ - ('comment', 'Comment a document'), - ('reply', 'Reply to a message'), - ('mass_mail', 'Mass mailing') - ], string='Composition mode'), + 'composition_mode': fields.selection( + lambda s, *a, **k: s._get_composition_mode_selection(*a, **k), + string='Composition mode'), 'partner_ids': fields.many2many('res.partner', 'mail_compose_message_res_partner_rel', 'wizard_id', 'partner_id', 'Additional contacts'), From d91979671fb749fe379d1d0b995b5d19705175d3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thibault=20Delavall=C3=A9e?= Date: Fri, 24 Aug 2012 16:20:00 +0200 Subject: [PATCH 200/436] [REF] crm_partner_assign: refactored code a bit, following cleaning of mail, because it inherit from mail.compose.message. Removed send_to field, as the new composer is based on partners and simplify this wizard; removed a lot of code about setting email_from, email_to, ... because this should be managed by the other layers of the mail module. Cleaned the code in a more general way to remove weird things. Updated releveant form view, especially about context on Forward button, and the form view of the wizard that has been much simplified. bzr revid: tde@openerp.com-20120824142000-0jbgvgdiip5475yp --- addons/crm_partner_assign/crm_lead_view.xml | 7 +- .../test/partner_assign.yml | 4 +- .../wizard/crm_forward_to_partner.py | 226 ++++++++---------- .../wizard/crm_forward_to_partner_view.xml | 54 ++--- 4 files changed, 124 insertions(+), 167 deletions(-) diff --git a/addons/crm_partner_assign/crm_lead_view.xml b/addons/crm_partner_assign/crm_lead_view.xml index d4ff93d52fd..057d5240847 100644 --- a/addons/crm_partner_assign/crm_lead_view.xml +++ b/addons/crm_partner_assign/crm_lead_view.xml @@ -19,8 +19,7 @@ attrs="{'invisible':[('partner_assigned_id','=',False)]}" name="%(crm_lead_forward_to_partner_act)d" icon="terp-mail-forward" type="action" - context="{'default_name': 'partner', 'default_partner_id': partner_assigned_id}" - /> + context="{'mail.compose.message.mode': 'forward', 'default_partner_ids': [partner_assigned_id]}"/> @@ -37,8 +36,6 @@ - - crm.lead.geo_assign.tree.inherit crm.lead @@ -49,6 +46,7 @@
      + crm.opportunity.partner.filter.assigned crm.lead @@ -63,5 +61,6 @@
      + diff --git a/addons/crm_partner_assign/test/partner_assign.yml b/addons/crm_partner_assign/test/partner_assign.yml index bc4cd0d6e57..a055fc6cd6d 100644 --- a/addons/crm_partner_assign/test/partner_assign.yml +++ b/addons/crm_partner_assign/test/partner_assign.yml @@ -29,8 +29,8 @@ I forward this opportunity to its nearest partner. - !python {model: crm.lead.forward.to.partner}: | - context.update({'active_model': 'crm.lead', 'active_id': ref('crm.crm_case_19'), 'active_ids': [ref('crm.crm_case_19')]}) - forward_id = self.create(cr, uid, {'email_from': 'test@openerp.com', 'send_to': 'partner'}, context=context) + context.update({'default_model': 'crm.lead', 'default_res_id': ref('crm.crm_case_19'), 'active_ids': [ref('crm.crm_case_19')]}) + forward_id = self.create(cr, uid, {}, context=context) try: self.action_forward(cr, uid, [forward_id], context=context) except: diff --git a/addons/crm_partner_assign/wizard/crm_forward_to_partner.py b/addons/crm_partner_assign/wizard/crm_forward_to_partner.py index 14013c76b44..5b0fe97b015 100644 --- a/addons/crm_partner_assign/wizard/crm_forward_to_partner.py +++ b/addons/crm_partner_assign/wizard/crm_forward_to_partner.py @@ -28,107 +28,110 @@ from osv import osv, fields from tools.translate import _ class crm_lead_forward_to_partner(osv.osv_memory): - """Forwards lead history""" + """ Forward info history to partners. """ _name = 'crm.lead.forward.to.partner' _inherit = "mail.compose.message" + def default_get(self, cr, uid, fields, context=None): + if context is None: + context = {} + # set as comment, perform overrided document-like action that calls get_record_data + old_mode = context.get('mail.compose.message.mode', 'forward') + context['mail.compose.message.mode'] = 'comment' + res = super(crm_lead_forward_to_partner, self).default_get(cr, uid, fields, context=context) + # back to forward mode + context['mail.compose.message.mode'] = old_mode + res['composition_mode'] = context['mail.compose.message.mode'] + return res + def _get_composition_mode_selection(self, cr, uid, context=None): composition_mode = super(crm_lead_forward_to_partner, self)._get_composition_mode_selection(cr, uid, context=context) composition_mode.append(('forward', 'Forward')) return composition_mode _columns = { - 'send_to': fields.selection([('user', 'User'), ('partner', 'Partner'), \ - ('email', 'Email Address')], 'Send to', required=True), - 'user_id': fields.many2one('res.users', "User"), - 'attachment_ids': fields.many2many('ir.attachment','lead_forward_to_partner_attachment_rel', 'wizard_id', 'attachment_id', 'Attachments'), - 'partner_id' : fields.many2one('res.partner', 'Partner'), - 'history': fields.selection([('info', 'Case Information'), ('latest', 'Latest email'), ('whole', 'Whole Story')], 'Send history', required=True), + 'partner_ids': fields.many2many('res.partner', + 'lead_forward_to_partner_res_partner_rel', + 'wizard_id', 'partner_id', 'Additional contacts'), + 'attachment_ids': fields.many2many('ir.attachment', + 'lead_forward_to_partner_attachment_rel', + 'wizard_id', 'attachment_id', 'Attachments'), + 'history_mode': fields.selection([('info', 'Case Information'), + ('latest', 'Latest email'), ('whole', 'Whole Story')], + 'Send history', required=True), } _defaults = { - 'send_to' : 'email', - 'history': 'latest', + 'history_mode': 'latest', + 'content_subtype': lambda self,cr, uid, context={}: 'html', } - def on_change_email(self, cr, uid, ids, user, context=None): - if not user: - return {'value': {'email_to': False}} - return {'value': {'email_to': self.pool.get('res.users').browse(cr, uid, uid, context=context).email}} - - def on_change_history(self, cr, uid, ids, history_type, context=None): - """Gives message body according to type of history selected - * info: Forward the case information - * whole: Send the whole history - * latest: Send the latest histoy + def get_record_data(self, cr, uid, model, res_id, context=None): + """ Override of mail.compose.message, to add default values coming + form the related lead. """ - #TODO: ids and context are not comming - res = {} - res_id = context.get('active_id') - model = context.get('active_model') - lead = self.pool.get(model).browse(cr, uid, res_id, context) - body = self._get_message_body(cr, uid, lead, history_type, context=context) - if body: - res = {'value': {'body' : body}} + res = super(crm_lead_forward_to_partner, self).get_record_data(cr, uid, model, res_id, context=context) + if model not in ('crm.lead') or not res_id: + return res + lead_obj = self.pool.get(model) + lead = lead_obj.browse(cr, uid, res_id, context=context) + subject = '%s: %s - %s' % (_('Fwd'), _('Lead forward'), lead.name) + body = self._get_message_body(cr, uid, lead, 'info', context=context) + res.update({ + 'subject': subject, + 'body': body, + }) return res + + def on_change_history_mode(self, cr, uid, ids, history_mode, model, res_id, context=None): + """ Update body when changing history_mode """ + if model and model == 'crm.lead' and res_id: + lead = self.pool.get(model).browse(cr, uid, res_id, context=context) + body = self._get_message_body(cr, uid, lead, history_mode, context=context) + return {'value': {'body': body}} - def on_change_partners(self, cr, uid, ids, partner_id): - """This function fills address information based on partner/user selected - """ + def on_change_partner_ids(self, cr, uid, ids, partner_ids, context=None): + """ TDE-TODO: Keep void; maybe we could check that partner_ids have + email defined. """ return {} - # if not partner_id: - # return {'value' : {'email_to' : False}} - # partner_obj = self.pool.get('res.partner') - # data = {} - # partner = partner_obj.browse(cr, uid, [partner_id]) - # user_id = partner and partner[0].user_id or False - # data.update({'email_from': partner and partner[0].email or "", - # 'email_cc' : user_id and user_id.user or '', - # 'user_id': user_id and user_id.id or False}) - # return {'value' : data} + + def create(self, cr, uid, values, context=None): + """ TDE-HACK: remove 'type' from context, because when viewing an + opportunity form view, a default_type is set and propagated + to the wizard, that has a not matching type field. """ + default_type = context.pop('default_type', None) + new_id = super(crm_lead_forward_to_partner, self).create(cr, uid, values, context=context) + if default_type: + context['default_type'] = default_type + return new_id def action_forward(self, cr, uid, ids, context=None): """ - Forward the lead to a partner + Forward the lead to a partner """ if context is None: context = {} res = {'type': 'ir.actions.act_window_close'} - model = context.get('active_model') - if model not in ('crm.lead'): + wizard = self.browse(cr, uid, ids[0], context=context) + if wizard.model not in ('crm.lead'): return res - this = self.browse(cr, uid, ids[0], context=context) - lead = self.pool.get(model) - lead_id = context and context.get('active_id', False) or False - lead_ids = lead_id and [lead_id] or [] - mode = context.get('mail.compose.message.mode') - if mode == 'mass_mail': + lead = self.pool.get(wizard.model) + lead_ids = wizard.res_id and [wizard.res_id] or [] + + if wizard.composition_mode == 'mass_mail': lead_ids = context and context.get('active_ids', []) or [] - value = self.default_get(cr, uid, ['body', 'email_to', 'email_cc', 'subject', 'history'], context=context) + value = self.default_get(cr, uid, ['body', 'email_to', 'email_cc', 'subject', 'history_mode'], context=context) self.write(cr, uid, ids, value, context=context) - context['mail.compose.message.mode'] = mode self.send_mail(cr, uid, ids, context=context) - for case in lead.browse(cr, uid, lead_ids, context=context): - if (this.send_to == 'partner' and this.partner_id): - lead.assign_partner(cr, uid, [case.id], this.partner_id.id, context=context) + # for case in lead.browse(cr, uid, lead_ids, context=context): + # TDE: WHAT TO DO WITH THAT ? + # if (this.send_to == 'partner' and this.partner_id): + # lead.assign_partner(cr, uid, [case.id], this.partner_id.id, context=context) - if this.send_to == 'user': - lead.allocate_salesman(cr, uid, [case.id], [this.user_id.id], context=context) - - email_cc = tools.email_split(case.email_cc) - email_cc = email_cc and email_cc[0] or '' - new_cc = [] - if email_cc: - new_cc.append(email_cc) - for to in this.email_to.split(','): - email_to = tools.email_split(to) - email_to = email_to and email_to[0] or '' - if email_to not in new_cc: - new_cc.append(to) - update_vals = {'email_cc' : ', '.join(new_cc) } - lead.write(cr, uid, [case.id], update_vals, context=context) + # if this.send_to == 'user': + # lead.allocate_salesman(cr, uid, [case.id], [this.user_id.id], context=context) return res def _get_info_body(self, cr, uid, lead, context=None): @@ -137,75 +140,40 @@ class crm_lead_forward_to_partner(osv.osv_memory): if lead.type == 'opportunity': field_names += ['partner_id'] field_names += [ - 'partner_name' , 'title', 'function', 'street', 'street2', + 'partner_name' , 'title', 'function', 'street', 'street2', 'zip', 'city', 'country_id', 'state_id', 'email_from', 'phone', 'fax', 'mobile', 'categ_id', 'description', ] return proxy._mail_body(cr, uid, lead, field_names, context=context) - def _get_message_body(self, cr, uid, lead, mode='whole', context=None): - """This function gets whole communication history and returns as top posting style + def _get_message_body(self, cr, uid, lead, history_mode='whole', context=None): + """ This function gets whole communication history and returns as top + posting style + #1: form a body, based on the lead + #2: append to the body the communication history, based on the + history_mode parameter + + - info: Forward the case information + - latest: Send the latest history + - whole: Send the whole history + + :param lead: browse_record on crm.lead + :param history_mode: 'whole' or 'latest' """ mail_message = self.pool.get('mail.message') - message_ids = [] body = self._get_info_body(cr, uid, lead, context=context) - if mode in ('whole', 'latest'): - message_ids = lead.message_ids - message_ids = map(lambda x: x.id, filter(lambda x: x.email_from, message_ids)) - if mode == 'latest' and len(message_ids): - message_ids = [message_ids[0]] - for message in mail_message.browse(cr, uid, message_ids, context=context): - header = '-------- Original Message --------' - sender = 'From: %s' %(message.email_from or '') - to = 'To: %s' % (message.email_to or '') - sentdate = 'Date: %s' % (message.date or '') - desc = '\n%s'%(message.body) - original = [header, sender, to, sentdate, desc, '\n'] - original = '\n'.join(original) - body += original + if history_mode not in ('whole', 'latest'): + return body or '' + for message in lead.message_ids: + header = '-------- Original Message --------' + sentdate = 'Date: %s' % (message.date or '') + desc = '\n%s'%(message.body) + original = [header, sentdate, desc, '\n'] + original = '\n'.join(original) + body += original + if history_mode == 'latest': + break return body or '' - def get_value(self, cr, uid, model, res_id, context=None): - if context is None: - context = {} - res = super(crm_lead_forward_to_partner, self).get_value(cr, uid, model, res_id, context=context) - if model not in ("crm.lead"): - return res - proxy = self.pool.get(model) - partner = self.pool.get('res.partner') - lead = proxy.browse(cr, uid, res_id, context=context) - mode = context.get('mail.compose.message.mode') - if mode == "forward": - body_type = context.get('mail.compose.message.body') - email_cc = res.get('email_cc', "") - email = res.get('email_to', "") - subject = '%s: %s - %s' % (_('Fwd'), 'Lead forward', lead.name) - body = self._get_message_body(cr, uid, lead, body_type, context=context) - partner_assigned_id = lead.partner_assigned_id and lead.partner_assigned_id.id or False - user_id = False - if not partner_assigned_id: - partner_assigned_id = proxy.search_geo_partner(cr, uid, [lead.id], context=None).get(lead.id, False) - if partner_assigned_id: - assigned_partner = partner.browse(cr, uid, partner_assigned_id, context=context) - user_id = assigned_partner.user_id and assigned_partner.user_id.id or False - email_cc = assigned_partner.user_id and assigned_partner.user_id.email or '' - email = assigned_partner.email - - res.update({ - 'subject' : subject, - 'body' : body, - 'email_cc' : email_cc, - 'email_to' : email, - 'partner_assigned_id': partner_assigned_id, - 'user_id': user_id, - }) - return res - - def default_get(self, cr, uid, fields, context=None): - if context is None: - context = {} - context['mail.compose.message.mode'] = 'forward' - context['mail.compose.message.body'] = 'info' - return super(crm_lead_forward_to_partner, self).default_get(cr, uid, fields, context=context) # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/addons/crm_partner_assign/wizard/crm_forward_to_partner_view.xml b/addons/crm_partner_assign/wizard/crm_forward_to_partner_view.xml index 0613c0aacab..600bd301b3f 100644 --- a/addons/crm_partner_assign/wizard/crm_forward_to_partner_view.xml +++ b/addons/crm_partner_assign/wizard/crm_forward_to_partner_view.xml @@ -7,37 +7,31 @@ crm.lead.forward.to.partner
      + + + - - - - - - - - - + + + + + + + + + + + + - - - - - - - - - - - -
      -
      @@ -52,8 +46,6 @@ new - - - - + /> From ab6b3cc266068efc9e61d01d4dbb023eb3025093 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thibault=20Delavall=C3=A9e?= Date: Fri, 24 Aug 2012 16:31:12 +0200 Subject: [PATCH 201/436] [IMP] Remove some warnings about: alias_force_id -> alias_force_thread_id; type set in view that is not necessayr anymore. bzr revid: tde@openerp.com-20120824143112-krho3ahvklxcod29 --- addons/mail/mail_followers_view.xml | 1 - addons/mail/res_users.py | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/addons/mail/mail_followers_view.xml b/addons/mail/mail_followers_view.xml index 0637d4dc11e..eb22abc2629 100644 --- a/addons/mail/mail_followers_view.xml +++ b/addons/mail/mail_followers_view.xml @@ -6,7 +6,6 @@ mail.followers.tree mail.followers - tree 10 diff --git a/addons/mail/res_users.py b/addons/mail/res_users.py index babf2213bd5..b32b6a6ff65 100644 --- a/addons/mail/res_users.py +++ b/addons/mail/res_users.py @@ -75,7 +75,7 @@ class res_users(osv.Model): # Use read() not browse(), to avoid prefetching uninitialized inherited fields for user_data in res_users_model.read(cr, SUPERUSER_ID, users_no_alias, ['login']): alias_id = mail_alias.create_unique_alias(cr, SUPERUSER_ID, {'alias_name': user_data['login'], - 'alias_force_id': user_data['id']}, + 'alias_force_thread_id': user_data['id']}, model_name=self._name) res_users_model.write(cr, SUPERUSER_ID, user_data['id'], {'alias_id': alias_id}) _logger.info('Mail alias created for user %s (uid %s)', user_data['login'], user_data['id']) From 6e3ea98e486751a13fa6802b1a03b3c0365b20d6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thibault=20Delavall=C3=A9e?= Date: Fri, 24 Aug 2012 18:09:36 +0200 Subject: [PATCH 202/436] [IMP] mail_mail: re-instaured body_alternative, using html2plaintext of tools. Removed some dead imports. bzr revid: tde@openerp.com-20120824160936-n3fehtgf4f2k0dkn --- addons/mail/mail_mail.py | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/addons/mail/mail_mail.py b/addons/mail/mail_mail.py index c5a062054f3..fa0c18ef529 100644 --- a/addons/mail/mail_mail.py +++ b/addons/mail/mail_mail.py @@ -19,13 +19,9 @@ # ############################################################################## -import ast +# import ast import base64 -import datetime -import email import logging -import re -import time import tools from osv import osv @@ -158,10 +154,10 @@ class mail_mail(osv.Model): body = message.body_html if message.content_subtype == 'html' else message.body body_alternative = None content_subtype_alternative = None - if message.content_subtype == 'html' and message.body: + if message.content_subtype == 'html': # we have a plain text alternative prepared, pass it to # build_message instead of letting it build one - body_alternative = message.body + body_alternative = tools.html2plaintext(message.body) content_subtype_alternative = 'plain' # handle destination_partners From d583b98a9731118c123df2a99006ef24e0eacef8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thibault=20Delavall=C3=A9e?= Date: Fri, 24 Aug 2012 18:10:38 +0200 Subject: [PATCH 203/436] [IMP] mail_followers: moved email creation for emails send for notification into a dedicated method; updated subject and body creation (added default subject, added signature on body). bzr revid: tde@openerp.com-20120824161038-vhylgsify9f9xder --- addons/mail/mail_followers.py | 29 ++++++++++++++++++----------- 1 file changed, 18 insertions(+), 11 deletions(-) diff --git a/addons/mail/mail_followers.py b/addons/mail/mail_followers.py index 72dd3519ace..2045df49d70 100644 --- a/addons/mail/mail_followers.py +++ b/addons/mail/mail_followers.py @@ -71,19 +71,13 @@ class mail_notification(osv.Model): def notify(self, cr, uid, partner_ids, msg_id, context=None): """ Send by email the notification depending on the user preferences """ context = context or {} - partner_obj = self.pool.get('res.partner') mail_mail_obj = self.pool.get('mail.mail') - notification_obj = self.pool.get('mail.notification') msg_obj = self.pool.get('mail.message') msg = msg_obj.browse(cr, uid, msg_id, context=context) - towrite = { - 'email_to': [], - 'subject': msg.subject, - 'body': msg.body, - } + towrite = self.notify_get_notif_email_dict(cr, uid, msg, context=context) - for partner in partner_obj.browse(cr, uid, partner_ids, context=context): + for partner in self.pool.get('res.partner').browse(cr, uid, partner_ids, context=context): # Do not send an email to the writer if partner.user_id.id == uid: continue @@ -98,9 +92,7 @@ class mail_notification(osv.Model): if partner.email not in towrite['email_to']: towrite['email_to'].append(partner.email) - if towrite.get('state') and not context.get('noemail'): - if towrite.get('subject'): - towrite['subject'] = msg.name_get()[0][1] + if towrite.get('state') and not context.get('mail_noemail'): towrite['message_id'] = msg.id towrite['email_to'] = ', '.join(towrite['email_to']) @@ -108,3 +100,18 @@ class mail_notification(osv.Model): mail_mail_obj.send(cr, uid, [email_notif_id], context=context) return True + + def notify_get_notif_email_dict(self, cr, uid, msg, context=None): + """ Return the content of the email send for notification. + :param message: browse record on source mail.message + """ + subject = msg.subject or '%s posted a comment on %s' % (msg.author_id.name, msg.record_name) + body = msg.body + author_signature = msg.author_id.user_ids[0].signature + if author_signature: + body += '
      %s
      ' % (author_signature) + return { + 'email_to': [], + 'subject': subject, + 'body': body, + } From d19ad1ff4ba8008cc2829ada1e9ba8e6ea989b17 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thibault=20Delavall=C3=A9e?= Date: Fri, 24 Aug 2012 18:11:17 +0200 Subject: [PATCH 204/436] [TEST] mail: added tests for the composer, includes a first bench of tests for notifications and send emails. bzr revid: tde@openerp.com-20120824161117-abur02jlgs08my21 --- addons/mail/tests/test_mail.py | 82 ++++++++++++++++++++++++---------- 1 file changed, 58 insertions(+), 24 deletions(-) diff --git a/addons/mail/tests/test_mail.py b/addons/mail/tests/test_mail.py index 83c9f363c7f..29db0bc529e 100644 --- a/addons/mail/tests/test_mail.py +++ b/addons/mail/tests/test_mail.py @@ -20,6 +20,7 @@ ############################################################################## from openerp.tests import common +import tools MAIL_TEMPLATE = """Return-Path: To: {to} @@ -78,6 +79,9 @@ class test_mail(common.TransactionCase): self.res_users = self.registry('res.users') self.res_partner = self.registry('res.partner') + # Install mock SMTP gateway + self.registry('ir.mail_server').send_email = lambda *a,**kwargs: True + # groups@.. will cause the creation of new mail groups self.mail_group_model_id = self.ir_model.search(self.cr, self.uid, [('model','=', 'mail.group')])[0] self.mail_alias.create(self.cr, self.uid, {'alias_name': 'groups', @@ -235,41 +239,71 @@ class test_mail(common.TransactionCase): cr, uid = self.cr, self.uid mail_compose = self.registry('mail.compose.message') user_admin = self.res_users.browse(cr, uid, uid) + self.res_users.write(cr, uid, [uid], {'signature': 'Admin'}) + user_admin.refresh() group_pigs = self.mail_group.browse(cr, uid, self.group_pigs_id) - # print 'pigs message_ids: %s' % group_pigs.message_ids - # print 'pigs follower_ids: %s' % group_pigs.message_follower_ids - # Create partner Bert Poilu - partner_bert_id = self.res_partner.create(cr, uid, {'name': 'Bert Poilu', 'email': 'bert@poil.poil'}) - partner_raoul_id = self.res_partner.create(cr, uid, {'name': 'Raoul Grosbedon'}) + # Mail data + _subject = 'Pigs' + _mail_subject = '%s posted a comment on %s' % (user_admin.name, group_pigs.name) + _body_text = 'Pigs rules' + _body_html = 'Pigs rules' + + # Create partners + # 0 - Create an email address for Admin, to check email sending + self.res_users.write(cr, uid, [uid], {'email': 'a@a'}) + # 1 - Bert Tartopoils, with email, should receive emails for comments and emails + partner_bert_id = self.res_partner.create(cr, uid, {'name': 'Bert Tartopoils', 'email': 'b@b'}) + # 2 - Raoul Grosbedon, without email, to test email verification; should receive emails for every message + partner_raoul_id = self.res_partner.create(cr, uid, {'name': 'Raoul Grosbedon', 'notification_email_pref': 'all'}) + # 3 - Roger Poilvache, with email, should never receive emails + partner_roger_id = self.res_partner.create(cr, uid, {'name': 'Roger Poilvache', 'email': 'r@r', 'notification_email_pref': 'none'}) # Create a new comment on group_pigs compose_id = mail_compose.create(cr, uid, - {'subject': 'Pigs', 'body_text': 'Pigs rules', 'partner_ids': [(4, partner_bert_id), (4, partner_raoul_id)]}, + {'subject': _subject, 'body_text': _body_text, 'partner_ids': [(4, partner_bert_id), (4, partner_raoul_id), (4, partner_roger_id)]}, {'mail.compose.message': 'comment', 'default_model': 'mail.group', 'default_res_id': self.group_pigs_id}) - # print 'wizard id: %s' % compose_id compose = mail_compose.browse(cr, uid, compose_id) - # print 'wizard partner_ids: %s' % compose.partner_ids - self.assertTrue(compose.model == 'mail.group' and compose.res_id == self.group_pigs_id) + self.assertTrue(compose.model == 'mail.group' and compose.res_id == self.group_pigs_id, + 'Wizard message has model %s and res_id %s; should be mail.group and %s' % (compose.model, compose.res_id, self.group_pigs_id)) - # Send the comment + # Post the comment, get created message mail_compose.send_mail(cr, uid, [compose_id]) - group_pigs.refresh() - # print 'pigs message_ids: %s' % group_pigs.message_ids[0] - new_message = group_pigs.message_ids[0] - new_partner_ids = [partner.id for partner in new_message.partner_ids] + first_com = group_pigs.message_ids[0] - # print 'pigs new_message subject: %s' % new_message.subject - # print 'pigs new_message body: %s' % new_message.body - # print 'pigs new_message partner_ids: %s' % new_message.partner_ids - - notif_ids = self.mail_notification.search(cr, uid, [('message_id', '=', new_message.id)]) - # print notif_ids + # Check message content + self.assertTrue(first_com.subject == False and first_com.body == _body_text, + 'Posted comment subject is %s and body is %s; should be False and %s' % (first_com.subject, first_com.body, _body_text)) # Message partners = notified people = writer + partner_ids - self.assertTrue(len(new_partner_ids) == 3, 'There should be 3 partners linked to the newly posted comment.') - self.assertTrue(len(notif_ids) == 3, 'There should be only 3 entries in mail_notification') - self.assertTrue(all(id in [user_admin.partner_id.id, partner_bert_id, partner_raoul_id] for id in new_partner_ids), - 'Admin, Bert and Roaul should be the 3 partners of the newly created message') + first_com_pids = [partner.id for partner in first_com.partner_ids] + notif_ids = self.mail_notification.search(cr, uid, [('message_id', '=', first_com.id)]) + self.assertTrue(len(first_com_pids) == 4, 'There are %s partners linked to the newly posted comment; should be 4' % (len(first_com_pids))) + self.assertTrue(len(notif_ids) == 4, 'There are %s 3 entries in mail_notification: should be 4' % (len(notif_ids))) + self.assertTrue(all(id in [user_admin.partner_id.id, partner_bert_id, partner_raoul_id, partner_roger_id] for id in first_com_pids), + 'Admin, Bert Raoul and Roger should be the 4 partners of the newly created message') + + # Fetch latest created email, that should be the email send to partners + mail_ids = self.mail_mail.search(cr, uid, [], limit=1) + mail = self.mail_mail.browse(cr, uid, mail_ids[0]) + mail_emails = tools.email_split(mail.email_to) + + # Check email subject, body, and email_to + expected_emails = ['a@a', 'b@b'] + self.assertTrue(mail.subject == _mail_subject and _body_text in mail.body, + 'Send email subject is \'%s\' and should be %s; body is %s and should contain %s' % (mail.subject, _mail_subject, mail.body, _body_text)) + self.assertTrue(all(email in mail_emails for email in expected_emails) and len(mail_emails) == 2, + 'Send email emails are %s; should be %s' % (mail_emails, expected_emails)) + + # Create a reply to the last comment, with default partners + compose_id = mail_compose.create(cr, uid, + {}, {'mail.compose.message.mode': 'reply', 'default_model': 'mail.thread', 'default_res_id': self.group_pigs_id, + 'active_id': first_com.id}) + compose = mail_compose.browse(cr, uid, compose_id) + self.assertTrue(compose.model == 'mail.group' and compose.res_id == self.group_pigs_id, + 'Wizard message has model: %s and res_id:%s; should be mail.group and %s' % (compose.model, compose.res_id, self.group_pigs_id)) + self.assertTrue(compose.parent_id.id == first_com.id and len(compose.partner_ids) == 5, + 'Wizard parent_id is %d; should be %d; or wrong partner_ids (TO BE CONTINUED)' % (compose.parent_id.id, first_com.id)) + \ No newline at end of file From 971756e0161d48eb53f754068b6ba11edf8269dd Mon Sep 17 00:00:00 2001 From: Olivier Dony Date: Fri, 24 Aug 2012 18:13:20 +0200 Subject: [PATCH 205/436] [FIX] crm: correct mass conversion wizard defaults, fixes crm tests bzr revid: odo@openerp.com-20120824161320-mdfqibfe53v2op79 --- addons/crm/test/process/lead2opportunity2win.yml | 5 +++-- addons/crm/wizard/crm_lead_to_opportunity.py | 15 ++++++++++++++- 2 files changed, 17 insertions(+), 3 deletions(-) diff --git a/addons/crm/test/process/lead2opportunity2win.yml b/addons/crm/test/process/lead2opportunity2win.yml index 0156dc9a8b1..ed552421cf0 100644 --- a/addons/crm/test/process/lead2opportunity2win.yml +++ b/addons/crm/test/process/lead2opportunity2win.yml @@ -73,7 +73,7 @@ I convert mass lead into opportunity customer. - !python {model: crm.lead2opportunity.partner.mass}: | - context.update({'active_model': 'crm.lead', 'active_ids': [ref("crm_case_11"), ref("crm_case_2")], 'active_id': ref("crm_case_4")}) + context.update({'active_model': 'crm.lead', 'active_ids': [ref("crm_case_11"), ref("crm_case_2")], 'active_id': ref("crm_case_11")}) id = self.create(cr, uid, {'user_ids': [ref('base.user_root')], 'section_id': ref('crm.section_sales_department')}, context=context) self.mass_convert(cr, uid, [id], context=context) - @@ -83,7 +83,8 @@ opp = self.browse(cr, uid, ref('crm_case_11')) assert opp.name == "Need estimated cost for new project", "Opportunity name not correct" assert opp.type == 'opportunity', 'Lead is not converted to opportunity!' - assert opp.partner_id.name == "Thomas Passot", 'Partner mismatch!' + expected_partner = "Thomas Passot" + assert opp.partner_id.name == expected_partner, 'Partner mismatch! %s vs %s' % (opp.partner_id.name, expected_partner) assert opp.stage_id.id == ref("stage_lead1"), 'Stage of probability is incorrect!' - Then check for second lead converted on opportunity. diff --git a/addons/crm/wizard/crm_lead_to_opportunity.py b/addons/crm/wizard/crm_lead_to_opportunity.py index fc366768023..e90303dda66 100644 --- a/addons/crm/wizard/crm_lead_to_opportunity.py +++ b/addons/crm/wizard/crm_lead_to_opportunity.py @@ -153,11 +153,24 @@ class crm_lead2opportunity_mass_convert(osv.osv_memory): _description = 'Mass Lead To Opportunity Partner' _inherit = 'crm.lead2opportunity.partner' - _columns = { 'user_ids': fields.many2many('res.users', string='Salesmen'), 'section_id': fields.many2one('crm.case.section', 'Sales Team'), } + + def default_get(self, cr, uid, fields, context=None): + res = super(crm_lead2opportunity_mass_convert, self).default_get(cr, uid, fields, context) + if 'partner_id' in fields: + # avoid forcing the partner of the first lead as default + res['partner_id'] = False + if 'action' in fields: + res['action'] = 'create' + if 'name' in fields: + res['name'] = 'convert' + if 'opportunity_ids' in fields: + res['opportunity_ids'] = False + return res + def _convert_opportunity(self, cr, uid, ids, vals, context=None): data = self.browse(cr, uid, ids, context=context)[0] salesteam_id = data.section_id and data.section_id.id or False From e79baa9e8a5759a3626c15dba76b1f03d8ecd098 Mon Sep 17 00:00:00 2001 From: Jigar Amin - OpenERP Date: Mon, 27 Aug 2012 10:31:51 +0530 Subject: [PATCH 206/436] [FIX] resolve Conflicts reverts bzr revid: jam@tinyerp.com-20120827050151-yinbjtladoe3vu5w --- addons/web_diagram/static/src/js/diagram.js | 2 +- addons/web_diagram/static/src/xml/base_diagram.xml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/addons/web_diagram/static/src/js/diagram.js b/addons/web_diagram/static/src/js/diagram.js index 657e68d6f06..543cc6a9c12 100644 --- a/addons/web_diagram/static/src/js/diagram.js +++ b/addons/web_diagram/static/src/js/diagram.js @@ -47,7 +47,7 @@ instance.web.DiagramView = instance.web.View.extend({ return label.tag == "label"; }); - this.$el.html(QWeb.render("DiagramView", this)); + this.$el.html(QWeb.render("DiagramView", {'widget': this})); this.$el.addClass(this.fields_view.arch.attrs['class']); _.each(self.labels,function(label){ diff --git a/addons/web_diagram/static/src/xml/base_diagram.xml b/addons/web_diagram/static/src/xml/base_diagram.xml index b6065093231..f883f949457 100644 --- a/addons/web_diagram/static/src/xml/base_diagram.xml +++ b/addons/web_diagram/static/src/xml/base_diagram.xml @@ -2,7 +2,7 @@

      - +
      From b5cd423bcafe0857467e06ecc0fdd7dab7e76171 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thibault=20Delavall=C3=A9e?= Date: Mon, 27 Aug 2012 10:51:45 +0200 Subject: [PATCH 207/436] [FIX] Chatter: tried to fix the display on Wall / document. bzr revid: tde@openerp.com-20120827085145-7v4l5cz6u7lhsajw --- addons/mail/static/src/css/mail.css | 32 +++++++++++++++++++++-------- addons/mail/static/src/xml/mail.xml | 8 +++----- 2 files changed, 26 insertions(+), 14 deletions(-) diff --git a/addons/mail/static/src/css/mail.css b/addons/mail/static/src/css/mail.css index d094aa9fbcb..f33f1c6ed99 100644 --- a/addons/mail/static/src/css/mail.css +++ b/addons/mail/static/src/css/mail.css @@ -1,3 +1,20 @@ +/* ------------------------------------------------------------ */ +/* Reset because of ugly display of end of August +/* ------------------------------------------------------------ */ + +.openerp .oe_mail_wall ul, .openerp .oe_mail_wall li { + list-style-type: none; + padding: 0; + margin: 0; +} + +.openerp .oe_mail_recthread ul, .openerp .oe_mail_recthread li { + list-style-type: none; + padding: 0; + margin: 0; +} + + /* ------------------------------------------------------------ */ /* Wall /* ------------------------------------------------------------ */ @@ -8,19 +25,16 @@ background: white; } -.openerp div.oe_mail_wall_main { - float: left; - width: 560px; - margin: 8px; -} - -.openerp div.oe_mail_wall_aside { +.openerp div.oe_mail_wall div.oe_mail_wall_aside { margin-left: 565px; margin: 8px; } -.openerp ul.oe_mail_wall_threads { - margin-top: 8px; +.openerp div.oe_mail_wall ul.oe_mail_wall_threads { + float: left; + width: 560px; + margin: 8px; + list-style-type: none; } /* Specific display of threads in the wall */ diff --git a/addons/mail/static/src/xml/mail.xml b/addons/mail/static/src/xml/mail.xml index d0ae83316e7..06a1ce48605 100644 --- a/addons/mail/static/src/xml/mail.xml +++ b/addons/mail/static/src/xml/mail.xml @@ -27,11 +27,9 @@ -
      -
        - -
      -
      +
        + +
      From 3d78cacd447ee26e4f89af2e6afe8425523271bb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thibault=20Delavall=C3=A9e?= Date: Mon, 27 Aug 2012 11:28:24 +0200 Subject: [PATCH 208/436] [FIX] base_calendar: fixed a typo in crm_meeting_view added when merging trunk. bzr revid: tde@openerp.com-20120827092824-ic9v3ug6ljyqeip1 --- addons/base_calendar/crm_meeting_view.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addons/base_calendar/crm_meeting_view.xml b/addons/base_calendar/crm_meeting_view.xml index d5d5d2dcc72..928f8d94d5c 100644 --- a/addons/base_calendar/crm_meeting_view.xml +++ b/addons/base_calendar/crm_meeting_view.xml @@ -244,7 +244,7 @@ CRM - Meetings Tree crm.meeting - From 9e3184ad33ccdafe2277878c053870b7ed26bb1e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thibault=20Delavall=C3=A9e?= Date: Mon, 27 Aug 2012 11:42:28 +0200 Subject: [PATCH 209/436] [CLEAN] mail_message: get_record_name and name_get now use a dedicated function to truncate names. Moved arbitrary '18' into a variable. bzr revid: tde@openerp.com-20120827094228-xl2i88lhqgn0fw04 --- addons/mail/mail_message.py | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/addons/mail/mail_message.py b/addons/mail/mail_message.py index 4ce552d6b05..bd74e5eb0d0 100644 --- a/addons/mail/mail_message.py +++ b/addons/mail/mail_message.py @@ -21,7 +21,6 @@ import logging from email.header import decode_header - from osv import osv, fields import tools @@ -45,15 +44,22 @@ class mail_message(osv.Model): _inherit = ['ir.needaction_mixin'] _order = 'id desc' + _message_read_limit = 10 + _message_record_name_length = 18 + + def _shorten_name(self, name): + if len(name) <= (self._message_record_name_length+3): + return name + else: + return name[:18] + '...' + def get_record_name(self, cr, uid, ids, name, arg, context=None): + """ Return the related document name, using get_name. """ result = dict.fromkeys(ids, '') for message in self.browse(cr, uid, ids, context=context): if not message.model or not message.res_id: continue - doc = self.pool.get(message.model).name_get(cr, uid, [message.res_id], context=context)[0][1] - if len(doc)>18: - doc=doc[:18]+'...' - result[message.id] = doc + result[message.id] = self._shorten_name(self.pool.get(message.model).name_get(cr, uid, [message.res_id], context=context)[0][1]) return result def name_get(self, cr, uid, ids, context=None): @@ -62,12 +68,8 @@ class mail_message(osv.Model): ids = [ids] res = [] for message in self.browse(cr, uid, ids, context=context): - name = '' - if message.subject: - name = '%s: ' % (message.subject) - if message.body: - name = name + message.body[0:20] - res.append((message.id, name)) + name = '%s: %s' % (message.subject or '', message.body or '') + res.append((message.id, self._shorten_name(name.lstrip(' :')))) return res _columns = { From 1491a9f1368f11d0a53e4d6295dbfafa847d4b45 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thibault=20Delavall=C3=A9e?= Date: Mon, 27 Aug 2012 12:16:59 +0200 Subject: [PATCH 210/436] [REF] mail: mail_mail views are now in mail_mail_view.xml file. bzr revid: tde@openerp.com-20120827101659-9i1c0rcep4ixzxpb --- addons/mail/__openerp__.py | 1 + addons/mail/mail_mail_view.xml | 131 ++++++++++++++++++++++++++++++ addons/mail/mail_message_view.xml | 127 ----------------------------- 3 files changed, 132 insertions(+), 127 deletions(-) create mode 100644 addons/mail/mail_mail_view.xml diff --git a/addons/mail/__openerp__.py b/addons/mail/__openerp__.py index daec8d03183..99dd6bda6ac 100644 --- a/addons/mail/__openerp__.py +++ b/addons/mail/__openerp__.py @@ -64,6 +64,7 @@ The main features of the module are: 'wizard/mail_compose_message_view.xml', 'res_config_view.xml', 'mail_message_view.xml', + 'mail_mail_view.xml', 'mail_followers_view.xml', 'mail_thread_view.xml', 'mail_group_view.xml', diff --git a/addons/mail/mail_mail_view.xml b/addons/mail/mail_mail_view.xml new file mode 100644 index 00000000000..3abe0b1ddc6 --- /dev/null +++ b/addons/mail/mail_mail_view.xml @@ -0,0 +1,131 @@ + + + + + mail.mail.form + mail.mail + +
      + + +
      +
      +
      + + + mail.message.tree + mail.mail + + + + + + + + + + + + +

      - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
      -
      - - - mail.message.tree - mail.mail - - - - - - - - - - - - - From 8b98de00e43b2ba0a3df676f0362a9d3d342092b Mon Sep 17 00:00:00 2001 From: Raphael Collet Date: Tue, 4 Sep 2012 09:49:21 +0200 Subject: [PATCH 306/436] [IMP] simplification in list views: merge two css classes bzr revid: rco@openerp.com-20120904074921-ff9pkr64g58ku9n3 --- addons/web/static/src/css/base.css | 2 +- addons/web/static/src/css/base.sass | 2 +- addons/web/static/src/xml/base.xml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/addons/web/static/src/css/base.css b/addons/web/static/src/css/base.css index d32b0a9f42a..bbee2adfbc4 100644 --- a/addons/web/static/src/css/base.css +++ b/addons/web/static/src/css/base.css @@ -2640,7 +2640,7 @@ .openerp .oe_list_cannot_edit .oe_list_header_handle, .openerp .oe_list_cannot_edit .oe_list_field_handle { display: none !important; } -.openerp .oe_list_cannot_delete .oe_list_header_delete, .openerp .oe_list_cannot_delete .oe_list_record_delete { +.openerp .oe_list_cannot_delete .oe_list_record_delete { display: none !important; } .openerp .tree_header { diff --git a/addons/web/static/src/css/base.sass b/addons/web/static/src/css/base.sass index 8ddf9b56b9f..63c6f3398fa 100644 --- a/addons/web/static/src/css/base.sass +++ b/addons/web/static/src/css/base.sass @@ -2054,7 +2054,7 @@ $sheet-max-width: 860px .oe_list_header_handle, .oe_list_field_handle display: none !important .oe_list_cannot_delete - .oe_list_header_delete, .oe_list_record_delete + .oe_list_record_delete display: none !important // }}} diff --git a/addons/web/static/src/xml/base.xml b/addons/web/static/src/xml/base.xml index 7c135ad047c..a92eacc0710 100644 --- a/addons/web/static/src/xml/base.xml +++ b/addons/web/static/src/xml/base.xml @@ -635,7 +635,7 @@ - + From a0704feafd897d1f7e408464e09d1ea255850209 Mon Sep 17 00:00:00 2001 From: Raphael Collet Date: Tue, 4 Sep 2012 10:03:02 +0200 Subject: [PATCH 307/436] [FIX] web_kanban: remove unexpected css class bzr revid: rco@openerp.com-20120904080302-dcxytj3tkin6fdt3 --- addons/web_kanban/static/src/xml/web_kanban.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addons/web_kanban/static/src/xml/web_kanban.xml b/addons/web_kanban/static/src/xml/web_kanban.xml index d3ed0fed129..b2f9e757b43 100644 --- a/addons/web_kanban/static/src/xml/web_kanban.xml +++ b/addons/web_kanban/static/src/xml/web_kanban.xml @@ -28,7 +28,7 @@ -
      +
      ]
      From c52ab94960f0346ff82e2ecf72cda67d667d9f6f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thibault=20Delavall=C3=A9e?= Date: Tue, 4 Sep 2012 11:28:20 +0200 Subject: [PATCH 308/436] [REVIEW] addons: small cleanups nex to a diff read. Some improvements will follow, this commit is about small cleanups. bzr revid: tde@openerp.com-20120904092820-56zgwpkqfpd7as5k --- addons/crm_claim/test/ui/claim_demo.yml | 1 - addons/crm_partner_assign/crm_lead_view.xml | 2 +- .../wizard/crm_forward_to_partner.py | 8 +++--- .../wizard/crm_forward_to_partner_view.xml | 2 +- addons/email_template/email_template.py | 2 +- addons/hr_evaluation/hr_evaluation_view.xml | 25 ++++++++++--------- addons/mail/mail_group.py | 8 ++---- addons/mail/mail_mail_view.xml | 2 +- addons/mail/tests/test_mail.py | 4 +-- addons/mail/wizard/mail_compose_message.py | 8 +++--- .../marketing_campaign_demo.xml | 2 +- addons/purchase/purchase.py | 2 +- 12 files changed, 31 insertions(+), 35 deletions(-) diff --git a/addons/crm_claim/test/ui/claim_demo.yml b/addons/crm_claim/test/ui/claim_demo.yml index 6b6d380f5be..044e0482e93 100644 --- a/addons/crm_claim/test/ui/claim_demo.yml +++ b/addons/crm_claim/test/ui/claim_demo.yml @@ -12,4 +12,3 @@ self.message_update(cr, uid,[ref('crm_claim_4')], {'subject': 'Claim Update record','body': 'first training session completed',}) except: pass - diff --git a/addons/crm_partner_assign/crm_lead_view.xml b/addons/crm_partner_assign/crm_lead_view.xml index 057d5240847..cd2e3f08c9a 100644 --- a/addons/crm_partner_assign/crm_lead_view.xml +++ b/addons/crm_partner_assign/crm_lead_view.xml @@ -19,7 +19,7 @@ attrs="{'invisible':[('partner_assigned_id','=',False)]}" name="%(crm_lead_forward_to_partner_act)d" icon="terp-mail-forward" type="action" - context="{'mail.compose.message.mode': 'forward', 'default_partner_ids': [partner_assigned_id]}"/> + context="{'default_composition_mode': 'forward', 'default_partner_ids': [partner_assigned_id]}"/>
      diff --git a/addons/crm_partner_assign/wizard/crm_forward_to_partner.py b/addons/crm_partner_assign/wizard/crm_forward_to_partner.py index 5b0fe97b015..6ed8da7b231 100644 --- a/addons/crm_partner_assign/wizard/crm_forward_to_partner.py +++ b/addons/crm_partner_assign/wizard/crm_forward_to_partner.py @@ -36,12 +36,12 @@ class crm_lead_forward_to_partner(osv.osv_memory): if context is None: context = {} # set as comment, perform overrided document-like action that calls get_record_data - old_mode = context.get('mail.compose.message.mode', 'forward') - context['mail.compose.message.mode'] = 'comment' + old_mode = context.get('default_composition_mode', 'forward') + context['default_composition_mode'] = 'comment' res = super(crm_lead_forward_to_partner, self).default_get(cr, uid, fields, context=context) # back to forward mode - context['mail.compose.message.mode'] = old_mode - res['composition_mode'] = context['mail.compose.message.mode'] + context['default_composition_mode'] = old_mode + res['composition_mode'] = context['default_composition_mode'] return res def _get_composition_mode_selection(self, cr, uid, context=None): diff --git a/addons/crm_partner_assign/wizard/crm_forward_to_partner_view.xml b/addons/crm_partner_assign/wizard/crm_forward_to_partner_view.xml index 600bd301b3f..fe9e1476817 100644 --- a/addons/crm_partner_assign/wizard/crm_forward_to_partner_view.xml +++ b/addons/crm_partner_assign/wizard/crm_forward_to_partner_view.xml @@ -51,7 +51,7 @@ key2="client_action_multi" name="Mass forward to partner" res_model="crm.lead.forward.to.partner" src_model="crm.lead" view_mode="form" target="new" view_type="form" - context="{'mail.compose.message.mode' : 'mass_mail'}" + context="{'default_composition_mode' : 'mass_mail'}" view_id="crm_lead_forward_to_partner_form" /> diff --git a/addons/email_template/email_template.py b/addons/email_template/email_template.py index 73dc5ed2754..b68005bd997 100644 --- a/addons/email_template/email_template.py +++ b/addons/email_template/email_template.py @@ -171,7 +171,7 @@ class email_template(osv.osv): 'res_model': 'mail.compose.message', 'src_model': src_obj, 'view_type': 'form', - 'context': "{'mail.compose.message.mode':'mass_mail', 'mail.compose.template_id' : %d}" % (template.id), + 'context': "{'default_composition_mode': 'mass_mail', 'default_template_id' : %d}" % (template.id), 'view_mode':'form,tree', 'view_id': res_id, 'target': 'new', diff --git a/addons/hr_evaluation/hr_evaluation_view.xml b/addons/hr_evaluation/hr_evaluation_view.xml index 12c5dc0af6d..9f64df8808d 100644 --- a/addons/hr_evaluation/hr_evaluation_view.xml +++ b/addons/hr_evaluation/hr_evaluation_view.xml @@ -388,22 +388,23 @@ action="action_hr_evaluation_interview_tree"/> + + id="evaluation_reminders" name="Appraisal Reminders" + res_model="mail.compose.message" + src_model="hr.evaluation.interview" + view_type="form" view_mode="form" + target="new" multi="True" + key2="client_action_multi" + context="{'default_composition_mode': 'mass_mail'}"/> + context="{'search_default_user_to_review_id': [active_id], 'default_user_to_review_id': active_id}" + id="act_hr_employee_2_hr__evaluation_interview" + name="Appraisal Interviews" + res_model="hr.evaluation.interview" + src_model="hr.employee"/> diff --git a/addons/mail/mail_group.py b/addons/mail/mail_group.py index d17be6d578e..f5769e082a1 100644 --- a/addons/mail/mail_group.py +++ b/addons/mail/mail_group.py @@ -28,12 +28,8 @@ from osv import fields from tools.translate import _ class mail_group(osv.Model): - """ - A mail_group is a collection of users sharing messages in a discussion - group. Group users are users that follow the mail group, using the - subscription/follow mechanism of OpenSocial. A mail group has nothing - in common with res.users.group. - """ + """ A mail_group is a collection of users sharing messages in a discussion + group. The group mechanics are based on the followers. """ _description = 'Discussion group' _name = 'mail.group' _inherit = ['mail.thread'] diff --git a/addons/mail/mail_mail_view.xml b/addons/mail/mail_mail_view.xml index f6846954ec0..72d41e33a84 100644 --- a/addons/mail/mail_mail_view.xml +++ b/addons/mail/mail_mail_view.xml @@ -12,7 +12,7 @@
      by on
      diff --git a/addons/mail/tests/test_mail.py b/addons/mail/tests/test_mail.py index fa30c76aa1e..9c148647b9b 100644 --- a/addons/mail/tests/test_mail.py +++ b/addons/mail/tests/test_mail.py @@ -353,7 +353,7 @@ class test_mail(common.TransactionCase): # CASE1: comment group_pigs with body_text and subject compose_id = mail_compose.create(cr, uid, {'subject': _subject, 'body_text': _body_text, 'partner_ids': [(4, p_c_id), (4, p_d_id)]}, - {'mail.compose.message.mode': 'comment', 'default_model': 'mail.group', 'default_res_id': self.group_pigs_id}) + {'default_composition_mode': 'comment', 'default_model': 'mail.group', 'default_res_id': self.group_pigs_id}) compose = mail_compose.browse(cr, uid, compose_id) # Test: mail.compose.message: model, res_id self.assertEqual(compose.model, 'mail.group', 'mail.compose.message incorrect model') @@ -378,7 +378,7 @@ class test_mail(common.TransactionCase): message.write({'subject': _subject}) compose_id = mail_compose.create(cr, uid, {'attachment_ids': [(0, 0, _attachments[0]), (0, 0, _attachments[1])]}, - {'mail.compose.message.mode': 'reply', 'default_model': 'mail.thread', 'default_res_id': self.group_pigs_id, 'active_id': message.id}) + {'default_composition_mode': 'reply', 'default_model': 'mail.thread', 'default_res_id': self.group_pigs_id, 'default_parent_id': message.id}) compose = mail_compose.browse(cr, uid, compose_id) # Test: form view methods diff --git a/addons/mail/wizard/mail_compose_message.py b/addons/mail/wizard/mail_compose_message.py index 0c0626f44c9..15f428264ff 100644 --- a/addons/mail/wizard/mail_compose_message.py +++ b/addons/mail/wizard/mail_compose_message.py @@ -35,8 +35,7 @@ class mail_compose_message(osv.TransientModel): """ Generic message composition wizard. You may inherit from this wizard at model and view levels to provide specific features. - The behavior of the wizard can be modified through the context key - mail.compose.message.mode: + The behavior of the wizard depends on the composition_mode field: - 'reply': reply to a previous message. The wizard is pre-populated via ``get_message_data``. - 'comment': new post on a record. The wizard is pre-populated via @@ -56,10 +55,11 @@ class mail_compose_message(osv.TransientModel): - default_model or active_model - default_res_id or active_id - reply: active_id of a message the user replies to - - active_id: ID of a mail.message to which we are replying + - default_parent_id or message_id or active_id: ID of the + mail.message we reply to - message.res_model or default_model - message.res_id or default_res_id - - mass_mailing mode: model and IDs of records the user mass-mails + - mass_mail: model and IDs of records the user mass-mails - active_ids: record IDs - default_model or active_model """ diff --git a/addons/marketing_campaign/marketing_campaign_demo.xml b/addons/marketing_campaign/marketing_campaign_demo.xml index 19e44eb4ace..0c2d7f32715 100644 --- a/addons/marketing_campaign/marketing_campaign_demo.xml +++ b/addons/marketing_campaign/marketing_campaign_demo.xml @@ -64,7 +64,7 @@ - + diff --git a/addons/purchase/purchase.py b/addons/purchase/purchase.py index b1a5631e59e..592bf783a64 100644 --- a/addons/purchase/purchase.py +++ b/addons/purchase/purchase.py @@ -732,7 +732,7 @@ class purchase_order(osv.osv): # -------------------------------------- def needaction_domain_get(self, cr, uid, ids, context=None): - return [('state','=','draft')] + return [('state', '=', 'draft')] def create_send_note(self, cr, uid, ids, context=None): return self.message_post(cr, uid, ids, body=_("Request for quotation created."), context=context) From fdfaa0f5cfe33c9641c5c7a39c5ab474aeaf01a1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thibault=20Delavall=C3=A9e?= Date: Tue, 4 Sep 2012 11:29:38 +0200 Subject: [PATCH 309/436] [REF] project_mailgate: moved content of project_mailgate (basically message_new and message_update overrides, and a now dead message_thread_followers) into project. Removed project_mailgate module. bzr revid: tde@openerp.com-20120904092938-fmesjk7cwzxa3s0h --- addons/project/project.py | 39 +++++++ addons/project_mailgate/__init__.py | 24 ---- addons/project_mailgate/__openerp__.py | 52 --------- addons/project_mailgate/i18n/ar.po | 102 ----------------- addons/project_mailgate/i18n/ca.po | 106 ----------------- addons/project_mailgate/i18n/cs.po | 78 ------------- addons/project_mailgate/i18n/da.po | 78 ------------- addons/project_mailgate/i18n/de.po | 106 ----------------- addons/project_mailgate/i18n/es.po | 106 ----------------- addons/project_mailgate/i18n/es_CR.po | 107 ----------------- addons/project_mailgate/i18n/es_MX.po | 108 ------------------ addons/project_mailgate/i18n/es_VE.po | 108 ------------------ addons/project_mailgate/i18n/fi.po | 88 -------------- addons/project_mailgate/i18n/fr.po | 106 ----------------- addons/project_mailgate/i18n/gl.po | 105 ----------------- addons/project_mailgate/i18n/hr.po | 104 ----------------- addons/project_mailgate/i18n/hu.po | 84 -------------- addons/project_mailgate/i18n/it.po | 106 ----------------- addons/project_mailgate/i18n/ja.po | 78 ------------- addons/project_mailgate/i18n/lv.po | 87 -------------- addons/project_mailgate/i18n/mn.po | 85 -------------- addons/project_mailgate/i18n/nl.po | 104 ----------------- addons/project_mailgate/i18n/nl_BE.po | 105 ----------------- addons/project_mailgate/i18n/pl.po | 102 ----------------- .../i18n/project_mailgate.pot | 77 ------------- addons/project_mailgate/i18n/pt.po | 84 -------------- addons/project_mailgate/i18n/pt_BR.po | 105 ----------------- addons/project_mailgate/i18n/ro.po | 107 ----------------- addons/project_mailgate/i18n/sv.po | 78 ------------- addons/project_mailgate/i18n/tr.po | 78 ------------- addons/project_mailgate/i18n/zh_CN.po | 101 ---------------- addons/project_mailgate/project_mailgate.py | 80 ------------- 32 files changed, 39 insertions(+), 2839 deletions(-) delete mode 100644 addons/project_mailgate/__init__.py delete mode 100644 addons/project_mailgate/__openerp__.py delete mode 100644 addons/project_mailgate/i18n/ar.po delete mode 100644 addons/project_mailgate/i18n/ca.po delete mode 100644 addons/project_mailgate/i18n/cs.po delete mode 100644 addons/project_mailgate/i18n/da.po delete mode 100644 addons/project_mailgate/i18n/de.po delete mode 100644 addons/project_mailgate/i18n/es.po delete mode 100644 addons/project_mailgate/i18n/es_CR.po delete mode 100644 addons/project_mailgate/i18n/es_MX.po delete mode 100644 addons/project_mailgate/i18n/es_VE.po delete mode 100644 addons/project_mailgate/i18n/fi.po delete mode 100644 addons/project_mailgate/i18n/fr.po delete mode 100644 addons/project_mailgate/i18n/gl.po delete mode 100644 addons/project_mailgate/i18n/hr.po delete mode 100644 addons/project_mailgate/i18n/hu.po delete mode 100644 addons/project_mailgate/i18n/it.po delete mode 100644 addons/project_mailgate/i18n/ja.po delete mode 100644 addons/project_mailgate/i18n/lv.po delete mode 100644 addons/project_mailgate/i18n/mn.po delete mode 100644 addons/project_mailgate/i18n/nl.po delete mode 100644 addons/project_mailgate/i18n/nl_BE.po delete mode 100644 addons/project_mailgate/i18n/pl.po delete mode 100644 addons/project_mailgate/i18n/project_mailgate.pot delete mode 100644 addons/project_mailgate/i18n/pt.po delete mode 100644 addons/project_mailgate/i18n/pt_BR.po delete mode 100644 addons/project_mailgate/i18n/ro.po delete mode 100644 addons/project_mailgate/i18n/sv.po delete mode 100644 addons/project_mailgate/i18n/tr.po delete mode 100644 addons/project_mailgate/i18n/zh_CN.po delete mode 100644 addons/project_mailgate/project_mailgate.py diff --git a/addons/project/project.py b/addons/project/project.py index 42fbe56036d..241e3a24dc5 100644 --- a/addons/project/project.py +++ b/addons/project/project.py @@ -1155,6 +1155,45 @@ class task(base_stage, osv.osv): result += "\n" return result + # --------------------------------------------------- + # mail gateway + # --------------------------------------------------- + + def message_new(self, cr, uid, msg, custom_values=None, context=None): + """ Override to updates the document according to the email. """ + if custom_values is None: custom_values = {} + custom_values.update({ + 'name': subject, + 'planned_hours': 0.0, + 'subject': msg.get('subject'), + }) + return super(project_tasks,self).message_new(cr, uid, msg, custom_values=custom_values, context=context) + + def message_update(self, cr, uid, ids, msg, update_vals=None, context=None): + """ Override to update the task according to the email. """ + if update_vals is None: update_vals = {} + act = False + maps = { + 'cost':'planned_hours', + } + for line in msg['body'].split('\n'): + line = line.strip() + res = tools.misc.command_re.match(line) + if res: + match = res.group(1).lower() + field = maps.get(match) + if field: + try: + update_vals[field] = float(res.group(2).lower()) + except (ValueError, TypeError): + pass + elif match.lower() == 'state' \ + and res.group(2).lower() in ['cancel','close','draft','open','pending']: + act = 'do_%s' % res.group(2).lower() + if act: + getattr(self,act)(cr, uid, ids, context=context) + return super(project_tasks,self).message_update(cr, uid, msg, update_vals=update_vals, context=context) + # --------------------------------------------------- # OpenChatter methods and notifications # --------------------------------------------------- diff --git a/addons/project_mailgate/__init__.py b/addons/project_mailgate/__init__.py deleted file mode 100644 index 0f82ebd1b41..00000000000 --- a/addons/project_mailgate/__init__.py +++ /dev/null @@ -1,24 +0,0 @@ -# -*- 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 . -# -############################################################################## - -import project_mailgate - -# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/addons/project_mailgate/__openerp__.py b/addons/project_mailgate/__openerp__.py deleted file mode 100644 index 05eacb782cc..00000000000 --- a/addons/project_mailgate/__openerp__.py +++ /dev/null @@ -1,52 +0,0 @@ -# -*- 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 . -# -############################################################################## - - -{ - 'name': 'Tasks-Mail Integration', - 'version': '1.1', - 'author': 'OpenERP SA', - 'website': 'http://www.openerp.com', - 'category': 'Project Management', - 'images': ['images/project_mailgate_task.jpeg'], - 'depends': ['project', 'mail'], - 'description': """ -This module can automatically create Project Tasks based on incoming emails. -============================================================================ - -Allows creating tasks based on new emails arriving at a given mailbox, -similarly to what the CRM application has for Leads/Opportunities. - -There are two common alternatives to configure the mailbox integration: ------------------------------------------------------------------------ - * Install the ``fetchmail`` module and configure a new mailbox, then select - ``Project Tasks`` as the target for incoming emails. - * Set it up manually on your mail server based on the 'mail gateway' script - provided in the ``mail`` module - and connect it to the `project.task` model. - """, - 'data': [], - 'demo': [], - 'installable': True, - 'auto_install': False, - 'certificate': '001075048780413258261', -} - -# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/addons/project_mailgate/i18n/ar.po b/addons/project_mailgate/i18n/ar.po deleted file mode 100644 index 42b0945e3a8..00000000000 --- a/addons/project_mailgate/i18n/ar.po +++ /dev/null @@ -1,102 +0,0 @@ -# Arabic translation for openobject-addons -# Copyright (c) 2012 Rosetta Contributors and Canonical Ltd 2012 -# This file is distributed under the same license as the openobject-addons package. -# FIRST AUTHOR , 2012. -# -msgid "" -msgstr "" -"Project-Id-Version: openobject-addons\n" -"Report-Msgid-Bugs-To: FULL NAME \n" -"POT-Creation-Date: 2012-02-08 00:37+0000\n" -"PO-Revision-Date: 2012-01-12 21:12+0000\n" -"Last-Translator: FULL NAME \n" -"Language-Team: Arabic \n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=UTF-8\n" -"Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-08-28 06:40+0000\n" -"X-Generator: Launchpad (build 15864)\n" - -#. module: project_mailgate -#: view:project.task:0 -msgid "History Information" -msgstr "معلومات المحفوظات" - -#. module: project_mailgate -#: model:ir.model,name:project_mailgate.model_project_task -msgid "Task" -msgstr "مهمة" - -#. module: project_mailgate -#: constraint:project.task:0 -msgid "Error ! You cannot create recursive tasks." -msgstr "خطأ ! لا يمكنك انشاء مهام رجعية." - -#. module: project_mailgate -#: field:project.task,message_ids:0 -msgid "Messages" -msgstr "الرسائل" - -#. module: project_mailgate -#: code:addons/project_mailgate/project_mailgate.py:90 -#, python-format -msgid "Draft" -msgstr "مسودة" - -#. module: project_mailgate -#: constraint:project.task:0 -msgid "Error ! Task end-date must be greater then task start-date" -msgstr "خطأ! يجب ان يكون تاريخ انتهاء المهمة اكبر من تاريخ البداية" - -#. module: project_mailgate -#: code:addons/project_mailgate/project_mailgate.py:116 -#, python-format -msgid "Cancel" -msgstr "إلغاء" - -#. module: project_mailgate -#: code:addons/project_mailgate/project_mailgate.py:110 -#, python-format -msgid "Done" -msgstr "تم" - -#. module: project_mailgate -#: code:addons/project_mailgate/project_mailgate.py:96 -#, python-format -msgid "Open" -msgstr "فتح" - -#. module: project_mailgate -#: code:addons/project_mailgate/project_mailgate.py:102 -#, python-format -msgid "Pending" -msgstr "معلّق" - -#. module: project_mailgate -#: view:project.task:0 -msgid "History" -msgstr "محفوظات" - -#~ msgid "Attachments" -#~ msgstr "مرفقات" - -#~ msgid "Details" -#~ msgstr "تفاصيل" - -#~ msgid "Project MailGateWay" -#~ msgstr "مدخل بريد المشروع" - -#~ msgid "" -#~ "This module is an interface that synchronises mails with OpenERP Project " -#~ "Task.\n" -#~ "\n" -#~ "It allows creating tasks as soon as a new mail arrives in our configured " -#~ "mail server.\n" -#~ "Moreover, it keeps track of all further communications and task states.\n" -#~ " " -#~ msgstr "" -#~ "هذه الوحدة هي تفاعل التي تتزامن البريد مع مهام مشروع OpenERP .\n" -#~ "وهي تسمح بإنشاء المهام في اقرب وقت كما يصل البريد الجديد في خادم البريد الذي " -#~ "تم ضبطه.\n" -#~ "علاوة على ذلك, تستمر في تعقب المزيد من كل الاتصالات وحالات المهمة.\n" -#~ " " diff --git a/addons/project_mailgate/i18n/ca.po b/addons/project_mailgate/i18n/ca.po deleted file mode 100644 index 622d0a55ac1..00000000000 --- a/addons/project_mailgate/i18n/ca.po +++ /dev/null @@ -1,106 +0,0 @@ -# Catalan translation for openobject-addons -# Copyright (c) 2011 Rosetta Contributors and Canonical Ltd 2011 -# This file is distributed under the same license as the openobject-addons package. -# FIRST AUTHOR , 2011. -# -msgid "" -msgstr "" -"Project-Id-Version: openobject-addons\n" -"Report-Msgid-Bugs-To: FULL NAME \n" -"POT-Creation-Date: 2012-02-08 00:37+0000\n" -"PO-Revision-Date: 2011-02-06 21:57+0000\n" -"Last-Translator: FULL NAME \n" -"Language-Team: Catalan \n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=UTF-8\n" -"Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-08-28 06:40+0000\n" -"X-Generator: Launchpad (build 15864)\n" - -#. module: project_mailgate -#: view:project.task:0 -msgid "History Information" -msgstr "Informació històrica" - -#. module: project_mailgate -#: model:ir.model,name:project_mailgate.model_project_task -msgid "Task" -msgstr "Tasca" - -#. module: project_mailgate -#: constraint:project.task:0 -msgid "Error ! You cannot create recursive tasks." -msgstr "Error! No podeu crear tasques recursives." - -#. module: project_mailgate -#: field:project.task,message_ids:0 -msgid "Messages" -msgstr "Missatges" - -#. module: project_mailgate -#: code:addons/project_mailgate/project_mailgate.py:90 -#, python-format -msgid "Draft" -msgstr "Esborrany" - -#. module: project_mailgate -#: constraint:project.task:0 -msgid "Error ! Task end-date must be greater then task start-date" -msgstr "" -"Error ! La data final de la tasca ha de ser major que la data d'inici" - -#. module: project_mailgate -#: code:addons/project_mailgate/project_mailgate.py:116 -#, python-format -msgid "Cancel" -msgstr "Canceŀla" - -#. module: project_mailgate -#: code:addons/project_mailgate/project_mailgate.py:110 -#, python-format -msgid "Done" -msgstr "Realitzat" - -#. module: project_mailgate -#: code:addons/project_mailgate/project_mailgate.py:96 -#, python-format -msgid "Open" -msgstr "Obre" - -#. module: project_mailgate -#: code:addons/project_mailgate/project_mailgate.py:102 -#, python-format -msgid "Pending" -msgstr "Pendent" - -#. module: project_mailgate -#: view:project.task:0 -msgid "History" -msgstr "Històric" - -#~ msgid "Project MailGateWay" -#~ msgstr "Ruta d'enllaç del projecte" - -#~ msgid "Attachments" -#~ msgstr "Adjunts" - -#~ msgid "Details" -#~ msgstr "Detalls" - -#~ msgid "" -#~ "This module is an interface that synchronises mails with OpenERP Project " -#~ "Task.\n" -#~ "\n" -#~ "It allows creating tasks as soon as a new mail arrives in our configured " -#~ "mail server.\n" -#~ "Moreover, it keeps track of all further communications and task states.\n" -#~ " " -#~ msgstr "" -#~ "Aquest mòdul proporciona una interfície per sincronitzar correus amb les " -#~ "tasques de projectes d'OpenERP.\n" -#~ "\n" -#~ "Permet crear tasques tan aviat com arriba un nou correu en el nostre " -#~ "servidor de correu prèviament configurat.\n" -#~ "A més realitza un seguiment de totes les comunicacions addicionals i estats " -#~ "de la tasca.\n" -#~ " " diff --git a/addons/project_mailgate/i18n/cs.po b/addons/project_mailgate/i18n/cs.po deleted file mode 100644 index b65ae7910f5..00000000000 --- a/addons/project_mailgate/i18n/cs.po +++ /dev/null @@ -1,78 +0,0 @@ -# Czech translation for openobject-addons -# Copyright (c) 2012 Rosetta Contributors and Canonical Ltd 2012 -# This file is distributed under the same license as the openobject-addons package. -# FIRST AUTHOR , 2012. -# -msgid "" -msgstr "" -"Project-Id-Version: openobject-addons\n" -"Report-Msgid-Bugs-To: FULL NAME \n" -"POT-Creation-Date: 2012-02-08 00:37+0000\n" -"PO-Revision-Date: 2012-04-06 06:41+0000\n" -"Last-Translator: Jiří Hajda \n" -"Language-Team: Czech \n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=UTF-8\n" -"Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-08-28 06:40+0000\n" -"X-Generator: Launchpad (build 15864)\n" - -#. module: project_mailgate -#: view:project.task:0 -msgid "History Information" -msgstr "Informace historie" - -#. module: project_mailgate -#: model:ir.model,name:project_mailgate.model_project_task -msgid "Task" -msgstr "Úkol" - -#. module: project_mailgate -#: constraint:project.task:0 -msgid "Error ! You cannot create recursive tasks." -msgstr "Chyba ! Nemůžete vytvořit rekurzivní úkoly." - -#. module: project_mailgate -#: field:project.task,message_ids:0 -msgid "Messages" -msgstr "Zprávy" - -#. module: project_mailgate -#: code:addons/project_mailgate/project_mailgate.py:90 -#, python-format -msgid "Draft" -msgstr "Koncept" - -#. module: project_mailgate -#: constraint:project.task:0 -msgid "Error ! Task end-date must be greater then task start-date" -msgstr "Chyba ! Datum ukončení úkolu musí být větší než počáteční datum" - -#. module: project_mailgate -#: code:addons/project_mailgate/project_mailgate.py:116 -#, python-format -msgid "Cancel" -msgstr "Zrušit" - -#. module: project_mailgate -#: code:addons/project_mailgate/project_mailgate.py:110 -#, python-format -msgid "Done" -msgstr "Dokončené" - -#. module: project_mailgate -#: code:addons/project_mailgate/project_mailgate.py:96 -#, python-format -msgid "Open" -msgstr "Otevřené" - -#. module: project_mailgate -#: code:addons/project_mailgate/project_mailgate.py:102 -#, python-format -msgid "Pending" -msgstr "Čekající" - -#. module: project_mailgate -#: view:project.task:0 -msgid "History" -msgstr "Historie" diff --git a/addons/project_mailgate/i18n/da.po b/addons/project_mailgate/i18n/da.po deleted file mode 100644 index 43aeba4c665..00000000000 --- a/addons/project_mailgate/i18n/da.po +++ /dev/null @@ -1,78 +0,0 @@ -# Danish translation for openobject-addons -# Copyright (c) 2012 Rosetta Contributors and Canonical Ltd 2012 -# This file is distributed under the same license as the openobject-addons package. -# FIRST AUTHOR , 2012. -# -msgid "" -msgstr "" -"Project-Id-Version: openobject-addons\n" -"Report-Msgid-Bugs-To: FULL NAME \n" -"POT-Creation-Date: 2012-02-08 00:37+0000\n" -"PO-Revision-Date: 2012-01-27 06:33+0000\n" -"Last-Translator: FULL NAME \n" -"Language-Team: Danish \n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=UTF-8\n" -"Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-08-28 06:40+0000\n" -"X-Generator: Launchpad (build 15864)\n" - -#. module: project_mailgate -#: view:project.task:0 -msgid "History Information" -msgstr "" - -#. module: project_mailgate -#: model:ir.model,name:project_mailgate.model_project_task -msgid "Task" -msgstr "" - -#. module: project_mailgate -#: constraint:project.task:0 -msgid "Error ! You cannot create recursive tasks." -msgstr "" - -#. module: project_mailgate -#: field:project.task,message_ids:0 -msgid "Messages" -msgstr "" - -#. module: project_mailgate -#: code:addons/project_mailgate/project_mailgate.py:90 -#, python-format -msgid "Draft" -msgstr "" - -#. module: project_mailgate -#: constraint:project.task:0 -msgid "Error ! Task end-date must be greater then task start-date" -msgstr "" - -#. module: project_mailgate -#: code:addons/project_mailgate/project_mailgate.py:116 -#, python-format -msgid "Cancel" -msgstr "" - -#. module: project_mailgate -#: code:addons/project_mailgate/project_mailgate.py:110 -#, python-format -msgid "Done" -msgstr "" - -#. module: project_mailgate -#: code:addons/project_mailgate/project_mailgate.py:96 -#, python-format -msgid "Open" -msgstr "" - -#. module: project_mailgate -#: code:addons/project_mailgate/project_mailgate.py:102 -#, python-format -msgid "Pending" -msgstr "" - -#. module: project_mailgate -#: view:project.task:0 -msgid "History" -msgstr "" diff --git a/addons/project_mailgate/i18n/de.po b/addons/project_mailgate/i18n/de.po deleted file mode 100644 index a8f8bfa17c6..00000000000 --- a/addons/project_mailgate/i18n/de.po +++ /dev/null @@ -1,106 +0,0 @@ -# German translation for openobject-addons -# Copyright (c) 2010 Rosetta Contributors and Canonical Ltd 2010 -# This file is distributed under the same license as the openobject-addons package. -# FIRST AUTHOR , 2010. -# -msgid "" -msgstr "" -"Project-Id-Version: openobject-addons\n" -"Report-Msgid-Bugs-To: FULL NAME \n" -"POT-Creation-Date: 2012-02-08 00:37+0000\n" -"PO-Revision-Date: 2010-12-31 10:37+0000\n" -"Last-Translator: Thorsten Vocks (OpenBig.org) \n" -"Language-Team: German \n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=UTF-8\n" -"Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-08-28 06:40+0000\n" -"X-Generator: Launchpad (build 15864)\n" - -#. module: project_mailgate -#: view:project.task:0 -msgid "History Information" -msgstr "Information Historie" - -#. module: project_mailgate -#: model:ir.model,name:project_mailgate.model_project_task -msgid "Task" -msgstr "Aufgabe" - -#. module: project_mailgate -#: constraint:project.task:0 -msgid "Error ! You cannot create recursive tasks." -msgstr "Fehler ! Sie können keine rekursiven Aufgaben definieren." - -#. module: project_mailgate -#: field:project.task,message_ids:0 -msgid "Messages" -msgstr "Nachrichten" - -#. module: project_mailgate -#: code:addons/project_mailgate/project_mailgate.py:90 -#, python-format -msgid "Draft" -msgstr "Entwurf" - -#. module: project_mailgate -#: constraint:project.task:0 -msgid "Error ! Task end-date must be greater then task start-date" -msgstr "Fehler! Aufgaben End-Datum muss größer als Aufgaben-Beginn sein" - -#. module: project_mailgate -#: code:addons/project_mailgate/project_mailgate.py:116 -#, python-format -msgid "Cancel" -msgstr "Abbrechen" - -#. module: project_mailgate -#: code:addons/project_mailgate/project_mailgate.py:110 -#, python-format -msgid "Done" -msgstr "Erledigt" - -#. module: project_mailgate -#: code:addons/project_mailgate/project_mailgate.py:96 -#, python-format -msgid "Open" -msgstr "Offen" - -#. module: project_mailgate -#: code:addons/project_mailgate/project_mailgate.py:102 -#, python-format -msgid "Pending" -msgstr "Im Wartezustand" - -#. module: project_mailgate -#: view:project.task:0 -msgid "History" -msgstr "Historie" - -#~ msgid "Project MailGateWay" -#~ msgstr "Projekte Mailgateway" - -#~ msgid "" -#~ "This module is an interface that synchronises mails with OpenERP Project " -#~ "Task.\n" -#~ "\n" -#~ "It allows creating tasks as soon as a new mail arrives in our configured " -#~ "mail server.\n" -#~ "Moreover, it keeps track of all further communications and task states.\n" -#~ " " -#~ msgstr "" -#~ "Dieses Modul ermöglicht die Synchronisation von Email mit openERP " -#~ "Projektaufgaben.\n" -#~ "\n" -#~ "Es ermöglicht die Erstellung einer neuen Aufgabe sobald eine neue EMail von " -#~ "Ihrem Mailserver empfangen wird.\n" -#~ "Ausserdem können Sie dann alle weitere Korrespondenz und Kommunikation " -#~ "rückverfolgen sowie den Status der Aufgabe überwachen.\n" -#~ " " - -#~ msgid "Details" -#~ msgstr "Details" - -#~ msgid "Attachments" -#~ msgstr "Anhänge" diff --git a/addons/project_mailgate/i18n/es.po b/addons/project_mailgate/i18n/es.po deleted file mode 100644 index e5aab3c52b1..00000000000 --- a/addons/project_mailgate/i18n/es.po +++ /dev/null @@ -1,106 +0,0 @@ -# Spanish translation for openobject-addons -# Copyright (c) 2010 Rosetta Contributors and Canonical Ltd 2010 -# This file is distributed under the same license as the openobject-addons package. -# FIRST AUTHOR , 2010. -# -msgid "" -msgstr "" -"Project-Id-Version: openobject-addons\n" -"Report-Msgid-Bugs-To: FULL NAME \n" -"POT-Creation-Date: 2012-02-08 00:37+0000\n" -"PO-Revision-Date: 2010-12-28 08:35+0000\n" -"Last-Translator: Jordi Esteve (www.zikzakmedia.com) " -"\n" -"Language-Team: Spanish \n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=UTF-8\n" -"Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-08-28 06:40+0000\n" -"X-Generator: Launchpad (build 15864)\n" - -#. module: project_mailgate -#: view:project.task:0 -msgid "History Information" -msgstr "Información histórica" - -#. module: project_mailgate -#: model:ir.model,name:project_mailgate.model_project_task -msgid "Task" -msgstr "Tarea" - -#. module: project_mailgate -#: constraint:project.task:0 -msgid "Error ! You cannot create recursive tasks." -msgstr "¡Error! No puede crear tareas recursivas." - -#. module: project_mailgate -#: field:project.task,message_ids:0 -msgid "Messages" -msgstr "Mensajes" - -#. module: project_mailgate -#: code:addons/project_mailgate/project_mailgate.py:90 -#, python-format -msgid "Draft" -msgstr "Borrador" - -#. module: project_mailgate -#: constraint:project.task:0 -msgid "Error ! Task end-date must be greater then task start-date" -msgstr "" -"¡ Error ! La fecha final de la tarea debe ser mayor que la fecha de inicio" - -#. module: project_mailgate -#: code:addons/project_mailgate/project_mailgate.py:116 -#, python-format -msgid "Cancel" -msgstr "Cancelar" - -#. module: project_mailgate -#: code:addons/project_mailgate/project_mailgate.py:110 -#, python-format -msgid "Done" -msgstr "Realizado" - -#. module: project_mailgate -#: code:addons/project_mailgate/project_mailgate.py:96 -#, python-format -msgid "Open" -msgstr "Abrir" - -#. module: project_mailgate -#: code:addons/project_mailgate/project_mailgate.py:102 -#, python-format -msgid "Pending" -msgstr "Pendiente" - -#. module: project_mailgate -#: view:project.task:0 -msgid "History" -msgstr "Historial" - -#~ msgid "Project MailGateWay" -#~ msgstr "Ruta de enlace del proyecto" - -#~ msgid "Details" -#~ msgstr "Detalles" - -#~ msgid "" -#~ "This module is an interface that synchronises mails with OpenERP Project " -#~ "Task.\n" -#~ "\n" -#~ "It allows creating tasks as soon as a new mail arrives in our configured " -#~ "mail server.\n" -#~ "Moreover, it keeps track of all further communications and task states.\n" -#~ " " -#~ msgstr "" -#~ "Este módulo proporciona una interfaz para sincronizar correos con las tareas " -#~ "de proyectos de OpenERP.\n" -#~ "Permite crear tareas tan pronto como llega un nuevo correo en nuestro " -#~ "servidor de correo previamente configurado.\n" -#~ "Además realiza un seguimiento de todas las comunicaciones adicionales y " -#~ "estados de la tarea.\n" -#~ " " - -#~ msgid "Attachments" -#~ msgstr "Adjuntos" diff --git a/addons/project_mailgate/i18n/es_CR.po b/addons/project_mailgate/i18n/es_CR.po deleted file mode 100644 index 4e420fa49fa..00000000000 --- a/addons/project_mailgate/i18n/es_CR.po +++ /dev/null @@ -1,107 +0,0 @@ -# Spanish translation for openobject-addons -# Copyright (c) 2010 Rosetta Contributors and Canonical Ltd 2010 -# This file is distributed under the same license as the openobject-addons package. -# FIRST AUTHOR , 2010. -# -msgid "" -msgstr "" -"Project-Id-Version: openobject-addons\n" -"Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2012-02-08 00:37+0000\n" -"PO-Revision-Date: 2012-02-17 00:30+0000\n" -"Last-Translator: Carlos Vásquez (CLEARCORP) " -"\n" -"Language-Team: Spanish \n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=UTF-8\n" -"Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-08-28 06:40+0000\n" -"X-Generator: Launchpad (build 15864)\n" -"Language: es\n" - -#. module: project_mailgate -#: view:project.task:0 -msgid "History Information" -msgstr "Información histórica" - -#. module: project_mailgate -#: model:ir.model,name:project_mailgate.model_project_task -msgid "Task" -msgstr "Tarea" - -#. module: project_mailgate -#: constraint:project.task:0 -msgid "Error ! You cannot create recursive tasks." -msgstr "¡Error! No puede crear tareas recursivas." - -#. module: project_mailgate -#: field:project.task,message_ids:0 -msgid "Messages" -msgstr "Mensajes" - -#. module: project_mailgate -#: code:addons/project_mailgate/project_mailgate.py:90 -#, python-format -msgid "Draft" -msgstr "Borrador" - -#. module: project_mailgate -#: constraint:project.task:0 -msgid "Error ! Task end-date must be greater then task start-date" -msgstr "" -"¡ Error ! La fecha final de la tarea debe ser mayor que la fecha de inicio" - -#. module: project_mailgate -#: code:addons/project_mailgate/project_mailgate.py:116 -#, python-format -msgid "Cancel" -msgstr "Cancelar" - -#. module: project_mailgate -#: code:addons/project_mailgate/project_mailgate.py:110 -#, python-format -msgid "Done" -msgstr "Realizado" - -#. module: project_mailgate -#: code:addons/project_mailgate/project_mailgate.py:96 -#, python-format -msgid "Open" -msgstr "Abrir" - -#. module: project_mailgate -#: code:addons/project_mailgate/project_mailgate.py:102 -#, python-format -msgid "Pending" -msgstr "Pendiente" - -#. module: project_mailgate -#: view:project.task:0 -msgid "History" -msgstr "Historial" - -#~ msgid "Project MailGateWay" -#~ msgstr "Ruta de enlace del proyecto" - -#~ msgid "Details" -#~ msgstr "Detalles" - -#~ msgid "" -#~ "This module is an interface that synchronises mails with OpenERP Project " -#~ "Task.\n" -#~ "\n" -#~ "It allows creating tasks as soon as a new mail arrives in our configured " -#~ "mail server.\n" -#~ "Moreover, it keeps track of all further communications and task states.\n" -#~ " " -#~ msgstr "" -#~ "Este módulo proporciona una interfaz para sincronizar correos con las tareas " -#~ "de proyectos de OpenERP.\n" -#~ "Permite crear tareas tan pronto como llega un nuevo correo en nuestro " -#~ "servidor de correo previamente configurado.\n" -#~ "Además realiza un seguimiento de todas las comunicaciones adicionales y " -#~ "estados de la tarea.\n" -#~ " " - -#~ msgid "Attachments" -#~ msgstr "Adjuntos" diff --git a/addons/project_mailgate/i18n/es_MX.po b/addons/project_mailgate/i18n/es_MX.po deleted file mode 100644 index 2736d2572ac..00000000000 --- a/addons/project_mailgate/i18n/es_MX.po +++ /dev/null @@ -1,108 +0,0 @@ -# Spanish translation for openobject-addons -# Copyright (c) 2010 Rosetta Contributors and Canonical Ltd 2010 -# This file is distributed under the same license as the openobject-addons package. -# FIRST AUTHOR , 2010. -# -msgid "" -msgstr "" -"Project-Id-Version: openobject-addons\n" -"Report-Msgid-Bugs-To: FULL NAME \n" -"POT-Creation-Date: 2011-01-11 11:15+0000\n" -"PO-Revision-Date: 2010-12-28 08:35+0000\n" -"Last-Translator: Jordi Esteve (www.zikzakmedia.com) " -"\n" -"Language-Team: Spanish \n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=UTF-8\n" -"Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2011-09-05 05:56+0000\n" -"X-Generator: Launchpad (build 13830)\n" - -#. module: project_mailgate -#: view:project.task:0 -msgid "History Information" -msgstr "Información histórica" - -#. module: project_mailgate -#: model:ir.model,name:project_mailgate.model_project_task -msgid "Task" -msgstr "Tarea" - -#. module: project_mailgate -#: model:ir.module.module,description:project_mailgate.module_meta_information -msgid "" -"This module is an interface that synchronises mails with OpenERP Project " -"Task.\n" -"\n" -"It allows creating tasks as soon as a new mail arrives in our configured " -"mail server.\n" -"Moreover, it keeps track of all further communications and task states.\n" -" " -msgstr "" -"Este módulo proporciona una interfaz para sincronizar correos con las tareas " -"de proyectos de OpenERP.\n" -"Permite crear tareas tan pronto como llega un nuevo correo en nuestro " -"servidor de correo previamente configurado.\n" -"Además realiza un seguimiento de todas las comunicaciones adicionales y " -"estados de la tarea.\n" -" " - -#. module: project_mailgate -#: view:project.task:0 -msgid "Attachments" -msgstr "Adjuntos" - -#. module: project_mailgate -#: model:ir.module.module,shortdesc:project_mailgate.module_meta_information -msgid "Project MailGateWay" -msgstr "Ruta de enlace del proyecto" - -#. module: project_mailgate -#: constraint:project.task:0 -msgid "Error ! You cannot create recursive tasks." -msgstr "¡Error! No puede crear tareas recursivas." - -#. module: project_mailgate -#: field:project.task,message_ids:0 -msgid "Messages" -msgstr "Mensajes" - -#. module: project_mailgate -#: code:addons/project_mailgate/project_mailgate.py:123 -#, python-format -msgid "Draft" -msgstr "Borrador" - -#. module: project_mailgate -#: view:project.task:0 -msgid "Details" -msgstr "Detalles" - -#. module: project_mailgate -#: code:addons/project_mailgate/project_mailgate.py:149 -#, python-format -msgid "Cancel" -msgstr "Cancelar" - -#. module: project_mailgate -#: code:addons/project_mailgate/project_mailgate.py:143 -#, python-format -msgid "Done" -msgstr "Realizado" - -#. module: project_mailgate -#: code:addons/project_mailgate/project_mailgate.py:129 -#, python-format -msgid "Open" -msgstr "Abrir" - -#. module: project_mailgate -#: code:addons/project_mailgate/project_mailgate.py:135 -#, python-format -msgid "Pending" -msgstr "Pendiente" - -#. module: project_mailgate -#: view:project.task:0 -msgid "History" -msgstr "Historial" diff --git a/addons/project_mailgate/i18n/es_VE.po b/addons/project_mailgate/i18n/es_VE.po deleted file mode 100644 index 2736d2572ac..00000000000 --- a/addons/project_mailgate/i18n/es_VE.po +++ /dev/null @@ -1,108 +0,0 @@ -# Spanish translation for openobject-addons -# Copyright (c) 2010 Rosetta Contributors and Canonical Ltd 2010 -# This file is distributed under the same license as the openobject-addons package. -# FIRST AUTHOR , 2010. -# -msgid "" -msgstr "" -"Project-Id-Version: openobject-addons\n" -"Report-Msgid-Bugs-To: FULL NAME \n" -"POT-Creation-Date: 2011-01-11 11:15+0000\n" -"PO-Revision-Date: 2010-12-28 08:35+0000\n" -"Last-Translator: Jordi Esteve (www.zikzakmedia.com) " -"\n" -"Language-Team: Spanish \n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=UTF-8\n" -"Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2011-09-05 05:56+0000\n" -"X-Generator: Launchpad (build 13830)\n" - -#. module: project_mailgate -#: view:project.task:0 -msgid "History Information" -msgstr "Información histórica" - -#. module: project_mailgate -#: model:ir.model,name:project_mailgate.model_project_task -msgid "Task" -msgstr "Tarea" - -#. module: project_mailgate -#: model:ir.module.module,description:project_mailgate.module_meta_information -msgid "" -"This module is an interface that synchronises mails with OpenERP Project " -"Task.\n" -"\n" -"It allows creating tasks as soon as a new mail arrives in our configured " -"mail server.\n" -"Moreover, it keeps track of all further communications and task states.\n" -" " -msgstr "" -"Este módulo proporciona una interfaz para sincronizar correos con las tareas " -"de proyectos de OpenERP.\n" -"Permite crear tareas tan pronto como llega un nuevo correo en nuestro " -"servidor de correo previamente configurado.\n" -"Además realiza un seguimiento de todas las comunicaciones adicionales y " -"estados de la tarea.\n" -" " - -#. module: project_mailgate -#: view:project.task:0 -msgid "Attachments" -msgstr "Adjuntos" - -#. module: project_mailgate -#: model:ir.module.module,shortdesc:project_mailgate.module_meta_information -msgid "Project MailGateWay" -msgstr "Ruta de enlace del proyecto" - -#. module: project_mailgate -#: constraint:project.task:0 -msgid "Error ! You cannot create recursive tasks." -msgstr "¡Error! No puede crear tareas recursivas." - -#. module: project_mailgate -#: field:project.task,message_ids:0 -msgid "Messages" -msgstr "Mensajes" - -#. module: project_mailgate -#: code:addons/project_mailgate/project_mailgate.py:123 -#, python-format -msgid "Draft" -msgstr "Borrador" - -#. module: project_mailgate -#: view:project.task:0 -msgid "Details" -msgstr "Detalles" - -#. module: project_mailgate -#: code:addons/project_mailgate/project_mailgate.py:149 -#, python-format -msgid "Cancel" -msgstr "Cancelar" - -#. module: project_mailgate -#: code:addons/project_mailgate/project_mailgate.py:143 -#, python-format -msgid "Done" -msgstr "Realizado" - -#. module: project_mailgate -#: code:addons/project_mailgate/project_mailgate.py:129 -#, python-format -msgid "Open" -msgstr "Abrir" - -#. module: project_mailgate -#: code:addons/project_mailgate/project_mailgate.py:135 -#, python-format -msgid "Pending" -msgstr "Pendiente" - -#. module: project_mailgate -#: view:project.task:0 -msgid "History" -msgstr "Historial" diff --git a/addons/project_mailgate/i18n/fi.po b/addons/project_mailgate/i18n/fi.po deleted file mode 100644 index 76677ae237a..00000000000 --- a/addons/project_mailgate/i18n/fi.po +++ /dev/null @@ -1,88 +0,0 @@ -# Finnish translation for openobject-addons -# Copyright (c) 2011 Rosetta Contributors and Canonical Ltd 2011 -# This file is distributed under the same license as the openobject-addons package. -# FIRST AUTHOR , 2011. -# -msgid "" -msgstr "" -"Project-Id-Version: openobject-addons\n" -"Report-Msgid-Bugs-To: FULL NAME \n" -"POT-Creation-Date: 2012-02-08 00:37+0000\n" -"PO-Revision-Date: 2011-06-22 10:01+0000\n" -"Last-Translator: FULL NAME \n" -"Language-Team: Finnish \n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=UTF-8\n" -"Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-08-28 06:40+0000\n" -"X-Generator: Launchpad (build 15864)\n" - -#. module: project_mailgate -#: view:project.task:0 -msgid "History Information" -msgstr "Historiatiedot" - -#. module: project_mailgate -#: model:ir.model,name:project_mailgate.model_project_task -msgid "Task" -msgstr "Tehtävä" - -#. module: project_mailgate -#: constraint:project.task:0 -msgid "Error ! You cannot create recursive tasks." -msgstr "Virhe ! Et voi luoda rekursiivisiä tehtäviä." - -#. module: project_mailgate -#: field:project.task,message_ids:0 -msgid "Messages" -msgstr "Viestit" - -#. module: project_mailgate -#: code:addons/project_mailgate/project_mailgate.py:90 -#, python-format -msgid "Draft" -msgstr "Luonnos" - -#. module: project_mailgate -#: constraint:project.task:0 -msgid "Error ! Task end-date must be greater then task start-date" -msgstr "" -"Virhe! Tehtävän lopetuspäivän tulee olla myöhäisempi kuin aloituspäivä" - -#. module: project_mailgate -#: code:addons/project_mailgate/project_mailgate.py:116 -#, python-format -msgid "Cancel" -msgstr "Peruuta" - -#. module: project_mailgate -#: code:addons/project_mailgate/project_mailgate.py:110 -#, python-format -msgid "Done" -msgstr "Valmis" - -#. module: project_mailgate -#: code:addons/project_mailgate/project_mailgate.py:96 -#, python-format -msgid "Open" -msgstr "Auki" - -#. module: project_mailgate -#: code:addons/project_mailgate/project_mailgate.py:102 -#, python-format -msgid "Pending" -msgstr "Odottava" - -#. module: project_mailgate -#: view:project.task:0 -msgid "History" -msgstr "Historia" - -#~ msgid "Project MailGateWay" -#~ msgstr "Projektin sähköpostivälittäjä" - -#~ msgid "Attachments" -#~ msgstr "Liitteet" - -#~ msgid "Details" -#~ msgstr "Yksityiskohdat" diff --git a/addons/project_mailgate/i18n/fr.po b/addons/project_mailgate/i18n/fr.po deleted file mode 100644 index e763eca65a3..00000000000 --- a/addons/project_mailgate/i18n/fr.po +++ /dev/null @@ -1,106 +0,0 @@ -# French translation for openobject-addons -# Copyright (c) 2011 Rosetta Contributors and Canonical Ltd 2011 -# This file is distributed under the same license as the openobject-addons package. -# FIRST AUTHOR , 2011. -# -msgid "" -msgstr "" -"Project-Id-Version: openobject-addons\n" -"Report-Msgid-Bugs-To: FULL NAME \n" -"POT-Creation-Date: 2012-02-08 00:37+0000\n" -"PO-Revision-Date: 2011-01-13 23:14+0000\n" -"Last-Translator: lholivier \n" -"Language-Team: French \n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=UTF-8\n" -"Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-08-28 06:40+0000\n" -"X-Generator: Launchpad (build 15864)\n" - -#. module: project_mailgate -#: view:project.task:0 -msgid "History Information" -msgstr "Historique" - -#. module: project_mailgate -#: model:ir.model,name:project_mailgate.model_project_task -msgid "Task" -msgstr "Tâche" - -#. module: project_mailgate -#: constraint:project.task:0 -msgid "Error ! You cannot create recursive tasks." -msgstr "Erreur ! Vous ne pouvez pas créer de tâches récursives." - -#. module: project_mailgate -#: field:project.task,message_ids:0 -msgid "Messages" -msgstr "Messages" - -#. module: project_mailgate -#: code:addons/project_mailgate/project_mailgate.py:90 -#, python-format -msgid "Draft" -msgstr "Brouillon" - -#. module: project_mailgate -#: constraint:project.task:0 -msgid "Error ! Task end-date must be greater then task start-date" -msgstr "" -"Erreur ! La date de fin de la tâche doit être postérieure à la date de " -"démarrage" - -#. module: project_mailgate -#: code:addons/project_mailgate/project_mailgate.py:116 -#, python-format -msgid "Cancel" -msgstr "Annuler" - -#. module: project_mailgate -#: code:addons/project_mailgate/project_mailgate.py:110 -#, python-format -msgid "Done" -msgstr "Terminé" - -#. module: project_mailgate -#: code:addons/project_mailgate/project_mailgate.py:96 -#, python-format -msgid "Open" -msgstr "Ouvert" - -#. module: project_mailgate -#: code:addons/project_mailgate/project_mailgate.py:102 -#, python-format -msgid "Pending" -msgstr "En attente" - -#. module: project_mailgate -#: view:project.task:0 -msgid "History" -msgstr "Historique" - -#~ msgid "Attachments" -#~ msgstr "Pièces jointes" - -#~ msgid "Details" -#~ msgstr "Détails" - -#~ msgid "Project MailGateWay" -#~ msgstr "Messagerie de Projet" - -#~ msgid "" -#~ "This module is an interface that synchronises mails with OpenERP Project " -#~ "Task.\n" -#~ "\n" -#~ "It allows creating tasks as soon as a new mail arrives in our configured " -#~ "mail server.\n" -#~ "Moreover, it keeps track of all further communications and task states.\n" -#~ " " -#~ msgstr "" -#~ "Ce module est un interface de synchronisation des emails avec la gestion de " -#~ "projet d'OpenERP.\n" -#~ "\n" -#~ "Il permet la création de tâches sur l'arrivée de nouveaux emails dans le " -#~ "serveur de messagerie configuré.\n" -#~ "De plus, il conserve les échanges ultérieurs et l'état des tâches.\n" -#~ " " diff --git a/addons/project_mailgate/i18n/gl.po b/addons/project_mailgate/i18n/gl.po deleted file mode 100644 index 0e7334b1116..00000000000 --- a/addons/project_mailgate/i18n/gl.po +++ /dev/null @@ -1,105 +0,0 @@ -# Galician translation for openobject-addons -# Copyright (c) 2011 Rosetta Contributors and Canonical Ltd 2011 -# This file is distributed under the same license as the openobject-addons package. -# FIRST AUTHOR , 2011. -# -msgid "" -msgstr "" -"Project-Id-Version: openobject-addons\n" -"Report-Msgid-Bugs-To: FULL NAME \n" -"POT-Creation-Date: 2012-02-08 00:37+0000\n" -"PO-Revision-Date: 2011-05-10 08:39+0000\n" -"Last-Translator: FULL NAME \n" -"Language-Team: Galician \n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=UTF-8\n" -"Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-08-28 06:40+0000\n" -"X-Generator: Launchpad (build 15864)\n" - -#. module: project_mailgate -#: view:project.task:0 -msgid "History Information" -msgstr "Información histórica" - -#. module: project_mailgate -#: model:ir.model,name:project_mailgate.model_project_task -msgid "Task" -msgstr "Tarefa" - -#. module: project_mailgate -#: constraint:project.task:0 -msgid "Error ! You cannot create recursive tasks." -msgstr "Erro! Non pode crear tarefas recorrentes." - -#. module: project_mailgate -#: field:project.task,message_ids:0 -msgid "Messages" -msgstr "Mensaxes" - -#. module: project_mailgate -#: code:addons/project_mailgate/project_mailgate.py:90 -#, python-format -msgid "Draft" -msgstr "Borrador" - -#. module: project_mailgate -#: constraint:project.task:0 -msgid "Error ! Task end-date must be greater then task start-date" -msgstr "" -"Erro! A data de remate da tarefa debe ser posterior á data de inicio da " -"tarefa" - -#. module: project_mailgate -#: code:addons/project_mailgate/project_mailgate.py:116 -#, python-format -msgid "Cancel" -msgstr "Anular" - -#. module: project_mailgate -#: code:addons/project_mailgate/project_mailgate.py:110 -#, python-format -msgid "Done" -msgstr "Feito" - -#. module: project_mailgate -#: code:addons/project_mailgate/project_mailgate.py:96 -#, python-format -msgid "Open" -msgstr "Abrir" - -#. module: project_mailgate -#: code:addons/project_mailgate/project_mailgate.py:102 -#, python-format -msgid "Pending" -msgstr "Pendente" - -#. module: project_mailgate -#: view:project.task:0 -msgid "History" -msgstr "Historial" - -#~ msgid "Project MailGateWay" -#~ msgstr "Ruta de enlace do proxecto" - -#~ msgid "" -#~ "This module is an interface that synchronises mails with OpenERP Project " -#~ "Task.\n" -#~ "\n" -#~ "It allows creating tasks as soon as a new mail arrives in our configured " -#~ "mail server.\n" -#~ "Moreover, it keeps track of all further communications and task states.\n" -#~ " " -#~ msgstr "" -#~ "Este módulo proporciona unha interface para sincronizar correos coas tarefas " -#~ "de proxectos de OpenERP. Permite crear tarefas tan pronto como chegue un " -#~ "novo correo no noso servidor de correo previamente configurado. Ademais " -#~ "realiza un seguimento de tódalas comunicacións adicionais e dos estados da " -#~ "tarefa.\n" -#~ " " - -#~ msgid "Attachments" -#~ msgstr "Anexos" - -#~ msgid "Details" -#~ msgstr "Detalles" diff --git a/addons/project_mailgate/i18n/hr.po b/addons/project_mailgate/i18n/hr.po deleted file mode 100644 index 15952d948a1..00000000000 --- a/addons/project_mailgate/i18n/hr.po +++ /dev/null @@ -1,104 +0,0 @@ -# Croatian translation for openobject-addons -# Copyright (c) 2011 Rosetta Contributors and Canonical Ltd 2011 -# This file is distributed under the same license as the openobject-addons package. -# FIRST AUTHOR , 2011. -# -msgid "" -msgstr "" -"Project-Id-Version: openobject-addons\n" -"Report-Msgid-Bugs-To: FULL NAME \n" -"POT-Creation-Date: 2012-02-08 00:37+0000\n" -"PO-Revision-Date: 2011-12-12 08:56+0000\n" -"Last-Translator: Tomislav Bosnjakovic \n" -"Language-Team: Croatian \n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=UTF-8\n" -"Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-08-28 06:40+0000\n" -"X-Generator: Launchpad (build 15864)\n" - -#. module: project_mailgate -#: view:project.task:0 -msgid "History Information" -msgstr "Informacije o povjesti" - -#. module: project_mailgate -#: model:ir.model,name:project_mailgate.model_project_task -msgid "Task" -msgstr "Zadatak" - -#. module: project_mailgate -#: constraint:project.task:0 -msgid "Error ! You cannot create recursive tasks." -msgstr "Greška! Ne možete kreirati rekurzivne zadatke." - -#. module: project_mailgate -#: field:project.task,message_ids:0 -msgid "Messages" -msgstr "Poruke" - -#. module: project_mailgate -#: code:addons/project_mailgate/project_mailgate.py:90 -#, python-format -msgid "Draft" -msgstr "Nacrt" - -#. module: project_mailgate -#: constraint:project.task:0 -msgid "Error ! Task end-date must be greater then task start-date" -msgstr "" - -#. module: project_mailgate -#: code:addons/project_mailgate/project_mailgate.py:116 -#, python-format -msgid "Cancel" -msgstr "Odustani" - -#. module: project_mailgate -#: code:addons/project_mailgate/project_mailgate.py:110 -#, python-format -msgid "Done" -msgstr "Izvršeno" - -#. module: project_mailgate -#: code:addons/project_mailgate/project_mailgate.py:96 -#, python-format -msgid "Open" -msgstr "Otvoreno" - -#. module: project_mailgate -#: code:addons/project_mailgate/project_mailgate.py:102 -#, python-format -msgid "Pending" -msgstr "Na čekanju" - -#. module: project_mailgate -#: view:project.task:0 -msgid "History" -msgstr "Povijest" - -#~ msgid "Project MailGateWay" -#~ msgstr "MailGateWay projekta" - -#~ msgid "" -#~ "This module is an interface that synchronises mails with OpenERP Project " -#~ "Task.\n" -#~ "\n" -#~ "It allows creating tasks as soon as a new mail arrives in our configured " -#~ "mail server.\n" -#~ "Moreover, it keeps track of all further communications and task states.\n" -#~ " " -#~ msgstr "" -#~ "Ovaj modul je sučelje za sinhronizaciju poruka sa OpenERP projektnim " -#~ "zadacima.\n" -#~ "\n" -#~ "Omogućava kreiranje zadataka čim stigne novi e-mail na naš konfigurirani " -#~ "mail poslužitelj.\n" -#~ "Štoviše, čuva zapise o svim kasnijim komunikacijama i statusima zadataka.\n" -#~ " " - -#~ msgid "Attachments" -#~ msgstr "Privici" - -#~ msgid "Details" -#~ msgstr "Pojedinosti" diff --git a/addons/project_mailgate/i18n/hu.po b/addons/project_mailgate/i18n/hu.po deleted file mode 100644 index b1d551f476e..00000000000 --- a/addons/project_mailgate/i18n/hu.po +++ /dev/null @@ -1,84 +0,0 @@ -# Translation of OpenERP Server. -# This file contains the translation of the following modules: -# * project_mailgate -# -msgid "" -msgstr "" -"Project-Id-Version: OpenERP Server 6.0dev\n" -"Report-Msgid-Bugs-To: support@openerp.com\n" -"POT-Creation-Date: 2012-02-08 00:37+0000\n" -"PO-Revision-Date: 2011-01-25 13:21+0000\n" -"Last-Translator: NOVOTRADE RENDSZERHÁZ ( novotrade.hu ) " -"\n" -"Language-Team: \n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=utf-8\n" -"Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-08-28 06:40+0000\n" -"X-Generator: Launchpad (build 15864)\n" - -#. module: project_mailgate -#: view:project.task:0 -msgid "History Information" -msgstr "Előzmény" - -#. module: project_mailgate -#: model:ir.model,name:project_mailgate.model_project_task -msgid "Task" -msgstr "Feladat" - -#. module: project_mailgate -#: constraint:project.task:0 -msgid "Error ! You cannot create recursive tasks." -msgstr "Hiba! Nem hozhat létre rekurzív feladatokat." - -#. module: project_mailgate -#: field:project.task,message_ids:0 -msgid "Messages" -msgstr "Üzenetek" - -#. module: project_mailgate -#: code:addons/project_mailgate/project_mailgate.py:90 -#, python-format -msgid "Draft" -msgstr "Tervezet" - -#. module: project_mailgate -#: constraint:project.task:0 -msgid "Error ! Task end-date must be greater then task start-date" -msgstr "" - -#. module: project_mailgate -#: code:addons/project_mailgate/project_mailgate.py:116 -#, python-format -msgid "Cancel" -msgstr "Mégsem" - -#. module: project_mailgate -#: code:addons/project_mailgate/project_mailgate.py:110 -#, python-format -msgid "Done" -msgstr "Kész" - -#. module: project_mailgate -#: code:addons/project_mailgate/project_mailgate.py:96 -#, python-format -msgid "Open" -msgstr "Nyitott" - -#. module: project_mailgate -#: code:addons/project_mailgate/project_mailgate.py:102 -#, python-format -msgid "Pending" -msgstr "Függőben lévő" - -#. module: project_mailgate -#: view:project.task:0 -msgid "History" -msgstr "Előzmény" - -#~ msgid "Attachments" -#~ msgstr "Mellékletek" - -#~ msgid "Details" -#~ msgstr "Részletek" diff --git a/addons/project_mailgate/i18n/it.po b/addons/project_mailgate/i18n/it.po deleted file mode 100644 index a6d56020918..00000000000 --- a/addons/project_mailgate/i18n/it.po +++ /dev/null @@ -1,106 +0,0 @@ -# Italian translation for openobject-addons -# Copyright (c) 2011 Rosetta Contributors and Canonical Ltd 2011 -# This file is distributed under the same license as the openobject-addons package. -# FIRST AUTHOR , 2011. -# -msgid "" -msgstr "" -"Project-Id-Version: openobject-addons\n" -"Report-Msgid-Bugs-To: FULL NAME \n" -"POT-Creation-Date: 2012-02-08 00:37+0000\n" -"PO-Revision-Date: 2011-04-22 09:54+0000\n" -"Last-Translator: simone.sandri \n" -"Language-Team: Italian \n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=UTF-8\n" -"Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-08-28 06:40+0000\n" -"X-Generator: Launchpad (build 15864)\n" - -#. module: project_mailgate -#: view:project.task:0 -msgid "History Information" -msgstr "Storico informazioni" - -#. module: project_mailgate -#: model:ir.model,name:project_mailgate.model_project_task -msgid "Task" -msgstr "Attività" - -#. module: project_mailgate -#: constraint:project.task:0 -msgid "Error ! You cannot create recursive tasks." -msgstr "Errore ! Non è possibile creare attività ricorsive." - -#. module: project_mailgate -#: field:project.task,message_ids:0 -msgid "Messages" -msgstr "Messaggi" - -#. module: project_mailgate -#: code:addons/project_mailgate/project_mailgate.py:90 -#, python-format -msgid "Draft" -msgstr "Bozza" - -#. module: project_mailgate -#: constraint:project.task:0 -msgid "Error ! Task end-date must be greater then task start-date" -msgstr "" -"Errore ! La data finale della mansione deve essere più vecchia di quella " -"iniziale" - -#. module: project_mailgate -#: code:addons/project_mailgate/project_mailgate.py:116 -#, python-format -msgid "Cancel" -msgstr "Annulla" - -#. module: project_mailgate -#: code:addons/project_mailgate/project_mailgate.py:110 -#, python-format -msgid "Done" -msgstr "Completato" - -#. module: project_mailgate -#: code:addons/project_mailgate/project_mailgate.py:96 -#, python-format -msgid "Open" -msgstr "Apri" - -#. module: project_mailgate -#: code:addons/project_mailgate/project_mailgate.py:102 -#, python-format -msgid "Pending" -msgstr "In sospeso" - -#. module: project_mailgate -#: view:project.task:0 -msgid "History" -msgstr "Storico" - -#~ msgid "Attachments" -#~ msgstr "Allegati" - -#~ msgid "Details" -#~ msgstr "Dettagli" - -#~ msgid "" -#~ "This module is an interface that synchronises mails with OpenERP Project " -#~ "Task.\n" -#~ "\n" -#~ "It allows creating tasks as soon as a new mail arrives in our configured " -#~ "mail server.\n" -#~ "Moreover, it keeps track of all further communications and task states.\n" -#~ " " -#~ msgstr "" -#~ "Questo modulo è una infertaccia che sincronizza mail con le attività di " -#~ "progetto di OpenERP.\n" -#~ "\n" -#~ "Esso permette di creare attività non appena una nuova mail arriva nel mail " -#~ "server configurato.\n" -#~ "Inoltre, tiene traccia di tutte le comunicazioni e lo stato delle attività.\n" -#~ " " - -#~ msgid "Project MailGateWay" -#~ msgstr "Ingresso Mail di Progetto" diff --git a/addons/project_mailgate/i18n/ja.po b/addons/project_mailgate/i18n/ja.po deleted file mode 100644 index 2370edbec6d..00000000000 --- a/addons/project_mailgate/i18n/ja.po +++ /dev/null @@ -1,78 +0,0 @@ -# Japanese translation for openobject-addons -# Copyright (c) 2012 Rosetta Contributors and Canonical Ltd 2012 -# This file is distributed under the same license as the openobject-addons package. -# FIRST AUTHOR , 2012. -# -msgid "" -msgstr "" -"Project-Id-Version: openobject-addons\n" -"Report-Msgid-Bugs-To: FULL NAME \n" -"POT-Creation-Date: 2012-02-08 00:37+0000\n" -"PO-Revision-Date: 2012-06-10 03:13+0000\n" -"Last-Translator: Akira Hiyama \n" -"Language-Team: Japanese \n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=UTF-8\n" -"Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-08-28 06:40+0000\n" -"X-Generator: Launchpad (build 15864)\n" - -#. module: project_mailgate -#: view:project.task:0 -msgid "History Information" -msgstr "履歴情報" - -#. module: project_mailgate -#: model:ir.model,name:project_mailgate.model_project_task -msgid "Task" -msgstr "タスク" - -#. module: project_mailgate -#: constraint:project.task:0 -msgid "Error ! You cannot create recursive tasks." -msgstr "エラー。再帰的なタスクを作成することはできません。" - -#. module: project_mailgate -#: field:project.task,message_ids:0 -msgid "Messages" -msgstr "メッセージ" - -#. module: project_mailgate -#: code:addons/project_mailgate/project_mailgate.py:90 -#, python-format -msgid "Draft" -msgstr "ドラフト" - -#. module: project_mailgate -#: constraint:project.task:0 -msgid "Error ! Task end-date must be greater then task start-date" -msgstr "エラー。タスクの終了日は開始日以降の日付に設定しなければなりません。" - -#. module: project_mailgate -#: code:addons/project_mailgate/project_mailgate.py:116 -#, python-format -msgid "Cancel" -msgstr "キャンセル" - -#. module: project_mailgate -#: code:addons/project_mailgate/project_mailgate.py:110 -#, python-format -msgid "Done" -msgstr "完了" - -#. module: project_mailgate -#: code:addons/project_mailgate/project_mailgate.py:96 -#, python-format -msgid "Open" -msgstr "開く" - -#. module: project_mailgate -#: code:addons/project_mailgate/project_mailgate.py:102 -#, python-format -msgid "Pending" -msgstr "保留中" - -#. module: project_mailgate -#: view:project.task:0 -msgid "History" -msgstr "履歴" diff --git a/addons/project_mailgate/i18n/lv.po b/addons/project_mailgate/i18n/lv.po deleted file mode 100644 index 4bb1683a2f2..00000000000 --- a/addons/project_mailgate/i18n/lv.po +++ /dev/null @@ -1,87 +0,0 @@ -# Latvian translation for openobject-addons -# Copyright (c) 2010 Rosetta Contributors and Canonical Ltd 2010 -# This file is distributed under the same license as the openobject-addons package. -# FIRST AUTHOR , 2010. -# -msgid "" -msgstr "" -"Project-Id-Version: openobject-addons\n" -"Report-Msgid-Bugs-To: FULL NAME \n" -"POT-Creation-Date: 2012-02-08 00:37+0000\n" -"PO-Revision-Date: 2010-12-31 10:38+0000\n" -"Last-Translator: OpenERP Administrators \n" -"Language-Team: Latvian \n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=UTF-8\n" -"Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-08-28 06:40+0000\n" -"X-Generator: Launchpad (build 15864)\n" - -#. module: project_mailgate -#: view:project.task:0 -msgid "History Information" -msgstr "Vēstures informācija" - -#. module: project_mailgate -#: model:ir.model,name:project_mailgate.model_project_task -msgid "Task" -msgstr "Uzdevums" - -#. module: project_mailgate -#: constraint:project.task:0 -msgid "Error ! You cannot create recursive tasks." -msgstr "Kļūda! Nedrīkst veidot rekursīvus uzdevumus." - -#. module: project_mailgate -#: field:project.task,message_ids:0 -msgid "Messages" -msgstr "Ziņojumi" - -#. module: project_mailgate -#: code:addons/project_mailgate/project_mailgate.py:90 -#, python-format -msgid "Draft" -msgstr "Melnraksts" - -#. module: project_mailgate -#: constraint:project.task:0 -msgid "Error ! Task end-date must be greater then task start-date" -msgstr "" - -#. module: project_mailgate -#: code:addons/project_mailgate/project_mailgate.py:116 -#, python-format -msgid "Cancel" -msgstr "Atcelt" - -#. module: project_mailgate -#: code:addons/project_mailgate/project_mailgate.py:110 -#, python-format -msgid "Done" -msgstr "Pabeigts" - -#. module: project_mailgate -#: code:addons/project_mailgate/project_mailgate.py:96 -#, python-format -msgid "Open" -msgstr "Atvērt" - -#. module: project_mailgate -#: code:addons/project_mailgate/project_mailgate.py:102 -#, python-format -msgid "Pending" -msgstr "Neizlemts" - -#. module: project_mailgate -#: view:project.task:0 -msgid "History" -msgstr "Vēsture" - -#~ msgid "Project MailGateWay" -#~ msgstr "Projekta MailGateWay" - -#~ msgid "Attachments" -#~ msgstr "Piesaistnes" - -#~ msgid "Details" -#~ msgstr "Sīkāka informācija" diff --git a/addons/project_mailgate/i18n/mn.po b/addons/project_mailgate/i18n/mn.po deleted file mode 100644 index 157b726d3bd..00000000000 --- a/addons/project_mailgate/i18n/mn.po +++ /dev/null @@ -1,85 +0,0 @@ -# Mongolian translation for openobject-addons -# Copyright (c) 2010 Rosetta Contributors and Canonical Ltd 2010 -# This file is distributed under the same license as the openobject-addons package. -# FIRST AUTHOR , 2010. -# -msgid "" -msgstr "" -"Project-Id-Version: openobject-addons\n" -"Report-Msgid-Bugs-To: FULL NAME \n" -"POT-Creation-Date: 2012-02-08 00:37+0000\n" -"PO-Revision-Date: 2010-12-21 13:53+0000\n" -"Last-Translator: OpenERP Administrators \n" -"Language-Team: Mongolian \n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=UTF-8\n" -"Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-08-28 06:40+0000\n" -"X-Generator: Launchpad (build 15864)\n" - -#. module: project_mailgate -#: view:project.task:0 -msgid "History Information" -msgstr "Түүх" - -#. module: project_mailgate -#: model:ir.model,name:project_mailgate.model_project_task -msgid "Task" -msgstr "Ажил" - -#. module: project_mailgate -#: constraint:project.task:0 -msgid "Error ! You cannot create recursive tasks." -msgstr "Алдаа ! Та рекурсив цэс үүсгэж болохгүй!" - -#. module: project_mailgate -#: field:project.task,message_ids:0 -msgid "Messages" -msgstr "Мессежүүд" - -#. module: project_mailgate -#: code:addons/project_mailgate/project_mailgate.py:90 -#, python-format -msgid "Draft" -msgstr "Ноорог" - -#. module: project_mailgate -#: constraint:project.task:0 -msgid "Error ! Task end-date must be greater then task start-date" -msgstr "" -"Алдаа ! Даалгаврын дуусах хугацаа нь эхлэх хугацаанаасаа хойно байх ёстой" - -#. module: project_mailgate -#: code:addons/project_mailgate/project_mailgate.py:116 -#, python-format -msgid "Cancel" -msgstr "Болих" - -#. module: project_mailgate -#: code:addons/project_mailgate/project_mailgate.py:110 -#, python-format -msgid "Done" -msgstr "Хийсэн" - -#. module: project_mailgate -#: code:addons/project_mailgate/project_mailgate.py:96 -#, python-format -msgid "Open" -msgstr "Нээх" - -#. module: project_mailgate -#: code:addons/project_mailgate/project_mailgate.py:102 -#, python-format -msgid "Pending" -msgstr "Хүлээгдэж буй" - -#. module: project_mailgate -#: view:project.task:0 -msgid "History" -msgstr "Түүх" - -#~ msgid "Project MailGateWay" -#~ msgstr "MailGateWay Төсөл" - -#~ msgid "Details" -#~ msgstr "Нарийвчлал" diff --git a/addons/project_mailgate/i18n/nl.po b/addons/project_mailgate/i18n/nl.po deleted file mode 100644 index 1fffc910a74..00000000000 --- a/addons/project_mailgate/i18n/nl.po +++ /dev/null @@ -1,104 +0,0 @@ -# Dutch translation for openobject-addons -# Copyright (c) 2011 Rosetta Contributors and Canonical Ltd 2011 -# This file is distributed under the same license as the openobject-addons package. -# FIRST AUTHOR , 2011. -# -msgid "" -msgstr "" -"Project-Id-Version: openobject-addons\n" -"Report-Msgid-Bugs-To: FULL NAME \n" -"POT-Creation-Date: 2012-02-08 00:37+0000\n" -"PO-Revision-Date: 2011-01-18 20:55+0000\n" -"Last-Translator: Douwe Wullink (Dypalio) \n" -"Language-Team: Dutch \n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=UTF-8\n" -"Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-08-28 06:40+0000\n" -"X-Generator: Launchpad (build 15864)\n" - -#. module: project_mailgate -#: view:project.task:0 -msgid "History Information" -msgstr "Historie informatie" - -#. module: project_mailgate -#: model:ir.model,name:project_mailgate.model_project_task -msgid "Task" -msgstr "Taak" - -#. module: project_mailgate -#: constraint:project.task:0 -msgid "Error ! You cannot create recursive tasks." -msgstr "Fout! U kunt geen recursieve taken aanmaken." - -#. module: project_mailgate -#: field:project.task,message_ids:0 -msgid "Messages" -msgstr "Berichten" - -#. module: project_mailgate -#: code:addons/project_mailgate/project_mailgate.py:90 -#, python-format -msgid "Draft" -msgstr "Concept" - -#. module: project_mailgate -#: constraint:project.task:0 -msgid "Error ! Task end-date must be greater then task start-date" -msgstr "Fout! Einddatum taak moet groter zijn dat begindatum taak" - -#. module: project_mailgate -#: code:addons/project_mailgate/project_mailgate.py:116 -#, python-format -msgid "Cancel" -msgstr "Annuleren" - -#. module: project_mailgate -#: code:addons/project_mailgate/project_mailgate.py:110 -#, python-format -msgid "Done" -msgstr "Gereed" - -#. module: project_mailgate -#: code:addons/project_mailgate/project_mailgate.py:96 -#, python-format -msgid "Open" -msgstr "Open" - -#. module: project_mailgate -#: code:addons/project_mailgate/project_mailgate.py:102 -#, python-format -msgid "Pending" -msgstr "Wachtend" - -#. module: project_mailgate -#: view:project.task:0 -msgid "History" -msgstr "Geschiedenis" - -#~ msgid "" -#~ "This module is an interface that synchronises mails with OpenERP Project " -#~ "Task.\n" -#~ "\n" -#~ "It allows creating tasks as soon as a new mail arrives in our configured " -#~ "mail server.\n" -#~ "Moreover, it keeps track of all further communications and task states.\n" -#~ " " -#~ msgstr "" -#~ "Deze module is een interface dat mail synchroniseert met OpenERP Project " -#~ "taak.\n" -#~ "\n" -#~ "Het maakt nieuwe taken aan zodra mail binnenkomt op de ingestelde " -#~ "mailserver.\n" -#~ "Daarbij houdt het alle verdere communicaties en taak statussen bij.\n" -#~ " " - -#~ msgid "Attachments" -#~ msgstr "Bijlagen" - -#~ msgid "Details" -#~ msgstr "Details" - -#~ msgid "Project MailGateWay" -#~ msgstr "Project MailGateWay" diff --git a/addons/project_mailgate/i18n/nl_BE.po b/addons/project_mailgate/i18n/nl_BE.po deleted file mode 100644 index 40124ec1928..00000000000 --- a/addons/project_mailgate/i18n/nl_BE.po +++ /dev/null @@ -1,105 +0,0 @@ -# Dutch (Belgium) translation for openobject-addons -# Copyright (c) 2012 Rosetta Contributors and Canonical Ltd 2012 -# This file is distributed under the same license as the openobject-addons package. -# FIRST AUTHOR , 2012. -# -msgid "" -msgstr "" -"Project-Id-Version: openobject-addons\n" -"Report-Msgid-Bugs-To: FULL NAME \n" -"POT-Creation-Date: 2012-02-08 00:37+0000\n" -"PO-Revision-Date: 2012-03-01 15:19+0000\n" -"Last-Translator: FULL NAME \n" -"Language-Team: Dutch (Belgium) \n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=UTF-8\n" -"Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-08-28 06:40+0000\n" -"X-Generator: Launchpad (build 15864)\n" - -#. module: project_mailgate -#: view:project.task:0 -msgid "History Information" -msgstr "Historiekinformatie" - -#. module: project_mailgate -#: model:ir.model,name:project_mailgate.model_project_task -msgid "Task" -msgstr "Taak" - -#. module: project_mailgate -#: constraint:project.task:0 -msgid "Error ! You cannot create recursive tasks." -msgstr "U kunt niet dezelfde taken maken." - -#. module: project_mailgate -#: field:project.task,message_ids:0 -msgid "Messages" -msgstr "Berichten" - -#. module: project_mailgate -#: code:addons/project_mailgate/project_mailgate.py:90 -#, python-format -msgid "Draft" -msgstr "Voorlopig" - -#. module: project_mailgate -#: constraint:project.task:0 -msgid "Error ! Task end-date must be greater then task start-date" -msgstr "" -"Let op: de einddatum van de taak moet na de begindatum van de taak liggen" - -#. module: project_mailgate -#: code:addons/project_mailgate/project_mailgate.py:116 -#, python-format -msgid "Cancel" -msgstr "Annuleren" - -#. module: project_mailgate -#: code:addons/project_mailgate/project_mailgate.py:110 -#, python-format -msgid "Done" -msgstr "Gereed" - -#. module: project_mailgate -#: code:addons/project_mailgate/project_mailgate.py:96 -#, python-format -msgid "Open" -msgstr "Open" - -#. module: project_mailgate -#: code:addons/project_mailgate/project_mailgate.py:102 -#, python-format -msgid "Pending" -msgstr "Wachtend" - -#. module: project_mailgate -#: view:project.task:0 -msgid "History" -msgstr "Historiek" - -#~ msgid "" -#~ "This module is an interface that synchronises mails with OpenERP Project " -#~ "Task.\n" -#~ "\n" -#~ "It allows creating tasks as soon as a new mail arrives in our configured " -#~ "mail server.\n" -#~ "Moreover, it keeps track of all further communications and task states.\n" -#~ " " -#~ msgstr "" -#~ "Deze module is een interface waarmee mails met OpenERP-projecttaken worden " -#~ "gesynchroniseerd.\n" -#~ "\n" -#~ "Hiermee worden taken gemaakt zodra een nieuwe mail binnenkomt via de " -#~ "ingestelde mailserver.\n" -#~ "Bovendien worden alle communicatie en taakstatussen bijgehouden.\n" -#~ " " - -#~ msgid "Attachments" -#~ msgstr "Bijlagen" - -#~ msgid "Project MailGateWay" -#~ msgstr "Projectmailgateway" - -#~ msgid "Details" -#~ msgstr "Details" diff --git a/addons/project_mailgate/i18n/pl.po b/addons/project_mailgate/i18n/pl.po deleted file mode 100644 index 448e4b44ad9..00000000000 --- a/addons/project_mailgate/i18n/pl.po +++ /dev/null @@ -1,102 +0,0 @@ -# Polish translation for openobject-addons -# Copyright (c) 2010 Rosetta Contributors and Canonical Ltd 2010 -# This file is distributed under the same license as the openobject-addons package. -# FIRST AUTHOR , 2010. -# -msgid "" -msgstr "" -"Project-Id-Version: openobject-addons\n" -"Report-Msgid-Bugs-To: FULL NAME \n" -"POT-Creation-Date: 2012-02-08 00:37+0000\n" -"PO-Revision-Date: 2010-11-28 12:29+0000\n" -"Last-Translator: OpenERP Administrators \n" -"Language-Team: Polish \n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=UTF-8\n" -"Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-08-28 06:40+0000\n" -"X-Generator: Launchpad (build 15864)\n" - -#. module: project_mailgate -#: view:project.task:0 -msgid "History Information" -msgstr "Informacja o historii" - -#. module: project_mailgate -#: model:ir.model,name:project_mailgate.model_project_task -msgid "Task" -msgstr "Zadanie" - -#. module: project_mailgate -#: constraint:project.task:0 -msgid "Error ! You cannot create recursive tasks." -msgstr "" - -#. module: project_mailgate -#: field:project.task,message_ids:0 -msgid "Messages" -msgstr "Wiadomosći" - -#. module: project_mailgate -#: code:addons/project_mailgate/project_mailgate.py:90 -#, python-format -msgid "Draft" -msgstr "Projekt" - -#. module: project_mailgate -#: constraint:project.task:0 -msgid "Error ! Task end-date must be greater then task start-date" -msgstr "" - -#. module: project_mailgate -#: code:addons/project_mailgate/project_mailgate.py:116 -#, python-format -msgid "Cancel" -msgstr "Anuluj" - -#. module: project_mailgate -#: code:addons/project_mailgate/project_mailgate.py:110 -#, python-format -msgid "Done" -msgstr "Wykonano" - -#. module: project_mailgate -#: code:addons/project_mailgate/project_mailgate.py:96 -#, python-format -msgid "Open" -msgstr "Otwórz" - -#. module: project_mailgate -#: code:addons/project_mailgate/project_mailgate.py:102 -#, python-format -msgid "Pending" -msgstr "Oczekujące" - -#. module: project_mailgate -#: view:project.task:0 -msgid "History" -msgstr "Historia" - -#~ msgid "Project MailGateWay" -#~ msgstr "MailGateWay dla projektu" - -#~ msgid "" -#~ "This module is an interface that synchronises mails with OpenERP Project " -#~ "Task.\n" -#~ "\n" -#~ "It allows creating tasks as soon as a new mail arrives in our configured " -#~ "mail server.\n" -#~ "Moreover, it keeps track of all further communications and task states.\n" -#~ " " -#~ msgstr "" -#~ "Ten moduł jest interfejsem do synchronizacji poczty z zadaniami w projektach " -#~ "OpenERP.\n" -#~ "\n" -#~ "Pozwala tworzyć projekty po otrzymaniu wiadomości przez odpowiednio " -#~ "skonfigurowany\n" -#~ "server pocztowy. Co więcej, utrzymuje on w jednym miejscu dalszą komunikację " -#~ "i stany zadań.\n" -#~ " " - -#~ msgid "Details" -#~ msgstr "Szczegóły" diff --git a/addons/project_mailgate/i18n/project_mailgate.pot b/addons/project_mailgate/i18n/project_mailgate.pot deleted file mode 100644 index acb1e4e981c..00000000000 --- a/addons/project_mailgate/i18n/project_mailgate.pot +++ /dev/null @@ -1,77 +0,0 @@ -# Translation of OpenERP Server. -# This file contains the translation of the following modules: -# * project_mailgate -# -msgid "" -msgstr "" -"Project-Id-Version: OpenERP Server 6.1rc1\n" -"Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2012-02-08 00:37+0000\n" -"PO-Revision-Date: 2012-02-08 00:37+0000\n" -"Last-Translator: <>\n" -"Language-Team: \n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=UTF-8\n" -"Content-Transfer-Encoding: \n" -"Plural-Forms: \n" - -#. module: project_mailgate -#: view:project.task:0 -msgid "History Information" -msgstr "" - -#. module: project_mailgate -#: model:ir.model,name:project_mailgate.model_project_task -msgid "Task" -msgstr "" - -#. module: project_mailgate -#: constraint:project.task:0 -msgid "Error ! You cannot create recursive tasks." -msgstr "" - -#. module: project_mailgate -#: field:project.task,message_ids:0 -msgid "Messages" -msgstr "" - -#. module: project_mailgate -#: code:addons/project_mailgate/project_mailgate.py:90 -#, python-format -msgid "Draft" -msgstr "" - -#. module: project_mailgate -#: constraint:project.task:0 -msgid "Error ! Task end-date must be greater then task start-date" -msgstr "" - -#. module: project_mailgate -#: code:addons/project_mailgate/project_mailgate.py:116 -#, python-format -msgid "Cancel" -msgstr "" - -#. module: project_mailgate -#: code:addons/project_mailgate/project_mailgate.py:110 -#, python-format -msgid "Done" -msgstr "" - -#. module: project_mailgate -#: code:addons/project_mailgate/project_mailgate.py:96 -#, python-format -msgid "Open" -msgstr "" - -#. module: project_mailgate -#: code:addons/project_mailgate/project_mailgate.py:102 -#, python-format -msgid "Pending" -msgstr "" - -#. module: project_mailgate -#: view:project.task:0 -msgid "History" -msgstr "" - diff --git a/addons/project_mailgate/i18n/pt.po b/addons/project_mailgate/i18n/pt.po deleted file mode 100644 index 8a907a4d013..00000000000 --- a/addons/project_mailgate/i18n/pt.po +++ /dev/null @@ -1,84 +0,0 @@ -# Portuguese translation for openobject-addons -# Copyright (c) 2010 Rosetta Contributors and Canonical Ltd 2010 -# This file is distributed under the same license as the openobject-addons package. -# FIRST AUTHOR , 2010. -# -msgid "" -msgstr "" -"Project-Id-Version: openobject-addons\n" -"Report-Msgid-Bugs-To: FULL NAME \n" -"POT-Creation-Date: 2012-02-08 00:37+0000\n" -"PO-Revision-Date: 2010-12-12 10:12+0000\n" -"Last-Translator: OpenERP Administrators \n" -"Language-Team: Portuguese \n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=UTF-8\n" -"Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-08-28 06:40+0000\n" -"X-Generator: Launchpad (build 15864)\n" - -#. module: project_mailgate -#: view:project.task:0 -msgid "History Information" -msgstr "Informação do Histórico" - -#. module: project_mailgate -#: model:ir.model,name:project_mailgate.model_project_task -msgid "Task" -msgstr "Tarefa" - -#. module: project_mailgate -#: constraint:project.task:0 -msgid "Error ! You cannot create recursive tasks." -msgstr "Erro ! Não se pode criar tarefas recursivas" - -#. module: project_mailgate -#: field:project.task,message_ids:0 -msgid "Messages" -msgstr "Mensagens" - -#. module: project_mailgate -#: code:addons/project_mailgate/project_mailgate.py:90 -#, python-format -msgid "Draft" -msgstr "Rascunho" - -#. module: project_mailgate -#: constraint:project.task:0 -msgid "Error ! Task end-date must be greater then task start-date" -msgstr "Erro !Data final da tarefa dever ser posterior à data inicial" - -#. module: project_mailgate -#: code:addons/project_mailgate/project_mailgate.py:116 -#, python-format -msgid "Cancel" -msgstr "Cancelar" - -#. module: project_mailgate -#: code:addons/project_mailgate/project_mailgate.py:110 -#, python-format -msgid "Done" -msgstr "Concluído" - -#. module: project_mailgate -#: code:addons/project_mailgate/project_mailgate.py:96 -#, python-format -msgid "Open" -msgstr "Abrir" - -#. module: project_mailgate -#: code:addons/project_mailgate/project_mailgate.py:102 -#, python-format -msgid "Pending" -msgstr "Pendente" - -#. module: project_mailgate -#: view:project.task:0 -msgid "History" -msgstr "Histórico" - -#~ msgid "Details" -#~ msgstr "Detalhes" - -#~ msgid "Attachments" -#~ msgstr "Anexos" diff --git a/addons/project_mailgate/i18n/pt_BR.po b/addons/project_mailgate/i18n/pt_BR.po deleted file mode 100644 index cd084a2e1e2..00000000000 --- a/addons/project_mailgate/i18n/pt_BR.po +++ /dev/null @@ -1,105 +0,0 @@ -# Brazilian Portuguese translation for openobject-addons -# Copyright (c) 2010 Rosetta Contributors and Canonical Ltd 2010 -# This file is distributed under the same license as the openobject-addons package. -# FIRST AUTHOR , 2010. -# -msgid "" -msgstr "" -"Project-Id-Version: openobject-addons\n" -"Report-Msgid-Bugs-To: FULL NAME \n" -"POT-Creation-Date: 2012-02-08 00:37+0000\n" -"PO-Revision-Date: 2011-01-17 11:53+0000\n" -"Last-Translator: Emerson \n" -"Language-Team: Brazilian Portuguese \n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=UTF-8\n" -"Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-08-28 06:40+0000\n" -"X-Generator: Launchpad (build 15864)\n" - -#. module: project_mailgate -#: view:project.task:0 -msgid "History Information" -msgstr "Informação Histórica" - -#. module: project_mailgate -#: model:ir.model,name:project_mailgate.model_project_task -msgid "Task" -msgstr "Tarefa" - -#. module: project_mailgate -#: constraint:project.task:0 -msgid "Error ! You cannot create recursive tasks." -msgstr "Erro! Você não pode criar tarefas recursivas." - -#. module: project_mailgate -#: field:project.task,message_ids:0 -msgid "Messages" -msgstr "Mensagens" - -#. module: project_mailgate -#: code:addons/project_mailgate/project_mailgate.py:90 -#, python-format -msgid "Draft" -msgstr "Provisório" - -#. module: project_mailgate -#: constraint:project.task:0 -msgid "Error ! Task end-date must be greater then task start-date" -msgstr "Erro ! A data final deve ser maior do que a data inicial" - -#. module: project_mailgate -#: code:addons/project_mailgate/project_mailgate.py:116 -#, python-format -msgid "Cancel" -msgstr "Cancelar" - -#. module: project_mailgate -#: code:addons/project_mailgate/project_mailgate.py:110 -#, python-format -msgid "Done" -msgstr "Concluído" - -#. module: project_mailgate -#: code:addons/project_mailgate/project_mailgate.py:96 -#, python-format -msgid "Open" -msgstr "Abrir" - -#. module: project_mailgate -#: code:addons/project_mailgate/project_mailgate.py:102 -#, python-format -msgid "Pending" -msgstr "Pendente" - -#. module: project_mailgate -#: view:project.task:0 -msgid "History" -msgstr "Histórico" - -#~ msgid "Details" -#~ msgstr "Detalhes" - -#~ msgid "" -#~ "This module is an interface that synchronises mails with OpenERP Project " -#~ "Task.\n" -#~ "\n" -#~ "It allows creating tasks as soon as a new mail arrives in our configured " -#~ "mail server.\n" -#~ "Moreover, it keeps track of all further communications and task states.\n" -#~ " " -#~ msgstr "" -#~ "Este módulo é uma interface que sincroniza emails com as Tarefas de Projetos " -#~ "OpenERP.\n" -#~ "\n" -#~ "Permite a criação de tarefas tão logo um novo email chegue no nosso servidor " -#~ "de email configurado.\n" -#~ "Além disso, mantém o controle de todas as demais comunicações e estados das " -#~ "tarefas.\n" -#~ " " - -#~ msgid "Project MailGateWay" -#~ msgstr "Projeto Gateway de Email" - -#~ msgid "Attachments" -#~ msgstr "Anexos" diff --git a/addons/project_mailgate/i18n/ro.po b/addons/project_mailgate/i18n/ro.po deleted file mode 100644 index dacd3b477f8..00000000000 --- a/addons/project_mailgate/i18n/ro.po +++ /dev/null @@ -1,107 +0,0 @@ -# Romanian translation for openobject-addons -# Copyright (c) 2012 Rosetta Contributors and Canonical Ltd 2012 -# This file is distributed under the same license as the openobject-addons package. -# FIRST AUTHOR , 2012. -# -msgid "" -msgstr "" -"Project-Id-Version: openobject-addons\n" -"Report-Msgid-Bugs-To: FULL NAME \n" -"POT-Creation-Date: 2012-02-08 00:37+0000\n" -"PO-Revision-Date: 2012-01-13 11:48+0000\n" -"Last-Translator: FULL NAME \n" -"Language-Team: Romanian \n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=UTF-8\n" -"Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-08-28 06:40+0000\n" -"X-Generator: Launchpad (build 15864)\n" - -#. module: project_mailgate -#: view:project.task:0 -msgid "History Information" -msgstr "Istoric Informatii" - -#. module: project_mailgate -#: model:ir.model,name:project_mailgate.model_project_task -msgid "Task" -msgstr "Sarcina" - -#. module: project_mailgate -#: constraint:project.task:0 -msgid "Error ! You cannot create recursive tasks." -msgstr "Eroare ! Nu puteti crea sarcini recursive." - -#. module: project_mailgate -#: field:project.task,message_ids:0 -msgid "Messages" -msgstr "Mesaje" - -#. module: project_mailgate -#: code:addons/project_mailgate/project_mailgate.py:90 -#, python-format -msgid "Draft" -msgstr "Ciorna" - -#. module: project_mailgate -#: constraint:project.task:0 -msgid "Error ! Task end-date must be greater then task start-date" -msgstr "" -"Eroare ! Data de sfarsit a sarcinii trebuie sa fie mai mare decat data de " -"inceput" - -#. module: project_mailgate -#: code:addons/project_mailgate/project_mailgate.py:116 -#, python-format -msgid "Cancel" -msgstr "Anulati" - -#. module: project_mailgate -#: code:addons/project_mailgate/project_mailgate.py:110 -#, python-format -msgid "Done" -msgstr "Efectuat" - -#. module: project_mailgate -#: code:addons/project_mailgate/project_mailgate.py:96 -#, python-format -msgid "Open" -msgstr "Deschideti" - -#. module: project_mailgate -#: code:addons/project_mailgate/project_mailgate.py:102 -#, python-format -msgid "Pending" -msgstr "In asteptare" - -#. module: project_mailgate -#: view:project.task:0 -msgid "History" -msgstr "Istoric" - -#~ msgid "Project MailGateWay" -#~ msgstr "MailGAteWay Proiect" - -#~ msgid "" -#~ "This module is an interface that synchronises mails with OpenERP Project " -#~ "Task.\n" -#~ "\n" -#~ "It allows creating tasks as soon as a new mail arrives in our configured " -#~ "mail server.\n" -#~ "Moreover, it keeps track of all further communications and task states.\n" -#~ " " -#~ msgstr "" -#~ "Acest modul este o interfată care sincronizează email-urile cu Activitatea " -#~ "de Proiect OpenERP.\n" -#~ "\n" -#~ "Permite crearea de sarcini de indată ce un nou email soseste in serverul de " -#~ "mail configurat.\n" -#~ "In plus, tine evidenta tuturor comunicatiilor viitoare si a stărilor " -#~ "activitătilor.\n" -#~ " " - -#~ msgid "Attachments" -#~ msgstr "Atașamente" - -#~ msgid "Details" -#~ msgstr "Detalii" diff --git a/addons/project_mailgate/i18n/sv.po b/addons/project_mailgate/i18n/sv.po deleted file mode 100644 index 02d5c55be00..00000000000 --- a/addons/project_mailgate/i18n/sv.po +++ /dev/null @@ -1,78 +0,0 @@ -# Swedish translation for openobject-addons -# Copyright (c) 2012 Rosetta Contributors and Canonical Ltd 2012 -# This file is distributed under the same license as the openobject-addons package. -# FIRST AUTHOR , 2012. -# -msgid "" -msgstr "" -"Project-Id-Version: openobject-addons\n" -"Report-Msgid-Bugs-To: FULL NAME \n" -"POT-Creation-Date: 2012-02-08 00:37+0000\n" -"PO-Revision-Date: 2012-06-18 23:27+0000\n" -"Last-Translator: FULL NAME \n" -"Language-Team: Swedish \n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=UTF-8\n" -"Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-08-28 06:40+0000\n" -"X-Generator: Launchpad (build 15864)\n" - -#. module: project_mailgate -#: view:project.task:0 -msgid "History Information" -msgstr "Historikinformation" - -#. module: project_mailgate -#: model:ir.model,name:project_mailgate.model_project_task -msgid "Task" -msgstr "Aktivitet" - -#. module: project_mailgate -#: constraint:project.task:0 -msgid "Error ! You cannot create recursive tasks." -msgstr "Fel ! Du kan inte skapa rekursiva aktiviteter" - -#. module: project_mailgate -#: field:project.task,message_ids:0 -msgid "Messages" -msgstr "Meddelanden" - -#. module: project_mailgate -#: code:addons/project_mailgate/project_mailgate.py:90 -#, python-format -msgid "Draft" -msgstr "Preliminär" - -#. module: project_mailgate -#: constraint:project.task:0 -msgid "Error ! Task end-date must be greater then task start-date" -msgstr "Fel ! Aktivitetens slutdatum måste komma efter startdatumet" - -#. module: project_mailgate -#: code:addons/project_mailgate/project_mailgate.py:116 -#, python-format -msgid "Cancel" -msgstr "Avbryt" - -#. module: project_mailgate -#: code:addons/project_mailgate/project_mailgate.py:110 -#, python-format -msgid "Done" -msgstr "Färdig" - -#. module: project_mailgate -#: code:addons/project_mailgate/project_mailgate.py:96 -#, python-format -msgid "Open" -msgstr "Öppen" - -#. module: project_mailgate -#: code:addons/project_mailgate/project_mailgate.py:102 -#, python-format -msgid "Pending" -msgstr "Väntande" - -#. module: project_mailgate -#: view:project.task:0 -msgid "History" -msgstr "Historik" diff --git a/addons/project_mailgate/i18n/tr.po b/addons/project_mailgate/i18n/tr.po deleted file mode 100644 index 1f8a351bcbc..00000000000 --- a/addons/project_mailgate/i18n/tr.po +++ /dev/null @@ -1,78 +0,0 @@ -# Turkish translation for openobject-addons -# Copyright (c) 2012 Rosetta Contributors and Canonical Ltd 2012 -# This file is distributed under the same license as the openobject-addons package. -# FIRST AUTHOR , 2012. -# -msgid "" -msgstr "" -"Project-Id-Version: openobject-addons\n" -"Report-Msgid-Bugs-To: FULL NAME \n" -"POT-Creation-Date: 2012-02-08 00:37+0000\n" -"PO-Revision-Date: 2012-01-25 17:33+0000\n" -"Last-Translator: Ahmet Altınışık \n" -"Language-Team: Turkish \n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=UTF-8\n" -"Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-08-28 06:40+0000\n" -"X-Generator: Launchpad (build 15864)\n" - -#. module: project_mailgate -#: view:project.task:0 -msgid "History Information" -msgstr "" - -#. module: project_mailgate -#: model:ir.model,name:project_mailgate.model_project_task -msgid "Task" -msgstr "Görev" - -#. module: project_mailgate -#: constraint:project.task:0 -msgid "Error ! You cannot create recursive tasks." -msgstr "" - -#. module: project_mailgate -#: field:project.task,message_ids:0 -msgid "Messages" -msgstr "Mesajlar" - -#. module: project_mailgate -#: code:addons/project_mailgate/project_mailgate.py:90 -#, python-format -msgid "Draft" -msgstr "Taslak" - -#. module: project_mailgate -#: constraint:project.task:0 -msgid "Error ! Task end-date must be greater then task start-date" -msgstr "" - -#. module: project_mailgate -#: code:addons/project_mailgate/project_mailgate.py:116 -#, python-format -msgid "Cancel" -msgstr "İptal" - -#. module: project_mailgate -#: code:addons/project_mailgate/project_mailgate.py:110 -#, python-format -msgid "Done" -msgstr "Tamamlandı" - -#. module: project_mailgate -#: code:addons/project_mailgate/project_mailgate.py:96 -#, python-format -msgid "Open" -msgstr "Açık" - -#. module: project_mailgate -#: code:addons/project_mailgate/project_mailgate.py:102 -#, python-format -msgid "Pending" -msgstr "Bekleyen" - -#. module: project_mailgate -#: view:project.task:0 -msgid "History" -msgstr "" diff --git a/addons/project_mailgate/i18n/zh_CN.po b/addons/project_mailgate/i18n/zh_CN.po deleted file mode 100644 index c1b8ea02d5a..00000000000 --- a/addons/project_mailgate/i18n/zh_CN.po +++ /dev/null @@ -1,101 +0,0 @@ -# Chinese (Simplified) translation for openobject-addons -# Copyright (c) 2011 Rosetta Contributors and Canonical Ltd 2011 -# This file is distributed under the same license as the openobject-addons package. -# FIRST AUTHOR , 2011. -# -msgid "" -msgstr "" -"Project-Id-Version: openobject-addons\n" -"Report-Msgid-Bugs-To: FULL NAME \n" -"POT-Creation-Date: 2012-02-08 00:37+0000\n" -"PO-Revision-Date: 2011-06-28 16:25+0000\n" -"Last-Translator: FULL NAME \n" -"Language-Team: Chinese (Simplified) \n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=UTF-8\n" -"Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-08-28 06:40+0000\n" -"X-Generator: Launchpad (build 15864)\n" - -#. module: project_mailgate -#: view:project.task:0 -msgid "History Information" -msgstr "日志信息" - -#. module: project_mailgate -#: model:ir.model,name:project_mailgate.model_project_task -msgid "Task" -msgstr "任务" - -#. module: project_mailgate -#: constraint:project.task:0 -msgid "Error ! You cannot create recursive tasks." -msgstr "错误!不能创建循环引用的任务" - -#. module: project_mailgate -#: field:project.task,message_ids:0 -msgid "Messages" -msgstr "消息" - -#. module: project_mailgate -#: code:addons/project_mailgate/project_mailgate.py:90 -#, python-format -msgid "Draft" -msgstr "草稿" - -#. module: project_mailgate -#: constraint:project.task:0 -msgid "Error ! Task end-date must be greater then task start-date" -msgstr "错误!任务结束日期必须大于任务开始日期" - -#. module: project_mailgate -#: code:addons/project_mailgate/project_mailgate.py:116 -#, python-format -msgid "Cancel" -msgstr "取消" - -#. module: project_mailgate -#: code:addons/project_mailgate/project_mailgate.py:110 -#, python-format -msgid "Done" -msgstr "完成" - -#. module: project_mailgate -#: code:addons/project_mailgate/project_mailgate.py:96 -#, python-format -msgid "Open" -msgstr "未结" - -#. module: project_mailgate -#: code:addons/project_mailgate/project_mailgate.py:102 -#, python-format -msgid "Pending" -msgstr "等待中" - -#. module: project_mailgate -#: view:project.task:0 -msgid "History" -msgstr "日志" - -#~ msgid "Project MailGateWay" -#~ msgstr "Project MailGateWay" - -#~ msgid "" -#~ "This module is an interface that synchronises mails with OpenERP Project " -#~ "Task.\n" -#~ "\n" -#~ "It allows creating tasks as soon as a new mail arrives in our configured " -#~ "mail server.\n" -#~ "Moreover, it keeps track of all further communications and task states.\n" -#~ " " -#~ msgstr "" -#~ "此模块是邮件和任务的接口。\n" -#~ "允许在特定邮件服务器收到邮件后马上创建任务。\n" -#~ "还能跟踪进一步的沟通和任务阶段\n" -#~ " " - -#~ msgid "Attachments" -#~ msgstr "附件" - -#~ msgid "Details" -#~ msgstr "详细信息" diff --git a/addons/project_mailgate/project_mailgate.py b/addons/project_mailgate/project_mailgate.py deleted file mode 100644 index 3096a7381e6..00000000000 --- a/addons/project_mailgate/project_mailgate.py +++ /dev/null @@ -1,80 +0,0 @@ -# -*- 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 . -# -############################################################################## - -import binascii -from osv import fields, osv -from tools.translate import _ -import tools - -class project_tasks(osv.osv): - _inherit = 'project.task' - - def message_new(self, cr, uid, msg, custom_values=None, context=None): - """ Overrides mail_thread message_new that is called by the mailgateway - through message_process. - This override updates the document according to the email. - """ - if custom_values is None: custom_values = {} - custom_values.update({ - 'name': subject, - 'planned_hours': 0.0, - 'subject': msg.get('subject'), - }) - return super(project_tasks,self).message_new(cr, uid, msg, custom_values=custom_values, context=context) - - def message_update(self, cr, uid, ids, msg, update_vals=None, context=None): - """ Overrides mail_thread message_update that is called by the mailgateway - through message_process. - This method updates the task according to the email. - """ - if update_vals is None: update_vals = {} - act = False - maps = { - 'cost':'planned_hours', - } - for line in msg['body'].split('\n'): - line = line.strip() - res = tools.misc.command_re.match(line) - if res: - match = res.group(1).lower() - field = maps.get(match) - if field: - try: - update_vals[field] = float(res.group(2).lower()) - except (ValueError, TypeError): - pass - elif match.lower() == 'state' \ - and res.group(2).lower() in ['cancel','close','draft','open','pending']: - act = 'do_%s' % res.group(2).lower() - if act: - getattr(self,act)(cr, uid, ids, context=context) - return super(project_tasks,self).message_update(cr, uid, msg, update_vals=update_vals, context=context) - - def message_thread_followers(self, cr, uid, ids, context=None): - followers = super(project_tasks,self).message_thread_followers(cr, uid, ids, context=context) - for task in self.browse(cr, uid, followers.keys(), context=context): - task_followers = set(followers[task.id]) - task_followers.add(task.user_id.email) - followers[task.id] = filter(None, task_followers) - return followers - - -# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: From 5d2b0fc63b8bf029c92553b7d5b61f9f89d45f31 Mon Sep 17 00:00:00 2001 From: "Ajay Chauhan (OpenERP)" Date: Tue, 4 Sep 2012 15:02:20 +0530 Subject: [PATCH 310/436] [IMP] crm: made some little changes in domain for field 'stage_id' bzr revid: cha@tinyerp.com-20120904093220-b2kunh9ccec9z3xo --- addons/crm/crm_lead.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addons/crm/crm_lead.py b/addons/crm/crm_lead.py index 8f857c28556..aa6a7a66e5c 100644 --- a/addons/crm/crm_lead.py +++ b/addons/crm/crm_lead.py @@ -213,7 +213,7 @@ class crm_lead(base_stage, osv.osv): 'priority': fields.selection(crm.AVAILABLE_PRIORITIES, 'Priority', select=True), 'date_closed': fields.datetime('Closed', readonly=True), 'stage_id': fields.many2one('crm.case.stage', 'Stage', - domain="['&', '|', ('section_ids', '=', section_id), ('case_default', '=', True), '&', ('fold', '=', False), '|', ('type', '=', type), ('type', '=', 'both')]"), + domain="['&', '&', '|', ('fold', '=', False),('section_ids', '=', section_id), ('case_default', '=', True), '|', ('type', '=', type), ('type', '=', 'both')]"), 'user_id': fields.many2one('res.users', 'Salesperson'), 'referred': fields.char('Referred By', size=64), 'date_open': fields.datetime('Opened', readonly=True), From 9612626dacbaf3a7956a68f8bd7696966430c0ec Mon Sep 17 00:00:00 2001 From: Fabien Meghazi Date: Tue, 4 Sep 2012 12:19:16 +0200 Subject: [PATCH 311/436] [ADD] Kanban dummy cell click triggers 'add column' button bounce effect bzr revid: fme@openerp.com-20120904101916-wzkxutkcthgyv2fe --- addons/web/static/src/css/base.css | 4 +++- addons/web/static/src/css/base.sass | 2 ++ addons/web_kanban/static/src/js/kanban.js | 9 +++++++++ addons/web_kanban/static/src/xml/web_kanban.xml | 4 +++- 4 files changed, 17 insertions(+), 2 deletions(-) diff --git a/addons/web/static/src/css/base.css b/addons/web/static/src/css/base.css index dd168653f03..94cb4d3a007 100644 --- a/addons/web/static/src/css/base.css +++ b/addons/web/static/src/css/base.css @@ -69,7 +69,6 @@ display: none !important; } } - .openerp.openerp_webclient_container { height: 100%; position: relative; @@ -238,6 +237,9 @@ .openerp .ui-widget-content a { color: #7c7bad; } +.openerp .oe_bounce_container { + display: inline-block; +} .openerp.ui-dialog { display: none; padding: 6px; diff --git a/addons/web/static/src/css/base.sass b/addons/web/static/src/css/base.sass index 537988e9c4a..ad5794ab5cf 100644 --- a/addons/web/static/src/css/base.sass +++ b/addons/web/static/src/css/base.sass @@ -251,6 +251,8 @@ $sheet-max-width: 860px // Jquery ui Overrides {{{ .ui-widget-content a color: $link-color + .oe_bounce_container + display: inline-block // Modal box &.ui-dialog diff --git a/addons/web_kanban/static/src/js/kanban.js b/addons/web_kanban/static/src/js/kanban.js index ffec52e8b97..e00f6d9f44e 100644 --- a/addons/web_kanban/static/src/js/kanban.js +++ b/addons/web_kanban/static/src/js/kanban.js @@ -42,6 +42,15 @@ instance.web_kanban.KanbanView = instance.web.View.extend({ this.limit = options.limit || 80; this.add_group_mutex = new $.Mutex(); }, + start: function() { + var self = this; + this._super.apply(this, arguments); + this.$el.on('click', '.oe_kanban_dummy_cell', function() { + if (self.$buttons) { + self.$buttons.find('.oe_kanban_add_column').effect('bounce', {distance: 18, times: 5}, 150); + } + }); + }, destroy: function() { this._super.apply(this, arguments); $('html').off('click.kanban'); diff --git a/addons/web_kanban/static/src/xml/web_kanban.xml b/addons/web_kanban/static/src/xml/web_kanban.xml index 39bca51c1f5..20b361e9445 100644 --- a/addons/web_kanban/static/src/xml/web_kanban.xml +++ b/addons/web_kanban/static/src/xml/web_kanban.xml @@ -20,7 +20,9 @@
      Add a new column + From fd40dcbeff2973c6501db4eb4e752597ef7fcbd2 Mon Sep 17 00:00:00 2001 From: Xavier Morel Date: Tue, 4 Sep 2012 12:11:39 +0200 Subject: [PATCH 312/436] [FIX] add minimum height to listview rows matching that of edition row fields Also fix reset of border-radius: none is not a valid value for border-radius, change to 0. bzr revid: xmo@openerp.com-20120904101139-xyl0dskdk6dbdfe1 --- addons/web/static/src/css/base.css | 20 ++++++++++--------- addons/web/static/src/css/base.sass | 12 ++++++----- .../web/static/src/js/view_list_editable.js | 2 +- 3 files changed, 19 insertions(+), 15 deletions(-) diff --git a/addons/web/static/src/css/base.css b/addons/web/static/src/css/base.css index 94cb4d3a007..c20cdf92891 100644 --- a/addons/web/static/src/css/base.css +++ b/addons/web/static/src/css/base.css @@ -523,9 +523,9 @@ padding: 0; margin: 0; background: none; - -moz-border-radius: none; - -webkit-border-radius: none; - border-radius: none; + -moz-border-radius: 0; + -webkit-border-radius: 0; + border-radius: 0; -moz-box-shadow: none; -webkit-box-shadow: none; box-shadow: none; @@ -538,9 +538,9 @@ padding: 0; margin: 0; background: none; - -moz-border-radius: none; - -webkit-border-radius: none; - border-radius: none; + -moz-border-radius: 0; + -webkit-border-radius: 0; + border-radius: 0; -moz-box-shadow: none; -webkit-box-shadow: none; box-shadow: none; @@ -2479,10 +2479,11 @@ height: 27px; } .openerp .oe_list.oe_list_editable.oe_editing .oe_form_field input, .openerp .oe_list.oe_list_editable.oe_editing .oe_form_field textarea { + -moz-border-radius: 0; + -webkit-border-radius: 0; + border-radius: 0; border: 1px solid #aaaaff; - border-radius: 0px; - margin: 0px; - -webkit-border-radius: 0px; + margin: 0; } .openerp .oe_list.oe_list_editable.oe_editing .oe_form_field textarea { height: 60px; @@ -2582,6 +2583,7 @@ cursor: pointer; } .openerp .oe_list_content > tbody > tr { + height: 27px; border-top: 1px solid #dddddd; } .openerp .oe_list_content > tbody > tr > td.oe_list_field_cell { diff --git a/addons/web/static/src/css/base.sass b/addons/web/static/src/css/base.sass index ad5794ab5cf..5f72b7ae548 100644 --- a/addons/web/static/src/css/base.sass +++ b/addons/web/static/src/css/base.sass @@ -36,7 +36,7 @@ $sheet-max-width: 860px padding: 0 margin: 0 background: none - @include radius(none) + @include radius(0) @include box-shadow(none) @mixin vertical-gradient($startColor: #555, $endColor: #333) @@ -1911,6 +1911,8 @@ $sheet-max-width: 860px background-color: #d2d2ff td.oe_readonly background-color: #eee + + $row-height: 27px .oe_list_editable .oe_list_content td.oe_list_field_cell @@ -1925,12 +1927,11 @@ $sheet-max-width: 860px display: none .oe_form_field input - height: 27px + height: $row-height input, textarea + @include radius(0) border: 1px solid #aaf - border-radius: 0px - margin: 0px - -webkit-border-radius: 0px + margin: 0 textarea height: 60px &.oe_form_field_float,&.oe_form_view_integer @@ -2015,6 +2016,7 @@ $sheet-max-width: 860px > tbody cursor: pointer > tr + height: $row-height border-top: 1px solid #ddd > td.oe_list_field_cell padding: 3px 6px diff --git a/addons/web/static/src/js/view_list_editable.js b/addons/web/static/src/js/view_list_editable.js index 65f5620c1a9..9998eb5fce2 100644 --- a/addons/web/static/src/js/view_list_editable.js +++ b/addons/web/static/src/js/view_list_editable.js @@ -249,7 +249,7 @@ openerp.web.list_editable = function (instance) { var position = $cell.position(); // jquery does not understand !important - field.$el.attr('style', 'width: '+$cell.outerWidth()+'px !important') + field.$el.attr('style', 'width: '+$cell.outerWidth()+'px !important'); field.$el.css({ top: position.top, left: position.left, From cccbe77b77ce060cb39ed609812bfad947dff553 Mon Sep 17 00:00:00 2001 From: "Ajay Chauhan (OpenERP)" Date: Tue, 4 Sep 2012 15:52:12 +0530 Subject: [PATCH 313/436] [IMP] hr_recruitment, project, project_issue: made some little changes in domain for field 'stage_id' bzr revid: cha@tinyerp.com-20120904102212-ccxp2ik0r5kp3vc3 --- addons/hr_recruitment/hr_recruitment.py | 2 +- addons/project/project.py | 2 +- addons/project_issue/project_issue.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/addons/hr_recruitment/hr_recruitment.py b/addons/hr_recruitment/hr_recruitment.py index 2a3fab58730..581f9263264 100644 --- a/addons/hr_recruitment/hr_recruitment.py +++ b/addons/hr_recruitment/hr_recruitment.py @@ -185,7 +185,7 @@ class hr_applicant(base_stage, osv.Model): 'create_date': fields.datetime('Creation Date', readonly=True, select=True), 'write_date': fields.datetime('Update Date', readonly=True), 'stage_id': fields.many2one ('hr.recruitment.stage', 'Stage', - domain="['&', ('fold', '=', False), '|', ('department_id', '=', department_id), ('department_id', '=', False)]"), + domain="['&','|',('fold', '=', False), ('department_id', '=', department_id), ('department_id', '=', False)]"), 'state': fields.related('stage_id', 'state', type="selection", store=True, selection=AVAILABLE_STATES, string="State", readonly=True, help='The state is set to \'Draft\', when a case is created.\ diff --git a/addons/project/project.py b/addons/project/project.py index c9bcb643150..55cb8c2ff37 100644 --- a/addons/project/project.py +++ b/addons/project/project.py @@ -739,7 +739,7 @@ class task(base_stage, osv.osv): 'priority': fields.selection([('4','Very Low'), ('3','Low'), ('2','Medium'), ('1','Important'), ('0','Very important')], 'Priority', select=True), 'sequence': fields.integer('Sequence', select=True, help="Gives the sequence order when displaying a list of tasks."), 'stage_id': fields.many2one('project.task.type', 'Stage', - domain="['&', ('fold', '=', False), '|', ('project_ids', '=', project_id), ('case_default', '=', True)]"), + domain="['&', '|', ('fold', '=', False), ('project_ids', '=', project_id), ('case_default', '=', True)]"), 'state': fields.related('stage_id', 'state', type="selection", store=True, selection=_TASK_STATE, string="State", readonly=True, help='The state is set to \'Draft\', when a case is created.\ diff --git a/addons/project_issue/project_issue.py b/addons/project_issue/project_issue.py index 368ec2cb9d7..d5279e31e41 100644 --- a/addons/project_issue/project_issue.py +++ b/addons/project_issue/project_issue.py @@ -240,7 +240,7 @@ class project_issue(base_stage, osv.osv): 'priority': fields.selection(crm.AVAILABLE_PRIORITIES, 'Priority', select=True), 'version_id': fields.many2one('project.issue.version', 'Version'), 'stage_id': fields.many2one ('project.task.type', 'Stage', - domain="['&', ('fold', '=', False), '|', ('project_ids', '=', project_id), ('case_default', '=', True)]"), + domain="['&', '|', ('fold', '=', False), ('project_ids', '=', project_id), ('case_default', '=', True)]"), 'project_id':fields.many2one('project.project', 'Project'), 'duration': fields.float('Duration'), 'task_id': fields.many2one('project.task', 'Task', domain="[('project_id','=',project_id)]"), From af953dab4619c3cb995bae3921e1b7d90ba3dd40 Mon Sep 17 00:00:00 2001 From: Stephane Wirtel Date: Tue, 4 Sep 2012 12:29:25 +0200 Subject: [PATCH 314/436] [IMP] disable the document_ftp in the run_all_with_test script bzr revid: stw@openerp.com-20120904102925-0yaxin1doamxyrxd --- scripts/run_all_with_tests.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/run_all_with_tests.sh b/scripts/run_all_with_tests.sh index 4103b6243e2..877a5908b1a 100755 --- a/scripts/run_all_with_tests.sh +++ b/scripts/run_all_with_tests.sh @@ -1,7 +1,7 @@ DATABASE=trunk dropdb ${DATABASE} REPOSITORIES=../../addons/trunk -MODULES=`python -c "import os; print ','.join(os.listdir('${REPOSITORIES}'))"` +MODULES=`python -c "import os; print ','.join(list(set(os.listdir('${REPOSITORIES}')) - set(['document_ftp'])))"` createdb ${DATABASE} rm openerp-server.log ./openerp-server \ From c675e67b1404f171ceb58cb608732e43e94f2263 Mon Sep 17 00:00:00 2001 From: "Ajay Chauhan (OpenERP)" Date: Tue, 4 Sep 2012 16:00:56 +0530 Subject: [PATCH 315/436] [IMP] hr_recruitment: made minor change in domain bzr revid: cha@tinyerp.com-20120904103056-2lz4ukxpnlsi7c25 --- addons/hr_recruitment/hr_recruitment.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addons/hr_recruitment/hr_recruitment.py b/addons/hr_recruitment/hr_recruitment.py index 581f9263264..05c7a9df2bd 100644 --- a/addons/hr_recruitment/hr_recruitment.py +++ b/addons/hr_recruitment/hr_recruitment.py @@ -185,7 +185,7 @@ class hr_applicant(base_stage, osv.Model): 'create_date': fields.datetime('Creation Date', readonly=True, select=True), 'write_date': fields.datetime('Update Date', readonly=True), 'stage_id': fields.many2one ('hr.recruitment.stage', 'Stage', - domain="['&','|',('fold', '=', False), ('department_id', '=', department_id), ('department_id', '=', False)]"), + domain="['&', '|', ('fold', '=', False), ('department_id', '=', department_id), ('department_id', '=', False)]"), 'state': fields.related('stage_id', 'state', type="selection", store=True, selection=AVAILABLE_STATES, string="State", readonly=True, help='The state is set to \'Draft\', when a case is created.\ From 95837af09c769370fa7c7aa1bc89bbee0868d50f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thibault=20Delavall=C3=A9e?= Date: Tue, 4 Sep 2012 12:32:42 +0200 Subject: [PATCH 316/436] [IMP] ir.needaction: made the model Abstract. Fixed a bug when inheriting from AbstractModels: _auto (to create a database for the model) was not correctly set. bzr revid: tde@openerp.com-20120904103242-o6ky20u9gvhu0t3u --- openerp/addons/base/ir/ir_needaction.py | 2 +- openerp/osv/orm.py | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/openerp/addons/base/ir/ir_needaction.py b/openerp/addons/base/ir/ir_needaction.py index ba3a5c4a998..db133ad9904 100644 --- a/openerp/addons/base/ir/ir_needaction.py +++ b/openerp/addons/base/ir/ir_needaction.py @@ -21,7 +21,7 @@ from osv import osv -class ir_needaction_mixin(osv.Model): +class ir_needaction_mixin(osv.AbstractModel): '''Mixin class for objects using the need action feature. Need action feature can be used by models that have to be able to diff --git a/openerp/osv/orm.py b/openerp/osv/orm.py index 650a9ac78c5..df12062f922 100644 --- a/openerp/osv/orm.py +++ b/openerp/osv/orm.py @@ -654,6 +654,7 @@ class BaseModel(object): may be set to False. """ __metaclass__ = MetaModel + _auto = True # create database backend _register = False # Set to false if the model shouldn't be automatically discovered. _name = None _columns = {} @@ -5076,6 +5077,7 @@ class Model(BaseModel): The system will later instantiate the class once per database (on which the class' module is installed). """ + _auto = True _register = False # not visible in ORM registry, meant to be python-inherited only _transient = False # True in a TransientModel @@ -5088,6 +5090,7 @@ class TransientModel(BaseModel): records they created. The super-user has unrestricted access to all TransientModel records. """ + _auto = True _register = False # not visible in ORM registry, meant to be python-inherited only _transient = True From 58a5e1c01321dff78f4ab7d7b74acd171c7e9be4 Mon Sep 17 00:00:00 2001 From: Xavier Morel Date: Tue, 4 Sep 2012 13:04:22 +0200 Subject: [PATCH 317/436] [FIX] reversal of aggregate function and title when extracted Column objects bzr revid: xmo@openerp.com-20120904110422-zgk7icj5lgygnvpo --- addons/web/static/src/js/view_list.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addons/web/static/src/js/view_list.js b/addons/web/static/src/js/view_list.js index a2dccf2ec73..a83f7891a08 100644 --- a/addons/web/static/src/js/view_list.js +++ b/addons/web/static/src/js/view_list.js @@ -1995,7 +1995,7 @@ instance.web.list.Column = instance.web.Class.extend({ if (!(aggregation_func in this)) { return {}; } - var C = function (label, fn) { + var C = function (fn, label) { this['function'] = fn; this.label = label; }; From 4260913fb76e830b31ba8a99b546729af8a35d20 Mon Sep 17 00:00:00 2001 From: "Ajay Chauhan (OpenERP)" Date: Tue, 4 Sep 2012 16:41:12 +0530 Subject: [PATCH 318/436] [IMP] crm: made state field invisible in form & tree view bzr revid: cha@tinyerp.com-20120904111112-zwjcdwjq9ldkkv73 --- addons/crm/crm_lead_view.xml | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/addons/crm/crm_lead_view.xml b/addons/crm/crm_lead_view.xml index 6a715893a17..9c0fb812fcb 100644 --- a/addons/crm/crm_lead_view.xml +++ b/addons/crm/crm_lead_view.xml @@ -211,7 +211,7 @@ - + @@ -257,7 +257,7 @@ - + @@ -386,7 +386,6 @@ - @@ -515,7 +514,7 @@ - + @@ -562,7 +561,7 @@ - + @@ -600,7 +599,6 @@ - From 41423b675022bcad1b1ac609e48ff11d79f892ef Mon Sep 17 00:00:00 2001 From: "Ajay Chauhan (OpenERP)" Date: Tue, 4 Sep 2012 17:03:05 +0530 Subject: [PATCH 319/436] [IMP] hr_recruitment: made state field invisible in form & tree view & deleted from search view bzr revid: cha@tinyerp.com-20120904113305-b3w9trvw071nt1ts --- .../hr_recruitment/board_hr_recruitment_statistical_view.xml | 2 +- addons/hr_recruitment/hr_recruitment_view.xml | 3 +-- addons/hr_recruitment/report/hr_recruitment_report_view.xml | 1 - 3 files changed, 2 insertions(+), 4 deletions(-) diff --git a/addons/hr_recruitment/board_hr_recruitment_statistical_view.xml b/addons/hr_recruitment/board_hr_recruitment_statistical_view.xml index 461ccdf0bbe..d39a5a74fc1 100644 --- a/addons/hr_recruitment/board_hr_recruitment_statistical_view.xml +++ b/addons/hr_recruitment/board_hr_recruitment_statistical_view.xml @@ -11,7 +11,7 @@ - + diff --git a/addons/hr_recruitment/hr_recruitment_view.xml b/addons/hr_recruitment/hr_recruitment_view.xml index 1ded2e67db9..7eb5187ad88 100644 --- a/addons/hr_recruitment/hr_recruitment_view.xml +++ b/addons/hr_recruitment/hr_recruitment_view.xml @@ -95,7 +95,7 @@ - + @@ -228,7 +228,6 @@ - diff --git a/addons/hr_recruitment/report/hr_recruitment_report_view.xml b/addons/hr_recruitment/report/hr_recruitment_report_view.xml index 30a20a25eb8..1b0b8bfa7c5 100644 --- a/addons/hr_recruitment/report/hr_recruitment_report_view.xml +++ b/addons/hr_recruitment/report/hr_recruitment_report_view.xml @@ -71,7 +71,6 @@ - From 526b38a1f812de77598a9b7580f94fc4b51a52cf Mon Sep 17 00:00:00 2001 From: "Ajay Chauhan (OpenERP)" Date: Tue, 4 Sep 2012 17:19:02 +0530 Subject: [PATCH 320/436] [IMP] crm: removed duplicated field in search view & delete state field too bzr revid: cha@tinyerp.com-20120904114902-rfxk2xleihid083b --- addons/crm/report/crm_lead_report_view.xml | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/addons/crm/report/crm_lead_report_view.xml b/addons/crm/report/crm_lead_report_view.xml index 4283dd8fb3c..35ed9411215 100644 --- a/addons/crm/report/crm_lead_report_view.xml +++ b/addons/crm/report/crm_lead_report_view.xml @@ -105,21 +105,11 @@ - - - - - - - - - Date: Tue, 4 Sep 2012 13:54:16 +0200 Subject: [PATCH 321/436] [IMP] hr_evaluation: send remainder email now uses context values to set some default values when sending an email. This is a basic fix of missing feature due to hr_evaluation/wizard/mail_compose_message override. Updated classic form view of mail.compose.message. Not sure it is usefull to keep two form (one classic and one Chatter)... maybe there is something to do here. bzr revid: tde@openerp.com-20120904115416-zuvna57r3oj37hpu --- addons/hr_evaluation/hr_evaluation_view.xml | 23 ++++++++++---- addons/mail/mail_message.py | 3 +- addons/mail/wizard/mail_compose_message.py | 2 +- .../mail/wizard/mail_compose_message_view.xml | 31 ++++++++++++------- 4 files changed, 40 insertions(+), 19 deletions(-) diff --git a/addons/hr_evaluation/hr_evaluation_view.xml b/addons/hr_evaluation/hr_evaluation_view.xml index 9f64df8808d..78997cff83b 100644 --- a/addons/hr_evaluation/hr_evaluation_view.xml +++ b/addons/hr_evaluation/hr_evaluation_view.xml @@ -267,16 +267,27 @@
      -
      -
      diff --git a/addons/mail/mail_message.py b/addons/mail/mail_message.py index 8fc9fb27b86..d87e503011d 100644 --- a/addons/mail/mail_message.py +++ b/addons/mail/mail_message.py @@ -135,7 +135,8 @@ class mail_message(osv.Model): _defaults = { 'type': 'email', 'date': lambda *a: fields.datetime.now(), - 'author_id': _get_default_author + 'author_id': lambda s,cr,uid,ctx={}: self._get_default_author(cr, uid, ctx), + 'body': '', } #------------------------------------------------------ diff --git a/addons/mail/wizard/mail_compose_message.py b/addons/mail/wizard/mail_compose_message.py index 15f428264ff..3a42336de23 100644 --- a/addons/mail/wizard/mail_compose_message.py +++ b/addons/mail/wizard/mail_compose_message.py @@ -106,7 +106,7 @@ class mail_compose_message(osv.TransientModel): 'mail_compose_message_ir_attachments_rel', 'wizard_id', 'attachment_id', 'Attachments'), 'filter_id': fields.many2one('ir.filters', 'Filters'), - 'body_text': fields.text('Plain-text editor body'), + 'body_text': fields.text('Plain-text Contents'), 'content_subtype': fields.char('Message content subtype', size=32, readonly=1, help="Type of message, usually 'html' or 'plain', used to select "\ "plain-text or rich-text contents accordingly"), diff --git a/addons/mail/wizard/mail_compose_message_view.xml b/addons/mail/wizard/mail_compose_message_view.xml index 4ce5db3f4e3..70b50c154ac 100644 --- a/addons/mail/wizard/mail_compose_message_view.xml +++ b/addons/mail/wizard/mail_compose_message_view.xml @@ -6,34 +6,43 @@ mail.compose.message + + + + + - - - - - - - + + - + - + +
      -
      - mail.compose.message.form + mail.compose.message.form.chatter mail.compose.message 18 From b4e20f0c14e66bb8d5b74910c938a6a90de72f6c Mon Sep 17 00:00:00 2001 From: Xavier Morel Date: Tue, 4 Sep 2012 13:56:27 +0200 Subject: [PATCH 322/436] [IMP] use list.Column's parsed (and potentially altered) modifiers to generate the edition form view This way, listview 'widgets' can alter the modifiers object and influence the corresponding form widget bzr revid: xmo@openerp.com-20120904115627-v626hcb9o0s7bcrp --- addons/web/static/src/js/view_list.js | 5 +++++ .../web/static/src/js/view_list_editable.js | 19 +++++++++++-------- 2 files changed, 16 insertions(+), 8 deletions(-) diff --git a/addons/web/static/src/js/view_list.js b/addons/web/static/src/js/view_list.js index a83f7891a08..40bdee3a07f 100644 --- a/addons/web/static/src/js/view_list.js +++ b/addons/web/static/src/js/view_list.js @@ -2115,6 +2115,11 @@ instance.web.list.ProgressBar = instance.web.list.Column.extend({ } }); instance.web.list.Handle = instance.web.list.Column.extend({ + init: function () { + this._super.apply(this, arguments); + // Handle overrides the field to not be form-editable. + this.modifiers.readonly = true; + }, /** * Return styling hooks for a drag handle * diff --git a/addons/web/static/src/js/view_list_editable.js b/addons/web/static/src/js/view_list_editable.js index 9998eb5fce2..4ea46c51e07 100644 --- a/addons/web/static/src/js/view_list_editable.js +++ b/addons/web/static/src/js/view_list_editable.js @@ -354,14 +354,17 @@ openerp.web.list_editable = function (instance) { 'class': 'oe_form_container', version: '7.0' }); - _(view.arch.children).each(function (widget) { - var modifiers = JSON.parse(widget.attrs.modifiers || '{}'); - widget.attrs.nolabel = true; - if (modifiers['tree_invisible'] || widget.tag === 'button') { - modifiers.invisible = true; - } - widget.attrs.modifiers = JSON.stringify(modifiers); - }); + _(view.arch.children).chain() + .zip(this.columns) + .each(function (ar) { + var widget = ar[0], column = ar[1]; + var modifiers = _.extend({}, column.modifiers); + widget.attrs.nolabel = true; + if (modifiers['tree_invisible'] || widget.tag === 'button') { + modifiers.invisible = true; + } + widget.attrs.modifiers = JSON.stringify(modifiers); + }); return view; }, handle_onwrite: function (source_record) { From b4b104e0d03dccc01e5963b0a71134f925aaa23b Mon Sep 17 00:00:00 2001 From: "vta vta@openerp.com" <> Date: Tue, 4 Sep 2012 13:56:38 +0200 Subject: [PATCH 323/436] [FIX] Document page css. bzr revid: vta@openerp.com-20120904115638-nccbm09g3cof803q --- addons/document_page/static/src/css/document_page.css | 4 ---- 1 file changed, 4 deletions(-) diff --git a/addons/document_page/static/src/css/document_page.css b/addons/document_page/static/src/css/document_page.css index 723ff8d9b11..77597fb4281 100644 --- a/addons/document_page/static/src/css/document_page.css +++ b/addons/document_page/static/src/css/document_page.css @@ -1,7 +1,3 @@ -.oe_form_readonly .oe_notebook { - display: none; -} - .oe_document_page ul, .oe_document_page li { padding: 2px 8px; margin: 2px 8px; From 54ec6e88926df67bb1963d0c20c7ee9cd6599fc9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thibault=20Delavall=C3=A9e?= Date: Tue, 4 Sep 2012 13:57:23 +0200 Subject: [PATCH 324/436] [IMP] hr_evaluation: also updated default values when sending a mass_mail reminder. bzr revid: tde@openerp.com-20120904115723-v5ifjg18l8hmbdj2 --- addons/hr_evaluation/hr_evaluation_view.xml | 5 +++-- addons/mail/wizard/mail_compose_message.py | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/addons/hr_evaluation/hr_evaluation_view.xml b/addons/hr_evaluation/hr_evaluation_view.xml index 78997cff83b..4f49b7ba516 100644 --- a/addons/hr_evaluation/hr_evaluation_view.xml +++ b/addons/hr_evaluation/hr_evaluation_view.xml @@ -399,7 +399,6 @@ action="action_hr_evaluation_interview_tree"/> - + context="{'default_composition_mode': 'mass_mail', + 'default_body_text': 'Hello,\n\nKindly post your response for the survey interview.\n\nThanks', + 'default_subject': 'Reminder to fill up Survey'}"/> Date: Tue, 4 Sep 2012 14:02:40 +0200 Subject: [PATCH 325/436] [IMP] crm_forward_to_partner: on_change_partner_ids -> onchange_partner_ids, already defined on mail.compose.message. bzr revid: tde@openerp.com-20120904120240-hidqb9e3ivys9vpo --- .../wizard/crm_forward_to_partner.py | 11 ++--------- .../wizard/crm_forward_to_partner_view.xml | 2 +- 2 files changed, 3 insertions(+), 10 deletions(-) diff --git a/addons/crm_partner_assign/wizard/crm_forward_to_partner.py b/addons/crm_partner_assign/wizard/crm_forward_to_partner.py index 6ed8da7b231..0cac38e0163 100644 --- a/addons/crm_partner_assign/wizard/crm_forward_to_partner.py +++ b/addons/crm_partner_assign/wizard/crm_forward_to_partner.py @@ -89,11 +89,6 @@ class crm_lead_forward_to_partner(osv.osv_memory): lead = self.pool.get(model).browse(cr, uid, res_id, context=context) body = self._get_message_body(cr, uid, lead, history_mode, context=context) return {'value': {'body': body}} - - def on_change_partner_ids(self, cr, uid, ids, partner_ids, context=None): - """ TDE-TODO: Keep void; maybe we could check that partner_ids have - email defined. """ - return {} def create(self, cr, uid, values, context=None): """ TDE-HACK: remove 'type' from context, because when viewing an @@ -106,9 +101,7 @@ class crm_lead_forward_to_partner(osv.osv_memory): return new_id def action_forward(self, cr, uid, ids, context=None): - """ - Forward the lead to a partner - """ + """ Forward the lead to a partner """ if context is None: context = {} res = {'type': 'ir.actions.act_window_close'} @@ -126,7 +119,7 @@ class crm_lead_forward_to_partner(osv.osv_memory): self.send_mail(cr, uid, ids, context=context) # for case in lead.browse(cr, uid, lead_ids, context=context): - # TDE: WHAT TO DO WITH THAT ? + # TODO: WHAT TO DO WITH THAT ? # if (this.send_to == 'partner' and this.partner_id): # lead.assign_partner(cr, uid, [case.id], this.partner_id.id, context=context) diff --git a/addons/crm_partner_assign/wizard/crm_forward_to_partner_view.xml b/addons/crm_partner_assign/wizard/crm_forward_to_partner_view.xml index fe9e1476817..0d8f27eec74 100644 --- a/addons/crm_partner_assign/wizard/crm_forward_to_partner_view.xml +++ b/addons/crm_partner_assign/wizard/crm_forward_to_partner_view.xml @@ -16,7 +16,7 @@ on_change="on_change_history_mode(history_mode, model, res_id)"/> + on_change="onchange_partner_ids(partner_ids)"/> From 6838e9193b2a873b66a9d58873085b0b533fdbd7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thibault=20Delavall=C3=A9e?= Date: Tue, 4 Sep 2012 14:18:02 +0200 Subject: [PATCH 326/436] [FIX] mail_message: fixed typo. bzr revid: tde@openerp.com-20120904121802-7lbajrbon7f83vux --- 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 d87e503011d..d22bd4c2e3c 100644 --- a/addons/mail/mail_message.py +++ b/addons/mail/mail_message.py @@ -135,7 +135,7 @@ class mail_message(osv.Model): _defaults = { 'type': 'email', 'date': lambda *a: fields.datetime.now(), - 'author_id': lambda s,cr,uid,ctx={}: self._get_default_author(cr, uid, ctx), + 'author_id': lambda s,cr,uid,ctx={}: s._get_default_author(cr, uid, ctx), 'body': '', } From 1f1a0ef14b9632847b909485265417b8c6f5fc0f Mon Sep 17 00:00:00 2001 From: Xavier Morel Date: Tue, 4 Sep 2012 14:18:23 +0200 Subject: [PATCH 327/436] [IMP] cancel pending edition when hiding the list view, if editable otherwise the edition disappears but part of the list view (e.g. buttons at the top) remain in the wrong state bzr revid: xmo@openerp.com-20120904121823-kejzzquax2hdedoo --- addons/web/static/src/js/view_list_editable.js | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/addons/web/static/src/js/view_list_editable.js b/addons/web/static/src/js/view_list_editable.js index 4ea46c51e07..bdd266454dd 100644 --- a/addons/web/static/src/js/view_list_editable.js +++ b/addons/web/static/src/js/view_list_editable.js @@ -55,6 +55,12 @@ openerp.web.list_editable = function (instance) { instance.web.bus.off('resize', this, this.resize_fields); this._super(); }, + do_hide: function () { + if (this.editor.is_editing()) { + this.cancel_edition(true); + } + this._super(); + }, /** * Handles the activation of a record in editable mode (making a record * editable), called *after* the record has become editable. From 674ea0583077ed4d19547017b733a4fe44ee8a89 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thibault=20Delavall=C3=A9e?= Date: Tue, 4 Sep 2012 14:18:38 +0200 Subject: [PATCH 328/436] [IMP] mail_thread: cleaned comments and some var name in the modified many2many field. Also made mail_thread an AbstractModel. bzr revid: tde@openerp.com-20120904121838-rpaesid3stxm8gus --- addons/mail/mail_thread.py | 43 +++++++++++++++++--------------------- 1 file changed, 19 insertions(+), 24 deletions(-) diff --git a/addons/mail/mail_thread.py b/addons/mail/mail_thread.py index 4e35f3fd197..6e5f594354a 100644 --- a/addons/mail/mail_thread.py +++ b/addons/mail/mail_thread.py @@ -29,7 +29,6 @@ import time import tools import xmlrpclib -from email.utils import parsedate from email.message import Message from mail_message import decode from osv import osv, fields @@ -42,24 +41,20 @@ def decode_header(message, header, separator=' '): return separator.join(map(decode,message.get_all(header, []))) class many2many_reference(fields.many2many): - """ many2many_reference is an override of fields.many2many. It manages - many2many-like table where one id is given by two fields, res_model - and res_id. - """ + """ many2many_reference manages many2many fields where one id is found + by a reference-like key (a char column in addition to the foreign id). + The reference_column attribute on the many2many fields is used; + if not defined, ``res_model`` is used. """ def _get_query_and_where_params(self, cr, model, ids, values, where_params): - """ Add in where: - - ``mail_followers``.res_model = 'crm.lead' - """ - # reference column name: given by attribute or res_model - ref_col_name = self.ref_col_name if self.ref_col_name else 'res_model' - values.update({'ref_col_name': ref_col_name, 'ref_col_value': model._name}) - + """ Add in where condition like mail_followers.res_model = 'crm.lead' """ + reference_column = self.reference_column if self.reference_column else 'res_model' + values.update(reference_column=reference_column, reference_value=model._name) query = 'SELECT %(rel)s.%(id2)s, %(rel)s.%(id1)s \ FROM %(rel)s, %(from_c)s \ WHERE %(rel)s.%(id1)s IN %%s \ AND %(rel)s.%(id2)s = %(tbl)s.id \ - AND %(rel)s.%(ref_col_name)s = \'%(ref_col_value)s\' \ + AND %(rel)s.%(reference_column)s = \'%(reference_value)s\' \ %(where_c)s \ %(order_by)s \ %(limit)s \ @@ -68,41 +63,41 @@ class many2many_reference(fields.many2many): return query, where_params def set(self, cr, model, id, name, values, user=None, context=None): - """ Override to add the res_model field in queries. """ + """ Override to add the reference field in queries. """ if not values: return rel, id1, id2 = self._sql_names(model) obj = model.pool.get(self._obj) # reference column name: given by attribute or res_model - ref_col_name = self.ref_col_name if self.ref_col_name else 'res_model' + reference_column = self.reference_column if self.reference_column else 'res_model' for act in values: if not (isinstance(act, list) or isinstance(act, tuple)) or not act: continue if act[0] == 0: idnew = obj.create(cr, user, act[2], context=context) - cr.execute('INSERT INTO '+rel+' ('+id1+','+id2+','+ref_col_name+') VALUES (%s,%s,%s)', (id, idnew, model._name)) + cr.execute('INSERT INTO '+rel+' ('+id1+','+id2+','+reference_column+') VALUES (%s,%s,%s)', (id, idnew, model._name)) elif act[0] == 3: - cr.execute('DELETE FROM '+rel+' WHERE '+id1+'=%s AND '+id2+'=%s AND '+ref_col_name+'=%s', (id, act[1], model._name)) + cr.execute('DELETE FROM '+rel+' WHERE '+id1+'=%s AND '+id2+'=%s AND '+reference_column+'=%s', (id, act[1], model._name)) elif act[0] == 4: # following queries are in the same transaction - so should be relatively safe - cr.execute('SELECT 1 FROM '+rel+' WHERE '+id1+'=%s AND '+id2+'=%s AND '+ref_col_name+'=%s', (id, act[1], model._name)) + cr.execute('SELECT 1 FROM '+rel+' WHERE '+id1+'=%s AND '+id2+'=%s AND '+reference_column+'=%s', (id, act[1], model._name)) if not cr.fetchone(): - cr.execute('INSERT INTO '+rel+' ('+id1+','+id2+','+ref_col_name+') VALUES (%s,%s,%s)', (id, act[1], model._name)) + cr.execute('INSERT INTO '+rel+' ('+id1+','+id2+','+reference_column+') VALUES (%s,%s,%s)', (id, act[1], model._name)) elif act[0] == 5: - cr.execute('delete from '+rel+' where '+id1+' = %s AND '+ref_col_name+'=%s', (id, model._name)) + cr.execute('delete from '+rel+' where '+id1+' = %s AND '+reference_column+'=%s', (id, model._name)) elif act[0] == 6: d1, d2,tables = obj.pool.get('ir.rule').domain_get(cr, user, obj._name, context=context) if d1: d1 = ' and ' + ' and '.join(d1) else: d1 = '' - cr.execute('DELETE FROM '+rel+' WHERE '+id1+'=%s AND '+ref_col_name+'=%s AND '+id2+' IN (SELECT '+rel+'.'+id2+' FROM '+rel+', '+','.join(tables)+' WHERE '+rel+'.'+id1+'=%s AND '+rel+'.'+id2+' = '+obj._table+'.id '+ d1 +')', [id, model._name, id]+d2) + cr.execute('DELETE FROM '+rel+' WHERE '+id1+'=%s AND '+reference_column+'=%s AND '+id2+' IN (SELECT '+rel+'.'+id2+' FROM '+rel+', '+','.join(tables)+' WHERE '+rel+'.'+id1+'=%s AND '+rel+'.'+id2+' = '+obj._table+'.id '+ d1 +')', [id, model._name, id]+d2) for act_nbr in act[2]: - cr.execute('INSERT INTO '+rel+' ('+id1+','+id2+','+ref_col_name+') VALUES (%s,%s,%s)', (id, act_nbr, model._name)) + cr.execute('INSERT INTO '+rel+' ('+id1+','+id2+','+reference_column+') VALUES (%s,%s,%s)', (id, act_nbr, model._name)) # cases 1, 2: performs write and unlink -> default implementation is ok else: return super(many2many_reference, self).set(cr, model, id, name, values, user, context) -class mail_thread(osv.Model): +class mail_thread(osv.AbstractModel): ''' mail_thread model is meant to be inherited by any model that needs to act as a discussion topic on which messages can be attached. Public methods are prefixed with ``message_`` in order to avoid name @@ -161,7 +156,7 @@ class mail_thread(osv.Model): type='boolean', string='Is a Follower', multi='_get_message_data'), 'message_follower_ids': many2many_reference('res.partner', 'mail_followers', 'res_id', 'partner_id', - ref_col_name='res_model', string='Followers'), + reference_column='res_model', string='Followers'), 'message_comment_ids': fields.one2many('mail.message', 'res_id', domain=lambda self: [('model','=',self._name),('type','in',('comment','email'))], string='Related Messages', From a613b710e92c09ed0da76fef145ef91a7a041d3d Mon Sep 17 00:00:00 2001 From: "vta vta@openerp.com" <> Date: Tue, 4 Sep 2012 14:41:17 +0200 Subject: [PATCH 329/436] [FIX] Fixed padding in popups. bzr revid: vta@openerp.com-20120904124117-3ilh4ryfu4r0wpqx --- addons/web/static/src/css/base.css | 95 ++++++------- addons/web/static/src/css/base.sass | 204 ++++++++++++++-------------- 2 files changed, 151 insertions(+), 148 deletions(-) diff --git a/addons/web/static/src/css/base.css b/addons/web/static/src/css/base.css index dd168653f03..f28e3c6aa0d 100644 --- a/addons/web/static/src/css/base.css +++ b/addons/web/static/src/css/base.css @@ -69,7 +69,6 @@ display: none !important; } } - .openerp.openerp_webclient_container { height: 100%; position: relative; @@ -1933,6 +1932,35 @@ margin: -16px -16px 0 -16px; padding: 8px; } +.openerp .oe_form_sheetbg { + padding: 8px 0; +} +.openerp .oe_form_sheet_width { + min-width: 650px; + max-width: 860px; + margin: 0 auto; +} +.openerp .oe_form_sheet { + background: white; + min-height: 330px; + padding: 16px; +} +.openerp .oe_application .oe_form_sheetbg { + background: url(/web/static/src/img/form_sheetbg.png); + border-bottom: 1px solid #dddddd; +} +.openerp .oe_application .oe_form_sheet { + border: 1px solid #afafb6; + -moz-box-shadow: 0 0 10px rgba(0, 0, 0, 0.3); + -webkit-box-shadow: 0 0 10px rgba(0, 0, 0, 0.3); + box-shadow: 0 0 10px rgba(0, 0, 0, 0.3); +} +.openerp .oe_application .oe_form_sheet .ui-tabs { + margin: 0 -16px; +} +.openerp .oe_application .oe_form_sheet .oe_notebook_page { + padding: 0 16px; +} .openerp .oe_form header { position: relative; border-bottom: 1px solid #cacaca; @@ -1972,11 +2000,11 @@ .openerp .oe_form div.oe_form_configuration .oe_form_group_cell div div { padding: 1px 0; } -.openerp ul.oe_form_steps, .openerp ul.oe_form_steps_clickable { +.openerp .oe_form ul.oe_form_steps, .openerp .oe_form ul.oe_form_steps_clickable { display: inline-block; padding-right: 18px; } -.openerp ul.oe_form_steps li, .openerp ul.oe_form_steps_clickable li { +.openerp .oe_form ul.oe_form_steps li, .openerp .oe_form ul.oe_form_steps_clickable li { display: inline-block; margin-right: -20px; background-color: #fcfcfc; @@ -1987,20 +2015,20 @@ background-image: -o-linear-gradient(top, #fcfcfc, #dedede); background-image: linear-gradient(to bottom, #fcfcfc, #dedede); } -.openerp ul.oe_form_steps li:first-child .label, .openerp ul.oe_form_steps_clickable li:first-child .label { +.openerp .oe_form ul.oe_form_steps li:first-child .label, .openerp .oe_form ul.oe_form_steps_clickable li:first-child .label { border-left: 1px solid #cacaca; padding-left: 14px; } -.openerp ul.oe_form_steps li:last-child, .openerp ul.oe_form_steps_clickable li:last-child { +.openerp .oe_form ul.oe_form_steps li:last-child, .openerp .oe_form ul.oe_form_steps_clickable li:last-child { border-right: 1px solid #cacaca; } -.openerp ul.oe_form_steps li:last-child .label, .openerp ul.oe_form_steps_clickable li:last-child .label { +.openerp .oe_form ul.oe_form_steps li:last-child .label, .openerp .oe_form ul.oe_form_steps_clickable li:last-child .label { padding-right: 14px; } -.openerp ul.oe_form_steps li:last-child .arrow, .openerp ul.oe_form_steps_clickable li:last-child .arrow { +.openerp .oe_form ul.oe_form_steps li:last-child .arrow, .openerp .oe_form ul.oe_form_steps_clickable li:last-child .arrow { display: none; } -.openerp ul.oe_form_steps li .label, .openerp ul.oe_form_steps_clickable li .label { +.openerp .oe_form ul.oe_form_steps li .label, .openerp .oe_form ul.oe_form_steps_clickable li .label { color: #4c4c4c; text-shadow: 0 1px 1px #fcfcfc, 0 -1px 1px #dedede; padding: 7px; @@ -2009,14 +2037,14 @@ margin: 0; position: relative; } -.openerp ul.oe_form_steps li .arrow, .openerp ul.oe_form_steps_clickable li .arrow { +.openerp .oe_form ul.oe_form_steps li .arrow, .openerp .oe_form ul.oe_form_steps_clickable li .arrow { width: 17px; display: inline-block; vertical-align: top; overflow: hidden; margin-left: -5px; } -.openerp ul.oe_form_steps li .arrow span, .openerp ul.oe_form_steps_clickable li .arrow span { +.openerp .oe_form ul.oe_form_steps li .arrow span, .openerp .oe_form ul.oe_form_steps_clickable li .arrow span { position: relative; width: 24px; height: 24px; @@ -2036,7 +2064,7 @@ -o-transform: rotate(45deg); -ms-transform: rotate(45deg); } -.openerp ul.oe_form_steps li.oe_active, .openerp ul.oe_form_steps_clickable li.oe_active { +.openerp .oe_form ul.oe_form_steps li.oe_active, .openerp .oe_form ul.oe_form_steps_clickable li.oe_active { background-color: #729fcf; background-image: -webkit-gradient(linear, left top, left bottom, from(#729fcf), to(#3465a4)); background-image: -webkit-linear-gradient(top, #729fcf, #3465a4); @@ -2045,20 +2073,20 @@ background-image: -o-linear-gradient(top, #729fcf, #3465a4); background-image: linear-gradient(to bottom, #729fcf, #3465a4); } -.openerp ul.oe_form_steps li.oe_active .arrow span, .openerp ul.oe_form_steps_clickable li.oe_active .arrow span { +.openerp .oe_form ul.oe_form_steps li.oe_active .arrow span, .openerp .oe_form ul.oe_form_steps_clickable li.oe_active .arrow span { background-color: #3465a4; background: -moz-linear-gradient(135deg, #3465a4, #729fcf); background: -o-linear-gradient(135deg, #729fcf, #3465a4); background: -webkit-gradient(linear, left top, right bottom, from(#729fcf), to(#3465a4)); } -.openerp ul.oe_form_steps li.oe_active .label, .openerp ul.oe_form_steps_clickable li.oe_active .label { +.openerp .oe_form ul.oe_form_steps li.oe_active .label, .openerp .oe_form ul.oe_form_steps_clickable li.oe_active .label { color: white; text-shadow: 0 1px 1px #729fcf, 0 -1px 1px #3465a4; } -.openerp ul.oe_form_steps_clickable li { +.openerp .oe_form ul.oe_form_steps_clickable li { cursor: pointer; } -.openerp ul.oe_form_steps_clickable li:hover { +.openerp .oe_form ul.oe_form_steps_clickable li:hover { background-color: #e8e8e8; background-image: -webkit-gradient(linear, left top, left bottom, from(#e8e8e8), to(#cacaca)); background-image: -webkit-linear-gradient(top, #e8e8e8, #cacaca); @@ -2067,10 +2095,10 @@ background-image: -o-linear-gradient(top, #e8e8e8, #cacaca); background-image: linear-gradient(to bottom, #e8e8e8, #cacaca); } -.openerp ul.oe_form_steps_clickable li:hover .label { +.openerp .oe_form ul.oe_form_steps_clickable li:hover .label { text-shadow: 0 -1px 1px #fcfcfc, 0 1px 1px #dedede; } -.openerp ul.oe_form_steps_clickable li:hover .arrow span { +.openerp .oe_form ul.oe_form_steps_clickable li:hover .arrow span { background-color: #e8e8e8; background-image: -webkit-gradient(linear, left top, left bottom, from(#e8e8e8), to(#cacaca)); background-image: -webkit-linear-gradient(top, #e8e8e8, #cacaca); @@ -2079,10 +2107,10 @@ background-image: -o-linear-gradient(top, #e8e8e8, #cacaca); background-image: linear-gradient(to bottom, #e8e8e8, #cacaca); } -.openerp ul.oe_form_steps_clickable li .label { +.openerp .oe_form ul.oe_form_steps_clickable li .label { color: #7c7bad; } -.openerp ul.oe_form_steps_clickable li.oe_active:hover { +.openerp .oe_form ul.oe_form_steps_clickable li.oe_active:hover { background-color: #4c85c2; background-image: -webkit-gradient(linear, left top, left bottom, from(#4c85c2), to(#284d7d)); background-image: -webkit-linear-gradient(top, #4c85c2, #284d7d); @@ -2091,10 +2119,10 @@ background-image: -o-linear-gradient(top, #4c85c2, #284d7d); background-image: linear-gradient(to bottom, #4c85c2, #284d7d); } -.openerp ul.oe_form_steps_clickable li.oe_active:hover .label { +.openerp .oe_form ul.oe_form_steps_clickable li.oe_active:hover .label { text-shadow: 0 -1px 1px #729fcf, 0 1px 1px #3465a4; } -.openerp ul.oe_form_steps_clickable li.oe_active:hover .arrow span { +.openerp .oe_form ul.oe_form_steps_clickable li.oe_active:hover .arrow span { background-color: #284d7d; background: -moz-linear-gradient(135deg, #284d7d, #4c85c2); background: -o-linear-gradient(135deg, #4c85c2, #284d7d); @@ -2126,31 +2154,6 @@ .openerp .oe_form .oe_subtotal_footer label.oe_form_label_help { font-weight: normal; } -.openerp .oe_application .oe_form_sheetbg { - background: url(/web/static/src/img/form_sheetbg.png); - padding: 8px 0; - border-bottom: 1px solid #dddddd; -} -.openerp .oe_application .oe_form_sheet_width { - min-width: 650px; - max-width: 860px; - margin: 0 auto; -} -.openerp .oe_application .oe_form_sheet { - background: white; - min-height: 330px; - padding: 16px; - border: 1px solid #afafb6; - -moz-box-shadow: 0 0 10px rgba(0, 0, 0, 0.3); - -webkit-box-shadow: 0 0 10px rgba(0, 0, 0, 0.3); - box-shadow: 0 0 10px rgba(0, 0, 0, 0.3); -} -.openerp .oe_application .oe_form_sheet .ui-tabs { - margin: 0 -16px; -} -.openerp .oe_application .oe_form_sheet .oe_notebook_page { - padding: 0 16px; -} .openerp .oe_form .oe_form_button { margin: 2px; } diff --git a/addons/web/static/src/css/base.sass b/addons/web/static/src/css/base.sass index 537988e9c4a..4f231331ff7 100644 --- a/addons/web/static/src/css/base.sass +++ b/addons/web/static/src/css/base.sass @@ -1498,11 +1498,34 @@ $sheet-max-width: 860px display: none !important .oe_form .oe_form_field_date width: auto + // Sheet and padding .oe_form_nosheet margin: 16px > header margin: -16px -16px 0 -16px padding: 8px + .oe_form_sheetbg + padding: 8px 0 + .oe_form_sheet_width + min-width: 650px + max-width: $sheet-max-width + margin: 0 auto + .oe_form_sheet + background: white + min-height: 330px + padding: 16px + // Sheet inline mode + .oe_application + .oe_form_sheetbg + background: url(/web/static/src/img/form_sheetbg.png) + border-bottom: 1px solid #ddd + .oe_form_sheet + border: 1px solid $tag-border + @include box-shadow(0 0 10px rgba(0,0,0,0.3)) + .ui-tabs + margin: 0 -16px + .oe_notebook_page + padding: 0 16px // }}} // FormView.custom tags and classes {{{ .oe_form @@ -1532,115 +1555,92 @@ $sheet-max-width: 860px padding: 2px 0 .oe_form_group_cell div div padding: 1px 0 - - - ul.oe_form_steps, ul.oe_form_steps_clickable - display: inline-block - padding-right: 18px - li + ul.oe_form_steps, ul.oe_form_steps_clickable display: inline-block - margin-right: -20px - @include vertical-gradient(#fcfcfc, #dedede) - &:first-child .label - border-left: 1px solid #cacaca - padding-left: 14px - &:last-child - border-right: 1px solid #cacaca - .label - padding-right: 14px - .arrow - display: none - .label - color: #4c4c4c - text-shadow: 0 1px 1px #fcfcfc, 0 -1px 1px #dedede - padding: 7px + padding-right: 18px + li display: inline-block - padding-left: 24px - margin: 0 - position: relative - .arrow - width: 17px - display: inline-block - vertical-align: top - overflow: hidden - margin-left: -5px - span - position: relative - width: 24px - height: 24px + margin-right: -20px + @include vertical-gradient(#fcfcfc, #dedede) + &:first-child .label + border-left: 1px solid #cacaca + padding-left: 14px + &:last-child + border-right: 1px solid #cacaca + .label + padding-right: 14px + .arrow + display: none + .label + color: #4c4c4c + text-shadow: 0 1px 1px #fcfcfc, 0 -1px 1px #dedede + padding: 7px display: inline-block - margin-left: -12px - margin-top: 3px - box-shadow: -1px 1px 2px rgba(255,255,255,0.2), inset -1px 1px 1px rgba(0,0,0,0.2) - @include skew-gradient(#fcfcfc, #dedede) - @include radius(3px) - @include transform(rotate(45deg)) - li.oe_active - @include vertical-gradient(#729fcf, #3465a4) - .arrow span - @include skew-gradient(#729fcf, #3465a4) - .label - color: white - text-shadow: 0 1px 1px #729fcf, 0 -1px 1px #3465a4 - - ul.oe_form_steps_clickable - li - cursor: pointer - &:hover - @include vertical-gradient(darken(#fcfcfc, 8%), darken(#dedede, 8%)) - .label - text-shadow: 0 -1px 1px #fcfcfc, 0 1px 1px #dedede + padding-left: 24px + margin: 0 + position: relative + .arrow + width: 17px + display: inline-block + vertical-align: top + overflow: hidden + margin-left: -5px + span + position: relative + width: 24px + height: 24px + display: inline-block + margin-left: -12px + margin-top: 3px + box-shadow: -1px 1px 2px rgba(255,255,255,0.2), inset -1px 1px 1px rgba(0,0,0,0.2) + @include skew-gradient(#fcfcfc, #dedede) + @include radius(3px) + @include transform(rotate(45deg)) + li.oe_active + @include vertical-gradient(#729fcf, #3465a4) .arrow span + @include skew-gradient(#729fcf, #3465a4) + .label + color: white + text-shadow: 0 1px 1px #729fcf, 0 -1px 1px #3465a4 + ul.oe_form_steps_clickable + li + cursor: pointer + &:hover @include vertical-gradient(darken(#fcfcfc, 8%), darken(#dedede, 8%)) - .label - color: $link-color - li.oe_active - &:hover - @include vertical-gradient(darken(#729fcf, 10%), darken(#3465a4, 10%)) + .label + text-shadow: 0 -1px 1px #fcfcfc, 0 1px 1px #dedede + .arrow span + @include vertical-gradient(darken(#fcfcfc, 8%), darken(#dedede, 8%)) .label - text-shadow: 0 -1px 1px #729fcf, 0 1px 1px #3465a4 - .arrow span - @include skew-gradient(darken(#729fcf, 10%), darken(#3465a4, 10%)) + color: $link-color + li.oe_active + &:hover + @include vertical-gradient(darken(#729fcf, 10%), darken(#3465a4, 10%)) + .label + text-shadow: 0 -1px 1px #729fcf, 0 1px 1px #3465a4 + .arrow span + @include skew-gradient(darken(#729fcf, 10%), darken(#3465a4, 10%)) + .oe_subtotal_footer + width: 1% !important + td.oe_form_group_cell + text-align: right + padding: 0 !important + td.oe_form_group_cell_label + border-right: none + .oe_subtotal_footer_separator + width: 108px + border-top: 1px solid #cacaca + font-weight: bold + font-size: 18px + label:after + content: ":" + label.oe_subtotal_footer_separator + font-weight: bold !important + padding: 2px 11px 2px 0px !important + label.oe_form_label_help + font-weight: normal - .oe_form .oe_subtotal_footer - width: 1% !important - td.oe_form_group_cell - text-align: right - padding: 0 !important - td.oe_form_group_cell_label - border-right: none - .oe_subtotal_footer_separator - width: 108px - border-top: 1px solid #cacaca - font-weight: bold - font-size: 18px - label:after - content: ":" - label.oe_subtotal_footer_separator - font-weight: bold !important - padding: 2px 11px 2px 0px !important - label.oe_form_label_help - font-weight: normal - // no sheet in popups - .oe_application - .oe_form_sheetbg - background: url(/web/static/src/img/form_sheetbg.png) - padding: 8px 0 - border-bottom: 1px solid #ddd - .oe_form_sheet_width - min-width: 650px - max-width: $sheet-max-width - margin: 0 auto - .oe_form_sheet - background: white - min-height: 330px - padding: 16px - border: 1px solid $tag-border - @include box-shadow(0 0 10px rgba(0,0,0,0.3)) - .ui-tabs - margin: 0 -16px - .oe_notebook_page - padding: 0 16px // }}} // FormView.group {{{ .oe_form From abdbc220d37ff68312db1b9c43c1ed801b1ad9ec Mon Sep 17 00:00:00 2001 From: Xavier Morel Date: Tue, 4 Sep 2012 14:49:21 +0200 Subject: [PATCH 330/436] [REM] incomplete and unused bounce CSS animation bzr revid: xmo@openerp.com-20120904124921-yur5k231wp8gn7vy --- addons/web/static/src/css/base.css | 49 ++--------------------------- addons/web/static/src/css/base.sass | 46 +++++++++------------------ 2 files changed, 18 insertions(+), 77 deletions(-) diff --git a/addons/web/static/src/css/base.css b/addons/web/static/src/css/base.css index 25df1334d21..de30642dfd6 100644 --- a/addons/web/static/src/css/base.css +++ b/addons/web/static/src/css/base.css @@ -20,50 +20,6 @@ font-style: normal; } -@-moz-keyframes bounce { - 0% { - -moz-transform: scale(0); - opacity: 0; - } - - 50% { - -moz-transform: scale(1.3); - opacity: 0.4; - } - - 75% { - -moz-transform: scale(0.9); - opacity: 0.7; - } - - 100% { - -moz-transform: scale(1); - opacity: 1; - } -} - -@-webkit-keyframes bounce { - 0% { - -webkit-transform: scale(0); - opacity: 0; - } - - 50% { - -webkit-transform: scale(1.3); - opacity: 0.4; - } - - 75% { - -webkit-transform: scale(0.9); - opacity: 0.7; - } - - 100% { - -webkit-transform: scale(1); - opacity: 1; - } -} - @media print { .oe_topbar, .oe_leftbar, .oe_loading { display: none !important; @@ -2062,10 +2018,11 @@ -moz-border-radius: 3px; -webkit-border-radius: 3px; border-radius: 3px; - -moz-transform: rotate(45deg); -webkit-transform: rotate(45deg); - -o-transform: rotate(45deg); + -moz-transform: rotate(45deg); -ms-transform: rotate(45deg); + -o-transform: rotate(45deg); + transform: rotate(45deg); } .openerp .oe_form ul.oe_form_steps li.oe_active, .openerp .oe_form ul.oe_form_steps_clickable li.oe_active { background-color: #729fcf; diff --git a/addons/web/static/src/css/base.sass b/addons/web/static/src/css/base.sass index 3c61702a60c..82c8e1a57ab 100644 --- a/addons/web/static/src/css/base.sass +++ b/addons/web/static/src/css/base.sass @@ -95,10 +95,23 @@ $sheet-max-width: 860px background: -webkit-gradient(linear, left top, right bottom, from($startColor), to($endColor)) @mixin transform($transform) - -moz-transform: $transform -webkit-transform: $transform - -o-transform: $transform + -moz-transform: $transform -ms-transform: $transform + -o-transform: $transform + transform: $transform + +@mixin keyframes($name) + @-webkit-keyframes #{$name} + @content + @-moz-keyframes #{$name} + @content + @-ms-keyframes #{$name} + @content + @-o-keyframes #{$name} + @content + @keyframes #{$name} + @content // Transforms the (readable) text of an inline element into an mmlicons icon, // allows for actual readable text in-code (and in readers?) with iconic looks @@ -112,35 +125,6 @@ $sheet-max-width: 860px color: $color // }}} -// CSS animation bounces {{{ -@-moz-keyframes bounce - 0% - -moz-transform: scale(0) - opacity: 0 - 50% - -moz-transform: scale(1.3) - opacity: 0.4 - 75% - -moz-transform: scale(0.9) - opacity: 0.7 - 100% - -moz-transform: scale(1) - opacity: 1 - -@-webkit-keyframes bounce - 0% - -webkit-transform: scale(0) - opacity: 0 - 50% - -webkit-transform: scale(1.3) - opacity: 0.4 - 75% - -webkit-transform: scale(0.9) - opacity: 0.7 - 100% - -webkit-transform: scale(1) - opacity: 1 -// }}} @media print .oe_topbar, .oe_leftbar, .oe_loading From 4dc1a363f06ec02ba39ffcff54ccf63318596e60 Mon Sep 17 00:00:00 2001 From: "Ajay Chauhan (OpenERP)" Date: Tue, 4 Sep 2012 18:22:33 +0530 Subject: [PATCH 331/436] [IMP] project: made state field invisible in form & tree view & deleted from search view bzr revid: cha@tinyerp.com-20120904125233-lq4wh9epkteom0z3 --- addons/project/board_project_view.xml | 2 +- addons/project/project_view.xml | 7 +++---- addons/project/report/project_report_view.xml | 1 - 3 files changed, 4 insertions(+), 6 deletions(-) diff --git a/addons/project/board_project_view.xml b/addons/project/board_project_view.xml index 2023ba5a0b9..122536d3690 100644 --- a/addons/project/board_project_view.xml +++ b/addons/project/board_project_view.xml @@ -16,7 +16,7 @@ - + diff --git a/addons/project/project_view.xml b/addons/project/project_view.xml index 7da9b31da18..5a805e153e3 100644 --- a/addons/project/project_view.xml +++ b/addons/project/project_view.xml @@ -484,7 +484,7 @@ - + @@ -500,7 +500,7 @@ - + @@ -609,7 +609,7 @@ - + @@ -676,7 +676,6 @@ - diff --git a/addons/project/report/project_report_view.xml b/addons/project/report/project_report_view.xml index 5d6132b1b8f..1d60479f65c 100644 --- a/addons/project/report/project_report_view.xml +++ b/addons/project/report/project_report_view.xml @@ -79,7 +79,6 @@ - From 09fc23c99ef172dc3e8c8ceeaee40709c5091477 Mon Sep 17 00:00:00 2001 From: Minh Tran Date: Tue, 4 Sep 2012 15:05:09 +0200 Subject: [PATCH 332/436] changed red button style bzr revid: mit@openerp.com-20120904130509-94atd9x6rf3lb0sq --- addons/web/static/src/css/base.css | 88 +++++++++++++---------------- addons/web/static/src/css/base.sass | 33 ++++++----- 2 files changed, 56 insertions(+), 65 deletions(-) diff --git a/addons/web/static/src/css/base.css b/addons/web/static/src/css/base.css index e875ff9fab2..52e74b7c17e 100644 --- a/addons/web/static/src/css/base.css +++ b/addons/web/static/src/css/base.css @@ -419,40 +419,35 @@ background: #dc5f59; } .openerp button.oe_highlight { - background-color: #dc5f59; - background-image: -webkit-gradient(linear, left top, left bottom, from(#dc5f59), to(#b33630)); - background-image: -webkit-linear-gradient(top, #dc5f59, #b33630); - background-image: -moz-linear-gradient(top, #dc5f59, #b33630); - background-image: -ms-linear-gradient(top, #dc5f59, #b33630); - background-image: -o-linear-gradient(top, #dc5f59, #b33630); - background-image: linear-gradient(to bottom, #dc5f59, #b33630); - -moz-box-shadow: none; - -webkit-box-shadow: none; - box-shadow: none; -} -.openerp button.oe_highlight:active { - background-color: #b33630; - background-image: -webkit-gradient(linear, left top, left bottom, from(#b33630), to(#dc5f59)); - background-image: -webkit-linear-gradient(top, #b33630, #dc5f59); - background-image: -moz-linear-gradient(top, #b33630, #dc5f59); - background-image: -ms-linear-gradient(top, #b33630, #dc5f59); - background-image: -o-linear-gradient(top, #b33630, #dc5f59); - background-image: linear-gradient(to bottom, #b33630, #dc5f59); - -moz-box-shadow: none; - -webkit-box-shadow: none; - box-shadow: none; + border: 1px solid #795151; + background-color: #df3f3f; + background-image: -webkit-gradient(linear, left top, left bottom, from(#df3f3f), to(#a21a1a)); + background-image: -webkit-linear-gradient(top, #df3f3f, #a21a1a); + background-image: -moz-linear-gradient(top, #df3f3f, #a21a1a); + background-image: -ms-linear-gradient(top, #df3f3f, #a21a1a); + background-image: -o-linear-gradient(top, #df3f3f, #a21a1a); + background-image: linear-gradient(to bottom, #df3f3f, #a21a1a); + -moz-box-shadow: 0 1px 2px rgba(0, 0, 0, 0.1), 0 1px 1px rgba(155, 155, 155, 0.4) inset; + -webkit-box-shadow: 0 1px 2px rgba(0, 0, 0, 0.1), 0 1px 1px rgba(155, 155, 155, 0.4) inset; + box-shadow: 0 1px 2px rgba(0, 0, 0, 0.1), 0 1px 1px rgba(155, 155, 155, 0.4) inset; } .openerp button.oe_highlight:hover { - background-color: #df6b66; - background-image: -webkit-gradient(linear, left top, left bottom, from(#df6b66), to(#bf3a33)); - background-image: -webkit-linear-gradient(top, #df6b66, #bf3a33); - background-image: -moz-linear-gradient(top, #df6b66, #bf3a33); - background-image: -ms-linear-gradient(top, #df6b66, #bf3a33); - background-image: -o-linear-gradient(top, #df6b66, #bf3a33); - background-image: linear-gradient(to bottom, #df6b66, #bf3a33); - -moz-box-shadow: 0 0 1px rgba(0, 0, 0, 0.2); - -webkit-box-shadow: 0 0 1px rgba(0, 0, 0, 0.2); - box-shadow: 0 0 1px rgba(0, 0, 0, 0.2); + background-color: #e46565; + background-image: -webkit-gradient(linear, left top, left bottom, from(#e46565), to(#b81e1e)); + background-image: -webkit-linear-gradient(top, #e46565, #b81e1e); + background-image: -moz-linear-gradient(top, #e46565, #b81e1e); + background-image: -ms-linear-gradient(top, #e46565, #b81e1e); + background-image: -o-linear-gradient(top, #e46565, #b81e1e); + background-image: linear-gradient(to bottom, #e46565, #b81e1e); +} +.openerp button.oe_highlight:active { + background-color: #ab1b1b; + background-image: -webkit-gradient(linear, left top, left bottom, from(#ab1b1b), to(#e04848)); + background-image: -webkit-linear-gradient(top, #ab1b1b, #e04848); + background-image: -moz-linear-gradient(top, #ab1b1b, #e04848); + background-image: -ms-linear-gradient(top, #ab1b1b, #e04848); + background-image: -o-linear-gradient(top, #ab1b1b, #e04848); + background-image: linear-gradient(to bottom, #ab1b1b, #e04848); } .openerp .oe_background_grey { background: #eeeeee !important; @@ -1223,18 +1218,12 @@ } .openerp .oe_secondary_submenu .oe_active { border-top: 1px solid lightGray; - border-bottom: 1px solid #e0e0e0; + border-bottom: 1px solid #dedede; text-shadow: 0 1px 1px rgba(0, 0, 0, 0.2); - -moz-box-shadow: inset 0 2px 3px rgba(0, 0, 0, 0.2), inset 0 -1px 2px rgba(0, 0, 0, 0.2); - -webkit-box-shadow: inset 0 2px 3px rgba(0, 0, 0, 0.2), inset 0 -1px 2px rgba(0, 0, 0, 0.2); - box-shadow: inset 0 2px 3px rgba(0, 0, 0, 0.2), inset 0 -1px 2px rgba(0, 0, 0, 0.2); - background-color: #7372a7; - background-image: -webkit-gradient(linear, left top, left bottom, from(#7372a7), to(#8281b1)); - background-image: -webkit-linear-gradient(top, #7372a7, #8281b1); - background-image: -moz-linear-gradient(top, #7372a7, #8281b1); - background-image: -ms-linear-gradient(top, #7372a7, #8281b1); - background-image: -o-linear-gradient(top, #7372a7, #8281b1); - background-image: linear-gradient(to bottom, #7372a7, #8281b1); + -moz-box-shadow: inset 0 1px 3px rgba(0, 0, 0, 0.2), inset 0 -1px 3px rgba(40, 40, 40, 0.2); + -webkit-box-shadow: inset 0 1px 3px rgba(0, 0, 0, 0.2), inset 0 -1px 3px rgba(40, 40, 40, 0.2); + box-shadow: inset 0 1px 3px rgba(0, 0, 0, 0.2), inset 0 -1px 3px rgba(40, 40, 40, 0.2); + background: #7c7bad; } .openerp .oe_secondary_submenu .oe_active a { color: white; @@ -1948,6 +1937,7 @@ .openerp .oe_form header { position: relative; border-bottom: 1px solid #cacaca; + padding-left: 2px; background-color: #fcfcfc; background-image: -webkit-gradient(linear, left top, left bottom, from(#fcfcfc), to(#dedede)); background-image: -webkit-linear-gradient(top, #fcfcfc, #dedede); @@ -1960,6 +1950,9 @@ display: inline-block; float: right; } +.openerp .oe_form header .oe_form_button { + margin: 3px 2px; +} .openerp .oe_form div.oe_chatter { min-width: 650px; max-width: 860px; @@ -1979,7 +1972,7 @@ min-width: 150px; } .openerp .oe_form div.oe_form_configuration .oe_form_group_cell_label { - padding: 2px 0; + padding: 1px 0; } .openerp .oe_form div.oe_form_configuration .oe_form_group_cell div div { padding: 1px 0; @@ -2168,7 +2161,7 @@ } .openerp .oe_form td.oe_form_group_cell_label { border-right: 1px solid #dddddd; - padding: 4px 0px 4px 0px; + padding: 2px 0px; } .openerp .oe_form td.oe_form_group_cell_label label { line-height: 18px; @@ -2176,7 +2169,7 @@ min-width: 140px; } .openerp .oe_form td.oe_form_group_cell + .oe_form_group_cell { - padding-left: 6px; + padding: 2px 0 2px 8px; } .openerp .oe_form .oe_form_group { width: 100%; @@ -2188,7 +2181,7 @@ .openerp .oe_form .oe_form_label_help[for], .openerp .oe_form .oe_form_label[for] { font-weight: bold; white-space: nowrap; - padding-right: 6px; + padding-right: 8px; } .openerp .oe_form .oe_form_label_help[for] span, .openerp .oe_form .oe_form_label[for] span { font-size: 80%; @@ -2264,7 +2257,6 @@ width: 100%; display: inline-block; padding: 2px 2px 2px 0px; - line-height: 1em; } .openerp .oe_form .oe_form_field input { margin: 0px; diff --git a/addons/web/static/src/css/base.sass b/addons/web/static/src/css/base.sass index 637a1975e32..975a8103847 100644 --- a/addons/web/static/src/css/base.sass +++ b/addons/web/static/src/css/base.sass @@ -381,15 +381,13 @@ $sheet-max-width: 860px color: white background: #DC5F59 button.oe_highlight - @include vertical-gradient(#dc5f59, #b33630) - @include box-shadow(none) - button.oe_highlight:active - @include vertical-gradient(#b33630, #dc5f59) - @include box-shadow(none) + border: 1px solid #795151 + @include vertical-gradient(#df3f3f, #a21a1a) + @include box-shadow((0 1px 2px rgba(0, 0, 0, 0.1), 0 1px 1px rgba(155, 155, 155, 0.4) inset)) button.oe_highlight:hover - @include vertical-gradient(#DF6B66, #BF3A33) - //@include vertical-gradient(lighten(#dc5f59, 3%), lighten(#b33630, 3%)) - @include box-shadow(0 0 1px rgba(0, 0, 0, 0.2)) + @include vertical-gradient(lighten(#e04f4f, 5%), lighten(#a21a1a, 5%)) + button.oe_highlight:active + @include vertical-gradient(lighten(#a21a1a, 2%), lighten(#df3f3f, 2%)) .oe_background_grey background: #eee !important @@ -955,11 +953,10 @@ $sheet-max-width: 860px @include transform(scale(1.1)) .oe_active border-top: 1px solid lightGray - border-bottom: 1px solid #e0e0e0 + border-bottom: 1px solid #dedede text-shadow: 0 1px 1px rgba(0,0,0,0.2) - @include box-shadow((inset 0 2px 3px rgba(0, 0, 0, 0.2), inset 0 -1px 2px rgba(0, 0, 0, 0.2))) - //background: $link-color - @include vertical-gradient(darken($link-color, 3%), lighten($link-color, 2%)) + @include box-shadow((inset 0 1px 3px rgba(0, 0, 0, 0.2), inset 0 -1px 3px rgba(40, 40, 40, 0.2))) + background: $link-color a color: white .oe_menu_label @@ -1513,10 +1510,13 @@ $sheet-max-width: 860px header position: relative border-bottom: 1px solid #cacaca + padding-left: 2px @include vertical-gradient(#fcfcfc, #dedede) ul display: inline-block float: right + .oe_form_button + margin: 3px 2px div.oe_chatter min-width: 650px max-width: $sheet-max-width @@ -1533,7 +1533,7 @@ $sheet-max-width: 860px label min-width: 150px .oe_form_group_cell_label - padding: 2px 0 + padding: 1px 0 .oe_form_group_cell div div padding: 1px 0 @@ -1652,13 +1652,13 @@ $sheet-max-width: 860px margin: 2px td.oe_form_group_cell_label border-right: 1px solid #ddd - padding: 4px 0px 4px 0px + padding: 2px 0px label line-height: 18px display: block min-width: 140px td.oe_form_group_cell + .oe_form_group_cell - padding-left: 6px + padding: 2px 0 2px 8px .oe_form_group width: 100% margin: 9px 0 9px 0 @@ -1670,7 +1670,7 @@ $sheet-max-width: 860px .oe_form_label_help[for], .oe_form_label[for] font-weight: bold white-space: nowrap - padding-right: 6px + padding-right: 8px span font-size: 80% color: darkGreen @@ -1733,7 +1733,6 @@ $sheet-max-width: 860px width: 100% display: inline-block padding: 2px 2px 2px 0px - line-height: 1em input margin: 0px input[type="text"], input[type="password"], input[type="file"], select From ea0f6bfd50199d4947ad773fff567da7541d152e Mon Sep 17 00:00:00 2001 From: "Ajay Chauhan (OpenERP)" Date: Tue, 4 Sep 2012 18:56:23 +0530 Subject: [PATCH 333/436] [IMP] project_issue: made state field invisible in form & tree view & deleted from search view bzr revid: cha@tinyerp.com-20120904132623-58h3r440ji5ahtp9 --- addons/project_issue/board_project_issue_view.xml | 2 +- addons/project_issue/project_issue_view.xml | 5 ++--- addons/project_issue/report/project_issue_report_view.xml | 1 - 3 files changed, 3 insertions(+), 5 deletions(-) diff --git a/addons/project_issue/board_project_issue_view.xml b/addons/project_issue/board_project_issue_view.xml index 3b4fa57fc58..9a8a3c3aaec 100644 --- a/addons/project_issue/board_project_issue_view.xml +++ b/addons/project_issue/board_project_issue_view.xml @@ -16,7 +16,7 @@ - + diff --git a/addons/project_issue/project_issue_view.xml b/addons/project_issue/project_issue_view.xml index 75bf50b23a4..f87ff9cf78d 100644 --- a/addons/project_issue/project_issue_view.xml +++ b/addons/project_issue/project_issue_view.xml @@ -151,7 +151,7 @@ - + @@ -182,7 +182,7 @@ - + @@ -212,7 +212,6 @@ - diff --git a/addons/project_issue/report/project_issue_report_view.xml b/addons/project_issue/report/project_issue_report_view.xml index 625e6e64374..f90f349ea3b 100644 --- a/addons/project_issue/report/project_issue_report_view.xml +++ b/addons/project_issue/report/project_issue_report_view.xml @@ -67,7 +67,6 @@ - From d6ddb23a4ede1f4405b735f501f599d0c0c33021 Mon Sep 17 00:00:00 2001 From: Olivier Dony Date: Tue, 4 Sep 2012 15:28:03 +0200 Subject: [PATCH 334/436] [REM] email.template: remove all text versions of demo/data templates bzr revid: odo@openerp.com-20120904132803-9cgj4g10xtm121ko --- addons/account/edi/invoice_action_data.xml | 58 --------------- .../auth_reset_password.xml | 13 ++-- addons/event/email_template.xml | 30 +++----- .../marketing_campaign_demo.xml | 18 ++--- .../marketing_campaign_demo.xml | 74 ++++++++++--------- .../edi/purchase_order_action_data.xml | 45 ----------- addons/sale/edi/sale_order_action_data.xml | 61 --------------- 7 files changed, 66 insertions(+), 233 deletions(-) diff --git a/addons/account/edi/invoice_action_data.xml b/addons/account/edi/invoice_action_data.xml index 9aa1a4b720f..53fa03a6902 100644 --- a/addons/account/edi/invoice_action_data.xml +++ b/addons/account/edi/invoice_action_data.xml @@ -123,64 +123,6 @@ ]]> - '%(object.user_id.email) or ''} - -You can view the invoice document, download it and pay online using the following link: - ${ctx.get('edi_web_url_view') or 'n/a'} - -% if object.company_id.paypal_account and object.type in ('out_invoice', 'in_refund'): -<% -comp_name = quote(object.company_id.name) -inv_number = quote(object.number) -paypal_account = quote(object.company_id.paypal_account) -inv_amount = quote(str(object.amount_total)) -cur_name = quote(object.currency_id.name) -paypal_url = "https://www.paypal.com/cgi-bin/webscr?cmd=_xclick&business=%s&item_name=%s%%20Invoice%%20%s"\ - "&invoice=%s&amount=%s¤cy_code=%s&button_subtype=services&no_note=1&bn=OpenERP_Invoice_PayNow_%s" % \ - (paypal_account,comp_name,inv_number,inv_number,inv_amount,cur_name,cur_name) -%> -It is also possible to directly pay with Paypal: - ${paypal_url} -% endif - -If you have any question, do not hesitate to contact us. - - -Thank you for choosing ${object.company_id.name}! - - --- -${object.user_id.name} ${object.user_id.email and '<%s>'%(object.user_id.email) or ''} -${object.company_id.name} -% if object.company_id.street: -${object.company_id.street or ''} -% endif -% if object.company_id.street2: -${object.company_id.street2} -% endif -% if object.company_id.city or object.company_id.zip: -${object.company_id.zip or ''} ${object.company_id.city or ''} -% endif -% if object.company_id.country_id: -${object.company_id.state_id and ('%s, ' % object.company_id.state_id.name) or ''} ${object.company_id.country_id.name or ''} -% endif -% if object.company_id.phone: -Phone: ${object.company_id.phone} -% endif -% if object.company_id.website: -${object.company_id.website or ''} -% endif - ]]> diff --git a/addons/auth_reset_password/auth_reset_password.xml b/addons/auth_reset_password/auth_reset_password.xml index d2256a8911a..b7d7e0baef6 100644 --- a/addons/auth_reset_password/auth_reset_password.xml +++ b/addons/auth_reset_password/auth_reset_password.xml @@ -9,16 +9,13 @@ ]]> Password reset - A password reset was requested the OpenERP account linked to this email on ${object._auth_reset_password_host()}

      -You may change your password following this link: +

      You may change your password following this link, +or by copy-pasting the following URL in your browser: ${object._auth_reset_password_link()}

      -${object._auth_reset_password_link()} - -If you don't have asked for password reset, you can safely ignore this email. - -]]>
      +

      Note: If you did not ask for a password reset, you can safely ignore this email.

      ]]>
      diff --git a/addons/event/email_template.xml b/addons/event/email_template.xml index 49738185547..95ffbf46d8d 100644 --- a/addons/event/email_template.xml +++ b/addons/event/email_template.xml @@ -7,15 +7,12 @@ ${object.user_id.email or object.company_id.email or 'noreply@' + object.company_id.name + '.com'} ${object.email} Your registration at ${object.event_id.name} - - hello ${object.name}, - - The event ${object.event_id.name} that you registered for is confirmed and will be held from ${object.event_id.date_begin} to ${object.event_id.date_end}. For any further information please contact our event department. - - we thank you for your participation - - best regards - + Hello ${object.name},

      +

      The event ${object.event_id.name} that you registered for is confirmed and will be held from ${object.event_id.date_begin} to ${object.event_id.date_end}. + For any further information please contact our event department.

      +

      Thank you for your participation!

      +

      Best regards

      ]]>
      @@ -27,15 +24,12 @@ ${object.user_id.email or object.company_id.email or 'noreply@' + object.company_id.name + '.com'} ${object.email} Your registration at ${object.event_id.name} - - hello ${object.name}, - - We confirm you that your registration to the event ${object.event_id.name} has been recorded. You will automatically receive an email providing you more practical information (such as the schedule, the plan...) as soon as the event will be confirmed to be held. - - we thank you for your participation - - best regards - + Hello ${object.name},

      +

      We confirm that your registration to the event ${object.event_id.name} has been recorded. + You will automatically receive an email providing you more practical information (such as the schedule, the agenda...) as soon as the event is confirmed.

      +

      Thank you for your participation!

      +

      Best regards

      ]]>
      diff --git a/addons/marketing_campaign/marketing_campaign_demo.xml b/addons/marketing_campaign/marketing_campaign_demo.xml index 0c2d7f32715..92443fe987d 100644 --- a/addons/marketing_campaign/marketing_campaign_demo.xml +++ b/addons/marketing_campaign/marketing_campaign_demo.xml @@ -3,29 +3,29 @@ - Template for New Partner + welcome new partner info@openerp.com - Welcome in OpenERP Partner Channel! + Welcome to the OpenERP Partner Channel! ${object.email or ''} - Hello, We are very happy to send Welcome message. + Hello, you will receive your welcome pack via email shortly. - Template for Silver Partner + congrats silver partner info@openerp.com - Congratulation! You become now our Silver Partner. + Congratulations! You are now a Silver Partner! ${object.email or ''} - Hello, We are happy to announce that you now become our Silver Partner. + Hi, we are delighted to welcome you among our Silver Partners as of today! - Template for Gold Partner + congrats gold partner info@openerp.com - Congratulation! You become our Gold Partner. + Congratulations! You are now one of our Gold Partners! ${object.email or ''} - Hello, We are happy to announce that you become our Gold Partner. + Hi, we are delighted to let you know that you have entered the select circle of our Gold Partners diff --git a/addons/marketing_campaign_crm_demo/marketing_campaign_demo.xml b/addons/marketing_campaign_crm_demo/marketing_campaign_demo.xml index 6545dd93cb2..141981a21d6 100644 --- a/addons/marketing_campaign_crm_demo/marketing_campaign_demo.xml +++ b/addons/marketing_campaign_crm_demo/marketing_campaign_demo.xml @@ -40,7 +40,10 @@ - Hello,Thanks for generous interest you have shown in the openERP.Regards,OpenERP Team, + Hello,

      +

      Thanks for the genuine interest you have shown in OpenERP.

      +

      If any further information is required, do not hesitate to reply to this message.

      +

      Regards,OpenERP Team,

      ]]>
      For OpenERP OnDemand Free Trial 2010 @@ -50,11 +53,11 @@ - Hello,We have very good offer that might suit you. - We propose you to subscribe to the OpenERP Discovery Day on May 2010. - If any further information required kindly revert back. - We really appreciate your co-operation on this. - Regards,OpenERP Team, + Hello,

      +

      We have very good offer that might suit you. + We suggest you subscribe to the OpenERP Discovery Day on May 2010.

      +

      If any further information is required, do not hesitate to reply to this message.

      +

      Regards,OpenERP Team,

      ]]>
      For OpenERP Discovery Day on May 2010
      @@ -65,10 +68,10 @@ - Hello,Thanks for showing intrest and for subscribing to the OpenERP Discovery Day. - If any further information required kindly revert back. - I really appreciate your co-operation on this. - Regards,OpenERP Team, + Hello,

      +

      Thanks for showing interest and for subscribing to the OpenERP Discovery Day.

      +

      If any further information is required, do not hesitate to reply to this message.

      +

      Regards,OpenERP Team,

      ]]>
      For OpenERP Discovery Day @@ -79,10 +82,10 @@ - Hello,Thanks for showing intrest and buying the OpenERP book. + Hello,

      +

      Thanks for showing interest and buying the OpenERP book.

      If any further information required kindly revert back. - I really appreciate your co-operation on this. - Regards,OpenERP Team,
      +

      Regards,OpenERP Team,

      ]]>
      For OpenERP book @@ -92,12 +95,12 @@ - Hello, We have very good offer that might suit you. - For our gold partners,We are arranging free technical training on june,2010. - If any further information required kindly revert back. - I really appreciate your co-operation on this. - Regards,OpenERP Team, - For technical training to Gold partners + Hello,

      +

      We have very good offer that might suit you. + For our gold partners,We are arranging free technical training on june,2010.

      +

      If any further information is required, do not hesitate to reply to this message.

      +

      Regards,OpenERP Team,

      ]]>
      + technical training to gold partners @@ -107,12 +110,12 @@ - Hello, We have very good offer that might suit you. - For our silver partners,We are paid technical training on june,2010. - If any further information required kindly revert back. - I really appreciate your co-operation on this. - Regards,OpenERP Team, - For training to Silver partners + Hello,

      +

      We have very good offer that might suit you. + For our silver partners,We are paid technical training on june,2010.

      +

      If any further information is required, do not hesitate to reply to this message.

      +

      Regards,OpenERP Team,

      ]]>
      + training to silver partners
      @@ -122,12 +125,12 @@ - Hello, We have very good offer that might suit you. - For our silver partners,We are offering Gold partnership. - If any further information required kindly revert back. - I really appreciate your co-operation on this. - Regards,OpenERP Team, - For gold partnership to silver partners + Hello,

      +

      We have very good offer that might suit you. + For our silver partners, we are offering Gold partnership.

      +

      If any further information is required, do not hesitate to reply to this message.

      +

      Regards,OpenERP Team,

      ]]>
      + gold partnership to silver partners
      @@ -137,9 +140,12 @@ - Hello, Thanks for showing intrest and for subscribing to technical training.If any further information required kindly revert back.I really appreciate your co-operation on this. - Regards,OpenERP Team, - For subscribing to technical training + Hello,

      +

      Thanks for showing interest and for subscribing to technical training.

      + If any further information required kindly revert back.I really appreciate your co-operation on this.

      +

      If any further information is required, do not hesitate to reply to this message.

      +

      Regards,OpenERP Team,

      ]]>
      + subscribing to technical training
      diff --git a/addons/purchase/edi/purchase_order_action_data.xml b/addons/purchase/edi/purchase_order_action_data.xml index 95e6adda3d9..daad4467929 100644 --- a/addons/purchase/edi/purchase_order_action_data.xml +++ b/addons/purchase/edi/purchase_order_action_data.xml @@ -108,51 +108,6 @@ ]]>
      - - % endif - | Your contact: ${object.validator.name} ${object.validator.email and '<%s>'%(object.validator.email) or ''} - -You can view the ${object.state in ('draft', 'sent') and 'request for quotation' or 'order confirmation'} and download it using the following link: - ${ctx.get('edi_web_url_view') or 'n/a'} - -If you have any question, do not hesitate to contact us. - -Thank you! - - --- -${object.validator.name} ${object.validator.email and '<%s>'%(object.validator.email) or ''} -${object.company_id.name} -% if object.company_id.street: -${object.company_id.street or ''} -% endif -% if object.company_id.street2: -${object.company_id.street2} -% endif -% if object.company_id.city or object.company_id.zip: -${object.company_id.zip or ''} ${object.company_id.city or ''} -% endif -% if object.company_id.country_id: -${object.company_id.state_id and ('%s, ' % object.company_id.state_id.name) or ''} ${object.company_id.country_id.name or ''} -% endif -% if object.company_id.phone: -Phone: ${object.company_id.phone} -% endif -% if object.company_id.website: -${object.company_id.website or ''} -% endif - ]]>
      diff --git a/addons/sale/edi/sale_order_action_data.xml b/addons/sale/edi/sale_order_action_data.xml index 478f0b17a8c..49ebe6053e6 100644 --- a/addons/sale/edi/sale_order_action_data.xml +++ b/addons/sale/edi/sale_order_action_data.xml @@ -126,67 +126,6 @@ ]]>
      - - % endif - | Your contact: ${object.user_id.name} ${object.user_id.email and '<%s>'%(object.user_id.email) or ''} - -You can view the ${object.state in ('draft', 'sent') and 'quotation' or 'order confirmation'}, download it and even pay online using the following link: - ${ctx.get('edi_web_url_view') or 'n/a'} - -% if object.order_policy in ('prepaid','manual') and object.company_id.paypal_account and object.state not in ('draft', 'sent'): -<% -comp_name = quote(object.company_id.name) -order_name = quote(object.name) -paypal_account = quote(object.company_id.paypal_account) -order_amount = quote(str(object.amount_total)) -cur_name = quote(object.pricelist_id.currency_id.name) -paypal_url = "https://www.paypal.com/cgi-bin/webscr?cmd=_xclick&business=%s&item_name=%s%%20Order%%20%s&invoice=%s&amount=%s" \ - "¤cy_code=%s&button_subtype=services&no_note=1&bn=OpenERP_Order_PayNow_%s" % \ - (paypal_account,comp_name,order_name,order_name,order_amount,cur_name,cur_name) -%> -It is also possible to directly pay with Paypal: - ${paypal_url} -% endif - -If you have any question, do not hesitate to contact us. - - -Thank you for choosing ${object.company_id.name}! - - --- -${object.user_id.name} ${object.user_id.email and '<%s>'%(object.user_id.email) or ''} -${object.company_id.name} -% if object.company_id.street: -${object.company_id.street or ''} -% endif -% if object.company_id.street2: -${object.company_id.street2} -% endif -% if object.company_id.city or object.company_id.zip: -${object.company_id.zip or ''} ${object.company_id.city or ''} -% endif -% if object.company_id.country_id: -${object.company_id.state_id and ('%s, ' % object.company_id.state_id.name) or ''} ${object.company_id.country_id.name or ''} -% endif -% if object.company_id.phone: -Phone: ${object.company_id.phone} -% endif -% if object.company_id.website: -${object.company_id.website or ''} -% endif - ]]> From c0d92e38b15c6c7dbc662a307fd1e3e259e9469a Mon Sep 17 00:00:00 2001 From: Fabien Meghazi Date: Tue, 4 Sep 2012 15:31:37 +0200 Subject: [PATCH 335/436] [FIX] Use call_button in order to have the action cleaned by the controller bzr revid: fme@openerp.com-20120904133137-0e0gyu69p89yx3sr --- addons/web/static/src/js/view_form.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/addons/web/static/src/js/view_form.js b/addons/web/static/src/js/view_form.js index b0c8b6618ae..a53807a9d79 100644 --- a/addons/web/static/src/js/view_form.js +++ b/addons/web/static/src/js/view_form.js @@ -1997,9 +1997,9 @@ instance.web.form.AbstractField = instance.web.form.FormWidget.extend(instance.w }, on_translate: function() { var self = this; - var trans = new instance.web.DataSet(this, 'ir.translation', this.view.dataset.get_context()); - return trans.call('translate_fields', [this.view.dataset.model, this.view.datarecord.id, this.name]).then(function(action) { - self.do_action(action); + var trans = new instance.web.DataSet(this, 'ir.translation'); + return trans.call_button('translate_fields', [this.view.dataset.model, this.view.datarecord.id, this.name, this.view.dataset.get_context()]).then(function(r) { + self.do_action(r.result); }); }, }); From 6e5a9053e50f1b74ad53c9a644fe307ecc5c8348 Mon Sep 17 00:00:00 2001 From: Fabien Meghazi Date: Tue, 4 Sep 2012 15:32:16 +0200 Subject: [PATCH 336/436] [FIX] Fixed remarks of merge proposal bzr revid: fme@openerp.com-20120904133216-7v82tmcqi1n21c3f --- openerp/addons/base/ir/ir_translation.py | 27 ++++++++++++------------ 1 file changed, 13 insertions(+), 14 deletions(-) diff --git a/openerp/addons/base/ir/ir_translation.py b/openerp/addons/base/ir/ir_translation.py index 55902389ee4..3650f28d3f5 100644 --- a/openerp/addons/base/ir/ir_translation.py +++ b/openerp/addons/base/ir/ir_translation.py @@ -330,18 +330,18 @@ class ir_translation(osv.osv): def translate_fields(self, cr, uid, model, id, field=None, context=None): trans_model = self.pool.get(model) - domain = ['&', ('res_id', '=', id), ('name', 'ilike', model + ',')] - langs_ids = self.pool.get('res.lang').search(cr, uid, [], context=context) - langs = [lg.get('code') for lg in self.pool.get('res.lang').read(cr, uid, langs_ids, ['code'], context=context)] - main_lang = langs.pop(0) + domain = ['&', ('res_id', '=', id), ('name', '=like', model + ',%')] + langs_ids = self.pool.get('res.lang').search(cr, uid, [('code', '!=', 'en_US')], context=context) + langs = [lg.code for lg in self.pool.get('res.lang').browse(cr, uid, langs_ids, context=context)] + main_lang = 'en_US' translatable_fields = [] for f, info in trans_model._all_columns.items(): if info.column.translate: if info.parent_model: - domain_id = trans_model.read(cr, uid, [id], [info.parent_column], context=context)[0][info.parent_column][0] - translatable_fields.append({ 'name': f, 'id': domain_id, 'model': info.parent_model }) + parent_id = trans_model.read(cr, uid, [id], [info.parent_column], context=context)[0][info.parent_column][0] + translatable_fields.append({ 'name': f, 'id': parent_id, 'model': info.parent_model }) domain.insert(0, '|') - domain.extend(['&', ('res_id', '=', domain_id), ('name', '=', "%s,%s" % (info.parent_model, f))]) + domain.extend(['&', ('res_id', '=', parent_id), ('name', '=', "%s,%s" % (info.parent_model, f))]) else: translatable_fields.append({ 'name': f, 'id': id, 'model': model }) if len(langs): @@ -351,22 +351,21 @@ class ir_translation(osv.osv): for f in translatable_fields: # Check if record exists, else create it (at once) sql = """INSERT INTO ir_translation (lang, src, name, type, res_id, value) - SELECT %s, %s, %s, 'field', %s, %s WHERE NOT EXISTS - (SELECT 1 FROM ir_translation WHERE lang=%s AND name=%s AND res_id=%s); - UPDATE ir_translation SET src = %s WHERE lang=%s AND name=%s AND res_id=%s; + SELECT %s, %s, %s, 'model', %s, %s WHERE NOT EXISTS + (SELECT 1 FROM ir_translation WHERE lang=%s AND name=%s AND res_id=%s AND type='model'); + UPDATE ir_translation SET src = %s WHERE lang=%s AND name=%s AND res_id=%s AND type='model'; """ - src = record[f['name']] + src = record[f['name']] or None name = "%s,%s" % (f['model'], f['name']) cr.execute(sql, (lg, src , name, f['id'], src, lg, name, f['id'], src, lg, name, id)) action = { 'name': 'Translate', - 'view_type': 'list', - 'view_mode': 'list', 'res_model': 'ir.translation', 'type': 'ir.actions.act_window', + 'view_type': 'form', + 'view_mode': 'tree,form', 'domain': domain, - 'views': [(False, 'list'), (False, 'form')], } if field: info = trans_model._all_columns[field] From fd2a4139694e2548a6bccb154a6a6c5be6d19b5e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thibault=20Delavall=C3=A9e?= Date: Tue, 4 Sep 2012 15:36:48 +0200 Subject: [PATCH 337/436] [FIX] [CLEAN] mail_thread: misc cleanup (comments, vars, typo). Fixed subscriber API. It now handles a read_back key in context to return the new value of the field. The purpose is to use it in mail_followers widget, that behaves asynchronously compared to the form_view, and that need updated values after subscribing a user. Updated mail_followers widget, cleaned its code. Updated mail_group with wrappers on message_subscriber_users, because it was setting user_ids as context. bzr revid: tde@openerp.com-20120904133648-plsziijac64lw4de --- addons/mail/mail_group.py | 17 ++++-- addons/mail/mail_group_view.xml | 5 +- addons/mail/mail_message.py | 2 +- addons/mail/mail_thread.py | 64 +++++++++------------ addons/mail/static/src/js/mail_followers.js | 22 +++---- addons/mail/tests/test_mail.py | 4 +- addons/mail/wizard/mail_compose_message.py | 2 +- 7 files changed, 55 insertions(+), 61 deletions(-) diff --git a/addons/mail/mail_group.py b/addons/mail/mail_group.py index f5769e082a1..fdfeafd4afe 100644 --- a/addons/mail/mail_group.py +++ b/addons/mail/mail_group.py @@ -44,10 +44,6 @@ class mail_group(osv.Model): def _set_image(self, cr, uid, id, name, value, args, context=None): return self.write(cr, uid, [id], {'image': tools.image_resize_image_big(value)}, context=context) - def _get_default_image(self, cr, uid, context=None): - image_path = openerp.modules.get_module_resource('mail', 'static/src/img', 'groupdefault.png') - return tools.image_resize_image_big(open(image_path, 'rb').read().encode('base64')) - _columns = { 'description': fields.text('Description'), 'menu_id': fields.many2one('ir.ui.menu', string='Related Menu', required=True, ondelete="cascade"), @@ -89,6 +85,10 @@ class mail_group(osv.Model): ref = self.pool.get('ir.model.data').get_object_reference(cr, uid, 'base', 'group_user') return ref and ref[1] or False + def _get_default_image(self, cr, uid, context=None): + image_path = openerp.modules.get_module_resource('mail', 'static/src/img', 'groupdefault.png') + return tools.image_resize_image_big(open(image_path, 'rb').read().encode('base64')) + def _get_menu_parent(self, cr, uid, context=None): ref = self.pool.get('ir.model.data').get_object_reference(cr, uid, 'mail', 'mail_group_root') return ref and ref[1] or False @@ -156,3 +156,12 @@ class mail_group(osv.Model): self._subscribe_users(cr, uid, ids, vals.get('group_ids'), context=context) return result + def action_follow(self, cr, uid, ids, context=None): + """ Wrapper because message_subscribe_users take a user_ids=None + that receive the context without the wrapper. """ + return self.message_subscribe_users(cr, uid, ids, context=context) + + def action_unfollow(self, cr, uid, ids, context=None): + """ Wrapper because message_unsubscribe_users take a user_ids=None + that receive the context without the wrapper. """ + return self.message_unsubscribe_users(cr, uid, ids, context=context) diff --git a/addons/mail/mail_group_view.xml b/addons/mail/mail_group_view.xml index acd9f320757..51a7a926dc9 100644 --- a/addons/mail/mail_group_view.xml +++ b/addons/mail/mail_group_view.xml @@ -33,8 +33,8 @@

      @@ -81,7 +81,6 @@
      - diff --git a/addons/mail/mail_message.py b/addons/mail/mail_message.py index d22bd4c2e3c..bca46a3c91c 100644 --- a/addons/mail/mail_message.py +++ b/addons/mail/mail_message.py @@ -135,7 +135,7 @@ class mail_message(osv.Model): _defaults = { 'type': 'email', 'date': lambda *a: fields.datetime.now(), - 'author_id': lambda s,cr,uid,ctx={}: s._get_default_author(cr, uid, ctx), + 'author_id': lambda self,cr,uid,ctx: self._get_default_author(cr, uid, ctx), 'body': '', } diff --git a/addons/mail/mail_thread.py b/addons/mail/mail_thread.py index 6e5f594354a..eda7adb995c 100644 --- a/addons/mail/mail_thread.py +++ b/addons/mail/mail_thread.py @@ -158,17 +158,16 @@ class mail_thread(osv.AbstractModel): 'mail_followers', 'res_id', 'partner_id', reference_column='res_model', string='Followers'), 'message_comment_ids': fields.one2many('mail.message', 'res_id', - domain=lambda self: [('model','=',self._name),('type','in',('comment','email'))], - string='Related Messages', - help="All messages related to the current document."), + domain=lambda self: [('model', '=', self._name), ('type', 'in', ('comment', 'email'))], + string='Comments and emails', + help="Comments and emails"), 'message_ids': fields.one2many('mail.message', 'res_id', domain=lambda self: [('model','=',self._name)], - string='Related Messages', - help="All messages related to the current document."), + string='Messages', + help="Messages and communication history"), 'message_unread': fields.function(_get_message_data, fnct_search=_search_unread, - string='Has Unread Messages', type='boolean', - help="When checked, new messages require your attention.", - multi="_get_message_data"), + type='boolean', string='Unread Messages', multi="_get_message_data", + help="If checked new messages require your attention."), 'message_summary': fields.function(_get_message_data, method=True, type='text', string='Summary', multi="_get_message_data", help="Holds the Chatter summary (number of messages, ...). "\ @@ -181,7 +180,7 @@ class mail_thread(osv.AbstractModel): #------------------------------------------------------ def create(self, cr, uid, vals, context=None): - """ Override of create to subscribe the current user. """ + """ Override to subscribe the current user. """ thread_id = super(mail_thread, self).create(cr, uid, vals, context=context) self.message_subscribe_users(cr, uid, [thread_id], [uid], context=context) return thread_id @@ -215,11 +214,8 @@ class mail_thread(osv.AbstractModel): def _message_find_partners(self, cr, uid, message, header_fields=['From'], context=None): """ Find partners related to some header fields of the message. """ s = ', '.join([decode(message.get(h)) for h in header_fields if message.get(h)]) - mails = tools.email_split(s) - result = [] - for email in mails: - result += self.pool.get('res.partner').search(cr, uid, [('email', 'ilike', email)], context=context) - return result + return [partner_id for email in tools.email_split(s) + for partner_id in self.pool.get('res.partner').search(cr, uid, [('email', 'ilike', email)], context=context)] def _message_find_user_id(self, cr, uid, message, context=None): from_local_part = tools.email_split(decode(message.get('From')))[0] @@ -551,16 +547,16 @@ class mail_thread(osv.AbstractModel): #------------------------------------------------------ def log(self, cr, uid, id, message, secondary=False, context=None): - _logger.warning("log() is deprecated. As this module inherit from \ - mail.thread, the message will be managed by this \ - module instead of by the res.log mechanism. Please \ - use the mail.thread OpenChatter API instead of the \ - now deprecated res.log.") + _logger.warning("log() is deprecated. As this module inherit from "\ + "mail.thread, the message will be managed by this "\ + "module instead of by the res.log mechanism. Please "\ + "use mail_thread.message_post() instead of the "\ + "now deprecated res.log.") self.message_post(cr, uid, [id], message, context=context) def message_post(self, cr, uid, thread_id, body='', subject=False, - msg_type='notification', parent_id=False, attachments=None, context=None, **kwargs): - """ Post a new message in an existing message thread, returning the new + type='notification', parent_id=False, attachments=None, context=None, **kwargs): + """ Post a new message in an existing thread, returning the new mail.message ID. Extra keyword arguments will be used as default column values for the new mail.message record. @@ -568,7 +564,7 @@ class mail_thread(osv.AbstractModel): :param str body: body of the message, usually raw HTML that will be sanitized :param str subject: optional subject - :param str msg_type: mail_message.type + :param str type: mail_message.type :param int parent_id: optional ID of parent message in this thread :param tuple(str,str) attachments: list of attachment tuples in the form ``(name,content)``, where content is NOT base64 encoded @@ -601,7 +597,7 @@ class mail_thread(osv.AbstractModel): 'res_id': thread_id or False, 'body': body, 'subject': subject, - 'type': msg_type, + 'type': type, 'parent_id': parent_id, 'attachment_ids': attachment_ids, }) @@ -615,40 +611,37 @@ class mail_thread(osv.AbstractModel): def message_subscribe_users(self, cr, uid, ids, user_ids=None, context=None): """ Wrapper on message_subscribe, using users. If user_ids is not provided, subscribe uid instead. """ - # isinstance: because using message_subscribe_users called in a view set the context as user_ids - if not user_ids or isinstance(user_ids, dict): user_ids = [uid] + if not user_ids: user_ids = [uid] partner_ids = [user.partner_id.id for user in self.pool.get('res.users').browse(cr, uid, user_ids, context=context)] return self.message_subscribe(cr, uid, ids, partner_ids, context=context) def message_subscribe(self, cr, uid, ids, partner_ids, context=None): """ Add partners to the records followers. - :param partner_ids: a list of partner_ids to subscribe - :param return: new value of followers, for Chatter + :param return: new value of followers if read_back key in context """ self.write(cr, uid, ids, {'message_follower_ids': [(4, pid) for pid in partner_ids]}, context=context) - # TDE: temp, must check followers widget + if context and context.get('read_back'): + return [follower.id for thread in self.browse(cr, uid, ids, context=context) for follower in thread.message_follower_ids] return [] - # return [follower.id for thread in self.browse(cr, uid, ids, context=context) for follower in thread.message_follower_ids] + def message_unsubscribe_users(self, cr, uid, ids, user_ids=None, context=None): """ Wrapper on message_subscribe, using users. If user_ids is not provided, unsubscribe uid instead. """ - # isinstance: because using message_subscribe_users called in a view set the context as user_ids - if not user_ids or isinstance(user_ids, dict): user_ids = [uid] + if not user_ids: user_ids = [uid] partner_ids = [user.partner_id.id for user in self.pool.get('res.users').browse(cr, uid, user_ids, context=context)] return self.message_unsubscribe(cr, uid, ids, partner_ids, context=context) def message_unsubscribe(self, cr, uid, ids, partner_ids, context=None): """ Remove partners from the records followers. - :param partner_ids: a list of partner_ids to unsubscribe - :param return: new value of followers, for Chatter + :param return: new value of followers if read_back key in context """ self.write(cr, uid, ids, {'message_follower_ids': [(3, pid) for pid in partner_ids]}, context=context) - # TDE: temp, must check followers widget + if context and context.get('read_back'): + return [follower.id for thread in self.browse(cr, uid, ids, context=context) for follower in thread.message_follower_ids] return [] - # return [follower.id for thread in self.browse(cr, uid, ids, context=context) for follower in thread.message_follower_ids] #------------------------------------------------------ # Thread state @@ -679,4 +672,3 @@ class mail_thread(osv.AbstractModel): partner_id = %s ''', (ids, self._name, partner_id)) return True - diff --git a/addons/mail/static/src/js/mail_followers.js b/addons/mail/static/src/js/mail_followers.js index 6125aac12dc..e5d216ed3d2 100644 --- a/addons/mail/static/src/js/mail_followers.js +++ b/addons/mail/static/src/js/mail_followers.js @@ -9,7 +9,7 @@ openerp_mail_followers = function(session, mail) { * mail_followers Widget * ------------------------------------------------------------ * - * This widget handles the display of a list of records as a vetical + * This widget handles the display of a list of records as a vertical * list, with an image on the left. The widget itself is a floatting * right-sided box. * This widget is mainly used to display the followers of records @@ -65,25 +65,17 @@ openerp_mail_followers = function(session, mail) { this.$el.find('div.oe_mail_recthread_aside').hide(); return; } - if (this.getParent().fields.message_is_follower === undefined) { - // TDE: TMP, need to change all form views - this.message_is_follower = false; - } - else { - this.message_is_follower = this.getParent().fields.message_is_follower.get_value(); - } return this.fetch_followers(value_); }, fetch_followers: function (value_) { - return this.ds_follow.call('read', [value_ || this.get_value(), ['name']]).then(this.proxy('display_followers')); + return this.ds_follow.call('read', [value_ || this.get_value(), ['name', 'user_ids']]).then(this.proxy('display_followers')); }, - /** - * Display the followers. - * TODO: replace the is_follower check by fields read */ + /** Display the followers, evaluate is_follower directly */ display_followers: function (records) { var self = this; + this.message_is_follower = _.indexOf(_.flatten(_.pluck(records, 'user_ids')), this.session.uid) != -1; var node_user_list = this.$el.find('ul.oe_mail_followers_display').empty(); this.$el.find('div.oe_mail_recthread_followers h4').html(this.options.title + ' (' + records.length + ')'); _(records).each(function (record) { @@ -99,11 +91,13 @@ openerp_mail_followers = function(session, mail) { }, do_follow: function () { - return this.ds_model.call('message_subscribe_users', [[this.view.datarecord.id]]).pipe(this.proxy('set_value')); + var context = new session.web.CompoundContext(this.build_context(), {'read_back': true}); + return this.ds_model.call('message_subscribe_users', [[this.view.datarecord.id], undefined, context]).pipe(this.proxy('set_value')); }, do_unfollow: function () { - return this.ds_model.call('message_unsubscribe_users', [[this.view.datarecord.id]]).pipe(this.proxy('set_value')); + var context = new session.web.CompoundContext(this.build_context(), {'read_back': true}); + return this.ds_model.call('message_unsubscribe_users', [[this.view.datarecord.id], undefined, context]).pipe(this.proxy('set_value')); }, }); }; diff --git a/addons/mail/tests/test_mail.py b/addons/mail/tests/test_mail.py index 9c148647b9b..f8fd02bfb7e 100644 --- a/addons/mail/tests/test_mail.py +++ b/addons/mail/tests/test_mail.py @@ -250,7 +250,7 @@ class test_mail(common.TransactionCase): _attachments = [('First', 'My first attachment'), ('Second', 'My second attachment')] # CASE1: post comment, body and subject specified - msg_id = self.mail_group.message_post(cr, uid, self.group_pigs_id, body=_body1, subject=_subject, msg_type='comment') + msg_id = self.mail_group.message_post(cr, uid, self.group_pigs_id, body=_body1, subject=_subject, type='comment') message = self.mail_message.browse(cr, uid, msg_id) mail_ids = self.mail_mail.search(cr, uid, [], limit=1) mail = self.mail_mail.browse(cr, uid, mail_ids[0]) @@ -280,7 +280,7 @@ class test_mail(common.TransactionCase): # CASE2: post an email with attachments, parent_id, partner_ids # TESTS: automatic subject, signature in body_html, attachments propagation - msg_id2 = self.mail_group.message_post(cr, uid, self.group_pigs_id, body=_body2, msg_type='email', + msg_id2 = self.mail_group.message_post(cr, uid, self.group_pigs_id, body=_body2, type='email', partner_ids=[(6, 0, [p_d_id])], parent_id=msg_id, attachments=_attachments) message = self.mail_message.browse(cr, uid, msg_id2) mail_ids = self.mail_mail.search(cr, uid, [], limit=1) diff --git a/addons/mail/wizard/mail_compose_message.py b/addons/mail/wizard/mail_compose_message.py index 41b10d6d4a3..3c93440642d 100644 --- a/addons/mail/wizard/mail_compose_message.py +++ b/addons/mail/wizard/mail_compose_message.py @@ -269,7 +269,7 @@ class mail_compose_message(osv.TransientModel): post_values['attachments'] += new_attachments post_values.update(email_dict) # post the message - active_model_pool.message_post(cr, uid, [res_id], msg_type='comment', context=context, **post_values) + active_model_pool.message_post(cr, uid, [res_id], type='comment', context=context, **post_values) return {'type': 'ir.actions.act_window_close'} From c0fbdc25566af3ee35fac1509748a9bb9715a682 Mon Sep 17 00:00:00 2001 From: "vta vta@openerp.com" <> Date: Tue, 4 Sep 2012 15:50:45 +0200 Subject: [PATCH 338/436] [IMP] kanban tooltip on quickcreate bzr revid: vta@openerp.com-20120904135045-husx3of9jnjhzpqp --- addons/web_kanban/static/src/js/kanban.js | 5 +++-- addons/web_kanban/static/src/xml/web_kanban.xml | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/addons/web_kanban/static/src/js/kanban.js b/addons/web_kanban/static/src/js/kanban.js index e00f6d9f44e..e18b7c33d96 100644 --- a/addons/web_kanban/static/src/js/kanban.js +++ b/addons/web_kanban/static/src/js/kanban.js @@ -517,7 +517,6 @@ instance.web_kanban.KanbanGroup = instance.web.Widget.extend({ self.quick.focus(); }); // Add bounce effect on image '+' of kanban header when click on empty space of kanban grouped column. - var add_btn = this.$el.find('.oe_kanban_add'); this.$records.find('.oe_kanban_show_more').click(this.do_show_more); if (this.state.folded) { this.do_toggle_fold(); @@ -526,10 +525,12 @@ instance.web_kanban.KanbanGroup = instance.web.Widget.extend({ this.$records.data('widget', this); this.$has_been_started.resolve(); this.compute_cards_auto_height(); + var add_btn = this.$el.find('.oe_kanban_add'); + add_btn.tipsy({delayIn: 500, delayOut: 1000}); this.$records.click(function (ev) { if (ev.target == ev.currentTarget) { if (!self.state.folded) { - add_btn.effect('bounce', {distance: 18, times: 5}, 150); + add_btn.effect('bounce', {distance: 18, times: 5}, 150); } } }); diff --git a/addons/web_kanban/static/src/xml/web_kanban.xml b/addons/web_kanban/static/src/xml/web_kanban.xml index 20b361e9445..17d45d90505 100644 --- a/addons/web_kanban/static/src/xml/web_kanban.xml +++ b/addons/web_kanban/static/src/xml/web_kanban.xml @@ -32,7 +32,7 @@
      -
      ]
      +
      ]
      í From e386d895ad1aab43d88b4241e24b8659314cfe3b Mon Sep 17 00:00:00 2001 From: Fabien Meghazi Date: Tue, 4 Sep 2012 15:57:28 +0200 Subject: [PATCH 339/436] [FIX] Do not ilike on name for the translation search view bzr revid: fme@openerp.com-20120904135728-6i5nrwemsluexlx0 --- openerp/addons/base/ir/ir.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openerp/addons/base/ir/ir.xml b/openerp/addons/base/ir/ir.xml index 4fb3c82dd24..bd98bb34c12 100644 --- a/openerp/addons/base/ir/ir.xml +++ b/openerp/addons/base/ir/ir.xml @@ -1173,7 +1173,7 @@ - + From 440b8aabbd9ca0e1928d39207c6ed957fec613c8 Mon Sep 17 00:00:00 2001 From: Raphael Collet Date: Tue, 4 Sep 2012 15:59:05 +0200 Subject: [PATCH 340/436] [IMP] web_kanban: remove inappropriate method _is_action_enabled bzr revid: rco@openerp.com-20120904135905-4f7lu2qk0p5qk8cs --- addons/web_kanban/static/src/js/kanban.js | 6 ------ 1 file changed, 6 deletions(-) diff --git a/addons/web_kanban/static/src/js/kanban.js b/addons/web_kanban/static/src/js/kanban.js index 6efd8593deb..143ff7d685b 100644 --- a/addons/web_kanban/static/src/js/kanban.js +++ b/addons/web_kanban/static/src/js/kanban.js @@ -633,9 +633,6 @@ instance.web_kanban.KanbanGroup = instance.web.Widget.extend({ self.view.dataset.ids.push(id); self.do_add_records(records, true); }); - }, - _is_action_enabled: function(action) { - return (_.has(this.fields_view.arch.attrs, action)) ? JSON.parse(this.fields_view.arch.attrs[action]) : true; } }); @@ -910,9 +907,6 @@ instance.web_kanban.KanbanRecord = instance.web.Widget.extend({ }, kanban_compute_domain: function(domain) { return instance.web.form.compute_domain(domain, this.values); - }, - _is_action_enabled: function(action) { - return (_.has(this.fields_view.arch.attrs, action)) ? JSON.parse(this.fields_view.arch.attrs[action]) : true; } }); From 14e87c09f3ecf5cefd9b6d6bcfc6b5efa35e7729 Mon Sep 17 00:00:00 2001 From: Raphael Collet Date: Tue, 4 Sep 2012 16:11:55 +0200 Subject: [PATCH 341/436] [IMP] rename method _is_action_enabled to is_action_enabled bzr revid: rco@openerp.com-20120904141155-288vwhonbt07dfcs --- addons/web/static/src/js/view_form.js | 10 +++++----- addons/web/static/src/js/view_list.js | 10 +++++----- addons/web/static/src/js/view_list_editable.js | 2 +- addons/web/static/src/js/views.js | 2 +- addons/web/static/src/xml/base.xml | 6 +++--- addons/web_diagram/static/src/xml/base_diagram.xml | 2 +- addons/web_gantt/static/src/js/gantt.js | 2 +- addons/web_kanban/static/src/js/kanban.js | 4 ++-- addons/web_kanban/static/src/xml/web_kanban.xml | 2 +- 9 files changed, 20 insertions(+), 20 deletions(-) diff --git a/addons/web/static/src/js/view_form.js b/addons/web/static/src/js/view_form.js index cca3d410a50..669126820e8 100644 --- a/addons/web/static/src/js/view_form.js +++ b/addons/web/static/src/js/view_form.js @@ -148,12 +148,12 @@ instance.web.FormView = instance.web.View.extend(instance.web.form.FieldManagerM if (this.fields_view.toolbar) { this.sidebar.add_toolbar(this.fields_view.toolbar); } - if (self._is_action_enabled('delete')) { + if (self.is_action_enabled('delete')) { this.sidebar.add_items('other', [ { label: _t('Delete'), callback: self.on_button_delete } ]); } - if (self._is_action_enabled('create')) { + if (self.is_action_enabled('create')) { this.sidebar.add_items('other', [ { label: _t('Duplicate'), callback: self.on_button_duplicate } ]); @@ -3485,7 +3485,7 @@ instance.web.form.One2ManyListView = instance.web.ListView.extend({ return self.o2m.dataset.read_ids.apply(self.o2m.dataset, arguments); }, form_view_options: {'not_interactible_on_create':true}, - readonly: !this._is_action_enabled('edit') || self.o2m.get("effective_readonly") + readonly: !this.is_action_enabled('edit') || self.o2m.get("effective_readonly") }); }, do_button_action: function (name, id, callback) { @@ -3573,7 +3573,7 @@ instance.web.form.One2ManyListView = instance.web.ListView.extend({ }); instance.web.form.One2ManyList = instance.web.ListView.List.extend({ pad_table_to: function (count) { - this._super(this.view._is_action_enabled('create') ? (count > 0 ? count - 1 : 0) : this.records.length); + this._super(this.view.is_action_enabled('create') ? (count > 0 ? count - 1 : 0) : this.records.length); // magical invocation of wtf does that do if (this.view.o2m.get('effective_readonly')) { @@ -3586,7 +3586,7 @@ instance.web.form.One2ManyList = instance.web.ListView.List.extend({ }).length; if (this.options.selectable) { columns++; } if (this.options.deletable) { columns++; } - if (this.view._is_action_enabled('create')) { + if (this.view.is_action_enabled('create')) { var $cell = $('', { colspan: columns, 'class': 'oe_form_field_one2many_list_row_add' diff --git a/addons/web/static/src/js/view_list.js b/addons/web/static/src/js/view_list.js index 06d2548339c..da806f415cf 100644 --- a/addons/web/static/src/js/view_list.js +++ b/addons/web/static/src/js/view_list.js @@ -252,13 +252,13 @@ instance.web.ListView = instance.web.View.extend( /** @lends instance.web.ListVi this.$el.addClass(this.fields_view.arch.attrs['class']); // add css classes that reflect the (absence of) access rights - if (!this._is_action_enabled('create')) { + if (!this.is_action_enabled('create')) { this.$el.addClass('oe_list_cannot_create'); } - if (!this._is_action_enabled('edit')) { + if (!this.is_action_enabled('edit')) { this.$el.addClass('oe_list_cannot_edit'); } - if (!this._is_action_enabled('delete')) { + if (!this.is_action_enabled('delete')) { this.$el.addClass('oe_list_cannot_delete'); } @@ -363,7 +363,7 @@ instance.web.ListView = instance.web.View.extend( /** @lends instance.web.ListVi if (!this.sidebar && this.options.$sidebar) { this.sidebar = new instance.web.Sidebar(this); this.sidebar.appendTo(this.options.$sidebar); - if (self._is_action_enabled('create')) { + if (self.is_action_enabled('create')) { this.sidebar.add_items('other', [ { label: _t("Import"), callback: this.on_sidebar_import } ]); @@ -371,7 +371,7 @@ instance.web.ListView = instance.web.View.extend( /** @lends instance.web.ListVi this.sidebar.add_items('other', [ { label: _t("Export"), callback: this.on_sidebar_export } ]); - if (self._is_action_enabled('delete')) { + if (self.is_action_enabled('delete')) { this.sidebar.add_items('other', [ { label: _t('Delete'), callback: this.do_delete_selected } ]); diff --git a/addons/web/static/src/js/view_list_editable.js b/addons/web/static/src/js/view_list_editable.js index 2615d8c66dd..5a1eac23a90 100644 --- a/addons/web/static/src/js/view_list_editable.js +++ b/addons/web/static/src/js/view_list_editable.js @@ -750,7 +750,7 @@ openerp.web.list_editable = function (instance) { instance.web.ListView.List.include(/** @lends instance.web.ListView.List# */{ row_clicked: function (event) { - if (!this.view.editable() || ! this.view._is_action_enabled('edit')) { + if (!this.view.editable() || ! this.view.is_action_enabled('edit')) { return this._super.apply(this, arguments); } var record_id = $(event.currentTarget).data('id'); diff --git a/addons/web/static/src/js/views.js b/addons/web/static/src/js/views.js index 183b63a218e..43d935b91e8 100644 --- a/addons/web/static/src/js/views.js +++ b/addons/web/static/src/js/views.js @@ -1363,7 +1363,7 @@ instance.web.View = instance.web.Widget.extend({ * An action is disabled by setting the corresponding attribute in the view's main element, * like:
      */ - _is_action_enabled: function(action) { + is_action_enabled: function(action) { return (_.has(this.fields_view.arch.attrs, action)) ? JSON.parse(this.fields_view.arch.attrs[action]) : true; } }); diff --git a/addons/web/static/src/xml/base.xml b/addons/web/static/src/xml/base.xml index a92eacc0710..789fbcb4baa 100644 --- a/addons/web/static/src/xml/base.xml +++ b/addons/web/static/src/xml/base.xml @@ -649,7 +649,7 @@
      - + @@ -725,12 +725,12 @@
      - +
      - +
      diff --git a/addons/web_diagram/static/src/xml/base_diagram.xml b/addons/web_diagram/static/src/xml/base_diagram.xml index 619ebb241f4..eec8e4f9139 100644 --- a/addons/web_diagram/static/src/xml/base_diagram.xml +++ b/addons/web_diagram/static/src/xml/base_diagram.xml @@ -2,7 +2,7 @@

      - +
      diff --git a/addons/web_gantt/static/src/js/gantt.js b/addons/web_gantt/static/src/js/gantt.js index 44adbc41b10..8d782c5fd51 100644 --- a/addons/web_gantt/static/src/js/gantt.js +++ b/addons/web_gantt/static/src/js/gantt.js @@ -183,7 +183,7 @@ instance.web_gantt.GanttView = instance.web.View.extend({ self.on_task_display(task_info.internal_task); } }); - if (this._is_action_enabled('create')) { + if (this.is_action_enabled('create')) { // insertion of create button var td = $($("table td", self.$el)[0]); var rendered = QWeb.render("GanttView-create-button"); diff --git a/addons/web_kanban/static/src/js/kanban.js b/addons/web_kanban/static/src/js/kanban.js index 143ff7d685b..cbe88ec847b 100644 --- a/addons/web_kanban/static/src/js/kanban.js +++ b/addons/web_kanban/static/src/js/kanban.js @@ -71,7 +71,7 @@ instance.web_kanban.KanbanView = instance.web.View.extend({ return JSON.parse(this.fields_view.arch.attrs.quick_create); return !! this.group_by; }, - _is_action_enabled: function(action) { + is_action_enabled: function(action) { if (action === 'create' && !this.options.creatable) return false; return this._super(action); @@ -735,7 +735,7 @@ instance.web_kanban.KanbanRecord = instance.web.Widget.extend({ var $action = $(this), type = $action.data('type') || 'button', method = 'do_action_' + (type === 'action' ? 'object' : type); - if ((type === 'edit' || type === 'delete') && ! self.view._is_action_enabled(type)) { + if ((type === 'edit' || type === 'delete') && ! self.view.is_action_enabled(type)) { self.view.open_record(self.id); } else if (_.str.startsWith(type, 'switch_')) { self.view.do_switch_view(type.substr(7)); diff --git a/addons/web_kanban/static/src/xml/web_kanban.xml b/addons/web_kanban/static/src/xml/web_kanban.xml index b2f9e757b43..278b9cc02b6 100644 --- a/addons/web_kanban/static/src/xml/web_kanban.xml +++ b/addons/web_kanban/static/src/xml/web_kanban.xml @@ -14,7 +14,7 @@
      - + From 570f76fa9aa46919f7ee95e973caee6d99cdfa7c Mon Sep 17 00:00:00 2001 From: Raphael Collet Date: Tue, 4 Sep 2012 16:12:04 +0200 Subject: [PATCH 342/436] [IMP] rename method _is_action_enabled to is_action_enabled bzr revid: rco@openerp.com-20120904141204-oqykdxnwb9yf3itc --- addons/crm/crm_lead_view.xml | 4 ++-- addons/hr_recruitment/hr_recruitment_view.xml | 4 ++-- addons/project/project_view.xml | 8 ++++---- addons/project_issue/project_issue_view.xml | 4 ++-- addons/survey/survey_view.xml | 4 ++-- 5 files changed, 12 insertions(+), 12 deletions(-) diff --git a/addons/crm/crm_lead_view.xml b/addons/crm/crm_lead_view.xml index 17cc7e9498d..2eb6981319a 100644 --- a/addons/crm/crm_lead_view.xml +++ b/addons/crm/crm_lead_view.xml @@ -311,8 +311,8 @@
      í