Skip to content

Commit

Permalink
Rewrote authors plugin as a Taxonomy plugin.
Browse files Browse the repository at this point in the history
  • Loading branch information
felixfontein committed Oct 16, 2016
1 parent f6c86a2 commit 0c31b3f
Showing 1 changed file with 68 additions and 280 deletions.
348 changes: 68 additions & 280 deletions nikola/plugins/task/authors.py
Expand Up @@ -27,300 +27,88 @@
"""Render the author pages and feeds."""

from __future__ import unicode_literals
import os
import natsort
try:
from urlparse import urljoin
except ImportError:
from urllib.parse import urljoin # NOQA
from collections import defaultdict

from blinker import signal

from nikola.plugin_categories import Task
from nikola.plugin_categories import Taxonomy
from nikola import utils


class RenderAuthors(Task):
class ClassifyAuthors(Taxonomy):
"""Render the author pages and feeds."""

name = "render_authors"
posts_per_author = None
name = "classify_authors"

classification_name = "author"
metadata_name = "author"
overview_page_variable_name = "authors"
more_than_one_classifications_per_post = False
has_hierarchy = False
template_for_classification_overview = "authors.tmpl"
apply_to_posts = True
apply_to_pages = False
minimum_post_count_per_classification_in_overview = 1
omit_empty_classifications = False
also_create_classifications_from_other_languages = False

def set_site(self, site):
"""Set Nikola site."""
self.generate_author_pages = False
if site.config["ENABLE_AUTHOR_PAGES"]:
site.register_path_handler('author_index', self.author_index_path)
site.register_path_handler('author', self.author_path)
site.register_path_handler('author_atom', self.author_atom_path)
site.register_path_handler('author_rss', self.author_rss_path)
signal('scanned').connect(self.posts_scanned)
return super(RenderAuthors, self).set_site(site)

def posts_scanned(self, event):
"""Called after posts are scanned via signal."""
self.generate_author_pages = self.site.config["ENABLE_AUTHOR_PAGES"] and len(self._posts_per_author()) > 1
self.site.GLOBAL_CONTEXT["author_pages_generated"] = self.generate_author_pages

def gen_tasks(self):
"""Render the author pages and feeds."""
kw = {
"translations": self.site.config["TRANSLATIONS"],
"blog_title": self.site.config["BLOG_TITLE"],
"site_url": self.site.config["SITE_URL"],
"base_url": self.site.config["BASE_URL"],
"messages": self.site.MESSAGES,
"output_folder": self.site.config['OUTPUT_FOLDER'],
"filters": self.site.config['FILTERS'],
'author_path': self.site.config['AUTHOR_PATH'],
"author_pages_are_indexes": self.site.config['AUTHOR_PAGES_ARE_INDEXES'],
"generate_rss": self.site.config['GENERATE_RSS'],
"feed_teasers": self.site.config["FEED_TEASERS"],
"feed_plain": self.site.config["FEED_PLAIN"],
"feed_link_append_query": self.site.config["FEED_LINKS_APPEND_QUERY"],
"show_untranslated_posts": self.site.config['SHOW_UNTRANSLATED_POSTS'],
"feed_length": self.site.config['FEED_LENGTH'],
"tzinfo": self.site.tzinfo,
"pretty_urls": self.site.config['PRETTY_URLS'],
"strip_indexes": self.site.config['STRIP_INDEXES'],
"index_file": self.site.config['INDEX_FILE'],
}

self.site.scan_posts()
yield self.group_task()

if self.generate_author_pages:
yield self.list_authors_page(kw)

if not self._posts_per_author(): # this may be self.site.posts_per_author
return

author_list = list(self._posts_per_author().items())

def render_lists(author, posts):
"""Render author pages as RSS files and lists/indexes."""
post_list = sorted(posts, key=lambda a: a.date)
post_list.reverse()
for lang in kw["translations"]:
if kw["show_untranslated_posts"]:
filtered_posts = post_list
else:
filtered_posts = [x for x in post_list if x.is_translation_available(lang)]
if kw["generate_rss"]:
yield self.author_rss(author, lang, filtered_posts, kw)
# Render HTML
if kw['author_pages_are_indexes']:
yield self.author_page_as_index(author, lang, filtered_posts, kw)
else:
yield self.author_page_as_list(author, lang, filtered_posts, kw)

for author, posts in author_list:
for task in render_lists(author, posts):
yield task

def _create_authors_page(self, kw):
"""Create a global "all authors" page for each language."""
template_name = "authors.tmpl"
kw = kw.copy()
for lang in kw["translations"]:
authors = natsort.natsorted([author for author in self._posts_per_author().keys()],
alg=natsort.ns.F | natsort.ns.IC)
has_authors = (authors != [])
kw['authors'] = authors
output_name = os.path.join(
kw['output_folder'], self.site.path('author_index', None, lang))
context = {}
if has_authors:
context["title"] = kw["messages"][lang]["Authors"]
context["items"] = [(author, self.site.link("author", author, lang)) for author
in authors]
context["description"] = context["title"]
else:
context["items"] = None
context["permalink"] = self.site.link("author_index", None, lang)
context["pagekind"] = ["list", "authors_page"]
task = self.site.generic_post_list_renderer(
lang,
[],
output_name,
template_name,
kw['filters'],
context,
)
task['uptodate'] = task['uptodate'] + [utils.config_changed(kw, 'nikola.plugins.task.authors:page')]
task['basename'] = str(self.name)
yield task

def list_authors_page(self, kw):
"""Create a global "all authors" page for each language."""
yield self._create_authors_page(kw)

def _get_title(self, author):
return author

def _get_description(self, author, lang):
descriptions = self.site.config['AUTHOR_PAGES_DESCRIPTIONS']
return descriptions[lang][author] if lang in descriptions and author in descriptions[lang] else None

def author_page_as_index(self, author, lang, post_list, kw):
"""Render a sort of index page collection using only this author's posts."""
kind = "author"

def page_link(i, displayed_i, num_pages, force_addition, extension=None):
feed = "_atom" if extension == ".atom" else ""
return utils.adjust_name_for_index_link(self.site.link(kind + feed, author, lang), i, displayed_i, lang, self.site, force_addition, extension)

def page_path(i, displayed_i, num_pages, force_addition, extension=None):
feed = "_atom" if extension == ".atom" else ""
return utils.adjust_name_for_index_path(self.site.path(kind + feed, author, lang), i, displayed_i, lang, self.site, force_addition, extension)

context_source = {}
title = self._get_title(author)
if kw["generate_rss"]:
# On a author page, the feeds include the author's feeds
rss_link = ("""<link rel="alternate" type="application/rss+xml" """
"""title="RSS for author """
"""{0} ({1})" href="{2}">""".format(
title, lang, self.site.link(kind + "_rss", author, lang)))
context_source['rss_link'] = rss_link
context_source["author"] = title
indexes_title = kw["messages"][lang]["Posts by %s"] % title
context_source["description"] = self._get_description(author, lang)
context_source["pagekind"] = ["index", "author_page"]
template_name = "authorindex.tmpl"
self.show_list_as_index = site.config['AUTHOR_PAGES_ARE_INDEXES']
self.template_for_list_of_one_classification = "authorindex.tmpl" if self.show_list_as_index else "author.tmpl"
return super(ClassifyAuthors, self).set_site(site)

yield self.site.generic_index_renderer(lang, post_list, indexes_title, template_name, context_source, kw, str(self.name), page_link, page_path)
def is_enabled(self):
"""Return True if this taxonomy is enabled, or False otherwise."""
return self.site.config["ENABLE_AUTHOR_PAGES"]

def author_page_as_list(self, author, lang, post_list, kw):
"""Render a single flat link list with this author's posts."""
kind = "author"
template_name = "author.tmpl"
output_name = os.path.join(kw['output_folder'], self.site.path(
kind, author, lang))
context = {}
context["lang"] = lang
title = self._get_title(author)
context["author"] = title
context["title"] = kw["messages"][lang]["Posts by %s"] % title
context["posts"] = post_list
context["permalink"] = self.site.link(kind, author, lang)
context["kind"] = kind
context["description"] = self._get_description(author, lang)
context["pagekind"] = ["list", "author_page"]
task = self.site.generic_post_list_renderer(
lang,
post_list,
output_name,
template_name,
kw['filters'],
context,
)
task['uptodate'] = task['uptodate'] + [utils.config_changed(kw, 'nikola.plugins.task.authors:list')]
task['basename'] = str(self.name)
yield task
def classify(self, post, lang):
"""Classify the given post for the given language."""
return [post.author()]

def author_rss(self, author, lang, posts, kw):
"""Create a RSS feed for a single author in a given language."""
kind = "author"
# Render RSS
output_name = os.path.normpath(
os.path.join(kw['output_folder'],
self.site.path(kind + "_rss", author, lang)))
feed_url = urljoin(self.site.config['BASE_URL'], self.site.link(kind + "_rss", author, lang).lstrip('/'))
deps = []
deps_uptodate = []
post_list = sorted(posts, key=lambda a: a.date)
post_list.reverse()
for post in post_list:
deps += post.deps(lang)
deps_uptodate += post.deps_uptodate(lang)
task = {
'basename': str(self.name),
'name': output_name,
'file_dep': deps,
'targets': [output_name],
'actions': [(utils.generic_rss_renderer,
(lang, "{0} ({1})".format(kw["blog_title"](lang), self._get_title(author)),
kw["site_url"], None, post_list,
output_name, kw["feed_teasers"], kw["feed_plain"], kw['feed_length'],
feed_url, None, kw["feed_link_append_query"]))],
'clean': True,
'uptodate': [utils.config_changed(kw, 'nikola.plugins.task.authors:rss')] + deps_uptodate,
'task_dep': ['render_posts'],
}
return utils.apply_filters(task, kw['filters'])
def get_list_path(self, lang):
"""A path handler for the list of all classifications."""
return [self.site.config['AUTHOR_PATH']], True

def slugify_author_name(self, name, lang=None):
"""Slugify an author name."""
if lang is None: # TODO: remove in v8
utils.LOGGER.warn("RenderAuthors.slugify_author_name() called without language!")
lang = ''
def get_path(self, author, lang):
"""A path handler for the given classification."""
if self.site.config['SLUG_AUTHOR_PATH']:
name = utils.slugify(name, lang)
return name

def author_index_path(self, name, lang):
"""Link to the author's index.
Example:
link://authors/ => /authors/index.html
"""
return [_f for _f in [self.site.config['TRANSLATIONS'][lang],
self.site.config['AUTHOR_PATH'],
self.site.config['INDEX_FILE']] if _f]

def author_path(self, name, lang):
"""Link to an author's page.
Example:
link://author/joe => /authors/joe.html
"""
if self.site.config['PRETTY_URLS']:
return [_f for _f in [
self.site.config['TRANSLATIONS'][lang],
self.site.config['AUTHOR_PATH'],
self.slugify_author_name(name, lang),
self.site.config['INDEX_FILE']] if _f]
slug = utils.slugify(author, lang)
else:
return [_f for _f in [
self.site.config['TRANSLATIONS'][lang],
self.site.config['AUTHOR_PATH'],
self.slugify_author_name(name, lang) + ".html"] if _f]

def author_atom_path(self, name, lang):
"""Link to an author's Atom feed.
Example:
link://author_atom/joe => /authors/joe.atom
"""
return [_f for _f in [self.site.config['TRANSLATIONS'][lang],
self.site.config['AUTHOR_PATH'], self.slugify_author_name(name, lang) + ".atom"] if
_f]

def author_rss_path(self, name, lang):
"""Link to an author's RSS feed.
Example:
slug = author
return [self.site.config['AUTHOR_PATH'], slug], False

link://author_rss/joe => /authors/joe.rss
"""
return [_f for _f in [self.site.config['TRANSLATIONS'][lang],
self.site.config['AUTHOR_PATH'], self.slugify_author_name(name, lang) + ".xml"] if
_f]

def _add_extension(self, path, extension):
path[-1] += extension
return path
def provide_list_context_and_uptodate(self, lang):
"""Provide data for the context and the uptodate list for the list of all classifiations."""
kw = {
"messages": self.site.MESSAGES,
}
context = {
"title": kw["messages"][lang]["Authors"],
"description": kw["messages"][lang]["Authors"],
"permalink": self.site.link("author_index", None, lang),
"pagekind": ["list", "authors_page"],
}
kw.update(context)
return context, kw

def _posts_per_author(self):
"""Return a dict of posts per author."""
if self.posts_per_author is None:
self.posts_per_author = defaultdict(list)
for post in self.site.timeline:
if post.is_post:
self.posts_per_author[post.author()].append(post)
return self.posts_per_author
def provide_context_and_uptodate(self, author, lang):
"""Provide data for the context and the uptodate list for the list of the given classifiation."""
descriptions = self.site.config['AUTHOR_PAGES_DESCRIPTIONS']
kw = {
"messages": self.site.MESSAGES,
}
context = {
"title": kw["messages"][lang]["Posts by %s"] % author,
"classification_title": author,
"description": descriptions[lang][author] if lang in descriptions and author in descriptions[lang] else None,
"pagekind": ["index" if self.show_list_as_index else "list", "author_page"],
}
kw.update(context)
return context, kw

def postprocess_posts_per_classification(self, posts_per_author_per_language, flat_hierarchy_per_lang=None, hierarchy_lookup_per_lang=None):
"""Rearrange, modify or otherwise use the list of posts per classification and per language."""
more_than_one = False
for lang, posts_per_author in posts_per_author_per_language.item():
if len(posts_per_author.keys()) > 1:
more_than_one = True
self.generate_author_pages = self.site.config["ENABLE_AUTHOR_PAGES"] and more_than_one
self.site.GLOBAL_CONTEXT["author_pages_generated"] = self.generate_author_pages

0 comments on commit 0c31b3f

Please sign in to comment.