From d465346f1ee4d2399576f03fd5644f74191fc14b Mon Sep 17 00:00:00 2001 From: Raphael Collet Date: Mon, 2 May 2016 15:41:08 +0200 Subject: [PATCH] [MIG] base/module --- openerp/addons/base/module/module.py | 706 ++++++++---------- .../report/ir_module_reference_print.py | 35 +- .../module/wizard/base_export_language.py | 55 +- .../module/wizard/base_import_language.py | 67 +- .../module/wizard/base_language_install.py | 51 +- .../wizard/base_module_configuration.py | 44 +- .../base/module/wizard/base_module_update.py | 12 +- .../base/module/wizard/base_module_upgrade.py | 105 ++- .../module/wizard/base_update_translations.py | 71 +- 9 files changed, 518 insertions(+), 628 deletions(-) diff --git a/openerp/addons/base/module/module.py b/openerp/addons/base/module/module.py index d1adf9ad9a3..2860c8b0930 100644 --- a/openerp/addons/base/module/module.py +++ b/openerp/addons/base/module/module.py @@ -1,40 +1,31 @@ # -*- coding: utf-8 -*- # Part of Odoo. See LICENSE file for full copyright and licensing details. +from collections import defaultdict +from operator import attrgetter +import importlib +import logging +import os +import shutil +import tempfile +import urllib2 +import urlparse +import zipfile + from docutils import nodes from docutils.core import publish_string from docutils.transforms import Transform, writer_aux from docutils.writers.html4css1 import Writer -import importlib -import logging -from operator import attrgetter -import os -import re -import shutil -import tempfile -import urllib -import urllib2 -import urlparse -import zipfile -import zipimport import lxml.html -from openerp.exceptions import UserError try: from cStringIO import StringIO except ImportError: from StringIO import StringIO # NOQA -import openerp -import openerp.exceptions -from openerp import modules, tools -from openerp.modules.db import create_categories -from openerp.modules import get_module_resource -from openerp.tools import ormcache -from openerp.tools.parse_version import parse_version -from openerp.tools.translate import _ -from openerp.tools import html_sanitize -from openerp.osv import osv, orm, fields -from openerp import api, fields as fields2 +import odoo +from odoo import api, fields, models, modules, tools, _ +from odoo.exceptions import AccessDenied, UserError +from odoo.tools.parse_version import parse_version _logger = logging.getLogger(__name__) @@ -61,42 +52,45 @@ def backup(path, raise_exception=True): cnt += 1 -class module_category(osv.osv): +class ModuleCategory(models.Model): _name = "ir.module.category" _description = "Application" + _order = 'name' - def _module_nbr(self, cr, uid, ids, prop, unknow_none, context): + @api.depends('module_ids') + def _compute_module_nr(self): + cr = self._cr 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)} + GROUP BY category_id', {'ids': tuple(self.ids)} ) result = dict(cr.fetchall()) - for id in ids: - cr.execute('select id from ir_module_category where parent_id=%s', (id,)) - result[id] = sum([result.get(c, 0) for (c,) in cr.fetchall()], - result.get(id, 0)) - return result + for cat in self.filtered('id'): + cr.execute('SELECT id FROM ir_module_category WHERE parent_id=%s', (cat.id,)) + cat.module_nr = sum([result.get(c, 0) for (c,) in cr.fetchall()], result.get(cat.id, 0)) - _columns = { - 'name': fields.char("Name", required=True, translate=True, select=True), - 'parent_id': fields.many2one('ir.module.category', 'Parent Application', select=True), - 'child_ids': fields.one2many('ir.module.category', 'parent_id', 'Child Applications'), - 'module_nr': fields.function(_module_nbr, string='Number of Apps', type='integer'), - 'module_ids': fields.one2many('ir.module.module', 'category_id', 'Modules'), - 'description': fields.text("Description", translate=True), - 'sequence': fields.integer('Sequence'), - 'visible': fields.boolean('Visible'), - 'xml_id': fields.function(osv.osv.get_external_id, type='char', string="External ID"), - } - _order = 'name' + name = fields.Char(string='Name', required=True, translate=True, index=True) + parent_id = fields.Many2one('ir.module.category', string='Parent Application', index=True) + child_ids = fields.One2many('ir.module.category', 'parent_id', string='Child Applications') + module_nr = fields.Integer(string='Number of Apps', compute='_compute_module_nr') + module_ids = fields.One2many('ir.module.module', 'category_id', string='Modules') + description = fields.Text(string='Description', translate=True) + sequence = fields.Integer(string='Sequence') + visible = fields.Boolean(string='Visible', default=True) + xml_id = fields.Char(string='External ID', compute='_compute_xml_id') + + def _compute_xml_id(self): + xml_ids = defaultdict(list) + domain = [('model', '=', self._name), ('res_id', 'in', self.ids)] + for data in self.env['ir.model.data'].search_read(domain, ['module', 'name', 'res_id']): + xml_ids[data['res_id']].append("%s.%s" % (data['module'], data['name'])) + for cat in self: + cat.xml_id = xml_ids.get(cat.id, [''])[0] - _defaults = { - 'visible': 1, - } class MyFilterMessages(Transform): """ @@ -107,7 +101,6 @@ class MyFilterMessages(Transform): the `settings_override` dictionary, but if we use it, we can't see them and generate warnings.) """ - default_priority = 870 def apply(self): @@ -115,43 +108,52 @@ class MyFilterMessages(Transform): _logger.warning("docutils' system message present: %s", str(node)) node.parent.remove(node) + class MyWriter(Writer): """ Custom docutils html4ccs1 writer that doesn't add the warnings to the output document. """ - def get_transforms(self): return [MyFilterMessages, writer_aux.Admonitions] -class module(osv.osv): + +STATES = [ + ('uninstallable', 'Uninstallable'), + ('uninstalled', 'Not Installed'), + ('installed', 'Installed'), + ('to upgrade', 'To be upgraded'), + ('to remove', 'To be removed'), + ('to install', 'To be installed'), +] + +class Module(models.Model): _name = "ir.module.module" _rec_name = "shortdesc" _description = "Module" + _order = 'sequence,name' - def fields_view_get(self, cr, uid, view_id=None, view_type='form', context=None, toolbar=False, submenu=False): - res = super(module, self).fields_view_get(cr, uid, view_id=view_id, view_type=view_type, context=context, toolbar=toolbar, submenu=False) - result = self.pool.get('ir.model.data').get_object_reference(cr, uid, 'base', 'action_server_module_immediate_install')[1] - if view_type == 'form': - if res.get('toolbar',False): - list = [rec for rec in res['toolbar']['action'] if rec.get('id', False) != result] - res['toolbar'] = {'action': list} - return res + @api.model + def fields_view_get(self, view_id=None, view_type='form', toolbar=False, submenu=False): + res = super(Module, self).fields_view_get(view_id, view_type, toolbar=toolbar, submenu=False) + if view_type == 'form' and res.get('toolbar',False): + install_id = self.env.ref('base.action_server_module_immediate_install').id + action = [rec for rec in res['toolbar']['action'] if rec.get('id', False) != install_id] + res['toolbar'] = {'action': action} + return res @classmethod def get_module_info(cls, name): - info = {} try: - info = modules.load_information_from_description_file(name) + return modules.load_information_from_description_file(name) except Exception: - _logger.debug('Error when trying to fetch information for ' - 'module %s', name, exc_info=True) - return info + _logger.debug('Error when trying to fetch information for module %s', name, exc_info=True) + return {} - def _get_desc(self, cr, uid, ids, field_name=None, arg=None, context=None): - res = dict.fromkeys(ids, '') - for module in self.browse(cr, uid, ids, context=context): - path = get_module_resource(module.name, 'static/description/index.html') + @api.depends('name', 'description') + def _get_desc(self): + for module in self: + path = modules.get_module_resource(module.name, 'static/description/index.html') if path: with tools.file_open(path, 'rb') as desc_file: doc = desc_file.read() @@ -159,7 +161,7 @@ class module(osv.osv): for element, attribute, link, pos in html.iterlinks(): if element.get('src') and not '//' in element.get('src') and not 'static/' in element.get('src'): element.set('src', "/%s/static/description/%s" % (module.name, element.get('src'))) - res[module.id] = html_sanitize(lxml.html.tostring(html)) + module.description_html = tools.html_sanitize(lxml.html.tostring(html)) else: overrides = { 'embed_stylesheet': False, @@ -168,170 +170,115 @@ class module(osv.osv): 'xml_declaration': False, } output = publish_string(source=module.description or '', settings_overrides=overrides, writer=MyWriter()) - res[module.id] = html_sanitize(output) - return res + module.description_html = tools.html_sanitize(output) - def _get_latest_version(self, cr, uid, ids, field_name=None, arg=None, context=None): + @api.depends('name') + def _get_latest_version(self): default_version = modules.adapt_version('1.0') - res = dict.fromkeys(ids, default_version) - for m in self.browse(cr, uid, ids): - res[m.id] = self.get_module_info(m.name).get('version', default_version) - return res + for module in self: + module.installed_version = self.get_module_info(module.name).get('version', default_version) - def _get_views(self, cr, uid, ids, field_name=None, arg=None, context=None): - res = {} - model_data_obj = self.pool.get('ir.model.data') - - dmodels = [] - if field_name is None or 'views_by_module' in field_name: - dmodels.append('ir.ui.view') - if field_name is None or 'reports_by_module' in field_name: - dmodels.append('ir.actions.report.xml') - if field_name is None or 'menus_by_module' in field_name: - dmodels.append('ir.ui.menu') - assert dmodels, "no models for %s" % field_name - - for module_rec in self.browse(cr, uid, ids, context=context): - res_mod_dic = res[module_rec.id] = { - 'menus_by_module': [], - 'reports_by_module': [], - 'views_by_module': [] - } + @api.depends('name', 'state') + def _get_views(self): + IrModelData = self.env['ir.model.data'].with_context(active_test=True) + dmodels = ['ir.ui.view', 'ir.actions.report.xml', 'ir.ui.menu'] + for module in self: # Skip uninstalled modules below, no data to find anyway. - if module_rec.state not in ('installed', 'to upgrade', 'to remove'): + if module.state not in ('installed', 'to upgrade', 'to remove'): + module.views_by_module = "" + module.reports_by_module = "" + module.menus_by_module = "" continue # then, search and group ir.model.data records - imd_models = dict([(m, []) for m in dmodels]) - imd_ids = model_data_obj.search(cr, uid, [ - ('module', '=', module_rec.name), - ('model', 'in', tuple(dmodels)) - ]) - - for imd_res in model_data_obj.read(cr, uid, imd_ids, ['model', 'res_id'], context=context): - imd_models[imd_res['model']].append(imd_res['res_id']) + imd_models = defaultdict(list) + imd_domain = [('module', '=', module.name), ('model', 'in', tuple(dmodels))] + for data in IrModelData.search(imd_domain): + imd_models[data.model].append(data.res_id) def browse(model): - M = self.pool[model] - # as this method is called before the module update, some xmlid may be invalid at this stage - # explictly filter records before reading them - ids = M.exists(cr, uid, imd_models.get(model, []), context) - return M.browse(cr, uid, ids, context) + # as this method is called before the module update, some xmlid + # may be invalid at this stage; explictly filter records before + # reading them + return self.env[model].browse(imd_models[model]).exists() def format_view(v): - aa = v.inherit_id and '* INHERIT ' or '' - return '%s%s (%s)' % (aa, v.name, v.type) + return '%s%s (%s)' % (v.inherit_id and '* INHERIT ' or '', v.name, v.type) - res_mod_dic['views_by_module'] = map(format_view, browse('ir.ui.view')) - res_mod_dic['reports_by_module'] = map(attrgetter('name'), browse('ir.actions.report.xml')) - res_mod_dic['menus_by_module'] = map(attrgetter('complete_name'), browse('ir.ui.menu')) + module.views_by_module = "\n".join(sorted(map(format_view, browse('ir.ui.view')))) + module.reports_by_module = "\n".join(sorted(map(attrgetter('name'), browse('ir.actions.report.xml')))) + module.menus_by_module = "\n".join(sorted(map(attrgetter('complete_name'), browse('ir.ui.menu')))) - for key in res.iterkeys(): - for k, v in res[key].iteritems(): - res[key][k] = "\n".join(sorted(v)) - return res - - def _get_icon_image(self, cr, uid, ids, field_name=None, arg=None, context=None): - res = dict.fromkeys(ids, '') - for module in self.browse(cr, uid, ids, context=context): - path = get_module_resource(module.name, 'static', 'description', 'icon.png') + @api.depends('name') + def _get_icon_image(self): + for module in self: + module.icon_image = '' + path = modules.get_module_resource(module.name, 'static', 'description', 'icon.png') if path: - image_file = tools.file_open(path, 'rb') - try: - res[module.id] = image_file.read().encode('base64') - finally: - image_file.close() - return res + with tools.file_open(path, 'rb') as image_file: + module.icon_image = image_file.read().encode('base64') - _columns = { - 'name': fields.char("Technical Name", readonly=True, required=True, select=True), - 'category_id': fields.many2one('ir.module.category', 'Category', readonly=True, select=True), - 'shortdesc': fields.char('Module Name', readonly=True, translate=True), - 'summary': fields.char('Summary', readonly=True, translate=True), - 'description': fields.text("Description", readonly=True, translate=True), - 'description_html': fields.function(_get_desc, string='Description HTML', type='html', method=True, readonly=True), - 'author': fields.char("Author", readonly=True), - 'maintainer': fields.char('Maintainer', readonly=True), - 'contributors': fields.text('Contributors', readonly=True), - 'website': fields.char("Website", readonly=True), + name = fields.Char('Technical Name', readonly=True, required=True, index=True) + category_id = fields.Many2one('ir.module.category', string='Category', readonly=True, index=True) + shortdesc = fields.Char('Module Name', readonly=True, translate=True) + summary = fields.Char('Summary', readonly=True, translate=True) + description = fields.Text('Description', readonly=True, translate=True) + description_html = fields.Html('Description HTML', compute='_get_desc') + author = fields.Char("Author", readonly=True) + maintainer = fields.Char('Maintainer', readonly=True) + contributors = fields.Text('Contributors', readonly=True) + website = fields.Char("Website", readonly=True) - # attention: Incorrect field names !! - # installed_version refers the latest version (the one on disk) - # latest_version refers the installed version (the one in database) - # published_version refers the version available on the repository - 'installed_version': fields.function(_get_latest_version, string='Latest Version', type='char'), - 'latest_version': fields.char('Installed Version', readonly=True), - 'published_version': fields.char('Published Version', readonly=True), + # attention: Incorrect field names !! + # installed_version refers the latest version (the one on disk) + # latest_version refers the installed version (the one in database) + # published_version refers the version available on the repository + installed_version = fields.Char('Latest Version', compute='_get_latest_version') + latest_version = fields.Char('Installed Version', readonly=True) + published_version = fields.Char('Published Version', readonly=True) - 'url': fields.char('URL', readonly=True), - 'sequence': fields.integer('Sequence'), - 'dependencies_id': fields.one2many('ir.module.module.dependency', 'module_id', 'Dependencies', readonly=True), - 'auto_install': fields.boolean('Automatic Installation', - help='An auto-installable module is automatically installed by the ' - 'system when all its dependencies are satisfied. ' - 'If the module has no dependency, it is always installed.'), - 'state': fields.selection([ - ('uninstallable', 'Not Installable'), - ('uninstalled', 'Not Installed'), - ('installed', 'Installed'), - ('to upgrade', 'To be upgraded'), - ('to remove', 'To be removed'), - ('to install', 'To be installed') - ], string='Status', readonly=True, select=True), - 'demo': fields.boolean('Demo Data', readonly=True), - 'license': fields.selection([ - ('GPL-2', 'GPL Version 2'), - ('GPL-2 or any later version', 'GPL-2 or later version'), - ('GPL-3', 'GPL Version 3'), - ('GPL-3 or any later version', 'GPL-3 or later version'), - ('AGPL-3', 'Affero GPL-3'), - ('LGPL-3', 'LGPL Version 3'), - ('Other OSI approved licence', 'Other OSI Approved Licence'), - ('OEEL-1', 'Odoo Enterprise Edition License v1.0'), - ('Other proprietary', 'Other Proprietary') - ], string='License', readonly=True), - 'menus_by_module': fields.function(_get_views, string='Menus', type='text', multi="meta", store=True), - 'reports_by_module': fields.function(_get_views, string='Reports', type='text', multi="meta", store=True), - 'views_by_module': fields.function(_get_views, string='Views', type='text', multi="meta", store=True), - 'application': fields.boolean('Application', readonly=True), - 'icon': fields.char('Icon URL'), - 'icon_image': fields.function(_get_icon_image, string='Icon', type="binary"), - } - - _defaults = { - 'state': 'uninstalled', - 'sequence': 100, - 'demo': False, - 'license': 'LGPL-3', - } - _order = 'sequence,name' - - def _name_uniq_msg(self, cr, uid, ids, context=None): - return _('The name of the module must be unique !') + url = fields.Char('URL', readonly=True) + sequence = fields.Integer('Sequence', default=100) + dependencies_id = fields.One2many('ir.module.module.dependency', 'module_id', + string='Dependencies', readonly=True) + auto_install = fields.Boolean('Automatic Installation', + help='An auto-installable module is automatically installed by the ' + 'system when all its dependencies are satisfied. ' + 'If the module has no dependency, it is always installed.') + state = fields.Selection(STATES, string='Status', default='uninstalled', readonly=True, index=True) + demo = fields.Boolean('Demo Data', default=False, readonly=True) + license = fields.Selection([ + ('GPL-2', 'GPL Version 2'), + ('GPL-2 or any later version', 'GPL-2 or later version'), + ('GPL-3', 'GPL Version 3'), + ('GPL-3 or any later version', 'GPL-3 or later version'), + ('AGPL-3', 'Affero GPL-3'), + ('LGPL-3', 'LGPL Version 3'), + ('Other OSI approved licence', 'Other OSI Approved Licence'), + ('OEEL-1', 'Odoo Enterprise Edition License v1.0'), + ('Other proprietary', 'Other Proprietary') + ], string='License', default='LGPL-3', readonly=True) + menus_by_module = fields.Text(string='Menus', compute='_get_views', store=True) + reports_by_module = fields.Text(string='Reports', compute='_get_views', store=True) + views_by_module = fields.Text(string='Views', compute='_get_views', store=True) + application = fields.Boolean('Application', readonly=True) + icon = fields.Char('Icon URL') + icon_image = fields.Binary(string='Icon', compute='_get_icon_image') _sql_constraints = [ - ('name_uniq', 'UNIQUE (name)', _name_uniq_msg), + ('name_uniq', 'UNIQUE (name)', 'The name of the module must be unique!'), ] - def unlink(self, cr, uid, ids, context=None): - if not ids: + @api.multi + def unlink(self): + if not self: return True - if isinstance(ids, (int, long)): - ids = [ids] - mod_names = [] - for mod in self.read(cr, uid, ids, ['state', 'name'], context): - if mod['state'] in ('installed', 'to upgrade', 'to remove', 'to install'): + for module in self: + if module.state in ('installed', 'to upgrade', 'to remove', 'to install'): raise UserError(_('You try to remove a module that is installed or will be installed')) - mod_names.append(mod['name']) - #Removing the entry from ir_model_data - #ids_meta = self.pool.get('ir.model.data').search(cr, uid, [('name', '=', 'module_meta_information'), ('module', 'in', mod_names)]) - - #if ids_meta: - # self.pool.get('ir.model.data').unlink(cr, uid, ids_meta, context) - self.clear_caches() - return super(module, self).unlink(cr, uid, ids, context=context) + return super(Module, self).unlink() @staticmethod def _check_external_dependencies(terp): @@ -447,163 +394,166 @@ class module(osv.osv): ) % ("\n".join(themes_list))) return dict(ACTION_DICT, name=_('Install')) - def button_immediate_install(self, cr, uid, ids, context=None): + @api.multi + def button_immediate_install(self): """ Installs the selected module(s) immediately and fully, returns the next res.config action to execute - :param ids: identifiers of the modules to install :returns: next res.config item to execute :rtype: dict[str, object] """ - return self._button_immediate_function(cr, uid, ids, self.button_install, context=context) + return self._button_immediate_function(type(self).button_install) - def button_install_cancel(self, cr, uid, ids, context=None): - self.write(cr, uid, ids, {'state': 'uninstalled', 'demo': False}) + @api.multi + def button_install_cancel(self): + self.write({'state': 'uninstalled', 'demo': False}) return True - def module_uninstall(self, cr, uid, ids, context=None): - """Perform the various steps required to uninstall a module completely + @api.multi + def module_uninstall(self): + """ Perform the various steps required to uninstall a module completely including the deletion of all database structures created by the module: - tables, columns, constraints, etc.""" - ir_model_data = self.pool.get('ir.model.data') - modules_to_remove = [m.name for m in self.browse(cr, uid, ids, context)] - ir_model_data._module_data_uninstall(cr, uid, modules_to_remove, context) - self.write(cr, uid, ids, {'state': 'uninstalled', 'latest_version': False}) + tables, columns, constraints, etc. + """ + modules_to_remove = self.mapped('name') + self.env['ir.model.data']._module_data_uninstall(modules_to_remove) + self.write({'state': 'uninstalled', 'latest_version': False}) return True - def downstream_dependencies(self, cr, uid, ids, known_dep_ids=None, - exclude_states=['uninstalled', 'uninstallable', 'to remove'], - context=None): - """Return the ids of all modules that directly or indirectly depend - on the given module `ids`, and that satisfy the `exclude_states` - filter""" - if not ids: - return [] - known_dep_ids = set(known_dep_ids or []) - cr.execute('''SELECT DISTINCT m.id - FROM - ir_module_module_dependency d - JOIN - ir_module_module m ON (d.module_id=m.id) - WHERE - d.name IN (SELECT name from ir_module_module where id in %s) AND - m.state NOT IN %s AND - m.id NOT IN %s ''', - (tuple(ids), tuple(exclude_states), tuple(known_dep_ids or ids))) - new_dep_ids = set([m[0] for m in cr.fetchall()]) - missing_mod_ids = new_dep_ids - known_dep_ids - known_dep_ids |= new_dep_ids - if missing_mod_ids: - known_dep_ids |= set(self.downstream_dependencies(cr, uid, list(missing_mod_ids), - known_dep_ids, exclude_states, context)) - return list(known_dep_ids) + @api.multi + @api.returns('self') + def downstream_dependencies(self, known_deps=None, + exclude_states=('uninstalled', 'uninstallable', 'to remove')): + """ Return the modules that directly or indirectly depend on the modules + in `self`, and that satisfy the `exclude_states` filter. + """ + if not self: + return self + known_deps = known_deps or self.browse() + query = """ SELECT DISTINCT m.id + FROM ir_module_module_dependency d + JOIN ir_module_module m ON (d.module_id=m.id) + WHERE + d.name IN (SELECT name from ir_module_module where id in %s) AND + m.state NOT IN %s AND + m.id NOT IN %s """ + self._cr.execute(query, (tuple(self.ids), tuple(exclude_states), tuple(known_deps.ids or self.ids))) + new_deps = self.browse([row[0] for row in self._cr.fetchall()]) + missing_mods = new_deps - known_deps + known_deps |= new_deps + if missing_mods: + known_deps |= missing_mods.downstream_dependencies(known_deps, exclude_states) + return known_deps - def upstream_dependencies(self, cr, uid, ids, known_dep_ids=None, - exclude_states=['installed', 'uninstallable', 'to remove'], - context=None): - """ Return the dependency tree of modules of the given `ids`, and that - satisfy the `exclude_states` filter """ - if not ids: - return [] - known_dep_ids = set(known_dep_ids or []) - cr.execute('''SELECT DISTINCT m.id - FROM - ir_module_module_dependency d - JOIN - ir_module_module m ON (d.module_id=m.id) - WHERE - m.name IN (SELECT name from ir_module_module_dependency where module_id in %s) AND - m.state NOT IN %s AND - m.id NOT IN %s ''', - (tuple(ids), tuple(exclude_states), tuple(known_dep_ids or ids))) - new_dep_ids = set([m[0] for m in cr.fetchall()]) - missing_mod_ids = new_dep_ids - known_dep_ids - known_dep_ids |= new_dep_ids - if missing_mod_ids: - known_dep_ids |= set(self.upstream_dependencies(cr, uid, list(missing_mod_ids), - known_dep_ids, exclude_states, context)) - return list(known_dep_ids) + @api.multi + @api.returns('self') + def upstream_dependencies(self, known_deps=None, + exclude_states=('installed', 'uninstallable', 'to remove')): + """ Return the dependency tree of modules of the modules in `self`, and + that satisfy the `exclude_states` filter. + """ + if not self: + return self + known_deps = known_deps or self.browse() + query = """ SELECT DISTINCT m.id + FROM ir_module_module_dependency d + JOIN ir_module_module m ON (d.module_id=m.id) + WHERE + m.name IN (SELECT name from ir_module_module_dependency where module_id in %s) AND + m.state NOT IN %s AND + m.id NOT IN %s """ + self._cr.execute(query, (tuple(self.ids), tuple(exclude_states), tuple(known_deps.ids or self.ids))) + new_deps = self.browse([row[0] for row in self._cr.fetchall()]) + missing_mods = new_deps - known_deps + known_deps |= new_deps + if missing_mods: + known_deps |= missing_mods.upstream_dependencies(known_deps, exclude_states) + return known_deps - def _button_immediate_function(self, cr, uid, ids, function, context=None): - function(cr, uid, ids, context=context) + @api.multi + def _button_immediate_function(self, function): + function(self) - cr.commit() + self._cr.commit() api.Environment.reset() - registry = openerp.modules.registry.RegistryManager.new(cr.dbname, update_module=True) + modules.registry.RegistryManager.new(self._cr.dbname, update_module=True) - cr.commit() - config = registry['res.config'].next(cr, uid, [], context=context) or {} + self._cr.commit() + env = api.Environment(self._cr, self._uid, self._context) + config = env['res.config'].next() or {} if config.get('type') not in ('ir.actions.act_window_close',): return config # reload the client; open the first available root menu - menu_obj = registry['ir.ui.menu'] - menu_ids = menu_obj.search(cr, uid, [('parent_id', '=', False)], context=context) + menu = env['ir.ui.menu'].search([('parent_id', '=', False)])[:1] return { 'type': 'ir.actions.client', 'tag': 'reload', - 'params': {'menu_id': menu_ids and menu_ids[0] or False} + 'params': {'menu_id': menu.id}, } - def button_immediate_uninstall(self, cr, uid, ids, context=None): + @api.multi + def button_immediate_uninstall(self): """ Uninstall the selected module(s) immediately and fully, returns the next res.config action to execute """ - return self._button_immediate_function(cr, uid, ids, self.button_uninstall, context=context) + return self._button_immediate_function(type(self).button_uninstall) - def button_uninstall(self, cr, uid, ids, context=None): - if any(m.name == 'base' for m in self.browse(cr, uid, ids, context=context)): + @api.multi + def button_uninstall(self): + if 'base' in self.mapped('name'): raise UserError(_("The `base` module cannot be uninstalled")) - dep_ids = self.downstream_dependencies(cr, uid, ids, context=context) - self.write(cr, uid, ids + dep_ids, {'state': 'to remove'}) + deps = self.downstream_dependencies() + (self + deps).write({'state': 'to remove'}) return dict(ACTION_DICT, name=_('Uninstall')) - def button_uninstall_cancel(self, cr, uid, ids, context=None): - self.write(cr, uid, ids, {'state': 'installed'}) + @api.multi + def button_uninstall_cancel(self): + self.write({'state': 'installed'}) return True - def button_immediate_upgrade(self, cr, uid, ids, context=None): + @api.multi + def button_immediate_upgrade(self): """ Upgrade the selected module(s) immediately and fully, return the next res.config action to execute """ - return self._button_immediate_function(cr, uid, ids, self.button_upgrade, context=context) + return self._button_immediate_function(type(self).button_upgrade) - def button_upgrade(self, cr, uid, ids, context=None): - depobj = self.pool.get('ir.module.module.dependency') - todo = list(self.browse(cr, uid, ids, context=context)) - self.update_list(cr, uid) + @api.multi + def button_upgrade(self): + Dependency = self.env['ir.module.module.dependency'] + self.update_list() + todo = list(self) i = 0 while i < len(todo): - mod = todo[i] + module = todo[i] i += 1 - if mod.state not in ('installed', 'to upgrade'): - raise UserError(_("Can not upgrade module '%s'. It is not installed.") % (mod.name,)) - self.check_external_dependencies(mod.name, 'to upgrade') - iids = depobj.search(cr, uid, [('name', '=', mod.name)], context=context) - for dep in depobj.browse(cr, uid, iids, context=context): + if module.state not in ('installed', 'to upgrade'): + raise UserError(_("Can not upgrade module '%s'. It is not installed.") % (module.name,)) + self.check_external_dependencies(module.name, 'to upgrade') + for dep in Dependency.search([('name', '=', module.name)]): if dep.module_id.state == 'installed' and dep.module_id not in todo: todo.append(dep.module_id) - ids = map(lambda x: x.id, todo) - self.write(cr, uid, ids, {'state': 'to upgrade'}, context=context) + self.browse(module.id for module in todo).write({'state': 'to upgrade'}) to_install = [] - for mod in todo: - for dep in mod.dependencies_id: + for module in todo: + for dep in module.dependencies_id: if dep.state == 'unknown': raise UserError(_('You try to upgrade a module that depends on the module: %s.\nBut this module is not available in your system.') % (dep.name,)) if dep.state == 'uninstalled': - ids2 = self.search(cr, uid, [('name', '=', dep.name)]) - to_install.extend(ids2) + to_install += self.search([('name', '=', dep.name)]).ids - self.button_install(cr, uid, to_install, context=context) + self.browse(to_install).button_install() return dict(ACTION_DICT, name=_('Apply Schedule Upgrade')) - def button_upgrade_cancel(self, cr, uid, ids, context=None): - self.write(cr, uid, ids, {'state': 'installed'}) + @api.multi + def button_upgrade_cancel(self): + self.write({'state': 'installed'}) return True @staticmethod @@ -624,25 +574,27 @@ class module(osv.osv): 'url': terp.get('url') or terp.get('live_test_url', ''), } - def create(self, cr, uid, vals, context=None): - new_id = super(module, self).create(cr, uid, vals, context=context) + @api.model + def create(self, vals): + new = super(Module, self).create(vals) module_metadata = { 'name': 'module_%s' % vals['name'], 'model': 'ir.module.module', 'module': 'base', - 'res_id': new_id, + 'res_id': new.id, 'noupdate': True, } - self.pool['ir.model.data'].create(cr, uid, module_metadata) - return new_id + self.env['ir.model.data'].create(module_metadata) + return new # update the list of available packages - def update_list(self, cr, uid, context=None): + @api.model + def update_list(self): res = [0, 0] # [update, add] default_version = modules.adapt_version('1.0') - known_mods = self.browse(cr, uid, self.search(cr, uid, [])) - known_mods_names = dict([(m.name, m) for m in known_mods]) + known_mods = self.search([]) + known_mods_names = {mod.name: mod for mod in known_mods} # iterate through detected modules and update/create them in db for mod_name in modules.get_modules(): @@ -654,7 +606,7 @@ class module(osv.osv): updated_values = {} for key in values: old = getattr(mod, key) - updated = isinstance(values[key], basestring) and tools.ustr(values[key]) or values[key] + updated = tools.ustr(values[key]) if isinstance(values[key], basestring) else values[key] if (old or updated) and updated != old: updated_values[key] = values[key] if terp.get('installable', True) and mod.state == 'uninstallable': @@ -662,43 +614,44 @@ class module(osv.osv): if parse_version(terp.get('version', default_version)) > parse_version(mod.latest_version or default_version): res[0] += 1 if updated_values: - self.write(cr, uid, mod.id, updated_values) + mod.write(updated_values) else: mod_path = modules.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, dict(name=mod_name, state='uninstalled', **values)) - mod = self.browse(cr, uid, id) + mod = self.create(dict(name=mod_name, state='uninstalled', **values)) res[1] += 1 - self._update_dependencies(cr, uid, mod, terp.get('depends', [])) - self._update_category(cr, uid, mod, terp.get('category', 'Uncategorized')) + mod._update_dependencies(terp.get('depends', [])) + mod._update_category(terp.get('category', 'Uncategorized')) return res - def download(self, cr, uid, ids, download=True, context=None): + @api.multi + def download(self, download=True): return [] - def install_from_urls(self, cr, uid, urls, context=None): - if not self.pool['res.users'].has_group(cr, uid, 'base.group_system'): - raise openerp.exceptions.AccessDenied() + @api.model + def install_from_urls(self, urls): + if not self.env.user.has_group('base.group_system'): + raise AccessDenied() - apps_server = urlparse.urlparse(self.get_apps_server(cr, uid, context=context)) + apps_server = urlparse.urlparse(self.get_apps_server()) - OPENERP = openerp.release.product_name.lower() + OPENERP = odoo.release.product_name.lower() tmp = tempfile.mkdtemp() _logger.debug('Install from url: %r', urls) try: # 1. Download & unzip missing modules - for module_name, url in urls.items(): + for module_name, url in urls.iteritems(): if not url: continue # nothing to download, local version is already the last one up = urlparse.urlparse(url) if up.scheme != apps_server.scheme or up.netloc != apps_server.netloc: - raise openerp.exceptions.AccessDenied() + raise AccessDenied() try: _logger.info('Downloading module `%s` from OpenERP Apps', module_name) @@ -711,7 +664,7 @@ class module(osv.osv): assert os.path.isdir(os.path.join(tmp, module_name)) # 2a. Copy/Replace module source in addons path - for module_name, url in urls.items(): + for module_name, url in urls.iteritems(): if module_name == OPENERP or not url: continue # OPENERP is special case, handled below, and no URL means local module module_path = modules.get_module_path(module_name, downloaded=True, display_warning=False) @@ -722,7 +675,7 @@ class module(osv.osv): shutil.rmtree(bck) # 2b. Copy/Replace server+base module source if downloaded - if urls.get(OPENERP, None): + if urls.get(OPENERP): # special case. it contains the server and the base module. # extract path is not the same base_path = os.path.dirname(modules.get_module_path('base')) @@ -734,51 +687,51 @@ class module(osv.osv): shutil.copytree(os.path.join(base_path, d), destdir) # then replace the server by the new "base" module - server_dir = openerp.tools.config['root_path'] # XXX or dirname() + server_dir = tools.config['root_path'] # XXX or dirname() bck = backup(server_dir) _logger.info('Copy downloaded module `openerp` to `%s`', server_dir) shutil.move(os.path.join(tmp, OPENERP), server_dir) #if bck: # shutil.rmtree(bck) - self.update_list(cr, uid, context=context) + self.update_list() - with_urls = [m for m, u in urls.items() if u] - downloaded_ids = self.search(cr, uid, [('name', 'in', with_urls)], context=context) - already_installed = self.search(cr, uid, [('id', 'in', downloaded_ids), ('state', '=', 'installed')], context=context) + with_urls = [module_name for module_name, url in urls.iteritems() if url] + downloaded = self.search([('name', 'in', with_urls)]) + installed = self.search([('id', 'in', downloaded.ids), ('state', '=', 'installed')]) - to_install_ids = self.search(cr, uid, [('name', 'in', urls.keys()), ('state', '=', 'uninstalled')], context=context) - post_install_action = self.button_immediate_install(cr, uid, to_install_ids, context=context) + to_install = self.search([('name', 'in', urls.keys()), ('state', '=', 'uninstalled')]) + post_install_action = to_install.button_immediate_install() - if already_installed: + if installed: # in this case, force server restart to reload python code... - cr.commit() - openerp.service.server.restart() + self._cr.commit() + odoo.service.server.restart() return { 'type': 'ir.actions.client', 'tag': 'home', 'params': {'wait': True}, } return post_install_action + finally: shutil.rmtree(tmp) - def get_apps_server(self, cr, uid, context=None): + @api.model + def get_apps_server(self): return tools.config.get('apps_server', 'https://apps.openerp.com/apps') - def _update_dependencies(self, cr, uid, mod_browse, depends=None): - if depends is None: - depends = [] - existing = set(x.name for x in mod_browse.dependencies_id) - needed = set(depends) + def _update_dependencies(self, depends=None): + existing = set(dep.name for dep in self.dependencies_id) + needed = set(depends or []) for dep in (needed - existing): - cr.execute('INSERT INTO ir_module_module_dependency (module_id, name) values (%s, %s)', (mod_browse.id, dep)) + self._cr.execute('INSERT INTO ir_module_module_dependency (module_id, name) values (%s, %s)', (self.id, dep)) for dep in (existing - needed): - cr.execute('DELETE FROM ir_module_module_dependency WHERE module_id = %s and name = %s', (mod_browse.id, dep)) - self.invalidate_cache(cr, uid, ['dependencies_id'], [mod_browse.id]) + self._cr.execute('DELETE FROM ir_module_module_dependency WHERE module_id = %s and name = %s', (self.id, dep)) + self.invalidate_cache(['dependencies_id'], self.ids) - def _update_category(self, cr, uid, mod_browse, category='Uncategorized'): - current_category = mod_browse.category_id + def _update_category(self, category='Uncategorized'): + current_category = self.category_id current_category_path = [] while current_category: current_category_path.insert(0, current_category.name) @@ -786,26 +739,27 @@ class module(osv.osv): categs = category.split('/') if categs != current_category_path: - cat_id = create_categories(cr, categs) - mod_browse.write({'category_id': cat_id}) + cat_id = modules.db.create_categories(self._cr, categs) + self.write({'category_id': cat_id}) - def update_translations(self, cr, uid, ids, filter_lang=None, context=None): + @api.multi + def update_translations(self, filter_lang=None): if not filter_lang: - res_lang = self.pool.get('res.lang') - lang_ids = res_lang.search(cr, uid, [('translatable', '=', True)]) - filter_lang = [lang.code for lang in res_lang.browse(cr, uid, lang_ids)] + langs = self.env['res.lang'].search([('translatable', '=', True)]) + filter_lang = [lang.code for lang in langs] elif not isinstance(filter_lang, (list, tuple)): filter_lang = [filter_lang] - modules = [m.name for m in self.browse(cr, uid, ids) if m.state in ('installed', 'to install', 'to upgrade')] - self.pool.get('ir.translation').load_module_terms(cr, modules, filter_lang, context=context) + mod_names = [mod.name for mod in self if mod.state in ('installed', 'to install', 'to upgrade')] + self.env['ir.translation'].load_module_terms(mod_names, filter_lang) - def check(self, cr, uid, ids, context=None): - for mod in self.browse(cr, uid, ids, context=context): - if not mod.description: - _logger.warning('module %s: description is empty !', mod.name) + @api.multi + def check(self): + for module in self: + if not module.description: + _logger.warning('module %s: description is empty !', module.name) @api.model - @ormcache() + @tools.ormcache() def _installed(self): """ Return the set of installed modules as a dictionary {name: id} """ return { @@ -814,29 +768,21 @@ class module(osv.osv): } -DEP_STATES = [ - ('uninstallable', 'Uninstallable'), - ('uninstalled', 'Not Installed'), - ('installed', 'Installed'), - ('to upgrade', 'To be upgraded'), - ('to remove', 'To be removed'), - ('to install', 'To be installed'), - ('unknown', 'Unknown'), -] +DEP_STATES = STATES + [('unknown', 'Unknown')] -class module_dependency(osv.Model): +class ModuleDependency(models.Model): _name = "ir.module.module.dependency" _description = "Module dependency" # the dependency name - name = fields2.Char(index=True) + name = fields.Char(index=True) # the module that depends on it - module_id = fields2.Many2one('ir.module.module', 'Module', ondelete='cascade') + module_id = fields.Many2one('ir.module.module', 'Module', ondelete='cascade') # the module corresponding to the dependency, and its status - depend_id = fields2.Many2one('ir.module.module', 'Dependency', compute='_compute_depend') - state = fields2.Selection(DEP_STATES, string='Status', compute='_compute_state') + depend_id = fields.Many2one('ir.module.module', 'Dependency', compute='_compute_depend') + state = fields.Selection(DEP_STATES, string='Status', compute='_compute_state') @api.multi @api.depends('name') diff --git a/openerp/addons/base/module/report/ir_module_reference_print.py b/openerp/addons/base/module/report/ir_module_reference_print.py index 881655294b9..c4c855e4c36 100644 --- a/openerp/addons/base/module/report/ir_module_reference_print.py +++ b/openerp/addons/base/module/report/ir_module_reference_print.py @@ -1,42 +1,39 @@ # -*- coding: utf-8 -*- # Part of Odoo. See LICENSE file for full copyright and licensing details. -from openerp import models, api +from odoo import api, models class IrModelReferenceReport(models.AbstractModel): _name = 'report.base.report_irmodulereference' @api.model def _object_find(self, module): - model_data_obj = self.env['ir.model.data'] - models_data = model_data_obj.search([('model','=','ir.model'), ('module','=',module.name)]) - res_ids = [model_data.res_id for model_data in models_data] + Data = self.env['ir.model.data'] + data = Data.search([('model','=','ir.model'), ('module','=',module.name)]) + res_ids = data.mapped('res_id') return self.env['ir.model'].browse(res_ids) @api.multi def _fields_find(self, model, module): - res = [] - model_obj = self.env[model] - model_fields = self.env['ir.model.fields'] - fname_wildcard = 'field_' + model_obj._name.replace('.', '_') + '_%' - module_fields_ids = self.env['ir.model.data'].search([('model', '=', 'ir.model.fields'), ('module', '=', module.name), ('name', 'like', fname_wildcard)]) - module_fields_res_ids = [x.res_id for x in module_fields_ids] - if module_fields_res_ids: - module_fields_names = [x.name for x in model_fields.browse(module_fields_res_ids)] - res = model_obj.fields_get(allfields=module_fields_names).items() - res.sort() - return res + Data = self.env['ir.model.data'] + fname_wildcard = 'field_' + model.replace('.', '_') + '_%' + data = Data.search([('model', '=', 'ir.model.fields'), ('module', '=', module.name), ('name', 'like', fname_wildcard)]) + if data: + res_ids = data.mapped('res_ids') + fnames = self.env['ir.model.fields'].browse(res_ids).mapped('name') + return sorted(self.env[model].fields_get(fnames).iteritems()) + return [] @api.multi def render_html(self, data=None): - report_obj = self.env['report'] - module_report = report_obj._get_report_from_name('base.report_irmodulereference') + Report = self.env['report'] + report = Report._get_report_from_name('base.report_irmodulereference') selected_modules = self.env['ir.module.module'].browse(self.ids) docargs = { 'doc_ids': self.ids, - 'doc_model': module_report.model, + 'doc_model': report.model, 'docs': selected_modules, 'findobj': self._object_find, 'findfields': self._fields_find, } - return report_obj.render('base.report_irmodulereference', docargs) \ No newline at end of file + return Report.render('base.report_irmodulereference', docargs) diff --git a/openerp/addons/base/module/wizard/base_export_language.py b/openerp/addons/base/module/wizard/base_export_language.py index b2fa8077481..cd19222af14 100644 --- a/openerp/addons/base/module/wizard/base_export_language.py +++ b/openerp/addons/base/module/wizard/base_export_language.py @@ -5,58 +5,49 @@ import base64 import contextlib import cStringIO -from openerp import tools -from openerp.osv import fields,osv -from openerp.tools.translate import _ -from openerp.tools.misc import get_iso_codes +from odoo import api, fields, models, tools, _ NEW_LANG_KEY = '__new__' -class base_language_export(osv.osv_memory): +class BaseLanguageExport(models.TransientModel): _name = "base.language.export" - def _get_languages(self, cr, uid, context): - lang_obj = self.pool.get('res.lang') - ids = lang_obj.search(cr, uid, [('translatable', '=', True)]) - langs = lang_obj.browse(cr, uid, ids) - return [(NEW_LANG_KEY, _('New Language (Empty translation template)'))] + [(lang.code, lang.name) for lang in langs] + @api.model + def _get_languages(self): + langs = self.env['res.lang'].search([('translatable', '=', True)]) + return [(NEW_LANG_KEY, _('New Language (Empty translation template)'))] + \ + [(lang.code, lang.name) for lang in langs] - _columns = { - 'name': fields.char('File Name', readonly=True), - 'lang': fields.selection(_get_languages, 'Language', required=True), - 'format': fields.selection([('csv','CSV File'), - ('po','PO File'), - ('tgz', 'TGZ Archive')], 'File Format', required=True), - 'modules': fields.many2many('ir.module.module', 'rel_modules_langexport', 'wiz_id', 'module_id', 'Apps To Export', domain=[('state','=','installed')]), - 'data': fields.binary('File', readonly=True), - 'state': fields.selection([('choose', 'choose'), # choose language - ('get', 'get')]) # get the file - } - _defaults = { - 'state': 'choose', - 'lang': NEW_LANG_KEY, - 'format': 'csv', - } + name = fields.Char('File Name', readonly=True) + lang = fields.Selection(_get_languages, string='Language', required=True, default=NEW_LANG_KEY) + format = fields.Selection([('csv','CSV File'), ('po','PO File'), ('tgz', 'TGZ Archive')], + string='File Format', required=True, default='csv') + modules = fields.Many2many('ir.module.module', 'rel_modules_langexport', 'wiz_id', 'module_id', + string='Apps To Export', domain=[('state','=','installed')]) + data = fields.Binary('File', readonly=True) + state = fields.Selection([('choose', 'choose'), ('get', 'get')], # choose language or get the file + default='choose') - def act_getfile(self, cr, uid, ids, context=None): - this = self.browse(cr, uid, ids, context=context)[0] + @api.multi + def act_getfile(self): + this = self[0] lang = this.lang if this.lang != NEW_LANG_KEY else False - mods = sorted(map(lambda m: m.name, this.modules)) or ['all'] + mods = sorted(this.mapped('modules.name')) or ['all'] with contextlib.closing(cStringIO.StringIO()) as buf: - tools.trans_export(lang, mods, buf, this.format, cr) + tools.trans_export(lang, mods, buf, this.format, self._cr) out = base64.encodestring(buf.getvalue()) filename = 'new' if lang: - filename = get_iso_codes(lang) + filename = tools.get_iso_codes(lang) elif len(mods) == 1: filename = mods[0] extension = this.format if not lang and extension == 'po': extension = 'pot' name = "%s.%s" % (filename, extension) - this.write({ 'state': 'get', 'data': out, 'name': name }) + this.write({'state': 'get', 'data': out, 'name': name}) return { 'type': 'ir.actions.act_window', 'res_model': 'base.language.export', diff --git a/openerp/addons/base/module/wizard/base_import_language.py b/openerp/addons/base/module/wizard/base_import_language.py index 0a5cc592651..c0aca1669bd 100644 --- a/openerp/addons/base/module/wizard/base_import_language.py +++ b/openerp/addons/base/module/wizard/base_import_language.py @@ -1,52 +1,45 @@ # -*- coding: utf-8 -*- # Part of Odoo. See LICENSE file for full copyright and licensing details. -import os import base64 import logging +import os from tempfile import TemporaryFile -from openerp import tools -from openerp.osv import osv, fields -from openerp.exceptions import UserError -from openerp.tools.translate import _ +from odoo import api, fields, models, tools, _ +from odoo.exceptions import UserError _logger = logging.getLogger(__name__) -class base_language_import(osv.osv_memory): - """ Language Import """ - +class BaseLanguageImport(models.TransientModel): _name = "base.language.import" _description = "Language Import" - _columns = { - 'name': fields.char('Language Name', required=True), - 'code': fields.char('ISO Code', size=5, help="ISO Language and Country code, e.g. en_US", required=True), - 'data': fields.binary('File', required=True), - 'filename': fields.char('File Name', required=True), - 'overwrite': fields.boolean('Overwrite Existing Terms', - help="If you enable this option, existing translations (including custom ones) " - "will be overwritten and replaced by those in this file"), - } - def import_lang(self, cr, uid, ids, context=None): - if context is None: - context = {} - this = self.browse(cr, uid, ids[0]) - if this.overwrite: - context = dict(context, overwrite=True) - fileobj = TemporaryFile('w+') - try: - fileobj.write(base64.decodestring(this.data)) - - # now we determine the file format - fileobj.seek(0) - fileformat = os.path.splitext(this.filename)[-1][1:].lower() - - tools.trans_load_data(cr, fileobj, fileformat, this.code, lang_name=this.name, context=context) - except Exception, e: - _logger.exception('File unsuccessfully imported, due to format mismatch.') - raise UserError(_('File not imported due to format mismatch or a malformed file. (Valid formats are .csv, .po, .pot)\n\nTechnical Details:\n%s') % tools.ustr(e)) - finally: - fileobj.close() + name = fields.Char('Language Name', required=True) + code = fields.Char('ISO Code', size=5, required=True, + help="ISO Language and Country code, e.g. en_US") + data = fields.Binary('File', required=True) + filename = fields.Char('File Name', required=True) + overwrite = fields.Boolean('Overwrite Existing Terms', + help="If you enable this option, existing translations (including custom ones) " + "will be overwritten and replaced by those in this file") + + @api.multi + def import_lang(self): + this = self[0] + this = this.with_context(overwrite=this.overwrite) + with TemporaryFile('w+') as buf: + try: + buf.write(base64.decodestring(this.data)) + + # now we determine the file format + buf.seek(0) + fileformat = os.path.splitext(this.filename)[-1][1:].lower() + + tools.trans_load_data(this._cr, buf, fileformat, this.code, + lang_name=this.name, context=this._context) + except Exception as e: + _logger.exception('File unsuccessfully imported, due to format mismatch.') + raise UserError(_('File not imported due to format mismatch or a malformed file. (Valid formats are .csv, .po, .pot)\n\nTechnical Details:\n%s') % tools.ustr(e)) return True diff --git a/openerp/addons/base/module/wizard/base_language_install.py b/openerp/addons/base/module/wizard/base_language_install.py index e89f44e91b2..e8dba882fc8 100644 --- a/openerp/addons/base/module/wizard/base_language_install.py +++ b/openerp/addons/base/module/wizard/base_language_install.py @@ -1,43 +1,30 @@ # -*- coding: utf-8 -*- # Part of Odoo. See LICENSE file for full copyright and licensing details. -from operator import itemgetter +from odoo import api, fields, models, _ -from openerp import tools -from openerp.osv import osv, fields -from openerp.tools.translate import _ -class base_language_install(osv.osv_memory): - """ Install Language""" +class BaseLanguageInstall(models.TransientModel): _name = "base.language.install" _description = "Install Language" - def _languages(self, cr, uid, context=None): - return self.pool['res.lang'].get_available(cr, uid, context=context) + @api.model + def _get_languages(self): + return self.env['res.lang'].get_available() - _columns = { - 'lang': fields.selection(_languages, 'Language', required=True), - 'overwrite': fields.boolean('Overwrite Existing Terms', help="If you check this box, your customized translations will be overwritten and replaced by the official ones."), - 'state':fields.selection([('init','init'),('done','done')], 'Status', readonly=True), - } + lang = fields.Selection(_get_languages, string='Language', required=True) + overwrite = fields.Boolean('Overwrite Existing Terms', + help="If you check this box, your customized translations will be overwritten and replaced by the official ones.") + state = fields.Selection([('init', 'init'), ('done', 'done')], + string='Status', readonly=True, default='init') - _defaults = { - 'state': 'init', - 'overwrite': False - } - - def lang_install(self, cr, uid, ids, context=None): - if context is None: - context = {} - language_obj = self.browse(cr, uid, ids)[0] - lang = language_obj.lang - if lang: - modobj = self.pool.get('ir.module.module') - mids = modobj.search(cr, uid, [('state', '=', 'installed')]) - if language_obj.overwrite: - context = {'overwrite': True} - modobj.update_translations(cr, uid, mids, lang, context or {}) - self.write(cr, uid, ids, {'state': 'done'}, context=context) + @api.multi + def lang_install(self): + this = self[0] + this = this.with_context(overwrite=this.overwrite) + mods = self.env['ir.module.module'].search([('state', '=', 'installed')]) + mods.update_translations(this.lang) + this.state = 'done' return { 'name': _('Language Pack'), 'view_type': 'form', @@ -45,8 +32,8 @@ class base_language_install(osv.osv_memory): 'view_id': False, 'res_model': 'base.language.install', 'domain': [], - 'context': dict(context, active_ids=ids), + 'context': dict(self._context, active_ids=self.ids), 'type': 'ir.actions.act_window', 'target': 'new', - 'res_id': ids and ids[0] or False, + 'res_id': this.id, } diff --git a/openerp/addons/base/module/wizard/base_module_configuration.py b/openerp/addons/base/module/wizard/base_module_configuration.py index 4bd1eea5cf8..1e889cade42 100644 --- a/openerp/addons/base/module/wizard/base_module_configuration.py +++ b/openerp/addons/base/module/wizard/base_module_configuration.py @@ -1,33 +1,27 @@ # -*- coding: utf-8 -*- # Part of Odoo. See LICENSE file for full copyright and licensing details. -from openerp.osv import osv -from openerp.tools.translate import _ +from odoo import api, models, _ -class base_module_configuration(osv.osv_memory): +class BaseModuleConfiguration(models.TransientModel): _name = "base.module.configuration" - def start(self, cr, uid, ids, context=None): - todo_ids = self.pool.get('ir.actions.todo').search(cr, uid, - ['|', ('type','=','recurring'), ('state', '=', 'open')]) - if not todo_ids: + @api.multi + def start(self): + todos_domain = ['|', ('type','=','recurring'), ('state', '=', 'open')] + if self.env['ir.actions.todo'].search_count(todos_domain): + # Run the config wizards + return self.env['res.config'].start() + else: # When there is no wizard todo it will display message - data_obj = self.pool.get('ir.model.data') - result = data_obj._get_id(cr, uid, 'base', 'view_base_module_configuration_form') - view_id = data_obj.browse(cr, uid, result).res_id - value = { - 'name': _('System Configuration done'), - 'view_type': 'form', - 'view_mode': 'form', - 'res_model': 'base.module.configuration', - 'view_id': [view_id], - 'type': 'ir.actions.act_window', - 'target': 'new' - } - return value - # Run the config wizards - config_pool = self.pool.get('res.config') - return config_pool.start(cr, uid, ids, context=context) - -base_module_configuration() + view = self.env.ref('base.view_base_module_configuration_form') + return { + 'name': _('System Configuration done'), + 'view_type': 'form', + 'view_mode': 'form', + 'res_model': 'base.mdule.configuration', + 'view_id': [view.id], + 'type': 'ir.actions.act_window', + 'target': 'new', + } diff --git a/openerp/addons/base/module/wizard/base_module_update.py b/openerp/addons/base/module/wizard/base_module_update.py index 2f3d0cc1af2..47de6ed7bfa 100644 --- a/openerp/addons/base/module/wizard/base_module_update.py +++ b/openerp/addons/base/module/wizard/base_module_update.py @@ -1,8 +1,9 @@ # -*- coding: utf-8 -*- -from openerp import models, fields, api +from odoo import api, fields, models -class base_module_update(models.TransientModel): + +class BaseModuleUpdate(models.TransientModel): _name = "base.module.update" _description = "Update Module" @@ -10,10 +11,11 @@ class base_module_update(models.TransientModel): added = fields.Integer('Number of modules added', readonly=True) state = fields.Selection([('init', 'init'), ('done', 'done')], 'Status', readonly=True, default='init') - @api.one + @api.multi def update_module(self): - self.updated, self.added = self.env['ir.module.module'].update_list() - self.state = 'done' + for this in self: + updated, added = self.env['ir.module.module'].update_list() + this.write({'updated': updated, 'added': added, 'state': 'done'}) return False @api.multi diff --git a/openerp/addons/base/module/wizard/base_module_upgrade.py b/openerp/addons/base/module/wizard/base_module_upgrade.py index 5d0bd0f79d0..62180217795 100644 --- a/openerp/addons/base/module/wizard/base_module_upgrade.py +++ b/openerp/addons/base/module/wizard/base_module_upgrade.py @@ -1,34 +1,37 @@ # -*- coding: utf-8 -*- # Part of Odoo. See LICENSE file for full copyright and licensing details. -import openerp -from openerp.osv import osv, fields -from openerp.tools.translate import _ -from openerp.exceptions import UserError +import odoo +from odoo import api, fields, models, _ +from odoo.exceptions import UserError -class base_module_upgrade(osv.osv_memory): - """ Module Upgrade """ +class BaseModuleUpgrade(models.TransientModel): _name = "base.module.upgrade" _description = "Module Upgrade" - _columns = { - 'module_info': fields.text('Apps to Update',readonly=True), - } + @api.model + @api.returns('ir.module.module') + def get_module_list(self): + states = ['to upgrade', 'to remove', 'to install'] + return self.env['ir.module.module'].search([('state', 'in', states)]) - def fields_view_get(self, cr, uid, view_id=None, view_type='form', context=None, toolbar=False, submenu=False): - res = super(base_module_upgrade, self).fields_view_get(cr, uid, view_id=view_id, view_type=view_type, context=context, toolbar=toolbar,submenu=False) + @api.model + def _default_module_info(self): + return "\n".join("%s: %s" % (mod.name, mod.state) for mod in self.get_module_list()) + + module_info = fields.Text('Apps to Update', readonly=True, default=_default_module_info) + + @api.model + def fields_view_get(self, view_id=None, view_type='form', toolbar=False, submenu=False): + res = super(BaseModuleUpgrade, self).fields_view_get(view_id, view_type, toolbar=toolbar,submenu=False) if view_type != 'form': return res - context = {} if context is None else context - record_id = context and context.get('active_id', False) or False - active_model = context.get('active_model') - if (not record_id) or (not active_model): + if not(self._context.get('active_model') and self._context.get('active_id')): return res - ids = self.get_module_list(cr, uid, context=context) - if not ids: + if not self.get_module_list(): res['arch'] = '''