Dynastie/dynastie/generators/post.py

302 lines
11 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 datetime
import os
import xml
import re
from xml.dom.minidom import parse, parseString
from dynastie.generators.generator import DynastieGenerator, StrictUTF8Writer
from dynastie.generators.index import Index
from django.db import models
from dynastie.tree import TreeNode
class Post(Index):
def createReplace(self, post, dom, root, replace_elem):
if not replace_elem.hasAttribute('div_name'):
self.addError('No attribute div_name for a replace tag')
return
div_element = replace_elem.cloneNode(True)
div_element.tagName = replace_elem.getAttribute('div_name')
div_element.removeAttribute('div_name')
for key,value in replace_elem.attributes.items():
if key == 'div_name': continue
value = value.replace('dyn:post_id', str(post.id))
if self.cur_comment is None:
value = value.replace('dyn:comment_index', '0')
value = value.replace('dyn:comment_id', '0')
else:
value = value.replace('dyn:comment_index', str(self.comment_index[self.cur_comment.id]))
value = value.replace('dyn:comment_id', str(self.cur_comment.id))
value = value.replace('dyn:blog_id', str(self.blog.id))
url = post.getPath()
full_url = post.blog.name + url
value = value.replace('dyn:post_url', url)
value = value.replace('dyn:post_full_url', full_url)
div_element.setAttribute(key, value)
root.replaceChild(div_element, replace_elem)
return div_element
def createComment(self, comment, dom, comment_elem, root):
values = {}
values['comment_index'] = str(self.comment_index[comment.id])
values['comment_author'] = comment.author
values['comment_date'] = comment.date.strftime('%d %B %Y %H:%m')
values['comment_content'] = comment.the_comment
self.simpleTransform(values, dom, comment_elem, root)
def _createComments(self, rootNode, post, dom, root_comment, root):
self.cur_comment = rootNode.value
comment_element = self.createElement(dom, 'comment')
self.createComment(self.cur_comment, dom, comment_element, root)
root_comment.appendChild(comment_element)
# Parse inner HTML
self._parse(self.hooks, post, dom, comment_element)
for commentNode in rootNode.childs:
self._createComments(commentNode, post, dom, comment_element, root)
def createComments(self, post, dom, post_elem, root):
from dynastie.models import Post, Blog, Comment
comments = Comment.objects.filter(post=post).order_by('date')
self.cur_comment = None
self.comment_index = {}
index = 1
rootNode = TreeNode('', '')
for comment in comments:
self.comment_index[comment.id] = index
index = index + 1
tnode = TreeNode(comment.id, comment)
if comment.parent is None:
rootNode.addChildNode(tnode)
else:
temp = rootNode.find(comment.parent.id)
if temp is None:
self.addWarning('Error with comments chain')
rootNode.addChildNode(tnode)
else:
temp.addChildNode(tnode)
initial_root_comment = root_comment = self.createElement(dom, 'comments')
for tnode in rootNode.childs:
self._createComments(tnode, post, dom, root_comment, root)
# Empty tag seems to crap rendering
if len(rootNode.childs) == 0:
post_elem.removeChild(root)
return None
else:
post_elem.replaceChild(root_comment, root)
return root_comment
def createMetas(self, post, dom, meta_elem, root):
name = root.getAttribute('name')
if name is None:
self.addError('Missing name attribute in dyn:meta')
return
new_elem = None
if name == 'keywords':
value = post.keywords.replace('"', '&ldquo;')
new_elem = self.createMeta(dom, name, value)
elif name == 'title':
value = post.title.replace('"', '&ldquo;')
new_elem = self.createMeta(dom, name, value)
elif name == 'description':
value = post.description.replace('"', '&ldquo;')
new_elem = self.createMeta(dom, name, value)
elif name == 'author':
try:
new_elem = self.createMeta(dom, name, post.author.first_name + ' ' + post.author.last_name)
except:
return None
elif name == 'lang':
meta_elem.removeChild(root)
# Add attribute lang in <html> tag
if post.language != None:
meta_elem.parentNode.setAttribute('lang', post.language.abbrev)
return None
if not new_elem is None:
root.parentNode.replaceChild(new_elem, root)
return new_elem
else:
self.addError('name attribute \'' + name + '\' unknown for dyn:meta' )
return None
def createPostTitle(self, post, dom, root, node):
value = post.title.replace('"', '&ldquo;')
self.replaceByText(dom, root, node, value)
return None
def _createPost(self, post, dom, root, node):
self.cur_post_obj = post
posts = [post]
post_elem = self.createElement(dom, 'post')
post_elem = self.createPost(posts, dom, post_elem, node)
# Post are appended by index. Remove template
root.replaceChild(post_elem, node)
title_nodes = dom.getElementsByTagName('title')
# Set title to be title's post
for node in title_nodes:
if node.hasChildNodes():
node.removeChild(node.childNodes[0])
node.appendChild(dom.createTextNode(post.title))
return post_elem
def _generate(self, blog, src, output, posts):
from dynastie.search import Search
self.cur_comment = None
self.comment_index = {}
self.hooks['post'] = self._createPost
self.hooks['meta'] = self.createMetas
self.hooks['comments'] = self.createComments
self.hooks['replace'] = self.createReplace
self.hooks['post_title'] = self.createPostTitle
del self.hooks['navigation']
del self.hooks['recents']
del self.hooks['posts']
dom = self.parseTemplate(blog, src, output, 'post', None, False)
if dom is None: return self.report
impl = xml.dom.getDOMImplementation()
s = Search()
for post in posts:
filename = output + '/post/'
filename = filename + post.creation_date.strftime("%Y") + '/' + post.creation_date.strftime("%m") + '/'
if not os.path.exists(filename):
os.makedirs(filename)
filename = filename + post.title_slug + '.html'
if not post.published:
if os.path.exists(filename):
os.unlink(filename)
os.unlink(filename + '.gz')
self.addReport('Remove ' + filename)
s.delete_post(blog, post.id)
continue
#print 'Generate ' + filename
dom_ = impl.createDocument('', 'xml', None)
dom_.replaceChild(dom.firstChild.cloneNode(True), dom_.firstChild)
nodes = self.parse(src, self.hooks, post, dom_, dom_.firstChild)
self.writeIfNotTheSame(filename, nodes)
if not self.somethingWrote:
self.addReport('Nothing changed')
return self.report
def generate(self, blog, src, output):
from dynastie.models import Post, Blog
self.blog = blog
self.parent_posts = []
posts = Post.objects.all()
return self._generate(blog, src, output, posts)
def createPreview(self, values, dom, root, node):
now = datetime.datetime.now()
v = {}
v['title'] = self.createLinkElem(dom, '/preview.html', values['title'])
v['author'] = values['author']
v['date'] = now.strftime("%A, %d %B %Y %H:%m")
v['post_content'] = ''
post_content = self._manageInternalPosts(None, values['content'], self.user)
post_content = self.pygmentCode(post_content)
self.simpleTransform(v, dom, root, node)
content_nodes = root.getElementsByTagName("div")
post_transform = ('post_content')
content_node = None
for content_node in content_nodes:
the_class = content_node.getAttribute('class')
if not the_class in post_transform:
continue
if the_class == 'post_content':
s = '<div>' + post_content + u'</div>'
new_node = parseString(s)
for n in new_node.childNodes[0].childNodes:
content_node.appendChild(n)
break
post_nodes = dom.getElementsByTagNameNS(self.URI, "post")
post_elem = post_nodes[0]
post_elem.parentNode.removeChild(post_elem)
return content_node
def preview(self, request, src, values):
from dynastie.models import Blog
iframe_re = re.compile(r'(<iframe.*)/>')
self.user = request.user
# Override all hooks
self.hooks = {'post' : self.createPreview,
'tags' : self.createTags,
}
if not os.path.exists(src + '/_post.html'):
self.addError('No _post.html found, exiting')
return self.report
try:
dom = parse(src + '/_post.html')
except xml.dom.DOMException as e:
self.addError('Error parsing _post.html : ' + e)
return self.report
post_nodes = dom.getElementsByTagNameNS(self.URI, "posts")
if post_nodes is None:
self.addError('No tag dyn:posts found')
return self.report
nodes = dom.getElementsByTagName("*")
nodes[0] = self.parse(src, self.hooks, values, dom, nodes[0])
writer = StrictUTF8Writer()
nodes[0].writexml(writer)
content = writer.getvalue().decode('utf-8')
# iframe tag must be like <iframe ... ></iframe>, not optimized <iframe ... />
content = self.iframe_re.sub('\\1></iframe>', content)
return content