diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..de9d237 --- /dev/null +++ b/.gitignore @@ -0,0 +1,7 @@ +dist +build +*.egg-info +*.pyc +*.pyo +*~ +*# \ No newline at end of file diff --git a/lektor_blog_archive.py b/lektor_blog_archive.py new file mode 100644 index 0000000..d87e35a --- /dev/null +++ b/lektor_blog_archive.py @@ -0,0 +1,239 @@ +# -*- coding: utf-8 -*- +import posixpath +from datetime import date +from itertools import chain + +from werkzeug.utils import cached_property + +from lektor.pluginsystem import Plugin +from lektor.sourceobj import VirtualSourceObject +from lektor.build_programs import BuildProgram +from lektor.utils import build_url, parse_path + + +class BlogArchive(VirtualSourceObject): + + def __init__(self, record, plugin, items=None): + VirtualSourceObject.__init__(self, record) + self.plugin = plugin + self._items = items + + @property + def path(self): + return self.record.path + '@blog-archive' + + template_name = 'blog-archive/index.html' + + @property + def parent(self): + return self.record + + @cached_property + def year_archives(self): + years = set() + for item in self.record.children: + pub_date = self.plugin.get_pub_date(item) + if pub_date: + years.add(pub_date.year) + return [BlogYearArchive(self.record, self.plugin, + year=year) for year in sorted(years)] + + @property + def items(self): + if self._items is not None: + return self._items + rv = list(self._iter_items()) + self._items = rv + return rv + + def _iter_items(self): + return iter(()) + + @property + def has_any_items(self): + if self._items is not None: + return bool(self._items) + for item in self._iter_items(): + return True + return False + + @property + def items_by_months(self): + months = {} + for item in self.items: + pub_date = self.plugin.get_pub_date(item) + months.setdefault(date(pub_date.year, pub_date.month, 1), + []).append(item) + return [(BlogMonthArchive(self.record, self.plugin, + year=d.year, month=d.month), i) + for d, i in sorted(months.items())] + + def get_archive_url_path(self): + return self.plugin.get_url_path('archive_path') + + @property + def url_path(self): + return build_url(chain([self.record.url_path.strip('/')], + self.get_archive_url_path() or ())) + + +class BlogYearArchive(BlogArchive): + template_name = 'blog-archive/year.html' + + def __init__(self, record, plugin, items=None, year=None): + BlogArchive.__init__(self, record, plugin, items) + self.year = year + + def _iter_items(self): + for item in self.record.children: + pub_date = self.plugin.get_pub_date(item) + if pub_date is not None and \ + pub_date.year == self.year: + yield item + + @property + def path(self): + return '%s@blog-archive/%s' % ( + self.record.path, + self.year, + ) + + @property + def parent(self): + return BlogArchive(self.record, self.plugin) + + @property + def date(self): + return date(self.year, 1, 1) + + def get_archive_url_path(self): + return self.plugin.get_url_path('year_archive_prefix') + [self.year] + + +class BlogMonthArchive(BlogArchive): + template_name = 'blog-archive/month.html' + + def __init__(self, record, plugin, items=None, year=None, month=None): + BlogArchive.__init__(self, record, plugin, items) + self.year = year + self.month = month + + def _iter_items(self): + for item in self.record.children: + pub_date = self.plugin.get_pub_date(item) + if pub_date is not None and \ + pub_date.year == self.year and \ + pub_date.month == self.month: + yield item + + @property + def path(self): + return '%s@blog-archive/%s/%s' % ( + self.record.path, + self.year, + self.month + ) + + @property + def parent(self): + return BlogYearArchive(self.record, self.plugin, year=self.year) + + @property + def date(self): + return date(self.year, self.month, 1) + + def get_archive_url_path(self): + return self.plugin.get_url_path('month_archive_prefix') + [ + self.year, self.month] + + +class BlogArchiveBuildProgram(BuildProgram): + + def produce_artifacts(self): + self.declare_artifact( + posixpath.join(self.source.url_path, 'index.html'), + sources=list(self.source.iter_source_filenames())) + + def build_artifact(self, artifact): + artifact.render_template_into(self.source.template_name, + this=self.source) + + +class BlogArchivePlugin(Plugin): + name = u'Blog Archive' + description = u'Adds archives to a blog.' + + def get_pub_date(self, post): + key = self.get_config().get('pub_date_field', 'pub_date') + return post[key] + + def get_blog_path(self): + return self.get_config().get('blog_path', '/neues') + + def get_url_path(self, name, default='archive'): + return parse_path(self.get_config().get(name, default)) + + def on_setup_env(self, **extra): + self.env.add_build_program(BlogArchive, BlogArchiveBuildProgram) + + @self.env.virtualpathresolver('blog-archive') + def blog_archive_resolver(node, pieces): + if node.path == self.get_blog_path(): + if not pieces: + return BlogArchive(node, self) + elif len(pieces) == 1 and pieces[0].isdigit(): + return BlogYearArchive(node, self, year=int(pieces[0])) + elif len(pieces) == 2 and pieces[0].isdigit() \ + and pieces[1].isdigit(): + return BlogMonthArchive(node, self, year=int(pieces[0]), + month=int(pieces[1])) + + @self.env.urlresolver + def archive_urlresolver(node, url_path): + if node.path != self.get_blog_path(): + return + + archive_index = self.get_url_path('archive_path') + if url_path == archive_index: + return BlogArchive(node, self) + + year_prefix = self.get_url_path('year_archive_prefix') + if url_path[:len(year_prefix)] == year_prefix and \ + url_path[len(year_prefix)].isdigit() and \ + len(url_path) == len(year_prefix) + 1: + year = int(url_path[len(year_prefix)]) + rv = BlogYearArchive(node, self, year=year) + if rv.has_any_items: + return rv + + month_prefix = self.get_url_path('month_archive_prefix') + if url_path[:len(month_prefix)] == month_prefix and \ + len(url_path) == len(month_prefix) + 2 and \ + url_path[len(month_prefix)].isdigit() and \ + url_path[len(month_prefix) + 1].isdigit(): + year = int(url_path[len(month_prefix)]) + month = int(url_path[len(month_prefix) + 1]) + rv = BlogMonthArchive(node, self, year=year, month=month) + if rv.has_any_items: + return rv + + @self.env.generator + def genererate_blog_archive_pages(source): + if source.path != self.get_blog_path(): + return + + years = {} + months = {} + for post in source.children: + pub_date = self.get_pub_date(post) + if pub_date: + years.setdefault(pub_date.year, []).append(post) + months.setdefault((pub_date.year, + pub_date.month), []).append(post) + + yield BlogArchive(source, self) + for year, items in sorted(years.items()): + yield BlogYearArchive(source, self, year=year, items=items) + for (year, month), items in sorted(months.items()): + yield BlogMonthArchive(source, self, year=year, month=month, + items=items) diff --git a/setup.cfg b/setup.cfg new file mode 100644 index 0000000..3c6e79c --- /dev/null +++ b/setup.cfg @@ -0,0 +1,2 @@ +[bdist_wheel] +universal=1 diff --git a/setup.py b/setup.py new file mode 100644 index 0000000..4913d8b --- /dev/null +++ b/setup.py @@ -0,0 +1,15 @@ +from setuptools import setup + +setup( + name='lektor-blog-archive', + version='0.1', + author=u'Armin Ronacher', + author_email='armin.ronacher@active-4.com', + license='MIT', + py_modules=['lektor_blog_archive'], + entry_points={ + 'lektor.plugins': [ + 'blog-archive = lektor_blog_archive:BlogArchivePlugin', + ] + } +)