1 # -*- coding: utf-8 -*-
2 ##############################################################################
4 # OpenERP, Open Source Management Solution
5 # Copyright (C) 2004-2009 Tiny SPRL (<http://tiny.be>).
6 # Copyright (C) 2010 OpenERP s.a. (<http://openerp.com>).
8 # This program is free software: you can redistribute it and/or modify
9 # it under the terms of the GNU Affero General Public License as
10 # published by the Free Software Foundation, either version 3 of the
11 # License, or (at your option) any later version.
13 # This program is distributed in the hope that it will be useful,
14 # but WITHOUT ANY WARRANTY; without even the implied warranty of
15 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 # GNU Affero General Public License for more details.
18 # You should have received a copy of the GNU Affero General Public License
19 # along with this program. If not, see <http://www.gnu.org/licenses/>.
21 ##############################################################################
24 from os.path import join as opj
31 from tools.safe_eval import safe_eval as eval
42 from zipfile import PyZipFile, ZIP_DEFLATED
43 from cStringIO import StringIO
48 logger = netsvc.Logger()
50 _ad = os.path.abspath(opj(tools.config['root_path'], 'addons')) # default addons path (base)
51 ad_paths= map(lambda m: os.path.abspath(m.strip()),tools.config['addons_path'].split(','))
53 sys.path.insert(1, _ad)
58 sys.path.insert(ad_cnt, adp)
61 ad_paths.append(_ad) # for get_module_path
63 # Modules already loaded
68 def addNode(self, name, deps):
69 max_depth, father = 0, None
70 for n in [Node(x, self) for x in deps]:
71 if n.depth >= max_depth:
79 def update_from_db(self, cr):
82 # update the graph with values from the database (if exist)
83 ## First, we set the default values for each package in graph
84 additional_data = dict.fromkeys(self.keys(), {'id': 0, 'state': 'uninstalled', 'dbdemo': False, 'installed_version': None})
85 ## Then we get the values from the database
86 cr.execute('SELECT name, id, state, demo AS dbdemo, latest_version AS installed_version'
87 ' FROM ir_module_module'
88 ' WHERE name IN %s',(tuple(additional_data),)
91 ## and we update the default values with values from the database
92 additional_data.update(dict([(x.pop('name'), x) for x in cr.dictfetchall()]))
94 for package in self.values():
95 for k, v in additional_data[package.name].items():
96 setattr(package, k, v)
100 done = set(self.keys())
102 level_modules = [(name, module) for name, module in self.items() if module.depth==level]
103 for name, module in level_modules:
108 class Singleton(object):
109 def __new__(cls, name, graph):
113 inst = object.__new__(cls)
119 class Node(Singleton):
121 def __init__(self, name, graph):
123 if not hasattr(self, 'children'):
125 if not hasattr(self, 'depth'):
128 def addChild(self, name):
129 node = Node(name, self.graph)
130 node.depth = self.depth + 1
131 if node not in self.children:
132 self.children.append(node)
133 for attr in ('init', 'update', 'demo'):
134 if hasattr(self, attr):
135 setattr(node, attr, True)
136 self.children.sort(lambda x, y: cmp(x.name, y.name))
138 def __setattr__(self, name, value):
139 super(Singleton, self).__setattr__(name, value)
140 if name in ('init', 'update', 'demo'):
141 tools.config[name][self.name] = 1
142 for child in self.children:
143 setattr(child, name, value)
145 for child in self.children:
146 setattr(child, name, value + 1)
149 return itertools.chain(iter(self.children), *map(iter, self.children))
152 return self._pprint()
154 def _pprint(self, depth=0):
155 s = '%s\n' % self.name
156 for c in self.children:
157 s += '%s`-> %s' % (' ' * depth, c._pprint(depth+1))
161 def get_module_path(module, downloaded=False):
162 """Return the path of the given module."""
164 if os.path.exists(opj(adp, module)) or os.path.exists(opj(adp, '%s.zip' % module)):
165 return opj(adp, module)
168 return opj(_ad, module)
169 logger.notifyChannel('init', netsvc.LOG_WARNING, 'module %s: module not found' % (module,))
173 def get_module_filetree(module, dir='.'):
174 path = get_module_path(module)
178 dir = os.path.normpath(dir)
181 if dir.startswith('..') or (dir and dir[0] == '/'):
182 raise Exception('Cannot access file outside the module')
184 if not os.path.isdir(path):
186 zip = zipfile.ZipFile(path + ".zip")
187 files = ['/'.join(f.split('/')[1:]) for f in zip.namelist()]
189 files = tools.osutil.listdir(path, True)
193 if not f.startswith(dir):
197 f = f[len(dir)+int(not dir.endswith('/')):]
198 lst = f.split(os.sep)
201 current = current.setdefault(lst.pop(0), {})
202 current[lst.pop(0)] = None
206 def zip_directory(directory, b64enc=True, src=True):
207 """Compress a directory
209 @param directory: The directory to compress
210 @param base64enc: if True the function will encode the zip file with base64
211 @param src: Integrate the source files
213 @return: a string containing the zip file
216 RE_exclude = re.compile('(?:^\..+\.swp$)|(?:\.py[oc]$)|(?:\.bak$)|(?:\.~.~$)', re.I)
218 def _zippy(archive, path, src=True):
219 path = os.path.abspath(path)
220 base = os.path.basename(path)
221 for f in tools.osutil.listdir(path, True):
222 bf = os.path.basename(f)
223 if not RE_exclude.search(bf) and (src or bf in ('__openerp__.py', '__terp__.py') or not bf.endswith('.py')):
224 archive.write(os.path.join(path, f), os.path.join(base, f))
226 archname = StringIO()
227 archive = PyZipFile(archname, "w", ZIP_DEFLATED)
228 archive.writepy(directory)
229 _zippy(archive, directory, src=src)
231 archive_data = archname.getvalue()
235 return base64.encodestring(archive_data)
239 def get_module_as_zip(modulename, b64enc=True, src=True):
240 """Generate a module as zip file with the source or not and can do a base64 encoding
242 @param modulename: The module name
243 @param b64enc: if True the function will encode the zip file with base64
244 @param src: Integrate the source files
246 @return: a stream to store in a file-like object
249 ap = get_module_path(str(modulename))
251 raise Exception('Unable to find path for module %s' % modulename)
253 ap = ap.encode('utf8')
254 if os.path.isfile(ap + '.zip'):
255 val = file(ap + '.zip', 'rb').read()
257 val = base64.encodestring(val)
259 val = zip_directory(ap, b64enc, src)
264 def get_module_resource(module, *args):
265 """Return the full path of a resource of the given module.
267 @param module: the module
268 @param args: the resource path components
270 @return: absolute path to the resource
272 a = get_module_path(module)
273 if not a: return False
274 resource_path = opj(a, *args)
275 if zipfile.is_zipfile( a +'.zip') :
276 zip = zipfile.ZipFile( a + ".zip")
277 files = ['/'.join(f.split('/')[1:]) for f in zip.namelist()]
278 resource_path = '/'.join(args)
279 if resource_path in files:
280 return opj(a, resource_path)
281 elif os.path.exists(resource_path):
288 """Returns the list of module names
292 name = os.path.basename(name)
293 if name[-4:] == '.zip':
297 def is_really_module(name):
298 name = opj(dir, name)
299 return os.path.isdir(name) or zipfile.is_zipfile(name)
300 return map(clean, filter(is_really_module, os.listdir(dir)))
304 plist.extend(listdir(ad))
305 return list(set(plist))
307 def load_information_from_description_file(module):
309 :param module: The name of the module (sale, purchase, ...)
312 for filename in ['__openerp__.py', '__terp__.py']:
313 description_file = get_module_resource(module, filename)
314 if description_file :
315 return eval(tools.file_open(description_file).read())
317 #TODO: refactor the logger in this file to follow the logging guidelines
319 logging.getLogger('addons').debug('The module %s does not contain a description file:'\
320 '__openerp__.py or __terp__.py (deprecated)', module)
323 def get_modules_with_version():
324 modules = get_modules()
326 for module in modules:
328 info = load_information_from_description_file(module)
329 res[module] = "%s.%s" % (release.major_version, info['version'])
334 def create_graph(cr, module_list, force=None):
336 upgrade_graph(graph, cr, module_list, force)
339 def upgrade_graph(graph, cr, module_list, force=None):
343 len_graph = len(graph)
344 for module in module_list:
345 mod_path = get_module_path(module)
346 terp_file = get_module_resource(module, '__openerp__.py')
348 terp_file = get_module_resource(module, '__terp__.py')
349 if not mod_path or not terp_file:
350 logger.notifyChannel('init', netsvc.LOG_WARNING, 'module %s: not found, skipped' % (module))
353 if os.path.isfile(terp_file) or zipfile.is_zipfile(mod_path+'.zip'):
355 info = eval(tools.file_open(terp_file).read())
357 logger.notifyChannel('init', netsvc.LOG_ERROR, 'module %s: eval file %s' % (module, terp_file))
359 if info.get('installable', True):
360 packages.append((module, info.get('depends', []), info))
362 logger.notifyChannel('init', netsvc.LOG_WARNING, 'module %s: not installable, skipped' % (module))
364 dependencies = dict([(p, deps) for p, deps, data in packages])
365 current, later = set([p for p, dep, data in packages]), set()
367 while packages and current > later:
368 package, deps, data = packages[0]
370 # if all dependencies of 'package' are already in the graph, add 'package' in the graph
371 if reduce(lambda x, y: x and y in graph, deps, True):
372 if not package in current:
376 current.remove(package)
377 graph.addNode(package, deps)
378 node = Node(package, graph)
380 for kind in ('init', 'demo', 'update'):
381 if package in tools.config[kind] or 'all' in tools.config[kind] or kind in force:
382 setattr(node, kind, True)
385 packages.append((package, deps, data))
388 graph.update_from_db(cr)
390 for package in later:
391 unmet_deps = filter(lambda p: p not in graph, dependencies[package])
392 logger.notifyChannel('init', netsvc.LOG_ERROR, 'module %s: Unmet dependencies: %s' % (package, ', '.join(unmet_deps)))
394 result = len(graph) - len_graph
395 if result != len(module_list):
396 logger.notifyChannel('init', netsvc.LOG_WARNING, 'Not all modules have loaded.')
400 def init_module_objects(cr, module_name, obj_list):
401 logger.notifyChannel('init', netsvc.LOG_INFO, 'module %s: creating or updating database tables' % module_name)
405 result = obj._auto_init(cr, {'module': module_name})
410 if hasattr(obj, 'init'):
419 def register_class(m):
421 Register module named m, if not already registered
425 mt = isinstance(e, zipimport.ZipImportError) and 'zip ' or ''
426 msg = "Couldn't load %smodule %s" % (mt, m)
427 logger.notifyChannel('init', netsvc.LOG_CRITICAL, msg)
428 logger.notifyChannel('init', netsvc.LOG_CRITICAL, e)
433 logger.notifyChannel('init', netsvc.LOG_INFO, 'module %s: registering objects' % m)
434 mod_path = get_module_path(m)
437 zip_mod_path = mod_path + '.zip'
438 if not os.path.isfile(zip_mod_path):
439 fm = imp.find_module(m, ad_paths)
441 imp.load_module(m, *fm)
446 zimp = zipimport.zipimporter(zip_mod_path)
455 class MigrationManager(object):
457 This class manage the migration of modules
458 Migrations files must be python files containing a "migrate(cr, installed_version)" function.
459 Theses files must respect a directory tree structure: A 'migrations' folder which containt a
460 folder by version. Version can be 'module' version or 'server.module' version (in this case,
461 the files will only be processed by this version of the server). Python file names must start
462 by 'pre' or 'post' and will be executed, respectively, before and after the module initialisation
468 | |-- pre-update_table_x.py
469 | |-- pre-update_table_y.py
470 | |-- post-clean-data.py
471 | `-- README.txt # not processed
472 |-- 5.0.1.1 # files in this folder will be executed only on a 5.0 server
473 | |-- pre-delete_table_z.py
474 | `-- post-clean-data.py
475 `-- foo.py # not processed
477 This similar structure is generated by the maintenance module with the migrations files get by
478 the maintenance contract
481 def __init__(self, cr, graph):
487 def _get_files(self):
490 import addons.base.maintenance.utils as maintenance_utils
491 maintenance_utils.update_migrations_files(self.cr)
494 for pkg in self.graph:
495 self.migrations[pkg.name] = {}
496 if not (hasattr(pkg, 'update') or pkg.state == 'to upgrade'):
499 self.migrations[pkg.name]['module'] = get_module_filetree(pkg.name, 'migrations') or {}
500 self.migrations[pkg.name]['maintenance'] = get_module_filetree('base', 'maintenance/migrations/' + pkg.name) or {}
502 def migrate_module(self, pkg, stage):
503 assert stage in ('pre', 'post')
504 stageformat = {'pre': '[>%s]',
508 if not (hasattr(pkg, 'update') or pkg.state == 'to upgrade'):
511 def convert_version(version):
512 if version.startswith(release.major_version) and version != release.major_version:
513 return version # the version number already containt the server version
514 return "%s.%s" % (release.major_version, version)
516 def _get_migration_versions(pkg):
518 return [d for d in tree if tree[d] is not None]
521 __get_dir(self.migrations[pkg.name]['module']) +
522 __get_dir(self.migrations[pkg.name]['maintenance'])
524 versions.sort(key=lambda k: parse_version(convert_version(k)))
527 def _get_migration_files(pkg, version, stage):
528 """ return a list of tuple (module, file)
530 m = self.migrations[pkg.name]
533 mapping = {'module': opj(pkg.name, 'migrations'),
534 'maintenance': opj('base', 'maintenance', 'migrations', pkg.name),
537 for x in mapping.keys():
539 for f in m[x][version]:
540 if m[x][version][f] is not None:
542 if not f.startswith(stage + '-'):
544 lst.append(opj(mapping[x], version, f))
553 from tools.parse_version import parse_version
555 parsed_installed_version = parse_version(pkg.installed_version or '')
556 current_version = parse_version(convert_version(pkg.data.get('version', '0')))
558 versions = _get_migration_versions(pkg)
560 for version in versions:
561 if parsed_installed_version < parse_version(convert_version(version)) <= current_version:
563 strfmt = {'addon': pkg.name,
565 'version': stageformat[stage] % version,
568 for pyfile in _get_migration_files(pkg, version, stage):
569 name, ext = os.path.splitext(os.path.basename(pyfile))
570 if ext.lower() != '.py':
572 mod = fp = fp2 = None
574 fp = tools.file_open(pyfile)
576 # imp.load_source need a real file object, so we create
577 # one from the file-like object we get from file_open
582 mod = imp.load_source(name, pyfile, fp2)
583 logger.notifyChannel('migration', netsvc.LOG_INFO, 'module %(addon)s: Running migration %(version)s %(name)s' % mergedict({'name': mod.__name__}, strfmt))
584 mod.migrate(self.cr, pkg.installed_version)
586 logger.notifyChannel('migration', netsvc.LOG_ERROR, 'module %(addon)s: Unable to load %(stage)s-migration file %(file)s' % mergedict({'file': pyfile}, strfmt))
588 except AttributeError:
589 logger.notifyChannel('migration', netsvc.LOG_ERROR, 'module %(addon)s: Each %(stage)s-migration file must have a "migrate(cr, installed_version)" function' % strfmt)
600 log = logging.getLogger('init')
602 def load_module_graph(cr, graph, status=None, perform_checks=True, **kwargs):
604 def process_sql_file(cr, fp):
605 queries = fp.read().split(';')
606 for query in queries:
607 new_query = ' '.join(query.split())
609 cr.execute(new_query)
611 def load_init_update_xml(cr, m, idref, mode, kind):
612 for filename in package.data.get('%s_xml' % kind, []):
613 logger.notifyChannel('init', netsvc.LOG_INFO, 'module %s: loading %s' % (m, filename))
614 _, ext = os.path.splitext(filename)
615 fp = tools.file_open(opj(m, filename))
617 noupdate = (kind == 'init')
618 tools.convert_csv_import(cr, m, os.path.basename(filename), fp.read(), idref, mode=mode, noupdate=noupdate)
620 process_sql_file(cr, fp)
622 tools.convert_yaml_import(cr, m, fp, idref, mode=mode, **kwargs)
624 tools.convert_xml_import(cr, m, fp, idref, mode=mode, **kwargs)
627 def load_demo_xml(cr, m, idref, mode):
628 for xml in package.data.get('demo_xml', []):
629 name, ext = os.path.splitext(xml)
630 logger.notifyChannel('init', netsvc.LOG_INFO, 'module %s: loading %s' % (m, xml))
631 fp = tools.file_open(opj(m, xml))
633 tools.convert_csv_import(cr, m, os.path.basename(xml), fp.read(), idref, mode=mode, noupdate=True)
635 tools.convert_yaml_import(cr, m, fp, idref, mode=mode, noupdate=True, **kwargs)
637 tools.convert_xml_import(cr, m, fp, idref, mode=mode, noupdate=True, **kwargs)
640 def load_data(cr, module_name, id_map, mode):
641 _load_data(cr, module_name, id_map, mode, 'data')
643 def load_demo(cr, module_name, id_map, mode):
644 _load_data(cr, module_name, id_map, mode, 'demo')
646 def load_test(cr, module_name, id_map, mode):
648 if not tools.config.options['test_disable']:
650 _load_data(cr, module_name, id_map, mode, 'test')
652 logger.notifyChannel('ERROR', netsvc.LOG_TEST, e)
655 if tools.config.options['test_commit']:
660 def _load_data(cr, module_name, id_map, mode, kind):
661 noupdate = (kind == 'demo')
662 for filename in package.data.get(kind, []):
663 _, ext = os.path.splitext(filename)
664 log.info("module %s: loading %s", module_name, filename)
665 pathname = os.path.join(module_name, filename)
666 file = tools.file_open(pathname)
667 # TODO manage .csv file with noupdate == (kind == 'init')
669 process_sql_file(cr, file)
671 noupdate = (kind == 'init')
672 tools.convert_csv_import(cr, module_name, pathname, file.read(), id_map, mode, noupdate)
674 tools.convert_yaml_import(cr, module_name, file, id_map, mode, noupdate)
676 tools.convert_xml_import(cr, module_name, file, id_map, mode, noupdate)
679 # **kwargs is passed directly to convert_xml_import
683 status = status.copy()
686 pool = pooler.get_pool(cr.dbname)
688 migrations = MigrationManager(cr, graph)
693 logger.notifyChannel('init', netsvc.LOG_DEBUG, 'loading %d packages..' % len(graph))
695 for package in graph:
696 logger.notifyChannel('init', netsvc.LOG_INFO, 'module %s: loading objects' % package.name)
697 migrations.migrate_module(package, 'pre')
698 register_class(package.name)
699 modules = pool.instanciate(package.name, cr)
700 if hasattr(package, 'init') or hasattr(package, 'update') or package.state in ('to install', 'to upgrade'):
701 init_module_objects(cr, package.name, modules)
704 for package in graph:
705 status['progress'] = (float(statusi)+0.1) / len(graph)
710 modobj = pool.get('ir.module.module')
712 if modobj and perform_checks:
713 modobj.check(cr, 1, [mid])
716 status['progress'] = (float(statusi)+0.4) / len(graph)
719 if hasattr(package, 'init') or package.state == 'to install':
722 if hasattr(package, 'init') or hasattr(package, 'update') or package.state in ('to install', 'to upgrade'):
724 for kind in ('init', 'update'):
725 if package.state=='to upgrade':
726 # upgrading the module information
727 modobj.write(cr, 1, [mid], modobj.get_values_from_terp(package.data))
728 load_init_update_xml(cr, m, idref, mode, kind)
729 load_data(cr, m, idref, mode)
730 if hasattr(package, 'demo') or (package.dbdemo and package.state != 'installed'):
731 status['progress'] = (float(statusi)+0.75) / len(graph)
732 load_demo_xml(cr, m, idref, mode)
733 load_demo(cr, m, idref, mode)
734 cr.execute('update ir_module_module set demo=%s where id=%s', (True, mid))
736 # launch tests only in demo mode, as most tests will depend
737 # on demo data. Other tests can be added into the regular
738 # 'data' section, but should probably not alter the data,
739 # as there is no rollback.
740 load_test(cr, m, idref, mode)
742 package_todo.append(package.name)
744 migrations.migrate_module(package, 'post')
747 ver = release.major_version + '.' + package.data.get('version', '1.0')
748 # Set new modules and dependencies
749 modobj.write(cr, 1, [mid], {'state': 'installed', 'latest_version': ver})
751 # Update translations for all installed languages
752 modobj.update_translations(cr, 1, [mid], None)
755 package.state = 'installed'
756 for kind in ('init', 'demo', 'update'):
757 if hasattr(package, kind):
758 delattr(package, kind)
762 cr.execute('select model from ir_model where state=%s', ('manual',))
763 for model in cr.dictfetchall():
764 pool.get('ir.model').instanciate(cr, 1, model['model'], {})
766 pool.get('ir.model.data')._process_end(cr, 1, package_todo)
771 def _check_module_names(cr, module_names):
772 mod_names = set(module_names)
773 if 'base' in mod_names:
774 # ignore dummy 'all' module
775 if 'all' in mod_names:
776 mod_names.remove('all')
778 cr.execute("SELECT count(id) AS count FROM ir_module_module WHERE name in %s", (tuple(mod_names),))
779 if cr.dictfetchone()['count'] != len(mod_names):
780 # find out what module name(s) are incorrect:
781 cr.execute("SELECT name FROM ir_module_module")
782 incorrect_names = mod_names.difference([x['name'] for x in cr.dictfetchall()])
783 logging.getLogger('init').warning('invalid module names, ignored: %s', ", ".join(incorrect_names))
785 def load_modules(db, force_demo=False, status=None, update_module=False):
790 cr.execute("SELECT relname FROM pg_class WHERE relkind='r' AND relname='ir_module_module'")
791 if len(cr.fetchall())==0:
792 logger.notifyChannel("init", netsvc.LOG_INFO, "init db")
794 tools.config["init"]["all"] = 1
795 tools.config['update']['all'] = 1
796 if not tools.config['without_demo']:
797 tools.config["demo"]['all'] = 1
801 pool = pooler.get_pool(cr.dbname)
803 report = tools.assertion_report()
804 # NOTE: Try to also load the modules that have been marked as uninstallable previously...
805 STATES_TO_LOAD = ['installed', 'to upgrade', 'uninstallable']
806 graph = create_graph(cr, ['base'], force)
808 logger.notifyChannel('init', netsvc.LOG_CRITICAL, 'module base cannot be loaded! (hint: verify addons-path)')
809 raise osv.osv.except_osv('Could not load base module', 'module base cannot be loaded! (hint: verify addons-path)')
810 has_updates = load_module_graph(cr, graph, status, perform_checks=(not update_module), report=report)
813 modobj = pool.get('ir.module.module')
814 logger.notifyChannel('init', netsvc.LOG_INFO, 'updating modules list')
815 if ('base' in tools.config['init']) or ('base' in tools.config['update']):
816 modobj.update_list(cr, 1)
818 _check_module_names(cr, itertools.chain(tools.config['init'].keys(), tools.config['update'].keys()))
820 mods = [k for k in tools.config['init'] if tools.config['init'][k]]
822 ids = modobj.search(cr, 1, ['&', ('state', '=', 'uninstalled'), ('name', 'in', mods)])
824 modobj.button_install(cr, 1, ids)
826 mods = [k for k in tools.config['update'] if tools.config['update'][k]]
828 ids = modobj.search(cr, 1, ['&', ('state', '=', 'installed'), ('name', 'in', mods)])
830 modobj.button_upgrade(cr, 1, ids)
832 cr.execute("update ir_module_module set state=%s where name=%s", ('installed', 'base'))
834 STATES_TO_LOAD += ['to install']
839 if loop_guardrail > 100:
840 raise ValueError('Possible recursive module tree detected, aborting.')
841 cr.execute("SELECT name from ir_module_module WHERE state IN %s" ,(tuple(STATES_TO_LOAD),))
843 module_list = [name for (name,) in cr.fetchall() if name not in graph]
847 new_modules_in_graph = upgrade_graph(graph, cr, module_list, force)
848 if new_modules_in_graph == 0:
852 logger.notifyChannel('init', netsvc.LOG_DEBUG, 'Updating graph with %d more modules' % (len(module_list)))
853 r = load_module_graph(cr, graph, status, report=report)
854 has_updates = has_updates or r
857 cr.execute("""select model,name from ir_model where id NOT IN (select distinct model_id from ir_model_access)""")
858 for (model, name) in cr.fetchall():
859 model_obj = pool.get(model)
860 if not isinstance(model_obj, osv.osv.osv_memory):
861 logger.notifyChannel('init', netsvc.LOG_WARNING, 'object %s (%s) has no access rules!' % (model, name))
863 # Temporary warning while we remove access rights on osv_memory objects, as they have
864 # been replaced by owner-only access rights
865 cr.execute("""select distinct mod.model, mod.name from ir_model_access acc, ir_model mod where acc.model_id = mod.id""")
866 for (model, name) in cr.fetchall():
867 model_obj = pool.get(model)
868 if isinstance(model_obj, osv.osv.osv_memory):
869 logger.notifyChannel('init', netsvc.LOG_WARNING, 'In-memory object %s (%s) should not have explicit access rules!' % (model, name))
871 cr.execute("SELECT model from ir_model")
872 for (model,) in cr.fetchall():
873 obj = pool.get(model)
875 obj._check_removed_columns(cr, log=True)
877 if report.get_report():
878 logger.notifyChannel('init', netsvc.LOG_INFO, report)
880 for kind in ('init', 'demo', 'update'):
881 tools.config[kind] = {}
885 cr.execute("select id,name from ir_module_module where state=%s", ('to remove',))
886 for mod_id, mod_name in cr.fetchall():
887 cr.execute('select model,res_id from ir_model_data where noupdate=%s and module=%s order by id desc', (False, mod_name,))
888 for rmod, rid in cr.fetchall():
890 rmod_module= pool.get(rmod)
892 rmod_module.unlink(cr, uid, [rid])
894 logger.notifyChannel('init', netsvc.LOG_ERROR, 'Could not locate %s to remove res=%d' % (rmod,rid))
895 cr.execute('delete from ir_model_data where noupdate=%s and module=%s', (False, mod_name,))
898 # TODO: remove menu without actions of children
901 cr.execute('''delete from
904 (id not IN (select parent_id from ir_ui_menu where parent_id is not null))
906 (id not IN (select res_id from ir_values where model='ir.ui.menu'))
908 (id not IN (select res_id from ir_model_data where model='ir.ui.menu'))''')
913 logger.notifyChannel('init', netsvc.LOG_INFO, 'removed %d unused menus' % (cr.rowcount,))
915 cr.execute("update ir_module_module set state=%s where state=%s", ('uninstalled', 'to remove',))
921 # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: