############################################################################## # # Copyright (c) 2004-2006 TINY SPRL. (http://tiny.be) All Rights Reserved. # # 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 xml.dom.minidom import os, time import ir, netsvc import osv from interface import report_rml import re import tools import pooler import copy parents = { 'tr':1, 'li':1, 'story': 0, 'section': 0 } # # Context: {'node': node.dom} # class browse_record_list(list): def __init__(self, lst, context): super(browse_record_list, self).__init__(lst) self.parents = copy.copy(parents) self.context = context def __getattr__(self, name): res = browse_record_list([getattr(x,name) for x in self], self.context) return res def __str__(self): return "browse_record_list("+str(len(self))+")" def repeatIn(self, name): print 'Decrecated ! Use repeatIn(object_list, \'variable\')' node = self.context['_node'] node.data = '' while True: if not node.parentNode: break node = node.parentNode if node.nodeType == node.ELEMENT_NODE and node.localName in parents: break parent_node = node if not len(self): return None nodes = [(0,node)] for i in range(1,len(self)): newnode = parent_node.cloneNode(1) n = parent_node.parentNode n.insertBefore(newnode, parent_node) nodes.append((i,newnode)) for i,node in nodes: self.context[name] = self[i] self.context['_self']._parse_node(node) return None class rml_parse(object): def __init__(self, cr, uid, name, context={}): self.cr = cr self.uid = uid self.pool = pooler.get_pool(cr.dbname) user = self.pool.get('res.users').browse(cr, uid, uid) self.localcontext = { 'user': user, 'company': user.company_id, 'repeatIn': self.repeatIn, 'setLang': self.setLang, 'setTag': self.setTag, 'removeParentNode': self.removeParentNode, } self.localcontext.update(context) self.name = name self._regex = re.compile('\[\[(.+?)\]\]') self._transl_regex = re.compile('(\[\[.+?\]\])') self._node = None # self.already = {} def setTag(self, oldtag, newtag): node = self._find_parent(self._node, [oldtag]) if node: node.tagName = newtag return None def removeParentNode(self, tag): node = self._find_parent(self._node, [tag]) if node: parentNode = node.parentNode parentNode.removeChild(node) self._node = parentNode def setLang(self, lang): self.localcontext['lang'] = lang def repeatIn(self, lst, name, nodes_parent=False): self._node.data = '' node = self._find_parent(self._node, nodes_parent or parents) pp = node.parentNode ns = node.nextSibling pp.removeChild(node) self._node = pp if not len(lst): return None nodes = [] for i in range(len(lst)): newnode = node.cloneNode(1) if ns: pp.insertBefore(newnode, ns) else: pp.appendChild(newnode) nodes.append((i, newnode)) for i, node in nodes: self.node_context[node] = {name: lst[i]} return None def _eval(self, expr): try: res = eval(expr, self.localcontext) if res is False or res is None: res = '' except Exception,e: print e.args print e print dir(e) res = 'Error' return res def _find_parent(self, node, parents): while True: if not node.parentNode: return False node = node.parentNode if node.nodeType == node.ELEMENT_NODE and node.localName in parents: break return node def _parse_text(self, text, level=[]): res = self._regex.findall(text) todo = [] for key in res: newtext = self._eval(key) for i in range(len(level)): if isinstance(newtext, list): newtext = newtext[level[i]] if isinstance(newtext, list): todo.append((key, newtext)) else: if not isinstance(newtext, basestring): newtext = str(newtext) # if there are two [[]] blocks the same, it will replace both # but it's ok because it should evaluate to the same thing # anyway text = text.replace('[['+key+']]', newtext.decode('utf8')) # translate the text # the "split [[]] if not match [[]]" is not very nice, but I # don't see how I could do it better... # what I'd like to do is a re.sub(NOT pattern, func, string) # but I don't know how to do that... # translate the RML file if 'lang' in self.localcontext: lang = self.localcontext['lang'] if lang not in (False, 'en') and text and not text.isspace(): transl_obj = self.pool.get('ir.translation') piece_list = self._transl_regex.split(text) for pn in range(len(piece_list)): if not self._transl_regex.match(piece_list[pn]): source_string = piece_list[pn].replace('\n', ' ').strip() if len(source_string): translated_string = transl_obj._get_source(self.cr, self.uid, self.name, 'rml', lang, source_string) if translated_string: piece_list[pn] = piece_list[pn].replace(source_string, translated_string.decode('utf8')) text = ''.join(piece_list) self._node.data = text if len(todo): for key, newtext in todo: parent_node = self._find_parent(self._node, parents) assert parents.get(parent_node.localName, False), 'No parent node found !' nodes = [parent_node] for i in range(len(newtext) - 1): newnode = parent_node.cloneNode(1) if parents.get(parent_node.localName, False): n = parent_node.parentNode parent_node.parentNode.insertAfter(newnode, parent_node) nodes.append(newnode) return False return text def _parse_node(self): level = [] while True: if self._node.hasChildNodes(): self._node = self._node.firstChild elif self._node.nextSibling: self._node = self._node.nextSibling else: while self._node and not self._node.nextSibling: self._node = self._node.parentNode if not self._node: break self._node = self._node.nextSibling if self._node in self.node_context: self.localcontext.update(self.node_context[self._node]) if self._node.nodeType in (self._node.CDATA_SECTION_NODE, self._node.TEXT_NODE): # if self._node in self.already: # self.already[self._node] += 1 # print "second pass!", self.already[self._node], '---%s---' % self._node.data # else: # self.already[self._node] = 0 self._parse_text(self._node.data, level) return True def _find_node(self, node, localname): if node.localName==localname: return node for tag in node.childNodes: if tag.nodeType==tag.ELEMENT_NODE: found = self._find_node(tag, localname) if found: return found return False def _add_header(self, node): rml_head = tools.file_open('custom/corporate_rml_header.rml').read() head_dom = xml.dom.minidom.parseString(rml_head) for frame in head_dom.getElementsByTagName('frame'): frame.parentNode.removeChild(frame) node2 = head_dom.documentElement for tag in node2.childNodes: if tag.nodeType==tag.ELEMENT_NODE: found = self._find_node(node, tag.localName) rml_frames = found.getElementsByTagName('frame') if found: found.parentNode.replaceChild(tag, found) for frame in rml_frames: tag.appendChild(frame) return True def preprocess(self, objects, data, ids): self.localcontext['data'] = data self.localcontext['objects'] = objects self.datas = data self.ids = ids self.objects = objects def _parse(self, rml_dom, objects, data): self.node_context = {} self.dom = rml_dom self._node = self.dom.documentElement self._add_header(self._node) self._parse_node() res = self.dom.documentElement.toxml('utf-8') return res class report_sxw(report_rml): def __init__(self, name, table, rml, parser=rml_parse): report_rml.__init__(self, name, table, rml, '') self.name = name self.parser = parser def getObjects(self, cr, uid, ids, context): table_obj = pooler.get_pool(cr.dbname).get(self.table) return table_obj.browse(cr, uid, ids, list_class=browse_record_list, context=context) def create(self, cr, uid, ids, data, context={}): rml = file(os.path.join(tools.config['root_path'], self.tmpl)).read() rml_parser = self.parser(cr, uid, self.name2, context) objs = self.getObjects(cr, uid, ids, context) rml_parser.preprocess(objs, data, ids) rml_dom = xml.dom.minidom.parseString(rml) rml2 = rml_parser._parse(rml_dom, objs, data) f = file("/tmp/debug.rml", "w") f.write(rml2) f.close() report_type= data.get('report_type','pdf') create_doc = self.generators[report_type] pdf = create_doc(rml2) return (pdf, report_type)