[MERGE] forward port of branch saas-2 up to revid 4984 chs@openerp.com-20131125151017-lmj53bxg1pfrr4a7

bzr revid: chs@openerp.com-20131125154140-cpbk4tcdml2a3b9n
This commit is contained in:
Christophe Simonis 2013-11-25 16:41:40 +01:00
commit ab386c0f5f
24 changed files with 230 additions and 121 deletions

View File

@ -25,25 +25,25 @@ create table ir_values
CREATE TABLE ir_model (
id serial,
model varchar(64) DEFAULT ''::varchar NOT NULL,
name varchar(64),
state varchar(16),
model varchar DEFAULT ''::varchar NOT NULL,
name varchar,
state varchar,
info text,
primary key(id)
);
CREATE TABLE ir_model_fields (
id serial,
model varchar(64) DEFAULT ''::varchar NOT NULL,
model varchar DEFAULT ''::varchar NOT NULL,
model_id int references ir_model on delete cascade,
name varchar(64) DEFAULT ''::varchar NOT NULL,
relation varchar(64),
select_level varchar(4),
field_description varchar(256),
ttype varchar(64),
state varchar(64) default 'base',
name varchar DEFAULT ''::varchar NOT NULL,
relation varchar,
select_level varchar,
field_description varchar,
ttype varchar,
state varchar default 'base',
relate boolean default False,
relation_field varchar(128),
relation_field varchar,
translate boolean default False,
primary key(id)
);
@ -350,11 +350,11 @@ CREATE TABLE ir_model_data (
write_date timestamp without time zone,
write_uid integer,
noupdate boolean,
name character varying(128) NOT NULL,
name varchar NOT NULL,
date_init timestamp without time zone,
date_update timestamp without time zone,
module character varying(64) NOT NULL,
model character varying(64) NOT NULL,
module varchar NOT NULL,
model varchar NOT NULL,
res_id integer, primary key(id)
);
@ -373,7 +373,7 @@ CREATE TABLE ir_model_constraint (
module integer NOT NULL references ir_module_module on delete restrict,
model integer NOT NULL references ir_model on delete restrict,
type character varying(1) NOT NULL,
name character varying(128) NOT NULL
name varchar NOT NULL
);
-- Records relation tables (i.e. implementing many2many) installed by a module
@ -388,7 +388,7 @@ CREATE TABLE ir_model_relation (
date_update timestamp without time zone,
module integer NOT NULL references ir_module_module on delete restrict,
model integer NOT NULL references ir_model on delete restrict,
name character varying(128) NOT NULL
name varchar NOT NULL
);
---------------------------------

View File

@ -2,7 +2,7 @@
##############################################################################
#
# OpenERP, Open Source Management Solution
# Copyright (C) 2004-2011 OpenERP S.A. <http://www.openerp.com>
# Copyright (C) 2004-2013 OpenERP S.A. <http://www.openerp.com>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
@ -801,7 +801,7 @@ class ir_actions_server(osv.osv):
raise osv.except_osv(_('Error'), _("Please specify an action to launch!"))
return self.pool[action.action_id.type].read(cr, uid, action.action_id.id, context=context)
def run_action_code(self, cr, uid, action, eval_context=None, context=None):
def run_action_code_multi(self, cr, uid, action, eval_context=None, context=None):
eval(action.code.strip(), eval_context, mode="exec", nocopy=True) # nocopy allows to return 'action'
if 'action' in eval_context:
return eval_context['action']
@ -933,38 +933,50 @@ class ir_actions_server(osv.osv):
context = {}
res = False
user = self.pool.get('res.users').browse(cr, uid, uid)
active_ids = context.get('active_ids', [context.get('active_id', None)])
active_ids = context.get('active_ids', [context.get('active_id')])
for action in self.browse(cr, uid, ids, context):
obj = None
obj_pool = self.pool[action.model_id.model]
for active_id in active_ids:
if context.get('active_model') == action.model_id.model and active_id:
obj = obj_pool.browse(cr, uid, context['active_id'], context=context)
# run context dedicated to a particular active_id
run_context = dict(context, active_ids=[active_id], active_id=active_id)
# evaluation context for python strings to evaluate
eval_context = {
'self': obj_pool,
'object': obj,
'obj': obj,
'pool': self.pool,
'time': time,
'cr': cr,
'context': dict(run_context), # copy context to prevent side-effects of eval
'uid': uid,
'user': user
}
obj = None
if context.get('active_model') == action.model_id.model and context.get('active_id'):
obj = obj_pool.browse(cr, uid, context['active_id'], context=context)
# evaluate the condition, with the specific case that a void (aka False) condition is considered as True
condition = action.condition
if action.condition is False:
condition = True
# evaluation context for python strings to evaluate
eval_context = {
'self': obj_pool,
'object': obj,
'obj': obj,
'pool': self.pool,
'time': time,
'cr': cr,
'uid': uid,
'user': user,
}
condition = action.condition
if condition is False:
# Void (aka False) conditions are considered as True
condition = True
if hasattr(self, 'run_action_%s_multi' % action.state):
# set active_ids in context only needed if one active_id
run_context = dict(context, active_ids=active_ids)
eval_context["context"] = run_context
expr = eval(str(condition), eval_context)
if not expr:
continue
# call the method related to the action: run_action_<STATE>
if hasattr(self, 'run_action_%s' % action.state):
res = getattr(self, 'run_action_%s' % action.state)(cr, uid, action, eval_context=eval_context, context=run_context)
# call the multi method
func = getattr(self, 'run_action_%s_multi' % action.state)
res = func(cr, uid, action, eval_context=eval_context, context=run_context)
elif hasattr(self, 'run_action_%s' % action.state):
func = getattr(self, 'run_action_%s' % action.state)
for active_id in active_ids:
# run context dedicated to a particular active_id
run_context = dict(context, active_ids=[active_id], active_id=active_id)
eval_context["context"] = run_context
expr = eval(str(condition), eval_context)
if not expr:
continue
# call the single method related to the action: run_action_<STATE>
res = func(cr, uid, action, eval_context=eval_context, context=run_context)
return res

View File

@ -476,7 +476,8 @@
<field name="model_id"/>
<field name="state"/>
<group expand="0" string="Group By" colspan="4" col="4">
<filter string="Action Type" icon="terp-stock_symbol-selection" domain="[]" context="{'group_by':'state'}"/>
<filter string="Action Type" domain="[]" context="{'group_by':'state'}"/>
<filter string="Model" domain="[]" context="{'group_by':'model_id'}"/>
</group>
</search>
</field>

View File

@ -198,7 +198,7 @@ class ir_attachment(osv.osv):
continue
res_ids.setdefault(rmod,set()).add(rid)
if values:
if values.get('res_model') and 'res_id' in values:
if values.get('res_model') and values.get('res_id'):
res_ids.setdefault(values['res_model'],set()).add(values['res_id'])
ima = self.pool.get('ir.model.access')

View File

@ -253,7 +253,7 @@ class ir_fields_converter(orm.Model):
if not isinstance(selection, (tuple, list)):
# FIXME: Don't pass context to avoid translations?
# Or just copy context & remove lang?
selection = selection(model, cr, uid)
selection = selection(model, cr, uid, context=None)
for item, label in selection:
labels = self._get_translations(
cr, uid, ('selection', 'model', 'code'), label, context=context)

View File

@ -96,8 +96,8 @@ class ir_model(osv.osv):
return res
_columns = {
'name': fields.char('Model Description', size=64, translate=True, required=True),
'model': fields.char('Model', size=64, required=True, select=1),
'name': fields.char('Model Description', translate=True, required=True),
'model': fields.char('Model', required=True, select=1),
'info': fields.text('Information'),
'field_id': fields.one2many('ir.model.fields', 'model_id', 'Fields', required=True),
'state': fields.selection([('manual','Custom Object'),('base','Base Object')],'Type',readonly=True),
@ -105,7 +105,7 @@ class ir_model(osv.osv):
'osv_memory': fields.function(_is_osv_memory, string='Transient Model', type='boolean',
fnct_search=_search_osv_memory,
help="This field specifies whether the model is transient or not (i.e. if records are automatically deleted from the database or not)"),
'modules': fields.function(_in_modules, type='char', size=128, string='In Modules', help='List of modules in which the object is defined or inherited'),
'modules': fields.function(_in_modules, type='char', string='In Modules', help='List of modules in which the object is defined or inherited'),
'view_ids': fields.function(_view_ids, type='one2many', obj='ir.ui.view', string='Views'),
}
@ -223,19 +223,19 @@ class ir_model_fields(osv.osv):
_rec_name = 'field_description'
_columns = {
'name': fields.char('Name', required=True, size=64, select=1),
'complete_name': fields.char('Complete Name', size=64, select=1),
'model': fields.char('Object Name', size=64, required=True, select=1,
'name': fields.char('Name', required=True, select=1),
'complete_name': fields.char('Complete Name', select=1),
'model': fields.char('Object Name', required=True, select=1,
help="The technical name of the model this field belongs to"),
'relation': fields.char('Object Relation', size=64,
'relation': fields.char('Object Relation',
help="For relationship fields, the technical name of the target model"),
'relation_field': fields.char('Relation Field', size=64,
'relation_field': fields.char('Relation Field',
help="For one2many fields, the field on the target model that implement the opposite many2one relationship"),
'model_id': fields.many2one('ir.model', 'Model', required=True, select=True, ondelete='cascade',
help="The model this field belongs to"),
'field_description': fields.char('Field Label', required=True, size=256),
'ttype': fields.selection(_get_fields_type, 'Field Type',size=64, required=True),
'selection': fields.char('Selection Options',size=128, help="List of options for a selection field, "
'field_description': fields.char('Field Label', required=True),
'ttype': fields.selection(_get_fields_type, 'Field Type', required=True),
'selection': fields.char('Selection Options', help="List of options for a selection field, "
"specified as a Python expression defining a list of (key, label) pairs. "
"For example: [('blue','Blue'),('yellow','Yellow')]"),
'required': fields.boolean('Required'),
@ -245,12 +245,12 @@ class ir_model_fields(osv.osv):
'size': fields.integer('Size'),
'state': fields.selection([('manual','Custom Field'),('base','Base Field')],'Type', required=True, readonly=True, select=1),
'on_delete': fields.selection([('cascade','Cascade'),('set null','Set NULL')], 'On Delete', help='On delete property for many2one fields'),
'domain': fields.char('Domain', size=256, help="The optional domain to restrict possible values for relationship fields, "
'domain': fields.char('Domain', help="The optional domain to restrict possible values for relationship fields, "
"specified as a Python expression defining a list of triplets. "
"For example: [('color','=','red')]"),
'groups': fields.many2many('res.groups', 'ir_model_fields_group_rel', 'field_id', 'group_id', 'Groups'),
'selectable': fields.boolean('Selectable'),
'modules': fields.function(_in_modules, type='char', size=128, string='In Modules', help='List of modules in which the field is defined'),
'modules': fields.function(_in_modules, type='char', string='In Modules', help='List of modules in which the field is defined'),
'serialization_field_id': fields.many2one('ir.model.fields', 'Serialization Field', domain = "[('ttype','=','serialized')]",
ondelete='cascade', help="If set, this field will be stored in the sparse "
"structure of the serialization field, instead "
@ -483,7 +483,7 @@ class ir_model_constraint(Model):
"""
_name = 'ir.model.constraint'
_columns = {
'name': fields.char('Constraint', required=True, size=128, select=1,
'name': fields.char('Constraint', required=True, select=1,
help="PostgreSQL constraint or foreign key name."),
'model': fields.many2one('ir.model', string='Model',
required=True, select=1),
@ -552,7 +552,7 @@ class ir_model_relation(Model):
"""
_name = 'ir.model.relation'
_columns = {
'name': fields.char('Relation Name', required=True, size=128, select=1,
'name': fields.char('Relation Name', required=True, select=1,
help="PostgreSQL table name implementing a many2many relation."),
'model': fields.many2one('ir.model', string='Model',
required=True, select=1),
@ -601,7 +601,7 @@ class ir_model_relation(Model):
class ir_model_access(osv.osv):
_name = 'ir.model.access'
_columns = {
'name': fields.char('Name', size=64, required=True, select=True),
'name': fields.char('Name', required=True, select=True),
'active': fields.boolean('Active', help='If you uncheck the active field, it will disable the ACL without deleting it (if you delete a native ACL, it will be re-created when you reload the module.'),
'model_id': fields.many2one('ir.model', 'Object', required=True, domain=[('osv_memory','=', False)], select=True, ondelete='cascade'),
'group_id': fields.many2one('res.groups', 'Group', ondelete='cascade', select=True),
@ -815,13 +815,13 @@ class ir_model_data(osv.osv):
return result
_columns = {
'name': fields.char('External Identifier', required=True, size=128, select=1,
'name': fields.char('External Identifier', required=True, select=1,
help="External Key/Identifier that can be used for "
"data integration with third-party systems"),
'complete_name': fields.function(_complete_name_get, type='char', string='Complete ID'),
'display_name': fields.function(_display_name_get, type='char', string='Record Name'),
'model': fields.char('Model Name', required=True, size=64, select=1),
'module': fields.char('Module', required=True, size=64, select=1),
'model': fields.char('Model Name', required=True, select=1),
'module': fields.char('Module', required=True, select=1),
'res_id': fields.integer('Record ID', select=1,
help="ID of the target record in the database"),
'noupdate': fields.boolean('Non Updatable'),
@ -1128,7 +1128,7 @@ class ir_model_data(osv.osv):
return True
to_unlink = []
cr.execute("""SELECT id,name,model,res_id,module FROM ir_model_data
WHERE module IN %s AND res_id IS NOT NULL AND noupdate=%s""",
WHERE module IN %s AND res_id IS NOT NULL AND noupdate=%s ORDER BY id DESC""",
(tuple(modules), False))
for (id, name, model, res_id, module) in cr.fetchall():
if (module,name) not in self.loads:

View File

@ -182,7 +182,7 @@ class ir_translation(osv.osv):
if context is None:
context = {}
record = self.browse(cr, uid, id, context=context)
if value and record.type == 'model':
if record.type == 'model':
model_name, field = record.name.split(',')
model = self.pool.get(model_name)
#We need to take the context without the language information, because we want to write on the

View File

@ -1212,7 +1212,7 @@
<record id="ve" model="res.country">
<field name="name">Venezuela</field>
<field name="code">ve</field>
<field name="currency_id" ref="VUB"/>
<field name="currency_id" ref="VEF"/>
</record>
<record id="vg" model="res.country">
<field name="name">Virgin Islands (British)</field>

View File

@ -1540,7 +1540,7 @@
</record>
<record id="VUB" model="res.currency">
<field name="name">VUB</field>
<field name="name">VEB</field>
<field name="symbol">Bs</field>
<field name="rounding">0.01</field>
<field name="accuracy">4</field>

View File

@ -329,7 +329,7 @@
<field name="arch" type="xml">
<search string="Search Partner">
<field name="name"
filter_domain="['|','|',('display_name','ilike',self),('ref','=',self),('email,'ilike',self)]"/>
filter_domain="['|','|',('display_name','ilike',self),('ref','=',self),('email','ilike',self)]"/>
<filter help="My Partners" icon="terp-personal+" domain="[('user_id','=',uid)]"/>
<separator/>
<filter string="Persons" name="type_person" domain="[('is_company','=',0)]"/>

View File

@ -169,7 +169,8 @@ class res_users(osv.osv):
}
def on_change_login(self, cr, uid, ids, login, context=None):
return {'value': {'email': login}}
v = {'email': login} if tools.single_email_re.match(login) else {}
return {'value': v}
def onchange_state(self, cr, uid, ids, state_id, context=None):
partner_ids = [user.partner_id.id for user in self.browse(cr, uid, ids, context=context)]

View File

@ -304,5 +304,47 @@ class test_partner_recursion(common.TransactionCase):
cr, uid, p1, p2, p3 = self.cr, self.uid, self.p1, self.p2, self.p3
self.assertTrue(self.res_partner.write(cr, uid, [p1,p2,p3], {'phone': '123456'}))
class test_translation(common.TransactionCase):
def setUp(self):
super(test_translation, self).setUp()
self.res_category = self.registry('res.partner.category')
self.ir_translation = self.registry('ir.translation')
cr, uid = self.cr, self.uid
self.registry('ir.translation').load(cr, ['base'], ['fr_FR'])
self.cat_id = self.res_category.create(cr, uid, {'name': 'Customers'})
self.ir_translation.create(cr, uid, {'name': 'res.partner.category,name', 'module':'base',
'value': 'Clients', 'res_id': self.cat_id, 'lang':'fr_FR', 'state':'translated', 'type': 'model'})
def test_101_create_translated_record(self):
cr, uid = self.cr, self.uid
no_context_cat = self.res_category.browse(cr, uid, self.cat_id)
self.assertEqual(no_context_cat.name, 'Customers', "Error in basic name_get")
fr_context_cat = self.res_category.browse(cr, uid, self.cat_id, context={'lang':'fr_FR'})
self.assertEqual(fr_context_cat.name, 'Clients', "Translation not found")
def test_102_duplicate_record(self):
cr, uid = self.cr, self.uid
self.new_cat_id = self.res_category.copy(cr, uid, self.cat_id, context={'lang':'fr_FR'})
no_context_cat = self.res_category.browse(cr, uid, self.new_cat_id)
self.assertEqual(no_context_cat.name, 'Customers', "Duplication did not set untranslated value")
fr_context_cat = self.res_category.browse(cr, uid, self.new_cat_id, context={'lang':'fr_FR'})
self.assertEqual(fr_context_cat.name, 'Clients', "Did not found translation for initial value")
def test_103_duplicate_record_fr(self):
cr, uid = self.cr, self.uid
self.new_fr_cat_id = self.res_category.copy(cr, uid, self.cat_id, default={'name': 'Clients (copie)'}, context={'lang':'fr_FR'})
no_context_cat = self.res_category.browse(cr, uid, self.new_fr_cat_id)
self.assertEqual(no_context_cat.name, 'Customers', "Duplication erased original untranslated value")
fr_context_cat = self.res_category.browse(cr, uid, self.new_fr_cat_id, context={'lang':'fr_FR'})
self.assertEqual(fr_context_cat.name, 'Clients (copie)', "Did not used default value for translated value")
if __name__ == '__main__':
unittest2.main()

View File

@ -40,6 +40,19 @@ class test_views(common.TransactionCase):
""",
})
def test_20_remove_unexisting_attribute(self):
Views = self.registry('ir.ui.view')
Views.create(self.cr, self.uid, {
'name': 'Test View',
'model': 'ir.ui.view',
'inherit_id': self.browse_ref('base.view_view_tree').id,
'arch': """<?xml version="1.0"?>
<xpath expr="//field[@name='name']" position="attributes">
<attribute name="non_existing_attribute"></attribute>
</xpath>
""",
})
def _insert_view(self, **kw):
"""Insert view into database via a query to passtrough validation"""
kw.pop('id', None)

View File

@ -94,11 +94,11 @@ def preload_registry(dbname):
""" Preload a registry, and start the cron."""
try:
update_module = True if openerp.tools.config['init'] or openerp.tools.config['update'] else False
openerp.modules.registry.RegistryManager.new(dbname, update_module=update_module)
registry = openerp.modules.registry.RegistryManager.new(dbname, update_module=update_module)
except Exception:
_logger.exception('Failed to initialize database `%s`.', dbname)
return False
return True
return registry._assertion_report.failures == 0
def run_test_file(dbname, test_file):
""" Preload a registry, possibly run a test file, and start the cron."""

View File

@ -342,7 +342,7 @@ def load_modules(db, force_demo=False, status=None, update_module=False):
if processed_modules:
cr.execute("""select model,name from ir_model where id NOT IN (select distinct model_id from ir_model_access)""")
for (model, name) in cr.fetchall():
if model in registry and not registry[model].is_transient():
if model in registry and not registry[model].is_transient() and isinstance(registry[model], openerp.osv.orm.AbstractModel):
_logger.warning('The model %s has no access rules, consider adding one. E.g. access_%s,access_%s,model_%s,,1,1,1,1',
model, model.replace('.', '_'), model.replace('.', '_'), model.replace('.', '_'))

View File

@ -199,15 +199,16 @@ class RegistryManager(object):
@classmethod
def get(cls, db_name, force_demo=False, status=None, update_module=False):
""" Return a registry for a given database name."""
try:
return cls.registries[db_name]
except KeyError:
return cls.new(db_name, force_demo, status,
update_module)
finally:
# set db tracker - cleaned up at the WSGI
# dispatching phase in openerp.service.wsgi_server.application
threading.current_thread().dbname = db_name
with cls.registries_lock:
try:
return cls.registries[db_name]
except KeyError:
return cls.new(db_name, force_demo, status,
update_module)
finally:
# set db tracker - cleaned up at the WSGI
# dispatching phase in openerp.service.wsgi_server.application
threading.current_thread().dbname = db_name
@classmethod
def new(cls, db_name, force_demo=False, status=None,

View File

@ -1147,7 +1147,7 @@ class function(_column):
# make the result a tuple if it is not already one
if isinstance(value, (int,long)) and hasattr(obj._columns[field], 'relation'):
obj_model = obj.pool[obj._columns[field].relation]
dict_names = dict(obj_model.name_get(cr, uid, [value], context))
dict_names = dict(obj_model.name_get(cr, SUPERUSER_ID, [value], context))
result = (value, dict_names[value])
if field_type == 'binary':

View File

@ -399,7 +399,15 @@ class browse_record(object):
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)
field_values = self._table.read(self._cr, self._uid, ids, field_names, context=self._context, load="_classic_write")
try:
field_values = self._table.read(self._cr, self._uid, ids, field_names, context=self._context, load="_classic_write")
except (openerp.exceptions.AccessError, except_orm):
if len(ids) == 1:
raise
# prefetching attempt failed, perhaps we're violating ACL restrictions involuntarily
_logger.info('Prefetching attempt for fields %s on %s failed for ids %s, re-trying just for id %s', field_names, self._model._name, ids, self._id)
ids = [self._id]
field_values = self._table.read(self._cr, self._uid, ids, field_names, context=self._context, load="_classic_write")
# TODO: improve this, very slow for reports
if self._fields_process:
@ -2153,8 +2161,8 @@ class BaseModel(object):
attribute = (child.get('name'), child.text and child.text.encode('utf8') or None)
if attribute[1]:
node.set(attribute[0], attribute[1])
else:
del(node.attrib[attribute[0]])
elif attribute[0] in node.attrib:
del node.attrib[attribute[0]]
else:
sib = node.getnext()
for child in spec:
@ -4997,10 +5005,7 @@ class BaseModel(object):
else:
default['state'] = self._defaults['state']
context_wo_lang = context.copy()
if 'lang' in context:
del context_wo_lang['lang']
data = self.read(cr, uid, [id,], context=context_wo_lang)
data = self.read(cr, uid, [id,], context=context)
if data:
data = data[0]
else:
@ -5060,36 +5065,50 @@ class BaseModel(object):
# TODO it seems fields_get can be replaced by _all_columns (no need for translation)
fields = self.fields_get(cr, uid, context=context)
translation_records = []
for field_name, field_def in fields.items():
# removing the lang to compare untranslated values
context_wo_lang = dict(context, lang=None)
old_record, new_record = self.browse(cr, uid, [old_id, new_id], context=context_wo_lang)
# we must recursively copy the translations for o2o and o2m
if field_def['type'] == 'one2many':
target_obj = self.pool[field_def['relation']]
old_record, new_record = self.read(cr, uid, [old_id, new_id], [field_name], context=context)
# here we rely on the order of the ids to match the translations
# as foreseen in copy_data()
old_children = sorted(old_record[field_name])
new_children = sorted(new_record[field_name])
old_children = sorted(r.id for r in old_record[field_name])
new_children = sorted(r.id for r in new_record[field_name])
for (old_child, new_child) in zip(old_children, new_children):
target_obj.copy_translations(cr, uid, old_child, new_child, context=context)
# and for translatable fields we keep them for copy
elif field_def.get('translate'):
trans_name = ''
if field_name in self._columns:
trans_name = self._name + "," + field_name
target_id = new_id
source_id = old_id
elif field_name in self._inherit_fields:
trans_name = self._inherit_fields[field_name][0] + "," + field_name
if trans_name:
trans_ids = trans_obj.search(cr, uid, [
('name', '=', trans_name),
('res_id', '=', old_id)
])
translation_records.extend(trans_obj.read(cr, uid, trans_ids, context=context))
# get the id of the parent record to set the translation
inherit_field_name = self._inherit_fields[field_name][1]
target_id = new_record[inherit_field_name].id
source_id = old_record[inherit_field_name].id
else:
continue
for record in translation_records:
del record['id']
record['res_id'] = new_id
trans_obj.create(cr, uid, record, context=context)
trans_ids = trans_obj.search(cr, uid, [
('name', '=', trans_name),
('res_id', '=', source_id)
])
user_lang = context.get('lang')
for record in trans_obj.read(cr, uid, trans_ids, context=context):
del record['id']
# remove source to avoid triggering _set_src
del record['source']
record.update({'res_id': target_id})
if user_lang and user_lang == record['lang']:
# 'source' to force the call to _set_src
# 'value' needed if value is changed in copy(), want to see the new_value
record['source'] = old_record[field_name]
record['value'] = new_record[field_name]
trans_obj.create(cr, uid, record, context=context)
def copy(self, cr, uid, id, default=None, context=None):

View File

@ -242,7 +242,7 @@ class _rml_styles(object,):
if sname in self.styles_obj:
style = self.styles_obj[sname]
else:
_logger.warning('Warning: style not found, %s - setting default!\n' % (node.get('style'),) )
_logger.debug('Warning: style not found, %s - setting default!', node.get('style'))
if not style:
style = self.default_style['Normal']
para_update = self._para_style_update(node)
@ -631,6 +631,18 @@ class _rml_Illustration(platypus.flowables.Flowable):
drw = _rml_draw(self.localcontext ,self.node,self.styles, images=self.self2.images, path=self.self2.path, title=self.self2.title)
drw.render(self.canv, None)
# Workaround for issue #15: https://bitbucket.org/rptlab/reportlab/issue/15/infinite-pages-produced-when-splitting
original_pto_split = platypus.flowables.PTOContainer.split
def split(self, availWidth, availHeight):
res = original_pto_split(self, availWidth, availHeight)
if len(res) > 2 and len(self._content) > 0:
header = self._content[0]._ptoinfo.header
trailer = self._content[0]._ptoinfo.trailer
if isinstance(res[-2], platypus.flowables.UseUpSpace) and len(header + trailer) == len(res[:-2]):
return []
return res
platypus.flowables.PTOContainer.split = split
class _rml_flowable(object):
def __init__(self, doc, localcontext, images=None, path='.', title=None, canvas=None):
if images is None:
@ -1012,11 +1024,16 @@ class _rml_template(object):
# Reset Page Number with new story tag
fis.append(PageReset())
story_cnt += 1
if self.localcontext and self.localcontext.get('internal_header',False):
self.doc_tmpl.afterFlowable(fis)
self.doc_tmpl.build(fis,canvasmaker=NumberedCanvas)
else:
self.doc_tmpl.build(fis)
try:
if self.localcontext and self.localcontext.get('internal_header',False):
self.doc_tmpl.afterFlowable(fis)
self.doc_tmpl.build(fis,canvasmaker=NumberedCanvas)
else:
self.doc_tmpl.build(fis)
except platypus.doctemplate.LayoutError, e:
e.name = 'Print Error'
e.value = 'The document you are trying to print contains a table row that does not fit on one page. Please try to split it in smaller rows or contact your administrator.'
raise
def parseNode(rml, localcontext=None, fout=None, images=None, path='.', title=None):
node = etree.XML(rml)

View File

@ -95,9 +95,10 @@ class TestACL(common.TransactionCase):
part = P.browse(self.cr, self.demo_uid, pid)
# accessing fields must no raise exceptions...
part.name
# ... except they are restricted
# ... except if they are restricted
with self.assertRaises(openerp.osv.orm.except_orm) as cm:
part.email
with mute_logger('openerp.osv.orm'):
part.email
self.assertEqual(cm.exception.args[0], 'Access Denied')
finally:

View File

@ -87,7 +87,9 @@ def html_sanitize(src, silent=True):
# some corner cases make the parser crash (such as <SCRIPT/XSS SRC=\"http://ha.ckers.org/xss.js\"></SCRIPT> in test_mail)
cleaner = clean.Cleaner(**kwargs)
cleaned = cleaner.clean_html(src)
except etree.ParserError:
except etree.ParserError, e:
if 'empty' in str(e):
return ""
if not silent:
raise
logger.warning('ParserError obtained when sanitizing %r', src, exc_info=True)

View File

@ -470,6 +470,7 @@ ALL_LANGUAGES = {
'ja_JP': u'Japanese / 日本語',
'ko_KP': u'Korean (KP) / 한국어 (KP)',
'ko_KR': u'Korean (KR) / 한국어 (KR)',
'lo_LA': u'Lao / ພາສາລາວ',
'lt_LT': u'Lithuanian / Lietuvių kalba',
'lv_LV': u'Latvian / latviešu valoda',
'ml_IN': u'Malayalam / മലയാളം',

View File

@ -448,9 +448,9 @@ class YamlInterpreter(object):
result = getattr(model, match.group(1))(self.cr, SUPERUSER_ID, [], *args)
for key, val in (result or {}).get('value', {}).items():
assert key in fg, "The returning field '%s' from your on_change call '%s' does not exist either on the object '%s', either in the view '%s' used for the creation" % (key, match.group(1), model._name, view_info['name'])
record_dict[key] = process_val(key, val)
#if (key in fields) and record_dict[key] == process_val(key, val):
# print '*** You can remove these lines:', key, val
if key not in fields:
# do not shadow values explicitly set in yaml.
record_dict[key] = process_val(key, val)
else:
nodes = list(el) + nodes
else:

View File

@ -40,8 +40,7 @@ def get_addons_from_paths(paths, exclude):
module_names = []
for p in paths:
if os.path.exists(p):
names = list(set(os.listdir(p)))
names = filter(lambda a: not (a.startswith('.') or a in exclude), names)
names = [n for n in os.listdir(p) if os.path.isfile(os.path.join(p, n, '__openerp__.py')) and not n.startswith('.') and n not in exclude]
names = filter(lambda a: os.path.isdir(os.path.join(p, a)), names)
names = filter(lambda a: os.path.exists(os.path.join(p, a, '__openerp__.py')), names)
module_names.extend(names)