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

Archive navigation #2599

Merged
merged 10 commits into from Dec 27, 2016
1 change: 1 addition & 0 deletions CHANGES.txt
Expand Up @@ -22,6 +22,7 @@ Bugfixes
Features
--------

* Allow creating archive navigation (Issue #1639)
* Accept a ``page`` argument for taxonomy paths (Issue #2585)
* Query strings in magic links are passed as keyword arguments to path
handlers (via Issue #2580)
Expand Down
2 changes: 2 additions & 0 deletions nikola/conf.py.in
Expand Up @@ -432,6 +432,8 @@ FRONT_INDEX_HEADER = {
# CREATE_FULL_ARCHIVES = False
# If monthly archives or full archives are created, adds also one archive per day
# CREATE_DAILY_ARCHIVE = False
# Create previous, up, next navigation links for archives
# CREATE_ARCHIVE_NAVIGATION = False
# Final locations for the archives are:
# output / TRANSLATION[lang] / ARCHIVE_PATH / ARCHIVE_FILENAME
# output / TRANSLATION[lang] / ARCHIVE_PATH / YEAR / index.html
Expand Down
@@ -0,0 +1,27 @@
{# -*- coding: utf-8 -*- #}

{% macro archive_navigation() %}
{% if 'archive_page' in pagekind %}
{% if has_archive_navigation %}
<nav class="archivenav">
<ul class="pager">
{% if previous_archive %}
<li class="previous"><a href="{{ previous_archive }}" rel="prev">{{ messages("Previous") }}</a></li>
{% else %}
<li class="previous disabled"><a href="#" rel="prev">{{ messages("Previous") }}</a></li>
{% endif %}
{% if up_archive %}
<li class="up"><a href="{{ up_archive }}" rel="up">{{ messages("Up") }}</a></li>
{% else %}
<li class="up disabled"><a href="#" rel="up">{{ messages("Up") }}</a></li>
{% endif %}
{% if next_archive %}
<li class="next"><a href="{{ next_archive }}" rel="next">{{ messages("Next") }}</a></li>
{% else %}
<li class="next disabled"><a href="#" rel="next">{{ messages("Next") }}</a></li>
{% endif %}
</ul>
</nav>
{% endif %}
{% endif %}
{% endmacro %}
7 changes: 6 additions & 1 deletion nikola/data/themes/base-jinja/templates/archiveindex.tmpl
@@ -1,13 +1,18 @@
{# -*- coding: utf-8 -*- #}
{% extends 'index.tmpl' %}
{% import 'archive_navigation_helper.tmpl' as archive_nav with context %}

{% block extra_head %}
{{ super() }}
{% if translations|length > 1 and generate_atom %}
{% for language in translations|sort %}
<link rel="alternate" type="application/atom+xml" title="Atom for the {{ archive_name }} section ({{ language }})" href="{{ _link("archive_atom", archive_name, language) }}">
<link rel="alternate" type="application/atom+xml" title="Atom for the {{ archive_name }} archive ({{ language }})" href="{{ _link("archive_atom", archive_name, language) }}">
{% endfor %}
{% elif generate_atom %}
<link rel="alternate" type="application/atom+xml" title="Atom for the {{ archive_name }} archive" href="{{ _link("archive_atom", archive_name) }}">
{% endif %}
{% endblock %}

{% block content_header %}
{{ archive_nav.archive_navigation() }}
{% endblock %}
2 changes: 2 additions & 0 deletions nikola/data/themes/base-jinja/templates/list.tmpl
@@ -1,11 +1,13 @@
{# -*- coding: utf-8 -*- #}
{% extends 'base.tmpl' %}
{% import 'archive_navigation_helper.tmpl' as archive_nav with context %}

{% block content %}
<article class="listpage">
<header>
<h1>{{ title|e }}</h1>
</header>
{{ archive_nav.archive_navigation() }}
{% if items %}
<ul class="postlist">
{% for text, link, count in items %}
Expand Down
2 changes: 2 additions & 0 deletions nikola/data/themes/base-jinja/templates/list_post.tmpl
@@ -1,11 +1,13 @@
{# -*- coding: utf-8 -*- #}
{% extends 'base.tmpl' %}
{% import 'archive_navigation_helper.tmpl' as archive_nav with context %}

{% block content %}
<article class="listpage">
<header>
<h1>{{ title|e }}</h1>
</header>
{{ archive_nav.archive_navigation() }}
{% if posts %}
<ul class="postlist">
{% for post in posts %}
Expand Down
40 changes: 30 additions & 10 deletions nikola/data/themes/base/assets/css/theme.css
Expand Up @@ -131,19 +131,24 @@ body {
.postindexpager .pager .next:before {
content: "↓ ";
}
.postindexpager .pager .previous:before {
.postindexpager .pager .previous:before,
.archivenav .pager .up:before {
content: "↑ ";
}
.postpromonav .pager .next:after {
.postpromonav .pager .next:after,
.archivenav .pager .next:after {
content: " →";
}
.postpromonav .pager .previous:dir(rtl):after {
.postpromonav .pager .previous:dir(rtl):after,
.archivenav .pager .previous:dir(rtl):after {
content: " →";
}
.postpromonav .pager .previous:before {
.postpromonav .pager .previous:before,
.archivenav .pager .previous:before {
content: "← ";
}
.postpromonav .pager .next:dir(rtl):before {
.postpromonav .pager .next:dir(rtl):before,
.archivenav .pager .next:dir(rtl):before {
content: "← ";
}

Expand All @@ -156,23 +161,38 @@ body {
height: 1em;
}
.postpromonav .tags li,
.postpromonav .pager li {
.postpromonav .pager li,
.archivenav .pager li {
display: inline-block;
}
.postpromonav .pager .next {
.archivenav .pager {
text-align: center
}
.postpromonav .pager .next,
.archivenav .pager .next {
float: right;
}
.postpromonav .pager .next:dir(rtl) {
.postpromonav .pager .next:dir(rtl),
.archivenav .pager .next:dir(rtl) {
float: left;
}

.postpromonav .pager .previous {
.postpromonav .pager .previous,
.archivenav .pager .previous {
float: left;
}
.postpromonav .pager .previous:dir(rtl) {
.postpromonav .pager .previous:dir(rtl),
.archivenav .pager .previous:dir(rtl) {
float: right;
}

.archivenav .pager .disabled,
.archivenav .pager .disabled a,
.archivenav .pager .disabled:link {
color: #888;
cursor: not-allowed;
}

.metadata p {
display: inline;
}
Expand Down
3 changes: 3 additions & 0 deletions nikola/data/themes/base/messages/messages_en.py
Expand Up @@ -14,6 +14,7 @@
"More posts about %s": "More posts about %s",
"Newer posts": "Newer posts",
"Next post": "Next post",
"Next": "Next",
"No posts found.": "No posts found.",
"Nothing found.": "Nothing found.",
"Older posts": "Older posts",
Expand All @@ -25,6 +26,7 @@
"Posts for {month} {day}, {year}": "Posts for {month} {day}, {year}",
"Posts for {month} {year}": "Posts for {month} {year}",
"Previous post": "Previous post",
"Previous": "Previous",
"Publication date": "Publication date",
"RSS feed": "RSS feed",
"Read in English": "Read in English",
Expand All @@ -36,6 +38,7 @@
"Tags": "Tags",
"Toggle navigation": "Toggle navigation",
"Uncategorized": "Uncategorized",
"Up": "Up",
"Updates": "Updates",
"Write your page here.": "Write your page here.",
"Write your post here.": "Write your post here.",
Expand Down
27 changes: 27 additions & 0 deletions nikola/data/themes/base/templates/archive_navigation_helper.tmpl
@@ -0,0 +1,27 @@
## -*- coding: utf-8 -*-

<%def name="archive_navigation()">
%if 'archive_page' in pagekind:
%if has_archive_navigation:
<nav class="archivenav">
<ul class="pager">
%if previous_archive:
<li class="previous"><a href="${previous_archive}" rel="prev">${messages("Previous")}</a></li>
%else:
<li class="previous disabled"><a href="#" rel="prev">${messages("Previous")}</a></li>
% endif
%if up_archive:
<li class="up"><a href="${up_archive}" rel="up">${messages("Up")}</a></li>
%else:
<li class="up disabled"><a href="#" rel="up">${messages("Up")}</a></li>
%endif
%if next_archive:
<li class="next"><a href="${next_archive}" rel="next">${messages("Next")}</a></li>
%else:
<li class="next disabled"><a href="#" rel="next">${messages("Next")}</a></li>
%endif
</ul>
</nav>
%endif
% endif
</%def>
7 changes: 6 additions & 1 deletion nikola/data/themes/base/templates/archiveindex.tmpl
@@ -1,13 +1,18 @@
## -*- coding: utf-8 -*-
<%inherit file="index.tmpl"/>
<%namespace name="archive_nav" file="archive_navigation_helper.tmpl" import="*"/>

<%block name="extra_head">
${parent.extra_head()}
%if len(translations) > 1 and generate_atom:
%for language in sorted(translations):
<link rel="alternate" type="application/atom+xml" title="Atom for the ${archive_name} section (${language})" href="${_link("archive_atom", archive_name, language)}">
<link rel="alternate" type="application/atom+xml" title="Atom for the ${archive_name} archive (${language})" href="${_link("archive_atom", archive_name, language)}">
%endfor
%elif generate_atom:
<link rel="alternate" type="application/atom+xml" title="Atom for the ${archive_name} archive" href="${_link("archive_atom", archive_name)}">
%endif
</%block>

<%block name="content_header">
${archive_nav.archive_navigation()}
</%block>
2 changes: 2 additions & 0 deletions nikola/data/themes/base/templates/list.tmpl
@@ -1,11 +1,13 @@
## -*- coding: utf-8 -*-
<%inherit file="base.tmpl"/>
<%namespace name="archive_nav" file="archive_navigation_helper.tmpl" import="*"/>

<%block name="content">
<article class="listpage">
<header>
<h1>${title|h}</h1>
</header>
${archive_nav.archive_navigation()}
%if items:
<ul class="postlist">
% for text, link, count in items:
Expand Down
2 changes: 2 additions & 0 deletions nikola/data/themes/base/templates/list_post.tmpl
@@ -1,11 +1,13 @@
## -*- coding: utf-8 -*-
<%inherit file="base.tmpl"/>
<%namespace name="archive_nav" file="archive_navigation_helper.tmpl" import="*"/>

<%block name="content">
<article class="listpage">
<header>
<h1>${title|h}</h1>
</header>
${archive_nav.archive_navigation()}
%if posts:
<ul class="postlist">
% for post in posts:
Expand Down
1 change: 1 addition & 0 deletions nikola/nikola.py
Expand Up @@ -473,6 +473,7 @@ def __init__(self, **config):
'CONTENT_FOOTER': '',
'CONTENT_FOOTER_FORMATS': {},
'COPY_SOURCES': True,
'CREATE_ARCHIVE_NAVIGATION': False,
'CREATE_MONTHLY_ARCHIVE': False,
'CREATE_SINGLE_ARCHIVE': False,
'CREATE_FULL_ARCHIVES': False,
Expand Down
52 changes: 51 additions & 1 deletion nikola/plugins/task/archive.py
Expand Up @@ -27,8 +27,10 @@
"""Classify the posts in archives."""

import os
import natsort
import nikola.utils
import datetime
from collections import defaultdict
from nikola.plugin_categories import Taxonomy


Expand Down Expand Up @@ -93,7 +95,7 @@ def get_classification_friendly_name(self, classification, lang, only_last_compo
"""Extract a friendly name from the classification."""
classification = self.extract_hierarchy(classification)
if len(classification) == 0:
return ""
return self.site.MESSAGES[lang]['Archive']
elif len(classification) == 1:
return classification[0]
elif len(classification) == 2:
Expand Down Expand Up @@ -160,16 +162,64 @@ def provide_context_and_uptodate(self, classification, lang, node=None):
kw["is_feed_stale"] = (datetime.datetime.utcnow().strftime("%Y/%m/%d") != classification)
else:
raise Exception("Cannot interpret classification {}!".format(repr(classification)))

context = {
"title": title,
"pagekind": [page_kind, "archive_page"],
"create_archive_navigation": self.site.config["CREATE_ARCHIVE_NAVIGATION"],
}

# Generate links for hierarchies
if context["create_archive_navigation"]:
if hierarchy:
# Up level link makes sense only if this is not the top-level
# page (hierarchy is empty)
parent = '/'.join(hierarchy[:-1])
context["up_archive"] = self.site.link('archive', parent, lang)
context["up_archive_name"] = self.get_classification_friendly_name(parent, lang)
else:
context["up_archive"] = None
context["up_archive_name"] = None

nodelevel = len(hierarchy)
Copy link
Contributor

Choose a reason for hiding this comment

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

You should also insert nodelevel into the context so that link texts (next year/month/day) can be chosen appropriately without unnecessary complicated code (like manually counting the number of slashes in the classification etc.).

flat_samelevel = self.archive_navigation[lang][nodelevel]
idx = flat_samelevel.index(classification)
if idx == -1:
raise Exception("Cannot find classification {0} in flat hierarchy!".format(classification))
previdx, nextidx = idx - 1, idx + 1
# If the previous index is -1, or the next index is 1, the previous/next archive does not exist.
context["previous_archive"] = self.site.link('archive', flat_samelevel[previdx], lang) if previdx != -1 else None
context["previous_archive_name"] = self.get_classification_friendly_name(flat_samelevel[previdx], lang) if previdx != -1 else None
context["next_archive"] = self.site.link('archive', flat_samelevel[nextidx], lang) if nextidx != len(flat_samelevel) else None
context["next_archive_name"] = self.get_classification_friendly_name(flat_samelevel[nextidx], lang) if nextidx != len(flat_samelevel) else None
context["archive_nodelevel"] = nodelevel
context["has_archive_navigation"] = bool(context["previous_archive"] or context["up_archive"] or context["next_archive"])
else:
context["has_archive_navigation"] = False
if page_kind == 'index':
context["archive_name"] = classification if classification else None
context["is_feed_stale"] = kw["is_feed_stale"]
kw.update(context)
return context, kw

def postprocess_posts_per_classification(self, posts_per_section_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."""
# Build a lookup table for archive navigation, if we’ll need one.
if self.site.config['CREATE_ARCHIVE_NAVIGATION']:
if flat_hierarchy_per_lang is None:
raise ValueError('Archives need flat_hierarchy_per_lang')
self.archive_navigation = {}
for lang, flat_hierarchy in flat_hierarchy_per_lang.items():
self.archive_navigation[lang] = defaultdict(list)
for node in flat_hierarchy:
self.archive_navigation[lang][len(node.classification_path)].append(node.classification_name)

# We need to sort it. Natsort means it’s year 10000 compatible!
for k, v in self.archive_navigation[lang].items():
self.archive_navigation[lang][k] = natsort.natsorted(v, alg=natsort.ns.F | natsort.ns.IC)

return super(Archive, self).postprocess_posts_per_classification(posts_per_section_per_language, flat_hierarchy_per_lang, hierarchy_lookup_per_lang)

def should_generate_classification_page(self, classification, post_list, lang):
"""Only generates list of posts for classification if this function returns True."""
return len(classification.split('/')) < 3 or len(post_list) > 0
5 changes: 4 additions & 1 deletion scripts/import_po.py
Expand Up @@ -9,8 +9,11 @@
import sys
import polib

if 'nopull' not in sys.argv:
if 'nopull' not in sys.argv or '--nopull' in sys.argv:
os.system("tx pull -a")
elif '-h' in sys.argv or '--help' in sys.argv:
print("Internal use only. Takes optional 'nopull' argument to prevent pulling from Transifex.")
exit()

trans_files = glob(os.path.join('translations', 'nikola.messages', '*.po'))
for fname in trans_files:
Expand Down
9 changes: 9 additions & 0 deletions translations/nikola.messages/en.po
Expand Up @@ -154,3 +154,12 @@ msgstr "{month} {year}"
#. Used to format a date
msgid "{month} {day}, {year}"
msgstr "{month} {day}, {year}"

msgid "Previous"
msgstr "Previous"

msgid "Up"
msgstr "Up"

msgid "Next"
msgstr "Next"