2006-12-07 13:41:40 +00:00
|
|
|
##############################################################################
|
|
|
|
#
|
2008-06-16 11:00:21 +00:00
|
|
|
# Copyright (c) 2004-2008 TINY SPRL. (http://tiny.be) All Rights Reserved.
|
2006-12-07 13:41:40 +00:00
|
|
|
#
|
2008-06-16 06:44:24 +00:00
|
|
|
# $Id$
|
2006-12-07 13:41:40 +00:00
|
|
|
#
|
|
|
|
# WARNING: This program as such is intended to be used by professional
|
|
|
|
# programmers who take the whole responsability of assessing all potential
|
|
|
|
# consequences resulting from its eventual inadequacies and bugs
|
|
|
|
# End users who are looking for a ready-to-use solution with commercial
|
|
|
|
# garantees and support are strongly adviced to contract a Free Software
|
|
|
|
# Service Company
|
|
|
|
#
|
|
|
|
# This program is Free Software; you can redistribute it and/or
|
|
|
|
# modify it under the terms of the GNU General Public License
|
|
|
|
# as published by the Free Software Foundation; either version 2
|
|
|
|
# of the License, or (at your option) any later version.
|
|
|
|
#
|
|
|
|
# This program is distributed in the hope that it will be useful,
|
|
|
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
# GNU General Public License for more details.
|
|
|
|
#
|
|
|
|
# You should have received a copy of the GNU General Public License
|
|
|
|
# along with this program; if not, write to the Free Software
|
|
|
|
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
|
|
|
#
|
|
|
|
##############################################################################
|
|
|
|
|
|
|
|
import os,re
|
|
|
|
|
|
|
|
#Ged> Why do we use libxml2 here instead of xml.dom like in other places of the code?
|
|
|
|
import libxml2
|
|
|
|
import libxslt
|
|
|
|
|
|
|
|
import netsvc
|
|
|
|
import pooler
|
|
|
|
|
|
|
|
import tools
|
|
|
|
import print_xml
|
|
|
|
import render
|
2008-01-16 15:57:02 +00:00
|
|
|
import urllib
|
2006-12-07 13:41:40 +00:00
|
|
|
|
|
|
|
#
|
|
|
|
# encode a value to a string in utf8 and converts XML entities
|
|
|
|
#
|
|
|
|
def toxml(val):
|
|
|
|
if isinstance(val, str):
|
|
|
|
str_utf8 = val
|
|
|
|
elif isinstance(val, unicode):
|
|
|
|
str_utf8 = val.encode('utf-8')
|
|
|
|
else:
|
|
|
|
str_utf8 = str(val)
|
|
|
|
return str_utf8.replace('&', '&').replace('<','<').replace('>','>')
|
|
|
|
|
|
|
|
class report_int(netsvc.Service):
|
|
|
|
def __init__(self, name, audience='*'):
|
2007-06-07 12:00:30 +00:00
|
|
|
assert not netsvc.service_exist(name), 'The report "%s" already exist!'%name
|
2006-12-07 13:41:40 +00:00
|
|
|
super(report_int, self).__init__(name, audience)
|
|
|
|
if name[0:7]<>'report.':
|
2007-08-09 08:18:06 +00:00
|
|
|
raise Exception, 'ConceptionError, bad report name, should start with "report."'
|
2006-12-07 13:41:40 +00:00
|
|
|
self.name = name
|
|
|
|
self.id = 0
|
|
|
|
self.name2 = '.'.join(name.split('.')[1:])
|
|
|
|
self.joinGroup('report')
|
|
|
|
self.exportMethod(self.create)
|
|
|
|
|
2007-07-23 05:16:03 +00:00
|
|
|
def create(self, cr, uid, ids, datas, context=None):
|
2006-12-07 13:41:40 +00:00
|
|
|
return False
|
|
|
|
|
|
|
|
"""
|
|
|
|
Class to automatically build a document using the transformation process:
|
|
|
|
XML -> DATAS -> RML -> PDF
|
|
|
|
-> HTML
|
|
|
|
using a XSL:RML transformation
|
|
|
|
"""
|
|
|
|
class report_rml(report_int):
|
|
|
|
def __init__(self, name, table, tmpl, xsl):
|
|
|
|
super(report_rml, self).__init__(name)
|
|
|
|
self.table = table
|
|
|
|
self.tmpl = tmpl
|
|
|
|
self.xsl = xsl
|
|
|
|
self.bin_datas = {}
|
2007-12-17 07:58:52 +00:00
|
|
|
self.generators = {
|
|
|
|
'pdf': self.create_pdf,
|
|
|
|
'html': self.create_html,
|
|
|
|
'raw': self.create_raw,
|
|
|
|
'sxw': self.create_sxw,
|
|
|
|
}
|
2006-12-07 13:41:40 +00:00
|
|
|
|
|
|
|
def create(self, cr, uid, ids, datas, context):
|
|
|
|
xml = self.create_xml(cr, uid, ids, datas, context)
|
|
|
|
# file('/tmp/terp.xml','wb+').write(xml)
|
|
|
|
if datas.get('report_type', 'pdf') == 'raw':
|
|
|
|
return xml
|
|
|
|
rml = self.create_rml(cr, xml, uid, context)
|
|
|
|
# file('/tmp/terp.rml','wb+').write(rml)
|
|
|
|
report_type = datas.get('report_type', 'pdf')
|
|
|
|
create_doc = self.generators[report_type]
|
|
|
|
pdf = create_doc(rml)
|
|
|
|
return (pdf, report_type)
|
|
|
|
|
2007-07-23 05:16:03 +00:00
|
|
|
def create_xml(self, cr, uid, ids, datas, context=None):
|
|
|
|
if not context:
|
|
|
|
context={}
|
2006-12-07 13:41:40 +00:00
|
|
|
doc = print_xml.document(cr, uid, datas, {})
|
2008-04-21 13:04:49 +00:00
|
|
|
self.bin_datas.update( doc.bin_datas or {})
|
2006-12-07 13:41:40 +00:00
|
|
|
doc.parse(self.tmpl, ids, self.table, context)
|
|
|
|
xml = doc.xml_get()
|
|
|
|
doc.close()
|
|
|
|
return self.post_process_xml_data(cr, uid, xml, context)
|
|
|
|
|
2007-07-23 05:16:03 +00:00
|
|
|
def post_process_xml_data(self, cr, uid, xml, context=None):
|
|
|
|
if not context:
|
|
|
|
context={}
|
2006-12-07 13:41:40 +00:00
|
|
|
# find the position of the 3rd tag
|
|
|
|
# (skip the <?xml ...?> and the "root" tag)
|
|
|
|
iter = re.finditer('<[^>]*>', xml)
|
|
|
|
i = iter.next()
|
|
|
|
i = iter.next()
|
|
|
|
pos_xml = i.end()
|
|
|
|
|
|
|
|
doc = print_xml.document(cr, uid, {}, {})
|
|
|
|
tmpl_path = os.path.join(tools.config['root_path'], 'addons/custom/corporate_defaults.xml')
|
|
|
|
doc.parse(tmpl_path, [uid], 'res.users', context)
|
|
|
|
corporate_header = doc.xml_get()
|
|
|
|
doc.close()
|
|
|
|
|
|
|
|
# find the position of the tag after the <?xml ...?> tag
|
|
|
|
iter = re.finditer('<[^>]*>', corporate_header)
|
|
|
|
i = iter.next()
|
|
|
|
pos_header = i.end()
|
|
|
|
|
|
|
|
return xml[:pos_xml] + corporate_header[pos_header:] + xml[pos_xml:]
|
|
|
|
|
|
|
|
#
|
|
|
|
# TODO: The translation doesn't work for "<tag t="1">textext<tag> tex</tag>text</tag>"
|
|
|
|
#
|
2007-07-23 05:16:03 +00:00
|
|
|
def create_rml(self, cr, xml, uid, context=None):
|
|
|
|
if not context:
|
|
|
|
context={}
|
2006-12-07 13:41:40 +00:00
|
|
|
service = netsvc.LocalService("object_proxy")
|
|
|
|
|
|
|
|
# In some case we might not use xsl ...
|
|
|
|
if not self.xsl:
|
|
|
|
return xml
|
|
|
|
|
|
|
|
# load XSL (parse it to the XML level)
|
2007-10-24 13:29:30 +00:00
|
|
|
styledoc = libxml2.parseDoc(tools.file_open(
|
|
|
|
os.path.join(tools.config['root_path'], self.xsl)).read())
|
2007-10-26 10:06:20 +00:00
|
|
|
xsl_path, tail = os.path.split(os.path.join(tools.config['root_path'],
|
|
|
|
self.xsl))
|
|
|
|
for child in styledoc.children:
|
|
|
|
if child.name == 'import':
|
|
|
|
if child.hasProp('href'):
|
|
|
|
file = child.prop('href')
|
2008-01-16 15:57:02 +00:00
|
|
|
child.setProp('href', urllib.quote(str(
|
|
|
|
os.path.normpath(os.path.join(xsl_path, file)))))
|
2007-10-24 13:29:30 +00:00
|
|
|
|
|
|
|
#TODO: get all the translation in one query. That means we have to:
|
|
|
|
# * build a list of items to translate,
|
2006-12-07 13:41:40 +00:00
|
|
|
# * issue the query to translate them,
|
|
|
|
# * (re)build/update the stylesheet with the translated items
|
|
|
|
|
|
|
|
# translate the XSL stylesheet
|
|
|
|
def look_down(child, lang):
|
|
|
|
while child is not None:
|
|
|
|
if (child.type == "element") and child.hasProp('t'):
|
|
|
|
#FIXME: use cursor
|
2007-10-26 10:06:20 +00:00
|
|
|
res = service.execute(cr.dbname, uid, 'ir.translation',
|
|
|
|
'_get_source', self.name2, 'xsl', lang, child.content)
|
2006-12-07 13:41:40 +00:00
|
|
|
if res:
|
|
|
|
child.setContent(res)
|
|
|
|
look_down(child.children, lang)
|
|
|
|
child = child.next
|
|
|
|
|
|
|
|
if context.get('lang', False):
|
|
|
|
look_down(styledoc.children, context['lang'])
|
|
|
|
|
2007-10-26 10:06:20 +00:00
|
|
|
# parse XSL
|
|
|
|
style = libxslt.parseStylesheetDoc(styledoc)
|
|
|
|
# load XML (data)
|
|
|
|
doc = libxml2.parseMemory(xml,len(xml))
|
|
|
|
# create RML (apply XSL to XML data)
|
|
|
|
result = style.applyStylesheet(doc, None)
|
|
|
|
# save result to string
|
|
|
|
xml = style.saveResultToString(result)
|
2006-12-07 13:41:40 +00:00
|
|
|
|
|
|
|
style.freeStylesheet()
|
|
|
|
doc.freeDoc()
|
|
|
|
result.freeDoc()
|
|
|
|
return xml
|
|
|
|
|
2008-04-21 13:04:49 +00:00
|
|
|
def create_pdf(self, xml, logo=None):
|
|
|
|
if logo:
|
|
|
|
self.bin_datas['logo'] = logo
|
|
|
|
else:
|
|
|
|
if 'logo' in self.bin_datas:
|
|
|
|
del self.bin_datas['logo']
|
2007-12-28 09:48:56 +00:00
|
|
|
obj = render.rml(xml, self.bin_datas, tools.config['root_path'])
|
2006-12-07 13:41:40 +00:00
|
|
|
obj.render()
|
|
|
|
return obj.get()
|
|
|
|
|
2008-04-21 13:04:49 +00:00
|
|
|
def create_html(self, xml, logo=None):
|
2006-12-07 13:41:40 +00:00
|
|
|
obj = render.rml2html(xml, self.bin_datas)
|
|
|
|
obj.render()
|
|
|
|
return obj.get()
|
|
|
|
|
2008-04-21 13:04:49 +00:00
|
|
|
def create_raw(self, xml, logo=None):
|
2006-12-07 13:41:40 +00:00
|
|
|
return xml
|
|
|
|
|
2008-04-21 13:04:49 +00:00
|
|
|
def create_sxw(self, path, logo=None):
|
2007-12-17 07:58:52 +00:00
|
|
|
return path
|
|
|
|
|
2006-12-07 13:41:40 +00:00
|
|
|
from report_sxw import report_sxw
|
|
|
|
|
|
|
|
def register_all(db):
|
|
|
|
opj = os.path.join
|
|
|
|
cr = db.cursor()
|
|
|
|
cr.execute("SELECT * FROM ir_act_report_xml WHERE auto ORDER BY id")
|
|
|
|
result = cr.dictfetchall()
|
|
|
|
cr.close()
|
|
|
|
for r in result:
|
2007-06-07 12:00:30 +00:00
|
|
|
if netsvc.service_exist('report.'+r['report_name']):
|
|
|
|
continue
|
2007-09-12 13:21:01 +00:00
|
|
|
if r['report_rml'] or r['report_rml_content_data']:
|
|
|
|
report_sxw('report.'+r['report_name'], r['model'],
|
|
|
|
opj('addons',r['report_rml'] or '/'), header=r['header'])
|
2006-12-07 13:41:40 +00:00
|
|
|
if r['report_xsl']:
|
2007-09-12 13:21:01 +00:00
|
|
|
report_rml('report.'+r['report_name'], r['model'],
|
|
|
|
opj('addons',r['report_xml']),
|
|
|
|
r['report_xsl'] and opj('addons',r['report_xsl']))
|
2006-12-07 13:41:40 +00:00
|
|
|
|