[MIG] base/module

This commit is contained in:
Raphael Collet
2016-05-11 11:40:25 +02:00
parent de54107fec
commit d465346f1e
9 changed files with 518 additions and 628 deletions
+326 -380
View File
@@ -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')
@@ -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)
return Report.render('base.report_irmodulereference', docargs)
@@ -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',
@@ -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
@@ -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,
}
@@ -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',
}
@@ -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
@@ -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'] = '''<form string="Upgrade Completed" version="7.0">
<separator string="Upgrade Completed" colspan="4"/>
<footer>
@@ -39,55 +42,41 @@ class base_module_upgrade(osv.osv_memory):
return res
def get_module_list(self, cr, uid, context=None):
mod_obj = self.pool.get('ir.module.module')
ids = mod_obj.search(cr, uid, [
('state', 'in', ['to upgrade', 'to remove', 'to install'])])
return ids
def default_get(self, cr, uid, fields, context=None):
mod_obj = self.pool.get('ir.module.module')
ids = self.get_module_list(cr, uid, context=context)
res = mod_obj.read(cr, uid, ids, ['name','state'], context)
return {'module_info': '\n'.join(map(lambda x: x['name']+' : '+x['state'], res))}
def upgrade_module_cancel(self, cr, uid, ids, context=None):
mod_obj = self.pool.get('ir.module.module')
to_installed_ids = mod_obj.search(cr, uid, [
('state', 'in', ['to upgrade', 'to remove'])])
if to_installed_ids:
mod_obj.write(cr, uid, to_installed_ids, {'state': 'installed'}, context=context)
to_uninstalled_ids = mod_obj.search(cr, uid, [
('state', '=', 'to install')])
if to_uninstalled_ids:
mod_obj.write(cr, uid, to_uninstalled_ids, {'state': 'uninstalled'}, context=context)
@api.multi
def upgrade_module_cancel(self):
Module = self.env['ir.module.module']
to_install = Module.search([('state', 'in', ['to upgrade', 'to remove'])])
to_install.write({'state': 'installed'})
to_uninstall = Module.search([('state', '=', 'to install')])
to_uninstall.write({'state': 'uninstalled'})
return {'type': 'ir.actions.act_window_close'}
def upgrade_module(self, cr, uid, ids, context=None):
ir_module = self.pool.get('ir.module.module')
@api.multi
def upgrade_module(self):
Module = self.env['ir.module.module']
# install/upgrade: double-check preconditions
ids = ir_module.search(cr, uid, [('state', 'in', ['to upgrade', 'to install'])])
if ids:
cr.execute("""SELECT d.name FROM ir_module_module m
JOIN ir_module_module_dependency d ON (m.id = d.module_id)
LEFT JOIN ir_module_module m2 ON (d.name = m2.name)
WHERE m.id in %s and (m2.state IS NULL or m2.state IN %s)""",
(tuple(ids), ('uninstalled',)))
unmet_packages = [x[0] for x in cr.fetchall()]
mods = Module.search([('state', 'in', ['to upgrade', 'to install'])])
if mods:
query = """ SELECT d.name
FROM ir_module_module m
JOIN ir_module_module_dependency d ON (m.id = d.module_id)
LEFT JOIN ir_module_module m2 ON (d.name = m2.name)
WHERE m.id in %s and (m2.state IS NULL or m2.state IN %s) """
self._cr.execute(query, (tuple(mods.ids), ('uninstalled',)))
unmet_packages = [row[0] for row in self._cr.fetchall()]
if unmet_packages:
raise UserError(_('Following modules are not installed or unknown: %s') % ('\n\n' + '\n'.join(unmet_packages)))
raise UserError(_('The following modules are not installed or unknown: %s') % ('\n\n' + '\n'.join(unmet_packages)))
ir_module.download(cr, uid, ids, context=context)
mods.download()
# terminate transaction before re-creating cursor below
cr.commit()
openerp.api.Environment.reset()
openerp.modules.registry.RegistryManager.new(cr.dbname, update_module=True)
self._cr.commit()
api.Environment.reset()
odoo.modules.registry.RegistryManager.new(self._cr.dbname, update_module=True)
return {'type': 'ir.actions.act_window_close'}
def config(self, cr, uid, ids, context=None):
return self.pool.get('res.config').next(cr, uid, [], context=context)
@api.multi
def config(self):
return self.env['res.config'].next()
@@ -1,52 +1,43 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
import contextlib
import cStringIO
from openerp import tools
from openerp.osv import osv, fields
from openerp.tools.translate import _
from openerp.exceptions import UserError
from odoo import api, fields, models, tools, _
from odoo.exceptions import UserError
class base_update_translations(osv.osv_memory):
def _get_languages(self, cr, uid, context):
lang_obj = self.pool.get('res.lang')
ids = lang_obj.search(cr, uid, ['&', ('active', '=', True), ('translatable', '=', True),])
langs = lang_obj.browse(cr, uid, ids)
class BaseUpdateTranslations(models.TransientModel):
_name = 'base.update.translations'
@api.model
def _get_languages(self):
langs = self.env['res.lang'].search([('active', '=', True), ('translatable', '=', True)])
return [(lang.code, lang.name) for lang in langs]
def _get_lang_name(self, cr, uid, lang_code):
lang_obj = self.pool.get('res.lang')
ids = lang_obj.search(cr, uid, [('code', '=', lang_code)])
if not ids:
@api.model
def _default_language(self):
if self._context.get('active_model') == 'res.lang':
lang = self.env['res.lang'].browse(self._context.get('active_id'))
return lang.code
return False
lang = fields.Selection(_get_languages, 'Language', required=True,
default=_default_language)
@api.model
def _get_lang_name(self, lang_code):
lang = self.env['res.lang'].search([('code', '=', lang_code)], limit=1)
if not lang:
raise UserError(_('No language with code "%s" exists') % lang_code)
lang = lang_obj.browse(cr, uid, ids[0])
return lang.name
def act_update(self, cr, uid, ids, context=None):
this = self.browse(cr, uid, ids)[0]
lang_name = self._get_lang_name(cr, uid, this.lang)
buf = cStringIO.StringIO()
tools.trans_export(this.lang, ['all'], buf, 'csv', cr)
tools.trans_load_data(cr, buf, 'csv', this.lang, lang_name=lang_name)
buf.close()
@api.multi
def act_update(self):
this = self[0]
lang_name = self._get_lang_name(this.lang)
with contextlib.closing(cStringIO.StringIO()) as buf:
tools.trans_export(this.lang, ['all'], buf, 'csv', self._cr)
tools.trans_load_data(self._cr, buf, 'csv', this.lang, lang_name=lang_name)
return {'type': 'ir.actions.act_window_close'}
def default_get(self, cr, uid, fields, context=None):
if context is None:
context = {}
res = super(base_update_translations, self).default_get(cr, uid, fields, context=context)
if context.get('active_model') != "res.lang":
return res
record_id = context.get('active_id', False) or False
if record_id:
lang = self.pool.get('res.lang').browse(cr, uid, record_id).code
res.update(lang=lang)
return res
_name = 'base.update.translations'
_columns = {
'lang': fields.selection(_get_languages, 'Language', required=True),
}