From 40bce2fead943e85d6318098845e545ed8cd5427 Mon Sep 17 00:00:00 2001 From: Vo Minh Thu Date: Wed, 11 May 2011 19:48:41 +0200 Subject: [PATCH] [REF] renamed modules.__init__ to modules.loading. bzr revid: vmt@openerp.com-20110511174841-wk1yyr9971pu0pwh --- openerp/modules/__init__.py | 386 +-------------------------------- openerp/modules/loading.py | 422 ++++++++++++++++++++++++++++++++++++ 2 files changed, 426 insertions(+), 382 deletions(-) create mode 100644 openerp/modules/loading.py diff --git a/openerp/modules/__init__.py b/openerp/modules/__init__.py index c37016628a9..171d8418269 100644 --- a/openerp/modules/__init__.py +++ b/openerp/modules/__init__.py @@ -24,36 +24,13 @@ """ -import os, sys, imp -from os.path import join as opj -import itertools -import zipimport - -import openerp - -import openerp.osv as osv -import openerp.tools as tools -import openerp.tools.osutil as osutil -from openerp.tools.safe_eval import safe_eval as eval -import openerp.pooler as pooler -from openerp.tools.translate import _ - -import openerp.netsvc as netsvc - -import zipfile -import openerp.release as release - -import re -import base64 -from zipfile import PyZipFile, ZIP_DEFLATED -from cStringIO import StringIO - -import logging - import openerp.modules.db import openerp.modules.graph +import openerp.modules.loading import openerp.modules.migration +import openerp.modules.module +# TODO temporarily expose those things from openerp.modules.module import \ get_modules, get_modules_with_version, \ load_information_from_description_file, \ @@ -61,362 +38,7 @@ from openerp.modules.module import \ get_module_path, initialize_sys_path, \ register_module_classes, init_module_models -logger = netsvc.Logger() - - -def open_openerp_namespace(): - # See comment for open_openerp_namespace. - if openerp.conf.deprecation.open_openerp_namespace: - for k, v in list(sys.modules.items()): - if k.startswith('openerp.') and sys.modules.get(k[8:]) is None: - sys.modules[k[8:]] = v - - -def load_module_graph(cr, graph, status=None, perform_checks=True, skip_modules=None, report=None): - """Migrates+Updates or Installs all module nodes from ``graph`` - :param graph: graph of module nodes to load - :param status: status dictionary for keeping track of progress - :param perform_checks: whether module descriptors should be checked for validity (prints warnings - for same cases, and even raise osv_except if certificate is invalid) - :param skip_modules: optional list of module names (packages) which have previously been loaded and can be skipped - :return: list of modules that were installed or updated - """ - def process_sql_file(cr, fp): - queries = fp.read().split(';') - for query in queries: - new_query = ' '.join(query.split()) - if new_query: - cr.execute(new_query) - - def load_init_xml(cr, m, idref, mode): - _load_data(cr, m, idref, mode, 'init_xml') - - def load_update_xml(cr, m, idref, mode): - _load_data(cr, m, idref, mode, 'update_xml') - - def load_demo_xml(cr, m, idref, mode): - _load_data(cr, m, idref, mode, 'demo_xml') - - def load_data(cr, module_name, idref, mode): - _load_data(cr, module_name, idref, mode, 'data') - - def load_demo(cr, module_name, idref, mode): - _load_data(cr, module_name, idref, mode, 'demo') - - def load_test(cr, module_name, idref, mode): - cr.commit() - if not tools.config.options['test_disable']: - try: - _load_data(cr, module_name, idref, mode, 'test') - except Exception, e: - logging.getLogger('test').exception('Tests failed to execute in module %s', module_name) - finally: - if tools.config.options['test_commit']: - cr.commit() - else: - cr.rollback() - - def _load_data(cr, module_name, idref, mode, kind): - """ - - kind: data, demo, test, init_xml, update_xml, demo_xml. - - noupdate is False, unless it is demo data or it is csv data in - init mode. - - """ - for filename in package.data[kind]: - log = logging.getLogger('init') - log.info("module %s: loading %s", module_name, filename) - _, ext = os.path.splitext(filename) - pathname = os.path.join(module_name, filename) - fp = tools.file_open(pathname) - noupdate = False - if kind in ('demo', 'demo_xml'): - noupdate = True - try: - if ext == '.csv': - if kind in ('init', 'init_xml'): - noupdate = True - tools.convert_csv_import(cr, module_name, pathname, fp.read(), idref, mode, noupdate) - elif ext == '.sql': - process_sql_file(cr, fp) - elif ext == '.yml': - tools.convert_yaml_import(cr, module_name, fp, idref, mode, noupdate) - else: - tools.convert_xml_import(cr, module_name, fp, idref, mode, noupdate, report) - finally: - fp.close() - - if status is None: - status = {} - - processed_modules = [] - statusi = 0 - pool = pooler.get_pool(cr.dbname) - migrations = openerp.modules.migration.MigrationManager(cr, graph) - logger.notifyChannel('init', netsvc.LOG_DEBUG, 'loading %d packages..' % len(graph)) - - # register, instanciate and initialize models for each modules - for package in graph: - if skip_modules and package.name in skip_modules: - continue - logger.notifyChannel('init', netsvc.LOG_INFO, 'module %s: loading objects' % package.name) - migrations.migrate_module(package, 'pre') - register_module_classes(package.name) - models = pool.instanciate(package.name, cr) - if hasattr(package, 'init') or hasattr(package, 'update') or package.state in ('to install', 'to upgrade'): - init_module_models(cr, package.name, models) - cr.commit() - - # load data for each modules - modobj = pool.get('ir.module.module') - for package in graph: - status['progress'] = (float(statusi)+0.1) / len(graph) - m = package.name - mid = package.id - - if skip_modules and m in skip_modules: - continue - - if perform_checks: - modobj.check(cr, 1, [mid]) - - idref = {} - status['progress'] = (float(statusi)+0.4) / len(graph) - - mode = 'update' - if hasattr(package, 'init') or package.state == 'to install': - mode = 'init' - - if hasattr(package, 'init') or hasattr(package, 'update') or package.state in ('to install', 'to upgrade'): - if package.state=='to upgrade': - # upgrading the module information - modobj.write(cr, 1, [mid], modobj.get_values_from_terp(package.data)) - load_init_xml(cr, m, idref, mode) - load_update_xml(cr, m, idref, mode) - load_data(cr, m, idref, mode) - if hasattr(package, 'demo') or (package.dbdemo and package.state != 'installed'): - status['progress'] = (float(statusi)+0.75) / len(graph) - load_demo_xml(cr, m, idref, mode) - load_demo(cr, m, idref, mode) - cr.execute('update ir_module_module set demo=%s where id=%s', (True, mid)) - - # 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) - - processed_modules.append(package.name) - - migrations.migrate_module(package, 'post') - - ver = release.major_version + '.' + package.data['version'] - # Set new modules and dependencies - modobj.write(cr, 1, [mid], {'state': 'installed', 'latest_version': ver}) - cr.commit() - # Update translations for all installed languages - modobj.update_translations(cr, 1, [mid], None) - cr.commit() - - package.state = 'installed' - for kind in ('init', 'demo', 'update'): - if hasattr(package, kind): - delattr(package, kind) - - statusi += 1 - - cr.commit() - - return processed_modules - -def _check_module_names(cr, module_names): - mod_names = set(module_names) - if 'base' in mod_names: - # ignore dummy 'all' module - if 'all' in mod_names: - mod_names.remove('all') - if mod_names: - cr.execute("SELECT count(id) AS count FROM ir_module_module WHERE name in %s", (tuple(mod_names),)) - if cr.dictfetchone()['count'] != len(mod_names): - # find out what module name(s) are incorrect: - cr.execute("SELECT name FROM ir_module_module") - incorrect_names = mod_names.difference([x['name'] for x in cr.dictfetchall()]) - logging.getLogger('init').warning('invalid module names, ignored: %s', ", ".join(incorrect_names)) - -def load_modules(db, force_demo=False, status=None, update_module=False): - # TODO status['progress'] reporting is broken: used twice (and reset each - # time to zero) in load_module_graph, not fine-grained enough. - # It should be a method exposed by the pool. - - initialize_sys_path() - - open_openerp_namespace() - - cr = db.cursor() - if cr: - cr.execute("SELECT relname FROM pg_class WHERE relkind='r' AND relname='ir_module_module'") - if len(cr.fetchall())==0: - logger.notifyChannel("init", netsvc.LOG_INFO, "init db") - openerp.modules.db.initialize(cr) - tools.config["init"]["all"] = 1 - tools.config['update']['all'] = 1 - if not tools.config['without_demo']: - tools.config["demo"]['all'] = 1 - force = [] - if force_demo: - force.append('demo') - - # This is a brand new pool, just created in pooler.get_db_and_pool() - pool = pooler.get_pool(cr.dbname) - - try: - processed_modules = [] - report = tools.assertion_report() - # NOTE: Try to also load the modules that have been marked as uninstallable previously... - STATES_TO_LOAD = ['installed', 'to upgrade', 'uninstallable'] - if 'base' in tools.config['update'] or 'all' in tools.config['update']: - cr.execute("update ir_module_module set state=%s where name=%s and state=%s", ('to upgrade', 'base', 'installed')) - - # STEP 1: LOAD BASE (must be done before module dependencies can be computed for later steps) - graph = openerp.modules.graph.Graph() - graph.add_module(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)')) - processed_modules.extend(load_module_graph(cr, graph, status, perform_checks=(not update_module), report=report)) - - if tools.config['load_language']: - for lang in tools.config['load_language'].split(','): - tools.load_language(cr, lang) - - # STEP 2: Mark other modules to be loaded/updated - if update_module: - modobj = pool.get('ir.module.module') - if ('base' in tools.config['init']) or ('base' in tools.config['update']): - logger.notifyChannel('init', netsvc.LOG_INFO, 'updating modules list') - modobj.update_list(cr, 1) - - _check_module_names(cr, itertools.chain(tools.config['init'].keys(), tools.config['update'].keys())) - - mods = [k for k in tools.config['init'] if tools.config['init'][k]] - if mods: - ids = modobj.search(cr, 1, ['&', ('state', '=', 'uninstalled'), ('name', 'in', mods)]) - if ids: - modobj.button_install(cr, 1, ids) - - mods = [k for k in tools.config['update'] if tools.config['update'][k]] - if mods: - ids = modobj.search(cr, 1, ['&', ('state', '=', 'installed'), ('name', 'in', mods)]) - if ids: - modobj.button_upgrade(cr, 1, ids) - - cr.execute("update ir_module_module set state=%s where name=%s", ('installed', 'base')) - - STATES_TO_LOAD += ['to install'] - - - # STEP 3: Load marked modules (skipping base which was done in STEP 1) - loop_guardrail = 0 - while True: - loop_guardrail += 1 - if loop_guardrail > 100: - 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: - break - - new_modules_in_graph = graph.add_modules(cr, module_list, force) - if new_modules_in_graph == 0: - # nothing to load - break - - logger.notifyChannel('init', netsvc.LOG_DEBUG, 'Updating graph with %d more modules' % (len(module_list))) - processed_modules.extend(load_module_graph(cr, graph, status, report=report, skip_modules=processed_modules)) - - # load custom models - cr.execute('select model from ir_model where state=%s', ('manual',)) - for model in cr.dictfetchall(): - pool.get('ir.model').instanciate(cr, 1, model['model'], {}) - - # STEP 4: Finish and cleanup - if processed_modules: - 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(): - model_obj = pool.get(model) - if model_obj and 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(): - obj = pool.get(model) - if obj: - obj._check_removed_columns(cr, log=True) - else: - logger.notifyChannel('init', netsvc.LOG_WARNING, "Model %s is referenced but not present in the orm pool!" % model) - - # Cleanup orphan records - pool.get('ir.model.data')._process_end(cr, 1, processed_modules) - - if report.get_report(): - logger.notifyChannel('init', netsvc.LOG_INFO, report) - - for kind in ('init', 'demo', 'update'): - tools.config[kind] = {} - - cr.commit() - if update_module: - # Remove records referenced from ir_model_data for modules to be - # removed (and removed the references from ir_model_data). - cr.execute("select id,name from ir_module_module where state=%s", ('to remove',)) - for mod_id, mod_name in cr.fetchall(): - cr.execute('select model,res_id from ir_model_data where noupdate=%s and module=%s order by id desc', (False, mod_name,)) - for rmod, rid in cr.fetchall(): - uid = 1 - rmod_module= pool.get(rmod) - if rmod_module: - # TODO group by module so that we can delete multiple ids in a call - rmod_module.unlink(cr, uid, [rid]) - else: - logger.notifyChannel('init', netsvc.LOG_ERROR, 'Could not locate %s to remove res=%d' % (rmod,rid)) - cr.execute('delete from ir_model_data where noupdate=%s and module=%s', (False, mod_name,)) - cr.commit() - - # Remove menu items that are not referenced by any of other - # (child) menu item, ir_values, or ir_model_data. - # This code could be a method of ir_ui_menu. - # TODO: remove menu without actions of children - while True: - cr.execute('''delete from - ir_ui_menu - where - (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')) - and - (id not IN (select res_id from ir_model_data where model='ir.ui.menu'))''') - cr.commit() - if not cr.rowcount: - break - else: - logger.notifyChannel('init', netsvc.LOG_INFO, 'removed %d unused menus' % (cr.rowcount,)) - - # Pretend that modules to be removed are actually uninstalled. - cr.execute("update ir_module_module set state=%s where state=%s", ('uninstalled', 'to remove',)) - cr.commit() - finally: - cr.close() +from openerp.modules.loading import load_modules # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/openerp/modules/loading.py b/openerp/modules/loading.py new file mode 100644 index 00000000000..c37016628a9 --- /dev/null +++ b/openerp/modules/loading.py @@ -0,0 +1,422 @@ +# -*- coding: utf-8 -*- +############################################################################## +# +# OpenERP, Open Source Management Solution +# Copyright (C) 2004-2009 Tiny SPRL (). +# Copyright (C) 2010-2011 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 +# 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 . +# +############################################################################## + +""" Modules (also called addons) management. + +""" + +import os, sys, imp +from os.path import join as opj +import itertools +import zipimport + +import openerp + +import openerp.osv as osv +import openerp.tools as tools +import openerp.tools.osutil as osutil +from openerp.tools.safe_eval import safe_eval as eval +import openerp.pooler as pooler +from openerp.tools.translate import _ + +import openerp.netsvc as netsvc + +import zipfile +import openerp.release as release + +import re +import base64 +from zipfile import PyZipFile, ZIP_DEFLATED +from cStringIO import StringIO + +import logging + +import openerp.modules.db +import openerp.modules.graph +import openerp.modules.migration + +from openerp.modules.module import \ + get_modules, get_modules_with_version, \ + load_information_from_description_file, \ + get_module_resource, zip_directory, \ + get_module_path, initialize_sys_path, \ + register_module_classes, init_module_models + +logger = netsvc.Logger() + + +def open_openerp_namespace(): + # See comment for open_openerp_namespace. + if openerp.conf.deprecation.open_openerp_namespace: + for k, v in list(sys.modules.items()): + if k.startswith('openerp.') and sys.modules.get(k[8:]) is None: + sys.modules[k[8:]] = v + + +def load_module_graph(cr, graph, status=None, perform_checks=True, skip_modules=None, report=None): + """Migrates+Updates or Installs all module nodes from ``graph`` + :param graph: graph of module nodes to load + :param status: status dictionary for keeping track of progress + :param perform_checks: whether module descriptors should be checked for validity (prints warnings + for same cases, and even raise osv_except if certificate is invalid) + :param skip_modules: optional list of module names (packages) which have previously been loaded and can be skipped + :return: list of modules that were installed or updated + """ + def process_sql_file(cr, fp): + queries = fp.read().split(';') + for query in queries: + new_query = ' '.join(query.split()) + if new_query: + cr.execute(new_query) + + def load_init_xml(cr, m, idref, mode): + _load_data(cr, m, idref, mode, 'init_xml') + + def load_update_xml(cr, m, idref, mode): + _load_data(cr, m, idref, mode, 'update_xml') + + def load_demo_xml(cr, m, idref, mode): + _load_data(cr, m, idref, mode, 'demo_xml') + + def load_data(cr, module_name, idref, mode): + _load_data(cr, module_name, idref, mode, 'data') + + def load_demo(cr, module_name, idref, mode): + _load_data(cr, module_name, idref, mode, 'demo') + + def load_test(cr, module_name, idref, mode): + cr.commit() + if not tools.config.options['test_disable']: + try: + _load_data(cr, module_name, idref, mode, 'test') + except Exception, e: + logging.getLogger('test').exception('Tests failed to execute in module %s', module_name) + finally: + if tools.config.options['test_commit']: + cr.commit() + else: + cr.rollback() + + def _load_data(cr, module_name, idref, mode, kind): + """ + + kind: data, demo, test, init_xml, update_xml, demo_xml. + + noupdate is False, unless it is demo data or it is csv data in + init mode. + + """ + for filename in package.data[kind]: + log = logging.getLogger('init') + log.info("module %s: loading %s", module_name, filename) + _, ext = os.path.splitext(filename) + pathname = os.path.join(module_name, filename) + fp = tools.file_open(pathname) + noupdate = False + if kind in ('demo', 'demo_xml'): + noupdate = True + try: + if ext == '.csv': + if kind in ('init', 'init_xml'): + noupdate = True + tools.convert_csv_import(cr, module_name, pathname, fp.read(), idref, mode, noupdate) + elif ext == '.sql': + process_sql_file(cr, fp) + elif ext == '.yml': + tools.convert_yaml_import(cr, module_name, fp, idref, mode, noupdate) + else: + tools.convert_xml_import(cr, module_name, fp, idref, mode, noupdate, report) + finally: + fp.close() + + if status is None: + status = {} + + processed_modules = [] + statusi = 0 + pool = pooler.get_pool(cr.dbname) + migrations = openerp.modules.migration.MigrationManager(cr, graph) + logger.notifyChannel('init', netsvc.LOG_DEBUG, 'loading %d packages..' % len(graph)) + + # register, instanciate and initialize models for each modules + for package in graph: + if skip_modules and package.name in skip_modules: + continue + logger.notifyChannel('init', netsvc.LOG_INFO, 'module %s: loading objects' % package.name) + migrations.migrate_module(package, 'pre') + register_module_classes(package.name) + models = pool.instanciate(package.name, cr) + if hasattr(package, 'init') or hasattr(package, 'update') or package.state in ('to install', 'to upgrade'): + init_module_models(cr, package.name, models) + cr.commit() + + # load data for each modules + modobj = pool.get('ir.module.module') + for package in graph: + status['progress'] = (float(statusi)+0.1) / len(graph) + m = package.name + mid = package.id + + if skip_modules and m in skip_modules: + continue + + if perform_checks: + modobj.check(cr, 1, [mid]) + + idref = {} + status['progress'] = (float(statusi)+0.4) / len(graph) + + mode = 'update' + if hasattr(package, 'init') or package.state == 'to install': + mode = 'init' + + if hasattr(package, 'init') or hasattr(package, 'update') or package.state in ('to install', 'to upgrade'): + if package.state=='to upgrade': + # upgrading the module information + modobj.write(cr, 1, [mid], modobj.get_values_from_terp(package.data)) + load_init_xml(cr, m, idref, mode) + load_update_xml(cr, m, idref, mode) + load_data(cr, m, idref, mode) + if hasattr(package, 'demo') or (package.dbdemo and package.state != 'installed'): + status['progress'] = (float(statusi)+0.75) / len(graph) + load_demo_xml(cr, m, idref, mode) + load_demo(cr, m, idref, mode) + cr.execute('update ir_module_module set demo=%s where id=%s', (True, mid)) + + # 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) + + processed_modules.append(package.name) + + migrations.migrate_module(package, 'post') + + ver = release.major_version + '.' + package.data['version'] + # Set new modules and dependencies + modobj.write(cr, 1, [mid], {'state': 'installed', 'latest_version': ver}) + cr.commit() + # Update translations for all installed languages + modobj.update_translations(cr, 1, [mid], None) + cr.commit() + + package.state = 'installed' + for kind in ('init', 'demo', 'update'): + if hasattr(package, kind): + delattr(package, kind) + + statusi += 1 + + cr.commit() + + return processed_modules + +def _check_module_names(cr, module_names): + mod_names = set(module_names) + if 'base' in mod_names: + # ignore dummy 'all' module + if 'all' in mod_names: + mod_names.remove('all') + if mod_names: + cr.execute("SELECT count(id) AS count FROM ir_module_module WHERE name in %s", (tuple(mod_names),)) + if cr.dictfetchone()['count'] != len(mod_names): + # find out what module name(s) are incorrect: + cr.execute("SELECT name FROM ir_module_module") + incorrect_names = mod_names.difference([x['name'] for x in cr.dictfetchall()]) + logging.getLogger('init').warning('invalid module names, ignored: %s', ", ".join(incorrect_names)) + +def load_modules(db, force_demo=False, status=None, update_module=False): + # TODO status['progress'] reporting is broken: used twice (and reset each + # time to zero) in load_module_graph, not fine-grained enough. + # It should be a method exposed by the pool. + + initialize_sys_path() + + open_openerp_namespace() + + cr = db.cursor() + if cr: + cr.execute("SELECT relname FROM pg_class WHERE relkind='r' AND relname='ir_module_module'") + if len(cr.fetchall())==0: + logger.notifyChannel("init", netsvc.LOG_INFO, "init db") + openerp.modules.db.initialize(cr) + tools.config["init"]["all"] = 1 + tools.config['update']['all'] = 1 + if not tools.config['without_demo']: + tools.config["demo"]['all'] = 1 + force = [] + if force_demo: + force.append('demo') + + # This is a brand new pool, just created in pooler.get_db_and_pool() + pool = pooler.get_pool(cr.dbname) + + try: + processed_modules = [] + report = tools.assertion_report() + # NOTE: Try to also load the modules that have been marked as uninstallable previously... + STATES_TO_LOAD = ['installed', 'to upgrade', 'uninstallable'] + if 'base' in tools.config['update'] or 'all' in tools.config['update']: + cr.execute("update ir_module_module set state=%s where name=%s and state=%s", ('to upgrade', 'base', 'installed')) + + # STEP 1: LOAD BASE (must be done before module dependencies can be computed for later steps) + graph = openerp.modules.graph.Graph() + graph.add_module(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)')) + processed_modules.extend(load_module_graph(cr, graph, status, perform_checks=(not update_module), report=report)) + + if tools.config['load_language']: + for lang in tools.config['load_language'].split(','): + tools.load_language(cr, lang) + + # STEP 2: Mark other modules to be loaded/updated + if update_module: + modobj = pool.get('ir.module.module') + if ('base' in tools.config['init']) or ('base' in tools.config['update']): + logger.notifyChannel('init', netsvc.LOG_INFO, 'updating modules list') + modobj.update_list(cr, 1) + + _check_module_names(cr, itertools.chain(tools.config['init'].keys(), tools.config['update'].keys())) + + mods = [k for k in tools.config['init'] if tools.config['init'][k]] + if mods: + ids = modobj.search(cr, 1, ['&', ('state', '=', 'uninstalled'), ('name', 'in', mods)]) + if ids: + modobj.button_install(cr, 1, ids) + + mods = [k for k in tools.config['update'] if tools.config['update'][k]] + if mods: + ids = modobj.search(cr, 1, ['&', ('state', '=', 'installed'), ('name', 'in', mods)]) + if ids: + modobj.button_upgrade(cr, 1, ids) + + cr.execute("update ir_module_module set state=%s where name=%s", ('installed', 'base')) + + STATES_TO_LOAD += ['to install'] + + + # STEP 3: Load marked modules (skipping base which was done in STEP 1) + loop_guardrail = 0 + while True: + loop_guardrail += 1 + if loop_guardrail > 100: + 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: + break + + new_modules_in_graph = graph.add_modules(cr, module_list, force) + if new_modules_in_graph == 0: + # nothing to load + break + + logger.notifyChannel('init', netsvc.LOG_DEBUG, 'Updating graph with %d more modules' % (len(module_list))) + processed_modules.extend(load_module_graph(cr, graph, status, report=report, skip_modules=processed_modules)) + + # load custom models + cr.execute('select model from ir_model where state=%s', ('manual',)) + for model in cr.dictfetchall(): + pool.get('ir.model').instanciate(cr, 1, model['model'], {}) + + # STEP 4: Finish and cleanup + if processed_modules: + 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(): + model_obj = pool.get(model) + if model_obj and 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(): + obj = pool.get(model) + if obj: + obj._check_removed_columns(cr, log=True) + else: + logger.notifyChannel('init', netsvc.LOG_WARNING, "Model %s is referenced but not present in the orm pool!" % model) + + # Cleanup orphan records + pool.get('ir.model.data')._process_end(cr, 1, processed_modules) + + if report.get_report(): + logger.notifyChannel('init', netsvc.LOG_INFO, report) + + for kind in ('init', 'demo', 'update'): + tools.config[kind] = {} + + cr.commit() + if update_module: + # Remove records referenced from ir_model_data for modules to be + # removed (and removed the references from ir_model_data). + cr.execute("select id,name from ir_module_module where state=%s", ('to remove',)) + for mod_id, mod_name in cr.fetchall(): + cr.execute('select model,res_id from ir_model_data where noupdate=%s and module=%s order by id desc', (False, mod_name,)) + for rmod, rid in cr.fetchall(): + uid = 1 + rmod_module= pool.get(rmod) + if rmod_module: + # TODO group by module so that we can delete multiple ids in a call + rmod_module.unlink(cr, uid, [rid]) + else: + logger.notifyChannel('init', netsvc.LOG_ERROR, 'Could not locate %s to remove res=%d' % (rmod,rid)) + cr.execute('delete from ir_model_data where noupdate=%s and module=%s', (False, mod_name,)) + cr.commit() + + # Remove menu items that are not referenced by any of other + # (child) menu item, ir_values, or ir_model_data. + # This code could be a method of ir_ui_menu. + # TODO: remove menu without actions of children + while True: + cr.execute('''delete from + ir_ui_menu + where + (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')) + and + (id not IN (select res_id from ir_model_data where model='ir.ui.menu'))''') + cr.commit() + if not cr.rowcount: + break + else: + logger.notifyChannel('init', netsvc.LOG_INFO, 'removed %d unused menus' % (cr.rowcount,)) + + # Pretend that modules to be removed are actually uninstalled. + cr.execute("update ir_module_module set state=%s where state=%s", ('uninstalled', 'to remove',)) + cr.commit() + finally: + cr.close() + + +# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: