diff options
Diffstat (limited to 'nikola')
128 files changed, 1340 insertions, 431 deletions
diff --git a/nikola/__init__.py b/nikola/__init__.py index a04e3b9..0a82198 100644 --- a/nikola/__init__.py +++ b/nikola/__init__.py @@ -1,8 +1,34 @@ # -*- coding: utf-8 -*- +# Copyright © 2012-2014 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. + from __future__ import absolute_import +import os + +__version__ = "6.3.0" +DEBUG = bool(os.getenv('NIKOLA_DEBUG')) from .nikola import Nikola # NOQA from . import plugins # NOQA - -__version__ = "6.2.1" diff --git a/nikola/main.py b/nikola/__main__.py index f00a4d2..6b549b4 100644 --- a/nikola/main.py +++ b/nikola/__main__.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright © 2012-2013 Roberto Alsina and others. +# Copyright © 2012-2014 Roberto Alsina and others. # Permission is hereby granted, free of charge, to any # person obtaining a copy of this software and associated @@ -38,11 +38,12 @@ from doit.doit_cmd import DoitMain from doit.cmd_help import Help as DoitHelp from doit.cmd_run import Run as DoitRun from doit.cmd_clean import Clean as DoitClean +from doit.cmd_auto import Auto as DoitAuto from logbook import NullHandler from . import __version__ from .nikola import Nikola -from .utils import _reload, sys_decode, LOGGER, STRICT_HANDLER +from .utils import _reload, sys_decode, get_root_dir, LOGGER, STRICT_HANDLER config = {} @@ -58,6 +59,11 @@ def main(args): nullhandler.push_application() quiet = True global config + + root = get_root_dir() + if root: + os.chdir(root) + sys.path.append('') try: import conf @@ -127,6 +133,10 @@ class Clean(DoitClean): shutil.rmtree(cache_folder) return super(Clean, self).clean_tasks(tasks, dryrun) +# Nikola has its own "auto" commands that uses livereload. +# Expose original doit "auto" command as "doit_auto". +DoitAuto.name = 'doit_auto' + class NikolaTaskLoader(TaskLoader): """custom task loader to get tasks from Nikola instead of dodo.py file""" @@ -156,7 +166,7 @@ class NikolaTaskLoader(TaskLoader): class DoitNikola(DoitMain): # overwite help command - DOIT_CMDS = list(DoitMain.DOIT_CMDS) + [Help, Build, Clean] + DOIT_CMDS = list(DoitMain.DOIT_CMDS) + [Help, Build, Clean, DoitAuto] TASK_LOADER = NikolaTaskLoader def __init__(self, nikola, quiet=False): @@ -181,7 +191,9 @@ class DoitNikola(DoitMain): args = ['help'] # Hide run because Nikola uses build sub_cmds.pop('run') - + if len(args) == 0 or any(arg in ["--version", '-V'] for arg in args): + cmd_args = ['version'] + args = ['version'] if len(args) == 0 or args[0] not in sub_cmds.keys() or \ args[0] == 'build': # Check for conf.py before launching run @@ -193,4 +205,7 @@ class DoitNikola(DoitMain): @staticmethod def print_version(): - print("Nikola version " + __version__) + print("Nikola v" + __version__) + +if __name__ == "__main__": + sys.exit(main(sys.argv[1:])) diff --git a/nikola/conf.py.in b/nikola/conf.py.in index 20605d8..6ae0e1d 100644 --- a/nikola/conf.py.in +++ b/nikola/conf.py.in @@ -25,28 +25,33 @@ BLOG_DESCRIPTION = "${BLOG_DESCRIPTION}" # Currently supported languages are: # bg Bulgarian # ca Catalan +# cs Czech [ALTERNATIVELY cz] # de German # el Greek [NOT gr!] # en English # eo Esperanto # es Spanish +# et Estonian +# eu Basque # fa Persian # fi Finnish # fr French # hr Croatian # it Italian -# jp Japanese +# ja Japanese [NOT jp!] +# nb Norwegian Bokmål # nl Dutch # pt_br Portuguese (Brasil) # pl Polish # ru Russian # sl Slovenian [NOT sl_si!] -# tr_tr Turkish (Turkey) +# tr Turkish (Turkey) [NOT tr_tr!] +# ur Urdu # zh_cn Chinese (Simplified) # # If you want to use Nikola with a non-supported language you have to provide # a module containing the necessary translations -# (p.e. look at the modules at: ./nikola/data/themes/default/messages/fr.py). +# (cf. the modules at nikola/data/themes/base/messages/fr.py). # If a specific post is not translated to a language, then the version # in the default language will be shown instead. @@ -62,6 +67,20 @@ TRANSLATIONS = { # "es": "./es", } +# What will translated input files be named like? + +# If you have a page something.rst, then something.rst.pl will be considered +# its Polish translation. +# (in the above example: path == "something", lang == "pl", ext == "rst") +# this pattern is also used for metadata: +# something.meta -> something.meta.pl + +TRANSLATIONS_PATTERN = "{path}.{ext}.{lang}" + +# If you don't want your Polish files to be considered Perl code, use this: +# TRANSLATIONS_PATTERN = "{path}.{lang}.{ext}" +# Note that this pattern will become the default in v7.0.0. + # Links for the sidebar / navigation bar. # You should provide a key-value pair for each used language. NAVIGATION_LINKS = { @@ -96,6 +115,8 @@ NAVIGATION_LINKS = { # and optionally translated files (example for spanish, with code "es"): # whatever/thing.txt.es and whatever/thing.meta.es # +# This assumes you use the default TRANSLATIONS_PATTERN. +# # From those files, a set of HTML fragment files will be generated: # cache/whatever/thing.html (and maybe cache/whatever/thing.html.es) # @@ -165,6 +186,12 @@ COMPILERS = ${COMPILERS} # ARCHIVE_PATH = "" # ARCHIVE_FILENAME = "archive.html" +# URLs to other posts/pages can take 3 forms: +# rel_path: a relative URL to the current page/post (default) +# full_path: a URL with the full path from the root +# absolute: a complete URL (that includes the SITE_URL) +# URL_TYPE = 'rel_path' + # Final locations are: # output / TRANSLATION[lang] / RSS_PATH / rss.xml # RSS_PATH = "" @@ -187,7 +214,7 @@ COMPILERS = ${COMPILERS} # Commands to execute to deploy. Can be anything, for example, # you may use rsync: -# "rsync -rav output/* joe@my.site:/srv/www/site" +# "rsync -rav --delete output/ joe@my.site:/srv/www/site" # And then do a backup, or run `nikola ping` from the `ping` # plugin (`nikola install_plugin ping`). # To do manual deployment, set it to [] @@ -235,11 +262,17 @@ COMPILERS = ${COMPILERS} # Use an external gzip command? None means no. # Example: GZIP_COMMAND = "pigz -k {filename}" # GZIP_COMMAND = None -# Make sure the server does not return a "Accept-Ranges: bytes" header for +# Make sure the server does not return a "Accept-Ranges: bytes" header for # files compressed by this option! OR make sure that a ranged request does not # return partial content of another representation for these resources. Do not # use this feature if you do not understand what this means. +# Compiler to process LESS files. +# LESS_COMPILER = 'lessc' + +# Compiler to process Sass files. +# SASS_COMPILER = 'sass' + # ############################################################################# # Image Gallery Options # ############################################################################# @@ -259,10 +292,13 @@ COMPILERS = ${COMPILERS} # HTML fragments and diverse things that are used by the templates # ############################################################################# -# Data about post-per-page indexes -# INDEXES_TITLE = "" # If this is empty, the default is BLOG_TITLE -# INDEXES_PAGES = "" # If this is empty, the default is 'old posts page %d' -# translated +# Data about post-per-page indexes. +# INDEXES_PAGES defaults to 'old posts, page %d' or 'page %d' (translated), +# depending on the value of INDEXES_PAGES_MAIN. +# INDEXES_TITLE = "" # If this is empty, defaults to BLOG_TITLE +# INDEXES_PAGES = "" # If this is empty, defaults to '[old posts,] page %d' (see above) +# INDEXES_PAGES_MAIN = False # If True, INDEXES_PAGES is also displayed on + # the main (the newest) index page (index.html) # Name of the theme to use. THEME = "${THEME}" @@ -330,7 +366,7 @@ CONTENT_FOOTER = CONTENT_FOOTER.format(email=BLOG_EMAIL, # To use comments, you can choose between different third party comment # systems, one of "disqus", "livefyre", "intensedebate", "moot", -# "googleplus" or "facebook" +# "googleplus", "facebook" or "isso" # COMMENT_SYSTEM = "disqus" # And you also need to add your COMMENT_SYSTEM_ID which # depends on what comment system you use. The default is @@ -583,14 +619,15 @@ CONTENT_FOOTER = CONTENT_FOOTER.format(email=BLOG_EMAIL, # } -# Post's dates are considered in GMT by default, if you want to use -# another timezone, please set TIMEZONE to match. Check the available +# Post's dates are considered in UTC by default, if you want to use +# another time zone, please set TIMEZONE to match. Check the available # list from Wikipedia: # http://en.wikipedia.org/wiki/List_of_tz_database_time_zones -# Also, if you want to use a different timezone in some of your posts, +# (eg. 'Europe/Zurich') +# Also, if you want to use a different time zone in some of your posts, # you can use W3C-DTF Format (ex. 2012-03-30T23:00:00+02:00) # -# TIMEZONE = 'Europe/Zurich' +# TIMEZONE = 'UTC' # If webassets is installed, bundle JS and CSS to make site loading faster # USE_BUNDLES = True @@ -598,6 +635,11 @@ CONTENT_FOOTER = CONTENT_FOOTER.format(email=BLOG_EMAIL, # Plugins you don't want to use. Be careful :-) # DISABLED_PLUGINS = ["render_galleries"] +# Add the absolute paths to directories containing plugins to use them. +# For example, the `plugins` directory of your clone of the Nikola plugins +# repository. +# EXTRA_PLUGINS_DIRS = [] + # Experimental plugins - use at your own risk. # They probably need some manual adjustments - please see their respective # readme. @@ -615,6 +657,14 @@ CONTENT_FOOTER = CONTENT_FOOTER.format(email=BLOG_EMAIL, # If set to True, enable optional hyphenation in your posts (requires pyphen) # HYPHENATE = False +# The <hN> tags in HTML generated by certain compilers (reST/Markdown) +# will be demoted by that much (1 → h1 will become h2 and so on) +# This was a hidden feature of the Markdown and reST compilers in the +# past. Useful especially if your post titles are in <h1> tags too, for +# example. +# (defaults to 1.) +# DEMOTE_HEADERS = 1 + # You can configure the logging handlers installed as plugins or change the # log level of the default stdout handler. LOGGING_HANDLERS = { diff --git a/nikola/data/samplesite/files/assets/css/custom.css b/nikola/data/samplesite/files/assets/css/custom.css deleted file mode 100644 index e69de29..0000000 --- a/nikola/data/samplesite/files/assets/css/custom.css +++ /dev/null diff --git a/nikola/data/samplesite/galleries/demo/index.txt b/nikola/data/samplesite/galleries/demo/index.txt index d3d5a44..e29f66e 100644 --- a/nikola/data/samplesite/galleries/demo/index.txt +++ b/nikola/data/samplesite/galleries/demo/index.txt @@ -1,2 +1,4 @@ +.. title: Nikola Tesla + Some public domain pictures of Nikola Tesla -(copied from `here <http://kerryr.net/pioneers/gallery/tesla.htm>`_)
\ No newline at end of file +(copied from `here <http://kerryr.net/pioneers/gallery/tesla.htm>`_) diff --git a/nikola/data/themes/base/assets/css/theme.css b/nikola/data/themes/base/assets/css/theme.css index e69de29..2a924f1 100644 --- a/nikola/data/themes/base/assets/css/theme.css +++ b/nikola/data/themes/base/assets/css/theme.css @@ -0,0 +1 @@ +/* This file intentionally left blank. */ diff --git a/nikola/data/themes/base/assets/js/mathjax.js b/nikola/data/themes/base/assets/js/mathjax.js index 2f4e773..82c1f6c 100644 --- a/nikola/data/themes/base/assets/js/mathjax.js +++ b/nikola/data/themes/base/assets/js/mathjax.js @@ -6,7 +6,12 @@ window.onload = function () { setTimeout(function () { var script = document.createElement("script"); script.type = "text/javascript"; - script.src = "https://c328740.ssl.cf1.rackcdn.com/mathjax/latest/MathJax.js?config=TeX-AMS_HTML"; + if (location.protocol == 'https:') { + scriptbase = "https://c328740.ssl.cf1.rackcdn.com/"; + } else { + scriptbase = "http://cdn.mathjax.org/"; + } + script.src = scriptbase + "mathjax/latest/MathJax.js?config=TeX-AMS-MML_HTMLorMML"; document.getElementsByTagName("body")[0].appendChild(script); },1) -}
\ No newline at end of file +} diff --git a/nikola/data/themes/base/messages/messages_bg.py b/nikola/data/themes/base/messages/messages_bg.py index 2d36959..d4881b5 100644 --- a/nikola/data/themes/base/messages/messages_bg.py +++ b/nikola/data/themes/base/messages/messages_bg.py @@ -3,15 +3,18 @@ from __future__ import unicode_literals MESSAGES = { "Also available in": "Също достъпно в", + "Also available in:": "Също достъпно в:", "Archive": "Архив", "Categories": "Категории", "LANGUAGE": "Български", - "More posts about": "Още публикации относно ", + "More posts about %s": "Още публикации относно %s", + "More posts about": "Още публикации относно", "Newer posts": "Нови публикации", "Next post": "Следваща публикация", "Older posts": "Стари публикации", "Original site": "Оригиналния сайт", "Posted": "Публиковано", + "Posted:": "Публиковано:", "Posts about %s": "Публикации относно %s", "Posts for year %s": "Публикации за %s година", "Posts for {month} {year}": "Публикации за {month} {year}", @@ -21,5 +24,6 @@ MESSAGES = { "Source": "Source", "Tags and Categories": "Тагове и Категории", "Tags": "Тагове", - "old posts page %d": "стари публикации страница %d", + "old posts, page %d": "стари публикации, страница %d", + "page %d": "страница %d", } diff --git a/nikola/data/themes/base/messages/messages_ca.py b/nikola/data/themes/base/messages/messages_ca.py index 80c4ee4..d3a97b5 100644 --- a/nikola/data/themes/base/messages/messages_ca.py +++ b/nikola/data/themes/base/messages/messages_ca.py @@ -3,15 +3,18 @@ from __future__ import unicode_literals MESSAGES = { "Also available in": "També disponibles en", + "Also available in:": "També disponibles en:", "Archive": "Arxiu", "Categories": "", "LANGUAGE": "Català", + "More posts about %s": "Més entrades sobre %s", "More posts about": "Més entrades sobre", "Newer posts": "Entrades posteriors", "Next post": "Entrada següent", "Older posts": "Entrades anteriors", "Original site": "Lloc original", "Posted": "Publicat", + "Posted:": "Publicat:", "Posts about %s": "Entrades sobre %s", "Posts for year %s": "Entrades de l'any %s", "Posts for {month} {year}": "", @@ -21,5 +24,6 @@ MESSAGES = { "Source": "Codi", "Tags and Categories": "", "Tags": "Etiquetes", - "old posts page %d": "entrades antigues pàgina %d", + "old posts, page %d": "entrades antigues, pàgina %d", + "page %d": "pàgina %d", } diff --git a/nikola/data/themes/base/messages/messages_cs.py b/nikola/data/themes/base/messages/messages_cs.py new file mode 100644 index 0000000..33482b5 --- /dev/null +++ b/nikola/data/themes/base/messages/messages_cs.py @@ -0,0 +1,29 @@ +# -*- encoding:utf-8 -*- +from __future__ import unicode_literals + +MESSAGES = { + "Also available in": "Dostupné také v", + "Also available in:": "Dostupné také v", + "Archive": "Archiv", + "Categories": "Kategorie", + "LANGUAGE": "Čeština", + "More posts about %s": "Další příspěvky o %s", + "More posts about": "Další příspěvky o", + "Newer posts": "Novější příspěvky", + "Next post": "Další příspěvek", + "Older posts": "Starší příspěvky", + "Original site": "Původní stránka", + "Posted": "Zveřejněno", + "Posted:": "Zveřejněno:", + "Posts about %s": "Příspěvky o %s", + "Posts for year %s": "Příspěvky v roce %s", + "Posts for {month} {year}": "Příspěvky v {month} {year}", + "Previous post": "Předchozí příspěvek", + "Read in English": "Číst v češtině", + "Read more": "Číst dál", + "Source": "Zdroj", + "Tags and Categories": "Štítky a kategorie", + "Tags": "Štítky", + "old posts, page %d": "staré příspěvky, strana %d", + "page %d": "strana %d", +} diff --git a/nikola/data/themes/base/messages/messages_cz.py b/nikola/data/themes/base/messages/messages_cz.py new file mode 120000 index 0000000..d784fd5 --- /dev/null +++ b/nikola/data/themes/base/messages/messages_cz.py @@ -0,0 +1 @@ +messages_cs.py
\ No newline at end of file diff --git a/nikola/data/themes/base/messages/messages_de.py b/nikola/data/themes/base/messages/messages_de.py index c885304..6795031 100644 --- a/nikola/data/themes/base/messages/messages_de.py +++ b/nikola/data/themes/base/messages/messages_de.py @@ -3,15 +3,18 @@ from __future__ import unicode_literals MESSAGES = { "Also available in": "Auch verfügbar in", + "Also available in:": "Auch verfügbar in:", "Archive": "Archiv", "Categories": "Kategorien", "LANGUAGE": "Deutsch", + "More posts about %s": "Weitere Einträge über %s", "More posts about": "Weitere Einträge über", "Newer posts": "Neuere Einträge", "Next post": "Nächster Eintrag", "Older posts": "Ältere Einträge", "Original site": "Original-Seite", "Posted": "Veröffentlicht", + "Posted:": "Veröffentlicht:", "Posts about %s": "Einträge über %s", "Posts for year %s": "Einträge aus dem Jahr %s", "Posts for {month} {year}": "Einträge aus {month} {year}", @@ -21,5 +24,6 @@ MESSAGES = { "Source": "Source", "Tags and Categories": "Tags und Kategorien", "Tags": "Tags", - "old posts page %d": "Vorherige Einträge %d", + "old posts, page %d": "Vorherige Einträge, Seite %d", + "page %d": "Seite %d", } diff --git a/nikola/data/themes/base/messages/messages_el.py b/nikola/data/themes/base/messages/messages_el.py index a1745fa..710558b 100644 --- a/nikola/data/themes/base/messages/messages_el.py +++ b/nikola/data/themes/base/messages/messages_el.py @@ -3,15 +3,18 @@ from __future__ import unicode_literals MESSAGES = { "Also available in": "Διαθέσιμο και στα", + "Also available in:": "Διαθέσιμο και στα:", "Archive": "Αρχείο", "Categories": "Κατηγορίες", "LANGUAGE": "Ελληνικά", + "More posts about %s": "Περισσότερες αναρτήσεις για %s", "More posts about": "Περισσότερες αναρτήσεις για", "Newer posts": "Νεότερες αναρτήσεις", "Next post": "Επόμενη ανάρτηση", "Older posts": "Παλαιότερες αναρτήσεις", "Original site": "Ιστοσελίδα αρχικής ανάρτησης", "Posted": "Αναρτήθηκε", + "Posted:": "Αναρτήθηκε:", "Posts about %s": "Αναρτήσεις για %s", "Posts for year %s": "Αναρτήσεις για το έτος %s", "Posts for {month} {year}": "Αναρτήσεις για τον {month} του {year}", @@ -19,7 +22,8 @@ MESSAGES = { "Read in English": "Διαβάστε στα Ελληνικά", "Read more": "Διαβάστε περισσότερα", "Source": "Πηγαίος κώδικας", - "Tags and Categories": "", + "Tags and Categories": "Ετικέτες και κατηγορίες", "Tags": "Ετικέτες", - "old posts page %d": "σελίδα παλαιότερων αναρτήσεων %d", + "old posts, page %d": "σελίδα παλαιότερων αναρτήσεων %d", + "page %d": "σελίδα %d", } diff --git a/nikola/data/themes/base/messages/messages_en.py b/nikola/data/themes/base/messages/messages_en.py index 1e695ac..021f5e7 100644 --- a/nikola/data/themes/base/messages/messages_en.py +++ b/nikola/data/themes/base/messages/messages_en.py @@ -3,17 +3,18 @@ from __future__ import unicode_literals MESSAGES = { "Also available in": "Also available in", + "Also available in:": "Also available in:", "Archive": "Archive", "Categories": "Categories", "LANGUAGE": "English", + "More posts about %s": "More posts about %s", "More posts about": "More posts about", "Newer posts": "Newer posts", "Next post": "Next post", - "No content": "", - "No title": "", "Older posts": "Older posts", "Original site": "Original site", "Posted": "Posted", + "Posted:": "Posted:", "Posts about %s": "Posts about %s", "Posts for year %s": "Posts for year %s", "Posts for {month} {year}": "Posts for {month} {year}", @@ -23,5 +24,6 @@ MESSAGES = { "Source": "Source", "Tags and Categories": "Tags and Categories", "Tags": "Tags", - "old posts page %d": "old posts page %d", + "old posts, page %d": "old posts, page %d", + "page %d": "page %d", } diff --git a/nikola/data/themes/base/messages/messages_eo.py b/nikola/data/themes/base/messages/messages_eo.py index 50bd25a..fdbea88 100644 --- a/nikola/data/themes/base/messages/messages_eo.py +++ b/nikola/data/themes/base/messages/messages_eo.py @@ -3,15 +3,18 @@ from __future__ import unicode_literals MESSAGES = { "Also available in": "Ankaŭ disponebla en", + "Also available in:": "Ankaŭ disponebla en:", "Archive": "Arĥivo", "Categories": "Kategorioj", "LANGUAGE": "Anglalingve", - "More posts about": "Pli artikoloj pri...", + "More posts about %s": "Pli artikoloj pri %s", + "More posts about": "Pli artikoloj pri", "Newer posts": "Pli novaj artikoloj", "Next post": "Venonta artikolo", "Older posts": "Pli malnovaj artikoloj", "Original site": "Originala interretejo", "Posted": "Skribita", + "Posted:": "Skribita:", "Posts about %s": "Artikoloj pri %s", "Posts for year %s": "Artikoloj de la jaro %s", "Posts for {month} {year}": "Artikoloj skribitaj en {month} {year}", @@ -21,5 +24,6 @@ MESSAGES = { "Source": "Fonto", "Tags and Categories": "Etikedoj kaj Kategorioj", "Tags": "Etikedoj", - "old posts page %d": "paĝo de malnovaj artikoloj %d", + "old posts, page %d": "paĝo de malnovaj artikoloj %d", + "page %d": "paĝo %d", } diff --git a/nikola/data/themes/base/messages/messages_es.py b/nikola/data/themes/base/messages/messages_es.py index 79ffac6..6c48fb9 100644 --- a/nikola/data/themes/base/messages/messages_es.py +++ b/nikola/data/themes/base/messages/messages_es.py @@ -3,15 +3,18 @@ from __future__ import unicode_literals MESSAGES = { "Also available in": "También disponible en", + "Also available in:": "También disponible en:", "Archive": "Archivo", "Categories": "Categorías", "LANGUAGE": "Español", + "More posts about %s": "Más posts sobre %s", "More posts about": "Más posts sobre", "Newer posts": "Posts posteriores", "Next post": "Siguiente post", "Older posts": "Posts anteriores", "Original site": "Sitio original", "Posted": "Publicado", + "Posted:": "Publicado:", "Posts about %s": "Posts sobre %s", "Posts for year %s": "Posts del año %s", "Posts for {month} {year}": "Posts de {month} {year}", @@ -21,5 +24,6 @@ MESSAGES = { "Source": "Código", "Tags and Categories": "Tags y Categorías", "Tags": "Tags", - "old posts page %d": "posts antiguos página %d", + "old posts, page %d": "posts antiguos, página %d", + "page %d": "página %d", } diff --git a/nikola/data/themes/base/messages/messages_et.py b/nikola/data/themes/base/messages/messages_et.py new file mode 100644 index 0000000..314f3b8 --- /dev/null +++ b/nikola/data/themes/base/messages/messages_et.py @@ -0,0 +1,29 @@ +# -*- encoding:utf-8 -*- +from __future__ import unicode_literals + +MESSAGES = { + "Also available in": "Saadaval ka", + "Also available in:": "Saadaval ka:", + "Archive": "Arhiiv", + "Categories": "Kategooriad", + "LANGUAGE": "Eesti", + "More posts about %s": "Veel postitusi %s kohta", + "More posts about": "Veel postitusi kohta", + "Newer posts": "Uued postitused", + "Next post": "Järgmine postitus", + "Older posts": "Vanemad postitused", + "Original site": "Algallikas", + "Posted": "Postitatud", + "Posted:": "Postitatud:", + "Posts about %s": "Postitused %s kohta", + "Posts for year %s": "Postitused aastast %s", + "Posts for {month} {year}": "Postitused {year} aasta kuust {month} ", + "Previous post": "Eelmine postitus", + "Read in English": "Loe eesti keeles", + "Read more": "Loe veel", + "Source": "Lähtekood", + "Tags and Categories": "Sildid ja kategooriad", + "Tags": "Märksõnad", + "old posts, page %d": "vanade postituste, leht %d", + "page %d": "leht %d", +} diff --git a/nikola/data/themes/base/messages/messages_eu.py b/nikola/data/themes/base/messages/messages_eu.py new file mode 100644 index 0000000..18d7575 --- /dev/null +++ b/nikola/data/themes/base/messages/messages_eu.py @@ -0,0 +1,29 @@ +# -*- encoding:utf-8 -*- +from __future__ import unicode_literals + +MESSAGES = { + "Also available in": "Eskuragarria hemen ere", + "Also available in:": "Eskuragarria hemen ere:", + "Archive": "Artxiboa", + "Categories": "Kategoriak", + "LANGUAGE": "Euskara", + "More posts about %s": "%s-ri buruzko post gehiago", + "More posts about": "-ri buruzko post gehiago", + "Newer posts": "Post berrienak", + "Next post": "Hurrengo posta", + "Older posts": "Post zaharrenak", + "Original site": "Jatorrizko orria", + "Posted": "Argitaratuta", + "Posted:": "Argitaratuta:", + "Posts about %s": "%s-ri buruzko postak", + "Posts for year %s": "%s. urteko postak", + "Posts for {month} {year}": "{year}ko {month}ren postak", + "Previous post": "Aurreko posta", + "Read in English": "Euskaraz irakurri", + "Read more": "Irakurri gehiago", + "Source": "Iturria", + "Tags and Categories": "Etiketak eta Kategoriak", + "Tags": "Etiketak", + "old posts, page %d": "Post zaharren, orria %d", + "page %d": "orria %d", +} diff --git a/nikola/data/themes/base/messages/messages_fa.py b/nikola/data/themes/base/messages/messages_fa.py index 786893c..bd278ca 100644 --- a/nikola/data/themes/base/messages/messages_fa.py +++ b/nikola/data/themes/base/messages/messages_fa.py @@ -3,15 +3,18 @@ from __future__ import unicode_literals MESSAGES = { "Also available in": "همچنین قابل دسترس از", + "Also available in:": "همچنین قابل دسترس از:", "Archive": "آرشیو", "Categories": "دستهها", "LANGUAGE": "فارسی", + "More posts about %s": "ارسالهای بیشتر دربارهٔ%s", "More posts about": "ارسالهای بیشتر دربارهٔ", "Newer posts": "ارسالهای جدیدتر", "Next post": "ارسال بعدی", "Older posts": "پستهای قدیمیتر", "Original site": "سایت اصلی", "Posted": "ارسال شده", + "Posted:": "ارسال شده:", "Posts about %s": "ارسالها دربارهٔ %s", "Posts for year %s": "ارسالها برای سال %s", "Posts for {month} {year}": "ارسال برای {month} {year}", @@ -21,5 +24,6 @@ MESSAGES = { "Source": "منبع", "Tags and Categories": "برچسبها و دستهها", "Tags": "برچسبها", - "old posts page %d": "صفحهٔ ارسالهای قدیمی %d", + "old posts, page %d": "صفحهٔ ارسالهای قدیمی %d", + "page %d": "", } diff --git a/nikola/data/themes/base/messages/messages_fi.py b/nikola/data/themes/base/messages/messages_fi.py index ca64919..b24ee2c 100644 --- a/nikola/data/themes/base/messages/messages_fi.py +++ b/nikola/data/themes/base/messages/messages_fi.py @@ -3,15 +3,18 @@ from __future__ import unicode_literals MESSAGES = { "Also available in": "Saatavilla myös", + "Also available in:": "Saatavilla myös:", "Archive": "Arkisto", "Categories": "Kategoriat", "LANGUAGE": "Suomi", + "More posts about %s": "Lisää postauksia aiheesta %s", "More posts about": "Lisää postauksia aiheesta", "Newer posts": "Uudempia postauksia", "Next post": "Seuraava postaus", "Older posts": "Vanhempia postauksia", "Original site": "Alkuperäinen sivusto", "Posted": "Postattu", + "Posted:": "Postattu:", "Posts about %s": "Postauksia aiheesta %s", "Posts for year %s": "Postauksia vuodelta %s", "Posts for {month} {year}": "Postauksia ajalle {month} {year}", @@ -21,5 +24,6 @@ MESSAGES = { "Source": "Lähde", "Tags and Categories": "Tagit ja kategoriat", "Tags": "Tagit", - "old posts page %d": "vanhojen postauksien sivu %d", + "old posts, page %d": "vanhojen postauksien, sivu %d", + "page %d": "sivu %d", } diff --git a/nikola/data/themes/base/messages/messages_fr.py b/nikola/data/themes/base/messages/messages_fr.py index 45c7474..ad4aea0 100644 --- a/nikola/data/themes/base/messages/messages_fr.py +++ b/nikola/data/themes/base/messages/messages_fr.py @@ -2,24 +2,28 @@ from __future__ import unicode_literals MESSAGES = { - "Also available in": "Disponible aussi en", + "Also available in": "Egalement disponible en", + "Also available in:": "Egalement disponible en:", "Archive": "Archives", "Categories": "Catégories", "LANGUAGE": "Français", - "More posts about": "Plus de billets sur", + "More posts about %s": "Plus d'articles sur %s", + "More posts about": "Plus d'articles sur", "Newer posts": "Billets récents", - "Next post": "Billet suivant", - "Older posts": "Anciens billets", + "Next post": "Article suivant", + "Older posts": "Anciens articles", "Original site": "Site d'origine", "Posted": "Publié", - "Posts about %s": "Billets sur %s", - "Posts for year %s": "Billets de l'année %s", - "Posts for {month} {year}": "Billets de {month} {year}", - "Previous post": "Billet précédent", + "Posted:": "Publié:", + "Posts about %s": "Articles sur %s", + "Posts for year %s": "Articles de l'année %s", + "Posts for {month} {year}": "Articles de {month} {year}", + "Previous post": "Article précédent", "Read in English": "Lire en français", "Read more": "Lire la suite", "Source": "Source", "Tags and Categories": "Étiquettes et catégories", "Tags": "Étiquettes", - "old posts page %d": "anciens billets page %d", + "old posts, page %d": "anciens articles, page %d", + "page %d": "page %d", } diff --git a/nikola/data/themes/base/messages/messages_hr.py b/nikola/data/themes/base/messages/messages_hr.py index 420159d..ad74078 100644 --- a/nikola/data/themes/base/messages/messages_hr.py +++ b/nikola/data/themes/base/messages/messages_hr.py @@ -3,15 +3,18 @@ from __future__ import unicode_literals MESSAGES = { "Also available in": "Također dostupno i u", + "Also available in:": "Također dostupno i u:", "Archive": "Arhiva", - "Categories": "", + "Categories": "Kategorije", "LANGUAGE": "hrvatski", + "More posts about %s": "Više postova o %s", "More posts about": "Više postova o", "Newer posts": "Noviji postovi", "Next post": "Sljedeći post", "Older posts": "Stariji postovi", "Original site": "Izvorna stranica", "Posted": "Objavljeno", + "Posted:": "Objavljeno:", "Posts about %s": "Postovi o %s", "Posts for year %s": "Postovi za godinu %s", "Posts for {month} {year}": "Postovi za {month} {year}", @@ -19,7 +22,8 @@ MESSAGES = { "Read in English": "Čitaj na hrvatskom", "Read more": "Čitaj dalje", "Source": "Izvor", - "Tags and Categories": "", + "Tags and Categories": "Tagovi i kategorije", "Tags": "Tagovi", - "old posts page %d": "stari postovi stranice %d", + "old posts, page %d": "stari postovi, stranice %d", + "page %d": "stranice %d", } diff --git a/nikola/data/themes/base/messages/messages_it.py b/nikola/data/themes/base/messages/messages_it.py index b460f22..912342e 100644 --- a/nikola/data/themes/base/messages/messages_it.py +++ b/nikola/data/themes/base/messages/messages_it.py @@ -3,15 +3,18 @@ from __future__ import unicode_literals MESSAGES = { "Also available in": "Anche disponibile in", + "Also available in:": "Anche disponibile in:", "Archive": "Archivio", "Categories": "Categorie", "LANGUAGE": "Italiano", + "More posts about %s": "Altri articoli collegati %s", "More posts about": "Altri articoli collegati", "Newer posts": "Articoli recenti", "Next post": "Articolo successivo", "Older posts": "Articoli precedenti", "Original site": "Sito originale", "Posted": "Pubblicato", + "Posted:": "Pubblicato:", "Posts about %s": "Articoli su %s", "Posts for year %s": "Articoli per l'anno %s", "Posts for {month} {year}": "Articoli per {month} {year}", @@ -21,5 +24,6 @@ MESSAGES = { "Source": "Sorgente", "Tags and Categories": "Tags e Categorie", "Tags": "Tags", - "old posts page %d": "pagina dei vecchi articoli %d", + "old posts, page %d": "pagina dei vecchi articoli %d", + "page %d": "pagina %d", } diff --git a/nikola/data/themes/base/messages/messages_ja.py b/nikola/data/themes/base/messages/messages_ja.py index 4a238cc..1bdf168 100644 --- a/nikola/data/themes/base/messages/messages_ja.py +++ b/nikola/data/themes/base/messages/messages_ja.py @@ -3,15 +3,18 @@ from __future__ import unicode_literals MESSAGES = { "Also available in": "他の言語で読む", + "Also available in:": "他の言語で読む:", "Archive": "過去の記事", - "Categories": "", + "Categories": "カテゴリー", "LANGUAGE": "日本語", - "More posts about": "タグ", + "More posts about %s": "タグ: %s", + "More posts about": "タグ:", "Newer posts": "新しい記事", "Next post": "次の記事", "Older posts": "過去の記事", "Original site": "元のサイト", "Posted": "投稿日時", + "Posted:": "投稿日時:", "Posts about %s": "%sについての記事", "Posts for year %s": "%s年の記事", "Posts for {month} {year}": "{year}年{month}月の記事", @@ -19,7 +22,8 @@ MESSAGES = { "Read in English": "日本語で読む", "Read more": "続きを読む", "Source": "ソース", - "Tags and Categories": "", + "Tags and Categories": "タグとカテゴリー", "Tags": "タグ", - "old posts page %d": "前の記事 %dページ目", + "old posts, page %d": "前の記事 %dページ目", + "page %d": "", } diff --git a/nikola/data/themes/base/messages/messages_nb.py b/nikola/data/themes/base/messages/messages_nb.py new file mode 100644 index 0000000..154e329 --- /dev/null +++ b/nikola/data/themes/base/messages/messages_nb.py @@ -0,0 +1,29 @@ +# -*- encoding:utf-8 -*- +from __future__ import unicode_literals + +MESSAGES = { + "Also available in": "Også tilgjengelig på", + "Also available in:": "Også tilgjengelig på:", + "Archive": "Arkiv", + "Categories": "Kategorier", + "LANGUAGE": "norsk", + "More posts about %s": "Flere innlegg om %s", + "More posts about": "Flere innlegg om", + "Newer posts": "Nyere innlegg", + "Next post": "Neste innlegg", + "Older posts": "Eldre innlegg", + "Original site": "Opprinnelig side", + "Posted": "Publisert", + "Posted:": "Publisert:", + "Posts about %s": "Innlegg om %s", + "Posts for year %s": "Innlegg fra %s", + "Posts for {month} {year}": "Innlegg fra {month} {year}", + "Previous post": "Forrige innlegg", + "Read in English": "Les på norsk", + "Read more": "Les mer", + "Source": "Kilde", + "Tags and Categories": "Merker og kategorier", + "Tags": "Merker", + "old posts, page %d": "eldre innlegg, side %d", + "page %d": "side %d", +} diff --git a/nikola/data/themes/base/messages/messages_nl.py b/nikola/data/themes/base/messages/messages_nl.py index a2da822..887e85f 100644 --- a/nikola/data/themes/base/messages/messages_nl.py +++ b/nikola/data/themes/base/messages/messages_nl.py @@ -3,15 +3,18 @@ from __future__ import unicode_literals MESSAGES = { "Also available in": "Ook beschikbaar in", + "Also available in:": "Ook beschikbaar in:", "Archive": "Archief", "Categories": "Categorieën", "LANGUAGE": "Nederlands", + "More posts about %s": "Meer berichten over %s", "More posts about": "Meer berichten over", "Newer posts": "Nieuwere berichten", "Next post": "Volgend bericht", "Older posts": "Oudere berichten", "Original site": "Originele site", "Posted": "Geplaatst", + "Posted:": "Geplaatst:", "Posts about %s": "Berichten over %s", "Posts for year %s": "Berichten voor het jaar %s", "Posts for {month} {year}": "Berichten voor {month} {year}", @@ -21,5 +24,6 @@ MESSAGES = { "Source": "Bron", "Tags and Categories": "Tags en Categorieën", "Tags": "Tags", - "old posts page %d": "oude berichten pagina %d", + "old posts, page %d": "oude berichten, pagina %d", + "page %d": "pagina %d", } diff --git a/nikola/data/themes/base/messages/messages_pl.py b/nikola/data/themes/base/messages/messages_pl.py index cf5c51c..352b0ed 100644 --- a/nikola/data/themes/base/messages/messages_pl.py +++ b/nikola/data/themes/base/messages/messages_pl.py @@ -2,16 +2,19 @@ from __future__ import unicode_literals MESSAGES = { - "Also available in": "Również dostępny w", + "Also available in": "Również dostępny w językach", + "Also available in:": "Również dostępny w językach:", "Archive": "Archiwum", "Categories": "Kategorie", "LANGUAGE": "polski", + "More posts about %s": "Więcej postów o %s", "More posts about": "Więcej postów o", "Newer posts": "Nowsze posty", "Next post": "Następny post", "Older posts": "Starsze posty", "Original site": "Oryginalna strona", - "Posted": "Opublikowany", + "Posted": "Opublikowano", + "Posted:": "Opublikowano:", "Posts about %s": "Posty o %s", "Posts for year %s": "Posty z roku %s", "Posts for {month} {year}": "Posty z {month} {year}", @@ -21,5 +24,6 @@ MESSAGES = { "Source": "Źródło", "Tags and Categories": "Tagi i Kategorie", "Tags": "Tags", - "old posts page %d": "stare posty, strona %d", + "old posts, page %d": "stare posty, strona %d", + "page %d": "strona %d", } diff --git a/nikola/data/themes/base/messages/messages_pt_br.py b/nikola/data/themes/base/messages/messages_pt_br.py index ebf642a..1283a2a 100644 --- a/nikola/data/themes/base/messages/messages_pt_br.py +++ b/nikola/data/themes/base/messages/messages_pt_br.py @@ -3,15 +3,18 @@ from __future__ import unicode_literals MESSAGES = { "Also available in": "Também disponível em", + "Also available in:": "Também disponível em:", "Archive": "Arquivo", "Categories": "Categorias", "LANGUAGE": "Português", + "More posts about %s": "Mais posts sobre %s", "More posts about": "Mais posts sobre", "Newer posts": "Posts mais recentes", "Next post": "Próximo post", "Older posts": "Posts mais antigos", "Original site": "Site original", "Posted": "Publicado", + "Posted:": "Publicado:", "Posts about %s": "Posts sobre %s", "Posts for year %s": "Posts do ano %s", "Posts for {month} {year}": "Posts de {month} {year}", @@ -21,5 +24,6 @@ MESSAGES = { "Source": "Código", "Tags and Categories": "Tags e Categorias", "Tags": "Tags", - "old posts page %d": "Posts antigos página %d", + "old posts, page %d": "Posts antigos, página %d", + "page %d": "página %d", } diff --git a/nikola/data/themes/base/messages/messages_ru.py b/nikola/data/themes/base/messages/messages_ru.py index 44ce1f2..3462292 100644 --- a/nikola/data/themes/base/messages/messages_ru.py +++ b/nikola/data/themes/base/messages/messages_ru.py @@ -3,15 +3,18 @@ from __future__ import unicode_literals MESSAGES = { "Also available in": "Также доступно на", + "Also available in:": "Также доступно на:", "Archive": "Архив", "Categories": "Категории", "LANGUAGE": "Русский", + "More posts about %s": "Больше записей о %s", "More posts about": "Больше записей о", "Newer posts": "Новые записи", "Next post": "Следующая запись", "Older posts": "Старые записи", "Original site": "Оригинальный сайт", "Posted": "Опубликовано", + "Posted:": "Опубликовано:", "Posts about %s": "Записи о %s", "Posts for year %s": "Записи за %s год", "Posts for {month} {year}": "Записи за {month} {year}", @@ -21,5 +24,6 @@ MESSAGES = { "Source": "Источник", "Tags and Categories": "Тэги и категории", "Tags": "Тэги", - "old posts page %d": "%d страница со старыми записями", + "old posts, page %d": "%d страница со старыми записями", + "page %d": "%d страница", } diff --git a/nikola/data/themes/base/messages/messages_sl.py b/nikola/data/themes/base/messages/messages_sl.py index bad3327..817bcee 100644 --- a/nikola/data/themes/base/messages/messages_sl.py +++ b/nikola/data/themes/base/messages/messages_sl.py @@ -3,15 +3,18 @@ from __future__ import unicode_literals MESSAGES = { "Also available in": "Na voljo tudi v", + "Also available in:": "Na voljo tudi v:", "Archive": "Arhiv", "Categories": "Kategorije", "LANGUAGE": "Slovenščina", + "More posts about %s": "Več objav o %s", "More posts about": "Več objav o", "Newer posts": "Novejše objave", "Next post": "Naslednja objava", "Older posts": "Starejše objave", "Original site": "Izvorna spletna stran", "Posted": "Objavljeno", + "Posted:": "Objavljeno:", "Posts about %s": "Objave o %s", "Posts for year %s": "Objave za leto %s", "Posts for {month} {year}": "Objave za {month} {year}", @@ -21,5 +24,6 @@ MESSAGES = { "Source": "Izvor", "Tags and Categories": "Značke in kategorije", "Tags": "Značke", - "old posts page %d": "stare objave, stran %d", + "old posts, page %d": "stare objave, stran %d", + "page %d": "stran %d", } diff --git a/nikola/data/themes/base/messages/messages_tr.py b/nikola/data/themes/base/messages/messages_tr.py new file mode 120000 index 0000000..ad92768 --- /dev/null +++ b/nikola/data/themes/base/messages/messages_tr.py @@ -0,0 +1 @@ +messages_tr_tr.py
\ No newline at end of file diff --git a/nikola/data/themes/base/messages/messages_tr_tr.py b/nikola/data/themes/base/messages/messages_tr_tr.py index 7cf0e9f..633f057 100644 --- a/nikola/data/themes/base/messages/messages_tr_tr.py +++ b/nikola/data/themes/base/messages/messages_tr_tr.py @@ -2,16 +2,19 @@ from __future__ import unicode_literals MESSAGES = { - "Also available in": "Şu dilde de mevcut:", + "Also available in": "Şu dilde de mevcut", + "Also available in:": "Şu dilde de mevcut:", "Archive": "Arşiv", "Categories": "Kategoriler", "LANGUAGE": "Türkçe", - "More posts about": "ilgili diğer yazılar", + "More posts about %s": "%s ilgili diğer yazılar", + "More posts about": " ilgili diğer yazılar", "Newer posts": "Daha yeni yazılar", "Next post": "Sonraki yazı", "Older posts": "Daha eski yazılar", "Original site": "Orjinal web sayfası", "Posted": "Yayın tarihi", + "Posted:": "Yayın tarihi:", "Posts about %s": "%s ile ilgili yazılar", "Posts for year %s": "%s yılındaki yazılar", "Posts for {month} {year}": "{month} {year} göre yazılar", @@ -21,5 +24,6 @@ MESSAGES = { "Source": "Kaynak", "Tags and Categories": "Etiketler ve Kategoriler", "Tags": "Etiketler", - "old posts page %d": "eski yazılar sayfa %d", + "old posts, page %d": "eski yazılar, sayfa %d", + "page %d": "sayfa %d", } diff --git a/nikola/data/themes/base/messages/messages_ur.py b/nikola/data/themes/base/messages/messages_ur.py new file mode 100644 index 0000000..d9c2f2b --- /dev/null +++ b/nikola/data/themes/base/messages/messages_ur.py @@ -0,0 +1,29 @@ +# -*- encoding:utf-8 -*- +from __future__ import unicode_literals + +MESSAGES = { + "Also available in": "ان زبانوں میں بھی دستیاب", + "Also available in:": "ان زبانوں میں بھی دستیاب:", + "Archive": "آرکائیو", + "Categories": "زمرے", + "LANGUAGE": "اردو", + "More posts about %s": "%s کے بارے میں مزید تحاریر", + "More posts about": " کے بارے میں مزید تحاریر", + "Newer posts": "نئی تحاریر", + "Next post": "اگلی تحریر", + "Older posts": "پرانی تحاریر", + "Original site": "اصلی سائٹ", + "Posted": "اشاعت", + "Posted:": "اشاعت:", + "Posts about %s": "%s کے بارے میں تحاریر", + "Posts for year %s": "سال %s کی تحاریر", + "Posts for {month} {year}": "{month} {year} کی تحاریر", + "Previous post": "پچھلی تحریر", + "Read in English": "اردو میں پڑھیے", + "Read more": "مزید پڑھیے", + "Source": "سورس", + "Tags and Categories": "ٹیگز اور زمرے", + "Tags": "ٹیگز", + "old posts, page %d": "پرانی تحاریر صفحہ %d", + "page %d": "", +} diff --git a/nikola/data/themes/base/messages/messages_zh_cn.py b/nikola/data/themes/base/messages/messages_zh_cn.py index fc79313..cb9a2f7 100644 --- a/nikola/data/themes/base/messages/messages_zh_cn.py +++ b/nikola/data/themes/base/messages/messages_zh_cn.py @@ -3,15 +3,18 @@ from __future__ import unicode_literals MESSAGES = { "Also available in": "其他语言版本", + "Also available in:": "其他语言版本:", "Archive": "文章存档", - "Categories": "", + "Categories": "分类", "LANGUAGE": "简体中文", + "More posts about %s": "更多相关文章: %s", "More posts about": "更多相关文章:", "Newer posts": "新一篇", "Next post": "后一篇", "Older posts": "旧一篇", "Original site": "原文地址", "Posted": "发表于", + "Posted:": "发表于:", "Posts about %s": "文章分类:%s", "Posts for year %s": "%s年文章", "Posts for {month} {year}": "{year}年{month}月文章", @@ -19,7 +22,8 @@ MESSAGES = { "Read in English": "中文版", "Read more": "更多", "Source": "源代码", - "Tags and Categories": "", + "Tags and Categories": "标签和分类", "Tags": "标签", - "old posts page %d": "旧文章页 %d", + "old posts, page %d": "旧文章页 %d", + "page %d": "", } diff --git a/nikola/data/themes/base/templates/base.tmpl b/nikola/data/themes/base/templates/base.tmpl index 31c1747..7c6cc35 100644 --- a/nikola/data/themes/base/templates/base.tmpl +++ b/nikola/data/themes/base/templates/base.tmpl @@ -21,7 +21,7 @@ lang="${lang}"> <%block name="belowtitle"> %if len(translations) > 1: <small> - ${(messages("Also available in"))}: + ${messages("Also available in:")} ${base.html_translations()} </small> %endif diff --git a/nikola/data/themes/base/templates/base_helper.tmpl b/nikola/data/themes/base/templates/base_helper.tmpl index ac1dea3..880a998 100644 --- a/nikola/data/themes/base/templates/base_helper.tmpl +++ b/nikola/data/themes/base/templates/base_helper.tmpl @@ -21,6 +21,7 @@ <link href="/assets/css/custom.css" rel="stylesheet" type="text/css"> %endif %endif + <link rel="canonical" href="${abs_link(permalink)}"> <!--[if lt IE 9]> <script src="http://html5shim.googlecode.com/svn/trunk/html5.js" type="text/javascript"></script> <![endif]--> @@ -49,7 +50,7 @@ </%def> <%def name="html_social()"> - ${social_buttons_code} + ${social_buttons_code} </%def> <!--FIXME: remove in v7 --> @@ -64,7 +65,7 @@ <ul> %for suburl, text in url: % if rel_link(permalink, suburl) == "#": - <li class="active"><a href="${suburl}">${text}</a> + <li class="active"><a href="${permalink}">${text}</a> %else: <li><a href="${suburl}">${text}</a> %endif @@ -72,7 +73,7 @@ </ul> % else: % if rel_link(permalink, url) == "#": - <li class="active"><a href="${url}">${text}</a> + <li class="active"><a href="${permalink}">${text}</a> %else: <li><a href="${url}">${text}</a> %endif @@ -84,7 +85,7 @@ <%def name="html_translations()"> %for langname in translations.keys(): %if langname != lang: - <a href="${_link("index", None, langname)}">${messages("LANGUAGE", langname)}</a> + <a href="${_link("index", None, langname)}" rel="alternate" hreflang="${langname}">${messages("LANGUAGE", langname)}</a> %endif %endfor </%def> diff --git a/nikola/data/themes/base/templates/comments_helper.tmpl b/nikola/data/themes/base/templates/comments_helper.tmpl index e74a146..d3a2704 100644 --- a/nikola/data/themes/base/templates/comments_helper.tmpl +++ b/nikola/data/themes/base/templates/comments_helper.tmpl @@ -5,6 +5,8 @@ <%namespace name="intensedebate" file="intensedebate_helper.tmpl"/> <%namespace name="moot" file="moot_helper.tmpl"/> <%namespace name="googleplus" file="googleplus_helper.tmpl"/> +<%namespace name="facebook" file="facebook_helper.tmpl"/> +<%namespace name="isso" file="isso_helper.tmpl"/> <%def name="comment_form(url, title, identifier)"> %if comment_system == 'disqus': @@ -17,6 +19,10 @@ ${moot.comment_form(url, title, identifier)} % elif comment_system == 'googleplus': ${googleplus.comment_form(url, title, identifier)} + % elif comment_system == 'facebook': + ${facebook.comment_form(url, title, identifier)} + % elif comment_system == 'isso': + ${isso.comment_form(url, title, identifier)} %endif </%def> @@ -31,6 +37,10 @@ ${moot.comment_link(link, identifier)} % elif comment_system == 'googleplus': ${googleplus.comment_link(link, identifier)} + % elif comment_system == 'facebook': + ${facebook.comment_link(link, identifier)} + % elif comment_system == 'isso': + ${isso.comment_link(link, identifier)} %endif </%def> @@ -45,5 +55,9 @@ ${moot.comment_link_script()} % elif comment_system == 'googleplus': ${googleplus.comment_link_script()} + % elif comment_system == 'facebook': + ${facebook.comment_link_script()} + % elif comment_system == 'isso': + ${isso.comment_link_script()} %endif </%def> diff --git a/nikola/data/themes/base/templates/disqus_helper.tmpl b/nikola/data/themes/base/templates/disqus_helper.tmpl index 211f27a..74187b0 100644 --- a/nikola/data/themes/base/templates/disqus_helper.tmpl +++ b/nikola/data/themes/base/templates/disqus_helper.tmpl @@ -9,24 +9,24 @@ <%def name="comment_form(url, title, identifier)"> %if comment_system_id: <div id="disqus_thread"></div> - <script type="text/javascript"> - var disqus_shortname ="${comment_system_id}"; + <script> + var disqus_shortname ="${comment_system_id}", %if url: - var disqus_url="${url}"; + disqus_url="${url}", %endif - var disqus_title=${json.dumps(title)}; - var disqus_identifier="${identifier}"; - var disqus_config = function () { + disqus_title=${json.dumps(title)}, + disqus_identifier="${identifier}", + disqus_config = function () { this.language = "${translations.get(lang, lang)}"; }; (function() { - var dsq = document.createElement('script'); dsq.type = 'text/javascript'; dsq.async = true; - dsq.src = 'http://' + disqus_shortname + '.disqus.com/embed.js'; + var dsq = document.createElement('script'); dsq.async = true; + dsq.src = '//' + disqus_shortname + '.disqus.com/embed.js'; (document.getElementsByTagName('head')[0] || document.getElementsByTagName('body')[0]).appendChild(dsq); })(); </script> - <noscript>Please enable JavaScript to view the <a href="http://disqus.com/?ref_noscript">comments powered by Disqus.</a></noscript> - <a href="http://disqus.com" class="dsq-brlink">Comments powered by <span class="logo-disqus">Disqus</span></a> + <noscript>Please enable JavaScript to view the <a href="http://disqus.com/?ref_noscript" rel="nofollow">comments powered by Disqus.</a></noscript> + <a href="http://disqus.com" class="dsq-brlink" rel="nofollow">Comments powered by <span class="logo-disqus">Disqus</span></a> %endif </%def> @@ -40,7 +40,7 @@ <%def name="comment_link_script()"> %if comment_system_id: - <script type="text/javascript">var disqus_shortname="${comment_system_id}";(function(){var a=document.createElement("script");a.async=true;a.type="text/javascript";a.src="http://"+disqus_shortname+".disqus.com/count.js";(document.getElementsByTagName("HEAD")[0]||document.getElementsByTagName("BODY")[0]).appendChild(a)}());</script> + <script>var disqus_shortname="${comment_system_id}";(function(){var a=document.createElement("script");a.async=true;a.src="//"+disqus_shortname+".disqus.com/count.js";(document.getElementsByTagName("head")[0]||document.getElementsByTagName("body")[0]).appendChild(a)}());</script> %endif </%def> diff --git a/nikola/data/themes/base/templates/gallery.tmpl b/nikola/data/themes/base/templates/gallery.tmpl index be9cc6f..e4eab27 100644 --- a/nikola/data/themes/base/templates/gallery.tmpl +++ b/nikola/data/themes/base/templates/gallery.tmpl @@ -6,14 +6,18 @@ <%block name="content"> ${ui.bar(crumbs)} + %if title: + <h1>${title}</h1> + %endif %if text: <p> ${text} </p> %endif <ul> - % for folder in folders: - <li><a href="${folder}"><i class="icon-folder-open"></i> ${folder}</a></li> + % for folder, ftitle in folders: + <li><a href="${folder}"><i + class="icon-folder-open"></i> ${ftitle}</a></li> % endfor </ul> <ul class="thumbnails"> diff --git a/nikola/data/themes/base/templates/index.tmpl b/nikola/data/themes/base/templates/index.tmpl index 6de79e2..1a280b0 100644 --- a/nikola/data/themes/base/templates/index.tmpl +++ b/nikola/data/themes/base/templates/index.tmpl @@ -4,10 +4,10 @@ <%inherit file="base.tmpl"/> <%block name="content"> % for post in posts: - <div class="postbox h-entry"> + <article class="postbox h-entry post-${post.meta('type')}"> <h1 class="p-name"><a href="${post.permalink()}" class="u-url">${post.title()}</a> <small> - ${messages("Posted")}: <time class="published dt-published" datetime="${post.date.isoformat()}">${post.formatted_date(date_format)}</time> + ${messages("Posted:")} <time class="published dt-published" datetime="${post.date.isoformat()}">${post.formatted_date(date_format)}</time> </small></h1> <hr> %if index_teasers: @@ -21,7 +21,7 @@ % if not post.meta('nocomments'): ${comments.comment_link(post.permalink(), post._base_path)} % endif - </div> + </article> % endfor ${helper.html_pager()} ${comments.comment_link_script()} diff --git a/nikola/data/themes/base/templates/index_helper.tmpl b/nikola/data/themes/base/templates/index_helper.tmpl index a914846..56f5127 100644..100755 --- a/nikola/data/themes/base/templates/index_helper.tmpl +++ b/nikola/data/themes/base/templates/index_helper.tmpl @@ -1,23 +1,25 @@ ## -*- coding: utf-8 -*- <%def name="html_pager()"> -<div> -<ul class="pager"> - %if prevlink: - <li class="previous"> - <a href="${prevlink}" rel="prev">← ${messages("Newer posts")}</a> - </li> - %endif - %if nextlink: - <li class="next"> - <a href="${nextlink}" rel="next">${messages("Older posts")} →</a> - </li> - %endif -</ul> -</div> + %if prevlink or nextlink: + <div> + <ul class="pager"> + %if prevlink: + <li class="previous"> + <a href="${prevlink}" rel="prev">← ${messages("Newer posts")}</a> + </li> + %endif + %if nextlink: + <li class="next"> + <a href="${nextlink}" rel="next">${messages("Older posts")} →</a> + </li> + %endif + </ul> + </div> + %endif </%def> <%def name="mathjax_script(posts)"> %if any(post.is_mathjax for post in posts): - <script src="/assets/js/mathjax.js" type="text/javascript"></script> + <script src="/assets/js/mathjax.js"></script> %endif </%def> diff --git a/nikola/data/themes/base/templates/isso_helper.tmpl b/nikola/data/themes/base/templates/isso_helper.tmpl new file mode 100644 index 0000000..8dc95f5 --- /dev/null +++ b/nikola/data/themes/base/templates/isso_helper.tmpl @@ -0,0 +1,20 @@ +## -*- coding: utf-8 -*- +<%def name="comment_form(url, title, identifier)"> + %if comment_system_id: + <div data-title="${title|u}" id="isso-thread"></div> + <script src="${comment_system_id}js/embed.min.js" data-isso="${comment_system_id}"></script> + %endif +</%def> + +<%def name="comment_link(link, identifier)"> + %if comment_system_id: + <a href="${link}#isso-thread">Comments</a> + %endif +</%def> + + +<%def name="comment_link_script()"> + %if comment_system_id: + <script src="${comment_system_id}js/count.min.js" data-isso="${comment_system_id}"></script> + %endif +</%def> diff --git a/nikola/data/themes/base/templates/post.tmpl b/nikola/data/themes/base/templates/post.tmpl index fd6316d..981fd97 100644 --- a/nikola/data/themes/base/templates/post.tmpl +++ b/nikola/data/themes/base/templates/post.tmpl @@ -7,14 +7,15 @@ ${helper.twitter_card_information(post)} % if post.meta('keywords'): <meta name="keywords" content="${post.meta('keywords')|h}"> % endif +${helper.meta_translations(post)} </%block> <%block name="content"> - <div class="postbox"> + <article class="postbox post-${post.meta('type')}"> <div class="h-entry" itemscope="itemscope" itemtype="http://schema.org/Article"> ${helper.html_title()} <hr> <small> - ${messages("Posted")}: <time class="published dt-published" datetime="${post.date.isoformat()}" itemprop="datePublished">${post.formatted_date(date_format)}</time> + ${messages("Posted:")} <time class="published dt-published" datetime="${post.date.isoformat()}" itemprop="datePublished">${post.formatted_date(date_format)}</time> ${helper.html_translations(post)} ${helper.html_tags(post)} | @@ -37,5 +38,5 @@ ${helper.twitter_card_information(post)} ${comments.comment_form(post.permalink(absolute=True), post.title(), post._base_path)} % endif ${helper.mathjax_script(post)} - </div> + </article> </%block> diff --git a/nikola/data/themes/base/templates/post_helper.tmpl b/nikola/data/themes/base/templates/post_helper.tmpl index f6b0535..69784ea 100644..100755 --- a/nikola/data/themes/base/templates/post_helper.tmpl +++ b/nikola/data/themes/base/templates/post_helper.tmpl @@ -12,37 +12,52 @@ %for langname in translations.keys(): %if langname != lang and post.is_translation_available(langname): | - <a href="${post.permalink(langname)}">${messages("Read in English", langname)}</a> + <a href="${post.permalink(langname)}" rel="alternate" hreflang="${langname}">${messages("Read in English", langname)}</a> %endif %endfor %endif </%def> +<%def name="meta_translations(post)"> + %if len(translations) > 1: + %for langname in translations.keys(): + %if langname != lang and post.is_translation_available(langname): + <link rel="alternate" hreflang="${langname}" href="${post.permalink(langname)}"> + %endif + %endfor + %endif +</%def> + +<%def name="html_list_tags(post)" buffered="True"> + <span itemprop="keywords"> + %for tag in post.tags: + <a class="tag p-category" href="${_link('tag', tag)}"><span class="badge badge-info">${tag}</span></a> + %endfor + </span> +</%def> <%def name="html_tags(post)"> %if post.tags: - | ${messages("More posts about")} - <span itemprop="keywords"> - %for tag in post.tags: - <a class="tag p-category" href="${_link('tag', tag)}"><span class="badge badge-info">${tag}</span></a> - %endfor - </span> + | + ${formatmsg(messages("More posts about %s"), html_list_tags(post))} %endif </%def> <%def name="html_pager(post)"> - <ul class="pager"> - %if post.prev_post: - <li class="previous"> - <a href="${post.prev_post.permalink()}" rel="prev">← ${messages("Previous post")}</a> - </li> - %endif - %if post.next_post: - <li class="next"> - <a href="${post.next_post.permalink()}" rel="next">${messages("Next post")} →</a> - </li> + %if post.prev_post or post.next_post: + <ul class="pager"> + %if post.prev_post: + <li class="previous"> + <a href="${post.prev_post.permalink()}" rel="prev">← ${messages("Previous post")}</a> + </li> + %endif + %if post.next_post: + <li class="next"> + <a href="${post.next_post.permalink()}" rel="next">${messages("Next post")} →</a> + </li> + %endif + </ul> %endif - </ul> </%def> <%def name="twitter_card_information(post)"> diff --git a/nikola/data/themes/base/templates/tagindex.tmpl b/nikola/data/themes/base/templates/tagindex.tmpl new file mode 100644 index 0000000..9dda70a --- /dev/null +++ b/nikola/data/themes/base/templates/tagindex.tmpl @@ -0,0 +1,2 @@ +## -*- coding: utf-8 -*- +<%inherit file="index.tmpl"/> diff --git a/nikola/data/themes/bootstrap/assets/css/theme.css b/nikola/data/themes/bootstrap/assets/css/theme.css index 2ff1a80..952073f 100644 --- a/nikola/data/themes/bootstrap/assets/css/theme.css +++ b/nikola/data/themes/bootstrap/assets/css/theme.css @@ -84,4 +84,20 @@ pre, pre code { white-space: pre; word-wrap: normal; overflow: auto; -}
\ No newline at end of file +} + +article.post-micro { + font-family: Georgia, 'Times New Roman', Times, serif; + font-size: 1.5em; +} + +/* fix anchors for headers */ +h1, h2, h3 { + margin-top: -40px; + padding-top: 60px; +} + +h4, h5, h6 { + margin-top: -50px; + padding-top: 60px; +} diff --git a/nikola/data/themes/bootstrap/templates/bootstrap_helper.tmpl b/nikola/data/themes/bootstrap/templates/bootstrap_helper.tmpl index 763ac20..f0d1986 100644 --- a/nikola/data/themes/bootstrap/templates/bootstrap_helper.tmpl +++ b/nikola/data/themes/bootstrap/templates/bootstrap_helper.tmpl @@ -29,6 +29,7 @@ <link href="/assets/css/custom.css" rel="stylesheet" type="text/css"> %endif %endif + <link rel="canonical" href="${abs_link(permalink)}"> <!--[if lt IE 9]> <script src="http://html5shim.googlecode.com/svn/trunk/html5.js" type="text/javascript"></script> <![endif]--> @@ -79,7 +80,7 @@ <ul class="dropdown-menu"> %for suburl, text in url: % if rel_link(permalink, suburl) == "#": - <li class="active"><a href="${suburl}">${text}</a> + <li class="active"><a href="${permalink}">${text}</a> %else: <li><a href="${suburl}">${text}</a> %endif @@ -87,7 +88,7 @@ </ul> % else: % if rel_link(permalink, url) == "#": - <li class="active"><a href="${url}">${text}</a> + <li class="active"><a href="${permalink}">${text}</a> %else: <li><a href="${url}">${text}</a> %endif diff --git a/nikola/data/themes/bootstrap/templates/gallery.tmpl b/nikola/data/themes/bootstrap/templates/gallery.tmpl index 17ae61c..0dd5eea 100644 --- a/nikola/data/themes/bootstrap/templates/gallery.tmpl +++ b/nikola/data/themes/bootstrap/templates/gallery.tmpl @@ -6,14 +6,18 @@ <%block name="content"> ${ui.bar(crumbs)} + %if title: + <h1>${title}</h1> + %endif %if text: <p> ${text} </p> %endif <ul> - % for folder in folders: - <li><a href="${folder}"><i class="icon-folder-open"></i> ${folder}</a></li> + % for folder, ftitle in folders: + <li><a href="${folder}"><i + class="icon-folder-open"></i> ${ftitle}</a></li> % endfor </ul> diff --git a/nikola/filters.py b/nikola/filters.py index 04703cb..aa7ba8a 100644 --- a/nikola/filters.py +++ b/nikola/filters.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright © 2012-2013 Roberto Alsina and others. +# Copyright © 2012-2014 Roberto Alsina and others. # Permission is hereby granted, free of charge, to any # person obtaining a copy of this software and associated @@ -26,6 +26,7 @@ """Utility functions to help you run filters on files.""" +from .utils import req_missing from functools import wraps import os import re @@ -37,7 +38,7 @@ import shlex try: import typogrify.filters as typo except ImportError: - typo = None + typo = None # NOQA def apply_to_file(f): @@ -102,7 +103,21 @@ def runinplace(command, infile): def yui_compressor(infile): - return runinplace(r'yui-compressor --nomunge %1 -o %2', infile) + yuicompressor = False + try: + subprocess.call('yui-compressor', stdout=open(os.devnull, 'w'), stderr=open(os.devnull, 'w')) + yuicompressor = 'yui-compressor' + except Exception: + pass + if not yuicompressor: + try: + subprocess.call('yuicompressor', stdout=open(os.devnull, 'w'), stderr=open(os.devnull, 'w')) + yuicompressor = 'yuicompressor' + except: + raise Exception("yui-compressor is not installed.") + return False + + return runinplace(r'{} --nomunge %1 -o %2'.format(yuicompressor), infile) def optipng(infile): @@ -114,7 +129,7 @@ def jpegoptim(infile): def tidy(inplace): - # Goggle site verifcation files are no HTML + # Google site verifcation files are not HTML if re.match(r"google[a-f0-9]+.html", os.path.basename(inplace)) \ and open(inplace).readline().startswith( "google-site-verification:"): @@ -161,7 +176,8 @@ def tidy(inplace): def typogrify(data): global typogrify_filter if typo is None: - raise Exception("To use the typogrify filter, you need to install typogrify.") + req_missing(['typogrify', 'use the typogrify filter']) + data = typo.amp(data) data = typo.widont(data) data = typo.smartypants(data) diff --git a/nikola/nikola.py b/nikola/nikola.py index 13c91a7..6971c0c 100644 --- a/nikola/nikola.py +++ b/nikola/nikola.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright © 2012-2013 Roberto Alsina and others. +# Copyright © 2012-2014 Roberto Alsina and others. # Permission is hereby granted, free of charge, to any # person obtaining a copy of this software and associated @@ -43,7 +43,9 @@ except ImportError: pyphen = None import logging -if os.getenv('NIKOLA_DEBUG'): +from . import DEBUG + +if DEBUG: logging.basicConfig(level=logging.DEBUG) else: logging.basicConfig(level=logging.ERROR) @@ -105,6 +107,7 @@ class Nikola(object): self._scanned = False self._template_system = None self._THEMES = None + self.debug = DEBUG self.loghandlers = [] if not config: self.configured = False @@ -143,6 +146,7 @@ class Nikola(object): 'DEFAULT_LANG': "en", 'DEPLOY_COMMANDS': [], 'DISABLED_PLUGINS': (), + 'EXTRA_PLUGINS_DIRS': [], 'COMMENT_SYSTEM_ID': 'nikolademo', 'ENABLED_EXTRAS': (), 'EXTRA_HEAD_DATA': '', @@ -165,8 +169,10 @@ class Nikola(object): 'INDEX_TEASERS': False, 'INDEXES_TITLE': "", 'INDEXES_PAGES': "", + 'INDEXES_PAGES_MAIN': False, 'INDEX_PATH': '', 'IPYNB_CONFIG': {}, + 'LESS_COMPILER': 'lessc', 'LICENSE': '', 'LINK_CHECK_WHITELIST': [], 'LISTINGS_FOLDER': 'listings', @@ -185,6 +191,7 @@ class Nikola(object): 'RSS_LINK': None, 'RSS_PATH': '', 'RSS_TEASERS': True, + 'SASS_COMPILER': 'sass', 'SEARCH_FORM': '', 'SLUG_TAG_PATH': True, 'SOCIAL_BUTTONS_CODE': SOCIAL_BUTTONS_CODE, @@ -198,16 +205,19 @@ class Nikola(object): 'THEME_REVEAL_CONFIG_SUBTHEME': 'sky', 'THEME_REVEAL_CONFIG_TRANSITION': 'cube', 'THUMBNAIL_SIZE': 180, + 'URL_TYPE': 'rel_path', 'USE_BUNDLES': True, 'USE_CDN': False, 'USE_FILENAME_AS_TITLE': True, - 'TIMEZONE': None, + 'TIMEZONE': 'UTC', 'DEPLOY_DRAFTS': True, 'DEPLOY_FUTURE': False, 'SCHEDULE_ALL': False, 'SCHEDULE_RULE': '', 'SCHEDULE_FORCE_TODAY': False, 'LOGGING_HANDLERS': {'stderr': {'loglevel': 'WARNING', 'bubble': True}}, + 'DEMOTE_HEADERS': 1, + 'TRANSLATIONS_PATTERN': '{path}.{ext}.{lang}', } self.config.update(config) @@ -337,16 +347,18 @@ class Nikola(object): "SignalHandler": SignalHandler, }) self.plugin_manager.setPluginInfoExtension('plugin') + extra_plugins_dirs = self.config['EXTRA_PLUGINS_DIRS'] if sys.version_info[0] == 3: places = [ os.path.join(os.path.dirname(__file__), 'plugins'), os.path.join(os.getcwd(), 'plugins'), - ] + ] + [path for path in extra_plugins_dirs if path] else: places = [ os.path.join(os.path.dirname(__file__), utils.sys_encode('plugins')), os.path.join(os.getcwd(), utils.sys_encode('plugins')), - ] + ] + [utils.sys_encode(path) for path in extra_plugins_dirs if path] + self.plugin_manager.setPluginPlaces(places) self.plugin_manager.collectPlugins() @@ -590,6 +602,8 @@ class Nikola(object): local_context["template_name"] = template_name local_context.update(self.GLOBAL_CONTEXT) local_context.update(context) + # string, arguments + local_context["formatmsg"] = lambda s, *a: s % a data = self.template_system.render_template( template_name, None, local_context) @@ -617,14 +631,35 @@ class Nikola(object): else: return dst + # Refuse to replace links that consist of a fragment only + if ((not dst_url.scheme) and (not dst_url.netloc) and + (not dst_url.path) and (not dst_url.params) and + (not dst_url.query) and dst_url.fragment): + return dst + # Normalize dst = urljoin(src, dst) + # Avoid empty links. if src == dst: - return "#" + if self.config.get('URL_TYPE') == 'absolute': + dst = urljoin(self.config['BASE_URL'], dst) + return dst + elif self.config.get('URL_TYPE') == 'full_path': + return dst + else: + return "#" + # Check that link can be made relative, otherwise return dest parsed_dst = urlsplit(dst) if parsed_src[:2] != parsed_dst[:2]: + if self.config.get('URL_TYPE') == 'absolute': + dst = urljoin(self.config['BASE_URL'], dst) + return dst + + if self.config.get('URL_TYPE') in ('full_path', 'absolute'): + if self.config.get('URL_TYPE') == 'absolute': + dst = urljoin(self.config['BASE_URL'], dst) return dst # Now both paths are on the same site and absolute @@ -733,7 +768,7 @@ class Nikola(object): # Normalize dst = urljoin(self.config['BASE_URL'], dst) - return urlparse(dst).path + return urlparse(dst).geturl() def rel_link(self, src, dst): # Normalize @@ -825,13 +860,16 @@ class Nikola(object): full_list = glob.glob(dir_glob) # Now let's look for things that are not in default_lang for lang in self.config['TRANSLATIONS'].keys(): - lang_glob = dir_glob + "." + lang + lang_glob = utils.get_translation_candidate(self.config, dir_glob, lang) translated_list = glob.glob(lang_glob) - for fname in translated_list: - orig_name = os.path.splitext(fname)[0] - if orig_name in full_list: - continue - full_list.append(orig_name) + # dir_glob could have put it already in full_list + full_list = list(set(full_list + translated_list)) + # Eliminate translations from full_list (even from dir_glob) + for fname in full_list: + translation = utils.get_translation_candidate(self.config, fname, lang) + if translation in full_list: + full_list.remove(translation) + # We eliminate from the list the files inside any .ipynb folder full_list = [p for p in full_list if not any([x.startswith('.') @@ -849,7 +887,7 @@ class Nikola(object): use_in_feeds, self.MESSAGES, template_name, - self.get_compiler(base_path).compile_html + self.get_compiler(base_path) ) self.global_data[post.source_path] = post if post.use_in_feeds: @@ -873,8 +911,6 @@ class Nikola(object): self.posts_per_category[post.meta('category')].append(post.source_path) else: self.pages.append(post) - if self.config['OLD_THEME_SUPPORT']: - post._add_old_metadata() self.post_per_file[post.destination_path(lang=lang)] = post self.post_per_file[post.destination_path(lang=lang, extension=post.source_ext())] = post diff --git a/nikola/plugin_categories.py b/nikola/plugin_categories.py index 23ca287..68327aa 100644 --- a/nikola/plugin_categories.py +++ b/nikola/plugin_categories.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright © 2012-2013 Roberto Alsina and others. +# Copyright © 2012-2014 Roberto Alsina and others. # Permission is hereby granted, free of charge, to any # person obtaining a copy of this software and associated @@ -42,6 +42,12 @@ from doit.cmd_base import Command as DoitCommand from .utils import LOGGER, first_line +try: + # ordereddict does not exist in py2.6 + from collections import OrderedDict +except ImportError: + OrderedDict = None # NOQA + class BasePlugin(IPlugin): """Base plugin class.""" @@ -178,14 +184,22 @@ class PageCompiler(BasePlugin): """Plugins that compile text files into HTML.""" name = "dummy compiler" - default_metadata = { - 'title': '', - 'slug': '', - 'date': '', - 'tags': '', - 'link': '', - 'description': '', - } + demote_headers = False + if OrderedDict is not None: + default_metadata = OrderedDict() + else: + # Graceful fallback. We could use a backport, but it is + # not going to change anything, other than a bit uglier + # and nonsensical layout. Not enough to care. + default_metadata = {} + + default_metadata['title'] = '' + default_metadata['slug'] = '' + default_metadata['date'] = '' + default_metadata['tags'] = '' + default_metadata['link'] = '' + default_metadata['description'] = '' + default_metadata['type'] = 'text' def compile_html(self, source, dest, is_two_file=False): """Compile the source, save it on dest.""" diff --git a/nikola/plugins/basic_import.py b/nikola/plugins/basic_import.py index e368fca..0d94d16 100644 --- a/nikola/plugins/basic_import.py +++ b/nikola/plugins/basic_import.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright © 2012-2013 Roberto Alsina and others. +# Copyright © 2012-2014 Roberto Alsina and others. # Permission is hereby granted, free of charge, to any # person obtaining a copy of this software and associated diff --git a/nikola/plugins/command/__init__.py b/nikola/plugins/command/__init__.py index 9be4d63..6ad8bac 100644 --- a/nikola/plugins/command/__init__.py +++ b/nikola/plugins/command/__init__.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright © 2012-2013 Roberto Alsina and others. +# Copyright © 2012-2014 Roberto Alsina and others. # Permission is hereby granted, free of charge, to any # person obtaining a copy of this software and associated diff --git a/nikola/plugins/command/auto.py b/nikola/plugins/command/auto.py index cb726d9..01116d1 100644 --- a/nikola/plugins/command/auto.py +++ b/nikola/plugins/command/auto.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright © 2012-2013 Roberto Alsina and others. +# Copyright © 2012-2014 Roberto Alsina and others. # Permission is hereby granted, free of charge, to any # person obtaining a copy of this software and associated @@ -26,30 +26,13 @@ from __future__ import print_function, unicode_literals -import codecs -import json import os import subprocess +import webbrowser from nikola.plugin_categories import Command from nikola.utils import req_missing -GUARDFILE = """#!/usr/bin/env python -# -*- coding: utf-8 -*- -from livereload.task import Task -import json -import subprocess - -def f(): - import subprocess - subprocess.call(("nikola", "build")) - -fdata = json.loads('''{0}''') - -for watch in fdata: - Task.add(watch, f) -""" - class Auto(Command): """Start debugging console.""" @@ -76,9 +59,9 @@ class Auto(Command): def _execute(self, options, args): """Start the watcher.""" try: - from livereload.server import start + from livereload import Server except ImportError: - req_missing(['livereload'], 'use the "auto" command') + req_missing(['livereload>=2.0.0'], 'use the "auto" command') return # Run an initial build so we are uptodate @@ -86,18 +69,18 @@ class Auto(Command): port = options and options.get('port') - # Create a Guardfile - with codecs.open("Guardfile", "wb+", "utf8") as guardfile: - l = ["conf.py", "themes", "templates", self.site.config['GALLERY_PATH']] - for item in self.site.config['post_pages']: - l.append(os.path.dirname(item[0])) - for item in self.site.config['FILES_FOLDERS']: - l.append(os.path.dirname(item)) - data = GUARDFILE.format(json.dumps(l)) - guardfile.write(data) + server = Server() + server.watch('conf.py') + server.watch('themes/') + server.watch('templates/') + server.watch(self.site.config['GALLERY_PATH']) + for item in self.site.config['post_pages']: + server.watch(os.path.dirname(item[0])) + for item in self.site.config['FILES_FOLDERS']: + server.watch(os.path.dirname(item)) out_folder = self.site.config['OUTPUT_FOLDER'] + if options and options.get('browser'): + webbrowser.open('http://localhost:{0}'.format(port)) - os.chmod("Guardfile", 0o755) - - start(port, out_folder, options and options.get('browser')) + server.serve(port, out_folder) diff --git a/nikola/plugins/command/bootswatch_theme.py b/nikola/plugins/command/bootswatch_theme.py index eb27f94..94f37f2 100644 --- a/nikola/plugins/command/bootswatch_theme.py +++ b/nikola/plugins/command/bootswatch_theme.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright © 2012-2013 Roberto Alsina and others. +# Copyright © 2012-2014 Roberto Alsina and others. # Permission is hereby granted, free of charge, to any # person obtaining a copy of this software and associated diff --git a/nikola/plugins/command/check.py b/nikola/plugins/command/check.py index 5c7e49a..a7e8c13 100644 --- a/nikola/plugins/command/check.py +++ b/nikola/plugins/command/check.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright © 2012-2013 Roberto Alsina and others. +# Copyright © 2012-2014 Roberto Alsina and others. # Permission is hereby granted, free of charge, to any # person obtaining a copy of this software and associated @@ -40,6 +40,29 @@ from nikola.plugin_categories import Command from nikola.utils import get_logger +def real_scan_files(site): + task_fnames = set([]) + real_fnames = set([]) + output_folder = site.config['OUTPUT_FOLDER'] + # First check that all targets are generated in the right places + for task in os.popen('nikola list --all', 'r').readlines(): + task = task.strip() + if output_folder in task and ':' in task: + fname = task.split(':', 1)[-1] + task_fnames.add(fname) + # And now check that there are no non-target files + for root, dirs, files in os.walk(output_folder): + for src_name in files: + fname = os.path.join(root, src_name) + real_fnames.add(fname) + + only_on_output = list(real_fnames - task_fnames) + + only_on_input = list(task_fnames - real_fnames) + + return (only_on_output, only_on_input) + + class CommandCheck(Command): """Check the generated site.""" @@ -63,7 +86,7 @@ class CommandCheck(Command): 'long': 'check-files', 'type': bool, 'default': False, - 'help': 'Check for unknown files', + 'help': 'Check for unknown (orphaned and not generated) files', }, { 'name': 'clean', @@ -154,7 +177,7 @@ class CommandCheck(Command): failure = False self.logger.notice("Checking Files:") self.logger.notice("===============\n") - only_on_output, only_on_input = self.real_scan_files() + only_on_output, only_on_input = real_scan_files(self.site) # Ignore folders only_on_output = [p for p in only_on_output if not os.path.isdir(p)] @@ -162,7 +185,7 @@ class CommandCheck(Command): if only_on_output: only_on_output.sort() - self.logger.warn("Files from unknown origins:") + self.logger.warn("Files from unknown origins (orphans):") for f in only_on_output: self.logger.warn(f) failure = True @@ -180,25 +203,3 @@ class CommandCheck(Command): for f in only_on_output: os.unlink(f) return True - - def real_scan_files(self): - task_fnames = set([]) - real_fnames = set([]) - output_folder = self.site.config['OUTPUT_FOLDER'] - # First check that all targets are generated in the right places - for task in os.popen('nikola list --all', 'r').readlines(): - task = task.strip() - if output_folder in task and ':' in task: - fname = task.split(':', 1)[-1] - task_fnames.add(fname) - # And now check that there are no non-target files - for root, dirs, files in os.walk(output_folder): - for src_name in files: - fname = os.path.join(root, src_name) - real_fnames.add(fname) - - only_on_output = list(real_fnames - task_fnames) - - only_on_input = list(task_fnames - real_fnames) - - return (only_on_output, only_on_input) diff --git a/nikola/plugins/command/console.plugin b/nikola/plugins/command/console.plugin index a2be9ca..2eeedae 100644 --- a/nikola/plugins/command/console.plugin +++ b/nikola/plugins/command/console.plugin @@ -3,7 +3,7 @@ Name = console Module = console [Documentation] -Author = Roberto Alsina +Author = Chris Warrick, Roberto Alsina Version = 0.1 Website = http://getnikola.com Description = Start a debugging python console diff --git a/nikola/plugins/command/console.py b/nikola/plugins/command/console.py index fe17dfc..e66b650 100644 --- a/nikola/plugins/command/console.py +++ b/nikola/plugins/command/console.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright © 2012-2013 Roberto Alsina and others. +# Copyright © 2012-2014 Chris Warrick, Roberto Alsina and others. # Permission is hereby granted, free of charge, to any # person obtaining a copy of this software and associated @@ -39,8 +39,21 @@ class Console(Command): """Start debugging console.""" name = "console" shells = ['ipython', 'bpython', 'plain'] - doc_purpose = "Start an interactive Python (IPython->bpython->plain) console with access to your site and configuration" + doc_purpose = "start an interactive Python console with access to your site" + doc_description = """\ +Order of resolution: IPython → bpython [deprecated] → plain Python interpreter +The site engine is accessible as `SITE`, and the config as `conf`.""" header = "Nikola v" + __version__ + " -- {0} Console (conf = configuration, SITE = site engine)" + cmd_options = [ + { + 'name': 'plain', + 'short': 'p', + 'long': 'plain', + 'type': bool, + 'default': False, + 'help': 'Force the plain Python console', + } + ] def ipython(self): """IPython shell.""" @@ -102,9 +115,12 @@ class Console(Command): def _execute(self, options, args): """Start the console.""" - for shell in self.shells: - try: - return getattr(self, shell)() - except ImportError: - pass - raise ImportError + if options['plain']: + self.plain() + else: + for shell in self.shells: + try: + return getattr(self, shell)() + except ImportError: + pass + raise ImportError diff --git a/nikola/plugins/command/deploy.py b/nikola/plugins/command/deploy.py index efb909d..eb5787e 100644 --- a/nikola/plugins/command/deploy.py +++ b/nikola/plugins/command/deploy.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright © 2012-2013 Roberto Alsina and others. +# Copyright © 2012-2014 Roberto Alsina and others. # Permission is hereby granted, free of charge, to any # person obtaining a copy of this software and associated @@ -85,18 +85,17 @@ class Deploy(Command): sys.exit(e.returncode) self.logger.notice("Successful deployment") - if self.site.config['TIMEZONE'] is not None: - tzinfo = pytz.timezone(self.site.config['TIMEZONE']) - else: - tzinfo = pytz.UTC + tzinfo = pytz.timezone(self.site.config['TIMEZONE']) try: with open(timestamp_path, 'rb') as inf: last_deploy = literal_eval(inf.read().strip()) - # this might ignore DST - last_deploy = last_deploy.replace(tzinfo=tzinfo) + if tzinfo: + last_deploy = last_deploy.replace(tzinfo=tzinfo) clean = False except Exception: - last_deploy = datetime(1970, 1, 1).replace(tzinfo=tzinfo) + last_deploy = datetime(1970, 1, 1) + if tzinfo: + last_deploy = last_deploy.replace(tzinfo=tzinfo) clean = True new_deploy = datetime.now() diff --git a/nikola/plugins/command/import_blogger.py b/nikola/plugins/command/import_blogger.py index 53618b4..ea12b4a 100644 --- a/nikola/plugins/command/import_blogger.py +++ b/nikola/plugins/command/import_blogger.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright © 2012-2013 Roberto Alsina and others. +# Copyright © 2012-2014 Roberto Alsina and others. # Permission is hereby granted, free of charge, to any # person obtaining a copy of this software and associated diff --git a/nikola/plugins/command/import_feed.py b/nikola/plugins/command/import_feed.py index b25d9ec..70a5cd5 100644 --- a/nikola/plugins/command/import_feed.py +++ b/nikola/plugins/command/import_feed.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright © 2012-2013 Roberto Alsina and others. +# Copyright © 2012-2014 Roberto Alsina and others. # Permission is hereby granted, free of charge, to any # person obtaining a copy of this software and associated diff --git a/nikola/plugins/command/import_wordpress.py b/nikola/plugins/command/import_wordpress.py index 4f32198..0c9915a 100644 --- a/nikola/plugins/command/import_wordpress.py +++ b/nikola/plugins/command/import_wordpress.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright © 2012-2013 Roberto Alsina and others. +# Copyright © 2012-2014 Roberto Alsina and others. # Permission is hereby granted, free of charge, to any # person obtaining a copy of this software and associated @@ -145,7 +145,7 @@ class CommandImportWordpress(Command, ImportMixin): rendered_template = re.sub('# REDIRECTIONS = ', 'REDIRECTIONS = ', rendered_template) if self.timezone: - rendered_template = re.sub('# TIMEZONE = \'Europe/Zurich\'', + rendered_template = re.sub('# TIMEZONE = \'UTC\'', 'TIMEZONE = \'' + self.timezone + '\'', rendered_template) self.write_configuration(self.get_configuration_output_path(), diff --git a/nikola/plugins/command/init.py b/nikola/plugins/command/init.py index 1873ec4..96caad8 100644 --- a/nikola/plugins/command/init.py +++ b/nikola/plugins/command/init.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright © 2012-2013 Roberto Alsina and others. +# Copyright © 2012-2014 Roberto Alsina and others. # Permission is hereby granted, free of charge, to any # person obtaining a copy of this software and associated diff --git a/nikola/plugins/command/install_plugin.py b/nikola/plugins/command/install_plugin.py index fdbd0b7..1d6584d 100644 --- a/nikola/plugins/command/install_plugin.py +++ b/nikola/plugins/command/install_plugin.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright © 2012-2013 Roberto Alsina and others. +# Copyright © 2012-2014 Roberto Alsina and others. # Permission is hereby granted, free of charge, to any # person obtaining a copy of this software and associated @@ -27,6 +27,7 @@ from __future__ import print_function import codecs import os +import sys import json import shutil import subprocess @@ -145,7 +146,6 @@ class CommandInstallPlugin(Command): shutil.copytree(plugin_path, dest_path) reqpath = os.path.join(dest_path, 'requirements.txt') - print(reqpath) if os.path.exists(reqpath): LOGGER.notice('This plugin has Python dependencies.') LOGGER.notice('Installing dependencies with pip...') @@ -180,6 +180,10 @@ class CommandInstallPlugin(Command): LOGGER.notice('This plugin has a sample config file.') print('Contents of the conf.py.sample file:\n') with codecs.open(confpypath, 'rb', 'utf-8') as fh: - print(indent(pygments.highlight( - fh.read(), PythonLexer(), TerminalFormatter()), 4 * ' ')) + if sys.platform == 'win32': + print(indent(pygments.highlight( + fh.read(), PythonLexer(), TerminalFormatter()), + 4 * ' ')) + else: + print(indent(fh.read(), 4 * ' ')) return True diff --git a/nikola/plugins/command/install_theme.py b/nikola/plugins/command/install_theme.py index a9d835a..569397b 100644 --- a/nikola/plugins/command/install_theme.py +++ b/nikola/plugins/command/install_theme.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright © 2012-2013 Roberto Alsina and others. +# Copyright © 2012-2014 Roberto Alsina and others. # Permission is hereby granted, free of charge, to any # person obtaining a copy of this software and associated @@ -26,9 +26,10 @@ from __future__ import print_function import os +import sys +import codecs import json import shutil -import codecs from io import BytesIO import pygments @@ -116,18 +117,22 @@ class CommandInstallTheme(Command): print(theme) return True else: - self.do_install(name, data) - # See if the theme's parent is available. If not, install it - while True: - parent_name = utils.get_parent_theme_name(name) - if parent_name is None: - break - try: - utils.get_theme_path(parent_name) - break - except: # Not available - self.do_install(parent_name, data) - name = parent_name + # `name` may be modified by the while loop. + origname = name + installstatus = self.do_install(name, data) + # See if the theme's parent is available. If not, install it + while True: + parent_name = utils.get_parent_theme_name(name) + if parent_name is None: + break + try: + utils.get_theme_path(parent_name) + break + except: # Not available + self.do_install(parent_name, data) + name = parent_name + if installstatus: + LOGGER.notice('Remember to set THEME="{0}" in conf.py to use this theme.'.format(origname)) def do_install(self, name, data): if name in data: @@ -155,9 +160,13 @@ class CommandInstallTheme(Command): shutil.copytree(theme_path, dest_path) confpypath = os.path.join(dest_path, 'conf.py.sample') if os.path.exists(confpypath): - LOGGER.notice('This plugin has a sample config file.') + LOGGER.notice('This plugin has a sample config file. Integrate it with yours in order to make this theme work!') print('Contents of the conf.py.sample file:\n') with codecs.open(confpypath, 'rb', 'utf-8') as fh: - print(indent(pygments.highlight( - fh.read(), PythonLexer(), TerminalFormatter()), 4 * ' ')) - return True + if sys.platform == 'win32': + print(indent(pygments.highlight( + fh.read(), PythonLexer(), TerminalFormatter()), + 4 * ' ')) + else: + print(indent(fh.read(), 4 * ' ')) + return True diff --git a/nikola/plugins/command/mincss.py b/nikola/plugins/command/mincss.py index 5c9a7cb..0193458 100644 --- a/nikola/plugins/command/mincss.py +++ b/nikola/plugins/command/mincss.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright © 2012-2013 Roberto Alsina and others. +# Copyright © 2012-2014 Roberto Alsina and others. # Permission is hereby granted, free of charge, to any # person obtaining a copy of this software and associated diff --git a/nikola/plugins/command/new_post.py b/nikola/plugins/command/new_post.py index ea0f3de..a5c551d 100644 --- a/nikola/plugins/command/new_post.py +++ b/nikola/plugins/command/new_post.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright © 2012-2013 Roberto Alsina and others. +# Copyright © 2012-2014 Roberto Alsina and others. # Permission is hereby granted, free of charge, to any # person obtaining a copy of this software and associated diff --git a/nikola/plugins/command/orphans.plugin b/nikola/plugins/command/orphans.plugin new file mode 100644 index 0000000..408578b --- /dev/null +++ b/nikola/plugins/command/orphans.plugin @@ -0,0 +1,10 @@ +[Core] +Name = orphans +Module = orphans + +[Documentation] +Author = Roberto Alsina, Chris Warrick +Version = 0.1 +Website = http://getnikola.com +Description = List all orphans + diff --git a/nikola/plugins/command/orphans.py b/nikola/plugins/command/orphans.py new file mode 100644 index 0000000..ff114b4 --- /dev/null +++ b/nikola/plugins/command/orphans.py @@ -0,0 +1,45 @@ +# -*- coding: utf-8 -*- + +# Copyright © 2012-2014 Roberto Alsina, Chris Warrick 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. + +from __future__ import print_function +import os + +from nikola.plugin_categories import Command +from nikola.plugins.command.check import real_scan_files + + +class CommandOrphans(Command): + name = "orphans" + doc_purpose = "list all orphans" + doc_description = """\ +List all orphans, i.e. all files that are in the output directory, +but are not generated by Nikola. + +Output contains filenames only (it is passable to `xargs rm` or the like).""" + + def _execute(self, options, args): + orphans = real_scan_files(self.site)[0] + print('\n'.join([p for p in orphans if not os.path.isdir(p)])) diff --git a/nikola/plugins/command/planetoid/__init__.py b/nikola/plugins/command/planetoid/__init__.py index 369862b..ff5dd13 100644 --- a/nikola/plugins/command/planetoid/__init__.py +++ b/nikola/plugins/command/planetoid/__init__.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright © 2012-2013 Roberto Alsina and others. +# Copyright © 2012-2014 Roberto Alsina and others. # Permission is hereby granted, free of charge, to any # person obtaining a copy of this software and associated diff --git a/nikola/plugins/command/serve.py b/nikola/plugins/command/serve.py index 07403d4..2dd15c1 100644 --- a/nikola/plugins/command/serve.py +++ b/nikola/plugins/command/serve.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright © 2012-2013 Roberto Alsina and others. +# Copyright © 2012-2014 Roberto Alsina and others. # Permission is hereby granted, free of charge, to any # person obtaining a copy of this software and associated diff --git a/nikola/plugins/command/version.py b/nikola/plugins/command/version.py index 65896e9..9b42423 100644 --- a/nikola/plugins/command/version.py +++ b/nikola/plugins/command/version.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright © 2012-2013 Roberto Alsina and others. +# Copyright © 2012-2014 Roberto Alsina and others. # Permission is hereby granted, free of charge, to any # person obtaining a copy of this software and associated @@ -41,4 +41,4 @@ class CommandVersion(Command): def _execute(self, options={}, args=None): """Print the version number.""" - print("Nikola version " + __version__) + print("Nikola v" + __version__) diff --git a/nikola/plugins/compile/__init__.py b/nikola/plugins/compile/__init__.py index e69de29..6ad8bac 100644 --- a/nikola/plugins/compile/__init__.py +++ b/nikola/plugins/compile/__init__.py @@ -0,0 +1,25 @@ +# -*- coding: utf-8 -*- + +# Copyright © 2012-2014 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. diff --git a/nikola/plugins/compile/asciidoc.py b/nikola/plugins/compile/asciidoc.py index 67dfe1a..12cb4bf 100644 --- a/nikola/plugins/compile/asciidoc.py +++ b/nikola/plugins/compile/asciidoc.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright © 2012-2013 Roberto Alsina and others. +# Copyright © 2012-2014 Roberto Alsina and others. # Permission is hereby granted, free of charge, to any # person obtaining a copy of this software and associated @@ -37,11 +37,17 @@ import subprocess from nikola.plugin_categories import PageCompiler from nikola.utils import makedirs, req_missing +try: + from collections import OrderedDict +except ImportError: + OrderedDict = None # NOQA + class CompileAsciiDoc(PageCompiler): """Compile asciidoc into HTML.""" name = "asciidoc" + demote_headers = True def compile_html(self, source, dest, is_two_file=True): makedirs(os.path.dirname(dest)) @@ -52,7 +58,10 @@ class CompileAsciiDoc(PageCompiler): req_missing(['asciidoc'], 'build this site (compile with asciidoc)', python=False) def create_post(self, path, onefile=False, **kw): - metadata = {} + if OrderedDict is not None: + metadata = OrderedDict() + else: + metadata = {} metadata.update(self.default_metadata) metadata.update(kw) makedirs(os.path.dirname(path)) diff --git a/nikola/plugins/compile/bbcode.py b/nikola/plugins/compile/bbcode.py index e998417..5345be3 100644 --- a/nikola/plugins/compile/bbcode.py +++ b/nikola/plugins/compile/bbcode.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright © 2012-2013 Roberto Alsina and others. +# Copyright © 2012-2014 Roberto Alsina and others. # Permission is hereby granted, free of charge, to any # person obtaining a copy of this software and associated @@ -37,6 +37,10 @@ except ImportError: from nikola.plugin_categories import PageCompiler from nikola.utils import makedirs, req_missing +try: + from collections import OrderedDict +except ImportError: + OrderedDict = None # NOQA class CompileBbcode(PageCompiler): @@ -63,7 +67,10 @@ class CompileBbcode(PageCompiler): out_file.write(output) def create_post(self, path, onefile=False, **kw): - metadata = {} + if OrderedDict is not None: + metadata = OrderedDict() + else: + metadata = {} metadata.update(self.default_metadata) metadata.update(kw) makedirs(os.path.dirname(path)) diff --git a/nikola/plugins/compile/html.py b/nikola/plugins/compile/html.py index a309960..5352f00 100644 --- a/nikola/plugins/compile/html.py +++ b/nikola/plugins/compile/html.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright © 2012-2013 Roberto Alsina and others. +# Copyright © 2012-2014 Roberto Alsina and others. # Permission is hereby granted, free of charge, to any # person obtaining a copy of this software and associated @@ -27,25 +27,40 @@ """Implementation of compile_html for HTML source files.""" import os -import shutil +import re import codecs from nikola.plugin_categories import PageCompiler from nikola.utils import makedirs +try: + from collections import OrderedDict +except ImportError: + OrderedDict = None # NOQA + + +_META_SEPARATOR = '(' + os.linesep * 2 + '|' + ('\n' * 2) + '|' + ("\r\n" * 2) + ')' + class CompileHtml(PageCompiler): """Compile HTML into HTML.""" - name = "html" def compile_html(self, source, dest, is_two_file=True): makedirs(os.path.dirname(dest)) - shutil.copyfile(source, dest) + with codecs.open(dest, "w+", "utf8") as out_file: + with codecs.open(source, "r", "utf8") as in_file: + data = in_file.read() + if not is_two_file: + data = re.split(_META_SEPARATOR, data, maxsplit=1)[-1] + out_file.write(data) return True def create_post(self, path, onefile=False, **kw): - metadata = {} + if OrderedDict is not None: + metadata = OrderedDict() + else: + metadata = {} metadata.update(self.default_metadata) metadata.update(kw) makedirs(os.path.dirname(path)) @@ -55,4 +70,4 @@ class CompileHtml(PageCompiler): for k, v in metadata.items(): fd.write('.. {0}: {1}\n'.format(k, v)) fd.write('-->\n\n') - fd.write("\n<p>Write your post here.</p>") + fd.write("\n<p>Write your post here.</p>\n") diff --git a/nikola/plugins/compile/ipynb/__init__.py b/nikola/plugins/compile/ipynb/__init__.py index 7c318ca..5f2f0b3 100644 --- a/nikola/plugins/compile/ipynb/__init__.py +++ b/nikola/plugins/compile/ipynb/__init__.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright © 2013 Damian Avila. +# Copyright © 2013-2014 Damián Avila and others. # Permission is hereby granted, free of charge, to any # person obtaining a copy of this software and associated @@ -41,6 +41,11 @@ except ImportError: from nikola.plugin_categories import PageCompiler from nikola.utils import makedirs, req_missing +try: + from collections import OrderedDict +except ImportError: + OrderedDict = None # NOQA + class CompileIPynb(PageCompiler): """Compile IPynb into HTML.""" @@ -49,7 +54,7 @@ class CompileIPynb(PageCompiler): def compile_html(self, source, dest, is_two_file=True): if flag is None: - req_missing(['ipython>=1.0.0'], 'build this site (compile ipynb)') + req_missing(['ipython>=1.1.0'], 'build this site (compile ipynb)') makedirs(os.path.dirname(dest)) HTMLExporter.default_template = 'basic' c = Config(self.site.config['IPYNB_CONFIG']) @@ -62,18 +67,20 @@ class CompileIPynb(PageCompiler): out_file.write(body) def create_post(self, path, onefile=False, **kw): - metadata = {} + if OrderedDict is not None: + metadata = OrderedDict() + else: + metadata = {} metadata.update(self.default_metadata) metadata.update(kw) d_name = os.path.dirname(path) makedirs(os.path.dirname(path)) meta_path = os.path.join(d_name, kw['slug'] + ".meta") with codecs.open(meta_path, "wb+", "utf8") as fd: - if onefile: - fd.write('%s\n' % kw['title']) - fd.write('%s\n' % kw['slug']) - fd.write('%s\n' % kw['date']) - fd.write('%s\n' % kw['tags']) + fd.write('\n'.join((metadata['title'], metadata['slug'], + metadata['date'], metadata['tags'], + metadata['link'], + metadata['description'], metadata['type']))) print("Your post's metadata is at: ", meta_path) with codecs.open(path, "wb+", "utf8") as fd: fd.write("""{ diff --git a/nikola/plugins/compile/markdown/__init__.py b/nikola/plugins/compile/markdown/__init__.py index b41c6b5..1376b11 100644 --- a/nikola/plugins/compile/markdown/__init__.py +++ b/nikola/plugins/compile/markdown/__init__.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright © 2012-2013 Roberto Alsina and others. +# Copyright © 2012-2014 Roberto Alsina and others. # Permission is hereby granted, free of charge, to any # person obtaining a copy of this software and associated @@ -50,6 +50,12 @@ except ImportError: gist_extension = None podcast_extension = None + +try: + from collections import OrderedDict +except ImportError: + OrderedDict = None # NOQA + from nikola.plugin_categories import PageCompiler from nikola.utils import makedirs, req_missing @@ -58,6 +64,7 @@ class CompileMarkdown(PageCompiler): """Compile markdown into HTML.""" name = "markdown" + demote_headers = True extensions = [gist_extension, nikola_extension, podcast_extension] site = None @@ -75,7 +82,10 @@ class CompileMarkdown(PageCompiler): out_file.write(output) def create_post(self, path, onefile=False, **kw): - metadata = {} + if OrderedDict is not None: + metadata = OrderedDict() + else: + metadata = {} metadata.update(self.default_metadata) metadata.update(kw) makedirs(os.path.dirname(path)) diff --git a/nikola/plugins/compile/markdown/mdx_gist.py b/nikola/plugins/compile/markdown/mdx_gist.py index 3c3bef9..d92295d 100644 --- a/nikola/plugins/compile/markdown/mdx_gist.py +++ b/nikola/plugins/compile/markdown/mdx_gist.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- # -# Copyright (c) 2013 Michael Rabbitt. +# Copyright © 2013 Michael Rabbitt. # # Permission is hereby granted, free of charge, to any person obtaining a # copy of this software and associated documentation files (the diff --git a/nikola/plugins/compile/markdown/mdx_nikola.py b/nikola/plugins/compile/markdown/mdx_nikola.py index b0ad2f7..b7c29a5 100644 --- a/nikola/plugins/compile/markdown/mdx_nikola.py +++ b/nikola/plugins/compile/markdown/mdx_nikola.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright © 2012-2013 Roberto Alsina and others. +# Copyright © 2012-2014 Roberto Alsina and others. # Permission is hereby granted, free of charge, to any # person obtaining a copy of this software and associated @@ -34,10 +34,6 @@ from markdown.extensions import Extension class NikolaPostProcessor(Postprocessor): def run(self, text): output = text - # h1 is reserved for the title so increment all header levels - for n in reversed(range(1, 9)): - output = re.sub('<h%i>' % n, '<h%i>' % (n + 1), output) - output = re.sub('</h%i>' % n, '</h%i>' % (n + 1), output) # python-markdown's highlighter uses the class 'codehilite' to wrap # code, instead of the standard 'code'. None of the standard diff --git a/nikola/plugins/compile/markdown/mdx_podcast.py b/nikola/plugins/compile/markdown/mdx_podcast.py index be8bb6b..b38b969 100644 --- a/nikola/plugins/compile/markdown/mdx_podcast.py +++ b/nikola/plugins/compile/markdown/mdx_podcast.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- # -# Copyright (c) 2013 Michael Rabbitt, Roberto Alsina +# Copyright © 2013-2014 Michael Rabbitt, 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 diff --git a/nikola/plugins/compile/misaka.py b/nikola/plugins/compile/misaka.py index 3733a85..8777ffc 100644 --- a/nikola/plugins/compile/misaka.py +++ b/nikola/plugins/compile/misaka.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (c) 2013 Chris Lee +# Copyright © 2013-2014 Chris Lee and others. # Permission is hereby granted, free of charge, to any # person obtaining a copy of this software and associated @@ -37,6 +37,11 @@ try: except ImportError: misaka = None # NOQA nikola_extension = None +try: + from collections import OrderedDict +except ImportError: + OrderedDict = None # NOQA + gist_extension = None podcast_extension = None @@ -48,6 +53,7 @@ class CompileMisaka(PageCompiler): """Compile Misaka into HTML.""" name = "misaka" + demote_headers = True def __init__(self, *args, **kwargs): super(CompileMisaka, self).__init__(*args, **kwargs) @@ -68,7 +74,10 @@ class CompileMisaka(PageCompiler): out_file.write(output) def create_post(self, path, onefile=False, **kw): - metadata = {} + if OrderedDict is not None: + metadata = OrderedDict() + else: + metadata = {} metadata.update(self.default_metadata) metadata.update(kw) makedirs(os.path.dirname(path)) diff --git a/nikola/plugins/compile/pandoc.py b/nikola/plugins/compile/pandoc.py index 3a2911f..57c7d71 100644 --- a/nikola/plugins/compile/pandoc.py +++ b/nikola/plugins/compile/pandoc.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright © 2012-2013 Roberto Alsina and others. +# Copyright © 2012-2014 Roberto Alsina and others. # Permission is hereby granted, free of charge, to any # person obtaining a copy of this software and associated @@ -37,6 +37,11 @@ import subprocess from nikola.plugin_categories import PageCompiler from nikola.utils import req_missing, makedirs +try: + from collections import OrderedDict +except ImportError: + OrderedDict = None # NOQA + class CompilePandoc(PageCompiler): """Compile markups into HTML using pandoc.""" @@ -52,7 +57,10 @@ class CompilePandoc(PageCompiler): req_missing(['pandoc'], 'build this site (compile with pandoc)', python=False) def create_post(self, path, onefile=False, **kw): - metadata = {} + if OrderedDict is not None: + metadata = OrderedDict() + else: + metadata = {} metadata.update(self.default_metadata) metadata.update(kw) makedirs(os.path.dirname(path)) diff --git a/nikola/plugins/compile/php.py b/nikola/plugins/compile/php.py index 44701c8..14b80e8 100644 --- a/nikola/plugins/compile/php.py +++ b/nikola/plugins/compile/php.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright © 2012-2013 Roberto Alsina and others. +# Copyright © 2012-2014 Roberto Alsina and others. # Permission is hereby granted, free of charge, to any # person obtaining a copy of this software and associated @@ -35,6 +35,11 @@ import codecs from nikola.plugin_categories import PageCompiler from nikola.utils import makedirs +try: + from collections import OrderedDict +except ImportError: + OrderedDict = None # NOQA + class CompilePhp(PageCompiler): """Compile PHP into PHP.""" @@ -46,7 +51,10 @@ class CompilePhp(PageCompiler): shutil.copyfile(source, dest) def create_post(self, path, onefile=False, **kw): - metadata = {} + if OrderedDict is not None: + metadata = OrderedDict() + else: + metadata = {} metadata.update(self.default_metadata) metadata.update(kw) os.makedirs(os.path.dirname(path)) diff --git a/nikola/plugins/compile/rest/__init__.py b/nikola/plugins/compile/rest/__init__.py index c71a5f8..50b37cf 100644 --- a/nikola/plugins/compile/rest/__init__.py +++ b/nikola/plugins/compile/rest/__init__.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright © 2012-2013 Roberto Alsina and others. +# Copyright © 2012-2014 Roberto Alsina and others. # Permission is hereby granted, free of charge, to any # person obtaining a copy of this software and associated @@ -35,10 +35,16 @@ try: import docutils.utils import docutils.io import docutils.readers.standalone + import docutils.writers.html4css1 has_docutils = True except ImportError: has_docutils = False +try: + from collections import OrderedDict +except ImportError: + OrderedDict = None # NOQA + from nikola.plugin_categories import PageCompiler from nikola.utils import get_logger, makedirs, req_missing @@ -47,6 +53,7 @@ class CompileRest(PageCompiler): """Compile reSt into HTML.""" name = "rest" + demote_headers = True logger = None def compile_html(self, source, dest, is_two_file=True): @@ -71,14 +78,16 @@ class CompileRest(PageCompiler): # author). add_ln = len(spl[0].splitlines()) + 1 + default_template_path = os.path.join(os.path.dirname(__file__), 'template.txt') output, error_level, deps = rst2html( data, settings_overrides={ - 'initial_header_level': 2, + 'initial_header_level': 1, 'record_dependencies': True, 'stylesheet_path': None, 'link_stylesheet': True, 'syntax_highlight': 'short', 'math_output': 'mathjax', + 'template': default_template_path, }, logger=self.logger, l_source=source, l_add_ln=add_ln) out_file.write(output) deps_path = dest + '.dep' @@ -94,7 +103,10 @@ class CompileRest(PageCompiler): return False def create_post(self, path, onefile=False, **kw): - metadata = {} + if OrderedDict is not None: + metadata = OrderedDict() + else: + metadata = {} metadata.update(self.default_metadata) metadata.update(kw) makedirs(os.path.dirname(path)) @@ -117,6 +129,9 @@ class CompileRest(PageCompiler): plugin_info.plugin_object.short_help = plugin_info.description self.logger = get_logger('compile_rest', site.loghandlers) + if not site.debug: + self.logger.level = 4 + return super(CompileRest, self).set_site(site) @@ -139,7 +154,10 @@ def get_observer(settings): """ errormap = {0: 1, 1: 2, 2: 4, 3: 5, 4: 6} text = docutils.nodes.Element.astext(msg) - out = '[{source}:{line}] {text}'.format(source=settings['source'], line=msg['line'] + settings['add_ln'], text=text) + line = msg['line'] + settings['add_ln'] if 'line' in msg else 0 + out = '[{source}{colon}{line}] {text}'.format( + source=settings['source'], colon=(':' if line else ''), + line=line, text=text) settings['logger'].log(errormap[msg['level']], out) return observer @@ -155,6 +173,14 @@ class NikolaReader(docutils.readers.standalone.Reader): return document +def add_node(node, visit_function=None, depart_function=None): + docutils.nodes._add_node_class_names([node.__name__]) + if visit_function: + setattr(docutils.writers.html4css1.HTMLTranslator, 'visit_' + node.__name__, visit_function) + if depart_function: + setattr(docutils.writers.html4css1.HTMLTranslator, 'depart_' + node.__name__, depart_function) + + def rst2html(source, source_path=None, source_class=docutils.io.StringInput, destination_path=None, reader=None, parser=None, parser_name='restructuredtext', writer=None, diff --git a/nikola/plugins/compile/rest/chart.py b/nikola/plugins/compile/rest/chart.py index ee917b9..03878a3 100644 --- a/nikola/plugins/compile/rest/chart.py +++ b/nikola/plugins/compile/rest/chart.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright © 2012-2013 Roberto Alsina and others. +# Copyright © 2012-2014 Roberto Alsina and others. # Permission is hereby granted, free of charge, to any # person obtaining a copy of this software and associated diff --git a/nikola/plugins/compile/rest/doc.py b/nikola/plugins/compile/rest/doc.py index 915a7e1..a150a81 100644 --- a/nikola/plugins/compile/rest/doc.py +++ b/nikola/plugins/compile/rest/doc.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright © 2012-2013 Roberto Alsina and others. +# Copyright © 2012-2014 Roberto Alsina and others. # Permission is hereby granted, free of charge, to any # person obtaining a copy of this software and associated diff --git a/nikola/plugins/compile/rest/listing.py b/nikola/plugins/compile/rest/listing.py index 31975bb..ecf885f 100644 --- a/nikola/plugins/compile/rest/listing.py +++ b/nikola/plugins/compile/rest/listing.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright © 2012-2013 Roberto Alsina and others. +# Copyright © 2012-2014 Roberto Alsina and others. # Permission is hereby granted, free of charge, to any # person obtaining a copy of this software and associated diff --git a/nikola/plugins/compile/rest/media.py b/nikola/plugins/compile/rest/media.py index d1930dd..ccda559 100644 --- a/nikola/plugins/compile/rest/media.py +++ b/nikola/plugins/compile/rest/media.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright © 2012-2013 Roberto Alsina and others. +# Copyright © 2012-2014 Roberto Alsina and others. # Permission is hereby granted, free of charge, to any # person obtaining a copy of this software and associated diff --git a/nikola/plugins/compile/rest/post_list.py b/nikola/plugins/compile/rest/post_list.py index eae4016..6804b58 100644 --- a/nikola/plugins/compile/rest/post_list.py +++ b/nikola/plugins/compile/rest/post_list.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright © 2013 Udo Spallek, Roberto Alsina and others. +# Copyright © 2013-2014 Udo Spallek, Roberto Alsina and others. # Permission is hereby granted, free of charge, to any # person obtaining a copy of this software and associated @@ -33,6 +33,9 @@ from docutils.parsers.rst import Directive, directives from nikola import utils from nikola.plugin_categories import RestExtension +# WARNING: the directive name is post-list +# (with a DASH instead of an UNDERSCORE) + class Plugin(RestExtension): name = "rest_post_list" diff --git a/nikola/plugins/compile/rest/slides.py b/nikola/plugins/compile/rest/slides.py index 41c3314..203ae51 100644 --- a/nikola/plugins/compile/rest/slides.py +++ b/nikola/plugins/compile/rest/slides.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright © 2012-2013 Roberto Alsina and others. +# Copyright © 2012-2014 Roberto Alsina and others. # Permission is hereby granted, free of charge, to any # person obtaining a copy of this software and associated diff --git a/nikola/plugins/compile/rest/soundcloud.py b/nikola/plugins/compile/rest/soundcloud.py index 6fb3e99..a26806c 100644 --- a/nikola/plugins/compile/rest/soundcloud.py +++ b/nikola/plugins/compile/rest/soundcloud.py @@ -15,12 +15,13 @@ class Plugin(RestExtension): def set_site(self, site): self.site = site directives.register_directive('soundcloud', SoundCloud) + directives.register_directive('soundcloud_playlist', SoundCloudPlaylist) return super(Plugin, self).set_site(site) CODE = ("""<iframe width="{width}" height="{height}" scrolling="no" frameborder="no" -src="https://w.soundcloud.com/player/?url=http://api.soundcloud.com/tracks/""" +src="https://w.soundcloud.com/player/?url=http://api.soundcloud.com/{preslug}/""" """{sid}"> </iframe>""") @@ -40,6 +41,7 @@ class SoundCloud(Directive): 'width': directives.positive_int, 'height': directives.positive_int, } + preslug = "tracks" def run(self): """ Required by the Directive interface. Create docutils nodes """ @@ -48,6 +50,7 @@ class SoundCloud(Directive): 'sid': self.arguments[0], 'width': 600, 'height': 160, + 'preslug': self.preslug, } options.update(self.options) return [nodes.raw('', CODE.format(**options), format='html')] @@ -58,3 +61,7 @@ class SoundCloud(Directive): raise self.warning("This directive does not accept content. The " "'key=value' format for options is deprecated, " "use ':key: value' instead") + + +class SoundCloudPlaylist(SoundCloud): + preslug = "playlists" diff --git a/nikola/plugins/compile/rest/template.txt b/nikola/plugins/compile/rest/template.txt new file mode 100644 index 0000000..2591bce --- /dev/null +++ b/nikola/plugins/compile/rest/template.txt @@ -0,0 +1,8 @@ +%(head_prefix)s +%(head)s +%(stylesheet)s +%(body_prefix)s +%(body_pre_docinfo)s +%(docinfo)s +%(body)s +%(body_suffix)s diff --git a/nikola/plugins/compile/rest/vimeo.py b/nikola/plugins/compile/rest/vimeo.py index 6d66648..82c4dc1 100644 --- a/nikola/plugins/compile/rest/vimeo.py +++ b/nikola/plugins/compile/rest/vimeo.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright © 2012-2013 Roberto Alsina and others. +# Copyright © 2012-2014 Roberto Alsina and others. # Permission is hereby granted, free of charge, to any # person obtaining a copy of this software and associated diff --git a/nikola/plugins/compile/rest/youtube.py b/nikola/plugins/compile/rest/youtube.py index 3d4bdd3..19e12d1 100644 --- a/nikola/plugins/compile/rest/youtube.py +++ b/nikola/plugins/compile/rest/youtube.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright © 2012-2013 Roberto Alsina and others. +# Copyright © 2012-2014 Roberto Alsina and others. # Permission is hereby granted, free of charge, to any # person obtaining a copy of this software and associated diff --git a/nikola/plugins/compile/textile.py b/nikola/plugins/compile/textile.py index b402329..73f35c0 100644 --- a/nikola/plugins/compile/textile.py +++ b/nikola/plugins/compile/textile.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright © 2012-2013 Roberto Alsina and others. +# Copyright © 2012-2014 Roberto Alsina and others. # Permission is hereby granted, free of charge, to any # person obtaining a copy of this software and associated @@ -38,11 +38,17 @@ except ImportError: from nikola.plugin_categories import PageCompiler from nikola.utils import makedirs, req_missing +try: + from collections import OrderedDict +except ImportError: + OrderedDict = None # NOQA + class CompileTextile(PageCompiler): """Compile textile into HTML.""" name = "textile" + demote_headers = True def compile_html(self, source, dest, is_two_file=True): if textile is None: @@ -57,7 +63,10 @@ class CompileTextile(PageCompiler): out_file.write(output) def create_post(self, path, onefile=False, **kw): - metadata = {} + if OrderedDict is not None: + metadata = OrderedDict() + else: + metadata = {} metadata.update(self.default_metadata) metadata.update(kw) makedirs(os.path.dirname(path)) diff --git a/nikola/plugins/compile/txt2tags.py b/nikola/plugins/compile/txt2tags.py index 2f62f04..8c9724e 100644 --- a/nikola/plugins/compile/txt2tags.py +++ b/nikola/plugins/compile/txt2tags.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright © 2012-2013 Roberto Alsina and others. +# Copyright © 2012-2014 Roberto Alsina and others. # Permission is hereby granted, free of charge, to any # person obtaining a copy of this software and associated @@ -40,14 +40,20 @@ try: except ImportError: txt2tags = None # NOQA +try: + from collections import OrderedDict +except ImportError: + OrderedDict = None # NOQA + from nikola.plugin_categories import PageCompiler from nikola.utils import makedirs, req_missing -class CompileTextile(PageCompiler): +class CompileTxt2tags(PageCompiler): """Compile txt2tags into HTML.""" name = "txt2tags" + demote_headers = True def compile_html(self, source, dest, is_two_file=True): if txt2tags is None: @@ -57,7 +63,10 @@ class CompileTextile(PageCompiler): txt2tags(cmd) def create_post(self, path, onefile=False, **kw): - metadata = {} + if OrderedDict is not None: + metadata = OrderedDict() + else: + metadata = {} metadata.update(self.default_metadata) metadata.update(kw) makedirs(os.path.dirname(path)) diff --git a/nikola/plugins/compile/wiki.py b/nikola/plugins/compile/wiki.py index b2c4afc..9a365fa 100644 --- a/nikola/plugins/compile/wiki.py +++ b/nikola/plugins/compile/wiki.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright © 2012-2013 Roberto Alsina and others. +# Copyright © 2012-2014 Roberto Alsina and others. # Permission is hereby granted, free of charge, to any # person obtaining a copy of this software and associated @@ -37,6 +37,11 @@ except ImportError: creole = None from nikola.plugin_categories import PageCompiler +try: + from collections import OrderedDict +except ImportError: + OrderedDict = None # NOQA + from nikola.utils import makedirs, req_missing @@ -44,6 +49,7 @@ class CompileWiki(PageCompiler): """Compile CreoleWiki into HTML.""" name = "wiki" + demote_headers = True def compile_html(self, source, dest, is_two_file=True): if creole is None: @@ -57,7 +63,10 @@ class CompileWiki(PageCompiler): out_file.write(output) def create_post(self, path, onefile=False, **kw): - metadata = {} + if OrderedDict is not None: + metadata = OrderedDict() + else: + metadata = {} metadata.update(self.default_metadata) metadata.update(kw) makedirs(os.path.dirname(path)) diff --git a/nikola/plugins/loghandler/__init__.py b/nikola/plugins/loghandler/__init__.py new file mode 100644 index 0000000..6ad8bac --- /dev/null +++ b/nikola/plugins/loghandler/__init__.py @@ -0,0 +1,25 @@ +# -*- coding: utf-8 -*- + +# Copyright © 2012-2014 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. diff --git a/nikola/plugins/loghandler/smtp.py b/nikola/plugins/loghandler/smtp.py index deb8f4e..2c9fd9c 100644 --- a/nikola/plugins/loghandler/smtp.py +++ b/nikola/plugins/loghandler/smtp.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright © 2012-2013 Daniel Devine and others. +# Copyright © 2012-2014 Daniel Devine and others. # Permission is hereby granted, free of charge, to any # person obtaining a copy of this software and associated diff --git a/nikola/plugins/loghandler/stderr.py b/nikola/plugins/loghandler/stderr.py index 71f1de5..75acffc 100644 --- a/nikola/plugins/loghandler/stderr.py +++ b/nikola/plugins/loghandler/stderr.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright © 2012-2013 Daniel Devine and others. +# Copyright © 2012-2014 Daniel Devine and others. # Permission is hereby granted, free of charge, to any # person obtaining a copy of this software and associated @@ -29,6 +29,8 @@ from blinker import signal import logbook import os +from nikola import DEBUG + class StderrHandler(SignalHandler): """Logs messages to stderr.""" @@ -39,7 +41,7 @@ class StderrHandler(SignalHandler): conf = self.site.config.get('LOGGING_HANDLERS').get('stderr') if conf or os.getenv('NIKOLA_DEBUG'): self.site.loghandlers.append(logbook.StderrHandler( - level='DEBUG' if os.getenv('NIKOLA_DEBUG') else conf.get('loglevel', 'WARNING').upper(), + level='DEBUG' if DEBUG else conf.get('loglevel', 'WARNING').upper(), format_string=u'[{record.time:%Y-%m-%dT%H:%M:%SZ}] {record.level_name}: {record.channel}: {record.message}' )) diff --git a/nikola/plugins/task/__init__.py b/nikola/plugins/task/__init__.py index e69de29..6ad8bac 100644 --- a/nikola/plugins/task/__init__.py +++ b/nikola/plugins/task/__init__.py @@ -0,0 +1,25 @@ +# -*- coding: utf-8 -*- + +# Copyright © 2012-2014 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. diff --git a/nikola/plugins/task/archive.py b/nikola/plugins/task/archive.py index 3afbea1..a65a63f 100644 --- a/nikola/plugins/task/archive.py +++ b/nikola/plugins/task/archive.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright © 2012-2013 Roberto Alsina and others. +# Copyright © 2012-2014 Roberto Alsina and others. # Permission is hereby granted, free of charge, to any # person obtaining a copy of this software and associated @@ -92,7 +92,8 @@ class Archive(Task): kw['filters'], context, ) - task_cfg = {1: task['uptodate'][0].config, 2: kw} + n = len(post_list) if 'posts' in context else len(months) + task_cfg = {1: task['uptodate'][0].config, 2: kw, 3: n} task['uptodate'] = [config_changed(task_cfg)] task['basename'] = self.name yield task @@ -123,7 +124,7 @@ class Archive(Task): kw['filters'], context, ) - task_cfg = {1: task['uptodate'][0].config, 2: kw} + task_cfg = {1: task['uptodate'][0].config, 2: kw, 3: len(post_list)} task['uptodate'] = [config_changed(task_cfg)] task['basename'] = self.name yield task @@ -151,7 +152,7 @@ class Archive(Task): kw['filters'], context, ) - task_cfg = {1: task['uptodate'][0].config, 2: kw} + task_cfg = {1: task['uptodate'][0].config, 2: kw, 3: len(years)} task['uptodate'] = [config_changed(task_cfg)] task['basename'] = self.name yield task diff --git a/nikola/plugins/task/build_less.py b/nikola/plugins/task/build_less.py index 8889cbe..14a53f9 100644 --- a/nikola/plugins/task/build_less.py +++ b/nikola/plugins/task/build_less.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright © 2012-2013 Roberto Alsina and others. +# Copyright © 2012-2014 Roberto Alsina and others. # Permission is hereby granted, free of charge, to any # person obtaining a copy of this software and associated @@ -29,6 +29,7 @@ from __future__ import unicode_literals import codecs import glob import os +import sys import subprocess from nikola.plugin_categories import Task @@ -41,10 +42,10 @@ class BuildLess(Task): name = "build_less" sources_folder = "less" sources_ext = ".less" - compiler_name = "lessc" def gen_tasks(self): """Generate CSS out of LESS sources.""" + self.compiler_name = self.site.config['LESS_COMPILER'] kw = { 'cache_folder': self.site.config['CACHE_FOLDER'], @@ -79,7 +80,13 @@ class BuildLess(Task): def compile_target(target, dst): utils.makedirs(dst_dir) src = os.path.join(kw['cache_folder'], self.sources_folder, target) - compiled = subprocess.check_output([self.compiler_name, src]) + run_in_shell = sys.platform == 'win32' + try: + compiled = subprocess.check_output([self.compiler_name, src], shell=run_in_shell) + except OSError: + utils.req_missing([self.compiler_name], + 'build LESS files (and use this theme)', + False, False) with open(dst, "wb+") as outf: outf.write(compiled) diff --git a/nikola/plugins/task/build_sass.py b/nikola/plugins/task/build_sass.py index a5d22fb..7575505 100644 --- a/nikola/plugins/task/build_sass.py +++ b/nikola/plugins/task/build_sass.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright © 2012-2013 Roberto Alsina and others. +# Copyright © 2012-2014 Roberto Alsina and others. # Permission is hereby granted, free of charge, to any # person obtaining a copy of this software and associated @@ -29,6 +29,7 @@ from __future__ import unicode_literals import codecs import glob import os +import sys import subprocess from nikola.plugin_categories import Task @@ -41,11 +42,11 @@ class BuildSass(Task): name = "build_sass" sources_folder = "sass" sources_ext = (".sass", ".scss") - compiler_name = "sass" def gen_tasks(self): """Generate CSS out of Sass sources.""" self.logger = utils.get_logger('build_sass', self.site.loghandlers) + self.compiler_name = self.site.config['SASS_COMPILER'] kw = { 'cache_folder': self.site.config['CACHE_FOLDER'], @@ -79,8 +80,14 @@ class BuildSass(Task): def compile_target(target, dst): utils.makedirs(dst_dir) + run_in_shell = sys.platform == 'win32' src = os.path.join(kw['cache_folder'], self.sources_folder, target) - compiled = subprocess.check_output([self.compiler_name, src]) + try: + compiled = subprocess.check_output([self.compiler_name, src], shell=run_in_shell) + except OSError: + utils.req_missing([self.compiler_name], + 'build Sass files (and use this theme)', + False, False) with open(dst, "wb+") as outf: outf.write(compiled) @@ -99,7 +106,7 @@ class BuildSass(Task): if base in seennames: self.logger.error( - 'Duplicate filenames for SASS compiled files: {0} and ' + 'Duplicate filenames for Sass compiled files: {0} and ' '{1} (both compile to {2})'.format( seennames[base], target, base + ".css")) else: diff --git a/nikola/plugins/task/bundles.py b/nikola/plugins/task/bundles.py index 488f96f..b035b97 100644 --- a/nikola/plugins/task/bundles.py +++ b/nikola/plugins/task/bundles.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright © 2012-2013 Roberto Alsina and others. +# Copyright © 2012-2014 Roberto Alsina and others. # Permission is hereby granted, free of charge, to any # person obtaining a copy of this software and associated @@ -44,7 +44,9 @@ class BuildBundles(LateTask): def set_site(self, site): super(BuildBundles, self).set_site(site) - if webassets is None: + if webassets is None and self.site.config['USE_BUNDLES']: + utils.req_missing(['webassets'], 'USE_BUNDLES', optional=True) + utils.LOGGER.warn('Setting USE_BUNDLES to False.') self.site.config['USE_BUNDLES'] = False def gen_tasks(self): @@ -111,6 +113,6 @@ def get_theme_bundles(themes): for line in fd: name, files = line.split('=') files = [f.strip() for f in files.split(',')] - bundles[name.strip()] = files + bundles[name.strip().replace('/', os.sep)] = files break return bundles diff --git a/nikola/plugins/task/copy_assets.py b/nikola/plugins/task/copy_assets.py index f3d85df..21f1f85 100644 --- a/nikola/plugins/task/copy_assets.py +++ b/nikola/plugins/task/copy_assets.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright © 2012-2013 Roberto Alsina and others. +# Copyright © 2012-2014 Roberto Alsina and others. # Permission is hereby granted, free of charge, to any # person obtaining a copy of this software and associated diff --git a/nikola/plugins/task/copy_files.py b/nikola/plugins/task/copy_files.py index 88e89eb..9846ca0 100644 --- a/nikola/plugins/task/copy_files.py +++ b/nikola/plugins/task/copy_files.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright © 2012-2013 Roberto Alsina and others. +# Copyright © 2012-2014 Roberto Alsina and others. # Permission is hereby granted, free of charge, to any # person obtaining a copy of this software and associated diff --git a/nikola/plugins/task/galleries.py b/nikola/plugins/task/galleries.py index cf670e0..6977eab 100644 --- a/nikola/plugins/task/galleries.py +++ b/nikola/plugins/task/galleries.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright © 2012-2013 Roberto Alsina and others. +# Copyright © 2012-2014 Roberto Alsina and others. # Permission is hereby granted, free of charge, to any # person obtaining a copy of this software and associated @@ -51,6 +51,7 @@ import PyRSS2Gen as rss from nikola.plugin_categories import Task from nikola import utils from nikola.post import Post +from nikola.utils import req_missing class Galleries(Task): @@ -77,6 +78,9 @@ class Galleries(Task): def gen_tasks(self): """Render image galleries.""" + if Image is None: + req_missing(['pillow'], 'render galleries') + self.logger = utils.get_logger('render_galleries', self.site.loghandlers) self.image_ext_list = ['.jpg', '.png', '.jpeg', '.gif', '.svg', '.bmp', '.tiff'] self.image_ext_list.extend(self.site.config.get('EXTRA_IMAGE_EXTENSIONS', [])) @@ -111,7 +115,7 @@ class Galleries(Task): for gallery in self.gallery_list: # Create subfolder list - folder_list = [x.split(os.sep)[-2] for x in + folder_list = [(x, x.split(os.sep)[-2]) for x in glob.glob(os.path.join(gallery, '*') + os.sep)] # Parse index into a post (with translations) @@ -137,7 +141,7 @@ class Galleries(Task): for task in self.remove_excluded_image(image): yield task - crumbs = utils.get_crumbs(gallery) + crumbs = utils.get_crumbs(gallery, index_folder=self) # Create index.html for each language for lang in self.kw['translations']: @@ -173,9 +177,20 @@ class Galleries(Task): thumbs = ['.thumbnail'.join(os.path.splitext(p)) for p in image_list] thumbs = [os.path.join(self.kw['output_folder'], t) for t in thumbs] + folders = [] + + # Generate friendly gallery names + for path, folder in folder_list: + fpost = self.parse_index(path) + if fpost: + ft = fpost.title(lang) or folder + else: + ft = folder + folders.append((folder, ft)) + ## TODO: in v7 remove images from context, use photo_array context["images"] = list(zip(image_name_list, thumbs, img_titles)) - context["folders"] = folder_list + context["folders"] = folders context["crumbs"] = crumbs context["permalink"] = self.site.link( "gallery", os.path.basename(gallery), lang) @@ -288,8 +303,14 @@ class Galleries(Task): False, self.site.MESSAGES, 'story.tmpl', - self.site.get_compiler(index_path).compile_html + self.site.get_compiler(index_path) ) + # If this did not exist, galleries without a title in the + # index.txt file would be errorneously named `index` + # (warning: galleries titled index and filenamed differently + # may break) + if post.title == 'index': + post.title = os.path.split(gallery)[1] else: post = None return post @@ -460,7 +481,7 @@ class Galleries(Task): os.path.join( self.site.config['OUTPUT_FOLDER'], img)).st_size args = { - 'title': full_title.split('"')[-2], + 'title': full_title.split('"')[-2] if full_title else '', 'link': make_url(img), 'guid': rss.Guid(img, False), 'pubDate': self.image_date(img), @@ -477,7 +498,7 @@ class Galleries(Task): description='', lastBuildDate=datetime.datetime.now(), items=items, - generator='nikola', + generator='Nikola <http://getnikola.com/>', language=lang ) rss_obj.self_url = make_url(permalink) @@ -523,8 +544,9 @@ class Galleries(Task): try: im.thumbnail(size, Image.ANTIALIAS) im.save(dst) - except Exception: - self.logger.warn("Can't thumbnail {0}, using original image as thumbnail".format(src)) + except Exception as e: + self.logger.warn("Can't thumbnail {0}, using original " + "image as thumbnail ({1})".format(src, e)) utils.copy_file(src, dst) else: # Image is small utils.copy_file(src, dst) diff --git a/nikola/plugins/task/gzip.py b/nikola/plugins/task/gzip.py index 738d52c..bcc9637 100644 --- a/nikola/plugins/task/gzip.py +++ b/nikola/plugins/task/gzip.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright © 2012-2013 Roberto Alsina and others. +# Copyright © 2012-2014 Roberto Alsina and others. # Permission is hereby granted, free of charge, to any # person obtaining a copy of this software and associated diff --git a/nikola/plugins/task/indexes.py b/nikola/plugins/task/indexes.py index 0d20422..3f45161 100644 --- a/nikola/plugins/task/indexes.py +++ b/nikola/plugins/task/indexes.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright © 2012-2013 Roberto Alsina and others. +# Copyright © 2012-2014 Roberto Alsina and others. # Permission is hereby granted, free of charge, to any # person obtaining a copy of this software and associated @@ -57,6 +57,7 @@ class Indexes(Task): "hide_untranslated_posts": self.site.config['HIDE_UNTRANSLATED_POSTS'], "indexes_title": self.site.config['INDEXES_TITLE'], "indexes_pages": self.site.config['INDEXES_PAGES'], + "indexes_pages_main": self.site.config['INDEXES_PAGES_MAIN'], "blog_title": self.site.config["BLOG_TITLE"], } @@ -78,12 +79,18 @@ class Indexes(Task): for i, post_list in enumerate(lists): context = {} indexes_title = kw['indexes_title'] or kw['blog_title'] + if kw["indexes_pages_main"]: + ipages_i = i + 1 + ipages_msg = "page %d" + else: + ipages_i = i + ipages_msg = "old posts, page %d" if kw["indexes_pages"]: - indexes_pages = kw["indexes_pages"] % i + indexes_pages = kw["indexes_pages"] % ipages_i else: indexes_pages = " (" + \ - kw["messages"][lang]["old posts page %d"] % i + ")" - if i > 0: + kw["messages"][lang][ipages_msg] % ipages_i + ")" + if i > 0 or kw["indexes_pages_main"]: context["title"] = indexes_title + indexes_pages else: context["title"] = indexes_title diff --git a/nikola/plugins/task/listings.py b/nikola/plugins/task/listings.py index ab62e74..d8ed43b 100644 --- a/nikola/plugins/task/listings.py +++ b/nikola/plugins/task/listings.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright © 2012-2013 Roberto Alsina and others. +# Copyright © 2012-2014 Roberto Alsina and others. # Permission is hereby granted, free of charge, to any # person obtaining a copy of this software and associated diff --git a/nikola/plugins/task/localsearch/__init__.py b/nikola/plugins/task/localsearch/__init__.py index 9162604..c501d80 100644 --- a/nikola/plugins/task/localsearch/__init__.py +++ b/nikola/plugins/task/localsearch/__init__.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright © 2012-2013 Roberto Alsina and others. +# Copyright © 2012-2014 Roberto Alsina and others. # Permission is hereby granted, free of charge, to any # person obtaining a copy of this software and associated diff --git a/nikola/plugins/task/mustache/__init__.py b/nikola/plugins/task/mustache/__init__.py index c392e3b..5be98f0 100644 --- a/nikola/plugins/task/mustache/__init__.py +++ b/nikola/plugins/task/mustache/__init__.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright © 2012-2013 Roberto Alsina and others. +# Copyright © 2012-2014 Roberto Alsina and others. # Permission is hereby granted, free of charge, to any # person obtaining a copy of this software and associated @@ -31,7 +31,9 @@ import json import os from nikola.plugin_categories import Task -from nikola.utils import config_changed, copy_file, unicode_str, makedirs +from nikola.utils import ( + config_changed, copy_file, LocaleBorg, makedirs, unicode_str, +) class Mustache(Task): @@ -106,7 +108,7 @@ class Mustache(Task): }) # Comments - context = dict(post=post, lang=self.site.current_lang()) + context = dict(post=post, lang=LocaleBorg().current_lang) context.update(self.site.GLOBAL_CONTEXT) data["comment_html"] = self.site.template_system.render_template( 'mustache-comment-form.tmpl', None, context).strip() diff --git a/nikola/plugins/task/pages.py b/nikola/plugins/task/pages.py index eb5b49e..f4c0469 100644 --- a/nikola/plugins/task/pages.py +++ b/nikola/plugins/task/pages.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright © 2012-2013 Roberto Alsina and others. +# Copyright © 2012-2014 Roberto Alsina and others. # Permission is hereby granted, free of charge, to any # person obtaining a copy of this software and associated diff --git a/nikola/plugins/task/posts.py b/nikola/plugins/task/posts.py index 18d61b8..a502b81 100644 --- a/nikola/plugins/task/posts.py +++ b/nikola/plugins/task/posts.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright © 2012-2013 Roberto Alsina and others. +# Copyright © 2012-2014 Roberto Alsina and others. # Permission is hereby granted, free of charge, to any # person obtaining a copy of this software and associated diff --git a/nikola/plugins/task/redirect.py b/nikola/plugins/task/redirect.py index ade878a..6fafd13 100644 --- a/nikola/plugins/task/redirect.py +++ b/nikola/plugins/task/redirect.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright © 2012-2013 Roberto Alsina and others. +# Copyright © 2012-2014 Roberto Alsina and others. # Permission is hereby granted, free of charge, to any # person obtaining a copy of this software and associated diff --git a/nikola/plugins/task/rss.py b/nikola/plugins/task/rss.py index bcca4da..e5f7548 100644 --- a/nikola/plugins/task/rss.py +++ b/nikola/plugins/task/rss.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright © 2012-2013 Roberto Alsina and others. +# Copyright © 2012-2014 Roberto Alsina and others. # Permission is hereby granted, free of charge, to any # person obtaining a copy of this software and associated diff --git a/nikola/plugins/task/sitemap/__init__.py b/nikola/plugins/task/sitemap/__init__.py index f34bc0a..0164000 100644 --- a/nikola/plugins/task/sitemap/__init__.py +++ b/nikola/plugins/task/sitemap/__init__.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright © 2012-2013 Roberto Alsina and others. +# Copyright © 2012-2014 Roberto Alsina and others. # Permission is hereby granted, free of charge, to any # person obtaining a copy of this software and associated diff --git a/nikola/plugins/task/sources.py b/nikola/plugins/task/sources.py index 672f354..2324af2 100644 --- a/nikola/plugins/task/sources.py +++ b/nikola/plugins/task/sources.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright © 2012-2013 Roberto Alsina and others. +# Copyright © 2012-2014 Roberto Alsina and others. # Permission is hereby granted, free of charge, to any # person obtaining a copy of this software and associated @@ -66,7 +66,7 @@ class Sources(Task): if dest_ext == post.source_ext(): continue if lang != kw["default_lang"]: - source_lang = source + '.' + lang + source_lang = utils.get_translation_candidate(self.site.config, source, lang) if os.path.exists(source_lang): source = source_lang if os.path.isfile(source): diff --git a/nikola/plugins/task/tags.py b/nikola/plugins/task/tags.py index 299dca4..a2444ec 100644 --- a/nikola/plugins/task/tags.py +++ b/nikola/plugins/task/tags.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright © 2012-2013 Roberto Alsina and others. +# Copyright © 2012-2014 Roberto Alsina and others. # Permission is hereby granted, free of charge, to any # person obtaining a copy of this software and associated @@ -110,8 +110,14 @@ class RenderTags(Task): # Tag cloud json file tag_cloud_data = {} for tag, posts in self.site.posts_per_tag.items(): + tag_posts = dict(posts=[{'title': post.meta[post.default_lang]['title'], + 'date': post.date.strftime('%m/%d/%Y'), + 'isodate': post.date.isoformat(), + 'url': post.base_path.replace('cache', '')} + for post in reversed(sorted(self.site.timeline, key=lambda post: post.date)) + if tag in post.alltags]) tag_cloud_data[tag] = [len(posts), self.site.link( - 'tag', tag, self.site.config['DEFAULT_LANG'])] + 'tag', tag, self.site.config['DEFAULT_LANG']), tag_posts] output_name = os.path.join(kw['output_folder'], 'assets', 'js', 'tag_cloud_data.json') @@ -124,6 +130,7 @@ class RenderTags(Task): 'basename': str(self.name), 'name': str(output_name) } + task['uptodate'] = [utils.config_changed(tag_cloud_data)] task['targets'] = [output_name] task['actions'] = [(write_tag_data, [tag_cloud_data])] @@ -189,7 +196,7 @@ class RenderTags(Task): return name # FIXME: deduplicate this with render_indexes - template_name = "index.tmpl" + template_name = "tagindex.tmpl" # Split in smaller lists lists = [] while post_list: diff --git a/nikola/plugins/template/__init__.py b/nikola/plugins/template/__init__.py index e69de29..6ad8bac 100644 --- a/nikola/plugins/template/__init__.py +++ b/nikola/plugins/template/__init__.py @@ -0,0 +1,25 @@ +# -*- coding: utf-8 -*- + +# Copyright © 2012-2014 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. diff --git a/nikola/plugins/template/jinja.py b/nikola/plugins/template/jinja.py index a40a476..17c33d4 100644 --- a/nikola/plugins/template/jinja.py +++ b/nikola/plugins/template/jinja.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright © 2012-2013 Roberto Alsina and others. +# Copyright © 2012-2014 Roberto Alsina and others. # Permission is hereby granted, free of charge, to any # person obtaining a copy of this software and associated @@ -55,7 +55,7 @@ class JinjaTemplates(TemplateSystem): self.lookup.globals['enumerate'] = enumerate def set_directories(self, directories, cache_folder): - """Createa template lookup.""" + """Create a template lookup.""" if jinja2 is None: req_missing(['jinja2'], 'use this theme') self.lookup.loader = jinja2.FileSystemLoader(directories, @@ -74,9 +74,8 @@ class JinjaTemplates(TemplateSystem): return output def render_template_to_string(self, template, context): - """ Render template to a string using context. """ - - return jinja2.Template(template).render(**context) + """Render template to a string using context.""" + return self.lookup.from_string(template).render(**context) def template_deps(self, template_name): # Cache the lists of dependencies for each template name. diff --git a/nikola/plugins/template/mako.py b/nikola/plugins/template/mako.py index ae51cac..45f4335 100644 --- a/nikola/plugins/template/mako.py +++ b/nikola/plugins/template/mako.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright © 2012-2013 Roberto Alsina and others. +# Copyright © 2012-2014 Roberto Alsina and others. # Permission is hereby granted, free of charge, to any # person obtaining a copy of this software and associated diff --git a/nikola/post.py b/nikola/post.py index a41901d..810474b 100644 --- a/nikola/post.py +++ b/nikola/post.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright © 2012-2013 Roberto Alsina and others. +# Copyright © 2012-2014 Roberto Alsina and others. # Permission is hereby granted, free of charge, to any # person obtaining a copy of this software and associated @@ -54,6 +54,8 @@ from .utils import ( slugify, to_datetime, unicode_str, + demote_headers, + get_translation_candidate, ) from .rc4 import rc4 @@ -83,11 +85,11 @@ class Post(object): the meta file, as well as any translations available, and the .html fragment file path. """ - self.compiler = compiler self.config = config - tzinfo = None - if self.config['TIMEZONE'] is not None: - tzinfo = pytz.timezone(self.config['TIMEZONE']) + self.compiler = compiler + self.compile_html = self.compiler.compile_html + self.demote_headers = self.compiler.demote_headers and self.config['DEMOTE_HEADERS'] + tzinfo = pytz.timezone(self.config['TIMEZONE']) if self.config['FUTURE_IS_NOW']: self.current_time = None else: @@ -127,7 +129,7 @@ class Post(object): # Load internationalized metadata for lang in self.translations: if lang != self.default_lang: - if os.path.isfile(self.source_path + "." + lang): + if os.path.isfile(get_translation_candidate(self.config, self.source_path, lang)): self.translated_to.add(lang) meta = defaultdict(lambda: '') @@ -146,11 +148,7 @@ class Post(object): if 'date' not in default_metadata and not use_in_feeds: # For stories we don't *really* need a date default_metadata['date'] = datetime.datetime.utcfromtimestamp( - os.stat(self.source_path).st_ctime) - - if tzinfo: - default_metadata['date'] = default_metadata['date'].replace( - tzinfo=pytz.UTC).astimezone(tzinfo) + os.stat(self.source_path).st_ctime).replace(tzinfo=pytz.UTC).astimezone(tzinfo) if 'title' not in default_metadata or 'slug' not in default_metadata \ or 'date' not in default_metadata: @@ -161,7 +159,11 @@ class Post(object): default_metadata.get('date', None), source_path)) - # If timezone is set, build localized datetime. + if 'type' not in default_metadata: + # default value is 'text' + default_metadata['type'] = 'text' + + # If time zone is set, build localized datetime. self.date = to_datetime(self.meta[self.default_lang]['date'], tzinfo) self.publish_later = False if self.current_time is None else self.date >= self.current_time @@ -185,6 +187,7 @@ class Post(object): # While draft comes from the tags, it's not really a tag self.is_draft = is_draft self.is_retired = is_retired + self.is_post = use_in_feeds self.use_in_feeds = use_in_feeds and not is_draft and not is_retired \ and not self.publish_later @@ -253,15 +256,6 @@ class Post(object): def template_name(self): return self.meta('template') or self._template_name - def _add_old_metadata(self): - # Compatibility for themes up to Nikola 5.4.1 - # TODO: remove before Nikola 6 - self.pagenames = {} - self.titles = {} - for lang in self.translations: - self.pagenames[lang] = self.meta[lang]['slug'] - self.titles[lang] = self.meta[lang]['title'] - def formatted_date(self, date_format): """Return the formatted date, as unicode.""" fmt_date = self.date.strftime(date_format) @@ -292,7 +286,7 @@ class Post(object): if self.default_lang in self.translated_to: deps.append(self.base_path) if lang != self.default_lang: - deps += [self.base_path + "." + lang] + deps += [get_translation_candidate(self.config, self.base_path, lang)] deps += self.fragment_deps(lang) return deps @@ -312,7 +306,7 @@ class Post(object): if not self.is_translation_available(lang) and self.config['HIDE_UNTRANSLATED_POSTS']: return else: - self.compiler( + self.compile_html( self.translated_source_path(lang), dest, self.is_two_file), @@ -333,11 +327,11 @@ class Post(object): if os.path.isfile(dep_path): with codecs.open(dep_path, 'rb+', 'utf8') as depf: deps.extend([l.strip() for l in depf.readlines()]) + lang_deps = [] if lang != self.default_lang: - lang_deps = list(filter(os.path.exists, [x + "." + lang for x in - deps])) + lang_deps = [get_translation_candidate(self.config, d, lang) for d in deps] deps += lang_deps - return deps + return [d for d in deps if os.path.exists(d)] def is_translation_available(self, lang): """Return true if the translation actually exists.""" @@ -349,18 +343,18 @@ class Post(object): if lang == self.default_lang: return self.source_path else: - return '.'.join((self.source_path, lang)) + return get_translation_candidate(self.config, self.source_path, lang) elif lang != self.default_lang: return self.source_path else: - return '.'.join((self.source_path, sorted(self.translated_to)[0])) + return get_translation_candidate(self.config, self.source_path, sorted(self.translated_to)[0]) def translated_base_path(self, lang): """Return path to the translation's base_path file.""" if lang == self.default_lang: return self.base_path else: - return '.'.join((self.base_path, lang)) + return get_translation_candidate(self.config, self.base_path, lang) def _translated_file_path(self, lang): """Return path to the translation's file, or to the original.""" @@ -368,11 +362,11 @@ class Post(object): if lang == self.default_lang: return self.base_path else: - return '.'.join((self.base_path, lang)) + return get_translation_candidate(self.config, self.base_path, lang) elif lang != self.default_lang: return self.base_path else: - return '.'.join((self.base_path, sorted(self.translated_to)[0])) + return get_translation_candidate(self.config, self.base_path, sorted(self.translated_to)[0]) def text(self, lang=None, teaser_only=False, strip_html=False, really_absolute=False): """Read the post file for that language and return its contents. @@ -438,6 +432,16 @@ class Post(object): data = content.text_content().strip() # No whitespace wanted. except lxml.etree.ParserError: data = "" + elif data: + if self.demote_headers: + # see above + try: + document = lxml.html.fromstring(data) + demote_headers(document, self.demote_headers) + data = lxml.html.tostring(document, encoding='unicode') + except lxml.etree.ParserError: + pass + return data @property @@ -536,11 +540,13 @@ def _get_metadata_from_filename_by_regex(filename, metadata_regexp): return meta -def get_metadata_from_file(source_path, lang=None): +def get_metadata_from_file(source_path, config=None, lang=None): """Extracts metadata from the file itself, by parsing contents.""" try: - if lang: - source_path = "{0}.{1}".format(source_path, lang) + if lang and config: + source_path = get_translation_candidate(config, source_path, lang) + elif lang: + source_path += '.' + lang with codecs.open(source_path, "r", "utf8") as meta_file: meta_data = [x.strip() for x in meta_file.readlines()] return _get_metadata_from_file(meta_data) @@ -601,18 +607,20 @@ def _get_metadata_from_file(meta_data): return meta -def get_metadata_from_meta_file(path, lang=None): +def get_metadata_from_meta_file(path, config=None, lang=None): """Takes a post path, and gets data from a matching .meta file.""" meta_path = os.path.splitext(path)[0] + '.meta' - if lang: + if lang and config: + meta_path = get_translation_candidate(config, meta_path, lang) + elif lang: meta_path += '.' + lang if os.path.isfile(meta_path): with codecs.open(meta_path, "r", "utf8") as meta_file: meta_data = meta_file.readlines() - while len(meta_data) < 6: + while len(meta_data) < 7: meta_data.append("") - (title, slug, date, tags, link, description) = [ - x.strip() for x in meta_data][:6] + (title, slug, date, tags, link, description, _type) = [ + x.strip() for x in meta_data][:7] meta = {} @@ -628,6 +636,8 @@ def get_metadata_from_meta_file(path, lang=None): meta['link'] = link if description: meta['description'] = description + if _type: + meta['type'] = _type return meta @@ -635,7 +645,7 @@ def get_metadata_from_meta_file(path, lang=None): # Metadata file doesn't exist, but not default language, # So, if default language metadata exists, return that. # This makes the 2-file format detection more reliable (Issue #525) - return get_metadata_from_meta_file(path, lang=None) + return get_metadata_from_meta_file(path, config, lang=None) else: return {} @@ -650,7 +660,12 @@ def get_meta(post, file_metadata_regexp=None, lang=None): """ meta = defaultdict(lambda: '') - meta.update(get_metadata_from_meta_file(post.metadata_path, lang)) + try: + config = post.config + except AttributeError: + config = None + + meta.update(get_metadata_from_meta_file(post.metadata_path, config, lang)) if meta: return meta @@ -660,7 +675,7 @@ def get_meta(post, file_metadata_regexp=None, lang=None): meta.update(_get_metadata_from_filename_by_regex(post.source_path, file_metadata_regexp)) - meta.update(get_metadata_from_file(post.source_path, lang)) + meta.update(get_metadata_from_file(post.source_path, config, lang)) if lang is None: # Only perform these checks for the default language diff --git a/nikola/rc4.py b/nikola/rc4.py index fe2ab17..b46d602 100644 --- a/nikola/rc4.py +++ b/nikola/rc4.py @@ -1,5 +1,7 @@ # -*- coding: utf-8 -*- """ + A RC4 encryption library (used for password-protected posts) + --- Copyright (C) 2012 Bo Zhu http://about.bozhu.me Permission is hereby granted, free of charge, to any person obtaining a diff --git a/nikola/utils.py b/nikola/utils.py index 0a615f8..0c3b4c0 100644 --- a/nikola/utils.py +++ b/nikola/utils.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright © 2012-2013 Roberto Alsina and others. +# Copyright © 2012-2014 Roberto Alsina and others. # Permission is hereby granted, free of charge, to any # person obtaining a copy of this software and associated @@ -50,6 +50,8 @@ import logbook from logbook.more import ExceptionHandler import pytz +from . import DEBUG + class ApplicationWarning(Exception): pass @@ -67,13 +69,13 @@ def get_logger(name, handlers): STDERR_HANDLER = [logbook.StderrHandler( - level=logbook.NOTICE if not os.getenv('NIKOLA_DEBUG') else logbook.DEBUG, + level=logbook.NOTICE if not DEBUG else logbook.DEBUG, format_string=u'[{record.time:%Y-%m-%dT%H:%M:%SZ}] {record.level_name}: {record.channel}: {record.message}' )] LOGGER = get_logger('Nikola', STDERR_HANDLER) STRICT_HANDLER = ExceptionHandler(ApplicationWarning, level='WARNING') -if os.getenv('NIKOLA_DEBUG'): +if DEBUG: logging.basicConfig(level=logging.DEBUG) else: logging.basicConfig(level=logging.WARNING) @@ -101,7 +103,8 @@ def req_missing(names, purpose, python=True, optional=False): LOGGER.warn(msg) else: LOGGER.error(msg) - raise Exception('Missing dependencies: {0}'.format(', '.join(names))) + LOGGER.error('Exiting due to missing dependencies.') + sys.exit(5) return msg @@ -127,7 +130,8 @@ __all__ = ['get_theme_path', 'get_theme_chain', 'load_messages', 'copy_tree', 'to_datetime', 'apply_filters', 'config_changed', 'get_crumbs', 'get_tzname', 'get_asset_path', '_reload', 'unicode_str', 'bytes_str', 'unichr', 'Functionary', 'LocaleBorg', 'sys_encode', 'sys_decode', - 'makedirs', 'get_parent_theme_name', 'ExtendedRSS2'] + 'makedirs', 'get_parent_theme_name', 'ExtendedRSS2', 'demote_headers', + 'get_translation_candidate'] ENCODING = sys.getfilesystemencoding() or sys.stdin.encoding @@ -346,7 +350,7 @@ def generic_rss_renderer(lang, title, link, description, timeline, output_path, description=description, lastBuildDate=datetime.datetime.now(), items=items, - generator='nikola', + generator='Nikola <http://getnikola.com/>', language=lang ) rss_obj.self_url = feed_url @@ -477,7 +481,7 @@ def to_datetime(value, tzinfo=None): dt = datetime.datetime.strptime(value, format) if tzinfo is None: return dt - # Build a localized time by using a given timezone. + # Build a localized time by using a given time zone. return tzinfo.localize(dt) except ValueError: pass @@ -496,7 +500,7 @@ def to_datetime(value, tzinfo=None): def get_tzname(dt): """ - Give a datetime value, find the name of the timezone + Given a datetime value, find the name of the time zone. """ try: from dateutil import tz @@ -555,8 +559,10 @@ def apply_filters(task, filters): return task -def get_crumbs(path, is_file=False): +def get_crumbs(path, is_file=False, index_folder=None): """Create proper links for a crumb bar. + index_folder is used if you want to use title from index file + instead of folder name as breadcrumb text. >>> crumbs = get_crumbs('galleries') >>> len(crumbs) @@ -595,6 +601,16 @@ def get_crumbs(path, is_file=False): for i, crumb in enumerate(crumbs[::-1]): _path = '/'.join(['..'] * i) or '#' _crumbs.append([_path, crumb]) + if index_folder and hasattr(index_folder, 'parse_index'): + folder = path + for i, crumb in enumerate(crumbs[::-1]): + if folder[-1] == os.sep: + folder = folder[:-1] + index_post = index_folder.parse_index(folder) + folder = folder.replace(crumb, '') + if index_post: + crumb = index_post.title() or crumb + _crumbs[i][1] = crumb return list(reversed(_crumbs)) @@ -792,3 +808,46 @@ def first_line(doc): if striped: return striped return '' + + +def demote_headers(doc, level=1): + """Demote <hN> elements by one.""" + if level == 0: + return doc + elif level > 0: + r = range(1, 7 - level) + elif level < 0: + r = range(1 + level, 7) + for i in reversed(r): + # html headers go to 6, so we can’t “lower” beneath five + elements = doc.xpath('//h' + str(i)) + for e in elements: + e.tag = 'h' + str(i + level) + + +def get_root_dir(): + """Find root directory of nikola installation by looking for conf.py""" + root = os.getcwd() + + while True: + if os.path.exists(os.path.join(root, 'conf.py')): + return root + else: + basedir = os.path.split(root)[0] + # Top directory, already checked + if basedir == root: + break + root = basedir + + return None + + +def get_translation_candidate(config, path, lang): + """ + Return a possible path where we can find the translated version of some page + based on the TRANSLATIONS_PATTERN configuration variable + """ + pattern = config['TRANSLATIONS_PATTERN'] + path, ext = os.path.splitext(path) + ext = ext[1:] if len(ext) > 0 else ext + return pattern.format(path=path, lang=lang, ext=ext) diff --git a/nikola/winutils.py b/nikola/winutils.py index 291c99d..517a326 100644 --- a/nikola/winutils.py +++ b/nikola/winutils.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright © 2012-2013 Roberto Alsina and others. +# Copyright © 2012-2014 Roberto Alsina and others. # Permission is hereby granted, free of charge, to any # person obtaining a copy of this software and associated |
