[MIG] base/module
This commit is contained in:
@@ -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),
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user