[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:
commit
ab386c0f5f
|
@ -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
|
||||
);
|
||||
|
||||
---------------------------------
|
||||
|
|
|
@ -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
|
||||
|
||||
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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')
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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)]"/>
|
||||
|
|
|
@ -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)]
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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."""
|
||||
|
|
|
@ -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('.', '_'))
|
||||
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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':
|
||||
|
|
|
@ -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):
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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 / മലയാളം',
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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)
|
||||
|
|
Loading…
Reference in New Issue