[IMP] Multiple improvements: eval_param is set on the controller, the subprocess to get the version of wkhtmltopdf is only open at OpenERP start, better exceptions handling (try to avoid exceptions shallowing) and the rpc call from the webclient to know the version of wkhtmltopdf is only done once
bzr revid: sle@openerp.com-20140321164716-uksuu6hsjj7q3698
This commit is contained in:
parent
5ac5805da0
commit
8581b1847f
|
@ -35,11 +35,13 @@ class ReportController(Controller):
|
|||
@route('/report/<reportname>/<docids>', type='http', auth='user', website=True, multilang=True)
|
||||
def report_html(self, reportname, docids):
|
||||
cr, uid, context = request.cr, request.uid, request.context
|
||||
docids = self._eval_params(docids)
|
||||
return request.registry['report'].get_html(cr, uid, docids, reportname, context=context)
|
||||
|
||||
@route('/report/pdf/report/<reportname>/<docids>', type='http', auth="user", website=True)
|
||||
def report_pdf(self, reportname, docids):
|
||||
cr, uid, context = request.cr, request.uid, request.context
|
||||
docids = self._eval_params(docids)
|
||||
pdf = request.registry['report'].get_pdf(cr, uid, docids, reportname, context=context)
|
||||
pdfhttpheaders = [('Content-Type', 'application/pdf'), ('Content-Length', len(pdf))]
|
||||
return request.make_response(pdf, headers=pdfhttpheaders)
|
||||
|
@ -51,14 +53,14 @@ class ReportController(Controller):
|
|||
def report_html_particular(self, reportname, **data):
|
||||
cr, uid, context = request.cr, request.uid, request.context
|
||||
report_obj = request.registry['report']
|
||||
data = report_obj.eval_params(data) # Sanitizing
|
||||
data = self._eval_params(data) # Sanitizing
|
||||
return report_obj.get_html(cr, uid, [], reportname, data=data, context=context)
|
||||
|
||||
@route('/report/pdf/report/<reportname>', type='http', auth='user', website=True, multilang=True)
|
||||
def report_pdf_particular(self, reportname, **data):
|
||||
cr, uid, context = request.cr, request.uid, request.context
|
||||
report_obj = request.registry['report']
|
||||
data = report_obj.eval_params(data) # Sanitizing
|
||||
data = self._eval_params(data) # Sanitizing
|
||||
pdf = report_obj.get_pdf(cr, uid, [], reportname, data=data, context=context)
|
||||
pdfhttpheaders = [('Content-Type', 'application/pdf'), ('Content-Length', len(pdf))]
|
||||
return request.make_response(pdf, headers=pdfhttpheaders)
|
||||
|
@ -121,4 +123,29 @@ class ReportController(Controller):
|
|||
|
||||
@route(['/report/check_wkhtmltopdf'], type='json', auth="user")
|
||||
def check_wkhtmltopdf(self):
|
||||
return request.registry['report'].check_wkhtmltopdf()
|
||||
return request.registry['report']._check_wkhtmltopdf()
|
||||
|
||||
def _eval_params(self, param):
|
||||
"""Parse a dict generated by the webclient (javascript) into a python dict.
|
||||
"""
|
||||
if isinstance(param, dict):
|
||||
for key, value in param.iteritems():
|
||||
if value.lower() == 'false':
|
||||
param[key] = False
|
||||
elif value.lower() == 'true':
|
||||
param[key] = True
|
||||
elif ',' in value:
|
||||
param[key] = [int(i) for i in value.split(',')]
|
||||
else:
|
||||
try:
|
||||
param[key] = int(value)
|
||||
except (ValueError, TypeError):
|
||||
pass
|
||||
else:
|
||||
if isinstance(param, (str, unicode)):
|
||||
param = [int(i) for i in param.split(',')]
|
||||
if isinstance(param, list):
|
||||
param = list(set(param))
|
||||
if isinstance(param, int):
|
||||
param = [param]
|
||||
return param
|
||||
|
|
|
@ -35,6 +35,7 @@ import lxml.html
|
|||
import cStringIO
|
||||
import subprocess
|
||||
from datetime import datetime
|
||||
from functools import partial
|
||||
from distutils.version import LooseVersion
|
||||
try:
|
||||
from pyPdf import PdfFileWriter, PdfFileReader
|
||||
|
@ -45,6 +46,24 @@ except ImportError:
|
|||
_logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
"""Check the presence of wkhtmltopdf and return its version."""
|
||||
wkhtmltopdf_state = 'install'
|
||||
try:
|
||||
process = subprocess.Popen(
|
||||
['wkhtmltopdf', '--version'], stdout=subprocess.PIPE, stderr=subprocess.PIPE
|
||||
)
|
||||
except OSError:
|
||||
_logger.error('You need wkhtmltopdf to print a pdf version of the reports.')
|
||||
else:
|
||||
out, err = process.communicate()
|
||||
version = out.splitlines()[1].strip()
|
||||
version = version.split(' ')[1]
|
||||
if LooseVersion(version) < LooseVersion('0.12.0'):
|
||||
_logger.warning('Upgrade wkhtmltopdf to (at least) 0.12.0')
|
||||
wkhtmltopdf_state = 'upgrade'
|
||||
wkhtmltopdf_state = 'ok'
|
||||
|
||||
|
||||
class Report(osv.Model):
|
||||
_name = "report"
|
||||
_description = "Report"
|
||||
|
@ -55,7 +74,7 @@ class Report(osv.Model):
|
|||
# Extension of ir_ui_view.render with arguments frequently used in reports
|
||||
#--------------------------------------------------------------------------
|
||||
|
||||
def get_digits(self, cr, uid, obj=None, f=None, dp=None):
|
||||
def _get_digits(self, cr, uid, obj=None, f=None, dp=None):
|
||||
d = DEFAULT_DIGITS = 2
|
||||
if dp:
|
||||
decimal_precision_obj = self.pool['decimal.precision']
|
||||
|
@ -101,9 +120,9 @@ class Report(osv.Model):
|
|||
|
||||
if digits is None:
|
||||
if dp:
|
||||
digits = self.get_digits(cr, uid, dp=dp)
|
||||
digits = self._get_digits(cr, uid, dp=dp)
|
||||
else:
|
||||
digits = self.get_digits(cr, uid, value)
|
||||
digits = self._get_digits(cr, uid, value)
|
||||
|
||||
if isinstance(value, (str, unicode)) and not value:
|
||||
return ''
|
||||
|
@ -192,8 +211,8 @@ class Report(osv.Model):
|
|||
|
||||
values.update({
|
||||
'time': time,
|
||||
'formatLang': lambda *args, **kwargs: self.formatLang(*args, cr=cr, uid=uid, **kwargs),
|
||||
'get_digits': self.get_digits,
|
||||
'formatLang': partial(self.formatLang, cr=cr, uid=uid),
|
||||
'get_digits': self._get_digits,
|
||||
'render_doc': render_doc,
|
||||
'editable': True, # Will active inherit_branding
|
||||
'res_company': self.pool['res.users'].browse(cr, uid, uid).company_id
|
||||
|
@ -208,35 +227,22 @@ class Report(osv.Model):
|
|||
def get_html(self, cr, uid, ids, report_name, data=None, context=None):
|
||||
"""This method generates and returns html version of a report.
|
||||
"""
|
||||
if context is None:
|
||||
context = {}
|
||||
|
||||
if isinstance(ids, (str, unicode)):
|
||||
ids = [int(i) for i in ids.split(',')]
|
||||
if isinstance(ids, list):
|
||||
ids = list(set(ids))
|
||||
if isinstance(ids, int):
|
||||
ids = [ids]
|
||||
|
||||
# If the report is using a custom model to render its html, we must use it.
|
||||
# Otherwise, fallback on the generic html rendering.
|
||||
try:
|
||||
report_model_name = 'report.%s' % report_name
|
||||
particularreport_obj = self.pool[report_model_name]
|
||||
return particularreport_obj.render_html(cr, uid, ids, data=data, context=context)
|
||||
except:
|
||||
pass
|
||||
|
||||
report = self._get_report_from_name(cr, uid, report_name)
|
||||
report_obj = self.pool[report.model]
|
||||
docs = report_obj.browse(cr, uid, ids, context=context)
|
||||
|
||||
docargs = {
|
||||
'doc_ids': ids,
|
||||
'doc_model': report.model,
|
||||
'docs': docs,
|
||||
}
|
||||
return self.render(cr, uid, [], report.report_name, docargs, context=context)
|
||||
return particularreport_obj.render_html(cr, uid, ids, data={'form': data}, context=context)
|
||||
except KeyError:
|
||||
report = self._get_report_from_name(cr, uid, report_name)
|
||||
report_obj = self.pool[report.model]
|
||||
docs = report_obj.browse(cr, uid, ids, context=context)
|
||||
docargs = {
|
||||
'doc_ids': ids,
|
||||
'doc_model': report.model,
|
||||
'docs': docs,
|
||||
}
|
||||
return self.render(cr, uid, [], report.report_name, docargs, context=context)
|
||||
|
||||
def get_pdf(self, cr, uid, ids, report_name, html=None, data=None, context=None):
|
||||
"""This method generates and returns pdf version of a report.
|
||||
|
@ -244,13 +250,6 @@ class Report(osv.Model):
|
|||
if context is None:
|
||||
context = {}
|
||||
|
||||
if isinstance(ids, (str, unicode)):
|
||||
ids = [int(i) for i in ids.split(',')]
|
||||
if isinstance(ids, list):
|
||||
ids = list(set(ids))
|
||||
if isinstance(ids, int):
|
||||
ids = [ids]
|
||||
|
||||
if html is None:
|
||||
html = self.get_html(cr, uid, ids, report_name, data=data, context=context)
|
||||
|
||||
|
@ -260,7 +259,7 @@ class Report(osv.Model):
|
|||
report = self._get_report_from_name(cr, uid, report_name)
|
||||
|
||||
# Check attachment_use field. If set to true and an existing pdf is already saved, load
|
||||
# this one now. If not, mark save it.
|
||||
# this one now. Else, mark save it.
|
||||
save_in_attachment = {}
|
||||
|
||||
if report.attachment_use is True:
|
||||
|
@ -307,7 +306,7 @@ class Report(osv.Model):
|
|||
base_url = self.pool['ir.config_parameter'].get_param(cr, uid, 'web.base.url')
|
||||
|
||||
minimalhtml = """
|
||||
<base href="{3}">
|
||||
<base href="{base_url}">
|
||||
<!DOCTYPE html>
|
||||
<html style="height: 0;">
|
||||
<head>
|
||||
|
@ -315,11 +314,11 @@ class Report(osv.Model):
|
|||
<link href="/web/static/lib/bootstrap/css/bootstrap.css" rel="stylesheet"/>
|
||||
<link href="/website/static/src/css/website.css" rel="stylesheet"/>
|
||||
<link href="/web/static/lib/fontawesome/css/font-awesome.css" rel="stylesheet"/>
|
||||
<style type='text/css'>{0}</style>
|
||||
{1}
|
||||
<style type='text/css'>{css}</style>
|
||||
{subst}
|
||||
</head>
|
||||
<body class="container" onload='subst()'>
|
||||
{2}
|
||||
{body}
|
||||
</body>
|
||||
</html>"""
|
||||
|
||||
|
@ -333,12 +332,12 @@ class Report(osv.Model):
|
|||
|
||||
for node in root.xpath("//div[@class='header']"):
|
||||
body = lxml.html.tostring(node)
|
||||
header = minimalhtml.format(css, subst, body, base_url)
|
||||
header = minimalhtml.format(css=css, subst=subst, body=body, base_url=base_url)
|
||||
headerhtml.append(header)
|
||||
|
||||
for node in root.xpath("//div[@class='footer']"):
|
||||
body = lxml.html.tostring(node)
|
||||
footer = minimalhtml.format(css, subst, body, base_url)
|
||||
footer = minimalhtml.format(css=css, subst=subst, body=body, base_url=base_url)
|
||||
footerhtml.append(footer)
|
||||
|
||||
for node in root.xpath("//div[@class='page']"):
|
||||
|
@ -346,16 +345,16 @@ class Report(osv.Model):
|
|||
# must set a relation between report ids and report's content. We use the QWeb
|
||||
# branding in order to do so: searching after a node having a data-oe-model
|
||||
# attribute with the value of the current report model and read its oe-id attribute
|
||||
oemodelnode = node.find(".//*[@data-oe-model='" + report.model + "']")
|
||||
oemodelnode = node.find(".//*[@data-oe-model='%s']" % report.model)
|
||||
if oemodelnode is not None:
|
||||
reportid = oemodelnode.get('data-oe-id', False)
|
||||
if reportid is not False:
|
||||
reportid = oemodelnode.get('data-oe-id')
|
||||
if reportid:
|
||||
reportid = int(reportid)
|
||||
else:
|
||||
reportid = False
|
||||
|
||||
body = lxml.html.tostring(node)
|
||||
reportcontent = minimalhtml.format(css, '', body, base_url)
|
||||
reportcontent = minimalhtml.format(css=css, subst='', body=body, base_url=base_url)
|
||||
contenthtml.append(tuple([reportid, reportcontent]))
|
||||
|
||||
except lxml.etree.XMLSyntaxError:
|
||||
|
@ -414,27 +413,8 @@ class Report(osv.Model):
|
|||
# Report generation helpers
|
||||
#--------------------------------------------------------------------------
|
||||
|
||||
def check_wkhtmltopdf(self):
|
||||
"""Check the presence of wkhtmltopdf and return its version. If wkhtmltopdf
|
||||
cannot be found, return False.
|
||||
"""
|
||||
try:
|
||||
process = subprocess.Popen(['wkhtmltopdf', '--version'], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
||||
out, err = process.communicate()
|
||||
if err:
|
||||
raise
|
||||
|
||||
version = out.splitlines()[1].strip()
|
||||
version = version.split(' ')[1]
|
||||
|
||||
if LooseVersion(version) < LooseVersion('0.12.0'):
|
||||
_logger.warning('Upgrade WKHTMLTOPDF to (at least) 0.12.0')
|
||||
return 'upgrade'
|
||||
|
||||
return True
|
||||
except:
|
||||
_logger.error('You need WKHTMLTOPDF to print a pdf version of this report.')
|
||||
return False
|
||||
def _check_wkhtmltopdf(self):
|
||||
return wkhtmltopdf_state
|
||||
|
||||
def _generate_wkhtml_pdf(self, cr, uid, headers, footers, bodies, landscape, paperformat, spec_paperformat_args=None, save_in_attachment=None):
|
||||
"""Execute wkhtmltopdf as a subprocess in order to convert html given in input into a pdf
|
||||
|
@ -450,14 +430,14 @@ class Report(osv.Model):
|
|||
:returns: Content of the pdf as a string
|
||||
"""
|
||||
command = ['wkhtmltopdf']
|
||||
command_args = []
|
||||
tmp_dir = tempfile.gettempdir()
|
||||
|
||||
command_args = []
|
||||
# Passing the cookie to wkhtmltopdf in order to resolve URL.
|
||||
try:
|
||||
from openerp.addons.web.http import request
|
||||
command_args.extend(['--cookie', 'session_id', request.httprequest.cookies['session_id']])
|
||||
except:
|
||||
command_args.extend(['--cookie', 'session_id', request.session.sid])
|
||||
except AttributeError:
|
||||
pass
|
||||
|
||||
# Display arguments
|
||||
|
@ -510,8 +490,8 @@ class Report(osv.Model):
|
|||
content_file.flush()
|
||||
|
||||
try:
|
||||
# If the server is running with only one worker, increase it to two to be able
|
||||
# to serve the http request from wkhtmltopdf.
|
||||
# If the server is running with only one worker, ask to create a secund to be able
|
||||
# to serve the http request of wkhtmltopdf subprocess.
|
||||
if config['workers'] == 1:
|
||||
ppid = psutil.Process(os.getpid()).ppid
|
||||
os.kill(ppid, signal.SIGTTIN)
|
||||
|
@ -629,26 +609,3 @@ class Report(osv.Model):
|
|||
content = merged.read()
|
||||
merged.close()
|
||||
return content
|
||||
|
||||
def eval_params(self, dict_param):
|
||||
"""Parse a dict generated by the webclient (javascript) into a python dict.
|
||||
"""
|
||||
for key, value in dict_param.iteritems():
|
||||
if value.lower() == 'false':
|
||||
dict_param[key] = False
|
||||
elif value.lower() == 'true':
|
||||
dict_param[key] = True
|
||||
elif ',' in value:
|
||||
dict_param[key] = [int(i) for i in value.split(',')]
|
||||
elif '%2C' in value:
|
||||
dict_param[key] = [int(i) for i in value.split('%2C')]
|
||||
else:
|
||||
try:
|
||||
i = int(value)
|
||||
dict_param[key] = i
|
||||
except (ValueError, TypeError):
|
||||
pass
|
||||
|
||||
data = {}
|
||||
data['form'] = dict_param
|
||||
return data
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
openerp.report = function(instance) {
|
||||
var wkhtmltopdf_state;
|
||||
|
||||
instance.web.ActionManager = instance.web.ActionManager.extend({
|
||||
ir_actions_report_xml: function(action, options) {
|
||||
|
@ -12,7 +13,7 @@ openerp.report = function(instance) {
|
|||
// QWeb reports
|
||||
if ('report_type' in action && (action.report_type == 'qweb-html' || action.report_type == 'qweb-pdf' || action.report_type == 'controller')) {
|
||||
|
||||
var report_url = ''
|
||||
var report_url = '';
|
||||
switch (action.report_type) {
|
||||
case 'qweb-html':
|
||||
report_url = '/report/' + action.report_name;
|
||||
|
@ -55,20 +56,19 @@ openerp.report = function(instance) {
|
|||
} else {
|
||||
// Trigger the download of the pdf report
|
||||
var c = openerp.webclient.crashmanager;
|
||||
var response = new Array()
|
||||
response[0] = report_url
|
||||
response[1] = action.report_type
|
||||
var response = new Array();
|
||||
response[0] = report_url;
|
||||
response[1] = action.report_type;
|
||||
|
||||
openerp.session.rpc('/report/check_wkhtmltopdf').then(function (presence) {
|
||||
(wkhtmltopdf_state = wkhtmltopdf_state || openerp.session.rpc('/report/check_wkhtmltopdf')).then(function (presence) {
|
||||
// Fallback of qweb-pdf if wkhtmltopdf is not installed
|
||||
if (!presence && action.report_type == 'qweb-pdf') {
|
||||
if (presence == 'install' && action.report_type == 'qweb-pdf') {
|
||||
self.do_notify(_t('Report'), _t('Unable to find Wkhtmltopdf on this \
|
||||
system. The report will be shown in html.<br><br><a href="http://wkhtmltopdf.org/" target="_blank">\
|
||||
wkhtmltopdf.org</a>'), true);
|
||||
window.open(report_url.substring(12), '_blank', 'height=768,width=1024');
|
||||
instance.web.unblockUI();
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
if (presence == 'upgrade') {
|
||||
self.do_notify(_t('Report'), _t('You should upgrade your version of\
|
||||
Wkhtmltopdf to at least 0.12.0 in order to get a correct display of headers and footers as well as\
|
||||
|
|
Loading…
Reference in New Issue