[MERGE] Forward-port of latest 7.0 bugfixes, up to rev. 5054 rev-id: odo@openerp.com-20130820091157-e5brwlxuhujf8rrd
bzr revid: chs@openerp.com-20130724085026-525l9apggc9yzx0h bzr revid: odo@openerp.com-20130730140644-1xih0as5jsks4pub bzr revid: dle@openerp.com-20130801130723-khgwjkglgsdn34fj bzr revid: odo@openerp.com-20130820091638-jpbcmh653bpa29em
This commit is contained in:
commit
fbd0758cb5
|
@ -21,3 +21,14 @@ Changelog
|
|||
``openerp.exceptions.RedirectWarning``.
|
||||
- Give a pair of new methods to ``res.config.settings`` and a helper to make
|
||||
them easier to use: ``get_config_warning()``.
|
||||
|
||||
|
||||
`7.0`
|
||||
-----
|
||||
|
||||
- Modules may now include an ``i18n_extra`` directory that will be treated like the
|
||||
default ``i18n`` directory. This is typically useful for manual translation files
|
||||
that are not managed by Launchpad's translation system. An example is l10n modules
|
||||
that depend on ``l10n_multilang``.
|
||||
|
||||
|
||||
|
|
|
@ -195,7 +195,8 @@ class ir_model(osv.osv):
|
|||
ctx = dict(context,
|
||||
field_name=vals['name'],
|
||||
field_state='manual',
|
||||
select=vals.get('select_level', '0'))
|
||||
select=vals.get('select_level', '0'),
|
||||
update_custom_fields=True)
|
||||
self.pool[vals['model']]._auto_init(cr, ctx)
|
||||
openerp.modules.registry.RegistryManager.signal_registry_change(cr.dbname)
|
||||
return res
|
||||
|
|
|
@ -445,6 +445,13 @@ class ir_translation(osv.osv):
|
|||
tools.trans_load(cr, base_trans_file, lang, verbose=False, module_name=module_name, context=context)
|
||||
context['overwrite'] = True # make sure the requested translation will override the base terms later
|
||||
|
||||
# i18n_extra folder is for additional translations handle manually (eg: for l10n_be)
|
||||
base_trans_extra_file = openerp.modules.get_module_resource(module_name, 'i18n_extra', base_lang_code + '.po')
|
||||
if base_trans_extra_file:
|
||||
_logger.info('module %s: loading extra base translation file %s for language %s', module_name, base_lang_code, lang)
|
||||
tools.trans_load(cr, base_trans_extra_file, lang, verbose=False, module_name=module_name, context=context)
|
||||
context['overwrite'] = True # make sure the requested translation will override the base terms later
|
||||
|
||||
# Step 2: then load the main translation file, possibly overriding the terms coming from the base language
|
||||
trans_file = openerp.modules.get_module_resource(module_name, 'i18n', lang_code + '.po')
|
||||
if trans_file:
|
||||
|
@ -452,6 +459,11 @@ class ir_translation(osv.osv):
|
|||
tools.trans_load(cr, trans_file, lang, verbose=False, module_name=module_name, context=context)
|
||||
elif lang_code != 'en_US':
|
||||
_logger.warning('module %s: no translation for language %s', module_name, lang_code)
|
||||
|
||||
trans_extra_file = openerp.modules.get_module_resource(module_name, 'i18n_extra', lang_code + '.po')
|
||||
if trans_extra_file:
|
||||
_logger.info('module %s: loading extra translation file (%s) for language %s', module_name, lang_code, lang)
|
||||
tools.trans_load(cr, trans_extra_file, lang, verbose=False, module_name=module_name, context=context)
|
||||
return True
|
||||
|
||||
|
||||
|
|
|
@ -100,7 +100,7 @@ class view(osv.osv):
|
|||
else:
|
||||
inferred_type = etree.fromstring(values['arch'].encode('utf8')).tag
|
||||
values['name'] = "%s %s" % (values['model'], inferred_type)
|
||||
return super(osv.osv, self).create(cr, uid, values, context)
|
||||
return super(view, self).create(cr, uid, values, context)
|
||||
|
||||
def _relaxng(self):
|
||||
if not self._relaxng_validator:
|
||||
|
@ -179,7 +179,7 @@ class view(osv.osv):
|
|||
if self.pool._init:
|
||||
# Module init currently in progress, only consider views from modules whose code was already loaded
|
||||
query = """SELECT v.id FROM ir_ui_view v LEFT JOIN ir_model_data md ON (md.model = 'ir.ui.view' AND md.res_id = v.id)
|
||||
WHERE v.inherit_id=%s AND v.model=%s AND md.module in %s
|
||||
WHERE v.inherit_id=%s AND v.model=%s AND (md.module IS NULL or md.module in %s)
|
||||
ORDER BY priority"""
|
||||
query_params = (view_id, model, tuple(self.pool._init_modules))
|
||||
else:
|
||||
|
|
|
@ -30,11 +30,13 @@ from openerp.tools.translate import _
|
|||
CURRENCY_DISPLAY_PATTERN = re.compile(r'(\w+)\s*(?:\((.*)\))?')
|
||||
|
||||
class res_currency(osv.osv):
|
||||
|
||||
def _current_rate(self, cr, uid, ids, name, arg, context=None):
|
||||
return self._get_current_rate(cr, uid, ids, name, arg, context=context)
|
||||
return self._get_current_rate(cr, uid, ids, context=context)
|
||||
|
||||
def _get_current_rate(self, cr, uid, ids, name, arg, context=None):
|
||||
def _current_rate_silent(self, cr, uid, ids, name, arg, context=None):
|
||||
return self._get_current_rate(cr, uid, ids, raise_on_no_rate=False, context=context)
|
||||
|
||||
def _get_current_rate(self, cr, uid, ids, raise_on_no_rate=True, context=None):
|
||||
if context is None:
|
||||
context = {}
|
||||
res = {}
|
||||
|
@ -52,9 +54,12 @@ class res_currency(osv.osv):
|
|||
if cr.rowcount:
|
||||
id, rate = cr.fetchall()[0]
|
||||
res[id] = rate
|
||||
elif not raise_on_no_rate:
|
||||
res[id] = 0
|
||||
else:
|
||||
raise osv.except_osv(_('Error!'),_("No currency rate associated for currency %d for the given period" % (id)))
|
||||
return res
|
||||
|
||||
_name = "res.currency"
|
||||
_description = "Currency"
|
||||
_columns = {
|
||||
|
@ -63,6 +68,10 @@ class res_currency(osv.osv):
|
|||
'symbol': fields.char('Symbol', size=4, help="Currency sign, to be used when printing amounts."),
|
||||
'rate': fields.function(_current_rate, string='Current Rate', digits=(12,6),
|
||||
help='The rate of the currency to the currency of rate 1.'),
|
||||
|
||||
# Do not use for computation ! Same as rate field with silent failing
|
||||
'rate_silent': fields.function(_current_rate_silent, string='Current Rate', digits=(12,6),
|
||||
help='The rate of the currency to the currency of rate 1 (0 if no rate defined).'),
|
||||
'rate_ids': fields.one2many('res.currency.rate', 'currency_id', 'Rates'),
|
||||
'accuracy': fields.integer('Computational Accuracy'),
|
||||
'rounding': fields.float('Rounding Factor', digits=(12,6)),
|
||||
|
|
|
@ -22,7 +22,7 @@
|
|||
<field name="company_id" groups="base.group_multi_company"/>
|
||||
<field name="rate_ids" invisible="1"/>
|
||||
<field name="date"/>
|
||||
<field name="rate"/>
|
||||
<field name="rate_silent"/>
|
||||
<field name="rounding"/>
|
||||
<field name="accuracy"/>
|
||||
<field name="position"/>
|
||||
|
@ -37,7 +37,7 @@
|
|||
<form string="Currency" version="7.0">
|
||||
<group col="4">
|
||||
<field name="name"/>
|
||||
<field name="rate"/>
|
||||
<field name="rate_silent"/>
|
||||
<field name="company_id" groups="base.group_multi_company"/>
|
||||
</group>
|
||||
|
||||
|
|
|
@ -398,7 +398,9 @@ def is_leaf(element, internal=False):
|
|||
INTERNAL_OPS += ('inselect',)
|
||||
return (isinstance(element, tuple) or isinstance(element, list)) \
|
||||
and len(element) == 3 \
|
||||
and element[1] in INTERNAL_OPS
|
||||
and element[1] in INTERNAL_OPS \
|
||||
and ((isinstance(element[0], basestring) and element[0])
|
||||
or element in (TRUE_LEAF, FALSE_LEAF))
|
||||
|
||||
|
||||
# --------------------------------------------------
|
||||
|
|
|
@ -380,16 +380,18 @@ class browse_record(object):
|
|||
raise KeyError(error_msg)
|
||||
|
||||
# if the field is a classic one or a many2one, we'll fetch all classic and many2one fields
|
||||
if col._prefetch:
|
||||
if col._prefetch and not col.groups:
|
||||
# gen the list of "local" (ie not inherited) fields which are classic or many2one
|
||||
fields_to_fetch = filter(lambda x: x[1]._classic_write and x[1]._prefetch, self._table._columns.items())
|
||||
field_filter = lambda x: x[1]._classic_write and x[1]._prefetch and not x[1].groups
|
||||
fields_to_fetch = filter(field_filter, self._table._columns.items())
|
||||
# gen the list of inherited fields
|
||||
inherits = map(lambda x: (x[0], x[1][2]), self._table._inherit_fields.items())
|
||||
# complete the field list with the inherited fields which are classic or many2one
|
||||
fields_to_fetch += filter(lambda x: x[1]._classic_write and x[1]._prefetch, inherits)
|
||||
fields_to_fetch += filter(field_filter, inherits)
|
||||
# otherwise we fetch only that field
|
||||
else:
|
||||
fields_to_fetch = [(name, col)]
|
||||
|
||||
ids = filter(lambda id: name not in self._data[id], self._data.keys())
|
||||
# read the results
|
||||
field_names = map(lambda x: x[0], fields_to_fetch)
|
||||
|
|
|
@ -109,7 +109,7 @@ class _date_format(str, _format):
|
|||
if self.val:
|
||||
if getattr(self,'name', None):
|
||||
date = datetime.strptime(self.name[:get_date_length()], DEFAULT_SERVER_DATE_FORMAT)
|
||||
return date.strftime(str(self.lang_obj.date_format))
|
||||
return date.strftime(self.lang_obj.date_format.encode('utf-8'))
|
||||
return self.val
|
||||
|
||||
class _dttime_format(str, _format):
|
||||
|
@ -120,8 +120,8 @@ class _dttime_format(str, _format):
|
|||
def __str__(self):
|
||||
if self.val and getattr(self,'name', None):
|
||||
return datetime.strptime(self.name, DEFAULT_SERVER_DATETIME_FORMAT)\
|
||||
.strftime("%s %s"%(str(self.lang_obj.date_format),
|
||||
str(self.lang_obj.time_format)))
|
||||
.strftime("%s %s"%((self.lang_obj.date_format).encode('utf-8'),
|
||||
(self.lang_obj.time_format).encode('utf-8')))
|
||||
return self.val
|
||||
|
||||
|
||||
|
@ -313,7 +313,7 @@ class rml_parse(object):
|
|||
date = datetime_field.context_timestamp(self.cr, self.uid,
|
||||
timestamp=date,
|
||||
context=self.localcontext)
|
||||
return date.strftime(date_format)
|
||||
return date.strftime(date_format.encode('utf-8'))
|
||||
|
||||
res = self.lang_dict['lang_obj'].format('%.' + str(digits) + 'f', value, grouping=grouping, monetary=monetary)
|
||||
if currency_obj:
|
||||
|
|
|
@ -9,6 +9,7 @@ import common
|
|||
# test group that demo user should not have
|
||||
GROUP_TECHNICAL_FEATURES = 'base.group_no_one'
|
||||
|
||||
|
||||
class TestACL(common.TransactionCase):
|
||||
|
||||
def setUp(self):
|
||||
|
@ -22,25 +23,25 @@ class TestACL(common.TransactionCase):
|
|||
|
||||
def test_field_visibility_restriction(self):
|
||||
"""Check that model-level ``groups`` parameter effectively restricts access to that
|
||||
field for users who do not belong to one of the explicitly allowed groups"""
|
||||
field for users who do not belong to one of the explicitly allowed groups"""
|
||||
# Verify the test environment first
|
||||
original_fields = self.res_currency.fields_get(self.cr, self.demo_uid, [])
|
||||
form_view = self.res_currency.fields_view_get(self.cr, self.demo_uid, False, 'form')
|
||||
view_arch = etree.fromstring(form_view.get('arch'))
|
||||
has_tech_feat = self.res_users.has_group(self.cr, self.demo_uid, GROUP_TECHNICAL_FEATURES)
|
||||
self.assertFalse(has_tech_feat, "`demo` user should not belong to the restricted group before the test")
|
||||
self.assertTrue('rate' in original_fields, "'rate' field must be properly visible before the test")
|
||||
self.assertNotEquals(view_arch.xpath("//field[@name='rate']"), [],
|
||||
"Field 'rate' must be found in view definition before the test")
|
||||
self.assertTrue('accuracy' in original_fields, "'accuracy' field must be properly visible before the test")
|
||||
self.assertNotEquals(view_arch.xpath("//field[@name='accuracy']"), [],
|
||||
"Field 'accuracy' must be found in view definition before the test")
|
||||
|
||||
# Restrict access to the field and check it's gone
|
||||
self.res_currency._columns['rate'].groups = GROUP_TECHNICAL_FEATURES
|
||||
self.res_currency._columns['accuracy'].groups = GROUP_TECHNICAL_FEATURES
|
||||
fields = self.res_currency.fields_get(self.cr, self.demo_uid, [])
|
||||
form_view = self.res_currency.fields_view_get(self.cr, self.demo_uid, False, 'form')
|
||||
view_arch = etree.fromstring(form_view.get('arch'))
|
||||
self.assertFalse('rate' in fields, "'rate' field should be gone")
|
||||
self.assertEquals(view_arch.xpath("//field[@name='rate']"), [],
|
||||
"Field 'rate' must not be found in view definition")
|
||||
self.assertFalse('accuracy' in fields, "'accuracy' field should be gone")
|
||||
self.assertEquals(view_arch.xpath("//field[@name='accuracy']"), [],
|
||||
"Field 'accuracy' must not be found in view definition")
|
||||
|
||||
# Make demo user a member of the restricted group and check that the field is back
|
||||
self.tech_group.write({'users': [(4, self.demo_uid)]})
|
||||
|
@ -50,13 +51,13 @@ class TestACL(common.TransactionCase):
|
|||
view_arch = etree.fromstring(form_view.get('arch'))
|
||||
#import pprint; pprint.pprint(fields); pprint.pprint(form_view)
|
||||
self.assertTrue(has_tech_feat, "`demo` user should now belong to the restricted group")
|
||||
self.assertTrue('rate' in fields, "'rate' field must be properly visible again")
|
||||
self.assertNotEquals(view_arch.xpath("//field[@name='rate']"), [],
|
||||
"Field 'rate' must be found in view definition again")
|
||||
self.assertTrue('accuracy' in fields, "'accuracy' field must be properly visible again")
|
||||
self.assertNotEquals(view_arch.xpath("//field[@name='accuracy']"), [],
|
||||
"Field 'accuracy' must be found in view definition again")
|
||||
|
||||
#cleanup
|
||||
self.tech_group.write({'users': [(3, self.demo_uid)]})
|
||||
self.res_currency._columns['rate'].groups = False
|
||||
self.res_currency._columns['accuracy'].groups = False
|
||||
|
||||
@mute_logger('openerp.osv.orm')
|
||||
def test_field_crud_restriction(self):
|
||||
|
@ -65,7 +66,7 @@ class TestACL(common.TransactionCase):
|
|||
has_tech_feat = self.res_users.has_group(self.cr, self.demo_uid, GROUP_TECHNICAL_FEATURES)
|
||||
self.assertFalse(has_tech_feat, "`demo` user should not belong to the restricted group")
|
||||
self.assert_(self.res_partner.read(self.cr, self.demo_uid, [1], ['bank_ids']))
|
||||
self.assert_(self.res_partner.write(self.cr, self.demo_uid, [1], {'bank_ids': []}))
|
||||
self.assert_(self.res_partner.write(self.cr, self.demo_uid, [1], {'bank_ids': []}))
|
||||
|
||||
# Now restrict access to the field and check it's forbidden
|
||||
self.res_partner._columns['bank_ids'].groups = GROUP_TECHNICAL_FEATURES
|
||||
|
@ -79,12 +80,30 @@ class TestACL(common.TransactionCase):
|
|||
has_tech_feat = self.res_users.has_group(self.cr, self.demo_uid, GROUP_TECHNICAL_FEATURES)
|
||||
self.assertTrue(has_tech_feat, "`demo` user should now belong to the restricted group")
|
||||
self.assert_(self.res_partner.read(self.cr, self.demo_uid, [1], ['bank_ids']))
|
||||
self.assert_(self.res_partner.write(self.cr, self.demo_uid, [1], {'bank_ids': []}))
|
||||
|
||||
self.assert_(self.res_partner.write(self.cr, self.demo_uid, [1], {'bank_ids': []}))
|
||||
|
||||
#cleanup
|
||||
self.tech_group.write({'users': [(3, self.demo_uid)]})
|
||||
self.res_partner._columns['bank_ids'].groups = False
|
||||
|
||||
def test_fields_browse_restriction(self):
|
||||
"""Test access to records having restricted fields"""
|
||||
self.res_partner._columns['email'].groups = GROUP_TECHNICAL_FEATURES
|
||||
try:
|
||||
P = self.res_partner
|
||||
pid = P.search(self.cr, self.demo_uid, [], limit=1)[0]
|
||||
part = P.browse(self.cr, self.demo_uid, pid)
|
||||
# accessing fields must no raise exceptions...
|
||||
part.name
|
||||
# ... except they are restricted
|
||||
with self.assertRaises(openerp.osv.orm.except_orm) as cm:
|
||||
part.email
|
||||
|
||||
self.assertEqual(cm.exception.args[0], 'Access Denied')
|
||||
finally:
|
||||
self.res_partner._columns['email'].groups = False
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest2.main()
|
||||
|
||||
|
|
|
@ -433,6 +433,7 @@ def get_iso_codes(lang):
|
|||
|
||||
ALL_LANGUAGES = {
|
||||
'ab_RU': u'Abkhazian / аҧсуа',
|
||||
'am_ET': u'Amharic / አምሃርኛ',
|
||||
'ar_SY': u'Arabic / الْعَرَبيّة',
|
||||
'bg_BG': u'Bulgarian / български език',
|
||||
'bs_BS': u'Bosnian / bosanski jezik',
|
||||
|
|
Loading…
Reference in New Issue