Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

WIP: render author pages #1972

Merged
merged 35 commits into from
Aug 31, 2015
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
c0efaef
WIP: render authors pages
Aug 24, 2015
124dcc5
Fix style
Aug 24, 2015
63fd0ef
Add AUTHOR* variables to config
Aug 24, 2015
8d0baae
Fix
Aug 24, 2015
ccf0820
Use author description
Aug 24, 2015
07f0bb4
Add author related tempaltes to base
Aug 24, 2015
8d80677
Add author tempalte to bootstrap3
Aug 24, 2015
2dbe15b
Fix tag template
Aug 24, 2015
be162a0
Update messages_en.py
Aug 24, 2015
a8422d2
Remove pointless code
Aug 24, 2015
ad710b1
Fix key error
Aug 24, 2015
d58afc6
Remove unused imports
Aug 24, 2015
e3e0b04
Fix style
Aug 24, 2015
a594801
Fix
Aug 24, 2015
083ce3c
pygal v2.0.4
Kwpolska Aug 27, 2015
0b24500
Merge pull request #1981 from getnikola/posts-categories
da2x Aug 31, 2015
14ca39d
WIP: render authors pages
Aug 24, 2015
5caf0c3
Fix style
Aug 24, 2015
e75341e
Add AUTHOR* variables to config
Aug 24, 2015
f4fb9c8
Fix
Aug 24, 2015
eb11672
Use author description
Aug 24, 2015
7eaa217
Add author related tempaltes to base
Aug 24, 2015
e2f9dac
Add author tempalte to bootstrap3
Aug 24, 2015
e8b4498
Fix tag template
Aug 24, 2015
2a0f5e8
Update messages_en.py
Aug 24, 2015
c763177
Remove pointless code
Aug 24, 2015
7ed2db0
Fix key error
Aug 24, 2015
664a1b8
Remove unused imports
Aug 24, 2015
be91d91
Fix style
Aug 24, 2015
4de1133
Fix
Aug 24, 2015
98d8973
Merge branch 'master' of github.com:jjconti/nikola
Aug 31, 2015
1f47671
Restore removed \n
Aug 31, 2015
d9f3387
Add ENABLE_AUTHOR_PAGES conf
Aug 31, 2015
c0841d4
Only generate author pages if ENABLE_AUTHOR_PAGES is True and there i…
Aug 31, 2015
679ed98
Add author_pages_generated to global context to use it in tamplates
Aug 31, 2015
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Next Next commit
WIP: render authors pages
  • Loading branch information
Juanjo Conti committed Aug 24, 2015
commit c0efaef3ee7d247695cd2df962db4af816857a4b
10 changes: 10 additions & 0 deletions nikola/plugins/task/authors.plugin
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
[Core]
Name = render_authors
Module = authors

[Documentation]
Author = Juanjo Conti
Version = 0.1
Website = http://getnikola.com
Description = Render the author pages and feeds.

294 changes: 294 additions & 0 deletions nikola/plugins/task/authors.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,294 @@
# -*- 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 json
import os
import sys
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."""
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'],
"rss_teasers": self.site.config["RSS_TEASERS"],
"rss_plain": self.site.config["RSS_PLAIN"],
"rss_link_append_query": self.site.config["RSS_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()

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(tag, 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))
output_name = output_name
context = {}
if has_authors:
context["title"] = kw["messages"][lang]["Authors"]
context["items"] = [(author, self.site.link("author", author, lang)) for author
in authors]
else:
context["items"] = None
context["permalink"] = self.site.link("author_index", None, lang)
context["description"] = context["title"]
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):
return author

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" """
"""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"]
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I added documentation for this new pagekind as well as all the other existing ones to docs/theming.txt.

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["rss_teasers"], kw["rss_plain"], kw['feed_length'],
feed_url, None, kw["rss_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):
"""Return path to the author index."""
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):
"""Return path to an author."""
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):
"""Return path to an author Atom feed."""
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):
"""Return path to an author RSS feed."""
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:
self.posts_per_author[post.author()].append(post)
return self.posts_per_author