[Merge] Merge with trunk addons
[odoo/odoo.git] / bin / addons / base / ir / ir_ui_view.py
1 # -*- coding: utf-8 -*-
2 ##############################################################################
3 #
4 #    OpenERP, Open Source Management Solution
5 #    Copyright (C) 2004-2009 Tiny SPRL (<http://tiny.be>).
6 #
7 #    This program is free software: you can redistribute it and/or modify
8 #    it under the terms of the GNU Affero General Public License as
9 #    published by the Free Software Foundation, either version 3 of the
10 #    License, or (at your option) any later version.
11 #
12 #    This program is distributed in the hope that it will be useful,
13 #    but WITHOUT ANY WARRANTY; without even the implied warranty of
14 #    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 #    GNU Affero General Public License for more details.
16 #
17 #    You should have received a copy of the GNU Affero General Public License
18 #    along with this program.  If not, see <http://www.gnu.org/licenses/>.
19 #
20 ##############################################################################
21
22 from osv import fields,osv
23 from lxml import etree
24 from tools import graph
25 import tools
26 import netsvc
27 import os
28
29 def _check_xml(self, cr, uid, ids, context={}):
30     for view in self.browse(cr, uid, ids, context):
31         eview = etree.fromstring(view.arch.encode('utf8'))
32         frng = tools.file_open(os.path.join('base','rng','view.rng'))
33         relaxng_doc = etree.parse(frng)
34         relaxng = etree.RelaxNG(relaxng_doc)
35         if not relaxng.validate(eview):
36             logger = netsvc.Logger()
37             logger.notifyChannel('init', netsvc.LOG_ERROR, 'The view does not fit the required schema !')
38             logger.notifyChannel('init', netsvc.LOG_ERROR, tools.ustr(relaxng.error_log.last_error))
39             return False
40     return True
41
42 class view_custom(osv.osv):
43     _name = 'ir.ui.view.custom'
44     _columns = {
45         'ref_id': fields.many2one('ir.ui.view', 'Original View'),
46         'user_id': fields.many2one('res.users', 'User'),
47         'arch': fields.text('View Architecture', required=True),
48     }
49 view_custom()
50
51 class view(osv.osv):
52     _name = 'ir.ui.view'
53     _columns = {
54         'name': fields.char('View Name',size=64,  required=True),
55         'model': fields.char('Object', size=64, required=True),
56         'priority': fields.integer('Priority', required=True),
57         'type': fields.selection((
58             ('tree','Tree'),
59             ('form','Form'),
60             ('mdx','mdx'),
61             ('graph', 'Graph'),
62             ('calendar', 'Calendar'),
63             ('diagram','Diagram'),
64             ('gantt', 'Gantt'),
65             ('search','Search')), 'View Type', required=True),
66         'arch': fields.text('View Architecture', required=True),
67         'inherit_id': fields.many2one('ir.ui.view', 'Inherited View', ondelete='cascade'),
68         'field_parent': fields.char('Child Field',size=64),
69         'xml_id': fields.function(osv.osv.get_xml_id, type='char', size=128, string="XML ID",
70                                   method=True),
71     }
72     _defaults = {
73         'arch': '<?xml version="1.0"?>\n<tree string="Unknwown">\n\t<field name="name"/>\n</tree>',
74         'priority': 16
75     }
76     _order = "priority"
77     _constraints = [
78         (_check_xml, 'Invalid XML for View Architecture!', ['arch'])
79     ]
80
81     def read(self, cr, uid, ids, fields=None, context={}, load='_classic_read'):
82
83         if not isinstance(ids, (list, tuple)):
84             ids = [ids]
85
86         result = super(view, self).read(cr, uid, ids, fields, context, load)
87
88         for rs in result:
89             if rs.get('model') == 'board.board':
90                 cr.execute("select id,arch,ref_id from ir_ui_view_custom where user_id=%s and ref_id=%s", (uid, rs['id']))
91                 oview = cr.dictfetchall()
92                 if oview:
93                     rs['arch'] = oview[0]['arch']
94
95
96         return result
97
98     def write(self, cr, uid, ids, vals, context={}):
99
100         if not isinstance(ids, (list, tuple)):
101             ids = [ids]
102
103         exist = self.pool.get('ir.ui.view').browse(cr, uid, ids[0])
104         if exist.model == 'board.board' and 'arch' in vals:
105             vids = self.pool.get('ir.ui.view.custom').search(cr, uid, [('user_id','=',uid), ('ref_id','=',ids[0])])
106             vals2 = {'user_id': uid, 'ref_id': ids[0], 'arch': vals.pop('arch')}
107
108             # write fields except arch to the `ir.ui.view`
109             result = super(view, self).write(cr, uid, ids, vals, context)
110
111             if not vids:
112                 self.pool.get('ir.ui.view.custom').create(cr, uid, vals2)
113             else:
114                 self.pool.get('ir.ui.view.custom').write(cr, uid, vids, vals2)
115
116             return result
117
118         return super(view, self).write(cr, uid, ids, vals, context)
119
120     def graph_get(self, cr, uid, id, model, node_obj, conn_obj, src_node, des_node,label,scale,context={}):
121         if not label:
122             label = []
123         nodes=[]
124         nodes_name=[]
125         transitions=[]
126         start=[]
127         tres={}
128         labels={}
129         no_ancester=[]
130         blank_nodes = []
131
132         _Model_Obj=self.pool.get(model)
133         _Node_Obj=self.pool.get(node_obj)
134         _Arrow_Obj=self.pool.get(conn_obj)
135
136         for model_key,model_value in _Model_Obj._columns.items():
137                 if model_value._type=='one2many':
138                     if model_value._obj==node_obj:
139                         _Node_Field=model_key
140                         _Model_Field=model_value._fields_id
141                     flag=False
142                     for node_key,node_value in _Node_Obj._columns.items():
143                         if node_value._type=='one2many':
144                              if node_value._obj==conn_obj:
145                                  if src_node in _Arrow_Obj._columns and flag:
146                                     _Source_Field=node_key
147                                  if des_node in _Arrow_Obj._columns and not flag:
148                                     _Destination_Field=node_key
149                                     flag = True
150
151         datas = _Model_Obj.read(cr, uid, id, [],context)
152         for a in _Node_Obj.read(cr,uid,datas[_Node_Field],[]):
153             if a[_Source_Field] or a[_Destination_Field]:
154                 nodes_name.append((a['id'],a['name']))
155                 nodes.append(a['id'])
156             else:
157                 blank_nodes.append({'id': a['id'],'name':a['name']})
158
159             if a.has_key('flow_start') and a['flow_start']:
160                 start.append(a['id'])
161             else:
162                 if not a[_Source_Field]:
163                     no_ancester.append(a['id'])
164             for t in _Arrow_Obj.read(cr,uid, a[_Destination_Field],[]):
165                 transitions.append((a['id'], t[des_node][0]))
166                 tres[str(t['id'])] = (a['id'],t[des_node][0])
167                 label_string = ""
168                 if label:
169                     for lbl in eval(label):
170                         if t.has_key(str(lbl)) and str(t[lbl])=='False':
171                             label_string = label_string + ' '
172                         else:
173                             label_string = label_string + " " + t[lbl]
174                 labels[str(t['id'])] = (a['id'],label_string)
175         g  = graph(nodes, transitions, no_ancester)
176         g.process(start)
177         g.scale(*scale)
178         result = g.result_get()
179         results = {}
180         for node in nodes_name:
181             results[str(node[0])] = result[node[0]]
182             results[str(node[0])]['name'] = node[1]
183         return {'nodes': results, 'transitions': tres, 'label' : labels, 'blank_nodes': blank_nodes}
184 view()
185
186 class view_sc(osv.osv):
187     _name = 'ir.ui.view_sc'
188     _columns = {
189         'name': fields.char('Shortcut Name', size=64, required=True),
190         'res_id': fields.many2one('ir.ui.menu','Resource Ref.', ondelete='cascade'),
191         'sequence': fields.integer('Sequence'),
192         'user_id': fields.many2one('res.users', 'User Ref.', required=True, ondelete='cascade'),
193         'resource': fields.char('Resource Name', size=64, required=True)
194     }
195
196     def get_sc(self, cr, uid, user_id, model='ir.ui.menu', context={}):
197         ids = self.search(cr, uid, [('user_id','=',user_id),('resource','=',model)], context=context)
198         return self.read(cr, uid, ids, ['res_id','name'], context=context)
199
200     _order = 'sequence'
201     _defaults = {
202         'resource': lambda *a: 'ir.ui.menu',
203         'user_id': lambda obj, cr, uid, context: uid,
204     }
205     _sql_constraints = [
206         ('shortcut_unique', 'unique(res_id, user_id)', 'Shortcut for this menu already exists!'),
207     ]
208         
209 view_sc()
210
211 # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
212