From afb0ff3190575f15f0e8a1e401e173d1df8f6cf3 Mon Sep 17 00:00:00 2001 From: Leandro Lucarella Date: Thu, 7 Aug 2008 13:33:08 -0300 Subject: [PATCH] Add online commenting support This patch adds support for online commenting. A new template ``com_form.html`` to customize the commenting form is added. Online commenting is only available if ``enable_comments`` is True, and you need to let the web server write to the ``comments_path`` directory. Note that this patch exposes the race in the CommentDB.save() method (described in the previous commit), but since this event is unlikely enough, we don't care at the moment and can be fixed later if needed. --- blitiri.cgi | 189 +++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 186 insertions(+), 3 deletions(-) diff --git a/blitiri.cgi b/blitiri.cgi index 161449a..22532ed 100755 --- a/blitiri.cgi +++ b/blitiri.cgi @@ -48,6 +48,8 @@ encoding = "utf8" import sys import os +import errno +import shutil import time import datetime import calendar @@ -150,6 +152,47 @@ default_comment_footer = """ """ +default_comment_form = """ +
+ +

Your comment

+
+
+
+ + +
+ +
+ + +
+ in + \ +RestructuredText format, please +
+
+
+ +
+
+
+
+""" + # Default CSS default_css = """ @@ -234,6 +277,57 @@ div.comment { margin-bottom: 1em; } +div.comforminner { + margin-left: 2em; +} + +div.comform { + margin-left: 1em; + margin-bottom: 1em; +} + +div.comform label { + display: block; + border-bottom: 1px solid #99C; + margin-top: 0.5em; + clear: both; +} + +div.comform span.comformoptional { + font-size: xx-small; + color: #666; +} + +div.comform input { + font-size: small; + width: 99%; +} + +div.comformhelp { + font-size: xx-small; + text-align: right; + float: right; +} + +span.formurlexample { + color: #111; + background-color: #EEF; + font-family: monospace; + padding-left: 0.2em; + padding-right: 0.2em; +} + +textarea.comformbody { + font-family: monospace; + font-size: small; + width: 99%; + height: 15em; +} + +button.comformsend { + margin-top: 0.5em; +} + hr { float: left; height: 2px; @@ -270,11 +364,20 @@ def rst_to_html(rst): settings = { 'input_encoding': encoding, 'output_encoding': 'utf8', + 'halt_level': 1, + 'traceback': 1, } parts = publish_parts(rst, settings_overrides = settings, writer_name = "html") return parts['body'].encode('utf8') +def valid_rst(rst): + try: + rst_to_html(rst) + return True + except: + return False + def sanitize(obj): if isinstance(obj, basestring): return cgi.escape(obj, True) @@ -348,6 +451,13 @@ class Templates (object): return self.get_template( 'com_footer', default_comment_footer, comment.to_vars()) + def get_comment_form(self, article, method, action): + vars = article.to_vars() + vars['form_method'] = method + vars['form_action'] = action + return self.get_template( + 'com_footer', default_comment_form, vars) + class Comment (object): def __init__(self, article, number, created = None): @@ -385,6 +495,14 @@ class Comment (object): raw_content = property(fget = get_raw_content) + def set(self, author, raw_content, link = '', created = None): + self.loaded = True + self._author = author + self._raw_content = raw_content + self._link = link + self.created = created or datetime.datetime.now() + + def load(self): filename = os.path.join(comments_path, self.article.uuid, str(self.number)) @@ -408,6 +526,19 @@ class Comment (object): self._raw_content = ''.join(raw[count + 1:]) self.loaded = True + def save(self): + filename = os.path.join(comments_path, self.article.uuid, + str(self.number)) + try: + f = open(filename, 'w') + f.write('Author: %s\n' % self.author) + f.write('Link: %s\n' % self.link) + f.write('\n') + f.write(self.raw_content) + except: + return + + def to_html(self): return rst_to_html(self.raw_content) @@ -533,6 +664,13 @@ class Article (object): return cmp(self.title, other.title) + def add_comment(self, author, raw_content, link = ''): + c = Comment(self, len(self.comments)) + c.set(author, raw_content, link) + self.comments.append(c) + return c + + def load(self): # XXX this tweak is only needed for old DB format, where # article's paths started with a slash @@ -690,9 +828,14 @@ class ArticleDB (object): # -def render_html(articles, db, actyear = None, show_comments = False): +def render_html(articles, db, actyear = None, show_comments = False, + redirect = None): + if redirect is not None: + print 'Status: 303 See Other\r\n', + print 'Location: %s\r\n' % redirect, + print 'Content-type: text/html; charset=utf-8\r\n', + print '\r\n', template = Templates(templates_path, db, actyear) - print 'Content-type: text/html; charset=utf-8\n' print template.get_main_header() for a in articles: print template.get_article_header(a) @@ -706,6 +849,8 @@ def render_html(articles, db, actyear = None, show_comments = False): print template.get_comment_header(c) print c.to_html() print template.get_comment_footer(c) + print template.get_comment_form(a, 'post', + blog_url + '/comment/' + a.uuid) print template.get_main_footer() def render_artlist(articles, db, actyear = None): @@ -786,6 +931,7 @@ def handle_cgi(): style = False post = False artlist = False + comment = False if os.environ.has_key('PATH_INFO'): path_info = os.environ['PATH_INFO'] @@ -794,8 +940,9 @@ def handle_cgi(): tag = path_info.startswith('/tag/') post = path_info.startswith('/post/') artlist = path_info.startswith('/list') + comment = path_info.startswith('/comment/') and enable_comments if not style and not atom and not post and not tag \ - and not artlist: + and not comment and not artlist: date = path_info.split('/')[1:] try: if len(date) > 1 and date[0]: @@ -814,6 +961,12 @@ def handle_cgi(): t = t.replace('/', '') t = urllib.unquote_plus(t) tags = set((t,)) + elif comment: + uuid = path_info.replace('/comment/', '') + uuid = uuid.replace('/', '') + author = form.getfirst('comformauthor', '') + link = form.getfirst('comformlink', '') + body = form.getfirst('comformbody', '') db = ArticleDB(os.path.join(data_path, 'db')) if atom: @@ -828,6 +981,21 @@ def handle_cgi(): articles = db.get_articles() articles.sort(cmp = Article.title_cmp) render_artlist(articles, db) + elif comment: + author = author.strip().replace('\n', ' ') + link = link.strip().replace('\n', ' ') + body = body.strip() + article = db.get_article(uuid) + redirect = blog_url + '/post/' + uuid + '#comment' + if author and body and valid_rst(body): + c = article.add_comment(author, body, link) + c.save() + cdb = CommentDB(article) + cdb.comments = article.comments + cdb.save() + redirect += '-' + str(c.number) + render_html( [article], db, year, enable_comments, + redirect = redirect ) else: articles = db.get_articles(year, month, day, tags) articles.sort(reverse = True) @@ -867,6 +1035,17 @@ def handle_cmd(): return 1 db.articles.append(article) db.save() + if enable_comments: + comment_dir = os.path.join(comments_path, article.uuid) + try: + os.mkdir(comment_dir, 0775) + except OSError, e: + if e.errno != errno.EEXIST: + print "Error: can't create comments " \ + "directory %s (%s)" \ + % (comment_dir, e) + # otherwise is probably a removed and re-added + # article elif cmd == 'rm': article = Article(art_path) for a in db.articles: @@ -875,8 +1054,12 @@ def handle_cmd(): else: print "Error: no such article" return 1 + if enable_comments: + r = raw_input('Remove comments [y/N]? ') db.articles.remove(a) db.save() + if enable_comments and r.lower() == 'y': + shutil.rmtree(os.path.join(comments_path, a.uuid)) elif cmd == 'update': article = Article(art_path) for a in db.articles: -- 2.43.0