1 # -*- coding: utf-8 -*-
2 ##############################################################################
4 # OpenERP, Open Source Management Solution
5 # Copyright (C) 2004-2009 Tiny SPRL (<http://tiny.be>).
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.
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.
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/>.
20 ##############################################################################
23 from lxml import etree
24 import openerp.netsvc as netsvc
25 import openerp.tools as tools
26 from openerp.tools.safe_eval import safe_eval
29 from openerp.osv.orm import browse_null, browse_record
30 import openerp.pooler as pooler
32 class InheritDict(dict):
33 # Might be usefull when we're doing name lookup for call or eval.
35 def __init__(self, parent=None):
38 def __getitem__(self, name):
40 return super(InheritDict, self).__getitem__(name)
45 return self.parent[name]
48 if isinstance(val, str):
49 unicode_val = unicode(val, 'utf-8')
50 elif isinstance(val, unicode):
53 unicode_val = unicode(val)
56 class document(object):
57 def __init__(self, cr, uid, datas, func=False):
58 # create a new document
60 self.pool = pooler.get_pool(cr.dbname)
61 self.func = func or {}
66 def node_attrs_get(self, node):
71 def get_value(self, browser, field_path):
72 fields = field_path.split('.')
80 if isinstance(value, list):
84 if isinstance(value, browse_null):
89 if isinstance(value, browse_null) or (type(value)==bool and not value):
94 def get_value2(self, browser, field_path):
95 value = self.get_value(browser, field_path)
96 if isinstance(value, browse_record):
98 elif isinstance(value, browse_null):
103 def eval(self, record, expr):
104 #TODO: support remote variables (eg address.title) in expr
105 # how to do that: parse the string, find dots, replace those dotted variables by temporary
106 # "simple ones", fetch the value of those variables and add them (temporarily) to the _data
107 # dictionary passed to eval
109 #FIXME: it wont work if the data hasn't been fetched yet... this could
110 # happen if the eval node is the first one using this browse_record
111 # the next line is a workaround for the problem: it causes the resource to be loaded
112 #Pinky: Why not this ? eval(expr, browser) ?
113 # name = browser.name
114 # data_dict = browser._data[self.get_value(browser, 'id')]
115 return safe_eval(expr, {}, {'obj': record})
117 def parse_node(self, node, parent, browser, datas=None):
118 attrs = self.node_attrs_get(node)
120 if attrs['type']=='field':
121 value = self.get_value(browser, attrs['name'])
123 if value == '' and 'default' in attrs:
124 value = attrs['default']
125 el = etree.SubElement(parent, node.tag)
126 el.text = tounicode(value)
128 for key, value in attrs.iteritems():
129 if key not in ('type', 'name', 'default'):
132 elif attrs['type']=='attachment':
133 if isinstance(browser, list):
134 model = browser[0]._table_name
136 model = browser._table_name
138 value = self.get_value(browser, attrs['name'])
140 ids = self.pool.get('ir.attachment').search(self.cr, self.uid, [('res_model','=',model),('res_id','=',int(value))])
141 datas = self.pool.get('ir.attachment').read(self.cr, self.uid, ids)
144 # if there are several, pick first
146 fname = str(datas['datas_fname'])
147 ext = fname.split('.')[-1].lower()
148 if ext in ('jpg','jpeg', 'png'):
150 from StringIO import StringIO
151 dt = base64.decodestring(datas['datas'])
154 i = str(len(self.bin_datas))
155 self.bin_datas[i] = fp
156 el = etree.SubElement(parent, node.tag)
159 elif attrs['type']=='data':
161 txt = self.datas.get('form', {}).get(attrs['name'], '')
162 el = etree.SubElement(parent, node.tag)
165 elif attrs['type']=='function':
166 if attrs['name'] in self.func:
167 txt = self.func[attrs['name']](node)
169 txt = print_fnc.print_fnc(attrs['name'], node)
170 el = etree.SubElement(parent, node.tag)
173 elif attrs['type']=='eval':
174 value = self.eval(browser, attrs['expr'])
175 el = etree.SubElement(parent, node.tag)
178 elif attrs['type']=='fields':
179 fields = attrs['name'].split(',')
182 value = tuple([self.get_value2(b, f) for f in fields])
183 if not value in vals:
185 vals[value].append(b)
189 if 'order' in attrs and attrs['order']=='desc':
192 v_list = [vals[k] for k in keys]
194 el = etree.SubElement(parent, node.tag)
196 self.parse_node(el_cld, el, v)
198 elif attrs['type']=='call':
199 if len(attrs['args']):
201 # fetches the values of the variables which names where passed in the args attribute
202 args = [self.eval(browser, arg) for arg in attrs['args'].split(',')]
206 if attrs.has_key('model'):
207 obj = self.pool.get(attrs['model'])
209 if isinstance(browser, list):
210 obj = browser[0]._table
215 if attrs.has_key('ids'):
216 ids = self.eval(browser, attrs['ids'])
218 if isinstance(browser, list):
219 ids = [b.id for b in browser]
223 # call the method itself
224 newdatas = getattr(obj, attrs['name'])(self.cr, self.uid, ids, *args)
226 def parse_result_tree(node, parent, datas):
227 if not node.tag == etree.Comment:
228 el = etree.SubElement(parent, node.tag)
229 atr = self.node_attrs_get(node)
231 if not isinstance(datas[atr['value']], (str, unicode)):
232 txt = str(datas[atr['value']])
234 txt = datas[atr['value']]
238 parse_result_tree(el_cld, el, datas)
239 if not isinstance(newdatas, list):
240 newdatas = [newdatas]
241 for newdata in newdatas:
242 parse_result_tree(node, parent, newdata)
244 elif attrs['type']=='zoom':
245 value = self.get_value(browser, attrs['name'])
247 if not isinstance(value, list):
252 el = etree.SubElement(parent, node.tag)
254 self.parse_node(el_cld, el, v)
256 # if there is no "type" attribute in the node, copy it to the xml data and parse its children
257 if not node.tag == etree.Comment:
258 if node.tag == parent.tag:
261 el = etree.SubElement(parent, node.tag)
263 self.parse_node(el_cld,el, browser)
265 return etree.tostring(self.doc,encoding="utf-8",xml_declaration=True,pretty_print=True)
267 def parse_tree(self, ids, model, context=None):
270 browser = self.pool.get(model).browse(self.cr, self.uid, ids, context)
271 self.parse_node(self.dom, self.doc, browser)
273 def parse_string(self, xml, ids, model, context=None):
276 # parses the xml template to memory
277 self.dom = etree.XML(xml)
278 # create the xml data from the xml template
279 self.parse_tree(ids, model, context)
281 def parse(self, filename, ids, model, context=None):
284 # parses the xml template to memory
285 src_file = tools.file_open(filename)
287 self.dom = etree.XML(src_file.read())
288 self.doc = etree.Element(self.dom.tag)
289 self.parse_tree(ids, model, context)
298 # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: