aboutsummaryrefslogtreecommitdiffstats
path: root/nikola/plugins/task/authors.py
diff options
context:
space:
mode:
Diffstat (limited to 'nikola/plugins/task/authors.py')
-rw-r--r--nikola/plugins/task/authors.py387
1 files changed, 110 insertions, 277 deletions
diff --git a/nikola/plugins/task/authors.py b/nikola/plugins/task/authors.py
index ec61800..24fe650 100644
--- a/nikola/plugins/task/authors.py
+++ b/nikola/plugins/task/authors.py
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
-# Copyright © 2015-2016 Juanjo Conti and others.
+# Copyright © 2015-2020 Juanjo Conti and others.
# Permission is hereby granted, free of charge, to any
# person obtaining a copy of this software and associated
@@ -26,301 +26,134 @@
"""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):
- """Render the author pages and feeds."""
-
- name = "render_authors"
- posts_per_author = None
-
- 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())
+class ClassifyAuthors(Taxonomy):
+ """Classify the posts by authors."""
- 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)
+ name = "classify_authors"
- for author, posts in author_list:
- for task in render_lists(author, posts):
- yield task
+ classification_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
+ add_other_languages_variable = True
+ path_handler_docstrings = {
+ 'author_index': """ Link to the authors index.
- 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
+ Example:
- def list_authors_page(self, kw):
- """Create a global "all authors" page for each language."""
- yield self._create_authors_page(kw)
+ link://authors/ => /authors/index.html""",
+ 'author': """Link to an author's page.
- def _get_title(self, author):
- return author
+ Example:
- 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
+ link://author/joe => /authors/joe.html""",
+ 'author_atom': """Link to an author's Atom feed.
- 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"
+Example:
- 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)
+link://author_atom/joe => /authors/joe.atom""",
+ 'author_rss': """Link to an author's RSS feed.
- 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)
+Example:
- 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"
+link://author_rss/joe => /authors/joe.xml""",
+ }
- yield self.site.generic_index_renderer(lang, post_list, indexes_title, template_name, context_source, kw, str(self.name), page_link, page_path)
+ def set_site(self, site):
+ """Set Nikola site."""
+ super().set_site(site)
+ self.show_list_as_index = site.config['AUTHOR_PAGES_ARE_INDEXES']
+ self.more_than_one_classifications_per_post = site.config.get('MULTIPLE_AUTHORS_PER_POST', False)
+ self.template_for_single_list = "authorindex.tmpl" if self.show_list_as_index else "author.tmpl"
+ self.translation_manager = utils.ClassificationTranslationManager()
+
+ def is_enabled(self, lang=None):
+ """Return True if this taxonomy is enabled, or False otherwise."""
+ if not self.site.config["ENABLE_AUTHOR_PAGES"]:
+ return False
+ if lang is not None:
+ return self.generate_author_pages
+ return True
+
+ def classify(self, post, lang):
+ """Classify the given post for the given language."""
+ if self.more_than_one_classifications_per_post:
+ return post.authors(lang=lang)
+ else:
+ return [post.author(lang=lang)]
- 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 get_classification_friendly_name(self, classification, lang, only_last_component=False):
+ """Extract a friendly name from the classification."""
+ return classification
- 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_overview_path(self, lang, dest_type='page'):
+ """Return a path for the list of all classifications."""
+ path = self.site.config['AUTHOR_PATH'](lang)
+ return [component for component in path.split('/') if component], 'always'
- 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, classification, lang, dest_type='page'):
+ """Return a path 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(classification, 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:
-
- 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]
+ slug = classification
+ return [self.site.config['AUTHOR_PATH'](lang), slug], 'auto'
- def _add_extension(self, path, extension):
- path[-1] += extension
- return path
+ def provide_overview_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, classification, lang, node=None):
+ """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 = {
+ "author": classification,
+ "title": kw["messages"][lang]["Posts by %s"] % classification,
+ "description": descriptions[lang][classification] if lang in descriptions and classification in descriptions[lang] else None,
+ "pagekind": ["index" if self.show_list_as_index else "list", "author_page"],
+ }
+ kw.update(context)
+ return context, kw
+
+ def get_other_language_variants(self, classification, lang, classifications_per_language):
+ """Return a list of variants of the same author in other languages."""
+ return self.translation_manager.get_translations_as_list(classification, lang, classifications_per_language)
+
+ def postprocess_posts_per_classification(self, posts_per_classification_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_classification_per_language.items():
+ authors = set()
+ for author, posts in posts_per_author.items():
+ for post in posts:
+ if not self.site.config["SHOW_UNTRANSLATED_POSTS"] and not post.is_translation_available(lang):
+ continue
+ authors.add(author)
+ if len(authors) > 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
+ self.translation_manager.add_defaults(posts_per_classification_per_language)