aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLibravatarAgustin Henze <tin@sluc.org.ar>2014-03-09 03:14:44 +0100
committerLibravatarAgustin Henze <tin@sluc.org.ar>2014-03-09 03:14:44 +0100
commitde1d7f85f5f67b269178ea05b7c59deb21ac1720 (patch)
tree3125810eecb495e9ce216dd95561cd1dd3977235
parentc14a6f46d2a76a9c228b6c2543939315068fad5f (diff)
parentfa50632a9d87c3989566fed3e49c160a132e0d14 (diff)
downloadnikola-de1d7f85f5f67b269178ea05b7c59deb21ac1720.tar.bz2
nikola-de1d7f85f5f67b269178ea05b7c59deb21ac1720.tar.xz
nikola-de1d7f85f5f67b269178ea05b7c59deb21ac1720.tar.zst
Merge tag 'upstream/6.4.0'
Upstream version 6.4.0
-rw-r--r--.travis.yml14
-rw-r--r--AUTHORS.txt7
-rw-r--r--CHANGES.txt135
-rw-r--r--docs/creating-a-site.txt2
-rw-r--r--docs/creating-a-theme.txt2
-rw-r--r--docs/extending.txt20
-rw-r--r--docs/getting-help.txt2
-rw-r--r--docs/internals.txt18
-rw-r--r--docs/man/nikola.111
-rw-r--r--docs/manual.txt140
-rw-r--r--docs/social_buttons.txt2
-rw-r--r--docs/sphinx/conf.py4
-rw-r--r--docs/theming.txt7
-rw-r--r--docs/upgrading-to-v6.txt4
-rw-r--r--dodo.py14
-rw-r--r--nikola/__init__.py2
-rw-r--r--nikola/__main__.py14
-rw-r--r--nikola/conf.py.in55
-rw-r--r--nikola/data/themes/base/messages/messages_bg.py2
-rw-r--r--nikola/data/themes/base/messages/messages_ca.py2
-rw-r--r--nikola/data/themes/base/messages/messages_cs.py2
-rw-r--r--nikola/data/themes/base/messages/messages_de.py2
-rw-r--r--nikola/data/themes/base/messages/messages_el.py2
-rw-r--r--nikola/data/themes/base/messages/messages_en.py2
-rw-r--r--nikola/data/themes/base/messages/messages_eo.py2
-rw-r--r--nikola/data/themes/base/messages/messages_es.py2
-rw-r--r--nikola/data/themes/base/messages/messages_et.py2
-rw-r--r--nikola/data/themes/base/messages/messages_eu.py2
-rw-r--r--nikola/data/themes/base/messages/messages_fa.py2
-rw-r--r--nikola/data/themes/base/messages/messages_fi.py2
-rw-r--r--nikola/data/themes/base/messages/messages_fr.py2
-rw-r--r--nikola/data/themes/base/messages/messages_hi.py31
-rw-r--r--nikola/data/themes/base/messages/messages_hr.py2
-rw-r--r--nikola/data/themes/base/messages/messages_it.py2
-rw-r--r--nikola/data/themes/base/messages/messages_ja.py2
-rw-r--r--nikola/data/themes/base/messages/messages_nb.py2
-rw-r--r--nikola/data/themes/base/messages/messages_nl.py2
-rw-r--r--nikola/data/themes/base/messages/messages_pl.py2
-rw-r--r--nikola/data/themes/base/messages/messages_pt_br.py2
-rw-r--r--nikola/data/themes/base/messages/messages_ru.py2
-rw-r--r--nikola/data/themes/base/messages/messages_sl.py2
-rw-r--r--nikola/data/themes/base/messages/messages_tr_tr.py2
-rw-r--r--nikola/data/themes/base/messages/messages_ur.py4
-rw-r--r--nikola/data/themes/base/messages/messages_zh_cn.py2
-rw-r--r--nikola/data/themes/base/templates/base.tmpl5
-rw-r--r--nikola/data/themes/base/templates/base_helper.tmpl4
-rw-r--r--nikola/data/themes/base/templates/crumbs.tmpl2
-rw-r--r--nikola/data/themes/base/templates/gallery.tmpl4
-rw-r--r--[-rwxr-xr-x]nikola/data/themes/base/templates/index_helper.tmpl5
-rw-r--r--nikola/data/themes/base/templates/list.tmpl2
-rw-r--r--nikola/data/themes/base/templates/list_post.tmpl2
-rw-r--r--nikola/data/themes/base/templates/listing.tmpl2
-rw-r--r--[-rwxr-xr-x]nikola/data/themes/base/templates/post_helper.tmpl5
-rw-r--r--nikola/data/themes/base/templates/post_list_directive.tmpl2
-rw-r--r--nikola/data/themes/base/templates/tag.tmpl2
-rw-r--r--nikola/data/themes/bootstrap/templates/base.tmpl1
-rw-r--r--nikola/data/themes/bootstrap/templates/bootstrap_helper.tmpl4
-rw-r--r--nikola/data/themes/bootstrap/templates/gallery.tmpl4
-rw-r--r--nikola/nikola.py283
-rw-r--r--nikola/plugin_categories.py4
-rw-r--r--nikola/plugins/basic_import.py8
-rw-r--r--nikola/plugins/command/auto.py18
-rw-r--r--nikola/plugins/command/bootswatch_theme.py9
-rw-r--r--nikola/plugins/command/check.py56
-rw-r--r--nikola/plugins/command/console.py2
-rw-r--r--nikola/plugins/command/deploy.py29
-rw-r--r--nikola/plugins/command/import_blogger.py9
-rw-r--r--nikola/plugins/command/import_feed.py13
-rw-r--r--nikola/plugins/command/import_wordpress.py139
-rw-r--r--nikola/plugins/command/init.py110
-rw-r--r--nikola/plugins/command/install_plugin.py15
-rw-r--r--nikola/plugins/command/install_theme.py11
-rw-r--r--nikola/plugins/command/new_page.plugin9
-rw-r--r--nikola/plugins/command/new_page.py80
-rw-r--r--nikola/plugins/command/new_post.py61
-rw-r--r--nikola/plugins/command/planetoid/__init__.py14
-rw-r--r--nikola/plugins/command/serve.py19
-rw-r--r--nikola/plugins/compile/asciidoc.py11
-rw-r--r--nikola/plugins/compile/bbcode.py11
-rw-r--r--nikola/plugins/compile/html.py11
-rw-r--r--nikola/plugins/compile/ipynb/__init__.py11
-rw-r--r--nikola/plugins/compile/markdown/__init__.py11
-rw-r--r--nikola/plugins/compile/misaka.py11
-rw-r--r--nikola/plugins/compile/pandoc.py11
-rw-r--r--nikola/plugins/compile/php.py11
-rw-r--r--nikola/plugins/compile/rest/__init__.py43
-rw-r--r--nikola/plugins/compile/rest/listing.py19
-rw-r--r--nikola/plugins/compile/textile.py11
-rw-r--r--nikola/plugins/compile/txt2tags.py11
-rw-r--r--nikola/plugins/compile/wiki.py11
-rw-r--r--nikola/plugins/loghandler/stderr.py4
-rw-r--r--nikola/plugins/task/build_less.py16
-rw-r--r--nikola/plugins/task/build_sass.py19
-rw-r--r--nikola/plugins/task/bundles.py4
-rw-r--r--nikola/plugins/task/copy_assets.py4
-rw-r--r--nikola/plugins/task/galleries.py6
-rw-r--r--nikola/plugins/task/listings.py2
-rw-r--r--[-rwxr-xr-x]nikola/plugins/task/localsearch/files/assets/css/img/search.pngbin315 -> 315 bytes
-rw-r--r--[-rwxr-xr-x]nikola/plugins/task/localsearch/files/assets/css/tipuesearch.css0
-rw-r--r--[-rwxr-xr-x]nikola/plugins/task/localsearch/files/tipue_search.html0
-rw-r--r--nikola/plugins/task/rss.py5
-rw-r--r--nikola/plugins/task/sitemap/__init__.py19
-rw-r--r--nikola/plugins/task/tags.py14
-rw-r--r--nikola/plugins/template/jinja.py5
-rw-r--r--nikola/plugins/template/mako.py8
-rw-r--r--nikola/post.py5
-rw-r--r--nikola/utils.py81
-rw-r--r--requirements-full.txt5
-rw-r--r--requirements-tests.txt1
-rw-r--r--requirements.txt1
-rwxr-xr-xscripts/getpyver.py25
-rwxr-xr-xscripts/import_po.py6
-rwxr-xr-xscripts/set_version.py4
-rw-r--r--setup.cfg3
-rwxr-xr-xsetup.py2
-rw-r--r--tests/test_command_import_wordpress.py83
-rw-r--r--tests/test_command_init.py12
-rw-r--r--tests/test_integration.py61
-rw-r--r--tests/test_rss_feeds.py2
-rw-r--r--tests/test_utils.py112
-rw-r--r--tests/wordpress_export_example.xml31
-rw-r--r--translations/nikola.messages/bg.po8
-rw-r--r--translations/nikola.messages/ca.po8
-rw-r--r--translations/nikola.messages/cs.po8
-rw-r--r--translations/nikola.messages/de.po10
-rw-r--r--translations/nikola.messages/el.po8
-rw-r--r--translations/nikola.messages/en.po6
-rw-r--r--translations/nikola.messages/eo.po8
-rw-r--r--translations/nikola.messages/es.po8
-rw-r--r--translations/nikola.messages/et.po8
-rw-r--r--translations/nikola.messages/eu.po8
-rw-r--r--translations/nikola.messages/fa.po8
-rw-r--r--translations/nikola.messages/fi.po8
-rw-r--r--translations/nikola.messages/fr.po8
-rw-r--r--translations/nikola.messages/hi.po91
-rw-r--r--translations/nikola.messages/hr.po8
-rw-r--r--translations/nikola.messages/it.po8
-rw-r--r--translations/nikola.messages/ja.po8
-rw-r--r--translations/nikola.messages/nb.po8
-rw-r--r--translations/nikola.messages/nl.po10
-rw-r--r--translations/nikola.messages/pl.po10
-rw-r--r--translations/nikola.messages/pt_BR.po8
-rw-r--r--translations/nikola.messages/ru.po8
-rw-r--r--translations/nikola.messages/sl.po10
-rw-r--r--translations/nikola.messages/tr_TR.po8
-rw-r--r--translations/nikola.messages/ur.po11
-rw-r--r--translations/nikola.messages/zh_CN.po8
147 files changed, 1810 insertions, 682 deletions
diff --git a/.travis.yml b/.travis.yml
index 680496f..fc27848 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -1,6 +1,7 @@
before_install:
- "if [[ $NMODE == 'nikola' ]]; then sudo apt-get update -qq || true; fi"
- "if [[ $NMODE == 'nikola' ]]; then sudo apt-get --reinstall install -qq language-pack-en-base language-pack-pl-base; fi"
+ - "pip install --upgrade pip wheel"
language: python
cache: apt
python:
@@ -14,14 +15,17 @@ matrix:
env:
NMODE=nikola
install:
- - "if [[ $NMODE == 'nikola' ]]; then pip install -r requirements-tests.txt --use-mirrors; fi"
- - "if [[ $NMODE == 'nikola' ]]; then pip install . --use-mirrors; fi"
- - "if [[ $NMODE == 'flake8' ]]; then pip install flake8 --use-mirrors; fi"
-# We run tests, nikola (to see if the command is executable) and flake8.
+ - "if [[ $NMODE == 'nikola' ]]; then wget https://github.com/getnikola/wheelhouse/archive/$(scripts/getpyver.py).zip; fi"
+ - "if [[ $NMODE == 'nikola' ]]; then unzip $(scripts/getpyver.py).zip; fi"
+ - "if [[ $NMODE == 'nikola' ]]; then pip install --use-wheel --no-index --find-links=wheelhouse-$(scripts/getpyver.py short) lxml Pillow ipython; fi"
+ - "if [[ $NMODE == 'nikola' ]]; then pip install -r requirements-tests.txt; fi"
+ - "if [[ $NMODE == 'nikola' ]]; then pip install .; fi"
+ - "if [[ $NMODE == 'flake8' ]]; then pip install flake8; fi"
+# We run tests and nikola (to see if the command is executable) OR flake8.
script:
- "if [[ $NMODE == 'nikola' ]]; then nosetests --with-coverage --cover-package=nikola --with-doctest --doctest-options=+NORMALIZE_WHITESPACE --logging-filter=-yapsy; fi"
- "if [[ $NMODE == 'nikola' ]]; then nikola help; fi"
- - "if [[ $NMODE == 'flake8' ]]; then flake8 --ignore=E501 .; fi"
+ - "if [[ $NMODE == 'flake8' ]]; then flake8 .; fi"
after_success:
- "if [[ $NMODE == 'nikola' ]]; then coveralls; fi"
notifications:
diff --git a/AUTHORS.txt b/AUTHORS.txt
index 59dc3db..cfb0ea0 100644
--- a/AUTHORS.txt
+++ b/AUTHORS.txt
@@ -13,12 +13,15 @@ Chris “Kwpolska” Warrick <https://github.com/Kwpolska>
Daniel Aleksandersen <https://github.com/Aeyoun>
DoctorMalboro <https://github.com/DoctorMalboro>
Eduardo Schettino <https://github.com/schettino72>
+Evgeni Golov <https://github.com/evgeni>
Kay Hayen <https://github.com/kayhayen>
Niko Wenselowski <https://github.com/okin>
+Ondřej Grover <https://github.com/smartass101>
Raimon Esteve <https://github.com/raimonesteve>
Roberto Alsina <https://github.com/ralsina>
Roman Imankulov <https://github.com/imankulov>
StyXman <https://github.com/StyXman>
+Tim Chase <https://github.com/Gumnos>
Tordek <https://github.com/Tordek>
Troy Toman <https://github.com/troytoman>
Zhaojun Meng <https://github.com/zhaojunmeng>
@@ -34,6 +37,7 @@ dflock <https://github.com/dflock>
dhruvbaldawa <https://github.com/dhruvbaldawa>
dmoisset <https://github.com/dmoisset>
edwinsteele <https://github.com/edwinsteele>
+ermeaney <https://github.com/ermeney>
fisadev <https://github.com/fisadev>
fizyk <https://github.com/fizyk>
ivanov <https://github.com/ivanov>
@@ -48,6 +52,7 @@ marianoguerra <https://github.com/marianoguerra>
mattgaviota <https://github.com/mattgaviota>
mgaitan <https://github.com/mgaitan>
mrabbitt <https://github.com/mrabbitt>
+neiesc <https://github.com/neiesc>
neilmb <https://github.com/neilmb>
notfoss <https://github.com/notfoss>
numshub <https://github.com/numshub>
@@ -64,5 +69,3 @@ snaewe <https://github.com/snaewe>
tolusonaike <https://github.com/tolusonaike>
wimpr1m <https://github.com/wimpr1m>
yarko <https://github.com/yarko>
-neiesc <https://github.com/neiesc>
-ermeaney <https://github.com/ermeney>
diff --git a/CHANGES.txt b/CHANGES.txt
index 59318e3..8454939 100644
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@ -1,5 +1,52 @@
-New in 6.3.0
-============
+New in v6.4.0
+=============
+
+Features
+--------
+
+* Add `nikola new_page` command (equivalent to `nikola new_post -p`) (Issue #1060)
+* Add LESS_OPTIONS and SASS_OPTIONS for specifying additional parameters to LESS/Sass compilers (Issue #1020)
+* Warn users about bootswatch_theme being incompatible with bootstrap3-gradients
+* Add link://filename/foo/bar.rst syntax to refer to the post generated from foo/bar.rst (Issue #1035)
+* Log messages are colorized (colorama is required under Windows) (Issue #1044)
+* Template filters are configurable via the TEMPLATE_FILTERS config variable (Issue #1038)
+* Added Hindi translation by Sean Pue
+* Livereload v2.1.0 is supported and frozen as the version to use due to backwards-incompatible updates (Issue #1023)
+* Support :linenos: and :number-lines: in listings (Issue #1010)
+* Support :linenos: in code blocks for Sphinx compatibility (Issue #1010)
+* New link:///foo links which always point to absolute paths (/foo in that case) (Issue #986)
+* Bootstrap 3 has been updated to v3.1.1 (Issue #1015)
+* New --browser/-b option on the serve command to open instantly in a web browser (Issue #997)
+* SASS/LESS files and targets file will be processed from site root (Issue #941)
+
+Bugfixes
+--------
+
+* lastdeploy time recording was broken, changed to ISO format (Issue #1083)
+* Avoid undefined behaviour if NAVIGATION_LINKS is missing keys for a translation (Issue #1082)
+* Make livereload actually rebuild the site when changes are made (Issue #1067)
+* Fix filename encodings in WordPress imports (Issue #1053)
+* nikola check supports URL_TYPE="absolute" and URL_TYPE="full_path" (Issue #1046)
+* Fix URL_TYPE=absolute and URL_TYPE=full_path on non-root sites (Issue #1046)
+* Avoid running bundle tasks twice (Issue #1032)
+* Fix post links in RSS feeds for sites outside of webserver root (Issue #986)
+* Only run sitemap once (Issue #1032)
+* Fix a bug with some multilingual pages
+* Normalize paths (Issue #1028)
+* Fix drafts being leaked in feeds (Issues #934, #971)
+* If two tags generate the same slug, they are the same tag (Issue #1022)
+* Assume UTF-8 if user's locale doesn't have any encodings attached (Issue #1026 via #1021)
+* Support PRETTY_URLS for tag files (Issue #655)
+* Change code.css and rst.css to have sane output everywhere (Issues #913, #1009, #1050)
+* Fix image URLs in galleries for sites outside of webserver root (Issue #986)
+* Fix link:// in RSS contents (Issue #952)
+* HIDE_SOURCELINK was set to True sometimes (Issue #1004)
+* Properly import cool URIs from WordPress, instead of just slugify-ing the path (Issue #693)
+* Fixed SASS/LESS errors when more than one theme in chain has a targets file (Issue #941)
+* Guard against empty list-items in base templates (Issue #936)
+
+New in v6.3.0
+=============
Features
--------
@@ -59,8 +106,8 @@ Bugfixes
* Remove some unnecessary elements (Issues #969, #970)
* abs_link should return absolute URI
-New in 6.2.1
-============
+New in v6.2.1
+=============
Features
--------
@@ -77,8 +124,8 @@ Bugfixes
* Fixed some locale problems in posix systems (Issues #886, #884, #875)
* Don’t include BLOG_DESCRIPTION as meta tag on tag and gallery pages (Issue #876)
-New in 6.2.0
-============
+New in v6.2.0
+=============
Features
--------
@@ -125,8 +172,8 @@ Other
* the bpython console is now “slightly deprecated”
-New in 6.1.1
-============
+New in v6.1.1
+=============
Features
--------
@@ -141,8 +188,8 @@ Bugfixes
* A standardized way of reporting missing requirements (Issue #797)
* Don’t force requests for compile.rest.gist (Issue #795)
-New in 6.1.0
-============
+New in v6.1.0
+=============
Features
--------
@@ -172,8 +219,8 @@ Bugfixes
* LOGGER was incorrectly imported in planetoid.py
* Order Monthly Archive properly, descending order (Issue #740)
-New in 6.0.4
-============
+New in v6.0.4
+=============
Features
--------
@@ -200,8 +247,8 @@ Bugfixes
* Consider the current theme chain's parent, bundles and engine part of the file_dep for
files created by generic_page_renderer (Issue #711)
-New in 6.0.3
-============
+New in v6.0.3
+=============
Features
--------
@@ -222,8 +269,8 @@ Bugfixes
* Apply filters to files generated by the gallery tasks (Issue #708)
* More robust handling of filter commands' shell quoting (Issue #705)
-New in 6.0.2
-============
+New in v6.0.2
+=============
Features
--------
@@ -238,8 +285,8 @@ Bugfixes
* Make footnote-references keep line height in rst.css
* Make bootswatch_theme work under Python 3 (Issue #695)
-New in 6.0.1
-============
+New in v6.0.1
+=============
Features
--------
@@ -254,8 +301,8 @@ Bugfixes
* Remove decoding errors if files are not proper UTF-8 (Issue #691)
* Stop ignoring \*.JPG and \*.PNG by galleries (Issue #690)
-New in 6.0.0
-============
+New in v6.0.0
+=============
Features
--------
@@ -317,8 +364,8 @@ Bugfixes
* Added LICENSE in the footer (Issue #528)
* Use random IDs for slides so you can have more than one in a page (Issue #572)
-New in 5.5.1
-============
+New in v5.5.1
+=============
Features
--------
@@ -351,8 +398,8 @@ Bugfixes
* Fixed IPython plugin to work with the latest IPython.nbconvert machinery
* Fixed failing build because of hidden folders and files inside post folder
-New in 5.5
-==========
+New in v5.5
+===========
Features
--------
@@ -415,8 +462,8 @@ Bugfixes
* Don't map empty folders, map folders with index.html, and don't map index.html (Issue #430)
* Wordpress import: write correct redirections for URLs not ending in an / (Issue #459)
-New in 5.4.4
-============
+New in v5.4.4
+=============
Features
--------
@@ -448,8 +495,8 @@ Bugfixes
* Fix aspect ratio detection in Vimeo videos (Issue #440)
* Blogger importer was passing wrong options to "nikola init" (Issue #408)
-New in 5.4.3
-============
+New in v5.4.3
+=============
Features
--------
@@ -512,8 +559,8 @@ Bugfixes
* Planetoid requires only 3 runs now ;-)
* Blogger import: imports will not result in an TypeError because str.join expects all it's arguments to be of type str
-New in 5.4.2
-============
+New in v5.4.2
+=============
Bugfixes
--------
@@ -521,8 +568,8 @@ Bugfixes
* Fix relative paths when stories is "dropped to root" (Issue #362)
* Pick translated titles in 1-file posts (Issue #365)
-New in 5.4
-==========
+New in v5.4
+===========
Features
--------
@@ -560,8 +607,8 @@ Bugfixes
* Wordpress Import: Do not break indentation (issue #189)
* Make things work even if SITE_URL has a path (Fix #307)
-New in 5.3
-==========
+New in v5.3
+===========
Features
--------
@@ -588,8 +635,8 @@ Bugfixes
* Inconsistent breadcrumbs in gallery pages (Issue #303)
* Use source files as bundle dependencies instead of outputs (Issue #294)
-New in 5.2
-==========
+New in v5.2
+===========
Features
--------
@@ -703,8 +750,8 @@ Bugfixes
* Fixed Issue 161: webassets setting USE_BUNDLES was ignored (fix by schettino72)
* Fixed Issue 153: index.txt was being ignored in galleries.
-New in Version 4.0.3
-====================
+New in v4.0.3
+=============
Bugfixes
--------
@@ -718,8 +765,8 @@ Bugfixes
* Fix empty tag feeds.
* Refactored Post out of nikola.py
-New in Version 4.0.2
-====================
+New in v4.0.2
+=============
Features
--------
@@ -746,8 +793,8 @@ Bugfixes
* Made jinja-default follow default more closely.
* Don't say "reSt", say "Source" since it can be markdown or other stuff.
-New in Version 4.0.1
-====================
+New in v4.0.1
+=============
Features
--------
@@ -761,8 +808,8 @@ Bugfixes
* "Read More" is translatable.
* Fixed Issue 121: CSS was not found if webassets was not installed.
-New in Version 4
-================
+New in v4
+=========
Features
--------
diff --git a/docs/creating-a-site.txt b/docs/creating-a-site.txt
index 097bfb5..423b2c1 100644
--- a/docs/creating-a-site.txt
+++ b/docs/creating-a-site.txt
@@ -8,6 +8,8 @@
Creating a Site (Not a Blog) with Nikola
========================================
+.. class:: lead
+
One of the most frequent questions I get about Nikola is "but how do
I create a site that's not a blog?". And of course, that's because the
documentation is heavily blog-oriented. This document will change that ;-)
diff --git a/docs/creating-a-theme.txt b/docs/creating-a-theme.txt
index 35c2dbf..40a4566 100644
--- a/docs/creating-a-theme.txt
+++ b/docs/creating-a-theme.txt
@@ -8,6 +8,8 @@
Creating A Theme From Scratch (Almost)
======================================
+.. class:: lead
+
There is some documentation about creating themes for Nikola, but maybe a tutorial is also a useful way
to explain it. So, here it is. I'll explain how to create a theme (almost) from scratch. All themes
in Nikola must inherit from the ``base`` theme. In this case, we will inherit from ``bootstrap``
diff --git a/docs/extending.txt b/docs/extending.txt
index 25d1782..fb216c5 100644
--- a/docs/extending.txt
+++ b/docs/extending.txt
@@ -8,7 +8,7 @@
Extending Nikola
================
-:Version: 6.3.0
+:Version: 6.4.0
:Author: Roberto Alsina <ralsina@netmanagers.com.ar>
.. class:: alert alert-info pull-right
@@ -16,11 +16,13 @@ Extending Nikola
.. contents::
+.. class:: lead
+
Nikola is extensible. Almost all its functionality is based on plugins,
and you can add your own or replace the provided ones.
Plugins consist of a metadata file (with ``.plugin`` extension) and
-a python module (a ``.py`` file) or package (a folder containing
+a Python module (a ``.py`` file) or package (a folder containing
a ``__init__.py`` file.
To use a plugin in your site, you just have to put it in a ``plugins``
@@ -74,7 +76,7 @@ When you run ``nikola --help`` you will see something like this::
That will give you a list of all available commands in your version of Nikola.
Each and every one of those is a plugin. Let's look at a typical example:
-First, the ``command_serve.plugin`` file:
+First, the ``serve.plugin`` file:
.. code-block:: ini
@@ -94,7 +96,7 @@ First, the ``command_serve.plugin`` file:
(and the .plugin file examples and explanations).
For your own plugin, just change the values in a sensible way. The
-``Module`` will be used to find the matching python module, in this case
+``Module`` will be used to find the matching Python module, in this case
``serve.py``, from which this is the interesting bit:
.. code-block:: python
@@ -104,7 +106,7 @@ For your own plugin, just change the values in a sensible way. The
# You have to inherit Command for this to be a
# command plugin:
- class CommandBuild(Command):
+ class CommandServe(Command):
"""Start test server."""
name = "serve"
@@ -272,9 +274,9 @@ These have access to the ``site`` object which contains your timeline and
your configuration.
The critical bit of Task plugins is their ``gen_tasks`` method, which ``yields``
-`doit tasks <http://pydoit.org/tasks.html>`_
+`doit tasks <http://pydoit.org/tasks.html>`_.
-The details of how to handle dependencies, etc. are a bit too much for this
+The details of how to handle dependencies, etc., are a bit too much for this
document, so I'll just leave you with an example, the ``copy_assets`` task.
First the ``task_copy_assets.plugin`` file, which you should copy and edit
in the logical ways:
@@ -371,7 +373,7 @@ SignalHandler Plugins
---------------------
These plugins extend the ``SignalHandler`` class and connect to one or more
-signals via `blinker <http://pythonhosted.org/blinker/>`_
+signals via `blinker <http://pythonhosted.org/blinker/>`_.
The easiest way to do this is to reimplement ``set_site()`` and just connect to
whatever signals you want there.
@@ -381,7 +383,7 @@ Currently Nikola emits the following signals:
``sighandlers_loaded``
Right after SignalHandler plugin activation.
``initialized``
- Right after plugin activation
+ Right after plugin activation.
``configured``
When all the configuration file is processed. Note that plugins are activated before this is emitted.
``new_post``
diff --git a/docs/getting-help.txt b/docs/getting-help.txt
index 9cad1b3..31edc8d 100644
--- a/docs/getting-help.txt
+++ b/docs/getting-help.txt
@@ -3,7 +3,7 @@
.. date: 1970-01-01 15:00:00
.. description: Get help using Nikola, or contact us.
-:Version: 6.3.0
+:Version: 6.4.0
.. class:: alert alert-info pull-right
diff --git a/docs/internals.txt b/docs/internals.txt
index 75e2a97..06a2747 100644
--- a/docs/internals.txt
+++ b/docs/internals.txt
@@ -8,6 +8,8 @@
Nikola Internals
================
+.. class:: lead
+
When trying to guide someone into adding a feature in Nikola, it hit me that
while the way it's structured makes sense **to me** it is far from obvious.
@@ -52,7 +54,7 @@ actions
What the task **does**. For example, convert a markdown document into HTML.
dependencies
- If this file changes, then we need to redo the actions. If this confguration
+ If this file changes, then we need to redo the actions. If this configuration
option changes, redo it, etc.
targets
@@ -90,7 +92,7 @@ Posts and Stories
Nikola has a concept of posts and stories. Both are more or less the same thing, except
posts are added into RSS feeds and stories are not. All of them are in a list called
-"the timeline" formed by objects of class ``Post``
+"the timeline" formed by objects of class ``Post``.
When you are creating a task that needs the list of posts and/or stories (for example,
the RSS creation plugin), your plugin should call ``self.site.scan_posts()`` to ensure
@@ -115,12 +117,12 @@ The workflow included with nikola is as follows (incomplete!):
fragment is stored in a cache (the ``posts`` plugin).
#. The configured theme has templates (and a template engine), which are applied to the post's
HTML fragment and metadata (the ``pages`` plugin).
-#. The original sources for the post are copied to some accessible place (the ``sources`` plugin)
-#. If the post is tagged, some pages and RSS feeds for each tag are updated (the ``tags`` plugin)
-#. If the post is new, it's included in the blog's RSS feed (the ``rss`` plugin)
-#. The post is added in the right place in the index pages for the blog (the ``indexes`` plugin)
-#. CSS/JS/Images for the theme are put in the right places (the ``copy_assets`` and ``bundles`` plugins)
-#. A File describing the whole site is created (the ``sitemap`` plugin)
+#. The original sources for the post are copied to some accessible place (the ``sources`` plugin).
+#. If the post is tagged, some pages and RSS feeds for each tag are updated (the ``tags`` plugin).
+#. If the post is new, it's included in the blog's RSS feed (the ``rss`` plugin).
+#. The post is added in the right place in the index pages for the blog (the ``indexes`` plugin).
+#. CSS/JS/Images for the theme are put in the right places (the ``copy_assets`` and ``bundles`` plugins).
+#. A File describing the whole site is created (the ``sitemap`` plugin).
You can add whatever you want to that list: just create a plugin for it.
diff --git a/docs/man/nikola.1 b/docs/man/nikola.1
index f794ccb..214a924 100644
--- a/docs/man/nikola.1
+++ b/docs/man/nikola.1
@@ -1,9 +1,9 @@
-.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.43.3.
-.TH NIKOLA "1" "January 2014" "nikola 6.3.0" "User Commands"
+.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.44.1.
+.TH NIKOLA "1" "March 2014" "nikola 6.4.0" "User Commands"
.SH NAME
-nikola \- manual page for nikola 6.3.0
+nikola \- manual page for nikola 6.4.0
.SH DESCRIPTION
-Nikola is a tool to create static websites and blogs. For full documentation and more information, please visit http://getnikola.com
+Nikola is a tool to create static websites and blogs. For full documentation and more information, please visit http://getnikola.com/
.SS "Available commands:"
.TP
nikola auto
@@ -66,6 +66,9 @@ list tasks from dodo file
nikola mincss
apply mincss to the generated site
.TP
+nikola new_page
+create a new page in the site
+.TP
nikola new_post
create a new blog post or site page
.TP
diff --git a/docs/manual.txt b/docs/manual.txt
index d78b4f6..ea080bb 100644
--- a/docs/manual.txt
+++ b/docs/manual.txt
@@ -8,7 +8,7 @@
The Nikola Handbook
===================
-:Version: 6.3.0
+:Version: 6.4.0
.. class:: alert alert-info pull-right
@@ -203,16 +203,6 @@ of how to "get" something for your specific operating system are left to you.
The short version is: ``pip install nikola``
-If you are running Arch Linux, there are AUR packages, available in Python 2/3
-and stable/git master flavors: `python-nikola`__ / `python2-nikola`__ for the
-latest stable release or `python-nikola-git`__ / `python2-nikola-git`__ for the
-GitHub master. (only one package may be installed at the same time.)
-
-__ https://aur.archlinux.org/packages/python-nikola/
-__ https://aur.archlinux.org/packages/python2-nikola/
-__ https://aur.archlinux.org/packages/python-nikola-git/
-__ https://aur.archlinux.org/packages/python2-nikola-git/
-
Note that you need Python v2.6 or newer OR v3.3 or newer.
For some features it may give you an error message telling you that you need to
@@ -257,7 +247,16 @@ Longer version:
After that, run ``nikola init --demo sitename`` and that will create a folder called
``sitename`` containing a functional demo site.
-Nikola is packaged for some Linux distributions, you may get that instead.
+Nikola is packaged for some Linux distributions, you may get that instead. e.g.
+If you are running Arch Linux, there are AUR packages, available in Python 2/3
+and stable/git master flavors: `python-nikola`__ / `python2-nikola`__ for the
+latest stable release or `python-nikola-git`__ / `python2-nikola-git`__ for the
+GitHub master. (only one package may be installed at the same time.)
+
+__ https://aur.archlinux.org/packages/python-nikola/
+__ https://aur.archlinux.org/packages/python2-nikola/
+__ https://aur.archlinux.org/packages/python-nikola-git/
+__ https://aur.archlinux.org/packages/python2-nikola-git/
libxml/libxslt errors
~~~~~~~~~~~~~~~~~~~~~
@@ -310,10 +309,15 @@ Installation on Windows and Windows support
Nikola supports Windows! Keep in mind, though, that there are some
caveats:
-1. ``lxml`` and ``Pillow`` require compiled extensions. Compiling them on
-Windows is hard for most people. Fortunately, compiled packages exist. Check
-their `PyPI <https://pypi.python.org/>`_ pages to find official packages, `the unofficial Gohlke binaries <http://www.lfd.uci.edu/~gohlke/pythonlibs/>`_ site, or get them somewhere else. If you are using virtualenvs, using those pre-built packages is possible through ``virtualenv --system-site-packages``.
-2. Windows has some differences over POSIX, which may cause some features to work incorrectly under Windows. If any problems occur, please do not hesitate to report them. Some of the differeces include:
+#. ``lxml`` and ``Pillow`` require compiled extensions. Compiling them on
+ Windows is hard for most people. Fortunately, compiled packages exist.
+ Check their `PyPI <https://pypi.python.org/>`_ pages to find official packages,
+ `the unofficial Gohlke binaries <http://www.lfd.uci.edu/~gohlke/pythonlibs/>`_
+ site, or get them somewhere else. If you are using virtualenvs, using those
+ pre-built packages is possible through ``virtualenv --system-site-packages``.
+#. Windows has some differences over POSIX, which may cause some features to
+ work incorrectly under Windows. If any problems occur, please do not
+ hesitate to report them. Some of the differeces include:
* ``\`` as path separator (instead of ``/``)
* the concept of HDD partitions and letters (instead of
@@ -322,8 +326,8 @@ their `PyPI <https://pypi.python.org/>`_ pages to find official packages, `the u
problems)
* CR+LF (aka ``\r\n``) as the line separator (instead of LF ``\n``)
-3. Windows also dislikes some characters in paths.
-4. Most of our developers run Linux on a daily basis and may not have the full knowledge required to resolve issues relating to Windows.
+#. Most of our developers run Linux on a daily basis and may not have the full
+ knowledge required to resolve issues relating to Windows.
Getting Started
---------------
@@ -397,39 +401,41 @@ and even individual files like ``nikola build output/index.html``
Nikola also has other commands besides ``build``::
$ nikola help
- Nikola is a tool to create static websites and blogs. For full documentation and more information,
- please visit http://getnikola.com
-
-
- Available commands:
- nikola auto automatically detect site changes, rebuild and optionally refresh a browser
- nikola bootswatch_theme given a swatch name from bootswatch.com and a parent theme, creates a custom theme
- nikola build run tasks
- nikola check check links and files in the generated site
- nikola clean clean action / remove targets
- nikola console start an interactive python console with access to your site and configuration
- nikola deploy deploy the site
- nikola dumpdb dump dependency DB
- nikola forget clear successful run status from internal DB
- nikola help show help
- nikola ignore ignore task (skip) on subsequent runs
- nikola import_blogger import a blogger dump
- nikola import_feed import a RSS/Atom dump
- nikola import_wordpress import a WordPress dump
- nikola init create a Nikola site in the specified folder
- nikola install_theme install theme into current site
- nikola list list tasks from dodo file
- nikola mincss apply mincss to the generated site
- nikola new_post create a new blog post or site page
- nikola run run tasks
- nikola serve start the test webserver
- nikola strace use strace to list file_deps and targets
- nikola version print the Nikola version number
-
- nikola help show help / reference
- nikola help <command> show command usage
- nikola help <task-name> show task usage
-
+ Nikola is a tool to create static websites and blogs. For full documentation and more information, please visit http://getnikola.com/
+
+
+ Available commands:
+ nikola auto automatically detect site changes, rebuild and optionally refresh a browser
+ nikola bootswatch_theme given a swatch name from bootswatch.com and a parent theme, creates a custom theme
+ nikola build run tasks
+ nikola check check links and files in the generated site
+ nikola clean clean action / remove targets
+ nikola console start an interactive Python console with access to your site
+ nikola deploy deploy the site
+ nikola doit_auto automatically execute tasks when a dependency changes
+ nikola dumpdb dump dependency DB
+ nikola forget clear successful run status from internal DB
+ nikola help show help
+ nikola ignore ignore task (skip) on subsequent runs
+ nikola import_blogger import a blogger dump
+ nikola import_feed import a RSS/Atom dump
+ nikola import_wordpress import a WordPress dump
+ nikola init create a Nikola site in the specified folder
+ nikola install_plugin install plugin into current site
+ nikola install_theme install theme into current site
+ nikola list list tasks from dodo file
+ nikola mincss apply mincss to the generated site
+ nikola new_post create a new blog post or site page
+ nikola orphans list all orphans
+ nikola run run tasks
+ nikola serve start the test webserver
+ nikola strace use strace to list file_deps and targets
+ nikola tabcompletion generate script for tab-complention
+ nikola version print the Nikola version number
+
+ nikola help show help / reference
+ nikola help <command> show command usage
+ nikola help <task-name> show task usage
The ``serve`` command starts a web server so you can see the site you are creating::
@@ -496,7 +502,7 @@ The third line is the post's date, and is set to "now".
The other lines are optional. Tags are comma-separated. The ``link`` is an original
source for the content, and ``description`` is mostly useful for SEO.
``type`` is the post type, whatever you set here (prepended with ``post-``)
-will become a class of the `<article>` element for this post. Defaults to
+will become a CSS class of the ``<article>`` element for this post. Defaults to
``text`` (resulting in a ``post-text`` class)
You can add your own metadata fields in the same manner, if you use a theme that
@@ -661,8 +667,10 @@ and ``PAGES`` configuration options::
("stories/*.rst", "stories", "story.tmpl"),
)
-It will use the first location that has the last item in ``POSTS``, or the last
-one in ``PAGES`` if ``POSTS`` is empty.
+``new_post`` will use the *first* path in ``POSTS`` (or ``PAGES`` if ``-p`` is
+supplied) that ends with the extension of your desired markup format (as
+defined in ``COMPILERS`` in conf.py) as the directory that the new post will be
+written into. If no such entry can be found, the post won’t be created.
The ``new_post`` command supports some options::
@@ -798,7 +806,7 @@ Post Types
Nikola supports specifying post types, just like Tumblr does. Post
types affect the look of your posts, by adding a ``post-YOURINPUTHERE``
-class to the post. Each post can have one and exactly one type. Nikola
+CSS class to the post. Each post can have one and exactly one type. Nikola
styles the following types in the default themes:
+-----------------+----------------------------+------------------+
@@ -870,26 +878,38 @@ There are lots of things you can do to personalize your website, but let's see
the easy ones!
CSS tweaking
- The default configuration includes a file, ``themes/default/assets/css/custom.css``
- which is empty. Put your CSS there, for minimal disruption of the provided CSS files.
+ Using the default configuration, you can create a ``assets/css/custom.css``
+ file and then it will be loaded from the ``<head>`` blocks of your site
+ pages. Create it and put your CSS code there, for minimal disruption of the
+ provided CSS files.
If you feel tempted to touch other files in assets, you probably will be better off
with a `custom theme <theming.html>`__.
+ If you want to use LESS_ or Sass_ for your custom CSS, or the theme you use
+ contains LESS or Sass code that you want to override, create a ``less`` or
+ ``sass`` directory in your site root, put your ``.less`` or ``.scss`` files
+ there and a targets file containing the files you want compiled. Any
+ ``.less`` or ``.scss`` files from the theme chain that you want to use will
+ need to be included in your files.
+
+.. _LESS: http://lesscss.org/
+.. _Sass: http://sass-lang.com/
+
Template tweaking
If you really want to change the pages radically, you will want to do a
`custom theme <theming.html>`__.
Navigation Links
- The 'NAVIGATION_LINKS' option lets you define what links go in a sidebar or menu
+ The ``NAVIGATION_LINKS`` option lets you define what links go in a sidebar or menu
(depending on your theme) so you can link to important pages, or to other sites.
The format is a language-indexed dictionary, where each element is a tuple of
tuples which are one of:
1. A (url, text) tuple, describing a link
- 2. A ((url, text), (url, text), (url, text), title) tuple, describing a submenu / sublist.
+ 2. A (((url, text), (url, text), (url, text)), title) tuple, describing a submenu / sublist.
Example::
@@ -1148,7 +1168,7 @@ You can apply post processing to the files in your site, in order to optimize th
or change them in arbitrary ways. For example, you may want to compress all CSS
and JS files using yui-compressor.
-To do that, you can use the provided helper adding this in your ``config.py``::
+To do that, you can use the provided helper adding this in your ``conf.py``::
from nikola import filters
diff --git a/docs/social_buttons.txt b/docs/social_buttons.txt
index 61260bc..de7fecf 100644
--- a/docs/social_buttons.txt
+++ b/docs/social_buttons.txt
@@ -8,7 +8,7 @@
Using Alternative Social Buttons with Nikola
============================================
-:Version: 6.3.0
+:Version: 6.4.0
.. class:: alert alert-info pull-right
diff --git a/docs/sphinx/conf.py b/docs/sphinx/conf.py
index 26080aa..faf2db1 100644
--- a/docs/sphinx/conf.py
+++ b/docs/sphinx/conf.py
@@ -54,9 +54,9 @@ copyright = '2012-2014, The Nikola Contributors'
# built documents.
#
# The short X.Y version.
-version = '6.3.0'
+version = '6.4.0'
# The full version, including alpha/beta/rc tags.
-release = '6.3.0'
+release = '6.4.0'
# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.
diff --git a/docs/theming.txt b/docs/theming.txt
index 1886741..b370948 100644
--- a/docs/theming.txt
+++ b/docs/theming.txt
@@ -8,13 +8,15 @@
Theming Nikola
==============
-:Version: 6.3.0
+:Version: 6.4.0
:Author: Roberto Alsina <ralsina@netmanagers.com.ar>
.. class:: alert alert-info pull-right
.. contents::
+.. class:: lead
+
This document is a reference about themes. If you want a tutorial, please read
`Creating a Theme <creating-a-theme.html>`_
@@ -127,7 +129,8 @@ These are the templates that come with the included themes:
This template handles comments. You should probably never touch it :-)
It uses a bunch of helper templates, one for each supported comment system:
``disqus_helper.tmpl`` ``facebook_helper.tmpl`` ``googleplus_helper.tmpl``
- ``intensedebate_helper.tmpl`` ``livefyre_helper.tmpl`` ``moot_helper.tmpl``
+ ``intensedebate_helper.tmpl`` ``isso_helper.tmpl`` ``livefyre_helper.tmpl``
+ ``moot_helper.tmpl``
``crumbs.tmpl`` ``slides.tmpl``
These templates help render specific UI items, and can be tweaked as needed.
diff --git a/docs/upgrading-to-v6.txt b/docs/upgrading-to-v6.txt
index adcad08..ab89614 100644
--- a/docs/upgrading-to-v6.txt
+++ b/docs/upgrading-to-v6.txt
@@ -8,7 +8,9 @@
Upgrading to v6
===============
-:Version: 6.3.0
+:Version: 6.4.0
+
+.. class:: lead
Nikola tries fairly hard to be compatible between versions. However, there were
a few areas which were getting clunky, and needed fxing. So, here's what you may
diff --git a/dodo.py b/dodo.py
index 7ad61d6..27c39fa 100644
--- a/dodo.py
+++ b/dodo.py
@@ -19,14 +19,10 @@ def recursive_glob(path, pattern):
def task_flake8():
"""flake8 - static check for python files"""
- for fname in recursive_glob('.', '*.py'):
- if fname.startswith('./cache'):
- continue
- yield {
- 'name': fname,
- 'actions': ['flake8 --ignore=E501 {0}'.format(fname)],
- 'file_dep': [fname],
- }
+ yield {
+ 'name': os.getcwd(),
+ 'actions': ['flake8 --ignore=E501 .'],
+ }
def task_locale():
@@ -64,7 +60,7 @@ def task_test():
"""run unit-tests using nose"""
return {
'task_dep': ['locale'],
- 'actions': ['nosetests'],
+ 'actions': ['nosetests --with-doctest --doctest-options=+NORMALIZE_WHITESPACE --logging-filter=-yapsy'],
}
diff --git a/nikola/__init__.py b/nikola/__init__.py
index 0a82198..787ce8e 100644
--- a/nikola/__init__.py
+++ b/nikola/__init__.py
@@ -27,7 +27,7 @@
from __future__ import absolute_import
import os
-__version__ = "6.3.0"
+__version__ = "6.4.0"
DEBUG = bool(os.getenv('NIKOLA_DEBUG'))
from .nikola import Nikola # NOQA
diff --git a/nikola/__main__.py b/nikola/__main__.py
index 6b549b4..715f5b3 100644
--- a/nikola/__main__.py
+++ b/nikola/__main__.py
@@ -60,6 +60,16 @@ def main(args):
quiet = True
global config
+ colorful = False
+ if sys.stderr.isatty():
+ colorful = True
+ try:
+ import colorama
+ colorama.init()
+ except ImportError:
+ if os.name == 'nt':
+ colorful = False
+
root = get_root_dir()
if root:
os.chdir(root)
@@ -76,6 +86,8 @@ def main(args):
sys.exit(1)
config = {}
+ config.update({'__colorful__': colorful})
+
site = Nikola(**config)
return DoitNikola(site, quiet).run(args)
@@ -86,7 +98,7 @@ class Help(DoitHelp):
@staticmethod
def print_usage(cmds):
"""print nikola "usage" (basic help) instructions"""
- print("Nikola is a tool to create static websites and blogs. For full documentation and more information, please visit http://getnikola.com\n\n")
+ print("Nikola is a tool to create static websites and blogs. For full documentation and more information, please visit http://getnikola.com/\n\n")
print("Available commands:")
for cmd in sorted(cmds.values(), key=attrgetter('name')):
print(" nikola %-*s %s" % (20, cmd.name, cmd.doc_purpose))
diff --git a/nikola/conf.py.in b/nikola/conf.py.in
index 6ae0e1d..b398ac3 100644
--- a/nikola/conf.py.in
+++ b/nikola/conf.py.in
@@ -4,21 +4,21 @@
from __future__ import unicode_literals
import time
-##############################################
-# Configuration, please edit
-##############################################
+#!! This is the configuration of Nikola. !!#
+#!! You should edit it to your liking. !!#
+
# Data about this site
-BLOG_AUTHOR = "${BLOG_AUTHOR}"
-BLOG_TITLE = "${BLOG_TITLE}"
+BLOG_AUTHOR = ${BLOG_AUTHOR}
+BLOG_TITLE = ${BLOG_TITLE}
# This is the main URL for your site. It will be used
# in a prominent link
-SITE_URL = "${SITE_URL}"
+SITE_URL = ${SITE_URL}
# This is the URL where nikola's output will be deployed.
# If not set, defaults to SITE_URL
-# BASE_URL = "${SITE_URL}"
-BLOG_EMAIL = "${BLOG_EMAIL}"
-BLOG_DESCRIPTION = "${BLOG_DESCRIPTION}"
+# BASE_URL = ${SITE_URL}
+BLOG_EMAIL = ${BLOG_EMAIL}
+BLOG_DESCRIPTION = ${BLOG_DESCRIPTION}
# Nikola is multilingual!
#
@@ -36,6 +36,7 @@ BLOG_DESCRIPTION = "${BLOG_DESCRIPTION}"
# fa Persian
# fi Finnish
# fr French
+# hi Hindi
# hr Croatian
# it Italian
# ja Japanese [NOT jp!]
@@ -51,12 +52,12 @@ BLOG_DESCRIPTION = "${BLOG_DESCRIPTION}"
#
# If you want to use Nikola with a non-supported language you have to provide
# a module containing the necessary translations
-# (cf. the modules at nikola/data/themes/base/messages/fr.py).
+# (cf. the modules at nikola/data/themes/base/messages/).
# If a specific post is not translated to a language, then the version
# in the default language will be shown instead.
# What is the default language?
-DEFAULT_LANG = "${DEFAULT_LANG}"
+DEFAULT_LANG = ${DEFAULT_LANG}
# What other languages do you have?
# The format is {"translationcode" : "path/to/translation" }
@@ -75,7 +76,7 @@ TRANSLATIONS = {
# this pattern is also used for metadata:
# something.meta -> something.meta.pl
-TRANSLATIONS_PATTERN = "{path}.{ext}.{lang}"
+TRANSLATIONS_PATTERN = ${TRANSLATIONS_PATTERN}
# If you don't want your Polish files to be considered Perl code, use this:
# TRANSLATIONS_PATTERN = "{path}.{lang}.{ext}"
@@ -172,7 +173,8 @@ COMPILERS = ${COMPILERS}
# the posts themselves. If set to False, it will be just a list of links.
# TAG_PAGES_ARE_INDEXES = True
-# Final location is output / TRANSLATION[lang] / INDEX_PATH / index-*.html
+# Final location for the main blog page and sibling paginated pages is
+# output / TRANSLATION[lang] / INDEX_PATH / index-*.html
# INDEX_PATH = ""
# Create per-month archives instead of per-year
@@ -192,7 +194,7 @@ COMPILERS = ${COMPILERS}
# absolute: a complete URL (that includes the SITE_URL)
# URL_TYPE = 'rel_path'
-# Final locations are:
+# Final location for the blog main RSS feed is:
# output / TRANSLATION[lang] / RSS_PATH / rss.xml
# RSS_PATH = ""
@@ -270,9 +272,17 @@ COMPILERS = ${COMPILERS}
# Compiler to process LESS files.
# LESS_COMPILER = 'lessc'
+# A list of options to pass to the LESS compiler.
+# Final command is: LESS_COMPILER LESS_OPTIONS file.less
+# LESS_OPTIONS = []
+
# Compiler to process Sass files.
# SASS_COMPILER = 'sass'
+# A list of options to pass to the Sass compiler.
+# Final command is: SASS_COMPILER SASS_OPTIONS file.s(a|c)ss
+# SASS_OPTIONS = []
+
# #############################################################################
# Image Gallery Options
# #############################################################################
@@ -301,7 +311,7 @@ COMPILERS = ${COMPILERS}
# the main (the newest) index page (index.html)
# Name of the theme to use.
-THEME = "${THEME}"
+THEME = ${THEME}
# Color scheme to be used for code blocks. If your theme provides
# "assets/css/code.css" this is ignored.
@@ -354,7 +364,6 @@ LICENSE = ""
# src="http://i.creativecommons.org/l/by-nc-sa/2.5/ar/88x31.png"></a>"""
# A small copyright notice for the page footer (in HTML).
-# Default is ''
CONTENT_FOOTER = 'Contents &copy; {date} \
<a href="mailto:{email}">{author}</a> - Powered by \
<a href="http://getnikola.com" rel="nofollow">Nikola</a> \
@@ -367,12 +376,12 @@ 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", "facebook" or "isso"
-# COMMENT_SYSTEM = "disqus"
+# COMMENT_SYSTEM = ${COMMENT_SYSTEM}
# And you also need to add your COMMENT_SYSTEM_ID which
# depends on what comment system you use. The default is
# "nikolademo" which is a test account for Disqus. More information
# is in the manual.
-# COMMENT_SYSTEM_ID = "nikolademo"
+# COMMENT_SYSTEM_ID = ${COMMENT_SYSTEM_ID}
# Enable annotations using annotateit.org?
# If set to False, you can still enable them for individual posts and pages
@@ -572,9 +581,9 @@ CONTENT_FOOTER = CONTENT_FOOTER.format(email=BLOG_EMAIL,
# USE_CDN = False
# Extra things you want in the pages HEAD tag. This will be added right
-# before </HEAD>
+# before </head>
# EXTRA_HEAD_DATA = ""
-# Google analytics or whatever else you use. Added to the bottom of <body>
+# Google Analytics or whatever else you use. Added to the bottom of <body>
# in the default template (base.tmpl).
# BODY_END = ""
@@ -680,7 +689,11 @@ LOGGING_HANDLERS = {
#}
}
+# Templates will use those filters, along with the defaults.
+# Consult your engine's documentation on filters if you need help defining
+# those.
+# TEMPLATE_FILTERS = {}
+
# Put in global_context things you want available on all your templates.
# It can be anything, data, functions, modules, etc.
-
GLOBAL_CONTEXT = {}
diff --git a/nikola/data/themes/base/messages/messages_bg.py b/nikola/data/themes/base/messages/messages_bg.py
index d4881b5..6e85212 100644
--- a/nikola/data/themes/base/messages/messages_bg.py
+++ b/nikola/data/themes/base/messages/messages_bg.py
@@ -11,6 +11,8 @@ MESSAGES = {
"More posts about": "Още публикации относно",
"Newer posts": "Нови публикации",
"Next post": "Следваща публикация",
+ "No posts found.": "",
+ "Nothing found.": "",
"Older posts": "Стари публикации",
"Original site": "Оригиналния сайт",
"Posted": "Публиковано",
diff --git a/nikola/data/themes/base/messages/messages_ca.py b/nikola/data/themes/base/messages/messages_ca.py
index d3a97b5..220d571 100644
--- a/nikola/data/themes/base/messages/messages_ca.py
+++ b/nikola/data/themes/base/messages/messages_ca.py
@@ -11,6 +11,8 @@ MESSAGES = {
"More posts about": "Més entrades sobre",
"Newer posts": "Entrades posteriors",
"Next post": "Entrada següent",
+ "No posts found.": "",
+ "Nothing found.": "",
"Older posts": "Entrades anteriors",
"Original site": "Lloc original",
"Posted": "Publicat",
diff --git a/nikola/data/themes/base/messages/messages_cs.py b/nikola/data/themes/base/messages/messages_cs.py
index 33482b5..f66c2c4 100644
--- a/nikola/data/themes/base/messages/messages_cs.py
+++ b/nikola/data/themes/base/messages/messages_cs.py
@@ -11,6 +11,8 @@ MESSAGES = {
"More posts about": "Další příspěvky o",
"Newer posts": "Novější příspěvky",
"Next post": "Další příspěvek",
+ "No posts found.": "",
+ "Nothing found.": "",
"Older posts": "Starší příspěvky",
"Original site": "Původní stránka",
"Posted": "Zveřejněno",
diff --git a/nikola/data/themes/base/messages/messages_de.py b/nikola/data/themes/base/messages/messages_de.py
index 6795031..41fe015 100644
--- a/nikola/data/themes/base/messages/messages_de.py
+++ b/nikola/data/themes/base/messages/messages_de.py
@@ -11,6 +11,8 @@ MESSAGES = {
"More posts about": "Weitere Einträge über",
"Newer posts": "Neuere Einträge",
"Next post": "Nächster Eintrag",
+ "No posts found.": "Keine einträge gefunden.",
+ "Nothing found.": "Nichts gefunden.",
"Older posts": "Ältere Einträge",
"Original site": "Original-Seite",
"Posted": "Veröffentlicht",
diff --git a/nikola/data/themes/base/messages/messages_el.py b/nikola/data/themes/base/messages/messages_el.py
index 710558b..f658fa0 100644
--- a/nikola/data/themes/base/messages/messages_el.py
+++ b/nikola/data/themes/base/messages/messages_el.py
@@ -11,6 +11,8 @@ MESSAGES = {
"More posts about": "Περισσότερες αναρτήσεις για",
"Newer posts": "Νεότερες αναρτήσεις",
"Next post": "Επόμενη ανάρτηση",
+ "No posts found.": "",
+ "Nothing found.": "",
"Older posts": "Παλαιότερες αναρτήσεις",
"Original site": "Ιστοσελίδα αρχικής ανάρτησης",
"Posted": "Αναρτήθηκε",
diff --git a/nikola/data/themes/base/messages/messages_en.py b/nikola/data/themes/base/messages/messages_en.py
index 021f5e7..e2bff53 100644
--- a/nikola/data/themes/base/messages/messages_en.py
+++ b/nikola/data/themes/base/messages/messages_en.py
@@ -11,6 +11,8 @@ MESSAGES = {
"More posts about": "More posts about",
"Newer posts": "Newer posts",
"Next post": "Next post",
+ "No posts found.": "No posts found.",
+ "Nothing found.": "Nothing found.",
"Older posts": "Older posts",
"Original site": "Original site",
"Posted": "Posted",
diff --git a/nikola/data/themes/base/messages/messages_eo.py b/nikola/data/themes/base/messages/messages_eo.py
index fdbea88..f59a441 100644
--- a/nikola/data/themes/base/messages/messages_eo.py
+++ b/nikola/data/themes/base/messages/messages_eo.py
@@ -11,6 +11,8 @@ MESSAGES = {
"More posts about": "Pli artikoloj pri",
"Newer posts": "Pli novaj artikoloj",
"Next post": "Venonta artikolo",
+ "No posts found.": "",
+ "Nothing found.": "",
"Older posts": "Pli malnovaj artikoloj",
"Original site": "Originala interretejo",
"Posted": "Skribita",
diff --git a/nikola/data/themes/base/messages/messages_es.py b/nikola/data/themes/base/messages/messages_es.py
index 6c48fb9..1923683 100644
--- a/nikola/data/themes/base/messages/messages_es.py
+++ b/nikola/data/themes/base/messages/messages_es.py
@@ -11,6 +11,8 @@ MESSAGES = {
"More posts about": "Más posts sobre",
"Newer posts": "Posts posteriores",
"Next post": "Siguiente post",
+ "No posts found.": "",
+ "Nothing found.": "",
"Older posts": "Posts anteriores",
"Original site": "Sitio original",
"Posted": "Publicado",
diff --git a/nikola/data/themes/base/messages/messages_et.py b/nikola/data/themes/base/messages/messages_et.py
index 314f3b8..058ab5f 100644
--- a/nikola/data/themes/base/messages/messages_et.py
+++ b/nikola/data/themes/base/messages/messages_et.py
@@ -11,6 +11,8 @@ MESSAGES = {
"More posts about": "Veel postitusi kohta",
"Newer posts": "Uued postitused",
"Next post": "Järgmine postitus",
+ "No posts found.": "",
+ "Nothing found.": "",
"Older posts": "Vanemad postitused",
"Original site": "Algallikas",
"Posted": "Postitatud",
diff --git a/nikola/data/themes/base/messages/messages_eu.py b/nikola/data/themes/base/messages/messages_eu.py
index 18d7575..a8eb743 100644
--- a/nikola/data/themes/base/messages/messages_eu.py
+++ b/nikola/data/themes/base/messages/messages_eu.py
@@ -11,6 +11,8 @@ MESSAGES = {
"More posts about": "-ri buruzko post gehiago",
"Newer posts": "Post berrienak",
"Next post": "Hurrengo posta",
+ "No posts found.": "",
+ "Nothing found.": "",
"Older posts": "Post zaharrenak",
"Original site": "Jatorrizko orria",
"Posted": "Argitaratuta",
diff --git a/nikola/data/themes/base/messages/messages_fa.py b/nikola/data/themes/base/messages/messages_fa.py
index bd278ca..4475e1b 100644
--- a/nikola/data/themes/base/messages/messages_fa.py
+++ b/nikola/data/themes/base/messages/messages_fa.py
@@ -11,6 +11,8 @@ MESSAGES = {
"More posts about": "ارسال‌های بیشتر دربارهٔ",
"Newer posts": "ارسال‌های جدید‌تر",
"Next post": "ارسال بعدی",
+ "No posts found.": "",
+ "Nothing found.": "",
"Older posts": "پست‌های قدیمی‌تر",
"Original site": "سایت اصلی",
"Posted": "ارسال شده",
diff --git a/nikola/data/themes/base/messages/messages_fi.py b/nikola/data/themes/base/messages/messages_fi.py
index b24ee2c..42e6fa2 100644
--- a/nikola/data/themes/base/messages/messages_fi.py
+++ b/nikola/data/themes/base/messages/messages_fi.py
@@ -11,6 +11,8 @@ MESSAGES = {
"More posts about": "Lisää postauksia aiheesta",
"Newer posts": "Uudempia postauksia",
"Next post": "Seuraava postaus",
+ "No posts found.": "",
+ "Nothing found.": "",
"Older posts": "Vanhempia postauksia",
"Original site": "Alkuperäinen sivusto",
"Posted": "Postattu",
diff --git a/nikola/data/themes/base/messages/messages_fr.py b/nikola/data/themes/base/messages/messages_fr.py
index ad4aea0..484d695 100644
--- a/nikola/data/themes/base/messages/messages_fr.py
+++ b/nikola/data/themes/base/messages/messages_fr.py
@@ -11,6 +11,8 @@ MESSAGES = {
"More posts about": "Plus d'articles sur",
"Newer posts": "Billets récents",
"Next post": "Article suivant",
+ "No posts found.": "",
+ "Nothing found.": "",
"Older posts": "Anciens articles",
"Original site": "Site d'origine",
"Posted": "Publié",
diff --git a/nikola/data/themes/base/messages/messages_hi.py b/nikola/data/themes/base/messages/messages_hi.py
new file mode 100644
index 0000000..f72d5af
--- /dev/null
+++ b/nikola/data/themes/base/messages/messages_hi.py
@@ -0,0 +1,31 @@
+# -*- 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": "अगली पोस्ट",
+ "No posts found.": "",
+ "Nothing found.": "",
+ "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": "पृष्‍ठ %d",
+}
diff --git a/nikola/data/themes/base/messages/messages_hr.py b/nikola/data/themes/base/messages/messages_hr.py
index ad74078..ee5ce41 100644
--- a/nikola/data/themes/base/messages/messages_hr.py
+++ b/nikola/data/themes/base/messages/messages_hr.py
@@ -11,6 +11,8 @@ MESSAGES = {
"More posts about": "Više postova o",
"Newer posts": "Noviji postovi",
"Next post": "Sljedeći post",
+ "No posts found.": "",
+ "Nothing found.": "",
"Older posts": "Stariji postovi",
"Original site": "Izvorna stranica",
"Posted": "Objavljeno",
diff --git a/nikola/data/themes/base/messages/messages_it.py b/nikola/data/themes/base/messages/messages_it.py
index 912342e..87e25e5 100644
--- a/nikola/data/themes/base/messages/messages_it.py
+++ b/nikola/data/themes/base/messages/messages_it.py
@@ -11,6 +11,8 @@ MESSAGES = {
"More posts about": "Altri articoli collegati",
"Newer posts": "Articoli recenti",
"Next post": "Articolo successivo",
+ "No posts found.": "",
+ "Nothing found.": "",
"Older posts": "Articoli precedenti",
"Original site": "Sito originale",
"Posted": "Pubblicato",
diff --git a/nikola/data/themes/base/messages/messages_ja.py b/nikola/data/themes/base/messages/messages_ja.py
index 1bdf168..2df16a4 100644
--- a/nikola/data/themes/base/messages/messages_ja.py
+++ b/nikola/data/themes/base/messages/messages_ja.py
@@ -11,6 +11,8 @@ MESSAGES = {
"More posts about": "タグ:",
"Newer posts": "新しい記事",
"Next post": "次の記事",
+ "No posts found.": "",
+ "Nothing found.": "",
"Older posts": "過去の記事",
"Original site": "元のサイト",
"Posted": "投稿日時",
diff --git a/nikola/data/themes/base/messages/messages_nb.py b/nikola/data/themes/base/messages/messages_nb.py
index 154e329..44fde8a 100644
--- a/nikola/data/themes/base/messages/messages_nb.py
+++ b/nikola/data/themes/base/messages/messages_nb.py
@@ -11,6 +11,8 @@ MESSAGES = {
"More posts about": "Flere innlegg om",
"Newer posts": "Nyere innlegg",
"Next post": "Neste innlegg",
+ "No posts found.": "",
+ "Nothing found.": "",
"Older posts": "Eldre innlegg",
"Original site": "Opprinnelig side",
"Posted": "Publisert",
diff --git a/nikola/data/themes/base/messages/messages_nl.py b/nikola/data/themes/base/messages/messages_nl.py
index 887e85f..1952d2e 100644
--- a/nikola/data/themes/base/messages/messages_nl.py
+++ b/nikola/data/themes/base/messages/messages_nl.py
@@ -11,6 +11,8 @@ MESSAGES = {
"More posts about": "Meer berichten over",
"Newer posts": "Nieuwere berichten",
"Next post": "Volgend bericht",
+ "No posts found.": "",
+ "Nothing found.": "",
"Older posts": "Oudere berichten",
"Original site": "Originele site",
"Posted": "Geplaatst",
diff --git a/nikola/data/themes/base/messages/messages_pl.py b/nikola/data/themes/base/messages/messages_pl.py
index 352b0ed..a1183ba 100644
--- a/nikola/data/themes/base/messages/messages_pl.py
+++ b/nikola/data/themes/base/messages/messages_pl.py
@@ -11,6 +11,8 @@ MESSAGES = {
"More posts about": "Więcej postów o",
"Newer posts": "Nowsze posty",
"Next post": "Następny post",
+ "No posts found.": "Nie znaleziono żadnych postów.",
+ "Nothing found.": "Nic nie znaleziono.",
"Older posts": "Starsze posty",
"Original site": "Oryginalna strona",
"Posted": "Opublikowano",
diff --git a/nikola/data/themes/base/messages/messages_pt_br.py b/nikola/data/themes/base/messages/messages_pt_br.py
index 1283a2a..bf515e4 100644
--- a/nikola/data/themes/base/messages/messages_pt_br.py
+++ b/nikola/data/themes/base/messages/messages_pt_br.py
@@ -11,6 +11,8 @@ MESSAGES = {
"More posts about": "Mais posts sobre",
"Newer posts": "Posts mais recentes",
"Next post": "Próximo post",
+ "No posts found.": "",
+ "Nothing found.": "",
"Older posts": "Posts mais antigos",
"Original site": "Site original",
"Posted": "Publicado",
diff --git a/nikola/data/themes/base/messages/messages_ru.py b/nikola/data/themes/base/messages/messages_ru.py
index 3462292..fb33b85 100644
--- a/nikola/data/themes/base/messages/messages_ru.py
+++ b/nikola/data/themes/base/messages/messages_ru.py
@@ -11,6 +11,8 @@ MESSAGES = {
"More posts about": "Больше записей о",
"Newer posts": "Новые записи",
"Next post": "Следующая запись",
+ "No posts found.": "",
+ "Nothing found.": "",
"Older posts": "Старые записи",
"Original site": "Оригинальный сайт",
"Posted": "Опубликовано",
diff --git a/nikola/data/themes/base/messages/messages_sl.py b/nikola/data/themes/base/messages/messages_sl.py
index 817bcee..92ad483 100644
--- a/nikola/data/themes/base/messages/messages_sl.py
+++ b/nikola/data/themes/base/messages/messages_sl.py
@@ -11,6 +11,8 @@ MESSAGES = {
"More posts about": "Več objav o",
"Newer posts": "Novejše objave",
"Next post": "Naslednja objava",
+ "No posts found.": "",
+ "Nothing found.": "",
"Older posts": "Starejše objave",
"Original site": "Izvorna spletna stran",
"Posted": "Objavljeno",
diff --git a/nikola/data/themes/base/messages/messages_tr_tr.py b/nikola/data/themes/base/messages/messages_tr_tr.py
index 633f057..95c5736 100644
--- a/nikola/data/themes/base/messages/messages_tr_tr.py
+++ b/nikola/data/themes/base/messages/messages_tr_tr.py
@@ -11,6 +11,8 @@ MESSAGES = {
"More posts about": " ilgili diğer yazılar",
"Newer posts": "Daha yeni yazılar",
"Next post": "Sonraki yazı",
+ "No posts found.": "",
+ "Nothing found.": "",
"Older posts": "Daha eski yazılar",
"Original site": "Orjinal web sayfası",
"Posted": "Yayın tarihi",
diff --git a/nikola/data/themes/base/messages/messages_ur.py b/nikola/data/themes/base/messages/messages_ur.py
index d9c2f2b..794861d 100644
--- a/nikola/data/themes/base/messages/messages_ur.py
+++ b/nikola/data/themes/base/messages/messages_ur.py
@@ -11,6 +11,8 @@ MESSAGES = {
"More posts about": " کے بارے میں مزید تحاریر",
"Newer posts": "نئی تحاریر",
"Next post": "اگلی تحریر",
+ "No posts found.": "",
+ "Nothing found.": "",
"Older posts": "پرانی تحاریر",
"Original site": "اصلی سائٹ",
"Posted": "اشاعت",
@@ -25,5 +27,5 @@ MESSAGES = {
"Tags and Categories": "ٹیگز اور زمرے",
"Tags": "ٹیگز",
"old posts, page %d": "پرانی تحاریر صفحہ %d",
- "page %d": "",
+ "page %d": "صفحہ %d",
}
diff --git a/nikola/data/themes/base/messages/messages_zh_cn.py b/nikola/data/themes/base/messages/messages_zh_cn.py
index cb9a2f7..2f937c7 100644
--- a/nikola/data/themes/base/messages/messages_zh_cn.py
+++ b/nikola/data/themes/base/messages/messages_zh_cn.py
@@ -11,6 +11,8 @@ MESSAGES = {
"More posts about": "更多相关文章:",
"Newer posts": "新一篇",
"Next post": "后一篇",
+ "No posts found.": "",
+ "Nothing found.": "",
"Older posts": "旧一篇",
"Original site": "原文地址",
"Posted": "发表于",
diff --git a/nikola/data/themes/base/templates/base.tmpl b/nikola/data/themes/base/templates/base.tmpl
index 7c6cc35..8a90349 100644
--- a/nikola/data/themes/base/templates/base.tmpl
+++ b/nikola/data/themes/base/templates/base.tmpl
@@ -30,11 +30,16 @@ lang="${lang}">
<small>${content_footer}</small>
<!--Sidebar content-->
<ul class="unstyled">
+ %if license:
<li>${license}
+ %endif
${base.html_social()}
${base.html_navigation_links()}
+ %if search_form:
<li>${search_form}
+ %endif
</ul>
${base.late_load_js()}
${social_buttons_code}
</body>
+</html>
diff --git a/nikola/data/themes/base/templates/base_helper.tmpl b/nikola/data/themes/base/templates/base_helper.tmpl
index 880a998..501c06e 100644
--- a/nikola/data/themes/base/templates/base_helper.tmpl
+++ b/nikola/data/themes/base/templates/base_helper.tmpl
@@ -21,7 +21,9 @@
<link href="/assets/css/custom.css" rel="stylesheet" type="text/css">
%endif
%endif
- <link rel="canonical" href="${abs_link(permalink)}">
+ %if permalink:
+ <link rel="canonical" href="${abs_link(permalink)}">
+ %endif
<!--[if lt IE 9]>
<script src="http://html5shim.googlecode.com/svn/trunk/html5.js" type="text/javascript"></script>
<![endif]-->
diff --git a/nikola/data/themes/base/templates/crumbs.tmpl b/nikola/data/themes/base/templates/crumbs.tmpl
index c458cbe..8fbafcf 100644
--- a/nikola/data/themes/base/templates/crumbs.tmpl
+++ b/nikola/data/themes/base/templates/crumbs.tmpl
@@ -1,9 +1,11 @@
## -*- coding: utf-8 -*-
<%def name="bar(crumbs)">
+%if crumbs:
<ul class="breadcrumb">
% for link, text in crumbs:
<li><a href="${link}">${text}</a></li>
% endfor
</ul>
+%endif
</%def>
diff --git a/nikola/data/themes/base/templates/gallery.tmpl b/nikola/data/themes/base/templates/gallery.tmpl
index e4eab27..731a75a 100644
--- a/nikola/data/themes/base/templates/gallery.tmpl
+++ b/nikola/data/themes/base/templates/gallery.tmpl
@@ -14,18 +14,22 @@
${text}
</p>
%endif
+ %if folders:
<ul>
% for folder, ftitle in folders:
<li><a href="${folder}"><i
class="icon-folder-open"></i>&nbsp;${ftitle}</a></li>
% endfor
</ul>
+ %endif
+ %if photo_array:
<ul class="thumbnails">
%for image in photo_array:
<li><a href="${image['url']}" class="thumbnail image-reference" title="${image['title']}">
<img src="${image['url_thumb']}" alt="${image['title']}" /></a>
%endfor
</ul>
+ %endif
%if enable_comments:
${comments.comment_form(None, permalink, title)}
%endif
diff --git a/nikola/data/themes/base/templates/index_helper.tmpl b/nikola/data/themes/base/templates/index_helper.tmpl
index 56f5127..c925559 100755..100644
--- a/nikola/data/themes/base/templates/index_helper.tmpl
+++ b/nikola/data/themes/base/templates/index_helper.tmpl
@@ -20,6 +20,11 @@
<%def name="mathjax_script(posts)">
%if any(post.is_mathjax for post in posts):
+ <script type="text/x-mathjax-config">
+ MathJax.Hub.Config({
+ tex2jax: {inlineMath: [['$latex ','$'], ['\\(','\\)']]}
+ });
+ </script>
<script src="/assets/js/mathjax.js"></script>
%endif
</%def>
diff --git a/nikola/data/themes/base/templates/list.tmpl b/nikola/data/themes/base/templates/list.tmpl
index a60b508..4136eb9 100644
--- a/nikola/data/themes/base/templates/list.tmpl
+++ b/nikola/data/themes/base/templates/list.tmpl
@@ -4,11 +4,13 @@
<!--Body content-->
<div class="postbox">
<h1>${title}</h1>
+ %if items:
<ul class="unstyled">
% for text, link in items:
<li><a href="${link}">${text}</a>
% endfor
</ul>
+ %endif
</div>
<!--End of body content-->
</%block>
diff --git a/nikola/data/themes/base/templates/list_post.tmpl b/nikola/data/themes/base/templates/list_post.tmpl
index f0e159d..b27f230 100644
--- a/nikola/data/themes/base/templates/list_post.tmpl
+++ b/nikola/data/themes/base/templates/list_post.tmpl
@@ -4,11 +4,13 @@
<!--Body content-->
<div class="postbox">
<h1>${title}</h1>
+ %if posts:
<ul class="unstyled">
% for post in posts:
<li><a href="${post.permalink()}">[${post.formatted_date(date_format)}] ${post.title()}</a>
% endfor
</ul>
+ %endif
</div>
<!--End of body content-->
</%block>
diff --git a/nikola/data/themes/base/templates/listing.tmpl b/nikola/data/themes/base/templates/listing.tmpl
index b6ca83f..0662360 100644
--- a/nikola/data/themes/base/templates/listing.tmpl
+++ b/nikola/data/themes/base/templates/listing.tmpl
@@ -3,6 +3,7 @@
<%namespace name="ui" file="crumbs.tmpl" import="bar"/>
<%block name="content">
${ui.bar(crumbs)}
+%if folders or files:
<ul class="unstyled">
% for name in folders:
<li><a href="${name}"><i class="icon-folder-open"></i> ${name}</a>
@@ -11,6 +12,7 @@ ${ui.bar(crumbs)}
<li><a href="${name}.html"><i class="icon-file"></i> ${name}</a>
% endfor
</ul>
+%endif
% if code:
${code}
% endif
diff --git a/nikola/data/themes/base/templates/post_helper.tmpl b/nikola/data/themes/base/templates/post_helper.tmpl
index 69784ea..391350d 100755..100644
--- a/nikola/data/themes/base/templates/post_helper.tmpl
+++ b/nikola/data/themes/base/templates/post_helper.tmpl
@@ -85,6 +85,11 @@
<%def name="mathjax_script(post)">
%if post.is_mathjax:
+ <script type="text/x-mathjax-config">
+ MathJax.Hub.Config({
+ tex2jax: {inlineMath: [['$latex ','$'], ['\\(','\\)']]}
+ });
+ </script>
<script src="/assets/js/mathjax.js"></script>
%endif
</%def>
diff --git a/nikola/data/themes/base/templates/post_list_directive.tmpl b/nikola/data/themes/base/templates/post_list_directive.tmpl
index 3345ae4..b31d242 100644
--- a/nikola/data/themes/base/templates/post_list_directive.tmpl
+++ b/nikola/data/themes/base/templates/post_list_directive.tmpl
@@ -1,6 +1,7 @@
## -*- coding: utf-8 -*-
<!-- Begin post-list ${post_list_id} -->
<div id="${post_list_id}" class="post-list">
+ %if posts:
<ul class="post-list">
% for post in posts:
<li class="post-list-item">
@@ -10,5 +11,6 @@
</li>
% endfor
</ul>
+ %endif
</div>
<!-- End post-list ${post_list_id} -->
diff --git a/nikola/data/themes/base/templates/tag.tmpl b/nikola/data/themes/base/templates/tag.tmpl
index 2ca9db4..43afd54 100644
--- a/nikola/data/themes/base/templates/tag.tmpl
+++ b/nikola/data/themes/base/templates/tag.tmpl
@@ -22,11 +22,13 @@
<a href="${_link(kind + "_rss", tag)}">RSS</a>
%endif
<br>
+ %if posts:
<ul class="unstyled">
% for post in posts:
<li><a href="${post.permalink()}">[${post.formatted_date(date_format)}] ${post.title()}</a>
% endfor
</ul>
+ %endif
</div>
<!--End of body content-->
</%block>
diff --git a/nikola/data/themes/bootstrap/templates/base.tmpl b/nikola/data/themes/bootstrap/templates/base.tmpl
index 8cb2e43..65132b7 100644
--- a/nikola/data/themes/bootstrap/templates/base.tmpl
+++ b/nikola/data/themes/bootstrap/templates/base.tmpl
@@ -91,3 +91,4 @@ ${base.html_social()}
% endif
${body_end}
</body>
+</html>
diff --git a/nikola/data/themes/bootstrap/templates/bootstrap_helper.tmpl b/nikola/data/themes/bootstrap/templates/bootstrap_helper.tmpl
index f0d1986..c041e50 100644
--- a/nikola/data/themes/bootstrap/templates/bootstrap_helper.tmpl
+++ b/nikola/data/themes/bootstrap/templates/bootstrap_helper.tmpl
@@ -29,7 +29,9 @@
<link href="/assets/css/custom.css" rel="stylesheet" type="text/css">
%endif
%endif
- <link rel="canonical" href="${abs_link(permalink)}">
+ %if permalink:
+ <link rel="canonical" href="${abs_link(permalink)}">
+ %endif
<!--[if lt IE 9]>
<script src="http://html5shim.googlecode.com/svn/trunk/html5.js" type="text/javascript"></script>
<![endif]-->
diff --git a/nikola/data/themes/bootstrap/templates/gallery.tmpl b/nikola/data/themes/bootstrap/templates/gallery.tmpl
index 0dd5eea..7b0d505 100644
--- a/nikola/data/themes/bootstrap/templates/gallery.tmpl
+++ b/nikola/data/themes/bootstrap/templates/gallery.tmpl
@@ -14,14 +14,17 @@
${text}
</p>
%endif
+ %if folders:
<ul>
% for folder, ftitle in folders:
<li><a href="${folder}"><i
class="icon-folder-open"></i>&nbsp;${ftitle}</a></li>
% endfor
</ul>
+ %endif
<div id="gallery_container"></div>
+ %if photo_array:
<noscript>
<ul class="thumbnails">
%for image in photo_array:
@@ -30,6 +33,7 @@
%endfor
</ul>
</noscript>
+ %endif
%if enable_comments:
${comments.comment_form(None, permalink, title)}
%endif
diff --git a/nikola/nikola.py b/nikola/nikola.py
index 6971c0c..1d59954 100644
--- a/nikola/nikola.py
+++ b/nikola/nikola.py
@@ -25,8 +25,10 @@
# SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
from __future__ import print_function, unicode_literals
+import codecs
from collections import defaultdict
from copy import copy
+import datetime
import glob
import locale
import os
@@ -41,6 +43,7 @@ try:
import pyphen
except ImportError:
pyphen = None
+import pytz
import logging
from . import DEBUG
@@ -66,11 +69,15 @@ from .plugin_categories import (
SignalHandler,
)
+from .utils import ColorfulStderrHandler
config_changed = utils.config_changed
__all__ = ['Nikola']
+# Default pattern for translation files' names
+DEFAULT_TRANSLATIONS_PATTERN = '{path}.{ext}.{lang}'
+
class Nikola(object):
@@ -92,6 +99,7 @@ class Nikola(object):
self.path_handlers = {
'slug': self.slug_path,
'post_path': self.post_path,
+ 'filename': self.filename_path,
}
self.strict = False
@@ -111,8 +119,15 @@ class Nikola(object):
self.loghandlers = []
if not config:
self.configured = False
+ self.colorful = False
else:
self.configured = True
+ self.colorful = config.pop('__colorful__', False)
+
+ ColorfulStderrHandler._colorful = self.colorful
+
+ # Maintain API
+ utils.generic_rss_renderer = self.generic_rss_renderer
# This is the default config
self.config = {
@@ -173,6 +188,7 @@ class Nikola(object):
'INDEX_PATH': '',
'IPYNB_CONFIG': {},
'LESS_COMPILER': 'lessc',
+ 'LESS_OPTIONS': [],
'LICENSE': '',
'LINK_CHECK_WHITELIST': [],
'LISTINGS_FOLDER': 'listings',
@@ -192,6 +208,7 @@ class Nikola(object):
'RSS_PATH': '',
'RSS_TEASERS': True,
'SASS_COMPILER': 'sass',
+ 'SASS_OPTIONS': [],
'SEARCH_FORM': '',
'SLUG_TAG_PATH': True,
'SOCIAL_BUTTONS_CODE': SOCIAL_BUTTONS_CODE,
@@ -201,6 +218,7 @@ class Nikola(object):
'SITEMAP_INCLUDE_FILELESS_DIRS': True,
'TAG_PATH': 'categories',
'TAG_PAGES_ARE_INDEXES': False,
+ 'TEMPLATE_FILTERS': {},
'THEME': 'bootstrap',
'THEME_REVEAL_CONFIG_SUBTHEME': 'sky',
'THEME_REVEAL_CONFIG_TRANSITION': 'cube',
@@ -217,7 +235,7 @@ class Nikola(object):
'SCHEDULE_FORCE_TODAY': False,
'LOGGING_HANDLERS': {'stderr': {'loglevel': 'WARNING', 'bubble': True}},
'DEMOTE_HEADERS': 1,
- 'TRANSLATIONS_PATTERN': '{path}.{ext}.{lang}',
+ 'TRANSLATIONS_PATTERN': DEFAULT_TRANSLATIONS_PATTERN,
}
self.config.update(config)
@@ -302,10 +320,10 @@ class Nikola(object):
self.config['STRIP_INDEXES'] = config['STRIP_INDEX_HTML']
# PRETTY_URLS defaults to enabling STRIP_INDEXES unless explicitly disabled
- if config.get('PRETTY_URLS', False) and 'STRIP_INDEXES' not in config:
+ if self.config.get('PRETTY_URLS') and 'STRIP_INDEXES' not in config:
self.config['STRIP_INDEXES'] = True
- if config.get('COPY_SOURCES') and not self.config['HIDE_SOURCELINK']:
+ if not self.config.get('COPY_SOURCES'):
self.config['HIDE_SOURCELINK'] = True
self.config['TRANSLATIONS'] = self.config.get('TRANSLATIONS',
@@ -408,6 +426,13 @@ class Nikola(object):
self.plugin_manager.activatePluginByName(plugin_info.name)
plugin_info.plugin_object.set_site(self)
+ # Also add aliases for combinations with TRANSLATIONS_PATTERN
+ self.config['COMPILERS'] = dict([(lang, list(exts) + [
+ utils.get_translation_candidate(self.config, "f" + ext, lang)[1:]
+ for ext in exts
+ for lang in self.config['TRANSLATIONS'].keys()])
+ for lang, exts in list(self.config['COMPILERS'].items())])
+
# Activate all required compiler plugins
for plugin_info in self.plugin_manager.getPluginsOfCategory("PageCompiler"):
if plugin_info.name in self.config["COMPILERS"].keys():
@@ -462,6 +487,11 @@ class Nikola(object):
self._GLOBAL_CONTEXT['navigation_links'] = utils.Functionary(list, self.config['DEFAULT_LANG'])
for k, v in self.config.get('NAVIGATION_LINKS', {}).items():
self._GLOBAL_CONTEXT['navigation_links'][k] = v
+
+ # avoid #1082 by making sure all keys in navigation_links are read once
+ for k in self._GLOBAL_CONTEXT['translations']:
+ self._GLOBAL_CONTEXT['navigation_links'][k]
+
# TODO: remove on v7
# Compatibility alias
self._GLOBAL_CONTEXT['sidebar_links'] = self._GLOBAL_CONTEXT['navigation_links']
@@ -498,7 +528,7 @@ class Nikola(object):
self.config['THEME'] = theme_replacements[self.config['THEME']]
if self.config['THEME'] == 'oldfashioned':
utils.LOGGER.warn('''You may need to install the "oldfashioned" theme '''
- '''from themes.nikola.ralsina.com.ar because it's not '''
+ '''from themes.getnikola.com because it's not '''
'''shipped by default anymore.''')
utils.LOGGER.warn('Please change your THEME setting.')
try:
@@ -560,6 +590,7 @@ class Nikola(object):
for name in self.THEMES]
self._template_system.set_directories(lookup_dirs,
self.config['CACHE_FOLDER'])
+ self._template_system.set_site(self)
return self._template_system
template_system = property(_get_template_system)
@@ -576,9 +607,9 @@ class Nikola(object):
compile_html = self.inverse_compilers[ext]
except KeyError:
# Find the correct compiler for this files extension
- langs = [lang for lang, exts in
- list(self.config['COMPILERS'].items())
- if ext in exts]
+ lang_exts_tab = list(self.config['COMPILERS'].items())
+ langs = [lang for lang, exts in lang_exts_tab if ext in exts or
+ len([ext_ for ext_ in exts if source_name.endswith(ext_)]) > 0]
if len(langs) != 1:
if len(set(langs)) > 1:
exit("Your file extension->compiler definition is"
@@ -618,78 +649,158 @@ class Nikola(object):
# The os.sep is because normpath will change "/" to "\" on windows
src = "/".join(src.split(os.sep))
- parsed_src = urlsplit(src)
- src_elems = parsed_src.path.split('/')[1:]
+ utils.makedirs(os.path.dirname(output_name))
+ doc = lxml.html.document_fromstring(data)
+ doc.rewrite_links(lambda dst: self.url_replacer(src, dst, context['lang']))
+ data = b'<!DOCTYPE html>' + lxml.html.tostring(doc, encoding='utf8')
+ with open(output_name, "wb+") as post_file:
+ post_file.write(data)
- def replacer(dst):
- # Refuse to replace links that are full URLs.
- dst_url = urlparse(dst)
- if dst_url.netloc:
- if dst_url.scheme == 'link': # Magic link
- dst = self.link(dst_url.netloc, dst_url.path.lstrip('/'),
- context['lang'])
- else:
- return dst
+ def url_replacer(self, src, dst, lang=None):
+ """URL mangler.
- # 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
+ * Replaces link:// URLs with real links
+ * Makes dst relative to src
+ * Leaves fragments unchanged
+ * Leaves full URLs unchanged
+ * Avoids empty links
- # Normalize
- dst = urljoin(src, dst)
+ src is the URL where this link is used
+ dst is the link to be mangled
+ lang is used for language-sensitive URLs in link://
- # Avoid empty links.
- if src == dst:
- 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 "#"
+ """
+ parsed_src = urlsplit(src)
+ src_elems = parsed_src.path.split('/')[1:]
+ dst_url = urlparse(dst)
+ if lang is None:
+ lang = self.default_lang
- # 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)
+ # Refuse to replace links that are full URLs.
+ if dst_url.netloc:
+ if dst_url.scheme == 'link': # Magic link
+ dst = self.link(dst_url.netloc, dst_url.path.lstrip('/'), lang)
+ else:
return dst
+ elif dst_url.scheme == 'link': # Magic absolute path link:
+ dst = dst_url.path
+ 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
+ # 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
- # Now both paths are on the same site and absolute
- dst_elems = parsed_dst.path.split('/')[1:]
+ # Normalize
+ dst = urljoin(src, dst)
- i = 0
- for (i, s), d in zip(enumerate(src_elems), dst_elems):
- if s != d:
- break
- # Now i is the longest common prefix
- result = '/'.join(['..'] * (len(src_elems) - i - 1) +
- dst_elems[i:])
+ # Avoid empty links.
+ if src == dst:
+ if self.config.get('URL_TYPE') == 'absolute':
+ dst = urljoin(self.config['BASE_URL'], dst.lstrip('/'))
+ return dst
+ elif self.config.get('URL_TYPE') == 'full_path':
+ dst = urljoin(self.config['BASE_URL'], dst.lstrip('/'))
+ return urlparse(dst).path
+ else:
+ return "#"
- if not result:
- result = "."
+ # 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
- # Don't forget the fragment (anchor) part of the link
- if parsed_dst.fragment:
- result += "#" + parsed_dst.fragment
+ if self.config.get('URL_TYPE') in ('full_path', 'absolute'):
+ dst = urljoin(self.config['BASE_URL'], dst.lstrip('/'))
+ if self.config.get('URL_TYPE') == 'full_path':
+ parsed = urlparse(urljoin(self.config['BASE_URL'], dst.lstrip('/')))
+ if parsed.fragment:
+ dst = '{0}#{1}'.format(parsed.path, parsed.fragment)
+ else:
+ dst = parsed.path
+ return dst
- assert result, (src, dst, i, src_elems, dst_elems)
+ # Now both paths are on the same site and absolute
+ dst_elems = parsed_dst.path.split('/')[1:]
- return result
+ i = 0
+ for (i, s), d in zip(enumerate(src_elems), dst_elems):
+ if s != d:
+ break
+ # Now i is the longest common prefix
+ result = '/'.join(['..'] * (len(src_elems) - i - 1) + dst_elems[i:])
+
+ if not result:
+ result = "."
+
+ # Don't forget the fragment (anchor) part of the link
+ if parsed_dst.fragment:
+ result += "#" + parsed_dst.fragment
+
+ assert result, (src, dst, i, src_elems, dst_elems)
+
+ return result
+
+ def generic_rss_renderer(self, lang, title, link, description, timeline, output_path,
+ rss_teasers, feed_length=10, feed_url=None):
+ """Takes all necessary data, and renders a RSS feed in output_path."""
+ items = []
+ for post in timeline[:feed_length]:
+ # Massage the post's HTML
+ data = post.text(lang, teaser_only=rss_teasers, really_absolute=True)
+ if feed_url is not None and data:
+ # FIXME: this is duplicated with code in Post.text()
+ try:
+ doc = lxml.html.document_fromstring(data)
+ doc.rewrite_links(lambda dst: self.url_replacer(feed_url, dst, lang))
+ try:
+ body = doc.body
+ data = (body.text or '') + ''.join(
+ [lxml.html.tostring(child, encoding='unicode')
+ for child in body.iterchildren()])
+ except IndexError: # No body there, it happens sometimes
+ data = ''
+ except lxml.etree.ParserError as e:
+ if str(e) == "Document is empty":
+ data = ""
+ else: # let other errors raise
+ raise(e)
+
+ args = {
+ 'title': post.title(lang),
+ 'link': post.permalink(lang, absolute=True),
+ 'description': data,
+ 'guid': post.permalink(lang, absolute=True),
+ # PyRSS2Gen's pubDate is GMT time.
+ 'pubDate': (post.date if post.date.tzinfo is None else
+ post.date.astimezone(pytz.timezone('UTC'))),
+ 'categories': post._tags.get(lang, []),
+ 'author': post.meta('author'),
+ }
- utils.makedirs(os.path.dirname(output_name))
- doc = lxml.html.document_fromstring(data)
- doc.rewrite_links(replacer)
- data = b'<!DOCTYPE html>' + lxml.html.tostring(doc, encoding='utf8')
- with open(output_name, "wb+") as post_file:
- post_file.write(data)
+ items.append(utils.ExtendedItem(**args))
+ rss_obj = utils.ExtendedRSS2(
+ title=title,
+ link=link,
+ description=description,
+ lastBuildDate=datetime.datetime.now(),
+ items=items,
+ generator='Nikola <http://getnikola.com/>',
+ language=lang
+ )
+ rss_obj.self_url = feed_url
+ rss_obj.rss_attrs["xmlns:atom"] = "http://www.w3.org/2005/Atom"
+ rss_obj.rss_attrs["xmlns:dc"] = "http://purl.org/dc/elements/1.1/"
+ dst_dir = os.path.dirname(output_path)
+ utils.makedirs(dst_dir)
+ with codecs.open(output_path, "wb+", "utf-8") as rss_file:
+ data = rss_obj.to_xml(encoding='utf-8')
+ if isinstance(data, utils.bytes_str):
+ data = data.decode('utf-8')
+ rss_file.write(data)
def path(self, kind, name, lang=None, is_link=False):
"""Build the path to a certain kind of page.
@@ -712,6 +823,7 @@ class Nikola(object):
* listing (name is the source code file name)
* post_path (name is 1st element in a POSTS/PAGES tuple)
* slug (name is the slug of a post or story)
+ * filename (name is the source filename of a post/story, in DEFAULT_LANG, relative to conf.py)
The returned value is always a path relative to output, like
"categories/whatever.html"
@@ -727,6 +839,7 @@ class Nikola(object):
lang = utils.LocaleBorg().current_lang
path = self.path_handlers[kind](name, lang)
+ path = [os.path.normpath(p) for p in path if p != '.'] # Fix Issue #1028
if is_link:
link = '/' + ('/'.join(path))
@@ -755,6 +868,16 @@ class Nikola(object):
utils.LOGGER.warning('Ambiguous path request for slug: {0}'.format(name))
return [_f for _f in results[0].permalink(lang).split('/') if _f]
+ def filename_path(self, name, lang):
+ """filename path handler"""
+ results = [p for p in self.timeline if p.source_path == name]
+ if not results:
+ utils.LOGGER.warning("Can't resolve path request for filename: {0}".format(name))
+ else:
+ if len(results) > 1:
+ utils.LOGGER.error("Ambiguous path request for filename: {0}".format(name))
+ return [_f for _f in results[0].permalink(lang).split('/') if _f]
+
def register_path_handler(self, kind, f):
if kind in self.path_handlers:
utils.LOGGER.warning('Conflicting path handlers for kind: {0}'.format(kind))
@@ -766,8 +889,10 @@ class Nikola(object):
def abs_link(self, dst):
# Normalize
- dst = urljoin(self.config['BASE_URL'], dst)
-
+ if dst: # Mako templates and empty strings evaluate to False
+ dst = urljoin(self.config['BASE_URL'], dst.lstrip('/'))
+ else:
+ dst = self.config['BASE_URL']
return urlparse(dst).geturl()
def rel_link(self, src, dst):
@@ -848,7 +973,8 @@ class Nikola(object):
return
seen = set([])
print("Scanning posts", end='', file=sys.stderr)
- lower_case_tags = set([])
+ slugged_tags = set([])
+ quit = False
for wildcard, destination, template_name, use_in_feeds in \
self.config['post_pages']:
print(".", end='', file=sys.stderr)
@@ -864,11 +990,16 @@ class Nikola(object):
translated_list = glob.glob(lang_glob)
# 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:
+
+ # Eliminate translations from full_list if they are not the primary,
+ # or a secondary with no primary
+ limited_list = full_list[:]
+ for fname in full_list:
+ for lang in self.config['TRANSLATIONS'].keys():
translation = utils.get_translation_candidate(self.config, fname, lang)
if translation in full_list:
- full_list.remove(translation)
+ limited_list.remove(translation)
+ full_list = limited_list
# We eliminate from the list the files inside any .ipynb folder
full_list = [p for p in full_list
@@ -897,16 +1028,16 @@ class Nikola(object):
self.posts_per_month[
'{0}/{1:02d}'.format(post.date.year, post.date.month)].append(post.source_path)
for tag in post.alltags:
- if tag.lower() in lower_case_tags:
+ if utils.slugify(tag) in slugged_tags:
if tag not in self.posts_per_tag:
# Tags that differ only in case
other_tag = [k for k in self.posts_per_tag.keys() if k.lower() == tag.lower()][0]
- utils.LOGGER.error('You have cases that differ only in upper/lower case: {0} and {1}'.format(tag, other_tag))
+ utils.LOGGER.error('You have tags that are too similar: {0} and {1}'.format(tag, other_tag))
utils.LOGGER.error('Tag {0} is used in: {1}'.format(tag, post.source_path))
utils.LOGGER.error('Tag {0} is used in: {1}'.format(other_tag, ', '.join(self.posts_per_tag[other_tag])))
- sys.exit(1)
+ quit = True
else:
- lower_case_tags.add(tag.lower())
+ slugged_tags.add(utils.slugify(tag))
self.posts_per_tag[tag].append(post.source_path)
self.posts_per_category[post.meta('category')].append(post.source_path)
else:
@@ -925,6 +1056,8 @@ class Nikola(object):
p.prev_post = post_timeline[i + 1]
self._scanned = True
print("done!", file=sys.stderr)
+ if quit:
+ sys.exit(1)
def generic_page_renderer(self, lang, post, filters):
"""Render post fragments to final HTML pages."""
diff --git a/nikola/plugin_categories.py b/nikola/plugin_categories.py
index 68327aa..516df92 100644
--- a/nikola/plugin_categories.py
+++ b/nikola/plugin_categories.py
@@ -151,6 +151,10 @@ class TemplateSystem(BasePlugin):
"""Sets the list of folders where templates are located and cache."""
raise NotImplementedError()
+ def set_site(self, site):
+ """Sets the site."""
+ self.site = site
+
def template_deps(self, template_name):
"""Returns filenames which are dependencies for a template."""
raise NotImplementedError()
diff --git a/nikola/plugins/basic_import.py b/nikola/plugins/basic_import.py
index 0d94d16..27c0eb4 100644
--- a/nikola/plugins/basic_import.py
+++ b/nikola/plugins/basic_import.py
@@ -48,8 +48,8 @@ class ImportMixin(object):
name = "import_mixin"
needs_config = False
- doc_usage = "[options] wordpress_export_file"
- doc_purpose = "import a wordpress dump."
+ doc_usage = "[options] export_file"
+ doc_purpose = "import a dump from a different engine."
cmd_options = [
{
'name': 'output_folder',
@@ -93,7 +93,7 @@ class ImportMixin(object):
else:
self.import_into_existing_site = True
utils.LOGGER.notice('The folder {0} already exists - assuming that this is a '
- 'already existing nikola site.'.format(self.output_folder))
+ 'already existing Nikola site.'.format(self.output_folder))
filename = os.path.join(os.path.dirname(utils.__file__), 'conf.py.in')
# The 'strict_undefined=True' will give the missing symbol name if any,
@@ -151,7 +151,7 @@ class ImportMixin(object):
time=datetime.datetime.now().strftime('%Y%m%d_%H%M%S'),
name=self.name)
config_output_path = os.path.join(self.output_folder, filename)
- utils.LOGGER.notice('Configuration will be written to: {0}'.format(config_output_path))
+ utils.LOGGER.info('Configuration will be written to: {0}'.format(config_output_path))
return config_output_path
diff --git a/nikola/plugins/command/auto.py b/nikola/plugins/command/auto.py
index 01116d1..d707d53 100644
--- a/nikola/plugins/command/auto.py
+++ b/nikola/plugins/command/auto.py
@@ -34,7 +34,7 @@ from nikola.plugin_categories import Command
from nikola.utils import req_missing
-class Auto(Command):
+class CommandAuto(Command):
"""Start debugging console."""
name = "auto"
doc_purpose = "automatically detect site changes, rebuild and optionally refresh a browser"
@@ -61,26 +61,26 @@ class Auto(Command):
try:
from livereload import Server
except ImportError:
- req_missing(['livereload>=2.0.0'], 'use the "auto" command')
+ req_missing(['livereload==2.1.0'], 'use the "auto" command')
return
- # Run an initial build so we are uptodate
+ # Run an initial build so we are up-to-date
subprocess.call(("nikola", "build"))
port = options and options.get('port')
server = Server()
- server.watch('conf.py')
- server.watch('themes/')
- server.watch('templates/')
+ server.watch('conf.py', 'nikola build')
+ server.watch('themes/', 'nikola build')
+ server.watch('templates/', 'nikola build')
server.watch(self.site.config['GALLERY_PATH'])
for item in self.site.config['post_pages']:
- server.watch(os.path.dirname(item[0]))
+ server.watch(os.path.dirname(item[0]), 'nikola build')
for item in self.site.config['FILES_FOLDERS']:
- server.watch(os.path.dirname(item))
+ server.watch(os.path.dirname(item), 'nikola build')
out_folder = self.site.config['OUTPUT_FOLDER']
if options and options.get('browser'):
webbrowser.open('http://localhost:{0}'.format(port))
- server.serve(port, out_folder)
+ server.serve(port, None, out_folder)
diff --git a/nikola/plugins/command/bootswatch_theme.py b/nikola/plugins/command/bootswatch_theme.py
index 94f37f2..82c47d2 100644
--- a/nikola/plugins/command/bootswatch_theme.py
+++ b/nikola/plugins/command/bootswatch_theme.py
@@ -86,12 +86,14 @@ class CommandBootswatchTheme(Command):
version = '2'
elif 'bootstrap' not in themes:
LOGGER.warn('"bootswatch_theme" only makes sense for themes that use bootstrap')
+ elif 'bootstrap3-gradients' in themes or 'bootstrap3-gradients-jinja' in themes:
+ LOGGER.warn('"bootswatch_theme" doesn\'t work well with the bootstrap3-gradients family')
- LOGGER.notice("Creating '{0}' theme from '{1}' and '{2}'".format(name, swatch, parent))
+ LOGGER.info("Creating '{0}' theme from '{1}' and '{2}'".format(name, swatch, parent))
utils.makedirs(os.path.join('themes', name, 'assets', 'css'))
for fname in ('bootstrap.min.css', 'bootstrap.css'):
url = '/'.join(('http://bootswatch.com', version, swatch, fname))
- LOGGER.notice("Downloading: " + url)
+ LOGGER.info("Downloading: " + url)
data = requests.get(url).text
with open(os.path.join('themes', name, 'assets', 'css', fname),
'wb+') as output:
@@ -99,5 +101,4 @@ class CommandBootswatchTheme(Command):
with open(os.path.join('themes', name, 'parent'), 'wb+') as output:
output.write(parent.encode('utf-8'))
- LOGGER.notice('Theme created. Change the THEME setting to "{0}" to use '
- 'it.'.format(name))
+ LOGGER.notice('Theme created. Change the THEME setting to "{0}" to use it.'.format(name))
diff --git a/nikola/plugins/command/check.py b/nikola/plugins/command/check.py
index a7e8c13..26db321 100644
--- a/nikola/plugins/command/check.py
+++ b/nikola/plugins/command/check.py
@@ -102,6 +102,14 @@ class CommandCheck(Command):
'default': False,
'help': 'List possible source files for files with broken links.',
},
+ {
+ 'name': 'verbose',
+ 'long': 'verbose',
+ 'short': 'v',
+ 'type': bool,
+ 'default': False,
+ 'help': 'Be more verbose.',
+ },
]
def _execute(self, options, args):
@@ -112,6 +120,10 @@ class CommandCheck(Command):
if not options['links'] and not options['files'] and not options['clean']:
print(self.help())
return False
+ if options['verbose']:
+ self.logger.level = 1
+ else:
+ self.logger.level = 4
if options['links']:
failure = self.scan_links(options['find_sources'])
if options['files']:
@@ -126,6 +138,8 @@ class CommandCheck(Command):
def analyze(self, task, find_sources=False):
rv = False
self.whitelist = [re.compile(x) for x in self.site.config['LINK_CHECK_WHITELIST']]
+ base_url = urlparse(self.site.config['BASE_URL'])
+ url_type = self.site.config['URL_TYPE']
try:
filename = task.split(":")[-1]
d = lxml.html.fromstring(open(filename).read())
@@ -134,31 +148,51 @@ class CommandCheck(Command):
if target == "#":
continue
parsed = urlparse(target)
- if parsed.scheme or target.startswith('//'):
+
+ # Absolute links when using only paths, skip.
+ if (parsed.scheme or target.startswith('//')) and url_type in ('rel_path', 'full_path'):
continue
+
+ # Absolute links to other domains, skip
+ if (parsed.scheme or target.startswith('//')) and parsed.netloc != base_url.netloc:
+ continue
+
if parsed.fragment:
target = target.split('#')[0]
- target_filename = os.path.abspath(
- os.path.join(os.path.dirname(filename), unquote(target)))
+ if url_type == 'rel_path':
+ target_filename = os.path.abspath(
+ os.path.join(os.path.dirname(filename), unquote(target)))
+
+ elif url_type in ('full_path', 'absolute'):
+ target_filename = os.path.abspath(
+ os.path.join(os.path.dirname(filename), parsed.path))
+ if parsed.path.endswith('/'): # abspath removes trailing slashes
+ target_filename += '/{0}'.format(self.site.config['INDEX_FILE'])
+ if target_filename.startswith(base_url.path):
+ target_filename = target_filename[len(base_url.path):]
+ target_filename = os.path.join(self.site.config['OUTPUT_FOLDER'], target_filename)
+
if any(re.match(x, target_filename) for x in self.whitelist):
continue
elif target_filename not in self.existing_targets:
if os.path.exists(target_filename):
+ self.logger.notice("Good link {0} => {1}".format(target, target_filename))
self.existing_targets.add(target_filename)
else:
rv = True
- self.logger.warn("Broken link in {0}: ".format(filename), target)
+ self.logger.warn("Broken link in {0}: {1}".format(filename, target))
if find_sources:
self.logger.warn("Possible sources:")
self.logger.warn(os.popen('nikola list --deps ' + task, 'r').read())
self.logger.warn("===============================\n")
except Exception as exc:
- self.logger.error("Error with:", filename, exc)
+ self.logger.error("Error with: {0} {1}".format(filename, exc))
return rv
def scan_links(self, find_sources=False):
- self.logger.notice("Checking Links:")
- self.logger.notice("===============")
+ self.logger.info("Checking Links:")
+ self.logger.info("===============\n")
+ self.logger.notice("{0} mode".format(self.site.config['URL_TYPE']))
failure = False
for task in os.popen('nikola list --all', 'r').readlines():
task = task.strip()
@@ -170,13 +204,13 @@ class CommandCheck(Command):
if self.analyze(task, find_sources):
failure = True
if not failure:
- self.logger.notice("All links checked.")
+ self.logger.info("All links checked.")
return failure
def scan_files(self):
failure = False
- self.logger.notice("Checking Files:")
- self.logger.notice("===============\n")
+ self.logger.info("Checking Files:")
+ self.logger.info("===============\n")
only_on_output, only_on_input = real_scan_files(self.site)
# Ignore folders
@@ -195,7 +229,7 @@ class CommandCheck(Command):
for f in only_on_input:
self.logger.warn(f)
if not failure:
- self.logger.notice("All files checked.")
+ self.logger.info("All files checked.")
return failure
def clean_files(self):
diff --git a/nikola/plugins/command/console.py b/nikola/plugins/command/console.py
index e66b650..b0a8958 100644
--- a/nikola/plugins/command/console.py
+++ b/nikola/plugins/command/console.py
@@ -35,7 +35,7 @@ from nikola.utils import get_logger, STDERR_HANDLER
LOGGER = get_logger('console', STDERR_HANDLER)
-class Console(Command):
+class CommandConsole(Command):
"""Start debugging console."""
name = "console"
shells = ['ipython', 'bpython', 'plain']
diff --git a/nikola/plugins/command/deploy.py b/nikola/plugins/command/deploy.py
index eb5787e..bd1c15f 100644
--- a/nikola/plugins/command/deploy.py
+++ b/nikola/plugins/command/deploy.py
@@ -25,7 +25,6 @@
# SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
from __future__ import print_function
-from ast import literal_eval
import codecs
from datetime import datetime
import os
@@ -40,8 +39,8 @@ from nikola.plugin_categories import Command
from nikola.utils import remove_file, get_logger
-class Deploy(Command):
- """Deploy site. """
+class CommandDeploy(Command):
+ """Deploy site."""
name = "deploy"
doc_usage = ""
@@ -76,7 +75,7 @@ class Deploy(Command):
undeployed_posts.append(post)
for command in self.site.config['DEPLOY_COMMANDS']:
- self.logger.notice("==> {0}".format(command))
+ self.logger.info("==> {0}".format(command))
try:
subprocess.check_call(command, shell=True)
except subprocess.CalledProcessError as e:
@@ -84,26 +83,22 @@ class Deploy(Command):
'returned {1}'.format(e.cmd, e.returncode))
sys.exit(e.returncode)
- self.logger.notice("Successful deployment")
- tzinfo = pytz.timezone(self.site.config['TIMEZONE'])
+ self.logger.info("Successful deployment")
try:
- with open(timestamp_path, 'rb') as inf:
- last_deploy = literal_eval(inf.read().strip())
- if tzinfo:
- last_deploy = last_deploy.replace(tzinfo=tzinfo)
+ with codecs.open(timestamp_path, 'rb', 'utf8') as inf:
+ last_deploy = datetime.strptime(inf.read().strip(), "%Y-%m-%dT%H:%M:%S.%f")
clean = False
- except Exception:
+ except (IOError, Exception) as e:
+ self.logger.debug("Problem when reading `{0}`: {1}".format(timestamp_path, e))
last_deploy = datetime(1970, 1, 1)
- if tzinfo:
- last_deploy = last_deploy.replace(tzinfo=tzinfo)
clean = True
- new_deploy = datetime.now()
+ new_deploy = datetime.utcnow()
self._emit_deploy_event(last_deploy, new_deploy, clean, undeployed_posts)
# Store timestamp of successful deployment
with codecs.open(timestamp_path, 'wb+', 'utf8') as outf:
- outf.write(repr(new_deploy))
+ outf.write(new_deploy.isoformat())
def _emit_deploy_event(self, last_deploy, new_deploy, clean=False, undeployed=None):
""" Emit events for all timeline entries newer than last deploy.
@@ -129,9 +124,11 @@ class Deploy(Command):
'undeployed': undeployed
}
+ tzinfo = pytz.timezone(self.site.config['TIMEZONE'])
+
deployed = [
entry for entry in self.site.timeline
- if entry.date > last_deploy and entry not in undeployed
+ if entry.date > (last_deploy.replace(tzinfo=tzinfo) if tzinfo else last_deploy) and entry not in undeployed
]
event['deployed'] = deployed
diff --git a/nikola/plugins/command/import_blogger.py b/nikola/plugins/command/import_blogger.py
index ea12b4a..dd629c4 100644
--- a/nikola/plugins/command/import_blogger.py
+++ b/nikola/plugins/command/import_blogger.py
@@ -43,6 +43,7 @@ from nikola.plugin_categories import Command
from nikola import utils
from nikola.utils import req_missing
from nikola.plugins.basic_import import ImportMixin
+from nikola.plugins.command.init import SAMPLE_CONF, prepare_config
LOGGER = utils.get_logger('import_blogger', utils.STDERR_HANDLER)
@@ -95,8 +96,8 @@ class CommandImportBlogger(Command, ImportMixin):
conf_out_path = self.get_configuration_output_path()
# if it tracebacks here, look a comment in
# basic_import.Import_Mixin.generate_base_site
- conf_termplate_render = conf_template.render(**self.context)
- self.write_configuration(conf_out_path, conf_termplate_render)
+ conf_template_render = conf_template.render(**prepare_config(self.context))
+ self.write_configuration(conf_out_path, conf_template_render)
@classmethod
def get_channel_from_file(cls, filename):
@@ -106,8 +107,7 @@ class CommandImportBlogger(Command, ImportMixin):
@staticmethod
def populate_context(channel):
- # may need changes when the template conf.py.in changes
- context = {}
+ context = SAMPLE_CONF.copy()
context['DEFAULT_LANG'] = 'en' # blogger doesn't include the language
# in the dump
context['BLOG_TITLE'] = channel.feed.title
@@ -131,7 +131,6 @@ class CommandImportBlogger(Command, ImportMixin):
"html": ('.html', '.htm')
}
'''
- context['THEME'] = 'bootstrap3'
return context
diff --git a/nikola/plugins/command/import_feed.py b/nikola/plugins/command/import_feed.py
index 70a5cd5..ee59277 100644
--- a/nikola/plugins/command/import_feed.py
+++ b/nikola/plugins/command/import_feed.py
@@ -43,6 +43,7 @@ from nikola.plugin_categories import Command
from nikola import utils
from nikola.utils import req_missing
from nikola.plugins.basic_import import ImportMixin
+from nikola.plugins.command.init import SAMPLE_CONF, prepare_config
LOGGER = utils.get_logger('import_feed', utils.STDERR_HANDLER)
@@ -82,7 +83,7 @@ class CommandImportFeed(Command, ImportMixin):
self.import_posts(channel)
self.write_configuration(self.get_configuration_output_path(
- ), conf_template.render(**self.context))
+ ), conf_template.render(**prepare_config(self.context)))
@classmethod
def get_channel_from_file(cls, filename):
@@ -90,7 +91,7 @@ class CommandImportFeed(Command, ImportMixin):
@staticmethod
def populate_context(channel):
- context = {}
+ context = SAMPLE_CONF.copy()
context['DEFAULT_LANG'] = channel.feed.title_detail.language \
if channel.feed.title_detail.language else 'en'
context['BLOG_TITLE'] = channel.feed.title
@@ -100,9 +101,11 @@ class CommandImportFeed(Command, ImportMixin):
context['BLOG_EMAIL'] = channel.feed.author_detail.get('email', '') if 'author_detail' in channel.feed else ''
context['BLOG_AUTHOR'] = channel.feed.author_detail.get('name', '') if 'author_detail' in channel.feed else ''
- context['POST_PAGES'] = '''(
- ("posts/*.html", "posts", "post.tmpl", True),
- ("stories/*.html", "stories", "story.tmpl", False),
+ context['POSTS'] = '''(
+ ("posts/*.html", "posts", "post.tmpl"),
+ )'''
+ context['PAGES'] = '''(
+ ("stories/*.html", "stories", "story.tmpl"),
)'''
context['COMPILERS'] = '''{
"rest": ('.txt', '.rst'),
diff --git a/nikola/plugins/command/import_wordpress.py b/nikola/plugins/command/import_wordpress.py
index 0c9915a..b567c77 100644
--- a/nikola/plugins/command/import_wordpress.py
+++ b/nikola/plugins/command/import_wordpress.py
@@ -50,6 +50,8 @@ from nikola.plugin_categories import Command
from nikola import utils
from nikola.utils import req_missing
from nikola.plugins.basic_import import ImportMixin, links
+from nikola.nikola import DEFAULT_TRANSLATIONS_PATTERN
+from nikola.plugins.command.init import SAMPLE_CONF, prepare_config
LOGGER = utils.get_logger('import_wordpress', utils.STDERR_HANDLER)
@@ -84,6 +86,23 @@ class CommandImportWordpress(Command, ImportMixin):
'type': bool,
'help': "Do not try to download files for the import",
},
+ {
+ 'name': 'separate_qtranslate_content',
+ 'long': 'qtranslate',
+ 'default': False,
+ 'type': bool,
+ 'help': "Look for translations generated by qtranslate plugin",
+ # WARNING: won't recover translated titles that actually
+ # don't seem to be part of the wordpress XML export at the
+ # time of writing :(
+ },
+ {
+ 'name': 'translations_pattern',
+ 'long': 'translations_pattern',
+ 'default': None,
+ 'type': str,
+ 'help': "The pattern for translation files names",
+ },
]
def _execute(self, options={}, args=[]):
@@ -114,6 +133,9 @@ class CommandImportWordpress(Command, ImportMixin):
self.exclude_drafts = options.get('exclude_drafts', False)
self.no_downloads = options.get('no_downloads', False)
+ self.separate_qtranslate_content = options.get('separate_qtranslate_content')
+ self.translations_pattern = options.get('translations_pattern')
+
if not self.no_downloads:
def show_info_about_mising_module(modulename):
LOGGER.error(
@@ -135,15 +157,21 @@ class CommandImportWordpress(Command, ImportMixin):
self.context = self.populate_context(channel)
conf_template = self.generate_base_site()
+ # If user has specified a custom pattern for translation files we
+ # need to fix the config
+ if self.translations_pattern:
+ self.context['TRANSLATIONS_PATTERN'] = self.translations_pattern
+
self.import_posts(channel)
self.context['REDIRECTIONS'] = self.configure_redirections(
self.url_map)
self.write_urlmap_csv(
os.path.join(self.output_folder, 'url_map.csv'), self.url_map)
- rendered_template = conf_template.render(**self.context)
+ rendered_template = conf_template.render(**prepare_config(self.context))
rendered_template = re.sub('# REDIRECTIONS = ', 'REDIRECTIONS = ',
rendered_template)
+
if self.timezone:
rendered_template = re.sub('# TIMEZONE = \'UTC\'',
'TIMEZONE = \'' + self.timezone + '\'',
@@ -194,8 +222,9 @@ class CommandImportWordpress(Command, ImportMixin):
def populate_context(channel):
wordpress_namespace = channel.nsmap['wp']
- context = {}
+ context = SAMPLE_CONF.copy()
context['DEFAULT_LANG'] = get_text_tag(channel, 'language', 'en')[:2]
+ context['TRANSLATIONS_PATTERN'] = DEFAULT_TRANSLATIONS_PATTERN
context['BLOG_TITLE'] = get_text_tag(channel, 'title',
'PUT TITLE HERE')
context['BLOG_DESCRIPTION'] = get_text_tag(
@@ -205,9 +234,10 @@ class CommandImportWordpress(Command, ImportMixin):
base_site_url = channel.find('{{{0}}}author'.format(wordpress_namespace))
context['BASE_URL'] = get_text_tag(base_site_url,
None,
- "http://foo.com")
+ "http://foo.com/")
+ if not context['BASE_URL'].endswith('/'):
+ context['BASE_URL'] += '/'
context['SITE_URL'] = context['BASE_URL']
- context['THEME'] = 'bootstrap3'
author = channel.find('{{{0}}}author'.format(wordpress_namespace))
context['BLOG_EMAIL'] = get_text_tag(
@@ -253,7 +283,7 @@ class CommandImportWordpress(Command, ImportMixin):
+ list(path.split('/'))))
dst_dir = os.path.dirname(dst_path)
utils.makedirs(dst_dir)
- LOGGER.notice("Downloading {0} => {1}".format(url, dst_path))
+ LOGGER.info("Downloading {0} => {1}".format(url, dst_path))
self.download_url_content_to_file(url, dst_path)
dst_url = '/'.join(dst_path.split(os.sep)[2:])
links[link] = '/' + dst_url
@@ -288,7 +318,7 @@ class CommandImportWordpress(Command, ImportMixin):
# your blogging into another site or system its not.
# Why don't they just use JSON?
if sys.version_info[0] == 2:
- metadata = phpserialize.loads(meta_value.text)
+ metadata = phpserialize.loads(utils.sys_encode(meta_value.text))
size_key = 'sizes'
file_key = 'file'
else:
@@ -307,7 +337,7 @@ class CommandImportWordpress(Command, ImportMixin):
+ list(path.split('/'))))
dst_dir = os.path.dirname(dst_path)
utils.makedirs(dst_dir)
- LOGGER.notice("Downloading {0} => {1}".format(url, dst_path))
+ LOGGER.info("Downloading {0} => {1}".format(url, dst_path))
self.download_url_content_to_file(url, dst_path)
dst_url = '/'.join(dst_path.split(os.sep)[2:])
links[url] = '/' + dst_url
@@ -350,14 +380,17 @@ class CommandImportWordpress(Command, ImportMixin):
# link is something like http://foo.com/2012/09/01/hello-world/
# So, take the path, utils.slugify it, and that's our slug
link = get_text_tag(item, 'link', None)
- path = unquote(urlparse(link).path)
+ path = unquote(urlparse(link).path.strip('/'))
# In python 2, path is a str. slug requires a unicode
# object. According to wikipedia, unquoted strings will
# usually be UTF8
if isinstance(path, utils.bytes_str):
path = path.decode('utf8')
- slug = utils.slugify(path)
+ pathlist = path.split('/')
+ if len(pathlist) > 1:
+ out_folder = os.path.join(*([out_folder] + pathlist[:-1]))
+ slug = utils.slugify(pathlist[-1])
if not slug: # it happens if the post has no "nice" URL
slug = get_text_tag(
item, '{{{0}}}post_name'.format(wordpress_namespace), None)
@@ -395,21 +428,43 @@ class CommandImportWordpress(Command, ImportMixin):
continue
tags.append(text)
+ if '$latex' in content:
+ tags.append('mathjax')
+
if is_draft and self.exclude_drafts:
LOGGER.notice('Draft "{0}" will not be imported.'.format(title))
elif content.strip():
# If no content is found, no files are written.
- self.url_map[link] = self.context['SITE_URL'] + '/' + \
- out_folder + '/' + slug + '.html'
-
- content = self.transform_content(content)
-
- self.write_metadata(os.path.join(self.output_folder, out_folder,
- slug + '.meta'),
- title, slug, post_date, description, tags)
- self.write_content(
- os.path.join(self.output_folder, out_folder, slug + '.wp'),
- content)
+ self.url_map[link] = (self.context['SITE_URL'] + out_folder + '/'
+ + slug + '.html')
+ if hasattr(self, "separate_qtranslate_content") \
+ and self.separate_qtranslate_content:
+ content_translations = separate_qtranslate_content(content)
+ else:
+ content_translations = {"": content}
+ default_language = self.context["DEFAULT_LANG"]
+ for lang, content in content_translations.items():
+ if lang:
+ out_meta_filename = slug + '.meta'
+ if lang == default_language:
+ out_content_filename = slug + '.wp'
+ else:
+ out_content_filename \
+ = utils.get_translation_candidate(self.context,
+ slug + ".wp", lang)
+ meta_slug = slug
+ else:
+ out_meta_filename = slug + '.meta'
+ out_content_filename = slug + '.wp'
+ meta_slug = slug
+ content = self.transform_content(content)
+ self.write_metadata(os.path.join(self.output_folder, out_folder,
+ out_meta_filename),
+ title, meta_slug, post_date, description, tags)
+ self.write_content(
+ os.path.join(self.output_folder,
+ out_folder, out_content_filename),
+ content)
else:
LOGGER.warn('Not going to import "{0}" because it seems to contain'
' no content.'.format(title))
@@ -441,3 +496,47 @@ def get_text_tag(tag, name, default):
return t.text
else:
return default
+
+
+def separate_qtranslate_content(text):
+ """Parse the content of a wordpress post or page and separate
+ the various language specific contents when they are delimited
+ with qtranslate tags: <!--:LL-->blabla<!--:-->"""
+ # TODO: uniformize qtranslate tags <!--/en--> => <!--:-->
+ qt_start = "<!--:"
+ qt_end = "-->"
+ qt_end_with_lang_len = 5
+ qt_chunks = text.split(qt_start)
+ content_by_lang = {}
+ common_txt_list = []
+ for c in qt_chunks:
+ if not c.strip():
+ continue
+ if c.startswith(qt_end):
+ # just after the end of a language specific section, there may
+ # be some piece of common text or tags, or just nothing
+ lang = "" # default language
+ c = c.lstrip(qt_end)
+ if not c:
+ continue
+ elif c[2:].startswith(qt_end):
+ # a language specific section (with language code at the begining)
+ lang = c[:2]
+ c = c[qt_end_with_lang_len:]
+ else:
+ # nowhere specific (maybe there is no language section in the
+ # currently parsed content)
+ lang = "" # default language
+ if not lang:
+ common_txt_list.append(c)
+ for l in content_by_lang.keys():
+ content_by_lang[l].append(c)
+ else:
+ content_by_lang[lang] = content_by_lang.get(lang, common_txt_list) + [c]
+ # in case there was no language specific section, just add the text
+ if common_txt_list and not content_by_lang:
+ content_by_lang[""] = common_txt_list
+ # Format back the list to simple text
+ for l in content_by_lang.keys():
+ content_by_lang[l] = " ".join(content_by_lang[l])
+ return content_by_lang
diff --git a/nikola/plugins/command/init.py b/nikola/plugins/command/init.py
index 96caad8..d7eeed7 100644
--- a/nikola/plugins/command/init.py
+++ b/nikola/plugins/command/init.py
@@ -28,18 +28,71 @@ from __future__ import print_function
import os
import shutil
import codecs
+import json
from mako.template import Template
import nikola
+from nikola.nikola import DEFAULT_TRANSLATIONS_PATTERN
from nikola.plugin_categories import Command
from nikola.utils import get_logger, makedirs, STDERR_HANDLER
from nikola.winutils import fix_git_symlinked
LOGGER = get_logger('init', STDERR_HANDLER)
+SAMPLE_CONF = {
+ 'BLOG_AUTHOR': "Your Name",
+ 'BLOG_TITLE': "Demo Site",
+ 'SITE_URL': "http://getnikola.com/",
+ 'BLOG_EMAIL': "joe@demo.site",
+ 'BLOG_DESCRIPTION': "This is a demo site for Nikola.",
+ 'DEFAULT_LANG': "en",
+ 'THEME': 'bootstrap3',
+ 'COMMENT_SYSTEM': 'disqus',
+ 'COMMENT_SYSTEM_ID': 'nikolademo',
+ 'TRANSLATIONS_PATTERN': DEFAULT_TRANSLATIONS_PATTERN,
+ 'POSTS': """(
+("posts/*.rst", "posts", "post.tmpl"),
+("posts/*.txt", "posts", "post.tmpl"),
+)""",
+ 'PAGES': """(
+("stories/*.rst", "stories", "story.tmpl"),
+("stories/*.txt", "stories", "story.tmpl"),
+)""",
+ 'COMPILERS': """{
+"rest": ('.rst', '.txt'),
+"markdown": ('.md', '.mdown', '.markdown'),
+"textile": ('.textile',),
+"txt2tags": ('.t2t',),
+"bbcode": ('.bb',),
+"wiki": ('.wiki',),
+"ipynb": ('.ipynb',),
+"html": ('.html', '.htm'),
+# PHP files are rendered the usual way (i.e. with the full templates).
+# The resulting files have .php extensions, making it possible to run
+# them without reconfiguring your server to recognize them.
+"php": ('.php',),
+# Pandoc detects the input from the source filename
+# but is disabled by default as it would conflict
+# with many of the others.
+# "pandoc": ('.rst', '.md', '.txt'),
+}""",
+ 'REDIRECTIONS': [],
+}
+
+
+# In order to ensure proper escaping, all variables but the three
+# pre-formatted ones are handled by json.dumps().
+def prepare_config(config):
+ """Parse sample config with JSON."""
+ p = config.copy()
+ p.update(dict((k, json.dumps(v)) for k, v in p.items()
+ if k not in ('POSTS', 'PAGES', 'COMPILERS')))
+ return p
+
class CommandInit(Command):
+
"""Create a new site."""
name = "init"
@@ -57,40 +110,6 @@ class CommandInit(Command):
}
]
- SAMPLE_CONF = {
- 'BLOG_AUTHOR': "Your Name",
- 'BLOG_TITLE': "Demo Site",
- 'SITE_URL': "http://getnikola.com/",
- 'BLOG_EMAIL': "joe@demo.site",
- 'BLOG_DESCRIPTION': "This is a demo site for Nikola.",
- 'DEFAULT_LANG': "en",
- 'THEME': 'bootstrap3',
-
- 'POSTS': """(
- ("posts/*.rst", "posts", "post.tmpl"),
- ("posts/*.txt", "posts", "post.tmpl"),
-)""",
- 'PAGES': """(
- ("stories/*.rst", "stories", "story.tmpl"),
- ("stories/*.txt", "stories", "story.tmpl"),
-)""",
- 'COMPILERS': """{
- "rest": ('.rst', '.txt'),
- "markdown": ('.md', '.mdown', '.markdown'),
- "textile": ('.textile',),
- "txt2tags": ('.t2t',),
- "bbcode": ('.bb',),
- "wiki": ('.wiki',),
- "ipynb": ('.ipynb',),
- "html": ('.html', '.htm'),
- # Pandoc detects the input from the source filename
- # but is disabled by default as it would conflict
- # with many of the others.
- # "pandoc": ('.rst', '.md', '.txt'),
-}""",
- 'REDIRECTIONS': '[]',
- }
-
@classmethod
def copy_sample_site(cls, target):
lib_path = cls.get_path_to_nikola_modules()
@@ -105,7 +124,7 @@ class CommandInit(Command):
conf_template = Template(filename=template_path)
conf_path = os.path.join(target, 'conf.py')
with codecs.open(conf_path, 'w+', 'utf8') as fd:
- fd.write(conf_template.render(**cls.SAMPLE_CONF))
+ fd.write(conf_template.render(**prepare_config(SAMPLE_CONF)))
@classmethod
def create_empty_site(cls, target):
@@ -122,16 +141,13 @@ class CommandInit(Command):
print("Usage: nikola init folder [options]")
return False
target = args[0]
- if target is None:
- print(self.usage)
+ if not options or not options.get('demo'):
+ self.create_empty_site(target)
+ LOGGER.info('Created empty site at {0}.'.format(target))
else:
- if not options or not options.get('demo'):
- self.create_empty_site(target)
- LOGGER.notice('Created empty site at {0}.'.format(target))
- else:
- self.copy_sample_site(target)
- LOGGER.notice("A new site with example data has been created at "
- "{0}.".format(target))
- LOGGER.notice("See README.txt in that folder for more information.")
-
- self.create_configuration(target)
+ self.copy_sample_site(target)
+ LOGGER.info("A new site with example data has been created at "
+ "{0}.".format(target))
+ LOGGER.info("See README.txt in that folder for more information.")
+
+ self.create_configuration(target)
diff --git a/nikola/plugins/command/install_plugin.py b/nikola/plugins/command/install_plugin.py
index 1d6584d..34223c0 100644
--- a/nikola/plugins/command/install_plugin.py
+++ b/nikola/plugins/command/install_plugin.py
@@ -27,7 +27,6 @@
from __future__ import print_function
import codecs
import os
-import sys
import json
import shutil
import subprocess
@@ -123,10 +122,10 @@ class CommandInstallPlugin(Command):
def do_install(self, name, data):
if name in data:
utils.makedirs(self.output_dir)
- LOGGER.notice('Downloading: ' + data[name])
+ LOGGER.info('Downloading: ' + data[name])
zip_file = BytesIO()
zip_file.write(requests.get(data[name]).content)
- LOGGER.notice('Extracting: {0} into plugins'.format(name))
+ LOGGER.info('Extracting: {0} into plugins'.format(name))
utils.extract_all(zip_file, 'plugins')
dest_path = os.path.join('plugins', name)
else:
@@ -142,13 +141,13 @@ class CommandInstallPlugin(Command):
LOGGER.error("{0} is already installed".format(name))
return False
- LOGGER.notice('Copying {0} into plugins'.format(plugin_path))
+ LOGGER.info('Copying {0} into plugins'.format(plugin_path))
shutil.copytree(plugin_path, dest_path)
reqpath = os.path.join(dest_path, 'requirements.txt')
if os.path.exists(reqpath):
LOGGER.notice('This plugin has Python dependencies.')
- LOGGER.notice('Installing dependencies with pip...')
+ LOGGER.info('Installing dependencies with pip...')
try:
subprocess.check_call(('pip', 'install', '-r', reqpath))
except subprocess.CalledProcessError:
@@ -159,7 +158,7 @@ class CommandInstallPlugin(Command):
print('You have to install those yourself or through a '
'package manager.')
else:
- LOGGER.notice('Dependency installation succeeded.')
+ LOGGER.info('Dependency installation succeeded.')
reqnpypath = os.path.join(dest_path, 'requirements-nonpy.txt')
if os.path.exists(reqnpypath):
LOGGER.notice('This plugin has third-party '
@@ -177,10 +176,10 @@ class CommandInstallPlugin(Command):
'manager.')
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 plugin work!')
print('Contents of the conf.py.sample file:\n')
with codecs.open(confpypath, 'rb', 'utf-8') as fh:
- if sys.platform == 'win32':
+ if self.site.colorful:
print(indent(pygments.highlight(
fh.read(), PythonLexer(), TerminalFormatter()),
4 * ' '))
diff --git a/nikola/plugins/command/install_theme.py b/nikola/plugins/command/install_theme.py
index 569397b..47c73b4 100644
--- a/nikola/plugins/command/install_theme.py
+++ b/nikola/plugins/command/install_theme.py
@@ -26,7 +26,6 @@
from __future__ import print_function
import os
-import sys
import codecs
import json
import shutil
@@ -137,10 +136,10 @@ class CommandInstallTheme(Command):
def do_install(self, name, data):
if name in data:
utils.makedirs(self.output_dir)
- LOGGER.notice('Downloading: ' + data[name])
+ LOGGER.info('Downloading: ' + data[name])
zip_file = BytesIO()
zip_file.write(requests.get(data[name]).content)
- LOGGER.notice('Extracting: {0} into themes'.format(name))
+ LOGGER.info('Extracting: {0} into themes'.format(name))
utils.extract_all(zip_file)
dest_path = os.path.join('themes', name)
else:
@@ -156,14 +155,14 @@ class CommandInstallTheme(Command):
LOGGER.error("{0} is already installed".format(name))
return False
- LOGGER.notice('Copying {0} into themes'.format(theme_path))
+ LOGGER.info('Copying {0} into themes'.format(theme_path))
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. Integrate it with yours in order to make this theme work!')
+ LOGGER.notice('This theme 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:
- if sys.platform == 'win32':
+ if self.site.colorful:
print(indent(pygments.highlight(
fh.read(), PythonLexer(), TerminalFormatter()),
4 * ' '))
diff --git a/nikola/plugins/command/new_page.plugin b/nikola/plugins/command/new_page.plugin
new file mode 100644
index 0000000..1f1c84c
--- /dev/null
+++ b/nikola/plugins/command/new_page.plugin
@@ -0,0 +1,9 @@
+[Core]
+Name = new_page
+Module = new_page
+
+[Documentation]
+Author = Roberto Alsina, Chris Warrick
+Version = 0.1
+Website = http://getnikola.com
+Description = Create a new page.
diff --git a/nikola/plugins/command/new_page.py b/nikola/plugins/command/new_page.py
new file mode 100644
index 0000000..39c0c1d
--- /dev/null
+++ b/nikola/plugins/command/new_page.py
@@ -0,0 +1,80 @@
+# -*- 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 unicode_literals, print_function
+
+from nikola.plugin_categories import Command
+
+
+class CommandNewPage(Command):
+ """Create a new page."""
+
+ name = "new_page"
+ doc_usage = "[options] [path]"
+ doc_purpose = "create a new page in the site"
+ cmd_options = [
+ {
+ 'name': 'title',
+ 'short': 't',
+ 'long': 'title',
+ 'type': str,
+ 'default': '',
+ 'help': 'Title for the page.'
+ },
+ {
+ 'name': 'onefile',
+ 'short': '1',
+ 'type': bool,
+ 'default': False,
+ 'help': 'Create the page with embedded metadata (single file format)'
+ },
+ {
+ 'name': 'twofile',
+ 'short': '2',
+ 'type': bool,
+ 'default': False,
+ 'help': 'Create the page with separate metadata (two file format)'
+ },
+ {
+ 'name': 'content_format',
+ 'short': 'f',
+ 'long': 'format',
+ 'type': str,
+ 'default': '',
+ 'help': 'Markup format for the page, one of rest, markdown, wiki, '
+ 'bbcode, html, textile, txt2tags',
+ },
+ ]
+
+ def _execute(self, options, args):
+ """Create a new page."""
+ options['tags'] = ''
+ options['schedule'] = False
+ options['is_page'] = True
+ # Even though stuff was split into `new_page`, it’s easier to do it
+ # there not to duplicate the code.
+ p = self.site.plugin_manager.getPluginByName('new_post', 'Command').plugin_object
+ return p.execute(options, args)
diff --git a/nikola/plugins/command/new_post.py b/nikola/plugins/command/new_post.py
index a5c551d..cd37a75 100644
--- a/nikola/plugins/command/new_post.py
+++ b/nikola/plugins/command/new_post.py
@@ -35,7 +35,9 @@ from blinker import signal
from nikola.plugin_categories import Command
from nikola import utils
-LOGGER = utils.get_logger('new_post', utils.STDERR_HANDLER)
+POSTLOGGER = utils.get_logger('new_post', utils.STDERR_HANDLER)
+PAGELOGGER = utils.get_logger('new_page', utils.STDERR_HANDLER)
+LOGGER = POSTLOGGER
def filter_post_pages(compiler, is_post, compilers, post_pages):
@@ -57,8 +59,8 @@ def filter_post_pages(compiler, is_post, compilers, post_pages):
type_name = "post" if is_post else "page"
raise Exception("Can't find a way, using your configuration, to create "
"a {0} in format {1}. You may want to tweak "
- "COMPILERS or POSTS/PAGES in conf.py".format(
- type_name, compiler))
+ "COMPILERS or {2}S in conf.py".format(
+ type_name, compiler, type_name.upper()))
return filtered[0]
@@ -134,7 +136,7 @@ class CommandNewPost(Command):
'long': 'page',
'type': bool,
'default': False,
- 'help': 'Create a page instead of a blog post.'
+ 'help': 'Create a page instead of a blog post. (see also: `nikola new_page`)'
},
{
'name': 'title',
@@ -142,36 +144,36 @@ class CommandNewPost(Command):
'long': 'title',
'type': str,
'default': '',
- 'help': 'Title for the page/post.'
+ 'help': 'Title for the post.'
},
{
'name': 'tags',
'long': 'tags',
'type': str,
'default': '',
- 'help': 'Comma-separated tags for the page/post.'
+ 'help': 'Comma-separated tags for the post.'
},
{
'name': 'onefile',
'short': '1',
'type': bool,
'default': False,
- 'help': 'Create post with embedded metadata (single file format)'
+ 'help': 'Create the post with embedded metadata (single file format)'
},
{
'name': 'twofile',
'short': '2',
'type': bool,
'default': False,
- 'help': 'Create post with separate metadata (two file format)'
+ 'help': 'Create the post with separate metadata (two file format)'
},
{
- 'name': 'post_format',
+ 'name': 'content_format',
'short': 'f',
'long': 'format',
'type': str,
'default': '',
- 'help': 'Markup format for post, one of rest, markdown, wiki, '
+ 'help': 'Markup format for the post, one of rest, markdown, wiki, '
'bbcode, html, textile, txt2tags',
},
{
@@ -179,13 +181,14 @@ class CommandNewPost(Command):
'short': 's',
'type': bool,
'default': False,
- 'help': 'Schedule post based on recurrence rule'
+ 'help': 'Schedule the post based on recurrence rule'
},
]
def _execute(self, options, args):
"""Create a new post or page."""
+ global LOGGER
compiler_names = [p.name for p in
self.site.plugin_manager.getPluginsOfCategory(
"PageCompiler")]
@@ -198,38 +201,46 @@ class CommandNewPost(Command):
else:
path = None
+ # Even though stuff was split into `new_page`, it’s easier to do it
+ # here not to duplicate the code.
is_page = options.get('is_page', False)
is_post = not is_page
+ content_type = 'page' if is_page else 'post'
title = options['title'] or None
tags = options['tags']
onefile = options['onefile']
twofile = options['twofile']
+ if is_page:
+ LOGGER = PAGELOGGER
+ else:
+ LOGGER = POSTLOGGER
+
if twofile:
onefile = False
if not onefile and not twofile:
onefile = self.site.config.get('ONE_FILE_POSTS', True)
- post_format = options['post_format']
+ content_format = options['content_format']
- if not post_format: # Issue #400
- post_format = get_default_compiler(
+ if not content_format: # Issue #400
+ content_format = get_default_compiler(
is_post,
self.site.config['COMPILERS'],
self.site.config['post_pages'])
- if post_format not in compiler_names:
- LOGGER.error("Unknown post format " + post_format)
+ if content_format not in compiler_names:
+ LOGGER.error("Unknown {0} format {1}".format(content_type, content_format))
return
compiler_plugin = self.site.plugin_manager.getPluginByName(
- post_format, "PageCompiler").plugin_object
+ content_format, "PageCompiler").plugin_object
# Guess where we should put this
- entry = filter_post_pages(post_format, is_post,
+ entry = filter_post_pages(content_format, is_post,
self.site.config['COMPILERS'],
self.site.config['post_pages'])
- print("Creating New Post")
+ print("Creating New {0}".format(content_type.title()))
print("-----------------\n")
if title is None:
print("Enter title: ", end='')
@@ -247,7 +258,7 @@ class CommandNewPost(Command):
if isinstance(path, utils.bytes_str):
path = path.decode(sys.stdin.encoding)
slug = utils.slugify(os.path.splitext(os.path.basename(path))[0])
- # Calculate the date to use for the post
+ # Calculate the date to use for the content
schedule = options['schedule'] or self.site.config['SCHEDULE_ALL']
rule = self.site.config['SCHEDULE_RULE']
force_today = self.site.config['SCHEDULE_FORCE_TODAY']
@@ -275,7 +286,7 @@ class CommandNewPost(Command):
metadata = self.site.config['ADDITIONAL_METADATA']
compiler_plugin.create_post(
txt_path, onefile, title=title,
- slug=slug, date=date, tags=tags, **metadata)
+ slug=slug, date=date, tags=tags, is_page=is_page, **metadata)
event = dict(path=txt_path)
@@ -283,9 +294,9 @@ class CommandNewPost(Command):
with codecs.open(meta_path, "wb+", "utf8") as fd:
fd.write('\n'.join(data))
with codecs.open(txt_path, "wb+", "utf8") as fd:
- fd.write("Write your post here.")
- LOGGER.notice("Your post's metadata is at: {0}".format(meta_path))
+ fd.write("Write your {0} here.".format(content_type))
+ LOGGER.info("Your {0}'s metadata is at: {1}".format(content_type, meta_path))
event['meta_path'] = meta_path
- LOGGER.notice("Your post's text is at: {0}".format(txt_path))
+ LOGGER.info("Your {0}'s text is at: {1}".format(content_type, txt_path))
- signal('new_post').send(self, **event)
+ signal('new_' + content_type).send(self, **event)
diff --git a/nikola/plugins/command/planetoid/__init__.py b/nikola/plugins/command/planetoid/__init__.py
index ff5dd13..fe1a59b 100644
--- a/nikola/plugins/command/planetoid/__init__.py
+++ b/nikola/plugins/command/planetoid/__init__.py
@@ -162,12 +162,12 @@ class Planetoid(Command, Task):
# TODO: log failure
return
if parsed.feed.get('title'):
- LOGGER.notice(parsed.feed.title)
+ LOGGER.info(parsed.feed.title)
else:
- LOGGER.notice(feed.url)
+ LOGGER.info(feed.url)
feed.etag = parsed.get('etag', 'foo')
modified = tuple(parsed.get('date_parsed', (1970, 1, 1)))[:6]
- LOGGER.notice("==========>", modified)
+ LOGGER.info("==========>", modified)
modified = datetime.datetime(*modified)
feed.last_modified = modified
feed.save()
@@ -176,14 +176,14 @@ class Planetoid(Command, Task):
# TODO log failure
return
for entry_data in parsed.entries:
- LOGGER.notice("=========================================")
+ LOGGER.info("=========================================")
date = entry_data.get('published_parsed', None)
if date is None:
date = entry_data.get('updated_parsed', None)
if date is None:
LOGGER.error("Can't parse date from:\n", entry_data)
return False
- LOGGER.notice("DATE:===>", date)
+ LOGGER.info("DATE:===>", date)
date = datetime.datetime(*(date[:6]))
title = "%s: %s" % (feed.name, entry_data.get('title', 'Sin título'))
content = entry_data.get('content', None)
@@ -195,9 +195,9 @@ class Planetoid(Command, Task):
content = entry_data.get('summary', 'Sin contenido')
guid = str(entry_data.get('guid', entry_data.link))
link = entry_data.link
- LOGGER.notice(repr([date, title]))
+ LOGGER.info(repr([date, title]))
e = list(Entry.select().where(Entry.guid == guid))
- LOGGER.notice(
+ LOGGER.info(
repr(dict(
date=date,
title=title,
diff --git a/nikola/plugins/command/serve.py b/nikola/plugins/command/serve.py
index 2dd15c1..f27d1f7 100644
--- a/nikola/plugins/command/serve.py
+++ b/nikola/plugins/command/serve.py
@@ -26,6 +26,7 @@
from __future__ import print_function
import os
+import webbrowser
try:
from BaseHTTPServer import HTTPServer
from SimpleHTTPServer import SimpleHTTPRequestHandler
@@ -37,7 +38,7 @@ from nikola.plugin_categories import Command
from nikola.utils import get_logger
-class CommandBuild(Command):
+class CommandServe(Command):
"""Start test server."""
name = "serve"
@@ -57,11 +58,19 @@ class CommandBuild(Command):
{
'name': 'address',
'short': 'a',
- 'long': '--address',
+ 'long': 'address',
'type': str,
'default': '127.0.0.1',
'help': 'Address to bind (default: 127.0.0.1)',
},
+ {
+ 'name': 'browser',
+ 'short': 'b',
+ 'long': 'browser',
+ 'type': bool,
+ 'default': False,
+ 'help': 'Open the test server in a web browser',
+ }
)
def _execute(self, options, args):
@@ -75,7 +84,11 @@ class CommandBuild(Command):
httpd = HTTPServer((options['address'], options['port']),
OurHTTPRequestHandler)
sa = httpd.socket.getsockname()
- self.logger.notice("Serving HTTP on {0} port {1} ...".format(*sa))
+ self.logger.info("Serving HTTP on {0} port {1} ...".format(*sa))
+ if options['browser']:
+ server_url = "http://{0}:{1}/".format(options['address'], options['port'])
+ self.logger.info("Opening {0} in the default web browser ...".format(server_url))
+ webbrowser.open(server_url)
httpd.serve_forever()
diff --git a/nikola/plugins/compile/asciidoc.py b/nikola/plugins/compile/asciidoc.py
index 12cb4bf..68f96d9 100644
--- a/nikola/plugins/compile/asciidoc.py
+++ b/nikola/plugins/compile/asciidoc.py
@@ -40,7 +40,7 @@ from nikola.utils import makedirs, req_missing
try:
from collections import OrderedDict
except ImportError:
- OrderedDict = None # NOQA
+ OrderedDict = dict # NOQA
class CompileAsciiDoc(PageCompiler):
@@ -57,11 +57,8 @@ class CompileAsciiDoc(PageCompiler):
if e.strreror == 'No such file or directory':
req_missing(['asciidoc'], 'build this site (compile with asciidoc)', python=False)
- def create_post(self, path, onefile=False, **kw):
- if OrderedDict is not None:
- metadata = OrderedDict()
- else:
- metadata = {}
+ def create_post(self, path, onefile=False, is_page=False, **kw):
+ metadata = OrderedDict()
metadata.update(self.default_metadata)
metadata.update(kw)
makedirs(os.path.dirname(path))
@@ -71,4 +68,4 @@ class CompileAsciiDoc(PageCompiler):
for k, v in metadata.items():
fd.write('.. {0}: {1}\n'.format(k, v))
fd.write("/////////////////////////////////////////////\n")
- fd.write("\nWrite your post here.")
+ fd.write("\nWrite your {0} here.".format('page' if is_page else 'post'))
diff --git a/nikola/plugins/compile/bbcode.py b/nikola/plugins/compile/bbcode.py
index 5345be3..0961ffe 100644
--- a/nikola/plugins/compile/bbcode.py
+++ b/nikola/plugins/compile/bbcode.py
@@ -40,7 +40,7 @@ from nikola.utils import makedirs, req_missing
try:
from collections import OrderedDict
except ImportError:
- OrderedDict = None # NOQA
+ OrderedDict = dict # NOQA
class CompileBbcode(PageCompiler):
@@ -66,11 +66,8 @@ class CompileBbcode(PageCompiler):
output = self.parser.format(data)
out_file.write(output)
- def create_post(self, path, onefile=False, **kw):
- if OrderedDict is not None:
- metadata = OrderedDict()
- else:
- metadata = {}
+ def create_post(self, path, onefile=False, is_page=False, **kw):
+ metadata = OrderedDict()
metadata.update(self.default_metadata)
metadata.update(kw)
makedirs(os.path.dirname(path))
@@ -80,4 +77,4 @@ class CompileBbcode(PageCompiler):
for k, v in metadata.items():
fd.write('.. {0}: {1}\n'.format(k, v))
fd.write('-->[/note]\n\n')
- fd.write("Write your post here.")
+ fd.write("Write your {0} here.".format('page' if is_page else 'post'))
diff --git a/nikola/plugins/compile/html.py b/nikola/plugins/compile/html.py
index 5352f00..09a9756 100644
--- a/nikola/plugins/compile/html.py
+++ b/nikola/plugins/compile/html.py
@@ -36,7 +36,7 @@ from nikola.utils import makedirs
try:
from collections import OrderedDict
except ImportError:
- OrderedDict = None # NOQA
+ OrderedDict = dict # NOQA
_META_SEPARATOR = '(' + os.linesep * 2 + '|' + ('\n' * 2) + '|' + ("\r\n" * 2) + ')'
@@ -56,11 +56,8 @@ class CompileHtml(PageCompiler):
out_file.write(data)
return True
- def create_post(self, path, onefile=False, **kw):
- if OrderedDict is not None:
- metadata = OrderedDict()
- else:
- metadata = {}
+ def create_post(self, path, onefile=False, is_page=False, **kw):
+ metadata = OrderedDict()
metadata.update(self.default_metadata)
metadata.update(kw)
makedirs(os.path.dirname(path))
@@ -70,4 +67,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>\n")
+ fd.write("\n<p>Write your {0} here.</p>\n".format('page' if is_page else 'post'))
diff --git a/nikola/plugins/compile/ipynb/__init__.py b/nikola/plugins/compile/ipynb/__init__.py
index 5f2f0b3..2b1fd28 100644
--- a/nikola/plugins/compile/ipynb/__init__.py
+++ b/nikola/plugins/compile/ipynb/__init__.py
@@ -44,7 +44,7 @@ from nikola.utils import makedirs, req_missing
try:
from collections import OrderedDict
except ImportError:
- OrderedDict = None # NOQA
+ OrderedDict = dict # NOQA
class CompileIPynb(PageCompiler):
@@ -66,11 +66,8 @@ class CompileIPynb(PageCompiler):
(body, resources) = exportHtml.from_notebook_node(nb_json)
out_file.write(body)
- def create_post(self, path, onefile=False, **kw):
- if OrderedDict is not None:
- metadata = OrderedDict()
- else:
- metadata = {}
+ def create_post(self, path, onefile=False, is_page=False, **kw):
+ metadata = OrderedDict()
metadata.update(self.default_metadata)
metadata.update(kw)
d_name = os.path.dirname(path)
@@ -81,7 +78,7 @@ class CompileIPynb(PageCompiler):
metadata['date'], metadata['tags'],
metadata['link'],
metadata['description'], metadata['type'])))
- print("Your post's metadata is at: ", meta_path)
+ print("Your {0}'s metadata is at: {1}".format('page' if is_page else 'post', meta_path))
with codecs.open(path, "wb+", "utf8") as fd:
fd.write("""{
"metadata": {
diff --git a/nikola/plugins/compile/markdown/__init__.py b/nikola/plugins/compile/markdown/__init__.py
index 1376b11..d0fa66a 100644
--- a/nikola/plugins/compile/markdown/__init__.py
+++ b/nikola/plugins/compile/markdown/__init__.py
@@ -54,7 +54,7 @@ except ImportError:
try:
from collections import OrderedDict
except ImportError:
- OrderedDict = None # NOQA
+ OrderedDict = dict # NOQA
from nikola.plugin_categories import PageCompiler
from nikola.utils import makedirs, req_missing
@@ -81,11 +81,8 @@ class CompileMarkdown(PageCompiler):
output = markdown(data, self.extensions)
out_file.write(output)
- def create_post(self, path, onefile=False, **kw):
- if OrderedDict is not None:
- metadata = OrderedDict()
- else:
- metadata = {}
+ def create_post(self, path, onefile=False, is_page=False, **kw):
+ metadata = OrderedDict()
metadata.update(self.default_metadata)
metadata.update(kw)
makedirs(os.path.dirname(path))
@@ -95,4 +92,4 @@ class CompileMarkdown(PageCompiler):
for k, v in metadata.items():
fd.write('.. {0}: {1}\n'.format(k, v))
fd.write('-->\n\n')
- fd.write("Write your post here.")
+ fd.write("Write your {0} here.".format('page' if is_page else 'post'))
diff --git a/nikola/plugins/compile/misaka.py b/nikola/plugins/compile/misaka.py
index 8777ffc..4951c9f 100644
--- a/nikola/plugins/compile/misaka.py
+++ b/nikola/plugins/compile/misaka.py
@@ -40,7 +40,7 @@ except ImportError:
try:
from collections import OrderedDict
except ImportError:
- OrderedDict = None # NOQA
+ OrderedDict = dict # NOQA
gist_extension = None
podcast_extension = None
@@ -73,11 +73,8 @@ class CompileMisaka(PageCompiler):
output = misaka.html(data, extensions=self.ext)
out_file.write(output)
- def create_post(self, path, onefile=False, **kw):
- if OrderedDict is not None:
- metadata = OrderedDict()
- else:
- metadata = {}
+ def create_post(self, path, onefile=False, is_page=False, **kw):
+ metadata = OrderedDict()
metadata.update(self.default_metadata)
metadata.update(kw)
makedirs(os.path.dirname(path))
@@ -87,4 +84,4 @@ class CompileMisaka(PageCompiler):
for k, v in metadata.items():
fd.write('.. {0}: {1}\n'.format(k, v))
fd.write('-->\n\n')
- fd.write("\nWrite your post here.")
+ fd.write("\nWrite your {0} here.".format('page' if is_page else 'post'))
diff --git a/nikola/plugins/compile/pandoc.py b/nikola/plugins/compile/pandoc.py
index 57c7d71..654c7c8 100644
--- a/nikola/plugins/compile/pandoc.py
+++ b/nikola/plugins/compile/pandoc.py
@@ -40,7 +40,7 @@ from nikola.utils import req_missing, makedirs
try:
from collections import OrderedDict
except ImportError:
- OrderedDict = None # NOQA
+ OrderedDict = dict # NOQA
class CompilePandoc(PageCompiler):
@@ -56,11 +56,8 @@ class CompilePandoc(PageCompiler):
if e.strreror == 'No such file or directory':
req_missing(['pandoc'], 'build this site (compile with pandoc)', python=False)
- def create_post(self, path, onefile=False, **kw):
- if OrderedDict is not None:
- metadata = OrderedDict()
- else:
- metadata = {}
+ def create_post(self, path, onefile=False, is_page=False, **kw):
+ metadata = OrderedDict()
metadata.update(self.default_metadata)
metadata.update(kw)
makedirs(os.path.dirname(path))
@@ -70,4 +67,4 @@ class CompilePandoc(PageCompiler):
for k, v in metadata.items():
fd.write('.. {0}: {1}\n'.format(k, v))
fd.write('-->\n\n')
- fd.write("Write your post here.")
+ fd.write("Write your {0} here.".format('page' if is_page else 'post'))
diff --git a/nikola/plugins/compile/php.py b/nikola/plugins/compile/php.py
index 14b80e8..0a652a6 100644
--- a/nikola/plugins/compile/php.py
+++ b/nikola/plugins/compile/php.py
@@ -38,7 +38,7 @@ from nikola.utils import makedirs
try:
from collections import OrderedDict
except ImportError:
- OrderedDict = None # NOQA
+ OrderedDict = dict # NOQA
class CompilePhp(PageCompiler):
@@ -50,11 +50,8 @@ class CompilePhp(PageCompiler):
makedirs(os.path.dirname(dest))
shutil.copyfile(source, dest)
- def create_post(self, path, onefile=False, **kw):
- if OrderedDict is not None:
- metadata = OrderedDict()
- else:
- metadata = {}
+ def create_post(self, path, onefile=False, is_page=False, **kw):
+ metadata = OrderedDict()
metadata.update(self.default_metadata)
metadata.update(kw)
os.makedirs(os.path.dirname(path))
@@ -64,7 +61,7 @@ class CompilePhp(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 {0} here.</p>".format('page' if is_page else 'post'))
def extension(self):
return ".php"
diff --git a/nikola/plugins/compile/rest/__init__.py b/nikola/plugins/compile/rest/__init__.py
index 50b37cf..9a4e19b 100644
--- a/nikola/plugins/compile/rest/__init__.py
+++ b/nikola/plugins/compile/rest/__init__.py
@@ -43,7 +43,7 @@ except ImportError:
try:
from collections import OrderedDict
except ImportError:
- OrderedDict = None # NOQA
+ OrderedDict = dict # NOQA
from nikola.plugin_categories import PageCompiler
from nikola.utils import get_logger, makedirs, req_missing
@@ -102,11 +102,8 @@ class CompileRest(PageCompiler):
else:
return False
- def create_post(self, path, onefile=False, **kw):
- if OrderedDict is not None:
- metadata = OrderedDict()
- else:
- metadata = {}
+ def create_post(self, path, onefile=False, is_page=False, **kw):
+ metadata = OrderedDict()
metadata.update(self.default_metadata)
metadata.update(kw)
makedirs(os.path.dirname(path))
@@ -114,7 +111,7 @@ class CompileRest(PageCompiler):
if onefile:
for k, v in metadata.items():
fd.write('.. {0}: {1}\n'.format(k, v))
- fd.write("\nWrite your post here.")
+ fd.write("\nWrite your {0} here.".format('page' if is_page else 'post'))
def set_site(self, site):
for plugin_info in site.plugin_manager.getPluginsOfCategory("RestExtension"):
@@ -174,6 +171,38 @@ class NikolaReader(docutils.readers.standalone.Reader):
def add_node(node, visit_function=None, depart_function=None):
+ """
+ Register a Docutils node class.
+ This function is completely optional. It is a same concept as
+ `Sphinx add_node function <http://sphinx-doc.org/ext/appapi.html#sphinx.application.Sphinx.add_node>`_.
+
+ For example::
+
+ class Plugin(RestExtension):
+
+ name = "rest_math"
+
+ def set_site(self, site):
+ self.site = site
+ directives.register_directive('math', MathDirective)
+ add_node(MathBlock, visit_Math, depart_Math)
+ return super(Plugin, self).set_site(site)
+
+ class MathDirective(Directive):
+ def run(self):
+ node = MathBlock()
+ return [node]
+
+ class Math(docutils.nodes.Element): pass
+
+ def visit_Math(self, node):
+ self.body.append(self.starttag(node, 'math'))
+
+ def depart_Math(self, node):
+ self.body.append('</math>')
+
+ For full example, you can refer to `Microdata plugin <http://plugins.getnikola.com/#microdata>`_
+ """
docutils.nodes._add_node_class_names([node.__name__])
if visit_function:
setattr(docutils.writers.html4css1.HTMLTranslator, 'visit_' + node.__name__, visit_function)
diff --git a/nikola/plugins/compile/rest/listing.py b/nikola/plugins/compile/rest/listing.py
index ecf885f..d70e02d 100644
--- a/nikola/plugins/compile/rest/listing.py
+++ b/nikola/plugins/compile/rest/listing.py
@@ -56,6 +56,18 @@ except ImportError: # docutils < 0.9 (Debian Sid For The Loss)
from nikola.plugin_categories import RestExtension
+# Add sphinx compatibility option
+CodeBlock.option_spec['linenos'] = directives.unchanged
+
+
+class FlexibleCodeBlock(CodeBlock):
+
+ def run(self):
+ if 'linenos' in self.options:
+ self.options['number-lines'] = self.options['linenos']
+ return super(FlexibleCodeBlock, self).run()
+CodeBlock = FlexibleCodeBlock
+
class Plugin(RestExtension):
@@ -71,6 +83,10 @@ class Plugin(RestExtension):
directives.register_directive('listing', Listing)
return super(Plugin, self).set_site(site)
+# Add sphinx compatibility option
+listing_spec = Include.option_spec
+listing_spec['linenos'] = directives.unchanged
+
class Listing(Include):
""" listing directive: create a highlighted block of code from a file in listings/
@@ -84,6 +100,7 @@ class Listing(Include):
has_content = False
required_arguments = 1
optional_arguments = 1
+ option_spec = listing_spec
def run(self):
fname = self.arguments.pop(0)
@@ -91,6 +108,8 @@ class Listing(Include):
fpath = os.path.join('listings', fname)
self.arguments.insert(0, fpath)
self.options['code'] = lang
+ if 'linenos' in self.options:
+ self.options['number-lines'] = self.options['linenos']
with codecs_open(fpath, 'rb+', 'utf8') as fileobject:
self.content = fileobject.read().splitlines()
self.state.document.settings.record_dependencies.add(fpath)
diff --git a/nikola/plugins/compile/textile.py b/nikola/plugins/compile/textile.py
index 73f35c0..1679831 100644
--- a/nikola/plugins/compile/textile.py
+++ b/nikola/plugins/compile/textile.py
@@ -41,7 +41,7 @@ from nikola.utils import makedirs, req_missing
try:
from collections import OrderedDict
except ImportError:
- OrderedDict = None # NOQA
+ OrderedDict = dict # NOQA
class CompileTextile(PageCompiler):
@@ -62,11 +62,8 @@ class CompileTextile(PageCompiler):
output = textile(data, head_offset=1)
out_file.write(output)
- def create_post(self, path, onefile=False, **kw):
- if OrderedDict is not None:
- metadata = OrderedDict()
- else:
- metadata = {}
+ def create_post(self, path, onefile=False, is_page=False, **kw):
+ metadata = OrderedDict()
metadata.update(self.default_metadata)
metadata.update(kw)
makedirs(os.path.dirname(path))
@@ -76,4 +73,4 @@ class CompileTextile(PageCompiler):
for k, v in metadata.items():
fd.write('.. {0}: {1}\n'.format(k, v))
fd.write('--></notextile>\n\n')
- fd.write("\nWrite your post here.")
+ fd.write("\nWrite your {0} here.".format('page' if is_page else 'post'))
diff --git a/nikola/plugins/compile/txt2tags.py b/nikola/plugins/compile/txt2tags.py
index 8c9724e..bb6afa5 100644
--- a/nikola/plugins/compile/txt2tags.py
+++ b/nikola/plugins/compile/txt2tags.py
@@ -43,7 +43,7 @@ except ImportError:
try:
from collections import OrderedDict
except ImportError:
- OrderedDict = None # NOQA
+ OrderedDict = dict # NOQA
from nikola.plugin_categories import PageCompiler
from nikola.utils import makedirs, req_missing
@@ -62,11 +62,8 @@ class CompileTxt2tags(PageCompiler):
cmd = ["-t", "html", "--no-headers", "--outfile", dest, source]
txt2tags(cmd)
- def create_post(self, path, onefile=False, **kw):
- if OrderedDict is not None:
- metadata = OrderedDict()
- else:
- metadata = {}
+ def create_post(self, path, onefile=False, is_page=False, **kw):
+ metadata = OrderedDict()
metadata.update(self.default_metadata)
metadata.update(kw)
makedirs(os.path.dirname(path))
@@ -76,4 +73,4 @@ class CompileTxt2tags(PageCompiler):
for k, v in metadata.items():
fd.write('.. {0}: {1}\n'.format(k, v))
fd.write("-->\n'''\n")
- fd.write("\nWrite your post here.")
+ fd.write("\nWrite your {0} here.".format('page' if is_page else 'post'))
diff --git a/nikola/plugins/compile/wiki.py b/nikola/plugins/compile/wiki.py
index 9a365fa..f4858c7 100644
--- a/nikola/plugins/compile/wiki.py
+++ b/nikola/plugins/compile/wiki.py
@@ -40,7 +40,7 @@ from nikola.plugin_categories import PageCompiler
try:
from collections import OrderedDict
except ImportError:
- OrderedDict = None # NOQA
+ OrderedDict = dict # NOQA
from nikola.utils import makedirs, req_missing
@@ -62,11 +62,8 @@ class CompileWiki(PageCompiler):
output = HtmlEmitter(document).emit()
out_file.write(output)
- def create_post(self, path, onefile=False, **kw):
- if OrderedDict is not None:
- metadata = OrderedDict()
- else:
- metadata = {}
+ def create_post(self, path, onefile=False, is_page=False, **kw):
+ metadata = OrderedDict()
metadata.update(self.default_metadata)
metadata.update(kw)
makedirs(os.path.dirname(path))
@@ -75,4 +72,4 @@ class CompileWiki(PageCompiler):
'one-file format is not possible, use the -2 '
'option.')
with codecs.open(path, "wb+", "utf8") as fd:
- fd.write("Write your post here.")
+ fd.write("Write your {0} here.".format('page' if is_page else 'post'))
diff --git a/nikola/plugins/loghandler/stderr.py b/nikola/plugins/loghandler/stderr.py
index 75acffc..fdc892e 100644
--- a/nikola/plugins/loghandler/stderr.py
+++ b/nikola/plugins/loghandler/stderr.py
@@ -26,10 +26,10 @@
from nikola.plugin_categories import SignalHandler
from blinker import signal
-import logbook
import os
from nikola import DEBUG
+from nikola.utils import ColorfulStderrHandler
class StderrHandler(SignalHandler):
@@ -40,7 +40,7 @@ class StderrHandler(SignalHandler):
"""Attach the handler to the logger."""
conf = self.site.config.get('LOGGING_HANDLERS').get('stderr')
if conf or os.getenv('NIKOLA_DEBUG'):
- self.site.loghandlers.append(logbook.StderrHandler(
+ self.site.loghandlers.append(ColorfulStderrHandler(
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/build_less.py b/nikola/plugins/task/build_less.py
index 14a53f9..a672282 100644
--- a/nikola/plugins/task/build_less.py
+++ b/nikola/plugins/task/build_less.py
@@ -46,22 +46,34 @@ class BuildLess(Task):
def gen_tasks(self):
"""Generate CSS out of LESS sources."""
self.compiler_name = self.site.config['LESS_COMPILER']
+ self.compiler_options = self.site.config['LESS_OPTIONS']
kw = {
'cache_folder': self.site.config['CACHE_FOLDER'],
'themes': self.site.THEMES,
}
+ tasks = {}
# Find where in the theme chain we define the LESS targets
# There can be many *.less in the folder, but we only will build
# the ones listed in less/targets
- targets_path = utils.get_asset_path(os.path.join(self.sources_folder, "targets"), self.site.THEMES)
+ if os.path.isfile(os.path.join(self.sources_folder, "targets")):
+ targets_path = os.path.join(self.sources_folder, "targets")
+ else:
+ targets_path = utils.get_asset_path(os.path.join(self.sources_folder, "targets"), self.site.THEMES)
try:
with codecs.open(targets_path, "rb", "utf-8") as inf:
targets = [x.strip() for x in inf.readlines()]
except Exception:
targets = []
+ for task in utils.copy_tree(self.sources_folder, os.path.join(kw['cache_folder'], self.sources_folder)):
+ if task['name'] in tasks:
+ continue
+ task['basename'] = 'prepare_less_sources'
+ tasks[task['name']] = task
+ yield task
+
for theme_name in kw['themes']:
src = os.path.join(utils.get_theme_path(theme_name), self.sources_folder)
for task in utils.copy_tree(src, os.path.join(kw['cache_folder'], self.sources_folder)):
@@ -82,7 +94,7 @@ class BuildLess(Task):
src = os.path.join(kw['cache_folder'], self.sources_folder, target)
run_in_shell = sys.platform == 'win32'
try:
- compiled = subprocess.check_output([self.compiler_name, src], shell=run_in_shell)
+ compiled = subprocess.check_output([self.compiler_name] + self.compiler_options + [src], shell=run_in_shell)
except OSError:
utils.req_missing([self.compiler_name],
'build LESS files (and use this theme)',
diff --git a/nikola/plugins/task/build_sass.py b/nikola/plugins/task/build_sass.py
index 7575505..becc843 100644
--- a/nikola/plugins/task/build_sass.py
+++ b/nikola/plugins/task/build_sass.py
@@ -47,26 +47,41 @@ class BuildSass(Task):
"""Generate CSS out of Sass sources."""
self.logger = utils.get_logger('build_sass', self.site.loghandlers)
self.compiler_name = self.site.config['SASS_COMPILER']
+ self.compiler_options = self.site.config['SASS_OPTIONS']
kw = {
'cache_folder': self.site.config['CACHE_FOLDER'],
'themes': self.site.THEMES,
}
+ tasks = {}
# Find where in the theme chain we define the Sass targets
# There can be many *.sass/*.scss in the folder, but we only
# will build the ones listed in sass/targets
- targets_path = utils.get_asset_path(os.path.join(self.sources_folder, "targets"), self.site.THEMES)
+ if os.path.isfile(os.path.join(self.sources_folder, "targets")):
+ targets_path = os.path.join(self.sources_folder, "targets")
+ else:
+ targets_path = utils.get_asset_path(os.path.join(self.sources_folder, "targets"), self.site.THEMES)
try:
with codecs.open(targets_path, "rb", "utf-8") as inf:
targets = [x.strip() for x in inf.readlines()]
except Exception:
targets = []
+ for task in utils.copy_tree(self.sources_folder, os.path.join(kw['cache_folder'], self.sources_folder)):
+ if task['name'] in tasks:
+ continue
+ task['basename'] = 'prepare_sass_sources'
+ tasks[task['name']] = task
+ yield task
+
for theme_name in kw['themes']:
src = os.path.join(utils.get_theme_path(theme_name), self.sources_folder)
for task in utils.copy_tree(src, os.path.join(kw['cache_folder'], self.sources_folder)):
+ if task['name'] in tasks:
+ continue
task['basename'] = 'prepare_sass_sources'
+ tasks[task['name']] = task
yield task
# Build targets and write CSS files
@@ -83,7 +98,7 @@ class BuildSass(Task):
run_in_shell = sys.platform == 'win32'
src = os.path.join(kw['cache_folder'], self.sources_folder, target)
try:
- compiled = subprocess.check_output([self.compiler_name, src], shell=run_in_shell)
+ compiled = subprocess.check_output([self.compiler_name] + self.compiler_options + [src], shell=run_in_shell)
except OSError:
utils.req_missing([self.compiler_name],
'build Sass files (and use this theme)',
diff --git a/nikola/plugins/task/bundles.py b/nikola/plugins/task/bundles.py
index b035b97..fcfaf42 100644
--- a/nikola/plugins/task/bundles.py
+++ b/nikola/plugins/task/bundles.py
@@ -87,8 +87,8 @@ class BuildBundles(LateTask):
output_path = os.path.join(kw['output_folder'], name)
dname = os.path.dirname(name)
file_dep = [os.path.join(kw['output_folder'], dname, fname)
- for fname in files]
- file_dep = filter(os.path.isfile, file_dep) # removes missing files
+ for fname in files if
+ utils.get_asset_path(fname, self.site.THEMES, self.site.config['FILES_FOLDERS'])]
task = {
'file_dep': list(file_dep),
'task_dep': ['copy_assets'],
diff --git a/nikola/plugins/task/copy_assets.py b/nikola/plugins/task/copy_assets.py
index 21f1f85..93b7fb3 100644
--- a/nikola/plugins/task/copy_assets.py
+++ b/nikola/plugins/task/copy_assets.py
@@ -75,8 +75,8 @@ class CopyAssets(Task):
formatter = get_formatter_by_name('html', style=kw["code_color_scheme"])
utils.makedirs(os.path.dirname(code_css_path))
with codecs.open(code_css_path, 'wb+', 'utf8') as outf:
- outf.write(formatter.get_style_defs('.code'))
- outf.write("table.codetable { width: 100%;} td.linenos {text-align: right; width: 4em;}")
+ outf.write(formatter.get_style_defs(['pre.code', 'div.code pre']))
+ outf.write("\ntable.codetable { width: 100%;} td.linenos {text-align: right; width: 4em;}\n")
task = {
'basename': self.name,
diff --git a/nikola/plugins/task/galleries.py b/nikola/plugins/task/galleries.py
index 6977eab..880d47c 100644
--- a/nikola/plugins/task/galleries.py
+++ b/nikola/plugins/task/galleries.py
@@ -176,6 +176,7 @@ 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]
+ dest_img_list = [os.path.join(self.kw['output_folder'], t) for t in image_list]
folders = []
@@ -193,7 +194,8 @@ class Galleries(Task):
context["folders"] = folders
context["crumbs"] = crumbs
context["permalink"] = self.site.link(
- "gallery", os.path.basename(gallery), lang)
+ "gallery", os.path.basename(
+ os.path.relpath(gallery, self.kw['gallery_path'])), lang)
# FIXME: use kw
context["enable_comments"] = (
self.site.config["COMMENTS_IN_GALLERIES"])
@@ -219,7 +221,7 @@ class Galleries(Task):
template_name,
dst,
context,
- image_list,
+ dest_img_list,
thumbs,
file_dep))],
'clean': True,
diff --git a/nikola/plugins/task/listings.py b/nikola/plugins/task/listings.py
index d8ed43b..86be6c4 100644
--- a/nikola/plugins/task/listings.py
+++ b/nikola/plugins/task/listings.py
@@ -55,7 +55,7 @@ class Listings(Task):
}
# Things to ignore in listings
- ignored_extensions = (".pyc",)
+ ignored_extensions = (".pyc", ".pyo")
def render_listing(in_name, out_name, folders=[], files=[]):
if in_name:
diff --git a/nikola/plugins/task/localsearch/files/assets/css/img/search.png b/nikola/plugins/task/localsearch/files/assets/css/img/search.png
index 9ab0f2c..9ab0f2c 100755..100644
--- a/nikola/plugins/task/localsearch/files/assets/css/img/search.png
+++ b/nikola/plugins/task/localsearch/files/assets/css/img/search.png
Binary files differ
diff --git a/nikola/plugins/task/localsearch/files/assets/css/tipuesearch.css b/nikola/plugins/task/localsearch/files/assets/css/tipuesearch.css
index 2230193..2230193 100755..100644
--- a/nikola/plugins/task/localsearch/files/assets/css/tipuesearch.css
+++ b/nikola/plugins/task/localsearch/files/assets/css/tipuesearch.css
diff --git a/nikola/plugins/task/localsearch/files/tipue_search.html b/nikola/plugins/task/localsearch/files/tipue_search.html
index 789fbe5..789fbe5 100755..100644
--- a/nikola/plugins/task/localsearch/files/tipue_search.html
+++ b/nikola/plugins/task/localsearch/files/tipue_search.html
diff --git a/nikola/plugins/task/rss.py b/nikola/plugins/task/rss.py
index e5f7548..9e4204c 100644
--- a/nikola/plugins/task/rss.py
+++ b/nikola/plugins/task/rss.py
@@ -58,6 +58,11 @@ class GenerateRSS(Task):
"feed_length": self.site.config['FEED_LENGTH'],
}
self.site.scan_posts()
+ # Check for any changes in the state of use_in_feeds for any post.
+ # Issue #934
+ kw['use_in_feeds_status'] = ''.join(
+ ['T' if x.use_in_feeds else 'F' for x in self.site.timeline]
+ )
yield self.group_task()
for lang in kw["translations"]:
output_name = os.path.join(kw['output_folder'],
diff --git a/nikola/plugins/task/sitemap/__init__.py b/nikola/plugins/task/sitemap/__init__.py
index 0164000..147bd50 100644
--- a/nikola/plugins/task/sitemap/__init__.py
+++ b/nikola/plugins/task/sitemap/__init__.py
@@ -144,26 +144,35 @@ class Sitemap(LateTask):
def write_sitemap():
# Have to rescan, because files may have been added between
# task dep scanning and task execution
- scan_locs()
with codecs.open(sitemap_path, 'wb+', 'utf8') as outf:
outf.write(header)
for k in sorted(locs.keys()):
outf.write(locs[k])
outf.write("</urlset>")
- # Other tasks can depend on this output, instead of having
- # to scan locations.
+
+ # Yield a task to calculate the dependencies of the sitemap
+ # Other tasks can depend on this output, instead of having
+ # to scan locations.
+ def scan_locs_task():
+ scan_locs()
return {'locations': list(locs.keys())}
- scan_locs()
+ yield {
+ "basename": "_scan_locs",
+ "name": "sitemap",
+ "actions": [(scan_locs_task)]
+ }
+
yield self.group_task()
task = {
"basename": "sitemap",
"name": sitemap_path,
"targets": [sitemap_path],
"actions": [(write_sitemap,)],
- "uptodate": [config_changed({1: kw, 2: locs})],
+ "uptodate": [config_changed(kw)],
"clean": True,
"task_dep": ["render_site"],
+ "calc_dep": ["_scan_locs:sitemap"],
}
yield task
diff --git a/nikola/plugins/task/tags.py b/nikola/plugins/task/tags.py
index a2444ec..f6b8234 100644
--- a/nikola/plugins/task/tags.py
+++ b/nikola/plugins/task/tags.py
@@ -311,9 +311,17 @@ class RenderTags(Task):
self.site.config['INDEX_FILE']] if _f]
def tag_path(self, name, lang):
- return [_f for _f in [self.site.config['TRANSLATIONS'][lang],
- self.site.config['TAG_PATH'], self.slugify_name(name) + ".html"] if
- _f]
+ if self.site.config['PRETTY_URLS']:
+ return [_f for _f in [
+ self.site.config['TRANSLATIONS'][lang],
+ self.site.config['TAG_PATH'],
+ self.slugify_name(name),
+ self.site.config['INDEX_FILE']] if _f]
+ else:
+ return [_f for _f in [
+ self.site.config['TRANSLATIONS'][lang],
+ self.site.config['TAG_PATH'],
+ self.slugify_name(name) + ".html"] if _f]
def tag_rss_path(self, name, lang):
return [_f for _f in [self.site.config['TRANSLATIONS'][lang],
diff --git a/nikola/plugins/template/jinja.py b/nikola/plugins/template/jinja.py
index 17c33d4..f14adfe 100644
--- a/nikola/plugins/template/jinja.py
+++ b/nikola/plugins/template/jinja.py
@@ -61,6 +61,11 @@ class JinjaTemplates(TemplateSystem):
self.lookup.loader = jinja2.FileSystemLoader(directories,
encoding='utf-8')
+ def set_site(self, site):
+ """Sets the site."""
+ self.site = site
+ self.lookup.filters.update(self.site.config['TEMPLATE_FILTERS'])
+
def render_template(self, template_name, output_name, context):
"""Render the template into output_name using context."""
if jinja2 is None:
diff --git a/nikola/plugins/template/mako.py b/nikola/plugins/template/mako.py
index 45f4335..5a23230 100644
--- a/nikola/plugins/template/mako.py
+++ b/nikola/plugins/template/mako.py
@@ -49,6 +49,7 @@ class MakoTemplates(TemplateSystem):
lookup = None
cache = {}
+ filters = {}
def get_deps(self, filename):
text = util.read_file(filename)
@@ -81,6 +82,11 @@ class MakoTemplates(TemplateSystem):
module_directory=cache_dir,
output_encoding='utf-8')
+ def set_site(self, site):
+ """Sets the site."""
+ self.site = site
+ self.filters.update(self.site.config['TEMPLATE_FILTERS'])
+
def render_template(self, template_name, output_name, context):
"""Render the template into output_name using context."""
context['striphtml'] = striphtml
@@ -95,6 +101,8 @@ class MakoTemplates(TemplateSystem):
def render_template_to_string(self, template, context):
""" Render template to a string using context. """
+ context = context.update(self.filters)
+
return Template(template).render(**context)
def template_deps(self, template_name):
diff --git a/nikola/post.py b/nikola/post.py
index 810474b..5cf7236 100644
--- a/nikola/post.py
+++ b/nikola/post.py
@@ -194,6 +194,9 @@ class Post(object):
# If mathjax is a tag, then enable mathjax rendering support
self.is_mathjax = 'mathjax' in self.tags
+ def __repr__(self):
+ return '<Post: {0}>'.format(self.source_path)
+
def _has_pretty_url(self, lang):
if self.pretty_urls and \
self.meta[lang].get('pretty_url', '') != 'False' and \
@@ -493,7 +496,7 @@ class Post(object):
pieces = [_f for _f in pieces if _f and _f != '.']
link = '/' + '/'.join(pieces)
if absolute:
- link = urljoin(self.base_url, link)
+ link = urljoin(self.base_url, link[1:])
index_len = len(self.index_file)
if self.strip_indexes and link[-(1 + index_len):] == '/' + self.index_file:
return link[:-index_len]
diff --git a/nikola/utils.py b/nikola/utils.py
index 0c3b4c0..46e159e 100644
--- a/nikola/utils.py
+++ b/nikola/utils.py
@@ -35,7 +35,6 @@ import locale
import logging
import os
import re
-import codecs
import json
import shutil
import subprocess
@@ -47,7 +46,7 @@ except ImportError:
pass
import logbook
-from logbook.more import ExceptionHandler
+from logbook.more import ExceptionHandler, ColorizedStderrHandler
import pytz
from . import DEBUG
@@ -57,6 +56,15 @@ class ApplicationWarning(Exception):
pass
+class ColorfulStderrHandler(ColorizedStderrHandler):
+ """Stream handler with colors."""
+ _colorful = False
+
+ def should_colorize(self, record):
+ """Inform about colorization using the value obtained from Nikola."""
+ return self._colorful
+
+
def get_logger(name, handlers):
"""Get a logger with handlers attached."""
l = logbook.Logger(name)
@@ -68,7 +76,7 @@ def get_logger(name, handlers):
return l
-STDERR_HANDLER = [logbook.StderrHandler(
+STDERR_HANDLER = [ColorfulStderrHandler(
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}'
)]
@@ -126,11 +134,11 @@ from unidecode import unidecode
import PyRSS2Gen as rss
__all__ = ['get_theme_path', 'get_theme_chain', 'load_messages', 'copy_tree',
- 'generic_rss_renderer', 'copy_file', 'slugify', 'unslugify',
- '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', 'demote_headers',
+ 'copy_file', 'slugify', 'unslugify', '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', 'demote_headers',
'get_translation_candidate']
@@ -313,10 +321,14 @@ def copy_tree(src, dst, link_cutoff=None):
dst_dir = os.path.join(dst, *root_parts[base_len:])
makedirs(dst_dir)
for src_name in files:
- if src_name == '.DS_Store':
+ if src_name in ('.DS_Store', 'Thumbs.db'):
continue
dst_file = os.path.join(dst_dir, src_name)
src_file = os.path.join(root, src_name)
+ if sys.version_info[0] == 2:
+ # Python2 prefers encoded str here
+ dst_file = sys_encode(dst_file)
+ src_file = sys_encode(src_file)
yield {
'name': str(dst_file),
'file_dep': [src_file],
@@ -326,45 +338,6 @@ def copy_tree(src, dst, link_cutoff=None):
}
-def generic_rss_renderer(lang, title, link, description, timeline, output_path,
- rss_teasers, feed_length=10, feed_url=None):
- """Takes all necessary data, and renders a RSS feed in output_path."""
- items = []
- for post in timeline[:feed_length]:
- args = {
- 'title': post.title(lang),
- 'link': post.permalink(lang, absolute=True),
- 'description': post.text(lang, teaser_only=rss_teasers, really_absolute=True),
- 'guid': post.permalink(lang, absolute=True),
- # PyRSS2Gen's pubDate is GMT time.
- 'pubDate': (post.date if post.date.tzinfo is None else
- post.date.astimezone(pytz.timezone('UTC'))),
- 'categories': post._tags.get(lang, []),
- 'author': post.meta('author'),
- }
-
- items.append(ExtendedItem(**args))
- rss_obj = ExtendedRSS2(
- title=title,
- link=link,
- description=description,
- lastBuildDate=datetime.datetime.now(),
- items=items,
- generator='Nikola <http://getnikola.com/>',
- language=lang
- )
- rss_obj.self_url = feed_url
- rss_obj.rss_attrs["xmlns:atom"] = "http://www.w3.org/2005/Atom"
- rss_obj.rss_attrs["xmlns:dc"] = "http://purl.org/dc/elements/1.1/"
- dst_dir = os.path.dirname(output_path)
- makedirs(dst_dir)
- with codecs.open(output_path, "wb+", "utf-8") as rss_file:
- data = rss_obj.to_xml(encoding='utf-8')
- if isinstance(data, bytes_str):
- data = data.decode('utf-8')
- rss_file.write(data)
-
-
def copy_file(source, dest, cutoff=None):
dst_dir = os.path.dirname(dest)
makedirs(dst_dir)
@@ -518,9 +491,11 @@ def get_tzname(dt):
def current_time(tzinfo=None):
- dt = datetime.datetime.now()
+ dt = datetime.datetime.utcnow()
if tzinfo is not None:
- dt = tzinfo.localize(dt)
+ dt = tzinfo.fromutc(dt)
+ else:
+ dt = pytz.UTC.localize(dt)
return dt
@@ -749,7 +724,11 @@ class LocaleBorg(object):
else: # Python 2
with calendar.TimeEncoding(self.locales[lang]):
s = calendar.month_name[month_no]
- s = s.decode(self.encodings[lang])
+ enc = self.encodings[lang]
+ if not enc:
+ enc = 'UTF-8'
+
+ s = s.decode(enc)
# paranoid about calendar ending in the wrong locale (windows)
self.set_locale(self.current_lang)
return s
diff --git a/requirements-full.txt b/requirements-full.txt
index f7be8cf..2743f1b 100644
--- a/requirements-full.txt
+++ b/requirements-full.txt
@@ -1,15 +1,14 @@
-r requirements.txt
-mock>=1.0.0
requests>=1.0
markdown
Jinja2>=2.7
bbcode
-livereload>=2.0.0
+livereload==2.1.0
pyphen
python-dateutil
micawber
pygal
-typogrify
+typogrify>=2.0.4
phpserialize
webassets
ipython>=1.0.0
diff --git a/requirements-tests.txt b/requirements-tests.txt
index 168a927..fe07dfa 100644
--- a/requirements-tests.txt
+++ b/requirements-tests.txt
@@ -1,4 +1,5 @@
-r requirements-full.txt
+mock>=1.0.0
coverage
nose
freezegun
diff --git a/requirements.txt b/requirements.txt
index 94e4ae7..c4fa50d 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -11,3 +11,4 @@ pytz
logbook
blinker
setuptools
+colorama
diff --git a/scripts/getpyver.py b/scripts/getpyver.py
new file mode 100755
index 0000000..b5f83b0
--- /dev/null
+++ b/scripts/getpyver.py
@@ -0,0 +1,25 @@
+#!/usr/bin/env python
+# For internal use only.
+"""Return the Python version in a sane format (vX.Y).
+
+Also available a less sane format (X.Y) if `short` is provided
+as an argument.
+
+Or ([v]X.Y.Z) if `long` is provided.
+
+$ getpyver.py
+v2.7
+$ getpyver.py short
+2.7
+$ getpyver.py long
+v2.7.6
+$ getpyver.py long short
+2.7.6
+
+"""
+import sys
+limit = 3 if 'long' in sys.argv else 2
+if 'short' in sys.argv:
+ print(".".join([str(i) for i in sys.version_info[0:limit]]))
+else:
+ print("v" + (".".join([str(i) for i in sys.version_info[0:limit]])))
diff --git a/scripts/import_po.py b/scripts/import_po.py
index 224826c..7e99064 100755
--- a/scripts/import_po.py
+++ b/scripts/import_po.py
@@ -6,10 +6,12 @@ from __future__ import unicode_literals, print_function
import codecs
from glob import glob
import os
-
+import sys
import polib
-os.system("tx pull -a")
+if 'nopull' not in sys.argv:
+ os.system("tx pull -a")
+
trans_files = glob(os.path.join('translations', 'nikola.messages', '*.po'))
for fname in trans_files:
lang = os.path.splitext(os.path.basename(fname))[0].lower()
diff --git a/scripts/set_version.py b/scripts/set_version.py
index 289aaef..38ea03c 100755
--- a/scripts/set_version.py
+++ b/scripts/set_version.py
@@ -23,7 +23,7 @@ def sed_like_thing(pattern, repl, path):
outf.write(data)
if __name__ == "__main__":
- print("New version number: ", end="")
+ print("New version number (in format X.Y.Z): ", end="")
sys.stdout.flush()
version = sys.stdin.readline().strip()
@@ -34,5 +34,5 @@ if __name__ == "__main__":
sed_like_thing("version = '.+'", "version = '{0}'".format(version), os.path.join('docs', 'sphinx', 'conf.py'))
sed_like_thing("release = '.+'", "release = '{0}'".format(version), os.path.join('docs', 'sphinx', 'conf.py'))
sed_like_thing('__version__ = ".*"', '__version__ = "{0}"'.format(version), os.path.join('nikola', '__init__.py'))
- sed_like_thing('New in Master', 'New in {0}'.format(version), 'CHANGES.txt')
+ sed_like_thing('New in master', 'New in v{0}'.format(version), 'CHANGES.txt')
os.system("help2man -h help -N --version-string='{0}' nikola > {1}".format(version, os.path.join('docs', 'man', 'nikola.1')))
diff --git a/setup.cfg b/setup.cfg
index 5e40900..5995929 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -1,2 +1,5 @@
[wheel]
universal = 1
+
+[flake8]
+ignore=E501
diff --git a/setup.py b/setup.py
index 8cd113e..9afda23 100755
--- a/setup.py
+++ b/setup.py
@@ -135,7 +135,7 @@ class nikola_install(install):
setup(name='Nikola',
- version='6.3.0',
+ version='6.4.0',
description='A modular, fast, simple, static website generator',
long_description=open('README.rst').read(),
author='Roberto Alsina and others',
diff --git a/tests/test_command_import_wordpress.py b/tests/test_command_import_wordpress.py
index d12c7f6..04e0631 100644
--- a/tests/test_command_import_wordpress.py
+++ b/tests/test_command_import_wordpress.py
@@ -51,6 +51,79 @@ Easy.
self.assertEqual(expected_xml, self.import_command._glue_xml_lines(xml))
+class TestQTranslateContentSeparation(BasicCommandImportWordpress):
+
+ def test_conserves_qtranslate_less_post(self):
+ content = """Si vous préférez savoir à qui vous parlez commencez par visiter l'<a title="À propos" href="http://some.blog/about/">À propos</a>.
+
+Quoiqu'il en soit, commentaires, questions et suggestions sont les bienvenues !"""
+ content_translations = self.module.separate_qtranslate_content(content)
+ self.assertEqual(1, len(content_translations))
+ self.assertEqual(content, content_translations[""])
+
+ def test_split_a_two_language_post(self):
+ content = """<!--:fr-->Si vous préférez savoir à qui vous parlez commencez par visiter l'<a title="À propos" href="http://some.blog/about/">À propos</a>.
+
+Quoiqu'il en soit, commentaires, questions et suggestions sont les bienvenues !
+<!--:--><!--:en-->If you'd like to know who you're talking to, please visit the <a title="À propos" href="http://some.blog/about/">about page</a>.
+
+Comments, questions and suggestions are welcome !
+<!--:-->"""
+ content_translations = self.module.separate_qtranslate_content(content)
+ self.assertEqual("""Si vous préférez savoir à qui vous parlez commencez par visiter l'<a title="À propos" href="http://some.blog/about/">À propos</a>.
+
+Quoiqu'il en soit, commentaires, questions et suggestions sont les bienvenues !
+""", content_translations["fr"])
+ self.assertEqual("""If you'd like to know who you're talking to, please visit the <a title="À propos" href="http://some.blog/about/">about page</a>.
+
+Comments, questions and suggestions are welcome !
+""", content_translations["en"])
+
+ def test_split_a_two_language_post_with_teaser(self):
+ content = """<!--:fr-->Si vous préférez savoir à qui vous parlez commencez par visiter l'<a title="À propos" href="http://some.blog/about/">À propos</a>.
+
+Quoiqu'il en soit, commentaires, questions et suggestions sont les bienvenues !
+<!--:--><!--:en-->If you'd like to know who you're talking to, please visit the <a title="À propos" href="http://some.blog/about/">about page</a>.
+
+Comments, questions and suggestions are welcome !
+<!--:--><!--more--><!--:fr-->
+Plus de détails ici !
+<!--:--><!--:en-->
+More details here !
+<!--:-->"""
+ content_translations = self.module.separate_qtranslate_content(content)
+ self.assertEqual("""Si vous préférez savoir à qui vous parlez commencez par visiter l'<a title="À propos" href="http://some.blog/about/">À propos</a>.
+
+Quoiqu'il en soit, commentaires, questions et suggestions sont les bienvenues !
+ <!--more--> \n\
+Plus de détails ici !
+""", content_translations["fr"])
+ self.assertEqual("""If you'd like to know who you're talking to, please visit the <a title="À propos" href="http://some.blog/about/">about page</a>.
+
+Comments, questions and suggestions are welcome !
+ <!--more--> \n\
+More details here !
+""", content_translations["en"])
+
+ def test_split_a_two_language_post_with_intermission(self):
+ content = """<!--:fr-->Voila voila<!--:-->COMMON<!--:en-->BLA<!--:-->"""
+ content_translations = self.module.separate_qtranslate_content(content)
+ self.assertEqual("Voila voila COMMON", content_translations["fr"])
+ self.assertEqual("COMMON BLA", content_translations["en"])
+
+ def test_split_a_two_language_post_with_uneven_repartition(self):
+ content = """<!--:fr-->Voila voila<!--:-->COMMON<!--:fr-->MOUF<!--:--><!--:en-->BLA<!--:-->"""
+ content_translations = self.module.separate_qtranslate_content(content)
+ self.assertEqual("Voila voila COMMON MOUF", content_translations["fr"])
+ self.assertEqual("COMMON BLA", content_translations["en"])
+
+ def test_split_a_two_language_post_with_uneven_repartition_bis(self):
+ content = """<!--:fr-->Voila voila<!--:--><!--:en-->BLA<!--:-->COMMON<!--:fr-->MOUF<!--:-->"""
+ content_translations = self.module.separate_qtranslate_content(content)
+ self.assertEqual("Voila voila COMMON MOUF", content_translations["fr"])
+ self.assertEqual("BLA COMMON", content_translations["en"])
+
+
class CommandImportWordpressRunTest(BasicCommandImportWordpress):
def setUp(self):
super(self.__class__, self).setUp()
@@ -134,7 +207,7 @@ class CommandImportWordpressTest(BasicCommandImportWordpress):
self.assertEqual('Wordpress blog title', context['BLOG_TITLE'])
self.assertEqual('Nikola test blog ;) - with moré Ümläüts',
context['BLOG_DESCRIPTION'])
- self.assertEqual('http://some.blog', context['SITE_URL'])
+ self.assertEqual('http://some.blog/', context['SITE_URL'])
self.assertEqual('mail@some.blog', context['BLOG_EMAIL'])
self.assertEqual('Niko', context['BLOG_AUTHOR'])
@@ -173,7 +246,7 @@ class CommandImportWordpressTest(BasicCommandImportWordpress):
'kontakt', '2009-07-16 20:20:32', None, [])
self.assertTrue(write_content.called)
- write_content.assert_any_call('new_site/posts/200704hoert.wp'.replace('/', os.sep),
+ write_content.assert_any_call('new_site/posts/2007/04/hoert.wp'.replace('/', os.sep),
"""An image.
<img class="size-full wp-image-16" title="caption test" src="http://some.blog/wp-content/uploads/2009/07/caption_test.jpg" alt="caption test" width="739" height="517" />
@@ -193,7 +266,7 @@ The end.
""")
write_content.assert_any_call(
- 'new_site/posts/200807arzt-und-pfusch-s-i-c-k.wp'.replace('/', os.sep),
+ 'new_site/posts/2008/07/arzt-und-pfusch-s-i-c-k.wp'.replace('/', os.sep),
'''<img class="size-full wp-image-10 alignright" title="Arzt+Pfusch - S.I.C.K." src="http://some.blog/wp-content/uploads/2008/07/arzt_und_pfusch-sick-cover.png" alt="Arzt+Pfusch - S.I.C.K." width="210" height="209" />Arzt+Pfusch - S.I.C.K.Gerade bin ich \xfcber das Album <em>S.I.C.K</em> von <a title="Arzt+Pfusch" href="http://www.arztpfusch.com/" target="_blank">Arzt+Pfusch</a> gestolpert, welches Arzt+Pfusch zum Download f\xfcr lau anbieten. Das Album steht unter einer Creative Commons <a href="http://creativecommons.org/licenses/by-nc-nd/3.0/de/">BY-NC-ND</a>-Lizenz.
Die Ladung <em>noisebmstupidevildustrial</em> gibts als MP3s mit <a href="http://www.archive.org/download/dmp005/dmp005_64kb_mp3.zip">64kbps</a> und <a href="http://www.archive.org/download/dmp005/dmp005_vbr_mp3.zip">VBR</a>, als Ogg Vorbis und als FLAC (letztere <a href="http://www.archive.org/details/dmp005">hier</a>). <a href="http://www.archive.org/download/dmp005/dmp005-artwork.zip">Artwork</a> und <a href="http://www.archive.org/download/dmp005/dmp005-lyrics.txt">Lyrics</a> gibts nochmal einzeln zum Download.''')
write_content.assert_any_call(
@@ -214,11 +287,11 @@ Diese Daten sind f\xfcr mich nicht bestimmten Personen zuordenbar. Eine Zusammen
self.assertEqual(
self.import_command.url_map['http://some.blog/2007/04/hoert/'],
- 'http://some.blog/posts/200704hoert.html')
+ 'http://some.blog/posts/2007/04/hoert.html')
self.assertEqual(
self.import_command.url_map[
'http://some.blog/2008/07/arzt-und-pfusch-s-i-c-k/'],
- 'http://some.blog/posts/200807arzt-und-pfusch-s-i-c-k.html')
+ 'http://some.blog/posts/2008/07/arzt-und-pfusch-s-i-c-k.html')
self.assertEqual(
self.import_command.url_map['http://some.blog/kontakt/'],
'http://some.blog/stories/kontakt.html')
diff --git a/tests/test_command_init.py b/tests/test_command_init.py
index 04e7d5f..4332213 100644
--- a/tests/test_command_init.py
+++ b/tests/test_command_init.py
@@ -25,12 +25,12 @@ class CommandInitCallTest(unittest.TestCase):
create_empty_site_patch = mock.patch(
'nikola.plugins.command.init.CommandInit.create_empty_site', self.create_empty_site)
- self.patches = [copy_sample_site_patch,
- create_configuration_patch, create_empty_site_patch]
+ self.patches = [copy_sample_site_patch, create_configuration_patch,
+ create_empty_site_patch]
for patch in self.patches:
patch.start()
- self.init_commad = nikola.plugins.command.init.CommandInit()
+ self.init_command = nikola.plugins.command.init.CommandInit()
def tearDown(self):
for patch in self.patches:
@@ -43,21 +43,21 @@ class CommandInitCallTest(unittest.TestCase):
def test_init_default(self):
for arguments in (dict(options={'demo': True}, args=['destination']), {}):
- self.init_commad.execute(**arguments)
+ self.init_command.execute(**arguments)
self.assertTrue(self.create_configuration.called)
self.assertTrue(self.copy_sample_site.called)
self.assertFalse(self.create_empty_site.called)
def test_init_called_without_target(self):
- self.init_commad.execute()
+ self.init_command.execute()
self.assertFalse(self.create_configuration.called)
self.assertFalse(self.copy_sample_site.called)
self.assertFalse(self.create_empty_site.called)
def test_init_empty_dir(self):
- self.init_commad.execute(args=['destination'])
+ self.init_command.execute(args=['destination'])
self.assertTrue(self.create_configuration.called)
self.assertFalse(self.copy_sample_site.called)
diff --git a/tests/test_integration.py b/tests/test_integration.py
index 85d7892..44b28e9 100644
--- a/tests/test_integration.py
+++ b/tests/test_integration.py
@@ -220,6 +220,19 @@ class TranslationsPatternTest1(TranslatedBuildTest):
outf.write(data)
+class MissingDefaultLanguageTest(TranslatedBuildTest):
+ """Make sure posts only in secondary languages work."""
+
+ @classmethod
+ def fill_site(self):
+ super(MissingDefaultLanguageTest, self).fill_site()
+ os.unlink(os.path.join(self.target_dir, "stories", "1.txt"))
+
+ def test_translated_titles(self):
+ """Do not test titles as we just removed the translation"""
+ pass
+
+
class TranslationsPatternTest2(TranslatedBuildTest):
"""Check that the path_lang.ext TRANSLATIONS_PATTERN works too"""
@@ -292,6 +305,54 @@ class TestCheck(DemoBuildTest):
self.assertEqual(e.code, 0)
+class TestCheckAbsoluteSubFolder(TestCheck):
+ """Validate links in a site which is:
+
+ * built in URL_TYPE="absolute"
+ * deployable to a subfolder (BASE_URL="http://getnikola.com/foo/")
+ """
+
+ @classmethod
+ def patch_site(self):
+ conf_path = os.path.join(self.target_dir, "conf.py")
+ with codecs.open(conf_path, "rb", "utf-8") as inf:
+ data = inf.read()
+ data = data.replace('SITE_URL = "http://getnikola.com/"',
+ 'SITE_URL = "http://getnikola.com/foo/"')
+ data = data.replace("# URL_TYPE = 'rel_path'",
+ "URL_TYPE = 'absolute'")
+ with codecs.open(conf_path, "wb+", "utf8") as outf:
+ outf.write(data)
+ outf.flush()
+
+ def test_index_in_sitemap(self):
+ """Test that the correct path is in sitemap, and not the wrong one."""
+ sitemap_path = os.path.join(self.target_dir, "output", "sitemap.xml")
+ sitemap_data = codecs.open(sitemap_path, "r", "utf8").read()
+ self.assertTrue('<loc>http://getnikola.com/foo/index.html</loc>' in sitemap_data)
+
+
+class TestCheckFullPathSubFolder(TestCheckAbsoluteSubFolder):
+ """Validate links in a site which is:
+
+ * built in URL_TYPE="full_path"
+ * deployable to a subfolder (BASE_URL="http://getnikola.com/foo/")
+ """
+
+ @classmethod
+ def patch_site(self):
+ conf_path = os.path.join(self.target_dir, "conf.py")
+ with codecs.open(conf_path, "rb", "utf-8") as inf:
+ data = inf.read()
+ data = data.replace('SITE_URL = "http://getnikola.com/"',
+ 'SITE_URL = "http://getnikola.com/foo/"')
+ data = data.replace("# URL_TYPE = 'rel_path'",
+ "URL_TYPE = 'full_path'")
+ with codecs.open(conf_path, "wb+", "utf8") as outf:
+ outf.write(data)
+ outf.flush()
+
+
class TestCheckFailure(DemoBuildTest):
"""The demo build should pass 'nikola check'"""
diff --git a/tests/test_rss_feeds.py b/tests/test_rss_feeds.py
index d1404d8..c43b92b 100644
--- a/tests/test_rss_feeds.py
+++ b/tests/test_rss_feeds.py
@@ -63,7 +63,7 @@ class RSSFeedTest(unittest.TestCase):
opener_mock = mock.mock_open()
- with mock.patch('nikola.nikola.utils.codecs.open', opener_mock, create=True):
+ with mock.patch('nikola.nikola.codecs.open', opener_mock, create=True):
nikola.nikola.utils.generic_rss_renderer('en',
"blog_title",
self.blog_url,
diff --git a/tests/test_utils.py b/tests/test_utils.py
index 3e66157..5aeba19 100644
--- a/tests/test_utils.py
+++ b/tests/test_utils.py
@@ -147,21 +147,21 @@ class GetMetaTest(unittest.TestCase):
class HeaderDemotionTest(unittest.TestCase):
def demote_by_zero(self):
input_str = '''\
-<h1>header 1</h1>
-<h2>header 2</h2>
-<h3>header 3</h3>
-<h4>header 4</h4>
-<h5>header 5</h5>
-<h6>header 6</h6>
-'''
+ <h1>header 1</h1>
+ <h2>header 2</h2>
+ <h3>header 3</h3>
+ <h4>header 4</h4>
+ <h5>header 5</h5>
+ <h6>header 6</h6>
+ '''
expected_output = '''\
-<h1>header 1</h1>
-<h2>header 2</h2>
-<h3>header 3</h3>
-<h4>header 4</h4>
-<h5>header 5</h5>
-<h6>header 6</h6>
-'''
+ <h1>header 1</h1>
+ <h2>header 2</h2>
+ <h3>header 3</h3>
+ <h4>header 4</h4>
+ <h5>header 5</h5>
+ <h6>header 6</h6>
+ '''
doc = lxml.html.fromstring(input_str)
outdoc = lxml.html.fromstring(expected_output)
demote_headers(doc, 0)
@@ -169,21 +169,21 @@ class HeaderDemotionTest(unittest.TestCase):
def demote_by_one(self):
input_str = '''\
-<h1>header 1</h1>
-<h2>header 2</h2>
-<h3>header 3</h3>
-<h4>header 4</h4>
-<h5>header 5</h5>
-<h6>header 6</h6>
-'''
+ <h1>header 1</h1>
+ <h2>header 2</h2>
+ <h3>header 3</h3>
+ <h4>header 4</h4>
+ <h5>header 5</h5>
+ <h6>header 6</h6>
+ '''
expected_output = '''\
-<h2>header 1</h2>
-<h3>header 2</h3>
-<h4>header 3</h4>
-<h5>header 4</h5>
-<h6>header 5</h6>
-<h6>header 6</h6>
-'''
+ <h2>header 1</h2>
+ <h3>header 2</h3>
+ <h4>header 3</h4>
+ <h5>header 4</h5>
+ <h6>header 5</h6>
+ <h6>header 6</h6>
+ '''
doc = lxml.html.fromstring(input_str)
outdoc = lxml.html.fromstring(expected_output)
demote_headers(doc, 1)
@@ -191,21 +191,21 @@ class HeaderDemotionTest(unittest.TestCase):
def demote_by_two(self):
input_str = '''\
-<h1>header 1</h1>
-<h2>header 2</h2>
-<h3>header 3</h3>
-<h4>header 4</h4>
-<h5>header 5</h5>
-<h6>header 6</h6>
-'''
+ <h1>header 1</h1>
+ <h2>header 2</h2>
+ <h3>header 3</h3>
+ <h4>header 4</h4>
+ <h5>header 5</h5>
+ <h6>header 6</h6>
+ '''
expected_output = '''\
-<h3>header 1</h3>
-<h4>header 2</h4>
-<h5>header 3</h5>
-<h6>header 4</h6>
-<h6>header 5</h6>
-<h6>header 6</h6>
-'''
+ <h3>header 1</h3>
+ <h4>header 2</h4>
+ <h5>header 3</h5>
+ <h6>header 4</h6>
+ <h6>header 5</h6>
+ <h6>header 6</h6>
+ '''
doc = lxml.html.fromstring(input_str)
outdoc = lxml.html.fromstring(expected_output)
demote_headers(doc, 2)
@@ -213,21 +213,21 @@ class HeaderDemotionTest(unittest.TestCase):
def demote_by_minus_one(self):
input_str = '''\
-<h1>header 1</h1>
-<h2>header 2</h2>
-<h3>header 3</h3>
-<h4>header 4</h4>
-<h5>header 5</h5>
-<h6>header 6</h6>
-'''
+ <h1>header 1</h1>
+ <h2>header 2</h2>
+ <h3>header 3</h3>
+ <h4>header 4</h4>
+ <h5>header 5</h5>
+ <h6>header 6</h6>
+ '''
expected_output = '''\
-<h1>header 1</h1>
-<h1>header 2</h1>
-<h2>header 3</h2>
-<h3>header 4</h3>
-<h4>header 5</h4>
-<h5>header 6</h5>
-'''
+ <h1>header 1</h1>
+ <h1>header 2</h1>
+ <h2>header 3</h2>
+ <h3>header 4</h3>
+ <h4>header 5</h4>
+ <h5>header 6</h5>
+ '''
doc = lxml.html.fromstring(input_str)
outdoc = lxml.html.fromstring(expected_output)
demote_headers(doc, -1)
diff --git a/tests/wordpress_export_example.xml b/tests/wordpress_export_example.xml
index 5fd0a90..e2401f7 100644
--- a/tests/wordpress_export_example.xml
+++ b/tests/wordpress_export_example.xml
@@ -262,6 +262,37 @@ A listing with another listing inside.
</wp:postmeta>
</item>
+ <item>
+ <title>NoirsEtPleinsDeLumière</title>
+ <link>http://some.blog/2011/04/noirs-et-pourtant-pleins-de-lumiere/noirsetpleinsdelumiere/#main</link>
+ <pubDate>Tue, 12 Apr 2011 21:56:05 +0000</pubDate>
+ <dc:creator><![CDATA[tibonihoo]]></dc:creator>
+ <guid isPermaLink="false">http://some.blog/wp-content/uploads/2011/04/NoirsEtPleinsDeLumière.jpg</guid>
+ <description></description>
+ <content:encoded><![CDATA[]]></content:encoded>
+ <excerpt:encoded><![CDATA[]]></excerpt:encoded>
+ <wp:post_id>724</wp:post_id>
+ <wp:post_date>2011-04-12 23:56:05</wp:post_date>
+ <wp:post_date_gmt>2011-04-12 21:56:05</wp:post_date_gmt>
+ <wp:comment_status>open</wp:comment_status>
+ <wp:ping_status>closed</wp:ping_status>
+ <wp:post_name>noirsetpleinsdelumiere</wp:post_name>
+ <wp:status>inherit</wp:status>
+ <wp:post_parent>723</wp:post_parent>
+ <wp:menu_order>0</wp:menu_order>
+ <wp:post_type>attachment</wp:post_type>
+ <wp:post_password></wp:post_password>
+ <wp:is_sticky>0</wp:is_sticky>
+ <wp:attachment_url>http://some.blog/wp-content/uploads/2011/04/NoirsEtPleinsDeLumière.jpg</wp:attachment_url>
+ <wp:postmeta>
+ <wp:meta_key>_wp_attachment_metadata</wp:meta_key>
+ <wp:meta_value><![CDATA[a:6:{s:5:"width";s:3:"533";s:6:"height";s:3:"800";s:14:"hwstring_small";s:22:"height='96' width='63'";s:4:"file";s:35:"2011/04/NoirsEtPleinsDeLumière.jpg";s:5:"sizes";a:2:{s:9:"thumbnail";a:3:{s:4:"file";s:35:"NoirsEtPleinsDeLumière-150x150.jpg";s:5:"width";s:3:"150";s:6:"height";s:3:"150";}s:6:"medium";a:3:{s:4:"file";s:35:"NoirsEtPleinsDeLumière-199x300.jpg";s:5:"width";s:3:"199";s:6:"height";s:3:"300";}}s:10:"image_meta";a:10:{s:8:"aperture";s:1:"5";s:6:"credit";s:0:"";s:6:"camera";s:13:"Canon EOS 40D";s:7:"caption";s:0:"";s:17:"created_timestamp";s:1:"0";s:9:"copyright";s:0:"";s:12:"focal_length";s:3:"100";s:3:"iso";s:3:"200";s:13:"shutter_speed";s:9:"-42949672";s:5:"title";s:0:"";}}]]></wp:meta_value>
+ </wp:postmeta>
+ <wp:postmeta>
+ <wp:meta_key>_wp_attached_file</wp:meta_key>
+ <wp:meta_value><![CDATA[2011/04/NoirsEtPleinsDeLumière.jpg]]></wp:meta_value>
+ </wp:postmeta>
+ </item>
<item>
<title>Image Link Rewriting</title>
<link>http://some.blog/2012/12/wintermodus/</link>
diff --git a/translations/nikola.messages/bg.po b/translations/nikola.messages/bg.po
index bd5bb65..631c86e 100644
--- a/translations/nikola.messages/bg.po
+++ b/translations/nikola.messages/bg.po
@@ -11,7 +11,7 @@ msgstr ""
"Project-Id-Version: Nikola\n"
"Report-Msgid-Bugs-To: ralsina@netmanagers.com.ar\n"
"POT-Creation-Date: 2013-03-15 12:24-0300\n"
-"PO-Revision-Date: 2014-01-22 13:11+0000\n"
+"PO-Revision-Date: 2014-02-09 16:56+0000\n"
"Last-Translator: Kwpolska <kwpolska@gmail.com>\n"
"Language-Team: Bulgarian (http://www.transifex.com/projects/p/nikola/language/bg/)\n"
"MIME-Version: 1.0\n"
@@ -84,3 +84,9 @@ msgstr "Публиковано:"
msgid "Posts for {month} {year}"
msgstr "Публикации за {month} {year}"
+
+msgid "Nothing found."
+msgstr ""
+
+msgid "No posts found."
+msgstr ""
diff --git a/translations/nikola.messages/ca.po b/translations/nikola.messages/ca.po
index 1e8db82..bbcefd0 100644
--- a/translations/nikola.messages/ca.po
+++ b/translations/nikola.messages/ca.po
@@ -11,7 +11,7 @@ msgstr ""
"Project-Id-Version: Nikola\n"
"Report-Msgid-Bugs-To: ralsina@netmanagers.com.ar\n"
"POT-Creation-Date: 2013-03-15 12:24-0300\n"
-"PO-Revision-Date: 2014-01-22 13:11+0000\n"
+"PO-Revision-Date: 2014-02-09 16:56+0000\n"
"Last-Translator: Kwpolska <kwpolska@gmail.com>\n"
"Language-Team: Catalan (http://www.transifex.com/projects/p/nikola/language/ca/)\n"
"MIME-Version: 1.0\n"
@@ -84,3 +84,9 @@ msgstr "Publicat:"
msgid "Posts for {month} {year}"
msgstr ""
+
+msgid "Nothing found."
+msgstr ""
+
+msgid "No posts found."
+msgstr ""
diff --git a/translations/nikola.messages/cs.po b/translations/nikola.messages/cs.po
index b920038..d55d197 100644
--- a/translations/nikola.messages/cs.po
+++ b/translations/nikola.messages/cs.po
@@ -10,7 +10,7 @@ msgstr ""
"Project-Id-Version: Nikola\n"
"Report-Msgid-Bugs-To: ralsina@netmanagers.com.ar\n"
"POT-Creation-Date: 2013-03-15 12:24-0300\n"
-"PO-Revision-Date: 2014-01-22 13:09+0000\n"
+"PO-Revision-Date: 2014-02-09 16:56+0000\n"
"Last-Translator: Kwpolska <kwpolska@gmail.com>\n"
"Language-Team: Czech (http://www.transifex.com/projects/p/nikola/language/cs/)\n"
"MIME-Version: 1.0\n"
@@ -83,3 +83,9 @@ msgstr "Zveřejněno:"
msgid "Posts for {month} {year}"
msgstr "Příspěvky v {month} {year}"
+
+msgid "Nothing found."
+msgstr ""
+
+msgid "No posts found."
+msgstr ""
diff --git a/translations/nikola.messages/de.po b/translations/nikola.messages/de.po
index fdbb033..dde07a9 100644
--- a/translations/nikola.messages/de.po
+++ b/translations/nikola.messages/de.po
@@ -4,7 +4,7 @@
#
# Translators:
# Translators:
-# Kwpolska <kwpolska@gmail.com>, 2013
+# Kwpolska <kwpolska@gmail.com>, 2013-2014
# Kwpolska <kwpolska@gmail.com>, 2013
# Kwpolska <kwpolska@gmail.com>, 2013
# Niko <wenso@gmx.de>, 2013
@@ -18,7 +18,7 @@ msgstr ""
"Project-Id-Version: Nikola\n"
"Report-Msgid-Bugs-To: ralsina@netmanagers.com.ar\n"
"POT-Creation-Date: 2013-03-15 12:24-0300\n"
-"PO-Revision-Date: 2014-01-22 13:11+0000\n"
+"PO-Revision-Date: 2014-02-09 16:57+0000\n"
"Last-Translator: Kwpolska <kwpolska@gmail.com>\n"
"Language-Team: German (http://www.transifex.com/projects/p/nikola/language/de/)\n"
"MIME-Version: 1.0\n"
@@ -91,3 +91,9 @@ msgstr "Veröffentlicht:"
msgid "Posts for {month} {year}"
msgstr "Einträge aus {month} {year}"
+
+msgid "Nothing found."
+msgstr "Nichts gefunden."
+
+msgid "No posts found."
+msgstr "Keine einträge gefunden."
diff --git a/translations/nikola.messages/el.po b/translations/nikola.messages/el.po
index 549a76f..6a4f393 100644
--- a/translations/nikola.messages/el.po
+++ b/translations/nikola.messages/el.po
@@ -13,7 +13,7 @@ msgstr ""
"Project-Id-Version: Nikola\n"
"Report-Msgid-Bugs-To: ralsina@netmanagers.com.ar\n"
"POT-Creation-Date: 2013-03-15 12:24-0300\n"
-"PO-Revision-Date: 2014-01-22 13:11+0000\n"
+"PO-Revision-Date: 2014-02-09 16:56+0000\n"
"Last-Translator: Kwpolska <kwpolska@gmail.com>\n"
"Language-Team: Greek (http://www.transifex.com/projects/p/nikola/language/el/)\n"
"MIME-Version: 1.0\n"
@@ -86,3 +86,9 @@ msgstr "Αναρτήθηκε:"
msgid "Posts for {month} {year}"
msgstr "Αναρτήσεις για τον {month} του {year}"
+
+msgid "Nothing found."
+msgstr ""
+
+msgid "No posts found."
+msgstr ""
diff --git a/translations/nikola.messages/en.po b/translations/nikola.messages/en.po
index be110d5..dd51ce3 100644
--- a/translations/nikola.messages/en.po
+++ b/translations/nikola.messages/en.po
@@ -80,3 +80,9 @@ msgstr "Posted:"
msgid "Posts for {month} {year}"
msgstr "Posts for {month} {year}"
+
+msgid "Nothing found."
+msgstr "Nothing found."
+
+msgid "No posts found."
+msgstr "No posts found."
diff --git a/translations/nikola.messages/eo.po b/translations/nikola.messages/eo.po
index e3ee650..ee267de 100644
--- a/translations/nikola.messages/eo.po
+++ b/translations/nikola.messages/eo.po
@@ -11,7 +11,7 @@ msgstr ""
"Project-Id-Version: Nikola\n"
"Report-Msgid-Bugs-To: ralsina@netmanagers.com.ar\n"
"POT-Creation-Date: 2013-03-15 12:24-0300\n"
-"PO-Revision-Date: 2014-01-22 13:11+0000\n"
+"PO-Revision-Date: 2014-02-09 16:56+0000\n"
"Last-Translator: Kwpolska <kwpolska@gmail.com>\n"
"Language-Team: Esperanto (http://www.transifex.com/projects/p/nikola/language/eo/)\n"
"MIME-Version: 1.0\n"
@@ -84,3 +84,9 @@ msgstr "Skribita:"
msgid "Posts for {month} {year}"
msgstr "Artikoloj skribitaj en {month} {year}"
+
+msgid "Nothing found."
+msgstr ""
+
+msgid "No posts found."
+msgstr ""
diff --git a/translations/nikola.messages/es.po b/translations/nikola.messages/es.po
index c25a23f..3650dd5 100644
--- a/translations/nikola.messages/es.po
+++ b/translations/nikola.messages/es.po
@@ -11,7 +11,7 @@ msgstr ""
"Project-Id-Version: Nikola\n"
"Report-Msgid-Bugs-To: ralsina@netmanagers.com.ar\n"
"POT-Creation-Date: 2013-03-15 12:24-0300\n"
-"PO-Revision-Date: 2014-01-22 13:13+0000\n"
+"PO-Revision-Date: 2014-02-09 16:56+0000\n"
"Last-Translator: Kwpolska <kwpolska@gmail.com>\n"
"Language-Team: Spanish (http://www.transifex.com/projects/p/nikola/language/es/)\n"
"MIME-Version: 1.0\n"
@@ -84,3 +84,9 @@ msgstr "Publicado:"
msgid "Posts for {month} {year}"
msgstr "Posts de {month} {year}"
+
+msgid "Nothing found."
+msgstr ""
+
+msgid "No posts found."
+msgstr ""
diff --git a/translations/nikola.messages/et.po b/translations/nikola.messages/et.po
index d4301ba..5f5faa5 100644
--- a/translations/nikola.messages/et.po
+++ b/translations/nikola.messages/et.po
@@ -12,7 +12,7 @@ msgstr ""
"Project-Id-Version: Nikola\n"
"Report-Msgid-Bugs-To: ralsina@netmanagers.com.ar\n"
"POT-Creation-Date: 2013-03-15 12:24-0300\n"
-"PO-Revision-Date: 2014-01-22 13:12+0000\n"
+"PO-Revision-Date: 2014-02-09 16:56+0000\n"
"Last-Translator: Kwpolska <kwpolska@gmail.com>\n"
"Language-Team: Estonian (http://www.transifex.com/projects/p/nikola/language/et/)\n"
"MIME-Version: 1.0\n"
@@ -85,3 +85,9 @@ msgstr "Postitatud:"
msgid "Posts for {month} {year}"
msgstr "Postitused {year} aasta kuust {month} "
+
+msgid "Nothing found."
+msgstr ""
+
+msgid "No posts found."
+msgstr ""
diff --git a/translations/nikola.messages/eu.po b/translations/nikola.messages/eu.po
index 3447ee5..c6803b4 100644
--- a/translations/nikola.messages/eu.po
+++ b/translations/nikola.messages/eu.po
@@ -11,7 +11,7 @@ msgstr ""
"Project-Id-Version: Nikola\n"
"Report-Msgid-Bugs-To: ralsina@netmanagers.com.ar\n"
"POT-Creation-Date: 2013-03-15 12:24-0300\n"
-"PO-Revision-Date: 2014-01-22 13:12+0000\n"
+"PO-Revision-Date: 2014-02-09 16:56+0000\n"
"Last-Translator: Kwpolska <kwpolska@gmail.com>\n"
"Language-Team: Basque (http://www.transifex.com/projects/p/nikola/language/eu/)\n"
"MIME-Version: 1.0\n"
@@ -84,3 +84,9 @@ msgstr "Argitaratuta:"
msgid "Posts for {month} {year}"
msgstr "{year}ko {month}ren postak"
+
+msgid "Nothing found."
+msgstr ""
+
+msgid "No posts found."
+msgstr ""
diff --git a/translations/nikola.messages/fa.po b/translations/nikola.messages/fa.po
index 87a3d26..a6c4e33 100644
--- a/translations/nikola.messages/fa.po
+++ b/translations/nikola.messages/fa.po
@@ -13,7 +13,7 @@ msgstr ""
"Project-Id-Version: Nikola\n"
"Report-Msgid-Bugs-To: ralsina@netmanagers.com.ar\n"
"POT-Creation-Date: 2013-03-15 12:24-0300\n"
-"PO-Revision-Date: 2014-01-22 13:12+0000\n"
+"PO-Revision-Date: 2014-02-09 16:56+0000\n"
"Last-Translator: Kwpolska <kwpolska@gmail.com>\n"
"Language-Team: Persian (http://www.transifex.com/projects/p/nikola/language/fa/)\n"
"MIME-Version: 1.0\n"
@@ -86,3 +86,9 @@ msgstr "ارسال شده:"
msgid "Posts for {month} {year}"
msgstr "ارسال برای {month} {year}"
+
+msgid "Nothing found."
+msgstr ""
+
+msgid "No posts found."
+msgstr ""
diff --git a/translations/nikola.messages/fi.po b/translations/nikola.messages/fi.po
index 13e840e..6ce903f 100644
--- a/translations/nikola.messages/fi.po
+++ b/translations/nikola.messages/fi.po
@@ -11,7 +11,7 @@ msgstr ""
"Project-Id-Version: Nikola\n"
"Report-Msgid-Bugs-To: ralsina@netmanagers.com.ar\n"
"POT-Creation-Date: 2013-03-15 12:24-0300\n"
-"PO-Revision-Date: 2014-01-22 13:12+0000\n"
+"PO-Revision-Date: 2014-02-09 16:56+0000\n"
"Last-Translator: Kwpolska <kwpolska@gmail.com>\n"
"Language-Team: Finnish (http://www.transifex.com/projects/p/nikola/language/fi/)\n"
"MIME-Version: 1.0\n"
@@ -84,3 +84,9 @@ msgstr "Postattu:"
msgid "Posts for {month} {year}"
msgstr "Postauksia ajalle {month} {year}"
+
+msgid "Nothing found."
+msgstr ""
+
+msgid "No posts found."
+msgstr ""
diff --git a/translations/nikola.messages/fr.po b/translations/nikola.messages/fr.po
index 118f410..8b68987 100644
--- a/translations/nikola.messages/fr.po
+++ b/translations/nikola.messages/fr.po
@@ -16,7 +16,7 @@ msgstr ""
"Project-Id-Version: Nikola\n"
"Report-Msgid-Bugs-To: ralsina@netmanagers.com.ar\n"
"POT-Creation-Date: 2013-03-15 12:24-0300\n"
-"PO-Revision-Date: 2014-01-22 13:12+0000\n"
+"PO-Revision-Date: 2014-02-09 16:56+0000\n"
"Last-Translator: Kwpolska <kwpolska@gmail.com>\n"
"Language-Team: French (http://www.transifex.com/projects/p/nikola/language/fr/)\n"
"MIME-Version: 1.0\n"
@@ -89,3 +89,9 @@ msgstr "Publié:"
msgid "Posts for {month} {year}"
msgstr "Articles de {month} {year}"
+
+msgid "Nothing found."
+msgstr ""
+
+msgid "No posts found."
+msgstr ""
diff --git a/translations/nikola.messages/hi.po b/translations/nikola.messages/hi.po
new file mode 100644
index 0000000..d464f99
--- /dev/null
+++ b/translations/nikola.messages/hi.po
@@ -0,0 +1,91 @@
+# Messages in Nikola
+# Copyright (C) 2012-2013
+# This file is distributed under the same license as the Nikola package.
+#
+# Translators:
+# Translators:
+# seanpue <a@seanpue.com>, 2014
+msgid ""
+msgstr ""
+"Project-Id-Version: Nikola\n"
+"Report-Msgid-Bugs-To: ralsina@netmanagers.com.ar\n"
+"POT-Creation-Date: 2013-03-15 12:24-0300\n"
+"PO-Revision-Date: 2014-02-09 16:56+0000\n"
+"Last-Translator: Kwpolska <kwpolska@gmail.com>\n"
+"Language-Team: Hindi (http://www.transifex.com/projects/p/nikola/language/hi/)\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Language: hi\n"
+"Plural-Forms: nplurals=2; plural=(n != 1);\n"
+
+msgid "More posts about %s"
+msgstr "%s के बारे में अौर पोस्टें"
+
+msgid "LANGUAGE"
+msgstr "हिन्दी"
+
+msgid "Tags"
+msgstr "टैग्स"
+
+msgid "Categories"
+msgstr "श्रेणियाँ"
+
+msgid "Tags and Categories"
+msgstr "टैग्स और श्रेणियाँ"
+
+msgid "Read more"
+msgstr "और पढ़िए"
+
+msgid "Posts about %s"
+msgstr "%s के बारे में पोस्टें"
+
+msgid "Next post"
+msgstr "अगली पोस्ट"
+
+msgid "old posts, page %d"
+msgstr "पुरानी पोस्टें, पृष्‍ठ %d"
+
+msgid "page %d"
+msgstr "पृष्‍ठ %d"
+
+msgid "Source"
+msgstr "सोर्स"
+
+#. Here your translation should not say English but the name of your language
+#. instead
+msgid "Read in English"
+msgstr "हिन्दी में पढ़िए"
+
+msgid "Posts for year %s"
+msgstr "साल %s की पोस्टें"
+
+msgid "Newer posts"
+msgstr "नई पोस्टें"
+
+msgid "Previous post"
+msgstr "पिछली पोस्ट"
+
+msgid "Also available in:"
+msgstr "उपलब्ध भाषाएँ:"
+
+msgid "Original site"
+msgstr "असली साइट"
+
+msgid "Older posts"
+msgstr "पुरानी पोस्टें"
+
+msgid "Archive"
+msgstr "आर्काइव"
+
+msgid "Posted:"
+msgstr "पोस्टेड:"
+
+msgid "Posts for {month} {year}"
+msgstr "{month} {year} की पोस्टें"
+
+msgid "Nothing found."
+msgstr ""
+
+msgid "No posts found."
+msgstr ""
diff --git a/translations/nikola.messages/hr.po b/translations/nikola.messages/hr.po
index fba7ddc..c2bae75 100644
--- a/translations/nikola.messages/hr.po
+++ b/translations/nikola.messages/hr.po
@@ -11,7 +11,7 @@ msgstr ""
"Project-Id-Version: Nikola\n"
"Report-Msgid-Bugs-To: ralsina@netmanagers.com.ar\n"
"POT-Creation-Date: 2013-03-15 12:24-0300\n"
-"PO-Revision-Date: 2014-01-22 13:11+0000\n"
+"PO-Revision-Date: 2014-02-09 16:56+0000\n"
"Last-Translator: Kwpolska <kwpolska@gmail.com>\n"
"Language-Team: Croatian (http://www.transifex.com/projects/p/nikola/language/hr/)\n"
"MIME-Version: 1.0\n"
@@ -84,3 +84,9 @@ msgstr "Objavljeno:"
msgid "Posts for {month} {year}"
msgstr "Postovi za {month} {year}"
+
+msgid "Nothing found."
+msgstr ""
+
+msgid "No posts found."
+msgstr ""
diff --git a/translations/nikola.messages/it.po b/translations/nikola.messages/it.po
index 27f1e33..1c03e0b 100644
--- a/translations/nikola.messages/it.po
+++ b/translations/nikola.messages/it.po
@@ -13,7 +13,7 @@ msgstr ""
"Project-Id-Version: Nikola\n"
"Report-Msgid-Bugs-To: ralsina@netmanagers.com.ar\n"
"POT-Creation-Date: 2013-03-15 12:24-0300\n"
-"PO-Revision-Date: 2014-01-22 13:11+0000\n"
+"PO-Revision-Date: 2014-02-09 16:56+0000\n"
"Last-Translator: Kwpolska <kwpolska@gmail.com>\n"
"Language-Team: Italian (http://www.transifex.com/projects/p/nikola/language/it/)\n"
"MIME-Version: 1.0\n"
@@ -86,3 +86,9 @@ msgstr "Pubblicato:"
msgid "Posts for {month} {year}"
msgstr "Articoli per {month} {year}"
+
+msgid "Nothing found."
+msgstr ""
+
+msgid "No posts found."
+msgstr ""
diff --git a/translations/nikola.messages/ja.po b/translations/nikola.messages/ja.po
index 0bbeda3..7e0eaa7 100644
--- a/translations/nikola.messages/ja.po
+++ b/translations/nikola.messages/ja.po
@@ -15,7 +15,7 @@ msgstr ""
"Project-Id-Version: Nikola\n"
"Report-Msgid-Bugs-To: ralsina@netmanagers.com.ar\n"
"POT-Creation-Date: 2013-03-15 12:24-0300\n"
-"PO-Revision-Date: 2014-01-22 13:12+0000\n"
+"PO-Revision-Date: 2014-02-09 16:56+0000\n"
"Last-Translator: Kwpolska <kwpolska@gmail.com>\n"
"Language-Team: Japanese (http://www.transifex.com/projects/p/nikola/language/ja/)\n"
"MIME-Version: 1.0\n"
@@ -88,3 +88,9 @@ msgstr "投稿日時:"
msgid "Posts for {month} {year}"
msgstr "{year}年{month}月の記事"
+
+msgid "Nothing found."
+msgstr ""
+
+msgid "No posts found."
+msgstr ""
diff --git a/translations/nikola.messages/nb.po b/translations/nikola.messages/nb.po
index 1446d53..8ac9ae4 100644
--- a/translations/nikola.messages/nb.po
+++ b/translations/nikola.messages/nb.po
@@ -10,7 +10,7 @@ msgstr ""
"Project-Id-Version: Nikola\n"
"Report-Msgid-Bugs-To: ralsina@netmanagers.com.ar\n"
"POT-Creation-Date: 2013-03-15 12:24-0300\n"
-"PO-Revision-Date: 2014-01-22 13:13+0000\n"
+"PO-Revision-Date: 2014-02-09 16:56+0000\n"
"Last-Translator: Kwpolska <kwpolska@gmail.com>\n"
"Language-Team: Norwegian Bokmål (http://www.transifex.com/projects/p/nikola/language/nb/)\n"
"MIME-Version: 1.0\n"
@@ -83,3 +83,9 @@ msgstr "Publisert:"
msgid "Posts for {month} {year}"
msgstr "Innlegg fra {month} {year}"
+
+msgid "Nothing found."
+msgstr ""
+
+msgid "No posts found."
+msgstr ""
diff --git a/translations/nikola.messages/nl.po b/translations/nikola.messages/nl.po
index 32c0faf..637dd5e 100644
--- a/translations/nikola.messages/nl.po
+++ b/translations/nikola.messages/nl.po
@@ -11,8 +11,8 @@ msgstr ""
"Project-Id-Version: Nikola\n"
"Report-Msgid-Bugs-To: ralsina@netmanagers.com.ar\n"
"POT-Creation-Date: 2013-03-15 12:24-0300\n"
-"PO-Revision-Date: 2014-01-22 10:30+0000\n"
-"Last-Translator: Joes <joes@staalkemade.net>\n"
+"PO-Revision-Date: 2014-02-09 16:56+0000\n"
+"Last-Translator: Kwpolska <kwpolska@gmail.com>\n"
"Language-Team: Dutch (http://www.transifex.com/projects/p/nikola/language/nl/)\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
@@ -84,3 +84,9 @@ msgstr "Geplaatst:"
msgid "Posts for {month} {year}"
msgstr "Berichten voor {month} {year}"
+
+msgid "Nothing found."
+msgstr ""
+
+msgid "No posts found."
+msgstr ""
diff --git a/translations/nikola.messages/pl.po b/translations/nikola.messages/pl.po
index 02048dc..a9ee5d3 100644
--- a/translations/nikola.messages/pl.po
+++ b/translations/nikola.messages/pl.po
@@ -4,7 +4,7 @@
#
# Translators:
# Translators:
-# Kwpolska <kwpolska@gmail.com>, 2013
+# Kwpolska <kwpolska@gmail.com>, 2013-2014
# Kwpolska <kwpolska@gmail.com>, 2013
# Kwpolska <kwpolska@gmail.com>, 2013
# ralsina <ralsina@netmanagers.com.ar>, 2013
@@ -14,7 +14,7 @@ msgstr ""
"Project-Id-Version: Nikola\n"
"Report-Msgid-Bugs-To: ralsina@netmanagers.com.ar\n"
"POT-Creation-Date: 2013-03-15 12:24-0300\n"
-"PO-Revision-Date: 2014-01-22 13:13+0000\n"
+"PO-Revision-Date: 2014-02-09 16:56+0000\n"
"Last-Translator: Kwpolska <kwpolska@gmail.com>\n"
"Language-Team: Polish (http://www.transifex.com/projects/p/nikola/language/pl/)\n"
"MIME-Version: 1.0\n"
@@ -87,3 +87,9 @@ msgstr "Opublikowano:"
msgid "Posts for {month} {year}"
msgstr "Posty z {month} {year}"
+
+msgid "Nothing found."
+msgstr "Nic nie znaleziono."
+
+msgid "No posts found."
+msgstr "Nie znaleziono żadnych postów."
diff --git a/translations/nikola.messages/pt_BR.po b/translations/nikola.messages/pt_BR.po
index c741862..79239d9 100644
--- a/translations/nikola.messages/pt_BR.po
+++ b/translations/nikola.messages/pt_BR.po
@@ -15,7 +15,7 @@ msgstr ""
"Project-Id-Version: Nikola\n"
"Report-Msgid-Bugs-To: ralsina@netmanagers.com.ar\n"
"POT-Creation-Date: 2013-03-15 12:24-0300\n"
-"PO-Revision-Date: 2014-01-22 13:11+0000\n"
+"PO-Revision-Date: 2014-02-09 16:56+0000\n"
"Last-Translator: Kwpolska <kwpolska@gmail.com>\n"
"Language-Team: Portuguese (Brazil) (http://www.transifex.com/projects/p/nikola/language/pt_BR/)\n"
"MIME-Version: 1.0\n"
@@ -88,3 +88,9 @@ msgstr "Publicado:"
msgid "Posts for {month} {year}"
msgstr "Posts de {month} {year}"
+
+msgid "Nothing found."
+msgstr ""
+
+msgid "No posts found."
+msgstr ""
diff --git a/translations/nikola.messages/ru.po b/translations/nikola.messages/ru.po
index 1b8904f..b682307 100644
--- a/translations/nikola.messages/ru.po
+++ b/translations/nikola.messages/ru.po
@@ -13,7 +13,7 @@ msgstr ""
"Project-Id-Version: Nikola\n"
"Report-Msgid-Bugs-To: ralsina@netmanagers.com.ar\n"
"POT-Creation-Date: 2013-03-15 12:24-0300\n"
-"PO-Revision-Date: 2014-01-22 13:13+0000\n"
+"PO-Revision-Date: 2014-02-09 16:56+0000\n"
"Last-Translator: Kwpolska <kwpolska@gmail.com>\n"
"Language-Team: Russian (http://www.transifex.com/projects/p/nikola/language/ru/)\n"
"MIME-Version: 1.0\n"
@@ -86,3 +86,9 @@ msgstr "Опубликовано:"
msgid "Posts for {month} {year}"
msgstr "Записи за {month} {year}"
+
+msgid "Nothing found."
+msgstr ""
+
+msgid "No posts found."
+msgstr ""
diff --git a/translations/nikola.messages/sl.po b/translations/nikola.messages/sl.po
index 2e78b4c..50a0a2f 100644
--- a/translations/nikola.messages/sl.po
+++ b/translations/nikola.messages/sl.po
@@ -11,8 +11,8 @@ msgstr ""
"Project-Id-Version: Nikola\n"
"Report-Msgid-Bugs-To: ralsina@netmanagers.com.ar\n"
"POT-Creation-Date: 2013-03-15 12:24-0300\n"
-"PO-Revision-Date: 2014-01-22 10:36+0000\n"
-"Last-Translator: Venčeslav Vezjak <vezjakv@gmail.com>\n"
+"PO-Revision-Date: 2014-02-09 16:56+0000\n"
+"Last-Translator: Kwpolska <kwpolska@gmail.com>\n"
"Language-Team: Slovenian (http://www.transifex.com/projects/p/nikola/language/sl/)\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
@@ -84,3 +84,9 @@ msgstr "Objavljeno:"
msgid "Posts for {month} {year}"
msgstr "Objave za {month} {year}"
+
+msgid "Nothing found."
+msgstr ""
+
+msgid "No posts found."
+msgstr ""
diff --git a/translations/nikola.messages/tr_TR.po b/translations/nikola.messages/tr_TR.po
index 22bece3..0afcbfa 100644
--- a/translations/nikola.messages/tr_TR.po
+++ b/translations/nikola.messages/tr_TR.po
@@ -12,7 +12,7 @@ msgstr ""
"Project-Id-Version: Nikola\n"
"Report-Msgid-Bugs-To: ralsina@netmanagers.com.ar\n"
"POT-Creation-Date: 2013-03-15 12:24-0300\n"
-"PO-Revision-Date: 2014-01-22 13:12+0000\n"
+"PO-Revision-Date: 2014-02-09 16:56+0000\n"
"Last-Translator: Kwpolska <kwpolska@gmail.com>\n"
"Language-Team: Turkish (Turkey) (http://www.transifex.com/projects/p/nikola/language/tr_TR/)\n"
"MIME-Version: 1.0\n"
@@ -85,3 +85,9 @@ msgstr "Yayın tarihi:"
msgid "Posts for {month} {year}"
msgstr "{month} {year} göre yazılar"
+
+msgid "Nothing found."
+msgstr ""
+
+msgid "No posts found."
+msgstr ""
diff --git a/translations/nikola.messages/ur.po b/translations/nikola.messages/ur.po
index c5ff9fd..b70478a 100644
--- a/translations/nikola.messages/ur.po
+++ b/translations/nikola.messages/ur.po
@@ -4,6 +4,7 @@
#
# Translators:
# Translators:
+# seanpue <a@seanpue.com>, 2014
# saadat, 2013
# saadat <nastaliq@gmail.com>, 2013
# saadat <nastaliq@gmail.com>, 2013
@@ -12,7 +13,7 @@ msgstr ""
"Project-Id-Version: Nikola\n"
"Report-Msgid-Bugs-To: ralsina@netmanagers.com.ar\n"
"POT-Creation-Date: 2013-03-15 12:24-0300\n"
-"PO-Revision-Date: 2014-01-22 13:13+0000\n"
+"PO-Revision-Date: 2014-02-09 16:56+0000\n"
"Last-Translator: Kwpolska <kwpolska@gmail.com>\n"
"Language-Team: Urdu (http://www.transifex.com/projects/p/nikola/language/ur/)\n"
"MIME-Version: 1.0\n"
@@ -49,7 +50,7 @@ msgid "old posts, page %d"
msgstr "پرانی تحاریر صفحہ %d"
msgid "page %d"
-msgstr ""
+msgstr "صفحہ %d"
msgid "Source"
msgstr "سورس"
@@ -85,3 +86,9 @@ msgstr "اشاعت:"
msgid "Posts for {month} {year}"
msgstr "{month} {year} کی تحاریر"
+
+msgid "Nothing found."
+msgstr ""
+
+msgid "No posts found."
+msgstr ""
diff --git a/translations/nikola.messages/zh_CN.po b/translations/nikola.messages/zh_CN.po
index c1e37f7..ed466d2 100644
--- a/translations/nikola.messages/zh_CN.po
+++ b/translations/nikola.messages/zh_CN.po
@@ -14,7 +14,7 @@ msgstr ""
"Project-Id-Version: Nikola\n"
"Report-Msgid-Bugs-To: ralsina@netmanagers.com.ar\n"
"POT-Creation-Date: 2013-03-15 12:24-0300\n"
-"PO-Revision-Date: 2014-01-22 13:13+0000\n"
+"PO-Revision-Date: 2014-02-09 16:56+0000\n"
"Last-Translator: Kwpolska <kwpolska@gmail.com>\n"
"Language-Team: Chinese (China) (http://www.transifex.com/projects/p/nikola/language/zh_CN/)\n"
"MIME-Version: 1.0\n"
@@ -87,3 +87,9 @@ msgstr "发表于:"
msgid "Posts for {month} {year}"
msgstr "{year}年{month}月文章"
+
+msgid "Nothing found."
+msgstr ""
+
+msgid "No posts found."
+msgstr ""