[MERGE] merged trunk-dev-framework branch with latest changes: ir.rule refactoring, yaml improvements,...

bzr revid: odo@openerp.com-20100504010008-y2609d3pqcx8dqor
This commit is contained in:
OpenERP Framework R&D Team
2010-05-04 02:00:08 +01:00
committed by Olivier Dony
13 changed files with 303 additions and 290 deletions
+26 -12
View File
@@ -640,10 +640,20 @@ def load_module_graph(cr, graph, status=None, perform_checks=True, **kwargs):
def load_test(cr, module_name, id_map, mode):
cr.commit()
try:
_load_data(cr, module_name, id_map, mode, 'test')
finally:
cr.rollback()
if not tools.config.options['test-disable']:
try:
_load_data(cr, module_name, id_map, mode, 'test')
except Exception, e:
if tools.config.options['test-continue']:
logger.notifyChannel('ERROR', netsvc.LOG_TEST, e)
pass
else:
raise
finally:
if tools.config.options['test-rollback']:
cr.rollback()
else:
cr.commit()
def _load_data(cr, module_name, id_map, mode, kind):
noupdate = (kind == 'demo')
@@ -757,6 +767,15 @@ def load_module_graph(cr, graph, status=None, perform_checks=True, **kwargs):
return has_updates
def load_modules(db, force_demo=False, status=None, update_module=False):
def check_module_name(cr, mods, state):
for mod in mods:
id = modobj.search(cr, 1, ['&', ('state', '=', state), ('name', '=', mod)])
if id:
getattr(modobj, states[state])(cr, 1, id)
elif mod != 'all':
logger.notifyChannel('init', netsvc.LOG_WARNING, 'module %s: invalid module name!' % (mod))
if not status:
status = {}
cr = db.cursor()
@@ -787,21 +806,16 @@ def load_modules(db, force_demo=False, status=None, update_module=False):
return
if update_module:
modobj = pool.get('ir.module.module')
states = {'installed': 'button_upgrade', 'uninstalled': 'button_install'}
logger.notifyChannel('init', netsvc.LOG_INFO, 'updating modules list')
if ('base' in tools.config['init']) or ('base' in tools.config['update']):
modobj.update_list(cr, 1)
mods = [k for k in tools.config['init'] if tools.config['init'][k]]
if mods:
ids = modobj.search(cr, 1, ['&', ('state', '=', 'uninstalled'), ('name', 'in', mods)])
if ids:
modobj.button_install(cr, 1, ids)
check_module_name(cr, mods, 'uninstalled')
mods = [k for k in tools.config['update'] if tools.config['update'][k]]
if mods:
ids = modobj.search(cr, 1, ['&', ('state', '=', 'installed'), ('name', 'in', mods)])
if ids:
modobj.button_upgrade(cr, 1, ids)
check_module_name(cr, mods, 'installed')
cr.execute("update ir_module_module set state=%s where name=%s", ('installed', 'base'))
+33 -51
View File
@@ -1147,67 +1147,51 @@
<!-- Rules -->
<record id="view_rule_group_form" model="ir.ui.view">
<record id="view_rule_form" model="ir.ui.view">
<field name="name">Record rules</field>
<field name="model">ir.rule.group</field>
<field name="model">ir.rule</field>
<field name="type">form</field>
<field name="arch" type="xml">
<form string="Record rules">
<field name="model_id" select="1"/>
<field name="global" select="1"/>
<field colspan="4" name="name"/>
<group col="6" colspan="4" expand="1">
<field colspan="6" name="rules" nolabel="1"/>
<label align="0.0" colspan="6" string="The rule is satisfied if all test are True (AND)"/>
<label align="0.0" colspan="6" string="Multiple rules on same objects are joined using operator OR"/>
<separator colspan="4" string="Combination of rules"/>
<label align="0.0" angle="2" colspan="4" string="1. Global rules are combined together with a logical AND operator, and with the result of the following steps"/>
<label align="0.0" angle="2" colspan="4" string="2. Group-specific rules are combined together with a logical AND operator"/>
<label align="0.0" angle="2" colspan="4" string="3. If user belongs to several groups, the results from step 2 are combined with logical OR operator"/>
<label align="0.0" angle="2" colspan="4" string="Example: GLOBAL_RULE_1 AND GLOBAL_RULE_2 AND ( (GROUP_A_RULE_1 AND GROUP_A_RULE_2) OR (GROUP_B_RULE_1 AND GROUP_B_RULE_2) )"/>
<group col="2" colspan="2">
<separator colspan="2" string="General"/>
<field colspan="4" name="name"/>
<field name="model_id" select="1"/>
</group>
<group col="4" colspan="2">
<separator colspan="4" string="Access Rights"/>
<field name="perm_read"/>
<field name="perm_write"/>
<field name="perm_create"/>
<field name="perm_unlink"/>
</group>
<separator colspan="4" string="Domain Setup"/>
<group colspan="4">
<field name="domain_force" colspan="4"/>
</group>
<separator colspan="4" string="Groups (no group = global)"/>
<group colspan="4" expand="1">
<field name="global" select="1"/>
<field name="groups" select="1" nolabel="1" colspan="4"/>
</group>
</form>
</field>
</record>
<record id="view_rule_group_tree" model="ir.ui.view">
<record id="view_rule_tree" model="ir.ui.view">
<field name="name">Record rules</field>
<field name="model">ir.rule.group</field>
<field name="model">ir.rule</field>
<field name="type">tree</field>
<field name="arch" type="xml">
<tree string="Record rules">
<field name="model_id"/>
<field name="name"/>
<field name="global"/>
</tree>
</field>
</record>
<record id="view_rule_form" model="ir.ui.view">
<field name="name">Rule Definition</field>
<field name="model">ir.rule</field>
<field name="type">form</field>
<field name="arch" type="xml">
<form string="Test">
<separator colspan="4" string="Simple domain setup"/>
<group col="6" colspan="4">
<field name="field_id"/>
<field name="operator"/>
<field name="operand"/>
</group>
<separator colspan="4" string="Manual domain setup"/>
<field name="domain_force"/>
<label colspan="4" string="If you don't force the domain, it will use the simple domain setup"/>
<newline/>
<field name="perm_read"/>
<field name="perm_write"/>
<field name="perm_create"/>
<field name="perm_unlink"/>
</form>
</field>
</record>
<record id="view_rule_tree" model="ir.ui.view">
<field name="name">Rules</field>
<field name="model">ir.rule</field>
<field name="type">tree</field>
<field name="arch" type="xml">
<tree string="Test">
<field name="domain"/>
<field name="perm_read"/>
<field name="perm_write"/>
<field name="perm_create"/>
@@ -1215,22 +1199,20 @@
</tree>
</field>
</record>
<record id="action_rule" model="ir.actions.act_window">
<field name="name">Record Rules</field>
<field name="res_model">ir.rule.group</field>
<field name="res_model">ir.rule</field>
<field name="view_type">form</field>
<field name="view_id" ref="view_rule_group_tree"/>
<field name="view_id" ref="view_rule_tree"/>
</record>
<menuitem action="action_rule" id="menu_action_rule" parent="base.menu_security"/>
<record id="property_rule_group" model="ir.rule.group">
<record id="property_rule" model="ir.rule">
<field name="name">Property multi-company</field>
<field model="ir.model" name="model_id" ref="model_ir_property"/>
<field eval="True" name="global"/>
</record>
<record id="property_rule" model="ir.rule">
<field name="domain_force">['|',('company_id','child_of',user.company_id.id),('company_id','=',False)]</field>
<field name="rule_group" ref="property_rule_group"/>
</record>
<!--server action view-->
+7 -7
View File
@@ -191,7 +191,7 @@ class act_window(osv.osv):
return s.encode('utf8')
return s
for act in self.browse(cr, uid, ids):
fields_from_fields_get = self.pool.get(act.res_model).fields_get(cr, uid)
fields_from_fields_get = self.pool.get(act.res_model).fields_get(cr, uid, context=context)
search_view_id = False
if act.search_view_id:
search_view_id = act.search_view_id.id
@@ -387,13 +387,13 @@ class actions_server(osv.osv):
line = rs[0], "%s - (%s)" % (rs[1], rs[0])
res.append(line)
return res
def _select_objects(self, cr, uid, context={}):
model_pool = self.pool.get('ir.model')
ids = model_pool.search(cr, uid, [('name','not ilike','.')])
res = model_pool.read(cr, uid, ids, ['model', 'name'])
return [(r['model'], r['name']) for r in res] + [('','')]
def change_object(self, cr, uid, ids, copy_object, state, context={}):
if state == 'object_copy':
model_pool = self.pool.get('ir.model')
@@ -581,10 +581,10 @@ class actions_server(osv.osv):
continue
if not user:
raise osv.except_osv(_('Error'), _("Please specify server option --smtp-from !"))
subject = self.merge_message(cr, uid, action.subject, action, context)
body = self.merge_message(cr, uid, action.message, action, context)
if tools.email_send(user, [address], subject, body, debug=False, subtype='html') == True:
logger.notifyChannel('email', netsvc.LOG_INFO, 'Email successfully send to : %s' % (address))
else:
@@ -686,7 +686,7 @@ class actions_server(osv.osv):
cr.commit()
if action.record_id:
self.pool.get(action.model_id.model).write(cr, uid, [context.get('active_id')], {action.record_id.name:res_id})
if action.state == 'object_copy':
res = {}
for exp in action.fields_lines:
@@ -701,7 +701,7 @@ class actions_server(osv.osv):
obj_pool = None
res_id = False
model = action.copy_object.split(',')[0]
cid = action.copy_object.split(',')[1]
obj_pool = self.pool.get(model)
+6 -5
View File
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
##############################################################################
#
#
# OpenERP, Open Source Management Solution
# Copyright (C) 2004-2009 Tiny SPRL (<http://tiny.be>).
#
@@ -15,7 +15,7 @@
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
##############################################################################
import logging
@@ -80,7 +80,7 @@ class ir_model(osv.osv):
if context:
context.pop('__last_update', None)
return super(ir_model,self).write(cr, user, ids, vals, context)
def create(self, cr, user, vals, context=None):
if context and context.get('manual',False):
vals['state']='manual'
@@ -226,7 +226,7 @@ class ir_model_fields(osv.osv):
'domain': fields.char('Domain', size=256),
'groups': fields.many2many('res.groups', 'ir_model_fields_group_rel', 'field_id', 'group_id', 'Groups'),
'view_load': fields.boolean('View Auto-Load'),
'selectable': fields.boolean('Selectable'),
'selectable': fields.boolean('Selectable'),
}
_rec_name='field_description'
_defaults = {
@@ -278,7 +278,7 @@ class ir_model_fields(osv.osv):
self.pool.get(vals['model'])._auto_init(cr, ctx)
return res
ir_model_fields()
class ir_model_access(osv.osv):
@@ -378,6 +378,7 @@ class ir_model_access(osv.osv):
'create': _('You can not create this kind of document! (%s)'),
'unlink': _('You can not delete this document! (%s)'),
}
raise except_orm(_('AccessError'), msgs[mode] % model_name )
return r
+75 -108
View File
@@ -23,154 +23,121 @@ from osv import fields,osv
import time
import tools
class ir_rule_group(osv.osv):
_name = 'ir.rule.group'
_columns = {
'name': fields.char('Name', size=128, select=1),
'model_id': fields.many2one('ir.model', 'Object',select=1, required=True),
'global': fields.boolean('Global', select=1, help="Make the rule global, otherwise it needs to be put on a group"),
'rules': fields.one2many('ir.rule', 'rule_group', 'Tests', help="The rule is satisfied if at least one test is True"),
'groups': fields.many2many('res.groups', 'group_rule_group_rel', 'rule_group_id', 'group_id', 'Groups'),
'users': fields.many2many('res.users', 'user_rule_group_rel', 'rule_group_id', 'user_id', 'Users'),
}
_order = 'model_id, global DESC'
_defaults={
'global': lambda *a: True,
}
ir_rule_group()
class ir_rule(osv.osv):
_name = 'ir.rule'
_rec_name = 'field_id'
def _operand(self,cr,uid,context):
def get(object, level=3, recur=None, root_tech='', root=''):
res = []
if not recur:
recur = []
fields = self.pool.get(object).fields_get(cr,uid)
key = fields.keys()
key.sort()
for k in key:
if fields[k]['type'] in ('many2one'):
res.append((root_tech+'.'+k+'.id',
root+'/'+fields[k]['string']))
elif fields[k]['type'] in ('many2many', 'one2many'):
res.append(('\',\'.join(map(lambda x: str(x.id), '+root_tech+'.'+k+'))',
root+'/'+fields[k]['string']))
else:
res.append((root_tech+'.'+k,
root+'/'+fields[k]['string']))
if (fields[k]['type'] in recur) and (level>0):
res.extend(get(fields[k]['relation'], level-1,
recur, root_tech+'.'+k, root+'/'+fields[k]['string']))
return res
res = [("False", "False"), ("True", "True"), ("user.id", "User")]
res += get('res.users', level=1,
recur=['many2one'], root_tech='user', root='User')
return res
def _domain_force_get(self, cr, uid, ids, field_name, arg, context={}):
res = {}
for rule in self.browse(cr, uid, ids, context):
eval_user_data = {'user': self.pool.get('res.users').browse(cr, 1, uid),
'time':time}
if rule.domain_force:
res[rule.id] = eval(rule.domain_force, eval_user_data)
else:
if rule.operand and rule.operand.startswith('user.') and rule.operand.count('.') > 1:
#Need to check user.field.field1.field2(if field is False,it will break the chain)
op = rule.operand[5:]
rule.operand = rule.operand[:5+len(op[:op.find('.')])] +' and '+ rule.operand + ' or False'
if rule.operator in ('in', 'child_of'):
dom = eval("[('%s', '%s', [%s])]" % (rule.field_id.name, rule.operator,
eval(rule.operand,eval_user_data)), eval_user_data)
else:
dom = eval("[('%s', '%s', %s)]" % (rule.field_id.name, rule.operator,
rule.operand), eval_user_data)
res[rule.id] = dom
res[rule.id] = eval(rule.domain_force, eval_user_data)
return res
def _get_value(self, cr, uid, ids, field_name, arg, context={}):
res = {}
for rule in self.browse(cr, uid, ids, context):
if not rule.groups:
res[rule.id] = True
else:
res[rule.id] = False
return res
def _check_model_obj(self, cr, uid, ids, context={}):
model_obj = self.pool.get('ir.model')
for rule in self.browse(cr, uid, ids, context):
model = model_obj.browse(cr, uid, rule.model_id.id, context).model
obj = self.pool.get(model)
if isinstance(obj, osv.osv_memory):
return False
return True
_columns = {
'field_id': fields.many2one('ir.model.fields', 'Field',domain= "[('model_id','=', parent.model_id)]", select=1),
'operator':fields.selection((('=', '='), ('<>', '<>'), ('<=', '<='), ('>=', '>='), ('in', 'in'), ('child_of', 'child_of')), 'Operator'),
'operand':fields.selection(_operand,'Operand', size=64),
'rule_group': fields.many2one('ir.rule.group', 'Group', select=2, required=True, ondelete="cascade"),
'domain_force': fields.char('Force Domain', size=250),
'name': fields.char('Name', size=128, select=1),
'model_id': fields.many2one('ir.model', 'Object',select=1, required=True),
'global': fields.function(_get_value, method=True, string='Global', type='boolean', store=True, help="If no group is specified the rule is global and applied to everyone"),
'groups': fields.many2many('res.groups', 'rule_group_rel', 'rule_group_id', 'group_id', 'Groups'),
'domain_force': fields.char('Domain', size=250),
'domain': fields.function(_domain_force_get, method=True, string='Domain', type='char', size=250),
'perm_read': fields.boolean('Read Access'),
'perm_write': fields.boolean('Write Access'),
'perm_create': fields.boolean('Create Access'),
'perm_unlink': fields.boolean('Delete Access')
'perm_read': fields.boolean('Apply For Read'),
'perm_write': fields.boolean('Apply For Write'),
'perm_create': fields.boolean('Apply For Create'),
'perm_unlink': fields.boolean('Apply For Delete')
}
_order = 'model_id DESC'
_defaults = {
'perm_read': lambda *a: True,
'perm_write': lambda *a: True,
'perm_create': lambda *a: True,
'perm_unlink': lambda *a: True,
'perm_read': True,
'perm_write': True,
'perm_create': True,
'perm_unlink': True,
'global': True,
}
_sql_constraints = [
('no_access_rights', 'CHECK (perm_read!=False or perm_write!=False or perm_create!=False or perm_unlink!=False)', 'Rule must have atleast one checked access right'),
('no_access_rights', 'CHECK (perm_read!=False or perm_write!=False or perm_create!=False or perm_unlink!=False)', 'Rule must have at least one checked access right'),
]
_constraints = [
(_check_model_obj, 'Rules are not supported for osv_memory objects !', ['model_id'])
]
def onchange_all(self, cr, uid, ids, field_id, operator, operand):
if not (field_id or operator or operand):
return {}
def domain_create(self, cr, uid, rule_ids):
dom = ['&'] * (len(rule_ids)-1)
for rule in self.browse(cr, uid, rule_ids):
dom += rule.domain
return dom
@tools.cache()
def _compute_domain(self, cr, uid, model_name, mode="read"):
group_rule = {}
global_rules = []
def domain_get(self, cr, uid, model_name, mode='read', context={}):
if uid == 1:
return [], [], ['"'+self.pool.get(model_name)._table+'"']
return None
cr.execute("""SELECT r.id
FROM ir_rule r
JOIN (ir_rule_group g
JOIN ir_model m ON (g.model_id = m.id))
ON (g.id = r.rule_group)
JOIN ir_model m ON (r.model_id = m.id)
WHERE m.model = %s
AND r.perm_""" + mode + """
AND (g.id IN (SELECT rule_group_id FROM group_rule_group_rel g_rel
AND (r.id IN (SELECT rule_group_id FROM rule_group_rel g_rel
JOIN res_groups_users_rel u_rel ON (g_rel.group_id = u_rel.gid)
WHERE u_rel.uid = %s) OR g.global)""", (model_name, uid))
WHERE u_rel.uid = %s) OR r.global)""", (model_name, uid))
ids = map(lambda x: x[0], cr.fetchall())
dom = []
for rule in self.browse(cr, uid, ids):
dom += rule.domain
d1,d2,tables = self.pool.get(model_name)._where_calc(cr, uid, dom, active_test=False)
return d1, d2, tables
domain_get = tools.cache()(domain_get)
for group in rule.groups:
group_rule.setdefault(group.id, []).append(rule.id)
if not rule.groups:
global_rules.append(rule.id)
dom = self.domain_create(cr, uid, global_rules)
dom += ['|'] * (len(group_rule)-1)
for value in group_rule.values():
dom += self.domain_create(cr, uid, value)
return dom
def domain_get(self, cr, uid, model_name, mode='read', context={}):
dom = self._compute_domain(cr, uid, model_name, mode=mode)
if dom:
return self.pool.get(model_name)._where_calc(cr, uid, dom, active_test=False)
return [], [], ['"'+self.pool.get(model_name)._table+'"']
def unlink(self, cr, uid, ids, context=None):
res = super(ir_rule, self).unlink(cr, uid, ids, context=context)
# Restart the cache on the domain_get method of ir.rule
self.domain_get.clear_cache(cr.dbname)
# Restart the cache on the _compute_domain method of ir.rule
self._compute_domain.clear_cache(cr.dbname)
return res
def create(self, cr, user, vals, context=None):
res = super(ir_rule, self).create(cr, user, vals, context=context)
# Restart the cache on the domain_get method of ir.rule
self.domain_get.clear_cache(cr.dbname)
# Restart the cache on the _compute_domain method of ir.rule
self._compute_domain.clear_cache(cr.dbname)
return res
def write(self, cr, uid, ids, vals, context=None):
if not context:
context={}
res = super(ir_rule, self).write(cr, uid, ids, vals, context=context)
# Restart the cache on the domain_get method
self.domain_get.clear_cache(cr.dbname)
# Restart the cache on the _compute_domain method
self._compute_domain.clear_cache(cr.dbname)
return res
ir_rule()
+2 -3
View File
@@ -34,7 +34,7 @@ class groups(osv.osv):
_columns = {
'name': fields.char('Group Name', size=64, required=True),
'model_access': fields.one2many('ir.model.access', 'group_id', 'Access Controls'),
'rule_groups': fields.many2many('ir.rule.group', 'group_rule_group_rel',
'rule_groups': fields.many2many('ir.rule', 'rule_group_rel',
'group_id', 'rule_group_id', 'Rules', domain="[('global', '<>', True)]"),
'menu_access': fields.many2many('ir.ui.menu', 'ir_ui_menu_group_rel', 'gid', 'menu_id', 'Access Menu'),
'comment' : fields.text('Comment',size=250),
@@ -47,7 +47,7 @@ class groups(osv.osv):
group_name = self.read(cr, uid, [id], ['name'])[0]['name']
default.update({'name': group_name +' (copy)'})
return super(groups, self).copy(cr, uid, id, default, context)
def write(self, cr, uid, ids, vals, context=None):
if 'name' in vals:
if vals['name'].startswith('-'):
@@ -185,7 +185,6 @@ class users(osv.osv):
'menu_id': fields.many2one('ir.actions.actions', 'Menu Action'),
'groups_id': fields.many2many('res.groups', 'res_groups_users_rel', 'uid', 'gid', 'Groups'),
'roles_id': fields.many2many('res.roles', 'res_roles_users_rel', 'uid', 'rid', 'Roles'),
'rules_id': fields.many2many('ir.rule.group', 'user_rule_group_rel', 'user_id', 'rule_group_id', 'Rules'),
'company_id': fields.many2one('res.company', 'Company', required=True,
help="The company this user is currently working for."),
'company_ids':fields.many2many('res.company','res_company_users_rel','user_id','cid','Companies'),
+3 -16
View File
@@ -72,37 +72,24 @@
</record>
<record model="ir.rule.group" id="res_partner_address_rule_group">
<record model="ir.rule" id="res_partner_address_rule">
<field name="name">res.partner.address company</field>
<field name="model_id" ref="model_res_partner_address"/>
<field name="global" eval="True"/>
</record>
<record model="ir.rule" id="res_partner_address_comp_rule">
<field name="field_id" search="[('model','=','res.partner.address'),('name','=','company_id')]" model="ir.model.fields"/>
<field name="rule_group" ref="res_partner_address_rule_group"/>
<field name="domain_force">[('company_id','child_of',[user.company_id.id])]</field>
</record>
<record model="ir.rule.group" id="res_partner_rule_group">
<record model="ir.rule" id="res_partner_rule">
<field name="name">res.partner company</field>
<field name="model_id" ref="model_res_partner"/>
<field name="global" eval="True"/>
</record>
<record model="ir.rule" id="res_partner_comp_rule">
<field name="field_id" search="[('model','=','res.partner'),('name','=','company_id')]" model="ir.model.fields"/>
<field name="rule_group" ref="res_partner_rule_group"/>
<field name="domain_force">[('company_id','child_of',[user.company_id.id])]</field>
</record>
<record model="ir.rule.group" id="multi_company_default_rule_group">
<record model="ir.rule" id="multi_company_default_rule">
<field name="name">Multi_company_default company</field>
<field name="model_id" ref="model_multi_company_default"/>
<field name="global" eval="True"/>
</record>
<record model="ir.rule" id="multi_company_default_comp_rule">
<field name="field_id" search="[('model','=','multi_company.default'),('name','=','company_id')]" model="ir.model.fields"/>
<field name="rule_group" ref="multi_company_default_rule_group"/>
<field name="domain_force">[('company_id','child_of',[user.company_id.id])]</field>
</record>
@@ -23,9 +23,7 @@
"access_ir_report_custom_group_system","ir_report_custom group_system","model_ir_report_custom",,1,0,0,0
"access_ir_report_custom_fields_group_system","ir_report_custom_fields group_system","model_ir_report_custom_fields",,1,0,0,0
"access_ir_rule_group_user","ir_rule group_user","model_ir_rule",,1,0,0,0
"access_ir_rule_group_group_user","ir_rule_group group_user","model_ir_rule_group",,1,0,0,0
"access_ir_rule_group_erp_manager","ir_rule group_erp_manager","model_ir_rule","group_erp_manager",1,1,1,1
"access_ir_rule_group_group_erp_manager","ir_rule_group group_erp_manager","model_ir_rule_group","group_erp_manager",1,1,1,1
"access_ir_sequence_group_user","ir_sequence group_user","model_ir_sequence",,1,1,1,1
"access_ir_sequence_type_group_user","ir_sequence_type group_user","model_ir_sequence_type",,1,0,0,0
"access_ir_translation_group_system","ir_translation group_system","model_ir_translation",,1,1,1,1
1 id name model_id:id group_id:id perm_read perm_write perm_create perm_unlink
23 access_ir_report_custom_group_system ir_report_custom group_system model_ir_report_custom 1 0 0 0
24 access_ir_report_custom_fields_group_system ir_report_custom_fields group_system model_ir_report_custom_fields 1 0 0 0
25 access_ir_rule_group_user ir_rule group_user model_ir_rule 1 0 0 0
access_ir_rule_group_group_user ir_rule_group group_user model_ir_rule_group 1 0 0 0
26 access_ir_rule_group_erp_manager ir_rule group_erp_manager model_ir_rule group_erp_manager 1 1 1 1
access_ir_rule_group_group_erp_manager ir_rule_group group_erp_manager model_ir_rule_group group_erp_manager 1 1 1 1
27 access_ir_sequence_group_user ir_sequence group_user model_ir_sequence 1 1 1 1
28 access_ir_sequence_type_group_user ir_sequence_type group_user model_ir_sequence_type 1 0 0 0
29 access_ir_translation_group_system ir_translation group_system model_ir_translation 1 1 1 1
+5
View File
@@ -656,6 +656,11 @@ class function(_column):
self._symbol_f = float._symbol_f
self._symbol_set = float._symbol_set
if type == 'boolean':
self._symbol_c = boolean._symbol_c
self._symbol_f = boolean._symbol_f
self._symbol_set = boolean._symbol_set
def digits_change(self, cr):
if self.digits_compute:
t = self.digits_compute(cr)
+52 -29
View File
@@ -1139,6 +1139,25 @@ class orm_template(object):
return s.encode('utf8')
return s
# return True if node can be displayed to current user
def check_group(node):
if node.get('groups'):
groups = node.get('groups').split(',')
can_see = False
access_pool = self.pool.get('ir.model.access')
for group in groups:
can_see = can_see or access_pool.check_groups(cr, user, group)
if can_see:
break
if not can_see:
node.set('invisible', '1')
if 'attrs' in node.attrib:
del(node.attrib['attrs']) #avoid making field visible later
del(node.attrib['groups'])
return can_see
else:
return True
if node.tag in ('field', 'node', 'arrow'):
if node.get('object'):
attrs = {}
@@ -1185,15 +1204,18 @@ class orm_template(object):
}
attrs = {'views': views}
if node.get('widget') and node.get('widget') == 'selection':
if not check_group(node):
attrs['selection'] = []
# We can not use the 'string' domain has it is defined according to the record !
dom = []
if column._domain and not isinstance(column._domain, (str, unicode)):
dom = column._domain
dom += eval(node.get('domain','[]'), {'uid':user, 'time':time})
context.update(eval(node.get('context','{}')))
attrs['selection'] = self.pool.get(relation).name_search(cr, user, '', dom, context=context,limit=None)
if (node.get('required') and not int(node.get('required'))) or not column.required:
attrs['selection'].append((False,''))
else:
dom = []
if column._domain and not isinstance(column._domain, (str, unicode)):
dom = column._domain
dom += eval(node.get('domain','[]'), {'uid':user, 'time':time})
context.update(eval(node.get('context','{}')))
attrs['selection'] = self.pool.get(relation)._name_search(cr, user, '', dom, context=context, limit=None, name_get_uid=1)
if (node.get('required') and not int(node.get('required'))) or not column.required:
attrs['selection'].append((False,''))
fields[node.get('name')] = attrs
elif node.tag in ('form', 'tree'):
@@ -1207,15 +1229,7 @@ class orm_template(object):
fields[node.get(additional_field)] = {}
if 'groups' in node.attrib:
if node.get('groups'):
groups = node.get('groups').split(',')
readonly = False
access_pool = self.pool.get('ir.model.access')
for group in groups:
readonly = readonly or access_pool.check_groups(cr, user, group)
if not readonly:
node.set('invisible', '1')
del(node.attrib['groups'])
check_group(node)
# translate view
if ('lang' in context) and not result:
@@ -1725,6 +1739,7 @@ class orm_memory(orm_template):
def read(self, cr, user, ids, fields_to_read=None, context=None, load='_classic_read'):
if not context:
context = {}
self.pool.get('ir.model.access').check(cr, user, self._name, 'read', context=context)
if not fields_to_read:
fields_to_read = self._columns.keys()
result = []
@@ -1754,6 +1769,7 @@ class orm_memory(orm_template):
def write(self, cr, user, ids, vals, context=None):
if not ids:
return True
self.pool.get('ir.model.access').check(cr, user, self._name, 'write', context=context)
vals2 = {}
upd_todo = []
for field in vals:
@@ -1772,6 +1788,7 @@ class orm_memory(orm_template):
return id_new
def create(self, cr, user, vals, context=None):
self.pool.get('ir.model.access').check(cr, user, self._name, 'create', context=context)
self.vaccum(cr, user)
self.next_id += 1
id_new = self.next_id
@@ -1923,6 +1940,7 @@ class orm_memory(orm_template):
return res or []
def unlink(self, cr, uid, ids, context=None):
self.pool.get('ir.model.access').check(cr, uid, self._name, 'unlink', context=context)
for id in ids:
if id in self.datas:
del self.datas[id]
@@ -3225,7 +3243,7 @@ class orm(orm_template):
"""
if not context:
context = {}
self.pool.get('ir.model.access').check(cr, user, self._name, 'create')
self.pool.get('ir.model.access').check(cr, user, self._name, 'create', context=context)
default = []
@@ -3281,7 +3299,7 @@ class orm(orm_template):
del vals[self._inherits[table]]
record_id = tocreate[table].pop('id', None)
if record_id is None or not record_id:
record_id = self.pool.get(table).create(cr, user, tocreate[table], context=context)
else:
@@ -3649,6 +3667,20 @@ class orm(orm_template):
return [(r['id'], tools.ustr(r[self._rec_name])) for r in self.read(cr, user, ids,
[self._rec_name], context, load='_classic_write')]
# private implementation of name_search, allows passing a dedicated user for the name_get part to
# solve some access rights issues
def _name_search(self, cr, user, name='', args=None, operator='ilike', context=None, limit=100, name_get_uid=None):
if not args:
args = []
if not context:
context = {}
args = args[:]
if name:
args += [(self._rec_name, operator, name)]
ids = self.search(cr, user, args, limit=limit, context=context)
res = self.name_get(cr, name_get_uid or user, ids, context)
return res
def name_search(self, cr, user, name='', args=None, operator='ilike', context=None, limit=100):
"""
@@ -3664,16 +3696,7 @@ class orm(orm_template):
This method is equivalent of search() on name + name_get()
"""
if not args:
args = []
if not context:
context = {}
args = args[:]
if name:
args += [(self._rec_name, operator, name)]
ids = self.search(cr, user, args, limit=limit, context=context)
res = self.name_get(cr, user, ids, context)
return res
return self._name_search(cr, user, name, args, operator, context, limit)
def copy_data(self, cr, uid, id, default=None, context=None):
"""
+17 -1
View File
@@ -82,6 +82,9 @@ class configmanager(object):
'login_message': False,
'list_db' : True,
'timezone' : False, # to override the default TZ
'test-disable' : False,
'test-rollback' : True,
'test-continue' : False
}
self.misc = {}
@@ -134,7 +137,17 @@ class configmanager(object):
default="server.pkey",
help="specify the private key file for the SSL connection")
parser.add_option_group(group)
# Testing Group
group = optparse.OptionGroup(parser, "Testing Configuration")
group.add_option("--test-disable", action="store_true", dest="test_disable",
default=False, help="Disable loading test files.")
group.add_option("--test-no-rollback", action="store_false", dest="test_rollback",
default=True, help="Don't rollback after running test.")
group.add_option("--test-continue", action="store_true", dest="test_continue",
default=False, help="Display exception but then test should continue.")
parser.add_option_group(group)
# Logging Group
group = optparse.OptionGroup(parser, "Logging Configuration")
group.add_option("--logfile", dest="logfile", help="file where the server log will be stored")
@@ -274,6 +287,9 @@ class configmanager(object):
self.options['init'] = opt.init and dict.fromkeys(opt.init.split(','), 1) or {}
self.options["demo"] = not opt.without_demo and self.options['init'] or {}
self.options["test-disable"] = opt.test_disable
self.options["test-rollback"] = opt.test_rollback
self.options["test-continue"] = opt.test_continue
self.options['update'] = opt.update and dict.fromkeys(opt.update.split(','), 1) or {}
self.options['translate_modules'] = opt.translate_modules and map(lambda m: m.strip(), opt.translate_modules.split(',')) or ['all']
+59 -35
View File
@@ -254,38 +254,48 @@ class YamlInterpreter(object):
b = bool(value)
else:
b = default
return b
def process_record(self, node):
record, fields = node.items()[0]
self.validate_xml_id(record.id)
if self.isnoupdate(record) and self.mode != 'init':
id = self.pool.get('ir.model.data')._update_dummy(self.cr, self.uid, record.model, self.module, record.id)
# check if the resource already existed at the last update
if id:
self.id_map[record] = int(id)
return None
else:
if not self._coerce_bool(record.forcecreate):
return None
return b
def create_osv_memory_record(self, record, fields):
model = self.get_model(record.model)
record_dict = self._create_record(model, fields)
self.logger.debug("RECORD_DICT %s" % record_dict)
id = self.pool.get('ir.model.data')._update(self.cr, self.uid, record.model, \
self.module, record_dict, record.id, noupdate=self.isnoupdate(record), mode=self.mode)
self.id_map[record.id] = int(id)
if config.get('import_partial', False):
self.cr.commit()
id_new=model.create(self.cr, self.uid, record_dict, context=self.context)
self.id_map[record.id] = int(id_new)
return record_dict
def process_record(self, node):
import osv
record, fields = node.items()[0]
model = self.get_model(record.model)
if isinstance(model, osv.osv.osv_memory):
record_dict=self.create_osv_memory_record(record, fields)
else:
self.validate_xml_id(record.id)
if self.isnoupdate(record) and self.mode != 'init':
id = self.pool.get('ir.model.data')._update_dummy(self.cr, self.uid, record.model, self.module, record.id)
# check if the resource already existed at the last update
if id:
self.id_map[record] = int(id)
return None
else:
if not self._coerce_bool(record.forcecreate):
return None
record_dict = self._create_record(model, fields)
self.logger.debug("RECORD_DICT %s" % record_dict)
id = self.pool.get('ir.model.data')._update(self.cr, self.uid, record.model, \
self.module, record_dict, record.id, noupdate=self.isnoupdate(record), mode=self.mode)
self.id_map[record.id] = int(id)
if config.get('import_partial'):
self.cr.commit()
def _create_record(self, model, fields):
record_dict = {}
for field_name, expression in fields.items():
field_value = self._eval_field(model, field_name, expression)
record_dict[field_name] = field_value
return record_dict
return record_dict
def process_ref(self, node, column=None):
if node.search:
if node.model:
@@ -307,10 +317,10 @@ class YamlInterpreter(object):
else:
value = None
return value
def process_eval(self, node):
return eval(node.expression, self.eval_context)
def _eval_field(self, model, field_name, expression):
# TODO this should be refactored as something like model.get_field() in bin/osv
if field_name in model._columns:
@@ -390,6 +400,10 @@ class YamlInterpreter(object):
uid = workflow.uid
else:
uid = self.uid
self.cr.execute('select distinct signal from wkf_transition')
signals=[x['signal'] for x in self.cr.dictfetchall()]
if workflow.action not in signals:
raise YamlImportException('Incorrect action %s. No such action defined' % workflow.action)
wf_service = netsvc.LocalService("workflow")
wf_service.trg_validate(uid, workflow.model, id, workflow.action, self.cr)
@@ -528,15 +542,19 @@ class YamlInterpreter(object):
'tree_but_open', 'Menuitem', [('ir.ui.menu', int(parent_id))], action, True, True, xml_id=node.id)
def process_act_window(self, node):
assert getattr(node, 'id'), "Attribute %s of act_window is empty !" % ('id',)
assert getattr(node, 'name'), "Attribute %s of act_window is empty !" % ('name',)
assert getattr(node, 'res_model'), "Attribute %s of act_window is empty !" % ('res_model',)
self.validate_xml_id(node.id)
view_id = False
if node.view:
view_id = self.get_id(node.view)
context = eval(node.context, self.eval_context)
if not node.context:
node.context={}
context = eval(str(node.context), self.eval_context)
values = {
'name': node.name,
'type': type or 'ir.actions.act_window',
'type': node.type or 'ir.actions.act_window',
'view_id': view_id,
'domain': node.domain,
'context': context,
@@ -566,13 +584,17 @@ class YamlInterpreter(object):
# TODO add remove ir.model.data
def process_delete(self, node):
if len(node.search):
ids = self.pool.get(node.model).search(self.cr, self.uid, eval(node.search, self.eval_context))
assert getattr(node, 'model'), "Attribute %s of delete tag is empty !" % ('model',)
if self.pool.get(node.model):
if len(node.search):
ids = self.pool.get(node.model).search(self.cr, self.uid, eval(node.search, self.eval_context))
else:
ids = [self.get_id(node.id)]
if len(ids):
self.pool.get(node.model).unlink(self.cr, self.uid, ids)
self.pool.get('ir.model.data')._unlink(self.cr, self.uid, node.model, ids)
else:
ids = [self.get_id(node.id)]
if len(ids):
self.pool.get(node.model).unlink(self.cr, self.uid, ids)
self.pool.get('ir.model.data')._unlink(self.cr, self.uid, node.model, ids)
self.logger.log(logging.TEST, "Record not deleted.")
def process_url(self, node):
self.validate_xml_id(node.id)
@@ -649,6 +671,8 @@ class YamlInterpreter(object):
"""
Processes a Yaml string. Custom tags are interpreted by 'process_' instance methods.
"""
yaml_tag.add_constructors()
is_preceded_by_comment = False
for node in yaml.load(yaml_string):
is_preceded_by_comment = self._log(node, is_preceded_by_comment)
+18 -21
View File
@@ -48,9 +48,7 @@ class Workflow(YamlTag):
super(Workflow, self).__init__(**kwargs)
class ActWindow(YamlTag):
def __init__(self, model, action, **kwargs):
self.model = model
self.action = action
def __init__(self, **kwargs):
super(ActWindow, self).__init__(**kwargs)
class Function(YamlTag):
@@ -67,10 +65,7 @@ class Report(YamlTag):
super(Report, self).__init__(**kwargs)
class Delete(YamlTag):
def __init__(self, model, id, search, **kwargs):
self.model = model
self.id = id
self.search = search
def __init__(self, **kwargs):
super(Delete, self).__init__(**kwargs)
class Context(YamlTag):
@@ -156,17 +151,19 @@ def ir_set_constructor(loader, node):
# Registers constructors for custom tags.
# Constructors are actually defined globally: do not redefined them in another
# class/file/package. This means that module recorder need import this file.
yaml.add_constructor(u"!assert", assert_constructor)
yaml.add_constructor(u"!record", record_constructor)
yaml.add_constructor(u"!python", python_constructor)
yaml.add_constructor(u"!menuitem", menuitem_constructor)
yaml.add_constructor(u"!workflow", workflow_constructor)
yaml.add_constructor(u"!act_window", act_window_constructor)
yaml.add_constructor(u"!function", function_constructor)
yaml.add_constructor(u"!report", report_constructor)
yaml.add_constructor(u"!context", context_constructor)
yaml.add_constructor(u"!delete", delete_constructor)
yaml.add_constructor(u"!url", url_constructor)
yaml.add_constructor(u"!eval", eval_constructor)
yaml.add_multi_constructor(u"!ref", ref_constructor)
yaml.add_constructor(u"!ir_set", ir_set_constructor)
def add_constructors():
yaml.add_constructor(u"!assert", assert_constructor)
yaml.add_constructor(u"!record", record_constructor)
yaml.add_constructor(u"!python", python_constructor)
yaml.add_constructor(u"!menuitem", menuitem_constructor)
yaml.add_constructor(u"!workflow", workflow_constructor)
yaml.add_constructor(u"!act_window", act_window_constructor)
yaml.add_constructor(u"!function", function_constructor)
yaml.add_constructor(u"!report", report_constructor)
yaml.add_constructor(u"!context", context_constructor)
yaml.add_constructor(u"!delete", delete_constructor)
yaml.add_constructor(u"!url", url_constructor)
yaml.add_constructor(u"!eval", eval_constructor)
yaml.add_multi_constructor(u"!ref", ref_constructor)
yaml.add_constructor(u"!ir_set", ir_set_constructor)
add_constructors()