diff options
| author | Numerigraphe - Lionel Sausin <ls@numerigraphe.fr> | 2010-10-25 09:43:13 +0200 |
|---|---|---|
| committer | Numerigraphe - Lionel Sausin <ls@numerigraphe.fr> | 2010-10-25 09:43:13 +0200 |
| commit | e3e8be4b0181018db564587d49f29ac907445ac2 (patch) | |
| tree | c516f6257fb9a432534eef61da14b916c91df38a /bin/addons/base/module/module.py | |
| parent | 25a424a89d32e4485ae1e120c1d8b2ada216800e (diff) | |
| parent | 3af170274bc03f844210d2eb1db16158e9056280 (diff) | |
[MERGE] merge from 6.0.0-RC1
bzr revid: ls@numerigraphe.fr-20101025074313-uaspq2f2y523my99
Diffstat (limited to 'bin/addons/base/module/module.py')
| -rw-r--r-- | bin/addons/base/module/module.py | 244 |
1 files changed, 162 insertions, 82 deletions
diff --git a/bin/addons/base/module/module.py b/bin/addons/base/module/module.py index 223a9ac1e42..3f4b59dd0e6 100644 --- a/bin/addons/base/module/module.py +++ b/bin/addons/base/module/module.py @@ -3,6 +3,7 @@ # # OpenERP, Open Source Management Solution # Copyright (C) 2004-2009 Tiny SPRL (<http://tiny.be>). +# Copyright (C) 2010 OpenERP s.a. (<http://openerp.com>). # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as @@ -18,31 +19,37 @@ # along with this program. If not, see <http://www.gnu.org/licenses/>. # ############################################################################## - -import tarfile +import imp +import logging +import os import re import urllib -import os -import tools -from osv import fields, osv, orm -import zipfile -import release import zipimport -import wizard import addons -import pooler import netsvc +import pooler +import release +import tools from tools.parse_version import parse_version from tools.translate import _ +from osv import fields, osv, orm + class module_category(osv.osv): _name = "ir.module.category" _description = "Module Category" def _module_nbr(self,cr,uid, ids, prop, unknow_none,context): - cr.execute('select category_id,count(*) from ir_module_module where category_id in ('+','.join(map(str, ids))+') or category_id in (select id from ir_module_category where parent_id in ('+','.join(map(str, ids))+')) group by category_id') + cr.execute('SELECT category_id, COUNT(*) \ + FROM ir_module_module \ + WHERE category_id IN %(ids)s \ + OR category_id IN (SELECT id \ + FROM ir_module_category \ + WHERE parent_id IN %(ids)s) \ + GROUP BY category_id', {'ids': tuple(ids)} + ) result = dict(cr.fetchall()) for id in ids: cr.execute('select id from ir_module_category where parent_id=%s', (id,)) @@ -62,6 +69,7 @@ module_category() class module(osv.osv): _name = "ir.module.module" _description = "Module" + __logger = logging.getLogger('base.' + _name) def get_module_info(self, name): info = {} @@ -69,8 +77,9 @@ class module(osv.osv): info = addons.load_information_from_description_file(name) if 'version' in info: info['version'] = release.major_version + '.' + info['version'] - except: - pass + except Exception: + self.__logger.debug('Error when trying to fetch informations for ' + 'module %s', name, exc_info=True) return info def _get_latest_version(self, cr, uid, ids, field_name=None, arg=None, context={}): @@ -101,23 +110,36 @@ class module(osv.osv): try: key = data_id['model'] if key=='ir.ui.view': - v = view_obj.browse(cr,uid,data_id.res_id) - aa = v.inherit_id and '* INHERIT ' or '' - res[mnames[data_id.module]]['views_by_module'] += aa + v.name + ' ('+v.type+')\n' + try: + v = view_obj.browse(cr,uid,data_id.res_id) + aa = v.inherit_id and '* INHERIT ' or '' + res[mnames[data_id.module]]['views_by_module'] += aa + v.name + ' ('+v.type+')\n' + except Exception: + self.__logger.debug( + 'Unknown error while browsing ir.ui.view[%s]', + data_id.res_id, exc_info=True) elif key=='ir.actions.report.xml': res[mnames[data_id.module]]['reports_by_module'] += report_obj.browse(cr,uid,data_id.res_id).name + '\n' elif key=='ir.ui.menu': - res[mnames[data_id.module]]['menus_by_module'] += menu_obj.browse(cr,uid,data_id.res_id).complete_name + '\n' - except KeyError, e: + try: + m = menu_obj.browse(cr,uid,data_id.res_id) + res[mnames[data_id.module]]['menus_by_module'] += m.complete_name + '\n' + except Exception: + self.__logger.debug( + 'Unknown error while browsing ir.ui.menu[%s]', + data_id.res_id, exc_info=True) + except KeyError: pass return res _columns = { - 'name': fields.char("Name", size=128, readonly=True, required=True), - 'category_id': fields.many2one('ir.module.category', 'Category', readonly=True), + 'name': fields.char("Name", size=128, readonly=True, required=True, select=True), + 'category_id': fields.many2one('ir.module.category', 'Category', readonly=True, select=True), 'shortdesc': fields.char('Short Description', size=256, readonly=True, translate=True), 'description': fields.text("Description", readonly=True, translate=True), 'author': fields.char("Author", size=128, readonly=True), + 'maintainer': fields.char('Maintainer', size=128, readonly=True), + 'contributors': fields.text('Contributors', readonly=True), 'website': fields.char("Website", size=256, readonly=True), # attention: Incorrect field names !! @@ -139,25 +161,29 @@ class module(osv.osv): ('to upgrade','To be upgraded'), ('to remove','To be removed'), ('to install','To be installed') - ], string='State', readonly=True), + ], string='State', readonly=True, select=True), 'demo': fields.boolean('Demo data'), 'license': fields.selection([ - ('GPL-2', 'GPL-2'), + ('GPL-2', 'GPL Version 2'), ('GPL-2 or any later version', 'GPL-2 or later version'), - ('GPL-3', 'GPL-3'), + ('GPL-3', 'GPL Version 3'), ('GPL-3 or any later version', 'GPL-3 or later version'), - ('Other proprietary', 'Other proprietary') + ('AGPL-3', 'Affero GPL-3'), + ('Other OSI approved licence', 'Other OSI Approved Licence'), + ('Other proprietary', 'Other Proprietary') ], string='License', readonly=True), 'menus_by_module': fields.function(_get_views, method=True, string='Menus', type='text', multi="meta", store=True), 'reports_by_module': fields.function(_get_views, method=True, string='Reports', type='text', multi="meta", store=True), 'views_by_module': fields.function(_get_views, method=True, string='Views', type='text', multi="meta", store=True), 'certificate' : fields.char('Quality Certificate', size=64, readonly=True), + 'web': fields.boolean('Has a web component', readonly=True), } _defaults = { 'state': lambda *a: 'uninstalled', 'demo': lambda *a: False, - 'license': lambda *a: 'GPL-2', + 'license': lambda *a: 'AGPL-3', + 'web': False, } _order = 'name' @@ -185,6 +211,27 @@ class module(osv.osv): return super(module, self).unlink(cr, uid, ids, context=context) + @staticmethod + def _check_external_dependencies(terp): + depends = terp.get('external_dependencies') + if not depends: + return + for pydep in depends.get('python', []): + parts = pydep.split('.') + parts.reverse() + path = None + while parts: + part = parts.pop() + try: + f, path, descr = imp.find_module(part, path and [path] or None) + except ImportError: + raise ImportError('No module named %s' % (pydep,)) + + for binary in depends.get('bin', []): + if tools.find_in_path(binary) is None: + raise Exception('Unable to find %r in path' % (binary,)) + + def state_update(self, cr, uid, ids, newstate, states_to_update, context={}, level=100): if level<1: raise orm.except_orm(_('Error'), _('Recursion error in modules dependencies !')) @@ -200,6 +247,12 @@ class module(osv.osv): else: od = self.browse(cr, uid, ids2)[0] mdemo = od.demo or mdemo + + terp = self.get_module_info(module.name) + try: + self._check_external_dependencies(terp) + except Exception, e: + raise orm.except_orm(_('Error'), _('Unable %s the module "%s" because an external dependencie is not met: %s' % (newstate, module.name, e.args[0]))) if not module.dependencies_id: mdemo = module.demo if module.state in states_to_update: @@ -273,6 +326,20 @@ class module(osv.osv): self.update_translations(cr, uid, ids) return True + @staticmethod + def get_values_from_terp(terp): + return { + 'description': terp.get('description', ''), + 'shortdesc': terp.get('name', ''), + 'author': terp.get('author', 'Unknown'), + 'maintainer': terp.get('maintainer', False), + 'contributors': ', '.join(terp.get('contributors', [])) or False, + 'website': terp.get('website', ''), + 'license': terp.get('license', 'GPL-2'), + 'certificate': terp.get('certificate') or None, + 'web': terp.get('web') or False, + } + # update the list of available packages def update_list(self, cr, uid, context={}): res = [0, 0] # [update, add] @@ -280,46 +347,31 @@ class module(osv.osv): # iterate through installed modules and mark them as being so for mod_name in addons.get_modules(): ids = self.search(cr, uid, [('name','=',mod_name)]) + terp = self.get_module_info(mod_name) + values = self.get_values_from_terp(terp) + if ids: id = ids[0] mod = self.browse(cr, uid, id) - terp = self.get_module_info(mod_name) if terp.get('installable', True) and mod.state == 'uninstallable': self.write(cr, uid, id, {'state': 'uninstalled'}) if parse_version(terp.get('version', '')) > parse_version(mod.latest_version or ''): - self.write(cr, uid, id, { 'url': ''}) + self.write(cr, uid, id, {'url': ''}) res[0] += 1 - self.write(cr, uid, id, { - 'description': terp.get('description', ''), - 'shortdesc': terp.get('name', ''), - 'author': terp.get('author', 'Unknown'), - 'website': terp.get('website', ''), - 'license': terp.get('license', 'GPL-2'), - 'certificate': terp.get('certificate') or None, - }) + self.write(cr, uid, id, values) cr.execute('DELETE FROM ir_module_module_dependency WHERE module_id = %s', (id,)) - self._update_dependencies(cr, uid, ids[0], terp.get('depends', [])) - self._update_category(cr, uid, ids[0], terp.get('category', 'Uncategorized')) - continue - mod_path = addons.get_module_path(mod_name) - if mod_path: - terp = self.get_module_info(mod_name) + else: + mod_path = addons.get_module_path(mod_name) + if not mod_path: + continue if not terp or not terp.get('installable', True): continue - id = self.create(cr, uid, { - 'name': mod_name, - 'state': 'uninstalled', - 'description': terp.get('description', ''), - 'shortdesc': terp.get('name', ''), - 'author': terp.get('author', 'Unknown'), - 'website': terp.get('website', ''), - 'license': terp.get('license', 'GPL-2'), - 'certificate': terp.get('certificate') or None, - }) + ids = self.search(cr, uid, [('name','=',mod_name)]) + id = self.create(cr, uid, dict(name=mod_name, state='uninstalled', **values)) res[1] += 1 - self._update_dependencies(cr, uid, id, terp.get('depends', [])) - self._update_category(cr, uid, id, terp.get('category', 'Uncategorized')) + self._update_dependencies(cr, uid, id, terp.get('depends', [])) + self._update_category(cr, uid, id, terp.get('category', 'Uncategorized')) return res @@ -343,17 +395,12 @@ class module(osv.osv): fp = file(fname, 'wb') fp.write(zipfile) fp.close() - except Exception, e: + except Exception: + self.__logger.exception('Error when trying to create module ' + 'file %s', fname) raise orm.except_orm(_('Error'), _('Can not create the module file:\n %s') % (fname,)) terp = self.get_module_info(mod.name) - self.write(cr, uid, mod.id, { - 'description': terp.get('description', ''), - 'shortdesc': terp.get('name', ''), - 'author': terp.get('author', 'Unknown'), - 'website': terp.get('website', ''), - 'license': terp.get('license', 'GPL-2'), - 'certificate': terp.get('certificate') or None, - }) + self.write(cr, uid, mod.id, self.get_values_from_terp(terp)) cr.execute('DELETE FROM ir_module_module_dependency ' \ 'WHERE module_id = %s', (mod.id,)) self._update_dependencies(cr, uid, mod.id, terp.get('depends', @@ -388,7 +435,7 @@ class module(osv.osv): categs = categs[1:] self.write(cr, uid, [id], {'category_id': p_id}) - def update_translations(self, cr, uid, ids, filter_lang=None): + def update_translations(self, cr, uid, ids, filter_lang=None, context={}): logger = netsvc.Logger() if not filter_lang: pool = pooler.get_pool(cr.dbname) @@ -415,34 +462,69 @@ class module(osv.osv): iso_lang = iso_lang.split('_')[0] if os.path.exists(f): logger.notifyChannel("i18n", netsvc.LOG_INFO, 'module %s: loading translation file for language %s' % (mod.name, iso_lang)) - tools.trans_load(cr.dbname, f, lang, verbose=False) + tools.trans_load(cr.dbname, f, lang, verbose=False, context=context) def check(self, cr, uid, ids, context=None): - logger = netsvc.Logger() + logger = logging.getLogger('init') for mod in self.browse(cr, uid, ids, context=context): if not mod.description: - logger.notifyChannel("init", netsvc.LOG_WARNING, 'module %s: description is empty !' % (mod.name,)) + logger.warn('module %s: description is empty !', mod.name) if not mod.certificate or not mod.certificate.isdigit(): - logger.notifyChannel('init', netsvc.LOG_WARNING, 'module %s: no quality certificate' % (mod.name,)) + logger.info('module %s: no quality certificate', mod.name) else: val = long(mod.certificate[2:]) % 97 == 29 if not val: - logger.notifyChannel('init', netsvc.LOG_CRITICAL, 'module %s: invalid quality certificate: %s' % (mod.name, mod.certificate)) + logger.critical('module %s: invalid quality certificate: %s', mod.name, mod.certificate) raise osv.except_osv(_('Error'), _('Module %s: Invalid Quality Certificate') % (mod.name,)) + def list_web(self, cr, uid, context=None): + """ list_web(cr, uid, context) -> [module_name] + Lists all the currently installed modules with a web component. + + Returns a list of addon names. + """ + return [ + module['name'] + for module in self.browse(cr, uid, + self.search(cr, uid, + [('web', '=', True), + ('state', 'in', ['installed','to upgrade','to remove'])], + context=context), + context=context)] + def _web_dependencies(self, cr, uid, module, context=None): + for dependency in module.dependencies_id: + (parent,) = self.browse(cr, uid, self.search(cr, uid, + [('name', '=', dependency.name)], context=context), + context=context) + if parent.web: + yield parent.name + else: + self._web_dependencies( + cr, uid, parent, context=context) + def get_web(self, cr, uid, names, context=None): + """ get_web(cr, uid, [module_name], context) -> [{name, depends, content}] + + Returns the web content of all the named addons. + + The toplevel directory of the zipped content is called 'web', + its final naming has to be managed by the client + """ + modules = self.browse(cr, uid, + self.search(cr, uid, [('name', 'in', names)], context=context), + context=context) + if not modules: return [] + self.__logger.info('Sending web content of modules %s ' + 'to web client', names) + return [ + {'name': module.name, + 'depends': list(self._web_dependencies( + cr, uid, module, context=context)), + 'content': addons.zip_directory( + addons.get_module_resource(module.name, 'web'))} + for module in modules + ] - def create(self, cr, uid, data, context={}): - id = super(module, self).create(cr, uid, data, context) - if data.get('name'): - self.pool.get('ir.model.data').create(cr, uid, { - 'name': 'module_meta_information', - 'model': 'ir.module.module', - 'res_id': id, - 'module': data['name'], - 'noupdate': True, - }) - return id module() class module_dependency(osv.osv): @@ -461,7 +543,7 @@ class module_dependency(osv.osv): return result _columns = { - 'name': fields.char('Name', size=128), + 'name': fields.char('Name', size=128, select=True), 'module_id': fields.many2one('ir.module.module', 'Module', select=True, ondelete='cascade'), 'state': fields.function(_state, method=True, type='selection', selection=[ ('uninstallable','Uninstallable'), @@ -471,8 +553,6 @@ class module_dependency(osv.osv): ('to remove','To be removed'), ('to install','To be installed'), ('unknown', 'Unknown'), - ], string='State', readonly=True), + ], string='State', readonly=True, select=True), } module_dependency() -# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: - |
