Files
tryton/trytond/ir/ui/view.py
T

303 lines
11 KiB
Python
Raw Normal View History

2008-11-28 16:58:11 +01:00
#This file is part of Tryton. The COPYRIGHT file at the top level of
#this repository contains the full copyright notices and license terms.
2007-12-10 01:04:13 +01:00
"View"
from trytond.osv import fields, OSV
2008-02-11 14:01:47 +01:00
from difflib import SequenceMatcher
import os
2008-09-12 19:57:33 +02:00
import logging
2008-06-16 23:15:00 +02:00
from lxml import etree
from trytond.backend import TableHandler
2007-12-10 01:04:13 +01:00
class View(OSV):
"View"
_name = 'ir.ui.view'
_description = __doc__
2007-12-29 19:02:56 +01:00
_rec_name = 'model'
2008-06-10 14:05:37 +02:00
model = fields.Char('Model', required=True, select=1)
2008-04-25 13:09:16 +02:00
priority = fields.Integer('Priority', required=True, select=1)
2008-04-17 15:01:43 +02:00
type = fields.Selection([
2008-10-03 00:59:51 +02:00
(False, ''),
2008-10-03 01:02:02 +02:00
('tree','Tree'),
('form','Form'),
('graph', 'Graph'),
('calendar', 'Calendar'),
('board', 'Board'),
], 'View Type', select=1)
2008-12-10 22:22:17 +01:00
arch = fields.Text('View Architecture')
2008-08-19 09:51:02 +02:00
inherit = fields.Many2One('ir.ui.view', 'Inherited View', select=1,
ondelete='CASCADE')
2008-11-28 17:00:14 +01:00
field_childs = fields.Char('Childs Field', states={
'invisible': "type != 'tree'",
})
2008-06-10 14:05:37 +02:00
module = fields.Char('Module', readonly=True)
2008-11-28 16:58:11 +01:00
domain = fields.Char('Domain', states={
'invisible': "not bool(inherit)",
})
def __init__(self):
super(View, self).__init__()
self._constraints += [
2008-08-05 11:34:45 +02:00
('check_xml', 'invalid_xml'),
]
2008-08-05 11:34:45 +02:00
self._error_messages.update({
2008-10-03 01:00:13 +02:00
'invalid_xml': 'Invalid XML for View!',
2008-08-05 11:34:45 +02:00
})
2008-04-22 17:47:24 +02:00
self._order.insert(0, ('priority', 'ASC'))
2007-12-10 01:04:13 +01:00
2009-02-02 13:08:25 +01:00
def init(self, cursor, module_name):
super(View, self).init(cursor, module_name)
table = TableHandler(cursor, self._table, self._name, module_name)
2008-12-10 22:22:17 +01:00
# Migration from 1.0 arch no more required
table.not_null_action('arch', action='remove')
2008-12-10 22:22:17 +01:00
def default_arch(self, cursor, user, context=None):
return '<?xml version="1.0"?>'
def default_priority(self, cursor, user, context=None):
return 16
def default_module(self, cursor, user, context=None):
return context and context.get('module', '') or ''
def check_xml(self, cursor, user, ids):
2007-12-10 01:04:13 +01:00
"Check XML"
2008-02-11 14:01:47 +01:00
views = self.browse(cursor, user, ids)
for view in views:
cursor.execute('SELECT id, name, src FROM ir_translation ' \
'WHERE lang = %s ' \
'AND type = %s ' \
'AND name = %s '\
'AND module = %s',
('en_US', 'view', view.model, view.module))
trans_views = {}
for trans in cursor.dictfetchall():
trans_views[trans['src']] = trans
2008-12-10 22:22:17 +01:00
if not view.arch:
continue
2008-06-16 23:15:00 +02:00
xml = view.arch.strip()
2008-12-10 22:22:17 +01:00
if not xml:
continue
tree = etree.fromstring(xml)
2008-06-17 13:17:48 +02:00
# validate the tree using RelaxNG
rng_name = os.path.join(os.path.dirname(__file__),
(view.inherit and view.inherit.type or view.type) + '.rng')
if hasattr(etree, 'RelaxNG'):
validator = etree.RelaxNG(file=rng_name)
if not validator.validate(tree):
2008-09-12 19:57:33 +02:00
logger = logging.getLogger('ir')
error_log = reduce(lambda x, y: str(x) + '\n' + str(y),
2008-06-17 13:17:48 +02:00
validator.error_log.filter_from_errors())
2008-09-12 19:57:33 +02:00
logger.error(
'Invalid xml view:\n%s' % (str(error_log) + '\n' + xml))
return False
2008-06-16 23:15:00 +02:00
root_element = tree.getroottree().getroot()
strings = self._translate_view(root_element)
2008-02-11 14:01:47 +01:00
view_ids = self.search(cursor, 0, [
('model', '=', view.model),
('id', '!=', view.id),
('module', '=', view.module),
2008-02-11 14:01:47 +01:00
])
for view2 in self.browse(cursor, 0, view_ids):
2008-06-16 23:15:00 +02:00
tree2 = etree.fromstring(view2.arch)
root2_element = tree2.getroottree().getroot()
strings += self._translate_view(root2_element)
2008-02-11 14:01:47 +01:00
if not strings:
continue
for string in {}.fromkeys(strings).keys():
done = False
if string in trans_views:
del trans_views[string]
2008-02-11 14:01:47 +01:00
continue
for string_trans in trans_views:
if string_trans in strings:
continue
2008-02-11 14:01:47 +01:00
seqmatch = SequenceMatcher(lambda x: x == ' ',
string, string_trans)
if seqmatch.ratio() == 1.0:
del trans_views[string_trans]
2008-02-11 14:01:47 +01:00
done = True
break
if seqmatch.ratio() > 0.6:
cursor.execute('UPDATE ir_translation ' \
'SET src = %s, ' \
'fuzzy = True ' \
2008-12-05 18:30:54 +01:00
'WHERE id = %s ',
(string, trans_views[string_trans]['id']))
del trans_views[string_trans]
2008-02-11 14:01:47 +01:00
done = True
break
if not done:
cursor.execute('INSERT INTO ir_translation ' \
2008-07-30 12:26:32 +02:00
'(name, lang, type, src, value, module, fuzzy)' \
'VALUES (%s, %s, %s, %s, %s, %s, false)',
2008-02-11 14:01:47 +01:00
(view.model, 'en_US', 'view', string, '',
view.module))
2008-06-10 14:05:37 +02:00
if strings:
cursor.execute('DELETE FROM ir_translation ' \
'WHERE name = %s ' \
'AND type = %s ' \
'AND module = %s ' \
2008-06-10 14:05:37 +02:00
'AND src NOT IN ' \
'(' + ','.join(['%s' for x in strings]) + ')',
(view.model, 'view', view.module) + tuple(strings))
2007-12-10 01:04:13 +01:00
return True
2008-07-25 12:49:00 +02:00
def delete(self, cursor, user, ids, context=None):
2009-01-22 12:17:10 +01:00
res = super(View, self).delete(cursor, user, ids, context=context)
# Restart the cache
for model in self.pool.object_name_list():
2007-12-27 23:42:01 +01:00
try:
self.pool.get(model).fields_view_get(cursor.dbname)
2007-12-27 23:42:01 +01:00
except:
pass
return res
def create(self, cursor, user, vals, context=None):
res = super(View, self).create(cursor, user, vals, context=context)
# Restart the cache
for model in self.pool.object_name_list():
2007-12-27 23:42:01 +01:00
try:
2008-04-24 17:20:41 +02:00
self.pool.get(model).fields_view_get(cursor.dbname)
2007-12-27 23:42:01 +01:00
except:
pass
return res
def write(self, cursor, user, ids, vals, context=None):
res = super(View, self).write(cursor, user, ids, vals, context=context)
# Restart the cache
for model in self.pool.object_name_list():
2007-12-27 23:42:01 +01:00
try:
self.pool.get(model).fields_view_get(cursor.dbname)
2007-12-27 23:42:01 +01:00
except:
pass
return res
2008-06-16 23:15:00 +02:00
def _translate_view(self, element):
2008-02-11 14:01:47 +01:00
strings = []
for attr in ('string', 'sum', 'confirm', 'help'):
2008-12-30 14:12:22 +01:00
if element.get(attr):
string = element.get(attr)
if string:
strings.append(string)
2008-06-16 23:15:00 +02:00
for child in element:
2008-02-11 14:01:47 +01:00
strings.extend(self._translate_view(child))
return strings
2007-12-10 01:04:13 +01:00
View()
class ViewShortcut(OSV):
"View shortcut"
_name = 'ir.ui.view_sc'
_description = __doc__
2008-06-10 14:05:37 +02:00
name = fields.Char('Shortcut Name', required=True)
res_id = fields.Integer('Resource Ref.', required=True)
sequence = fields.Integer('Sequence')
user_id = fields.Many2One('res.user', 'User Ref.', required=True,
ondelete='cascade')
2008-06-10 14:05:37 +02:00
resource = fields.Char('Resource Name', required=True)
2007-12-10 01:04:13 +01:00
2008-03-03 18:30:39 +01:00
def __init__(self):
super(ViewShortcut, self).__init__()
self._rpc_allowed.append('get_sc')
2008-04-22 17:47:24 +02:00
self._order.insert(0, ('sequence', 'ASC'))
2007-12-10 01:04:13 +01:00
def get_sc(self, cursor, user, user_id, model='ir.ui.menu', context=None):
"Provide user's shortcuts"
ids = self.search(cursor, user, [
('user_id','=',user_id),
('resource','=',model),
], context=context)
return self.read(cursor, user, ids, ['res_id', 'name'], context=context)
def default_resource(self, cursor, user, context=None):
return 'ir.ui.menu'
2007-12-10 01:04:13 +01:00
ViewShortcut()
class ViewTreeWidth(OSV):
"View Tree Width"
_name = 'ir.ui.view_tree_width'
_description = __doc__
_rec_name = 'model'
model = fields.Char('Model', required=True, select=1)
field = fields.Char('Field', required=True, select=1)
user = fields.Many2One('res.user', 'User', required=True,
ondelete='CASCADE', select=1)
width = fields.Integer('Width')
def __init__(self):
super(ViewTreeWidth, self).__init__()
self._rpc_allowed += [
'set_width',
]
2008-07-25 12:49:00 +02:00
def delete(self, cursor, user, ids, context=None):
if isinstance(ids, (int, long)):
ids = [ids]
views = self.browse(cursor, user, ids, context=context)
for view in views:
# Restart the cache
try:
self.pool.get(view.model).fields_view_get(cursor.dbname)
except:
pass
2008-07-25 12:49:00 +02:00
res = super(ViewTreeWidth, self).delete(cursor, user, ids, context=context)
return res
def create(self, cursor, user, vals, context=None):
res = super(ViewTreeWidth, self).create(cursor, user, vals, context=context)
if 'model' in vals:
model = vals['model']
# Restart the cache
try:
self.pool.get(model).fields_view_get(cursor.dbname)
except:
pass
return res
def write(self, cursor, user, ids, vals, context=None):
if isinstance(ids, (int, long)):
ids = [ids]
views = self.browse(cursor, user, ids)
for view in views:
# Restart the cache
try:
self.pool.get(view.model).fields_view_get(cursor.dbname)
except:
pass
res = super(ViewTreeWidth, self).write(cursor, user, ids, vals, context=context)
views = self.browse(cursor, user, ids)
for view in views:
# Restart the cache
try:
self.pool.get(view.model).fields_view_get(cursor.dbname)
except:
pass
return res
def set_width(self, cursor, user, model, fields, context=None):
'''
Set width for the current user on the model.
fields is a dictionary with key: field name and value: width.
'''
ids = self.search(cursor, user, [
('user', '=', user),
('model', '=', model),
('field', 'in', fields.keys()),
], context=context)
2008-07-25 12:49:00 +02:00
self.delete(cursor, user, ids, context=context)
for field in fields.keys():
self.create(cursor, user, {
'model': model,
'field': field,
'user': user,
'width': fields[field],
}, context=context)
ViewTreeWidth()