1
##############################################################################
3
# Copyright (c) 2004-2006 TINY SPRL. (http://tiny.be) All Rights Reserved.
5
# $Id: interface.py 1304 2005-09-08 14:35:42Z nicoe $
7
# WARNING: This program as such is intended to be used by professional
8
# programmers who take the whole responsability of assessing all potential
9
# consequences resulting from its eventual inadequacies and bugs
10
# End users who are looking for a ready-to-use solution with commercial
11
# garantees and support are strongly adviced to contract a Free Software
14
# This program is Free Software; you can redistribute it and/or
15
# modify it under the terms of the GNU General Public License
16
# as published by the Free Software Foundation; either version 2
17
# of the License, or (at your option) any later version.
19
# This program is distributed in the hope that it will be useful,
20
# but WITHOUT ANY WARRANTY; without even the implied warranty of
21
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22
# GNU General Public License for more details.
24
# You should have received a copy of the GNU General Public License
25
# along with this program; if not, write to the Free Software
26
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
28
##############################################################################
32
#Ged> Why do we use libxml2 here instead of xml.dom like in other places of the code?
44
# encode a value to a string in utf8 and converts XML entities
47
if isinstance(val, str):
49
elif isinstance(val, unicode):
50
str_utf8 = val.encode('utf-8')
53
return str_utf8.replace('&', '&').replace('<','<').replace('>','>')
55
class report_int(netsvc.Service):
56
def __init__(self, name, audience='*'):
57
super(report_int, self).__init__(name, audience)
58
if name[0:7]<>'report.':
59
raise 'ConceptionError, bad report name'
62
self.name2 = '.'.join(name.split('.')[1:])
63
self.joinGroup('report')
64
self.exportMethod(self.create)
66
def create(self, cr, uid, ids, datas, context={}):
70
Class to automatically build a document using the transformation process:
71
XML -> DATAS -> RML -> PDF
73
using a XSL:RML transformation
75
class report_rml(report_int):
76
def __init__(self, name, table, tmpl, xsl):
77
super(report_rml, self).__init__(name)
82
self.generators = {'pdf': self.create_pdf, 'html': self.create_html, 'raw': self.create_raw}
84
def create(self, cr, uid, ids, datas, context):
85
xml = self.create_xml(cr, uid, ids, datas, context)
86
# file('/tmp/terp.xml','wb+').write(xml)
87
if datas.get('report_type', 'pdf') == 'raw':
89
rml = self.create_rml(cr, xml, uid, context)
90
# file('/tmp/terp.rml','wb+').write(rml)
91
report_type = datas.get('report_type', 'pdf')
92
create_doc = self.generators[report_type]
94
return (pdf, report_type)
96
def create_xml(self, cr, uid, ids, datas, context={}):
97
doc = print_xml.document(cr, uid, datas, {})
98
self.bin_datas = doc.bin_datas
99
doc.parse(self.tmpl, ids, self.table, context)
102
return self.post_process_xml_data(cr, uid, xml, context)
104
def post_process_xml_data(self, cr, uid, xml, context={}):
105
# find the position of the 3rd tag
106
# (skip the <?xml ...?> and the "root" tag)
107
iter = re.finditer('<[^>]*>', xml)
112
doc = print_xml.document(cr, uid, {}, {})
113
tmpl_path = os.path.join(tools.config['root_path'], 'addons/custom/corporate_defaults.xml')
114
doc.parse(tmpl_path, [uid], 'res.users', context)
115
corporate_header = doc.xml_get()
118
# find the position of the tag after the <?xml ...?> tag
119
iter = re.finditer('<[^>]*>', corporate_header)
123
return xml[:pos_xml] + corporate_header[pos_header:] + xml[pos_xml:]
126
# TODO: The translation doesn't work for "<tag t="1">textext<tag> tex</tag>text</tag>"
128
def create_rml(self, cr, xml, uid, context={}):
129
service = netsvc.LocalService("object_proxy")
131
# In some case we might not use xsl ...
135
# load XSL (parse it to the XML level)
136
styledoc = libxml2.parseFile(os.path.join(tools.config['root_path'],self.xsl))
138
#TODO: get all the translation in one query. That means we have to:
139
# * build a list of items to translate,
140
# * issue the query to translate them,
141
# * (re)build/update the stylesheet with the translated items
143
# translate the XSL stylesheet
144
def look_down(child, lang):
145
while child is not None:
146
if (child.type == "element") and child.hasProp('t'):
148
res = service.execute(cr.dbname, uid, 'ir.translation', '_get_source', self.name2, 'xsl', lang, child.content)
150
child.setContent(res)
151
look_down(child.children, lang)
154
if context.get('lang', False):
155
look_down(styledoc.children, context['lang'])
157
style = libxslt.parseStylesheetDoc(styledoc) # parse XSL
159
doc = libxml2.parseMemory(xml,len(xml)) # load XML (data)
160
result = style.applyStylesheet(doc, None) # create RML (apply XSL to XML data)
161
xml = style.saveResultToString(result) # save result to string
163
style.freeStylesheet()
168
def create_pdf(self, xml):
169
obj = render.rml(xml, self.bin_datas, os.path.dirname(self.tmpl))
173
def create_html(self, xml):
174
obj = render.rml2html(xml, self.bin_datas)
178
def create_raw(self, xml):
181
from report_sxw import report_sxw
183
def register_all(db):
185
#FIXME: multi-db, quoique... ca init le code donc ok. Enfin, du moins si les modules sont les memes.
187
cr.execute("SELECT * FROM ir_act_report_xml WHERE auto ORDER BY id")
188
result = cr.dictfetchall()
192
report_sxw('report.'+r['report_name'], r['model'], opj('addons',r['report_rml']))
194
report_rml('report.'+r['report_name'], r['model'], opj('addons',r['report_xml']), r['report_xsl'] and opj('addons',r['report_xsl']))