# below for details.
captcha_method = "title"
+# How many articles to show in the index
+index_articles = 10
+
#
# End of configuration
years: %(yearlinks)s<br/>
subscribe: <a href="%(url)s/atom">atom</a><br/>
views: <a href="%(url)s/">blog</a> <a href="%(url)s/list">list</a><br/>
+ tags: %(taglinks)s<br/>
</div>
</body>
def valid_link(link):
import re
- mail_re = r"^[^ \t\n\r@<>()]+@[a-z0-9][a-z0-9\.\-_]*\.[a-z]+$"
scheme_re = r'^[a-zA-Z]+:'
+ mail_re = r"^[^ \t\n\r@<>()]+@[a-z0-9][a-z0-9\.\-_]*\.[a-z]+$"
url_re = r'^(?:[a-z0-9\-]+|[a-z0-9][a-z0-9\-\.\_]*\.[a-z]+)' \
r'(?::[0-9]+)?(?:/.*)?$'
- scheme = ''
- rest = link
+
if re.match(scheme_re, link, re.I):
scheme, rest = link.split(':', 1)
- if (not scheme or scheme == 'mailto') and re.match(mail_re, rest, re.I):
+ # if we have an scheme and a rest, assume the link is valid
+ # and return it as-is; otherwise (having just the scheme) is
+ # invalid
+ if rest:
+ return link
+ return None
+
+ # at this point, we don't have a scheme; we will try to recognize some
+ # common addresses (mail and http at the moment) and complete them to
+ # form a valid link, if we fail we will just claim it's invalid
+ if re.match(mail_re, link, re.I):
return 'mailto:' + link
- if not scheme and re.match(url_re, rest, re.I):
- return 'http://' + rest
- if scheme:
- return link
+ elif re.match(url_re, link, re.I):
+ return 'http://' + link
+
return None
def sanitize(obj):
- if isinstance(obj, basestring):
- return cgi.escape(obj, True)
- return obj
+ return cgi.escape(obj, quote = True)
# find out our URL, needed for syndication
'showyear': showyear,
'monthlinks': ' '.join(db.get_month_links(showyear)),
'yearlinks': ' '.join(db.get_year_links()),
+ 'taglinks': ' '.join(db.get_tag_links()),
}
def get_template(self, page_name, default_template, extra_vars = None):
vars = comment.to_vars()
if comment.link:
vars['linked_author'] = '<a href="%s">%s</a>' \
- % (comment.link, comment.author)
+ % (vars['link'], vars['author'])
else:
- vars['linked_author'] = comment.author
+ vars['linked_author'] = vars['author']
return self.get_template(
'com_header', default_comment_header, vars)
class CommentDB (object):
def __init__(self, article):
self.path = os.path.join(comments_path, article.uuid)
+ # if comments were enabled after the article was added, we
+ # will need to create the directory
+ if not os.path.exists(self.path):
+ os.mkdir(self.path, 0777)
+
self.comments = []
self.load(article)
self.load()
return self._comments
- def __cmp__(self, other):
- if self.path == other.path:
- return 0
- if not self.created:
- return 1
- if not other.created:
- return -1
- if self.created < other.created:
- return -1
- return 1
-
- def title_cmp(self, other):
- return cmp(self.title, other.title)
-
def add_comment(self, author, raw_content, link = ''):
c = Comment(self, len(self.comments))
self.loaded = True
def to_html(self):
- return rst_to_html(self.raw_content)
+ dirname = os.path.dirname
+ post_url = '/'.join(dirname(full_url), data_path, dirname(self.path))
+ rst = self.raw_content.replace('##POST_URL##', post_url)
+ return rst_to_html(rst)
def to_vars(self):
return {
self.uuids = {}
self.actyears = set()
self.actmonths = set()
+ self.acttags = set()
self.load()
def get_articles(self, year = 0, month = 0, day = 0, tags = None):
datetime.datetime.fromtimestamp(float(l[1])),
datetime.datetime.fromtimestamp(float(l[2])))
self.uuids[a.uuid] = a
+ self.acttags.update(a.tags)
self.actyears.add(a.created.year)
self.actmonths.add((a.created.year, a.created.month))
self.articles.append(a)
ml.append(s)
return ml
+ def get_tag_links(self):
+ tl = list(self.acttags)
+ tl.sort()
+ return [ '<a href="%s/tag/%s">%s</a>' % (blog_url,
+ sanitize(t), sanitize(t)) for t in tl ]
+
#
# Main
#
<published>%(ciso)sZ</published>
<updated>%(uiso)sZ</updated>
<content type="xhtml">
- <div xmlns="http://www.w3.org/1999/xhtml"><p>
+ <div xmlns="http://www.w3.org/1999/xhtml">
%(contents)s
- </p></div>
+ </div>
</content>
</entry>
""" % vars
print 'Content-type: text/css\r\n\r\n',
print default_css
+# Get a dictionary with sort() arguments (key and reverse) by parsing the sort
+# specification format:
+# [+-]?<key>?
+# Where "-" is used to specify reverse order, while "+" is regular, ascending,
+# order (reverse = False). The key value is an Article's attribute name (title,
+# author, created, updated and uuid are accepted), and will be used as key for
+# sorting. If a value is omitted, that value is taken from the default, which
+# should be provided using the same format specification, with the difference
+# that all values must be provided for the default.
+def get_sort_args(sort_str, default):
+ def parse(s):
+ d = dict()
+ if not s:
+ return d
+ key = None
+ if len(s) > 0:
+ # accept ' ' as an alias of '+' since '+' is translated
+ # to ' ' in URLs
+ if s[0] in ('+', ' ', '-'):
+ key = s[1:]
+ d['reverse'] = (s[0] == '-')
+ else:
+ key = s
+ if key in ('title', 'author', 'created', 'updated', 'uuid'):
+ d['key'] = lambda a: getattr(a, key)
+ return d
+ args = parse(default)
+ assert args['key'] is not None and args['reverse'] is not None
+ args.update(parse(sort_str))
+ return args
+
def handle_cgi():
import cgitb; cgitb.enable()
month = int(form.getfirst("month", 0))
day = int(form.getfirst("day", 0))
tags = set(form.getlist("tag"))
+ sort_str = form.getfirst("sort", None)
uuid = None
atom = False
style = False
db = ArticleDB(os.path.join(data_path, 'db'))
if atom:
articles = db.get_articles(tags = tags)
- articles.sort(reverse = True)
- render_atom(articles[:10])
+ articles.sort(**get_sort_args(sort_str, '-created'))
+ render_atom(articles[:index_articles])
elif style:
render_style()
elif post:
render_html( [article], db, year, enable_comments )
elif artlist:
articles = db.get_articles()
- articles.sort(cmp = Article.title_cmp)
+ articles.sort(**get_sort_args(sort_str, '+title'))
render_artlist(articles, db)
- elif comment:
+ elif comment and enable_comments:
form_data = CommentFormData(author.strip().replace('\n', ' '),
link.strip().replace('\n', ' '), captcha,
body.replace('\r', ''))
form_data )
else:
articles = db.get_articles(year, month, day, tags)
- articles.sort(reverse = True)
+ articles.sort(**get_sort_args(sort_str, '-created'))
if not year and not month and not day and not tags:
- articles = articles[:10]
+ articles = articles[:index_articles]
render_html(articles, db, year)