diff --git a/addons/website/controllers/main.py b/addons/website/controllers/main.py index f300487e7b8..9f01f71b2a0 100644 --- a/addons/website/controllers/main.py +++ b/addons/website/controllers/main.py @@ -253,7 +253,8 @@ class Website(openerp.addons.web.controllers.main.Home): old_trans = irt.search_read( request.cr, request.uid, [ - ('type', '=', 'view'), + ('type', '=', 'model'), + ('name', '=', 'ir.ui.view,arch_db'), ('res_id', '=', view_id), ('lang', '=', lang), ('src', '=', initial_content), @@ -261,15 +262,16 @@ class Website(openerp.addons.web.controllers.main.Home): if old_trans: tid = old_trans[0]['id'] if tid: - vals = {'value': new_content} + vals = {'value': new_content, 'state': 'translated'} irt.write(request.cr, request.uid, [tid], vals) else: new_trans = { - 'name': 'website', + 'type': 'model', + 'name': 'ir.ui.view,arch_db', + 'state': 'translated', 'res_id': view_id, 'lang': lang, - 'type': 'view', - 'source': initial_content, + 'src': initial_content, 'value': new_content, } if t.get('gengo_translation'): diff --git a/addons/website_version/models/ir_ui_view.py b/addons/website_version/models/ir_ui_view.py index 94fa6cab45e..4a3ded70f43 100644 --- a/addons/website_version/models/ir_ui_view.py +++ b/addons/website_version/models/ir_ui_view.py @@ -170,7 +170,12 @@ class view(models.Model): for v in view_list: views = self.customize_template_get(v.id, full=True) views_ids += [v.get('id') for v in views if v.get('active')] - domain = [('type', '=', 'view'), ('res_id', 'in', views_ids), ('lang', '=', lang)] + domain = [ + ('type', '=', 'model'), + ('name', '=', 'ir.ui.view,arch_db'), + ('res_id', 'in', views_ids), + ('lang', '=', lang), + ] element_list = irt.search_read(self.env.cr, self.env.uid, domain, field, context=self.env.context) for element in element_list: diff --git a/openerp/addons/base/ir/ir_translation.py b/openerp/addons/base/ir/ir_translation.py index ea18178d57a..1b0c78c9991 100644 --- a/openerp/addons/base/ir/ir_translation.py +++ b/openerp/addons/base/ir/ir_translation.py @@ -17,7 +17,7 @@ TRANSLATION_TYPE = [ ('model', 'Object'), ('report', 'Report/Template'), ('selection', 'Selection'), - ('view', 'View'), + ('view', 'View'), # deprecated ('help', 'Help'), # deprecated ('code', 'Code'), ('constraint', 'Constraint'), @@ -62,11 +62,14 @@ class ir_translation_import_cursor(object): # ugly hack for QWeb views - pending refactoring of translations in master if params['imd_model'] == 'website': params['imd_model'] = "ir.ui.view" - # non-QWeb views do not need a matching res_id -> force to 0 to avoid dropping them - elif params['res_id'] is None: + # non-QWeb views do not need a matching res_id in case they do not + # have an xml id -> force to 0 to avoid dropping them + elif params['res_id'] is None and not params['imd_name']: + # maybe we should insert this translation for all views of the + # given model? params['res_id'] = 0 - # backward compatibility: convert 'field', 'help' into 'model' + # backward compatibility: convert 'field', 'help', 'view' into 'model' if params['type'] == 'field': model, field = params['name'].split(',') params['type'] = 'model' @@ -81,6 +84,11 @@ class ir_translation_import_cursor(object): params['imd_model'] = 'ir.model.fields' params['imd_name'] = 'field_%s_%s' % (model.replace('.', '_'), field) + elif params['type'] == 'view': + params['type'] = 'model' + params['name'] = 'ir.ui.view,arch_db' + params['imd_model'] = "ir.ui.view" + self._cr.execute("""INSERT INTO %s (name, lang, res_id, src, type, imd_model, module, imd_name, value, state, comments) VALUES (%%(name)s, %%(lang)s, %%(res_id)s, %%(src)s, %%(type)s, %%(imd_model)s, %%(module)s, %%(imd_name)s, %%(value)s, %%(state)s, %%(comments)s)""" % self._table_name, diff --git a/openerp/addons/base/ir/ir_ui_view.py b/openerp/addons/base/ir/ir_ui_view.py index 25101e8d6ff..58f3b12a412 100644 --- a/openerp/addons/base/ir/ir_ui_view.py +++ b/openerp/addons/base/ir/ir_ui_view.py @@ -3,11 +3,11 @@ import collections import copy import datetime -import dateutil from dateutil.relativedelta import relativedelta import fnmatch import logging import os +import re import time from operator import itemgetter @@ -18,6 +18,7 @@ from lxml import etree import openerp from openerp import tools, api +from openerp.exceptions import ValidationError from openerp.http import request from openerp.modules.module import get_resource_path, get_resource_from_path from openerp.osv import fields, osv, orm @@ -27,7 +28,7 @@ from openerp.tools.parse_version import parse_version from openerp.tools.safe_eval import safe_eval as eval from openerp.tools.view_validation import valid_view from openerp.tools import misc -from openerp.tools.translate import _ +from openerp.tools.translate import TRANSLATED_ATTRS, encode, xml_translate, _ _logger = logging.getLogger(__name__) @@ -118,6 +119,22 @@ def get_view_arch_from_file(filename, xmlid): xpath_utils = etree.FunctionNamespace(None) xpath_utils['hasclass'] = _hasclass +TRANSLATED_ATTRS_RE = re.compile(r"@(%s)\b" % "|".join(TRANSLATED_ATTRS)) + +def valid_inheritance(arch): + """ Check whether view inheritance is based on translated attribute. """ + for node in arch.xpath('//*[@position]'): + # inheritance may not use a translated attribute as selector + if node.tag == 'xpath': + match = TRANSLATED_ATTRS_RE.search(node.get('expr', '')) + if match: + raise ValidationError("View inheritance may not use attribute %r as a selector." % match.group(1)) + else: + for attr in TRANSLATED_ATTRS: + if node.get(attr): + raise ValidationError("View inheritance may not use attribute %r as a selector." % attr) + return True + class view(osv.osv): _name = 'ir.ui.view' @@ -135,7 +152,6 @@ class view(osv.osv): def _arch_get(self, cr, uid, ids, name, arg, context=None): result = {} - for view in self.browse(cr, uid, ids, context=context): arch_fs = None if config['dev_mode'] and view.arch_fs and view.xml_id: @@ -164,6 +180,16 @@ class view(osv.osv): return True + @api.multi + def _arch_base_get(self, name, arg): + """ Return the field 'arch' without translation. """ + return self.with_context(lang=None)._arch_get(name, arg) + + @api.multi + def _arch_base_set(self, name, value, arg): + """ Assign the field 'arch' without translation. """ + return self.with_context(lang=None)._arch_set(name, value, arg) + _columns = { 'name': fields.char('View Name', required=True), 'model': fields.char('Object', select=True), @@ -182,7 +208,8 @@ class view(osv.osv): ('search','Search'), ('qweb', 'QWeb')], string='View Type'), 'arch': fields.function(_arch_get, fnct_inv=_arch_set, string='View Architecture', type="text", nodrop=True), - 'arch_db': fields.text('Arch Blob', oldname='arch'), + 'arch_base': fields.function(_arch_base_get, fnct_inv=_arch_base_set, string='View Architecture', type="text"), + 'arch_db': fields.text('Arch Blob', translate=xml_translate, oldname='arch'), 'arch_fs': fields.char('Arch Filename'), 'inherit_id': fields.many2one('ir.ui.view', 'Inherited View', ondelete='restrict', select=True), 'inherit_children_ids': fields.one2many('ir.ui.view','inherit_id', 'Inherit Views'), @@ -249,6 +276,9 @@ class view(osv.osv): # Sanity checks: the view should not break anything upon rendering! # Any exception raised below will cause a transaction rollback. for view in self.browse(cr, uid, ids, context): + view_arch = etree.fromstring(encode(view.arch)) + if not valid_inheritance(view_arch): + return False view_def = self.read_combined(cr, uid, view.id, None, context=context) view_arch_utf8 = view_def['arch'] if view.type != 'qweb': @@ -278,7 +308,7 @@ class view(osv.osv): " extend an other view"), ] _constraints = [ - (_check_xml, 'Invalid view definition', ['arch']), + (_check_xml, 'Invalid view definition', ['arch', 'arch_base']), ] def _auto_init(self, cr, context=None): @@ -462,7 +492,6 @@ class view(osv.osv): self.inherit_branding(node, view_id, root_id) else: node.set('data-oe-id', str(view_id)) - node.set('data-oe-source-id', str(root_id)) node.set('data-oe-xpath', xpath) node.set('data-oe-model', 'ir.ui.view') node.set('data-oe-field', 'arch') @@ -735,46 +764,6 @@ class view(osv.osv): # Get the attrs before they are (possibly) deleted by check_group below orm.transfer_node_to_modifiers(node, modifiers, context, in_tree_view) - # TODO remove attrs counterpart in modifiers when invisible is true ? - - # translate view - if 'lang' in context: - Translations = self.pool['ir.translation'] - if node.text and node.text.strip(): - term = node.text.strip() - trans = Translations._get_source(cr, user, model, 'view', context['lang'], term) - if trans: - node.text = node.text.replace(term, trans) - if node.tail and node.tail.strip(): - term = node.tail.strip() - trans = Translations._get_source(cr, user, model, 'view', context['lang'], term) - if trans: - node.tail = node.tail.replace(term, trans) - - if node.get('string') and node.get('string').strip() and not result: - term = node.get('string').strip() - trans = Translations._get_source(cr, user, model, 'view', context['lang'], term) - if trans == term: - if 'base_model_name' in context: - # If translation is same as source, perhaps we'd have more luck with the alternative model name - # (in case we are in a mixed situation, such as an inherited view where parent_view.model != model - trans = Translations._get_source(cr, user, context['base_model_name'], 'view', context['lang'], term) - else: - inherit_model = self.browse(cr, user, view_id, context=context).inherit_id.model or model - if inherit_model != model: - # parent view has a different model, if the terms belongs to the parent view, the translation - # should be checked on the parent model as well - trans = Translations._get_source(cr, user, inherit_model, 'view', context['lang'], term) - if trans: - node.set('string', trans) - - for attr_name in ('confirm', 'sum', 'avg', 'help', 'placeholder'): - attr_value = node.get(attr_name) - if attr_value and attr_value.strip(): - trans = Translations._get_source(cr, user, model, 'view', context['lang'], attr_value.strip()) - if trans: - node.set(attr_name, trans) - for f in node: if children or (node.tag == 'field' and f.tag in ('filter','separator')): fields.update(self.postprocess(cr, user, model, f, view_id, in_tree_view, model_fields, context)) @@ -1104,6 +1093,11 @@ class view(osv.osv): # Misc #------------------------------------------------------ + @api.multi + def open_translations(self): + """ Open a view for editing the translations of field 'arch_db'. """ + return self.env['ir.translation'].translate_fields('ir.ui.view', self.id, 'arch_db') + def graph_get(self, cr, uid, id, model, node_obj, conn_obj, src_node, des_node, label, scale, context=None): nodes=[] nodes_name=[] diff --git a/openerp/addons/base/ir/ir_ui_view_view.xml b/openerp/addons/base/ir/ir_ui_view_view.xml index 171d04d5651..02c0f69e83e 100644 --- a/openerp/addons/base/ir/ir_ui_view_view.xml +++ b/openerp/addons/base/ir/ir_ui_view_view.xml @@ -6,7 +6,7 @@ ir.ui.view
- + @@ -25,7 +25,9 @@ - +