Merge pull request #133 from alistairmagee/subcategory

A plugin to add subcategories to Article categories
This commit is contained in:
Justin Mayer
2014-01-24 13:01:28 -08:00
3 changed files with 158 additions and 0 deletions

64
subcategory/README.md Normal file
View File

@@ -0,0 +1,64 @@
#Subcategory Plugin#
Adds support for subcategories in addition to article categories.
Subcategories are heirachial. Each subcategory has a parent, which is either a
regular category or another subcategory. Subcategories with the same name but
different parents are not the same. Their articles won't be grouped together
under that name.
Feeds can be generated for each subcategory just like categories and tags.
##Usage##
Subcategories are an extension to categories. Add subcategories to an article's
category metadata using a `/` like this:
Category: Regular Category/Sub-Category/Sub-Sub-category
then create a `subcategory.html` template in your theme similar to the
`category.html` or `tag.html`
In your templates `article.category` continues to act the same way. Your
subcategories are stored in a list `aricles.subcategories`. To create a
breadcrumb style navigation you might try something like this:
<nav class="breadcrumb">
<ol>
<li>
<a href="{{ SITEURL }}/{{ arcticle.categor.url }}">{{ article.category}}</a>
</li>
{% for subcategory in article.subcategories %}
<li>
<a href="{{ SITEURL }}/{{ category.url }}>{{ subcategory }}</a>
</li>
{% endfor %}
</ol>
</nav>
##Settings##
Consistent with the default settings for Tags and Categories, the default
settings for subcategoris are:
'SUBCATEGORY_SAVE_AS' = os.path.join('subcategory', '{savepath}.html')
'SUBCATEGORY_URL' = 'subcategory/(fullurl).html'
`savepath` and `fullurl` are generated recursively, using slugs. So the full
url would be:
category-slug/sub-category-slug/sub-sub-category-slug
with `savepath` being similar but joined using `os.path.join`
Similarily you can save a subcategory feeds by adding one of the following
to your pelicanconf file
SUBCATEGORY_FEED_ATOM = 'feeds/%s.atom.xml'
SUBCATEGORY_FEED_RSS = 'feeds/%s.rss.xml'
and this will create a feed with `fullurl` of the subcategory. Eg.
feeds/category/subcategory.atom.xml

1
subcategory/__init__.py Normal file
View File

@@ -0,0 +1 @@
from .subcategory import *

View File

@@ -0,0 +1,93 @@
# -*- coding: utf-8 -*-
"""
@Author: Alistair Magee
Adds support for subcategories on pelican articles
"""
import os
from collections import defaultdict
from pelican import signals
from pelican.urlwrappers import URLWrapper, Category
from operator import attrgetter
from functools import partial
from six import text_type
class SubCategory(URLWrapper):
def __init__(self, name, parent, *args, **kwargs):
super(SubCategory, self).__init__(name, *args, **kwargs)
self.parent = parent
if isinstance(self.parent, SubCategory):
self.savepath = os.path.join(self.parent.savepath, self.slug)
self.fullurl = '{}/{}'.format(self.parent.fullurl, self.slug)
else: #parent is a category
self.savepath = os.path.join(self.parent.slug, self.slug)
self.fullurl = '{}/{}'.format(self.parent.slug, self.slug)
def as_dict(self):
d = self.__dict__
d['name'] = self.name
d['savepath'] = self.savepath
d['fullurl'] = self.fullurl
d['parent'] = self.parent
return d
def get_subcategories(generator, metadata):
if 'SUBCATEGORY_SAVE_AS' not in generator.settings:
generator.settings['SUBCATEGORY_SAVE_AS'] = os.path.join(
'subcategory', '{savepath}.html')
if 'SUBCATEGORY_URL' not in generator.settings:
generator.settings['SUBCATEGORY_URL'] = 'subcategory/{fullurl}.html'
category_list = text_type(metadata.get('category')).split('/')
category = (category_list.pop(0)).strip()
category = Category(category, generator.settings)
metadata['category'] = category
#generate a list of subcategories with their parents
sub_list = []
parent = category
for subcategory in category_list:
subcategory.strip()
subcategory = SubCategory(subcategory, parent, generator.settings)
sub_list.append(subcategory)
parent = subcategory
metadata['subcategories'] = sub_list
def organize_subcategories(generator):
generator.subcategories = defaultdict(list)
for article in generator.articles:
subcategories = article.metadata.get('subcategories')
for cat in subcategories:
generator.subcategories[cat].append(article)
def generate_subcategories(generator, writer):
write = partial(writer.write_file,
relative_urls=generator.settings['RELATIVE_URLS'])
subcategory_template = generator.get_template('subcategory')
for subcat, articles in generator.subcategories.items():
articles.sort(key=attrgetter('date'), reverse=True)
dates = [article for article in generator.dates if article in articles]
write(subcat.save_as, subcategory_template, generator.context,
subcategory=subcat, articles=articles, dates=dates,
paginated={'articles': articles, 'dates': dates},
page_name=subcat.page_name, all_articles=generator.articles)
def generate_subcategory_feeds(generator, writer):
for subcat, articles in generator.subcategories.items():
articles.sort(key=attrgetter('date'), reverse=True)
if generator.settings.get('SUBCATEGORY_FEED_ATOM'):
writer.write_feed(articles, generator.context,
generator.settings['SUBCATEGORY_FEED_ATOM']
% subcat.fullurl)
if generator.settings.get('SUBCATEGORY_FEED_RSS'):
writer.write_feed(articles, generator.context,
generator.settings['SUBCATEGORY_FEED_RSS']
% subcat.fullurl, feed_type='rss')
def generate(generator, writer):
generate_subcategory_feeds(generator, writer)
generate_subcategories(generator, writer)
def register():
signals.article_generator_context.connect(get_subcategories)
signals.article_generator_finalized.connect(organize_subcategories)
signals.article_writer_finalized.connect(generate)