# -*- coding: utf-8 -*- """ Copyright 2012-2014 Grégory Soutadé This file is part of Dynastie. Dynastie 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 3 of the License, or (at your option) any later version. Dynastie 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 Dynastie. If not, see . """ import os import hashlib import gzip import math import codecs import re from xml.dom import * from xml.dom.minidom import parse from xml.parsers.expat import * class StrictUTF8Writer(codecs.StreamWriter): '''A StreamWriter for utf8''' encode = codecs.utf_8_encode value = '' def __init__(self): self.value = '' def write(self, object): object = object.replace('<', '<') object = object.replace('>', '>') object = object.replace('"', '"') object = object.replace(''', "'") object = object.replace('&', '&') if type(object) == bytes: self.value = self.value + object.decode('utf-8') else: self.value = self.value + object return self.value def reset(self): self.value = '' def getvalue(self): return self.value.encode('utf-8') class DynastieGenerator: URI = "http://indefero.soutade.fr/p/dynastie" def __init__(self, request, hash_posts={}, hash_posts_content={}): self.report = '' self.somethingWrote = False self.request = request self.hash_posts = hash_posts self.hash_posts_content = hash_posts_content self.user = request and request.user or None self.iframe_re = re.compile(r'(') def addReport(self, string, color=''): if string in self.report: return if color: self.report = self.report + '' self.report = self.report + '' + self.__class__.__name__ + ' : ' self.report = self.report + string if color: self.report = self.report + '' self.report = self.report + '
\n' def addWarning(self, string): self.addReport(string, 'yellow') def addError(self, string): self.addReport(string, 'red') # Virtual def generate(self, blog, src, output): return def computeNbPages(self, nb_post, nb_post_per_page): res = math.ceil((nb_post*1.0)/(nb_post_per_page*1.0)) return int(res) def writeIfNotTheSame(self, filename, node): from dynastie.models import FileOutputCache writer = StrictUTF8Writer() node.writexml(writer) content = writer.getvalue().decode('utf-8') # iframe tag must be like , not optimized ', content).encode('utf-8') dst_md5 = hashlib.md5() dst_md5.update(content) cache_objs = FileOutputCache.objects.filter(name=filename) if cache_objs.count() == 0: cache_obj = None else: cache_obj = cache_objs[0] if cache_obj and not os.path.exists(filename): cache_obj.delete() cache_obj = None if cache_obj or os.path.exists(filename): if not cache_obj: src_md5 = hashlib.md5() f = open(filename,'rb') src_md5.update(f.read()) f.close() src_md5 = src_md5.hexdigest() else: src_md5 = cache_obj.hash if src_md5 == dst_md5.hexdigest(): if cache_obj is None: cache_obj = FileOutputCache(name=filename, hash=src_md5) cache_obj.save() filename = filename + '.gz' if not os.path.exists(filename): f = gzip.open(filename, 'wb') f.write(content) f.close() return if os.path.exists(filename): os.unlink(filename) if cache_obj is None: cache_obj = FileOutputCache(name=filename, hash=dst_md5.hexdigest()) else: cache_obj.hash = dst_md5.hexdigest() self.addReport('Write (and compress) ' + filename) f = open(filename,'wb') f.write(content) f.close() filename = filename + '.gz' #self.addReport('Compressing it ' + filename) f = gzip.open(filename, 'wb') f.write(content) f.close() cache_obj.save() self.somethingWrote = True def createLinkElem(self, dom, path, title): link_elem = dom.createElement('a') link_elem.setAttribute('href', path) text_elem = dom.createTextNode(title) link_elem.appendChild(text_elem) return link_elem def cloneSubtree(self, div, subtree): for node in subtree.childNodes: div.appendChild(node.cloneNode(True)) def createElement(self, dom, name='', content='', subtree=None, _type='div'): div = dom.createElement(_type) if name: div.setAttribute('class', name) if content: div.appendChild(dom.createTextNode(content)) if subtree: self.cloneSubtree(div, subtree) return div def createMeta(self, dom, name='', content=''): div = dom.createElement('meta') if name: div.setAttribute('name', name) if content: div.setAttribute('content', content) return div # Recursively transform elements with the ones in values def simpleTransform(self, values, dom, elem, root): for node in root.childNodes: if node.prefix == 'dyn': if node.localName in values: content = values[node.localName] if isinstance(content, str): new_elem = self.createElement(dom, node.localName, content) else: new_elem = self.createElement(dom, node.localName) new_elem.appendChild(content) else: new_elem = node.cloneNode(False) self.simpleTransform(values, dom, new_elem, node) else: new_elem = node.cloneNode(False) self.simpleTransform(values, dom, new_elem, node) elem.appendChild(new_elem) def replaceByText(self, dom, root, node, content): new_node = dom.createTextNode(content) root.replaceChild(new_node, node) def _parse(self, hooks, posts, dom, root): for node in root.childNodes: if node.prefix == 'dyn' and node.localName in hooks: node = hooks[node.localName](posts, dom, root, node) if node and node.hasChildNodes(): self._parse(hooks, posts, dom, node) def parse(self, src, hooks, posts, dom, root): bases = dom.getElementsByTagNameNS(self.URI, 'base') if not bases: self._parse(hooks, posts, dom, root) return root if len(bases) != 1: self.addError('More than one base defined') return root base = bases[0] if not base.hasAttribute('file'): self.addError('No \'file\' attribute defined') return root filename = base.getAttribute('file') if not os.path.exists(src + '/' + filename): self.addError('Base ' + filename + ' doesn\'t exists') return root target_blocks = base.getElementsByTagNameNS(self.URI, 'block') if not target_blocks: self.addError('No \'block\' defined in ' + src + '/' + filename) return root for target_block in target_blocks: if not target_block.hasAttribute('name'): self.addError('Every block must have a name in ' + src + '/' + filename) return root dom2 = root try: dom2 = parse(src + '/' + filename) except ExpatError as e: self.addError('Error parsing ' + src + '/' + filename) return root blocks = dom2.getElementsByTagNameNS(self.URI, 'block') block_found = False for block in blocks: if not block.hasAttribute('name'): self.addError('block has no attribute \'name\' in ' + src + '/' + filename) return root blockname = block.getAttribute('name') for target_block in target_blocks: if blockname != target_block.getAttribute('name'): continue for child in target_block.childNodes: block.parentNode.insertBefore(child.cloneNode(True), block) block_found = True break block.parentNode.removeChild(block) if not block_found: self.addError('Any block found in ' + src + '/' + filename) return root root = dom2.firstChild self.parse(src, hooks, posts, dom2, root) return root