aboutsummaryrefslogtreecommitdiffstats
path: root/nikola/plugins/task
diff options
context:
space:
mode:
authorLibravatarDererk <dererk@debian.org>2015-11-11 16:34:34 -0300
committerLibravatarDererk <dererk@debian.org>2015-11-11 16:34:34 -0300
commit4e3224c012df9f74f010eb92203520515e8537b9 (patch)
tree19322dc0c595268cb6864f21d7e92fd93cb826e9 /nikola/plugins/task
parent787b97a4cb24330b36f11297c6d3a7a473a907d0 (diff)
Imported Upstream version 7.7.3upstream/7.7.3
Diffstat (limited to 'nikola/plugins/task')
-rw-r--r--nikola/plugins/task/archive.plugin2
-rw-r--r--nikola/plugins/task/archive.py23
-rw-r--r--nikola/plugins/task/authors.plugin10
-rw-r--r--nikola/plugins/task/authors.py316
-rw-r--r--nikola/plugins/task/bundles.plugin2
-rw-r--r--nikola/plugins/task/bundles.py8
-rw-r--r--nikola/plugins/task/copy_assets.plugin2
-rw-r--r--nikola/plugins/task/copy_assets.py10
-rw-r--r--nikola/plugins/task/copy_files.plugin2
-rw-r--r--nikola/plugins/task/copy_files.py1
-rw-r--r--nikola/plugins/task/galleries.plugin2
-rw-r--r--nikola/plugins/task/galleries.py43
-rw-r--r--nikola/plugins/task/gzip.plugin2
-rw-r--r--nikola/plugins/task/gzip.py1
-rw-r--r--nikola/plugins/task/indexes.plugin2
-rw-r--r--nikola/plugins/task/indexes.py158
-rw-r--r--nikola/plugins/task/listings.plugin2
-rw-r--r--nikola/plugins/task/listings.py21
-rw-r--r--nikola/plugins/task/pages.plugin2
-rw-r--r--nikola/plugins/task/pages.py1
-rw-r--r--nikola/plugins/task/posts.plugin2
-rw-r--r--nikola/plugins/task/posts.py5
-rw-r--r--nikola/plugins/task/py3_switch.plugin13
-rw-r--r--nikola/plugins/task/py3_switch.py103
-rw-r--r--nikola/plugins/task/redirect.plugin2
-rw-r--r--nikola/plugins/task/redirect.py1
-rw-r--r--nikola/plugins/task/robots.plugin2
-rw-r--r--nikola/plugins/task/robots.py8
-rw-r--r--nikola/plugins/task/rss.plugin2
-rw-r--r--nikola/plugins/task/rss.py21
-rw-r--r--nikola/plugins/task/scale_images.plugin2
-rw-r--r--nikola/plugins/task/scale_images.py1
-rw-r--r--nikola/plugins/task/sitemap.plugin2
-rw-r--r--nikola/plugins/task/sitemap/__init__.py27
-rw-r--r--nikola/plugins/task/sources.plugin2
-rw-r--r--nikola/plugins/task/sources.py1
-rw-r--r--nikola/plugins/task/tags.plugin2
-rw-r--r--nikola/plugins/task/tags.py136
38 files changed, 821 insertions, 121 deletions
diff --git a/nikola/plugins/task/archive.plugin b/nikola/plugins/task/archive.plugin
index 25f1195..eb079da 100644
--- a/nikola/plugins/task/archive.plugin
+++ b/nikola/plugins/task/archive.plugin
@@ -5,7 +5,7 @@ module = archive
[Documentation]
author = Roberto Alsina
version = 1.0
-website = http://getnikola.com
+website = https://getnikola.com/
description = Generates the blog's archive pages.
[Nikola]
diff --git a/nikola/plugins/task/archive.py b/nikola/plugins/task/archive.py
index 126aed4..3cdd33b 100644
--- a/nikola/plugins/task/archive.py
+++ b/nikola/plugins/task/archive.py
@@ -37,7 +37,6 @@ from nikola.utils import config_changed, adjust_name_for_index_path, adjust_name
class Archive(Task):
-
"""Render the post archives."""
name = "render_archive"
@@ -53,7 +52,7 @@ class Archive(Task):
"""Prepare an archive task."""
# name: used to build permalink and destination
# posts, items: posts or items; only one of them should be used,
- # the other be None
+ # the other should be None
# template_name: name of the template to use
# title: the (translated) title for the generated page
# deps_translatable: dependencies (None if not added)
@@ -175,10 +174,10 @@ class Archive(Task):
if not kw["create_monthly_archive"] or kw["create_full_archives"]:
yield self._generate_posts_task(kw, year, lang, posts, title, deps_translatable)
else:
- months = set([(m.split('/')[1], self.site.link("archive", m, lang)) for m in self.site.posts_per_month.keys() if m.startswith(str(year))])
+ months = set([(m.split('/')[1], self.site.link("archive", m, lang), len(self.site.posts_per_month[m])) for m in self.site.posts_per_month.keys() if m.startswith(str(year))])
months = sorted(list(months))
months.reverse()
- items = [[nikola.utils.LocaleBorg().get_month_name(int(month), lang), link] for month, link in months]
+ items = [[nikola.utils.LocaleBorg().get_month_name(int(month), lang), link, count] for month, link, count in months]
yield self._prepare_task(kw, year, lang, None, items, "list.tmpl", title, deps_translatable)
if not kw["create_monthly_archive"] and not kw["create_full_archives"] and not kw["create_daily_archive"]:
@@ -219,11 +218,16 @@ class Archive(Task):
years.sort(reverse=True)
kw['years'] = years
for lang in kw["translations"]:
- items = [(y, self.site.link("archive", y, lang)) for y in years]
+ items = [(y, self.site.link("archive", y, lang), len(self.site.posts_per_year[y])) for y in years]
yield self._prepare_task(kw, None, lang, None, items, "list.tmpl", kw["messages"][lang]["Archive"])
def archive_path(self, name, lang, is_feed=False):
- """Return archive paths."""
+ """Link to archive path, name is the year.
+
+ Example:
+
+ link://archive/2013 => /archives/2013/index.html
+ """
if is_feed:
extension = ".atom"
archive_file = os.path.splitext(self.site.config['ARCHIVE_FILENAME'])[0] + extension
@@ -241,5 +245,10 @@ class Archive(Task):
archive_file] if _f]
def archive_atom_path(self, name, lang):
- """Return Atom archive paths."""
+ """Link to atom archive path, name is the year.
+
+ Example:
+
+ link://archive_atom/2013 => /archives/2013/index.atom
+ """
return self.archive_path(name, lang, is_feed=True)
diff --git a/nikola/plugins/task/authors.plugin b/nikola/plugins/task/authors.plugin
new file mode 100644
index 0000000..3fc4ef2
--- /dev/null
+++ b/nikola/plugins/task/authors.plugin
@@ -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.
+
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
diff --git a/nikola/plugins/task/bundles.plugin b/nikola/plugins/task/bundles.plugin
index ca997d0..b5bf6e4 100644
--- a/nikola/plugins/task/bundles.plugin
+++ b/nikola/plugins/task/bundles.plugin
@@ -5,7 +5,7 @@ module = bundles
[Documentation]
author = Roberto Alsina
version = 1.0
-website = http://getnikola.com
+website = https://getnikola.com/
description = Theme bundles using WebAssets
[Nikola]
diff --git a/nikola/plugins/task/bundles.py b/nikola/plugins/task/bundles.py
index b9c57b9..e709133 100644
--- a/nikola/plugins/task/bundles.py
+++ b/nikola/plugins/task/bundles.py
@@ -40,7 +40,6 @@ from nikola import utils
class BuildBundles(LateTask):
-
"""Bundle assets using WebAssets."""
name = "create_bundles"
@@ -52,6 +51,7 @@ class BuildBundles(LateTask):
utils.req_missing(['webassets'], 'USE_BUNDLES', optional=True)
self.logger.warn('Setting USE_BUNDLES to False.')
site.config['USE_BUNDLES'] = False
+ site._GLOBAL_CONTEXT['use_bundles'] = False
super(BuildBundles, self).set_site(site)
def gen_tasks(self):
@@ -100,7 +100,11 @@ class BuildBundles(LateTask):
files.append(os.path.join(dname, fname))
file_dep = [os.path.join(kw['output_folder'], fname)
for fname in files if
- utils.get_asset_path(fname, self.site.THEMES, self.site.config['FILES_FOLDERS']) or fname == os.path.join('assets', 'css', 'code.css')]
+ utils.get_asset_path(
+ fname,
+ self.site.THEMES,
+ self.site.config['FILES_FOLDERS'],
+ output_dir=kw['output_folder']) or fname == os.path.join('assets', 'css', 'code.css')]
# code.css will be generated by us if it does not exist in
# FILES_FOLDERS or theme assets. It is guaranteed that the
# generation will happen before this task.
diff --git a/nikola/plugins/task/copy_assets.plugin b/nikola/plugins/task/copy_assets.plugin
index c182150..ddd38df 100644
--- a/nikola/plugins/task/copy_assets.plugin
+++ b/nikola/plugins/task/copy_assets.plugin
@@ -5,7 +5,7 @@ module = copy_assets
[Documentation]
author = Roberto Alsina
version = 1.0
-website = http://getnikola.com
+website = https://getnikola.com/
description = Copy theme assets into output.
[Nikola]
diff --git a/nikola/plugins/task/copy_assets.py b/nikola/plugins/task/copy_assets.py
index 58521d4..2cab71a 100644
--- a/nikola/plugins/task/copy_assets.py
+++ b/nikola/plugins/task/copy_assets.py
@@ -36,7 +36,6 @@ from nikola import utils
class CopyAssets(Task):
-
"""Copy theme assets into output."""
name = "copy_assets"
@@ -61,10 +60,7 @@ class CopyAssets(Task):
code_css_path = os.path.join(kw['output_folder'], 'assets', 'css', 'code.css')
code_css_input = utils.get_asset_path('assets/css/code.css',
themes=kw['themes'],
- files_folders=kw['files_folders'])
-
- kw["code.css_input"] = code_css_input
-
+ files_folders=kw['files_folders'], output_dir=None)
yield self.group_task()
for theme_name in kw['themes']:
@@ -77,7 +73,9 @@ class CopyAssets(Task):
task['uptodate'] = [utils.config_changed(kw, 'nikola.plugins.task.copy_assets')]
task['basename'] = self.name
if code_css_input:
- task['file_dep'] = [code_css_input]
+ if 'file_dep' not in task:
+ task['file_dep'] = []
+ task['file_dep'].append(code_css_input)
yield utils.apply_filters(task, kw['filters'])
# Check whether or not there is a code.css file around.
diff --git a/nikola/plugins/task/copy_files.plugin b/nikola/plugins/task/copy_files.plugin
index ce8f5d0..e4bb1cf 100644
--- a/nikola/plugins/task/copy_files.plugin
+++ b/nikola/plugins/task/copy_files.plugin
@@ -5,7 +5,7 @@ module = copy_files
[Documentation]
author = Roberto Alsina
version = 1.0
-website = http://getnikola.com
+website = https://getnikola.com/
description = Copy static files into the output.
[Nikola]
diff --git a/nikola/plugins/task/copy_files.py b/nikola/plugins/task/copy_files.py
index 1232248..0488011 100644
--- a/nikola/plugins/task/copy_files.py
+++ b/nikola/plugins/task/copy_files.py
@@ -33,7 +33,6 @@ from nikola import utils
class CopyFiles(Task):
-
"""Copy static files into the output folder."""
name = "copy_files"
diff --git a/nikola/plugins/task/galleries.plugin b/nikola/plugins/task/galleries.plugin
index 9d3fa28..2064e68 100644
--- a/nikola/plugins/task/galleries.plugin
+++ b/nikola/plugins/task/galleries.plugin
@@ -5,7 +5,7 @@ module = galleries
[Documentation]
author = Roberto Alsina
version = 1.0
-website = http://getnikola.com
+website = https://getnikola.com/
description = Create image galleries automatically.
[Nikola]
diff --git a/nikola/plugins/task/galleries.py b/nikola/plugins/task/galleries.py
index c0df4a4..d3f1db7 100644
--- a/nikola/plugins/task/galleries.py
+++ b/nikola/plugins/task/galleries.py
@@ -57,7 +57,6 @@ _image_size_cache = {}
class Galleries(Task, ImageProcessor):
-
"""Render image galleries."""
name = 'render_galleries'
@@ -122,20 +121,45 @@ class Galleries(Task, ImageProcessor):
sys.exit(1)
def gallery_path(self, name, lang):
- """Return a gallery path."""
+ """Link to an image gallery's path.
+
+ It will try to find a gallery with that name if it's not ambiguous
+ or with that path. For example:
+
+ link://gallery/london => /galleries/trips/london/index.html
+
+ link://gallery/trips/london => /galleries/trips/london/index.html
+ """
gallery_path = self._find_gallery_path(name)
return [_f for _f in [self.site.config['TRANSLATIONS'][lang]] +
gallery_path.split(os.sep) +
[self.site.config['INDEX_FILE']] if _f]
def gallery_global_path(self, name, lang):
- """Return the global gallery path, which contains images."""
+ """Link to the global gallery path, which contains all the images in galleries.
+
+ There is only one copy of an image on multilingual blogs, in the site root.
+
+ link://gallery_global/london => /galleries/trips/london/index.html
+
+ link://gallery_global/trips/london => /galleries/trips/london/index.html
+
+ (a ``gallery`` link could lead to eg. /en/galleries/trips/london/index.html)
+ """
gallery_path = self._find_gallery_path(name)
return [_f for _f in gallery_path.split(os.sep) +
[self.site.config['INDEX_FILE']] if _f]
def gallery_rss_path(self, name, lang):
- """Return path to the RSS file for a gallery."""
+ """Link to an image gallery's RSS feed.
+
+ It will try to find a gallery with that name if it's not ambiguous
+ or with that path. For example:
+
+ link://gallery_rss/london => /galleries/trips/london/rss.xml
+
+ link://gallery_rss/trips/london => /galleries/trips/london/rss.xml
+ """
gallery_path = self._find_gallery_path(name)
return [_f for _f in [self.site.config['TRANSLATIONS'][lang]] +
gallery_path.split(os.sep) +
@@ -538,9 +562,12 @@ class Galleries(Task, ImageProcessor):
for img, thumb, title in zip(img_list, thumbs, img_titles):
w, h = _image_size_cache.get(thumb, (None, None))
if w is None:
- im = Image.open(thumb)
- w, h = im.size
- _image_size_cache[thumb] = w, h
+ if os.path.splitext(thumb)[1] in ['.svg', '.svgz']:
+ w, h = 200, 200
+ else:
+ im = Image.open(thumb)
+ w, h = im.size
+ _image_size_cache[thumb] = w, h
# Thumbs are files in output, we need URLs
photo_array.append({
'url': url_from_path(img),
@@ -587,7 +614,7 @@ class Galleries(Task, ImageProcessor):
description='',
lastBuildDate=datetime.datetime.utcnow(),
items=items,
- generator='http://getnikola.com/',
+ generator='https://getnikola.com/',
language=lang
)
diff --git a/nikola/plugins/task/gzip.plugin b/nikola/plugins/task/gzip.plugin
index 7834d22..d3a34ee 100644
--- a/nikola/plugins/task/gzip.plugin
+++ b/nikola/plugins/task/gzip.plugin
@@ -5,7 +5,7 @@ module = gzip
[Documentation]
author = Roberto Alsina
version = 1.0
-website = http://getnikola.com
+website = https://getnikola.com/
description = Create gzipped copies of files
[Nikola]
diff --git a/nikola/plugins/task/gzip.py b/nikola/plugins/task/gzip.py
index cf16f63..aaa213d 100644
--- a/nikola/plugins/task/gzip.py
+++ b/nikola/plugins/task/gzip.py
@@ -35,7 +35,6 @@ from nikola.plugin_categories import TaskMultiplier
class GzipFiles(TaskMultiplier):
-
"""If appropiate, create tasks to create gzipped versions of files."""
name = "gzip"
diff --git a/nikola/plugins/task/indexes.plugin b/nikola/plugins/task/indexes.plugin
index d9b0e5f..553b5ad 100644
--- a/nikola/plugins/task/indexes.plugin
+++ b/nikola/plugins/task/indexes.plugin
@@ -5,7 +5,7 @@ module = indexes
[Documentation]
author = Roberto Alsina
version = 1.0
-website = http://getnikola.com
+website = https://getnikola.com/
description = Generates the blog's index pages.
[Nikola]
diff --git a/nikola/plugins/task/indexes.py b/nikola/plugins/task/indexes.py
index c02818e..2ab97fa 100644
--- a/nikola/plugins/task/indexes.py
+++ b/nikola/plugins/task/indexes.py
@@ -29,23 +29,45 @@
from __future__ import unicode_literals
from collections import defaultdict
import os
+try:
+ from urlparse import urljoin
+except ImportError:
+ from urllib.parse import urljoin # NOQA
from nikola.plugin_categories import Task
from nikola import utils
class Indexes(Task):
-
"""Render the blog indexes."""
name = "render_indexes"
def set_site(self, site):
"""Set Nikola site."""
+ self.number_of_pages = dict()
+ self.number_of_pages_section = {lang: dict() for lang in site.config['TRANSLATIONS']}
site.register_path_handler('index', self.index_path)
site.register_path_handler('index_atom', self.index_atom_path)
+ site.register_path_handler('section_index', self.index_section_path)
+ site.register_path_handler('section_index_atom', self.index_section_atom_path)
return super(Indexes, self).set_site(site)
+ def _get_filtered_posts(self, lang, show_untranslated_posts):
+ """Return a filtered list of all posts for the given language.
+
+ If show_untranslated_posts is True, will only include posts which
+ are translated to the given language. Otherwise, returns all posts.
+ """
+ if show_untranslated_posts:
+ return self.site.posts
+ else:
+ return [x for x in self.site.posts if x.is_translation_available(lang)]
+
+ def _compute_number_of_pages(self, filtered_posts, posts_count):
+ """Given a list of posts and the maximal number of posts per page, computes the number of pages needed."""
+ return min(1, (len(filtered_posts) + posts_count - 1) // posts_count)
+
def gen_tasks(self):
"""Render the blog indexes."""
self.site.scan_posts()
@@ -56,16 +78,16 @@ class Indexes(Task):
"messages": self.site.MESSAGES,
"output_folder": self.site.config['OUTPUT_FOLDER'],
"filters": self.site.config['FILTERS'],
+ "index_file": self.site.config['INDEX_FILE'],
"show_untranslated_posts": self.site.config['SHOW_UNTRANSLATED_POSTS'],
"index_display_post_count": self.site.config['INDEX_DISPLAY_POST_COUNT'],
"indexes_title": self.site.config['INDEXES_TITLE'],
+ "strip_indexes": self.site.config['STRIP_INDEXES'],
"blog_title": self.site.config["BLOG_TITLE"],
"generate_atom": self.site.config["GENERATE_ATOM"],
}
template_name = "index.tmpl"
- posts = self.site.posts
- self.number_of_pages = dict()
for lang in kw["translations"]:
def page_link(i, displayed_i, num_pages, force_addition, extension=None):
feed = "_atom" if extension == ".atom" else ""
@@ -77,19 +99,75 @@ class Indexes(Task):
return utils.adjust_name_for_index_path(self.site.path("index" + feed, None, lang), i, displayed_i,
lang, self.site, force_addition, extension)
- if kw["show_untranslated_posts"]:
- filtered_posts = posts
- else:
- filtered_posts = [x for x in posts if x.is_translation_available(lang)]
+ filtered_posts = self._get_filtered_posts(lang, kw["show_untranslated_posts"])
indexes_title = kw['indexes_title'](lang) or kw['blog_title'](lang)
- self.number_of_pages[lang] = (len(filtered_posts) + kw['index_display_post_count'] - 1) // kw['index_display_post_count']
+ self.number_of_pages[lang] = self._compute_number_of_pages(filtered_posts, kw['index_display_post_count'])
context = {}
- context["pagekind"] = ["index"]
+ context["pagekind"] = ["main_index", "index"]
yield self.site.generic_index_renderer(lang, filtered_posts, indexes_title, template_name, context, kw, 'render_indexes', page_link, page_path)
+ if self.site.config['POSTS_SECTIONS']:
+
+ kw["posts_section_are_indexes"] = self.site.config['POSTS_SECTION_ARE_INDEXES']
+ index_len = len(kw['index_file'])
+
+ groups = defaultdict(list)
+ for p in filtered_posts:
+ groups[p.section_slug(lang)].append(p)
+
+ # don't build sections when there is only one, aka. default setups
+ if not len(groups.items()) > 1:
+ continue
+
+ for section_slug, post_list in groups.items():
+ self.number_of_pages_section[lang][section_slug] = self._compute_number_of_pages(post_list, kw['index_display_post_count'])
+
+ def cat_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("section_index" + feed, section_slug, lang), i, displayed_i,
+ lang, self.site, force_addition, extension)
+
+ def cat_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("section_index" + feed, section_slug, lang), i, displayed_i,
+ lang, self.site, force_addition, extension)
+
+ context = {}
+
+ short_destination = os.path.join(section_slug, kw['index_file'])
+ link = short_destination.replace('\\', '/')
+ if kw['strip_indexes'] and link[-(1 + index_len):] == '/' + kw['index_file']:
+ link = link[:-index_len]
+ context["permalink"] = link
+ context["pagekind"] = ["section_page"]
+ context["description"] = self.site.config['POSTS_SECTION_DESCRIPTIONS'](lang)[section_slug] if section_slug in self.site.config['POSTS_SECTION_DESCRIPTIONS'](lang) else ""
+
+ if kw["posts_section_are_indexes"]:
+ context["pagekind"].append("index")
+ kw["posts_section_title"] = self.site.config['POSTS_SECTION_TITLE'](lang)
+
+ section_title = None
+ if type(kw["posts_section_title"]) is dict:
+ if section_slug in kw["posts_section_title"]:
+ section_title = kw["posts_section_title"][section_slug]
+ elif type(kw["posts_section_title"]) is str:
+ section_title = kw["posts_section_title"]
+ if not section_title:
+ section_title = post_list[0].section_name(lang)
+ section_title = section_title.format(name=post_list[0].section_name(lang))
+
+ task = self.site.generic_index_renderer(lang, post_list, section_title, "sectionindex.tmpl", context, kw, self.name, cat_link, cat_path)
+ else:
+ context["pagekind"].append("list")
+ output_name = os.path.join(kw['output_folder'], section_slug, kw['index_file'])
+ task = self.site.generic_post_list_renderer(lang, post_list, output_name, "list.tmpl", kw['filters'], context)
+ task['uptodate'] = [utils.config_changed(kw, 'nikola.plugins.task.indexes')]
+ task['basename'] = self.name
+ yield task
+
if not self.site.config["STORY_INDEX"]:
return
kw = {
@@ -134,7 +212,8 @@ class Indexes(Task):
should_render = False
else:
context["items"].append((post.title(lang),
- post.permalink(lang)))
+ post.permalink(lang),
+ None))
if should_render:
task = self.site.generic_post_list_renderer(lang, post_list,
@@ -147,22 +226,75 @@ class Indexes(Task):
yield task
def index_path(self, name, lang, is_feed=False):
- """Return path to an index."""
+ """Link to a numbered index.
+
+ Example:
+
+ link://index/3 => /index-3.html
+ """
extension = None
if is_feed:
extension = ".atom"
index_file = os.path.splitext(self.site.config['INDEX_FILE'])[0] + extension
else:
index_file = self.site.config['INDEX_FILE']
+ if lang in self.number_of_pages:
+ number_of_pages = self.number_of_pages[lang]
+ else:
+ number_of_pages = self._compute_number_of_pages(self._get_filtered_posts(lang, self.site.config['SHOW_UNTRANSLATED_POSTS']), self.site.config['INDEX_DISPLAY_POST_COUNT'])
+ self.number_of_pages[lang] = number_of_pages
return utils.adjust_name_for_index_path_list([_f for _f in [self.site.config['TRANSLATIONS'][lang],
self.site.config['INDEX_PATH'],
index_file] if _f],
name,
- utils.get_displayed_page_number(name, self.number_of_pages[lang], self.site),
+ utils.get_displayed_page_number(name, number_of_pages, self.site),
+ lang,
+ self.site,
+ extension=extension)
+
+ def index_section_path(self, name, lang, is_feed=False):
+ """Link to the index for a section.
+
+ Example:
+
+ link://section_index/cars => /cars/index.html
+ """
+ extension = None
+
+ if is_feed:
+ extension = ".atom"
+ index_file = os.path.splitext(self.site.config['INDEX_FILE'])[0] + extension
+ else:
+ index_file = self.site.config['INDEX_FILE']
+ if name in self.number_of_pages_section[lang]:
+ number_of_pages = self.number_of_pages_section[lang][name]
+ else:
+ posts = [post for post in self._get_filtered_posts(lang, self.site.config['SHOW_UNTRANSLATED_POSTS']) if post.section_slug(lang) == name]
+ number_of_pages = self._compute_number_of_pages(posts, self.site.config['INDEX_DISPLAY_POST_COUNT'])
+ self.number_of_pages_section[lang][name] = number_of_pages
+ return utils.adjust_name_for_index_path_list([_f for _f in [self.site.config['TRANSLATIONS'][lang],
+ name,
+ index_file] if _f],
+ None,
+ utils.get_displayed_page_number(None, number_of_pages, self.site),
lang,
self.site,
extension=extension)
def index_atom_path(self, name, lang):
- """Return path to an Atom index."""
+ """Link to a numbered Atom index.
+
+ Example:
+
+ link://index_atom/3 => /index-3.atom
+ """
return self.index_path(name, lang, is_feed=True)
+
+ def index_section_atom_path(self, name, lang):
+ """Link to the Atom index for a section.
+
+ Example:
+
+ link://section_index_atom/cars => /cars/index.atom
+ """
+ return self.index_section_path(name, lang, is_feed=True)
diff --git a/nikola/plugins/task/listings.plugin b/nikola/plugins/task/listings.plugin
index 435234b..8fc2e2d 100644
--- a/nikola/plugins/task/listings.plugin
+++ b/nikola/plugins/task/listings.plugin
@@ -5,7 +5,7 @@ module = listings
[Documentation]
author = Roberto Alsina
version = 1.0
-website = http://getnikola.com
+website = https://getnikola.com/
description = Render code listings into output
[Nikola]
diff --git a/nikola/plugins/task/listings.py b/nikola/plugins/task/listings.py
index 5f79724..891f361 100644
--- a/nikola/plugins/task/listings.py
+++ b/nikola/plugins/task/listings.py
@@ -28,6 +28,7 @@
from __future__ import unicode_literals, print_function
+from collections import defaultdict
import sys
import os
import lxml.html
@@ -41,16 +42,13 @@ from nikola import utils
class Listings(Task):
-
"""Render code listings."""
name = "render_listings"
def register_output_name(self, input_folder, rel_name, rel_output_name):
"""Register proper and improper file mappings."""
- if rel_name not in self.improper_input_file_mapping:
- self.improper_input_file_mapping[rel_name] = []
- self.improper_input_file_mapping[rel_name].append(rel_output_name)
+ self.improper_input_file_mapping[rel_name].add(rel_output_name)
self.proper_input_file_mapping[os.path.join(input_folder, rel_name)] = rel_output_name
self.proper_input_file_mapping[rel_output_name] = rel_output_name
@@ -85,7 +83,7 @@ class Listings(Task):
# a list is needed. This is needed for compatibility to previous Nikola
# versions, where there was no need to specify the input directory name
# when asking for a link via site.link('listing', ...).
- self.improper_input_file_mapping = {}
+ self.improper_input_file_mapping = defaultdict(set)
# proper_input_file_mapping maps relative input file (relative to CWD)
# to a generated output file. Since we don't allow an input directory
@@ -255,7 +253,16 @@ class Listings(Task):
}, self.kw["filters"])
def listing_path(self, namep, lang):
- """Return path to a listing."""
+ """A link to a listing.
+
+ It will try to use the file name if it's not ambiguous, or the file path.
+
+ Example:
+
+ link://listing/hello.py => /listings/tutorial/hello.py.html
+
+ link://listing/tutorial/hello.py => /listings/tutorial/hello.py.html
+ """
namep = namep.replace('/', os.sep)
nameh = namep + '.html'
for name in (namep, nameh):
@@ -271,7 +278,7 @@ class Listings(Task):
sys.exit(1)
if len(self.site.config['LISTINGS_FOLDERS']) > 1:
utils.LOGGER.notice("Using listings names in site.link() without input directory prefix while configuration's LISTINGS_FOLDERS has more than one entry.")
- name = self.improper_input_file_mapping[name][0]
+ name = list(self.improper_input_file_mapping[name])[0]
break
else:
utils.LOGGER.error("Unknown listing name {0}!".format(namep))
diff --git a/nikola/plugins/task/pages.plugin b/nikola/plugins/task/pages.plugin
index 023d41b..1bdc7f4 100644
--- a/nikola/plugins/task/pages.plugin
+++ b/nikola/plugins/task/pages.plugin
@@ -5,7 +5,7 @@ module = pages
[Documentation]
author = Roberto Alsina
version = 1.0
-website = http://getnikola.com
+website = https://getnikola.com/
description = Create pages in the output.
[Nikola]
diff --git a/nikola/plugins/task/pages.py b/nikola/plugins/task/pages.py
index e6a8a82..8d41035 100644
--- a/nikola/plugins/task/pages.py
+++ b/nikola/plugins/task/pages.py
@@ -32,7 +32,6 @@ from nikola.utils import config_changed
class RenderPages(Task):
-
"""Render pages into output."""
name = "render_pages"
diff --git a/nikola/plugins/task/posts.plugin b/nikola/plugins/task/posts.plugin
index 79b7c51..c9578bc 100644
--- a/nikola/plugins/task/posts.plugin
+++ b/nikola/plugins/task/posts.plugin
@@ -5,7 +5,7 @@ module = posts
[Documentation]
author = Roberto Alsina
version = 1.0
-website = http://getnikola.com
+website = https://getnikola.com/
description = Create HTML fragments out of posts.
[Nikola]
diff --git a/nikola/plugins/task/posts.py b/nikola/plugins/task/posts.py
index a3a8375..8735beb 100644
--- a/nikola/plugins/task/posts.py
+++ b/nikola/plugins/task/posts.py
@@ -44,7 +44,6 @@ def update_deps(post, lang, task):
class RenderPosts(Task):
-
"""Build HTML fragments from metadata and text."""
name = "render_posts"
@@ -77,6 +76,8 @@ class RenderPosts(Task):
deps_dict = copy(kw)
deps_dict.pop('timeline')
for post in kw['timeline']:
+ if not post.is_translation_available(lang) and not self.site.config['SHOW_UNTRANSLATED_POSTS']:
+ continue
# Extra config dependencies picked from config
for p in post.fragment_deps(lang):
if p.startswith('####MAGIC####CONFIG:'):
@@ -114,7 +115,7 @@ class RenderPosts(Task):
pass
else:
flist.append(f)
- yield utils.apply_filters(task, {os.path.splitext(dest): flist})
+ yield utils.apply_filters(task, {os.path.splitext(dest)[-1]: flist})
def dependence_on_timeline(self, post, lang):
"""Check if a post depends on the timeline."""
diff --git a/nikola/plugins/task/py3_switch.plugin b/nikola/plugins/task/py3_switch.plugin
new file mode 100644
index 0000000..b0014e1
--- /dev/null
+++ b/nikola/plugins/task/py3_switch.plugin
@@ -0,0 +1,13 @@
+[Core]
+name = py3_switch
+module = py3_switch
+
+[Documentation]
+author = Roberto Alsina
+version = 1.0
+website = https://getnikola.com/
+description = Beg the user to switch to Python 3
+
+[Nikola]
+plugincategory = Task
+
diff --git a/nikola/plugins/task/py3_switch.py b/nikola/plugins/task/py3_switch.py
new file mode 100644
index 0000000..930c593
--- /dev/null
+++ b/nikola/plugins/task/py3_switch.py
@@ -0,0 +1,103 @@
+# -*- coding: utf-8 -*-
+
+# Copyright © 2012-2015 Roberto Alsina and others.
+
+# 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.
+
+"""Beg the user to switch to python 3."""
+
+import datetime
+import os
+import random
+import sys
+
+import doit.tools
+
+from nikola.utils import get_logger, STDERR_HANDLER
+from nikola.plugin_categories import LateTask
+
+PY2_AND_NO_PY3_WARNING = """Nikola is going to deprecate Python 2 support in 2016. Your current
+version will continue to work, but please consider upgrading to Python 3.
+
+Please check http://bit.ly/1FKEsiX for details.
+"""
+PY2_WARNING = """Nikola is going to deprecate Python 2 support in 2016. You already have Python 3
+available in your system. Why not switch?
+
+Please check http://bit.ly/1FKEsiX for details.
+"""
+PY2_BARBS = [
+ "Python 2 has been deprecated for years. Stop clinging to your long gone youth and switch to Python3.",
+ "Python 2 is the safety blanket of languages. Be a big kid and switch to Python 3",
+ "Python 2 is old and busted. Python 3 is the new hotness.",
+ "Nice unicode you have there, would be a shame something happened to it.. switch to python 3!.",
+ "Don’t get in the way of progress! Upgrade to Python 3 and save a developer’s mind today!",
+ "Winners don't use Python 2 -- Signed: The FBI",
+ "Python 2? What year is it?",
+ "I just wanna tell you how I'm feeling\n"
+ "Gotta make you understand\n"
+ "Never gonna give you up [But Python 2 has to go]",
+ "The year 2009 called, and they want their Python 2.7 back.",
+]
+
+
+LOGGER = get_logger('Nikola', STDERR_HANDLER)
+
+
+def has_python_3():
+ """Check if python 3 is available."""
+ if 'win' in sys.platform:
+ py_bin = 'py.exe'
+ else:
+ py_bin = 'python3'
+ for path in os.environ["PATH"].split(os.pathsep):
+ if os.access(os.path.join(path, py_bin), os.X_OK):
+ return True
+ return False
+
+
+class Py3Switch(LateTask):
+ """Beg the user to switch to python 3."""
+
+ name = "_switch to py3"
+
+ def gen_tasks(self):
+ """Beg the user to switch to python 3."""
+ def give_warning():
+ if sys.version_info[0] == 3:
+ return
+ if has_python_3():
+ LOGGER.warn(random.choice(PY2_BARBS))
+ LOGGER.warn(PY2_WARNING)
+ else:
+ LOGGER.warn(PY2_AND_NO_PY3_WARNING)
+
+ task = {
+ 'basename': self.name,
+ 'name': 'please!',
+ 'actions': [give_warning],
+ 'clean': True,
+ 'uptodate': [doit.tools.timeout(datetime.timedelta(days=3))]
+ }
+
+ return task
diff --git a/nikola/plugins/task/redirect.plugin b/nikola/plugins/task/redirect.plugin
index c3137b9..c5a3042 100644
--- a/nikola/plugins/task/redirect.plugin
+++ b/nikola/plugins/task/redirect.plugin
@@ -5,7 +5,7 @@ module = redirect
[Documentation]
author = Roberto Alsina
version = 1.0
-website = http://getnikola.com
+website = https://getnikola.com/
description = Create redirect pages.
[Nikola]
diff --git a/nikola/plugins/task/redirect.py b/nikola/plugins/task/redirect.py
index 8530f5e..2d4eba4 100644
--- a/nikola/plugins/task/redirect.py
+++ b/nikola/plugins/task/redirect.py
@@ -35,7 +35,6 @@ from nikola import utils
class Redirect(Task):
-
"""Generate redirections."""
name = "redirect"
diff --git a/nikola/plugins/task/robots.plugin b/nikola/plugins/task/robots.plugin
index 72ce31f..7ae56c6 100644
--- a/nikola/plugins/task/robots.plugin
+++ b/nikola/plugins/task/robots.plugin
@@ -5,7 +5,7 @@ module = robots
[Documentation]
author = Daniel Aleksandersen
version = 1.0
-website = http://getnikola.com
+website = https://getnikola.com/
description = Generate /robots.txt exclusion file and promote sitemap.
[Nikola]
diff --git a/nikola/plugins/task/robots.py b/nikola/plugins/task/robots.py
index 65254b6..7c7f5df 100644
--- a/nikola/plugins/task/robots.py
+++ b/nikola/plugins/task/robots.py
@@ -39,7 +39,6 @@ from nikola import utils
class RobotsFile(LateTask):
-
"""Generate a robots.txt file."""
name = "robots_file"
@@ -64,14 +63,15 @@ class RobotsFile(LateTask):
with io.open(robots_path, 'w+', encoding='utf8') as outf:
outf.write("Sitemap: {0}\n\n".format(sitemapindex_url))
+ outf.write("User-Agent: *\n")
if kw["robots_exclusions"]:
- outf.write("User-Agent: *\n")
for loc in kw["robots_exclusions"]:
outf.write("Disallow: {0}\n".format(loc))
+ outf.write("Host: {0}\n".format(urlparse(kw["base_url"]).netloc))
yield self.group_task()
- if not utils.get_asset_path("robots.txt", [], files_folders=kw["files_folders"]):
+ if not utils.get_asset_path("robots.txt", [], files_folders=kw["files_folders"], output_dir=False):
yield utils.apply_filters({
"basename": self.name,
"name": robots_path,
@@ -82,6 +82,6 @@ class RobotsFile(LateTask):
"task_dep": ["sitemap"]
}, kw["filters"])
elif kw["robots_exclusions"]:
- utils.LOGGER.warn('Did not generate robots.txt as one already exists in FILES_FOLDERS. ROBOTS_EXCLUSIONS will not have any affect on the copied fie.')
+ utils.LOGGER.warn('Did not generate robots.txt as one already exists in FILES_FOLDERS. ROBOTS_EXCLUSIONS will not have any affect on the copied file.')
else:
utils.LOGGER.debug('Did not generate robots.txt as one already exists in FILES_FOLDERS.')
diff --git a/nikola/plugins/task/rss.plugin b/nikola/plugins/task/rss.plugin
index cf9b7a7..4dd8aba 100644
--- a/nikola/plugins/task/rss.plugin
+++ b/nikola/plugins/task/rss.plugin
@@ -5,7 +5,7 @@ module = rss
[Documentation]
author = Roberto Alsina
version = 1.0
-website = http://getnikola.com
+website = https://getnikola.com/
description = Generate RSS feeds.
[Nikola]
diff --git a/nikola/plugins/task/rss.py b/nikola/plugins/task/rss.py
index 9020a06..be57f5c 100644
--- a/nikola/plugins/task/rss.py
+++ b/nikola/plugins/task/rss.py
@@ -38,7 +38,6 @@ from nikola.plugin_categories import Task
class GenerateRSS(Task):
-
"""Generate RSS feeds."""
name = "generate_rss"
@@ -58,13 +57,14 @@ class GenerateRSS(Task):
"base_url": self.site.config["BASE_URL"],
"blog_description": self.site.config["BLOG_DESCRIPTION"],
"output_folder": self.site.config["OUTPUT_FOLDER"],
- "rss_teasers": self.site.config["RSS_TEASERS"],
- "rss_plain": self.site.config["RSS_PLAIN"],
+ "feed_teasers": self.site.config["FEED_TEASERS"],
+ "feed_plain": self.site.config["FEED_PLAIN"],
"show_untranslated_posts": self.site.config['SHOW_UNTRANSLATED_POSTS'],
"feed_length": self.site.config['FEED_LENGTH'],
+ "feed_previewimage": self.site.config["FEED_PREVIEWIMAGE"],
"tzinfo": self.site.tzinfo,
- "rss_read_more_link": self.site.config["RSS_READ_MORE_LINK"],
- "rss_links_append_query": self.site.config["RSS_LINKS_APPEND_QUERY"],
+ "feed_read_more_link": self.site.config["FEED_READ_MORE_LINK"],
+ "feed_links_append_query": self.site.config["FEED_LINKS_APPEND_QUERY"],
}
self.site.scan_posts()
# Check for any changes in the state of use_in_feeds for any post.
@@ -96,8 +96,8 @@ class GenerateRSS(Task):
'actions': [(utils.generic_rss_renderer,
(lang, kw["blog_title"](lang), kw["site_url"],
kw["blog_description"](lang), posts, output_name,
- kw["rss_teasers"], kw["rss_plain"], kw['feed_length'], feed_url,
- None, kw["rss_links_append_query"]))],
+ kw["feed_teasers"], kw["feed_plain"], kw['feed_length'], feed_url,
+ None, kw["feed_links_append_query"]))],
'task_dep': ['render_posts'],
'clean': True,
@@ -106,6 +106,11 @@ class GenerateRSS(Task):
yield utils.apply_filters(task, kw['filters'])
def rss_path(self, name, lang):
- """Return RSS path."""
+ """A link to the RSS feed path.
+
+ Example:
+
+ link://rss => /blog/rss.xml
+ """
return [_f for _f in [self.site.config['TRANSLATIONS'][lang],
self.site.config['RSS_PATH'], 'rss.xml'] if _f]
diff --git a/nikola/plugins/task/scale_images.plugin b/nikola/plugins/task/scale_images.plugin
index d906b8c..3edd0c6 100644
--- a/nikola/plugins/task/scale_images.plugin
+++ b/nikola/plugins/task/scale_images.plugin
@@ -5,7 +5,7 @@ module = scale_images
[Documentation]
author = Pelle Nilsson
version = 1.0
-website = http://getnikola.com
+website = https://getnikola.com/
description = Create down-scaled images and thumbnails.
[Nikola]
diff --git a/nikola/plugins/task/scale_images.py b/nikola/plugins/task/scale_images.py
index 22ed2ab..e55dc6c 100644
--- a/nikola/plugins/task/scale_images.py
+++ b/nikola/plugins/task/scale_images.py
@@ -34,7 +34,6 @@ from nikola import utils
class ScaleImage(Task, ImageProcessor):
-
"""Resize images and create thumbnails for them."""
name = "scale_images"
diff --git a/nikola/plugins/task/sitemap.plugin b/nikola/plugins/task/sitemap.plugin
index e3c991f..83e72c4 100644
--- a/nikola/plugins/task/sitemap.plugin
+++ b/nikola/plugins/task/sitemap.plugin
@@ -5,7 +5,7 @@ module = sitemap
[Documentation]
author = Roberto Alsina
version = 1.0
-website = http://getnikola.com
+website = https://getnikola.com/
description = Generate google sitemap.
[Nikola]
diff --git a/nikola/plugins/task/sitemap/__init__.py b/nikola/plugins/task/sitemap/__init__.py
index fd781d6..90acdd3 100644
--- a/nikola/plugins/task/sitemap/__init__.py
+++ b/nikola/plugins/task/sitemap/__init__.py
@@ -31,6 +31,7 @@ import io
import datetime
import dateutil.tz
import os
+import sys
try:
from urlparse import urljoin, urlparse
import robotparser as robotparser
@@ -39,7 +40,7 @@ except ImportError:
import urllib.robotparser as robotparser # NOQA
from nikola.plugin_categories import LateTask
-from nikola.utils import config_changed, apply_filters
+from nikola.utils import apply_filters, config_changed, encodelink
urlset_header = """<?xml version="1.0" encoding="UTF-8"?>
@@ -106,7 +107,6 @@ def get_base_path(base):
class Sitemap(LateTask):
-
"""Generate a sitemap."""
name = "sitemap"
@@ -146,7 +146,10 @@ class Sitemap(LateTask):
continue # Totally empty, not on sitemap
path = os.path.relpath(root, output)
# ignore the current directory.
- path = (path.replace(os.sep, '/') + '/').replace('./', '')
+ if path == '.':
+ path = ''
+ else:
+ path = path.replace(os.sep, '/') + '/'
lastmod = self.get_lastmod(root)
loc = urljoin(base_url, base_path + path)
if kw['index_file'] in files and kw['strip_indexes']: # ignore folders when not stripping urls
@@ -157,10 +160,10 @@ class Sitemap(LateTask):
if post:
for lang in kw['translations']:
alt_url = post.permalink(lang=lang, absolute=True)
- if loc == alt_url:
+ if encodelink(loc) == alt_url:
continue
alternates.append(alternates_format.format(lang, alt_url))
- urlset[loc] = loc_format.format(loc, lastmod, ''.join(alternates))
+ urlset[loc] = loc_format.format(encodelink(loc), lastmod, ''.join(alternates))
for fname in files:
if kw['strip_indexes'] and fname == kw['index_file']:
continue # We already mapped the folder
@@ -200,7 +203,7 @@ class Sitemap(LateTask):
path = path.replace(os.sep, '/')
lastmod = self.get_lastmod(real_path)
loc = urljoin(base_url, base_path + path)
- sitemapindex[loc] = sitemap_format.format(loc, lastmod)
+ sitemapindex[loc] = sitemap_format.format(encodelink(loc), lastmod)
continue
else:
continue # ignores all XML files except those presumed to be RSS
@@ -214,18 +217,22 @@ class Sitemap(LateTask):
if post:
for lang in kw['translations']:
alt_url = post.permalink(lang=lang, absolute=True)
- if loc == alt_url:
+ if encodelink(loc) == alt_url:
continue
alternates.append(alternates_format.format(lang, alt_url))
- urlset[loc] = loc_format.format(loc, lastmod, '\n'.join(alternates))
+ urlset[loc] = loc_format.format(encodelink(loc), lastmod, '\n'.join(alternates))
def robot_fetch(path):
"""Check if robots can fetch a file."""
for rule in kw["robots_exclusions"]:
robot = robotparser.RobotFileParser()
robot.parse(["User-Agent: *", "Disallow: {0}".format(rule)])
- if not robot.can_fetch("*", '/' + path):
- return False # not robot food
+ if sys.version_info[0] == 3:
+ if not robot.can_fetch("*", '/' + path):
+ return False # not robot food
+ else:
+ if not robot.can_fetch("*", ('/' + path).encode('utf-8')):
+ return False # not robot food
return True
def write_sitemap():
diff --git a/nikola/plugins/task/sources.plugin b/nikola/plugins/task/sources.plugin
index d232c2b..66856f1 100644
--- a/nikola/plugins/task/sources.plugin
+++ b/nikola/plugins/task/sources.plugin
@@ -5,7 +5,7 @@ module = sources
[Documentation]
author = Roberto Alsina
version = 1.0
-website = http://getnikola.com
+website = https://getnikola.com/
description = Copy page sources into the output.
[Nikola]
diff --git a/nikola/plugins/task/sources.py b/nikola/plugins/task/sources.py
index 87b4ae7..f782ad4 100644
--- a/nikola/plugins/task/sources.py
+++ b/nikola/plugins/task/sources.py
@@ -33,7 +33,6 @@ from nikola import utils
class Sources(Task):
-
"""Copy page sources into the output."""
name = "render_sources"
diff --git a/nikola/plugins/task/tags.plugin b/nikola/plugins/task/tags.plugin
index 283a16a..c3a5be3 100644
--- a/nikola/plugins/task/tags.plugin
+++ b/nikola/plugins/task/tags.plugin
@@ -5,7 +5,7 @@ module = tags
[Documentation]
author = Roberto Alsina
version = 1.0
-website = http://getnikola.com
+website = https://getnikola.com/
description = Render the tag pages and feeds.
[Nikola]
diff --git a/nikola/plugins/task/tags.py b/nikola/plugins/task/tags.py
index 3186636..6d9d495 100644
--- a/nikola/plugins/task/tags.py
+++ b/nikola/plugins/task/tags.py
@@ -41,7 +41,6 @@ from nikola import utils
class RenderTags(Task):
-
"""Render the tag/category pages and feeds."""
name = "render_tags"
@@ -74,9 +73,9 @@ class RenderTags(Task):
'category_prefix': self.site.config['CATEGORY_PREFIX'],
"category_pages_are_indexes": self.site.config['CATEGORY_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"],
+ "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'],
"taglist_minimum_post_count": self.site.config['TAGLIST_MINIMUM_POSTS'],
@@ -84,6 +83,10 @@ class RenderTags(Task):
"pretty_urls": self.site.config['PRETTY_URLS'],
"strip_indexes": self.site.config['STRIP_INDEXES'],
"index_file": self.site.config['INDEX_FILE'],
+ "category_pages_descriptions": self.site.config['CATEGORY_PAGES_DESCRIPTIONS'],
+ "category_pages_titles": self.site.config['CATEGORY_PAGES_TITLES'],
+ "tag_pages_descriptions": self.site.config['TAG_PAGES_DESCRIPTIONS'],
+ "tag_pages_titles": self.site.config['TAG_PAGES_TITLES'],
}
self.site.scan_posts()
@@ -168,7 +171,7 @@ class RenderTags(Task):
"""Write tag data into JSON file, for use in tag clouds."""
utils.makedirs(os.path.dirname(output_name))
with open(output_name, 'w+') as fd:
- json.dump(data, fd)
+ json.dump(data, fd, sort_keys=True)
if self.site.config['WRITE_TAG_CLOUD']:
task = {
@@ -199,7 +202,6 @@ class RenderTags(Task):
kw['tags'] = tags
output_name = os.path.join(
kw['output_folder'], self.site.path('tag_index' if has_tags else 'category_index', None, lang))
- output_name = output_name
context = {}
if has_categories and has_tags:
context["title"] = kw["messages"][lang]["Tags and Categories"]
@@ -251,6 +253,10 @@ class RenderTags(Task):
else:
return tag
+ def _get_indexes_title(self, tag, is_category, lang, messages):
+ titles = self.site.config['CATEGORY_PAGES_TITLES'] if is_category else self.site.config['TAG_PAGES_TITLES']
+ return titles[lang][tag] if lang in titles and tag in titles[lang] else messages[lang]["Posts about %s"] % tag
+
def _get_description(self, tag, is_category, lang):
descriptions = self.site.config['CATEGORY_PAGES_DESCRIPTIONS'] if is_category else self.site.config['TAG_PAGES_DESCRIPTIONS']
return descriptions[lang][tag] if lang in descriptions and tag in descriptions[lang] else None
@@ -276,7 +282,7 @@ class RenderTags(Task):
if kw["generate_rss"]:
# On a tag page, the feeds include the tag's feeds
rss_link = ("""<link rel="alternate" type="application/rss+xml" """
- """type="application/rss+xml" title="RSS for tag """
+ """title="RSS for tag """
"""{0} ({1})" href="{2}">""".format(
title, lang, self.site.link(kind + "_rss", tag, lang)))
context_source['rss_link'] = rss_link
@@ -284,7 +290,7 @@ class RenderTags(Task):
context_source["category"] = tag
context_source["category_path"] = self.site.parse_category_name(tag)
context_source["tag"] = title
- indexes_title = kw["messages"][lang]["Posts about %s"] % title
+ indexes_title = self._get_indexes_title(title, is_category, lang, kw["messages"])
context_source["description"] = self._get_description(tag, is_category, lang)
if is_category:
context_source["subcategories"] = self._get_subcategories(tag)
@@ -306,7 +312,7 @@ class RenderTags(Task):
context["category"] = tag
context["category_path"] = self.site.parse_category_name(tag)
context["tag"] = title
- context["title"] = kw["messages"][lang]["Posts about %s"] % title
+ context["title"] = self._get_indexes_title(title, is_category, lang, kw["messages"])
context["posts"] = post_list
context["permalink"] = self.site.link(kind, tag, lang)
context["kind"] = kind
@@ -326,6 +332,29 @@ class RenderTags(Task):
task['basename'] = str(self.name)
yield task
+ if self.site.config['GENERATE_ATOM']:
+ yield self.atom_feed_list(kind, tag, lang, post_list, context, kw)
+
+ def atom_feed_list(self, kind, tag, lang, post_list, context, kw):
+ """Generate atom feeds for tag lists."""
+ if kind == 'tag':
+ context['feedlink'] = self.site.abs_link(self.site.path('tag_atom', tag, lang))
+ feed_path = os.path.join(kw['output_folder'], self.site.path('tag_atom', tag, lang))
+ elif kind == 'category':
+ context['feedlink'] = self.site.abs_link(self.site.path('category_atom', tag, lang))
+ feed_path = os.path.join(kw['output_folder'], self.site.path('category_atom', tag, lang))
+
+ task = {
+ 'basename': str(self.name),
+ 'name': feed_path,
+ 'targets': [feed_path],
+ 'actions': [(self.site.atom_feed_renderer, (lang, post_list, feed_path, kw['filters'], context))],
+ 'clean': True,
+ 'uptodate': [utils.config_changed(kw, 'nikola.plugins.task.tags:atom')],
+ 'task_dep': ['render_posts'],
+ }
+ return task
+
def tag_rss(self, tag, lang, posts, kw, is_category):
"""Create a RSS feed for a single tag in a given language."""
kind = "category" if is_category else "tag"
@@ -349,8 +378,8 @@ class RenderTags(Task):
'actions': [(utils.generic_rss_renderer,
(lang, "{0} ({1})".format(kw["blog_title"](lang), self._get_title(tag, is_category)),
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"]))],
+ 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.tags:rss')] + deps_uptodate,
'task_dep': ['render_posts'],
@@ -364,41 +393,71 @@ class RenderTags(Task):
return name
def tag_index_path(self, name, lang):
- """Return path to the tag index."""
- return [_f for _f in [self.site.config['TRANSLATIONS'][lang],
- self.site.config['TAG_PATH'],
- self.site.config['INDEX_FILE']] if _f]
+ """A link to the tag index.
+
+ Example:
+
+ link://tag_index => /tags/index.html
+ """
+ if self.site.config['TAGS_INDEX_PATH'][lang]:
+ paths = [_f for _f in [self.site.config['TRANSLATIONS'][lang],
+ self.site.config['TAGS_INDEX_PATH'][lang]] if _f]
+ else:
+ paths = [_f for _f in [self.site.config['TRANSLATIONS'][lang],
+ self.site.config['TAG_PATH'][lang],
+ self.site.config['INDEX_FILE']] if _f]
+ return paths
def category_index_path(self, name, lang):
- """Return path to the category index."""
+ """A link to the category index.
+
+ Example:
+
+ link://category_index => /categories/index.html
+ """
return [_f for _f in [self.site.config['TRANSLATIONS'][lang],
- self.site.config['CATEGORY_PATH'],
+ self.site.config['CATEGORY_PATH'][lang],
self.site.config['INDEX_FILE']] if _f]
def tag_path(self, name, lang):
- """Return path to a tag."""
+ """A link to a tag's page.
+
+ Example:
+
+ link://tag/cats => /tags/cats.html
+ """
if self.site.config['PRETTY_URLS']:
return [_f for _f in [
self.site.config['TRANSLATIONS'][lang],
- self.site.config['TAG_PATH'],
+ self.site.config['TAG_PATH'][lang],
self.slugify_tag_name(name),
self.site.config['INDEX_FILE']] if _f]
else:
return [_f for _f in [
self.site.config['TRANSLATIONS'][lang],
- self.site.config['TAG_PATH'],
+ self.site.config['TAG_PATH'][lang],
self.slugify_tag_name(name) + ".html"] if _f]
def tag_atom_path(self, name, lang):
- """Return path to a tag Atom feed."""
+ """A link to a tag's Atom feed.
+
+ Example:
+
+ link://tag_atom/cats => /tags/cats.atom
+ """
return [_f for _f in [self.site.config['TRANSLATIONS'][lang],
- self.site.config['TAG_PATH'], self.slugify_tag_name(name) + ".atom"] if
+ self.site.config['TAG_PATH'][lang], self.slugify_tag_name(name) + ".atom"] if
_f]
def tag_rss_path(self, name, lang):
- """Return path to a tag RSS feed."""
+ """A link to a tag's RSS feed.
+
+ Example:
+
+ link://tag_rss/cats => /tags/cats.xml
+ """
return [_f for _f in [self.site.config['TRANSLATIONS'][lang],
- self.site.config['TAG_PATH'], self.slugify_tag_name(name) + ".xml"] if
+ self.site.config['TAG_PATH'][lang], self.slugify_tag_name(name) + ".xml"] if
_f]
def slugify_category_name(self, name):
@@ -417,24 +476,39 @@ class RenderTags(Task):
return path
def category_path(self, name, lang):
- """Return path to a category."""
+ """A link to a category.
+
+ Example:
+
+ link://category/dogs => /categories/dogs.html
+ """
if self.site.config['PRETTY_URLS']:
return [_f for _f in [self.site.config['TRANSLATIONS'][lang],
- self.site.config['CATEGORY_PATH']] if
+ self.site.config['CATEGORY_PATH'][lang]] if
_f] + self.slugify_category_name(name) + [self.site.config['INDEX_FILE']]
else:
return [_f for _f in [self.site.config['TRANSLATIONS'][lang],
- self.site.config['CATEGORY_PATH']] if
+ self.site.config['CATEGORY_PATH'][lang]] if
_f] + self._add_extension(self.slugify_category_name(name), ".html")
def category_atom_path(self, name, lang):
- """Return path to a category Atom feed."""
+ """A link to a category's Atom feed.
+
+ Example:
+
+ link://category_atom/dogs => /categories/dogs.atom
+ """
return [_f for _f in [self.site.config['TRANSLATIONS'][lang],
- self.site.config['CATEGORY_PATH']] if
+ self.site.config['CATEGORY_PATH'][lang]] if
_f] + self._add_extension(self.slugify_category_name(name), ".atom")
def category_rss_path(self, name, lang):
- """Return path to a category RSS feed."""
+ """A link to a category's RSS feed.
+
+ Example:
+
+ link://category_rss/dogs => /categories/dogs.xml
+ """
return [_f for _f in [self.site.config['TRANSLATIONS'][lang],
- self.site.config['CATEGORY_PATH']] if
+ self.site.config['CATEGORY_PATH'][lang]] if
_f] + self._add_extension(self.slugify_category_name(name), ".xml")