From 44e02f756b0066f61e1bbea648f92ea23a0aac02 Mon Sep 17 00:00:00 2001 From: Florent Xicluna Date: Wed, 8 Feb 2012 16:33:04 +0100 Subject: [PATCH 001/460] =?UTF-8?q?[FIX]=C2=A0file=5Fopen=20should=20not?= =?UTF-8?q?=20search=20zip=20files=20outside=20its=20root=20directory.=20?= =?UTF-8?q?=20Fix=20the=20returned=20value=20with=20pathinfo=3DTrue.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit lp bug: https://launchpad.net/bugs/928507 fixed lp bug: https://launchpad.net/bugs/928376 fixed bzr revid: florent.xicluna@gmail.com-20120208153304-9443zx2z09bws10x --- openerp/tools/misc.py | 89 ++++++++++++++++++++++++------------------- 1 file changed, 49 insertions(+), 40 deletions(-) diff --git a/openerp/tools/misc.py b/openerp/tools/misc.py index a5c72900fc7..3c9210214b1 100644 --- a/openerp/tools/misc.py +++ b/openerp/tools/misc.py @@ -134,7 +134,7 @@ def file_open(name, mode="r", subdir='addons', pathinfo=False): @param name name of the file @param mode file open mode @param subdir subdirectory - @param pathinfo if True returns tupple (fileobject, filepath) + @param pathinfo if True returns tuple (fileobject, filepath) @return fileobject if pathinfo is False else (fileobject, filepath) """ @@ -142,44 +142,51 @@ def file_open(name, mode="r", subdir='addons', pathinfo=False): adps = addons.module.ad_paths rtp = os.path.normcase(os.path.abspath(config['root_path'])) - if name.replace(os.path.sep, '/').startswith('addons/'): + if os.path.isabs(name): + # It is an absolute path + # Is it below 'addons_path' or 'root_path'? + name = os.path.normcase(os.path.normpath(name)) + for root in adps + [rtp]: + if name.startswith(root): + base = root.rstrip(os.sep) + name = name[len(base) + 1:] + break + else: + # It is outside the OpenERP root: skip zipfile lookup. + base, name = os.path.split(name) + return _fileopen(name, mode=mode, basedir=base, pathinfo=pathinfo) + + if name.replace(os.sep, '/').startswith('addons/'): subdir = 'addons' - name = name[7:] + name2 = name[7:] + elif subdir: + name = os.path.join(subdir, name) + if name.replace(os.sep, '/').startswith('addons/'): + subdir = 'addons' + name2 = name[7:] + else: + name2 = name - # First try to locate in addons_path + # First, try to locate in addons_path if subdir: - subdir2 = subdir - if subdir2.replace(os.path.sep, '/').startswith('addons/'): - subdir2 = subdir2[7:] - - subdir2 = (subdir2 != 'addons' or None) and subdir2 - for adp in adps: try: - if subdir2: - fn = os.path.join(adp, subdir2, name) - else: - fn = os.path.join(adp, name) - fn = os.path.normpath(fn) - fo = file_open(fn, mode=mode, subdir=None, pathinfo=pathinfo) - if pathinfo: - return fo, fn - return fo + return _fileopen(name2, mode=mode, basedir=adp, + pathinfo=pathinfo) except IOError: pass - if subdir: - name = os.path.join(rtp, subdir, name) - else: - name = os.path.join(rtp, name) + # Second, try to locate in root_path + return _fileopen(name, mode=mode, basedir=rtp, pathinfo=pathinfo) - name = os.path.normpath(name) - # Check for a zipfile in the path - head = name - zipname = False +def _fileopen(path, mode, basedir, pathinfo): + head = os.path.normpath(path) + name = os.path.normpath(os.path.join(basedir, path)) name2 = False - while True: + zipname = False + # Check for a zipfile in the path, but stop at the 'basedir' level + while os.sep in head: head, tail = os.path.split(head) if not tail: break @@ -187,9 +194,10 @@ def file_open(name, mode="r", subdir='addons', pathinfo=False): zipname = os.path.join(tail, zipname) else: zipname = tail - if zipfile.is_zipfile(head+'.zip'): + zpath = os.path.join(basedir, head + '.zip') + if zipfile.is_zipfile(zpath): from cStringIO import StringIO - zfile = zipfile.ZipFile(head+'.zip') + zfile = zipfile.ZipFile(zpath) try: fo = StringIO() fo.write(zfile.read(os.path.join( @@ -197,20 +205,21 @@ def file_open(name, mode="r", subdir='addons', pathinfo=False): os.sep, '/'))) fo.seek(0) if pathinfo: - return fo, name + return (fo, name) return fo except Exception: - name2 = os.path.normpath(os.path.join(head + '.zip', zipname)) - pass - for i in (name2, name): - if i and os.path.isfile(i): - fo = file(i, mode) + name2 = os.path.normpath(os.path.join(zpath, zipname)) + # Look for a normal file + for fname in (name2, name): + if fname and os.path.isfile(fname): + fo = open(fname, mode) if pathinfo: - return fo, i + return (fo, fname) return fo - if os.path.splitext(name)[1] == '.rml': - raise IOError, 'Report %s doesn\'t exist or deleted : ' %str(name) - raise IOError, 'File not found : %s' % name + # Not found + if name.endswith('.rml'): + raise IOError('Report %r doesn\'t exist or deleted' % name) + raise IOError('File not found: %s' % name) #---------------------------------------------------------- From 69a5eca5b48740c44e35ac5b0285e22a17c5dd8d Mon Sep 17 00:00:00 2001 From: Olivier Dony Date: Wed, 8 Feb 2012 18:39:32 +0100 Subject: [PATCH 002/460] [IMP] Give precedence to module directories instead of zips while locating resources The previous behavior gave the precedence to zipped modules, without any apparent reason, and this is sub-optimal for several reasons: 1. The default is to have regular modules, not zipped modules, so looking first for a regular module is more efficient. 2. Keeping a zipped module next to a regular module with the same name is not a documented or supported feature. 3. Even if you were relying on this behavior having the extracted module take precedence is more practical: you could simply extract the zipped module to test a quick fix. We have another issue related to this feature because the code looking for zipped modules escapes the addons paths chroot and goes up to the filesystem root looking for a zip module at each step. This is described in bug 928376 and a fix for it should follow. lp bug: https://launchpad.net/bugs/928376 fixed bzr revid: odo@openerp.com-20120208173932-pwhz53vxxdzbo8ja --- openerp/modules/module.py | 25 ++++++++++++++----------- openerp/tools/misc.py | 24 +++++++++++++++--------- 2 files changed, 29 insertions(+), 20 deletions(-) diff --git a/openerp/modules/module.py b/openerp/modules/module.py index 58a4547ff07..3d3bc0d9adf 100644 --- a/openerp/modules/module.py +++ b/openerp/modules/module.py @@ -280,25 +280,28 @@ def get_module_as_zip(modulename, b64enc=True, src=True): def get_module_resource(module, *args): """Return the full path of a resource of the given module. - @param module: the module - @param args: the resource path components + :param module: module name + :param list(str) args: resource path components within module - @return: absolute path to the resource + :rtype: str + :return: absolute path to the resource TODO name it get_resource_path TODO make it available inside on osv object (self.get_resource_path) """ - a = get_module_path(module) - if not a: return False - resource_path = opj(a, *args) - if zipfile.is_zipfile( a +'.zip') : - zip = zipfile.ZipFile( a + ".zip") + mod_path = get_module_path(module) + if not mod_path: return False + resource_path = opj(mod_path, *args) + if os.path.isdir(mod_path): + # the module is a directory - ignore zip behavior + if os.path.exists(resource_path): + return resource_path + elif zipfile.is_zipfile(mod_path + '.zip'): + zip = zipfile.ZipFile( mod_path + ".zip") files = ['/'.join(f.split('/')[1:]) for f in zip.namelist()] resource_path = '/'.join(args) if resource_path in files: - return opj(a, resource_path) - elif os.path.exists(resource_path): - return resource_path + return opj(mod_path, resource_path) return False def get_module_icon(module): diff --git a/openerp/tools/misc.py b/openerp/tools/misc.py index a5c72900fc7..e0be5ffaef9 100644 --- a/openerp/tools/misc.py +++ b/openerp/tools/misc.py @@ -175,10 +175,22 @@ def file_open(name, mode="r", subdir='addons', pathinfo=False): name = os.path.normpath(name) - # Check for a zipfile in the path + # Give higher priority to module directories, which is + # a more common case than zipped modules. + if os.path.isfile(name): + fo = file(name, mode) + if pathinfo: + return fo, name + return fo + + # Support for loading modules in zipped form. + # This will not work for zipped modules that are sitting + # outside of known addons paths. head = name zipname = False - name2 = False + # FIXME: implement chrooting inside addons paths and fix + # for incorrect path_info behavior. Work in progress by + # Florent X linked to bug 928376 while True: head, tail = os.path.split(head) if not tail: @@ -200,14 +212,8 @@ def file_open(name, mode="r", subdir='addons', pathinfo=False): return fo, name return fo except Exception: - name2 = os.path.normpath(os.path.join(head + '.zip', zipname)) pass - for i in (name2, name): - if i and os.path.isfile(i): - fo = file(i, mode) - if pathinfo: - return fo, i - return fo + if os.path.splitext(name)[1] == '.rml': raise IOError, 'Report %s doesn\'t exist or deleted : ' %str(name) raise IOError, 'File not found : %s' % name 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 003/460] [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 004/460] [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 41c90e8755b5b12f8e6f4e3870eb3a7f8e2b7b5b Mon Sep 17 00:00:00 2001 From: "Ajay Chauhan (OpenERP)" Date: Thu, 5 Jul 2012 18:34:55 +0530 Subject: [PATCH 005/460] [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/460] [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/460] [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/460] [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/460] [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/460] [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/460] [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 87c4a3a3b7406cd8bba071846e230f48d7c03da8 Mon Sep 17 00:00:00 2001 From: Anael Closson Date: Mon, 13 Aug 2012 19:04:29 +0200 Subject: [PATCH 012/460] initial note push bzr revid: acl@openerp.com-20120813170429-91dibb1dpe32j6w2 --- addons/crm_todo/crm_todo_view.xml | 2 +- addons/note/__init__.py | 23 ++++ addons/note/__openerp__.py | 59 +++++++++ addons/note/note.py | 134 +++++++++++++++++++ addons/note/note_view.xml | 158 +++++++++++++++++++++++ addons/note/security/ir.model.access.csv | 3 + addons/note/security/note_security.xml | 12 ++ addons/note/static/src/css/note.css | 58 +++++++++ 8 files changed, 448 insertions(+), 1 deletion(-) create mode 100644 addons/note/__init__.py create mode 100644 addons/note/__openerp__.py create mode 100644 addons/note/note.py create mode 100644 addons/note/note_view.xml create mode 100644 addons/note/security/ir.model.access.csv create mode 100644 addons/note/security/note_security.xml create mode 100644 addons/note/static/src/css/note.css diff --git a/addons/crm_todo/crm_todo_view.xml b/addons/crm_todo/crm_todo_view.xml index c959f1722e6..47b0655ac99 100644 --- a/addons/crm_todo/crm_todo_view.xml +++ b/addons/crm_todo/crm_todo_view.xml @@ -59,7 +59,7 @@ parent="base.menu_sales" action="crm_todo_action" sequence="6"/> - + diff --git a/addons/note/__init__.py b/addons/note/__init__.py new file mode 100644 index 00000000000..9bf064877b5 --- /dev/null +++ b/addons/note/__init__.py @@ -0,0 +1,23 @@ +# -*- 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 note + diff --git a/addons/note/__openerp__.py b/addons/note/__openerp__.py new file mode 100644 index 00000000000..96938f56ef1 --- /dev/null +++ b/addons/note/__openerp__.py @@ -0,0 +1,59 @@ +# -*- 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': 'Notes', + 'version': '0.1', + 'category': 'Tools', + 'description': """ +This module allows users to create their own notes inside OpenERP +============================================================================== + +With this module you can allow users to take notes inside OpenERP. +These notes can be shared with OpenERP or external users. +They also can be organized following user dependant categories. +Notes can be found in the 'Home' main menu, under 'Tool' submenu. +""", + 'author': 'OpenERP SA', + 'website': 'http://openerp.com', + 'depends': ['base_tools','mail','pad'], + 'init_xml': [], + 'update_xml': [ + 'security/note_security.xml', + 'security/ir.model.access.csv', + 'note_view.xml', + #'note_workflow.xml', + ], + 'demo_xml': [ + #"note_data.xml" + ], + 'test':[ + ], + 'css': [ + 'static/src/css/note.css', + ], + 'installable': True, + 'application': True, + 'category': 'Tools', + 'images': [], +} +# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/addons/note/note.py b/addons/note/note.py new file mode 100644 index 00000000000..a9afad770c4 --- /dev/null +++ b/addons/note/note.py @@ -0,0 +1,134 @@ +# -*- coding: utf-8 -*- +############################################################################## +# +# OpenERP, Open Source Management Solution +# Copyright (C) 2004-2010 Tiny SPRL (). +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# +############################################################################## + +from openerp.osv import osv, fields +from tools.translate import _ + +# +# Todo : + + +# ** fix editable when in form view ** not atm +# fix search +# fix design +# rights + + +class note_stage(osv.Model): + """ Category of Note """ + + _name = "note.stage" + _description = "Note Stage" + _columns = { + 'name': fields.char('Category Name', size=64, required=True), + 'sequence': fields.integer('Sequence', help="Used to order the note stages"), + 'user_id': fields.many2one('res.users', 'Owner', help="Owner of the note stage.", required=True, readonly=True), + 'fold': fields.boolean('Folded'), + + } + _sql_constraints = [ + ] + + _order = 'sequence asc' + + _defaults = { + 'fold': 0, + 'user_id': lambda self, cr, uid, ctx: uid, + 'sequence' : 1, + } + + def __init__(self, pool, cr): + osv.Model.__init__(self,pool, cr) + + +# class many2many_filter(fields.many2many) + +# grep many2many_mod dans le code + + + +class note_note(osv.Model): + """ Note """ + _name = 'note.note' + _inherit = ['mail.thread','pad.common'] + _pad_fields = ['note_pad'] + _description = "Note" + + def _get_note_first_line(self, cr, uid, ids, name, args, context=None): + res = {} + for note in self.browse(cr, uid, ids, context=context): + res[note.id] = note.note.split('\n')[0] + return res + + def _set_note_first_line(self, cr, uid, id, name, value, args, context=None): + # + # todo should set the pad first line (as title) + # + return self.write(cr, uid, [id], {'name': value}, context=context) + + _columns = { + 'name': fields.function(_get_note_first_line,_fnct_inv=_set_note_first_line, string='Note Summary', type="text", store=True), + 'note': fields.text('Pad Content'), + 'note_pad': fields.char('Pad Url', size=250), + 'sequence': fields.integer('Sequence'), + 'stage_id': fields.many2one('note.stage', 'Stage'), + 'active': fields.boolean('Active'), + 'color': fields.integer('Color Index'), + #'follower_ids': fields.one2many('mail.subscription', 'res_id', 'Followers', domain=[('res_model','=', 'note.note')]) + 'follower_ids': fields.many2many('res.users', 'mail_subscription', 'res_id', 'user_id', 'Followers', join_filter="mail_subscription.res_model='note.note'") + } + + _sql_constraints = [ + ] + + + def _get_default_stage_id(self,cr,uid,context=None): + id = self.pool.get('note.stage').search(cr,uid,[('sequence','=','1')]) + return id[0] + + _defaults = { + 'active' : 1, + 'stage_id' : _get_default_stage_id, + 'note_pad': lambda self, cr, uid, context: self.pad_generate_url(cr, uid, context), + } + + + _order = 'sequence asc' + + + + def _read_group_stage_ids(self, cr, uid, ids, domain, read_group_order=None, access_rights_uid=None, context=None): + access_rights_uid = access_rights_uid or uid + stage_obj = self.pool.get('note.stage') + + # only show stage groups not folded and owned by user + search_domain = [('fold', '=', False),('user_id', '=', uid)] + + stage_ids = stage_obj._search(cr, uid, search_domain, order=self._order, access_rights_uid=access_rights_uid, context=context) + result = stage_obj.name_get(cr, access_rights_uid, stage_ids, context=context) + return result + + _group_by_full = { + 'stage_id' : _read_group_stage_ids, + } + + def stage_set(self,cr,uid,ids,stage_id,context=None): + self.write(cr,uid,ids,{'stage_id': stage_id}) diff --git a/addons/note/note_view.xml b/addons/note/note_view.xml new file mode 100644 index 00000000000..9f8c91e9c62 --- /dev/null +++ b/addons/note/note_view.xml @@ -0,0 +1,158 @@ + + + + + + + note.stage.form + note.stage + form + +
+ + + +
+
+
+ + + + + + note.stage.tree + note.stage + tree + + + + + + + + + + + + + + Stages + note.stage + form + tree,form + [('user_id','=',uid)] + + + + + + + + + note.note.kanban + note.note + kanban + + + + + + + + + + + +
+ +
+ í +
+ + +
+ + + + +
+ +
+ + + +
+ +
+
+
+
+
+
+
+ + + + + + note.note.form + note.note + form + +
+
+ + +
+ +
+ +
+ +
+
+ + + + note.note.search + note.note + search + + + + + + + + + + + + + + + + note.note.tree + note.note + tree + + + + + Notes + note.note + form + kanban,tree,form + + + + + +
+
diff --git a/addons/note/security/ir.model.access.csv b/addons/note/security/ir.model.access.csv new file mode 100644 index 00000000000..fdd6030ca51 --- /dev/null +++ b/addons/note/security/ir.model.access.csv @@ -0,0 +1,3 @@ +id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink +access_note_stage_user,note.stage user,model_note_stage,base.group_user,1,1,1,1 +access_note_note_user,note.note user,model_note_note,base.group_user,1,1,1,1 diff --git a/addons/note/security/note_security.xml b/addons/note/security/note_security.xml new file mode 100644 index 00000000000..46eebee629c --- /dev/null +++ b/addons/note/security/note_security.xml @@ -0,0 +1,12 @@ + + + + + My notes + + + [('follower_ids','=',user.id)] + + + + diff --git a/addons/note/static/src/css/note.css b/addons/note/static/src/css/note.css new file mode 100644 index 00000000000..a6b097ece88 --- /dev/null +++ b/addons/note/static/src/css/note.css @@ -0,0 +1,58 @@ + + +.openerp .oe_webclient .oe_application .oe_view_manager .oe_view_manager_body .oe_view_manager_view_kanban .oe_kanban_view .oe_kanban_groups .oe_kanban_groups_records .oe_kanban_column .oe_fold_column .oe_kanban_card { + text-decoration:none; + color:#000; + display:block; + padding:1em; + margin-right: 1em; + margin-bottom: 1em; + + -moz-box-shadow:5px 5px 7px rgba(33,33,33,1); + -webkit-box-shadow: 5px 5px 7px rgba(33,33,33,.7); + box-shadow: 5px 5px 7px rgba(33,33,33,.7); +} + + +.openerp .oe_webclient .oe_application .oe_view_manager .oe_view_manager_body .oe_view_manager_view_kanban .oe_kanban_view .oe_kanban_groups .oe_kanban_groups_records .oe_kanban_column .oe_fold_column .oe_kanban_card { + -webkit-transform: rotate(-3deg); + -o-transform: rotate(-3deg); + -moz-transform:rotate(-3deg); +} + +.openerp .oe_webclient .oe_application .oe_view_manager .oe_view_manager_body .oe_view_manager_view_kanban .oe_kanban_view .oe_kanban_groups:nth-child(even) .oe_kanban_groups_records .oe_kanban_column .oe_fold_column .oe_kanban_card { + -o-transform:rotate(1deg); + -webkit-transform:rotate(1deg); + -moz-transform:rotate(1deg); + position:relative; + top:3px; +} + +.openerp .oe_webclient .oe_application .oe_view_manager .oe_view_manager_body .oe_view_manager_view_kanban .oe_kanban_view .oe_kanban_groups:nth-child(2n) .oe_kanban_groups_records .oe_kanban_column .oe_fold_column .oe_kanban_card { + -o-transform:rotate(-2deg); + -webkit-transform:rotate(-2deg); + -moz-transform:rotate(-2deg); + position:relative; + top:-3px; +} + +.openerp .oe_webclient .oe_application .oe_view_manager .oe_view_manager_body .oe_view_manager_view_kanban .oe_kanban_view .oe_kanban_groups:nth-child(2n) .oe_kanban_groups_records .oe_kanban_column .oe_fold_column .oe_kanban_card { + -o-transform:rotate(2deg); + -webkit-transform:rotate(2deg); + -moz-transform:rotate(2deg); + position:relative; + top:-5px; +} + +.openerp .oe_webclient .oe_application .oe_view_manager .oe_view_manager_body .oe_view_manager_view_kanban .oe_kanban_view .oe_kanban_groups .oe_kanban_groups_records .oe_kanban_column .oe_fold_column .oe_kanban_card:hover, +.openerp .oe_webclient .oe_application .oe_view_manager .oe_view_manager_body .oe_view_manager_view_kanban .oe_kanban_view .oe_kanban_groups .oe_kanban_groups_records .oe_kanban_column .oe_fold_column .oe_kanban_card:focus +{ + box-shadow:10px 10px 7px rgba(0,0,0,.7); + -moz-box-shadow:10px 10px 7px rgba(0,0,0,.7); + -webkit-box-shadow: 10px 10px 7px rgba(0,0,0,.7); + -webkit-transform: scale(1.25); + -moz-transform: scale(1.25); + -o-transform: scale(1.25); + position:relative; + z-index:5; +} From 076fdb09940c57c385c3b3f7639bb893464afb69 Mon Sep 17 00:00:00 2001 From: Jigar Amin - OpenERP Date: Tue, 14 Aug 2012 18:38:21 +0530 Subject: [PATCH 013/460] [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 014/460] [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 876dc272424e78cf45db33dddd23ce994da1ba5e Mon Sep 17 00:00:00 2001 From: Anael Closson Date: Tue, 14 Aug 2012 16:21:51 +0200 Subject: [PATCH 015/460] wip bzr revid: acl@openerp.com-20120814142151-uwi6xkdamv623pos --- addons/note/__openerp__.py | 3 +- addons/note/note.py | 2 +- addons/note/note_data.xml | 224 +++++++++++++++++++++++++++ addons/note/note_view.xml | 4 +- addons/note/static/src/css/Makefile | 3 + addons/note/static/src/css/note.css | 79 +++++++--- addons/note/static/src/css/note.sass | 137 ++++++++++++++++ 7 files changed, 427 insertions(+), 25 deletions(-) create mode 100644 addons/note/note_data.xml create mode 100644 addons/note/static/src/css/Makefile create mode 100644 addons/note/static/src/css/note.sass diff --git a/addons/note/__openerp__.py b/addons/note/__openerp__.py index 96938f56ef1..c9200324bb7 100644 --- a/addons/note/__openerp__.py +++ b/addons/note/__openerp__.py @@ -41,10 +41,9 @@ Notes can be found in the 'Home' main menu, under 'Tool' submenu. 'security/note_security.xml', 'security/ir.model.access.csv', 'note_view.xml', - #'note_workflow.xml', ], 'demo_xml': [ - #"note_data.xml" + "note_data.xml" ], 'test':[ ], diff --git a/addons/note/note.py b/addons/note/note.py index a9afad770c4..89049bf6ec8 100644 --- a/addons/note/note.py +++ b/addons/note/note.py @@ -93,7 +93,7 @@ class note_note(osv.Model): 'active': fields.boolean('Active'), 'color': fields.integer('Color Index'), #'follower_ids': fields.one2many('mail.subscription', 'res_id', 'Followers', domain=[('res_model','=', 'note.note')]) - 'follower_ids': fields.many2many('res.users', 'mail_subscription', 'res_id', 'user_id', 'Followers', join_filter="mail_subscription.res_model='note.note'") + #'follower_ids': fields.many2many('res.users', 'mail_subscription', 'res_id', 'user_id', 'Followers', join_filter="mail_subscription.res_model='note.note'") } _sql_constraints = [ diff --git a/addons/note/note_data.xml b/addons/note/note_data.xml new file mode 100644 index 00000000000..15226bd48b0 --- /dev/null +++ b/addons/note/note_data.xml @@ -0,0 +1,224 @@ + + + + + Today + 1 + + + + Tomorrow + 2 + + + + This week + 3 + + + + This month + 4 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/addons/note/note_view.xml b/addons/note/note_view.xml index 9f8c91e9c62..ded45474f2f 100644 --- a/addons/note/note_view.xml +++ b/addons/note/note_view.xml @@ -84,9 +84,11 @@
- +
diff --git a/addons/note/static/src/css/Makefile b/addons/note/static/src/css/Makefile new file mode 100644 index 00000000000..d6019f4ed4a --- /dev/null +++ b/addons/note/static/src/css/Makefile @@ -0,0 +1,3 @@ +note.css: note.sass + sass -t expanded note.sass note.css + diff --git a/addons/note/static/src/css/note.css b/addons/note/static/src/css/note.css index a6b097ece88..4b3c7e0e57d 100644 --- a/addons/note/static/src/css/note.css +++ b/addons/note/static/src/css/note.css @@ -1,6 +1,6 @@ -.openerp .oe_webclient .oe_application .oe_view_manager .oe_view_manager_body .oe_view_manager_view_kanban .oe_kanban_view .oe_kanban_groups .oe_kanban_groups_records .oe_kanban_column .oe_fold_column .oe_kanban_card { +.openerp .oe_fold_column .oe_kanban_card { text-decoration:none; color:#000; display:block; @@ -14,35 +14,72 @@ } -.openerp .oe_webclient .oe_application .oe_view_manager .oe_view_manager_body .oe_view_manager_view_kanban .oe_kanban_view .oe_kanban_groups .oe_kanban_groups_records .oe_kanban_column .oe_fold_column .oe_kanban_card { + +.oe_kanban_record .oe_kanban_card { + -webkit-transform: rotate(-2deg); + -o-transform: rotate(-2deg); + -moz-transform:rotate(-2deg); +} + +.oe_kanban_record:nth-of-type(even) .oe_kanban_card { + -webkit-transform: rotate(-1deg); + -o-transform: rotate(-1deg); + -moz-transform:rotate(-1deg); +} + + +.oe_kanban_record:nth-of-type(3n) .oe_kanban_card { + -webkit-transform: rotate(4deg); + -o-transform: rotate(4deg); + -moz-transform:rotate(4deg); +} + +.oe_kanban_column:nth-of-type(even) .oe_kanban_record .oe_kanban_card { + -webkit-transform: rotate(2deg); + -o-transform: rotate(2deg); + -moz-transform:rotate(2deg); +} + +.oe_kanban_column:nth-of-type(even) .oe_kanban_record:nth-of-type(even) .oe_kanban_card { -webkit-transform: rotate(-3deg); -o-transform: rotate(-3deg); -moz-transform:rotate(-3deg); } -.openerp .oe_webclient .oe_application .oe_view_manager .oe_view_manager_body .oe_view_manager_view_kanban .oe_kanban_view .oe_kanban_groups:nth-child(even) .oe_kanban_groups_records .oe_kanban_column .oe_fold_column .oe_kanban_card { - -o-transform:rotate(1deg); - -webkit-transform:rotate(1deg); +.oe_kanban_column:nth-of-type(even) .oe_kanban_record:nth-of-type(3n) .oe_kanban_card { + -webkit-transform: rotate(1deg); + -o-transform: rotate(1deg); -moz-transform:rotate(1deg); - position:relative; - top:3px; } +/* .openerp .oe_fold_column:nth-child(even) .oe_kanban_card { */ +/* -webkit-transform: rotate(-3deg); */ +/* -o-transform: rotate(-3deg); */ +/* -moz-transform:rotate(-3deg); */ +/* } */ -.openerp .oe_webclient .oe_application .oe_view_manager .oe_view_manager_body .oe_view_manager_view_kanban .oe_kanban_view .oe_kanban_groups:nth-child(2n) .oe_kanban_groups_records .oe_kanban_column .oe_fold_column .oe_kanban_card { - -o-transform:rotate(-2deg); - -webkit-transform:rotate(-2deg); - -moz-transform:rotate(-2deg); - position:relative; - top:-3px; -} +/* .openerp .oe_kanban_groups_records .oe_kanban_column .oe_fold_column:nth-child(even) .oe_kanban_card { */ +/* -o-transform:rotate(1deg); */ +/* -webkit-transform:rotate(1deg); */ +/* -moz-transform:rotate(1deg); */ +/* position:relative; */ +/* top:3px; */ +/* } */ -.openerp .oe_webclient .oe_application .oe_view_manager .oe_view_manager_body .oe_view_manager_view_kanban .oe_kanban_view .oe_kanban_groups:nth-child(2n) .oe_kanban_groups_records .oe_kanban_column .oe_fold_column .oe_kanban_card { - -o-transform:rotate(2deg); - -webkit-transform:rotate(2deg); - -moz-transform:rotate(2deg); - position:relative; - top:-5px; -} +/* .openerp .oe_fold_column:nth-child(3n) .oe_kanban_card { */ +/* -o-transform:rotate(-2deg); */ +/* -webkit-transform:rotate(-2deg); */ +/* -moz-transform:rotate(-2deg); */ +/* position:relative; */ +/* top:-3px; */ +/* } */ + +/* .openerp.oe_fold_column:nth-child(5n) .oe_kanban_card { */ +/* -o-transform:rotate(2deg); */ +/* -webkit-transform:rotate(2deg); */ +/* -moz-transform:rotate(2deg); */ +/* position:relative; */ +/* top:-5px; */ +/* } */ .openerp .oe_webclient .oe_application .oe_view_manager .oe_view_manager_body .oe_view_manager_view_kanban .oe_kanban_view .oe_kanban_groups .oe_kanban_groups_records .oe_kanban_column .oe_fold_column .oe_kanban_card:hover, .openerp .oe_webclient .oe_application .oe_view_manager .oe_view_manager_body .oe_view_manager_view_kanban .oe_kanban_view .oe_kanban_groups .oe_kanban_groups_records .oe_kanban_column .oe_fold_column .oe_kanban_card:focus diff --git a/addons/note/static/src/css/note.sass b/addons/note/static/src/css/note.sass new file mode 100644 index 00000000000..cd04015dcbb --- /dev/null +++ b/addons/note/static/src/css/note.sass @@ -0,0 +1,137 @@ +@charset "utf-8" + +// Variables {{{ +$section-title-color: #8786b7 +$tag-bg-light: #f0f0fa +$tag-bg-dark: #8786b7 +$tag-border: #afafb6 +$tag-border-selected: #a6a6fe +$hover-background: #f0f0fa +$link-color: #8a89ba +$sheet-max-width: 860px +// }}} +// Mixins {{{ +@font-face + font-family: 'mnmliconsRegular' + src: url('/web/static/src/font/mnmliconsv21-webfont.eot') format('eot') + src: url('/web/static/src/font/mnmliconsv21-webfont.woff') format('woff') + src: url('/web/static/src/font/mnmliconsv21-webfont.ttf') format('truetype') + src: url('/web/static/src/font/mnmliconsv21-webfont.svg') format('svg') active + font-weight: normal + font-style: normal + +@font-face + font-family: 'EntypoRegular' + src: url('/web/static/src/font/entypo-webfont.eot') format('eot') + src: url('/web/static/src/font/entypo-webfont.eot?#iefix') format('embedded-opentype') + src: url('/web/static/src/font/entypo-webfont.woff') format('woff') + src: url('/web/static/src/font/entypo-webfont.ttf') format('truetype') + src: url('/web/static/src/font/entypo-webfont.svg') format('svg') active + font-weight: normal + font-style: normal + +@mixin reset() + border: none + padding: 0 + margin: 0 + background: none + @include radius(none) + @include box-shadow(none) + +@mixin vertical-gradient($startColor: #555, $endColor: #333) + background-color: $startColor + background-image: -webkit-gradient(linear, left top, left bottom, from($startColor), to($endColor)) /* Saf4+, Chrome */ + background-image: -webkit-linear-gradient(top, $startColor, $endColor) /* Chrome 10+, Saf5.1+, iOS 5+ */ + background-image: -moz-linear-gradient(top, $startColor, $endColor) /* FF3.6 */ + background-image: -ms-linear-gradient(top, $startColor, $endColor) /* IE10 */ + background-image: -o-linear-gradient(top, $startColor, $endColor) /* Opera 11.10+ */ + background-image: linear-gradient(to bottom, $startColor, $endColor) + +@mixin radial-gradient($gradient) + background-position: center center + background-image: -webkit-radial-gradient(circle, $gradient) + background-image: -moz-radial-gradient($gradient) + background-image: -ms-radial-gradient($gradient) + background-image: radial-gradient($gradient) + +@mixin radius($radius: 5px) + -moz-border-radius: $radius + -webkit-border-radius: $radius + border-radius: $radius + +@mixin box-shadow($bsval: 0px 1px 4px #777) + -moz-box-shadow: $bsval + -webkit-box-shadow: $bsval + box-shadow: $bsval + +@mixin transition($transval: (border linear 0.2s, box-shadow linear 0.2s)) + -webkit-transition: $transval + -moz-transition: $transval + -ms-transition: $transval + -o-transition: $transval + transition: $transval + +@mixin opacity($opacity: .5) + filter: alpha(opacity=$opacity * 100) + opacity: $opacity + +@mixin background-clip($clip: padding-box) + -webkit-background-clip: $clip + -moz-background-clip: $clip + background-clip: $clip + +@mixin box-sizing($type: content) + // type = border || content || padding + -webkit-box-sizing: #{$type}-box + -moz-box-sizing: #{$type}-box + -ms-box-sizing: #{$type}-box + box-sizing: #{$type}-box + +// 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 +@mixin text-to-icon($icon-name, $color: #404040) + font-size: 1px + letter-spacing: -1px + color: transparent + &:before + font: 21px "mnmliconsRegular" + content: $icon-name + 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 +// }}} + + +.oe_kanban_color_2 + background-color:red + +// au BufWritePost,FileWritePost *.sass :!sass --style expanded --line-numbers > "%:p:r.css" + From 56666953b38e7ea2c6e8e8bc8391bd6da9b64276 Mon Sep 17 00:00:00 2001 From: Anael Closson Date: Tue, 14 Aug 2012 17:18:00 +0200 Subject: [PATCH 016/460] Some fixes bzr revid: acl@openerp.com-20120814151800-e0adyvny6v17c3qr --- addons/note/note.py | 15 +------- addons/note/note_view.xml | 16 ++------- addons/note/static/src/css/note.css | 54 +++++++++++------------------ 3 files changed, 24 insertions(+), 61 deletions(-) diff --git a/addons/note/note.py b/addons/note/note.py index 89049bf6ec8..7d0ff170d79 100644 --- a/addons/note/note.py +++ b/addons/note/note.py @@ -22,16 +22,6 @@ from openerp.osv import osv, fields from tools.translate import _ -# -# Todo : - - -# ** fix editable when in form view ** not atm -# fix search -# fix design -# rights - - class note_stage(osv.Model): """ Category of Note """ @@ -55,9 +45,6 @@ class note_stage(osv.Model): 'sequence' : 1, } - def __init__(self, pool, cr): - osv.Model.__init__(self,pool, cr) - # class many2many_filter(fields.many2many) @@ -93,7 +80,7 @@ class note_note(osv.Model): 'active': fields.boolean('Active'), 'color': fields.integer('Color Index'), #'follower_ids': fields.one2many('mail.subscription', 'res_id', 'Followers', domain=[('res_model','=', 'note.note')]) - #'follower_ids': fields.many2many('res.users', 'mail_subscription', 'res_id', 'user_id', 'Followers', join_filter="mail_subscription.res_model='note.note'") + 'follower_ids': fields.many2many('res.users', 'mail_subscription', 'res_id', 'user_id', 'Followers', join_filter="mail_subscription.res_model='note.note'") } _sql_constraints = [ diff --git a/addons/note/note_view.xml b/addons/note/note_view.xml index ded45474f2f..39dca553c39 100644 --- a/addons/note/note_view.xml +++ b/addons/note/note_view.xml @@ -16,8 +16,6 @@ - - note.stage.tree @@ -77,18 +75,14 @@
- -
- +
@@ -99,8 +93,6 @@ - - note.note.form @@ -112,7 +104,7 @@ - +
@@ -137,7 +129,6 @@
- note.note.tree @@ -145,7 +136,6 @@ tree - Notes note.note diff --git a/addons/note/static/src/css/note.css b/addons/note/static/src/css/note.css index 4b3c7e0e57d..39800834417 100644 --- a/addons/note/static/src/css/note.css +++ b/addons/note/static/src/css/note.css @@ -13,8 +13,6 @@ box-shadow: 5px 5px 7px rgba(33,33,33,.7); } - - .oe_kanban_record .oe_kanban_card { -webkit-transform: rotate(-2deg); -o-transform: rotate(-2deg); @@ -22,12 +20,11 @@ } .oe_kanban_record:nth-of-type(even) .oe_kanban_card { - -webkit-transform: rotate(-1deg); - -o-transform: rotate(-1deg); - -moz-transform:rotate(-1deg); + -webkit-transform: rotate(1deg); + -o-transform: rotate(1deg); + -moz-transform:rotate(1deg); } - .oe_kanban_record:nth-of-type(3n) .oe_kanban_card { -webkit-transform: rotate(4deg); -o-transform: rotate(4deg); @@ -51,38 +48,27 @@ -o-transform: rotate(1deg); -moz-transform:rotate(1deg); } -/* .openerp .oe_fold_column:nth-child(even) .oe_kanban_card { */ -/* -webkit-transform: rotate(-3deg); */ -/* -o-transform: rotate(-3deg); */ -/* -moz-transform:rotate(-3deg); */ -/* } */ -/* .openerp .oe_kanban_groups_records .oe_kanban_column .oe_fold_column:nth-child(even) .oe_kanban_card { */ -/* -o-transform:rotate(1deg); */ -/* -webkit-transform:rotate(1deg); */ -/* -moz-transform:rotate(1deg); */ -/* position:relative; */ -/* top:3px; */ -/* } */ +.oe_kanban_column:nth-of-type(3n) .oe_kanban_record .oe_kanban_card { + -webkit-transform: rotate(-2deg); + -o-transform: rotate(-2deg); + -moz-transform:rotate(-2deg); +} -/* .openerp .oe_fold_column:nth-child(3n) .oe_kanban_card { */ -/* -o-transform:rotate(-2deg); */ -/* -webkit-transform:rotate(-2deg); */ -/* -moz-transform:rotate(-2deg); */ -/* position:relative; */ -/* top:-3px; */ -/* } */ +.oe_kanban_column:nth-of-type(3n) .oe_kanban_record:nth-of-type(even) .oe_kanban_card { + -webkit-transform: rotate(1deg); + -o-transform: rotate(1deg); + -moz-transform:rotate(1deg); +} -/* .openerp.oe_fold_column:nth-child(5n) .oe_kanban_card { */ -/* -o-transform:rotate(2deg); */ -/* -webkit-transform:rotate(2deg); */ -/* -moz-transform:rotate(2deg); */ -/* position:relative; */ -/* top:-5px; */ -/* } */ +.oe_kanban_column:nth-of-type(3n) .oe_kanban_record:nth-of-type(3n) .oe_kanban_card { + -webkit-transform: rotate(-1deg); + -o-transform: rotate(-1deg); + -moz-transform:rotate(-1deg); +} -.openerp .oe_webclient .oe_application .oe_view_manager .oe_view_manager_body .oe_view_manager_view_kanban .oe_kanban_view .oe_kanban_groups .oe_kanban_groups_records .oe_kanban_column .oe_fold_column .oe_kanban_card:hover, -.openerp .oe_webclient .oe_application .oe_view_manager .oe_view_manager_body .oe_view_manager_view_kanban .oe_kanban_view .oe_kanban_groups .oe_kanban_groups_records .oe_kanban_column .oe_fold_column .oe_kanban_card:focus +.openerp .oe_kanban_column .oe_fold_column .oe_kanban_card:hover, +.openerp .oe_kanban_column .oe_fold_column .oe_kanban_card:focus { box-shadow:10px 10px 7px rgba(0,0,0,.7); -moz-box-shadow:10px 10px 7px rgba(0,0,0,.7); From 531e077f1bcbbb4d3e45fe1985e6f246132b468e Mon Sep 17 00:00:00 2001 From: Anael Closson Date: Tue, 14 Aug 2012 17:42:40 +0200 Subject: [PATCH 017/460] [FIX] some misc fixes bzr revid: acl@openerp.com-20120814154240-on30lw5qfp66xj7q --- addons/note/note.py | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/addons/note/note.py b/addons/note/note.py index 7d0ff170d79..05c5ea6eadf 100644 --- a/addons/note/note.py +++ b/addons/note/note.py @@ -32,8 +32,8 @@ class note_stage(osv.Model): 'sequence': fields.integer('Sequence', help="Used to order the note stages"), 'user_id': fields.many2one('res.users', 'Owner', help="Owner of the note stage.", required=True, readonly=True), 'fold': fields.boolean('Folded'), - } + _sql_constraints = [ ] @@ -64,13 +64,13 @@ class note_note(osv.Model): for note in self.browse(cr, uid, ids, context=context): res[note.id] = note.note.split('\n')[0] return res - + def _set_note_first_line(self, cr, uid, id, name, value, args, context=None): # # todo should set the pad first line (as title) # return self.write(cr, uid, [id], {'name': value}, context=context) - + _columns = { 'name': fields.function(_get_note_first_line,_fnct_inv=_set_note_first_line, string='Note Summary', type="text", store=True), 'note': fields.text('Pad Content'), @@ -79,8 +79,7 @@ class note_note(osv.Model): 'stage_id': fields.many2one('note.stage', 'Stage'), 'active': fields.boolean('Active'), 'color': fields.integer('Color Index'), - #'follower_ids': fields.one2many('mail.subscription', 'res_id', 'Followers', domain=[('res_model','=', 'note.note')]) - 'follower_ids': fields.many2many('res.users', 'mail_subscription', 'res_id', 'user_id', 'Followers', join_filter="mail_subscription.res_model='note.note'") + 'follower_ids': fields.many2many('res.users', 'mail_subscription', 'res_id', 'user_id', 'Followers', join_filter="mail_subscription.res_model='note.note'") } _sql_constraints = [ @@ -97,18 +96,15 @@ class note_note(osv.Model): 'note_pad': lambda self, cr, uid, context: self.pad_generate_url(cr, uid, context), } - _order = 'sequence asc' - - def _read_group_stage_ids(self, cr, uid, ids, domain, read_group_order=None, access_rights_uid=None, context=None): access_rights_uid = access_rights_uid or uid stage_obj = self.pool.get('note.stage') - + # only show stage groups not folded and owned by user search_domain = [('fold', '=', False),('user_id', '=', uid)] - + stage_ids = stage_obj._search(cr, uid, search_domain, order=self._order, access_rights_uid=access_rights_uid, context=context) result = stage_obj.name_get(cr, access_rights_uid, stage_ids, context=context) return result From c02116b41f9c500828c54725c11541ddcd1c5dfd Mon Sep 17 00:00:00 2001 From: Fabien Pinckaers Date: Tue, 14 Aug 2012 21:44:12 +0200 Subject: [PATCH 018/460] [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 019/460] [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 020/460] [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 021/460] [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 @@ - From 1b58af40cc5affbdde948ed323ba4d3e626c9d6d Mon Sep 17 00:00:00 2001 From: Fabien Pinckaers Date: Thu, 16 Aug 2012 09:26:42 +0200 Subject: [PATCH 030/460] [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 031/460] [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 032/460] [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 @@ - +