commit f91a913bbc245bbb9a53ad30b41f3f57aa7ffd31
parent a3a7cf1cf4f9479895fb4f554c2664fa52122a34
Author: Dan Callaghan <djc@djc.id.au>
Date: Sat, 7 Jun 2008 17:08:08 +1000
category and tag pages
committer: Dan Callaghan <djc@djc.id.au>
--HG--
extra : convert_revision : c2b5d0d89fbf691d0aa04a061efc3e11511d15cd
Diffstat:
5 files changed, 93 insertions(+), 17 deletions(-)
diff --git a/app.py b/app.py
@@ -4,7 +4,7 @@ from colubrid import RegexApplication, HttpResponse, execute
from colubrid.exceptions import PageNotFound, HttpFound
from colubrid.server import StaticExports
-from blog import Entries
+import blog
ENTRIES_DIR = os.path.join(os.path.dirname(__file__), u'entries')
BASE_URL = ''
@@ -18,12 +18,14 @@ class BlogApplication(RegexApplication):
urls = [(r'^$', 'index'),
(r'^feed$', 'feed'),
- (r'^([^/]+)/?$', 'post')]
+ (r'^\+categories/(.+)$', 'category'),
+ (r'^\+tags/(.+)$', 'tag'),
+ (r'^([^+/][^/]*)/?$', 'post')]
charset = 'utf-8'
def __init__(self, *args, **kwargs):
super(BlogApplication, self).__init__(*args, **kwargs)
- self.entries = Entries(ENTRIES_DIR)
+ self.entries = blog.Entries(ENTRIES_DIR)
def index(self):
rendered = template_loader.load('index.xml').generate(entries=self.entries).render('xhtml')
@@ -31,10 +33,34 @@ class BlogApplication(RegexApplication):
def post(self, id):
id = id.decode(self.charset) # shouldn't Colubrid do this?
- entry = self.entries[id]
- rendered = template_loader.load('single.xml').generate(entry=entry).render('xhtml')
+ try:
+ entry = self.entries[id]
+ rendered = template_loader.load('single.xml').generate(entry=entry).render('xhtml')
+ return HttpResponse(rendered, [('Content-Type', 'text/html')], 200)
+ except blog.EntryNotFoundError:
+ raise PageNotFound()
+
+ def category(self, category):
+ category = category.decode(self.charset)
+ categories = self.entries.by_category()
+ if category not in categories:
+ raise PageNotFound()
+ entries = categories[category]
+ rendered = template_loader.load('category.xml').generate(
+ category=category, entries=entries).render('xhtml')
+ return HttpResponse(rendered, [('Content-Type', 'text/html')], 200)
+
+ def tag(self, tag):
+ tag = tag.decode(self.charset)
+ by_tag = self.entries.by_tag()
+ if tag not in by_tag:
+ raise PageNotFound()
+ entries = by_tag[tag]
+ rendered = template_loader.load('tag.xml').generate(
+ tag=tag, entries=entries).render('xhtml')
return HttpResponse(rendered, [('Content-Type', 'text/html')], 200)
+
app = BlogApplication
app = StaticExports(app, {'/static': os.path.join(os.path.dirname(__file__), 'static')})
diff --git a/blog.py b/blog.py
@@ -38,6 +38,23 @@ class Entries(object):
for filename in os.listdir(self.entries_dir)
if not filename.startswith('.'))
+ def by_category(self):
+ """
+ Returns a dict of (category -> set of entries).
+ """
+ d = {}
+ for entry in self:
+ for category in entry.categories:
+ d.setdefault(category, set()).add(entry)
+ return d
+
+ def by_tag(self):
+ d = {}
+ for entry in self:
+ for tag in entry.tags:
+ d.setdefault(tag, set()).add(entry)
+ return d
+
class Entry(object):
@@ -60,14 +77,14 @@ class Entry(object):
raw_cats = self.metadata.get('categories', '').strip()
if raw_cats:
- self.categories = [cat.strip() for cat in raw_cats.split(',')]
+ self.categories = frozenset(cat.strip() for cat in raw_cats.split(','))
else:
- self.categories = []
+ self.categories = frozenset()
raw_tags = self.metadata.get('tags', '').strip()
if raw_tags:
- self.tags = [tag.strip() for tag in raw_tags.split(',')]
+ self.tags = frozenset(tag.strip() for tag in raw_tags.split(','))
else:
- self.tags = []
+ self.tags = frozenset()
self.modified_date = datetime.fromtimestamp(os.path.getmtime(os.path.join(self.dir, 'content.txt')))
self.publication_date = self.metadata.get('publication-date', None) or self.modified_date
diff --git a/templates/_entry.xml b/templates/_entry.xml
@@ -11,20 +11,15 @@
<div class="entrymeta">
Posted ${entry.publication_date.strftime(str('%-1d %B %Y'))}
<py:if test="entry.categories">
- in
- <py:with vars="category = entry.categories[0]">
- <a href="${BASE_URL}/+categories/${category}">${category}</a>
- </py:with>
- <py:for each="category in entry.categories[1:]">
- , <a href="${BASE_URL}/+categories/${category}">${category}</a>
- </py:for>
+ in <a py:for="category in entry.categories" href="${BASE_URL}/+categories/${category}">${category}</a>
</py:if>
</div>
<div class="entrybody">
${Markup(entry.body)}
<p py:if="entry.tags">
- <img src="${BASE_URL}/static/images/tag_blue.png" alt="Tags:" /> ${', '.join(entry.tags)}
+ <img src="${BASE_URL}/static/images/tag_blue.png" alt="Tags:" />
+ <a py:for="tag in entry.tags" rel="tag" href="${BASE_URL}/+tags/${tag}">${tag}</a>
</p>
</div>
diff --git a/templates/category.xml b/templates/category.xml
@@ -0,0 +1,19 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml"
+ xmlns:py="http://genshi.edgewall.org/"
+ xmlns:xi="http://www.w3.org/2001/XInclude"
+ lang="en-AU">
+<xi:include href="_commonwrapper.xml" />
+<xi:include href="_entry.xml" />
+
+<head>
+ <title>${category} category</title>
+</head>
+<body>
+
+<py:for each="entry in sorted(entries, key=lambda e: e.publication_date, reverse=True)[:20]">
+ ${show_entry(entry, show_comments=False)}
+</py:for>
+
+</body>
+</html>
diff --git a/templates/tag.xml b/templates/tag.xml
@@ -0,0 +1,19 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml"
+ xmlns:py="http://genshi.edgewall.org/"
+ xmlns:xi="http://www.w3.org/2001/XInclude"
+ lang="en-AU">
+<xi:include href="_commonwrapper.xml" />
+<xi:include href="_entry.xml" />
+
+<head>
+ <title>${tag} tag</title>
+</head>
+<body>
+
+<py:for each="entry in sorted(entries, key=lambda e: e.publication_date, reverse=True)[:20]">
+ ${show_entry(entry, show_comments=False)}
+</py:for>
+
+</body>
+</html>