[MERGE] forward port of branch saas-3 up to revid 5088 chs@openerp.com-20140311095550-lg3nvvjyojvgp2po
bzr revid: chs@openerp.com-20140311133850-11bw9vv90e40clw1
This commit is contained in:
commit
96f744b271
|
@ -77,7 +77,9 @@ class ir_http(osv.AbstractModel):
|
|||
# what if error in security.check()
|
||||
# -> res_users.check()
|
||||
# -> res_users.check_credentials()
|
||||
except Exception:
|
||||
except (openerp.exceptions.AccessDenied, openerp.http.SessionExpiredException):
|
||||
# All other exceptions mean undetermined status (e.g. connection pool full),
|
||||
# let them bubble up
|
||||
request.session.logout()
|
||||
getattr(self, "_auth_method_%s" % auth_method)()
|
||||
return auth_method
|
||||
|
|
|
@ -1117,6 +1117,10 @@ class ir_model_data(osv.osv):
|
|||
# Don't remove the LOG_ACCESS_COLUMNS unless _log_access
|
||||
# has been turned off on the model.
|
||||
field = self.pool[model].browse(cr, uid, [res_id], context=context)[0]
|
||||
if not field.exists():
|
||||
_logger.info('Deleting orphan external_ids %s', external_ids)
|
||||
self.unlink(cr, uid, external_ids)
|
||||
continue
|
||||
if field.name in openerp.osv.orm.LOG_ACCESS_COLUMNS and self.pool[field.model]._log_access:
|
||||
continue
|
||||
if field.name == 'id':
|
||||
|
|
|
@ -32,7 +32,7 @@ class QWebException(Exception):
|
|||
class QWebTemplateNotFound(QWebException):
|
||||
pass
|
||||
|
||||
def convert_to_qweb_exception(etype=None, **kw):
|
||||
def raise_qweb_exception(etype=None, **kw):
|
||||
if etype is None:
|
||||
etype = QWebException
|
||||
orig_type, original, tb = sys.exc_info()
|
||||
|
@ -43,7 +43,7 @@ def convert_to_qweb_exception(etype=None, **kw):
|
|||
e.qweb[k] = v
|
||||
# Will use `raise foo from bar` in python 3 and rename cause to __cause__
|
||||
e.qweb['cause'] = original
|
||||
return e
|
||||
raise
|
||||
|
||||
class QWebContext(dict):
|
||||
def __init__(self, cr, uid, data, loader=None, templates=None, context=None):
|
||||
|
@ -166,7 +166,7 @@ class QWeb(orm.AbstractModel):
|
|||
try:
|
||||
xml_doc = qwebcontext.loader(name)
|
||||
except ValueError:
|
||||
raise convert_to_qweb_exception(QWebTemplateNotFound, message="Loader could not find template %r" % name, template=origin_template)
|
||||
raise_qweb_exception(QWebTemplateNotFound, message="Loader could not find template %r" % name, template=origin_template)
|
||||
self.load_document(xml_doc, qwebcontext=qwebcontext)
|
||||
|
||||
if name in qwebcontext.templates:
|
||||
|
@ -179,7 +179,7 @@ class QWeb(orm.AbstractModel):
|
|||
return qwebcontext.safe_eval(expr)
|
||||
except Exception:
|
||||
template = qwebcontext.get('__template__')
|
||||
raise convert_to_qweb_exception(message="Could not evaluate expression %r" % expr, expression=expr, template=template)
|
||||
raise_qweb_exception(message="Could not evaluate expression %r" % expr, expression=expr, template=template)
|
||||
|
||||
def eval_object(self, expr, qwebcontext):
|
||||
return self.eval(expr, qwebcontext)
|
||||
|
@ -207,7 +207,7 @@ class QWeb(orm.AbstractModel):
|
|||
return str(expr % qwebcontext)
|
||||
except Exception:
|
||||
template = qwebcontext.get('__template__')
|
||||
raise convert_to_qweb_exception(message="Format error for expression %r" % expr, expression=expr, template=template)
|
||||
raise_qweb_exception(message="Format error for expression %r" % expr, expression=expr, template=template)
|
||||
|
||||
def eval_bool(self, expr, qwebcontext):
|
||||
return int(bool(self.eval(expr, qwebcontext)))
|
||||
|
@ -292,7 +292,7 @@ class QWeb(orm.AbstractModel):
|
|||
raise
|
||||
except Exception:
|
||||
template = qwebcontext.get('__template__')
|
||||
raise convert_to_qweb_exception(message="Could not render element %r" % element.nodeName, node=element, template=template)
|
||||
raise_qweb_exception(message="Could not render element %r" % element.nodeName, node=element, template=template)
|
||||
name = str(element.nodeName)
|
||||
inner = "".join(g_inner)
|
||||
trim = template_attributes.get("trim", 0)
|
||||
|
@ -611,6 +611,9 @@ class DateTimeConverter(osv.AbstractModel):
|
|||
strftime_pattern = (u"%s %s" % (lang.date_format, lang.time_format))
|
||||
pattern = openerp.tools.posix_to_ldml(strftime_pattern, locale=locale)
|
||||
|
||||
if options and options.get('hide_seconds'):
|
||||
pattern = pattern.replace(":ss", "").replace(":s", "")
|
||||
|
||||
return babel.dates.format_datetime(value, format=pattern, locale=locale)
|
||||
|
||||
class TextConverter(osv.AbstractModel):
|
||||
|
|
|
@ -728,9 +728,26 @@ class view(osv.osv):
|
|||
def clear_cache(self):
|
||||
self.read_template.clear_cache(self)
|
||||
|
||||
def _contains_branded(self, node):
|
||||
return node.tag == 't'\
|
||||
or 't-raw' in node.attrib\
|
||||
or any(self.is_node_branded(child) for child in node.iterdescendants())
|
||||
|
||||
def _pop_view_branding(self, element):
|
||||
distributed_branding = dict(
|
||||
(attribute, element.attrib.pop(attribute))
|
||||
for attribute in MOVABLE_BRANDING
|
||||
if element.get(attribute))
|
||||
return distributed_branding
|
||||
|
||||
def distribute_branding(self, e, branding=None, parent_xpath='',
|
||||
index_map=misc.ConstantMapping(1)):
|
||||
if e.get('t-ignore') or e.tag == 'head':
|
||||
# remove any view branding possibly injected by inheritance
|
||||
attrs = set(MOVABLE_BRANDING)
|
||||
for descendant in e.iterdescendants(tag=etree.Element):
|
||||
if not attrs.intersection(descendant.attrib): continue
|
||||
self._pop_view_branding(descendant)
|
||||
# TODO: find a better name and check if we have a string to boolean helper
|
||||
return
|
||||
|
||||
|
@ -742,15 +759,15 @@ class view(osv.osv):
|
|||
e.set('data-oe-xpath', node_path)
|
||||
if not e.get('data-oe-model'): return
|
||||
|
||||
if set(('t-esc', 't-escf', 't-raw', 't-rawf')).intersection(e.attrib):
|
||||
# nodes which fully generate their content and have no reason to
|
||||
# be branded because they can not sensibly be edited
|
||||
self._pop_view_branding(e)
|
||||
elif self._contains_branded(e):
|
||||
# if a branded element contains branded elements distribute own
|
||||
# branding to children unless it's t-raw, then just remove branding
|
||||
# on current element
|
||||
if e.tag == 't' or 't-raw' in e.attrib or \
|
||||
any(self.is_node_branded(child) for child in e.iterdescendants()):
|
||||
distributed_branding = dict(
|
||||
(attribute, e.attrib.pop(attribute))
|
||||
for attribute in MOVABLE_BRANDING
|
||||
if e.get(attribute))
|
||||
distributed_branding = self._pop_view_branding(e)
|
||||
|
||||
if 't-raw' not in e.attrib:
|
||||
# TODO: collections.Counter if remove p2.6 compat
|
||||
|
@ -760,11 +777,12 @@ class view(osv.osv):
|
|||
if child.get('data-oe-xpath'):
|
||||
# injected by view inheritance, skip otherwise
|
||||
# generated xpath is incorrect
|
||||
continue
|
||||
self.distribute_branding(child)
|
||||
else:
|
||||
indexes[child.tag] += 1
|
||||
self.distribute_branding(child, distributed_branding,
|
||||
parent_xpath=node_path,
|
||||
index_map=indexes)
|
||||
self.distribute_branding(
|
||||
child, distributed_branding,
|
||||
parent_xpath=node_path, index_map=indexes)
|
||||
|
||||
def is_node_branded(self, node):
|
||||
""" Finds out whether a node is branded or qweb-active (bears a
|
||||
|
|
|
@ -208,20 +208,19 @@ class res_company(osv.osv):
|
|||
res['value'] = {'currency_id': currency_id}
|
||||
return res
|
||||
|
||||
def _search(self, cr, uid, args, offset=0, limit=None, order=None,
|
||||
context=None, count=False, access_rights_uid=None):
|
||||
def name_search(self, cr, uid, name='', args=None, operator='ilike', context=None, limit=100):
|
||||
if context is None:
|
||||
context = {}
|
||||
if context.get('user_preference'):
|
||||
if context.pop('user_preference', None):
|
||||
# We browse as superuser. Otherwise, the user would be able to
|
||||
# select only the currently visible companies (according to rules,
|
||||
# which are probably to allow to see the child companies) even if
|
||||
# she belongs to some other companies.
|
||||
user = self.pool.get('res.users').browse(cr, SUPERUSER_ID, uid, context=context)
|
||||
cmp_ids = list(set([user.company_id.id] + [cmp.id for cmp in user.company_ids]))
|
||||
return cmp_ids
|
||||
return super(res_company, self)._search(cr, uid, args, offset=offset, limit=limit, order=order,
|
||||
context=context, count=count, access_rights_uid=access_rights_uid)
|
||||
uid = SUPERUSER_ID
|
||||
args = (args or []) + [('id', 'in', cmp_ids)]
|
||||
return super(res_company, self).name_search(cr, uid, name=name, args=args, operator=operator, context=context, limit=limit)
|
||||
|
||||
def _company_default_get(self, cr, uid, object=False, field=False, context=None):
|
||||
"""
|
||||
|
|
|
@ -350,6 +350,7 @@ class res_partner(osv.osv, format_address):
|
|||
def copy(self, cr, uid, id, default=None, context=None):
|
||||
if default is None:
|
||||
default = {}
|
||||
default['user_ids'] = False
|
||||
name = self.read(cr, uid, [id], ['name'], context)[0]['name']
|
||||
default.update({'name': _('%s (copy)') % name})
|
||||
return super(res_partner, self).copy(cr, uid, id, default, context)
|
||||
|
|
|
@ -282,7 +282,7 @@
|
|||
<group name="preferences" col="4">
|
||||
<field name="lang" readonly="0"/>
|
||||
<field name="tz" readonly="0"/>
|
||||
<field name="company_id" widget="selection" readonly="0"
|
||||
<field name="company_id" options="{'no_create': True}" readonly="0"
|
||||
groups="base.group_multi_company"/>
|
||||
</group>
|
||||
<group string="Email Preferences">
|
||||
|
|
|
@ -179,3 +179,51 @@ class TestPropertyField(common.TransactionCase):
|
|||
self.partner.write(cr, alice, [partner_id], {'property_country': country_be})
|
||||
self.assertEqual(self.partner.browse(cr, alice, partner_id).property_country.id, country_be, "Alice does not see the value he has set on the property field")
|
||||
self.assertEqual(self.partner.browse(cr, bob, partner_id).property_country.id, country_fr, "Changes made by Alice have overwritten Bob's value")
|
||||
|
||||
|
||||
class TestHtmlField(common.TransactionCase):
|
||||
|
||||
def setUp(self):
|
||||
super(TestHtmlField, self).setUp()
|
||||
self.partner = self.registry('res.partner')
|
||||
|
||||
def test_00_sanitize(self):
|
||||
cr, uid, context = self.cr, self.uid, {}
|
||||
old_columns = self.partner._columns
|
||||
self.partner._columns = dict(old_columns)
|
||||
self.partner._columns.update({
|
||||
'comment': fields.html('Secure Html', sanitize=False),
|
||||
})
|
||||
some_ugly_html = """<p>Oops this should maybe be sanitized
|
||||
% if object.some_field and not object.oriented:
|
||||
<table>
|
||||
% if object.other_field:
|
||||
<tr>
|
||||
${object.mako_thing}
|
||||
<td>
|
||||
</tr>
|
||||
% endif
|
||||
<tr>
|
||||
%if object.dummy_field:
|
||||
<p>Youpie</p>
|
||||
%endif"""
|
||||
|
||||
pid = self.partner.create(cr, uid, {
|
||||
'name': 'Raoul Poilvache',
|
||||
'comment': some_ugly_html,
|
||||
}, context=context)
|
||||
partner = self.partner.browse(cr, uid, pid, context=context)
|
||||
self.assertEqual(partner.comment, some_ugly_html, 'Error in HTML field: content was sanitized but field has sanitize=False')
|
||||
|
||||
self.partner._columns.update({
|
||||
'comment': fields.html('Unsecure Html', sanitize=True),
|
||||
})
|
||||
self.partner.write(cr, uid, [pid], {
|
||||
'comment': some_ugly_html,
|
||||
}, context=context)
|
||||
partner = self.partner.browse(cr, uid, pid, context=context)
|
||||
# sanitize should have closed tags left open in the original html
|
||||
self.assertIn('</table>', partner.comment, 'Error in HTML field: content does not seem to have been sanitized despise sanitize=True')
|
||||
self.assertIn('</td>', partner.comment, 'Error in HTML field: content does not seem to have been sanitized despise sanitize=True')
|
||||
|
||||
self.partner._columns = old_columns
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
# -*- encoding: utf-8 -*-
|
||||
from functools import partial
|
||||
|
||||
import unittest2
|
||||
|
||||
from lxml import etree as ET
|
||||
from lxml.builder import E
|
||||
|
||||
|
@ -8,6 +10,24 @@ from openerp.tests import common
|
|||
|
||||
Field = E.field
|
||||
|
||||
class ViewCase(common.TransactionCase):
|
||||
def setUp(self):
|
||||
super(ViewCase, self).setUp()
|
||||
self.addTypeEqualityFunc(ET._Element, self.assertTreesEqual)
|
||||
|
||||
def assertTreesEqual(self, n1, n2, msg=None):
|
||||
self.assertEqual(n1.tag, n2.tag)
|
||||
self.assertEqual((n1.text or '').strip(), (n2.text or '').strip(), msg)
|
||||
self.assertEqual((n1.tail or '').strip(), (n2.tail or '').strip(), msg)
|
||||
|
||||
# Because lxml uses ordereddicts in which order is important to
|
||||
# equality (!?!?!?!)
|
||||
self.assertEqual(dict(n1.attrib), dict(n2.attrib), msg)
|
||||
|
||||
for c1, c2 in zip(n1, n2):
|
||||
self.assertTreesEqual(c1, c2, msg)
|
||||
|
||||
|
||||
class TestNodeLocator(common.BaseCase):
|
||||
"""
|
||||
The node locator returns None when it can not find a node, and the first
|
||||
|
@ -98,7 +118,7 @@ class TestNodeLocator(common.BaseCase):
|
|||
E.foo(attr='1', version='3'))
|
||||
self.assertIsNone(node)
|
||||
|
||||
class TestViewInheritance(common.TransactionCase):
|
||||
class TestViewInheritance(ViewCase):
|
||||
def arch_for(self, name, view_type='form', parent=None):
|
||||
""" Generates a trivial view of the specified ``view_type``.
|
||||
|
||||
|
@ -206,7 +226,7 @@ class TestViewInheritance(common.TransactionCase):
|
|||
self.View.default_view(
|
||||
self.cr, self.uid, model=self.model, view_type='graph'))
|
||||
|
||||
class TestApplyInheritanceSpecs(common.TransactionCase):
|
||||
class TestApplyInheritanceSpecs(ViewCase):
|
||||
""" Applies a sequence of inheritance specification nodes to a base
|
||||
architecture. IO state parameters (cr, uid, model, context) are used for
|
||||
error reporting
|
||||
|
@ -230,8 +250,8 @@ class TestApplyInheritanceSpecs(common.TransactionCase):
|
|||
spec, None)
|
||||
|
||||
self.assertEqual(
|
||||
ET.tostring(self.base_arch),
|
||||
ET.tostring(E.form(Field(name="replacement"), string="Title")))
|
||||
self.base_arch,
|
||||
E.form(Field(name="replacement"), string="Title"))
|
||||
|
||||
def test_delete(self):
|
||||
spec = Field(name="target", position="replace")
|
||||
|
@ -241,8 +261,8 @@ class TestApplyInheritanceSpecs(common.TransactionCase):
|
|||
spec, None)
|
||||
|
||||
self.assertEqual(
|
||||
ET.tostring(self.base_arch),
|
||||
ET.tostring(E.form(string="Title")))
|
||||
self.base_arch,
|
||||
E.form(string="Title"))
|
||||
|
||||
def test_insert_after(self):
|
||||
spec = Field(
|
||||
|
@ -254,12 +274,12 @@ class TestApplyInheritanceSpecs(common.TransactionCase):
|
|||
spec, None)
|
||||
|
||||
self.assertEqual(
|
||||
ET.tostring(self.base_arch),
|
||||
ET.tostring(E.form(
|
||||
self.base_arch,
|
||||
E.form(
|
||||
Field(name="target"),
|
||||
Field(name="inserted"),
|
||||
string="Title"
|
||||
)))
|
||||
))
|
||||
|
||||
def test_insert_before(self):
|
||||
spec = Field(
|
||||
|
@ -271,11 +291,11 @@ class TestApplyInheritanceSpecs(common.TransactionCase):
|
|||
spec, None)
|
||||
|
||||
self.assertEqual(
|
||||
ET.tostring(self.base_arch),
|
||||
ET.tostring(E.form(
|
||||
self.base_arch,
|
||||
E.form(
|
||||
Field(name="inserted"),
|
||||
Field(name="target"),
|
||||
string="Title")))
|
||||
string="Title"))
|
||||
|
||||
def test_insert_inside(self):
|
||||
default = Field(Field(name="inserted"), name="target")
|
||||
|
@ -289,13 +309,13 @@ class TestApplyInheritanceSpecs(common.TransactionCase):
|
|||
spec, None)
|
||||
|
||||
self.assertEqual(
|
||||
ET.tostring(self.base_arch),
|
||||
ET.tostring(E.form(
|
||||
self.base_arch,
|
||||
E.form(
|
||||
Field(
|
||||
Field(name="inserted"),
|
||||
Field(name="inserted 2"),
|
||||
name="target"),
|
||||
string="Title")))
|
||||
string="Title"))
|
||||
|
||||
def test_unpack_data(self):
|
||||
spec = E.data(
|
||||
|
@ -310,15 +330,15 @@ class TestApplyInheritanceSpecs(common.TransactionCase):
|
|||
spec, None)
|
||||
|
||||
self.assertEqual(
|
||||
ET.tostring(self.base_arch),
|
||||
ET.tostring(E.form(
|
||||
self.base_arch,
|
||||
E.form(
|
||||
Field(
|
||||
Field(name="inserted 0"),
|
||||
Field(name="inserted 1"),
|
||||
Field(name="inserted 2"),
|
||||
Field(name="inserted 3"),
|
||||
name="target"),
|
||||
string="Title")))
|
||||
string="Title"))
|
||||
|
||||
def test_invalid_position(self):
|
||||
spec = Field(
|
||||
|
@ -350,18 +370,18 @@ class TestApplyInheritanceSpecs(common.TransactionCase):
|
|||
self.base_arch,
|
||||
spec, None)
|
||||
|
||||
class TestApplyInheritedArchs(common.TransactionCase):
|
||||
class TestApplyInheritedArchs(ViewCase):
|
||||
""" Applies a sequence of modificator archs to a base view
|
||||
"""
|
||||
|
||||
class TestViewCombined(common.TransactionCase):
|
||||
class TestViewCombined(ViewCase):
|
||||
"""
|
||||
Test fallback operations of View.read_combined:
|
||||
* defaults mapping
|
||||
* ?
|
||||
"""
|
||||
|
||||
class TestNoModel(common.TransactionCase):
|
||||
class TestNoModel(ViewCase):
|
||||
def test_create_view_nomodel(self):
|
||||
View = self.registry('ir.ui.view')
|
||||
view_id = View.create(self.cr, self.uid, {
|
||||
|
@ -413,11 +433,9 @@ class TestNoModel(common.TransactionCase):
|
|||
sarch = View.translate_qweb(self.cr, self.uid, None, self.arch, 'fr_FR')
|
||||
|
||||
self.text_para.text = translated_text
|
||||
self.assertEqual(
|
||||
ET.tostring(sarch, encoding='utf-8'),
|
||||
ET.tostring(self.arch, encoding='utf-8'))
|
||||
self.assertEqual(sarch, self.arch)
|
||||
|
||||
class TestTemplating(common.TransactionCase):
|
||||
class TestTemplating(ViewCase):
|
||||
def setUp(self):
|
||||
import openerp.modules
|
||||
super(TestTemplating, self).setUp()
|
||||
|
@ -473,7 +491,126 @@ class TestTemplating(common.TransactionCase):
|
|||
second.get('data-oe-id'),
|
||||
"second should come from the extension view")
|
||||
|
||||
class test_views(common.TransactionCase):
|
||||
def test_branding_distribute_inner(self):
|
||||
""" Checks that the branding is correctly distributed within a view
|
||||
extension
|
||||
"""
|
||||
Views = self.registry('ir.ui.view')
|
||||
id = Views.create(self.cr, self.uid, {
|
||||
'name': "Base view",
|
||||
'type': 'qweb',
|
||||
'arch': """<root>
|
||||
<item order="1"/>
|
||||
</root>"""
|
||||
})
|
||||
id2 = Views.create(self.cr, self.uid, {
|
||||
'name': "Extension",
|
||||
'type': 'qweb',
|
||||
'inherit_id': id,
|
||||
'arch': """<xpath expr="//item" position="before">
|
||||
<item order="2">
|
||||
<content t-att-href="foo">bar</content>
|
||||
</item>
|
||||
</xpath>"""
|
||||
})
|
||||
|
||||
arch_string = Views.read_combined(
|
||||
self.cr, self.uid, id, fields=['arch'],
|
||||
context={'inherit_branding': True})['arch']
|
||||
|
||||
arch = ET.fromstring(arch_string)
|
||||
Views.distribute_branding(arch)
|
||||
|
||||
self.assertEqual(
|
||||
arch,
|
||||
E.root(
|
||||
E.item(
|
||||
E.content("bar", {
|
||||
't-att-href': "foo",
|
||||
'data-oe-model': 'ir.ui.view',
|
||||
'data-oe-id': str(id2),
|
||||
'data-oe-field': 'arch',
|
||||
'data-oe-xpath': '/xpath/item/content[1]',
|
||||
}), {
|
||||
'order': '2',
|
||||
'data-oe-source-id': str(id)
|
||||
}),
|
||||
E.item({
|
||||
'order': '1',
|
||||
'data-oe-model': 'ir.ui.view',
|
||||
'data-oe-id': str(id),
|
||||
'data-oe-field': 'arch',
|
||||
'data-oe-xpath': '/root[1]/item[1]'
|
||||
})
|
||||
)
|
||||
)
|
||||
|
||||
def test_esc_no_branding(self):
|
||||
Views = self.registry('ir.ui.view')
|
||||
id = Views.create(self.cr, self.uid, {
|
||||
'name': "Base View",
|
||||
'type': 'qweb',
|
||||
'arch': """<root>
|
||||
<item><span t-esc="foo"/></item>
|
||||
</root>""",
|
||||
})
|
||||
|
||||
arch_string = Views.read_combined(
|
||||
self.cr, self.uid, id, fields=['arch'],
|
||||
context={'inherit_branding': True})['arch']
|
||||
arch = ET.fromstring(arch_string)
|
||||
Views.distribute_branding(arch)
|
||||
|
||||
self.assertEqual(arch, E.root(E.item(E.span({'t-esc': "foo"}))))
|
||||
|
||||
def test_ignore_unbrand(self):
|
||||
Views = self.registry('ir.ui.view')
|
||||
id = Views.create(self.cr, self.uid, {
|
||||
'name': "Base view",
|
||||
'type': 'qweb',
|
||||
'arch': """<root>
|
||||
<item order="1" t-ignore="true">
|
||||
<t t-esc="foo"/>
|
||||
</item>
|
||||
</root>"""
|
||||
})
|
||||
id2 = Views.create(self.cr, self.uid, {
|
||||
'name': "Extension",
|
||||
'type': 'qweb',
|
||||
'inherit_id': id,
|
||||
'arch': """<xpath expr="//item[@order='1']" position="inside">
|
||||
<item order="2">
|
||||
<content t-att-href="foo">bar</content>
|
||||
</item>
|
||||
</xpath>"""
|
||||
})
|
||||
|
||||
arch_string = Views.read_combined(
|
||||
self.cr, self.uid, id, fields=['arch'],
|
||||
context={'inherit_branding': True})['arch']
|
||||
|
||||
arch = ET.fromstring(arch_string)
|
||||
Views.distribute_branding(arch)
|
||||
|
||||
self.assertEqual(
|
||||
arch,
|
||||
E.root(
|
||||
E.item(
|
||||
{'t-ignore': 'true', 'order': '1'},
|
||||
E.t({'t-esc': 'foo'}),
|
||||
E.item(
|
||||
{'order': '2', 'data-oe-source-id': str(id)},
|
||||
E.content(
|
||||
{'t-att-href': 'foo'},
|
||||
"bar")
|
||||
)
|
||||
)
|
||||
),
|
||||
"t-ignore should apply to injected sub-view branding, not just to"
|
||||
" the main view's"
|
||||
)
|
||||
|
||||
class test_views(ViewCase):
|
||||
|
||||
def test_nonexistent_attribute_removal(self):
|
||||
Views = self.registry('ir.ui.view')
|
||||
|
@ -589,16 +726,17 @@ class test_views(common.TransactionCase):
|
|||
})
|
||||
self.assertEqual(view['type'], 'form')
|
||||
self.assertEqual(
|
||||
ET.tostring(ET.fromstring(
|
||||
ET.fromstring(
|
||||
view['arch'],
|
||||
parser=ET.XMLParser(remove_blank_text=True)
|
||||
)),
|
||||
'<form string="Replacement title" version="7.0">'
|
||||
'<p>Replacement data</p>'
|
||||
'<footer thing="bob">'
|
||||
'<button name="action_next" type="object" string="New button"/>'
|
||||
'</footer>'
|
||||
'</form>')
|
||||
),
|
||||
E.form(
|
||||
E.p("Replacement data"),
|
||||
E.footer(
|
||||
E.button(name="action_next", type="object", string="New button"),
|
||||
thing="bob"
|
||||
),
|
||||
string="Replacement title", version="7.0"))
|
||||
|
||||
def test_view_inheritance_divergent_models(self):
|
||||
Views = self.registry('ir.ui.view')
|
||||
|
@ -656,14 +794,13 @@ class test_views(common.TransactionCase):
|
|||
})
|
||||
self.assertEqual(view['type'], 'form')
|
||||
self.assertEqual(
|
||||
ET.tostring(ET.fromstring(
|
||||
ET.fromstring(
|
||||
view['arch'],
|
||||
parser=ET.XMLParser(remove_blank_text=True)
|
||||
)),
|
||||
'<form string="Replacement title" version="7.0">'
|
||||
'<p>Replacement data</p>'
|
||||
'<footer>'
|
||||
'<button name="action_next" type="object" string="New button"/>'
|
||||
'</footer>'
|
||||
'</form>')
|
||||
|
||||
),
|
||||
E.form(
|
||||
E.p("Replacement data"),
|
||||
E.footer(
|
||||
E.button(name="action_next", type="object", string="New button")),
|
||||
string="Replacement title", version="7.0"
|
||||
))
|
||||
|
|
|
@ -209,20 +209,19 @@ class WebRequest(object):
|
|||
# Backward for 7.0
|
||||
if self.endpoint.first_arg_is_req:
|
||||
args = (request,) + args
|
||||
|
||||
# Correct exception handling and concurency retry
|
||||
@service_model.check
|
||||
def checked_call(___dbname, *a, **kw):
|
||||
# The decorator can call us more than once if there is an database error. In this
|
||||
# case, the request cursor is unusable. Rollback transaction to create a new one.
|
||||
if self._cr:
|
||||
self._cr.rollback()
|
||||
return self.endpoint(*a, **kw)
|
||||
|
||||
# FIXME: code and rollback management could be cleaned
|
||||
try:
|
||||
if self.db:
|
||||
return checked_call(self.db, *args, **kwargs)
|
||||
return self.endpoint(*args, **kwargs)
|
||||
except Exception:
|
||||
if self._cr:
|
||||
self._cr.rollback()
|
||||
raise
|
||||
|
||||
@property
|
||||
def debug(self):
|
||||
|
@ -733,7 +732,7 @@ class OpenERPSession(werkzeug.contrib.sessions.Session):
|
|||
self.setdefault("uid", None)
|
||||
self.setdefault("login", None)
|
||||
self.setdefault("password", None)
|
||||
self.setdefault("context", {'tz': "UTC", "uid": None})
|
||||
self.setdefault("context", {})
|
||||
|
||||
def get_context(self):
|
||||
"""
|
||||
|
|
|
@ -236,15 +236,24 @@ class char(_column):
|
|||
class text(_column):
|
||||
_type = 'text'
|
||||
|
||||
|
||||
class html(text):
|
||||
_type = 'html'
|
||||
_symbol_c = '%s'
|
||||
def _symbol_f(x):
|
||||
if x is None or x == False:
|
||||
return None
|
||||
return html_sanitize(x)
|
||||
|
||||
_symbol_set = (_symbol_c, _symbol_f)
|
||||
def _symbol_set_html(self, value):
|
||||
if value is None or value is False:
|
||||
return None
|
||||
if not self._sanitize:
|
||||
return value
|
||||
return html_sanitize(value)
|
||||
|
||||
def __init__(self, string='unknown', sanitize=True, **args):
|
||||
super(html, self).__init__(string=string, **args)
|
||||
self._sanitize = sanitize
|
||||
# symbol_set redefinition because of sanitize specific behavior
|
||||
self._symbol_f = self._symbol_set_html
|
||||
self._symbol_set = (self._symbol_c, self._symbol_f)
|
||||
|
||||
import __builtin__
|
||||
|
||||
|
|
Loading…
Reference in New Issue