1 # -*- encoding: utf-8 -*-
2 ##############################################################################
4 # OpenERP, Open Source Management Solution
5 # Copyright (C) 2004-2009 Tiny SPRL (<http://tiny.be>). All Rights Reserved
8 # This program is free software: you can redistribute it and/or modify
9 # it under the terms of the GNU General Public License as published by
10 # the Free Software Foundation, either version 3 of the License, or
11 # (at your option) any later version.
13 # This program is distributed in the hope that it will be useful,
14 # but WITHOUT ANY WARRANTY; without even the implied warranty of
15 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 # GNU General Public License for more details.
18 # You should have received a copy of the GNU General Public License
19 # along with this program. If not, see <http://www.gnu.org/licenses/>.
21 ##############################################################################
28 from lxml import etree
39 # encode a value to a string in utf8 and converts XML entities
42 if isinstance(val, str):
44 elif isinstance(val, unicode):
45 str_utf8 = val.encode('utf-8')
48 return str_utf8.replace('&', '&').replace('<','<').replace('>','>')
50 class report_int(netsvc.Service):
51 def __init__(self, name, audience='*'):
52 assert not netsvc.service_exist(name), 'The report "%s" already exist!' % name
53 super(report_int, self).__init__(name, audience)
54 if name[0:7]<>'report.':
55 raise Exception, 'ConceptionError, bad report name, should start with "report."'
58 self.name2 = '.'.join(name.split('.')[1:])
60 self.joinGroup('report')
61 self.exportMethod(self.create)
63 def create(self, cr, uid, ids, datas, context=None):
67 Class to automatically build a document using the transformation process:
68 XML -> DATAS -> RML -> PDF
70 using a XSL:RML transformation
72 class report_rml(report_int):
73 def __init__(self, name, table, tmpl, xsl):
74 super(report_rml, self).__init__(name)
80 'pdf': self.create_pdf,
81 'html': self.create_html,
82 'raw': self.create_raw,
83 'sxw': self.create_sxw,
84 'txt': self.create_txt,
85 'odt': self.create_odt,
86 'html2html' : self.create_html2html,
89 def create(self, cr, uid, ids, datas, context):
90 xml = self.create_xml(cr, uid, ids, datas, context)
91 xml = tools.ustr(xml).encode('utf8')
92 if datas.get('report_type', 'pdf') == 'raw':
94 rml = self.create_rml(cr, xml, uid, context)
95 pool = pooler.get_pool(cr.dbname)
96 ir_actions_report_xml_obj = pool.get('ir.actions.report.xml')
97 report_xml_ids = ir_actions_report_xml_obj.search(cr, uid, [('report_name', '=', self.name[7:])], context=context)
98 self.title = report_xml_ids and ir_actions_report_xml_obj.browse(cr,uid,report_xml_ids)[0].name or 'OpenERP Report'
99 report_type = datas.get('report_type', 'pdf')
100 create_doc = self.generators[report_type]
101 pdf = create_doc(rml, title=self.title)
102 return (pdf, report_type)
104 def create_xml(self, cr, uid, ids, datas, context=None):
107 doc = print_xml.document(cr, uid, datas, {})
108 self.bin_datas.update( doc.bin_datas or {})
109 doc.parse(self.tmpl, ids, self.table, context)
112 return self.post_process_xml_data(cr, uid, xml, context)
114 def post_process_xml_data(self, cr, uid, xml, context=None):
117 # find the position of the 3rd tag
118 # (skip the <?xml ...?> and the "root" tag)
119 iter = re.finditer('<[^>]*>', xml)
124 doc = print_xml.document(cr, uid, {}, {})
125 tmpl_path = addons.get_module_resource('base', 'report', 'corporate_defaults.xml')
126 doc.parse(tmpl_path, [uid], 'res.users', context)
127 corporate_header = doc.xml_get()
130 # find the position of the tag after the <?xml ...?> tag
131 iter = re.finditer('<[^>]*>', corporate_header)
135 return xml[:pos_xml] + corporate_header[pos_header:] + xml[pos_xml:]
138 # TODO: The translation doesn't work for "<tag t="1">textext<tag> tex</tag>text</tag>"
140 def create_rml(self, cr, xml, uid, context=None):
143 service = netsvc.LocalService("object_proxy")
145 # In some case we might not use xsl ...
149 # load XSL (parse it to the XML level)
150 styledoc = libxml2.parseDoc(tools.file_open(self.xsl).read())
151 xsl_path, tail = os.path.split(self.xsl)
152 for child in styledoc.children:
153 if child.name == 'import':
154 if child.hasProp('href'):
155 imp_file = child.prop('href')
156 _x, imp_file = tools.file_open(imp_file, subdir=xsl_path, pathinfo=True)
157 child.setProp('href', urllib.quote(str(imp_file)))
159 #TODO: get all the translation in one query. That means we have to:
160 # * build a list of items to translate,
161 # * issue the query to translate them,
162 # * (re)build/update the stylesheet with the translated items
164 # translate the XSL stylesheet
165 def look_down(child, lang):
166 while child is not None:
167 if (child.type == "element") and child.hasProp('t'):
169 res = service.execute(cr.dbname, uid, 'ir.translation',
170 '_get_source', self.name2, 'xsl', lang, child.content)
172 child.setContent(res.encode('utf-8'))
173 look_down(child.children, lang)
176 if context.get('lang', False):
177 look_down(styledoc.children, context['lang'])
180 style = libxslt.parseStylesheetDoc(styledoc)
182 doc = libxml2.parseMemory(xml,len(xml))
183 # create RML (apply XSL to XML data)
184 result = style.applyStylesheet(doc, None)
185 # save result to string
186 xml = style.saveResultToString(result)
188 style.freeStylesheet()
193 def create_pdf(self, rml, localcontext = None, logo=None, title=None):
195 self.bin_datas['logo'] = logo
197 if 'logo' in self.bin_datas:
198 del self.bin_datas['logo']
199 obj = render.rml(rml, localcontext, self.bin_datas, tools.config['root_path'],title)
203 def create_html(self, rml, localcontext = None, logo=None, title=None):
204 obj = render.rml2html(rml, localcontext, self.bin_datas)
208 def create_txt(self, rml,localcontext, logo=None, title=None):
209 obj = render.rml2txt(rml, localcontext, self.bin_datas)
211 return obj.get().encode('utf-8')
213 def create_html2html(self, rml, localcontext = None, logo=None, title=None):
214 obj = render.html2html(rml, localcontext, self.bin_datas)
219 def create_raw(self,rml, localcontext = None, logo=None, title=None):
220 obj = render.odt2odt(etree.XML(rml),localcontext)
222 return etree.tostring(obj.get())
224 def create_sxw(self,rml,localcontext = None):
225 obj = render.odt2odt(rml,localcontext)
229 def create_odt(self,rml,localcontext = None):
230 obj = render.odt2odt(rml,localcontext)
234 from report_sxw import report_sxw
236 def register_all(db):
239 cr.execute("SELECT * FROM ir_act_report_xml WHERE auto=%s ORDER BY id", (True,))
240 result = cr.dictfetchall()
243 if netsvc.service_exist('report.'+r['report_name']):
245 if r['report_rml'] or r['report_rml_content_data']:
246 report_sxw('report.'+r['report_name'], r['model'],
247 opj('addons',r['report_rml'] or '/'), header=r['header'])
249 report_rml('report.'+r['report_name'], r['model'],
250 opj('addons',r['report_xml']),
251 r['report_xsl'] and opj('addons',r['report_xsl']))
254 # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: