[FIX] wall widget: fixed variable not refreshed
[odoo/odoo.git] / addons / report_webkit / webkit_report.py
1 # -*- coding: utf-8 -*-
2 ##############################################################################
3 #
4 # Copyright (c) 2010 Camptocamp SA (http://www.camptocamp.com)
5 # All Right Reserved
6 #
7 # Author : Nicolas Bessi (Camptocamp)
8 # Contributor(s) : Florent Xicluna (Wingo SA)
9 #
10 # WARNING: This program as such is intended to be used by professional
11 # programmers who take the whole responsability of assessing all potential
12 # consequences resulting from its eventual inadequacies and bugs
13 # End users who are looking for a ready-to-use solution with commercial
14 # garantees and support are strongly adviced to contract a Free Software
15 # Service Company
16 #
17 # This program is Free Software; you can redistribute it and/or
18 # modify it under the terms of the GNU General Public License
19 # as published by the Free Software Foundation; either version 2
20 # of the License, or (at your option) any later version.
21 #
22 # This program is distributed in the hope that it will be useful,
23 # but WITHOUT ANY WARRANTY; without even the implied warranty of
24 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
25 # GNU General Public License for more details.
26 #
27 # You should have received a copy of the GNU General Public License
28 # along with this program; if not, write to the Free Software
29 # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
30 #
31 ##############################################################################
32
33 import subprocess
34 import os
35 import sys
36 import report
37 import tempfile
38 import time
39 import logging
40
41 from mako.template import Template
42 from mako.lookup import TemplateLookup
43 from mako import exceptions
44
45 import netsvc
46 import pooler
47 from report_helper import WebKitHelper
48 from report.report_sxw import *
49 import addons
50 import tools
51 from tools.translate import _
52 from osv.osv import except_osv
53
54 logger = logging.getLogger('report_webkit')
55
56 def mako_template(text):
57     """Build a Mako template.
58
59     This template uses UTF-8 encoding
60     """
61     tmp_lookup  = TemplateLookup() #we need it in order to allow inclusion and inheritance
62     return Template(text, input_encoding='utf-8', output_encoding='utf-8', lookup=tmp_lookup)
63
64 class WebKitParser(report_sxw):
65     """Custom class that use webkit to render HTML reports
66        Code partially taken from report openoffice. Thanks guys :)
67     """
68     def __init__(self, name, table, rml=False, parser=False,
69         header=True, store=False):
70         self.parser_instance = False
71         self.localcontext = {}
72         report_sxw.__init__(self, name, table, rml, parser,
73             header, store)
74
75     def get_lib(self, cursor, uid):
76         """Return the lib wkhtml path"""
77         proxy = self.pool.get('ir.config_parameter')
78         webkit_path = proxy.get_param(cursor, uid, 'webkit_path')
79
80         if not webkit_path:
81             try:
82                 defpath = os.environ.get('PATH', os.defpath).split(os.pathsep)
83                 if hasattr(sys, 'frozen'):
84                     defpath.append(os.getcwd())
85                     if tools.config['root_path']:
86                         defpath.append(os.path.dirname(tools.config['root_path']))
87                 webkit_path = tools.which('wkhtmltopdf', path=os.pathsep.join(defpath))
88             except IOError:
89                 webkit_path = None
90
91         if webkit_path:
92             return webkit_path
93
94         raise except_osv(
95                          _('Wkhtmltopdf library path is not set'),
96                          _('Please install executable on your system' \
97                          ' (sudo apt-get install wkhtmltopdf) or download it from here:' \
98                          ' http://code.google.com/p/wkhtmltopdf/downloads/list and set the' \
99                          ' path in the ir.config_parameter with the webkit_path key.' \
100                          'Minimal version is 0.9.9')
101                         )
102
103     def generate_pdf(self, comm_path, report_xml, header, footer, html_list, webkit_header=False):
104         """Call webkit in order to generate pdf"""
105         if not webkit_header:
106             webkit_header = report_xml.webkit_header
107         tmp_dir = tempfile.gettempdir()
108         out_filename = tempfile.mktemp(suffix=".pdf", prefix="webkit.tmp.")
109         files = []
110         file_to_del = []
111         if comm_path:
112             command = [comm_path]
113         else:
114             command = ['wkhtmltopdf']
115
116         command.append('--quiet')
117         # default to UTF-8 encoding.  Use <meta charset="latin-1"> to override.
118         command.extend(['--encoding', 'utf-8'])
119         if header :
120             head_file = file( os.path.join(
121                                   tmp_dir,
122                                   str(time.time()) + '.head.html'
123                                  ),
124                                 'w'
125                             )
126             head_file.write(header)
127             head_file.close()
128             file_to_del.append(head_file.name)
129             command.extend(['--header-html', head_file.name])
130         if footer :
131             foot_file = file(  os.path.join(
132                                   tmp_dir,
133                                   str(time.time()) + '.foot.html'
134                                  ),
135                                 'w'
136                             )
137             foot_file.write(footer)
138             foot_file.close()
139             file_to_del.append(foot_file.name)
140             command.extend(['--footer-html', foot_file.name])
141
142         if webkit_header.margin_top :
143             command.extend(['--margin-top', str(webkit_header.margin_top).replace(',', '.')])
144         if webkit_header.margin_bottom :
145             command.extend(['--margin-bottom', str(webkit_header.margin_bottom).replace(',', '.')])
146         if webkit_header.margin_left :
147             command.extend(['--margin-left', str(webkit_header.margin_left).replace(',', '.')])
148         if webkit_header.margin_right :
149             command.extend(['--margin-right', str(webkit_header.margin_right).replace(',', '.')])
150         if webkit_header.orientation :
151             command.extend(['--orientation', str(webkit_header.orientation).replace(',', '.')])
152         if webkit_header.format :
153             command.extend(['--page-size', str(webkit_header.format).replace(',', '.')])
154         count = 0
155         for html in html_list :
156             html_file = file(os.path.join(tmp_dir, str(time.time()) + str(count) +'.body.html'), 'w')
157             count += 1
158             html_file.write(html)
159             html_file.close()
160             file_to_del.append(html_file.name)
161             command.append(html_file.name)
162         command.append(out_filename)
163         try:
164             status = subprocess.call(command, stderr=subprocess.PIPE) # ignore stderr
165             if status :
166                 raise except_osv(
167                                 _('Webkit raise an error' ),
168                                 status
169                             )
170         except Exception:
171             for f_to_del in file_to_del :
172                 os.unlink(f_to_del)
173
174         pdf = file(out_filename, 'rb').read()
175         for f_to_del in file_to_del :
176             os.unlink(f_to_del)
177
178         os.unlink(out_filename)
179         return pdf
180
181     def translate_call(self, src):
182         """Translate String."""
183         ir_translation = self.pool.get('ir.translation')
184         res = ir_translation._get_source(self.parser_instance.cr, self.parser_instance.uid,
185                                          None, 'report', self.parser_instance.localcontext.get('lang', 'en_US'), src)
186         if not res :
187             return src
188         return res
189
190     # override needed to keep the attachments storing procedure
191     def create_single_pdf(self, cursor, uid, ids, data, report_xml, context=None):
192         """generate the PDF"""
193
194         if context is None:
195             context={}
196         htmls = []
197         if report_xml.report_type != 'webkit':
198             return super(WebKitParser,self).create_single_pdf(cursor, uid, ids, data, report_xml, context=context)
199
200         self.parser_instance = self.parser(cursor,
201                                            uid,
202                                            self.name2,
203                                            context=context)
204
205         self.pool = pooler.get_pool(cursor.dbname)
206         objs = self.getObjects(cursor, uid, ids, context)
207         self.parser_instance.set_context(objs, data, ids, report_xml.report_type)
208
209         template =  False
210
211         if report_xml.report_file :
212             path = addons.get_module_resource(report_xml.report_file)
213             if os.path.exists(path) :
214                 template = file(path).read()
215         if not template and report_xml.report_webkit_data :
216             template =  report_xml.report_webkit_data
217         if not template :
218             raise except_osv(_('Error!'), _('Webkit Report template not found !'))
219         header = report_xml.webkit_header.html
220         footer = report_xml.webkit_header.footer_html
221         if not header and report_xml.header:
222             raise except_osv(
223                   _('No header defined for this Webkit report!'),
224                   _('Please set a header in company settings')
225               )
226         if not report_xml.header :
227             header = ''
228             default_head = addons.get_module_resource('report_webkit', 'default_header.html')
229             with open(default_head,'r') as f:
230                 header = f.read()
231         css = report_xml.webkit_header.css
232         if not css :
233             css = ''
234         user = self.pool.get('res.users').browse(cursor, uid, uid)
235         company= user.company_id
236
237         #default_filters=['unicode', 'entity'] can be used to set global filter
238         body_mako_tpl = mako_template(template)
239         helper = WebKitHelper(cursor, uid, report_xml.id, context)
240         if report_xml.precise_mode:
241             for obj in objs:
242                 self.parser_instance.localcontext['objects'] = [obj]
243                 try :
244                     html = body_mako_tpl.render(helper=helper,
245                                                 css=css,
246                                                 _=self.translate_call,
247                                                 **self.parser_instance.localcontext)
248                     htmls.append(html)
249                 except Exception, e:
250                     msg = exceptions.text_error_template().render()
251                     logger.error(msg)
252                     raise except_osv(_('Webkit render'), msg)
253         else:
254             try :
255                 html = body_mako_tpl.render(helper=helper,
256                                             css=css,
257                                             _=self.translate_call,
258                                             **self.parser_instance.localcontext)
259                 htmls.append(html)
260             except Exception, e:
261                 msg = exceptions.text_error_template().render()
262                 logger.error(msg)
263                 raise except_osv(_('Webkit render'), msg)
264         head_mako_tpl = mako_template(header)
265         try :
266             head = head_mako_tpl.render(helper=helper,
267                                         css=css,
268                                         _=self.translate_call,
269                                         _debug=False,
270                                         **self.parser_instance.localcontext)
271         except Exception, e:
272             raise except_osv(_('Webkit render'),
273                 exceptions.text_error_template().render())
274         foot = False
275         if footer :
276             foot_mako_tpl = mako_template(footer)
277             try :
278                 foot = foot_mako_tpl.render(helper=helper,
279                                             css=css,
280                                             _=self.translate_call,
281                                             **self.parser_instance.localcontext)
282             except:
283                 msg = exceptions.text_error_template().render()
284                 logger.error(msg)
285                 raise except_osv(_('Webkit render'), msg)
286         if report_xml.webkit_debug :
287             try :
288                 deb = head_mako_tpl.render(helper=helper,
289                                            css=css,
290                                            _debug=tools.ustr("\n".join(htmls)),
291                                            _=self.translate_call,
292                                            **self.parser_instance.localcontext)
293             except Exception, e:
294                 msg = exceptions.text_error_template().render()
295                 logger.error(msg)
296                 raise except_osv(_('Webkit render'), msg)
297             return (deb, 'html')
298         bin = self.get_lib(cursor, uid)
299         pdf = self.generate_pdf(bin, report_xml, head, foot, htmls)
300         return (pdf, 'pdf')
301
302
303     def create(self, cursor, uid, ids, data, context=None):
304         """We override the create function in order to handle generator
305            Code taken from report openoffice. Thanks guys :) """
306         pool = pooler.get_pool(cursor.dbname)
307         ir_obj = pool.get('ir.actions.report.xml')
308         report_xml_ids = ir_obj.search(cursor, uid,
309                 [('report_name', '=', self.name[7:])], context=context)
310         if report_xml_ids:
311
312             report_xml = ir_obj.browse(cursor,
313                                        uid,
314                                        report_xml_ids[0],
315                                        context=context)
316             report_xml.report_rml = None
317             report_xml.report_rml_content = None
318             report_xml.report_sxw_content_data = None
319             report_rml.report_sxw_content = None
320             report_rml.report_sxw = None
321         else:
322             return super(WebKitParser, self).create(cursor, uid, ids, data, context)
323         if report_xml.report_type != 'webkit' :
324             return super(WebKitParser, self).create(cursor, uid, ids, data, context)
325         result = self.create_source_pdf(cursor, uid, ids, data, report_xml, context)
326         if not result:
327             return (False,False)
328         return result
329
330 # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: