e0b8f544ff
Fix draft inclusion in preview Enhance cache post content (avoid recomputing md5sum if present) Add generation duration time Add post only generation (for Dev) Remove Draft when it becomes Post Update blog Copyright Update TinyMCE plugins for inclusion Sort tags by name
279 lines
9.0 KiB
Python
Executable File
279 lines
9.0 KiB
Python
Executable File
# -*- 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 <http://www.gnu.org/licenses/>.
|
|
"""
|
|
import os
|
|
import hashlib
|
|
import gzip
|
|
import math
|
|
import codecs
|
|
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 = u''
|
|
|
|
def write(self, object):
|
|
object = object.replace('<', '<')
|
|
object = object.replace('>', '>')
|
|
object = object.replace('"', '"')
|
|
object = object.replace(''', "'")
|
|
object = object.replace('&', '&')
|
|
|
|
if not type(object) == unicode:
|
|
self.value = self.value + unicode(object, 'utf-8')
|
|
else:
|
|
self.value = self.value + object
|
|
return self.value
|
|
|
|
def reset(self):
|
|
self.value = u''
|
|
|
|
def getvalue(self):
|
|
return self.value
|
|
|
|
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
|
|
|
|
def addReport(self, string, color=''):
|
|
if string in self.report: return
|
|
if color:
|
|
self.report = self.report + '<span style="color:' + color + '">'
|
|
self.report = self.report + '<b>' + self.__class__.__name__ + '</b> : '
|
|
self.report = self.report + string
|
|
if color:
|
|
self.report = self.report + '</span>'
|
|
self.report = self.report + '<br/>\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().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 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):
|
|
div = dom.createElement('div')
|
|
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 <dyn:XXX> 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, basestring):
|
|
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, 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.appendChild(child.cloneNode(True))
|
|
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
|