diff options
| author | 2015-11-11 16:34:34 -0300 | |
|---|---|---|
| committer | 2015-11-11 16:34:34 -0300 | |
| commit | 4e3224c012df9f74f010eb92203520515e8537b9 (patch) | |
| tree | 19322dc0c595268cb6864f21d7e92fd93cb826e9 /nikola/plugins/task/authors.py | |
| parent | 787b97a4cb24330b36f11297c6d3a7a473a907d0 (diff) | |
Imported Upstream version 7.7.3upstream/7.7.3
Diffstat (limited to 'nikola/plugins/task/authors.py')
| -rw-r--r-- | nikola/plugins/task/authors.py | 316 |
1 files changed, 316 insertions, 0 deletions
diff --git a/nikola/plugins/task/authors.py b/nikola/plugins/task/authors.py new file mode 100644 index 0000000..081d21d --- /dev/null +++ b/nikola/plugins/task/authors.py @@ -0,0 +1,316 @@ +# -*- coding: utf-8 -*- + +# Copyright © 2015 Juanjo Conti. + +# Permission is hereby granted, free of charge, to any +# person obtaining a copy of this software and associated +# documentation files (the "Software"), to deal in the +# Software without restriction, including without limitation +# the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the +# Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice +# shall be included in all copies or substantial portions of +# the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR +# PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS +# OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR +# OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +# OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +# SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +"""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 nikola.plugin_categories import Task +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.""" + 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) + return super(RenderAuthors, self).set_site(site) + + 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'], + } + + yield self.group_task() + self.site.scan_posts() + + generate_author_pages = self.site.config["ENABLE_AUTHOR_PAGES"] and len(self._posts_per_author()) > 1 + self.site.GLOBAL_CONTEXT["author_pages_generated"] = generate_author_pages + if 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" + + yield self.site.generic_index_renderer(lang, post_list, indexes_title, template_name, context_source, kw, str(self.name), page_link, page_path) + + 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 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 slugify_author_name(self, name): + """Slugify an author name.""" + if self.site.config['SLUG_AUTHOR_PATH']: + name = utils.slugify(name) + 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), + self.site.config['INDEX_FILE']] if _f] + else: + return [_f for _f in [ + self.site.config['TRANSLATIONS'][lang], + self.site.config['AUTHOR_PATH'], + self.slugify_author_name(name) + ".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) + ".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) + ".xml"] if + _f] + + def _add_extension(self, path, extension): + path[-1] += extension + return path + + 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 |
