From 0c4dfdec5b55b6064dccc38bbfb0a7c0699c895a Mon Sep 17 00:00:00 2001 From: Agustin Henze Date: Thu, 30 May 2013 17:41:06 -0300 Subject: Imported Upstream version 5.4.4 --- .gitignore | 1 - .travis.yml | 4 +- .tx/config | 7 + CHANGES.txt | 99 +- README.md | 19 +- docs/creating-a-theme.txt | 16 +- docs/extending.txt | 4 +- docs/internals.txt | 131 ++ docs/manual.txt | 221 +- docs/theming.txt | 55 +- extra_plugins/README.txt | 62 - extra_plugins/command_planetoid.plugin | 9 - extra_plugins/command_planetoid/__init__.py | 248 --- extra_plugins/command_planetoid/index.tmpl | 26 - extra_plugins/command_planetoid/post.tmpl | 10 - extra_plugins/compile_ipynb.plugin | 10 - extra_plugins/compile_ipynb/README.txt | 35 - extra_plugins/compile_ipynb/__init__.py | 98 - extra_plugins/task_localsearch.plugin | 10 - extra_plugins/task_localsearch/MIT-LICENSE.txt | 20 - extra_plugins/task_localsearch/__init__.py | 94 - .../task_localsearch/files/assets/css/loader.gif | Bin 4178 -> 0 bytes .../task_localsearch/files/assets/css/search.gif | Bin 208 -> 0 bytes .../files/assets/css/tipuesearch.css | 182 -- .../files/assets/js/tipuesearch.js | 367 ---- .../files/assets/js/tipuesearch_set.js | 28 - extra_plugins/task_mustache.plugin | 10 - extra_plugins/task_mustache/__init__.py | 189 -- extra_plugins/task_mustache/mustache-template.html | 29 - extra_plugins/task_mustache/mustache.html | 36 - install_requirements.py | 35 - nikola/conf.py.in | 43 +- nikola/data/themes/default/assets/css/code.css | 62 - nikola/data/themes/default/assets/css/slides.css | 11 - nikola/data/themes/default/assets/css/theme.css | 11 + .../data/themes/default/assets/js/slides.jquery.js | 555 ----- nikola/data/themes/default/bundles | 8 +- nikola/data/themes/default/messages/messages_ca.py | 23 +- nikola/data/themes/default/messages/messages_de.py | 25 +- nikola/data/themes/default/messages/messages_el.py | 23 + nikola/data/themes/default/messages/messages_en.py | 21 +- nikola/data/themes/default/messages/messages_es.py | 23 +- nikola/data/themes/default/messages/messages_fr.py | 24 +- nikola/data/themes/default/messages/messages_it.py | 24 +- nikola/data/themes/default/messages/messages_ja.py | 23 + nikola/data/themes/default/messages/messages_pl.py | 25 +- .../data/themes/default/messages/messages_pt-br.py | 22 - .../data/themes/default/messages/messages_pt_br.py | 23 + nikola/data/themes/default/messages/messages_ru.py | 23 +- .../data/themes/default/messages/messages_zh-cn.py | 22 - .../data/themes/default/messages/messages_zh_cn.py | 23 + nikola/data/themes/default/templates/base.tmpl | 6 +- .../data/themes/default/templates/base_helper.tmpl | 14 +- .../themes/default/templates/disqus_helper.tmpl | 7 +- nikola/data/themes/default/templates/index.tmpl | 10 +- .../themes/default/templates/index_helper.tmpl | 4 +- .../data/themes/default/templates/list_post.tmpl | 2 +- nikola/data/themes/default/templates/post.tmpl | 12 +- .../data/themes/default/templates/post_helper.tmpl | 26 +- nikola/data/themes/default/templates/story.tmpl | 10 +- nikola/data/themes/default/templates/tag.tmpl | 27 +- nikola/data/themes/default/templates/tags.tmpl | 18 +- .../data/themes/jinja-default/templates/base.tmpl | 10 +- .../data/themes/jinja-default/templates/index.tmpl | 12 +- .../themes/jinja-default/templates/list_post.tmpl | 2 +- .../data/themes/jinja-default/templates/post.tmpl | 27 +- .../data/themes/jinja-default/templates/story.tmpl | 21 +- .../data/themes/jinja-default/templates/tag.tmpl | 2 +- .../data/themes/jinja-default/templates/tags.tmpl | 4 +- nikola/data/themes/monospace/assets/css/code.css | 62 - nikola/data/themes/monospace/bundles | 1 + nikola/data/themes/monospace/templates/base.tmpl | 6 +- .../themes/monospace/templates/base_helper.tmpl | 36 +- .../themes/monospace/templates/disqus_helper.tmpl | 7 +- nikola/data/themes/monospace/templates/index.tmpl | 12 +- .../themes/monospace/templates/index_helper.tmpl | 4 +- .../data/themes/monospace/templates/list_post.tmpl | 2 +- nikola/data/themes/monospace/templates/post.tmpl | 15 +- .../themes/monospace/templates/post_helper.tmpl | 42 +- nikola/data/themes/monospace/templates/story.tmpl | 10 +- nikola/data/themes/monospace/templates/tag.tmpl | 2 +- nikola/data/themes/orphan/assets/css/code.css | 1 - nikola/data/themes/orphan/templates/base.tmpl | 4 +- .../data/themes/orphan/templates/base_helper.tmpl | 36 +- .../themes/orphan/templates/disqus_helper.tmpl | 7 +- nikola/data/themes/orphan/templates/index.tmpl | 10 +- .../data/themes/orphan/templates/index_helper.tmpl | 4 +- nikola/data/themes/orphan/templates/list_post.tmpl | 2 +- nikola/data/themes/orphan/templates/post.tmpl | 12 +- .../data/themes/orphan/templates/post_helper.tmpl | 44 +- nikola/data/themes/orphan/templates/story.tmpl | 10 +- nikola/data/themes/orphan/templates/tag.tmpl | 2 +- nikola/data/themes/site-planetoid/README | 1 + nikola/data/themes/site-planetoid/engine | 1 + nikola/data/themes/site-planetoid/parent | 1 + .../themes/site-planetoid/templates/index.tmpl | 16 + .../data/themes/site-planetoid/templates/post.tmpl | 9 + .../themes/site-planetoid/templates/story.tmpl | 25 + nikola/data/themes/site/assets/css/theme.css | 14 + nikola/data/themes/site/templates/base.tmpl | 12 +- nikola/main.py | 7 +- nikola/nikola.py | 157 +- nikola/plugin_categories.py | 11 +- nikola/plugins/command_check.py | 21 +- nikola/plugins/command_console.py | 76 +- nikola/plugins/command_deploy.py | 26 +- nikola/plugins/command_import_blogger.py | 17 +- nikola/plugins/command_import_wordpress.py | 32 +- nikola/plugins/command_install_theme.py | 4 + nikola/plugins/command_new_post.py | 36 +- nikola/plugins/command_planetoid.plugin | 9 + nikola/plugins/command_planetoid/__init__.py | 287 +++ nikola/plugins/compile_bbcode.py | 16 +- nikola/plugins/compile_html.py | 14 +- nikola/plugins/compile_ipynb.plugin | 10 + nikola/plugins/compile_ipynb/README.txt | 35 + nikola/plugins/compile_ipynb/__init__.py | 100 + nikola/plugins/compile_markdown/__init__.py | 51 +- nikola/plugins/compile_markdown/mdx_gist.py | 189 ++ nikola/plugins/compile_markdown/mdx_nikola.py | 56 + nikola/plugins/compile_markdown/mdx_podcast.py | 87 + nikola/plugins/compile_misaka.plugin | 10 + nikola/plugins/compile_misaka/__init__.py | 82 + nikola/plugins/compile_rest/__init__.py | 81 +- nikola/plugins/compile_rest/dummy.py | 44 + nikola/plugins/compile_rest/gist_directive.py | 2 +- nikola/plugins/compile_rest/listing.py | 121 ++ .../compile_rest/pygments_code_block_directive.py | 424 ---- nikola/plugins/compile_rest/slides.py | 79 +- nikola/plugins/compile_rest/soundcloud.py | 62 +- nikola/plugins/compile_rest/vimeo.py | 114 +- nikola/plugins/compile_rest/youtube.py | 57 +- nikola/plugins/compile_textile.py | 14 +- nikola/plugins/compile_txt2tags.py | 14 +- nikola/plugins/compile_wiki.py | 12 +- nikola/plugins/task_archive.py | 66 +- nikola/plugins/task_copy_assets.py | 25 + nikola/plugins/task_create_bundles.py | 46 +- nikola/plugins/task_indexes.py | 35 +- nikola/plugins/task_localsearch.plugin | 10 + nikola/plugins/task_localsearch/MIT-LICENSE.txt | 20 + nikola/plugins/task_localsearch/__init__.py | 102 + .../files/assets/css/img/expand.png | Bin 0 -> 424 bytes .../task_localsearch/files/assets/css/img/link.png | Bin 0 -> 463 bytes .../files/assets/css/img/loader.gif | Bin 0 -> 4178 bytes .../files/assets/css/img/search.gif | Bin 0 -> 208 bytes .../files/assets/css/tipuesearch.css | 232 +++ .../files/assets/js/tipuesearch.js | 426 ++++ .../files/assets/js/tipuesearch_set.js | 28 + .../task_localsearch/files/tipue_search.html | 31 + nikola/plugins/task_mustache.plugin | 10 + nikola/plugins/task_mustache/__init__.py | 197 ++ .../plugins/task_mustache/mustache-template.html | 29 + nikola/plugins/task_mustache/mustache.html | 36 + nikola/plugins/task_redirect.py | 2 +- nikola/plugins/task_render_galleries.py | 29 +- nikola/plugins/task_render_listings.py | 12 +- nikola/plugins/task_render_pages.py | 5 + nikola/plugins/task_render_posts.py | 84 +- nikola/plugins/task_render_rss.py | 11 +- nikola/plugins/task_render_sources.py | 21 +- nikola/plugins/task_render_tags.py | 20 +- nikola/plugins/task_sitemap/__init__.py | 94 +- nikola/plugins/task_sitemap/sitemap_gen.py | 2137 -------------------- nikola/post.py | 312 ++- nikola/rc4.py | 76 + nikola/utils.py | 149 +- requirements-3.txt | 6 - requirements.txt | 2 +- scripts/import_po.py | 29 + scripts/nikola.bat | 2 + setup.py | 27 +- tests/base.py | 58 + tests/test_compile_markdown.py | 122 ++ tests/test_integration.py | 49 + tests/test_plugin_importing.py | 6 +- tests/test_rst_extensions.py | 223 ++ translations/nikola.messages/ca.po | 74 + translations/nikola.messages/de.po | 75 + translations/nikola.messages/el.po | 75 + translations/nikola.messages/en.po | 72 + translations/nikola.messages/es.po | 74 + translations/nikola.messages/fr.po | 75 + translations/nikola.messages/it.po | 74 + translations/nikola.messages/ja.po | 75 + translations/nikola.messages/pl.po | 75 + translations/nikola.messages/pt_BR.po | 75 + translations/nikola.messages/ru.po | 74 + translations/nikola.messages/zh_CN.po | 75 + 189 files changed, 5924 insertions(+), 5788 deletions(-) create mode 100644 .tx/config create mode 100644 docs/internals.txt delete mode 100644 extra_plugins/README.txt delete mode 100644 extra_plugins/command_planetoid.plugin delete mode 100644 extra_plugins/command_planetoid/__init__.py delete mode 100644 extra_plugins/command_planetoid/index.tmpl delete mode 100644 extra_plugins/command_planetoid/post.tmpl delete mode 100644 extra_plugins/compile_ipynb.plugin delete mode 100644 extra_plugins/compile_ipynb/README.txt delete mode 100644 extra_plugins/compile_ipynb/__init__.py delete mode 100644 extra_plugins/task_localsearch.plugin delete mode 100644 extra_plugins/task_localsearch/MIT-LICENSE.txt delete mode 100644 extra_plugins/task_localsearch/__init__.py delete mode 100644 extra_plugins/task_localsearch/files/assets/css/loader.gif delete mode 100644 extra_plugins/task_localsearch/files/assets/css/search.gif delete mode 100755 extra_plugins/task_localsearch/files/assets/css/tipuesearch.css delete mode 100644 extra_plugins/task_localsearch/files/assets/js/tipuesearch.js delete mode 100644 extra_plugins/task_localsearch/files/assets/js/tipuesearch_set.js delete mode 100644 extra_plugins/task_mustache.plugin delete mode 100644 extra_plugins/task_mustache/__init__.py delete mode 100644 extra_plugins/task_mustache/mustache-template.html delete mode 100644 extra_plugins/task_mustache/mustache.html delete mode 100644 install_requirements.py delete mode 100644 nikola/data/themes/default/assets/css/code.css delete mode 100644 nikola/data/themes/default/assets/css/slides.css delete mode 100755 nikola/data/themes/default/assets/js/slides.jquery.js create mode 100644 nikola/data/themes/default/messages/messages_el.py create mode 100644 nikola/data/themes/default/messages/messages_ja.py delete mode 100644 nikola/data/themes/default/messages/messages_pt-br.py create mode 100644 nikola/data/themes/default/messages/messages_pt_br.py delete mode 100644 nikola/data/themes/default/messages/messages_zh-cn.py create mode 100644 nikola/data/themes/default/messages/messages_zh_cn.py delete mode 100644 nikola/data/themes/monospace/assets/css/code.css delete mode 120000 nikola/data/themes/orphan/assets/css/code.css create mode 100644 nikola/data/themes/site-planetoid/README create mode 100644 nikola/data/themes/site-planetoid/engine create mode 100644 nikola/data/themes/site-planetoid/parent create mode 100644 nikola/data/themes/site-planetoid/templates/index.tmpl create mode 100644 nikola/data/themes/site-planetoid/templates/post.tmpl create mode 100644 nikola/data/themes/site-planetoid/templates/story.tmpl create mode 100644 nikola/plugins/command_planetoid.plugin create mode 100644 nikola/plugins/command_planetoid/__init__.py create mode 100644 nikola/plugins/compile_ipynb.plugin create mode 100644 nikola/plugins/compile_ipynb/README.txt create mode 100644 nikola/plugins/compile_ipynb/__init__.py create mode 100644 nikola/plugins/compile_markdown/mdx_gist.py create mode 100644 nikola/plugins/compile_markdown/mdx_nikola.py create mode 100644 nikola/plugins/compile_markdown/mdx_podcast.py create mode 100644 nikola/plugins/compile_misaka.plugin create mode 100644 nikola/plugins/compile_misaka/__init__.py create mode 100644 nikola/plugins/compile_rest/dummy.py create mode 100644 nikola/plugins/compile_rest/listing.py delete mode 100644 nikola/plugins/compile_rest/pygments_code_block_directive.py create mode 100644 nikola/plugins/task_localsearch.plugin create mode 100644 nikola/plugins/task_localsearch/MIT-LICENSE.txt create mode 100644 nikola/plugins/task_localsearch/__init__.py create mode 100755 nikola/plugins/task_localsearch/files/assets/css/img/expand.png create mode 100755 nikola/plugins/task_localsearch/files/assets/css/img/link.png create mode 100644 nikola/plugins/task_localsearch/files/assets/css/img/loader.gif create mode 100644 nikola/plugins/task_localsearch/files/assets/css/img/search.gif create mode 100755 nikola/plugins/task_localsearch/files/assets/css/tipuesearch.css create mode 100644 nikola/plugins/task_localsearch/files/assets/js/tipuesearch.js create mode 100644 nikola/plugins/task_localsearch/files/assets/js/tipuesearch_set.js create mode 100755 nikola/plugins/task_localsearch/files/tipue_search.html create mode 100644 nikola/plugins/task_mustache.plugin create mode 100644 nikola/plugins/task_mustache/__init__.py create mode 100644 nikola/plugins/task_mustache/mustache-template.html create mode 100644 nikola/plugins/task_mustache/mustache.html delete mode 100644 nikola/plugins/task_sitemap/sitemap_gen.py create mode 100644 nikola/rc4.py delete mode 100644 requirements-3.txt create mode 100644 scripts/import_po.py create mode 100644 scripts/nikola.bat create mode 100644 tests/base.py create mode 100644 tests/test_compile_markdown.py create mode 100644 tests/test_rst_extensions.py create mode 100644 translations/nikola.messages/ca.po create mode 100644 translations/nikola.messages/de.po create mode 100644 translations/nikola.messages/el.po create mode 100644 translations/nikola.messages/en.po create mode 100644 translations/nikola.messages/es.po create mode 100644 translations/nikola.messages/fr.po create mode 100644 translations/nikola.messages/it.po create mode 100644 translations/nikola.messages/ja.po create mode 100644 translations/nikola.messages/pl.po create mode 100644 translations/nikola.messages/pt_BR.po create mode 100644 translations/nikola.messages/ru.po create mode 100644 translations/nikola.messages/zh_CN.po diff --git a/.gitignore b/.gitignore index 3af3e85..d556b59 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,5 @@ *.py[co] *.db -*.html tmp/ output/ build/ diff --git a/.travis.yml b/.travis.yml index c30c430..a4c3081 100644 --- a/.travis.yml +++ b/.travis.yml @@ -4,10 +4,8 @@ python: - "2.7" - "3.2" - "3.3" -# command to install dependencies -# pip is run inside the script because we need have different requirements for Python 2 / 3. install: - - "python install_requirements.py" + - "pip install -r requirements.txt --use-mirrors" - "pip install flake8 --use-mirrors" - "pip install . --use-mirrors" # We run tests and afterwards nikola to see if the command is executable. diff --git a/.tx/config b/.tx/config new file mode 100644 index 0000000..6a0ee7c --- /dev/null +++ b/.tx/config @@ -0,0 +1,7 @@ +[main] +host = https://www.transifex.com + +[nikola.messages] +file_filter = translations/nikola.messages/.po +source_lang = en + diff --git a/CHANGES.txt b/CHANGES.txt index 376bcdc..9e7976d 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -1,3 +1,100 @@ +New in 5.4.4 +============ + +Features +-------- + +* New Japanese translation. +* Nikola check exists with 1 if there is an error +* New HIDE_UNTRANSLATED_POSTS option that ensures you don't have mixed-language pages (Issue #373) +* New theme "site-planetoid" for use with the planetoid plugin. +* New 'retired' tag for posts that should no longer be in feeds. + +Bugfixes +-------- + +* Added post data as a uptodate check for mustache (Issue #456) +* Rebuild post pages when the post's translation list changes (Issue #458) +* Handle "-h" (Issue #460) +* Added correct help for console command (Issue #460) +* Escape twittercard data (Issue #452) +* Added missing "twittercard" in story template +* Added support for per-language tags (Issue #450) +* Fix wrong path splitting (Issue #434) +* Remember locale even when set_locale failes (Issue #446) +* Decode path argument in new_post (Issue #442) +* task_indexes had missing config dependencies (Issue #441) +* Removed bogus links to slides assets that were removed +* Compressed files were seen as unknown by "nikola check" +* local search and mustache plugins must be disabled by default (Issue #437) +* Avoid failure if there are no tags and USE_GZIP is enabled (Issue #439) +* 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 +============ + +Features +-------- + +* Simpler slideshows based on Bootstrap's Carousel +* New CREATE_MONTHLY_ARCHIVE option, defaults to False (Issue #433) +* Added gist support for Markdown. +* New "nocomments" metadata that disables comments for a page/post (Issue #278) +* New HIDE_UNTRANSLATED_POSTS option (does nothing yet) +* New EXTRA_HEAD_DATA option, which adds extra things in (Issue #385) +* Moved translations to transifex.com +* New custom sitemap generator (Issue #395) +* New STRIP_INDEX_HTML option for cleaner URLs +* New alternative markdown compiler based on misaka +* New "internals" doc +* Place links to RSS feeds more visible to the visitor +* New CODE_COLOR_SCHEME option +* New "template" metadata that changes the template for a page/post (Issue #199) +* Added workaround for when Disqus doesn't support your exact locale + (spanish only at the moment) Issue #389 +* Extra plugins can be enabled via conf.py. +* Password-protected pages. + +Bugfixes +-------- + +* Listings CSS fixes (Issue #416) +* If dateutil is installed, try to use it to parse dates (Issue #419) +* Fixed posterous import via import_wordpress (Issue #419) +* Set locale to the value of "lang" in templates, so things like strftime + use localized values. (Issue #368) +* Fixed console command. +* Cleaned up arbitrary metadata. +* Don't crash in posts without actual post text. +* Nicer tag listing. +* Fixed unicode bug in markdown compiler. +* Fixed unicode crash with polish dates and %B (Issue #383) +* Fixed localsearch plugin +* Warn if combining USE_CDN with a theme providing a copy of bootstrap (Issue #386) +* Improved localsearch README +* Updated to Tipue 2.1 +* Don't index draft posts for Tipue (Issue #387) +* Modernized all rst extensions, added tests (rbistolfi) +* Removed obsolete custom code-block directive +* New function messages(msgid, lang=current_lang) available for templates +* Fixed teasers (Issue #398) +* Smarter guessing of the default post format (Issue #400) +* Make headings not overlap navbar in site theme. +* Added dummy codeblock fallback for docutils < 0.9 +* Detect dependency on included files in rest compiler +* Use gallery path from config +* Don't fail in corrupted images +* Don't assume filenames are ASCII +* Don't crash if sidebar_links is not set for a language. +* All RSS feed links for tags pointed to the DEFAULT_LANG one in some themes. +* Nikola.link and Nikola.path are now locale aware and lang is optional. +* Make docutils a soft requirement +* Normalize paths on task names (Issue #406) +* Updated translations (all 100%!) +* 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 ============ @@ -15,7 +112,7 @@ Features * Twitter Card / Open Graph support. * Smart math support -* New soundcould directive +* New soundcloud directive * Custom "read more" links * Better time display, timezone support * Better doit integration (Issue #151) diff --git a/README.md b/README.md index 943128f..edb7a7d 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,7 @@ In goes content, out comes a website, ready to deploy. Why Static Websites? -------------------- -Static websites are safer, use fewer resources, and avoid vendor and platform lockin. +Static websites are safer, use fewer resources, and avoid vendor and platform lock-in. You can read more about this in the [Nikola Handbook.](http://nikola.ralsina.com.ar/handbook.html#why-static) What Can Nikola Do? @@ -16,15 +16,16 @@ What Can Nikola Do? It has many features, but here are some of the nicer ones: -* Blogs, with tags, feeds, archives, comments, etc. -* [Theming](http://nikola.ralsina.com.ar/theming.html) -* Fast builds, thanks to [doit](http://pydoit.org) -* Flexible -* Small codebase (programmers can understand all of Nikola in a couple of hours) -* [reStructuredText](http://nikola.ralsina.com.ar/quickstart.html) or [Markdown](http://daringfireball.net/projects/markdown) as input languages. -* Easy [image galleries](http://nikola.ralsina.com.ar/galleries/demo/) (just drop files in a folder!) +* `Blogs, with tags, feeds, archives, comments, etc. `_ +* `Themable `_ +* Fast builds, thanks to `doit `_ +* Flexible, extensible via plugins +* Small codebase (programmers can understand all of Nikola core in a day) +* `reStructuredText `_ [`Cheatsheet `_] or Markdown as input language (also Wiki, BBCode, Textile, and HTML) +* Easy `image galleries `_ (just drop files in a folder!) * Syntax highlighting for almost any programming language or markup -* Multilingual sites +* Multilingual sites, `translated to 11 languages `_. * Doesn't reinvent wheels, leverages existing tools. +* Python 2 and 3 compatible. For more information, see http://nikola.ralsina.com.ar diff --git a/docs/creating-a-theme.txt b/docs/creating-a-theme.txt index 21bf796..11428bd 100644 --- a/docs/creating-a-theme.txt +++ b/docs/creating-a-theme.txt @@ -15,7 +15,7 @@ Starting The Theme First, we create a testing site, and copy the orphan theme from nikola's sources into the right place:: - $ nikola init monospace-site --demo + $ nikola init --demo monospace-site A new site with some sample data has been created at monospace-site. See README.txt in that folder for more information. @@ -180,7 +180,7 @@ the posts themselves is different, and that was not described in ``base.tmpl`` a there is a placeholder called content: ``<%block name="content">`` That's because ``base.tmpl`` defines the *base* layout. The layout of more specific pages, like "the page that shows -a lis of posts" is defined in the other templates. Specifically, this is defined in ``index.tmpl``: +a list of posts" is defined in the other templates. Specifically, this is defined in ``index.tmpl``: .. code-block:: mako @@ -211,12 +211,13 @@ box, add links for the posts tags, move the date there, etc. ## -*- coding: utf-8 -*- <%namespace name="helper" file="index_helper.tmpl"/> + <%namespace name="disqus" file="disqus_helper.tmpl"/> <%inherit file="base.tmpl"/> <%block name="content"> % for post in posts:

${post.title(lang)}

-
+
${messages[lang]["Posted"]}: ${post.date.strftime(date_format)} @@ -230,10 +231,12 @@ box, add links for the posts tags, move the date there, etc.
${post.text(lang, index_teasers)} - ${helper.html_disqus_link(post)} + ${disqus.html_disqus_link(post.permalink()+"#disqus_thread", post.base_path)}
% endfor ${helper.html_pager()} + ${disqus.html_disqus_script()} + .. figure:: http://ralsina.com.ar/galleries/random/monospace-4.png :height: 400px @@ -246,11 +249,12 @@ Then if we click on the post title, we will see some broken details in the metad ## -*- coding: utf-8 -*- <%namespace name="helper" file="post_helper.tmpl"/> + <%namespace name="disqus" file="disqus_helper.tmpl"/> <%inherit file="base.tmpl"/> <%block name="content">
${helper.html_title()} -
+
${messages[lang]["Posted"]}: ${post.date.strftime(date_format)} [${messages[lang]["Source"]}] @@ -269,7 +273,7 @@ Then if we click on the post title, we will see some broken details in the metad
${post.text(lang)} ${helper.html_pager(post)} - ${helper.html_disqus(post)} + ${disqus.html_disqus(post.permalink(absolute=True), post.title(lang), post.base_path)}
diff --git a/docs/extending.txt b/docs/extending.txt index 84a382a..750ec98 100644 --- a/docs/extending.txt +++ b/docs/extending.txt @@ -52,7 +52,7 @@ Each and every one of those is a plugin. Let's look at a typical example: First, the ``command_serve.plugin`` file: -.. code-block:: init +.. code-block:: ini [Core] Name = serve @@ -210,7 +210,7 @@ probably want to do a Task plugin, which will make it be part of the ``nikola build`` command. There are the currently available tasks, all provided by plugins:: - $ nikola build list + $ nikola list build_bundles copy_assets diff --git a/docs/internals.txt b/docs/internals.txt new file mode 100644 index 0000000..7a38f27 --- /dev/null +++ b/docs/internals.txt @@ -0,0 +1,131 @@ +Nikola Internals +================ + +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. + +So, this is a short document explaining what each piece of Nikola does and +how it all fits together. + +Nikola is a Pile of Plugins + Most of Nikola is implemented as plugins using `Yapsy `_. + You can ignore that they are plugins and just think of them as regular python + modules and packages with a funny little ``.plugin`` file next to them. + + So, 90% of the time, what you want to do is either write a new plugin or extend + an existing one. + + There are several kinds of plugins, all implementing interfaces defined in + ``nikola/plugin_categories.py``. + + If your plugin has a dependency, please make sure it doesn't make Nikola + throw an exception when the dependency is missing. Try to fail gracefully + with an informative message. + +Commands are plugins + When you use ``nikola foo`` you are using the plugin ``command_foo``. Those are + used to extend Nikola's command line. Their interface is defined in the ``Command`` + class. They take options and arguments and do whatever you want, so go wild. + +The ``build`` command is special + The ``build`` command triggers a whole lot of things, and is the core of Nikola + because it's the one that you use to build sites. So it deserves its own section. + +The Build Command +----------------- + +Nikola's goal is similar, deep at heart, to a Makefile. Take sources, compile them +into something, in this case a website. Instead of a Makefile, Nikola uses +`doit `_ + +Doit has the concept of "tasks". The 1 minute summary of tasks is that they have: + +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 + option changes, redo it, etc. + +targets + Files that the action generates. No two actions can have the same targets. + +basename:name + Each task is identified by either a name or a basename:name pair. Nikola + +.. sidebar:: More about tasks + + If you ever want to do your own tasks, you really should read the doit + `documentation on tasks `_ + +So, what Nikola does, when you use the build command, is to read the +configuration ``conf.py`` from the current folder, instantiate +the ``Nikola`` class, and have it generate a whole list of tasks for doit +to process. Then doit will decide which tasks need doing, and do them, in +the right order. + +The place where the tasks are generated is in ``Nikola.gen_tasks``, which collects tasks +from all the plugins inheriting ``BaseTask``, massages them a bit, then passes them +to doit. + +So, if you want things to happen on ``build`` you want to create a Task plugin, or extend +one of the existing ones. + +.. sidebar:: Tests + + While Nikola is not a hardcore TDD project, we like tests. So, please add them if you can. + You can do doctests, you can do unit tests, you can do integration tests. There is support + for all of them. + +Post 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`` + +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 +the timeline is created and available in ``self.site.timeline``. You should not modify +the timeline, because it will cause consistency issues. + +.. sidebar:: scan_posts + + The scan_posts function is what reads your site and creates the timeline. + + I am considering moving scan_posts off the core and into its own plugin + so it can be replaced (for example, by a version that reads a database + instead of scanning a folder tree). + +Your plugin can use the timeline to generate "stuff" (technical term). For example, +Nikola comes with plugins that use the timeline to create a website (surprised?). + +The workflow included with nikola is as follows: + +#. The post is assigned a compiler based on its extension and the ``post_compilers`` option. +#. The compiler is applied to the post data and a "HTML fragment" is produced. That + fragment is stored in a cache (the ``render_posts`` plugin). +#. The configured theme has templates (and a template engine), which are applied to the post's + HTML fragment and metadata (the ``render_pages`` plugin). +#. The original sources for the post are copied to some accessible place (the ``render_sources`` plugin) +#. If the post is tagged, some pages and RSS feeds for each tag are updated (the ``render_tags`` plugin) +#. If the post is new, it's included in the blog's RSS feed (the ``render_rss`` plugin) +#. The post is added in the right place in the index pages for the blog (the ``task_indexes`` plugin) + +You can add whatever you want to that list: just create a plugin for it. + +You can also expand Nikola's capabilities at several points: + +compilers + Nikola supports a variety of markups. If you want to add another one, you need to create + a ``Compiler`` plugin. + +templates + Nikola's themes can use Jinja2 or Mako templates. If you prefer another template system, + you have to create a ``TemplateSystem`` plugin. + +themes + To change how the generated site looks, you can create custom themes. + +And of course, you can also replace or extend each of the existing plugins. + diff --git a/docs/manual.txt b/docs/manual.txt index b2290c2..4833eae 100644 --- a/docs/manual.txt +++ b/docs/manual.txt @@ -1,8 +1,7 @@ The Nikola Handbook =================== -:Version: 5.4.2 -:Author: Roberto Alsina +:Version: 5.4.4 .. class:: alert alert-info pull-right @@ -17,7 +16,7 @@ After you have Nikola installed: Create a empty site: ``nikola init mysite`` -You can create a site with demo files in it with ``nikola init mysite --demo`` +You can create a site with demo files in it with ``nikola init --demo mysite`` The rest of these commands have to be executed inside the new ``mysite`` folder. @@ -84,7 +83,7 @@ Getting Help ------------ * Feel free to contact me at ralsina@netmanagers.com.ar for questions about Nikola. -* You can file bugs at `the issue tracker `__ +* You can file bugs at `the issue tracker `__ * You can discuss Nikola at the `nikola-discuss google group `_ * You can subscribe to `the Nikola Blog `_ * You can follow `Nikola on Twitter `_ @@ -197,10 +196,10 @@ Longer version: #. Get `Nikola `_ #. Install dependencies. To do that, either: - #. ``pip install -r requirements.txt`` or... + #. ``pip install -r requirements.txt`` and ``pip install .`` or... #. Install your distribution's packages for all the things mentioned below, if they exist, or... - #. Get all of these manually (but why?, use requirements.txt): + #. Get all of these manually (but why?, use pip): #. Get python, if you don't have it. #. Get `doit `_ @@ -276,9 +275,9 @@ all the pages again, unless you changed something that the page requires. So, if the text of a post, or its title, that post page, and all index pages where it is mentioned, will be recreated. If you change the post page template, then all the post pages will be rebuilt. -Nikola is mostly a series of doit *tasks*, and you can see them by doing ``nikola build list``:: +Nikola is mostly a series of doit *tasks*, and you can see them by doing ``nikola list``:: - $ nikola build list + $ nikola list Scanning posts . . done! build_bundles copy_assets @@ -398,6 +397,24 @@ source for the content, and ``description`` is mostly useful for SEO. You can add your own metadata fields in the same manner, if you use a theme that supports them (for example: ``.. author: John Doe``) +.. sidebar:: Other Metadata Fields + + Nikola will also use other metadata fields: + + nocomments + Set to "True" to disable comments. Example:: + + .. nocomments: True + + template + Will change the template used to render this page/post specific page. Example:: + + .. template: story.tmpl + + password + The post will be encrypted and invisible until the reader enters the password. + Also, the post's sourcecode will not be available. + .. note:: The Two-File Format @@ -410,11 +427,11 @@ supports them (for example: ``.. author: John Doe``) 2012/09/15 19:52:05 If you are writing a multilingual site, you can also create a per-language -post file (for example: ``how-to-make-money.txt.es``). This one can have two -lines of metadata: +post file (for example: ``how-to-make-money.txt.es``). This one can replace +metadata of the default language, for example: -1) The translated title for the post or page -2) A translated version of the pagename +* The translated title for the post or page +* A translated version of the pagename You can edit these files with your favourite text editor, and once you are happy with the contents, generate the pages as explained in `Getting Started`_ @@ -470,10 +487,10 @@ one in the list if all of them have it set to False. The ``new_post`` command supports some options:: - $ nikola help new_post + $ nikola help new_post Purpose: Create a new blog post or site page. Usage: nikola new_post [options] [path] - + Options: -p, --page Create a page instead of a blog post. -t ARG, --title=ARG Title for the page/post. @@ -520,6 +537,12 @@ If you add a "draft" tag to a post, then it will not be shown in indexes and fee It *will* be compiled, and if you deploy it it *will* be made available, so use with care. +Retired Posts +~~~~~~~~~~~~~ + +If you add a "retired" tag to a post, then it will not be shown in indexes and feeds. +It *will* be compiled, and if you deploy it it *will* be made available, so it will +not generate 404s for people who had linked to it. Creating a Page --------------- @@ -572,7 +595,7 @@ You surely want to edit these options:: # Data about this site BLOG_TITLE = "Demo Site" - BLOG_URL = "http://nikola.ralsina.com.ar" + SITE_URL = "http://nikola.ralsina.com.ar" BLOG_EMAIL = "joe@demo.site" BLOG_DESCRIPTION = "This is a demo site for Nikola." @@ -718,6 +741,10 @@ Disqus is a good option because: 3) It's free. 4) It's damn nice. +You can disable comments for a post by adding a "nocomments" metadata field to it:: + + .. nocomments: True + .. admonition:: Important In some cases, when you run the test site, you won't see the comments. @@ -801,7 +828,7 @@ different ones, or about other webservers, please share! AddType text/css .css #. Optionally you can greate static compressed copies and save some CPU on your server - with the GZIP_FILES option in Nikola. + with the GZIP_FILES option in Nikola. #. The webassets Nikola plugin can drastically decrease the number of CSS and JS files your site fetches. @@ -844,8 +871,8 @@ the embedded player will be set to the native height and width of the video. You can override this if you wish:: .. vimeo:: 20241459 - height=240 - width=320 + :height: 240 + :width: 320 Soundcloud ~~~~~~~~~~ @@ -860,22 +887,19 @@ The ID is 78131362 and you can embed the audio with this:: .. soundcloud:: 78131362 -code-block -~~~~~~~~~~ - -This is a somewhat complicated directive to display code nicely. You can just -embed code like this:: - - .. code-block:: python +Code +~~~~ - print "Hello World!" +The ``code`` directive has been included in docutils since version 0.9 and now +replaces Nikola's ``code-block`` directive. To ease the transition, two aliases +for ``code`` directive are provided: ``code-block`` and ``sourcecode``:: -Or you can include the code from a file:: + .. code:: python + :number-lines: - .. code-block:: python - :include: /foo/bar/baz.py + print("Our virtues and our failings are inseparable") -listing +Listing ~~~~~~~ To use this, you have to put your source code files inside ``listings`` or whatever your @@ -886,27 +910,22 @@ To use this, you have to put your source code files inside ``listings`` or whate Will include the source code from ``foo.py``, highlight its syntax in python mode, and also create a ``listings/foo.py.html`` page and the listing will have a title linking to it. -Advanced Code Options -~~~~~~~~~~~~~~~~~~~~~ - -Both code-block and listing support a number of options, including these: +Listings support a few extra options so that you can display a fragment instead of the whole +file in a document: start-at - A string, the diplayed code will start when it finds this + Takes a string, and starts displaying the code at the first line that matches it. +start-before + Takes a string, and starts displaying the code right before the first line that matches it. end-at - A string, the diplayed code will end when it finds this -start-after - A string, the diplayed code will start in the line after this + Takes a string, and stops displaying the code at the first line that matches it. end-before - A string, the diplayed code will end in the line before this -linenos - Display line numbers -linenos_offset - Use the original file's line numbers (warning: broken) -tab-width - Size of the tabs (default 4) - -gist + Takes a string, and stops displaying the code right before the first line that matches it. + +If you set start-at and start-before, start-at wins. If you set end-at and end-before, end-at wins. +If you make it so your listing ends before it starts, it's frowned upon and nothing will be shown. + +Gist ~~~~ You can easily embed GitHub gists with this directive, like this:: @@ -925,8 +944,6 @@ Slideshows To create an image slideshow, you can use the ``slides`` directive. For example:: .. slides:: - :preload: - :play: 350 /galleries/demo/tesla_conducts_lg.jpg /galleries/demo/tesla_lightning2_lg.jpg @@ -934,13 +951,6 @@ To create an image slideshow, you can use the ``slides`` directive. For example: /galleries/demo/tesla_lightning1_lg.jpg /galleries/demo/tesla_tower1_lg.jpg -This is based on `slidejs `_ and it supports -`the options described there `_ with one minor tweak to make them -fit in docutils convention: If the option takes a boolean value, you just have to add it or not. For example, -to enable preloading, just use the ``:preload:`` option. - -If the option takes any other kind of argument, just use it after the option, like ``play`` in the -above example. Importing Your Wordpress Site Into Nikola ----------------------------------------- @@ -1012,7 +1022,7 @@ corresponding lines in your ``conf.py``. An example configuration that uses the Twitter nickname of the website and the authors Twitter user ID is found below. -.. code-block:: Python +.. code-block:: python TWITTER_CARD = { 'use_twitter_cards': True, # enable Twitter Cards / Open Graph @@ -1022,6 +1032,103 @@ and the authors Twitter user ID is found below. 'creator:id': 654321, # Same as creator, but the Twitter user's ID. } + +Extra Plugins +------------- + +These are plugins that may not be widely used or that are a bit too radical or +experimental for the general public. + +To enable them for your site please look for `ENABLED_EXTRAS` in your ``conf.py``. + +Planetoid +~~~~~~~~~ + +This plugin converts Nikola into the equivalent of `Planet `_ +a feed aggregator. It requires `PeeWee `_ and +`Feedparser `_ to work. + +It has a configuration option: PLANETOID_REFRESH which is the number of minutes +before retrying a feed (defaults to 60). + +You need to create a ``feeds`` file containing the data of which feeds you want to +aggregate. The format is very simple:: + + # Roberto Alsina + http://feeds2.feedburner.com/PostsInLateralOpinionAboutPython + Roberto Alsina + +#. Lines that start with ``#`` are comments and ignored. +#. Lines that start with http are feed URLs. +#. URL lines have to be followed by the "real name" of the feed. + +After all that is in place, just run ``nikola build`` and you'll get +a planet. +If you run ``nikola build`` for the first time you need to actually issue +the command three times until the planet is build. + +There is a special theme for the planets called `site-planetoid`. To use +this set `THEME` in your ``conf.py`` to ``'site-planetoid'``. +This is special in the case that it redirects users to the original URL of the post +when they try to open a post. + +Local Search +~~~~~~~~~~~~ + +If you don't want to depend on google or duckduckgo to implement search for you, +or just want it to wok even if you are offline, enable this plugin and the +search will be performed client side. + +This plugin implements a Tipue-based local search for your site. + +To use it, copy task_localsearch.plugin and task_localsearch +into a plugins/ folder in your nikola site. + +After you build your site, you will have several new files in assets/css and assets/js +and a tipue_search.html that you can use as a basis for using this in your site. + +For more information about how to customize it and use it, please refer to the tipue +docs at http://www.tipue.com/search/ + +Tipue is under an MIT license (see MIT-LICENSE.txt) + +Here's a set of example settings for conf.py that should work nicely with the "site" theme:: + + SEARCH_FORM = """ + + + """ + + ANALYTICS = """ + + + + """ + + EXTRA_HEAD_DATA = """ + +
+ """ + +The
in EXTRA_HEAD_DATA is a hack but it will migrate into the of the +documents thanks to magic, and will hold the search results after the user searches. + +Mustache +~~~~~~~~ + +This task gives you a ``mustache.html`` file which lets you access your whole +blog without reloading the page, using client-side templates. Makes it much +faster and modern ;-) + + License ------- diff --git a/docs/theming.txt b/docs/theming.txt index 6c0c0e4..d767cae 100644 --- a/docs/theming.txt +++ b/docs/theming.txt @@ -44,8 +44,8 @@ messages And these optional files: parent - A text file that, on its first line, contains the name of the **parent theme**. - Any resources missing on this theme, will be looked up in the parent theme + A text file that, on its first line, contains the name of the **parent theme**. + Any resources missing on this theme, will be looked up in the parent theme (and then in the grandparent, etc). The ``parent`` is so you don't have to create a full theme each time: just create an @@ -77,7 +77,7 @@ Templates In templates there is a number of files whose name ends in ``.tmpl``. Those are the theme's page templates. They are done using the `Mako `_ -or `Jinja2 `_ template languages. If you want to do a theme, you +or `Jinja2 `_ template languages. If you want to do a theme, you should learn one first. What engine is used by the theme is declared in the ``engine`` file. The rest of this document explains Mako templates, but Jinja2 is fairly similar. @@ -132,7 +132,7 @@ base.tmpl The included themes use at least these: * ``rss_link`` a link to custom RSS feed, although it may be empty) - * ``blog_url`` the URL for your site + * ``site_url`` the URL for your site * ``blog_title`` the name of your site * ``content_footer`` things like copyright notices, disclaimers, etc. * ``license`` a larger license badge @@ -195,47 +195,8 @@ you would need to maintain the inheritance as it is, or not require whatever dat Messages and Translations ------------------------- -When you modify templates, you may want to add text in them (for example: "About Me"). -Instead of adding the text directly, which makes it impossible to translate to other -languages, add it like this:: - - ${messages[lang]["About Me"]} - -Then, in ``messages/en.py`` add it along the other strings:: - - MESSAGES = [ - u"Posts for year %s", - u"Archive", - u"Posts about %s:", - u"Tags", - u"Also available in: ", - u"More posts about", - u"Posted:", - u"Original site", - u"Read in english", - u"About Me", - ] - -Then, when I want to use your theme in spanish, all I have to do is add a line in ``messages/es.py``:: - - MESSAGES = { - u"LANGUAGE": u"Español", - u"Posts for year %s": u"Posts del año %s", - u"Archive": u"Archivo", - u"Posts about %s:": u"Posts sobre %s", - u"Tags": u"Tags", - u"Also available in: ": u"También disponible en: ", - u"More posts about": u"Más posts sobre", - u"Posted:": u"Publicado:", - u"Original site": u"Sitio original", - u"Read in english": u"Leer en español", - u"About Me": u"Acerca del autor", - } - -And voilá, your theme works in spanish. Don't remove strings from these files even if it seems -your theme is not using them. Some are used internally in Nikola to generate titles and -similar things. - -To create a new translation, just copy one of the existing ones, translate the right side of -every string to your language, save it and send it to me, I will add it to Nikola! +The included themes are translated into a variety of languages. You can add your own translation +at https://www.transifex.com/projects/p/nikola/ +If you want to create a theme that has new strings, and you want those strings to be translatable, +then your theme will need a custom ``messages`` folder. diff --git a/extra_plugins/README.txt b/extra_plugins/README.txt deleted file mode 100644 index 2a04614..0000000 --- a/extra_plugins/README.txt +++ /dev/null @@ -1,62 +0,0 @@ -Extra Plugins -============= - -These are plugins that may not be widely used or that are a bit too radical or -experimental for the general public. - -To enable them for you, create a ``plugins/`` folder in your site, and copy -both the ``.plugin`` file and the matching ``.py`` file or folder. - -Planetoid ---------- - -This plugin converts Nikola into the equivalent of `Planet `_ -a feed aggregator. It requires `PeeWee `_ and -`Feedparser `_ to work. - -It has a configuration option: PLANETOID_REFRESH which is the number of minutes -before retrying a feed (defaults to 60). - -You need to create a ``feeds`` file containing the data of which feeds you want to -aggregate. The format is very simple:: - - # Roberto Alsina - http://feeds2.feedburner.com/PostsInLateralOpinionAboutPython - Roberto Alsina - -#. Lines that start with ``#`` are comments and ignored. -#. Lines that start with http are feed URLs. -#. URL lines have to be followed by the "real name" of the feed. - -FIXME: explain the planetoid theme stuff - -After all that is in place, just run ``nikola build`` and you'll get -a planet. - -Local Search ------------- - -If you don't want to depend on google or duckduckgo to implement search for you, -or just want it to wok even if you are offline, enable this plugin and the -search will be performed client side. - -This plugin implements a Tipue-based local search for your site. - -To use it, copy task_localsearch.plugin and task_localsearch -into a plugins/ folder in your nikola site. - -After you build your site, you will have several new files in assets/css and assets/js -and a search.html that you can use as a basis for using this in your site. - -For more information about how to customize it and use it, please refer to the tipue -docs at http://www.tipue.com/search/ - -Tipue is under an MIT license (see MIT-LICENSE.txt) - - -Mustache --------- - -This task gives you a ``mustache.html`` file which lets you access your whole -blog without reloading the page, using client-side templates. Makes it much -faster and modern ;-) diff --git a/extra_plugins/command_planetoid.plugin b/extra_plugins/command_planetoid.plugin deleted file mode 100644 index 8636d49..0000000 --- a/extra_plugins/command_planetoid.plugin +++ /dev/null @@ -1,9 +0,0 @@ -[Core] -Name = planetoid -Module = command_planetoid - -[Documentation] -Author = Roberto Alsina -Version = 0.1 -Website = http://nikola.ralsina.com.ar -Description = Maintain a planet-like site diff --git a/extra_plugins/command_planetoid/__init__.py b/extra_plugins/command_planetoid/__init__.py deleted file mode 100644 index 2428f10..0000000 --- a/extra_plugins/command_planetoid/__init__.py +++ /dev/null @@ -1,248 +0,0 @@ -# -*- coding: utf-8 -*- -# Copyright (c) 2012 Roberto Alsina y otros. - -# Permission is hereby granted, free of charge, to any -# person obtaining a copy of this software and associated -# documentation files (the "Software"), to deal in the -# Software without restriction, including without limitation -# the rights to use, copy, modify, merge, publish, -# distribute, sublicense, and/or sell copies of the -# Software, and to permit persons to whom the Software is -# furnished to do so, subject to the following conditions: -# -# The above copyright notice and this permission notice -# shall be included in all copies or substantial portions of -# the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY -# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE -# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR -# PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS -# OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR -# OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -# OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE -# SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - -from __future__ import print_function, unicode_literals -import codecs -import datetime -import hashlib -from optparse import OptionParser -import os - -from doit.tools import timeout -import feedparser -import peewee - -from nikola.plugin_categories import Command, Task -from nikola.utils import config_changed - - -class Feed(peewee.Model): - name = peewee.CharField() - url = peewee.CharField(max_length=200) - last_status = peewee.CharField(null=True) - etag = peewee.CharField(max_length=200) - last_modified = peewee.DateTimeField() - - -class Entry(peewee.Model): - date = peewee.DateTimeField() - feed = peewee.ForeignKeyField(Feed) - content = peewee.TextField(max_length=20000) - link = peewee.CharField(max_length=200) - title = peewee.CharField(max_length=200) - guid = peewee.CharField(max_length=200) - - -class Planetoid(Command, Task): - """Maintain a planet-like thing.""" - name = "planetoid" - - def init_db(self): - # setup database - Feed.create_table(fail_silently=True) - Entry.create_table(fail_silently=True) - - def gen_tasks(self): - self.init_db() - for task in self.task_load_feeds(): - yield task - for task in self.task_update_feeds(): - yield task - for task in self.task_generate_posts(): - yield task - - def run(self, *args): - self.init_db() - parser = OptionParser(usage="nikola %s [options]" % self.name) - (options, args) = parser.parse_args(list(args)) - - def task_load_feeds(self): - "Read the feeds file, add it to the database." - feeds = [] - feed = name = None - for line in codecs.open('feeds', 'r', 'utf-8'): - line = line.strip() - if line.startswith("#"): - continue - elif line.startswith('http'): - feed = line - elif line: - name = line - if feed and name: - feeds.append([feed, name]) - feed = name = None - - def add_feed(name, url): - f = Feed.create( - name=name, - url=url, - etag='foo', - last_modified=datetime.datetime(1970, 1, 1), - ) - f.save() - - def update_feed_url(feed, url): - feed.url = url - feed.save() - - for feed, name in feeds: - f = Feed.select().where(Feed.name == name) - if not list(f): - yield { - 'basename': self.name, - 'name': name.encode('utf8'), - 'actions': ((add_feed, (name, feed)), ), - 'file_dep': ['feeds'], - } - elif list(f)[0].url != feed: - yield { - 'basename': self.name, - 'name': ('updating_' + name).encode('utf8'), - 'actions': ((update_feed_url, (list(f)[0], feed)), ), - } - - def task_update_feeds(self): - """Download feed contents, add entries to the database.""" - def update_feed(feed): - modified = feed.last_modified.timetuple() - etag = feed.etag - try: - parsed = feedparser.parse( - feed.url, - etag=etag, - modified=modified - ) - feed.last_status = str(parsed.status) - except: # Probably a timeout - # TODO: log failure - return - if parsed.feed.get('title'): - print(parsed.feed.title) - else: - print(feed.url) - feed.etag = parsed.get('etag', 'foo') - modified = tuple(parsed.get('date_parsed', (1970, 1, 1)))[:6] - print("==========>", modified) - modified = datetime.datetime(*modified) - feed.last_modified = modified - feed.save() - # No point in adding items from missinfg feeds - if parsed.status > 400: - # TODO log failure - return - for entry_data in parsed.entries: - print("=========================================") - date = entry_data.get('published_parsed', None) - if date is None: - date = entry_data.get('updated_parsed', None) - if date is None: - print("Can't parse date from:") - print(entry_data) - return False - print("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) - if content: - content = content[0].value - if not content: - content = entry_data.get('description', None) - if not content: - content = entry_data.get('summary', 'Sin contenido') - guid = str(entry_data.get('guid', entry_data.link)) - link = entry_data.link - print(repr([date, title])) - e = list(Entry.select().where(Entry.guid == guid)) - print( - repr(dict( - date=date, - title=title, - content=content, - guid=guid, - feed=feed, - link=link, - )) - ) - if not e: - entry = Entry.create( - date=date, - title=title, - content=content, - guid=guid, - feed=feed, - link=link, - ) - else: - entry = e[0] - entry.date = date - entry.title = title - entry.content = content - entry.link = link - entry.save() - for feed in Feed.select(): - task = { - 'basename': self.name, - 'name': str(feed.url), - 'actions': [(update_feed, (feed, ))], - 'uptodate': [timeout(datetime.timedelta(minutes= - self.site.config.get('PLANETOID_REFRESH', 60)))], - } - yield task - - def task_generate_posts(self): - """Generate post files for the blog entries.""" - def gen_id(entry): - h = hashlib.md5() - h.update(entry.feed.name.encode('utf8')) - h.update(entry.guid) - return h.hexdigest() - - def generate_post(entry): - unique_id = gen_id(entry) - meta_path = os.path.join('posts', unique_id + '.meta') - post_path = os.path.join('posts', unique_id + '.txt') - with codecs.open(meta_path, 'wb+', 'utf8') as fd: - fd.write('%s\n' % entry.title.replace('\n', ' ')) - fd.write('%s\n' % unique_id) - fd.write('%s\n' % entry.date.strftime('%Y/%m/%d %H:%M')) - fd.write('\n') - fd.write('%s\n' % entry.link) - with codecs.open(post_path, 'wb+', 'utf8') as fd: - fd.write('.. raw:: html\n\n') - content = entry.content - if not content: - content = 'Sin contenido' - for line in content.splitlines(): - fd.write(' %s\n' % line) - - for entry in Entry.select().order_by(Entry.date.desc()): - entry_id = gen_id(entry) - yield { - 'basename': self.name, - 'targets': [os.path.join('posts', entry_id + '.meta'), os.path.join('posts', entry_id + '.txt')], - 'name': entry_id, - 'actions': [(generate_post, (entry,))], - 'uptodate': [config_changed({1: entry})] - } diff --git a/extra_plugins/command_planetoid/index.tmpl b/extra_plugins/command_planetoid/index.tmpl deleted file mode 100644 index da9ff5d..0000000 --- a/extra_plugins/command_planetoid/index.tmpl +++ /dev/null @@ -1,26 +0,0 @@ -## -*- coding: utf-8 -*- -<%inherit file="base.tmpl"/> -<%block name="content"> - % for post in posts: -
-

${post.title(lang)} -    - Publicado: ${post.date} -

- ${post.text(lang)} -
- % endfor -
- - diff --git a/extra_plugins/command_planetoid/post.tmpl b/extra_plugins/command_planetoid/post.tmpl deleted file mode 100644 index 2c4c220..0000000 --- a/extra_plugins/command_planetoid/post.tmpl +++ /dev/null @@ -1,10 +0,0 @@ -## -*- coding: utf-8 -*- - - - -Redirecting you to the original location. - \ No newline at end of file diff --git a/extra_plugins/compile_ipynb.plugin b/extra_plugins/compile_ipynb.plugin deleted file mode 100644 index 51051e0..0000000 --- a/extra_plugins/compile_ipynb.plugin +++ /dev/null @@ -1,10 +0,0 @@ -[Core] -Name = ipynb -Module = compile_ipynb - -[Documentation] -Author = Damián Avila -Version = 0.1 -Website = http://www.oquanta.info -Description = Compile IPython notebooks into HTML - diff --git a/extra_plugins/compile_ipynb/README.txt b/extra_plugins/compile_ipynb/README.txt deleted file mode 100644 index 2cfd45e..0000000 --- a/extra_plugins/compile_ipynb/README.txt +++ /dev/null @@ -1,35 +0,0 @@ -To make this work... - -1- First, you have to put this plugin in your_site/plugins/ folder. - -2- Then, you have to download the custom nbconvert from here: https://github.com/damianavila/compile_ipynb-for-Nikola.git -and put it inside your_site/plugins/compile_ipynb/ folder - -3- Also, you have to use the site-ipython theme (or make a new one containing the ipython css, mathjax.js and the proper template). -You can get it here: https://github.com/damianavila/site-ipython-theme-for-Nikola - -4- Finally, you have to put: - -post_pages = ( - ("posts/*.ipynb", "posts", "post.tmpl", True), - ("stories/*.ipynb", "stories", "story.tmpl", False), -) - -in your conf.py - -Then... to use it: - -$nikola new_page -f ipynb - -**NOTE**: Just IGNORE the "-1" and "-2" options in nikola new_page command, by default this compiler -create one metadata file and the corresponding naive IPython notebook. - -$nikola build - -And deploy the output folder... to see it locally: $nikola serve - -If you have any doubts, just ask: @damianavila - -Cheers. - -Damián diff --git a/extra_plugins/compile_ipynb/__init__.py b/extra_plugins/compile_ipynb/__init__.py deleted file mode 100644 index be83a84..0000000 --- a/extra_plugins/compile_ipynb/__init__.py +++ /dev/null @@ -1,98 +0,0 @@ -# Copyright (c) 2013 Damian Avila. - -# 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. - -"""Implementation of compile_html based on nbconvert.""" - -from __future__ import unicode_literals, print_function -import codecs -import os - -try: - from .nbformat import current as nbformat - from .nbconvert.converters import bloggerhtml as nbconverter - bloggerhtml = True -except ImportError: - bloggerhtml = None - -from nikola.plugin_categories import PageCompiler - - -class CompileIPynb(PageCompiler): - """Compile IPynb into HTML.""" - - name = "ipynb" - - def compile_html(self, source, dest): - if bloggerhtml is None: - raise Exception('To build this site, you also need ' - 'https://github.com/damianavila/com' - 'pile_ipynb-for-Nikola.git.') - try: - os.makedirs(os.path.dirname(dest)) - except: - pass - converter = nbconverter.ConverterBloggerHTML() - with codecs.open(dest, "w+", "utf8") as out_file: - with codecs.open(source, "r", "utf8") as in_file: - data = in_file.read() - converter.nb = nbformat.reads_json(data) - output = converter.convert() - out_file.write(output) - - def create_post(self, path, onefile=False, title="", slug="", date="", - tags=""): - d_name = os.path.dirname(path) - if not os.path.isdir(d_name): - os.makedirs(os.path.dirname(path)) - meta_path = os.path.join(d_name, slug + ".meta") - with codecs.open(meta_path, "wb+", "utf8") as fd: - if onefile: - fd.write('%s\n' % title) - fd.write('%s\n' % slug) - fd.write('%s\n' % date) - fd.write('%s\n' % tags) - print("Your post's metadata is at: ", meta_path) - with codecs.open(path, "wb+", "utf8") as fd: - fd.write("""{ - "metadata": { - "name": "%s" - }, - "nbformat": 3, - "nbformat_minor": 0, - "worksheets": [ - { - "cells": [ - { - "cell_type": "code", - "collapsed": false, - "input": [], - "language": "python", - "metadata": {}, - "outputs": [] - } - ], - "metadata": {} - } - ] -}""" % slug) diff --git a/extra_plugins/task_localsearch.plugin b/extra_plugins/task_localsearch.plugin deleted file mode 100644 index 33eb78b..0000000 --- a/extra_plugins/task_localsearch.plugin +++ /dev/null @@ -1,10 +0,0 @@ -[Core] -Name = local_search -Module = task_localsearch - -[Documentation] -Author = Roberto Alsina -Version = 0.1 -Website = http://nikola.ralsina.com.ar -Description = Create data files for local search via Tipue - diff --git a/extra_plugins/task_localsearch/MIT-LICENSE.txt b/extra_plugins/task_localsearch/MIT-LICENSE.txt deleted file mode 100644 index f131068..0000000 --- a/extra_plugins/task_localsearch/MIT-LICENSE.txt +++ /dev/null @@ -1,20 +0,0 @@ -Tipue Search Copyright (c) 2012 Tipue - -Permission is hereby granted, free of charge, to any person obtaining -a copy of this software and associated documentation files (the -"Software"), to deal in the Software without restriction, including -without limitation the rights to use, copy, modify, merge, publish, -distribute, sublicense, and/or sell copies of the Software, and to -permit persons to whom the Software is furnished to do so, subject to -the following conditions: - -The above copyright notice and this permission notice shall be -included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/extra_plugins/task_localsearch/__init__.py b/extra_plugins/task_localsearch/__init__.py deleted file mode 100644 index 9bb0a9e..0000000 --- a/extra_plugins/task_localsearch/__init__.py +++ /dev/null @@ -1,94 +0,0 @@ -# Copyright (c) 2012 Roberto Alsina y otros. - -# 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. - -import json -import os - -from nikola.plugin_categories import LateTask -from nikola.utils import config_changed, copy_tree - -# This is what we need to produce: -#var tipuesearch = {"pages": [ - #{"title": "Tipue Search, a jQuery site search engine", "text": "Tipue - #Search is a site search engine jQuery plugin. It's free for both commercial and - #non-commercial use and released under the MIT License. Tipue Search includes - #features such as word stemming and word replacement.", "tags": "JavaScript", - #"loc": "http://www.tipue.com/search"}, - #{"title": "Tipue Search demo", "text": "Tipue Search demo. Tipue Search is - #a site search engine jQuery plugin.", "tags": "JavaScript", "loc": - #"http://www.tipue.com/search/demo"}, - #{"title": "About Tipue", "text": "Tipue is a small web development/design - #studio based in North London. We've been around for over a decade.", "tags": "", - #"loc": "http://www.tipue.com/about"} -#]}; - - -class Tipue(LateTask): - """Render the blog posts as JSON data.""" - - name = "local_search" - - def gen_tasks(self): - self.site.scan_posts() - - kw = { - "translations": self.site.config['TRANSLATIONS'], - "output_folder": self.site.config['OUTPUT_FOLDER'], - } - - posts = self.site.timeline[:] - dst_path = os.path.join(kw["output_folder"], "assets", "js", - "tipuesearch_content.json") - - def save_data(): - pages = [] - for lang in kw["translations"]: - for post in posts: - data = {} - data["title"] = post.title(lang) - data["text"] = post.text(lang) - data["tags"] = ",".join(post.tags) - data["loc"] = post.permalink(lang) - pages.append(data) - output = json.dumps({"pages": pages}, indent=2) - try: - os.makedirs(os.path.dirname(dst_path)) - except: - pass - with open(dst_path, "wb+") as fd: - fd.write(output) - - yield { - "basename": str(self.name), - "name": os.path.join("assets", "js", "tipuesearch_content.js"), - "targets": [dst_path], - "actions": [(save_data, [])], - 'uptodate': [config_changed(kw)] - } - - # Copy all the assets to the right places - asset_folder = os.path.join(os.path.dirname(__file__), "files") - for task in copy_tree(asset_folder, kw["output_folder"]): - task["basename"] = str(self.name) - yield task diff --git a/extra_plugins/task_localsearch/files/assets/css/loader.gif b/extra_plugins/task_localsearch/files/assets/css/loader.gif deleted file mode 100644 index 9c97738..0000000 Binary files a/extra_plugins/task_localsearch/files/assets/css/loader.gif and /dev/null differ diff --git a/extra_plugins/task_localsearch/files/assets/css/search.gif b/extra_plugins/task_localsearch/files/assets/css/search.gif deleted file mode 100644 index 644bd17..0000000 Binary files a/extra_plugins/task_localsearch/files/assets/css/search.gif and /dev/null differ diff --git a/extra_plugins/task_localsearch/files/assets/css/tipuesearch.css b/extra_plugins/task_localsearch/files/assets/css/tipuesearch.css deleted file mode 100755 index 144c97d..0000000 --- a/extra_plugins/task_localsearch/files/assets/css/tipuesearch.css +++ /dev/null @@ -1,182 +0,0 @@ - -/* -Tipue Search 2.0 -Copyright (c) 2012 Tipue -Tipue Search is released under the MIT License -http://www.tipue.com/search -*/ - - -em -{ - font: inherit; - font-weight: 400; -} -#tipue_search_input -{ - font: 13px/1.5 'open sans', sans-serif; - color: #333; - padding: 7px; - margin: 0; - width: 160px; - border: 1px solid #d3d3d3; - border-radius: 3px; - -moz-appearance: none; - -webkit-appearance: none; - outline: none; -} -#tipue_search_input:focus -{ - border-color: #c3c3c3; - box-shadow: 0 0 3px rgba(0,0,0,.2); -} -#tipue_search_button -{ - width: 60px; - height: 33px; - margin-top: 1px; - border: 1px solid #dcdcdc; - border-radius: 3px; - background: #f1f1f1 url('search.gif') no-repeat center; - outline: none; -} -#tipue_search_button:hover -{ - border: 1px solid #c3c3c3; - -moz-box-shadow: 1px 1px 2px #e3e3e3; - -webkit-box-shadow: 1px 1px 2px #e3e3e3; - box-shadow: 1px 1px 2px #e3e3e3; -} - -#tipue_search_content -{ - clear: left; - width: 650px; - padding: 25px 0 13px 0; - margin: 0; -} -#tipue_search_loading -{ - padding-top: 60px; - background: #fff url('loader.gif') no-repeat left; -} - -#tipue_search_warning_head -{ - font: 14px/1.5 'open sans', sans-serif; - color: #333; -} -#tipue_search_warning -{ - font: 13px/1.5 'open sans', sans-serif; - color: #333; - font-weight: 300; - margin: 13px 0; -} -#tipue_search_warning a -{ - color: #36c; - text-decoration: none; -} -#tipue_search_warning a:hover -{ - padding-bottom: 1px; - border-bottom: 1px solid #ccc; -} - -#tipue_search_results_count -{ - font: 13px/1.5 'open sans', sans-serif; - color: #333; - font-weight: 300; -} - -#tipue_search_content_title -{ - font: 16px/1.5 'open sans', sans-serif; - color: #333; - margin-top: 27px; -} -#tipue_search_content_title a -{ - color: #36c; - text-decoration: none; -} -#tipue_search_content_title a:hover -{ - padding-bottom: 1px; - border-bottom: 1px solid #ccc; -} -#tipue_search_content_text -{ - font: 13px/1.5 'open sans', sans-serif; - color: #333; - font-weight: 300; - line-height: 21px; - padding: 9px 0; -} -#tipue_search_content_loc -{ - font: 13px/1.5 'open sans', sans-serif; - color: #333; - font-weight: 300; -} -#tipue_search_content_loc a -{ - color: #777; - text-decoration: none; -} -#tipue_search_content_loc a:hover -{ - padding-bottom: 1px; - border-bottom: 1px solid #ccc; -} - -#tipue_search_foot -{ - margin: 43px 0 31px 0; -} -#tipue_search_foot_boxes -{ - padding: 0; - margin: 0; - font: 12px/1 'open sans', sans-serif; -} -#tipue_search_foot_boxes li -{ - list-style: none; - margin: 0; - padding: 0; - display: inline; -} -#tipue_search_foot_boxes li a -{ - padding: 7px 10px 8px 10px; - background-color: #f5f5f5; - background: -webkit-linear-gradient(top, #f7f7f7, #f1f1f1); - background: -moz-linear-gradient(top, #f7f7f7, #f1f1f1); - background: -ms-linear-gradient(top, #f7f7f7, #f1f1f1); - background: -o-linear-gradient(top, #f7f7f7, #f1f1f1); - background: linear-gradient(top, #f7f7f7, #f1f1f1); - border: 1px solid #dcdcdc; - border-radius: 3px; - color: #333; - margin-right: 7px; - text-decoration: none; - text-align: center; -} -#tipue_search_foot_boxes li.current -{ - padding: 7px 10px 8px 10px; - background: #fff; - border: 1px solid #dcdcdc; - border-radius: 3px; - color: #333; - margin-right: 7px; - text-align: center; -} -#tipue_search_foot_boxes li a:hover -{ - border: 1px solid #c3c3c3; - box-shadow: 1px 1px 2px #e3e3e3; -} diff --git a/extra_plugins/task_localsearch/files/assets/js/tipuesearch.js b/extra_plugins/task_localsearch/files/assets/js/tipuesearch.js deleted file mode 100644 index 9a8d58e..0000000 --- a/extra_plugins/task_localsearch/files/assets/js/tipuesearch.js +++ /dev/null @@ -1,367 +0,0 @@ - -/* -Tipue Search 2.0 -Copyright (c) 2012 Tipue -Tipue Search is released under the MIT License -http://www.tipue.com/search -*/ - - -(function($) { - - $.fn.tipuesearch = function(options) { - - var set = $.extend( { - - 'show' : 7, - 'newWindow' : false, - 'showURL' : true, - 'minimumLength' : 3, - 'descriptiveWords' : 25, - 'highlightTerms' : true, - 'highlightEveryTerm' : false, - 'mode' : 'static', - 'contentLocation' : 'tipuesearch/tipuesearch_content.json' - - }, options); - - return this.each(function() { - - var tipuesearch_in = { - pages: [] - }; - $.ajaxSetup({ - async: false - }); - - if (set.mode == 'live') - { - for (var i = 0; i < tipuesearch_pages.length; i++) - { - $.get(tipuesearch_pages[i], '', - function (html) - { - var cont = $('*', html).text(); - cont = cont.replace(/\s+/g, ' '); - - var t_1 = html.toLowerCase().indexOf(''); - var t_2 = html.toLowerCase().indexOf('', t_1 + 7); - if (t_1 != -1 && t_2 != -1) - { - var tit = html.slice(t_1 + 7, t_2); - } - else - { - var tit = 'No title'; - } - var t_1 = html.toLowerCase().indexOf('= set.minimumLength) - { - if (replace) - { - var d_r = d; - for (var i = 0; i < d_w.length; i++) - { - for (var f = 0; f < tipuesearch_replace.words.length; f++) - { - if (d_w[i] == tipuesearch_replace.words[f].word) - { - d = d.replace(d_w[i], tipuesearch_replace.words[f].replace_with); - show_replace = true; - } - } - } - d_w = d.split(' '); - } - - var d_t = d; - for (var i = 0; i < d_w.length; i++) - { - for (var f = 0; f < tipuesearch_stem.words.length; f++) - { - if (d_w[i] == tipuesearch_stem.words[f].word) - { - d_t = d_t + ' ' + tipuesearch_stem.words[f].stem; - } - } - } - d_w = d_t.split(' '); - - var c = 0; - found = new Array(); - for (var i = 0; i < tipuesearch_in.pages.length; i++) - { - var score = 10000000; - var s_t = tipuesearch_in.pages[i].text; - for (var f = 0; f < d_w.length; f++) - { - var pat = new RegExp(d_w[f], 'i'); - if (tipuesearch_in.pages[i].title.search(pat) != -1) - { - score -= (2000 - i); - } - if (tipuesearch_in.pages[i].text.search(pat) != -1) - { - score -= (1500 - i); - } - - if (set.highlightTerms) - { - if (set.highlightEveryTerm) - { - var patr = new RegExp('(' + d_w[f] + ')', 'gi'); - } - else - { - var patr = new RegExp('(' + d_w[f] + ')', 'i'); - } - s_t = s_t.replace(patr, "$1"); - } - - if (tipuesearch_in.pages[i].tags.search(pat) != -1) - { - score -= (1000 - i); - } - } - if (score < 10000000) - { - found[c++] = score + '^' + tipuesearch_in.pages[i].title + '^' + s_t + '^' + tipuesearch_in.pages[i].loc; - } - } - - if (c != 0) - { - if (show_replace == 1) - { - out += '
Showing results for ' + d + '
'; - out += '
Show results for ' + d_r + '
'; - } - if (c == 1) - { - out += '
1 result
'; - } - else - { - c_c = c.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ","); - out += '
' + c_c + ' results
'; - } - - found.sort(); - var l_o = 0; - for (var i = 0; i < found.length; i++) - { - var fo = found[i].split('^'); - if (l_o >= start && l_o < set.show + start) - { - out += ''; - - var t = fo[2]; - var t_d = ''; - var t_w = t.split(' '); - if (t_w.length < set.descriptiveWords) - { - t_d = t; - } - else - { - for (var f = 0; f < set.descriptiveWords; f++) - { - t_d += t_w[f] + ' '; - } - } - t_d = $.trim(t_d); - if (t_d.charAt(t_d.length - 1) != '.') - { - t_d += ' ...'; - } - out += '
' + t_d + '
'; - - if (set.showURL) - { - out += ''; - } - } - l_o++; - } - - if (c > set.show) - { - var pages = Math.ceil(c / set.show); - var page = (start / set.show); - out += '
    '; - - if (start > 0) - { - out += '
  • « Previous
  • '; - } - - if (page <= 4) - { - var p_b = pages; - if (pages > 5) - { - p_b = 5; - } - for (var f = 0; f < p_b; f++) - { - if (f == page) - { - out += '
  • ' + (f + 1) + '
  • '; - } - else - { - out += '
  • ' + (f + 1) + '
  • '; - } - } - } - else - { - var p_b = pages + 4; - if (p_b > pages) - { - p_b = pages; - } - for (var f = page; f < p_b; f++) - { - if (f == page) - { - out += '
  • ' + (f + 1) + '
  • '; - } - else - { - out += '
  • ' + (f + 1) + '
  • '; - } - } - } - - if (page + 1 != pages) - { - out += '
  • Next »
  • '; - } - - out += '
'; - } - } - else - { - out += '
Nothing found
'; - } - } - else - { - if (show_stop) - { - out += '
Nothing found
Common words are largely ignored
'; - } - else - { - out += '
Search too short
'; - if (set.minimumLength == 1) - { - out += '
Should be one character or more
'; - } - else - { - out += '
Should be ' + set.minimumLength + ' characters or more
'; - } - } - } - - $('#tipue_search_content').html(out); - $('#tipue_search_content').slideDown(200); - - $('#tipue_search_replaced').click(function() - { - getTipueSearch(0, false); - }); - - $('.tipue_search_foot_box').click(function() - { - var id_v = $(this).attr('id'); - var id_a = id_v.split('_'); - - getTipueSearch(parseInt(id_a[0]), id_a[1]); - }); - } - - }); - }; - -})(jQuery); - diff --git a/extra_plugins/task_localsearch/files/assets/js/tipuesearch_set.js b/extra_plugins/task_localsearch/files/assets/js/tipuesearch_set.js deleted file mode 100644 index 8989c3c..0000000 --- a/extra_plugins/task_localsearch/files/assets/js/tipuesearch_set.js +++ /dev/null @@ -1,28 +0,0 @@ - -/* -Tipue Search 2.0 -Copyright (c) 2012 Tipue -Tipue Search is released under the MIT License -http://www.tipue.com/search -*/ - - -var tipuesearch_stop_words = ["and", "be", "by", "do", "for", "he", "how", "if", "is", "it", "my", "not", "of", "or", "the", "to", "up", "what", "when"]; - -var tipuesearch_replace = {"words": [ - {"word": "tipua", replace_with: "tipue"}, - {"word": "javscript", replace_with: "javascript"} -]}; - -var tipuesearch_stem = {"words": [ - {"word": "e-mail", stem: "email"}, - {"word": "javascript", stem: "script"}, - {"word": "javascript", stem: "js"} -]}; - -/* -Include the following variable listing the pages on your site if you're using Live mode -*/ - -var tipuesearch_pages = ["http://foo.com/", "http://foo.com/about/", "http://foo.com/blog/", "http://foo.com/tos/"]; - diff --git a/extra_plugins/task_mustache.plugin b/extra_plugins/task_mustache.plugin deleted file mode 100644 index 6103936..0000000 --- a/extra_plugins/task_mustache.plugin +++ /dev/null @@ -1,10 +0,0 @@ -[Core] -Name = render_mustache -Module = task_mustache - -[Documentation] -Author = Roberto Alsina -Version = 0.1 -Website = http://nikola.ralsina.com.ar -Description = Generates the blog's index pages in json. - diff --git a/extra_plugins/task_mustache/__init__.py b/extra_plugins/task_mustache/__init__.py deleted file mode 100644 index 8b5ec13..0000000 --- a/extra_plugins/task_mustache/__init__.py +++ /dev/null @@ -1,189 +0,0 @@ -# Copyright (c) 2012 Roberto Alsina y otros. - -# 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. - -import json -import os - -from nikola.plugin_categories import Task -from nikola.utils import config_changed, copy_file - - -class Mustache(Task): - """Render the blog posts as JSON data.""" - - name = "render_mustache" - - def gen_tasks(self): - self.site.scan_posts() - - kw = { - "translations": self.site.config['TRANSLATIONS'], - "index_display_post_count": - self.site.config['INDEX_DISPLAY_POST_COUNT'], - "messages": self.site.MESSAGES, - "index_teasers": self.site.config['INDEX_TEASERS'], - "output_folder": self.site.config['OUTPUT_FOLDER'], - "filters": self.site.config['FILTERS'], - "blog_title": self.site.config['BLOG_TITLE'], - "content_footer": self.site.config['CONTENT_FOOTER'], - } - - # TODO: timeline is global, get rid of it - posts = [x for x in self.site.timeline if x.use_in_feeds] - if not posts: - yield { - 'basename': 'render_mustache', - 'actions': [], - } - return - - def write_file(path, post, lang): - - # Prev/Next links - prev_link = False - if post.prev_post: - prev_link = post.prev_post.permalink(lang).replace(".html", - ".json") - next_link = False - if post.next_post: - next_link = post.next_post.permalink(lang).replace(".html", - ".json") - data = {} - - # Configuration - for k, v in self.site.config.items(): - # FIXME: not py3 ready - if isinstance(v, (str, unicode)): # NOQA - data[k] = v - - # Tag data - tags = [] - for tag in post.tags: - tags.append({'name': tag, 'link': self.site.link("tag", tag, - lang)}) - data.update({ - "tags": tags, - "tags?": True if tags else False, - }) - - # Template strings - for k, v in kw["messages"][lang].items(): - data["message_" + k] = v - - # Post data - data.update({ - "title": post.title(lang), - "text": post.text(lang), - "prev": prev_link, - "next": next_link, - "date": - post.date.strftime(self.site.GLOBAL_CONTEXT['date_format']), - }) - - # Disqus comments - data["disqus_html"] = ('
' - 'comments powered by DISQUS' % - (self.site.config['DISQUS_FORUM'], - post.permalink(absolute=True))) - - # Post translations - translations = [] - for langname in kw["translations"]: - if langname == lang: - continue - translations.append({'name': - kw["messages"][langname]["Read in English"], - 'link': "javascript:load_data('%s');" - % post.permalink(langname).replace( - ".html", ".json")}) - data["translations"] = translations - - try: - os.makedirs(os.path.dirname(path)) - except: - pass - with open(path, 'wb+') as fd: - fd.write(json.dumps(data)) - - for lang in kw["translations"]: - for i, post in enumerate(posts): - out_path = post.destination_path(lang, ".json") - out_file = os.path.join(kw['output_folder'], out_path) - task = { - 'basename': 'render_mustache', - 'name': str(out_path), - 'file_dep': post.fragment_deps(lang), - 'targets': [out_file], - 'actions': [(write_file, (out_file, post, lang))], - 'task_dep': ['render_posts'], - } - yield task - - if posts: - first_post_data = posts[0].permalink( - self.site.config["DEFAULT_LANG"]).replace(".html", ".json") - - # Copy mustache template - src = os.path.join(os.path.dirname(__file__), 'mustache-template.html') - dst = os.path.join(kw['output_folder'], 'mustache-template.html') - yield { - 'basename': 'render_mustache', - 'name': 'mustache-template.html', - 'targets': [dst], - 'file_dep': [src], - 'actions': [(copy_file, (src, dst))], - } - - # Copy mustache.html with the right starting file in it - src = os.path.join(os.path.dirname(__file__), 'mustache.html') - dst = os.path.join(kw['output_folder'], 'mustache.html') - - def copy_mustache(): - with open(src, 'rb') as in_file: - with open(dst, 'wb+') as out_file: - data = in_file.read().replace('{{first_post_data}}', - first_post_data) - out_file.write(data) - yield { - 'basename': 'render_mustache', - 'name': 'mustache.html', - 'targets': [dst], - 'file_dep': [src], - 'uptodate': [config_changed({1: first_post_data})], - 'actions': [(copy_mustache, [])], - } diff --git a/extra_plugins/task_mustache/mustache-template.html b/extra_plugins/task_mustache/mustache-template.html deleted file mode 100644 index 7f2b34c..0000000 --- a/extra_plugins/task_mustache/mustache-template.html +++ /dev/null @@ -1,29 +0,0 @@ - diff --git a/extra_plugins/task_mustache/mustache.html b/extra_plugins/task_mustache/mustache.html deleted file mode 100644 index 5dbebef..0000000 --- a/extra_plugins/task_mustache/mustache.html +++ /dev/null @@ -1,36 +0,0 @@ - - - - - - - - - - - - - - - - - - diff --git a/install_requirements.py b/install_requirements.py deleted file mode 100644 index bea8813..0000000 --- a/install_requirements.py +++ /dev/null @@ -1,35 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- -""" -Helper tool to install the right requirements using pip based on the -running Python version. It requires ``pip`` to be installed and found -in the $PATH. - -:author: Niko Wenselowski -""" -from __future__ import unicode_literals, print_function - -import sys -import os - - -def get_requirements_file_path(): - """Returns the absolute path to the correct requirements file.""" - directory = os.path.dirname(__file__) - - if sys.version_info[0] == 3: - requirements_file = 'requirements-3.txt' - else: - requirements_file = 'requirements.txt' - - return os.path.join(directory, requirements_file) - - -def main(): - requirements_file = get_requirements_file_path() - print('Installing requirements from %s' % requirements_file) - os.system('pip install -r %s --use-mirrors' % requirements_file) - - -if __name__ == '__main__': - main() diff --git a/nikola/conf.py.in b/nikola/conf.py.in index 7d75295..c2bf8ff 100644 --- a/nikola/conf.py.in +++ b/nikola/conf.py.in @@ -17,7 +17,7 @@ BLOG_TITLE = "${BLOG_TITLE}" 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} +# BASE_URL = "${SITE_URL}" BLOG_EMAIL = "${BLOG_EMAIL}" BLOG_DESCRIPTION = "${BLOG_DESCRIPTION}" @@ -47,7 +47,7 @@ DEFAULT_LANG = "${DEFAULT_LANG}" # The format is {"translationcode" : "path/to/translation" } # the path will be used as a prefix for the generated pages location TRANSLATIONS = { - "${DEFAULT_LANG}": "", + DEFAULT_LANG: "", # Example for another language: # "es": "./es", } @@ -58,6 +58,7 @@ SIDEBAR_LINKS = { DEFAULT_LANG: ( ('/archive.html', 'Archives'), ('/categories/index.html', 'Tags'), + ('/rss.xml', 'RSS'), ), } @@ -109,6 +110,12 @@ post_compilers = ${POST_COMPILERS} # Set to False for two-file posts, with separate metadata. # ONE_FILE_POSTS = True +# If this is set to True, then posts that are not translated to a language +# LANG will not be visible at all in the pages in that language. +# If set to False, the DEFAULT_LANG version will be displayed for +# untranslated posts. +# HIDE_UNTRANSLATED_POSTS = False + # Paths for different autogenerated bits. These are combined with the # translation paths. @@ -124,11 +131,16 @@ post_compilers = ${POST_COMPILERS} # Final location is output / TRANSLATION[lang] / INDEX_PATH / index-*.html # INDEX_PATH = "" + +# Create per-month archives instead of per-year +# CREATE_MONTHLY_ARCHIVE = False # Final locations for the archives are: # output / TRANSLATION[lang] / ARCHIVE_PATH / ARCHIVE_FILENAME # output / TRANSLATION[lang] / ARCHIVE_PATH / YEAR / index.html +# output / TRANSLATION[lang] / ARCHIVE_PATH / YEAR / MONTH / index.html # ARCHIVE_PATH = "" # ARCHIVE_FILENAME = "archive.html" + # Final locations are: # output / TRANSLATION[lang] / RSS_PATH / rss.xml # RSS_PATH = "" @@ -208,11 +220,17 @@ post_compilers = ${POST_COMPILERS} # INDEXES_TITLE = "" # If this is empty, the default is BLOG_TITLE # INDEXES_PAGES = "" # If this is empty, the default is 'old posts page %d' translated -# Name of the theme to use. Themes are located in themes/theme_name +# Name of the theme to use. # THEME = 'site' +# Color scheme to be used for code blocks. If your theme provide "assets/css/code.css" this +# is ignored. +# Can be any of autumn borland bw colorful default emacs friendly fruity manni monokai +# murphy native pastie perldoc rrt tango trac vim vs +# CODE_COLOR_SCHEME = default + # If you use 'site-reveal' theme you can select several subthemes -# THEME_REVEAL_CONGIF_SUBTHEME = 'sky' # You can also use: beige/serif/simple/night/default +# THEME_REVEAL_CONGIF_SUBTHEME = 'sky' # You can also use: beige/serif/simple/night/default # Again, if you use 'site-reveal' theme you can select several transitions between the slides # THEME_REVEAL_CONGIF_TRANSITION = 'cube' # You can also use: page/concave/linear/none/default @@ -261,6 +279,11 @@ CONTENT_FOOTER = CONTENT_FOOTER.format(email=BLOG_EMAIL, # Enable comments on picture gallery pages? # COMMENTS_IN_GALLERIES = False +# If a link ends in /index.html, drop the index.html part. +# http://mysite/foo/bar/index.html => http://mysite/foo/bar/ +# Default = False +# STRIP_INDEX_HTML = False + # Do you want a add a Mathjax config file? # MATHJAX_CONFIG = "" @@ -328,6 +351,9 @@ CONTENT_FOOTER = CONTENT_FOOTER.format(email=BLOG_EMAIL, # external resources. # USE_CDN = False +# Extra things you want in the pages HEAD tag. This will be added right +# before +# EXTRA_HEAD_DATA = "" # Google analytics or whatever else you use. Added to the bottom of # in the default template (base.tmpl). # ANALYTICS = "" @@ -379,6 +405,15 @@ CONTENT_FOOTER = CONTENT_FOOTER.format(email=BLOG_EMAIL, # Plugins you don't want to use. Be careful :-) # DISABLED_PLUGINS = ["render_galleries"] +# Experimental plugins - use at your own risk. +# They probably need some manual adjustments - please see their respective readme. +# ENABLED_EXTRAS = [ +# 'planetoid', +# 'ipynb', +# 'localsearch', +# 'mustache', +# ] + # Put in global_context things you want available on all your templates. # It can be anything, data, functions, modules, etc. diff --git a/nikola/data/themes/default/assets/css/code.css b/nikola/data/themes/default/assets/css/code.css deleted file mode 100644 index b1d7ace..0000000 --- a/nikola/data/themes/default/assets/css/code.css +++ /dev/null @@ -1,62 +0,0 @@ -pre { word-break: pre; white-space: pre; word-wrap: pre; overflow: auto; max-width: 100%;} -td.linenos { vertical-align: top; width: 4em;} -div.code > pre, .code -{ background: #f8f8f8; white-space: pre;} -.code .c { color: #008800; font-style: italic } /* Comment */ -.code .err { border: 1px solid #FF0000 } /* Error */ -.code .k { color: #AA22FF; font-weight: bold } /* Keyword */ -.code .o { color: #666666 } /* Operator */ -.code .cm { color: #008800; font-style: italic } /* Comment.Multiline */ -.code .cp { color: #008800 } /* Comment.Preproc */ -.code .c1 { color: #008800; font-style: italic } /* Comment.Single */ -.code .cs { color: #008800; font-weight: bold } /* Comment.Special */ -.code .gd { color: #A00000 } /* Generic.Deleted */ -.code .ge { font-style: italic } /* Generic.Emph */ -.code .gr { color: #FF0000 } /* Generic.Error */ -.code .gh { color: #000080; font-weight: bold } /* Generic.Heading */ -.code .gi { color: #00A000 } /* Generic.Inserted */ -.code .go { color: #808080 } /* Generic.Output */ -.code .gp { color: #000080; font-weight: bold } /* Generic.Prompt */ -.code .gs { font-weight: bold } /* Generic.Strong */ -.code .gu { color: #800080; font-weight: bold } /* Generic.Subheading */ -.code .gt { color: #0040D0 } /* Generic.Traceback */ -.code .kc { color: #AA22FF; font-weight: bold } /* Keyword.Constant */ -.code .kd { color: #AA22FF; font-weight: bold } /* Keyword.Declaration */ -.code .kp { color: #AA22FF } /* Keyword.Pseudo */ -.code .kr { color: #AA22FF; font-weight: bold } /* Keyword.Reserved */ -.code .kt { color: #AA22FF; font-weight: bold } /* Keyword.Type */ -.code .m { color: #666666 } /* Literal.Number */ -.code .s { color: #BB4444 } /* Literal.String */ -.code .na { color: #BB4444 } /* Name.Attribute */ -.code .nb { color: #AA22FF } /* Name.Builtin */ -.code .nc { color: #0000FF } /* Name.Class */ -.code .no { color: #880000 } /* Name.Constant */ -.code .nd { color: #AA22FF } /* Name.Decorator */ -.code .ni { color: #999999; font-weight: bold } /* Name.Entity */ -.code .ne { color: #D2413A; font-weight: bold } /* Name.Exception */ -.code .nf { color: #00A000 } /* Name.Function */ -.code .nl { color: #A0A000 } /* Name.Label */ -.code .nn { color: #0000FF; font-weight: bold } /* Name.Namespace */ -.code .nt { color: #008000; font-weight: bold } /* Name.Tag */ -.code .nv { color: #B8860B } /* Name.Variable */ -.code .ow { color: #AA22FF; font-weight: bold } /* Operator.Word */ -.code .mf { color: #666666 } /* Literal.Number.Float */ -.code .mh { color: #666666 } /* Literal.Number.Hex */ -.code .mi { color: #666666 } /* Literal.Number.Integer */ -.code .mo { color: #666666 } /* Literal.Number.Oct */ -.code .sb { color: #BB4444 } /* Literal.String.Backtick */ -.code .sc { color: #BB4444 } /* Literal.String.Char */ -.code .sd { color: #BB4444; font-style: italic } /* Literal.String.Doc */ -.code .s2 { color: #BB4444 } /* Literal.String.Double */ -.code .se { color: #BB6622; font-weight: bold } /* Literal.String.Escape */ -.code .sh { color: #BB4444 } /* Literal.String.Heredoc */ -.code .si { color: #BB6688; font-weight: bold } /* Literal.String.Interpol */ -.code .sx { color: #008000 } /* Literal.String.Other */ -.code .sr { color: #BB6688 } /* Literal.String.Regex */ -.code .s1 { color: #BB4444 } /* Literal.String.Single */ -.code .ss { color: #B8860B } /* Literal.String.Symbol */ -.code .bp { color: #AA22FF } /* Name.Builtin.Pseudo */ -.code .vc { color: #B8860B } /* Name.Variable.Class */ -.code .vg { color: #B8860B } /* Name.Variable.Global */ -.code .vi { color: #B8860B } /* Name.Variable.Instance */ -.code .il { color: #666666 } /* Literal.Number.Integer.Long */ diff --git a/nikola/data/themes/default/assets/css/slides.css b/nikola/data/themes/default/assets/css/slides.css deleted file mode 100644 index 272c83e..0000000 --- a/nikola/data/themes/default/assets/css/slides.css +++ /dev/null @@ -1,11 +0,0 @@ -.slides_container { - display: block; - margin-left: auto; - margin-right: auto; - max-width: 80%; - width: 400px; - height: 300px; -} -.slide-current { - font-weight: bold; -} diff --git a/nikola/data/themes/default/assets/css/theme.css b/nikola/data/themes/default/assets/css/theme.css index 0523ce9..08a71f3 100644 --- a/nikola/data/themes/default/assets/css/theme.css +++ b/nikola/data/themes/default/assets/css/theme.css @@ -60,3 +60,14 @@ blockquote p, blockquote { font-weight: 300; line-height: 1.25; } + +ul.bricks > li { + display: inline; + background-color: lightblue; + padding: 8px; + border-radius: 5px; + line-height: 3; + white-space:nowrap; + margin: 3px; +} + diff --git a/nikola/data/themes/default/assets/js/slides.jquery.js b/nikola/data/themes/default/assets/js/slides.jquery.js deleted file mode 100755 index f2e09c8..0000000 --- a/nikola/data/themes/default/assets/js/slides.jquery.js +++ /dev/null @@ -1,555 +0,0 @@ -/* -* Slides, A Slideshow Plugin for jQuery -* Intructions: http://slidesjs.com -* By: Nathan Searles, http://nathansearles.com -* Version: 1.1.9 -* Updated: September 5th, 2011 -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. -*/ -(function($){ - $.fn.slides = function( option ) { - // override defaults with specified option - option = $.extend( {}, $.fn.slides.option, option ); - - return this.each(function(){ - // wrap slides in control container, make sure slides are block level - $('.' + option.container, $(this)).children().wrapAll('
'); - - var elem = $(this), - control = $('.slides_control',elem), - total = control.children().size(), - width = control.children().outerWidth(), - height = control.children().outerHeight(), - start = option.start - 1, - effect = option.effect.indexOf(',') < 0 ? option.effect : option.effect.replace(' ', '').split(',')[0], - paginationEffect = option.effect.indexOf(',') < 0 ? effect : option.effect.replace(' ', '').split(',')[1], - next = 0, prev = 0, number = 0, current = 0, loaded, active, clicked, position, direction, imageParent, pauseTimeout, playInterval; - - // is there only one slide? - if (total < 2) { - // Fade in .slides_container - $('.' + option.container, $(this)).fadeIn(option.fadeSpeed, option.fadeEasing, function(){ - // let the script know everything is loaded - loaded = true; - // call the loaded funciton - option.slidesLoaded(); - }); - // Hide the next/previous buttons - $('.' + option.next + ', .' + option.prev).fadeOut(0); - return false; - } - - // animate slides - function animate(direction, effect, clicked) { - if (!active && loaded) { - active = true; - // start of animation - option.animationStart(current + 1); - switch(direction) { - case 'next': - // change current slide to previous - prev = current; - // get next from current + 1 - next = current + 1; - // if last slide, set next to first slide - next = total === next ? 0 : next; - // set position of next slide to right of previous - position = width*2; - // distance to slide based on width of slides - direction = -width*2; - // store new current slide - current = next; - break; - case 'prev': - // change current slide to previous - prev = current; - // get next from current - 1 - next = current - 1; - // if first slide, set next to last slide - next = next === -1 ? total-1 : next; - // set position of next slide to left of previous - position = 0; - // distance to slide based on width of slides - direction = 0; - // store new current slide - current = next; - break; - case 'pagination': - // get next from pagination item clicked, convert to number - next = parseInt(clicked,10); - // get previous from pagination item with class of current - prev = $('.' + option.paginationClass + ' li.'+ option.currentClass +' a', elem).attr('href').match('[^#/]+$'); - // if next is greater then previous set position of next slide to right of previous - if (next > prev) { - position = width*2; - direction = -width*2; - } else { - // if next is less then previous set position of next slide to left of previous - position = 0; - direction = 0; - } - // store new current slide - current = next; - break; - } - - // fade animation - if (effect === 'fade') { - // fade animation with crossfade - if (option.crossfade) { - // put hidden next above current - control.children(':eq('+ next +')', elem).css({ - zIndex: 10 - // fade in next - }).fadeIn(option.fadeSpeed, option.fadeEasing, function(){ - if (option.autoHeight) { - // animate container to height of next - control.animate({ - height: control.children(':eq('+ next +')', elem).outerHeight() - }, option.autoHeightSpeed, function(){ - // hide previous - control.children(':eq('+ prev +')', elem).css({ - display: 'none', - zIndex: 0 - }); - // reset z index - control.children(':eq('+ next +')', elem).css({ - zIndex: 0 - }); - // end of animation - option.animationComplete(next + 1); - active = false; - }); - } else { - // hide previous - control.children(':eq('+ prev +')', elem).css({ - display: 'none', - zIndex: 0 - }); - // reset zindex - control.children(':eq('+ next +')', elem).css({ - zIndex: 0 - }); - // end of animation - option.animationComplete(next + 1); - active = false; - } - }); - } else { - // fade animation with no crossfade - control.children(':eq('+ prev +')', elem).fadeOut(option.fadeSpeed, option.fadeEasing, function(){ - // animate to new height - if (option.autoHeight) { - control.animate({ - // animate container to height of next - height: control.children(':eq('+ next +')', elem).outerHeight() - }, option.autoHeightSpeed, - // fade in next slide - function(){ - control.children(':eq('+ next +')', elem).fadeIn(option.fadeSpeed, option.fadeEasing); - }); - } else { - // if fixed height - control.children(':eq('+ next +')', elem).fadeIn(option.fadeSpeed, option.fadeEasing, function(){ - // fix font rendering in ie, lame - if($.browser.msie) { - $(this).get(0).style.removeAttribute('filter'); - } - }); - } - // end of animation - option.animationComplete(next + 1); - active = false; - }); - } - // slide animation - } else { - // move next slide to right of previous - control.children(':eq('+ next +')').css({ - left: position, - display: 'block' - }); - // animate to new height - if (option.autoHeight) { - control.animate({ - left: direction, - height: control.children(':eq('+ next +')').outerHeight() - },option.slideSpeed, option.slideEasing, function(){ - control.css({ - left: -width - }); - control.children(':eq('+ next +')').css({ - left: width, - zIndex: 5 - }); - // reset previous slide - control.children(':eq('+ prev +')').css({ - left: width, - display: 'none', - zIndex: 0 - }); - // end of animation - option.animationComplete(next + 1); - active = false; - }); - // if fixed height - } else { - // animate control - control.animate({ - left: direction - },option.slideSpeed, option.slideEasing, function(){ - // after animation reset control position - control.css({ - left: -width - }); - // reset and show next - control.children(':eq('+ next +')').css({ - left: width, - zIndex: 5 - }); - // reset previous slide - control.children(':eq('+ prev +')').css({ - left: width, - display: 'none', - zIndex: 0 - }); - // end of animation - option.animationComplete(next + 1); - active = false; - }); - } - } - // set current state for pagination - if (option.pagination) { - // remove current class from all - $('.'+ option.paginationClass +' li.' + option.currentClass, elem).removeClass(option.currentClass); - // add current class to next - $('.' + option.paginationClass + ' li:eq('+ next +')', elem).addClass(option.currentClass); - } - } - } // end animate function - - function stop() { - // clear interval from stored id - clearInterval(elem.data('interval')); - } - - function pause() { - if (option.pause) { - // clear timeout and interval - clearTimeout(elem.data('pause')); - clearInterval(elem.data('interval')); - // pause slide show for option.pause amount - pauseTimeout = setTimeout(function() { - // clear pause timeout - clearTimeout(elem.data('pause')); - // start play interval after pause - playInterval = setInterval( function(){ - animate("next", effect); - },option.play); - // store play interval - elem.data('interval',playInterval); - },option.pause); - // store pause interval - elem.data('pause',pauseTimeout); - } else { - // if no pause, just stop - stop(); - } - } - - // 2 or more slides required - if (total < 2) { - return; - } - - // error corection for start slide - if (start < 0) { - start = 0; - } - - if (start > total) { - start = total - 1; - } - - // change current based on start option number - if (option.start) { - current = start; - } - - // randomizes slide order - if (option.randomize) { - control.randomize(); - } - - // make sure overflow is hidden, width is set - $('.' + option.container, elem).css({ - overflow: 'hidden', - // fix for ie - position: 'relative' - }); - - // set css for slides - control.children().css({ - position: 'absolute', - top: 0, - left: control.children().outerWidth(), - zIndex: 0, - display: 'none' - }); - - // set css for control div - control.css({ - position: 'relative', - // size of control 3 x slide width - width: (width * 3), - // set height to slide height - height: height, - // center control to slide - left: -width - }); - - // show slides - $('.' + option.container, elem).css({ - display: 'block' - }); - - // if autoHeight true, get and set height of first slide - if (option.autoHeight) { - control.children().css({ - height: 'auto' - }); - control.animate({ - height: control.children(':eq('+ start +')').outerHeight() - },option.autoHeightSpeed); - } - - // checks if image is loaded - if (option.preload && control.find('img:eq(' + start + ')').length) { - // adds preload image - $('.' + option.container, elem).css({ - background: 'url(' + option.preloadImage + ') no-repeat 50% 50%' - }); - - // gets image src, with cache buster - var img = control.find('img:eq(' + start + ')').attr('src') + '?' + (new Date()).getTime(); - - // check if the image has a parent - if ($('img', elem).parent().attr('class') != 'slides_control') { - // If image has parent, get tag name - imageParent = control.children(':eq(0)')[0].tagName.toLowerCase(); - } else { - // Image doesn't have parent, use image tag name - imageParent = control.find('img:eq(' + start + ')'); - } - - // checks if image is loaded - control.find('img:eq(' + start + ')').attr('src', img).load(function() { - // once image is fully loaded, fade in - control.find(imageParent + ':eq(' + start + ')').fadeIn(option.fadeSpeed, option.fadeEasing, function(){ - $(this).css({ - zIndex: 5 - }); - // removes preload image - $('.' + option.container, elem).css({ - background: '' - }); - // let the script know everything is loaded - loaded = true; - // call the loaded funciton - option.slidesLoaded(); - }); - }); - } else { - // if no preloader fade in start slide - control.children(':eq(' + start + ')').fadeIn(option.fadeSpeed, option.fadeEasing, function(){ - // let the script know everything is loaded - loaded = true; - // call the loaded funciton - option.slidesLoaded(); - }); - } - - // click slide for next - if (option.bigTarget) { - // set cursor to pointer - control.children().css({ - cursor: 'pointer' - }); - // click handler - control.children().click(function(){ - // animate to next on slide click - animate('next', effect); - return false; - }); - } - - // pause on mouseover - if (option.hoverPause && option.play) { - control.bind('mouseover',function(){ - // on mouse over stop - stop(); - }); - control.bind('mouseleave',function(){ - // on mouse leave start pause timeout - pause(); - }); - } - - // generate next/prev buttons - if (option.generateNextPrev) { - $('.' + option.container, elem).after('Prev'); - $('.' + option.prev, elem).after('Next'); - } - - // next button - $('.' + option.next ,elem).click(function(e){ - e.preventDefault(); - if (option.play) { - pause(); - } - animate('next', effect); - }); - - // previous button - $('.' + option.prev, elem).click(function(e){ - e.preventDefault(); - if (option.play) { - pause(); - } - animate('prev', effect); - }); - - // generate pagination - if (option.generatePagination) { - // create unordered list - if (option.prependPagination) { - elem.prepend('
    '); - } else { - elem.append('
      '); - } - // for each slide create a list item and link - control.children().each(function(){ - $('.' + option.paginationClass, elem).append('
    • '+ (number+1) +'
    • '); - number++; - }); - } else { - // if pagination exists, add href w/ value of item number to links - $('.' + option.paginationClass + ' li a', elem).each(function(){ - $(this).attr('href', '#' + number); - number++; - }); - } - - // add current class to start slide pagination - $('.' + option.paginationClass + ' li:eq('+ start +')', elem).addClass(option.currentClass); - - // click handling - $('.' + option.paginationClass + ' li a', elem ).click(function(){ - // pause slideshow - if (option.play) { - pause(); - } - // get clicked, pass to animate function - clicked = $(this).attr('href').match('[^#/]+$'); - // if current slide equals clicked, don't do anything - if (current != clicked) { - animate('pagination', paginationEffect, clicked); - } - return false; - }); - - // click handling - $('a.link', elem).click(function(){ - // pause slideshow - if (option.play) { - pause(); - } - // get clicked, pass to animate function - clicked = $(this).attr('href').match('[^#/]+$') - 1; - // if current slide equals clicked, don't do anything - if (current != clicked) { - animate('pagination', paginationEffect, clicked); - } - return false; - }); - - if (option.play) { - // set interval - playInterval = setInterval(function() { - animate('next', effect); - }, option.play); - // store interval id - elem.data('interval',playInterval); - } - }); - }; - - // default options - $.fn.slides.option = { - preload: false, // boolean, Set true to preload images in an image based slideshow - preloadImage: '/img/loading.gif', // string, Name and location of loading image for preloader. Default is "/img/loading.gif" - container: 'slides_container', // string, Class name for slides container. Default is "slides_container" - generateNextPrev: false, // boolean, Auto generate next/prev buttons - next: 'next', // string, Class name for next button - prev: 'prev', // string, Class name for previous button - pagination: true, // boolean, If you're not using pagination you can set to false, but don't have to - generatePagination: true, // boolean, Auto generate pagination - prependPagination: false, // boolean, prepend pagination - paginationClass: 'pagination', // string, Class name for pagination - currentClass: 'current', // string, Class name for current class - fadeSpeed: 350, // number, Set the speed of the fading animation in milliseconds - fadeEasing: '', // string, must load jQuery's easing plugin before http://gsgd.co.uk/sandbox/jquery/easing/ - slideSpeed: 350, // number, Set the speed of the sliding animation in milliseconds - slideEasing: '', // string, must load jQuery's easing plugin before http://gsgd.co.uk/sandbox/jquery/easing/ - start: 1, // number, Set the speed of the sliding animation in milliseconds - effect: 'slide', // string, '[next/prev], [pagination]', e.g. 'slide, fade' or simply 'fade' for both - crossfade: false, // boolean, Crossfade images in a image based slideshow - randomize: false, // boolean, Set to true to randomize slides - play: 0, // number, Autoplay slideshow, a positive number will set to true and be the time between slide animation in milliseconds - pause: 0, // number, Pause slideshow on click of next/prev or pagination. A positive number will set to true and be the time of pause in milliseconds - hoverPause: false, // boolean, Set to true and hovering over slideshow will pause it - autoHeight: false, // boolean, Set to true to auto adjust height - autoHeightSpeed: 350, // number, Set auto height animation time in milliseconds - bigTarget: false, // boolean, Set to true and the whole slide will link to next slide on click - animationStart: function(){}, // Function called at the start of animation - animationComplete: function(){}, // Function called at the completion of animation - slidesLoaded: function() {} // Function is called when slides is fully loaded - }; - - // Randomize slide order on load - $.fn.randomize = function(callback) { - function randomizeOrder() { return(Math.round(Math.random())-0.5); } - return($(this).each(function() { - var $this = $(this); - var $children = $this.children(); - var childCount = $children.length; - if (childCount > 1) { - $children.hide(); - var indices = []; - for (i=0;i +${set_locale(lang)} ${html_head()} <%block name="extra_head"> + ${extra_head_data} %if add_this_buttons: @@ -22,7 +24,7 @@ <%block name="belowtitle"> %if len(translations) > 1: - ${(messages[lang][u"Also available in"])}:  + ${(messages("Also available in"))}:  ${html_translations()} %endif @@ -52,7 +54,7 @@
      - ${analytics} ${late_load_js()} + ${analytics} diff --git a/nikola/data/themes/default/templates/base_helper.tmpl b/nikola/data/themes/default/templates/base_helper.tmpl index eb22905..a833c51 100644 --- a/nikola/data/themes/default/templates/base_helper.tmpl +++ b/nikola/data/themes/default/templates/base_helper.tmpl @@ -22,7 +22,6 @@ - %if has_custom_css: @@ -34,9 +33,13 @@ %if rss_link: ${rss_link} %else: - %for language in translations: - - %endfor + %if len(translations) > 1: + %for language in translations: + + %endfor + %else: + + %endif %endif %if favicons: %for name, file, size in favicons: @@ -63,7 +66,6 @@ %endif - %endif @@ -98,7 +100,7 @@ <%def name="html_translations()"> %for langname in translations.keys(): %if langname != lang: - ${messages[langname]["LANGUAGE"]} + ${messages("LANGUAGE", langname)} %endif %endfor diff --git a/nikola/data/themes/default/templates/disqus_helper.tmpl b/nikola/data/themes/default/templates/disqus_helper.tmpl index 674e20e..4c60f85 100644 --- a/nikola/data/themes/default/templates/disqus_helper.tmpl +++ b/nikola/data/themes/default/templates/disqus_helper.tmpl @@ -1,6 +1,9 @@ ## -*- coding: utf-8 -*- <%! import json + translations = { + 'es': 'es_ES', + } %> <%def name="html_disqus(url, title, identifier)"> %if disqus_forum: @@ -12,8 +15,8 @@ %endif var disqus_title=${json.dumps(title)}; var disqus_identifier="${identifier}"; - var disqus_config = function () { - this.language = "${lang}"; + var disqus_config = function () { + this.language = "${translations.get(lang, lang)}"; }; (function() { var dsq = document.createElement('script'); dsq.type = 'text/javascript'; dsq.async = true; diff --git a/nikola/data/themes/default/templates/index.tmpl b/nikola/data/themes/default/templates/index.tmpl index 4f66867..b49e764 100644 --- a/nikola/data/themes/default/templates/index.tmpl +++ b/nikola/data/themes/default/templates/index.tmpl @@ -5,13 +5,15 @@ <%block name="content"> % for post in posts:
      -

      ${post.title(lang)} +

      ${post.title()}    - ${messages[lang]["Posted"]}: + ${messages("Posted")}:


      - ${post.text(lang, index_teasers)} - ${disqus.html_disqus_link(post.permalink()+"#disqus_thread", post.base_path)} + ${post.text(teaser_only=index_teasers)} + % if not post.meta('nocomments'): + ${disqus.html_disqus_link(post.permalink()+"#disqus_thread", post.base_path)} + % endif
      % endfor ${helper.html_pager()} diff --git a/nikola/data/themes/default/templates/index_helper.tmpl b/nikola/data/themes/default/templates/index_helper.tmpl index 151b4d2..7859972 100644 --- a/nikola/data/themes/default/templates/index_helper.tmpl +++ b/nikola/data/themes/default/templates/index_helper.tmpl @@ -4,11 +4,11 @@
      diff --git a/nikola/data/themes/default/templates/list_post.tmpl b/nikola/data/themes/default/templates/list_post.tmpl index 1a1cdee..f0e159d 100644 --- a/nikola/data/themes/default/templates/list_post.tmpl +++ b/nikola/data/themes/default/templates/list_post.tmpl @@ -6,7 +6,7 @@

      ${title}

      diff --git a/nikola/data/themes/default/templates/post.tmpl b/nikola/data/themes/default/templates/post.tmpl index 22d8a58..f9e24d2 100644 --- a/nikola/data/themes/default/templates/post.tmpl +++ b/nikola/data/themes/default/templates/post.tmpl @@ -10,20 +10,24 @@ ${helper.twitter_card_information(post)} ${helper.html_title()}
      - ${messages[lang]["Posted"]}: + ${messages("Posted")}: ${helper.html_translations(post)} ${helper.html_tags(post)}
      - ${post.text(lang)} + ${post.text()} ${helper.html_pager(post)} - ${disqus.html_disqus(post.permalink(absolute=True), post.title(lang), post.base_path)} + % if not post.meta('nocomments'): + ${disqus.html_disqus(post.permalink(absolute=True), post.title(), post.base_path)} + % endif ${helper.mathjax_script(post)} <%block name="sourcelink"> +% if not post.meta('password'):
    • - ${messages[lang]["Source"]} + ${messages("Source")}
    • +% endif diff --git a/nikola/data/themes/default/templates/post_helper.tmpl b/nikola/data/themes/default/templates/post_helper.tmpl index 911a831..cce0ecf 100644 --- a/nikola/data/themes/default/templates/post_helper.tmpl +++ b/nikola/data/themes/default/templates/post_helper.tmpl @@ -2,7 +2,7 @@ <%def name="html_title()">

      ${title}

      % if link: -

      ${messages[lang]["Original site"]}

      +

      ${messages("Original site")}

      % endif @@ -12,7 +12,7 @@ %for langname in translations.keys(): %if langname != lang and post.is_translation_available(langname):   |   - ${messages[langname]["Read in English"]} + ${messages("Read in English", langname)} %endif %endfor %endif @@ -21,9 +21,9 @@ <%def name="html_tags(post)"> %if post.tags: -   |  ${messages[lang]["More posts about"]} +   |  ${messages("More posts about")} %for tag in post.tags: - ${tag} + ${tag} %endfor %endif @@ -32,19 +32,21 @@ <%def name="twitter_card_information(post)"> %if twitter_card and twitter_card['use_twitter_cards']: - - + + %if 'site:id' in twitter_card: %elif 'site' in twitter_card: @@ -55,11 +57,11 @@ %elif 'creator' in twitter_card: %endif - - %if post.description(lang): - + + %if post.description(): + %else: - + %endif %endif diff --git a/nikola/data/themes/default/templates/story.tmpl b/nikola/data/themes/default/templates/story.tmpl index d5c2f44..c1c06d8 100644 --- a/nikola/data/themes/default/templates/story.tmpl +++ b/nikola/data/themes/default/templates/story.tmpl @@ -1,12 +1,16 @@ ## -*- coding: utf-8 -*- <%inherit file="post.tmpl"/> +<%namespace name="helper" file="post_helper.tmpl"/> <%namespace name="disqus" file="disqus_helper.tmpl"/> +<%block name="extra_head"> +${helper.twitter_card_information(post)} + <%block name="content"> %if title:

      ${title}

      %endif - ${post.text(lang)} -%if enable_comments: - ${disqus.html_disqus(post.permalink(absolute=True), post.title(lang), post.base_path)} + ${post.text()} +%if enable_comments and not post.meta('nocomments'): + ${disqus.html_disqus(post.permalink(absolute=True), post.title(), post.base_path)} %endif diff --git a/nikola/data/themes/default/templates/tag.tmpl b/nikola/data/themes/default/templates/tag.tmpl index 7c89ad1..7fb43c0 100644 --- a/nikola/data/themes/default/templates/tag.tmpl +++ b/nikola/data/themes/default/templates/tag.tmpl @@ -1,7 +1,32 @@ ## -*- coding: utf-8 -*- <%inherit file="list_post.tmpl"/> <%block name="extra_head"> + %if len(translations) > 1: %for language in translations: - + %endfor + %else: + + %endif + + +<%block name="content"> + +
      +

      ${title}

      + %if len(translations) > 1: + %for language in translations: + RSS (${language})  + %endfor + %else: + RSS + %endif +
      + +
      + diff --git a/nikola/data/themes/default/templates/tags.tmpl b/nikola/data/themes/default/templates/tags.tmpl index 369a3d5..5727dc5 100644 --- a/nikola/data/themes/default/templates/tags.tmpl +++ b/nikola/data/themes/default/templates/tags.tmpl @@ -1,14 +1,12 @@ ## -*- coding: utf-8 -*- <%inherit file="base.tmpl"/> <%block name="content"> -
      - -

      ${title}

      -
        - % for text, link in items: -
      • ${text} - % endfor -
      - -
      + +

      ${title}

      +
        + % for text, link in items: +
      • ${text}
      • + % endfor +
      + diff --git a/nikola/data/themes/jinja-default/templates/base.tmpl b/nikola/data/themes/jinja-default/templates/base.tmpl index 97cddff..c104b20 100644 --- a/nikola/data/themes/jinja-default/templates/base.tmpl +++ b/nikola/data/themes/jinja-default/templates/base.tmpl @@ -1,4 +1,5 @@ +{{set_locale(lang)}} @@ -23,7 +24,6 @@ - {% if has_custom_css %} @@ -42,6 +42,7 @@ {% endif %} {% block extra_head %} {% endblock %} + {{extra_head_data}} {% if add_this_buttons %} @@ -58,10 +59,10 @@ {% block belowtitle%} {% if translations|length > 1 %} - {{ messages[lang]["Also available in"] }}:  + {{ messages("Also available in") }}:  {% for langname in translations.keys() %} {% if langname != lang %} - {{messages[langname]["LANGUAGE"]}} + {{messages("LANGUAGE", langname)}} {% endif %} {% endfor %} @@ -106,7 +107,6 @@ - {{analytics}} {% if use_bundles %} {% if use_cdn %} @@ -125,7 +125,7 @@ {% endif %} - {% endif %} + {{analytics}} diff --git a/nikola/data/themes/jinja-default/templates/index.tmpl b/nikola/data/themes/jinja-default/templates/index.tmpl index ab0392c..7d1aa00 100644 --- a/nikola/data/themes/jinja-default/templates/index.tmpl +++ b/nikola/data/themes/jinja-default/templates/index.tmpl @@ -2,14 +2,14 @@ {% block content %} {% for post in posts %}
      -

      {{post.title(lang)}} +

      {{post.title()}}    - {{messages[lang]["Posted"]}}: {{post.date.strftime(date_format)}} + {{messages("Posted")}}: {{post.formatted_date(date_format)}}


      - {{post.text(lang, index_teasers)}} + {{post.text(teaser_only=index_teasers)}}

      - {% if disqus_forum %} + {% if disqus_forum and not post.meta('nocomments')%} Comments {% endif %}

      @@ -18,11 +18,11 @@ diff --git a/nikola/data/themes/jinja-default/templates/list_post.tmpl b/nikola/data/themes/jinja-default/templates/list_post.tmpl index 7723214..b4ac59e 100644 --- a/nikola/data/themes/jinja-default/templates/list_post.tmpl +++ b/nikola/data/themes/jinja-default/templates/list_post.tmpl @@ -5,7 +5,7 @@

      {{title}}

      diff --git a/nikola/data/themes/jinja-default/templates/post.tmpl b/nikola/data/themes/jinja-default/templates/post.tmpl index d14e973..ab96682 100644 --- a/nikola/data/themes/jinja-default/templates/post.tmpl +++ b/nikola/data/themes/jinja-default/templates/post.tmpl @@ -3,49 +3,50 @@

      {{title}}

      {% if link %} -

      {{messages[lang]["Original site"]}}

      +

      {{messages("Original site")}}

      {% endif %}
      - {{messages[lang]["Posted"]}}: {{post.date.strftime(date_format)}}  |   + {{messages("Posted")}}: {{post.formatted_date(date_format)}}  |   {% if translations|length > 1 %} {% for langname in translations.keys() %} {% if langname != lang and post.is_translation_available(langname) %} - {{messages[langname]["Read in English"]}} + {{messages("Read in English", langname)}}   |   {% endif %} {% endfor %} {% endif %} - - {{messages[lang]["Source"]}} + {% if not post.meta('password') + {{messages("Source")}} + {% endif %} {% if post.tags %} -   |  {{messages[lang]["More posts about"]}} +   |  {{messages("More posts about")}} {% for tag in post.tags %} - {{tag}} + {{tag}} {% endfor %} {% endif %}
      - {{post.text(lang)}} + {{post.text()}} - {% if disqus_forum %} + {% if disqus_forum and not post.meta('nocomments')%}
      + {%endif%} {% endblock %} diff --git a/nikola/data/themes/jinja-default/templates/tag.tmpl b/nikola/data/themes/jinja-default/templates/tag.tmpl index 42720fd..77db27d 100644 --- a/nikola/data/themes/jinja-default/templates/tag.tmpl +++ b/nikola/data/themes/jinja-default/templates/tag.tmpl @@ -1,6 +1,6 @@ {% extends "list_post.tmpl"%} {%block extra_head %} {% for language in translations %} - + {% endfor %} {% endblock %} diff --git a/nikola/data/themes/jinja-default/templates/tags.tmpl b/nikola/data/themes/jinja-default/templates/tags.tmpl index 3eae88d..0fa9d0f 100644 --- a/nikola/data/themes/jinja-default/templates/tags.tmpl +++ b/nikola/data/themes/jinja-default/templates/tags.tmpl @@ -3,9 +3,9 @@

      {{title}}

      -
        + diff --git a/nikola/data/themes/monospace/assets/css/code.css b/nikola/data/themes/monospace/assets/css/code.css deleted file mode 100644 index b1d7ace..0000000 --- a/nikola/data/themes/monospace/assets/css/code.css +++ /dev/null @@ -1,62 +0,0 @@ -pre { word-break: pre; white-space: pre; word-wrap: pre; overflow: auto; max-width: 100%;} -td.linenos { vertical-align: top; width: 4em;} -div.code > pre, .code -{ background: #f8f8f8; white-space: pre;} -.code .c { color: #008800; font-style: italic } /* Comment */ -.code .err { border: 1px solid #FF0000 } /* Error */ -.code .k { color: #AA22FF; font-weight: bold } /* Keyword */ -.code .o { color: #666666 } /* Operator */ -.code .cm { color: #008800; font-style: italic } /* Comment.Multiline */ -.code .cp { color: #008800 } /* Comment.Preproc */ -.code .c1 { color: #008800; font-style: italic } /* Comment.Single */ -.code .cs { color: #008800; font-weight: bold } /* Comment.Special */ -.code .gd { color: #A00000 } /* Generic.Deleted */ -.code .ge { font-style: italic } /* Generic.Emph */ -.code .gr { color: #FF0000 } /* Generic.Error */ -.code .gh { color: #000080; font-weight: bold } /* Generic.Heading */ -.code .gi { color: #00A000 } /* Generic.Inserted */ -.code .go { color: #808080 } /* Generic.Output */ -.code .gp { color: #000080; font-weight: bold } /* Generic.Prompt */ -.code .gs { font-weight: bold } /* Generic.Strong */ -.code .gu { color: #800080; font-weight: bold } /* Generic.Subheading */ -.code .gt { color: #0040D0 } /* Generic.Traceback */ -.code .kc { color: #AA22FF; font-weight: bold } /* Keyword.Constant */ -.code .kd { color: #AA22FF; font-weight: bold } /* Keyword.Declaration */ -.code .kp { color: #AA22FF } /* Keyword.Pseudo */ -.code .kr { color: #AA22FF; font-weight: bold } /* Keyword.Reserved */ -.code .kt { color: #AA22FF; font-weight: bold } /* Keyword.Type */ -.code .m { color: #666666 } /* Literal.Number */ -.code .s { color: #BB4444 } /* Literal.String */ -.code .na { color: #BB4444 } /* Name.Attribute */ -.code .nb { color: #AA22FF } /* Name.Builtin */ -.code .nc { color: #0000FF } /* Name.Class */ -.code .no { color: #880000 } /* Name.Constant */ -.code .nd { color: #AA22FF } /* Name.Decorator */ -.code .ni { color: #999999; font-weight: bold } /* Name.Entity */ -.code .ne { color: #D2413A; font-weight: bold } /* Name.Exception */ -.code .nf { color: #00A000 } /* Name.Function */ -.code .nl { color: #A0A000 } /* Name.Label */ -.code .nn { color: #0000FF; font-weight: bold } /* Name.Namespace */ -.code .nt { color: #008000; font-weight: bold } /* Name.Tag */ -.code .nv { color: #B8860B } /* Name.Variable */ -.code .ow { color: #AA22FF; font-weight: bold } /* Operator.Word */ -.code .mf { color: #666666 } /* Literal.Number.Float */ -.code .mh { color: #666666 } /* Literal.Number.Hex */ -.code .mi { color: #666666 } /* Literal.Number.Integer */ -.code .mo { color: #666666 } /* Literal.Number.Oct */ -.code .sb { color: #BB4444 } /* Literal.String.Backtick */ -.code .sc { color: #BB4444 } /* Literal.String.Char */ -.code .sd { color: #BB4444; font-style: italic } /* Literal.String.Doc */ -.code .s2 { color: #BB4444 } /* Literal.String.Double */ -.code .se { color: #BB6622; font-weight: bold } /* Literal.String.Escape */ -.code .sh { color: #BB4444 } /* Literal.String.Heredoc */ -.code .si { color: #BB6688; font-weight: bold } /* Literal.String.Interpol */ -.code .sx { color: #008000 } /* Literal.String.Other */ -.code .sr { color: #BB6688 } /* Literal.String.Regex */ -.code .s1 { color: #BB4444 } /* Literal.String.Single */ -.code .ss { color: #B8860B } /* Literal.String.Symbol */ -.code .bp { color: #AA22FF } /* Name.Builtin.Pseudo */ -.code .vc { color: #B8860B } /* Name.Variable.Class */ -.code .vg { color: #B8860B } /* Name.Variable.Global */ -.code .vi { color: #B8860B } /* Name.Variable.Instance */ -.code .il { color: #666666 } /* Literal.Number.Integer.Long */ diff --git a/nikola/data/themes/monospace/bundles b/nikola/data/themes/monospace/bundles index aa35d9c..4760181 100644 --- a/nikola/data/themes/monospace/bundles +++ b/nikola/data/themes/monospace/bundles @@ -1 +1,2 @@ assets/css/all.css=rst.css,code.css,theme.css +assets/css/all-nocdn.css=rst.css,code.css,theme.css diff --git a/nikola/data/themes/monospace/templates/base.tmpl b/nikola/data/themes/monospace/templates/base.tmpl index 9eecbd4..806795d 100644 --- a/nikola/data/themes/monospace/templates/base.tmpl +++ b/nikola/data/themes/monospace/templates/base.tmpl @@ -1,11 +1,13 @@ ## -*- coding: utf-8 -*- <%namespace file="base_helper.tmpl" import="*"/> +${set_locale(lang)} ${html_head()} <%block name="extra_head"> + ${extra_head_data} %if add_this_buttons: @@ -23,7 +25,7 @@ <%block name="belowtitle"> %if len(translations) > 1: - ${(messages[lang][u"Also available in"])}:  + ${(messages("Also available in"))}:  ${html_translations()} %endif @@ -38,6 +40,6 @@ -
      +
      ${analytics} diff --git a/nikola/data/themes/monospace/templates/base_helper.tmpl b/nikola/data/themes/monospace/templates/base_helper.tmpl index aba8dff..4f3e45b 100644 --- a/nikola/data/themes/monospace/templates/base_helper.tmpl +++ b/nikola/data/themes/monospace/templates/base_helper.tmpl @@ -4,27 +4,29 @@ ${title} | ${blog_title} - + ${mathjax_config} %if use_bundles: - - + %if use_cdn: + + + %else: + + %endif %else: - + %if use_cdn: + + %else: + + + %endif - %if has_custom_css: %endif - - - - - %endif - @@ -32,7 +34,7 @@ ${rss_link} %else: %for language in translations: - + %endfor %endif %if favicons: @@ -48,10 +50,10 @@
      Share -
      • -
      • -
      • -
      • +
        • +
        • +
        • +
      @@ -74,7 +76,7 @@ <%def name="html_translations()"> %for langname in translations.keys(): %if langname != lang: - ${messages[langname]["LANGUAGE"]} + ${messages("LANGUAGE", langname)} %endif %endfor diff --git a/nikola/data/themes/monospace/templates/disqus_helper.tmpl b/nikola/data/themes/monospace/templates/disqus_helper.tmpl index 674e20e..4c60f85 100644 --- a/nikola/data/themes/monospace/templates/disqus_helper.tmpl +++ b/nikola/data/themes/monospace/templates/disqus_helper.tmpl @@ -1,6 +1,9 @@ ## -*- coding: utf-8 -*- <%! import json + translations = { + 'es': 'es_ES', + } %> <%def name="html_disqus(url, title, identifier)"> %if disqus_forum: @@ -12,8 +15,8 @@ %endif var disqus_title=${json.dumps(title)}; var disqus_identifier="${identifier}"; - var disqus_config = function () { - this.language = "${lang}"; + var disqus_config = function () { + this.language = "${translations.get(lang, lang)}"; }; (function() { var dsq = document.createElement('script'); dsq.type = 'text/javascript'; dsq.async = true; diff --git a/nikola/data/themes/monospace/templates/index.tmpl b/nikola/data/themes/monospace/templates/index.tmpl index ee57d26..4a0c630 100644 --- a/nikola/data/themes/monospace/templates/index.tmpl +++ b/nikola/data/themes/monospace/templates/index.tmpl @@ -5,22 +5,24 @@ <%block name="content"> % for post in posts:
      -

      ${post.title(lang)}

      +

      ${post.title()}

      - ${messages[lang]["Posted"]}: ${post.date.strftime(date_format)} + ${messages("Posted")}: ${post.formatted_date(date_format)}
      Tags:  %if post.tags: %for tag in post.tags: - ${tag} + ${tag} %endfor %endif
      - ${post.text(lang, index_teasers)} - ${disqus.html_disqus_link(post.permalink()+"#disqus_thread", post.base_path)} + ${post.text(teaser_only=index_teasers)} + % if not post.meta('nocomments'): + ${disqus.html_disqus_link(post.permalink()+"#disqus_thread", post.base_path)} + % endif
      % endfor ${helper.html_pager()} diff --git a/nikola/data/themes/monospace/templates/index_helper.tmpl b/nikola/data/themes/monospace/templates/index_helper.tmpl index 114a730..1bb700c 100644 --- a/nikola/data/themes/monospace/templates/index_helper.tmpl +++ b/nikola/data/themes/monospace/templates/index_helper.tmpl @@ -4,12 +4,12 @@ diff --git a/nikola/data/themes/monospace/templates/list_post.tmpl b/nikola/data/themes/monospace/templates/list_post.tmpl index 1a1cdee..f0e159d 100644 --- a/nikola/data/themes/monospace/templates/list_post.tmpl +++ b/nikola/data/themes/monospace/templates/list_post.tmpl @@ -6,7 +6,7 @@

      ${title}

      diff --git a/nikola/data/themes/monospace/templates/post.tmpl b/nikola/data/themes/monospace/templates/post.tmpl index 2ba27f1..0ec360d 100644 --- a/nikola/data/themes/monospace/templates/post.tmpl +++ b/nikola/data/themes/monospace/templates/post.tmpl @@ -7,13 +7,16 @@ ${helper.html_title()}
      - ${messages[lang]["Posted"]}: ${post.date.strftime(date_format)} [${messages[lang]["Source"]}] + ${messages("Posted")}: ${post.formatted_date(date_format)} + % if not post.meta('password'): + [${messages("Source")}] + % endif
      %if post.tags: - ${messages[lang]["Tags"]}:  + ${messages("Tags")}:  %for tag in post.tags: - ${tag} + ${tag} %endfor
      @@ -22,8 +25,10 @@ ${helper.html_translations(post)}
      - ${post.text(lang)} + ${post.text()} ${helper.html_pager(post)} - ${disqus.html_disqus(post.permalink(absolute=True), post.title(lang), post.base_path)} + % if not post.meta('nocomments'): + ${disqus.html_disqus(post.permalink(absolute=True), post.title(), post.base_path)} + % endif diff --git a/nikola/data/themes/monospace/templates/post_helper.tmpl b/nikola/data/themes/monospace/templates/post_helper.tmpl index 8651c65..cce0ecf 100644 --- a/nikola/data/themes/monospace/templates/post_helper.tmpl +++ b/nikola/data/themes/monospace/templates/post_helper.tmpl @@ -2,7 +2,7 @@ <%def name="html_title()">

      ${title}

      % if link: -

      ${messages[lang]["Original site"]}

      +

      ${messages("Original site")}

      % endif @@ -12,7 +12,7 @@ %for langname in translations.keys(): %if langname != lang and post.is_translation_available(langname):   |   - ${messages[langname]["Read in English"]} + ${messages("Read in English", langname)} %endif %endfor %endif @@ -21,25 +21,53 @@ <%def name="html_tags(post)"> %if post.tags: -   |  ${messages[lang]["More posts about"]} +   |  ${messages("More posts about")} %for tag in post.tags: - ${tag} + ${tag} %endfor %endif - <%def name="html_pager(post)"> + +<%def name="twitter_card_information(post)"> + %if twitter_card and twitter_card['use_twitter_cards']: + + + %if 'site:id' in twitter_card: + + %elif 'site' in twitter_card: + + %endif + %if 'creator:id' in twitter_card: + + %elif 'creator' in twitter_card: + + %endif + + %if post.description(): + + %else: + + %endif + %endif + + +<%def name="mathjax_script(post)"> + %if post.is_mathjax: + + %endif + diff --git a/nikola/data/themes/monospace/templates/story.tmpl b/nikola/data/themes/monospace/templates/story.tmpl index 30d263b..21d0e2f 100644 --- a/nikola/data/themes/monospace/templates/story.tmpl +++ b/nikola/data/themes/monospace/templates/story.tmpl @@ -1,11 +1,15 @@ ## -*- coding: utf-8 -*- <%inherit file="post.tmpl"/> +<%namespace name="helper" file="post_helper.tmpl"/> +<%block name="extra_head"> +${helper.twitter_card_information(post)} + <%block name="content"> %if title:

      ${title}

      %endif - ${post.text(lang)} -%if enable_comments: - ${disqus.html_disqus(post.permalink(absolute=True), post.title(lang), post.base_path)} + ${post.text()} +%if enable_comments and not post.meta('nocomments'): + ${disqus.html_disqus(post.permalink(absolute=True), post.title(), post.base_path)} %endif diff --git a/nikola/data/themes/monospace/templates/tag.tmpl b/nikola/data/themes/monospace/templates/tag.tmpl index 7c89ad1..97aafeb 100644 --- a/nikola/data/themes/monospace/templates/tag.tmpl +++ b/nikola/data/themes/monospace/templates/tag.tmpl @@ -2,6 +2,6 @@ <%inherit file="list_post.tmpl"/> <%block name="extra_head"> %for language in translations: - + %endfor diff --git a/nikola/data/themes/orphan/assets/css/code.css b/nikola/data/themes/orphan/assets/css/code.css deleted file mode 120000 index 6b2b872..0000000 --- a/nikola/data/themes/orphan/assets/css/code.css +++ /dev/null @@ -1 +0,0 @@ -../../../default/assets/css/code.css \ No newline at end of file diff --git a/nikola/data/themes/orphan/templates/base.tmpl b/nikola/data/themes/orphan/templates/base.tmpl index 39e2b9d..2a62b58 100644 --- a/nikola/data/themes/orphan/templates/base.tmpl +++ b/nikola/data/themes/orphan/templates/base.tmpl @@ -1,11 +1,13 @@ ## -*- coding: utf-8 -*- <%namespace file="base_helper.tmpl" import="*"/> +${set_locale(lang)} ${html_head()} <%block name="extra_head"> + ${extra_head_data} %if add_this_buttons: @@ -17,7 +19,7 @@ <%block name="belowtitle"> %if len(translations) > 1: - ${(messages[lang][u"Also available in"])}:  + ${(messages("Also available in"))}:  ${html_translations()} %endif diff --git a/nikola/data/themes/orphan/templates/base_helper.tmpl b/nikola/data/themes/orphan/templates/base_helper.tmpl index aba8dff..4f3e45b 100644 --- a/nikola/data/themes/orphan/templates/base_helper.tmpl +++ b/nikola/data/themes/orphan/templates/base_helper.tmpl @@ -4,27 +4,29 @@ ${title} | ${blog_title} - + ${mathjax_config} %if use_bundles: - - + %if use_cdn: + + + %else: + + %endif %else: - + %if use_cdn: + + %else: + + + %endif - %if has_custom_css: %endif - - - - - %endif - @@ -32,7 +34,7 @@ ${rss_link} %else: %for language in translations: - + %endfor %endif %if favicons: @@ -48,10 +50,10 @@
      Share -
      • -
      • -
      • -
      • +
        • +
        • +
        • +
      @@ -74,7 +76,7 @@ <%def name="html_translations()"> %for langname in translations.keys(): %if langname != lang: - ${messages[langname]["LANGUAGE"]} + ${messages("LANGUAGE", langname)} %endif %endfor diff --git a/nikola/data/themes/orphan/templates/disqus_helper.tmpl b/nikola/data/themes/orphan/templates/disqus_helper.tmpl index 674e20e..4c60f85 100644 --- a/nikola/data/themes/orphan/templates/disqus_helper.tmpl +++ b/nikola/data/themes/orphan/templates/disqus_helper.tmpl @@ -1,6 +1,9 @@ ## -*- coding: utf-8 -*- <%! import json + translations = { + 'es': 'es_ES', + } %> <%def name="html_disqus(url, title, identifier)"> %if disqus_forum: @@ -12,8 +15,8 @@ %endif var disqus_title=${json.dumps(title)}; var disqus_identifier="${identifier}"; - var disqus_config = function () { - this.language = "${lang}"; + var disqus_config = function () { + this.language = "${translations.get(lang, lang)}"; }; (function() { var dsq = document.createElement('script'); dsq.type = 'text/javascript'; dsq.async = true; diff --git a/nikola/data/themes/orphan/templates/index.tmpl b/nikola/data/themes/orphan/templates/index.tmpl index 1a436e2..59d391a 100644 --- a/nikola/data/themes/orphan/templates/index.tmpl +++ b/nikola/data/themes/orphan/templates/index.tmpl @@ -5,13 +5,15 @@ <%block name="content"> % for post in posts:
      -

      ${post.title(lang)} +

      ${post.title()}    - ${messages[lang]["Posted"]}: ${post.date.strftime(date_format)} + ${messages("Posted")}: ${post.formatted_date(date_format)}


      - ${post.text(lang, index_teasers)} - ${disqus.html_disqus_link(post.permalink()+"#disqus_thread", post.base_path)} + ${post.text(teaser_only=index_teasers)} + % if not post.meta('nocomments'): + ${disqus.html_disqus_link(post.permalink()+"#disqus_thread", post.base_path)} + % endif
      % endfor ${helper.html_pager()} diff --git a/nikola/data/themes/orphan/templates/index_helper.tmpl b/nikola/data/themes/orphan/templates/index_helper.tmpl index 114a730..1bb700c 100644 --- a/nikola/data/themes/orphan/templates/index_helper.tmpl +++ b/nikola/data/themes/orphan/templates/index_helper.tmpl @@ -4,12 +4,12 @@ diff --git a/nikola/data/themes/orphan/templates/list_post.tmpl b/nikola/data/themes/orphan/templates/list_post.tmpl index 1a1cdee..f0e159d 100644 --- a/nikola/data/themes/orphan/templates/list_post.tmpl +++ b/nikola/data/themes/orphan/templates/list_post.tmpl @@ -6,7 +6,7 @@

      ${title}

      diff --git a/nikola/data/themes/orphan/templates/post.tmpl b/nikola/data/themes/orphan/templates/post.tmpl index 672d4f6..6f6529d 100644 --- a/nikola/data/themes/orphan/templates/post.tmpl +++ b/nikola/data/themes/orphan/templates/post.tmpl @@ -7,15 +7,19 @@ ${helper.html_title()}
      - ${messages[lang]["Posted"]}: ${post.date.strftime(date_format)} + ${messages("Posted")}: ${post.formatted_date(date_format)} ${helper.html_translations(post)}   |   - ${messages[lang]["Source"]} + % if not post.meta('password'): + ${messages("Source")} + % endif ${helper.html_tags(post)}
      - ${post.text(lang)} + ${post.text()} ${helper.html_pager(post)} - ${disqus.html_disqus(post.permalink(absolute=True), post.title(lang), post.base_path)} + % if not post.meta('nocomments'): + ${disqus.html_disqus(post.permalink(absolute=True), post.title(), post.base_path)} + % endif diff --git a/nikola/data/themes/orphan/templates/post_helper.tmpl b/nikola/data/themes/orphan/templates/post_helper.tmpl index a3dc75f..cce0ecf 100644 --- a/nikola/data/themes/orphan/templates/post_helper.tmpl +++ b/nikola/data/themes/orphan/templates/post_helper.tmpl @@ -2,7 +2,7 @@ <%def name="html_title()">

      ${title}

      % if link: -

      ${messages[lang]["Original site"]}

      +

      ${messages("Original site")}

      % endif @@ -10,9 +10,9 @@ <%def name="html_translations(post)"> %if len(translations) > 1: %for langname in translations.keys(): - %if langname != lang and post.is_translation_available(langname): + %if langname != lang and post.is_translation_available(langname):   |   - ${messages[langname]["Read in English"]} + ${messages("Read in English", langname)} %endif %endfor %endif @@ -21,25 +21,53 @@ <%def name="html_tags(post)"> %if post.tags: -   |  ${messages[lang]["More posts about"]} +   |  ${messages("More posts about")} %for tag in post.tags: - ${tag} + ${tag} %endfor %endif - <%def name="html_pager(post)"> + +<%def name="twitter_card_information(post)"> + %if twitter_card and twitter_card['use_twitter_cards']: + + + %if 'site:id' in twitter_card: + + %elif 'site' in twitter_card: + + %endif + %if 'creator:id' in twitter_card: + + %elif 'creator' in twitter_card: + + %endif + + %if post.description(): + + %else: + + %endif + %endif + + +<%def name="mathjax_script(post)"> + %if post.is_mathjax: + + %endif + diff --git a/nikola/data/themes/orphan/templates/story.tmpl b/nikola/data/themes/orphan/templates/story.tmpl index 30d263b..21d0e2f 100644 --- a/nikola/data/themes/orphan/templates/story.tmpl +++ b/nikola/data/themes/orphan/templates/story.tmpl @@ -1,11 +1,15 @@ ## -*- coding: utf-8 -*- <%inherit file="post.tmpl"/> +<%namespace name="helper" file="post_helper.tmpl"/> +<%block name="extra_head"> +${helper.twitter_card_information(post)} + <%block name="content"> %if title:

      ${title}

      %endif - ${post.text(lang)} -%if enable_comments: - ${disqus.html_disqus(post.permalink(absolute=True), post.title(lang), post.base_path)} + ${post.text()} +%if enable_comments and not post.meta('nocomments'): + ${disqus.html_disqus(post.permalink(absolute=True), post.title(), post.base_path)} %endif diff --git a/nikola/data/themes/orphan/templates/tag.tmpl b/nikola/data/themes/orphan/templates/tag.tmpl index 7c89ad1..97aafeb 100644 --- a/nikola/data/themes/orphan/templates/tag.tmpl +++ b/nikola/data/themes/orphan/templates/tag.tmpl @@ -2,6 +2,6 @@ <%inherit file="list_post.tmpl"/> <%block name="extra_head"> %for language in translations: - + %endfor diff --git a/nikola/data/themes/site-planetoid/README b/nikola/data/themes/site-planetoid/README new file mode 100644 index 0000000..c148591 --- /dev/null +++ b/nikola/data/themes/site-planetoid/README @@ -0,0 +1 @@ +A version of the site theme for the use with the "planetoid" plugin. diff --git a/nikola/data/themes/site-planetoid/engine b/nikola/data/themes/site-planetoid/engine new file mode 100644 index 0000000..2951cdd --- /dev/null +++ b/nikola/data/themes/site-planetoid/engine @@ -0,0 +1 @@ +mako diff --git a/nikola/data/themes/site-planetoid/parent b/nikola/data/themes/site-planetoid/parent new file mode 100644 index 0000000..1320f90 --- /dev/null +++ b/nikola/data/themes/site-planetoid/parent @@ -0,0 +1 @@ +site diff --git a/nikola/data/themes/site-planetoid/templates/index.tmpl b/nikola/data/themes/site-planetoid/templates/index.tmpl new file mode 100644 index 0000000..29243e0 --- /dev/null +++ b/nikola/data/themes/site-planetoid/templates/index.tmpl @@ -0,0 +1,16 @@ +## -*- coding: utf-8 -*- +<%namespace name="helper" file="index_helper.tmpl"/> +<%inherit file="base.tmpl"/> +<%block name="content"> + % for post in posts: +
      +

      ${post.title(lang)} +    + ${messages("Posted")}: +

      + ${post.text(lang)} +
      + % endfor + ${helper.html_pager()} + + diff --git a/nikola/data/themes/site-planetoid/templates/post.tmpl b/nikola/data/themes/site-planetoid/templates/post.tmpl new file mode 100644 index 0000000..d60de78 --- /dev/null +++ b/nikola/data/themes/site-planetoid/templates/post.tmpl @@ -0,0 +1,9 @@ +## -*- coding: utf-8 -*- + + + + + +Redirecting you to the original location. + + diff --git a/nikola/data/themes/site-planetoid/templates/story.tmpl b/nikola/data/themes/site-planetoid/templates/story.tmpl new file mode 100644 index 0000000..7712e71 --- /dev/null +++ b/nikola/data/themes/site-planetoid/templates/story.tmpl @@ -0,0 +1,25 @@ +## -*- coding: utf-8 -*- +<%namespace name="helper" file="post_helper.tmpl"/> +<%namespace name="disqus" file="disqus_helper.tmpl"/> +<%inherit file="base.tmpl"/> +<%block name="extra_head"> +${helper.twitter_card_information(post)} + + +<%block name="content"> +%if title: +

      ${title}

      +%endif + ${post.text()} +%if enable_comments and not post.meta('nocomments'): + ${disqus.html_disqus(post.permalink(absolute=True), post.title(), post.base_path)} +%endif + + +<%block name="sourcelink"> +% if not post.meta('password'): +
    • + ${messages("Source")} +
    • +% endif + diff --git a/nikola/data/themes/site/assets/css/theme.css b/nikola/data/themes/site/assets/css/theme.css index aa0ee4a..24072ac 100644 --- a/nikola/data/themes/site/assets/css/theme.css +++ b/nikola/data/themes/site/assets/css/theme.css @@ -64,3 +64,17 @@ blockquote p, blockquote { line-height: 1.25; } +ul.bricks > li { + display: inline; + background-color: lightblue; + padding: 8px; + border-radius: 5px; + line-height: 3; + white-space:nowrap; + margin: 3px; +} + +h1, h2, h3, h4, h5, h6, h7 { + margin-top: -40px; + padding-top: 40px; +} diff --git a/nikola/data/themes/site/templates/base.tmpl b/nikola/data/themes/site/templates/base.tmpl index 416d04b..4efd0ad 100644 --- a/nikola/data/themes/site/templates/base.tmpl +++ b/nikola/data/themes/site/templates/base.tmpl @@ -1,5 +1,6 @@ ## -*- coding: utf-8 -*- <%namespace file="base_helper.tmpl" import="*"/> +${set_locale(lang)} @@ -7,20 +8,21 @@ ${html_head()} <%block name="extra_head"> + ${extra_head_data}
      ${content_footer}
      ${html_social()} -${analytics} ${late_load_js()} +${analytics} diff --git a/nikola/main.py b/nikola/main.py index b390387..8263b7e 100644 --- a/nikola/main.py +++ b/nikola/main.py @@ -35,16 +35,13 @@ from doit.cmd_help import Help as DoitHelp from doit.cmd_run import Run as DoitRun from .nikola import Nikola +from .utils import _reload def main(args): sys.path.append('') try: import conf - if sys.version_info[0] > 2: - from imp import reload as _reload - else: - _reload = reload # NOQA _reload(conf) config = conf.__dict__ except ImportError: @@ -111,7 +108,7 @@ class DoitNikola(DoitMain): sub_cmds = self.get_commands() args = self.process_args(cmd_args) - if len(args) == 0 or args == ["--help"]: + if len(args) == 0 or any(arg in ["--help", '-h'] for arg in args): cmd_args = ['help'] args = ['help'] diff --git a/nikola/nikola.py b/nikola/nikola.py index a1506e7..8660a0f 100644 --- a/nikola/nikola.py +++ b/nikola/nikola.py @@ -28,12 +28,14 @@ from collections import defaultdict from copy import copy import glob import gzip +import locale import os import sys try: from urlparse import urlparse, urlsplit, urljoin except ImportError: from urllib.parse import urlparse, urlsplit, urljoin # NOQA +import warnings import lxml.html from yapsy.PluginManager import PluginManager @@ -67,12 +69,19 @@ class Nikola(object): Takes a site config as argument on creation. """ + EXTRA_PLUGINS = [ + 'planetoid', + 'ipynb', + 'local_search', + 'render_mustache', + ] def __init__(self, **config): """Setup proper environment for running tasks.""" self.global_data = {} self.posts_per_year = defaultdict(list) + self.posts_per_month = defaultdict(list) self.posts_per_tag = defaultdict(list) self.timeline = [] self.pages = [] @@ -83,21 +92,24 @@ class Nikola(object): self.configured = True # This is the default config - # TODO: fill it self.config = { 'ADD_THIS_BUTTONS': True, 'ANALYTICS': '', 'ARCHIVE_PATH': "", 'ARCHIVE_FILENAME': "archive.html", 'CACHE_FOLDER': 'cache', + 'CODE_COLOR_SCHEME': 'default', 'COMMENTS_IN_GALLERIES': False, 'COMMENTS_IN_STORIES': False, 'CONTENT_FOOTER': '', + 'CREATE_MONTHLY_ARCHIVE': False, 'DATE_FORMAT': '%Y-%m-%d %H:%M', 'DEFAULT_LANG': "en", 'DEPLOY_COMMANDS': [], 'DISABLED_PLUGINS': (), 'DISQUS_FORUM': 'nikolademo', + 'ENABLED_EXTRAS': (), + 'EXTRA_HEAD_DATA': '', 'FAVICONS': {}, 'FILE_METADATA_REGEXP': None, 'FILES_FOLDERS': {'files': ''}, @@ -105,6 +117,7 @@ class Nikola(object): 'GALLERY_PATH': 'galleries', 'GZIP_FILES': False, 'GZIP_EXTENSIONS': ('.txt', '.htm', '.html', '.css', '.js', '.json'), + 'HIDE_UNTRANSLATED_POSTS': False, 'INDEX_DISPLAY_POST_COUNT': 10, 'INDEX_TEASERS': False, 'INDEXES_TITLE': "", @@ -114,6 +127,7 @@ class Nikola(object): 'LISTINGS_FOLDER': 'listings', 'MAX_IMAGE_SIZE': 1280, 'MATHJAX_CONFIG': '', + 'OLD_THEME_SUPPORT': True, 'OUTPUT_FOLDER': 'output', 'post_compilers': { "rest": ('.txt', '.rst'), @@ -136,6 +150,7 @@ class Nikola(object): 'SEARCH_FORM': '', 'SLUG_TAG_PATH': True, 'STORY_INDEX': False, + 'STRIP_INDEX_HTML': False, 'TAG_PATH': 'categories', 'TAG_PAGES_ARE_INDEXES': False, 'THEME': 'site', @@ -156,16 +171,19 @@ class Nikola(object): self.THEMES = utils.get_theme_chain(self.config['THEME']) self.MESSAGES = utils.load_messages(self.THEMES, - self.config['TRANSLATIONS']) + self.config['TRANSLATIONS'], + self.config['DEFAULT_LANG']) # SITE_URL is required, but if the deprecated BLOG_URL # is available, use it and warn if 'SITE_URL' not in self.config: if 'BLOG_URL' in self.config: print("WARNING: You should configure SITE_URL instead of BLOG_URL") - print("See docs at FIXME put URL") self.config['SITE_URL'] = self.config['BLOG_URL'] + self.default_lang = self.config['DEFAULT_LANG'] + self.translations = self.config['TRANSLATIONS'] + # BASE_URL defaults to SITE_URL if 'BASE_URL' not in self.config: self.config['BASE_URL'] = self.config.get('SITE_URL') @@ -187,23 +205,28 @@ class Nikola(object): self.commands = {} # Activate all command plugins - for pluginInfo in self.plugin_manager.getPluginsOfCategory("Command"): - if pluginInfo.name in self.config['DISABLED_PLUGINS']: - self.plugin_manager.removePluginFromCategory(pluginInfo, "Command") + for plugin_info in self.plugin_manager.getPluginsOfCategory("Command"): + if (plugin_info.name in self.config['DISABLED_PLUGINS'] + or (plugin_info.name in self.EXTRA_PLUGINS and + plugin_info.name not in self.config['ENABLED_EXTRAS'])): + self.plugin_manager.removePluginFromCategory(plugin_info, "Command") continue - self.plugin_manager.activatePluginByName(pluginInfo.name) - pluginInfo.plugin_object.set_site(self) - pluginInfo.plugin_object.short_help = pluginInfo.description - self.commands[pluginInfo.name] = pluginInfo.plugin_object + + self.plugin_manager.activatePluginByName(plugin_info.name) + plugin_info.plugin_object.set_site(self) + plugin_info.plugin_object.short_help = plugin_info.description + self.commands[plugin_info.name] = plugin_info.plugin_object # Activate all task plugins for task_type in ["Task", "LateTask"]: - for pluginInfo in self.plugin_manager.getPluginsOfCategory(task_type): - if pluginInfo.name in self.config['DISABLED_PLUGINS']: - self.plugin_manager.removePluginFromCategory(pluginInfo, task_type) + for plugin_info in self.plugin_manager.getPluginsOfCategory(task_type): + if (plugin_info.name in self.config['DISABLED_PLUGINS'] + or (plugin_info.name in self.EXTRA_PLUGINS and + plugin_info.name not in self.config['ENABLED_EXTRAS'])): + self.plugin_manager.removePluginFromCategory(plugin_info, task_type) continue - self.plugin_manager.activatePluginByName(pluginInfo.name) - pluginInfo.plugin_object.set_site(self) + self.plugin_manager.activatePluginByName(plugin_info.name) + plugin_info.plugin_object.set_site(self) # set global_context for template rendering self.GLOBAL_CONTEXT = { @@ -211,6 +234,7 @@ class Nikola(object): self.GLOBAL_CONTEXT['messages'] = self.MESSAGES self.GLOBAL_CONTEXT['_link'] = self.link + self.GLOBAL_CONTEXT['set_locale'] = s_l self.GLOBAL_CONTEXT['rel_link'] = self.rel_link self.GLOBAL_CONTEXT['abs_link'] = self.abs_link self.GLOBAL_CONTEXT['exists'] = self.file_exists @@ -244,9 +268,14 @@ class Nikola(object): 'CONTENT_FOOTER') self.GLOBAL_CONTEXT['rss_path'] = self.config.get('RSS_PATH') self.GLOBAL_CONTEXT['rss_link'] = self.config.get('RSS_LINK') - self.GLOBAL_CONTEXT['sidebar_links'] = self.config.get('SIDEBAR_LINKS') + + self.GLOBAL_CONTEXT['sidebar_links'] = utils.Functionary(list, self.config['DEFAULT_LANG']) + for k, v in self.config.get('SIDEBAR_LINKS', {}).items(): + self.GLOBAL_CONTEXT['sidebar_links'][k] = v + self.GLOBAL_CONTEXT['twitter_card'] = self.config.get( 'TWITTER_CARD', {}) + self.GLOBAL_CONTEXT['extra_head_data'] = self.config.get('EXTRA_HEAD_DATA') self.GLOBAL_CONTEXT.update(self.config.get('GLOBAL_CONTEXT', {})) @@ -273,14 +302,21 @@ class Nikola(object): self.template_system.set_directories(lookup_dirs, self.config['CACHE_FOLDER']) + # Check consistency of USE_CDN and the current THEME (Issue #386) + if self.config['USE_CDN']: + bootstrap_path = utils.get_asset_path(os.path.join( + 'assets', 'css', 'bootstrap.min.css'), self.THEMES) + if bootstrap_path.split(os.sep)[-4] != 'site': + warnings.warn('The USE_CDN option may be incompatible with your theme, because it uses a hosted version of bootstrap.') + # Load compiler plugins self.compilers = {} self.inverse_compilers = {} - for pluginInfo in self.plugin_manager.getPluginsOfCategory( + for plugin_info in self.plugin_manager.getPluginsOfCategory( "PageCompiler"): - self.compilers[pluginInfo.name] = \ - pluginInfo.plugin_object.compile_html + self.compilers[plugin_info.name] = \ + plugin_info.plugin_object.compile_html def get_compiler(self, source_name): """Get the correct compiler for a post from `conf.post_compilers` @@ -324,11 +360,9 @@ class Nikola(object): data = self.template_system.render_template( template_name, None, local_context) - assert isinstance(output_name, bytes) assert output_name.startswith( - self.config["OUTPUT_FOLDER"].encode('utf8')) - url_part = output_name.decode('utf8')[len(self.config["OUTPUT_FOLDER"]) - + 1:] + self.config["OUTPUT_FOLDER"]) + url_part = output_name[len(self.config["OUTPUT_FOLDER"]) + 1:] # Treat our site as if output/ is "/" and then make all URLs relative, # making the site "relocatable" @@ -392,7 +426,20 @@ class Nikola(object): with open(output_name, "wb+") as post_file: post_file.write(data) - def path(self, kind, name, lang, is_link=False): + def current_lang(self): # FIXME: this is duplicated, turn into a mixin + """Return the currently set locale, if it's one of the + available translations, or default_lang.""" + lang = utils.LocaleBorg().current_lang + if lang: + if lang in self.translations: + return lang + lang = lang.split('_')[0] + if lang in self.translations: + return lang + # whatever + return self.default_lang + + def path(self, kind, name, lang=None, is_link=False): """Build the path to a certain kind of page. kind is one of: @@ -417,6 +464,9 @@ class Nikola(object): (ex: "archive\\index.html") """ + if lang is None: + lang = self.current_lang() + path = [] if kind == "tag_index": @@ -465,7 +515,11 @@ class Nikola(object): path = [_f for _f in [self.config['LISTINGS_FOLDER'], name + '.html'] if _f] if is_link: - return '/' + ('/'.join(path)) + link = '/' + ('/'.join(path)) + if self.config['STRIP_INDEX_HTML'] and link.endswith('/index.html'): + return link[:-10] + else: + return link else: return os.path.join(*path) @@ -528,12 +582,14 @@ class Nikola(object): def add_gzipped_copies(task): if not self.config['GZIP_FILES']: return None + if task.get('name') is None: + return None gzip_task = { 'file_dep': [], 'targets': [], 'actions': [], 'basename': 'gzip', - 'name': task.get('name', 'unknown'), + 'name': task.get('name') + '.gz', 'clean': True, } targets = task.get('targets', []) @@ -597,7 +653,18 @@ class Nikola(object): dir_glob = os.path.join(dirpath, os.path.basename(wildcard)) dest_dir = os.path.normpath(os.path.join(destination, os.path.relpath(dirpath, dirname))) - for base_path in glob.glob(dir_glob): + full_list = glob.glob(dir_glob) + # Now let's look for things that are not in default_lang + for lang in self.config['TRANSLATIONS'].keys(): + lang_glob = dir_glob + "." + lang + translated_list = glob.glob(lang_glob) + for fname in translated_list: + orig_name = os.path.splitext(fname)[0] + if orig_name in full_list: + continue + full_list.append(orig_name) + + for base_path in full_list: post = Post( base_path, self.config['CACHE_FOLDER'], @@ -609,26 +676,32 @@ class Nikola(object): self.MESSAGES, template_name, self.config['FILE_METADATA_REGEXP'], + self.config['STRIP_INDEX_HTML'], tzinfo, + self.config['HIDE_UNTRANSLATED_POSTS'], ) for lang, langpath in list( self.config['TRANSLATIONS'].items()): dest = (destination, langpath, dir_glob, - post.pagenames[lang]) + post.meta[lang]['slug']) if dest in targets: raise Exception('Duplicated output path {0!r} ' 'in post {1!r}'.format( - post.pagenames[lang], + post.meta[lang]['slug'], base_path)) targets.add(dest) self.global_data[post.post_name] = post if post.use_in_feeds: self.posts_per_year[ str(post.date.year)].append(post.post_name) - for tag in post.tags: + self.posts_per_month[ + '{0}/{1:02d}'.format(post.date.year, post.date.month)].append(post.post_name) + for tag in post.alltags: self.posts_per_tag[tag].append(post.post_name) else: self.pages.append(post) + if self.config['OLD_THEME_SUPPORT']: + post._add_old_metadata() for name, post in list(self.global_data.items()): self.timeline.append(post) self.timeline.sort(key=lambda p: p.date) @@ -657,7 +730,7 @@ class Nikola(object): else: context['enable_comments'] = self.config['COMMENTS_IN_STORIES'] output_name = os.path.join(self.config['OUTPUT_FOLDER'], - post.destination_path(lang)).encode('utf8') + post.destination_path(lang)) deps_dict = copy(context) deps_dict.pop('post') if post.prev_post: @@ -668,9 +741,11 @@ class Nikola(object): deps_dict['TRANSLATIONS'] = self.config['TRANSLATIONS'] deps_dict['global'] = self.GLOBAL_CONTEXT deps_dict['comments'] = context['enable_comments'] + if post: + deps_dict['post_translations'] = post.translated_to task = { - 'name': output_name, + 'name': os.path.normpath(output_name), 'file_dep': deps, 'targets': [output_name], 'actions': [(self.render_template, [post.template_name, @@ -685,9 +760,6 @@ class Nikola(object): template_name, filters, extra_context): """Renders pages with lists of posts.""" - # This is a name on disk, has to be bytes - assert isinstance(output_name, bytes) - deps = self.template_system.template_deps(template_name) for post in posts: deps += post.deps(lang) @@ -700,11 +772,11 @@ class Nikola(object): context["nextlink"] = None context.update(extra_context) deps_context = copy(context) - deps_context["posts"] = [(p.titles[lang], p.permalink(lang)) for p in + deps_context["posts"] = [(p.meta[lang]['title'], p.permalink(lang)) for p in posts] deps_context["global"] = self.GLOBAL_CONTEXT task = { - 'name': output_name, + 'name': os.path.normpath(output_name), 'targets': [output_name], 'file_dep': deps, 'actions': [(self.render_template, [template_name, output_name, @@ -714,3 +786,14 @@ class Nikola(object): } return utils.apply_filters(task, filters) + + +def s_l(lang): + """A set_locale that uses utf8 encoding and returns ''.""" + utils.LocaleBorg().current_lang = lang + try: + locale.setlocale(locale.LC_ALL, (lang, "utf8")) + except Exception: + print("WARNING: could not set locale to {0}." + "This may cause some i18n features not to work.".format(lang)) + return '' diff --git a/nikola/plugin_categories.py b/nikola/plugin_categories.py index cff9b65..c4ca788 100644 --- a/nikola/plugin_categories.py +++ b/nikola/plugin_categories.py @@ -145,12 +145,19 @@ class PageCompiler(object): """Plugins that compile text files into HTML.""" name = "dummy compiler" + default_metadata = { + 'title': '', + 'slug': '', + 'date': '', + 'tags': '', + 'link': '', + 'description': '', + } def compile_html(self, source, dest): """Compile the source, save it on dest.""" raise NotImplementedError() - def create_post(self, path, onefile=False, title="", slug="", date="", - tags=""): + def create_post(self, path, onefile=False, **kw): """Create post file with optional metadata.""" raise NotImplementedError() diff --git a/nikola/plugins/command_check.py b/nikola/plugins/command_check.py index a396f63..ea82703 100644 --- a/nikola/plugins/command_check.py +++ b/nikola/plugins/command_check.py @@ -24,6 +24,7 @@ from __future__ import print_function import os +import sys try: from urllib import unquote from urlparse import urlparse @@ -74,14 +75,17 @@ class CommandCheck(Command): print(self.help()) return False if options['links']: - scan_links(options['find_sources']) + failure = scan_links(options['find_sources']) if options['files']: - scan_files() + failure = scan_files() + if failure: + sys.exit(1) existing_targets = set([]) def analize(task, find_sources=False): + rv = False try: filename = task.split(":")[-1] d = lxml.html.fromstring(open(filename).read()) @@ -100,6 +104,7 @@ def analize(task, find_sources=False): if os.path.exists(target_filename): existing_targets.add(target_filename) else: + rv = True print("Broken link in {0}: ".format(filename), target) if find_sources: print("Possible sources:") @@ -109,17 +114,21 @@ def analize(task, find_sources=False): except Exception as exc: print("Error with:", filename, exc) + return rv def scan_links(find_sources=False): print("Checking Links:\n===============\n") + failure = False for task in os.popen('nikola list --all', 'r').readlines(): task = task.strip() if task.split(':')[0] in ('render_tags', 'render_archive', 'render_galleries', 'render_indexes', - 'render_pages', + 'render_pages' 'render_site') and '.html' in task: - analize(task, find_sources) + if analize(task, find_sources): + failure = True + return failure def scan_files(): @@ -127,6 +136,7 @@ def scan_files(): task_fnames = set([]) real_fnames = set([]) # First check that all targets are generated in the right places + failure = False for task in os.popen('nikola list --all', 'r').readlines(): task = task.strip() if 'output' in task and ':' in task: @@ -144,6 +154,7 @@ def scan_files(): print("\nFiles from unknown origins:\n") for f in only_on_output: print(f) + failure = True only_on_input = list(task_fnames - real_fnames) if only_on_input: @@ -151,3 +162,5 @@ def scan_files(): print("\nFiles not generated:\n") for f in only_on_input: print(f) + + return failure diff --git a/nikola/plugins/command_console.py b/nikola/plugins/command_console.py index 4af759f..f4d0295 100644 --- a/nikola/plugins/command_console.py +++ b/nikola/plugins/command_console.py @@ -29,35 +29,77 @@ import os from nikola.plugin_categories import Command -class Deploy(Command): +class Console(Command): """Start debugging console.""" name = "console" + shells = ['ipython', 'bpython', 'plain'] + doc_purpose = "Start an interactive python console with access to your site and configuration." - def _execute(self, options, args): - """Start the console.""" + def ipython(self): + """IPython shell.""" from nikola import Nikola try: import conf + except ImportError: + print("No configuration found, cannot run the console.") + else: + import IPython SITE = Nikola(**conf.__dict__) SITE.scan_posts() - print("You can now access your configuration as conf and your " - "site engine as SITE.") + IPython.embed(header='Nikola Console (conf = configuration, SITE ' + '= site engine)') + + def bpython(self): + """bpython shell.""" + from nikola import Nikola + try: + import conf except ImportError: - print("No configuration found.") - import code + print("No configuration found, cannot run the console.") + else: + import bpython + SITE = Nikola(**conf.__dict__) + SITE.scan_posts() + gl = {'conf': conf, 'SITE': SITE, 'Nikola': Nikola} + bpython.embed(banner='Nikola Console (conf = configuration, SITE ' + '= site engine)', locals_=gl) + + def plain(self): + """Plain Python shell.""" + from nikola import Nikola try: - import readline + import conf + SITE = Nikola(**conf.__dict__) + SITE.scan_posts() + gl = {'conf': conf, 'SITE': SITE, 'Nikola': Nikola} except ImportError: - pass + print("No configuration found, cannot run the console.") else: - import rlcompleter - readline.set_completer(rlcompleter.Completer(globals()).complete) - readline.parse_and_bind("tab:complete") + import code + try: + import readline + except ImportError: + pass + else: + import rlcompleter + readline.set_completer(rlcompleter.Completer(gl).complete) + readline.parse_and_bind("tab:complete") + + pythonrc = os.environ.get("PYTHONSTARTUP") + if pythonrc and os.path.isfile(pythonrc): + try: + execfile(pythonrc) # NOQA + except NameError: + pass + + code.interact(local=gl, banner='Nikola Console (conf = ' + 'configuration, SITE = site engine)') - pythonrc = os.environ.get("PYTHONSTARTUP") - if pythonrc and os.path.isfile(pythonrc): + def _execute(self, options, args): + """Start the console.""" + for shell in self.shells: try: - execfile(pythonrc) # NOQA - except NameError: + return getattr(self, shell)() + except ImportError: pass - code.interact(local=globals()) + raise ImportError diff --git a/nikola/plugins/command_deploy.py b/nikola/plugins/command_deploy.py index ffa86ab..3277567 100644 --- a/nikola/plugins/command_deploy.py +++ b/nikola/plugins/command_deploy.py @@ -23,7 +23,12 @@ # 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 +import subprocess + from nikola.plugin_categories import Command @@ -37,5 +42,24 @@ class Deploy(Command): def _execute(self, command, args): for command in self.site.config['DEPLOY_COMMANDS']: + + # Get last succesful deploy date + timestamp_path = os.path.join(self.site.config['CACHE_FOLDER'], 'lastdeploy') + try: + with open(timestamp_path, 'rb') as inf: + last_deploy = literal_eval(inf.read().strip()) + except Exception: + last_deploy = datetime(1970, 1, 1) # NOQA + print("==>", command) - os.system(command) + ret = subprocess.check_call(command, shell=True) + if ret != 0: # failed deployment + raise Exception("Failed deployment") + print("Successful deployment") + new_deploy = datetime.now() + # Store timestamp of successful deployment + with codecs.open(timestamp_path, 'wb+', 'utf8') as outf: + outf.write(repr(new_deploy)) + # Here is where we would do things with whatever is + # on self.site.timeline and is newer than + # last_deploy diff --git a/nikola/plugins/command_import_blogger.py b/nikola/plugins/command_import_blogger.py index 35a702e..ecc4676 100644 --- a/nikola/plugins/command_import_blogger.py +++ b/nikola/plugins/command_import_blogger.py @@ -73,7 +73,7 @@ class CommandImportBlogger(Command): ] def _execute(self, options, args): - """Import a Wordpress blog from an export file into a Nikola site.""" + """Import a Blogger blog from an export file into a Nikola site.""" # Parse the data if feedparser is None: @@ -126,7 +126,7 @@ class CommandImportBlogger(Command): def generate_base_site(self): if not os.path.exists(self.output_folder): - os.system('nikola init --empty ' + self.output_folder) + os.system('nikola init ' + self.output_folder) else: self.import_into_existing_site = True print('The folder {0} already exists - assuming that this is a ' @@ -176,9 +176,16 @@ class CommandImportBlogger(Command): @staticmethod def write_metadata(filename, title, slug, post_date, description, tags): + if not description: + description = "" + with codecs.open(filename, "w+", "utf8") as fd: - fd.write('\n'.join((title, slug, post_date, ','.join(tags), '', - description))) + fd.write('{0}\n'.format(title)) + fd.write('{0}\n'.format(slug)) + fd.write('{0}\n'.format(post_date)) + fd.write('{0}\n'.format(','.join(tags))) + fd.write('\n') + fd.write('{0}\n'.format(description)) def import_item(self, item, out_folder=None): """Takes an item from the feed and creates a post file.""" @@ -284,7 +291,7 @@ class CommandImportBlogger(Command): if not self.import_into_existing_site: filename = 'conf.py' else: - filename = 'conf.py.wordpress_import-{0}'.format( + filename = 'conf.py.blogger_import-{0}'.format( datetime.datetime.now().strftime('%Y%m%d_%H%M%s')) config_output_path = os.path.join(self.output_folder, filename) print('Configuration will be written to: ' + config_output_path) diff --git a/nikola/plugins/command_import_wordpress.py b/nikola/plugins/command_import_wordpress.py index e7ecca0..b45fe78 100644 --- a/nikola/plugins/command_import_wordpress.py +++ b/nikola/plugins/command_import_wordpress.py @@ -90,7 +90,6 @@ class CommandImportWordpress(Command): def _execute(self, options={}, args=[]): """Import a Wordpress blog from an export file into a Nikola site.""" # Parse the data - print(options, args) if requests is None: print('To use the import_wordpress command,' ' you have to install the "requests" package.') @@ -100,10 +99,16 @@ class CommandImportWordpress(Command): print(self.help()) return - options['filename'] = args[0] + options['filename'] = args.pop(0) - if len(args) > 1: - options['output_folder'] = args[1] + if args and ('output_folder' not in args or + options['output_folder'] == 'new_site'): + options['output_folder'] = args.pop(0) + + if args: + print('You specified additional arguments ({0}). Please consider ' + 'putting these arguments before the filename if you ' + 'are running into problems.'.format(args)) self.wordpress_export_file = options['filename'] self.squash_newlines = options.get('squash_newlines', False) @@ -204,8 +209,12 @@ class CommandImportWordpress(Command): 'PUT TITLE HERE') context['BLOG_DESCRIPTION'] = get_text_tag( channel, 'description', 'PUT DESCRIPTION HERE') - context['SITE_URL'] = get_text_tag(channel, 'link', '#') context['BASE_URL'] = get_text_tag(channel, 'link', '#') + if not context['BASE_URL']: + base_site_url = channel.find('{{{0}}}author'.format(wordpress_namespace)) + context['BASE_URL'] = get_text_tag(base_site_url, None, "http://foo.com") + context['SITE_URL'] = context['BASE_URL'] + author = channel.find('{{{0}}}author'.format(wordpress_namespace)) context['BLOG_EMAIL'] = get_text_tag( author, @@ -314,7 +323,13 @@ class CommandImportWordpress(Command): # 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) - slug = utils.slugify(urlparse(link).path) + path = urlparse(link).path + + # In python 2, path is a str. slug requires a unicode + # object. Luckily, paths are also ASCII + if isinstance(path, utils.bytes_str): + path = path.decode('ASCII') + slug = utils.slugify(path) if not slug: # it happens if the post has no "nice" URL slug = get_text_tag( item, '{{{0}}}post_name'.format(wordpress_namespace), None) @@ -334,7 +349,10 @@ class CommandImportWordpress(Command): item, '{http://purl.org/rss/1.0/modules/content/}encoded', '') tags = [] - if status != 'publish': + if status == 'trash': + print('Trashed post "{0}" will not be imported.'.format(title)) + return + elif status != 'publish': tags.append('draft') is_draft = True else: diff --git a/nikola/plugins/command_install_theme.py b/nikola/plugins/command_install_theme.py index 04a2cce..2a0a0cc 100644 --- a/nikola/plugins/command_install_theme.py +++ b/nikola/plugins/command_install_theme.py @@ -64,6 +64,10 @@ class CommandInstallTheme(Command): def _execute(self, options, args): """Install theme into current site.""" + if requests is None: + print('This command requires the requests package be installed.') + return False + listing = options['list'] url = options['url'] if args: diff --git a/nikola/plugins/command_new_post.py b/nikola/plugins/command_new_post.py index a823da3..933a51a 100644 --- a/nikola/plugins/command_new_post.py +++ b/nikola/plugins/command_new_post.py @@ -49,13 +49,31 @@ def filter_post_pages(compiler, is_post, post_compilers, post_pages): if not filtered: type_name = "post" if is_post else "page" - raise Exception("Can't find a way, using your configuration, to create" + raise Exception("Can't find a way, using your configuration, to create " "a {0} in format {1}. You may want to tweak " "post_compilers or post_pages in conf.py".format( type_name, compiler)) return filtered[0] +def get_default_compiler(is_post, post_compilers, post_pages): + """Given post_compilers and post_pages, return a reasonable + default compiler for this kind of post/page. + """ + + # First throw away all the post_pages with the wrong is_post + filtered = [entry for entry in post_pages if entry[3] == is_post] + + # Get extensions in filtered post_pages until one matches a compiler + for entry in filtered: + extension = os.path.splitext(entry[0])[-1] + for compiler, extensions in post_compilers.items(): + if extension in extensions: + return compiler + # No idea, back to default behaviour + return 'rest' + + class CommandNewPost(Command): """Create a new post.""" @@ -105,7 +123,7 @@ class CommandNewPost(Command): 'short': 'f', 'long': 'format', 'type': str, - 'default': 'rest', + 'default': '', 'help': 'Markup format for post, one of rest, markdown, wiki, ' 'bbcode, html, textile, txt2tags', } @@ -140,6 +158,12 @@ class CommandNewPost(Command): post_format = options['post_format'] + if not post_format: # Issue #400 + post_format = get_default_compiler( + is_post, + self.site.config['post_compilers'], + self.site.config['post_pages']) + if post_format not in compiler_names: print("ERROR: Unknown post format " + post_format) return @@ -160,12 +184,14 @@ class CommandNewPost(Command): title = sys.stdin.readline() else: print("Title:", title) - if isinstance(title, bytes): + if isinstance(title, utils.bytes_str): title = title.decode(sys.stdin.encoding) title = title.strip() if not path: slug = utils.slugify(title) else: + if isinstance(path, utils.bytes_str): + path = path.decode(sys.stdin.encoding) slug = utils.slugify(os.path.splitext(os.path.basename(path))[0]) date = datetime.datetime.now().strftime('%Y/%m/%d %H:%M:%S') data = [title, slug, date, tags] @@ -186,7 +212,9 @@ class CommandNewPost(Command): d_name = os.path.dirname(txt_path) if not os.path.exists(d_name): os.makedirs(d_name) - compiler_plugin.create_post(txt_path, onefile, title, slug, date, tags) + compiler_plugin.create_post( + txt_path, onefile, title=title, + slug=slug, date=date, tags=tags) if not onefile: # write metadata file with codecs.open(meta_path, "wb+", "utf8") as fd: diff --git a/nikola/plugins/command_planetoid.plugin b/nikola/plugins/command_planetoid.plugin new file mode 100644 index 0000000..8636d49 --- /dev/null +++ b/nikola/plugins/command_planetoid.plugin @@ -0,0 +1,9 @@ +[Core] +Name = planetoid +Module = command_planetoid + +[Documentation] +Author = Roberto Alsina +Version = 0.1 +Website = http://nikola.ralsina.com.ar +Description = Maintain a planet-like site diff --git a/nikola/plugins/command_planetoid/__init__.py b/nikola/plugins/command_planetoid/__init__.py new file mode 100644 index 0000000..183dd51 --- /dev/null +++ b/nikola/plugins/command_planetoid/__init__.py @@ -0,0 +1,287 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2012 Roberto Alsina y otros. + +# Permission is hereby granted, free of charge, to any +# person obtaining a copy of this software and associated +# documentation files (the "Software"), to deal in the +# Software without restriction, including without limitation +# the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the +# Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice +# shall be included in all copies or substantial portions of +# the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR +# PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS +# OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR +# OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +# OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +# SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +from __future__ import print_function, unicode_literals +import codecs +import datetime +import hashlib +from optparse import OptionParser +import os +import sys + +from doit.tools import timeout +from nikola.plugin_categories import Command, Task +from nikola.utils import config_changed + +try: + import feedparser +except ImportError: + feedparser = None # NOQA + +try: + import peewee +except ImportError: + peewee = None + + +if peewee is not None: + class Feed(peewee.Model): + name = peewee.CharField() + url = peewee.CharField(max_length=200) + last_status = peewee.CharField(null=True) + etag = peewee.CharField(max_length=200) + last_modified = peewee.DateTimeField() + + class Entry(peewee.Model): + date = peewee.DateTimeField() + feed = peewee.ForeignKeyField(Feed) + content = peewee.TextField(max_length=20000) + link = peewee.CharField(max_length=200) + title = peewee.CharField(max_length=200) + guid = peewee.CharField(max_length=200) + + +class Planetoid(Command, Task): + """Maintain a planet-like thing.""" + name = "planetoid" + + def init_db(self): + # setup database + Feed.create_table(fail_silently=True) + Entry.create_table(fail_silently=True) + + def gen_tasks(self): + if peewee is None or sys.version_info[0] == 3: + if sys.version_info[0] == 3: + message = 'Peewee is currently incompatible with Python 3.' + else: + message = 'You need to install the \"peewee\" module.' + + yield { + 'basename': self.name, + 'name': '', + 'verbosity': 2, + 'actions': ['echo "%s"' % message] + } + else: + self.init_db() + self.load_feeds() + for task in self.task_update_feeds(): + yield task + for task in self.task_generate_posts(): + yield task + yield { + 'basename': self.name, + 'name': '', + 'actions': [], + 'file_dep': ['feeds'], + 'task_dep': [ + self.name + "_fetch_feed", + self.name + "_generate_posts", + ] + } + + def run(self, *args): + parser = OptionParser(usage="nikola %s [options]" % self.name) + (options, args) = parser.parse_args(list(args)) + + def load_feeds(self): + "Read the feeds file, add it to the database." + feeds = [] + feed = name = None + for line in codecs.open('feeds', 'r', 'utf-8'): + line = line.strip() + if line.startswith("#"): + continue + elif line.startswith('http'): + feed = line + elif line: + name = line + if feed and name: + feeds.append([feed, name]) + feed = name = None + + def add_feed(name, url): + f = Feed.create( + name=name, + url=url, + etag='foo', + last_modified=datetime.datetime(1970, 1, 1), + ) + f.save() + + def update_feed_url(feed, url): + feed.url = url + feed.save() + + for feed, name in feeds: + f = Feed.select().where(Feed.name == name) + if not list(f): + add_feed(name, feed) + elif list(f)[0].url != feed: + update_feed_url(list(f)[0], feed) + + def task_update_feeds(self): + """Download feed contents, add entries to the database.""" + def update_feed(feed): + modified = feed.last_modified.timetuple() + etag = feed.etag + try: + parsed = feedparser.parse( + feed.url, + etag=etag, + modified=modified + ) + feed.last_status = str(parsed.status) + except: # Probably a timeout + # TODO: log failure + return + if parsed.feed.get('title'): + print(parsed.feed.title) + else: + print(feed.url) + feed.etag = parsed.get('etag', 'foo') + modified = tuple(parsed.get('date_parsed', (1970, 1, 1)))[:6] + print("==========>", modified) + modified = datetime.datetime(*modified) + feed.last_modified = modified + feed.save() + # No point in adding items from missinfg feeds + if parsed.status > 400: + # TODO log failure + return + for entry_data in parsed.entries: + print("=========================================") + date = entry_data.get('published_parsed', None) + if date is None: + date = entry_data.get('updated_parsed', None) + if date is None: + print("Can't parse date from:") + print(entry_data) + return False + print("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) + if content: + content = content[0].value + if not content: + content = entry_data.get('description', None) + if not content: + content = entry_data.get('summary', 'Sin contenido') + guid = str(entry_data.get('guid', entry_data.link)) + link = entry_data.link + print(repr([date, title])) + e = list(Entry.select().where(Entry.guid == guid)) + print( + repr(dict( + date=date, + title=title, + content=content, + guid=guid, + feed=feed, + link=link, + )) + ) + if not e: + entry = Entry.create( + date=date, + title=title, + content=content, + guid=guid, + feed=feed, + link=link, + ) + else: + entry = e[0] + entry.date = date + entry.title = title + entry.content = content + entry.link = link + entry.save() + flag = False + for feed in Feed.select(): + flag = True + task = { + 'basename': self.name + "_fetch_feed", + 'name': str(feed.url), + 'actions': [(update_feed, (feed, ))], + 'uptodate': [timeout(datetime.timedelta(minutes= + self.site.config.get('PLANETOID_REFRESH', 60)))], + } + yield task + if not flag: + yield { + 'basename': self.name + "_fetch_feed", + 'name': '', + 'actions': [], + } + + def task_generate_posts(self): + """Generate post files for the blog entries.""" + def gen_id(entry): + h = hashlib.md5() + h.update(entry.feed.name.encode('utf8')) + h.update(entry.guid) + return h.hexdigest() + + def generate_post(entry): + unique_id = gen_id(entry) + meta_path = os.path.join('posts', unique_id + '.meta') + post_path = os.path.join('posts', unique_id + '.txt') + with codecs.open(meta_path, 'wb+', 'utf8') as fd: + fd.write('%s\n' % entry.title.replace('\n', ' ')) + fd.write('%s\n' % unique_id) + fd.write('%s\n' % entry.date.strftime('%Y/%m/%d %H:%M')) + fd.write('\n') + fd.write('%s\n' % entry.link) + with codecs.open(post_path, 'wb+', 'utf8') as fd: + fd.write('.. raw:: html\n\n') + content = entry.content + if not content: + content = 'Sin contenido' + for line in content.splitlines(): + fd.write(' %s\n' % line) + + if not os.path.isdir('posts'): + os.mkdir('posts') + flag = False + for entry in Entry.select().order_by(Entry.date.desc()): + flag = True + entry_id = gen_id(entry) + yield { + 'basename': self.name + "_generate_posts", + 'targets': [os.path.join('posts', entry_id + '.meta'), os.path.join('posts', entry_id + '.txt')], + 'name': entry_id, + 'actions': [(generate_post, (entry,))], + 'uptodate': [config_changed({1: entry})], + 'task_dep': [self.name + "_fetch_feed"], + } + if not flag: + yield { + 'basename': self.name + "_generate_posts", + 'name': '', + 'actions': [], + } diff --git a/nikola/plugins/compile_bbcode.py b/nikola/plugins/compile_bbcode.py index 26de727..f8022f3 100644 --- a/nikola/plugins/compile_bbcode.py +++ b/nikola/plugins/compile_bbcode.py @@ -60,19 +60,17 @@ class CompileTextile(PageCompiler): output = self.parser.format(data) out_file.write(output) - def create_post(self, path, onefile=False, title="", slug="", date="", - tags=""): + def create_post(self, path, onefile=False, **kw): + metadata = {} + metadata.update(self.default_metadata) + metadata.update(kw) d_name = os.path.dirname(path) if not os.path.isdir(d_name): os.makedirs(os.path.dirname(path)) with codecs.open(path, "wb+", "utf8") as fd: if onefile: fd.write('[note][/note]\n\n') - fd.write("\nWrite your post here.") + fd.write("Write your post here.") diff --git a/nikola/plugins/compile_html.py b/nikola/plugins/compile_html.py index 6c1c381..7551b33 100644 --- a/nikola/plugins/compile_html.py +++ b/nikola/plugins/compile_html.py @@ -43,19 +43,17 @@ class CompileHtml(PageCompiler): pass shutil.copyfile(source, dest) - def create_post(self, path, onefile=False, title="", slug="", - date="", tags=""): + def create_post(self, path, onefile=False, **kw): + metadata = {} + metadata.update(self.default_metadata) + metadata.update(kw) d_name = os.path.dirname(path) if not os.path.isdir(d_name): os.makedirs(os.path.dirname(path)) with codecs.open(path, "wb+", "utf8") as fd: if onefile: fd.write('\n\n') fd.write("\n

      Write your post here.

      ") diff --git a/nikola/plugins/compile_ipynb.plugin b/nikola/plugins/compile_ipynb.plugin new file mode 100644 index 0000000..51051e0 --- /dev/null +++ b/nikola/plugins/compile_ipynb.plugin @@ -0,0 +1,10 @@ +[Core] +Name = ipynb +Module = compile_ipynb + +[Documentation] +Author = Damián Avila +Version = 0.1 +Website = http://www.oquanta.info +Description = Compile IPython notebooks into HTML + diff --git a/nikola/plugins/compile_ipynb/README.txt b/nikola/plugins/compile_ipynb/README.txt new file mode 100644 index 0000000..2cfd45e --- /dev/null +++ b/nikola/plugins/compile_ipynb/README.txt @@ -0,0 +1,35 @@ +To make this work... + +1- First, you have to put this plugin in your_site/plugins/ folder. + +2- Then, you have to download the custom nbconvert from here: https://github.com/damianavila/compile_ipynb-for-Nikola.git +and put it inside your_site/plugins/compile_ipynb/ folder + +3- Also, you have to use the site-ipython theme (or make a new one containing the ipython css, mathjax.js and the proper template). +You can get it here: https://github.com/damianavila/site-ipython-theme-for-Nikola + +4- Finally, you have to put: + +post_pages = ( + ("posts/*.ipynb", "posts", "post.tmpl", True), + ("stories/*.ipynb", "stories", "story.tmpl", False), +) + +in your conf.py + +Then... to use it: + +$nikola new_page -f ipynb + +**NOTE**: Just IGNORE the "-1" and "-2" options in nikola new_page command, by default this compiler +create one metadata file and the corresponding naive IPython notebook. + +$nikola build + +And deploy the output folder... to see it locally: $nikola serve + +If you have any doubts, just ask: @damianavila + +Cheers. + +Damián diff --git a/nikola/plugins/compile_ipynb/__init__.py b/nikola/plugins/compile_ipynb/__init__.py new file mode 100644 index 0000000..d38f6f2 --- /dev/null +++ b/nikola/plugins/compile_ipynb/__init__.py @@ -0,0 +1,100 @@ +# Copyright (c) 2013 Damian Avila. + +# 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. + +"""Implementation of compile_html based on nbconvert.""" + +from __future__ import unicode_literals, print_function +import codecs +import os + +try: + from .nbformat import current as nbformat + from .nbconvert.converters import bloggerhtml as nbconverter + bloggerhtml = True +except ImportError: + bloggerhtml = None + +from nikola.plugin_categories import PageCompiler + + +class CompileIPynb(PageCompiler): + """Compile IPynb into HTML.""" + + name = "ipynb" + + def compile_html(self, source, dest): + if bloggerhtml is None: + raise Exception('To build this site, you also need ' + 'https://github.com/damianavila/com' + 'pile_ipynb-for-Nikola.git.') + try: + os.makedirs(os.path.dirname(dest)) + except: + pass + converter = nbconverter.ConverterBloggerHTML() + with codecs.open(dest, "w+", "utf8") as out_file: + with codecs.open(source, "r", "utf8") as in_file: + data = in_file.read() + converter.nb = nbformat.reads_json(data) + output = converter.convert() + out_file.write(output) + + def create_post(self, path, onefile=False, **kw): + metadata = {} + metadata.update(self.default_metadata) + metadata.update(kw) + d_name = os.path.dirname(path) + if not os.path.isdir(d_name): + os.makedirs(os.path.dirname(path)) + meta_path = os.path.join(d_name, kw['slug'] + ".meta") + with codecs.open(meta_path, "wb+", "utf8") as fd: + if onefile: + fd.write('%s\n' % kw['title']) + fd.write('%s\n' % kw['slug']) + fd.write('%s\n' % kw['date']) + fd.write('%s\n' % kw['tags']) + print("Your post's metadata is at: ", meta_path) + with codecs.open(path, "wb+", "utf8") as fd: + fd.write("""{ + "metadata": { + "name": "%s" + }, + "nbformat": 3, + "nbformat_minor": 0, + "worksheets": [ + { + "cells": [ + { + "cell_type": "code", + "collapsed": false, + "input": [], + "language": "python", + "metadata": {}, + "outputs": [] + } + ], + "metadata": {} + } + ] +}""" % kw['slug']) diff --git a/nikola/plugins/compile_markdown/__init__.py b/nikola/plugins/compile_markdown/__init__.py index 7aa03a9..ae700e6 100644 --- a/nikola/plugins/compile_markdown/__init__.py +++ b/nikola/plugins/compile_markdown/__init__.py @@ -24,14 +24,28 @@ """Implementation of compile_html based on markdown.""" +from __future__ import unicode_literals + import codecs import os -import re try: from markdown import markdown + + from nikola.plugins.compile_markdown.mdx_nikola import NikolaExtension + nikola_extension = NikolaExtension() + + from nikola.plugins.compile_markdown.mdx_gist import GistExtension + gist_extension = GistExtension() + + from nikola.plugins.compile_markdown.mdx_podcast import PodcastExtension + podcast_extension = PodcastExtension() + except ImportError: markdown = None # NOQA + nikola_extension = None + gist_extension = None + podcast_extension = None from nikola.plugin_categories import PageCompiler @@ -41,6 +55,9 @@ class CompileMarkdown(PageCompiler): name = "markdown" + extensions = ['fenced_code', 'codehilite', gist_extension, + nikola_extension, podcast_extension] + def compile_html(self, source, dest): if markdown is None: raise Exception('To build this site, you need to install the ' @@ -52,30 +69,20 @@ class CompileMarkdown(PageCompiler): with codecs.open(dest, "w+", "utf8") as out_file: with codecs.open(source, "r", "utf8") as in_file: data = in_file.read() - output = markdown(data, ['fenced_code', 'codehilite']) - # h1 is reserved for the title so increment all header levels - for n in reversed(range(1, 9)): - output = re.sub(''.format(n), ''.format(n + 1), - output) - output = re.sub(''.format(n), ''.format(n + 1), - output) - # python-markdown's highlighter uses the class 'codehilite' to wrap - # code, # instead of the standard 'code'. None of the standard - # pygments stylesheets use this class, so swap it to be 'code' - output = re.sub(r'(]+class="[^"]*)codehilite([^>]+)', - r'\1code\2', output) + output = markdown(data, self.extensions) out_file.write(output) - def create_post(self, path, onefile=False, title="", slug="", date="", - tags=""): + def create_post(self, path, onefile=False, **kw): + metadata = {} + metadata.update(self.default_metadata) + metadata.update(kw) + d_name = os.path.dirname(path) + if not os.path.isdir(d_name): + os.makedirs(os.path.dirname(path)) with codecs.open(path, "wb+", "utf8") as fd: if onefile: fd.write('\n\n') - fd.write("\nWrite your post here.") + fd.write("Write your post here.") diff --git a/nikola/plugins/compile_markdown/mdx_gist.py b/nikola/plugins/compile_markdown/mdx_gist.py new file mode 100644 index 0000000..808e383 --- /dev/null +++ b/nikola/plugins/compile_markdown/mdx_gist.py @@ -0,0 +1,189 @@ +# -*- coding: utf-8 -*- +# +# Copyright (c) 2013 Michael Rabbitt. +# +# 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. +# +# Inspired by "[Python] reStructuredText GitHub Gist directive" +# (https://gist.github.com/brianhsu/1407759), public domain by Brian Hsu + +from __future__ import print_function + + +''' +Extension to Python Markdown for Embedded Gists (gist.github.com) + +Basic Example: + + >>> import markdown + >>> text = """ + ... Text of the gist: + ... [:gist: 4747847] + ... """ + >>> html = markdown.markdown(text, [GistExtension()]) + >>> print(html) +

      Text of the gist: +

      + + +
      +

      + +Example with filename: + + >>> import markdown + >>> text = """ + ... Text of the gist: + ... [:gist: 4747847 zen.py] + ... """ + >>> html = markdown.markdown(text, [GistExtension()]) + >>> print(html) +

      Text of the gist: +

      + + +
      +

      + +Example using reStructuredText syntax: + + >>> import markdown + >>> text = """ + ... Text of the gist: + ... .. gist:: 4747847 zen.py + ... """ + >>> html = markdown.markdown(text, [GistExtension()]) + >>> print(html) +

      Text of the gist: +

      + + +
      +

      +''' +from __future__ import unicode_literals +import warnings +from markdown.extensions import Extension +from markdown.inlinepatterns import Pattern +from markdown.util import AtomicString +from markdown.util import etree + +try: + import requests +except ImportError: + requests = None # NOQA + +GIST_JS_URL = "https://gist.github.com/{0}.js" +GIST_FILE_JS_URL = "https://gist.github.com/{0}.js?file={1}" +GIST_RAW_URL = "https://raw.github.com/gist/{0}" +GIST_FILE_RAW_URL = "https://raw.github.com/gist/{0}/{1}" + +GIST_MD_RE = r'\[:gist:\s*(?P\d+)(?:\s*(?P.+?))?\]' +GIST_RST_RE = r'(?m)^\.\.\s*gist::\s*(?P\d+)(?:\s*(?P.+))\s*$' + + +class GistPattern(Pattern): + """ InlinePattern for footnote markers in a document's body text. """ + + def __init__(self, pattern, configs): + Pattern.__init__(self, pattern) + + def get_raw_gist_with_filename(self, gist_id, filename): + url = GIST_FILE_RAW_URL.format(gist_id, filename) + return requests.get(url).text + + def get_raw_gist(self, gist_id): + url = GIST_RAW_URL.format(gist_id) + return requests.get(url).text + + def handleMatch(self, m): + gist_id = m.group('gist_id') + gist_file = m.group('filename') + + gist_elem = etree.Element('div') + gist_elem.set('class', 'gist') + script_elem = etree.SubElement(gist_elem, 'script') + + if gist_file: + script_elem.set('src', GIST_FILE_JS_URL.format( + gist_id, gist_file)) + + else: + script_elem.set('src', GIST_JS_URL.format( + gist_id)) + + if requests: + if gist_file: + raw_gist = (self.get_raw_gist_with_filename( + gist_id, gist_file)) + script_elem.set('src', GIST_FILE_JS_URL.format( + gist_id, gist_file)) + + else: + raw_gist = (self.get_raw_gist(gist_id)) + script_elem.set('src', GIST_JS_URL.format( + gist_id)) + + # Insert source as
       within