diff --git a/MANIFEST.in b/MANIFEST.in index b25045b8213..84ed45985a9 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -9,7 +9,8 @@ include bin/server.pkey include bin/gpl.txt include man/openerp-server.1 include man/openerp_serverrc.5 -recursive-include pixmaps +recursive-include pixmaps * +recursive-include win32 * recursive-include doc * recursive-include bin *xml *xsl *sql *rml *sxw *csv *rng graft bin/addons/ diff --git a/bin/addons/__init__.py b/bin/addons/__init__.py index 82761d647bd..c8ef1719b3a 100644 --- a/bin/addons/__init__.py +++ b/bin/addons/__init__.py @@ -3,6 +3,7 @@ # # OpenERP, Open Source Management Solution # Copyright (C) 2004-2009 Tiny SPRL (). +# Copyright (C) 2010 OpenERP s.a. (). # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as @@ -27,12 +28,11 @@ import zipimport import osv import tools import tools.osutil +from tools.safe_eval import safe_eval as eval import pooler import netsvc -from osv import fields -import addons import zipfile import release @@ -63,9 +63,6 @@ ad_paths.append(_ad) # for get_module_path # Modules already loaded loaded = [] -#Modules whch raised error -not_loaded = [] - class Graph(dict): def addNode(self, name, deps): @@ -80,14 +77,15 @@ class Graph(dict): Node(name, self) def update_from_db(self, cr): + if not len(self): + return # update the graph with values from the database (if exist) ## First, we set the default values for each package in graph additional_data = dict.fromkeys(self.keys(), {'id': 0, 'state': 'uninstalled', 'dbdemo': False, 'installed_version': None}) ## Then we get the values from the database cr.execute('SELECT name, id, state, demo AS dbdemo, latest_version AS installed_version' ' FROM ir_module_module' - ' WHERE name in (%s)' % (','.join(['%s'] * len(self))), - additional_data.keys() + ' WHERE name IN %s',(tuple(additional_data),) ) ## and we update the default values with values from the database @@ -97,8 +95,6 @@ class Graph(dict): for k, v in additional_data[package.name].items(): setattr(package, k, v) - - def __iter__(self): level = 0 done = set(self.keys()) @@ -302,7 +298,7 @@ def load_information_from_description_file(module): :param module: The name of the module (sale, purchase, ...) """ for filename in ['__openerp__.py', '__terp__.py']: - description_file = addons.get_module_resource(module, filename) + description_file = get_module_resource(module, filename) if os.path.isfile(description_file): return eval(tools.file_open(description_file).read()) @@ -340,11 +336,8 @@ def upgrade_graph(graph, cr, module_list, force=None): terp_file = get_module_resource(module, '__terp__.py') if not mod_path or not terp_file: - global not_loaded - not_loaded.append(module) - logger.notifyChannel('init', netsvc.LOG_WARNING, 'module %s: not installable' % (module)) - raise osv.osv.except_osv('Error!',"Module '%s' was not found" % (module,)) - + logger.notifyChannel('init', netsvc.LOG_WARNING, 'module %s: not found, skipped' % (module)) + continue if os.path.isfile(terp_file) or zipfile.is_zipfile(mod_path+'.zip'): try: @@ -354,7 +347,8 @@ def upgrade_graph(graph, cr, module_list, force=None): raise if info.get('installable', True): packages.append((module, info.get('depends', []), info)) - + else: + logger.notifyChannel('init', netsvc.LOG_WARNING, 'module %s: not installable, skipped' % (module)) dependencies = dict([(p, deps) for p, deps, data in packages]) current, later = set([p for p, dep, data in packages]), set() @@ -644,16 +638,13 @@ def load_module_graph(cr, graph, status=None, perform_checks=True, **kwargs): try: _load_data(cr, module_name, id_map, mode, 'test') except Exception, e: - if tools.config.options['test-continue']: - logger.notifyChannel('ERROR', netsvc.LOG_TEST, e) - pass - else: - raise + logger.notifyChannel('ERROR', netsvc.LOG_TEST, e) + pass finally: - if tools.config.options['test-rollback']: - cr.rollback() - else: + if tools.config.options['test-commit']: cr.commit() + else: + cr.rollback() def _load_data(cr, module_name, id_map, mode, kind): noupdate = (kind == 'demo') @@ -665,6 +656,9 @@ def load_module_graph(cr, graph, status=None, perform_checks=True, **kwargs): # TODO manage .csv file with noupdate == (kind == 'init') if ext == '.sql': process_sql_file(cr, fp) + elif ext == '.csv': + noupdate = (kind == 'init') + tools.convert_csv_import(cr, module_name, pathname, file.read(), id_map, mode, noupdate) elif ext == '.yml': tools.convert_yaml_import(cr, module_name, file, id_map, mode, noupdate) else: @@ -719,14 +713,7 @@ def load_module_graph(cr, graph, status=None, perform_checks=True, **kwargs): for kind in ('init', 'update'): if package.state=='to upgrade': # upgrading the module information - modobj.write(cr, 1, [mid], { - 'description': package.data.get('description', ''), - 'shortdesc': package.data.get('name', ''), - 'author': package.data.get('author', 'Unknown'), - 'website': package.data.get('website', ''), - 'license': package.data.get('license', 'GPL-2'), - 'certificate': package.data.get('certificate') or None, - }) + modobj.write(cr, 1, [mid], modobj.get_values_from_terp(package.data)) load_init_update_xml(cr, m, idref, mode, kind) load_data(cr, m, idref, mode) if hasattr(package, 'demo') or (package.dbdemo and package.state != 'installed'): @@ -735,7 +722,11 @@ def load_module_graph(cr, graph, status=None, perform_checks=True, **kwargs): load_demo(cr, m, idref, mode) cr.execute('update ir_module_module set demo=%s where id=%s', (True, mid)) - load_test(cr, m, idref, mode) + # launch tests only in demo mode, as most tests will depend + # on demo data. Other tests can be added into the regular + # 'data' section, but should probably not alter the data, + # as there is no rollback. + load_test(cr, m, idref, mode) package_todo.append(package.name) @@ -797,13 +788,11 @@ def load_modules(db, force_demo=False, status=None, update_module=False): # NOTE: Try to also load the modules that have been marked as uninstallable previously... STATES_TO_LOAD = ['installed', 'to upgrade', 'uninstallable'] graph = create_graph(cr, ['base'], force) + if not graph: + logger.notifyChannel('init', netsvc.LOG_CRITICAL, 'module base cannot be loaded! (hint: verify addons-path)') + raise osv.osv.except_osv('Could not load base module', 'module base cannot be loaded! (hint: verify addons-path)') has_updates = load_module_graph(cr, graph, status, perform_checks=(not update_module), report=report) - global not_loaded - if not_loaded: - #If some module is not loaded don't proceed further - not_loaded = [] - return if update_module: modobj = pool.get('ir.module.module') states = {'installed': 'button_upgrade', 'uninstalled': 'button_install'} @@ -825,8 +814,8 @@ def load_modules(db, force_demo=False, status=None, update_module=False): while True: loop_guardrail += 1 if loop_guardrail > 100: - raise ProgrammingError() - cr.execute("SELECT name from ir_module_module WHERE state in (%s)" % ','.join(['%s']*len(STATES_TO_LOAD)), STATES_TO_LOAD) + raise ValueError('Possible recursive module tree detected, aborting.') + cr.execute("SELECT name from ir_module_module WHERE state IN %s" ,(tuple(STATES_TO_LOAD),)) module_list = [name for (name,) in cr.fetchall() if name not in graph] if not module_list: @@ -842,9 +831,19 @@ def load_modules(db, force_demo=False, status=None, update_module=False): has_updates = has_updates or r if has_updates: - cr.execute("""select model,name from ir_model where id not in (select model_id from ir_model_access)""") + cr.execute("""select model,name from ir_model where id NOT IN (select distinct model_id from ir_model_access)""") for (model, name) in cr.fetchall(): - logger.notifyChannel('init', netsvc.LOG_WARNING, 'object %s (%s) has no access rules!' % (model, name)) + model_obj = pool.get(model) + if not isinstance(model_obj, osv.osv.osv_memory): + logger.notifyChannel('init', netsvc.LOG_WARNING, 'object %s (%s) has no access rules!' % (model, name)) + + # Temporary warning while we remove access rights on osv_memory objects, as they have + # been replaced by owner-only access rights + cr.execute("""select distinct mod.model, mod.name from ir_model_access acc, ir_model mod where acc.model_id = mod.id""") + for (model, name) in cr.fetchall(): + model_obj = pool.get(model) + if isinstance(model_obj, osv.osv.osv_memory): + logger.notifyChannel('init', netsvc.LOG_WARNING, 'In-memory object %s (%s) should not have explicit access rules!' % (model, name)) cr.execute("SELECT model from ir_model") for (model,) in cr.fetchall(): @@ -879,11 +878,11 @@ def load_modules(db, force_demo=False, status=None, update_module=False): cr.execute('''delete from ir_ui_menu where - (id not in (select parent_id from ir_ui_menu where parent_id is not null)) + (id not IN (select parent_id from ir_ui_menu where parent_id is not null)) and - (id not in (select res_id from ir_values where model='ir.ui.menu')) + (id not IN (select res_id from ir_values where model='ir.ui.menu')) and - (id not in (select res_id from ir_model_data where model='ir.ui.menu'))''') + (id not IN (select res_id from ir_model_data where model='ir.ui.menu'))''') cr.commit() if not cr.rowcount: break diff --git a/bin/addons/base/__terp__.py b/bin/addons/base/__openerp__.py similarity index 81% rename from bin/addons/base/__terp__.py rename to bin/addons/base/__openerp__.py index d0dc9b68dd3..82b092d973c 100644 --- a/bin/addons/base/__terp__.py +++ b/bin/addons/base/__openerp__.py @@ -3,6 +3,7 @@ # # OpenERP, Open Source Management Solution # Copyright (C) 2004-2009 Tiny SPRL (). +# Copyright (C) 2010 OpenERP s.a. (). # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as @@ -25,7 +26,8 @@ 'version': '1.2', 'category': 'Generic Modules/Base', 'description': """The kernel of OpenERP, needed for all installation.""", - 'author': 'Tiny', + 'author': 'OpenERP s.a.', + 'maintainer': 'OpenERP s.a.', 'website': 'http://www.openerp.com', 'depends': [], 'init_xml': [ @@ -34,7 +36,6 @@ 'base_menu.xml', 'res/res_security.xml', 'res/res_config.xml', - 'maintenance/maintenance_security.xml', 'data/res.country.state.csv' ], 'update_xml': [ @@ -49,14 +50,15 @@ 'module/module_report.xml', 'res/res_request_view.xml', 'res/res_lang_view.xml', - 'res/res_company_view.xml', + 'res/res_log_view.xml', 'res/partner/partner_report.xml', 'res/partner/partner_view.xml', 'res/partner/partner_wizard.xml', 'res/bank_view.xml', 'res/country_view.xml', - 'res/res_currency_view.xml', + 'res/res_currency_view.xml', 'res/partner/crm_view.xml', + 'res/partner/wizard/partner_sms_send_view.xml', 'res/partner/partner_data.xml', 'res/ir_property_view.xml', 'security/base_security.xml', @@ -64,7 +66,17 @@ 'security/ir.model.access.csv' ], - 'demo_xml': ['base_demo.xml', 'res/partner/partner_demo.xml', 'res/partner/crm_demo.xml','test/base_test.xml'], + 'demo_xml': [ + 'base_demo.xml', + 'res/partner/partner_demo.xml', + 'res/partner/crm_demo.xml', + ], + 'test': [ + 'test/base_test.xml', + #'test/base_test.yml' + 'test/test_context.xml', + 'bug_lp541545.xml', + ], 'installable': True, 'active': True, 'certificate': '0076807797149', diff --git a/bin/addons/base/base_data.xml b/bin/addons/base/base_data.xml index ca211b4a6d5..3bc6e4d6322 100644 --- a/bin/addons/base/base_data.xml +++ b/bin/addons/base/base_data.xml @@ -1293,7 +1293,18 @@ - + + + 0.631 + + 2001-01-01 + + + 0.632 + + 2002-01-01 + + AUD AUD diff --git a/bin/addons/base/base_menu.xml b/bin/addons/base/base_menu.xml index 465856674f7..9553cd17f19 100644 --- a/bin/addons/base/base_menu.xml +++ b/bin/addons/base/base_menu.xml @@ -1,17 +1,16 @@ - + - - + @@ -23,5 +22,6 @@ groups="base.group_extended"/> + diff --git a/bin/addons/base/base_update.xml b/bin/addons/base/base_update.xml index 185be1eabdb..bf3711147f3 100644 --- a/bin/addons/base/base_update.xml +++ b/bin/addons/base/base_update.xml @@ -6,7 +6,7 @@ Languages ====================== --> - + @@ -248,9 +245,9 @@ - + - + ir.filters.tree ir.filters @@ -265,7 +262,7 @@ - + @@ -275,7 +272,7 @@ form - + form
- - - - - - - + + + + + + + + + + + + + + + @@ -628,9 +633,10 @@ + - + @@ -649,35 +655,37 @@ form
- + - - - - + + + + + + + + + + - - + + + + + - + @@ -816,6 +824,7 @@ + @@ -951,6 +960,39 @@ + + + ir.model.data.form + ir.model.data + form + +
+ + + + + + + + + +
+ + + ir.model.data.list + ir.model.data + tree + + + + + + + + + + + Objects ir.model @@ -958,7 +1000,7 @@ {'manual':True} - + @@ -970,6 +1012,15 @@ + + Module data + ir.model.data + form + + + + @@ -1204,11 +1255,6 @@ form
- -
@@ -1302,14 +1357,14 @@ -
- @@ -1426,15 +1481,15 @@
- List of Configuration Wizards + Configuration Wizards ir.actions.todo form - + Launch Configuration Wizards diff --git a/bin/addons/base/ir/ir_actions.py b/bin/addons/base/ir/ir_actions.py index 1dbb27222be..5f43f9c1bc7 100644 --- a/bin/addons/base/ir/ir_actions.py +++ b/bin/addons/base/ir/ir_actions.py @@ -20,6 +20,7 @@ ############################################################################## from osv import fields,osv +from tools.safe_eval import safe_eval as eval import tools import time from tools.config import config @@ -225,13 +226,19 @@ class act_window(osv.osv): 'name': fields.char('Action Name', size=64, translate=True), 'type': fields.char('Action Type', size=32, required=True), 'view_id': fields.many2one('ir.ui.view', 'View Ref.', ondelete='cascade'), - 'domain': fields.char('Domain Value', size=250), - 'context': fields.char('Context Value', size=250), - 'res_model': fields.char('Object', size=64), - 'src_model': fields.char('Source Object', size=64), + 'domain': fields.char('Domain Value', size=250, + help="Optional domain filtering of the destination data, as a Python expression"), + 'context': fields.char('Context Value', size=250, required=True, + help="Context dictionary as Python expression, empty by default (Default: {})"), + 'res_model': fields.char('Object', size=64, required=True, + help="Model name of the object to open in the view window"), + 'src_model': fields.char('Source Object', size=64, + help="Optional model name of the objects on which this action should be visible"), 'target': fields.selection([('current','Current Window'),('new','New Window')], 'Target Window'), - 'view_type': fields.selection((('tree','Tree'),('form','Form')),string='View Type'), - 'view_mode': fields.char('View Mode', size=250), + 'view_type': fields.selection((('tree','Tree'),('form','Form')), string='View Type', required=True, + help="View type: set to 'tree' for a hierarchical tree view, or 'form' for other views"), + 'view_mode': fields.char('View Mode', size=250, required=True, + help="Comma-separated list of allowed view modes, such as 'form', 'tree', 'calendar', etc. (Default: tree,form)"), 'usage': fields.char('Action Usage', size=32), 'view_ids': fields.one2many('ir.actions.act_window.view', 'act_window_id', 'Views'), 'views': fields.function(_views_get_fnc, method=True, type='binary', string='Views'), @@ -245,7 +252,9 @@ class act_window(osv.osv): 'auto_search':fields.boolean('Auto Search'), 'default_user_ids': fields.many2many('res.users', 'ir_act_window_user_rel', 'act_id', 'uid', 'Users'), 'search_view' : fields.function(_search_view, type='text', method=True, string='Search View'), - 'menus': fields.char('Menus', size=4096) + 'menus': fields.char('Menus', size=4096), + 'help': fields.text('Action description', + help='Optional help text for the users with a description of the target view, such as its usage and purpose.') } _defaults = { 'type': lambda *a: 'ir.actions.act_window', @@ -369,7 +378,7 @@ class actions_server(osv.osv): def _select_signals(self, cr, uid, context={}): cr.execute("SELECT distinct w.osv, t.signal FROM wkf w, wkf_activity a, wkf_transition t \ WHERE w.id = a.wkf_id AND t.act_from = a.id OR t.act_to = a.id AND t.signal!='' \ - AND t.signal not in (null, NULL)") + AND t.signal NOT IN (null, NULL)") result = cr.fetchall() or [] res = [] for rs in result: @@ -445,7 +454,7 @@ class actions_server(osv.osv): 'type': lambda *a: 'ir.actions.server', 'sequence': lambda *a: 5, 'code': lambda *a: """# You can use the following variables -# - object +# - object or obj # - time # - cr # - uid @@ -522,7 +531,6 @@ class actions_server(osv.osv): def run(self, cr, uid, ids, context={}): logger = netsvc.Logger() - for action in self.browse(cr, uid, ids, context): obj_pool = self.pool.get(action.model_id.model) obj = obj_pool.browse(cr, uid, context['active_id'], context=context) @@ -544,19 +552,27 @@ class actions_server(osv.osv): return self.pool.get(action.action_id.type)\ .read(cr, uid, action.action_id.id, context=context) - if action.state == 'code': - localdict = { - 'self': self.pool.get(action.model_id.model), - 'context': context, - 'time': time, - 'ids': ids, - 'cr': cr, - 'uid': uid, - 'object':obj - } - exec action.code in localdict - if 'action' in localdict: - return localdict['action'] + if action.state=='code': + if config['server_actions_allow_code']: + localdict = { + 'self': self.pool.get(action.model_id.model), + 'context': context, + 'time': time, + 'ids': ids, + 'cr': cr, + 'uid': uid, + 'object':obj, + 'obj': obj, + } + eval(action.code, localdict, mode="exec") + if 'action' in localdict: + return localdict['action'] + else: + netsvc.Logger().notifyChannel( + self._name, netsvc.LOG_ERROR, + "%s is a `code` server action, but " + "it isn't allowed in this configuration.\n\n" + "See server options to enable it"%action) if action.state == 'email': user = config['email_from'] @@ -570,7 +586,7 @@ class actions_server(osv.osv): logger.notifyChannel('email', netsvc.LOG_INFO, 'Partner Email address not Specified!') continue if not user: - raise osv.except_osv(_('Error'), _("Please specify server option --smtp-from !")) + raise osv.except_osv(_('Error'), _("Please specify server option --email-from !")) subject = self.merge_message(cr, uid, action.subject, action, context) body = self.merge_message(cr, uid, action.message, action, context) @@ -673,7 +689,6 @@ class actions_server(osv.osv): res_id = False obj_pool = self.pool.get(action.srcmodel_id.model) res_id = obj_pool.create(cr, uid, res) - cr.commit() if action.record_id: self.pool.get(action.model_id.model).write(cr, uid, [context.get('active_id')], {action.record_id.name:res_id}) diff --git a/bin/addons/base/ir/ir_attachment.py b/bin/addons/base/ir/ir_attachment.py index 89d4bbd49dd..abba9222d25 100644 --- a/bin/addons/base/ir/ir_attachment.py +++ b/bin/addons/base/ir/ir_attachment.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- ############################################################################## -# +# # OpenERP, Open Source Management Solution # Copyright (C) 2004-2009 Tiny SPRL (). # @@ -15,7 +15,7 @@ # 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 . +# along with this program. If not, see . # ############################################################################## @@ -25,20 +25,20 @@ import tools class ir_attachment(osv.osv): def check(self, cr, uid, ids, mode, context=None): - if not ids: + if not ids: return ima = self.pool.get('ir.model.access') if isinstance(ids, (int, long)): ids = [ids] - cr.execute('select distinct res_model from ir_attachment where id = ANY (%s)', (ids,)) + cr.execute('select distinct res_model from ir_attachment where id IN %s', (tuple(ids),)) for obj in cr.fetchall(): if obj[0]: ima.check(cr, uid, obj[0], mode, context=context) def search(self, cr, uid, args, offset=0, limit=None, order=None, context=None, count=False): - ids = super(ir_attachment, self).search(cr, uid, args, offset=offset, - limit=limit, order=order, + ids = super(ir_attachment, self).search(cr, uid, args, offset=offset, + limit=limit, order=order, context=context, count=False) if not ids: if count: @@ -66,7 +66,7 @@ class ir_attachment(osv.osv): def write(self, cr, uid, ids, vals, context=None): self.check(cr, uid, ids, 'write', context=context) return super(ir_attachment, self).write(cr, uid, ids, vals, context) - + def copy(self, cr, uid, id, default=None, context=None): self.check(cr, uid, [id], 'write', context=context) return super(ir_attachment, self).copy(cr, uid, id, default, context) @@ -86,35 +86,51 @@ class ir_attachment(osv.osv): res_id = dataobj.browse(cr, uid, data_id, context).res_id return self.pool.get('ir.actions.act_window').read(cr, uid, res_id, [], context) - def _get_preview(self, cr, uid, ids, name, arg, context=None): - result = {} - if context is None: - context = {} - ctx = context.copy() - ctx['bin_size'] = False - for i in self.browse(cr, uid, ids, context=ctx): - result[i.id] = False - for format in ('png','jpg','jpeg','gif','bmp'): - if (i.datas_fname and i.datas_fname.lower() or '').endswith(format): - result[i.id]= i.datas - break - return result + def _name_get_resname(self, cr, uid, ids, object,method, context): + data = {} + for attachment in self.browse(cr, uid, ids, context=context): + model_object = attachment.res_model + res_id = attachment.res_id + if model_object and res_id: + model_pool = self.pool.get(model_object) + res = model_pool.name_get(cr,uid,[res_id],context) + data[attachment.id] = (res and res[0][1]) or False + else: + data[attachment.id] = False + return data _name = 'ir.attachment' _columns = { - 'name': fields.char('Attachment Name',size=64, required=True), + 'name': fields.char('Attachment Name',size=256, required=True), 'datas': fields.binary('Data'), - 'preview': fields.function(_get_preview, type='binary', string='Image Preview', method=True), - 'datas_fname': fields.char('Filename',size=64), + 'datas_fname': fields.char('Filename',size=256), 'description': fields.text('Description'), - # Not required due to the document module ! - 'res_model': fields.char('Resource Object',size=64, readonly=True), - 'res_id': fields.integer('Resource ID', readonly=True), - 'link': fields.char('Link', size=256), + 'res_name': fields.function(_name_get_resname, type='char', + string='Resource Name', method=True), + 'res_model': fields.char('Resource Object',size=64, readonly=True, + help="The database object this attachment will be attached to"), + 'res_id': fields.integer('Resource ID', readonly=True, + help="The record id this is attached to"), + 'url': fields.char('Url', size=512, oldname="link"), + 'type': fields.selection( + [ ('url','URL'), ('binary','Binary'), ], + 'Type', help="Binary File or external URL", required=True), 'create_date': fields.datetime('Date Created', readonly=True), 'create_uid': fields.many2one('res.users', 'Creator', readonly=True), } + + _defaults = { + 'type': 'binary', + } + + def _auto_init(self, cr, context=None): + super(ir_attachment, self)._auto_init(cr, context) + cr.execute('SELECT indexname FROM pg_indexes WHERE indexname = %s', ('ir_attachment_res_idx',)) + if not cr.fetchone(): + cr.execute('CREATE INDEX ir_attachment_res_idx ON ir_attachment (res_model, res_id)') + cr.commit() + ir_attachment() diff --git a/bin/addons/base/ir/ir_cron.py b/bin/addons/base/ir/ir_cron.py index dcf76945904..905b2dc1778 100644 --- a/bin/addons/base/ir/ir_cron.py +++ b/bin/addons/base/ir/ir_cron.py @@ -25,6 +25,7 @@ from datetime import datetime from dateutil.relativedelta import relativedelta import netsvc import tools +from tools.safe_eval import safe_eval as eval import pooler from osv import fields, osv @@ -146,16 +147,19 @@ class ir_cron(osv.osv, netsvc.Agent): def create(self, cr, uid, vals, context=None): res = super(ir_cron, self).create(cr, uid, vals, context=context) cr.commit() + self.cancel(cr.dbname) self._poolJobs(cr.dbname) return res def write(self, cr, user, ids, vals, context=None): res = super(ir_cron, self).write(cr, user, ids, vals, context=context) cr.commit() + self.cancel(cr.dbname) self._poolJobs(cr.dbname) return res def unlink(self, cr, uid, ids, context=None): res = super(ir_cron, self).unlink(cr, uid, ids, context=context) cr.commit() + self.cancel(cr.dbname) self._poolJobs(cr.dbname) return res ir_cron() diff --git a/bin/addons/base/ir/ir_filters.py b/bin/addons/base/ir/ir_filters.py index 17610230d65..9c88e448417 100644 --- a/bin/addons/base/ir/ir_filters.py +++ b/bin/addons/base/ir/ir_filters.py @@ -27,17 +27,22 @@ class ir_filters(osv.osv): ''' _name = 'ir.filters' _description = 'Filters' - + def _list_all_models(self, cr, uid, context=None): cr.execute("SELECT model, name from ir_model") return cr.fetchall() + def get_filters(self, cr, uid, model): + act_ids = self.search(cr,uid,[('model_id','=',model),('user_id','=',uid)]) + my_acts = self.read(cr, uid, act_ids, ['name', 'domain','context']) + return my_acts + _columns = { - 'name': fields.char('Action Name', size=64, translate=True, required=True), - 'user_id':fields.many2one('res.users', 'User', help='False means for every user'), - 'domain': fields.char('Domain Value', size=250, required=True), - 'context': fields.char('Context Value', size=250, required=True), - 'model_id': fields.selection(_list_all_models, 'Model', required=True), + 'name': fields.char('Action Name', size=64, translate=True, required=True), + 'user_id':fields.many2one('res.users', 'User', help='False means for every user'), + 'domain': fields.char('Domain Value', size=250, required=True), + 'context': fields.char('Context Value', size=250, required=True), + 'model_id': fields.selection(_list_all_models, 'Model', required=True), } def get_filters(self, cr, uid, model): diff --git a/bin/addons/base/ir/ir_model.py b/bin/addons/base/ir/ir_model.py index f6412e85b75..20574545fbc 100644 --- a/bin/addons/base/ir/ir_model.py +++ b/bin/addons/base/ir/ir_model.py @@ -44,6 +44,25 @@ class ir_model(osv.osv): _name = 'ir.model' _description = "Objects" _rec_name = 'name' + + def _is_osv_memory(self, cr, uid, ids, field_name, arg, context=None): + models = self.browse(cr, uid, ids, context=context) + res = dict.fromkeys(ids) + for model in models: + res[model.id] = isinstance(self.pool.get(model.model), osv.osv_memory) + return res + + def _search_osv_memory(self, cr, uid, model, name, domain, context=None): + if not domain: + return [] + field, operator, value = domain[0] + if operator not in ['=', '!=']: + raise osv.except_osv('Invalid search criterions','The osv_memory field can only be compared with = and != operator.') + value = bool(value) if operator == '=' else not bool(value) + all_model_ids = self.search(cr, uid, [], context=context) + is_osv_mem = self._is_osv_memory(cr, uid, all_model_ids, 'osv_memory', arg=None, context=context) + return [('id', 'in', [id for id in is_osv_mem if bool(is_osv_mem[id]) == value])] + _columns = { 'name': fields.char('Object Name', size=64, translate=True, required=True), 'model': fields.char('Object', size=64, required=True, select=1), @@ -51,6 +70,9 @@ class ir_model(osv.osv): 'field_id': fields.one2many('ir.model.fields', 'model_id', 'Fields', required=True), 'state': fields.selection([('manual','Custom Object'),('base','Base Object')],'Manually Created',readonly=True), 'access_ids': fields.one2many('ir.model.access', 'model_id', 'Access'), + 'osv_memory': fields.function(_is_osv_memory, method=True, string='In-memory model', type='boolean', + fnct_search=_search_osv_memory, + help="Indicates whether this object model lives in memory only, i.e. is not persisted (osv.osv_memory)") } _defaults = { 'model': lambda *a: 'x_', @@ -68,9 +90,20 @@ class ir_model(osv.osv): _constraints = [ (_check_model_name, 'The Object name must start with x_ and not contain any special character !', ['model']), ] + + # overridden to allow searching both on model name (model field) + # and model description (name field) + def name_search(self, cr, uid, name='', args=None, operator='ilike', context=None, limit=None): + if args is None: + args = [] + domain = args + ['|', ('model', operator, name), ('name', operator, name)] + return super(ir_model, self).name_search(cr, uid, None, domain, + operator=operator, limit=limit, context=context) + + def unlink(self, cr, user, ids, context=None): for model in self.browse(cr, user, ids, context): - if model.state <> 'manual': + if model.state != 'manual': raise except_orm(_('Error'), _("You can not remove the model '%s' !") %(model.name,)) res = super(ir_model, self).unlink(cr, user, ids, context) pooler.restart_pool(cr.dbname) @@ -82,6 +115,8 @@ class ir_model(osv.osv): return super(ir_model,self).write(cr, user, ids, vals, context) def create(self, cr, user, vals, context=None): + if context is None: + context = {} if context and context.get('manual',False): vals['state']='manual' res = super(ir_model,self).create(cr, user, vals, context) @@ -209,7 +244,7 @@ class ir_model_fields(osv.osv): _description = "Fields" _columns = { 'name': fields.char('Name', required=True, size=64, select=1), - 'model': fields.char('Object Name', size=64, required=True), + 'model': fields.char('Object Name', size=64, required=True, select=1), 'relation': fields.char('Object Relation', size=64), 'relation_field': fields.char('Relation Field', size=64), 'model_id': fields.many2one('ir.model', 'Object ID', required=True, select=True, ondelete='cascade'), @@ -221,7 +256,7 @@ class ir_model_fields(osv.osv): 'select_level': fields.selection([('0','Not Searchable'),('1','Always Searchable'),('2','Advanced Search')],'Searchable', required=True), 'translate': fields.boolean('Translate'), 'size': fields.integer('Size'), - 'state': fields.selection([('manual','Custom Field'),('base','Base Field')],'Manually Created', required=True, readonly=True), + 'state': fields.selection([('manual','Custom Field'),('base','Base Field')],'Manually Created', required=True, readonly=True, select=1), 'on_delete': fields.selection([('cascade','Cascade'),('set null','Set NULL')], 'On delete', help='On delete property for many2one fields'), 'domain': fields.char('Domain', size=256), 'groups': fields.many2many('res.groups', 'ir_model_fields_group_rel', 'field_id', 'group_id', 'Groups'), @@ -260,6 +295,8 @@ class ir_model_fields(osv.osv): if 'model_id' in vals: model_data = self.pool.get('ir.model').browse(cr, user, vals['model_id']) vals['model'] = model_data.model + if context is None: + context = {} if context and context.get('manual',False): vals['state'] = 'manual' res = super(ir_model_fields,self).create(cr, user, vals, context) @@ -267,7 +304,7 @@ class ir_model_fields(osv.osv): if not vals['name'].startswith('x_'): raise except_orm(_('Error'), _("Custom fields must have a name that starts with 'x_' !")) - if 'relation' in vals and not self.pool.get('ir.model').search(cr, user, [('model','=',vals['relation'])]): + if vals.get('relation',False) and not self.pool.get('ir.model').search(cr, user, [('model','=',vals['relation'])]): raise except_orm(_('Error'), _("Model %s Does not Exist !" % vals['relation'])) if self.pool.get(vals['model']): @@ -284,9 +321,9 @@ ir_model_fields() class ir_model_access(osv.osv): _name = 'ir.model.access' _columns = { - 'name': fields.char('Name', size=64, required=True), - 'model_id': fields.many2one('ir.model', 'Object', required=True), - 'group_id': fields.many2one('res.groups', 'Group'), + 'name': fields.char('Name', size=64, required=True, select=True), + 'model_id': fields.many2one('ir.model', 'Object', required=True, domain=[('osv_memory','=', False)], select=True), + 'group_id': fields.many2one('res.groups', 'Group', ondelete='cascade', select=True), 'perm_read': fields.boolean('Read Access'), 'perm_write': fields.boolean('Write Access'), 'perm_create': fields.boolean('Create Access'), @@ -299,7 +336,7 @@ class ir_model_access(osv.osv): if not grouparr: return False - cr.execute("select 1 from res_groups_users_rel where uid=%s and gid in(select res_id from ir_model_data where module=%s and name=%s)", (uid, grouparr[0], grouparr[1],)) + cr.execute("select 1 from res_groups_users_rel where uid=%s and gid IN (select res_id from ir_model_data where module=%s and name=%s)", (uid, grouparr[0], grouparr[1],)) return bool(cr.fetchone()) def check_group(self, cr, uid, model, mode, group_ids): @@ -425,10 +462,10 @@ class ir_model_data(osv.osv): _name = 'ir.model.data' __logger = logging.getLogger('addons.base.'+_name) _columns = { - 'name': fields.char('XML Identifier', required=True, size=128), - 'model': fields.char('Object', required=True, size=64), - 'module': fields.char('Module', required=True, size=64), - 'res_id': fields.integer('Resource ID'), + 'name': fields.char('XML Identifier', required=True, size=128, select=1), + 'model': fields.char('Object', required=True, size=64, select=1), + 'module': fields.char('Module', required=True, size=64, select=1), + 'res_id': fields.integer('Resource ID', select=1), 'noupdate': fields.boolean('Non Updatable'), 'date_update': fields.datetime('Update Date'), 'date_init': fields.datetime('Init Date') @@ -453,7 +490,7 @@ class ir_model_data(osv.osv): def _get_id(self, cr, uid, module, xml_id): ids = self.search(cr, uid, [('module','=',module),('name','=', xml_id)]) if not ids: - raise Exception('No references to %s.%s' % (module, xml_id)) + raise ValueError('No references to %s.%s' % (module, xml_id)) # the sql constraints ensure us we have only one result return ids[0] @@ -590,13 +627,13 @@ class ir_model_data(osv.osv): return True modules = list(modules) module_in = ",".join(["%s"] * len(modules)) - cr.execute('select id,name,model,res_id,module from ir_model_data where module in (' + module_in + ') and noupdate=%s', modules + [False]) + cr.execute('select id,name,model,res_id,module from ir_model_data where module IN (' + module_in + ') and noupdate=%s', modules + [False]) wkf_todo = [] for (id, name, model, res_id,module) in cr.fetchall(): if (module,name) not in self.loads: self.unlink_mark[(model,res_id)] = id if model=='workflow.activity': - cr.execute('select res_type,res_id from wkf_instance where id in (select inst_id from wkf_workitem where act_id=%s)', (res_id,)) + cr.execute('select res_type,res_id from wkf_instance where id IN (select inst_id from wkf_workitem where act_id=%s)', (res_id,)) wkf_todo.extend(cr.fetchall()) cr.execute("update wkf_transition set condition='True', role_id=NULL, signal=NULL,act_to=act_from,act_from=%s where act_to=%s", (res_id,res_id)) cr.execute("delete from wkf_transition where act_to=%s", (res_id,)) diff --git a/bin/addons/base/ir/ir_report_custom.py b/bin/addons/base/ir/ir_report_custom.py index 3b20d193d8b..036469fb417 100644 --- a/bin/addons/base/ir/ir_report_custom.py +++ b/bin/addons/base/ir/ir_report_custom.py @@ -24,6 +24,7 @@ from osv.orm import browse_null import ir import report.custom from tools.translate import _ +from tools.safe_eval import safe_eval as eval import netsvc class report_custom(osv.osv): diff --git a/bin/addons/base/ir/ir_rule.py b/bin/addons/base/ir/ir_rule.py index de465fa8025..23981458606 100644 --- a/bin/addons/base/ir/ir_rule.py +++ b/bin/addons/base/ir/ir_rule.py @@ -21,10 +21,14 @@ from osv import fields,osv import time +from operator import itemgetter +from functools import partial import tools +from tools.safe_eval import safe_eval as eval class ir_rule(osv.osv): _name = 'ir.rule' + _MODES = ['read', 'write', 'create', 'unlink'] def _domain_force_get(self, cr, uid, ids, field_name, arg, context={}): res = {} @@ -44,13 +48,7 @@ class ir_rule(osv.osv): return res def _check_model_obj(self, cr, uid, ids, context={}): - model_obj = self.pool.get('ir.model') - for rule in self.browse(cr, uid, ids, context): - model = model_obj.browse(cr, uid, rule.model_id.id, context).model - obj = self.pool.get(model) - if isinstance(obj, osv.osv_memory): - return False - return True + return not any(isinstance(self.pool.get(rule.model_id.model), osv.osv_memory) for rule in self.browse(cr, uid, ids, context)) _columns = { 'name': fields.char('Name', size=128, select=1), @@ -89,6 +87,8 @@ class ir_rule(osv.osv): @tools.cache() def _compute_domain(self, cr, uid, model_name, mode="read"): + if mode not in self._MODES: + raise ValueError('Invalid mode: %r' % (mode,)) group_rule = {} global_rules = [] @@ -114,6 +114,24 @@ class ir_rule(osv.osv): dom += self.domain_create(cr, uid, value) return dom + def clear_cache(self, cr, uid): + cr.execute("""SELECT DISTINCT m.model + FROM ir_rule r + JOIN ir_model m + ON r.model_id = m.id + WHERE r.global + OR EXISTS (SELECT 1 + FROM rule_group_rel g_rel + JOIN res_groups_users_rel u_rel + ON g_rel.group_id = u_rel.gid + WHERE g_rel.rule_group_id = r.id + AND u_rel.uid = %s) + """, (uid,)) + models = map(itemgetter(0), cr.fetchall()) + clear = partial(self._compute_domain.clear_cache, cr.dbname, uid) + [clear(model, mode) for model in models for mode in self._MODES] + + def domain_get(self, cr, uid, model_name, mode='read', context={}): dom = self._compute_domain(cr, uid, model_name, mode=mode) if dom: diff --git a/bin/addons/base/ir/ir_translation.py b/bin/addons/base/ir/ir_translation.py index 73536ac339f..571e98b818d 100644 --- a/bin/addons/base/ir/ir_translation.py +++ b/bin/addons/base/ir/ir_translation.py @@ -88,8 +88,8 @@ class ir_translation(osv.osv): 'where lang=%s ' \ 'and type=%s ' \ 'and name=%s ' \ - 'and res_id in ('+','.join(map(str, ids))+')', - (lang,tt,name)) + 'and res_id IN %s', + (lang,tt,name,tuple(ids))) for res_id, value in cr.fetchall(): translations[res_id] = value return translations @@ -107,9 +107,8 @@ class ir_translation(osv.osv): 'where lang=%s ' \ 'and type=%s ' \ 'and name=%s ' \ - 'and res_id in ('+','.join(map(str, ids))+')', - (lang,tt,name)) - cr.commit() + 'and res_id IN %s', + (lang,tt,name,tuple(ids),)) for id in ids: self.create(cr, uid, { 'lang':lang, @@ -150,26 +149,26 @@ class ir_translation(osv.osv): if not context: context = {} ids = super(ir_translation, self).create(cursor, user, vals, context=context) - for trans_obj in self.read(cursor, user, [ids], ['name','type','res_id'], context=context): - self._get_source.clear_cache(cursor.dbname, user, trans_obj['name'], trans_obj['type'], lang=context.get('lang','en_US')) - self._get_ids.clear_cache(cursor.dbname, user, trans_obj['name'], trans_obj['type'], context.get('lang','en_US'), [trans_obj['res_id']]) + for trans_obj in self.read(cursor, user, [ids], ['name','type','res_id','src','lang'], context=context): + self._get_source.clear_cache(cursor.dbname, user, trans_obj['name'], trans_obj['type'], trans_obj['lang'], source=trans_obj['src']) + self._get_ids.clear_cache(cursor.dbname, user, trans_obj['name'], trans_obj['type'], trans_obj['lang'], [trans_obj['res_id']]) return ids def write(self, cursor, user, ids, vals, context=None): if not context: context = {} result = super(ir_translation, self).write(cursor, user, ids, vals, context=context) - for trans_obj in self.read(cursor, user, ids, ['name','type','res_id'], context=context): - self._get_source.clear_cache(cursor.dbname, user, trans_obj['name'], trans_obj['type'], lang=context.get('lang','en_US')) - self._get_ids.clear_cache(cursor.dbname, user, trans_obj['name'], trans_obj['type'], context.get('lang','en_US'), [trans_obj['res_id']]) + for trans_obj in self.read(cursor, user, ids, ['name','type','res_id','src','lang'], context=context): + self._get_source.clear_cache(cursor.dbname, user, trans_obj['name'], trans_obj['type'], trans_obj['lang'], source=trans_obj['src']) + self._get_ids.clear_cache(cursor.dbname, user, trans_obj['name'], trans_obj['type'], trans_obj['lang'], [trans_obj['res_id']]) return result def unlink(self, cursor, user, ids, context=None): if not context: context = {} - for trans_obj in self.read(cursor, user, ids, ['name','type','res_id'], context=context): - self._get_source.clear_cache(cursor.dbname, user, trans_obj['name'], trans_obj['type'], lang=context.get('lang','en_US')) - self._get_ids.clear_cache(cursor.dbname, user, trans_obj['name'], trans_obj['type'], context.get('lang','en_US'), [trans_obj['res_id']]) + for trans_obj in self.read(cursor, user, ids, ['name','type','res_id','src','lang'], context=context): + self._get_source.clear_cache(cursor.dbname, user, trans_obj['name'], trans_obj['type'], trans_obj['lang'], source=trans_obj['src']) + self._get_ids.clear_cache(cursor.dbname, user, trans_obj['name'], trans_obj['type'], trans_obj['lang'], [trans_obj['res_id']]) result = super(ir_translation, self).unlink(cursor, user, ids, context=context) return result diff --git a/bin/addons/base/ir/ir_ui_menu.py b/bin/addons/base/ir/ir_ui_menu.py index 6948c0a2100..c02f0d4309f 100644 --- a/bin/addons/base/ir/ir_ui_menu.py +++ b/bin/addons/base/ir/ir_ui_menu.py @@ -31,6 +31,10 @@ def one_in(setA, setB): return True return False +def cond(C, X, Y): + if C: return X + return Y + class many2many_unique(fields.many2many): def set(self, cr, obj, id, name, values, user=None, context=None): if not values: @@ -62,7 +66,7 @@ class ir_ui_menu(osv.osv): def clear_cache(self): # radical but this doesn't frequently happen self._cache = {} - + def create_shortcut(self, cr, uid, values, context={}): dataobj = self.pool.get('ir.model.data') menu_id = dataobj._get_id(cr, uid, 'base', 'menu_administration_shortcut', context) @@ -76,24 +80,22 @@ class ir_ui_menu(osv.osv): menu_id = self.pool.get('ir.ui.menu').create(cr, uid, menu_data) sc_data= {'name':values['name'], 'sequence': 1,'res_id': menu_id } sc_menu_id = self.pool.get('ir.ui.view_sc').create(cr, uid, sc_data, context) - user_groups = set(self.pool.get('res.users').read(cr, 1, uid, ['groups_id'])['groups_id']) key = (cr.dbname, shortcut_menu_id, tuple(user_groups)) self._cache[key] = True return True - - def search(self, cr, uid, args, offset=0, limit=2000, order=None, - context=None, count=False): - if context is None: - context = {} - ids = osv.orm.orm.search(self, cr, uid, args, offset, limit, order, context=context, count=(count and uid==1)) + + def search(self, cr, uid, args, offset=0, limit=None, order=None, + context=None, count=False): + + ids = super(ir_ui_menu, self).search(cr, uid, args, offset=0, + limit=None, order=order, context=context, count=False) + if not ids: if count: return 0 return [] - - if count and ids: - return ids + modelaccess = self.pool.get('ir.model.access') user_groups = set(self.pool.get('res.users').read(cr, 1, uid, ['groups_id'])['groups_id']) @@ -113,9 +115,9 @@ class ir_ui_menu(osv.osv): restrict_to_groups = [g.id for g in menu.groups_id] if not user_groups.intersection(restrict_to_groups): continue - result.append(menu.id) - self._cache[key] = True - continue + #result.append(menu.id) + #self._cache[key] = True + #continue if menu.action: # we check if the user has access to the action of the menu @@ -141,6 +143,11 @@ class ir_ui_menu(osv.osv): result.append(menu.id) self._cache[key] = True + if offset: + result = result[long(offset):] + if limit: + result = result[:long(limit)] + if count: return len(result) return result diff --git a/bin/addons/base/ir/ir_ui_view.py b/bin/addons/base/ir/ir_ui_view.py index b04343e821f..d990e94a84b 100644 --- a/bin/addons/base/ir/ir_ui_view.py +++ b/bin/addons/base/ir/ir_ui_view.py @@ -22,6 +22,7 @@ from osv import fields,osv from lxml import etree from tools import graph +from tools.safe_eval import safe_eval as eval import tools import netsvc import os @@ -66,10 +67,12 @@ class view(osv.osv): 'arch': fields.text('View Architecture', required=True), 'inherit_id': fields.many2one('ir.ui.view', 'Inherited View', ondelete='cascade'), 'field_parent': fields.char('Child Field',size=64), + 'xml_id': fields.function(osv.osv.get_xml_id, type='char', size=128, string="XML ID", + method=True), } _defaults = { - 'arch': lambda *a: '\n\n\t\n', - 'priority': lambda *a: 16 + 'arch': '\n\n\t\n', + 'priority': 16 } _order = "priority" _constraints = [ @@ -200,6 +203,10 @@ class view_sc(osv.osv): 'resource': lambda *a: 'ir.ui.menu', 'user_id': lambda obj, cr, uid, context: uid, } + _sql_constraints = [ + ('shortcut_unique', 'unique(res_id, user_id)', 'Shortcut for this menu already exists!'), + ] + view_sc() # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/bin/addons/base/ir/ir_values.py b/bin/addons/base/ir/ir_values.py index 4c26596faa5..d7baa6c0f37 100644 --- a/bin/addons/base/ir/ir_values.py +++ b/bin/addons/base/ir/ir_values.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- ############################################################################## -# +# # OpenERP, Open Source Management Solution # Copyright (C) 2004-2009 Tiny SPRL (). # @@ -15,7 +15,7 @@ # 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 . +# along with this program. If not, see . # ############################################################################## @@ -75,8 +75,7 @@ class ir_values(osv.osv): method=True, type='text', string='Value'), 'object': fields.boolean('Is Object'), 'key': fields.selection([('action','Action'),('default','Default')], 'Type', size=128), - 'key2': fields.char('Event Type', size=256, - help="The kind of action or button in the client side that will trigger the action."), + 'key2' : fields.char('Event Type',help="The kind of action or button in the client side that will trigger the action.", size=128), 'meta': fields.text('Meta Datas'), 'meta_unpickle': fields.function(_value_unpickle, fnct_inv=_value_pickle, method=True, type='text', string='Metadata'), @@ -95,7 +94,6 @@ class ir_values(osv.osv): cr.execute('SELECT indexname FROM pg_indexes WHERE indexname = \'ir_values_key_model_key2_index\'') if not cr.fetchone(): cr.execute('CREATE INDEX ir_values_key_model_key2_index ON ir_values (key, model, key2)') - cr.commit() def set(self, cr, uid, key, key2, name, models, value, replace=True, isobject=False, meta=False, preserve_user=False, company=False): if type(value)==type(u''): @@ -156,54 +154,33 @@ class ir_values(osv.osv): else: res_id=False - where1 = ['key=%s','model=%s'] - where2 = [key,str(m)] - where_opt = [] + where = ['key=%s','model=%s'] + params = [key, str(m)] if key2: - where1.append('key2=%s') - where2.append(key2[:200]) + where.append('key2=%s') + params.append(key2[:200]) else: - dest = where1 - if not key2_req or meta: - dest=where_opt - dest.append('key2 is null') - + if key2_req and not meta: + where.append('key2 is null') if res_id_req and (models[-1][0]==m): if res_id: - where1.append('res_id=%d' % (res_id,)) + where.append('res_id=%s') + params.append(res_id) else: - where1.append('(res_id is NULL)') + where.append('(res_id is NULL)') elif res_id: if (models[-1][0]==m): - where1.append('(res_id=%d or (res_id is null))' % (res_id,)) - where_opt.append('res_id=%d' % (res_id,)) + where.append('(res_id=%s or (res_id is null))') + params.append(res_id) else: - where1.append('res_id=%d' % (res_id,)) - -# if not without_user: - where_opt.append('user_id=%d' % (uid,)) - - result = [] - ok = True - result_ids = {} - while ok: - if not where_opt: - cr.execute('select id,name,value,object,meta, key from ir_values where ' +\ - ' and '.join(where1)+' and user_id is null', where2) - else: - cr.execute('select id,name,value,object,meta, key from ir_values where ' +\ - ' and '.join(where1+where_opt), where2) - for rec in cr.fetchall(): - if rec[0] in result_ids: - continue - if rec[2]: - result.append(rec) - result_ids[rec[0]] = True - if len(where_opt): - where_opt.pop() - else: - ok = False + where.append('res_id=%s') + params.append(res_id) + where.append('(user_id=%s or (user_id IS NULL))') + params.append(uid) + clause = ' and '.join(where) + cr.execute('select id,name,value,object,meta, key from ir_values where ' + clause, params) + result = cr.fetchall() if result: break @@ -228,7 +205,7 @@ class ir_values(osv.osv): pos+=1 try: datas = self.pool.get(model).read(cr, uid, [id], fields, context) - except except_orm: + except except_orm, e: return False datas= datas and datas[0] or None if not datas: @@ -245,19 +222,14 @@ class ir_values(osv.osv): res2 = res[:] for r in res: if type(r[2])==type({}) and 'type' in r[2]: - if r[2]['type'] in ('ir.actions.report.xml','ir.actions.act_window','ir.actions.wizard'): - if r[2].has_key('groups_id'): - groups = r[2]['groups_id'] - if len(groups) > 0: - cr.execute("SELECT count(*) FROM res_groups_users_rel WHERE gid = ANY(%s) AND uid=%s",(groups, uid)) - gr_ids = cr.fetchall() - if not gr_ids[0][0] > 0: - res2.remove(r) - if r[1]=='Menuitem' and not res2: - raise osv.except_osv('Error !','You do not have the permission to perform this operation !!!') - # else: - # #raise osv.except_osv('Error !','You have not permission to perform operation !!!') - # res2.remove(r) + groups = r[2].get('groups_id') + if groups: + cr.execute('SELECT COUNT(1) FROM res_groups_users_rel WHERE gid IN %s AND uid=%s',(tuple(groups), uid)) + cnt = cr.fetchone()[0] + if cnt: + res2.remove(r) + if r[1] == 'Menuitem' and not res2: + raise osv.except_osv('Error !','You do not have the permission to perform this operation !!!') return res2 ir_values() diff --git a/bin/addons/base/ir/workflow/print_instance.py b/bin/addons/base/ir/workflow/print_instance.py index bac8eedb476..259fe51d41a 100644 --- a/bin/addons/base/ir/workflow/print_instance.py +++ b/bin/addons/base/ir/workflow/print_instance.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- ############################################################################## -# +# # OpenERP, Open Source Management Solution # Copyright (C) 2004-2009 Tiny SPRL (). # @@ -15,7 +15,7 @@ # 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 . +# along with this program. If not, see . # ############################################################################## @@ -23,11 +23,11 @@ import time, os import netsvc import report,pooler,tools +from operator import itemgetter - -def graph_get(cr, graph, wkf_id, nested=False, workitem={}): +def graph_get(cr, graph, wkf_ids, nested=False, workitem={}): import pydot - cr.execute('select * from wkf_activity where wkf_id=%s', (wkf_id,)) + cr.execute('select * from wkf_activity where wkf_id in ('+','.join(['%s']*len(wkf_ids))+')', wkf_ids) nodes = cr.dictfetchall() activities = {} actfrom = {} @@ -56,9 +56,12 @@ def graph_get(cr, graph, wkf_id, nested=False, workitem={}): graph.add_node(pydot.Node(n['id'], **args)) actfrom[n['id']] = (n['id'],{}) actto[n['id']] = (n['id'],{}) - cr.execute('select * from wkf_transition where act_from in ('+','.join(map(lambda x: str(x['id']),nodes))+')') + node_ids = tuple(map(itemgetter('id'), nodes)) + cr.execute('select * from wkf_transition where act_from IN %s', (node_ids,)) transitions = cr.dictfetchall() for t in transitions: + if not t['act_to'] in activities: + continue args = {} args['label'] = str(t['condition']).replace(' or ', '\\nor ').replace(' and ', '\\nand ') if t['signal']: @@ -77,9 +80,9 @@ def graph_get(cr, graph, wkf_id, nested=False, workitem={}): activity_to = actto[t['act_to']][1].get(t['signal'], actto[t['act_to']][0]) graph.add_edge(pydot.Edge( str(activity_from) ,str(activity_to), fontsize='10', **args)) nodes = cr.dictfetchall() - cr.execute('select id from wkf_activity where flow_start=True and wkf_id=%s limit 1', (wkf_id,)) + cr.execute('select * from wkf_activity where flow_start=True and wkf_id in ('+','.join(['%s']*len(wkf_ids))+')', wkf_ids) start = cr.fetchone()[0] - cr.execute("select 'subflow.'||name,id from wkf_activity where flow_stop=True and wkf_id=%s", (wkf_id,)) + cr.execute("select 'subflow.'||name,id from wkf_activity where flow_stop=True and wkf_id in ("+','.join(['%s']*len(wkf_ids))+')', wkf_ids) stop = cr.fetchall() if (stop): stop = (stop[0][1], dict(stop)) @@ -90,8 +93,8 @@ def graph_get(cr, graph, wkf_id, nested=False, workitem={}): def graph_instance_get(cr, graph, inst_id, nested=False): workitems = {} - cr.execute('select * from wkf_instance where id=%s', (inst_id,)) - inst = cr.dictfetchone() + cr.execute('select wkf_id from wkf_instance where id=%s', (inst_id,)) + inst = cr.fetchall() def workitem_get(instance): cr.execute('select act_id,count(*) from wkf_workitem where inst_id=%s group by act_id', (instance,)) @@ -101,7 +104,7 @@ def graph_instance_get(cr, graph, inst_id, nested=False): for (subflow_id,) in cr.fetchall(): workitems.update(workitem_get(subflow_id)) return workitems - graph_get(cr, graph, inst['wkf_id'], nested, workitem_get(inst_id)) + graph_get(cr, graph, [x[0] for x in inst], nested, workitem_get(inst_id)) # # TODO: pas clean: concurrent !!! @@ -131,12 +134,9 @@ class report_graph_instance(object): (No workflow defined) show showpage''' else: - cr.execute('SELECT id FROM wkf_instance \ - WHERE res_id=%s AND wkf_id=%s \ - ORDER BY state LIMIT 1', - (data['id'], wkfinfo['id'])) - inst_id = cr.fetchone() - if not inst_id: + cr.execute('select i.id from wkf_instance i left join wkf w on (i.wkf_id=w.id) where res_id=%s and osv=%s',(data['id'],data['model'])) + inst_ids = cr.fetchall() + if not inst_ids: ps_string = '''%PS-Adobe-3.0 /inch {72 mul} def /Times-Roman findfont 50 scalefont setfont @@ -144,11 +144,14 @@ showpage''' (No workflow instance defined) show showpage''' else: - inst_id = inst_id[0] - graph = pydot.Dot(fontsize='16', label="""\\\n\\nWorkflow: %s\\n OSV: %s""" % (wkfinfo['name'],wkfinfo['osv']), - size='7.3, 10.1', center='1', ratio='auto', rotate='0', rankdir='TB', - ) - graph_instance_get(cr, graph, inst_id, data.get('nested', False)) + graph = pydot.Dot( + fontsize='16', + label="""\\\n\\nWorkflow: %s\\n OSV: %s""" % (wkfinfo['name'],wkfinfo['osv']), + size='7.3, 10.1', center='1', ratio='auto', rotate='0', rankdir='TB', + ) + for inst_id in inst_ids: + inst_id = inst_id[0] + graph_instance_get(cr, graph, inst_id, data.get('nested', False)) ps_string = graph.create(prog='dot', format='ps') except Exception, e: import traceback, sys diff --git a/bin/addons/base/ir/workflow/workflow_view.xml b/bin/addons/base/ir/workflow/workflow_view.xml index f6bfc0096f1..8713bb50698 100644 --- a/bin/addons/base/ir/workflow/workflow_view.xml +++ b/bin/addons/base/ir/workflow/workflow_view.xml @@ -1,7 +1,7 @@ - + + + + + + diff --git a/bin/addons/base/module/module_view.xml b/bin/addons/base/module/module_view.xml index cd57b59023c..a64ecb729ca 100644 --- a/bin/addons/base/module/module_view.xml +++ b/bin/addons/base/module/module_view.xml @@ -29,13 +29,6 @@
- - Categories of Modules - ir.module.category - tree - [('parent_id','=',False)] - - @@ -46,17 +39,23 @@ - - - + + + - + - - - + + + + + + + + + + - @@ -97,19 +96,21 @@ + + -