2
# -*- encoding: utf-8 -*-
3
##############################################################################
5
# OpenERP, Open Source Management Solution
6
# Copyright (C) 2011 TeMPO Consulting, MSF. All Rights Reserved
7
# Developer: Max Mumford
9
# This program is free software: you can redistribute it and/or modify
10
# it under the terms of the GNU Affero General Public License as
11
# published by the Free Software Foundation, either version 3 of the
12
# License, or (at your option) any later version.
14
# This program is distributed in the hope that it will be useful,
15
# but WITHOUT ANY WARRANTY; without even the implied warranty of
16
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17
# GNU Affero General Public License for more details.
19
# You should have received a copy of the GNU Affero General Public License
20
# along with this program. If not, see <http://www.gnu.org/licenses/>.
22
##############################################################################
25
from osv import fields
29
class field_access_rule(osv.osv):
31
Lets user create access and sync propagation rules for fields of models.
32
This class defines which model, instance level and groups to target
35
_name = "msf_field_access_rights.field_access_rule"
36
_description = 'Field Access Rule'
39
'name': fields.char('Name', size=256, required=True),
40
'model_id': fields.many2one('ir.model', 'Model', help='The type of data to which this rule applies', required=True, ondelete='cascade'),
41
'model_name': fields.char('Model Name', size=256, help='The technical name for the model. This is used to make searching for Field Access Rules easier.'),
42
'instance_level': fields.selection((('hq', 'HQ'), ('coordo', 'Coordo'), ('project', 'Project')), 'Instance Level', help='The Instance Level that this rule applies to'),
43
'domain_id': fields.many2one('ir.filters', 'Filter', domain='[("model_id","=",model_name)]', ondelete="set null", help='Choose a pre-defined Filter to filter which records this rule applies to. Click the Create New Filter button, define some seach criteria, save your custom filter, then return to this form and type your new filters name here to use it for this rule. Note: Due to a technical constraint, all "like" or "ilike" operators will be automatically replaced with "=".'),
44
'domain_text': fields.text('Advanced Filter', help='The Filter that chooses which records this rule applies to'),
45
'group_ids': fields.many2many('res.groups', 'field_access_rule_groups_rel', 'field_access_rule_id', 'group_id', 'Groups', help='A list of groups that should be affected by this rule. If you leave this empty, this rule will apply to all groups.'),
46
'field_access_rule_line_ids': fields.one2many('msf_field_access_rights.field_access_rule_line', 'field_access_rule', 'Field Access Rule Lines', help='A list of fields and their specific access and synchronization propagation rules that will be implemented by this rule. If you have left out any fields, users will have full write access, and all values will be synchronized when the record is created or editted.', required=True),
47
'comment': fields.text('Comment', help='A description of what this rule does'),
48
'active': fields.boolean('Active', help='If checked, this rule will be applied. This rule must be validated first.'),
49
'status': fields.selection((('not_validated', 'Not Validated'), ('validated', 'Model Validated'), ('domain_validated', 'Filter Validated')), 'Status', help='The validation status of the rule. The Filter must be valid for this rule to be validated.', required=True),
54
'status': 'not_validated'
58
('name_unique', 'unique (name)', "The name you have chosen has already been used, and it must be unique. Please choose a different name."),
59
('domaintext_ilike1', 'check(domain_text <> $$"like"$$)', 'Due to technical constraints, you cannot use the operator "ilike" in a domain'),
60
('domaintext_ilike2', "check(domain_text <> $$'like'$$)", 'Due to technical constraints, you cannot use the operator "ilike" in a domain'),
61
('domaintext_like1', 'check(domain_text <> $$"ilike"$$)', 'Due to technical constraints, you cannot use the operator "like" in a domain'),
62
('domaintext_like2', "check(domain_text <> $$'ilike'$$)", 'Due to technical constraints, you cannot use the operator "like" in a domain'),
65
def create(self, cr, user, vals, context=None):
67
# get model_name from model
68
vals['model_name'] = self.pool.get('ir.model').browse(cr, user, vals['model_id'], context=context).model
69
return super(field_access_rule, self).create(cr, user, vals, context=context)
71
def write(self, cr, uid, ids, values, context=None):
73
if not isinstance(ids, (list, tuple)):
76
# if domain_text has changed, change status to not_validated
77
if values.get('domain_text'):
79
record = self.browse(cr, uid, ids[0], context=context)
80
domain_text = getattr(record, 'domain_text', '')
82
if domain_text != values['domain_text']:
83
values['status'] = 'validated'
85
values['status'] = 'validated'
87
# deactivate if not validated
88
if 'status' in values and values['status'] == 'validated':
89
values['active'] = False
91
return super(field_access_rule, self).write(cr, uid, ids, values, context=context)
93
def copy(self, cr, uid, id, default, context=None):
94
raise orm.except_orm('Duplication Disabled', 'The duplication feature has been disabled for Field Access Rules')
96
def onchange_model_id(self, cr, uid, ids, model, context=None):
98
model = self.pool.get('ir.model').browse(cr, uid, model, context=context)
99
return {'value': {'model_name': model.model}}
101
return {'value': {'model_name': ''}}
103
def onchange_domain_id(self, cr, uid, ids, domain_id):
105
Returns the corresponding domain for the selected pre-defined domain filter after replacing like and ilike with '='
108
df = self.pool.get('ir.filters').browse(cr, uid, domain_id)
109
df.domain = df.domain.replace("'ilike'", "'='").replace('"ilike"', '"="').replace("'like'","'='").replace('"like"','"="')
110
return {'value': {'domain_text': df.domain, 'status': 'validated', 'active': False}}
112
return {'value': {'domain_text': '', 'status': 'validated', 'active': False}}
114
def onchange_domain_text(self, cr, uid, ids, domain_text, context=None):
116
return {'value': {'status': 'validated', 'active': False}}
120
def validate_button(self, cr, uid, ids, context=None):
121
return self.write(cr, uid, ids, {'status':'validated'}, context=context)
123
def create_new_filter_button(self, cr, uid, ids, context=None):
125
Send the user to the list view of the selected model so they can save a new filter
127
assert len(ids) <= 1, "Cannot work on list of ids longer than one"
129
record = self.browse(cr, uid, ids[0])
131
# search in ir.ui.view for form and tree views for this model. If they exist, return action, else return None, otherwise openerp will error
132
view_pool = self.pool.get('ir.ui.view')
133
form = view_pool.search(cr, 1, [('type','=','form'),('model','=',record.model_name)])
134
tree = view_pool.search(cr, 1, [('type','=','tree'),('model','=',record.model_name)])
138
'name': 'Create a New Filter For: %s' % record.model_id.name,
139
'res_model': record.model_id.model,
140
'type': 'ir.actions.act_window',
142
'view_mode':'tree,form',
147
raise osv.except_osv('No List View', 'The chosen model has no List view so this feature cannot be used. You can still manually type a filter in the Advanced Filter field...')
149
def generate_rules_button(self, cr, uid, ids, context=None):
151
Generate and return field_access_rule_lines for each field of the model and all inherited models, with Write Access checked
155
fields_pool = self.pool.get('ir.model.fields')
158
record = self.browse(cr, uid, id)
159
if record.field_access_rule_line_ids:
160
raise osv.except_osv('Remove Field Access Rule Lines First From %s' % id, 'Please remove all existing Field Access Rule Lines before generating new ones')
162
fields_search = fields_pool.search(cr, uid, [('model_id', '=', record.model_id.id)], context=context)
163
fields = fields_pool.browse(cr, uid, fields_search, context=context)
165
res = [(0, 0, {'field': i.id, 'field_name': i.name}) for i in fields]
166
self.write(cr, uid, id, {'field_access_rule_line_ids': res})
169
def manage_rule_lines_button(self, cr, uid, ids, context=None):
171
Send the user to a list view of field_access_rule_line's for this field_access_rule.
173
assert len(ids) <= 1, "Cannot work on list of ids != 1"
175
this = self.browse(cr, uid, ids, context=context)[0]
176
x, view_id = self.pool.get('ir.model.data').get_object_reference(cr, uid, 'msf_field_access_rights', 'field_access_rule_full_tree_view')
179
'type': 'ir.actions.act_window',
180
'name': 'Field Access Rule Lines for rule: %s' % this.name,
182
'view_mode':'tree,form',
183
'view_id': [view_id],
185
'res_model': 'msf_field_access_rights.field_access_rule_line',
187
'search_default_field_access_rule': ids[0],
191
def validate_domain_button(self, cr, uid, ids, context=None):
193
Validates the domain_text filter, and if successful, changes the Status field to validated
195
assert len(ids) <= 1, "Cannot work on list of ids != 1"
197
exception_title = 'Invalid Filter'
198
exception_body = 'The filter you have typed is invalid. You can create a filter using the Create New Filter button'
200
record = self.browse(cr, uid, ids[0], context=context)
202
if record.domain_text:
203
pool = self.pool.get(record.model_name)
205
raise osv.except_osv('Invalid Model', 'The model you have chosen is invalid. Please use the auto-complete to choose a valid one.')
208
domain = eval(record.domain_text)
209
if not isinstance(domain, list):
210
raise osv.except_osv(exception_title, exception_body)
212
raise osv.except_osv(exception_title, exception_body)
215
pool.search(cr, uid, domain, context=context)
216
except (ValueError, psycopg2.ProgrammingError):
217
raise osv.except_osv(exception_title, exception_body)
219
self.write(cr, uid, ids, {'status': 'domain_validated'}, context=context)
222
self.write(cr, uid, ids, {'status': 'domain_validated'}, context=context)