aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLibravatarAgustin Henze <tin@sluc.org.ar>2013-01-02 08:35:03 -0300
committerLibravatarAgustin Henze <tin@sluc.org.ar>2013-01-02 08:35:03 -0300
commit9c5708cc92af894e414bc76ee35ec2230de5d288 (patch)
tree61bd56b5517a4713626c254981143e008c719469
parent0f2c04e70a0ffdd0892d6970cafbcd952d221db5 (diff)
Imported Upstream version 5.1upstream/5.1
-rw-r--r--.travis.yml14
-rw-r--r--AUTHORS.txt2
-rw-r--r--CHANGES.txt41
-rw-r--r--LICENSE.txt23
-rw-r--r--docs/creating-a-theme.txt283
-rw-r--r--docs/extending.txt12
-rw-r--r--docs/manual.txt85
-rw-r--r--docs/theming.txt23
-rw-r--r--nikola/__init__.py6
-rwxr-xr-xnikola/conf.py.in (renamed from nikola/data/samplesite/conf.py.in)39
-rw-r--r--nikola/console.py7
-rw-r--r--nikola/data/samplesite/README.txt27
-rwxr-xr-xnikola/data/samplesite/conf.py274
-rw-r--r--nikola/data/samplesite/posts/1.txt2
-rw-r--r--nikola/data/samplesite/stories/listings-demo.txt10
-rw-r--r--nikola/data/samplesite/stories/slides-demo.txt17
-rw-r--r--nikola/data/themes/default/assets/css/bootstrap-responsive.css68
-rw-r--r--nikola/data/themes/default/assets/css/bootstrap.css1241
-rw-r--r--nikola/data/themes/default/assets/css/slides.css11
-rw-r--r--nikola/data/themes/default/assets/js/bootstrap.js386
-rwxr-xr-xnikola/data/themes/default/assets/js/slides.jquery.js555
-rw-r--r--nikola/data/themes/default/bundles4
-rw-r--r--nikola/data/themes/default/messages/de.py21
-rw-r--r--nikola/data/themes/default/messages/en.py25
-rw-r--r--nikola/data/themes/default/messages/es.py21
-rw-r--r--nikola/data/themes/default/messages/fr.py17
-rw-r--r--nikola/data/themes/default/messages/gr.py20
-rw-r--r--nikola/data/themes/default/messages/it.py20
-rw-r--r--nikola/data/themes/default/messages/messages_de.py22
-rw-r--r--nikola/data/themes/default/messages/messages_en.py27
-rw-r--r--nikola/data/themes/default/messages/messages_es.py22
-rw-r--r--nikola/data/themes/default/messages/messages_fr.py18
-rw-r--r--nikola/data/themes/default/messages/messages_gr.py21
-rw-r--r--nikola/data/themes/default/messages/messages_it.py23
-rw-r--r--nikola/data/themes/default/messages/messages_ru.py22
-rw-r--r--nikola/data/themes/default/messages/ru.py21
-rw-r--r--nikola/data/themes/default/templates/base.tmpl62
-rw-r--r--nikola/data/themes/default/templates/base_helper.tmpl74
-rw-r--r--nikola/data/themes/default/templates/index.tmpl28
-rw-r--r--nikola/data/themes/default/templates/index_helper.tmpl31
-rw-r--r--nikola/data/themes/default/templates/list_post.tmpl14
-rw-r--r--nikola/data/themes/default/templates/post.tmpl49
-rw-r--r--nikola/data/themes/default/templates/post_helper.tmpl54
-rw-r--r--nikola/data/themes/default/templates/tag.tmpl2
-rw-r--r--nikola/data/themes/default/templates/tags.tmpl2
-rw-r--r--nikola/data/themes/jinja-default/README2
-rw-r--r--nikola/data/themes/jinja-default/templates/base.tmpl31
-rw-r--r--nikola/data/themes/jinja-default/templates/index.tmpl2
-rw-r--r--nikola/data/themes/jinja-default/templates/list_post.tmpl13
-rw-r--r--nikola/data/themes/jinja-default/templates/post.tmpl2
-rw-r--r--nikola/data/themes/jinja-default/templates/tag.tmpl2
-rw-r--r--nikola/data/themes/monospace/assets/css/code.css62
-rw-r--r--nikola/data/themes/monospace/assets/css/colorbox.css85
-rw-r--r--nikola/data/themes/monospace/assets/css/rst.css315
-rw-r--r--nikola/data/themes/monospace/assets/css/theme.css14
-rw-r--r--nikola/data/themes/monospace/bundles1
l---------nikola/data/themes/monospace/messages1
-rw-r--r--nikola/data/themes/monospace/templates/base.tmpl44
-rw-r--r--nikola/data/themes/monospace/templates/base_helper.tmpl64
-rw-r--r--nikola/data/themes/monospace/templates/gallery.tmpl27
-rw-r--r--nikola/data/themes/monospace/templates/index.tmpl27
-rw-r--r--nikola/data/themes/monospace/templates/index_helper.tmpl31
-rw-r--r--nikola/data/themes/monospace/templates/list.tmpl14
-rw-r--r--nikola/data/themes/monospace/templates/list_post.tmpl14
-rw-r--r--nikola/data/themes/monospace/templates/listing.tmpl10
-rw-r--r--nikola/data/themes/monospace/templates/post.tmpl28
-rw-r--r--nikola/data/themes/monospace/templates/post_helper.tmpl54
-rw-r--r--nikola/data/themes/monospace/templates/story.tmpl8
-rw-r--r--nikola/data/themes/monospace/templates/tag.tmpl7
-rw-r--r--nikola/data/themes/monospace/templates/tags.tmpl14
l---------nikola/data/themes/orphan/assets/css/code.css1
l---------nikola/data/themes/orphan/assets/css/colorbox.css1
l---------nikola/data/themes/orphan/assets/css/rst.css1
-rw-r--r--nikola/data/themes/orphan/assets/css/theme.css0
-rw-r--r--nikola/data/themes/orphan/bundles1
l---------nikola/data/themes/orphan/messages1
-rw-r--r--nikola/data/themes/orphan/templates/base.tmpl36
-rw-r--r--nikola/data/themes/orphan/templates/base_helper.tmpl64
-rw-r--r--nikola/data/themes/orphan/templates/gallery.tmpl27
-rw-r--r--nikola/data/themes/orphan/templates/index.tmpl18
-rw-r--r--nikola/data/themes/orphan/templates/index_helper.tmpl31
-rw-r--r--nikola/data/themes/orphan/templates/list.tmpl14
-rw-r--r--nikola/data/themes/orphan/templates/list_post.tmpl14
-rw-r--r--nikola/data/themes/orphan/templates/listing.tmpl10
-rw-r--r--nikola/data/themes/orphan/templates/post.tmpl20
-rw-r--r--nikola/data/themes/orphan/templates/post_helper.tmpl54
-rw-r--r--nikola/data/themes/orphan/templates/story.tmpl8
-rw-r--r--nikola/data/themes/orphan/templates/tag.tmpl7
-rw-r--r--nikola/data/themes/orphan/templates/tags.tmpl14
-rw-r--r--nikola/data/themes/site/assets/css/theme.css12
-rw-r--r--nikola/data/themes/site/templates/base.tmpl67
-rw-r--r--nikola/data/themes/site/templates/post.tmpl45
-rw-r--r--nikola/filters.py75
-rw-r--r--nikola/nikola.py148
-rw-r--r--nikola/plugin_categories.py28
-rw-r--r--nikola/plugins/__init__.py4
-rw-r--r--nikola/plugins/command_bootswatch_theme.py47
-rw-r--r--nikola/plugins/command_build.py36
-rw-r--r--nikola/plugins/command_check.py58
-rw-r--r--nikola/plugins/command_console.plugin9
-rw-r--r--nikola/plugins/command_console.py35
-rw-r--r--nikola/plugins/command_deploy.py27
-rw-r--r--nikola/plugins/command_import_wordpress.py330
-rw-r--r--nikola/plugins/command_init.py72
-rw-r--r--nikola/plugins/command_install_theme.py53
-rw-r--r--nikola/plugins/command_new_post.py46
-rw-r--r--nikola/plugins/command_serve.py37
-rw-r--r--nikola/plugins/compile_html.py24
-rw-r--r--nikola/plugins/compile_markdown/__init__.py33
-rw-r--r--nikola/plugins/compile_rest/__init__.py34
-rw-r--r--nikola/plugins/compile_rest/pygments_code_block_directive.py38
-rw-r--r--nikola/plugins/compile_rest/slides.py89
-rw-r--r--nikola/plugins/compile_rest/youtube.py24
-rw-r--r--nikola/plugins/task_archive.py62
-rw-r--r--nikola/plugins/task_copy_assets.py35
-rw-r--r--nikola/plugins/task_copy_files.py24
-rw-r--r--nikola/plugins/task_create_bundles.py27
-rw-r--r--nikola/plugins/task_indexes.py37
-rw-r--r--nikola/plugins/task_redirect.py30
-rw-r--r--nikola/plugins/task_render_galleries.py81
-rw-r--r--nikola/plugins/task_render_listings.py24
-rw-r--r--nikola/plugins/task_render_pages.py24
-rw-r--r--nikola/plugins/task_render_posts.py24
-rw-r--r--nikola/plugins/task_render_rss.py26
-rw-r--r--nikola/plugins/task_render_sources.py24
-rw-r--r--nikola/plugins/task_render_tags.py304
-rw-r--r--nikola/plugins/task_sitemap/__init__.py38
-rwxr-xr-xnikola/plugins/task_sitemap/sitemap_gen.py47
-rw-r--r--nikola/plugins/template_jinja.py36
-rw-r--r--nikola/plugins/template_mako.py28
-rw-r--r--nikola/post.py35
-rw-r--r--nikola/utils.py69
-rw-r--r--requirements-3.txt12
-rw-r--r--requirements.txt4
-rwxr-xr-xscripts/nikola2
-rw-r--r--setup.py57
-rw-r--r--tests/context.py9
-rw-r--r--tests/rss-2_0.xsd500
-rw-r--r--tests/test_command_import_wordpress.py92
-rw-r--r--tests/test_rss_feeds.py104
-rw-r--r--tests/wordpress_export_example.xml174
-rw-r--r--tests/wordpress_unicode_export.xml114
142 files changed, 6581 insertions, 1773 deletions
diff --git a/.travis.yml b/.travis.yml
new file mode 100644
index 0000000..7720bab
--- /dev/null
+++ b/.travis.yml
@@ -0,0 +1,14 @@
+language: python
+python:
+ - "2.6"
+ - "2.7"
+# The dependencies we rely on are not yet full Python 3.x compatible.
+# - "3.2"
+# command to install dependencies
+install:
+ - "pip install -r requirements.txt --use-mirrors"
+ - "pip install . --use-mirrors"
+# We run tests and afterwards nikola to see if the command is executable.
+script:
+ - nosetests
+ - nikola
diff --git a/AUTHORS.txt b/AUTHORS.txt
new file mode 100644
index 0000000..8f2d70e
--- /dev/null
+++ b/AUTHORS.txt
@@ -0,0 +1,2 @@
+Roberto Alsina <ralsina@kde.org>
+Eduardo Schettino <https://github.com/schettino72>
diff --git a/CHANGES.txt b/CHANGES.txt
index f9a822d..8029e44 100644
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@ -1,5 +1,42 @@
-New in Master
-=============
+New in v5.1
+===========
+
+Features
+--------
+
+* Support for client-side cloud tags
+* New ``slides`` directive, for image slideshows.
+* New option DATE_FORMAT
+* New "nikola console" command
+* New option CACHE_FOLDER
+* Updated bootstrap to 2.2.2
+* Wordpress importer supports redirects
+* Wordpress importer creates a url_map for Disqus thread migration
+* Wordpress importer: support for [sourcecode]
+* Added unstyled theme "orphan", useful as a base for independent themes.
+* New "monospace" theme.
+* New "Create a Theme From Scratch" tutorial.
+
+Bugfixes
+--------
+
+* Added ID attribute to gallery images for backlinking.
+* Added ALT attribute to gallery images.
+* Issue 113: refactored code in Mako templates
+* Added newline after metadata in new_post template.
+* Issue 112: RSS feeds contained invalid links
+* Issue 88: RSS feed validation via lxml
+* Isuue 169: build subcommands were broken
+* Switched addThis to Peekaboo style, as seen on flexion.org (seems less broken)
+* Remove duplicated sample config file. always build it from template (schettino72)
+* Don't use hardcoded path for custom.css (schettino72)
+* Wordpress importer: fixed issue 190, convert embedded H1 tags into H2
+* Fixed bad interaction with Yapsy 1.10
+* More elegant handling of "nikola init"
+* Don't crash if there's no assets to copy
+
+New in v5
+=========
Features
--------
diff --git a/LICENSE.txt b/LICENSE.txt
new file mode 100644
index 0000000..19c37bb
--- /dev/null
+++ b/LICENSE.txt
@@ -0,0 +1,23 @@
+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.
diff --git a/docs/creating-a-theme.txt b/docs/creating-a-theme.txt
new file mode 100644
index 0000000..0535073
--- /dev/null
+++ b/docs/creating-a-theme.txt
@@ -0,0 +1,283 @@
+Creating A Theme From Scratch (Almost)
+======================================
+
+There is some documentation about creating themes for Nikola, but maybe a tutorial is also a useful way
+to explain it. So, here it is. I'll explain how to create a theme (almost) from scratch. Alternatively,
+you can take an existing theme and modify only parts of it via inheritance, but that's for another
+document.
+
+I will try to create a theme that looks like `Vinicius Massuchetto's Monospace Theme <http://vinicius.soylocoporti.org.br/monospace-wordpress-theme/#.UN4e9lLzu3c>`_.
+
+.. TEASER_END
+
+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
+ A new site with some sample data has been created at monospace-site.
+ See README.txt in that folder for more information.
+
+ $ cd monospace-site/
+ $ mkdir themes
+ $ cp -RL ~/Desktop/proyectos/nikola/nikola/nikola/data/themes/orphan/ themes/monospace
+
+The next step is to make the testing site use this new theme, by editing ``conf.py`` and
+changing the ``THEME`` option::
+
+ # Name of the theme to use. Themes are located in themes/theme_name
+ THEME = 'monospace'
+
+Now we can already build and test the site::
+
+ $ nikola build && nikola serve
+
+.. figure:: http://ralsina.com.ar/galleries/random/monospace-1.png
+ :height: 400px
+
+ This is the almost completely unstyled "orphan" theme.
+
+Of course, the page layout is completely broken. To fix that, we need to get into templates.
+
+Templates: Page Layout
+----------------------
+
+The general page layout for the theme is done by the ``base.tmpl`` template, which is done using
+`Mako <http://www.makotemplates.org/>`_. This is orphan's ``base.tmpl``, it's not very big:
+
+.. code-block:: mako
+
+ ## -*- coding: utf-8 -*-
+ <%namespace file="base_helper.tmpl" import="*"/>
+ <!DOCTYPE html>
+ <html lang="${lang}">
+ <head>
+ ${html_head()}
+ <%block name="extra_head">
+ </%block>
+ </head>
+ <body>
+ %if add_this_buttons:
+ <script type="text/javascript">var addthis_config={"ui_language":"${lang}"};</script>
+ % endif
+ <h1 id="blog-title">
+ <a href="${abs_link('/')}" title="${blog_title}">${blog_title}</a>
+ </h1>
+ <%block name="belowtitle">
+ %if len(translations) > 1:
+ <small>
+ ${(messages[lang][u"Also available in"])}:&nbsp;
+ ${html_translations()}
+ </small>
+ %endif
+ </%block>
+ <%block name="content"></%block>
+ <small>${content_footer}</small>
+ <!--Sidebar content-->
+ <ul class="unstyled">
+ <li>${license}
+ ${html_social()}
+ ${html_sidebar_links()}
+ <li>${search_form}
+ </ul>
+ ${analytics}
+ <script type="text/javascript">jQuery("a.image-reference").colorbox({rel:"gal",maxWidth:"80%",maxHeight:"80%",scalePhotos:true});</script>
+ </body>
+
+
+It's basically a HTML document with some placeholders to be replaced with actual content, configuration options, and some helper functions.
+For example, the ``html_head`` helper can be used to add CSS or JS files in all document's ``head`` tags.
+
+Monospace is a two-column-with-footer layout, so let's copy the basics from its HTML and see what happens:
+
+.. code-block:: mako
+
+ ## -*- coding: utf-8 -*-
+ <%namespace file="base_helper.tmpl" import="*"/>
+ <!DOCTYPE html>
+ <html lang="${lang}">
+ <head>
+ ${html_head()}
+ <%block name="extra_head">
+ </%block>
+ </head>
+ <body class="home blog">
+ %if add_this_buttons:
+ <script type="text/javascript">var addthis_config={"ui_language":"${lang}"};</script>
+ % endif
+ <div id="wrap" style="width:850px">
+ <div id="container" style="width:560px">
+ <%block name="content"></%block>
+ </div>
+ <div id="sidebar">
+ <!--Sidebar content-->
+ <h1 id="blog-title">
+ <a href="${abs_link('/')}" title="${blog_title}">${blog_title}</a>
+ </h1>
+ <%block name="belowtitle">
+ %if len(translations) > 1:
+ <small>
+ ${(messages[lang][u"Also available in"])}:&nbsp;
+ ${html_translations()}
+ </small>
+ %endif
+ </%block>
+ <ul class="unstyled">
+ <li>${license}
+ ${html_social()}
+ ${html_sidebar_links()}
+ <li>${search_form}
+ </ul>
+ </div>
+ <div id="footer">
+ ${content_footer}
+ </div>
+ </div>
+ ${analytics}
+ <script type="text/javascript">jQuery("a.image-reference").colorbox({rel:"gal",maxWidth:"80%",maxHeight:"80%",scalePhotos:true});</script>
+ </body>
+
+.. figure:: http://ralsina.com.ar/galleries/random/monospace-2.png
+
+ Yikes!
+
+This will get better quickly once we add some CSS
+
+
+Base CSS
+--------
+
+The orphan theme includes just a little styling, specifically ``rest.css`` so
+the restructured text output looks reasonable, and code.css for code snippets.
+
+It also includes an empty ``assets/css/theme.css`` where you can add your own CSS.
+For example, this is taken from the original monospace theme:
+
+.. code-block:: css
+
+ body { margin:0px; padding:20px 0px; text-align:center; font-family:Monospace; color:#585858; }
+ .post { margin:0px 0px 30px 0px; padding:0px 0px 30px 0px; border-bottom:1px dotted #C8C8C8; }
+ .meta { margin:10px; padding:15px; background:#EAEAEA; clear:both; }
+ #footer { text-align:center; clear:both; margin:30px 0px 0px 0px; padding:30px 0px 0px 0px; border-top:1px dotted #C8C8C8; }
+ #wrap { margin:0px auto; text-align:left; font-size: 13px; line-height: 1.4; }
+ #container { float:right; }
+ #sidebar { overflow:hidden; clear:left; text-align:right; width:250px; height:auto; padding:0px 15px 0px 0px; border-right:1px dotted #C8C8C8; }
+ #sidebar li { list-style-type:none; }
+ #sidebar > li { margin:20px 0px; }
+ #sidebar h1 { border-bottom:1px dotted #C8C8C8; }
+ #sidebar .description { display:block; width:100%; height:auto; margin:0px 0px 10px 0px; }
+
+This will (after we rebuild it) make the site looks different of course, and getting closer to our goal:
+
+.. figure:: http://ralsina.com.ar/galleries/random/monospace-3.png
+ :height: 400px
+
+ Monospaced allright.
+
+If you compare it to `the original <http://wp-themes.com/monospace/>`_, however, you will see that the layout of
+the posts themselves is different, and that was not described in ``base.tmpl`` at all. But if you look, you'll see that
+there is a placeholder called content: ``<%block name="content"></%block>``
+
+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``:
+
+.. code-block:: mako
+
+ ## -*- coding: utf-8 -*-
+ <%namespace name="helper" file="index_helper.tmpl"/>
+ <%inherit file="base.tmpl"/>
+ <%block name="content">
+ % for post in posts:
+ <div class="post">
+ <h1><a href="${post.permalink(lang)}">${post.title(lang)}</a>
+ <small>&nbsp;&nbsp;
+ ${messages[lang]["Posted"]}: ${post.date.strftime(date_format)}
+
+ </small></h1>
+ <hr>
+ ${post.text(lang, index_teasers)}
+ ${helper.html_disqus_link(post)}
+ </div>
+ % endfor
+ ${helper.html_pager()}
+ ${helper.html_disqus_script()}
+ </%block>
+
+So, let's tweak that to be closer to the original. We put the post's metadata in a
+box, add links for the posts tags, move the date there, etc.
+
+.. code-block:: mako
+
+ ## -*- coding: utf-8 -*-
+ <%namespace name="helper" file="index_helper.tmpl"/>
+ <%inherit file="base.tmpl"/>
+ <%block name="content">
+ % for post in posts:
+ <div class="postbox">
+ <h1><a href="${post.permalink(lang)}">${post.title(lang)}</a></h1>
+ <div class="meta" style="background-color: rgb(234, 234, 234); ">
+ <span class="authordate">
+ ${messages[lang]["Posted"]}: ${post.date.strftime(date_format)}
+ </span>
+ <br>
+ <span class="tags">Tags:&nbsp;
+ %if post.tags:
+ %for tag in post.tags:
+ <a class="tag" href="${_link('tag', tag, lang)}"><span class="badge badge-info">${tag}</span></a>
+ %endfor
+ %endif
+ </span>
+ </div>
+ ${post.text(lang, index_teasers)}
+ ${helper.html_disqus_link(post)}
+ </div>
+ % endfor
+ ${helper.html_pager()}
+
+.. figure:: http://ralsina.com.ar/galleries/random/monospace-4.png
+ :height: 400px
+
+ Close enough!
+
+Then if we click on the post title, we will see some broken details in the metadata that can be fixed in ``post.tmpl``, and so on.
+
+.. code-block:: mako
+
+ ## -*- coding: utf-8 -*-
+ <%namespace name="helper" file="post_helper.tmpl"/>
+ <%inherit file="base.tmpl"/>
+ <%block name="content">
+ <div class="post">
+ ${helper.html_title()}
+ <div class="meta" style="background-color: rgb(234, 234, 234); ">
+ <span class="authordate">
+ ${messages[lang]["Posted"]}: ${post.date.strftime(date_format)} [<a href="${post.pagenames[lang]+'.txt'}">${messages[lang]["Source"]}</a>]
+ </span>
+ <br>
+ %if post.tags:
+ <span class="tags">${messages[lang]["Tags"]}:&nbsp;
+ %for tag in post.tags:
+ <a class="tag" href="${_link('tag', tag, lang)}"><span class="badge badge-info">${tag}</span></a>
+ %endfor
+ </span>
+ <br>
+ %endif
+ <span class="authordate">
+ ${helper.html_translations(post)}
+ </span>
+ </div>
+ ${post.text(lang)}
+ ${helper.html_pager(post)}
+ ${helper.html_disqus(post)}
+ </div>
+ </%block>
+
+.. figure:: http://ralsina.com.ar/galleries/random/monospace-5.png
+ :height: 400px
+
+ Details, details.
+
+The demo site exercises most of the features in Nikola, so if you make it look good, your site probably will look good too.
+This monospace theme is included with nikola, if you want to use it or play with it.
+
diff --git a/docs/extending.txt b/docs/extending.txt
index 4bbc2ea..6410e69 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:: init
[Core]
Name = serve
@@ -68,7 +68,7 @@ For your own plugin, just change the values in a sensible way. The
``Module`` will be used to find the matching python module, in this case
``command_serve.py``, from which this is the interesting bit:
-.. code_block:: python
+.. code-block:: python
from nikola.plugin_categories import Command
@@ -144,7 +144,7 @@ Nikola supports Mako and Jinja2. If you prefer some other templating
system, then you will have to write a TemplateSystem plugin. Here's how they work.
First, you have to create a .plugin file. Here's the one for the Mako plugin:
-.. code_block:: ini
+.. code-block:: ini
[Core]
Name = mako
@@ -162,7 +162,7 @@ in the obvious ways.
The "Module" option is the name of the module, which has to look something like this,
a stub for a hypothetical system called "Templater":
-.. code_block:: python
+.. code-block:: python
from nikola.plugin_categories import TemplateSystem
@@ -240,7 +240,7 @@ document, so I'll just leave you with an example, the ``copy_assets`` task.
First the ``task_copy_assets.plugin`` file, which you should copy and edit
in the logical ways:
-.. code_block:: ini
+.. code-block:: ini
[Core]
Name = copy_assets
@@ -254,7 +254,7 @@ in the logical ways:
And the ``task_copy_assets.py`` file, in its entirety:
-.. code_block:: python
+.. code-block:: python
import os
diff --git a/docs/manual.txt b/docs/manual.txt
index 202afc1..ac15d8b 100644
--- a/docs/manual.txt
+++ b/docs/manual.txt
@@ -1,7 +1,7 @@
The Nikola Handbook
===================
-:Version: 5
+:Version: 5.1
:Author: Roberto Alsina <ralsina@netmanagers.com.ar>
.. class:: alert alert-info pull-right
@@ -169,6 +169,8 @@ If you want to create a blog or a site, Nikola provides:
`Markdown <http://daringfireball.net/projects/markdown/>`_)
* Easy-to-create image galleries
* Support for displaying source code
+* Image slideshows
+* Client-side cloud tags
Also:
@@ -194,13 +196,13 @@ Longer version:
#. ``pip install -r requirements.txt`` or...
#. Install your distribution's packages for all the things
mentioned below, if they exist, or...
- #. Get all of these manually:
+ #. Get all of these manually (but why?, use requirements.txt):
#. Get python, if you don't have it.
#. Get `doit <http://python-doit.sf.net>`_
#. Get `docutils <http://docutils.sf.net>`_
#. Get `Mako <http://makotemplates.org>`_
- #. Get `PIL <http://www.pythonware.com/products/pil/>`_
+ #. Get `PIL <http://www.pythonware.com/products/pil/>`_ (or Pillow)
#. Get `Pygments <http://pygments.org/>`_
#. Get `unidecode <http://pypi.python.org/pypi/Unidecode/>`_
#. Get `lxml <http://lxml.de/>`_
@@ -212,10 +214,7 @@ Longer version:
After that, run ``nikola init sitename`` and that will create a folder called
``sitename`` containing a functional demo site.
-.. note:: Are you using Ubuntu?
-
- Then you can try using `my PPA <https://launchpad.net/~ralsina/+archive/nikola>`_
- and installing python-nikola
+Nikola is packaged for some Linux distributions, you may get that instead.
Getting Started
---------------
@@ -786,7 +785,7 @@ embed code like this::
print "Hello World!"
-Or you can include the code from a file:
+Or you can include the code from a file::
.. code-block:: python
:include: /foo/bar/baz.py
@@ -797,10 +796,10 @@ listing
To use this, you have to put your source code files inside ``listings`` or whatever your
``LISTINGS_FOLDER`` variable is set to. Assuming you have a ``foo.py`` inside that folder::
- .. listing:: foo.py
+ .. listing:: foo.py python
-Will include the source code from ``foo.py`` and also create a ``listings/foo.py.html`` page
-and the listing will have a title linking to it.
+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
~~~~~~~~~~~~~~~~~~~~~
@@ -822,10 +821,72 @@ linenos_offset
tab-width
Size of the tabs (default 4)
+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
+ /galleries/demo/tesla4_lg.jpg
+ /galleries/demo/tesla_lightning1_lg.jpg
+ /galleries/demo/tesla_tower1_lg.jpg
+
+This is based on `slidejs <http://slidesjs.com/>`_ and it supports
+`the options described there <http://slidesjs.com/#options>`_ 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
+-----------------------------------------
+
+If you like Nikola, and want to start using it, but you have a Wordpress blog, Nikola
+supports importing it. Here's the steps to do it:
+
+1) Get a XML dump of your site [#]_
+2) nikola import_wordpress mysite.wordpress.2012-12-20.xml
+
+After some time, this will crate a ``new_site`` folder with all your data. It currently supports
+the following:
+
+* All your posts and pages
+* Keeps "draft" status
+* Your tags and categories
+* Imports your attachments and fixes links to point to the right places
+* Will try to add redirects that send the old post URLs to the new ones
+* Will give you a url_map so you know where each old post was
+
+ This is also useful for Disqus thread migration!
+
+* Will try to convert the content of your posts. This is *not* error free, because
+ wordpress uses some unholy mix of HTML and strange things. Currently we are treating it
+ as markdown, which does a reasonabe job of it.
+
+ You will find your old posts in ``new_site/posts/post-title.wp`` in case you need to fix
+ any of them.
+
+This feature is a work in progress, and the only way to improve it is to have it used for
+as many sites as possible and make it work better each time, so I am happy to get requests
+about it.
+
+.. [#] The dump needs to be in 1.2 format. You can check by reading it, it should say
+ ``xmlns:excerpt="http://wordpress.org/export/1.2/excerpt/"`` near the top of the
+ file. If it says ``1.1`` instead of ``1.2`` you will have to update your
+ wordpress before dumping.
+
+ Other versions may or may not work.
+
License
-------
-Nikola is released under the `GPL version 3 <http://www.gnu.org/licenses/gpl-3.0.html>`_ which
+Nikola is released under a `MIT license <https://github.com/ralsina/nikola/blob/master/LICENSE.txt>`_ which
is a free software license. Some components shipped along with Nikola, or required by it are
released under other licenses.
diff --git a/docs/theming.txt b/docs/theming.txt
index 93c7824..33884e9 100644
--- a/docs/theming.txt
+++ b/docs/theming.txt
@@ -8,6 +8,9 @@ Theming Nikola
.. contents::
+This document is a reference about themes. If you want a tutorial, please read
+`Creating a Theme <creating-a-theme.html>`_
+
The Structure
-------------
@@ -69,20 +72,15 @@ bundles
Templates should use either the bundle or the individual files based on the ``use_bundles``
variable, which in turn is set by the ``USE_BUNDLES`` option.
-Creating a New Theme
---------------------
-
-In your site's folder, create a ``themes`` folder. Choose a theme to start from, and
-create ``themes/yourthemename/parent`` as a file containing the parent theme's name.
-There, you just created a new theme. Of course it looks exactly like the other one,
-so let's customize it.
-
Templates
---------
In templates there is a number of files whose name ends in ``.tmpl``. Those are the
theme's page templates. They are done usig the `Mako <http://makotemplates.org>`_
-template language. If you want to do a theme, you should learn the Mako syntax first.
+or `Jinja2 <jinja.pocoo.org>`_ 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.
Mako has a nifty concept of template inheritance. That means that, a
template can inherit from another and only change small bits of the output. For example,
@@ -183,6 +181,13 @@ list.tmpl
* ``items``: a list of (text, link) elements.
+
+list_post.tmpl
+ Template used to display generic lists of links. Can use everything ``base.tmpl`` uses, plus:
+
+ * ``posts``: a list of Post objects.
+
+
You can add other templates for specific pages, which the user can the use in his ``post_pages``
option in ``dodo.py``. Also, keep in mind that your theme is yours, there is no reason why
you would need to maintain the inheritance as it is, or not require whatever data you want.
diff --git a/nikola/__init__.py b/nikola/__init__.py
index 3b6ad2a..94031c9 100644
--- a/nikola/__init__.py
+++ b/nikola/__init__.py
@@ -1 +1,5 @@
-from nikola import Nikola # NOQA
+from __future__ import absolute_import
+
+from .nikola import Nikola # NOQA
+from . import plugins
+
diff --git a/nikola/data/samplesite/conf.py.in b/nikola/conf.py.in
index 8794565..897a941 100755
--- a/nikola/data/samplesite/conf.py.in
+++ b/nikola/conf.py.in
@@ -1,10 +1,14 @@
# -*- coding: utf-8 -*-
-
+<%text>
+# -*- coding: utf-8 -*-
+from __future__ import unicode_literals
import os
+import time
########################################
# Configuration, please edit
########################################
+</%text>
# Data about this site
BLOG_AUTHOR = "${BLOG_AUTHOR}"
@@ -47,7 +51,7 @@ post_pages = ${POST_PAGES}
# Feel free to add or delete extensions to any list, but don't add any new
# compilers unless you write the interface for it yourself.
#
-# 'rest' is reStructuredTextq
+# 'rest' is reStructuredText
# 'markdown' is MarkDown
# 'html' assumes the file is html and just copies it
post_compilers = ${POST_COMPILERS}
@@ -120,7 +124,7 @@ SLUG_TAG_PATH = True
#
# If you don't need any of these, just set to []
-REDIRECTIONS = []
+REDIRECTIONS = ${REDIRECTIONS}
# Commands to execute to deploy. Can be anything, for example,
# you may use rsync:
@@ -135,6 +139,10 @@ DEPLOY_COMMANDS = []
OUTPUT_FOLDER = 'output'
+# where the "cache" of partial generated content should be located
+# default: 'cache'
+CACHE_FOLDER = 'cache'
+
# Filters to apply to the output.
# A directory where the keys are either: a file extensions, or
# a tuple of file extensions.
@@ -157,9 +165,9 @@ FILTERS = {
# ".jpg": ["jpegoptim --strip-all -m75 -v %s"],
}
-##############################################################################
+# #############################################################################
# Image Gallery Options
-##############################################################################
+# #############################################################################
# Galleries are folders in galleries/
# Final location of galleries will be output / GALLERY_PATH / gallery_name
@@ -168,9 +176,9 @@ THUMBNAIL_SIZE = 180
MAX_IMAGE_SIZE = 1280
USE_FILENAME_AS_TITLE = True
-##############################################################################
+# #############################################################################
# HTML fragments and diverse things that are used by the templates
-##############################################################################
+# #############################################################################
# Data about post-per-page indexes
INDEXES_TITLE = "" # If this is empty, the default is BLOG_TITLE
@@ -179,6 +187,9 @@ INDEXES_PAGES = "" # If this is empty, the default is 'old posts page %d' trans
# Name of the theme to use. Themes are located in themes/theme_name
THEME = 'site'
+# date format used to display post dates. (str used by datetime.datetime.strftime)
+DATE_FORMAT = '%Y-%m-%d %H:%M'
+
# Show only teasers in the index pages? Defaults to False.
# INDEX_TEASERS = False
@@ -192,7 +203,10 @@ style="border-width:0; margin-bottom:12px;"
src="http://i.creativecommons.org/l/by-nc-sa/2.5/ar/88x31.png"></a>"""
# A small copyright notice for the page footer (in HTML)
-CONTENT_FOOTER = u'Contents &copy; 2012 <a href="${BLOG_EMAIL}">${BLOG_AUTHOR}</a>'
+CONTENT_FOOTER = 'Contents &copy; {date} <a href="mailto:{email}">{author}</a> - Powered by <a href="http://nikola.ralsina.com.ar">Nikola</a>'
+CONTENT_FOOTER = CONTENT_FOOTER.format(email=BLOG_EMAIL,
+ author=BLOG_AUTHOR,
+ date=time.gmtime().tm_year)
# To enable comments via Disqus, you need to create a forum at
# http://disqus.com, and set DISQUS_FORUM to the short name you selected.
@@ -221,14 +235,14 @@ SEARCH_FORM = ""
# appears on the navigation bar
#SEARCH_FORM = """
#<!-- Custom search -->
-#<form method="get" id="search" action="http://duckduckgo.com/" """\
-#"""class="navbar-form pull-left">
+#<form method="get" id="search" action="http://duckduckgo.com/"
+# class="navbar-form pull-left">
#<input type="hidden" name="sites" value="%s"/>
#<input type="hidden" name="k8" value="#444444"/>
#<input type="hidden" name="k9" value="#D51920"/>
#<input type="hidden" name="kt" value="h"/>
-#<input type="text" name="q" maxlength="255" """\
-#"""placeholder="Search&hellip;" class="span2" style="margin-top: 4px;"/>
+#<input type="text" name="q" maxlength="255"
+# placeholder="Search&hellip;" class="span2" style="margin-top: 4px;"/>
#<input type="submit" value="DuckDuckGo Search" style="visibility: hidden;" />
#</form>
#<!-- End of custom search -->
@@ -247,6 +261,7 @@ GLOBAL_CONTEXT = {
'blog_title': BLOG_TITLE,
'blog_url': BLOG_URL,
'blog_desc': BLOG_DESCRIPTION,
+ 'date_format': DATE_FORMAT,
'translations': TRANSLATIONS,
'license': LICENSE,
'search_form': SEARCH_FORM,
diff --git a/nikola/console.py b/nikola/console.py
new file mode 100644
index 0000000..939b611
--- /dev/null
+++ b/nikola/console.py
@@ -0,0 +1,7 @@
+from __future__ import print_function, unicode_literals
+
+from nikola import Nikola
+import conf
+SITE = Nikola(**conf.__dict__)
+SITE.scan_posts()
+print("You can now access your configuration as conf and your site engine as SITE")
diff --git a/nikola/data/samplesite/README.txt b/nikola/data/samplesite/README.txt
index ca94b34..a1220d0 100644
--- a/nikola/data/samplesite/README.txt
+++ b/nikola/data/samplesite/README.txt
@@ -1,31 +1,20 @@
-How To make This Work
----------------------
+This folder contains the source used to generate a static site by nikola.
-The full manual is in stories/manual.txt, but here is the very short version:
+Installation and documentation at http://nikola.ralsina.com.ar
-1. Install docutils (http://docutils.sourceforge.net)
-2. Install Mako (http://makotemplates.org)
-3. Install doit (http://python-doit.sourceforge.net)
-4. Install PIL (http://www.pythonware.com/products/pil/)
-5. Install Pygments (http://pygments.org/)
-6. Install unidecode (http://pypi.python.org/pypi/Unidecode/)
-7. Install lxml (http://lxml.de/)
+Configuration file for the site is `conf.py`.
-To build or update the demo site run this command in the nikola's folder::
+To build the site::
- doit
+ nikola build
To see it::
- doit serve -p 8000
+ nikola serve
And point your browser to http://localhost:8000
-Notes on Requirements
----------------------
-If you don't have PIL, then image galleries will be inefficient because Nikola
-will not generate thumbnails. Alternatively, you may install pillow instead of
-PIL.
+To check all available commands::
-If you don't have pygments, the code-block directive will not highlight syntax.
+ nikola help
diff --git a/nikola/data/samplesite/conf.py b/nikola/data/samplesite/conf.py
deleted file mode 100755
index 552eb68..0000000
--- a/nikola/data/samplesite/conf.py
+++ /dev/null
@@ -1,274 +0,0 @@
-# -*- coding: utf-8 -*-
-
-import os
-
-########################################
-# Configuration, please edit
-########################################
-
-# Data about this site
-BLOG_AUTHOR = "Your Name"
-BLOG_TITLE = "Demo Site"
-BLOG_URL = "http://nikola.ralsina.com.ar"
-BLOG_EMAIL = "joe@demo.site"
-BLOG_DESCRIPTION = "This is a demo site for Nikola."
-
-# post_pages contains (wildcard, destination, template, use_in_feed) tuples.
-#
-# The wildcard is used to generate a list of reSt source files
-# (whatever/thing.txt).
-# That fragment must have an associated metadata file (whatever/thing.meta),
-# and opcionally translated files (example for spanish, with code "es"):
-# whatever/thing.txt.es and whatever/thing.meta.es
-#
-# From those files, a set of HTML fragment files will be generated:
-# cache/whatever/thing.html (and maybe cache/whatever/thing.html.es)
-#
-# These files are combinated with the template to produce rendered
-# pages, which will be placed at
-# output / TRANSLATIONS[lang] / destination / pagename.html
-#
-# where "pagename" is specified in the metadata file.
-#
-# if use_in_feed is True, then those posts will be added to the site's
-# rss feeds.
-#
-
-post_pages = (
- ("posts/*.txt", "posts", "post.tmpl", True),
- ("stories/*.txt", "stories", "story.tmpl", False),
-)
-
-# One or more folders containing files to be copied as-is into the output.
-# The format is a dictionary of "source" "relative destination".
-# Default is:
-# FILES_FOLDERS = {'files': '' }
-# Which means copy 'files' into 'output'
-
-# A mapping of languages to file-extensions that represent that language.
-# Feel free to add or delete extensions to any list, but don't add any new
-# compilers unless you write the interface for it yourself.
-#
-# 'rest' is reStructuredText
-# 'markdown' is MarkDown
-# 'html' assumes the file is html and just copies it
-post_compilers = {
- "rest": ('.txt', '.rst'),
- "markdown": ('.md', '.mdown', '.markdown'),
- "html": ('.html', '.htm')
- }
-
-# Nikola is multilingual!
-#
-# Currently supported languages are:
-# English -> en
-# Greek -> gr
-# German -> de
-# French -> fr
-# Russian -> ru
-# Spanish -> es
-# Italian -> it
-#
-# If you want to use Nikola with a non-supported language you have to provide
-# a module containing the necessary translations
-# (p.e. look at the modules at: ./nikola/data/themes/default/messages/fr.py).
-# If a specific post is not translated to a language, then the version
-# in the default language will be shown instead.
-
-# What is the default language?
-DEFAULT_LANG = "en"
-
-# What other languages do you have?
-# The format is {"translationcode" : "path/to/translation" }
-# the path will be used as a prefix for the generated pages location
-TRANSLATIONS = {
- DEFAULT_LANG: "",
- #"gr": "./gr",
- #"de": "./de",
- #"fr": "./fr",
- #"ru": "./ru",
- #"es": "./es",
- }
-
-# Paths for different autogenerated bits. These are combined with the
-# translation paths.
-
-# Final locations are:
-# output / TRANSLATION[lang] / TAG_PATH / index.html (list of tags)
-# output / TRANSLATION[lang] / TAG_PATH / tag.html (list of posts for a tag)
-# output / TRANSLATION[lang] / TAG_PATH / tag.xml (RSS feed for a tag)
-TAG_PATH = "categories"
-
-# If TAG_PAGES_ARE_INDEXES is set to True, each tag's page will contain
-# the posts themselves. If set to False, it will be just a list of links.
-TAG_PAGES_ARE_INDEXES = True
-
-# Final location is output / TRANSLATION[lang] / INDEX_PATH / index-*.html
-INDEX_PATH = ""
-# Final locations for the archives are:
-# output / TRANSLATION[lang] / ARCHIVE_PATH / ARCHIVE_FILENAME
-# output / TRANSLATION[lang] / ARCHIVE_PATH / YEAR / index.html
-ARCHIVE_PATH = ""
-ARCHIVE_FILENAME = "archive.html"
-# Final locations are:
-# output / TRANSLATION[lang] / RSS_PATH / rss.xml
-RSS_PATH = ""
-
-# Slug the Tag URL easier for users to type, special characters are
-# often removed or replaced as well.
-SLUG_TAG_PATH = True
-
-# A list of redirection tuples, [("foo/from.html", "/bar/to.html")].
-#
-# A HTML file will be created in output/foo/from.html that redirects
-# to the "/bar/to.html" URL. notice that the "from" side MUST be a
-# relative URL.
-#
-# If you don't need any of these, just set to []
-
-REDIRECTIONS = []
-
-# Commands to execute to deploy. Can be anything, for example,
-# you may use rsync:
-# "rsync -rav output/* joe@my.site:/srv/www/site"
-# And then do a backup, or ping pingomatic.
-# To do manual deployment, set it to []
-DEPLOY_COMMANDS = []
-
-# Where the output site should be located
-# If you don't use an absolute path, it will be considered as relative
-# to the location of conf.py
-
-OUTPUT_FOLDER = 'output'
-
-# Filters to apply to the output.
-# A directory where the keys are either: a file extensions, or
-# a tuple of file extensions.
-#
-# And the value is a list of commands to be applied in order.
-#
-# Each command must be either:
-#
-# A string containing a '%s' which will
-# be replaced with a filename. The command *must* produce output
-# in place.
-#
-# Or:
-#
-# A python callable, which will be called with the filename as
-# argument.
-#
-# By default, there are no filters.
-FILTERS = {
-# ".jpg": ["jpegoptim --strip-all -m75 -v %s"],
-}
-
-##############################################################################
-# Image Gallery Options
-##############################################################################
-
-# Galleries are folders in galleries/
-# Final location of galleries will be output / GALLERY_PATH / gallery_name
-GALLERY_PATH = "galleries"
-THUMBNAIL_SIZE = 180
-MAX_IMAGE_SIZE = 1280
-USE_FILENAME_AS_TITLE = True
-
-##############################################################################
-# HTML fragments and diverse things that are used by the templates
-##############################################################################
-
-# Data about post-per-page indexes
-INDEXES_TITLE = "" # If this is empty, the default is BLOG_TITLE
-INDEXES_PAGES = "" # If this is empty, the default is 'old posts page %d' translated
-
-# Name of the theme to use. Themes are located in themes/theme_name
-THEME = 'site'
-
-# Show only teasers in the index pages? Defaults to False.
-# INDEX_TEASERS = False
-
-# A HTML fragment describing the license, for the sidebar.
-# I recomment using the Creative Commons' wizard:
-# http://creativecommons.org/choose/
-LICENSE = """
-<a rel="license" href="http://creativecommons.org/licenses/by-nc-sa/2.5/ar/">
-<img alt="Creative Commons License BY-NC-SA"
-style="border-width:0; margin-bottom:12px;"
-src="http://i.creativecommons.org/l/by-nc-sa/2.5/ar/88x31.png"></a>"""
-
-# A small copyright notice for the page footer (in HTML)
-CONTENT_FOOTER = u'Contents &copy; 2012 Example Joe'
-
-# To enable comments via Disqus, you need to create a forum at
-# http://disqus.com, and set DISQUS_FORUM to the short name you selected.
-# If you want to disable comments, set it to False.
-DISQUS_FORUM = "nikolademo"
-
-# Enable Addthis social buttons?
-# Defaults to true
-# ADD_THIS_BUTTONS = True
-
-# Modify the number of Post per Index Page
-# Defaults to 10
-# INDEX_DISPLAY_POST_COUNT = 10
-
-# RSS_LINK is a HTML fragment to link the RSS or Atom feeds. If set to None,
-# the base.tmpl will use the feed Nikola generates. However, you may want to
-# change it for a feedburner feed or something else.
-RSS_LINK = None
-
-# A search form to search this site, for the sidebar. You can use a google
-# custom search (http://www.google.com/cse/)
-# Or a duckduckgo search: https://duckduckgo.com/search_box.html
-# This example should work for pretty much any site we generate.
-SEARCH_FORM = ""
-# This search form is better for the "site" theme where it
-# appears on the navigation bar
-#SEARCH_FORM = """
-#<!-- Custom search -->
-#<form method="get" id="search" action="http://duckduckgo.com/" """\
-#"""class="navbar-form pull-left">
-#<input type="hidden" name="sites" value="%s"/>
-#<input type="hidden" name="k8" value="#444444"/>
-#<input type="hidden" name="k9" value="#D51920"/>
-#<input type="hidden" name="kt" value="h"/>
-#<input type="text" name="q" maxlength="255" """\
-#"""placeholder="Search&hellip;" class="span2" style="margin-top: 4px;"/>
-#<input type="submit" value="DuckDuckGo Search" style="visibility: hidden;" />
-#</form>
-#<!-- End of custom search -->
-#""" % BLOG_URL
-
-# Google analytics or whatever else you use. Added to the bottom of <body>
-# in the default template (base.tmpl).
-ANALYTICS = """
- """
-
-# Put in global_context things you want available on all your templates.
-# It can be anything, data, functions, modules, etc.
-GLOBAL_CONTEXT = {
- 'analytics': ANALYTICS,
- 'blog_author': BLOG_AUTHOR,
- 'blog_title': BLOG_TITLE,
- 'blog_url': BLOG_URL,
- 'blog_desc': BLOG_DESCRIPTION,
- 'translations': TRANSLATIONS,
- 'license': LICENSE,
- 'search_form': SEARCH_FORM,
- 'disqus_forum': DISQUS_FORUM,
- 'content_footer': CONTENT_FOOTER,
- 'rss_path': RSS_PATH,
- 'rss_link': RSS_LINK,
- # Locale-dependent links for the sidebar
- # You should provide a key-value pair for each used language.
- 'sidebar_links': {
- DEFAULT_LANG: (
- ('/' + os.path.join(ARCHIVE_PATH, ARCHIVE_FILENAME), 'Archives'),
- ('/categories/index.html', 'Tags'),
- ('/stories/about-nikola.html', 'About Nikola'),
- ('/stories/handbook.html', 'The Nikola Handbook'),
- ('http://nikola.ralsina.com.ar', 'Powered by Nikola!'),
- ),
- }
- }
diff --git a/nikola/data/samplesite/posts/1.txt b/nikola/data/samplesite/posts/1.txt
index 4e583db..5741e05 100644
--- a/nikola/data/samplesite/posts/1.txt
+++ b/nikola/data/samplesite/posts/1.txt
@@ -9,5 +9,7 @@ and build a site using it. Congratulations!
* You can read the manual `here </stories/handbook.html>`__
* You can learn more about Nikola at http://nikola.ralsina.com.ar
* You can see a demo photo gallery `here </galleries/demo/>`__
+* Demo usage of listings `here </stories/listings-demo.html>`__
+* Demo of slideshows `here </stories/slides-demo.html>`__
Send feedback to ralsina@netmanagers.com.ar!
diff --git a/nikola/data/samplesite/stories/listings-demo.txt b/nikola/data/samplesite/stories/listings-demo.txt
new file mode 100644
index 0000000..7875f17
--- /dev/null
+++ b/nikola/data/samplesite/stories/listings-demo.txt
@@ -0,0 +1,10 @@
+.. title: Listings Demo
+.. slug: listings-demo
+.. date: 2012/12/15 10:16:20
+.. tags:
+.. link:
+.. description:
+
+Nikola intends to let you show code easily via listings:
+
+.. listing:: hello.py python
diff --git a/nikola/data/samplesite/stories/slides-demo.txt b/nikola/data/samplesite/stories/slides-demo.txt
new file mode 100644
index 0000000..fb1356b
--- /dev/null
+++ b/nikola/data/samplesite/stories/slides-demo.txt
@@ -0,0 +1,17 @@
+.. title: Slides Demo
+.. slug: slides-demo
+.. date: 2012/12/27 10:16:20
+.. tags:
+.. link:
+.. description:
+
+Nikola intends to let you do slideshows easily:
+
+.. slides::
+
+ /galleries/demo/tesla_conducts_lg.jpg
+ /galleries/demo/tesla_lightning2_lg.jpg
+ /galleries/demo/tesla4_lg.jpg
+ /galleries/demo/tesla_lightning1_lg.jpg
+ /galleries/demo/tesla_tower1_lg.jpg
+
diff --git a/nikola/data/themes/default/assets/css/bootstrap-responsive.css b/nikola/data/themes/default/assets/css/bootstrap-responsive.css
index daafa91..a3352d7 100644
--- a/nikola/data/themes/default/assets/css/bootstrap-responsive.css
+++ b/nikola/data/themes/default/assets/css/bootstrap-responsive.css
@@ -1,5 +1,5 @@
/*!
- * Bootstrap Responsive v2.1.0
+ * Bootstrap Responsive v2.2.2
*
* Copyright 2012 Twitter, Inc
* Licensed under the Apache License v2.0
@@ -8,6 +8,10 @@
* Designed and built with all the love in the world @twitter by @mdo and @fat.
*/
+@-ms-viewport {
+ width: device-width;
+}
+
.clearfix {
*zoom: 1;
}
@@ -107,6 +111,7 @@
}
[class*="span"] {
float: left;
+ min-height: 1px;
margin-left: 30px;
}
.container,
@@ -214,6 +219,9 @@
.row-fluid [class*="span"]:first-child {
margin-left: 0;
}
+ .row-fluid .controls-row [class*="span"] + [class*="span"] {
+ margin-left: 2.564102564102564%;
+ }
.row-fluid .span12 {
width: 100%;
*width: 99.94680851063829%;
@@ -453,6 +461,7 @@
}
[class*="span"] {
float: left;
+ min-height: 1px;
margin-left: 20px;
}
.container,
@@ -560,6 +569,9 @@
.row-fluid [class*="span"]:first-child {
margin-left: 0;
}
+ .row-fluid .controls-row [class*="span"] + [class*="span"] {
+ margin-left: 2.7624309392265194%;
+ }
.row-fluid .span12 {
width: 100%;
*width: 99.94680851063829%;
@@ -780,7 +792,8 @@
padding-left: 20px;
}
.navbar-fixed-top,
- .navbar-fixed-bottom {
+ .navbar-fixed-bottom,
+ .navbar-static-top {
margin-right: -20px;
margin-left: -20px;
}
@@ -811,11 +824,15 @@
margin-left: 0;
}
[class*="span"],
+ .uneditable-input[class*="span"],
.row-fluid [class*="span"] {
display: block;
float: none;
- width: auto;
+ width: 100%;
margin-left: 0;
+ -webkit-box-sizing: border-box;
+ -moz-box-sizing: border-box;
+ box-sizing: border-box;
}
.span12,
.row-fluid .span12 {
@@ -824,6 +841,9 @@
-moz-box-sizing: border-box;
box-sizing: border-box;
}
+ .row-fluid [class*="offset"]:first-child {
+ margin-left: 0;
+ }
.input-large,
.input-xlarge,
.input-xxlarge,
@@ -845,6 +865,9 @@
display: inline-block;
width: auto;
}
+ .controls-row [class*="span"] + [class*="span"] {
+ margin-left: 0;
+ }
.modal {
position: fixed;
top: 20px;
@@ -853,8 +876,11 @@
width: auto;
margin: 0;
}
+ .modal.fade {
+ top: -100px;
+ }
.modal.fade.in {
- top: auto;
+ top: 20px;
}
}
@@ -870,7 +896,7 @@
input[type="radio"] {
border: 1px solid #ccc;
}
- .form-horizontal .control-group > label {
+ .form-horizontal .control-label {
float: none;
width: auto;
padding-top: 0;
@@ -886,6 +912,16 @@
padding-right: 10px;
padding-left: 10px;
}
+ .media .pull-left,
+ .media .pull-right {
+ display: block;
+ float: none;
+ margin-bottom: 10px;
+ }
+ .media-object {
+ margin-right: 0;
+ margin-left: 0;
+ }
.modal {
top: 10px;
right: 10px;
@@ -944,14 +980,14 @@
display: none;
}
.nav-collapse .nav .nav-header {
- color: #555555;
+ color: #777777;
text-shadow: none;
}
.nav-collapse .nav > li > a,
.nav-collapse .dropdown-menu a {
padding: 9px 15px;
font-weight: bold;
- color: #555555;
+ color: #777777;
-webkit-border-radius: 3px;
-moz-border-radius: 3px;
border-radius: 3px;
@@ -970,6 +1006,10 @@
.nav-collapse .dropdown-menu a:hover {
background-color: #f2f2f2;
}
+ .navbar-inverse .nav-collapse .nav > li > a,
+ .navbar-inverse .nav-collapse .dropdown-menu a {
+ color: #999999;
+ }
.navbar-inverse .nav-collapse .nav > li > a:hover,
.navbar-inverse .nav-collapse .dropdown-menu a:hover {
background-color: #111111;
@@ -982,7 +1022,7 @@
position: static;
top: auto;
left: auto;
- display: block;
+ display: none;
float: none;
max-width: none;
padding: 0;
@@ -996,6 +1036,9 @@
-moz-box-shadow: none;
box-shadow: none;
}
+ .nav-collapse .open > .dropdown-menu {
+ display: block;
+ }
.nav-collapse .dropdown-menu:before,
.nav-collapse .dropdown-menu:after {
display: none;
@@ -1003,6 +1046,10 @@
.nav-collapse .dropdown-menu .divider {
display: none;
}
+ .nav-collapse .nav > li > .dropdown-menu:before,
+ .nav-collapse .nav > li > .dropdown-menu:after {
+ display: none;
+ }
.nav-collapse .navbar-form,
.nav-collapse .navbar-search {
float: none;
@@ -1014,6 +1061,11 @@
-moz-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1), 0 1px 0 rgba(255, 255, 255, 0.1);
box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1), 0 1px 0 rgba(255, 255, 255, 0.1);
}
+ .navbar-inverse .nav-collapse .navbar-form,
+ .navbar-inverse .nav-collapse .navbar-search {
+ border-top-color: #111111;
+ border-bottom-color: #111111;
+ }
.navbar .nav-collapse .nav.pull-right {
float: none;
margin-left: 0;
diff --git a/nikola/data/themes/default/assets/css/bootstrap.css b/nikola/data/themes/default/assets/css/bootstrap.css
index 0664207..8ab3cef 100644
--- a/nikola/data/themes/default/assets/css/bootstrap.css
+++ b/nikola/data/themes/default/assets/css/bootstrap.css
@@ -1,5 +1,5 @@
/*!
- * Bootstrap v2.1.0
+ * Bootstrap v2.2.2
*
* Copyright 2012 Twitter, Inc
* Licensed under the Apache License v2.0
@@ -67,6 +67,7 @@ sub {
}
img {
+ width: auto\9;
height: auto;
max-width: 100%;
vertical-align: middle;
@@ -74,7 +75,8 @@ img {
-ms-interpolation-mode: bicubic;
}
-#map_canvas img {
+#map_canvas img,
+.google-maps img {
max-width: none;
}
@@ -100,13 +102,24 @@ input::-moz-focus-inner {
}
button,
-input[type="button"],
+html input[type="button"],
input[type="reset"],
input[type="submit"] {
cursor: pointer;
-webkit-appearance: button;
}
+label,
+select,
+button,
+input[type="button"],
+input[type="reset"],
+input[type="submit"],
+input[type="radio"],
+input[type="checkbox"] {
+ cursor: pointer;
+}
+
input[type="search"] {
-webkit-box-sizing: content-box;
-moz-box-sizing: content-box;
@@ -124,6 +137,58 @@ textarea {
vertical-align: top;
}
+@media print {
+ * {
+ color: #000 !important;
+ text-shadow: none !important;
+ background: transparent !important;
+ box-shadow: none !important;
+ }
+ a,
+ a:visited {
+ text-decoration: underline;
+ }
+ a[href]:after {
+ content: " (" attr(href) ")";
+ }
+ abbr[title]:after {
+ content: " (" attr(title) ")";
+ }
+ .ir a:after,
+ a[href^="javascript:"]:after,
+ a[href^="#"]:after {
+ content: "";
+ }
+ pre,
+ blockquote {
+ border: 1px solid #999;
+ page-break-inside: avoid;
+ }
+ thead {
+ display: table-header-group;
+ }
+ tr,
+ img {
+ page-break-inside: avoid;
+ }
+ img {
+ max-width: 100% !important;
+ }
+ @page {
+ margin: 0.5cm;
+ }
+ p,
+ h2,
+ h3 {
+ orphans: 3;
+ widows: 3;
+ }
+ h2,
+ h3 {
+ page-break-after: avoid;
+ }
+}
+
.clearfix {
*zoom: 1;
}
@@ -215,6 +280,7 @@ a:hover {
[class*="span"] {
float: left;
+ min-height: 1px;
margin-left: 20px;
}
@@ -353,6 +419,10 @@ a:hover {
margin-left: 0;
}
+.row-fluid .controls-row [class*="span"] + [class*="span"] {
+ margin-left: 2.127659574468085%;
+}
+
.row-fluid .span12 {
width: 100%;
*width: 99.94680851063829%;
@@ -583,7 +653,7 @@ p {
.lead {
margin-bottom: 20px;
- font-size: 20px;
+ font-size: 21px;
font-weight: 200;
line-height: 30px;
}
@@ -608,6 +678,42 @@ cite {
color: #999999;
}
+a.muted:hover {
+ color: #808080;
+}
+
+.text-warning {
+ color: #c09853;
+}
+
+a.text-warning:hover {
+ color: #a47e3c;
+}
+
+.text-error {
+ color: #b94a48;
+}
+
+a.text-error:hover {
+ color: #953b39;
+}
+
+.text-info {
+ color: #3a87ad;
+}
+
+a.text-info:hover {
+ color: #2d6987;
+}
+
+.text-success {
+ color: #468847;
+}
+
+a.text-success:hover {
+ color: #356635;
+}
+
h1,
h2,
h3,
@@ -617,7 +723,7 @@ h6 {
margin: 10px 0;
font-family: inherit;
font-weight: bold;
- line-height: 1;
+ line-height: 20px;
color: inherit;
text-rendering: optimizelegibility;
}
@@ -633,42 +739,42 @@ h6 small {
color: #999999;
}
-h1 {
- font-size: 36px;
+h1,
+h2,
+h3 {
line-height: 40px;
}
+h1 {
+ font-size: 38.5px;
+}
+
h2 {
- font-size: 30px;
- line-height: 40px;
+ font-size: 31.5px;
}
h3 {
- font-size: 24px;
- line-height: 40px;
+ font-size: 24.5px;
}
h4 {
- font-size: 18px;
- line-height: 20px;
+ font-size: 17.5px;
}
h5 {
font-size: 14px;
- line-height: 20px;
}
h6 {
- font-size: 12px;
- line-height: 20px;
+ font-size: 11.9px;
}
h1 small {
- font-size: 24px;
+ font-size: 24.5px;
}
h2 small {
- font-size: 18px;
+ font-size: 17.5px;
}
h3 small {
@@ -708,6 +814,19 @@ ol.unstyled {
list-style: none;
}
+ul.inline,
+ol.inline {
+ margin-left: 0;
+ list-style: none;
+}
+
+ul.inline > li,
+ol.inline > li {
+ display: inline-block;
+ padding-right: 5px;
+ padding-left: 5px;
+}
+
dl {
margin-bottom: 20px;
}
@@ -725,9 +844,24 @@ dd {
margin-left: 10px;
}
+.dl-horizontal {
+ *zoom: 1;
+}
+
+.dl-horizontal:before,
+.dl-horizontal:after {
+ display: table;
+ line-height: 0;
+ content: "";
+}
+
+.dl-horizontal:after {
+ clear: both;
+}
+
.dl-horizontal dt {
float: left;
- width: 120px;
+ width: 160px;
overflow: hidden;
clear: left;
text-align: right;
@@ -736,7 +870,7 @@ dd {
}
.dl-horizontal dd {
- margin-left: 130px;
+ margin-left: 180px;
}
hr {
@@ -746,7 +880,8 @@ hr {
border-bottom: 1px solid #ffffff;
}
-abbr[title] {
+abbr[title],
+abbr[data-original-title] {
cursor: help;
border-bottom: 1px dotted #999999;
}
@@ -828,6 +963,7 @@ pre {
code {
padding: 2px 4px;
color: #d14;
+ white-space: nowrap;
background-color: #f7f7f9;
border: 1px solid #e1e1e8;
}
@@ -857,6 +993,8 @@ pre.prettyprint {
pre code {
padding: 0;
color: inherit;
+ white-space: pre;
+ white-space: pre-wrap;
background-color: transparent;
border: 0;
}
@@ -935,18 +1073,20 @@ input[type="color"],
display: inline-block;
height: 20px;
padding: 4px 6px;
- margin-bottom: 9px;
+ margin-bottom: 10px;
font-size: 14px;
line-height: 20px;
color: #555555;
- -webkit-border-radius: 3px;
- -moz-border-radius: 3px;
- border-radius: 3px;
+ vertical-align: middle;
+ -webkit-border-radius: 4px;
+ -moz-border-radius: 4px;
+ border-radius: 4px;
}
input,
-textarea {
- width: 210px;
+textarea,
+.uneditable-input {
+ width: 206px;
}
textarea {
@@ -1012,7 +1152,6 @@ input[type="checkbox"] {
margin-top: 1px \9;
*margin-top: 0;
line-height: normal;
- cursor: pointer;
}
input[type="file"],
@@ -1039,7 +1178,7 @@ input[type="file"] {
select {
width: 220px;
background-color: #ffffff;
- border: 1px solid #bbb;
+ border: 1px solid #cccccc;
}
select[multiple],
@@ -1094,14 +1233,14 @@ textarea::-webkit-input-placeholder {
.radio,
.checkbox {
- min-height: 18px;
- padding-left: 18px;
+ min-height: 20px;
+ padding-left: 20px;
}
.radio input[type="radio"],
.checkbox input[type="checkbox"] {
float: left;
- margin-left: -18px;
+ margin-left: -20px;
}
.controls > .radio:first-child,
@@ -1268,10 +1407,16 @@ textarea.span1,
clear: both;
}
-.controls-row [class*="span"] {
+.controls-row [class*="span"],
+.row-fluid .controls-row [class*="span"] {
float: left;
}
+.controls-row .checkbox[class*="span"],
+.controls-row .radio[class*="span"] {
+ padding-top: 5px;
+}
+
input[disabled],
select[disabled],
textarea[disabled],
@@ -1289,7 +1434,7 @@ input[type="checkbox"][readonly] {
background-color: transparent;
}
-.control-group.warning > label,
+.control-group.warning .control-label,
.control-group.warning .help-block,
.control-group.warning .help-inline {
color: #c09853;
@@ -1301,14 +1446,17 @@ input[type="checkbox"][readonly] {
.control-group.warning select,
.control-group.warning textarea {
color: #c09853;
+}
+
+.control-group.warning input,
+.control-group.warning select,
+.control-group.warning textarea {
border-color: #c09853;
-webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
-moz-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
}
-.control-group.warning .checkbox:focus,
-.control-group.warning .radio:focus,
.control-group.warning input:focus,
.control-group.warning select:focus,
.control-group.warning textarea:focus {
@@ -1325,7 +1473,7 @@ input[type="checkbox"][readonly] {
border-color: #c09853;
}
-.control-group.error > label,
+.control-group.error .control-label,
.control-group.error .help-block,
.control-group.error .help-inline {
color: #b94a48;
@@ -1337,14 +1485,17 @@ input[type="checkbox"][readonly] {
.control-group.error select,
.control-group.error textarea {
color: #b94a48;
+}
+
+.control-group.error input,
+.control-group.error select,
+.control-group.error textarea {
border-color: #b94a48;
-webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
-moz-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
}
-.control-group.error .checkbox:focus,
-.control-group.error .radio:focus,
.control-group.error input:focus,
.control-group.error select:focus,
.control-group.error textarea:focus {
@@ -1361,7 +1512,7 @@ input[type="checkbox"][readonly] {
border-color: #b94a48;
}
-.control-group.success > label,
+.control-group.success .control-label,
.control-group.success .help-block,
.control-group.success .help-inline {
color: #468847;
@@ -1373,14 +1524,17 @@ input[type="checkbox"][readonly] {
.control-group.success select,
.control-group.success textarea {
color: #468847;
+}
+
+.control-group.success input,
+.control-group.success select,
+.control-group.success textarea {
border-color: #468847;
-webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
-moz-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
}
-.control-group.success .checkbox:focus,
-.control-group.success .radio:focus,
.control-group.success input:focus,
.control-group.success select:focus,
.control-group.success textarea:focus {
@@ -1397,16 +1551,55 @@ input[type="checkbox"][readonly] {
border-color: #468847;
}
-input:focus:required:invalid,
-textarea:focus:required:invalid,
-select:focus:required:invalid {
+.control-group.info .control-label,
+.control-group.info .help-block,
+.control-group.info .help-inline {
+ color: #3a87ad;
+}
+
+.control-group.info .checkbox,
+.control-group.info .radio,
+.control-group.info input,
+.control-group.info select,
+.control-group.info textarea {
+ color: #3a87ad;
+}
+
+.control-group.info input,
+.control-group.info select,
+.control-group.info textarea {
+ border-color: #3a87ad;
+ -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
+ -moz-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
+ box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
+}
+
+.control-group.info input:focus,
+.control-group.info select:focus,
+.control-group.info textarea:focus {
+ border-color: #2d6987;
+ -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #7ab5d3;
+ -moz-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #7ab5d3;
+ box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #7ab5d3;
+}
+
+.control-group.info .input-prepend .add-on,
+.control-group.info .input-append .add-on {
+ color: #3a87ad;
+ background-color: #d9edf7;
+ border-color: #3a87ad;
+}
+
+input:focus:invalid,
+textarea:focus:invalid,
+select:focus:invalid {
color: #b94a48;
border-color: #ee5f5b;
}
-input:focus:required:invalid:focus,
-textarea:focus:required:invalid:focus,
-select:focus:required:invalid:focus {
+input:focus:invalid:focus,
+textarea:focus:invalid:focus,
+select:focus:invalid:focus {
border-color: #e9322d;
-webkit-box-shadow: 0 0 6px #f8b9b7;
-moz-box-shadow: 0 0 6px #f8b9b7;
@@ -1463,15 +1656,25 @@ select:focus:required:invalid:focus {
.input-append select,
.input-prepend select,
.input-append .uneditable-input,
+.input-prepend .uneditable-input,
+.input-append .dropdown-menu,
+.input-prepend .dropdown-menu {
+ font-size: 14px;
+}
+
+.input-append input,
+.input-prepend input,
+.input-append select,
+.input-prepend select,
+.input-append .uneditable-input,
.input-prepend .uneditable-input {
position: relative;
margin-bottom: 0;
*margin-left: 0;
- font-size: 14px;
vertical-align: top;
- -webkit-border-radius: 0 3px 3px 0;
- -moz-border-radius: 0 3px 3px 0;
- border-radius: 0 3px 3px 0;
+ -webkit-border-radius: 0 4px 4px 0;
+ -moz-border-radius: 0 4px 4px 0;
+ border-radius: 0 4px 4px 0;
}
.input-append input:focus,
@@ -1502,8 +1705,9 @@ select:focus:required:invalid:focus {
.input-append .add-on,
.input-prepend .add-on,
.input-append .btn,
-.input-prepend .btn {
- margin-left: -1px;
+.input-prepend .btn,
+.input-append .btn-group > .dropdown-toggle,
+.input-prepend .btn-group > .dropdown-toggle {
vertical-align: top;
-webkit-border-radius: 0;
-moz-border-radius: 0;
@@ -1523,24 +1727,39 @@ select:focus:required:invalid:focus {
.input-prepend .add-on:first-child,
.input-prepend .btn:first-child {
- -webkit-border-radius: 3px 0 0 3px;
- -moz-border-radius: 3px 0 0 3px;
- border-radius: 3px 0 0 3px;
+ -webkit-border-radius: 4px 0 0 4px;
+ -moz-border-radius: 4px 0 0 4px;
+ border-radius: 4px 0 0 4px;
}
.input-append input,
.input-append select,
.input-append .uneditable-input {
- -webkit-border-radius: 3px 0 0 3px;
- -moz-border-radius: 3px 0 0 3px;
- border-radius: 3px 0 0 3px;
+ -webkit-border-radius: 4px 0 0 4px;
+ -moz-border-radius: 4px 0 0 4px;
+ border-radius: 4px 0 0 4px;
+}
+
+.input-append input + .btn-group .btn:last-child,
+.input-append select + .btn-group .btn:last-child,
+.input-append .uneditable-input + .btn-group .btn:last-child {
+ -webkit-border-radius: 0 4px 4px 0;
+ -moz-border-radius: 0 4px 4px 0;
+ border-radius: 0 4px 4px 0;
+}
+
+.input-append .add-on,
+.input-append .btn,
+.input-append .btn-group {
+ margin-left: -1px;
}
.input-append .add-on:last-child,
-.input-append .btn:last-child {
- -webkit-border-radius: 0 3px 3px 0;
- -moz-border-radius: 0 3px 3px 0;
- border-radius: 0 3px 3px 0;
+.input-append .btn:last-child,
+.input-append .btn-group:last-child > .dropdown-toggle {
+ -webkit-border-radius: 0 4px 4px 0;
+ -moz-border-radius: 0 4px 4px 0;
+ border-radius: 0 4px 4px 0;
}
.input-prepend.input-append input,
@@ -1551,20 +1770,32 @@ select:focus:required:invalid:focus {
border-radius: 0;
}
+.input-prepend.input-append input + .btn-group .btn,
+.input-prepend.input-append select + .btn-group .btn,
+.input-prepend.input-append .uneditable-input + .btn-group .btn {
+ -webkit-border-radius: 0 4px 4px 0;
+ -moz-border-radius: 0 4px 4px 0;
+ border-radius: 0 4px 4px 0;
+}
+
.input-prepend.input-append .add-on:first-child,
.input-prepend.input-append .btn:first-child {
margin-right: -1px;
- -webkit-border-radius: 3px 0 0 3px;
- -moz-border-radius: 3px 0 0 3px;
- border-radius: 3px 0 0 3px;
+ -webkit-border-radius: 4px 0 0 4px;
+ -moz-border-radius: 4px 0 0 4px;
+ border-radius: 4px 0 0 4px;
}
.input-prepend.input-append .add-on:last-child,
.input-prepend.input-append .btn:last-child {
margin-left: -1px;
- -webkit-border-radius: 0 3px 3px 0;
- -moz-border-radius: 0 3px 3px 0;
- border-radius: 0 3px 3px 0;
+ -webkit-border-radius: 0 4px 4px 0;
+ -moz-border-radius: 0 4px 4px 0;
+ border-radius: 0 4px 4px 0;
+}
+
+.input-prepend.input-append .btn-group:first-child {
+ margin-left: 0;
}
input.search-query {
@@ -1706,7 +1937,7 @@ legend + .control-group {
.form-horizontal .control-label {
float: left;
- width: 140px;
+ width: 160px;
padding-top: 5px;
text-align: right;
}
@@ -1714,21 +1945,29 @@ legend + .control-group {
.form-horizontal .controls {
*display: inline-block;
*padding-left: 20px;
- margin-left: 160px;
+ margin-left: 180px;
*margin-left: 0;
}
.form-horizontal .controls:first-child {
- *padding-left: 160px;
+ *padding-left: 180px;
}
.form-horizontal .help-block {
- margin-top: 10px;
margin-bottom: 0;
}
+.form-horizontal input + .help-block,
+.form-horizontal select + .help-block,
+.form-horizontal textarea + .help-block,
+.form-horizontal .uneditable-input + .help-block,
+.form-horizontal .input-prepend + .help-block,
+.form-horizontal .input-append + .help-block {
+ margin-top: 10px;
+}
+
.form-horizontal .form-actions {
- padding-left: 160px;
+ padding-left: 180px;
}
table {
@@ -1773,6 +2012,10 @@ table {
border-top: 2px solid #dddddd;
}
+.table .table {
+ background-color: #ffffff;
+}
+
.table-condensed th,
.table-condensed td {
padding: 4px 5px;
@@ -1805,39 +2048,48 @@ table {
border-top: 0;
}
-.table-bordered thead:first-child tr:first-child th:first-child,
-.table-bordered tbody:first-child tr:first-child td:first-child {
+.table-bordered thead:first-child tr:first-child > th:first-child,
+.table-bordered tbody:first-child tr:first-child > td:first-child {
-webkit-border-top-left-radius: 4px;
border-top-left-radius: 4px;
-moz-border-radius-topleft: 4px;
}
-.table-bordered thead:first-child tr:first-child th:last-child,
-.table-bordered tbody:first-child tr:first-child td:last-child {
+.table-bordered thead:first-child tr:first-child > th:last-child,
+.table-bordered tbody:first-child tr:first-child > td:last-child {
-webkit-border-top-right-radius: 4px;
border-top-right-radius: 4px;
-moz-border-radius-topright: 4px;
}
-.table-bordered thead:last-child tr:last-child th:first-child,
-.table-bordered tbody:last-child tr:last-child td:first-child,
-.table-bordered tfoot:last-child tr:last-child td:first-child {
- -webkit-border-radius: 0 0 0 4px;
- -moz-border-radius: 0 0 0 4px;
- border-radius: 0 0 0 4px;
+.table-bordered thead:last-child tr:last-child > th:first-child,
+.table-bordered tbody:last-child tr:last-child > td:first-child,
+.table-bordered tfoot:last-child tr:last-child > td:first-child {
-webkit-border-bottom-left-radius: 4px;
border-bottom-left-radius: 4px;
-moz-border-radius-bottomleft: 4px;
}
-.table-bordered thead:last-child tr:last-child th:last-child,
-.table-bordered tbody:last-child tr:last-child td:last-child,
-.table-bordered tfoot:last-child tr:last-child td:last-child {
+.table-bordered thead:last-child tr:last-child > th:last-child,
+.table-bordered tbody:last-child tr:last-child > td:last-child,
+.table-bordered tfoot:last-child tr:last-child > td:last-child {
-webkit-border-bottom-right-radius: 4px;
border-bottom-right-radius: 4px;
-moz-border-radius-bottomright: 4px;
}
+.table-bordered tfoot + tbody:last-child tr:last-child td:first-child {
+ -webkit-border-bottom-left-radius: 0;
+ border-bottom-left-radius: 0;
+ -moz-border-radius-bottomleft: 0;
+}
+
+.table-bordered tfoot + tbody:last-child tr:last-child td:last-child {
+ -webkit-border-bottom-right-radius: 0;
+ border-bottom-right-radius: 0;
+ -moz-border-radius-bottomright: 0;
+}
+
.table-bordered caption + thead tr:first-child th:first-child,
.table-bordered caption + tbody tr:first-child td:first-child,
.table-bordered colgroup + thead tr:first-child th:first-child,
@@ -1853,11 +2105,11 @@ table {
.table-bordered colgroup + tbody tr:first-child td:last-child {
-webkit-border-top-right-radius: 4px;
border-top-right-radius: 4px;
- -moz-border-right-topleft: 4px;
+ -moz-border-radius-topright: 4px;
}
-.table-striped tbody tr:nth-child(odd) td,
-.table-striped tbody tr:nth-child(odd) th {
+.table-striped tbody > tr:nth-child(odd) > td,
+.table-striped tbody > tr:nth-child(odd) > th {
background-color: #f9f9f9;
}
@@ -1866,167 +2118,129 @@ table {
background-color: #f5f5f5;
}
-table [class*=span],
-.row-fluid table [class*=span] {
+table td[class*="span"],
+table th[class*="span"],
+.row-fluid table td[class*="span"],
+.row-fluid table th[class*="span"] {
display: table-cell;
float: none;
margin-left: 0;
}
-table .span1 {
+.table td.span1,
+.table th.span1 {
float: none;
width: 44px;
margin-left: 0;
}
-table .span2 {
+.table td.span2,
+.table th.span2 {
float: none;
width: 124px;
margin-left: 0;
}
-table .span3 {
+.table td.span3,
+.table th.span3 {
float: none;
width: 204px;
margin-left: 0;
}
-table .span4 {
+.table td.span4,
+.table th.span4 {
float: none;
width: 284px;
margin-left: 0;
}
-table .span5 {
+.table td.span5,
+.table th.span5 {
float: none;
width: 364px;
margin-left: 0;
}
-table .span6 {
+.table td.span6,
+.table th.span6 {
float: none;
width: 444px;
margin-left: 0;
}
-table .span7 {
+.table td.span7,
+.table th.span7 {
float: none;
width: 524px;
margin-left: 0;
}
-table .span8 {
+.table td.span8,
+.table th.span8 {
float: none;
width: 604px;
margin-left: 0;
}
-table .span9 {
+.table td.span9,
+.table th.span9 {
float: none;
width: 684px;
margin-left: 0;
}
-table .span10 {
+.table td.span10,
+.table th.span10 {
float: none;
width: 764px;
margin-left: 0;
}
-table .span11 {
+.table td.span11,
+.table th.span11 {
float: none;
width: 844px;
margin-left: 0;
}
-table .span12 {
+.table td.span12,
+.table th.span12 {
float: none;
width: 924px;
margin-left: 0;
}
-table .span13 {
- float: none;
- width: 1004px;
- margin-left: 0;
-}
-
-table .span14 {
- float: none;
- width: 1084px;
- margin-left: 0;
-}
-
-table .span15 {
- float: none;
- width: 1164px;
- margin-left: 0;
-}
-
-table .span16 {
- float: none;
- width: 1244px;
- margin-left: 0;
-}
-
-table .span17 {
- float: none;
- width: 1324px;
- margin-left: 0;
-}
-
-table .span18 {
- float: none;
- width: 1404px;
- margin-left: 0;
-}
-
-table .span19 {
- float: none;
- width: 1484px;
- margin-left: 0;
-}
-
-table .span20 {
- float: none;
- width: 1564px;
- margin-left: 0;
+.table tbody tr.success td {
+ background-color: #dff0d8;
}
-table .span21 {
- float: none;
- width: 1644px;
- margin-left: 0;
+.table tbody tr.error td {
+ background-color: #f2dede;
}
-table .span22 {
- float: none;
- width: 1724px;
- margin-left: 0;
+.table tbody tr.warning td {
+ background-color: #fcf8e3;
}
-table .span23 {
- float: none;
- width: 1804px;
- margin-left: 0;
+.table tbody tr.info td {
+ background-color: #d9edf7;
}
-table .span24 {
- float: none;
- width: 1884px;
- margin-left: 0;
+.table-hover tbody tr.success:hover td {
+ background-color: #d0e9c6;
}
-.table tbody tr.success td {
- background-color: #dff0d8;
+.table-hover tbody tr.error:hover td {
+ background-color: #ebcccc;
}
-.table tbody tr.error td {
- background-color: #f2dede;
+.table-hover tbody tr.warning:hover td {
+ background-color: #faf2cc;
}
-.table tbody tr.info td {
- background-color: #d9edf7;
+.table-hover tbody tr.info:hover td {
+ background-color: #c4e3f3;
}
[class^="icon-"],
@@ -2046,12 +2260,18 @@ table .span24 {
/* White icons with optional class, or on hover/active states of certain elements */
.icon-white,
-.nav > .active > a > [class^="icon-"],
-.nav > .active > a > [class*=" icon-"],
+.nav-pills > .active > a > [class^="icon-"],
+.nav-pills > .active > a > [class*=" icon-"],
+.nav-list > .active > a > [class^="icon-"],
+.nav-list > .active > a > [class*=" icon-"],
+.navbar-inverse .nav > .active > a > [class^="icon-"],
+.navbar-inverse .nav > .active > a > [class*=" icon-"],
.dropdown-menu > li > a:hover > [class^="icon-"],
.dropdown-menu > li > a:hover > [class*=" icon-"],
.dropdown-menu > .active > a > [class^="icon-"],
-.dropdown-menu > .active > a > [class*=" icon-"] {
+.dropdown-menu > .active > a > [class*=" icon-"],
+.dropdown-submenu:hover > a > [class^="icon-"],
+.dropdown-submenu:hover > a > [class*=" icon-"] {
background-image: url("../img/glyphicons-halflings-white.png");
}
@@ -2689,7 +2909,7 @@ table .span24 {
border-bottom: 1px solid #ffffff;
}
-.dropdown-menu a {
+.dropdown-menu li > a {
display: block;
padding: 3px 20px;
clear: both;
@@ -2704,7 +2924,6 @@ table .span24 {
.dropdown-submenu:hover > a {
color: #ffffff;
text-decoration: none;
- background-color: #0088cc;
background-color: #0081c2;
background-image: -moz-linear-gradient(top, #0088cc, #0077b3);
background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#0088cc), to(#0077b3));
@@ -2712,23 +2931,22 @@ table .span24 {
background-image: -o-linear-gradient(top, #0088cc, #0077b3);
background-image: linear-gradient(to bottom, #0088cc, #0077b3);
background-repeat: repeat-x;
- filter: progid:dximagetransform.microsoft.gradient(startColorstr='#ff0088cc', endColorstr='#ff0077b3', GradientType=0);
+ filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff0088cc', endColorstr='#ff0077b3', GradientType=0);
}
.dropdown-menu .active > a,
.dropdown-menu .active > a:hover {
color: #ffffff;
text-decoration: none;
- background-color: #0088cc;
background-color: #0081c2;
- background-image: linear-gradient(to bottom, #0088cc, #0077b3);
background-image: -moz-linear-gradient(top, #0088cc, #0077b3);
background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#0088cc), to(#0077b3));
background-image: -webkit-linear-gradient(top, #0088cc, #0077b3);
background-image: -o-linear-gradient(top, #0088cc, #0077b3);
+ background-image: linear-gradient(to bottom, #0088cc, #0077b3);
background-repeat: repeat-x;
outline: 0;
- filter: progid:dximagetransform.microsoft.gradient(startColorstr='#ff0088cc', endColorstr='#ff0077b3', GradientType=0);
+ filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff0088cc', endColorstr='#ff0077b3', GradientType=0);
}
.dropdown-menu .disabled > a,
@@ -2740,6 +2958,8 @@ table .span24 {
text-decoration: none;
cursor: default;
background-color: transparent;
+ background-image: none;
+ filter: progid:DXImageTransform.Microsoft.gradient(enabled=false);
}
.open {
@@ -2759,7 +2979,7 @@ table .span24 {
.navbar-fixed-bottom .dropdown .caret {
border-top: 0;
border-bottom: 4px solid #000000;
- content: "\2191";
+ content: "";
}
.dropup .dropdown-menu,
@@ -2783,10 +3003,20 @@ table .span24 {
border-radius: 0 6px 6px 6px;
}
-.dropdown-submenu:hover .dropdown-menu {
+.dropdown-submenu:hover > .dropdown-menu {
display: block;
}
+.dropup .dropdown-submenu > .dropdown-menu {
+ top: auto;
+ bottom: 0;
+ margin-top: 0;
+ margin-bottom: -2px;
+ -webkit-border-radius: 5px 5px 5px 0;
+ -moz-border-radius: 5px 5px 5px 0;
+ border-radius: 5px 5px 5px 0;
+}
+
.dropdown-submenu > a:after {
display: block;
float: right;
@@ -2805,12 +3035,25 @@ table .span24 {
border-left-color: #ffffff;
}
+.dropdown-submenu.pull-left {
+ float: none;
+}
+
+.dropdown-submenu.pull-left > .dropdown-menu {
+ left: -100%;
+ margin-left: 10px;
+ -webkit-border-radius: 6px 0 6px 6px;
+ -moz-border-radius: 6px 0 6px 6px;
+ border-radius: 6px 0 6px 6px;
+}
+
.dropdown .dropdown-menu .nav-header {
padding-right: 20px;
padding-left: 20px;
}
.typeahead {
+ z-index: 1051;
margin-top: 2px;
-webkit-border-radius: 4px;
-moz-border-radius: 4px;
@@ -2866,7 +3109,6 @@ table .span24 {
position: relative;
height: 0;
overflow: hidden;
- overflow: visible \9;
-webkit-transition: height 0.35s ease;
-moz-transition: height 0.35s ease;
-o-transition: height 0.35s ease;
@@ -2907,12 +3149,11 @@ button.close {
.btn {
display: inline-block;
*display: inline;
- padding: 4px 14px;
+ padding: 4px 12px;
margin-bottom: 0;
*margin-left: .3em;
font-size: 14px;
line-height: 20px;
- *line-height: 20px;
color: #333333;
text-align: center;
text-shadow: 0 1px 1px rgba(255, 255, 255, 0.75);
@@ -2920,22 +3161,22 @@ button.close {
cursor: pointer;
background-color: #f5f5f5;
*background-color: #e6e6e6;
+ background-image: -moz-linear-gradient(top, #ffffff, #e6e6e6);
background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#ffffff), to(#e6e6e6));
background-image: -webkit-linear-gradient(top, #ffffff, #e6e6e6);
background-image: -o-linear-gradient(top, #ffffff, #e6e6e6);
background-image: linear-gradient(to bottom, #ffffff, #e6e6e6);
- background-image: -moz-linear-gradient(top, #ffffff, #e6e6e6);
background-repeat: repeat-x;
border: 1px solid #bbbbbb;
*border: 0;
- border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);
border-color: #e6e6e6 #e6e6e6 #bfbfbf;
+ border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);
border-bottom-color: #a2a2a2;
-webkit-border-radius: 4px;
-moz-border-radius: 4px;
border-radius: 4px;
- filter: progid:dximagetransform.microsoft.gradient(startColorstr='#ffffffff', endColorstr='#ffe6e6e6', GradientType=0);
- filter: progid:dximagetransform.microsoft.gradient(enabled=false);
+ filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#ffe6e6e6', GradientType=0);
+ filter: progid:DXImageTransform.Microsoft.gradient(enabled=false);
*zoom: 1;
-webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 1px 2px rgba(0, 0, 0, 0.05);
-moz-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 1px 2px rgba(0, 0, 0, 0.05);
@@ -2964,10 +3205,6 @@ button.close {
.btn:hover {
color: #333333;
text-decoration: none;
- background-color: #e6e6e6;
- *background-color: #d9d9d9;
- /* Buttons in IE7 don't get borders, so darken on hover */
-
background-position: 0 -15px;
-webkit-transition: background-position 0.1s linear;
-moz-transition: background-position 0.1s linear;
@@ -2983,8 +3220,6 @@ button.close {
.btn.active,
.btn:active {
- background-color: #e6e6e6;
- background-color: #d9d9d9 \9;
background-image: none;
outline: 0;
-webkit-box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.15), 0 1px 2px rgba(0, 0, 0, 0.05);
@@ -2995,7 +3230,6 @@ button.close {
.btn.disabled,
.btn[disabled] {
cursor: default;
- background-color: #e6e6e6;
background-image: none;
opacity: 0.65;
filter: alpha(opacity=65);
@@ -3005,32 +3239,42 @@ button.close {
}
.btn-large {
- padding: 9px 14px;
- font-size: 16px;
- line-height: normal;
- -webkit-border-radius: 5px;
- -moz-border-radius: 5px;
- border-radius: 5px;
+ padding: 11px 19px;
+ font-size: 17.5px;
+ -webkit-border-radius: 6px;
+ -moz-border-radius: 6px;
+ border-radius: 6px;
}
-.btn-large [class^="icon-"] {
- margin-top: 2px;
+.btn-large [class^="icon-"],
+.btn-large [class*=" icon-"] {
+ margin-top: 4px;
}
.btn-small {
- padding: 3px 9px;
- font-size: 12px;
- line-height: 18px;
+ padding: 2px 10px;
+ font-size: 11.9px;
+ -webkit-border-radius: 3px;
+ -moz-border-radius: 3px;
+ border-radius: 3px;
}
-.btn-small [class^="icon-"] {
+.btn-small [class^="icon-"],
+.btn-small [class*=" icon-"] {
margin-top: 0;
}
+.btn-mini [class^="icon-"],
+.btn-mini [class*=" icon-"] {
+ margin-top: -1px;
+}
+
.btn-mini {
- padding: 2px 6px;
- font-size: 11px;
- line-height: 16px;
+ padding: 0 6px;
+ font-size: 10.5px;
+ -webkit-border-radius: 3px;
+ -moz-border-radius: 3px;
+ border-radius: 3px;
}
.btn-block {
@@ -3047,6 +3291,12 @@ button.close {
margin-top: 5px;
}
+input[type="submit"].btn-block,
+input[type="reset"].btn-block,
+input[type="button"].btn-block {
+ width: 100%;
+}
+
.btn-primary.active,
.btn-warning.active,
.btn-danger.active,
@@ -3066,16 +3316,16 @@ button.close {
text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25);
background-color: #006dcc;
*background-color: #0044cc;
+ background-image: -moz-linear-gradient(top, #0088cc, #0044cc);
background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#0088cc), to(#0044cc));
background-image: -webkit-linear-gradient(top, #0088cc, #0044cc);
background-image: -o-linear-gradient(top, #0088cc, #0044cc);
background-image: linear-gradient(to bottom, #0088cc, #0044cc);
- background-image: -moz-linear-gradient(top, #0088cc, #0044cc);
background-repeat: repeat-x;
border-color: #0044cc #0044cc #002a80;
border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);
- filter: progid:dximagetransform.microsoft.gradient(startColorstr='#ff0088cc', endColorstr='#ff0044cc', GradientType=0);
- filter: progid:dximagetransform.microsoft.gradient(enabled=false);
+ filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff0088cc', endColorstr='#ff0044cc', GradientType=0);
+ filter: progid:DXImageTransform.Microsoft.gradient(enabled=false);
}
.btn-primary:hover,
@@ -3098,16 +3348,16 @@ button.close {
text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25);
background-color: #faa732;
*background-color: #f89406;
+ background-image: -moz-linear-gradient(top, #fbb450, #f89406);
background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#fbb450), to(#f89406));
background-image: -webkit-linear-gradient(top, #fbb450, #f89406);
background-image: -o-linear-gradient(top, #fbb450, #f89406);
background-image: linear-gradient(to bottom, #fbb450, #f89406);
- background-image: -moz-linear-gradient(top, #fbb450, #f89406);
background-repeat: repeat-x;
border-color: #f89406 #f89406 #ad6704;
border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);
- filter: progid:dximagetransform.microsoft.gradient(startColorstr='#fffbb450', endColorstr='#fff89406', GradientType=0);
- filter: progid:dximagetransform.microsoft.gradient(enabled=false);
+ filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffbb450', endColorstr='#fff89406', GradientType=0);
+ filter: progid:DXImageTransform.Microsoft.gradient(enabled=false);
}
.btn-warning:hover,
@@ -3130,16 +3380,16 @@ button.close {
text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25);
background-color: #da4f49;
*background-color: #bd362f;
+ background-image: -moz-linear-gradient(top, #ee5f5b, #bd362f);
background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#ee5f5b), to(#bd362f));
background-image: -webkit-linear-gradient(top, #ee5f5b, #bd362f);
background-image: -o-linear-gradient(top, #ee5f5b, #bd362f);
background-image: linear-gradient(to bottom, #ee5f5b, #bd362f);
- background-image: -moz-linear-gradient(top, #ee5f5b, #bd362f);
background-repeat: repeat-x;
border-color: #bd362f #bd362f #802420;
border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);
- filter: progid:dximagetransform.microsoft.gradient(startColorstr='#ffee5f5b', endColorstr='#ffbd362f', GradientType=0);
- filter: progid:dximagetransform.microsoft.gradient(enabled=false);
+ filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffee5f5b', endColorstr='#ffbd362f', GradientType=0);
+ filter: progid:DXImageTransform.Microsoft.gradient(enabled=false);
}
.btn-danger:hover,
@@ -3162,16 +3412,16 @@ button.close {
text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25);
background-color: #5bb75b;
*background-color: #51a351;
+ background-image: -moz-linear-gradient(top, #62c462, #51a351);
background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#62c462), to(#51a351));
background-image: -webkit-linear-gradient(top, #62c462, #51a351);
background-image: -o-linear-gradient(top, #62c462, #51a351);
background-image: linear-gradient(to bottom, #62c462, #51a351);
- background-image: -moz-linear-gradient(top, #62c462, #51a351);
background-repeat: repeat-x;
border-color: #51a351 #51a351 #387038;
border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);
- filter: progid:dximagetransform.microsoft.gradient(startColorstr='#ff62c462', endColorstr='#ff51a351', GradientType=0);
- filter: progid:dximagetransform.microsoft.gradient(enabled=false);
+ filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff62c462', endColorstr='#ff51a351', GradientType=0);
+ filter: progid:DXImageTransform.Microsoft.gradient(enabled=false);
}
.btn-success:hover,
@@ -3194,16 +3444,16 @@ button.close {
text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25);
background-color: #49afcd;
*background-color: #2f96b4;
+ background-image: -moz-linear-gradient(top, #5bc0de, #2f96b4);
background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#5bc0de), to(#2f96b4));
background-image: -webkit-linear-gradient(top, #5bc0de, #2f96b4);
background-image: -o-linear-gradient(top, #5bc0de, #2f96b4);
background-image: linear-gradient(to bottom, #5bc0de, #2f96b4);
- background-image: -moz-linear-gradient(top, #5bc0de, #2f96b4);
background-repeat: repeat-x;
border-color: #2f96b4 #2f96b4 #1f6377;
border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);
- filter: progid:dximagetransform.microsoft.gradient(startColorstr='#ff5bc0de', endColorstr='#ff2f96b4', GradientType=0);
- filter: progid:dximagetransform.microsoft.gradient(enabled=false);
+ filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de', endColorstr='#ff2f96b4', GradientType=0);
+ filter: progid:DXImageTransform.Microsoft.gradient(enabled=false);
}
.btn-info:hover,
@@ -3226,16 +3476,16 @@ button.close {
text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25);
background-color: #363636;
*background-color: #222222;
+ background-image: -moz-linear-gradient(top, #444444, #222222);
background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#444444), to(#222222));
background-image: -webkit-linear-gradient(top, #444444, #222222);
background-image: -o-linear-gradient(top, #444444, #222222);
background-image: linear-gradient(to bottom, #444444, #222222);
- background-image: -moz-linear-gradient(top, #444444, #222222);
background-repeat: repeat-x;
border-color: #222222 #222222 #000000;
border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);
- filter: progid:dximagetransform.microsoft.gradient(startColorstr='#ff444444', endColorstr='#ff222222', GradientType=0);
- filter: progid:dximagetransform.microsoft.gradient(enabled=false);
+ filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff444444', endColorstr='#ff222222', GradientType=0);
+ filter: progid:DXImageTransform.Microsoft.gradient(enabled=false);
}
.btn-inverse:hover,
@@ -3284,7 +3534,8 @@ input[type="submit"].btn.btn-mini {
}
.btn-link,
-.btn-link:active {
+.btn-link:active,
+.btn-link[disabled] {
background-color: transparent;
background-image: none;
-webkit-box-shadow: none;
@@ -3307,11 +3558,20 @@ input[type="submit"].btn.btn-mini {
background-color: transparent;
}
+.btn-link[disabled]:hover {
+ color: #333333;
+ text-decoration: none;
+}
+
.btn-group {
position: relative;
+ display: inline-block;
+ *display: inline;
*margin-left: .3em;
font-size: 0;
white-space: nowrap;
+ vertical-align: middle;
+ *zoom: 1;
}
.btn-group:first-child {
@@ -3328,17 +3588,9 @@ input[type="submit"].btn.btn-mini {
font-size: 0;
}
-.btn-toolbar .btn-group {
- display: inline-block;
- *display: inline;
- /* IE7 inline-block hack */
-
- *zoom: 1;
-}
-
-.btn-toolbar .btn + .btn,
-.btn-toolbar .btn-group + .btn,
-.btn-toolbar .btn + .btn-group {
+.btn-toolbar > .btn + .btn,
+.btn-toolbar > .btn-group + .btn,
+.btn-toolbar > .btn + .btn-group {
margin-left: 5px;
}
@@ -3354,20 +3606,21 @@ input[type="submit"].btn.btn-mini {
}
.btn-group > .btn,
-.btn-group > .dropdown-menu {
+.btn-group > .dropdown-menu,
+.btn-group > .popover {
font-size: 14px;
}
.btn-group > .btn-mini {
- font-size: 11px;
+ font-size: 10.5px;
}
.btn-group > .btn-small {
- font-size: 12px;
+ font-size: 11.9px;
}
.btn-group > .btn-large {
- font-size: 16px;
+ font-size: 17.5px;
}
.btn-group > .btn:first-child {
@@ -3504,8 +3757,7 @@ input[type="submit"].btn.btn-mini {
}
.dropup .btn-large .caret {
- border-top: 0;
- border-bottom: 5px solid #000000;
+ border-bottom-width: 5px;
}
.btn-primary .caret,
@@ -3526,39 +3778,39 @@ input[type="submit"].btn.btn-mini {
*zoom: 1;
}
-.btn-group-vertical .btn {
+.btn-group-vertical > .btn {
display: block;
float: none;
- width: 100%;
+ max-width: 100%;
-webkit-border-radius: 0;
-moz-border-radius: 0;
border-radius: 0;
}
-.btn-group-vertical .btn + .btn {
+.btn-group-vertical > .btn + .btn {
margin-top: -1px;
margin-left: 0;
}
-.btn-group-vertical .btn:first-child {
+.btn-group-vertical > .btn:first-child {
-webkit-border-radius: 4px 4px 0 0;
-moz-border-radius: 4px 4px 0 0;
border-radius: 4px 4px 0 0;
}
-.btn-group-vertical .btn:last-child {
+.btn-group-vertical > .btn:last-child {
-webkit-border-radius: 0 0 4px 4px;
-moz-border-radius: 0 0 4px 4px;
border-radius: 0 0 4px 4px;
}
-.btn-group-vertical .btn-large:first-child {
+.btn-group-vertical > .btn-large:first-child {
-webkit-border-radius: 6px 6px 0 0;
-moz-border-radius: 6px 6px 0 0;
border-radius: 6px 6px 0 0;
}
-.btn-group-vertical .btn-large:last-child {
+.btn-group-vertical > .btn-large:last-child {
-webkit-border-radius: 0 0 6px 6px;
-moz-border-radius: 0 0 6px 6px;
border-radius: 0 0 6px 6px;
@@ -3567,7 +3819,6 @@ input[type="submit"].btn.btn-mini {
.alert {
padding: 8px 35px 8px 14px;
margin-bottom: 20px;
- color: #c09853;
text-shadow: 0 1px 0 rgba(255, 255, 255, 0.5);
background-color: #fcf8e3;
border: 1px solid #fbeed5;
@@ -3576,6 +3827,11 @@ input[type="submit"].btn.btn-mini {
border-radius: 4px;
}
+.alert,
+.alert h4 {
+ color: #c09853;
+}
+
.alert h4 {
margin: 0;
}
@@ -3593,6 +3849,10 @@ input[type="submit"].btn.btn-mini {
border-color: #d6e9c6;
}
+.alert-success h4 {
+ color: #468847;
+}
+
.alert-danger,
.alert-error {
color: #b94a48;
@@ -3600,12 +3860,21 @@ input[type="submit"].btn.btn-mini {
border-color: #eed3d7;
}
+.alert-danger h4,
+.alert-error h4 {
+ color: #b94a48;
+}
+
.alert-info {
color: #3a87ad;
background-color: #d9edf7;
border-color: #bce8f1;
}
+.alert-info h4 {
+ color: #3a87ad;
+}
+
.alert-block {
padding-top: 14px;
padding-bottom: 14px;
@@ -3635,6 +3904,10 @@ input[type="submit"].btn.btn-mini {
background-color: #eeeeee;
}
+.nav > li > a > img {
+ max-width: none;
+}
+
.nav > .pull-right {
float: right;
}
@@ -3678,7 +3951,8 @@ input[type="submit"].btn.btn-mini {
background-color: #0088cc;
}
-.nav-list [class^="icon-"] {
+.nav-list [class^="icon-"],
+.nav-list [class*=" icon-"] {
margin-right: 2px;
}
@@ -4018,7 +4292,6 @@ input[type="submit"].btn.btn-mini {
*z-index: 2;
margin-bottom: 20px;
overflow: visible;
- color: #555555;
}
.navbar-inner {
@@ -4036,18 +4309,31 @@ input[type="submit"].btn.btn-mini {
-webkit-border-radius: 4px;
-moz-border-radius: 4px;
border-radius: 4px;
- filter: progid:dximagetransform.microsoft.gradient(startColorstr='#ffffffff', endColorstr='#fff2f2f2', GradientType=0);
+ filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#fff2f2f2', GradientType=0);
+ *zoom: 1;
-webkit-box-shadow: 0 1px 4px rgba(0, 0, 0, 0.065);
-moz-box-shadow: 0 1px 4px rgba(0, 0, 0, 0.065);
box-shadow: 0 1px 4px rgba(0, 0, 0, 0.065);
}
+.navbar-inner:before,
+.navbar-inner:after {
+ display: table;
+ line-height: 0;
+ content: "";
+}
+
+.navbar-inner:after {
+ clear: both;
+}
+
.navbar .container {
width: auto;
}
.nav-collapse.collapse {
height: auto;
+ overflow: visible;
}
.navbar .brand {
@@ -4057,7 +4343,7 @@ input[type="submit"].btn.btn-mini {
margin-left: -20px;
font-size: 20px;
font-weight: 200;
- color: #555555;
+ color: #777777;
text-shadow: 0 1px 0 #ffffff;
}
@@ -4068,10 +4354,11 @@ input[type="submit"].btn.btn-mini {
.navbar-text {
margin-bottom: 0;
line-height: 40px;
+ color: #777777;
}
.navbar-link {
- color: #555555;
+ color: #777777;
}
.navbar-link:hover {
@@ -4087,11 +4374,13 @@ input[type="submit"].btn.btn-mini {
.navbar .btn,
.navbar .btn-group {
- margin-top: 6px;
+ margin-top: 5px;
}
-.navbar .btn-group .btn {
- margin: 0;
+.navbar .btn-group .btn,
+.navbar .input-prepend .btn,
+.navbar .input-append .btn {
+ margin-top: 0;
}
.navbar-form {
@@ -4132,7 +4421,7 @@ input[type="submit"].btn.btn-mini {
.navbar-form .input-append,
.navbar-form .input-prepend {
- margin-top: 6px;
+ margin-top: 5px;
white-space: nowrap;
}
@@ -4162,7 +4451,6 @@ input[type="submit"].btn.btn-mini {
.navbar-static-top {
position: static;
- width: 100%;
margin-bottom: 0;
}
@@ -4182,9 +4470,12 @@ input[type="submit"].btn.btn-mini {
}
.navbar-fixed-top .navbar-inner,
-.navbar-fixed-bottom .navbar-inner,
.navbar-static-top .navbar-inner {
- border: 0;
+ border-width: 0 0 1px;
+}
+
+.navbar-fixed-bottom .navbar-inner {
+ border-width: 1px 0 0;
}
.navbar-fixed-top .navbar-inner,
@@ -4208,9 +4499,9 @@ input[type="submit"].btn.btn-mini {
.navbar-fixed-top .navbar-inner,
.navbar-static-top .navbar-inner {
- -webkit-box-shadow: inset 0 -1px 0 rgba(0, 0, 0, 0.1), 0 1px 10px rgba(0, 0, 0, 0.1);
- -moz-box-shadow: inset 0 -1px 0 rgba(0, 0, 0, 0.1), 0 1px 10px rgba(0, 0, 0, 0.1);
- box-shadow: inset 0 -1px 0 rgba(0, 0, 0, 0.1), 0 1px 10px rgba(0, 0, 0, 0.1);
+ -webkit-box-shadow: 0 1px 10px rgba(0, 0, 0, 0.1);
+ -moz-box-shadow: 0 1px 10px rgba(0, 0, 0, 0.1);
+ box-shadow: 0 1px 10px rgba(0, 0, 0, 0.1);
}
.navbar-fixed-bottom {
@@ -4218,9 +4509,9 @@ input[type="submit"].btn.btn-mini {
}
.navbar-fixed-bottom .navbar-inner {
- -webkit-box-shadow: inset 0 1px 0 rgba(0, 0, 0, 0.1), 0 -1px 10px rgba(0, 0, 0, 0.1);
- -moz-box-shadow: inset 0 1px 0 rgba(0, 0, 0, 0.1), 0 -1px 10px rgba(0, 0, 0, 0.1);
- box-shadow: inset 0 1px 0 rgba(0, 0, 0, 0.1), 0 -1px 10px rgba(0, 0, 0, 0.1);
+ -webkit-box-shadow: 0 -1px 10px rgba(0, 0, 0, 0.1);
+ -moz-box-shadow: 0 -1px 10px rgba(0, 0, 0, 0.1);
+ box-shadow: 0 -1px 10px rgba(0, 0, 0, 0.1);
}
.navbar .nav {
@@ -4233,6 +4524,7 @@ input[type="submit"].btn.btn-mini {
.navbar .nav.pull-right {
float: right;
+ margin-right: 0;
}
.navbar .nav > li {
@@ -4242,7 +4534,7 @@ input[type="submit"].btn.btn-mini {
.navbar .nav > li > a {
float: none;
padding: 10px 15px 10px;
- color: #555555;
+ color: #777777;
text-decoration: none;
text-shadow: 0 1px 0 #ffffff;
}
@@ -4279,16 +4571,16 @@ input[type="submit"].btn.btn-mini {
text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25);
background-color: #ededed;
*background-color: #e5e5e5;
+ background-image: -moz-linear-gradient(top, #f2f2f2, #e5e5e5);
background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#f2f2f2), to(#e5e5e5));
background-image: -webkit-linear-gradient(top, #f2f2f2, #e5e5e5);
background-image: -o-linear-gradient(top, #f2f2f2, #e5e5e5);
background-image: linear-gradient(to bottom, #f2f2f2, #e5e5e5);
- background-image: -moz-linear-gradient(top, #f2f2f2, #e5e5e5);
background-repeat: repeat-x;
border-color: #e5e5e5 #e5e5e5 #bfbfbf;
border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);
- filter: progid:dximagetransform.microsoft.gradient(startColorstr='#fff2f2f2', endColorstr='#ffe5e5e5', GradientType=0);
- filter: progid:dximagetransform.microsoft.gradient(enabled=false);
+ filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff2f2f2', endColorstr='#ffe5e5e5', GradientType=0);
+ filter: progid:DXImageTransform.Microsoft.gradient(enabled=false);
-webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1), 0 1px 0 rgba(255, 255, 255, 0.075);
-moz-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1), 0 1px 0 rgba(255, 255, 255, 0.075);
box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1), 0 1px 0 rgba(255, 255, 255, 0.075);
@@ -4364,6 +4656,11 @@ input[type="submit"].btn.btn-mini {
border-bottom: 0;
}
+.navbar .nav li.dropdown > a:hover .caret {
+ border-top-color: #555555;
+ border-bottom-color: #555555;
+}
+
.navbar .nav li.dropdown.open > .dropdown-toggle,
.navbar .nav li.dropdown.active > .dropdown-toggle,
.navbar .nav li.dropdown.open.active > .dropdown-toggle {
@@ -4372,8 +4669,8 @@ input[type="submit"].btn.btn-mini {
}
.navbar .nav li.dropdown > .dropdown-toggle .caret {
- border-top-color: #555555;
- border-bottom-color: #555555;
+ border-top-color: #777777;
+ border-bottom-color: #777777;
}
.navbar .nav li.dropdown.open > .dropdown-toggle .caret,
@@ -4412,10 +4709,6 @@ input[type="submit"].btn.btn-mini {
border-radius: 6px 0 6px 6px;
}
-.navbar-inverse {
- color: #999999;
-}
-
.navbar-inverse .navbar-inner {
background-color: #1b1b1b;
background-image: -moz-linear-gradient(top, #222222, #111111);
@@ -4425,7 +4718,7 @@ input[type="submit"].btn.btn-mini {
background-image: linear-gradient(to bottom, #222222, #111111);
background-repeat: repeat-x;
border-color: #252525;
- filter: progid:dximagetransform.microsoft.gradient(startColorstr='#ff222222', endColorstr='#ff111111', GradientType=0);
+ filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff222222', endColorstr='#ff111111', GradientType=0);
}
.navbar-inverse .brand,
@@ -4439,6 +4732,14 @@ input[type="submit"].btn.btn-mini {
color: #ffffff;
}
+.navbar-inverse .brand {
+ color: #999999;
+}
+
+.navbar-inverse .navbar-text {
+ color: #999999;
+}
+
.navbar-inverse .nav > li > a:focus,
.navbar-inverse .nav > li > a:hover {
color: #ffffff;
@@ -4472,6 +4773,11 @@ input[type="submit"].btn.btn-mini {
background-color: #111111;
}
+.navbar-inverse .nav li.dropdown > a:hover .caret {
+ border-top-color: #ffffff;
+ border-bottom-color: #ffffff;
+}
+
.navbar-inverse .nav li.dropdown > .dropdown-toggle .caret {
border-top-color: #999999;
border-bottom-color: #999999;
@@ -4527,16 +4833,16 @@ input[type="submit"].btn.btn-mini {
text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25);
background-color: #0e0e0e;
*background-color: #040404;
+ background-image: -moz-linear-gradient(top, #151515, #040404);
background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#151515), to(#040404));
background-image: -webkit-linear-gradient(top, #151515, #040404);
background-image: -o-linear-gradient(top, #151515, #040404);
background-image: linear-gradient(to bottom, #151515, #040404);
- background-image: -moz-linear-gradient(top, #151515, #040404);
background-repeat: repeat-x;
border-color: #040404 #040404 #000000;
border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);
- filter: progid:dximagetransform.microsoft.gradient(startColorstr='#ff151515', endColorstr='#ff040404', GradientType=0);
- filter: progid:dximagetransform.microsoft.gradient(enabled=false);
+ filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff151515', endColorstr='#ff040404', GradientType=0);
+ filter: progid:DXImageTransform.Microsoft.gradient(enabled=false);
}
.navbar-inverse .btn-navbar:hover,
@@ -4564,24 +4870,23 @@ input[type="submit"].btn.btn-mini {
border-radius: 4px;
}
-.breadcrumb li {
+.breadcrumb > li {
display: inline-block;
*display: inline;
text-shadow: 0 1px 0 #ffffff;
*zoom: 1;
}
-.breadcrumb .divider {
+.breadcrumb > li > .divider {
padding: 0 5px;
color: #ccc;
}
-.breadcrumb .active {
+.breadcrumb > .active {
color: #999999;
}
.pagination {
- height: 40px;
margin: 20px 0;
}
@@ -4590,63 +4895,69 @@ input[type="submit"].btn.btn-mini {
*display: inline;
margin-bottom: 0;
margin-left: 0;
- -webkit-border-radius: 3px;
- -moz-border-radius: 3px;
- border-radius: 3px;
+ -webkit-border-radius: 4px;
+ -moz-border-radius: 4px;
+ border-radius: 4px;
*zoom: 1;
-webkit-box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05);
-moz-box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05);
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05);
}
-.pagination li {
+.pagination ul > li {
display: inline;
}
-.pagination a,
-.pagination span {
+.pagination ul > li > a,
+.pagination ul > li > span {
float: left;
- padding: 0 14px;
- line-height: 38px;
+ padding: 4px 12px;
+ line-height: 20px;
text-decoration: none;
background-color: #ffffff;
border: 1px solid #dddddd;
border-left-width: 0;
}
-.pagination a:hover,
-.pagination .active a,
-.pagination .active span {
+.pagination ul > li > a:hover,
+.pagination ul > .active > a,
+.pagination ul > .active > span {
background-color: #f5f5f5;
}
-.pagination .active a,
-.pagination .active span {
+.pagination ul > .active > a,
+.pagination ul > .active > span {
color: #999999;
cursor: default;
}
-.pagination .disabled span,
-.pagination .disabled a,
-.pagination .disabled a:hover {
+.pagination ul > .disabled > span,
+.pagination ul > .disabled > a,
+.pagination ul > .disabled > a:hover {
color: #999999;
cursor: default;
background-color: transparent;
}
-.pagination li:first-child a,
-.pagination li:first-child span {
+.pagination ul > li:first-child > a,
+.pagination ul > li:first-child > span {
border-left-width: 1px;
- -webkit-border-radius: 3px 0 0 3px;
- -moz-border-radius: 3px 0 0 3px;
- border-radius: 3px 0 0 3px;
+ -webkit-border-bottom-left-radius: 4px;
+ border-bottom-left-radius: 4px;
+ -webkit-border-top-left-radius: 4px;
+ border-top-left-radius: 4px;
+ -moz-border-radius-bottomleft: 4px;
+ -moz-border-radius-topleft: 4px;
}
-.pagination li:last-child a,
-.pagination li:last-child span {
- -webkit-border-radius: 0 3px 3px 0;
- -moz-border-radius: 0 3px 3px 0;
- border-radius: 0 3px 3px 0;
+.pagination ul > li:last-child > a,
+.pagination ul > li:last-child > span {
+ -webkit-border-top-right-radius: 4px;
+ border-top-right-radius: 4px;
+ -webkit-border-bottom-right-radius: 4px;
+ border-bottom-right-radius: 4px;
+ -moz-border-radius-topright: 4px;
+ -moz-border-radius-bottomright: 4px;
}
.pagination-centered {
@@ -4657,6 +4968,68 @@ input[type="submit"].btn.btn-mini {
text-align: right;
}
+.pagination-large ul > li > a,
+.pagination-large ul > li > span {
+ padding: 11px 19px;
+ font-size: 17.5px;
+}
+
+.pagination-large ul > li:first-child > a,
+.pagination-large ul > li:first-child > span {
+ -webkit-border-bottom-left-radius: 6px;
+ border-bottom-left-radius: 6px;
+ -webkit-border-top-left-radius: 6px;
+ border-top-left-radius: 6px;
+ -moz-border-radius-bottomleft: 6px;
+ -moz-border-radius-topleft: 6px;
+}
+
+.pagination-large ul > li:last-child > a,
+.pagination-large ul > li:last-child > span {
+ -webkit-border-top-right-radius: 6px;
+ border-top-right-radius: 6px;
+ -webkit-border-bottom-right-radius: 6px;
+ border-bottom-right-radius: 6px;
+ -moz-border-radius-topright: 6px;
+ -moz-border-radius-bottomright: 6px;
+}
+
+.pagination-mini ul > li:first-child > a,
+.pagination-small ul > li:first-child > a,
+.pagination-mini ul > li:first-child > span,
+.pagination-small ul > li:first-child > span {
+ -webkit-border-bottom-left-radius: 3px;
+ border-bottom-left-radius: 3px;
+ -webkit-border-top-left-radius: 3px;
+ border-top-left-radius: 3px;
+ -moz-border-radius-bottomleft: 3px;
+ -moz-border-radius-topleft: 3px;
+}
+
+.pagination-mini ul > li:last-child > a,
+.pagination-small ul > li:last-child > a,
+.pagination-mini ul > li:last-child > span,
+.pagination-small ul > li:last-child > span {
+ -webkit-border-top-right-radius: 3px;
+ border-top-right-radius: 3px;
+ -webkit-border-bottom-right-radius: 3px;
+ border-bottom-right-radius: 3px;
+ -moz-border-radius-topright: 3px;
+ -moz-border-radius-bottomright: 3px;
+}
+
+.pagination-small ul > li > a,
+.pagination-small ul > li > span {
+ padding: 2px 10px;
+ font-size: 11.9px;
+}
+
+.pagination-mini ul > li > a,
+.pagination-mini ul > li > span {
+ padding: 0 6px;
+ font-size: 10.5px;
+}
+
.pager {
margin: 20px 0;
text-align: center;
@@ -4679,7 +5052,8 @@ input[type="submit"].btn.btn-mini {
display: inline;
}
-.pager a {
+.pager li > a,
+.pager li > span {
display: inline-block;
padding: 5px 14px;
background-color: #fff;
@@ -4689,42 +5063,29 @@ input[type="submit"].btn.btn-mini {
border-radius: 15px;
}
-.pager a:hover {
+.pager li > a:hover {
text-decoration: none;
background-color: #f5f5f5;
}
-.pager .next a {
+.pager .next > a,
+.pager .next > span {
float: right;
}
-.pager .previous a {
+.pager .previous > a,
+.pager .previous > span {
float: left;
}
-.pager .disabled a,
-.pager .disabled a:hover {
+.pager .disabled > a,
+.pager .disabled > a:hover,
+.pager .disabled > span {
color: #999999;
cursor: default;
background-color: #fff;
}
-.modal-open .dropdown-menu {
- z-index: 2050;
-}
-
-.modal-open .dropdown.open {
- *z-index: 2050;
-}
-
-.modal-open .popover {
- z-index: 2060;
-}
-
-.modal-open .tooltip {
- z-index: 2080;
-}
-
.modal-backdrop {
position: fixed;
top: 0;
@@ -4747,12 +5108,11 @@ input[type="submit"].btn.btn-mini {
.modal {
position: fixed;
- top: 50%;
+ top: 10%;
left: 50%;
z-index: 1050;
width: 560px;
- margin: -250px 0 0 -280px;
- overflow: auto;
+ margin-left: -280px;
background-color: #ffffff;
border: 1px solid #999;
border: 1px solid rgba(0, 0, 0, 0.3);
@@ -4760,6 +5120,7 @@ input[type="submit"].btn.btn-mini {
-webkit-border-radius: 6px;
-moz-border-radius: 6px;
border-radius: 6px;
+ outline: none;
-webkit-box-shadow: 0 3px 7px rgba(0, 0, 0, 0.3);
-moz-box-shadow: 0 3px 7px rgba(0, 0, 0, 0.3);
box-shadow: 0 3px 7px rgba(0, 0, 0, 0.3);
@@ -4777,7 +5138,7 @@ input[type="submit"].btn.btn-mini {
}
.modal.fade.in {
- top: 50%;
+ top: 10%;
}
.modal-header {
@@ -4795,6 +5156,7 @@ input[type="submit"].btn.btn-mini {
}
.modal-body {
+ position: relative;
max-height: 400px;
padding: 15px;
overflow-y: auto;
@@ -4839,6 +5201,10 @@ input[type="submit"].btn.btn-mini {
margin-left: -1px;
}
+.modal-footer .btn-block + .btn-block {
+ margin-left: 0;
+}
+
.tooltip {
position: absolute;
z-index: 1030;
@@ -4931,6 +5297,8 @@ input[type="submit"].btn.btn-mini {
display: none;
width: 236px;
padding: 1px;
+ text-align: left;
+ white-space: normal;
background-color: #ffffff;
border: 1px solid #ccc;
border: 1px solid rgba(0, 0, 0, 0.2);
@@ -4946,7 +5314,7 @@ input[type="submit"].btn.btn-mini {
}
.popover.top {
- margin-bottom: 10px;
+ margin-top: -10px;
}
.popover.right {
@@ -4958,7 +5326,7 @@ input[type="submit"].btn.btn-mini {
}
.popover.left {
- margin-right: 10px;
+ margin-left: -10px;
}
.popover-title {
@@ -4978,85 +5346,87 @@ input[type="submit"].btn.btn-mini {
padding: 9px 14px;
}
-.popover-content p,
-.popover-content ul,
-.popover-content ol {
- margin-bottom: 0;
-}
-
.popover .arrow,
.popover .arrow:after {
position: absolute;
- display: inline-block;
+ display: block;
width: 0;
height: 0;
border-color: transparent;
border-style: solid;
}
+.popover .arrow {
+ border-width: 11px;
+}
+
.popover .arrow:after {
- z-index: -1;
+ border-width: 10px;
content: "";
}
.popover.top .arrow {
- bottom: -10px;
+ bottom: -11px;
left: 50%;
- margin-left: -10px;
- border-top-color: #ffffff;
- border-width: 10px 10px 0;
+ margin-left: -11px;
+ border-top-color: #999;
+ border-top-color: rgba(0, 0, 0, 0.25);
+ border-bottom-width: 0;
}
.popover.top .arrow:after {
- bottom: -1px;
- left: -11px;
- border-top-color: rgba(0, 0, 0, 0.25);
- border-width: 11px 11px 0;
+ bottom: 1px;
+ margin-left: -10px;
+ border-top-color: #ffffff;
+ border-bottom-width: 0;
}
.popover.right .arrow {
top: 50%;
- left: -10px;
- margin-top: -10px;
- border-right-color: #ffffff;
- border-width: 10px 10px 10px 0;
+ left: -11px;
+ margin-top: -11px;
+ border-right-color: #999;
+ border-right-color: rgba(0, 0, 0, 0.25);
+ border-left-width: 0;
}
.popover.right .arrow:after {
- bottom: -11px;
- left: -1px;
- border-right-color: rgba(0, 0, 0, 0.25);
- border-width: 11px 11px 11px 0;
+ bottom: -10px;
+ left: 1px;
+ border-right-color: #ffffff;
+ border-left-width: 0;
}
.popover.bottom .arrow {
- top: -10px;
+ top: -11px;
left: 50%;
- margin-left: -10px;
- border-bottom-color: #ffffff;
- border-width: 0 10px 10px;
+ margin-left: -11px;
+ border-bottom-color: #999;
+ border-bottom-color: rgba(0, 0, 0, 0.25);
+ border-top-width: 0;
}
.popover.bottom .arrow:after {
- top: -1px;
- left: -11px;
- border-bottom-color: rgba(0, 0, 0, 0.25);
- border-width: 0 11px 11px;
+ top: 1px;
+ margin-left: -10px;
+ border-bottom-color: #ffffff;
+ border-top-width: 0;
}
.popover.left .arrow {
top: 50%;
- right: -10px;
- margin-top: -10px;
- border-left-color: #ffffff;
- border-width: 10px 0 10px 10px;
+ right: -11px;
+ margin-top: -11px;
+ border-left-color: #999;
+ border-left-color: rgba(0, 0, 0, 0.25);
+ border-right-width: 0;
}
.popover.left .arrow:after {
- right: -1px;
- bottom: -11px;
- border-left-color: rgba(0, 0, 0, 0.25);
- border-width: 11px 0 11px 11px;
+ right: 1px;
+ bottom: -10px;
+ border-left-color: #ffffff;
+ border-right-width: 0;
}
.thumbnails {
@@ -5122,8 +5492,47 @@ a.thumbnail:hover {
color: #555555;
}
+.media,
+.media-body {
+ overflow: hidden;
+ *overflow: visible;
+ zoom: 1;
+}
+
+.media,
+.media .media {
+ margin-top: 15px;
+}
+
+.media:first-child {
+ margin-top: 0;
+}
+
+.media-object {
+ display: block;
+}
+
+.media-heading {
+ margin: 0 0 5px;
+}
+
+.media .pull-left {
+ margin-right: 10px;
+}
+
+.media .pull-right {
+ margin-left: 10px;
+}
+
+.media-list {
+ margin-left: 0;
+ list-style: none;
+}
+
.label,
.badge {
+ display: inline-block;
+ padding: 2px 4px;
font-size: 11.844px;
font-weight: bold;
line-height: 14px;
@@ -5135,19 +5544,24 @@ a.thumbnail:hover {
}
.label {
- padding: 1px 4px 2px;
-webkit-border-radius: 3px;
-moz-border-radius: 3px;
border-radius: 3px;
}
.badge {
- padding: 1px 9px 2px;
+ padding-right: 9px;
+ padding-left: 9px;
-webkit-border-radius: 9px;
-moz-border-radius: 9px;
border-radius: 9px;
}
+.label:empty,
+.badge:empty {
+ display: none;
+}
+
a.label:hover,
a.badge:hover {
color: #ffffff;
@@ -5275,7 +5689,7 @@ a.badge:hover {
-webkit-border-radius: 4px;
-moz-border-radius: 4px;
border-radius: 4px;
- filter: progid:dximagetransform.microsoft.gradient(startColorstr='#fff5f5f5', endColorstr='#fff9f9f9', GradientType=0);
+ filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5', endColorstr='#fff9f9f9', GradientType=0);
-webkit-box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1);
-moz-box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1);
box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1);
@@ -5296,7 +5710,7 @@ a.badge:hover {
background-image: -o-linear-gradient(top, #149bdf, #0480be);
background-image: linear-gradient(to bottom, #149bdf, #0480be);
background-repeat: repeat-x;
- filter: progid:dximagetransform.microsoft.gradient(startColorstr='#ff149bdf', endColorstr='#ff0480be', GradientType=0);
+ filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff149bdf', endColorstr='#ff0480be', GradientType=0);
-webkit-box-shadow: inset 0 -1px 0 rgba(0, 0, 0, 0.15);
-moz-box-shadow: inset 0 -1px 0 rgba(0, 0, 0, 0.15);
box-shadow: inset 0 -1px 0 rgba(0, 0, 0, 0.15);
@@ -5345,7 +5759,7 @@ a.badge:hover {
background-image: -o-linear-gradient(top, #ee5f5b, #c43c35);
background-image: linear-gradient(to bottom, #ee5f5b, #c43c35);
background-repeat: repeat-x;
- filter: progid:dximagetransform.microsoft.gradient(startColorstr='#ffee5f5b', endColorstr='#ffc43c35', GradientType=0);
+ filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffee5f5b', endColorstr='#ffc43c35', GradientType=0);
}
.progress-danger.progress-striped .bar,
@@ -5367,7 +5781,7 @@ a.badge:hover {
background-image: -o-linear-gradient(top, #62c462, #57a957);
background-image: linear-gradient(to bottom, #62c462, #57a957);
background-repeat: repeat-x;
- filter: progid:dximagetransform.microsoft.gradient(startColorstr='#ff62c462', endColorstr='#ff57a957', GradientType=0);
+ filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff62c462', endColorstr='#ff57a957', GradientType=0);
}
.progress-success.progress-striped .bar,
@@ -5389,7 +5803,7 @@ a.badge:hover {
background-image: -o-linear-gradient(top, #5bc0de, #339bb9);
background-image: linear-gradient(to bottom, #5bc0de, #339bb9);
background-repeat: repeat-x;
- filter: progid:dximagetransform.microsoft.gradient(startColorstr='#ff5bc0de', endColorstr='#ff339bb9', GradientType=0);
+ filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de', endColorstr='#ff339bb9', GradientType=0);
}
.progress-info.progress-striped .bar,
@@ -5411,7 +5825,7 @@ a.badge:hover {
background-image: -o-linear-gradient(top, #fbb450, #f89406);
background-image: linear-gradient(to bottom, #fbb450, #f89406);
background-repeat: repeat-x;
- filter: progid:dximagetransform.microsoft.gradient(startColorstr='#fffbb450', endColorstr='#fff89406', GradientType=0);
+ filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffbb450', endColorstr='#fff89406', GradientType=0);
}
.progress-warning.progress-striped .bar,
@@ -5466,7 +5880,7 @@ a.badge:hover {
overflow: hidden;
}
-.carousel .item {
+.carousel-inner > .item {
position: relative;
display: none;
-webkit-transition: 0.6s ease-in-out left;
@@ -5475,46 +5889,46 @@ a.badge:hover {
transition: 0.6s ease-in-out left;
}
-.carousel .item > img {
+.carousel-inner > .item > img {
display: block;
line-height: 1;
}
-.carousel .active,
-.carousel .next,
-.carousel .prev {
+.carousel-inner > .active,
+.carousel-inner > .next,
+.carousel-inner > .prev {
display: block;
}
-.carousel .active {
+.carousel-inner > .active {
left: 0;
}
-.carousel .next,
-.carousel .prev {
+.carousel-inner > .next,
+.carousel-inner > .prev {
position: absolute;
top: 0;
width: 100%;
}
-.carousel .next {
+.carousel-inner > .next {
left: 100%;
}
-.carousel .prev {
+.carousel-inner > .prev {
left: -100%;
}
-.carousel .next.left,
-.carousel .prev.right {
+.carousel-inner > .next.left,
+.carousel-inner > .prev.right {
left: 0;
}
-.carousel .active.left {
+.carousel-inner > .active.left {
left: -100%;
}
-.carousel .active.right {
+.carousel-inner > .active.right {
left: 100%;
}
@@ -5578,6 +5992,10 @@ a.badge:hover {
.hero-unit {
padding: 60px;
margin-bottom: 30px;
+ font-size: 18px;
+ font-weight: 200;
+ line-height: 30px;
+ color: inherit;
background-color: #eeeeee;
-webkit-border-radius: 6px;
-moz-border-radius: 6px;
@@ -5592,11 +6010,8 @@ a.badge:hover {
color: inherit;
}
-.hero-unit p {
- font-size: 18px;
- font-weight: 200;
+.hero-unit li {
line-height: 30px;
- color: inherit;
}
.pull-right {
diff --git a/nikola/data/themes/default/assets/css/slides.css b/nikola/data/themes/default/assets/css/slides.css
new file mode 100644
index 0000000..272c83e
--- /dev/null
+++ b/nikola/data/themes/default/assets/css/slides.css
@@ -0,0 +1,11 @@
+.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/js/bootstrap.js b/nikola/data/themes/default/assets/js/bootstrap.js
index 7f303eb..6c15a58 100644
--- a/nikola/data/themes/default/assets/js/bootstrap.js
+++ b/nikola/data/themes/default/assets/js/bootstrap.js
@@ -1,5 +1,5 @@
/* ===================================================
- * bootstrap-transition.js v2.1.0
+ * bootstrap-transition.js v2.2.2
* http://twitter.github.com/bootstrap/javascript.html#transitions
* ===================================================
* Copyright 2012 Twitter, Inc.
@@ -20,13 +20,13 @@
!function ($) {
- $(function () {
+ "use strict"; // jshint ;_;
- "use strict"; // jshint ;_;
+ /* CSS TRANSITION SUPPORT (http://www.modernizr.com/)
+ * ======================================================= */
- /* CSS TRANSITION SUPPORT (http://www.modernizr.com/)
- * ======================================================= */
+ $(function () {
$.support.transition = (function () {
@@ -58,7 +58,7 @@
})
}(window.jQuery);/* ==========================================================
- * bootstrap-alert.js v2.1.0
+ * bootstrap-alert.js v2.2.2
* http://twitter.github.com/bootstrap/javascript.html#alerts
* ==========================================================
* Copyright 2012 Twitter, Inc.
@@ -127,6 +127,8 @@
/* ALERT PLUGIN DEFINITION
* ======================= */
+ var old = $.fn.alert
+
$.fn.alert = function (option) {
return this.each(function () {
var $this = $(this)
@@ -139,15 +141,22 @@
$.fn.alert.Constructor = Alert
+ /* ALERT NO CONFLICT
+ * ================= */
+
+ $.fn.alert.noConflict = function () {
+ $.fn.alert = old
+ return this
+ }
+
+
/* ALERT DATA-API
* ============== */
- $(function () {
- $('body').on('click.alert.data-api', dismiss, Alert.prototype.close)
- })
+ $(document).on('click.alert.data-api', dismiss, Alert.prototype.close)
}(window.jQuery);/* ============================================================
- * bootstrap-button.js v2.1.0
+ * bootstrap-button.js v2.2.2
* http://twitter.github.com/bootstrap/javascript.html#buttons
* ============================================================
* Copyright 2012 Twitter, Inc.
@@ -199,7 +208,7 @@
}
Button.prototype.toggle = function () {
- var $parent = this.$element.parent('[data-toggle="buttons-radio"]')
+ var $parent = this.$element.closest('[data-toggle="buttons-radio"]')
$parent && $parent
.find('.active')
@@ -212,6 +221,8 @@
/* BUTTON PLUGIN DEFINITION
* ======================== */
+ var old = $.fn.button
+
$.fn.button = function (option) {
return this.each(function () {
var $this = $(this)
@@ -230,19 +241,26 @@
$.fn.button.Constructor = Button
+ /* BUTTON NO CONFLICT
+ * ================== */
+
+ $.fn.button.noConflict = function () {
+ $.fn.button = old
+ return this
+ }
+
+
/* BUTTON DATA-API
* =============== */
- $(function () {
- $('body').on('click.button.data-api', '[data-toggle^=button]', function ( e ) {
- var $btn = $(e.target)
- if (!$btn.hasClass('btn')) $btn = $btn.closest('.btn')
- $btn.button('toggle')
- })
+ $(document).on('click.button.data-api', '[data-toggle^=button]', function (e) {
+ var $btn = $(e.target)
+ if (!$btn.hasClass('btn')) $btn = $btn.closest('.btn')
+ $btn.button('toggle')
})
}(window.jQuery);/* ==========================================================
- * bootstrap-carousel.js v2.1.0
+ * bootstrap-carousel.js v2.2.2
* http://twitter.github.com/bootstrap/javascript.html#carousel
* ==========================================================
* Copyright 2012 Twitter, Inc.
@@ -272,7 +290,6 @@
var Carousel = function (element, options) {
this.$element = $(element)
this.options = options
- this.options.slide && this.slide(this.options.slide)
this.options.pause == 'hover' && this.$element
.on('mouseenter', $.proxy(this.pause, this))
.on('mouseleave', $.proxy(this.cycle, this))
@@ -337,9 +354,7 @@
, direction = type == 'next' ? 'left' : 'right'
, fallback = type == 'next' ? 'first' : 'last'
, that = this
- , e = $.Event('slide', {
- relatedTarget: $next[0]
- })
+ , e
this.sliding = true
@@ -347,6 +362,10 @@
$next = $next.length ? $next : this.$element.find('.item')[fallback]()
+ e = $.Event('slide', {
+ relatedTarget: $next[0]
+ })
+
if ($next.hasClass('active')) return
if ($.support.transition && this.$element.hasClass('slide')) {
@@ -382,6 +401,8 @@
/* CAROUSEL PLUGIN DEFINITION
* ========================== */
+ var old = $.fn.carousel
+
$.fn.carousel = function (option) {
return this.each(function () {
var $this = $(this)
@@ -403,21 +424,27 @@
$.fn.carousel.Constructor = Carousel
+ /* CAROUSEL NO CONFLICT
+ * ==================== */
+
+ $.fn.carousel.noConflict = function () {
+ $.fn.carousel = old
+ return this
+ }
+
/* CAROUSEL DATA-API
* ================= */
- $(function () {
- $('body').on('click.carousel.data-api', '[data-slide]', function ( e ) {
- var $this = $(this), href
- , $target = $($this.attr('data-target') || (href = $this.attr('href')) && href.replace(/.*(?=#[^\s]+$)/, '')) //strip for ie7
- , options = !$target.data('modal') && $.extend({}, $target.data(), $this.data())
- $target.carousel(options)
- e.preventDefault()
- })
+ $(document).on('click.carousel.data-api', '[data-slide]', function (e) {
+ var $this = $(this), href
+ , $target = $($this.attr('data-target') || (href = $this.attr('href')) && href.replace(/.*(?=#[^\s]+$)/, '')) //strip for ie7
+ , options = $.extend({}, $target.data(), $this.data())
+ $target.carousel(options)
+ e.preventDefault()
})
}(window.jQuery);/* =============================================================
- * bootstrap-collapse.js v2.1.0
+ * bootstrap-collapse.js v2.2.2
* http://twitter.github.com/bootstrap/javascript.html#collapse
* =============================================================
* Copyright 2012 Twitter, Inc.
@@ -538,8 +565,10 @@
}
- /* COLLAPSIBLE PLUGIN DEFINITION
- * ============================== */
+ /* COLLAPSE PLUGIN DEFINITION
+ * ========================== */
+
+ var old = $.fn.collapse
$.fn.collapse = function (option) {
return this.each(function () {
@@ -558,23 +587,30 @@
$.fn.collapse.Constructor = Collapse
- /* COLLAPSIBLE DATA-API
+ /* COLLAPSE NO CONFLICT
* ==================== */
- $(function () {
- $('body').on('click.collapse.data-api', '[data-toggle=collapse]', function (e) {
- var $this = $(this), href
- , target = $this.attr('data-target')
- || e.preventDefault()
- || (href = $this.attr('href')) && href.replace(/.*(?=#[^\s]+$)/, '') //strip for ie7
- , option = $(target).data('collapse') ? 'toggle' : $this.data()
- $this[$(target).hasClass('in') ? 'addClass' : 'removeClass']('collapsed')
- $(target).collapse(option)
- })
+ $.fn.collapse.noConflict = function () {
+ $.fn.collapse = old
+ return this
+ }
+
+
+ /* COLLAPSE DATA-API
+ * ================= */
+
+ $(document).on('click.collapse.data-api', '[data-toggle=collapse]', function (e) {
+ var $this = $(this), href
+ , target = $this.attr('data-target')
+ || e.preventDefault()
+ || (href = $this.attr('href')) && href.replace(/.*(?=#[^\s]+$)/, '') //strip for ie7
+ , option = $(target).data('collapse') ? 'toggle' : $this.data()
+ $this[$(target).hasClass('in') ? 'addClass' : 'removeClass']('collapsed')
+ $(target).collapse(option)
})
}(window.jQuery);/* ============================================================
- * bootstrap-dropdown.js v2.1.0
+ * bootstrap-dropdown.js v2.2.2
* http://twitter.github.com/bootstrap/javascript.html#dropdowns
* ============================================================
* Copyright 2012 Twitter, Inc.
@@ -628,9 +664,10 @@
if (!isActive) {
$parent.toggleClass('open')
- $this.focus()
}
+ $this.focus()
+
return false
}
@@ -657,7 +694,7 @@
if (!isActive || (isActive && e.keyCode == 27)) return $this.click()
- $items = $('[role=menu] li:not(.divider) a', $parent)
+ $items = $('[role=menu] li:not(.divider):visible a', $parent)
if (!$items.length) return
@@ -675,8 +712,9 @@
}
function clearMenus() {
- getParent($(toggle))
- .removeClass('open')
+ $(toggle).each(function () {
+ getParent($(this)).removeClass('open')
+ })
}
function getParent($this) {
@@ -685,7 +723,7 @@
if (!selector) {
selector = $this.attr('href')
- selector = selector && selector.replace(/.*(?=#[^\s]*$)/, '') //strip for ie7
+ selector = selector && /#/.test(selector) && selector.replace(/.*(?=#[^\s]*$)/, '') //strip for ie7
}
$parent = $(selector)
@@ -698,6 +736,8 @@
/* DROPDOWN PLUGIN DEFINITION
* ========================== */
+ var old = $.fn.dropdown
+
$.fn.dropdown = function (option) {
return this.each(function () {
var $this = $(this)
@@ -710,20 +750,27 @@
$.fn.dropdown.Constructor = Dropdown
+ /* DROPDOWN NO CONFLICT
+ * ==================== */
+
+ $.fn.dropdown.noConflict = function () {
+ $.fn.dropdown = old
+ return this
+ }
+
+
/* APPLY TO STANDARD DROPDOWN ELEMENTS
* =================================== */
- $(function () {
- $('html')
- .on('click.dropdown.data-api touchstart.dropdown.data-api', clearMenus)
- $('body')
- .on('click.dropdown touchstart.dropdown.data-api', '.dropdown', function (e) { e.stopPropagation() })
- .on('click.dropdown.data-api touchstart.dropdown.data-api' , toggle, Dropdown.prototype.toggle)
- .on('keydown.dropdown.data-api touchstart.dropdown.data-api', toggle + ', [role=menu]' , Dropdown.prototype.keydown)
- })
+ $(document)
+ .on('click.dropdown.data-api touchstart.dropdown.data-api', clearMenus)
+ .on('click.dropdown touchstart.dropdown.data-api', '.dropdown form', function (e) { e.stopPropagation() })
+ .on('touchstart.dropdown.data-api', '.dropdown-menu', function (e) { e.stopPropagation() })
+ .on('click.dropdown.data-api touchstart.dropdown.data-api' , toggle, Dropdown.prototype.toggle)
+ .on('keydown.dropdown.data-api touchstart.dropdown.data-api', toggle + ', [role=menu]' , Dropdown.prototype.keydown)
}(window.jQuery);/* =========================================================
- * bootstrap-modal.js v2.1.0
+ * bootstrap-modal.js v2.2.2
* http://twitter.github.com/bootstrap/javascript.html#modals
* =========================================================
* Copyright 2012 Twitter, Inc.
@@ -773,8 +820,6 @@
if (this.isShown || e.isDefaultPrevented()) return
- $('body').addClass('modal-open')
-
this.isShown = true
this.escape()
@@ -796,13 +841,12 @@
that.$element
.addClass('in')
.attr('aria-hidden', false)
- .focus()
that.enforceFocus()
transition ?
- that.$element.one($.support.transition.end, function () { that.$element.trigger('shown') }) :
- that.$element.trigger('shown')
+ that.$element.one($.support.transition.end, function () { that.$element.focus().trigger('shown') }) :
+ that.$element.focus().trigger('shown')
})
}
@@ -820,8 +864,6 @@
this.isShown = false
- $('body').removeClass('modal-open')
-
this.escape()
$(document).off('focusin.modal')
@@ -891,9 +933,11 @@
this.$backdrop = $('<div class="modal-backdrop ' + animate + '" />')
.appendTo(document.body)
- if (this.options.backdrop != 'static') {
- this.$backdrop.click($.proxy(this.hide, this))
- }
+ this.$backdrop.click(
+ this.options.backdrop == 'static' ?
+ $.proxy(this.$element[0].focus, this.$element[0])
+ : $.proxy(this.hide, this)
+ )
if (doAnimate) this.$backdrop[0].offsetWidth // force reflow
@@ -920,6 +964,8 @@
/* MODAL PLUGIN DEFINITION
* ======================= */
+ var old = $.fn.modal
+
$.fn.modal = function (option) {
return this.each(function () {
var $this = $(this)
@@ -940,28 +986,36 @@
$.fn.modal.Constructor = Modal
+ /* MODAL NO CONFLICT
+ * ================= */
+
+ $.fn.modal.noConflict = function () {
+ $.fn.modal = old
+ return this
+ }
+
+
/* MODAL DATA-API
* ============== */
- $(function () {
- $('body').on('click.modal.data-api', '[data-toggle="modal"]', function ( e ) {
- var $this = $(this)
- , href = $this.attr('href')
- , $target = $($this.attr('data-target') || (href && href.replace(/.*(?=#[^\s]+$)/, ''))) //strip for ie7
- , option = $target.data('modal') ? 'toggle' : $.extend({ remote: !/#/.test(href) && href }, $target.data(), $this.data())
+ $(document).on('click.modal.data-api', '[data-toggle="modal"]', function (e) {
+ var $this = $(this)
+ , href = $this.attr('href')
+ , $target = $($this.attr('data-target') || (href && href.replace(/.*(?=#[^\s]+$)/, ''))) //strip for ie7
+ , option = $target.data('modal') ? 'toggle' : $.extend({ remote:!/#/.test(href) && href }, $target.data(), $this.data())
- e.preventDefault()
+ e.preventDefault()
- $target
- .modal(option)
- .one('hide', function () {
- $this.focus()
- })
- })
+ $target
+ .modal(option)
+ .one('hide', function () {
+ $this.focus()
+ })
})
-}(window.jQuery);/* ===========================================================
- * bootstrap-tooltip.js v2.1.0
+}(window.jQuery);
+/* ===========================================================
+ * bootstrap-tooltip.js v2.2.2
* http://twitter.github.com/bootstrap/javascript.html#tooltips
* Inspired by the original jQuery.tipsy by Jason Frame
* ===========================================================
@@ -1081,9 +1135,9 @@
inside = /in/.test(placement)
$tip
- .remove()
+ .detach()
.css({ top: 0, left: 0, display: 'block' })
- .appendTo(inside ? this.$element : document.body)
+ .insertAfter(this.$element)
pos = this.getPosition(inside)
@@ -1106,7 +1160,7 @@
}
$tip
- .css(tp)
+ .offset(tp)
.addClass(placement)
.addClass('in')
}
@@ -1128,18 +1182,18 @@
function removeWithAnimation() {
var timeout = setTimeout(function () {
- $tip.off($.support.transition.end).remove()
+ $tip.off($.support.transition.end).detach()
}, 500)
$tip.one($.support.transition.end, function () {
clearTimeout(timeout)
- $tip.remove()
+ $tip.detach()
})
}
$.support.transition && this.$tip.hasClass('fade') ?
removeWithAnimation() :
- $tip.remove()
+ $tip.detach()
return this
}
@@ -1197,8 +1251,9 @@
this.enabled = !this.enabled
}
- , toggle: function () {
- this[this.tip().hasClass('in') ? 'hide' : 'show']()
+ , toggle: function (e) {
+ var self = $(e.currentTarget)[this.type](this._options).data(this.type)
+ self[self.tip().hasClass('in') ? 'hide' : 'show']()
}
, destroy: function () {
@@ -1211,6 +1266,8 @@
/* TOOLTIP PLUGIN DEFINITION
* ========================= */
+ var old = $.fn.tooltip
+
$.fn.tooltip = function ( option ) {
return this.each(function () {
var $this = $(this)
@@ -1231,12 +1288,20 @@
, trigger: 'hover'
, title: ''
, delay: 0
- , html: true
+ , html: false
}
-}(window.jQuery);
-/* ===========================================================
- * bootstrap-popover.js v2.1.0
+
+ /* TOOLTIP NO CONFLICT
+ * =================== */
+
+ $.fn.tooltip.noConflict = function () {
+ $.fn.tooltip = old
+ return this
+ }
+
+}(window.jQuery);/* ===========================================================
+ * bootstrap-popover.js v2.2.2
* http://twitter.github.com/bootstrap/javascript.html#popovers
* ===========================================================
* Copyright 2012 Twitter, Inc.
@@ -1281,7 +1346,7 @@
, content = this.getContent()
$tip.find('.popover-title')[this.options.html ? 'html' : 'text'](title)
- $tip.find('.popover-content > *')[this.options.html ? 'html' : 'text'](content)
+ $tip.find('.popover-content')[this.options.html ? 'html' : 'text'](content)
$tip.removeClass('fade top bottom left right in')
}
@@ -1318,6 +1383,8 @@
/* POPOVER PLUGIN DEFINITION
* ======================= */
+ var old = $.fn.popover
+
$.fn.popover = function (option) {
return this.each(function () {
var $this = $(this)
@@ -1334,11 +1401,20 @@
placement: 'right'
, trigger: 'click'
, content: ''
- , template: '<div class="popover"><div class="arrow"></div><div class="popover-inner"><h3 class="popover-title"></h3><div class="popover-content"><p></p></div></div></div>'
+ , template: '<div class="popover"><div class="arrow"></div><div class="popover-inner"><h3 class="popover-title"></h3><div class="popover-content"></div></div></div>'
})
+
+ /* POPOVER NO CONFLICT
+ * =================== */
+
+ $.fn.popover.noConflict = function () {
+ $.fn.popover = old
+ return this
+ }
+
}(window.jQuery);/* =============================================================
- * bootstrap-scrollspy.js v2.1.0
+ * bootstrap-scrollspy.js v2.2.2
* http://twitter.github.com/bootstrap/javascript.html#scrollspy
* =============================================================
* Copyright 2012 Twitter, Inc.
@@ -1398,7 +1474,7 @@
, $href = /^#\w/.test(href) && $(href)
return ( $href
&& $href.length
- && [[ $href.position().top, href ]] ) || null
+ && [[ $href.position().top + self.$scrollElement.scrollTop(), href ]] ) || null
})
.sort(function (a, b) { return a[0] - b[0] })
.each(function () {
@@ -1460,6 +1536,8 @@
/* SCROLLSPY PLUGIN DEFINITION
* =========================== */
+ var old = $.fn.scrollspy
+
$.fn.scrollspy = function (option) {
return this.each(function () {
var $this = $(this)
@@ -1477,6 +1555,15 @@
}
+ /* SCROLLSPY NO CONFLICT
+ * ===================== */
+
+ $.fn.scrollspy.noConflict = function () {
+ $.fn.scrollspy = old
+ return this
+ }
+
+
/* SCROLLSPY DATA-API
* ================== */
@@ -1488,7 +1575,7 @@
})
}(window.jQuery);/* ========================================================
- * bootstrap-tab.js v2.1.0
+ * bootstrap-tab.js v2.2.2
* http://twitter.github.com/bootstrap/javascript.html#tabs
* ========================================================
* Copyright 2012 Twitter, Inc.
@@ -1538,7 +1625,7 @@
if ( $this.parent('li').hasClass('active') ) return
- previous = $ul.find('.active a').last()[0]
+ previous = $ul.find('.active:last a')[0]
e = $.Event('show', {
relatedTarget: previous
@@ -1599,6 +1686,8 @@
/* TAB PLUGIN DEFINITION
* ===================== */
+ var old = $.fn.tab
+
$.fn.tab = function ( option ) {
return this.each(function () {
var $this = $(this)
@@ -1611,18 +1700,25 @@
$.fn.tab.Constructor = Tab
+ /* TAB NO CONFLICT
+ * =============== */
+
+ $.fn.tab.noConflict = function () {
+ $.fn.tab = old
+ return this
+ }
+
+
/* TAB DATA-API
* ============ */
- $(function () {
- $('body').on('click.tab.data-api', '[data-toggle="tab"], [data-toggle="pill"]', function (e) {
- e.preventDefault()
- $(this).tab('show')
- })
+ $(document).on('click.tab.data-api', '[data-toggle="tab"], [data-toggle="pill"]', function (e) {
+ e.preventDefault()
+ $(this).tab('show')
})
}(window.jQuery);/* =============================================================
- * bootstrap-typeahead.js v2.1.0
+ * bootstrap-typeahead.js v2.2.2
* http://twitter.github.com/bootstrap/javascript.html#typeahead
* =============================================================
* Copyright 2012 Twitter, Inc.
@@ -1656,8 +1752,8 @@
this.sorter = this.options.sorter || this.sorter
this.highlighter = this.options.highlighter || this.highlighter
this.updater = this.options.updater || this.updater
- this.$menu = $(this.options.menu).appendTo('body')
this.source = this.options.source
+ this.$menu = $(this.options.menu)
this.shown = false
this.listen()
}
@@ -1679,16 +1775,18 @@
}
, show: function () {
- var pos = $.extend({}, this.$element.offset(), {
+ var pos = $.extend({}, this.$element.position(), {
height: this.$element[0].offsetHeight
})
- this.$menu.css({
- top: pos.top + pos.height
- , left: pos.left
- })
+ this.$menu
+ .insertAfter(this.$element)
+ .css({
+ top: pos.top + pos.height
+ , left: pos.left
+ })
+ .show()
- this.$menu.show()
this.shown = true
return this
}
@@ -1797,7 +1895,7 @@
.on('keypress', $.proxy(this.keypress, this))
.on('keyup', $.proxy(this.keyup, this))
- if ($.browser.webkit || $.browser.msie) {
+ if (this.eventSupported('keydown')) {
this.$element.on('keydown', $.proxy(this.keydown, this))
}
@@ -1806,6 +1904,15 @@
.on('mouseenter', 'li', $.proxy(this.mouseenter, this))
}
+ , eventSupported: function(eventName) {
+ var isSupported = eventName in this.$element
+ if (!isSupported) {
+ this.$element.setAttribute(eventName, 'return;')
+ isSupported = typeof this.$element[eventName] === 'function'
+ }
+ return isSupported
+ }
+
, move: function (e) {
if (!this.shown) return
@@ -1831,7 +1938,7 @@
}
, keydown: function (e) {
- this.suppressKeyPressRepeat = !~$.inArray(e.keyCode, [40,38,9,13,27])
+ this.suppressKeyPressRepeat = ~$.inArray(e.keyCode, [40,38,9,13,27])
this.move(e)
}
@@ -1844,6 +1951,9 @@
switch(e.keyCode) {
case 40: // down arrow
case 38: // up arrow
+ case 16: // shift
+ case 17: // ctrl
+ case 18: // alt
break
case 9: // tab
@@ -1887,6 +1997,8 @@
/* TYPEAHEAD PLUGIN DEFINITION
* =========================== */
+ var old = $.fn.typeahead
+
$.fn.typeahead = function (option) {
return this.each(function () {
var $this = $(this)
@@ -1908,21 +2020,28 @@
$.fn.typeahead.Constructor = Typeahead
- /* TYPEAHEAD DATA-API
+ /* TYPEAHEAD NO CONFLICT
+ * =================== */
+
+ $.fn.typeahead.noConflict = function () {
+ $.fn.typeahead = old
+ return this
+ }
+
+
+ /* TYPEAHEAD DATA-API
* ================== */
- $(function () {
- $('body').on('focus.typeahead.data-api', '[data-provide="typeahead"]', function (e) {
- var $this = $(this)
- if ($this.data('typeahead')) return
- e.preventDefault()
- $this.typeahead($this.data())
- })
+ $(document).on('focus.typeahead.data-api', '[data-provide="typeahead"]', function (e) {
+ var $this = $(this)
+ if ($this.data('typeahead')) return
+ e.preventDefault()
+ $this.typeahead($this.data())
})
}(window.jQuery);
/* ==========================================================
- * bootstrap-affix.js v2.1.0
+ * bootstrap-affix.js v2.2.2
* http://twitter.github.com/bootstrap/javascript.html#affix
* ==========================================================
* Copyright 2012 Twitter, Inc.
@@ -1951,7 +2070,9 @@
var Affix = function (element, options) {
this.options = $.extend({}, $.fn.affix.defaults, options)
- this.$window = $(window).on('scroll.affix.data-api', $.proxy(this.checkPosition, this))
+ this.$window = $(window)
+ .on('scroll.affix.data-api', $.proxy(this.checkPosition, this))
+ .on('click.affix.data-api', $.proxy(function () { setTimeout($.proxy(this.checkPosition, this), 1) }, this))
this.$element = $(element)
this.checkPosition()
}
@@ -1989,6 +2110,8 @@
/* AFFIX PLUGIN DEFINITION
* ======================= */
+ var old = $.fn.affix
+
$.fn.affix = function (option) {
return this.each(function () {
var $this = $(this)
@@ -2006,6 +2129,15 @@
}
+ /* AFFIX NO CONFLICT
+ * ================= */
+
+ $.fn.affix.noConflict = function () {
+ $.fn.affix = old
+ return this
+ }
+
+
/* AFFIX DATA-API
* ============== */
diff --git a/nikola/data/themes/default/assets/js/slides.jquery.js b/nikola/data/themes/default/assets/js/slides.jquery.js
new file mode 100755
index 0000000..f2e09c8
--- /dev/null
+++ b/nikola/data/themes/default/assets/js/slides.jquery.js
@@ -0,0 +1,555 @@
+/*
+* 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('<div class="slides_control"/>');
+
+ 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('<a href="#" class="'+ option.prev +'">Prev</a>');
+ $('.' + option.prev, elem).after('<a href="#" class="'+ option.next +'">Next</a>');
+ }
+
+ // 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('<ul class='+ option.paginationClass +'></ul>');
+ } else {
+ elem.append('<ul class='+ option.paginationClass +'></ul>');
+ }
+ // for each slide create a list item and link
+ control.children().each(function(){
+ $('.' + option.paginationClass, elem).append('<li><a href="#'+ number +'">'+ (number+1) +'</a></li>');
+ 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<childCount;i++) { indices[indices.length] = i; }
+ indices = indices.sort(randomizeOrder);
+ $.each(indices,function(j,k) {
+ var $child = $children.eq(k);
+ var $clone = $child.clone(true);
+ $clone.show().appendTo($this);
+ if (callback !== undefined) {
+ callback($child, $clone);
+ }
+ $child.remove();
+ });
+ }
+ }));
+ };
+})(jQuery); \ No newline at end of file
diff --git a/nikola/data/themes/default/bundles b/nikola/data/themes/default/bundles
index 9d77983..ea9fba9 100644
--- a/nikola/data/themes/default/bundles
+++ b/nikola/data/themes/default/bundles
@@ -1,2 +1,2 @@
-assets/css/all.css=bootstrap.css,bootstrap-responsive.css,rst.css,code.css,colorbox.css,theme.css,custom.css
-assets/js/all.js=jquery-1.7.2.min.js,jquery.colorbox-min.js
+assets/css/all.css=bootstrap.css,bootstrap-responsive.css,rst.css,code.css,colorbox.css,slides.js,theme.css,custom.css
+assets/js/all.js=jquery-1.7.2.min.js,jquery.colorbox-min.js,slides.min.jquery.js
diff --git a/nikola/data/themes/default/messages/de.py b/nikola/data/themes/default/messages/de.py
deleted file mode 100644
index 6e16a21..0000000
--- a/nikola/data/themes/default/messages/de.py
+++ /dev/null
@@ -1,21 +0,0 @@
-# -*- encoding:utf-8 -*-
-
-MESSAGES = {
- u"LANGUAGE": u"Deutsch",
- u"Posts for year %s": u"Eintr&auml;ge aus dem Jahr %s",
- u"Archive": u"Archiv",
- u"Posts about %s": u"Eintr&auml;ge &uuml;ber %s",
- u"Tags": u"Tags",
- u"Also available in": u"Auch verf&uuml;gbar in",
- u"More posts about": u"Weitere Eintr&auml;ge &uuml;ber",
- u"Posted": u"Ver&ouml;ffentlicht",
- u"Original site": u"Original-Seite",
- u"Read in English": u"Auf Deutsch lesen",
- u"Older posts": u"&Auml;ltere Eintr&auml;ge",
- u"Newer posts": u"Neuere Eintr&auml;ge",
- u"Previous post": u"Vorheriger Eintrag",
- u"Next post": u"N&auml;chster Eintrag",
- u"Source": u"Source",
- u"Read more": u"Weiterlesen",
- u"old posts page %d": u'Vorherige Eintr&auml;ge %d'
-}
diff --git a/nikola/data/themes/default/messages/en.py b/nikola/data/themes/default/messages/en.py
deleted file mode 100644
index 95b1210..0000000
--- a/nikola/data/themes/default/messages/en.py
+++ /dev/null
@@ -1,25 +0,0 @@
-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"Newer posts",
- u"Older posts",
- u"Previous post",
- u"Next post",
- u"old posts page %d",
- u"Read more",
- u"Source",
-]
-
-# In english things are not translated
-msg_dict = {}
-for msg in MESSAGES:
- msg_dict[msg] = msg
-MESSAGES = msg_dict
-MESSAGES[u"LANGUAGE"] = "English"
diff --git a/nikola/data/themes/default/messages/es.py b/nikola/data/themes/default/messages/es.py
deleted file mode 100644
index 78de676..0000000
--- a/nikola/data/themes/default/messages/es.py
+++ /dev/null
@@ -1,21 +0,0 @@
-# -*- encoding:utf-8 -*-
-
-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"Older posts": u"Posts anteriores",
- u"Newer posts": u"Posts posteriores",
- u"Previous post": u"Post anterior",
- u"Next post": u"Siguiente post",
- u"old posts page %d": u"posts antiguos página %d",
- u"Read more": u"Leer más",
- u"Source": u"Código",
-}
diff --git a/nikola/data/themes/default/messages/fr.py b/nikola/data/themes/default/messages/fr.py
deleted file mode 100644
index 5db1a1f..0000000
--- a/nikola/data/themes/default/messages/fr.py
+++ /dev/null
@@ -1,17 +0,0 @@
-# -*- encoding:utf-8 -*-
-
-MESSAGES = {
- u"LANGUAGE": u"Français",
- u"Posts for year %s": u"Billets de l'année %s",
- u"Archive": u"Archives",
- u"Posts about %s": u"Billets sur %s",
- u"Tags": u"Étiquettes",
- u"Also available in": u"Disponible aussi en",
- u"More posts about": u"Plus de billets sur",
- u"Posted": u"Publié",
- u"Original site": u"Site d'origine",
- u"Read in English": u"Lire en français",
- u"Newer posts": u"Billets récents",
- u"Older posts": u"Anciens billets",
- u"Source": u"Source",
-}
diff --git a/nikola/data/themes/default/messages/gr.py b/nikola/data/themes/default/messages/gr.py
deleted file mode 100644
index fa6bb32..0000000
--- a/nikola/data/themes/default/messages/gr.py
+++ /dev/null
@@ -1,20 +0,0 @@
-# -*- encoding:utf-8 -*-
-
-MESSAGES = {
- u"LANGUAGE": u"Ελληνικά",
- u"Posts for year %s": u"Αναρτήσεις για τη χρονιά %s",
- u"Archive": u"Αρχείο",
- u"Posts about %s": u"Αναρτήσεις για %s",
- u"Tags": u"Ετικέτες",
- u"Also available in": u"Διαθέσιμο και στο",
- u"More posts about": u"Περισσότερες αναρτήσεις για",
- u"Posted": u"Αναρτήθηκε",
- u"Original site": u"Ιστοσελίδα αρχικής ανάρτησης",
- u"Read in English": u"Διαβάστε στα Ελληνικά",
- u"Newer posts": u"Νεότερες αναρτήσεις",
- u"Older posts": u"Παλαιότερες αναρτήσεις",
- u"Previous post": u"Προηγούμενη ανάρτηση",
- u"Next post": u"Επόμενη ανάρτηση",
- u"old posts page %d": u"σελίδα παλαιότερων αναρτήσεων %d",
- u"Source": u"Source",
-}
diff --git a/nikola/data/themes/default/messages/it.py b/nikola/data/themes/default/messages/it.py
deleted file mode 100644
index 01a97d5..0000000
--- a/nikola/data/themes/default/messages/it.py
+++ /dev/null
@@ -1,20 +0,0 @@
-MESSAGES = {
- u"LANGUAGE": u"Italiano",
- u"Posts for year %s": u"Articoli per l'anno %s",
- u"Archive": u"Archivio",
- u"Posts about %s": u"Articoli su %s",
- u"Tags": u"Tags",
- u"Also available in": u"Anche disponibile in",
- u"More posts about": u"Altri articoli su",
- u"Posted": u"Pubblicato",
- u"Original site": u"Sito originale",
- u"Read in English": u"Leggi in italiano",
- u"Newer posts": u"Articoli recenti",
- u"Older posts": u"Articoli più vecchi",
- u"Older posts": u"Articoli vecchi",
- u"Previous post": u"Articolo precedente",
- u"Next post": u"Articolo successivo",
- u"old posts page %d": u"pagina dei vecchi articoli %d",
- u"Read more": u"Espandi",
- u"Source": u"Source",
-}
diff --git a/nikola/data/themes/default/messages/messages_de.py b/nikola/data/themes/default/messages/messages_de.py
new file mode 100644
index 0000000..cafbcbb
--- /dev/null
+++ b/nikola/data/themes/default/messages/messages_de.py
@@ -0,0 +1,22 @@
+# -*- encoding:utf-8 -*-
+from __future__ import unicode_literals
+
+MESSAGES = {
+ "LANGUAGE": "Deutsch",
+ "Posts for year %s": "Eintr&auml;ge aus dem Jahr %s",
+ "Archive": "Archiv",
+ "Posts about %s": "Eintr&auml;ge &uuml;ber %s",
+ "Tags": "Tags",
+ "Also available in": "Auch verf&uuml;gbar in",
+ "More posts about": "Weitere Eintr&auml;ge &uuml;ber",
+ "Posted": "Ver&ouml;ffentlicht",
+ "Original site": "Original-Seite",
+ "Read in English": "Auf Deutsch lesen",
+ "Older posts": "&Auml;ltere Eintr&auml;ge",
+ "Newer posts": "Neuere Eintr&auml;ge",
+ "Previous post": "Vorheriger Eintrag",
+ "Next post": "N&auml;chster Eintrag",
+ "Source": "Source",
+ "Read more": "Weiterlesen",
+ "old posts page %d": "Vorherige Eintr&auml;ge %d"
+}
diff --git a/nikola/data/themes/default/messages/messages_en.py b/nikola/data/themes/default/messages/messages_en.py
new file mode 100644
index 0000000..6d39047
--- /dev/null
+++ b/nikola/data/themes/default/messages/messages_en.py
@@ -0,0 +1,27 @@
+from __future__ import unicode_literals
+
+MESSAGES = [
+ "Posts for year %s",
+ "Archive",
+ "Posts about %s",
+ "Tags",
+ "Also available in",
+ "More posts about",
+ "Posted",
+ "Original site",
+ "Read in English",
+ "Newer posts",
+ "Older posts",
+ "Previous post",
+ "Next post",
+ "old posts page %d",
+ "Read more",
+ "Source",
+]
+
+# In english things are not translated
+msg_dict = {}
+for msg in MESSAGES:
+ msg_dict[msg] = msg
+MESSAGES = msg_dict
+MESSAGES["LANGUAGE"] = "English"
diff --git a/nikola/data/themes/default/messages/messages_es.py b/nikola/data/themes/default/messages/messages_es.py
new file mode 100644
index 0000000..f17f058
--- /dev/null
+++ b/nikola/data/themes/default/messages/messages_es.py
@@ -0,0 +1,22 @@
+# -*- encoding:utf-8 -*-
+from __future__ import unicode_literals
+
+MESSAGES = {
+ "LANGUAGE": "Español",
+ "Posts for year %s": "Posts del año %s",
+ "Archive": "Archivo",
+ "Posts about %s": "Posts sobre %s",
+ "Tags": "Tags",
+ "Also available in": "También disponible en",
+ "More posts about": "Más posts sobre",
+ "Posted": "Publicado",
+ "Original site": "Sitio original",
+ "Read in English": "Leer en español",
+ "Older posts": "Posts anteriores",
+ "Newer posts": "Posts posteriores",
+ "Previous post": "Post anterior",
+ "Next post": "Siguiente post",
+ "old posts page %d": "posts antiguos página %d",
+ "Read more": "Leer más",
+ "Source": "Código",
+}
diff --git a/nikola/data/themes/default/messages/messages_fr.py b/nikola/data/themes/default/messages/messages_fr.py
new file mode 100644
index 0000000..776147b
--- /dev/null
+++ b/nikola/data/themes/default/messages/messages_fr.py
@@ -0,0 +1,18 @@
+# -*- encoding:utf-8 -*-
+from __future__ import unicode_literals
+
+MESSAGES = {
+ "LANGUAGE": "Français",
+ "Posts for year %s": "Billets de l'année %s",
+ "Archive": "Archives",
+ "Posts about %s": "Billets sur %s",
+ "Tags": "Étiquettes",
+ "Also available in": "Disponible aussi en",
+ "More posts about": "Plus de billets sur",
+ "Posted": "Publié",
+ "Original site": "Site d'origine",
+ "Read in English": "Lire en français",
+ "Newer posts": "Billets récents",
+ "Older posts": "Anciens billets",
+ "Source": "Source",
+}
diff --git a/nikola/data/themes/default/messages/messages_gr.py b/nikola/data/themes/default/messages/messages_gr.py
new file mode 100644
index 0000000..5965bc3
--- /dev/null
+++ b/nikola/data/themes/default/messages/messages_gr.py
@@ -0,0 +1,21 @@
+# -*- encoding:utf-8 -*-
+from __future__ import unicode_literals
+
+MESSAGES = {
+ "LANGUAGE": "Ελληνικά",
+ "Posts for year %s": "Αναρτήσεις για τη χρονιά %s",
+ "Archive": "Αρχείο",
+ "Posts about %s": "Αναρτήσεις για %s",
+ "Tags": "Ετικέτες",
+ "Also available in": "Διαθέσιμο και στο",
+ "More posts about": "Περισσότερες αναρτήσεις για",
+ "Posted": "Αναρτήθηκε",
+ "Original site": "Ιστοσελίδα αρχικής ανάρτησης",
+ "Read in English": "Διαβάστε στα Ελληνικά",
+ "Newer posts": "Νεότερες αναρτήσεις",
+ "Older posts": "Παλαιότερες αναρτήσεις",
+ "Previous post": "Προηγούμενη ανάρτηση",
+ "Next post": "Επόμενη ανάρτηση",
+ "old posts page %d": "σελίδα παλαιότερων αναρτήσεων %d",
+ "Source": "Source",
+}
diff --git a/nikola/data/themes/default/messages/messages_it.py b/nikola/data/themes/default/messages/messages_it.py
new file mode 100644
index 0000000..42f4709
--- /dev/null
+++ b/nikola/data/themes/default/messages/messages_it.py
@@ -0,0 +1,23 @@
+# vim: set fileencoding=utf-8 :
+from __future__ import unicode_literals
+
+MESSAGES = {
+ "LANGUAGE": "Italiano",
+ "Posts for year %s": "Articoli per l'anno %s",
+ "Archive": "Archivio",
+ "Posts about %s": "Articoli su %s",
+ "Tags": "Tags",
+ "Also available in": "Anche disponibile in",
+ "More posts about": "Altri articoli s",
+ "Posted": "Pubblicato",
+ "Original site": "Sito originale",
+ "Read in English": "Leggi in italiano",
+ "Newer posts": "Articoli recenti",
+ "Older posts": "Articoli più vecchi",
+ "Older posts": "Articoli vecchi",
+ "Previous post": "Articolo precedente",
+ "Next post": "Articolo successivo",
+ "old posts page %d": "pagina dei vecchi articoli %d",
+ "Read more": "Espandi",
+ "Source": "Source",
+}
diff --git a/nikola/data/themes/default/messages/messages_ru.py b/nikola/data/themes/default/messages/messages_ru.py
new file mode 100644
index 0000000..8a209ec
--- /dev/null
+++ b/nikola/data/themes/default/messages/messages_ru.py
@@ -0,0 +1,22 @@
+# -*- encoding:utf-8 -*-
+from __future__ import unicode_literals
+
+MESSAGES = {
+ "LANGUAGE": "Русский",
+ "Posts for year %s": "Записи за %s год",
+ "Archive": "Архив",
+ "Posts about %s": "Записи с тэгом %s:",
+ "Tags": "Тэги",
+ "Also available in": "Также доступно в",
+ "More posts about": "Больше записей о",
+ "Posted": "Опубликовано",
+ "Original site": "Оригинальный сайт",
+ "Read in English": "Прочесть по-русски",
+ "Older posts": "Старые записи",
+ "Newer posts": "Новые записи",
+ "Previous post": "Предыдущая запись",
+ "Next post": "Следующая запись",
+ "old posts page %d": "страница со старыми записями %d",
+ "Read more": "Продолжить чтение",
+ "Source": "Source",
+}
diff --git a/nikola/data/themes/default/messages/ru.py b/nikola/data/themes/default/messages/ru.py
deleted file mode 100644
index 5d5cb01..0000000
--- a/nikola/data/themes/default/messages/ru.py
+++ /dev/null
@@ -1,21 +0,0 @@
-# -*- encoding:utf-8 -*-
-
-MESSAGES = {
- u"LANGUAGE": u"Русский",
- u"Posts for year %s": u"Записи за %s год",
- u"Archive": u"Архив",
- u"Posts about %s": u"Записи с тэгом %s:",
- u"Tags": u"Тэги",
- u"Also available in": u"Также доступно в",
- u"More posts about": u"Больше записей о",
- u"Posted": u"Опубликовано",
- u"Original site": u"Оригинальный сайт",
- u"Read in English": u"Прочесть по-русски",
- u"Older posts": u"Старые записи",
- u"Newer posts": u"Новые записи",
- u"Previous post": u"Предыдущая запись",
- u"Next post": u"Следующая запись",
- u"old posts page %d": u"страница со старыми записями %d",
- u"Read more": u"Продолжить чтение",
- u"Source": u"Source",
-}
diff --git a/nikola/data/themes/default/templates/base.tmpl b/nikola/data/themes/default/templates/base.tmpl
index cb5e0dd..9c134b7 100644
--- a/nikola/data/themes/default/templates/base.tmpl
+++ b/nikola/data/themes/default/templates/base.tmpl
@@ -1,40 +1,9 @@
## -*- coding: utf-8 -*-
+<%namespace file="base_helper.tmpl" import="*"/>
<!DOCTYPE html>
<html lang="${lang}">
<head>
- <meta charset="utf-8">
- <meta name="title" content="${title} | ${blog_title}" >
- <meta name="description" content="${description}" >
- <meta name="author" content="${blog_author}">
- <title>${title} | ${blog_title}</title>
- <!-- Le styles -->
- %if use_bundles:
- <link href="/assets/css/all.css" rel="stylesheet" type="text/css">
- <script src="/assets/js/all.js" type="text/javascript"></script>
- %else:
- <link href="/assets/css/bootstrap.css" rel="stylesheet" type="text/css">
- <link href="/assets/css/bootstrap-responsive.css" rel="stylesheet" type="text/css">
- <link href="/assets/css/rst.css" rel="stylesheet" type="text/css">
- <link href="/assets/css/code.css" rel="stylesheet" type="text/css">
- <link href="/assets/css/colorbox.css" rel="stylesheet" type="text/css"/>
- <link href="/assets/css/theme.css" rel="stylesheet" type="text/css"/>
- %if exists("files/assets/css/custom.css", not_empty=True):
- <link href="/assets/css/custom.css" rel="stylesheet" type="text/css">
- %endif
- <script src="/assets/js/jquery-1.7.2.min.js" type="text/javascript"></script>
- <script src="/assets/js/jquery.colorbox-min.js" type="text/javascript"></script>
- %endif
- <!-- Le HTML5 shim, for IE6-8 support of HTML5 elements -->
- <!--[if lt IE 9]>
- <script src="http://html5shim.googlecode.com/svn/trunk/html5.js" type="text/javascript"></script>
- <![endif]-->
- %if rss_link:
- ${rss_link}
- %else:
- %for language in translations:
- <link rel="alternate" type="application/rss+xml" type="application/rss+xml" title="RSS (${language})" href="${_link("rss", None, lang)}">
- %endfor
- %endif
+ ${html_head()}
<%block name="extra_head">
</%block>
</head>
@@ -54,18 +23,14 @@
%if len(translations) > 1:
<small>
${(messages[lang][u"Also available in"])}:&nbsp;
- %for langname in translations.keys():
- %if langname != lang:
- <a href="${_link("index", None, langname)}">${messages[langname]["LANGUAGE"]}</a>
- %endif
- %endfor
+ ${html_translations()}
</small>
%endif
</%block>
<hr>
</div>
<!-- End of banner-like substance !-->
- <div class="row" id="contentrow">
+ <div class="row-fluid" id="contentrow">
<div class="span10" id="contentcolumn">
<!--Body content-->
<%block name="content"></%block>
@@ -77,23 +42,8 @@
<!--Sidebar content-->
<ul class="unstyled">
<li>${license}
- <!-- social buttons -->
- %if add_this_buttons:
- <li>
- <div id="addthisbox" class="addthis_toolbox addthis_default_style">
- <a class="addthis_button_preferred_1"></a>
- <a class="addthis_button_preferred_2"></a>
- <a class="addthis_button_preferred_3"></a>
- <a class="addthis_button_preferred_4"></a>
- <a class="addthis_button_compact"></a>
- <a class="addthis_counter addthis_bubble_style"></a>
- </div>
- <script type="text/javascript" src="http://s7.addthis.com/js/250/addthis_widget.js#pubid=ra-4f7088a56bb93798"></script>
- <!-- End of social buttons -->
- % endif
- %for url, text in sidebar_links[lang]:
- <li><a href="${url}">${text}</a>
- %endfor
+ ${html_social()}
+ ${html_sidebar_links()}
<li>${search_form}
</ul>
<!--End of sidebar content-->
diff --git a/nikola/data/themes/default/templates/base_helper.tmpl b/nikola/data/themes/default/templates/base_helper.tmpl
new file mode 100644
index 0000000..3f27f23
--- /dev/null
+++ b/nikola/data/themes/default/templates/base_helper.tmpl
@@ -0,0 +1,74 @@
+<%def name="html_head()">
+ <meta charset="utf-8">
+ <meta name="title" content="${title} | ${blog_title}" >
+ <meta name="description" content="${description}" >
+ <meta name="author" content="${blog_author}">
+ <title>${title} | ${blog_title}</title>
+ <!-- Le styles -->
+ %if use_bundles:
+ <link href="/assets/css/all.css" rel="stylesheet" type="text/css">
+ <script src="/assets/js/all.js" type="text/javascript"></script>
+ %else:
+ <link href="/assets/css/bootstrap.css" rel="stylesheet" type="text/css">
+ <link href="/assets/css/bootstrap-responsive.css" rel="stylesheet" type="text/css">
+ <link href="/assets/css/rst.css" rel="stylesheet" type="text/css">
+ <link href="/assets/css/code.css" rel="stylesheet" type="text/css">
+ <link href="/assets/css/colorbox.css" rel="stylesheet" type="text/css"/>
+ <link href="/assets/css/slides.css" rel="stylesheet" type="text/css"/>
+ <link href="/assets/css/theme.css" rel="stylesheet" type="text/css"/>
+ %if has_custom_css:
+ <link href="/assets/css/custom.css" rel="stylesheet" type="text/css">
+ %endif
+ <script src="/assets/js/jquery-1.7.2.min.js" type="text/javascript"></script>
+ <script src="/assets/js/jquery.colorbox-min.js" type="text/javascript"></script>
+ <script src="/assets/js/slides.min.jquery.js" type="text/javascript"></script>
+ %endif
+ <!-- Le HTML5 shim, for IE6-8 support of HTML5 elements -->
+ <!--[if lt IE 9]>
+ <script src="http://html5shim.googlecode.com/svn/trunk/html5.js" type="text/javascript"></script>
+ <![endif]-->
+ %if rss_link:
+ ${rss_link}
+ %else:
+ %for language in translations:
+ <link rel="alternate" type="application/rss+xml" title="RSS (${language})" href="${_link('rss', None, lang)}">
+ %endfor
+ %endif
+</%def>
+
+
+<%def name="html_social()">
+%if add_this_buttons:
+ <!-- Social buttons -->
+ <div id="addthisbox" class="addthis_toolbox addthis_peekaboo_style addthis_default_style addthis_label_style addthis_32x32_style">
+ <a class="addthis_button_more">Share</a>
+ <ul><li><a class="addthis_button_facebook"></a></li>
+ <li><a class="addthis_button_google_plusone_share"></a></li>
+ <li><a class="addthis_button_linkedin"></a></li>
+ <li><a class="addthis_button_twitter"></a></li>
+ </ul>
+ </div>
+ <script type="text/javascript" src="http://s7.addthis.com/js/300/addthis_widget.js#pubid=ra-4f7088a56bb93798"></script>
+ <!-- End of social buttons -->
+%endif
+</%def>
+
+
+<%def name="html_sidebar_links()">
+ %for url, text in sidebar_links[lang]:
+ % if rel_link(permalink, url) == "#":
+ <li class="active"><a href="${url}">${text}</a>
+ %else:
+ <li><a href="${url}">${text}</a>
+ %endif
+ %endfor
+</%def>
+
+
+<%def name="html_translations()">
+ %for langname in translations.keys():
+ %if langname != lang:
+ <a href="${_link("index", None, langname)}">${messages[langname]["LANGUAGE"]}</a>
+ %endif
+ %endfor
+</%def>
diff --git a/nikola/data/themes/default/templates/index.tmpl b/nikola/data/themes/default/templates/index.tmpl
index 2c7b4be..03dd1f8 100644
--- a/nikola/data/themes/default/templates/index.tmpl
+++ b/nikola/data/themes/default/templates/index.tmpl
@@ -1,36 +1,18 @@
## -*- coding: utf-8 -*-
+<%namespace name="helper" file="index_helper.tmpl"/>
<%inherit file="base.tmpl"/>
<%block name="content">
% for post in posts:
<div class="postbox">
<h1><a href="${post.permalink(lang)}">${post.title(lang)}</a>
<small>&nbsp;&nbsp;
- ${messages[lang]["Posted"]}: ${post.date}
+ ${messages[lang]["Posted"]}: ${post.date.strftime(date_format)}
</small></h1>
<hr>
${post.text(lang, index_teasers)}
- <p>
- %if disqus_forum:
- <a href="${post.permalink()}#disqus_thread">Comments</a>
- %endif
+ ${helper.html_disqus_link(post)}
</div>
% endfor
- <div>
-<ul class="pager">
- %if prevlink:
- <li class="previous">
- <a href="${prevlink}">&larr; ${messages[lang]["Newer posts"]}</a>
- </li>
- %endif
- %if nextlink:
- <li class="next">
- <a href="${nextlink}">${messages[lang]["Older posts"]} &rarr;</a>
- </li>
- %endif
-</ul>
-
- </div>
- %if disqus_forum:
- <script type="text/javascript">var disqus_shortname="${disqus_forum}";(function(){var a=document.createElement("script");a.async=true;a.type="text/javascript";a.src="http://"+disqus_shortname+".disqus.com/count.js";(document.getElementsByTagName("HEAD")[0]||document.getElementsByTagName("BODY")[0]).appendChild(a)}());</script>
- %endif
+ ${helper.html_pager()}
+ ${helper.html_disqus_script()}
</%block>
diff --git a/nikola/data/themes/default/templates/index_helper.tmpl b/nikola/data/themes/default/templates/index_helper.tmpl
new file mode 100644
index 0000000..cfecdf3
--- /dev/null
+++ b/nikola/data/themes/default/templates/index_helper.tmpl
@@ -0,0 +1,31 @@
+<%def name="html_pager()">
+<div>
+<ul class="pager">
+ %if prevlink:
+ <li class="previous">
+ <a href="${prevlink}">&larr; ${messages[lang]["Newer posts"]}</a>
+ </li>
+ %endif
+ %if nextlink:
+ <li class="next">
+ <a href="${nextlink}">${messages[lang]["Older posts"]} &rarr;</a>
+ </li>
+ %endif
+</ul>
+</div>
+</%def>
+
+
+<%def name="html_disqus_link(post)">
+ <p>
+ %if disqus_forum:
+ <a href="${post.permalink()}#disqus_thread">Comments</a>
+ %endif
+</%def>
+
+
+<%def name="html_disqus_script()">
+ %if disqus_forum:
+ <script type="text/javascript">var disqus_shortname="${disqus_forum}";(function(){var a=document.createElement("script");a.async=true;a.type="text/javascript";a.src="http://"+disqus_shortname+".disqus.com/count.js";(document.getElementsByTagName("HEAD")[0]||document.getElementsByTagName("BODY")[0]).appendChild(a)}());</script>
+ %endif
+</%def>
diff --git a/nikola/data/themes/default/templates/list_post.tmpl b/nikola/data/themes/default/templates/list_post.tmpl
new file mode 100644
index 0000000..1a1cdee
--- /dev/null
+++ b/nikola/data/themes/default/templates/list_post.tmpl
@@ -0,0 +1,14 @@
+## -*- coding: utf-8 -*-
+<%inherit file="base.tmpl"/>
+<%block name="content">
+ <!--Body content-->
+ <div class="postbox">
+ <h1>${title}</h1>
+ <ul class="unstyled">
+ % for post in posts:
+ <li><a href="${post.permalink(lang)}">[${post.date.strftime(date_format)}] ${post.title(lang)}</a>
+ % endfor
+ </ul>
+ </div>
+ <!--End of body content-->
+</%block>
diff --git a/nikola/data/themes/default/templates/post.tmpl b/nikola/data/themes/default/templates/post.tmpl
index 6bbb460..306192d 100644
--- a/nikola/data/themes/default/templates/post.tmpl
+++ b/nikola/data/themes/default/templates/post.tmpl
@@ -1,51 +1,20 @@
## -*- coding: utf-8 -*-
+<%namespace name="helper" file="post_helper.tmpl"/>
<%inherit file="base.tmpl"/>
<%block name="content">
<div class="postbox">
- <h1><a href='${permalink}'>${title}</a></h1>
- % if link:
- <p><a href='${link}'>${messages[lang]["Original site"]}</a></p>
- % endif
+ ${helper.html_title()}
<hr>
<small>
- ${messages[lang]["Posted"]}: ${post.date}&nbsp;&nbsp;|&nbsp;&nbsp;
-
- %if len(translations) > 1:
- %for langname in translations.keys():
- %if langname != lang:
- <a href="${post.permalink(langname)}">${messages[langname][u"Read in English"]}</a>
- &nbsp;&nbsp;|&nbsp;&nbsp;
- %endif
- %endfor
- %endif
-
- <a href="${post.pagenames[lang]+".txt"}">${messages[lang]["Source"]}</a>
- %if post.tags:
- &nbsp;&nbsp;|&nbsp;&nbsp;${messages[lang]["More posts about"]}
- %for tag in post.tags:
- <a href="${_link("tag", tag, lang)}"><span class="badge badge-info">${tag}</span></a>
- %endfor
- %endif
+ ${messages[lang]["Posted"]}: ${post.date.strftime(date_format)}
+ ${helper.html_translations(post)}
+ &nbsp;&nbsp;|&nbsp;&nbsp;
+ <a href="${post.pagenames[lang]+'.txt'}">${messages[lang]["Source"]}</a>
+ ${helper.html_tags(post)}
</small>
<hr>
${post.text(lang)}
- <ul class="pager">
- %if post.prev_post:
- <li class="previous">
- <a href="${post.prev_post.permalink(lang)}">&larr; ${messages[lang]["Previous post"]}</a>
- </li>
- %endif
- %if post.next_post:
- <li class="next">
- <a href="${post.next_post.permalink(lang)}">${messages[lang]["Next post"]} &rarr;</a>
- </li>
- %endif
- </ul>
- %if disqus_forum:
- <div id="disqus_thread"></div>
- <script type="text/javascript">var disqus_shortname="${disqus_forum}";var disqus_url="${post.permalink(absolute=True)}";(function(){var a=document.createElement("script");a.type="text/javascript";a.async=true;a.src="http://"+disqus_shortname+".disqus.com/embed.js";(document.getElementsByTagName("head")[0]||document.getElementsByTagName("body")[0]).appendChild(a)})(); </script>
- <noscript>Please enable JavaScript to view the <a href="http://disqus.com/?ref_noscript">comments powered by Disqus.</a></noscript>
- <a href="http://disqus.com" class="dsq-brlink">comments powered by <span class="logo-disqus">Disqus</span></a>
- %endif
+ ${helper.html_pager(post)}
+ ${helper.html_disqus(post)}
</div>
</%block>
diff --git a/nikola/data/themes/default/templates/post_helper.tmpl b/nikola/data/themes/default/templates/post_helper.tmpl
new file mode 100644
index 0000000..3e874e9
--- /dev/null
+++ b/nikola/data/themes/default/templates/post_helper.tmpl
@@ -0,0 +1,54 @@
+<%def name="html_title()">
+ <h1>${title}</h1>
+ % if link:
+ <p><a href='${link}'>${messages[lang]["Original site"]}</a></p>
+ % endif
+</%def>
+
+
+<%def name="html_translations(post)">
+ %if len(translations) > 1:
+ %for langname in translations.keys():
+ %if langname != lang:
+ &nbsp;&nbsp;|&nbsp;&nbsp;
+ <a href="${post.permalink(langname)}">${messages[langname]["Read in English"]}</a>
+ %endif
+ %endfor
+ %endif
+</%def>
+
+
+<%def name="html_tags(post)">
+ %if post.tags:
+ &nbsp;&nbsp;|&nbsp;&nbsp;${messages[lang]["More posts about"]}
+ %for tag in post.tags:
+ <a class="tag" href="${_link('tag', tag, lang)}"><span class="badge badge-info">${tag}</span></a>
+ %endfor
+ %endif
+</%def>
+
+
+<%def name="html_disqus(post)">
+ %if disqus_forum:
+ <div id="disqus_thread"></div>
+ <script type="text/javascript">var disqus_shortname="${disqus_forum}";var disqus_url="${post.permalink(absolute=True)}";(function(){var a=document.createElement("script");a.type="text/javascript";a.async=true;a.src="http://"+disqus_shortname+".disqus.com/embed.js";(document.getElementsByTagName("head")[0]||document.getElementsByTagName("body")[0]).appendChild(a)})(); </script>
+ <noscript>Please enable JavaScript to view the <a href="http://disqus.com/?ref_noscript">comments powered by Disqus.</a></noscript>
+ <a href="http://disqus.com" class="dsq-brlink">comments powered by <span class="logo-disqus">Disqus</span></a>
+ %endif
+</%def>
+
+
+<%def name="html_pager(post)">
+ <ul class="pager">
+ %if post.prev_post:
+ <li class="previous">
+ <a href="${post.prev_post.permalink(lang)}">&larr; ${messages[lang]["Previous post"]}</a>
+ </li>
+ %endif
+ %if post.next_post:
+ <li class="next">
+ <a href="${post.next_post.permalink(lang)}">${messages[lang]["Next post"]} &rarr;</a>
+ </li>
+ %endif
+ </ul>
+</%def>
diff --git a/nikola/data/themes/default/templates/tag.tmpl b/nikola/data/themes/default/templates/tag.tmpl
index ac97829..7c89ad1 100644
--- a/nikola/data/themes/default/templates/tag.tmpl
+++ b/nikola/data/themes/default/templates/tag.tmpl
@@ -1,5 +1,5 @@
## -*- coding: utf-8 -*-
-<%inherit file="list.tmpl"/>
+<%inherit file="list_post.tmpl"/>
<%block name="extra_head">
%for language in translations:
<link rel="alternate" type="application/rss+xml" type="application/rss+xml" title="RSS for tag ${tag} (${language})" href="${_link("tag_rss", tag, lang)}">
diff --git a/nikola/data/themes/default/templates/tags.tmpl b/nikola/data/themes/default/templates/tags.tmpl
index 10040ca..369a3d5 100644
--- a/nikola/data/themes/default/templates/tags.tmpl
+++ b/nikola/data/themes/default/templates/tags.tmpl
@@ -6,7 +6,7 @@
<h1>${title}</h1>
<ul class="unstyled">
% for text, link in items:
- <li><a href="${link}"><span class="badge badge-info">${text}</span></a>
+ <li><a class="tag" href="${link}"><span class="badge badge-info">${text}</span></a>
% endfor
</ul>
<!--End of body content-->
diff --git a/nikola/data/themes/jinja-default/README b/nikola/data/themes/jinja-default/README
index 3fb7d3f..4b0bf04 100644
--- a/nikola/data/themes/jinja-default/README
+++ b/nikola/data/themes/jinja-default/README
@@ -1,3 +1,3 @@
This theme is exactly the same as "default" but using Jinja2 templates.
-To try it, set TEMPLATE_ENGINE="jinja" and THEME="jinja-default" in your 'conf.py'
+To try it, set THEME="jinja-default" in your 'conf.py'
diff --git a/nikola/data/themes/jinja-default/templates/base.tmpl b/nikola/data/themes/jinja-default/templates/base.tmpl
index 546e1a7..0f394d3 100644
--- a/nikola/data/themes/jinja-default/templates/base.tmpl
+++ b/nikola/data/themes/jinja-default/templates/base.tmpl
@@ -17,7 +17,7 @@
<link href="/assets/css/code.css" rel="stylesheet" type="text/css">
<link href="/assets/css/colorbox.css" rel="stylesheet" type="text/css"/>
<link href="/assets/css/theme.css" rel="stylesheet" type="text/css"/>
- {% if exists("files/assets/css/custom.css", not_empty=True) %}
+ {% if has_custom_css %}
<link href="/assets/css/custom.css" rel="stylesheet">
{% endif %}
<script src="/assets/js/jquery-1.7.2.min.js" type="text/javascript"></script>
@@ -64,7 +64,7 @@
<hr>
</div>
<!-- End of banner-like substance !-->
- <div class="row" id="contentrow">
+ <div class="row-fluid" id="contentrow">
<div class="span10" id="contentcolumn">
<!--Body content-->
{% block content %}{% endblock %}
@@ -76,20 +76,6 @@
<!--Sidebar content-->
<ul class="unstyled">
<li>{{license}}
- <!-- social buttons -->
-` {% if add_this_buttons %}
- <li>
- <div id="addthisbox" class="addthis_toolbox addthis_default_style">
- <a class="addthis_button_preferred_1"></a>
- <a class="addthis_button_preferred_2"></a>
- <a class="addthis_button_preferred_3"></a>
- <a class="addthis_button_preferred_4"></a>
- <a class="addthis_button_compact"></a>
- <a class="addthis_counter addthis_bubble_style"></a>
- </div>
- <script type="text/javascript" src="http://s7.addthis.com/js/250/addthis_widget.js#pubid=ra-4f7088a56bb93798"></script>
- <!-- End of social buttons -->
- {% endif %}
{% for url, text in sidebar_links[lang] %}
<li><a href="{{url}}">{{text}}</a>
{% endfor %}
@@ -97,6 +83,19 @@
</ul>
<!--End of sidebar content-->
</div>
+ {% if add_this_buttons %}
+ <!-- social buttons -->
+ <div id="addthisbox" class="addthis_toolbox addthis_peekaboo_style addthis_default_style addthis_label_style addthis_32x32_style">
+ <a class="addthis_button_more">Share</a>
+ <ul><li><a class="addthis_button_facebook"></a></li>
+ <li><a class="addthis_button_google_plusone_share"></a></li>
+ <li><a class="addthis_button_linkedin"></a></li>
+ <li><a class="addthis_button_twitter"></a></li>
+ </ul>
+ </div>
+ <script type="text/javascript" src="http://s7.addthis.com/js/300/addthis_widget.js#pubid=ra-4f7088a56bb93798"></script>
+ <!-- End of social buttons -->
+ {% endif %}
{{analytics}}
<script type="text/javascript">jQuery("a.image-reference").colorbox({rel:"gal",maxWidth:"80%",maxHeight:"80%",scalePhotos:true});</script>
</body>
diff --git a/nikola/data/themes/jinja-default/templates/index.tmpl b/nikola/data/themes/jinja-default/templates/index.tmpl
index 6244e10..c068417 100644
--- a/nikola/data/themes/jinja-default/templates/index.tmpl
+++ b/nikola/data/themes/jinja-default/templates/index.tmpl
@@ -4,7 +4,7 @@
<div class="postbox">
<h1><a href="{{post.permalink(lang)}}">{{post.title(lang)}}</a>
<small>&nbsp;&nbsp;
- {{messages[lang]["Posted"]}}: {{post.date}}
+ {{messages[lang]["Posted"]}}: {{post.date.strftime(date_format)}}
</small></h1>
<hr>
{{post.text(lang, index_teasers)}}
diff --git a/nikola/data/themes/jinja-default/templates/list_post.tmpl b/nikola/data/themes/jinja-default/templates/list_post.tmpl
new file mode 100644
index 0000000..7723214
--- /dev/null
+++ b/nikola/data/themes/jinja-default/templates/list_post.tmpl
@@ -0,0 +1,13 @@
+{% extends "base.tmpl" %}
+{% block content %}
+ <!--Body content-->
+ <div class="postbox">
+ <h1>{{title}}</h1>
+ <ul class="unstyled">
+ {% for post in posts %}
+ <li><a href="{{post.permalink(lang)}}">[{{post.date.strftime(date_format)}}] {{post.title(lang)}}</a>
+ {% endfor %}
+ </ul>
+ </div>
+ <!--End of body content-->
+{% endblock %}
diff --git a/nikola/data/themes/jinja-default/templates/post.tmpl b/nikola/data/themes/jinja-default/templates/post.tmpl
index 4748959..3ce6abe 100644
--- a/nikola/data/themes/jinja-default/templates/post.tmpl
+++ b/nikola/data/themes/jinja-default/templates/post.tmpl
@@ -7,7 +7,7 @@
{% endif %}
<hr>
<small>
- {{messages[lang]["Posted"]}}: {{post.date}}&nbsp;&nbsp;|&nbsp;&nbsp;
+ {{messages[lang]["Posted"]}}: {{post.date.strftime(date_format)}}&nbsp;&nbsp;|&nbsp;&nbsp;
{% if translations|length > 1 %}
{% for langname in translations.keys() %}
diff --git a/nikola/data/themes/jinja-default/templates/tag.tmpl b/nikola/data/themes/jinja-default/templates/tag.tmpl
index 59facb4..42720fd 100644
--- a/nikola/data/themes/jinja-default/templates/tag.tmpl
+++ b/nikola/data/themes/jinja-default/templates/tag.tmpl
@@ -1,4 +1,4 @@
-{% extends "list.tmpl"%}
+{% extends "list_post.tmpl"%}
{%block extra_head %}
{% for language in translations %}
<link rel="alternate" type="application/rss+xml" type="application/rss+xml" title="RSS for tag {{tag}} ({{language}})" href="{{_link("tag_rss", tag, lang)}}">
diff --git a/nikola/data/themes/monospace/assets/css/code.css b/nikola/data/themes/monospace/assets/css/code.css
new file mode 100644
index 0000000..b1d7ace
--- /dev/null
+++ b/nikola/data/themes/monospace/assets/css/code.css
@@ -0,0 +1,62 @@
+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/assets/css/colorbox.css b/nikola/data/themes/monospace/assets/css/colorbox.css
new file mode 100644
index 0000000..f67c346
--- /dev/null
+++ b/nikola/data/themes/monospace/assets/css/colorbox.css
@@ -0,0 +1,85 @@
+/*
+ ColorBox Core Style:
+ The following CSS is consistent between example themes and should not be altered.
+*/
+#colorbox, #cboxOverlay, #cboxWrapper{position:absolute; top:0; left:0; z-index:9999; overflow:hidden;}
+#cboxOverlay{position:fixed; width:100%; height:100%;}
+#cboxMiddleLeft, #cboxBottomLeft{clear:left;}
+#cboxContent{position:relative;}
+#cboxLoadedContent{overflow:auto;}
+#cboxTitle{margin:0;}
+#cboxLoadingOverlay, #cboxLoadingGraphic{position:absolute; top:0; left:0; width:100%; height:100%;}
+#cboxPrevious, #cboxNext, #cboxClose, #cboxSlideshow{cursor:pointer;}
+.cboxPhoto{float:left; margin:auto; border:0; display:block;}
+.cboxIframe{width:100%; height:100%; display:block; border:0;}
+
+/*
+ User Style:
+ Change the following styles to modify the appearance of ColorBox. They are
+ ordered & tabbed in a way that represents the nesting of the generated HTML.
+*/
+#cboxOverlay{background:url(images/overlay.png) repeat 0 0;}
+#colorbox{}
+ #cboxTopLeft{width:21px; height:21px; background:url(images/controls.png) no-repeat -101px 0;}
+ #cboxTopRight{width:21px; height:21px; background:url(images/controls.png) no-repeat -130px 0;}
+ #cboxBottomLeft{width:21px; height:21px; background:url(images/controls.png) no-repeat -101px -29px;}
+ #cboxBottomRight{width:21px; height:21px; background:url(images/controls.png) no-repeat -130px -29px;}
+ #cboxMiddleLeft{width:21px; background:url(images/controls.png) left top repeat-y;}
+ #cboxMiddleRight{width:21px; background:url(images/controls.png) right top repeat-y;}
+ #cboxTopCenter{height:21px; background:url(images/border.png) 0 0 repeat-x;}
+ #cboxBottomCenter{height:21px; background:url(images/border.png) 0 -29px repeat-x;}
+ #cboxContent{background:#fff; overflow:hidden;}
+ .cboxIframe{background:#fff;}
+ #cboxError{padding:50px; border:1px solid #ccc;}
+ #cboxLoadedContent{margin-bottom:28px;}
+ #cboxTitle{position:absolute; bottom:4px; left:0; text-align:center; width:100%; color:#949494;}
+ #cboxCurrent{position:absolute; bottom:4px; left:58px; color:#949494;}
+ #cboxSlideshow{position:absolute; bottom:4px; right:30px; color:#0092ef;}
+ #cboxPrevious{position:absolute; bottom:0; left:0; background:url(images/controls.png) no-repeat -75px 0; width:25px; height:25px; text-indent:-9999px;}
+ #cboxPrevious:hover{background-position:-75px -25px;}
+ #cboxNext{position:absolute; bottom:0; left:27px; background:url(images/controls.png) no-repeat -50px 0; width:25px; height:25px; text-indent:-9999px;}
+ #cboxNext:hover{background-position:-50px -25px;}
+ #cboxLoadingOverlay{background:url(images/loading_background.png) no-repeat center center;}
+ #cboxLoadingGraphic{background:url(images/loading.gif) no-repeat center center;}
+ #cboxClose{position:absolute; bottom:0; right:0; background:url(images/controls.png) no-repeat -25px 0; width:25px; height:25px; text-indent:-9999px;}
+ #cboxClose:hover{background-position:-25px -25px;}
+
+/*
+ The following fixes a problem where IE7 and IE8 replace a PNG's alpha transparency with a black fill
+ when an alpha filter (opacity change) is set on the element or ancestor element. This style is not applied to or needed in IE9.
+ See: http://jacklmoore.com/notes/ie-transparency-problems/
+*/
+.cboxIE #cboxTopLeft,
+.cboxIE #cboxTopCenter,
+.cboxIE #cboxTopRight,
+.cboxIE #cboxBottomLeft,
+.cboxIE #cboxBottomCenter,
+.cboxIE #cboxBottomRight,
+.cboxIE #cboxMiddleLeft,
+.cboxIE #cboxMiddleRight {
+ filter: progid:DXImageTransform.Microsoft.gradient(startColorstr=#00FFFFFF,endColorstr=#00FFFFFF);
+}
+
+/*
+ The following provides PNG transparency support for IE6
+ Feel free to remove this and the /ie6/ directory if you have dropped IE6 support.
+*/
+.cboxIE6 #cboxTopLeft{background:url(images/ie6/borderTopLeft.png);}
+.cboxIE6 #cboxTopCenter{background:url(images/ie6/borderTopCenter.png);}
+.cboxIE6 #cboxTopRight{background:url(images/ie6/borderTopRight.png);}
+.cboxIE6 #cboxBottomLeft{background:url(images/ie6/borderBottomLeft.png);}
+.cboxIE6 #cboxBottomCenter{background:url(images/ie6/borderBottomCenter.png);}
+.cboxIE6 #cboxBottomRight{background:url(images/ie6/borderBottomRight.png);}
+.cboxIE6 #cboxMiddleLeft{background:url(images/ie6/borderMiddleLeft.png);}
+.cboxIE6 #cboxMiddleRight{background:url(images/ie6/borderMiddleRight.png);}
+
+.cboxIE6 #cboxTopLeft,
+.cboxIE6 #cboxTopCenter,
+.cboxIE6 #cboxTopRight,
+.cboxIE6 #cboxBottomLeft,
+.cboxIE6 #cboxBottomCenter,
+.cboxIE6 #cboxBottomRight,
+.cboxIE6 #cboxMiddleLeft,
+.cboxIE6 #cboxMiddleRight {
+ _behavior: expression(this.src = this.src ? this.src : this.currentStyle.backgroundImage.split('"')[1], this.style.background = "none", this.style.filter = "progid:DXImageTransform.Microsoft.AlphaImageLoader(src=" + this.src + ", sizingMethod='scale')");
+}
diff --git a/nikola/data/themes/monospace/assets/css/rst.css b/nikola/data/themes/monospace/assets/css/rst.css
new file mode 100644
index 0000000..1f0edcb
--- /dev/null
+++ b/nikola/data/themes/monospace/assets/css/rst.css
@@ -0,0 +1,315 @@
+/*
+:Author: David Goodger
+:Contact: goodger@users.sourceforge.net
+:Date: $Date: 2005-12-18 01:56:14 +0100 (Sun, 18 Dec 2005) $
+:Revision: $Revision: 4224 $
+:Copyright: This stylesheet has been placed in the public domain.
+
+Default cascading style sheet for the HTML output of Docutils.
+
+See http://docutils.sf.net/docs/howto/html-stylesheets.html for how to
+customize this style sheet.
+*/
+
+/* used to remove borders from tables and images */
+.borderless, table.borderless td, table.borderless th {
+ border: 0 }
+
+table.borderless td, table.borderless th {
+ /* Override padding for "table.docutils td" with "! important".
+ The right padding separates the table cells. */
+ padding: 0 0.5em 0 0 ! important }
+
+.first {
+ /* Override more specific margin styles with "! important". */
+ margin-top: 0 ! important }
+
+.last, .with-subtitle {
+ margin-bottom: 0 ! important }
+
+.hidden {
+ display: none }
+
+a.toc-backref {
+ text-decoration: none ;
+ color: black }
+
+blockquote.epigraph {
+ margin: 2em 1em ; }
+
+dl.docutils dd {
+ margin-bottom: 0.5em }
+
+/* Uncomment (and remove this text!) to get bold-faced definition list terms
+dl.docutils dt {
+ font-weight: bold }
+*/
+
+div.abstract {
+ margin: 2em 5em }
+
+div.abstract p.topic-title {
+ font-weight: bold ;
+ text-align: center }
+
+div.admonition, div.attention, div.caution, div.danger, div.error,
+div.hint, div.important, div.note, div.tip, div.warning {
+ padding: 8px 35px 8px 14px;
+ margin-bottom: 18px;
+ text-shadow: 0 1px 0 rgba(255, 255, 255, 0.5);
+ background-color: #d9edf7;
+ color: #3a87ad;
+ border: 1px solid #bce8f1;
+ -webkit-border-radius: 4px;
+ -moz-border-radius: 4px;
+ border-radius: 4px;
+}
+
+div.admonition p.admonition-title, div.hint p.admonition-title,
+div.important p.admonition-title, div.note p.admonition-title,
+div.tip p.admonition-title {
+ font-weight: bold ;
+ font-family: sans-serif }
+
+div.attention p.admonition-title, div.caution p.admonition-title,
+div.danger p.admonition-title, div.error p.admonition-title,
+div.warning p.admonition-title {
+ color: red ;
+ font-weight: bold ;
+ font-family: sans-serif }
+
+/* Uncomment (and remove this text!) to get reduced vertical space in
+ compound paragraphs.
+div.compound .compound-first, div.compound .compound-middle {
+ margin-bottom: 0.5em }
+
+div.compound .compound-last, div.compound .compound-middle {
+ margin-top: 0.5em }
+*/
+
+div.dedication {
+ margin: 2em 5em ;
+ text-align: center ;
+ font-style: italic }
+
+div.dedication p.topic-title {
+ font-weight: bold ;
+ font-style: normal }
+
+div.figure {
+ text-align: center;
+ margin-left: 2em ;
+ margin-right: 2em }
+
+div.footer, div.header {
+ clear: both;
+ font-size: smaller }
+
+div.line-block {
+ display: block ;
+ margin-top: 1em ;
+ margin-bottom: 1em }
+
+div.line-block div.line-block {
+ margin-top: 0 ;
+ margin-bottom: 0 ;
+ margin-left: 1.5em }
+
+div.sidebar {
+ margin-left: 1em ;
+ border: medium outset ;
+ padding: 1em ;
+ background-color: #ffffee ;
+ width: 40% ;
+ float: right ;
+ clear: right }
+
+div.sidebar p.rubric {
+ font-family: sans-serif ;
+ font-size: medium }
+
+div.system-messages {
+ margin: 5em }
+
+div.system-messages h1 {
+ color: red }
+
+div.system-message {
+ padding: 8px 35px 8px 14px;
+ margin-bottom: 18px;
+ text-shadow: 0 1px 0 rgba(255, 255, 255, 0.5);
+ border: 1px solid #eed3d7;
+ -webkit-border-radius: 4px;
+ -moz-border-radius: 4px;
+ border-radius: 4px;
+ padding: 1em;
+ background-color: #f2dede;
+ color: #b94a48;
+
+}
+
+div.system-message p.system-message-title {
+ color: inherit ;
+ font-weight: bold }
+
+div.topic {
+ margin: 2em }
+
+h1.section-subtitle, h2.section-subtitle, h3.section-subtitle,
+h4.section-subtitle, h5.section-subtitle, h6.section-subtitle {
+ margin-top: 0.4em }
+
+h1.title {
+ text-align: center }
+
+h2.subtitle {
+ text-align: center }
+
+hr.docutils {
+ width: 75% }
+
+img.align-left {
+ clear: left }
+
+img.align-right {
+ clear: right }
+
+ol.simple, ul.simple {
+ margin-bottom: 1em }
+
+ol.arabic {
+ list-style: decimal }
+
+ol.loweralpha {
+ list-style: lower-alpha }
+
+ol.upperalpha {
+ list-style: upper-alpha }
+
+ol.lowerroman {
+ list-style: lower-roman }
+
+ol.upperroman {
+ list-style: upper-roman }
+
+p.attribution {
+ text-align: right ;
+ margin-left: 50% }
+
+p.caption {
+ font-style: italic }
+
+p.credits {
+ font-style: italic ;
+ font-size: smaller }
+
+p.label {
+ white-space: nowrap }
+
+p.rubric {
+ font-weight: bold ;
+ font-size: larger ;
+ color: maroon ;
+ text-align: center }
+
+p.sidebar-title {
+ font-family: sans-serif ;
+ font-weight: bold ;
+ font-size: larger }
+
+p.sidebar-subtitle {
+ font-family: sans-serif ;
+ font-weight: bold }
+
+p.topic-title {
+ font-weight: bold }
+
+pre.address {
+ margin-bottom: 0 ;
+ margin-top: 0 ;
+ font-family: serif ;
+ font-size: 100% }
+
+pre.literal-block, pre.doctest-block {
+ margin: 0 0 0 0 ;
+ background-color: #eeeeee;
+ padding: 1em;
+ overflow: auto;
+/* font-family: "Courier New", Courier, monospace;*/
+}
+
+span.classifier {
+ font-family: sans-serif ;
+ font-style: oblique }
+
+span.classifier-delimiter {
+ font-family: sans-serif ;
+ font-weight: bold }
+
+span.interpreted {
+ font-family: sans-serif }
+
+span.option {
+ white-space: nowrap }
+
+span.pre {
+ white-space: pre }
+
+span.problematic {
+ color: red }
+
+span.section-subtitle {
+ /* font-size relative to parent (h1..h6 element) */
+ font-size: 80% }
+
+table.citation {
+ border-left: solid 1px gray;
+ margin-left: 1px }
+
+table.docinfo {
+ margin: 2em 4em }
+
+table.docutils {
+ margin-top: 0.5em ;
+ margin-bottom: 0.5em }
+
+table.footnote {
+ border-left: solid 1px black;
+ margin-left: 1px }
+
+table.docutils td, table.docutils th,
+table.docinfo td, table.docinfo th {
+ padding-left: 0.5em ;
+ padding-right: 0.5em ;
+ vertical-align: top }
+
+table.docutils th.field-name, table.docinfo th.docinfo-name {
+ font-weight: bold ;
+ text-align: left ;
+ white-space: nowrap ;
+ padding-left: 0 }
+
+h1 tt.docutils, h2 tt.docutils, h3 tt.docutils,
+h4 tt.docutils, h5 tt.docutils, h6 tt.docutils {
+ font-size: 100% }
+
+tt.docutils {
+ background-color: #eeeeee }
+
+ul.auto-toc {
+ list-style-type: none }
+
+#blog-title {
+ font-size: 34pt;
+ /*margin:0 0.3em -14px;*/
+ background-color: #FFF;
+ font-family: "courier";
+ text-align: right;
+ margin-top: 20px;
+ margin-bottom: 10px;
+}
+
+img {
+ margin-top: 12px;
+ margin-bottom: 12px;
+}
diff --git a/nikola/data/themes/monospace/assets/css/theme.css b/nikola/data/themes/monospace/assets/css/theme.css
new file mode 100644
index 0000000..b66d7bd
--- /dev/null
+++ b/nikola/data/themes/monospace/assets/css/theme.css
@@ -0,0 +1,14 @@
+body { margin:0px; padding:20px 0px; text-align:center; font-family:Monospace; color:#585858; }
+.post { margin:0px 0px 30px 0px; padding:0px 0px 30px 0px; border-bottom:1px dotted #C8C8C8; }
+.meta { margin:10px; padding:15px; background:#EAEAEA; clear:both; }
+#footer { text-align:center; clear:both; margin:30px 0px 0px 0px; padding:30px 0px 0px 0px; border-top:1px dotted #C8C8C8; }
+#wrap { margin:0px auto; text-align:left; font-size: 13px; line-height: 1.4; }
+#container { float:right; }
+#sidebar { overflow:hidden; clear:left; text-align:right; width:250px; height:auto; padding:0px 15px 0px 0px; border-right:1px dotted #C8C8C8; }
+#sidebar li { list-style-type:none; }
+#sidebar > li { margin:20px 0px; }
+#sidebar h1 { border-bottom:1px dotted #C8C8C8; }
+#sidebar .description { display:block; width:100%; height:auto; margin:0px 0px 10px 0px; }
+h1, h2, h3, h4, h5, h6, h7 { margin:0px; text-transform:uppercase; }
+h4, h5, h6 { font-size:14px; }
+h1 { padding:0px 0px 15px; margin:0px 0px 15px 0px; }
diff --git a/nikola/data/themes/monospace/bundles b/nikola/data/themes/monospace/bundles
new file mode 100644
index 0000000..aa35d9c
--- /dev/null
+++ b/nikola/data/themes/monospace/bundles
@@ -0,0 +1 @@
+assets/css/all.css=rst.css,code.css,theme.css
diff --git a/nikola/data/themes/monospace/messages b/nikola/data/themes/monospace/messages
new file mode 120000
index 0000000..3047ea2
--- /dev/null
+++ b/nikola/data/themes/monospace/messages
@@ -0,0 +1 @@
+../default/messages/ \ No newline at end of file
diff --git a/nikola/data/themes/monospace/templates/base.tmpl b/nikola/data/themes/monospace/templates/base.tmpl
new file mode 100644
index 0000000..7758ded
--- /dev/null
+++ b/nikola/data/themes/monospace/templates/base.tmpl
@@ -0,0 +1,44 @@
+## -*- coding: utf-8 -*-
+<%namespace file="base_helper.tmpl" import="*"/>
+<!DOCTYPE html>
+<html lang="${lang}">
+<head>
+ ${html_head()}
+ <%block name="extra_head">
+ </%block>
+</head>
+<body class="home blog">
+ %if add_this_buttons:
+ <script type="text/javascript">var addthis_config={"ui_language":"${lang}"};</script>
+ % endif
+ <div id="wrap" style="width:850px">
+ <div id="container" style="width:560px">
+ <%block name="content"></%block>
+ </div>
+ <div id="sidebar">
+ <!--Sidebar content-->
+ <h1 id="blog-title">
+ <a href="${abs_link('/')}" title="${blog_title}">${blog_title}</a>
+ </h1>
+ <%block name="belowtitle">
+ %if len(translations) > 1:
+ <small>
+ ${(messages[lang][u"Also available in"])}:&nbsp;
+ ${html_translations()}
+ </small>
+ %endif
+ </%block>
+ <ul class="unstyled">
+ <li>${license}
+ ${html_social()}
+ ${html_sidebar_links()}
+ <li>${search_form}
+ </ul>
+ </div>
+ <div id="footer">
+ ${content_footer}
+ </div>
+ </div>
+ ${analytics}
+ <script type="text/javascript">jQuery("a.image-reference").colorbox({rel:"gal",maxWidth:"80%",maxHeight:"80%",scalePhotos:true});</script>
+</body>
diff --git a/nikola/data/themes/monospace/templates/base_helper.tmpl b/nikola/data/themes/monospace/templates/base_helper.tmpl
new file mode 100644
index 0000000..f5fe80c
--- /dev/null
+++ b/nikola/data/themes/monospace/templates/base_helper.tmpl
@@ -0,0 +1,64 @@
+<%def name="html_head()">
+ <meta charset="utf-8">
+ <meta name="title" content="${title} | ${blog_title}" >
+ <meta name="description" content="${description}" >
+ <meta name="author" content="${blog_author}">
+ <title>${title} | ${blog_title}</title>
+ %if use_bundles:
+<!-- CSS and JS Bundles here -->
+ <link href="/assets/css/all.css" rel="stylesheet" type="text/css">
+ %else:
+<!-- CSS and JS here -->
+ <link href="/assets/css/rst.css" rel="stylesheet" type="text/css">
+ <link href="/assets/css/code.css" rel="stylesheet" type="text/css">
+ <link href="/assets/css/theme.css" rel="stylesheet" type="text/css"/>
+ %if has_custom_css:
+<!-- Custom CSS here -->
+ <link href="/assets/css/custom.css" rel="stylesheet" type="text/css">
+ %endif
+ %endif
+ %if rss_link:
+ ${rss_link}
+ %else:
+ %for language in translations:
+ <link rel="alternate" type="application/rss+xml" title="RSS (${language})" href="${_link('rss', None, lang)}">
+ %endfor
+ %endif
+</%def>
+
+
+<%def name="html_social()">
+%if add_this_buttons:
+ <!-- Social buttons -->
+ <div id="addthisbox" class="addthis_toolbox addthis_peekaboo_style addthis_default_style addthis_label_style addthis_32x32_style">
+ <a class="addthis_button_more">Share</a>
+ <ul><li><a class="addthis_button_facebook"></a></li>
+ <li><a class="addthis_button_google_plusone_share"></a></li>
+ <li><a class="addthis_button_linkedin"></a></li>
+ <li><a class="addthis_button_twitter"></a></li>
+ </ul>
+ </div>
+ <script type="text/javascript" src="http://s7.addthis.com/js/300/addthis_widget.js#pubid=ra-4f7088a56bb93798"></script>
+ <!-- End of social buttons -->
+%endif
+</%def>
+
+
+<%def name="html_sidebar_links()">
+ %for url, text in sidebar_links[lang]:
+ % if rel_link(permalink, url) == "#":
+ <li class="active"><a href="${url}">${text}</a>
+ %else:
+ <li><a href="${url}">${text}</a>
+ %endif
+ %endfor
+</%def>
+
+
+<%def name="html_translations()">
+ %for langname in translations.keys():
+ %if langname != lang:
+ <a href="${_link("index", None, langname)}">${messages[langname]["LANGUAGE"]}</a>
+ %endif
+ %endfor
+</%def>
diff --git a/nikola/data/themes/monospace/templates/gallery.tmpl b/nikola/data/themes/monospace/templates/gallery.tmpl
new file mode 100644
index 0000000..37d749f
--- /dev/null
+++ b/nikola/data/themes/monospace/templates/gallery.tmpl
@@ -0,0 +1,27 @@
+## -*- coding: utf-8 -*-
+<%inherit file="base.tmpl"/>
+<%block name="sourcelink"></%block>
+
+<%block name="content">
+ <ul class="breadcrumb">
+ % for link, crumb in crumbs:
+ <li><a href="${link}">/ ${crumb}</a></li>
+ % endfor
+ </ul>
+ %if text:
+ <p>
+ ${text}
+ </p>
+ %endif
+ <ul>
+ % for folder in folders:
+ <li><a href="${folder}"><i class="icon-folder-open"></i>&nbsp;${folder}</a></li>
+ % endfor
+ </ul>
+ <ul class="thumbnails">
+ %for image in images:
+ <li><a href="${image[0]}" class="thumbnail image-reference" ${image[2]}>
+ <img src="${image[1]}" /></a></li>
+ %endfor
+ </ul>
+</%block>
diff --git a/nikola/data/themes/monospace/templates/index.tmpl b/nikola/data/themes/monospace/templates/index.tmpl
new file mode 100644
index 0000000..bbf5529
--- /dev/null
+++ b/nikola/data/themes/monospace/templates/index.tmpl
@@ -0,0 +1,27 @@
+## -*- coding: utf-8 -*-
+<%namespace name="helper" file="index_helper.tmpl"/>
+<%inherit file="base.tmpl"/>
+<%block name="content">
+ % for post in posts:
+ <div class="postbox">
+ <h1><a href="${post.permalink(lang)}">${post.title(lang)}</a></h1>
+ <div class="meta" style="background-color: rgb(234, 234, 234); ">
+ <span class="authordate">
+ ${messages[lang]["Posted"]}: ${post.date.strftime(date_format)}
+ </span>
+ <br>
+ <span class="tags">Tags:&nbsp;
+ %if post.tags:
+ %for tag in post.tags:
+ <a class="tag" href="${_link('tag', tag, lang)}"><span class="badge badge-info">${tag}</span></a>
+ %endfor
+ %endif
+ </span>
+ </div>
+ ${post.text(lang, index_teasers)}
+ ${helper.html_disqus_link(post)}
+ </div>
+ % endfor
+ ${helper.html_pager()}
+ ${helper.html_disqus_script()}
+</%block>
diff --git a/nikola/data/themes/monospace/templates/index_helper.tmpl b/nikola/data/themes/monospace/templates/index_helper.tmpl
new file mode 100644
index 0000000..cfecdf3
--- /dev/null
+++ b/nikola/data/themes/monospace/templates/index_helper.tmpl
@@ -0,0 +1,31 @@
+<%def name="html_pager()">
+<div>
+<ul class="pager">
+ %if prevlink:
+ <li class="previous">
+ <a href="${prevlink}">&larr; ${messages[lang]["Newer posts"]}</a>
+ </li>
+ %endif
+ %if nextlink:
+ <li class="next">
+ <a href="${nextlink}">${messages[lang]["Older posts"]} &rarr;</a>
+ </li>
+ %endif
+</ul>
+</div>
+</%def>
+
+
+<%def name="html_disqus_link(post)">
+ <p>
+ %if disqus_forum:
+ <a href="${post.permalink()}#disqus_thread">Comments</a>
+ %endif
+</%def>
+
+
+<%def name="html_disqus_script()">
+ %if disqus_forum:
+ <script type="text/javascript">var disqus_shortname="${disqus_forum}";(function(){var a=document.createElement("script");a.async=true;a.type="text/javascript";a.src="http://"+disqus_shortname+".disqus.com/count.js";(document.getElementsByTagName("HEAD")[0]||document.getElementsByTagName("BODY")[0]).appendChild(a)}());</script>
+ %endif
+</%def>
diff --git a/nikola/data/themes/monospace/templates/list.tmpl b/nikola/data/themes/monospace/templates/list.tmpl
new file mode 100644
index 0000000..a60b508
--- /dev/null
+++ b/nikola/data/themes/monospace/templates/list.tmpl
@@ -0,0 +1,14 @@
+## -*- coding: utf-8 -*-
+<%inherit file="base.tmpl"/>
+<%block name="content">
+ <!--Body content-->
+ <div class="postbox">
+ <h1>${title}</h1>
+ <ul class="unstyled">
+ % for text, link in items:
+ <li><a href="${link}">${text}</a>
+ % endfor
+ </ul>
+ </div>
+ <!--End of body content-->
+</%block>
diff --git a/nikola/data/themes/monospace/templates/list_post.tmpl b/nikola/data/themes/monospace/templates/list_post.tmpl
new file mode 100644
index 0000000..1a1cdee
--- /dev/null
+++ b/nikola/data/themes/monospace/templates/list_post.tmpl
@@ -0,0 +1,14 @@
+## -*- coding: utf-8 -*-
+<%inherit file="base.tmpl"/>
+<%block name="content">
+ <!--Body content-->
+ <div class="postbox">
+ <h1>${title}</h1>
+ <ul class="unstyled">
+ % for post in posts:
+ <li><a href="${post.permalink(lang)}">[${post.date.strftime(date_format)}] ${post.title(lang)}</a>
+ % endfor
+ </ul>
+ </div>
+ <!--End of body content-->
+</%block>
diff --git a/nikola/data/themes/monospace/templates/listing.tmpl b/nikola/data/themes/monospace/templates/listing.tmpl
new file mode 100644
index 0000000..596a704
--- /dev/null
+++ b/nikola/data/themes/monospace/templates/listing.tmpl
@@ -0,0 +1,10 @@
+## -*- coding: utf-8 -*-
+<%inherit file="base.tmpl"/>
+<%block name="content">
+<ul class="breadcrumb">
+ % for link, crumb in crumbs:
+ <li><a href="${link}">/ ${crumb}</a></li>
+ % endfor
+</ul>
+${code}
+</%block>
diff --git a/nikola/data/themes/monospace/templates/post.tmpl b/nikola/data/themes/monospace/templates/post.tmpl
new file mode 100644
index 0000000..94a74f8
--- /dev/null
+++ b/nikola/data/themes/monospace/templates/post.tmpl
@@ -0,0 +1,28 @@
+## -*- coding: utf-8 -*-
+<%namespace name="helper" file="post_helper.tmpl"/>
+<%inherit file="base.tmpl"/>
+<%block name="content">
+ <div class="post">
+ ${helper.html_title()}
+ <div class="meta" style="background-color: rgb(234, 234, 234); ">
+ <span class="authordate">
+ ${messages[lang]["Posted"]}: ${post.date.strftime(date_format)} [<a href="${post.pagenames[lang]+'.txt'}">${messages[lang]["Source"]}</a>]
+ </span>
+ <br>
+ %if post.tags:
+ <span class="tags">${messages[lang]["Tags"]}:&nbsp;
+ %for tag in post.tags:
+ <a class="tag" href="${_link('tag', tag, lang)}"><span class="badge badge-info">${tag}</span></a>
+ %endfor
+ </span>
+ <br>
+ %endif
+ <span class="authordate">
+ ${helper.html_translations(post)}
+ </span>
+ </div>
+ ${post.text(lang)}
+ ${helper.html_pager(post)}
+ ${helper.html_disqus(post)}
+ </div>
+</%block>
diff --git a/nikola/data/themes/monospace/templates/post_helper.tmpl b/nikola/data/themes/monospace/templates/post_helper.tmpl
new file mode 100644
index 0000000..3e874e9
--- /dev/null
+++ b/nikola/data/themes/monospace/templates/post_helper.tmpl
@@ -0,0 +1,54 @@
+<%def name="html_title()">
+ <h1>${title}</h1>
+ % if link:
+ <p><a href='${link}'>${messages[lang]["Original site"]}</a></p>
+ % endif
+</%def>
+
+
+<%def name="html_translations(post)">
+ %if len(translations) > 1:
+ %for langname in translations.keys():
+ %if langname != lang:
+ &nbsp;&nbsp;|&nbsp;&nbsp;
+ <a href="${post.permalink(langname)}">${messages[langname]["Read in English"]}</a>
+ %endif
+ %endfor
+ %endif
+</%def>
+
+
+<%def name="html_tags(post)">
+ %if post.tags:
+ &nbsp;&nbsp;|&nbsp;&nbsp;${messages[lang]["More posts about"]}
+ %for tag in post.tags:
+ <a class="tag" href="${_link('tag', tag, lang)}"><span class="badge badge-info">${tag}</span></a>
+ %endfor
+ %endif
+</%def>
+
+
+<%def name="html_disqus(post)">
+ %if disqus_forum:
+ <div id="disqus_thread"></div>
+ <script type="text/javascript">var disqus_shortname="${disqus_forum}";var disqus_url="${post.permalink(absolute=True)}";(function(){var a=document.createElement("script");a.type="text/javascript";a.async=true;a.src="http://"+disqus_shortname+".disqus.com/embed.js";(document.getElementsByTagName("head")[0]||document.getElementsByTagName("body")[0]).appendChild(a)})(); </script>
+ <noscript>Please enable JavaScript to view the <a href="http://disqus.com/?ref_noscript">comments powered by Disqus.</a></noscript>
+ <a href="http://disqus.com" class="dsq-brlink">comments powered by <span class="logo-disqus">Disqus</span></a>
+ %endif
+</%def>
+
+
+<%def name="html_pager(post)">
+ <ul class="pager">
+ %if post.prev_post:
+ <li class="previous">
+ <a href="${post.prev_post.permalink(lang)}">&larr; ${messages[lang]["Previous post"]}</a>
+ </li>
+ %endif
+ %if post.next_post:
+ <li class="next">
+ <a href="${post.next_post.permalink(lang)}">${messages[lang]["Next post"]} &rarr;</a>
+ </li>
+ %endif
+ </ul>
+</%def>
diff --git a/nikola/data/themes/monospace/templates/story.tmpl b/nikola/data/themes/monospace/templates/story.tmpl
new file mode 100644
index 0000000..deb0a46
--- /dev/null
+++ b/nikola/data/themes/monospace/templates/story.tmpl
@@ -0,0 +1,8 @@
+## -*- coding: utf-8 -*-
+<%inherit file="post.tmpl"/>
+<%block name="content">
+%if title:
+ <h1>${title}</h1>
+%endif
+ ${post.text(lang)}
+</%block>
diff --git a/nikola/data/themes/monospace/templates/tag.tmpl b/nikola/data/themes/monospace/templates/tag.tmpl
new file mode 100644
index 0000000..7c89ad1
--- /dev/null
+++ b/nikola/data/themes/monospace/templates/tag.tmpl
@@ -0,0 +1,7 @@
+## -*- coding: utf-8 -*-
+<%inherit file="list_post.tmpl"/>
+<%block name="extra_head">
+ %for language in translations:
+ <link rel="alternate" type="application/rss+xml" type="application/rss+xml" title="RSS for tag ${tag} (${language})" href="${_link("tag_rss", tag, lang)}">
+ %endfor
+</%block>
diff --git a/nikola/data/themes/monospace/templates/tags.tmpl b/nikola/data/themes/monospace/templates/tags.tmpl
new file mode 100644
index 0000000..369a3d5
--- /dev/null
+++ b/nikola/data/themes/monospace/templates/tags.tmpl
@@ -0,0 +1,14 @@
+## -*- coding: utf-8 -*-
+<%inherit file="base.tmpl"/>
+<%block name="content">
+ <div class="postbox">
+ <!--Body content-->
+ <h1>${title}</h1>
+ <ul class="unstyled">
+ % for text, link in items:
+ <li><a class="tag" href="${link}"><span class="badge badge-info">${text}</span></a>
+ % endfor
+ </ul>
+ <!--End of body content-->
+ </div>
+</%block>
diff --git a/nikola/data/themes/orphan/assets/css/code.css b/nikola/data/themes/orphan/assets/css/code.css
new file mode 120000
index 0000000..6b2b872
--- /dev/null
+++ b/nikola/data/themes/orphan/assets/css/code.css
@@ -0,0 +1 @@
+../../../default/assets/css/code.css \ No newline at end of file
diff --git a/nikola/data/themes/orphan/assets/css/colorbox.css b/nikola/data/themes/orphan/assets/css/colorbox.css
new file mode 120000
index 0000000..2c48dd8
--- /dev/null
+++ b/nikola/data/themes/orphan/assets/css/colorbox.css
@@ -0,0 +1 @@
+../../../default/assets/css/colorbox.css \ No newline at end of file
diff --git a/nikola/data/themes/orphan/assets/css/rst.css b/nikola/data/themes/orphan/assets/css/rst.css
new file mode 120000
index 0000000..2e56146
--- /dev/null
+++ b/nikola/data/themes/orphan/assets/css/rst.css
@@ -0,0 +1 @@
+../../../default/assets/css/rst.css \ No newline at end of file
diff --git a/nikola/data/themes/orphan/assets/css/theme.css b/nikola/data/themes/orphan/assets/css/theme.css
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/nikola/data/themes/orphan/assets/css/theme.css
diff --git a/nikola/data/themes/orphan/bundles b/nikola/data/themes/orphan/bundles
new file mode 100644
index 0000000..aa35d9c
--- /dev/null
+++ b/nikola/data/themes/orphan/bundles
@@ -0,0 +1 @@
+assets/css/all.css=rst.css,code.css,theme.css
diff --git a/nikola/data/themes/orphan/messages b/nikola/data/themes/orphan/messages
new file mode 120000
index 0000000..3047ea2
--- /dev/null
+++ b/nikola/data/themes/orphan/messages
@@ -0,0 +1 @@
+../default/messages/ \ No newline at end of file
diff --git a/nikola/data/themes/orphan/templates/base.tmpl b/nikola/data/themes/orphan/templates/base.tmpl
new file mode 100644
index 0000000..f280d08
--- /dev/null
+++ b/nikola/data/themes/orphan/templates/base.tmpl
@@ -0,0 +1,36 @@
+## -*- coding: utf-8 -*-
+<%namespace file="base_helper.tmpl" import="*"/>
+<!DOCTYPE html>
+<html lang="${lang}">
+<head>
+ ${html_head()}
+ <%block name="extra_head">
+ </%block>
+</head>
+<body>
+ %if add_this_buttons:
+ <script type="text/javascript">var addthis_config={"ui_language":"${lang}"};</script>
+ % endif
+ <h1 id="blog-title">
+ <a href="${abs_link('/')}" title="${blog_title}">${blog_title}</a>
+ </h1>
+ <%block name="belowtitle">
+ %if len(translations) > 1:
+ <small>
+ ${(messages[lang][u"Also available in"])}:&nbsp;
+ ${html_translations()}
+ </small>
+ %endif
+ </%block>
+ <%block name="content"></%block>
+ <small>${content_footer}</small>
+ <!--Sidebar content-->
+ <ul class="unstyled">
+ <li>${license}
+ ${html_social()}
+ ${html_sidebar_links()}
+ <li>${search_form}
+ </ul>
+ ${analytics}
+ <script type="text/javascript">jQuery("a.image-reference").colorbox({rel:"gal",maxWidth:"80%",maxHeight:"80%",scalePhotos:true});</script>
+</body>
diff --git a/nikola/data/themes/orphan/templates/base_helper.tmpl b/nikola/data/themes/orphan/templates/base_helper.tmpl
new file mode 100644
index 0000000..f5fe80c
--- /dev/null
+++ b/nikola/data/themes/orphan/templates/base_helper.tmpl
@@ -0,0 +1,64 @@
+<%def name="html_head()">
+ <meta charset="utf-8">
+ <meta name="title" content="${title} | ${blog_title}" >
+ <meta name="description" content="${description}" >
+ <meta name="author" content="${blog_author}">
+ <title>${title} | ${blog_title}</title>
+ %if use_bundles:
+<!-- CSS and JS Bundles here -->
+ <link href="/assets/css/all.css" rel="stylesheet" type="text/css">
+ %else:
+<!-- CSS and JS here -->
+ <link href="/assets/css/rst.css" rel="stylesheet" type="text/css">
+ <link href="/assets/css/code.css" rel="stylesheet" type="text/css">
+ <link href="/assets/css/theme.css" rel="stylesheet" type="text/css"/>
+ %if has_custom_css:
+<!-- Custom CSS here -->
+ <link href="/assets/css/custom.css" rel="stylesheet" type="text/css">
+ %endif
+ %endif
+ %if rss_link:
+ ${rss_link}
+ %else:
+ %for language in translations:
+ <link rel="alternate" type="application/rss+xml" title="RSS (${language})" href="${_link('rss', None, lang)}">
+ %endfor
+ %endif
+</%def>
+
+
+<%def name="html_social()">
+%if add_this_buttons:
+ <!-- Social buttons -->
+ <div id="addthisbox" class="addthis_toolbox addthis_peekaboo_style addthis_default_style addthis_label_style addthis_32x32_style">
+ <a class="addthis_button_more">Share</a>
+ <ul><li><a class="addthis_button_facebook"></a></li>
+ <li><a class="addthis_button_google_plusone_share"></a></li>
+ <li><a class="addthis_button_linkedin"></a></li>
+ <li><a class="addthis_button_twitter"></a></li>
+ </ul>
+ </div>
+ <script type="text/javascript" src="http://s7.addthis.com/js/300/addthis_widget.js#pubid=ra-4f7088a56bb93798"></script>
+ <!-- End of social buttons -->
+%endif
+</%def>
+
+
+<%def name="html_sidebar_links()">
+ %for url, text in sidebar_links[lang]:
+ % if rel_link(permalink, url) == "#":
+ <li class="active"><a href="${url}">${text}</a>
+ %else:
+ <li><a href="${url}">${text}</a>
+ %endif
+ %endfor
+</%def>
+
+
+<%def name="html_translations()">
+ %for langname in translations.keys():
+ %if langname != lang:
+ <a href="${_link("index", None, langname)}">${messages[langname]["LANGUAGE"]}</a>
+ %endif
+ %endfor
+</%def>
diff --git a/nikola/data/themes/orphan/templates/gallery.tmpl b/nikola/data/themes/orphan/templates/gallery.tmpl
new file mode 100644
index 0000000..37d749f
--- /dev/null
+++ b/nikola/data/themes/orphan/templates/gallery.tmpl
@@ -0,0 +1,27 @@
+## -*- coding: utf-8 -*-
+<%inherit file="base.tmpl"/>
+<%block name="sourcelink"></%block>
+
+<%block name="content">
+ <ul class="breadcrumb">
+ % for link, crumb in crumbs:
+ <li><a href="${link}">/ ${crumb}</a></li>
+ % endfor
+ </ul>
+ %if text:
+ <p>
+ ${text}
+ </p>
+ %endif
+ <ul>
+ % for folder in folders:
+ <li><a href="${folder}"><i class="icon-folder-open"></i>&nbsp;${folder}</a></li>
+ % endfor
+ </ul>
+ <ul class="thumbnails">
+ %for image in images:
+ <li><a href="${image[0]}" class="thumbnail image-reference" ${image[2]}>
+ <img src="${image[1]}" /></a></li>
+ %endfor
+ </ul>
+</%block>
diff --git a/nikola/data/themes/orphan/templates/index.tmpl b/nikola/data/themes/orphan/templates/index.tmpl
new file mode 100644
index 0000000..03dd1f8
--- /dev/null
+++ b/nikola/data/themes/orphan/templates/index.tmpl
@@ -0,0 +1,18 @@
+## -*- coding: utf-8 -*-
+<%namespace name="helper" file="index_helper.tmpl"/>
+<%inherit file="base.tmpl"/>
+<%block name="content">
+ % for post in posts:
+ <div class="postbox">
+ <h1><a href="${post.permalink(lang)}">${post.title(lang)}</a>
+ <small>&nbsp;&nbsp;
+ ${messages[lang]["Posted"]}: ${post.date.strftime(date_format)}
+ </small></h1>
+ <hr>
+ ${post.text(lang, index_teasers)}
+ ${helper.html_disqus_link(post)}
+ </div>
+ % endfor
+ ${helper.html_pager()}
+ ${helper.html_disqus_script()}
+</%block>
diff --git a/nikola/data/themes/orphan/templates/index_helper.tmpl b/nikola/data/themes/orphan/templates/index_helper.tmpl
new file mode 100644
index 0000000..cfecdf3
--- /dev/null
+++ b/nikola/data/themes/orphan/templates/index_helper.tmpl
@@ -0,0 +1,31 @@
+<%def name="html_pager()">
+<div>
+<ul class="pager">
+ %if prevlink:
+ <li class="previous">
+ <a href="${prevlink}">&larr; ${messages[lang]["Newer posts"]}</a>
+ </li>
+ %endif
+ %if nextlink:
+ <li class="next">
+ <a href="${nextlink}">${messages[lang]["Older posts"]} &rarr;</a>
+ </li>
+ %endif
+</ul>
+</div>
+</%def>
+
+
+<%def name="html_disqus_link(post)">
+ <p>
+ %if disqus_forum:
+ <a href="${post.permalink()}#disqus_thread">Comments</a>
+ %endif
+</%def>
+
+
+<%def name="html_disqus_script()">
+ %if disqus_forum:
+ <script type="text/javascript">var disqus_shortname="${disqus_forum}";(function(){var a=document.createElement("script");a.async=true;a.type="text/javascript";a.src="http://"+disqus_shortname+".disqus.com/count.js";(document.getElementsByTagName("HEAD")[0]||document.getElementsByTagName("BODY")[0]).appendChild(a)}());</script>
+ %endif
+</%def>
diff --git a/nikola/data/themes/orphan/templates/list.tmpl b/nikola/data/themes/orphan/templates/list.tmpl
new file mode 100644
index 0000000..a60b508
--- /dev/null
+++ b/nikola/data/themes/orphan/templates/list.tmpl
@@ -0,0 +1,14 @@
+## -*- coding: utf-8 -*-
+<%inherit file="base.tmpl"/>
+<%block name="content">
+ <!--Body content-->
+ <div class="postbox">
+ <h1>${title}</h1>
+ <ul class="unstyled">
+ % for text, link in items:
+ <li><a href="${link}">${text}</a>
+ % endfor
+ </ul>
+ </div>
+ <!--End of body content-->
+</%block>
diff --git a/nikola/data/themes/orphan/templates/list_post.tmpl b/nikola/data/themes/orphan/templates/list_post.tmpl
new file mode 100644
index 0000000..1a1cdee
--- /dev/null
+++ b/nikola/data/themes/orphan/templates/list_post.tmpl
@@ -0,0 +1,14 @@
+## -*- coding: utf-8 -*-
+<%inherit file="base.tmpl"/>
+<%block name="content">
+ <!--Body content-->
+ <div class="postbox">
+ <h1>${title}</h1>
+ <ul class="unstyled">
+ % for post in posts:
+ <li><a href="${post.permalink(lang)}">[${post.date.strftime(date_format)}] ${post.title(lang)}</a>
+ % endfor
+ </ul>
+ </div>
+ <!--End of body content-->
+</%block>
diff --git a/nikola/data/themes/orphan/templates/listing.tmpl b/nikola/data/themes/orphan/templates/listing.tmpl
new file mode 100644
index 0000000..596a704
--- /dev/null
+++ b/nikola/data/themes/orphan/templates/listing.tmpl
@@ -0,0 +1,10 @@
+## -*- coding: utf-8 -*-
+<%inherit file="base.tmpl"/>
+<%block name="content">
+<ul class="breadcrumb">
+ % for link, crumb in crumbs:
+ <li><a href="${link}">/ ${crumb}</a></li>
+ % endfor
+</ul>
+${code}
+</%block>
diff --git a/nikola/data/themes/orphan/templates/post.tmpl b/nikola/data/themes/orphan/templates/post.tmpl
new file mode 100644
index 0000000..306192d
--- /dev/null
+++ b/nikola/data/themes/orphan/templates/post.tmpl
@@ -0,0 +1,20 @@
+## -*- coding: utf-8 -*-
+<%namespace name="helper" file="post_helper.tmpl"/>
+<%inherit file="base.tmpl"/>
+<%block name="content">
+ <div class="postbox">
+ ${helper.html_title()}
+ <hr>
+ <small>
+ ${messages[lang]["Posted"]}: ${post.date.strftime(date_format)}
+ ${helper.html_translations(post)}
+ &nbsp;&nbsp;|&nbsp;&nbsp;
+ <a href="${post.pagenames[lang]+'.txt'}">${messages[lang]["Source"]}</a>
+ ${helper.html_tags(post)}
+ </small>
+ <hr>
+ ${post.text(lang)}
+ ${helper.html_pager(post)}
+ ${helper.html_disqus(post)}
+ </div>
+</%block>
diff --git a/nikola/data/themes/orphan/templates/post_helper.tmpl b/nikola/data/themes/orphan/templates/post_helper.tmpl
new file mode 100644
index 0000000..3e874e9
--- /dev/null
+++ b/nikola/data/themes/orphan/templates/post_helper.tmpl
@@ -0,0 +1,54 @@
+<%def name="html_title()">
+ <h1>${title}</h1>
+ % if link:
+ <p><a href='${link}'>${messages[lang]["Original site"]}</a></p>
+ % endif
+</%def>
+
+
+<%def name="html_translations(post)">
+ %if len(translations) > 1:
+ %for langname in translations.keys():
+ %if langname != lang:
+ &nbsp;&nbsp;|&nbsp;&nbsp;
+ <a href="${post.permalink(langname)}">${messages[langname]["Read in English"]}</a>
+ %endif
+ %endfor
+ %endif
+</%def>
+
+
+<%def name="html_tags(post)">
+ %if post.tags:
+ &nbsp;&nbsp;|&nbsp;&nbsp;${messages[lang]["More posts about"]}
+ %for tag in post.tags:
+ <a class="tag" href="${_link('tag', tag, lang)}"><span class="badge badge-info">${tag}</span></a>
+ %endfor
+ %endif
+</%def>
+
+
+<%def name="html_disqus(post)">
+ %if disqus_forum:
+ <div id="disqus_thread"></div>
+ <script type="text/javascript">var disqus_shortname="${disqus_forum}";var disqus_url="${post.permalink(absolute=True)}";(function(){var a=document.createElement("script");a.type="text/javascript";a.async=true;a.src="http://"+disqus_shortname+".disqus.com/embed.js";(document.getElementsByTagName("head")[0]||document.getElementsByTagName("body")[0]).appendChild(a)})(); </script>
+ <noscript>Please enable JavaScript to view the <a href="http://disqus.com/?ref_noscript">comments powered by Disqus.</a></noscript>
+ <a href="http://disqus.com" class="dsq-brlink">comments powered by <span class="logo-disqus">Disqus</span></a>
+ %endif
+</%def>
+
+
+<%def name="html_pager(post)">
+ <ul class="pager">
+ %if post.prev_post:
+ <li class="previous">
+ <a href="${post.prev_post.permalink(lang)}">&larr; ${messages[lang]["Previous post"]}</a>
+ </li>
+ %endif
+ %if post.next_post:
+ <li class="next">
+ <a href="${post.next_post.permalink(lang)}">${messages[lang]["Next post"]} &rarr;</a>
+ </li>
+ %endif
+ </ul>
+</%def>
diff --git a/nikola/data/themes/orphan/templates/story.tmpl b/nikola/data/themes/orphan/templates/story.tmpl
new file mode 100644
index 0000000..deb0a46
--- /dev/null
+++ b/nikola/data/themes/orphan/templates/story.tmpl
@@ -0,0 +1,8 @@
+## -*- coding: utf-8 -*-
+<%inherit file="post.tmpl"/>
+<%block name="content">
+%if title:
+ <h1>${title}</h1>
+%endif
+ ${post.text(lang)}
+</%block>
diff --git a/nikola/data/themes/orphan/templates/tag.tmpl b/nikola/data/themes/orphan/templates/tag.tmpl
new file mode 100644
index 0000000..7c89ad1
--- /dev/null
+++ b/nikola/data/themes/orphan/templates/tag.tmpl
@@ -0,0 +1,7 @@
+## -*- coding: utf-8 -*-
+<%inherit file="list_post.tmpl"/>
+<%block name="extra_head">
+ %for language in translations:
+ <link rel="alternate" type="application/rss+xml" type="application/rss+xml" title="RSS for tag ${tag} (${language})" href="${_link("tag_rss", tag, lang)}">
+ %endfor
+</%block>
diff --git a/nikola/data/themes/orphan/templates/tags.tmpl b/nikola/data/themes/orphan/templates/tags.tmpl
new file mode 100644
index 0000000..369a3d5
--- /dev/null
+++ b/nikola/data/themes/orphan/templates/tags.tmpl
@@ -0,0 +1,14 @@
+## -*- coding: utf-8 -*-
+<%inherit file="base.tmpl"/>
+<%block name="content">
+ <div class="postbox">
+ <!--Body content-->
+ <h1>${title}</h1>
+ <ul class="unstyled">
+ % for text, link in items:
+ <li><a class="tag" href="${link}"><span class="badge badge-info">${text}</span></a>
+ % endfor
+ </ul>
+ <!--End of body content-->
+ </div>
+</%block>
diff --git a/nikola/data/themes/site/assets/css/theme.css b/nikola/data/themes/site/assets/css/theme.css
index 6b21e2e..32c9f24 100644
--- a/nikola/data/themes/site/assets/css/theme.css
+++ b/nikola/data/themes/site/assets/css/theme.css
@@ -1,5 +1,11 @@
-body { margin-top: 50px;}
-
+body {
+ padding-top: 60px;
+}
+@media (max-width: 979px) {
+ body {
+ padding-top: 0px;
+ }
+}
#container {
width: 960px;
margin: 50 auto;
@@ -23,5 +29,3 @@ img {
.footerbox {padding: 15px; text-align: center; margin-bottom: 15px;}
-#addthisbox {top: 180px; left:10px;}
-
diff --git a/nikola/data/themes/site/templates/base.tmpl b/nikola/data/themes/site/templates/base.tmpl
index 46e9b39..784f29a 100644
--- a/nikola/data/themes/site/templates/base.tmpl
+++ b/nikola/data/themes/site/templates/base.tmpl
@@ -1,40 +1,9 @@
## -*- coding: utf-8 -*-
+<%namespace file="base_helper.tmpl" import="*"/>
<!DOCTYPE html>
<html lang="${lang}">
<head>
- <meta charset="utf-8">
- <meta name="title" content="${title} | ${blog_title}" >
- <meta name="description" content="${description}" >
- <meta name="author" content="${blog_author}">
- <title>${title} | ${blog_title}</title>
- <!-- Le styles -->
- %if use_bundles:
- <link href="/assets/css/all.css" rel="stylesheet" type="text/css">
- <script src="/assets/js/all.js" type="text/javascript"></script>
- %else:
- <link href="/assets/css/bootstrap.css" rel="stylesheet" type="text/css">
- <link href="/assets/css/bootstrap-responsive.css" rel="stylesheet" type="text/css">
- <link href="/assets/css/rst.css" rel="stylesheet" type="text/css">
- <link href="/assets/css/code.css" rel="stylesheet" type="text/css">
- <link href="/assets/css/colorbox.css" rel="stylesheet" type="text/css"/>
- <link href="/assets/css/theme.css" rel="stylesheet" type="text/css"/>
- %if exists("files/assets/css/custom.css", not_empty=True):
- <link href="/assets/css/custom.css" rel="stylesheet" type="text/css">
- %endif
- <script src="/assets/js/jquery-1.7.2.min.js" type="text/javascript"></script>
- <script src="/assets/js/jquery.colorbox-min.js" type="text/javascript"></script>
- %endif
- <!-- Le HTML5 shim, for IE6-8 support of HTML5 elements -->
- <!--[if lt IE 9]>
- <script src="http://html5shim.googlecode.com/svn/trunk/html5.js"></script>
- <![endif]-->
- %if rss_link:
- ${rss_link}
- %else:
- %for language in translations:
- <link rel="alternate" type="application/rss+xml" title="RSS (${language})" href="${_link("rss", None, lang)}">
- %endfor
- %endif
+ ${html_head()}
<%block name="extra_head">
</%block>
</head>
@@ -47,13 +16,7 @@
${blog_title}
</a>
<ul class="nav">
- %for url, text in sidebar_links[lang]:
- % if rel_link(permalink, url) == "#":
- <li class="active"><a href="${url}">${text}</a>
- %else:
- <li><a href="${url}">${text}</a>
- %endif
- %endfor
+ ${html_sidebar_links()}
</ul>
%if search_form:
${search_form}
@@ -61,13 +24,7 @@
<ul class="nav pull-right">
<%block name="belowtitle">
%if len(translations) > 1:
- <li>
- %for langname in translations.keys():
- %if langname != lang:
- <a href="${_link("index", None, langname)}">${messages[langname]["LANGUAGE"]}</a>
- %endif
- %endfor
- </li>
+ <li>${html_translations()}</li>
%endif
</%block>
<%block name="sourcelink"> </%block>
@@ -76,7 +33,6 @@
</div>
</div>
<!-- End of Menubar -->
-
<div class="container" id="container">
<!--Body content-->
<%block name="content"></%block>
@@ -85,20 +41,7 @@
${content_footer}
</div>
</div>
- %if add_this_buttons:
- <!-- addthis -->
- <div id="addthisbox" class="addthis_bar addthis_bar_vertical addthis_bar_small">
- <div class="addthis_toolbox addthis_default_style">
- <span><a class="addthis_button_preferred_1"></a></span>
- <span><a class="addthis_button_preferred_2"></a></span>
- <span><a class="addthis_button_preferred_3"></a></span>
- <span><a class="addthis_button_preferred_4"></a></span>
- <span><a class="addthis_button_compact"></a></span>
- </div>
- </div>
- <script type="text/javascript" src="http://s7.addthis.com/js/300/addthis_widget.js#pubid=ra-4f7088a56bb93798"></script>
- <!-- End of addthis -->
- %endif
+${html_social()}
${analytics}
<script type="text/javascript">jQuery("a.image-reference").colorbox({rel:"gal",maxWidth:"80%",maxHeight:"80%",scalePhotos:true});</script>
</body>
diff --git a/nikola/data/themes/site/templates/post.tmpl b/nikola/data/themes/site/templates/post.tmpl
index f777366..06ca10f 100644
--- a/nikola/data/themes/site/templates/post.tmpl
+++ b/nikola/data/themes/site/templates/post.tmpl
@@ -1,50 +1,19 @@
## -*- coding: utf-8 -*-
+<%namespace name="helper" file="post_helper.tmpl"/>
<%inherit file="base.tmpl"/>
<%block name="content">
<div class="postbox">
- <h1>${title}</h1>
- % if link:
- <p><a href='${link}'>${messages[lang]["Original site"]}</a></p>
- % endif
+ ${helper.html_title()}
<hr>
<small>
- ${messages[lang]["Posted"]}: ${post.date}
-
- %if len(translations) > 1:
- %for langname in translations.keys():
- %if langname != lang:
- &nbsp;&nbsp;|&nbsp;&nbsp;
- <a href="${post.permalink(langname)}">${messages[langname][u"Read in English"]}</a>
- %endif
- %endfor
- %endif
- %if post.tags:
- &nbsp;&nbsp;|&nbsp;&nbsp;${messages[lang]["More posts about"]}
- %for tag in post.tags:
- <a href="${_link("tag", tag, lang)}"><span class="badge badge-info">${tag}</span></a>
- %endfor
- %endif
+ ${messages[lang]["Posted"]}: ${post.date.strftime(date_format)}
+ ${helper.html_translations(post)}
+ ${helper.html_tags(post)}
</small>
<hr>
${post.text(lang)}
- <ul class="pager">
- %if post.prev_post:
- <li class="previous">
- <a href="${post.prev_post.permalink(lang)}">&larr; ${messages[lang]["Previous post"]}</a>
- </li>
- %endif
- %if post.next_post:
- <li class="next">
- <a href="${post.next_post.permalink(lang)}">${messages[lang]["Next post"]} &rarr;</a>
- </li>
- %endif
- </ul>
- %if disqus_forum:
- <div id="disqus_thread"></div>
- <script type="text/javascript">var disqus_shortname="${disqus_forum}";var disqus_url="${post.permalink(absolute=True)}";(function(){var a=document.createElement("script");a.type="text/javascript";a.async=true;a.src="http://"+disqus_shortname+".disqus.com/embed.js";(document.getElementsByTagName("head")[0]||document.getElementsByTagName("body")[0]).appendChild(a)})();</script>
- <noscript>Please enable JavaScript to view the <a href="http://disqus.com/?ref_noscript">comments powered by Disqus.</a></noscript>
- <a href="http://disqus.com" class="dsq-brlink">comments powered by <span class="logo-disqus">Disqus</span></a>
- %endif
+ ${helper.html_pager(post)}
+ ${helper.html_disqus(post)}
</div>
</%block>
diff --git a/nikola/filters.py b/nikola/filters.py
index f450d10..15696f1 100644
--- a/nikola/filters.py
+++ b/nikola/filters.py
@@ -1,6 +1,31 @@
+# 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.
+
"""Utility functions to help you run filters on files."""
import os
+import re
import shutil
import subprocess
import tempfile
@@ -23,11 +48,53 @@ def runinplace(command, infile):
tmpdir = tempfile.mkdtemp()
tmpfname = os.path.join(tmpdir, os.path.basename(infile))
- command = command.replace('%1', infile)
- command = command.replace('%2', tmpfname)
+ command = command.replace('%1', "'%s'" % infile)
+
+ needs_tmp = "%2" in command
+ command = command.replace('%2', "'%s'"% tmpfname)
+
subprocess.check_call(command, shell=True)
- shutil.move(tmpfname, infile)
+
+ if needs_tmp:
+ shutil.move(tmpfname, infile)
def yui_compressor(infile):
- return runinplace(r'yui-compressor %1 -o %2', infile)
+ return runinplace(r'yui-compressor --nomunge %1 -o %2', infile)
+
+def optipng(infile):
+ return runinplace(r"optipng -preserve -o2 -quiet %1", infile)
+
+def jpegoptim(infile):
+ return runinplace(r"jpegoptim -p --strip-all -q %1",infile)
+
+def tidy(inplace):
+ # Goggle site verifcation files are no HTML
+ if re.match(r"google[a-f0-9]+.html", os.path.basename(inplace)) \
+ and open(inplace).readline().startswith("google-site-verification:"):
+ return
+
+ # Tidy will give error exits, that we will ignore.
+ output = subprocess.check_output( "tidy -m -w 90 --indent no --quote-marks no --keep-time yes --tidy-mark no '%s'; exit 0" % inplace, stderr = subprocess.STDOUT, shell = True )
+
+ for line in output.split( "\n" ):
+ if "Warning:" in line:
+ if '<meta> proprietary attribute "charset"' in line:
+ # We want to set it though.
+ continue
+ elif '<meta> lacks "content" attribute' in line:
+ # False alarm to me.
+ continue
+ elif '<div> anchor' in line and 'already defined' in line:
+ # Some seeming problem with JavaScript terminators.
+ continue
+ elif '<img> lacks "alt" attribute' in line:
+ # Happens in gallery code, probably can be tolerated.
+ continue
+ elif '<table> lacks "summary" attribute' in line:
+ # Happens for tables, TODO: Check this is normal.
+ continue
+ else:
+ assert False, (inplace,line)
+ elif "Error:" in line:
+ assert False, line
diff --git a/nikola/nikola.py b/nikola/nikola.py
index 8b69d02..4ce6f61 100644
--- a/nikola/nikola.py
+++ b/nikola/nikola.py
@@ -1,11 +1,38 @@
# -*- 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
from collections import defaultdict
from copy import copy
import glob
import os
import sys
-import urlparse
+try:
+ from urlparse import urlparse, urlsplit, urljoin
+except ImportError:
+ from urllib.parse import urlparse, urlsplit, urljoin
import lxml.html
from yapsy.PluginManager import PluginManager
@@ -17,9 +44,9 @@ else:
import logging
logging.basicConfig(level=logging.ERROR)
-from post import Post
-import utils
-from plugin_categories import (
+from .post import Post
+from . import utils
+from .plugin_categories import (
Command,
LateTask,
PageCompiler,
@@ -56,6 +83,7 @@ class Nikola(object):
'ARCHIVE_FILENAME': "archive.html",
'DEFAULT_LANG': "en",
'OUTPUT_FOLDER': 'output',
+ 'CACHE_FOLDER': 'cache',
'FILES_FOLDERS': {'files': ''},
'LISTINGS_FOLDER': 'listings',
'ADD_THIS_BUTTONS': True,
@@ -94,8 +122,8 @@ class Nikola(object):
})
self.plugin_manager.setPluginInfoExtension('plugin')
self.plugin_manager.setPluginPlaces([
- os.path.join(os.path.dirname(__file__), 'plugins'),
- os.path.join(os.getcwd(), 'plugins'),
+ str(os.path.join(os.path.dirname(__file__), 'plugins')),
+ str(os.path.join(os.getcwd(), 'plugins')),
])
self.plugin_manager.collectPlugins()
@@ -128,6 +156,17 @@ class Nikola(object):
self.GLOBAL_CONTEXT['index_display_post_count'] = self.config[
'INDEX_DISPLAY_POST_COUNT']
self.GLOBAL_CONTEXT['use_bundles'] = self.config['USE_BUNDLES']
+ if 'date_format' not in self.GLOBAL_CONTEXT:
+ self.GLOBAL_CONTEXT['date_format'] = '%Y-%m-%d %H:%M'
+
+ # check if custom css exist and is not empty
+ for files_path in list(self.config['FILES_FOLDERS'].keys()):
+ custom_css_path = os.path.join(files_path, 'assets/css/custom.css')
+ if self.file_exists(custom_css_path, not_empty=True):
+ self.GLOBAL_CONTEXT['has_custom_css'] = True
+ break
+ else:
+ self.GLOBAL_CONTEXT['has_custom_css'] = False
# Load template plugin
template_sys_name = utils.get_template_engine(self.THEMES)
@@ -138,9 +177,10 @@ class Nikola(object):
% template_sys_name)
sys.exit(1)
self.template_system = pi.plugin_object
- self.template_system.set_directories(
- [os.path.join(utils.get_theme_path(name), "templates")
- for name in self.THEMES])
+ lookup_dirs = [os.path.join(utils.get_theme_path(name), "templates")
+ for name in self.THEMES]
+ self.template_system.set_directories(lookup_dirs,
+ self.config['CACHE_FOLDER'])
# Load compiler plugins
self.compilers = {}
@@ -165,7 +205,7 @@ class Nikola(object):
except KeyError:
# Find the correct compiler for this files extension
langs = [lang for lang, exts in
- self.config['post_compilers'].items()
+ list(self.config['post_compilers'].items())
if ext in exts]
if len(langs) != 1:
if len(set(langs)) > 1:
@@ -199,14 +239,14 @@ class Nikola(object):
# This is to support windows paths
url_part = "/".join(url_part.split(os.sep))
- src = urlparse.urljoin(self.config["BLOG_URL"], url_part)
+ src = urljoin(self.config["BLOG_URL"], url_part)
- parsed_src = urlparse.urlsplit(src)
+ parsed_src = urlsplit(src)
src_elems = parsed_src.path.split('/')[1:]
def replacer(dst):
# Refuse to replace links that are full URLs.
- dst_url = urlparse.urlparse(dst)
+ dst_url = urlparse(dst)
if dst_url.netloc:
if dst_url.scheme == 'link': # Magic link
dst = self.link(dst_url.netloc, dst_url.path.lstrip('/'),
@@ -215,12 +255,12 @@ class Nikola(object):
return dst
# Normalize
- dst = urlparse.urljoin(src, dst)
+ dst = urljoin(src, dst)
# Avoid empty links.
if src == dst:
return "#"
# Check that link can be made relative, otherwise return dest
- parsed_dst = urlparse.urlsplit(dst)
+ parsed_dst = urlsplit(dst)
if parsed_src[:2] != parsed_dst[:2]:
return dst
@@ -252,8 +292,8 @@ class Nikola(object):
pass
doc = lxml.html.document_fromstring(data)
doc.rewrite_links(replacer)
- data = '<!DOCTYPE html>' + lxml.html.tostring(doc, encoding='utf8')
- with open(output_name, "w+") as post_file:
+ data = b'<!DOCTYPE html>' + lxml.html.tostring(doc, encoding='utf8')
+ with open(output_name, "wb+") as post_file:
post_file.write(data)
def path(self, kind, name, lang, is_link=False):
@@ -283,41 +323,39 @@ class Nikola(object):
path = []
if kind == "tag_index":
- path = filter(None, [self.config['TRANSLATIONS'][lang],
- self.config['TAG_PATH'], 'index.html'])
+ path = [_f for _f in [self.config['TRANSLATIONS'][lang],
+ self.config['TAG_PATH'], 'index.html'] if _f]
elif kind == "tag":
if self.config['SLUG_TAG_PATH']:
name = utils.slugify(name)
- path = filter(None, [self.config['TRANSLATIONS'][lang],
- self.config['TAG_PATH'], name + ".html"])
+ path = [_f for _f in [self.config['TRANSLATIONS'][lang],
+ self.config['TAG_PATH'], name + ".html"] if _f]
elif kind == "tag_rss":
if self.config['SLUG_TAG_PATH']:
name = utils.slugify(name)
- path = filter(None, [self.config['TRANSLATIONS'][lang],
- self.config['TAG_PATH'], name + ".xml"])
+ path = [_f for _f in [self.config['TRANSLATIONS'][lang],
+ self.config['TAG_PATH'], name + ".xml"] if _f]
elif kind == "index":
if name > 0:
- path = filter(None, [self.config['TRANSLATIONS'][lang],
- self.config['INDEX_PATH'], 'index-%s.html' % name])
+ path = [_f for _f in [self.config['TRANSLATIONS'][lang],
+ self.config['INDEX_PATH'], 'index-%s.html' % name] if _f]
else:
- path = filter(None, [self.config['TRANSLATIONS'][lang],
- self.config['INDEX_PATH'], 'index.html'])
+ path = [_f for _f in [self.config['TRANSLATIONS'][lang],
+ self.config['INDEX_PATH'], 'index.html'] if _f]
elif kind == "rss":
- path = filter(None, [self.config['TRANSLATIONS'][lang],
- self.config['RSS_PATH'], 'rss.xml'])
+ path = [_f for _f in [self.config['TRANSLATIONS'][lang],
+ self.config['RSS_PATH'], 'rss.xml'] if _f]
elif kind == "archive":
if name:
- path = filter(None, [self.config['TRANSLATIONS'][lang],
- self.config['ARCHIVE_PATH'], name, 'index.html'])
+ path = [_f for _f in [self.config['TRANSLATIONS'][lang],
+ self.config['ARCHIVE_PATH'], name, 'index.html'] if _f]
else:
- path = filter(None, [self.config['TRANSLATIONS'][lang],
- self.config['ARCHIVE_PATH'], self.config['ARCHIVE_FILENAME']])
+ path = [_f for _f in [self.config['TRANSLATIONS'][lang],
+ self.config['ARCHIVE_PATH'], self.config['ARCHIVE_FILENAME']] if _f]
elif kind == "gallery":
- path = filter(None,
- [self.config['GALLERY_PATH'], name, 'index.html'])
+ path = [_f for _f in [self.config['GALLERY_PATH'], name, 'index.html'] if _f]
elif kind == "listing":
- path = filter(None,
- [self.config['LISTINGS_FOLDER'], name + '.html'])
+ path = [_f for _f in [self.config['LISTINGS_FOLDER'], name + '.html'] if _f]
if is_link:
return '/' + ('/'.join(path))
else:
@@ -328,20 +366,20 @@ class Nikola(object):
def abs_link(self, dst):
# Normalize
- dst = urlparse.urljoin(self.config['BLOG_URL'], dst)
+ dst = urljoin(self.config['BLOG_URL'], dst)
- return urlparse.urlparse(dst).path
+ return urlparse(dst).path
def rel_link(self, src, dst):
# Normalize
- src = urlparse.urljoin(self.config['BLOG_URL'], src)
- dst = urlparse.urljoin(src, dst)
+ src = urljoin(self.config['BLOG_URL'], src)
+ dst = urljoin(src, dst)
# Avoid empty links.
if src == dst:
return "#"
# Check that link can be made relative, otherwise return dest
- parsed_src = urlparse.urlsplit(src)
- parsed_dst = urlparse.urlsplit(dst)
+ parsed_src = urlsplit(src)
+ parsed_dst = urlsplit(dst)
if parsed_src[:2] != parsed_dst[:2]:
return dst
# Now both paths are on the same site and absolute
@@ -379,7 +417,7 @@ class Nikola(object):
task_dep.append(pluginInfo.plugin_object.name)
yield {
- 'name': 'all',
+ 'name': b'all',
'actions': None,
'clean': True,
'task_dep': task_dep
@@ -388,18 +426,22 @@ class Nikola(object):
def scan_posts(self):
"""Scan all the posts."""
if not self._scanned:
- print "Scanning posts ",
+ print("Scanning posts ")
targets = set([])
for wildcard, destination, _, use_in_feeds in \
self.config['post_pages']:
- print ".",
+ print (".")
for base_path in glob.glob(wildcard):
- post = Post(base_path, destination, use_in_feeds,
+ post = Post(
+ base_path,
+ self.config['CACHE_FOLDER'],
+ destination,
+ use_in_feeds,
self.config['TRANSLATIONS'],
self.config['DEFAULT_LANG'],
self.config['BLOG_URL'],
self.MESSAGES)
- for lang, langpath in self.config['TRANSLATIONS'].items():
+ for lang, langpath in list(self.config['TRANSLATIONS'].items()):
dest = (destination, langpath, post.pagenames[lang])
if dest in targets:
raise Exception(
@@ -414,9 +456,9 @@ class Nikola(object):
self.posts_per_tag[tag].append(post.post_name)
else:
self.pages.append(post)
- for name, post in self.global_data.items():
+ for name, post in list(self.global_data.items()):
self.timeline.append(post)
- self.timeline.sort(cmp=lambda a, b: cmp(a.date, b.date))
+ self.timeline.sort(key=lambda p: p.date)
self.timeline.reverse()
post_timeline = [p for p in self.timeline if p.use_in_feeds]
for i, p in enumerate(post_timeline[1:]):
@@ -424,7 +466,7 @@ class Nikola(object):
for i, p in enumerate(post_timeline[:-1]):
p.prev_post = post_timeline[i + 1]
self._scanned = True
- print "done!"
+ print("done!")
def generic_page_renderer(self, lang, wildcard,
template_name, destination, filters):
@@ -497,4 +539,4 @@ class Nikola(object):
'uptodate': [config_changed(deps_context)]
}
- yield utils.apply_filters(task, filters)
+ return utils.apply_filters(task, filters)
diff --git a/nikola/plugin_categories.py b/nikola/plugin_categories.py
index cc59b24..bf810e3 100644
--- a/nikola/plugin_categories.py
+++ b/nikola/plugin_categories.py
@@ -1,3 +1,27 @@
+# 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.
+
__all__ = [
'Command',
'LateTask',
@@ -58,8 +82,8 @@ class TemplateSystem(object):
name = "dummy templates"
- def set_directories(self, directories):
- """Sets the list of folders where templates are located."""
+ def set_directories(self, directories, cache_folder):
+ """Sets the list of folders where templates are located and cache."""
raise Exception("Implement Me First")
def template_deps(self, template_name):
diff --git a/nikola/plugins/__init__.py b/nikola/plugins/__init__.py
new file mode 100644
index 0000000..ec6c8f5
--- /dev/null
+++ b/nikola/plugins/__init__.py
@@ -0,0 +1,4 @@
+from __future__ import absolute_import
+
+from . import command_import_wordpress
+
diff --git a/nikola/plugins/command_bootswatch_theme.py b/nikola/plugins/command_bootswatch_theme.py
index f077eb1..185717f 100644
--- a/nikola/plugins/command_bootswatch_theme.py
+++ b/nikola/plugins/command_bootswatch_theme.py
@@ -1,6 +1,35 @@
+# 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
from optparse import OptionParser
import os
-import urllib2
+
+try:
+ import requests
+except ImportError:
+ requests = None
from nikola.plugin_categories import Command
@@ -12,7 +41,9 @@ class CommandBootswatchTheme(Command):
def run(self, *args):
"""Given a swatch name and a parent theme, creates a custom theme."""
-
+ if requests is None:
+ print('To use the install_theme command, you need to install the "requests" package.')
+ return
parser = OptionParser(usage="nikola %s [options]" % self.name)
parser.add_option("-n", "--name", dest="name",
help="New theme name (default: custom)", default='custom')
@@ -27,21 +58,21 @@ class CommandBootswatchTheme(Command):
swatch = options.swatch
parent = options.parent
- print "Creating '%s' theme from '%s' and '%s'" % (
- name, swatch, parent)
+ print("Creating '%s' theme from '%s' and '%s'" % (
+ name, swatch, parent))
try:
os.makedirs(os.path.join('themes', name, 'assets', 'css'))
except:
pass
for fname in ('bootstrap.min.css', 'bootstrap.css'):
url = 'http://bootswatch.com/%s/%s' % (swatch, fname)
- print "Downloading: ", url
- data = urllib2.urlopen(url).read()
+ print("Downloading: ", url)
+ data = requests.get(url).text
with open(os.path.join(
'themes', name, 'assets', 'css', fname), 'wb+') as output:
output.write(data)
with open(os.path.join('themes', name, 'parent'), 'wb+') as output:
output.write(parent)
- print 'Theme created. Change the THEME setting to "%s" to use it.'\
- % name
+ print('Theme created. Change the THEME setting to "%s" to use it.'
+ % name)
diff --git a/nikola/plugins/command_build.py b/nikola/plugins/command_build.py
index cface15..867cbf9 100644
--- a/nikola/plugins/command_build.py
+++ b/nikola/plugins/command_build.py
@@ -1,3 +1,28 @@
+# 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 unicode_literals
import os
import tempfile
@@ -14,7 +39,7 @@ class CommandBuild(Command):
# FIXME: this is crap, do it right
with tempfile.NamedTemporaryFile(suffix='.py', delete=False) as dodo:
- dodo.write('''
+ dodo.write(b'''
from doit.reporter import ExecutedOnlyReporter
DOIT_CONFIG = {
'reporter': ExecutedOnlyReporter,
@@ -29,4 +54,11 @@ def task_render_site():
return SITE.gen_tasks()
''')
dodo.flush()
- os.system('doit -f %s -d . %s' % (dodo.name, ' '.join(args)))
+ first = args[0] if args else None
+ if first in ('auto', 'clean', 'forget', 'ignore', 'list', 'run'):
+ cmd = first
+ args = args[1:]
+ else:
+ cmd = 'run'
+ os.system('doit %s -f %s -d . %s' % (cmd, dodo.name, ' '.join(args)))
+
diff --git a/nikola/plugins/command_check.py b/nikola/plugins/command_check.py
index ce1e2e3..5fc8bfe 100644
--- a/nikola/plugins/command_check.py
+++ b/nikola/plugins/command_check.py
@@ -1,8 +1,36 @@
+# 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
from optparse import OptionParser
import os
import sys
-import urllib
-from urlparse import urlparse
+try:
+ from urllib import unquote
+ from urlparse import urlparse
+except ImportError:
+ from urllib.parse import unquote, urlparse
import lxml.html
@@ -48,24 +76,24 @@ def analize(task):
target = target.split('#')[0]
target_filename = os.path.abspath(
os.path.join(os.path.dirname(filename),
- urllib.unquote(target)))
+ unquote(target)))
if target_filename not in existing_targets:
if os.path.exists(target_filename):
existing_targets.add(target_filename)
else:
- print "In %s broken link: " % filename, target
+ print("In %s broken link: " % filename, target)
if '--find-sources' in sys.argv:
- print "Possible sources:"
- print os.popen(
- 'nikola build list --deps %s' % task, 'r').read()
- print "===============================\n"
+ print("Possible sources:")
+ print(os.popen(
+ 'nikola build list --deps %s' % task, 'r').read())
+ print("===============================\n")
except Exception as exc:
- print "Error with:", filename, exc
+ print("Error with:", filename, exc)
def scan_links():
- print "Checking Links:\n===============\n"
+ print("Checking Links:\n===============\n")
for task in os.popen('nikola build list --all', 'r').readlines():
task = task.strip()
if task.split(':')[0] in (
@@ -79,7 +107,7 @@ def scan_links():
def scan_files():
- print "Checking Files:\n===============\n"
+ print("Checking Files:\n===============\n")
task_fnames = set([])
real_fnames = set([])
# First check that all targets are generated in the right places
@@ -97,13 +125,13 @@ def scan_files():
only_on_output = list(real_fnames - task_fnames)
if only_on_output:
only_on_output.sort()
- print "\nFiles from unknown origins:\n"
+ print("\nFiles from unknown origins:\n")
for f in only_on_output:
- print f
+ print(f)
only_on_input = list(task_fnames - real_fnames)
if only_on_input:
only_on_input.sort()
- print "\nFiles not generated:\n"
+ print("\nFiles not generated:\n")
for f in only_on_input:
- print f
+ print(f)
diff --git a/nikola/plugins/command_console.plugin b/nikola/plugins/command_console.plugin
new file mode 100644
index 0000000..003b994
--- /dev/null
+++ b/nikola/plugins/command_console.plugin
@@ -0,0 +1,9 @@
+[Core]
+Name = console
+Module = command_console
+
+[Documentation]
+Author = Roberto Alsina
+Version = 0.1
+Website = http://nikola.ralsina.com.ar
+Description = Start a debugging python console
diff --git a/nikola/plugins/command_console.py b/nikola/plugins/command_console.py
new file mode 100644
index 0000000..ea517a0
--- /dev/null
+++ b/nikola/plugins/command_console.py
@@ -0,0 +1,35 @@
+# 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 os
+
+from nikola.plugin_categories import Command
+
+
+class Deploy(Command):
+ """Start debugging console."""
+ name = "console"
+
+ def run(self, *args):
+ os.system('python -i -c "from nikola.console import *"')
diff --git a/nikola/plugins/command_deploy.py b/nikola/plugins/command_deploy.py
index cb2eb41..48d6e91 100644
--- a/nikola/plugins/command_deploy.py
+++ b/nikola/plugins/command_deploy.py
@@ -1,3 +1,28 @@
+# 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
from optparse import OptionParser
import os
@@ -12,5 +37,5 @@ class Deploy(Command):
parser = OptionParser(usage="nikola %s [options]" % self.name)
(options, args) = parser.parse_args(list(args))
for command in self.site.config['DEPLOY_COMMANDS']:
- print "==>", command
+ print("==>", command)
os.system(command)
diff --git a/nikola/plugins/command_import_wordpress.py b/nikola/plugins/command_import_wordpress.py
index e75d022..1552da4 100644
--- a/nikola/plugins/command_import_wordpress.py
+++ b/nikola/plugins/command_import_wordpress.py
@@ -1,11 +1,45 @@
+# 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 unicode_literals, print_function
import codecs
+import csv
import os
-from urlparse import urlparse
-from urllib import urlopen
+import re
+try:
+ from urlparse import urlparse
+except ImportError:
+ from urllib.parse import urlparse
-from lxml import etree, html
+from lxml import etree, html, builder
from mako.template import Template
+try:
+ import requests
+except ImportError:
+ requests = None
+
from nikola.plugin_categories import Command
from nikola import utils
@@ -17,38 +51,67 @@ class CommandImportWordpress(Command):
name = "import_wordpress"
- def run(self, fname=None):
- # Parse the data
- if fname is None:
- print "Usage: nikola import_wordpress wordpress_dump.xml"
- return
- context = {}
- with open(fname) as fd:
- xml = []
+ @staticmethod
+ def read_xml_file(filename):
+ xml = []
+
+ with open(filename, 'rb') as fd:
for line in fd:
# These explode etree and are useless
- if '<atom:link rel=' in line:
+ if b'<atom:link rel=' in line:
continue
xml.append(line)
- xml = '\n'.join(xml)
+ xml = b'\n'.join(xml)
- tree = etree.fromstring(xml)
+ return xml
+
+ @classmethod
+ def get_channel_from_file(cls, filename):
+ tree = etree.fromstring(cls.read_xml_file(filename))
channel = tree.find('channel')
+ return channel
+
+ @staticmethod
+ def configure_redirections(url_map):
+ redirections = []
+ for k, v in url_map.items():
+ # remove the initial "/" because src is a relative file path
+ src = (urlparse(k).path + 'index.html')[1:]
+ dst = (urlparse(v).path)
+ if src == 'index.html':
+ print("Can't do a redirect for: %r" % k)
+ else:
+ redirections.append((src, dst))
+
+ return redirections
+ @staticmethod
+ def generate_base_site(context):
+ os.system('nikola init new_site')
+ conf_template = Template(filename=os.path.join(
+ os.path.dirname(utils.__file__), 'conf.py.in'))
+
+ return conf_template
+
+ @staticmethod
+ def populate_context(channel):
+ wordpress_namespace = channel.nsmap['wp']
+
+ context = {}
context['DEFAULT_LANG'] = get_text_tag(channel, 'language', 'en')[:2]
- context['BLOG_TITLE'] = get_text_tag(
- channel, 'title', 'PUT TITLE HERE')
+ context['BLOG_TITLE'] = get_text_tag(channel, 'title',
+ 'PUT TITLE HERE')
context['BLOG_DESCRIPTION'] = get_text_tag(
channel, 'description', 'PUT DESCRIPTION HERE')
context['BLOG_URL'] = get_text_tag(channel, 'link', '#')
- author = channel.find('{http://wordpress.org/export/1.2/}author')
+ author = channel.find('{%s}author' % wordpress_namespace)
context['BLOG_EMAIL'] = get_text_tag(
author,
- '{http://wordpress.org/export/1.2/}author_email',
+ '{%s}author_email' % wordpress_namespace,
"joe@example.com")
context['BLOG_AUTHOR'] = get_text_tag(
author,
- '{http://wordpress.org/export/1.2/}author_display_name',
+ '{%s}author_display_name' % wordpress_namespace,
"Joe Example")
context['POST_PAGES'] = '''(
("posts/*.wp", "posts", "post.tmpl", True),
@@ -61,19 +124,149 @@ class CommandImportWordpress(Command):
}
'''
- # Generate base site
- os.system('nikola init new_site')
- conf_template = Template(filename=os.path.join(
- os.path.dirname(utils.__file__), 'data', 'samplesite', 'conf.py.in'))
- with codecs.open(os.path.join('new_site', 'conf.py'),
- 'w+', 'utf8') as fd:
- fd.write(conf_template.render(**context))
+ return context
- # Import posts
- for item in channel.findall('item'):
- import_attachment(item)
+ @staticmethod
+ def download_url_content_to_file(url, dst_path):
+ with open(dst_path, 'wb+') as fd:
+ fd.write(requests.get(url).content)
+
+ def import_attachment(self, item, wordpress_namespace):
+ url = get_text_tag(item, '{%s}attachment_url' % wordpress_namespace, 'foo')
+ link = get_text_tag(item, '{%s}link' % wordpress_namespace, 'foo')
+ path = urlparse(url).path
+ dst_path = os.path.join(*(['new_site', 'files']
+ + list(path.split('/'))))
+ dst_dir = os.path.dirname(dst_path)
+ if not os.path.isdir(dst_dir):
+ os.makedirs(dst_dir)
+ print("Downloading %s => %s" % (url, dst_path))
+ self.download_url_content_to_file(url, dst_path)
+ dst_url = '/'.join(dst_path.split(os.sep)[2:])
+ links[link] = '/' + dst_url
+ links[url] = '/' + dst_url
+
+ @staticmethod
+ def write_content(filename, content):
+ with open(filename, "wb+") as fd:
+ if content.strip():
+ # Handle sourcecode pseudo-tags
+ content = re.sub('\[sourcecode language="([^"]+)"\]',
+ "\n~~~~~~~~~~~~{.\\1}\n", content)
+ content = content.replace('[/sourcecode]', "\n~~~~~~~~~~~~\n")
+ doc = html.document_fromstring(content)
+ doc.rewrite_links(replacer)
+ # Replace H1 elements with H2 elements
+ for tag in doc.findall('.//h1'):
+ if not tag.text:
+ print("Failed to fix bad title: %r" %
+ html.tostring(tag))
+ else:
+ tag.getparent().replace(tag, builder.E.h2(tag.text))
+ fd.write(html.tostring(doc, encoding='utf8'))
+
+ @staticmethod
+ def write_metadata(filename, title, slug, post_date, description, tags):
+ with codecs.open(filename, "w+", "utf8") as fd:
+ fd.write('%s\n' % title)
+ fd.write('%s\n' % slug)
+ fd.write('%s\n' % post_date)
+ fd.write('%s\n' % ','.join(tags))
+ fd.write('\n')
+ fd.write('%s\n' % description)
+
+ def import_item(self, item, wordpress_namespace, out_folder=None):
+ """Takes an item from the feed and creates a post file."""
+ if out_folder is None:
+ out_folder = 'posts'
+
+ title = get_text_tag(item, 'title', 'NO TITLE')
+ # 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)
+ if not slug: # it happens if the post has no "nice" URL
+ slug = get_text_tag(item, '{%s}post_name' % wordpress_namespace, None)
+ if not slug: # it *may* happen
+ slug = get_text_tag(item, '{%s}post_id' % wordpress_namespace, None)
+ if not slug: # should never happen
+ print("Error converting post:", title)
+ return
+
+ description = get_text_tag(item, 'description', '')
+ post_date = get_text_tag(item, '{%s}post_date' % wordpress_namespace, None)
+ status = get_text_tag(item, '{%s}status' % wordpress_namespace, 'publish')
+ content = get_text_tag(
+ item, '{http://purl.org/rss/1.0/modules/content/}encoded', '')
+
+ tags = []
+ if status != 'publish':
+ tags.append('draft')
+ for tag in item.findall('category'):
+ text = tag.text
+ if text == 'Uncategorized':
+ continue
+ tags.append(text)
+
+ self.url_map[link] = self.context['BLOG_URL'] + '/' + \
+ out_folder + '/' + slug + '.html'
+
+ self.write_metadata(os.path.join('new_site', out_folder,
+ slug + '.meta'),
+ title, slug, post_date, description, tags)
+ self.write_content(
+ os.path.join('new_site', out_folder, slug + '.wp'), content)
+
+ def process_item(self, item):
+ # The namespace usually is something like:
+ # http://wordpress.org/export/1.2/
+ wordpress_namespace = item.nsmap['wp']
+ post_type = get_text_tag(item, '{%s}post_type' % wordpress_namespace, 'post')
+
+ if post_type == 'attachment':
+ self.import_attachment(item, wordpress_namespace)
+ elif post_type == 'post':
+ self.import_item(item, wordpress_namespace, 'posts')
+ else:
+ self.import_item(item, wordpress_namespace, 'stories')
+
+ def import_posts(self, channel):
for item in channel.findall('item'):
- import_item(item)
+ self.process_item(item)
+
+ @staticmethod
+ def write_urlmap_csv(output_file, url_map):
+ with codecs.open(output_file, 'w+', 'utf8') as fd:
+ csv_writer = csv.writer(fd)
+ for item in url_map.items():
+ csv_writer.writerow(item)
+
+ @staticmethod
+ def write_configuration(filename, rendered_template):
+ with codecs.open(filename, 'w+', 'utf8') as fd:
+ fd.write(rendered_template)
+
+ def run(self, fname=None):
+ # Parse the data
+ if requests is None:
+ print('To use the import_wordpress command, you have to install the "requests" package.')
+ return
+ if fname is None:
+ print("Usage: nikola import_wordpress wordpress_dump.xml")
+ return
+
+ self.url_map = {}
+ channel = self.get_channel_from_file(fname)
+ self.context = self.populate_context(channel)
+ conf_template = self.generate_base_site(self.context)
+ self.context['REDIRECTIONS'] = self.configure_redirections(
+ self.url_map)
+
+ self.import_posts(channel)
+ self.write_urlmap_csv(
+ os.path.join('new_site', 'url_map.csv'), self.url_map)
+ self.write_configuration(os.path.join(
+ 'new_site', 'conf.py'), conf_template.render(**self.context))
def replacer(dst):
@@ -81,83 +274,10 @@ def replacer(dst):
def get_text_tag(tag, name, default):
+ if tag is None:
+ return default
t = tag.find(name)
if t is not None:
return t.text
else:
return default
-
-
-def import_attachment(item):
- post_type = get_text_tag(item,
- '{http://wordpress.org/export/1.2/}post_type', 'post')
- if post_type == 'attachment':
- url = get_text_tag(item,
- '{http://wordpress.org/export/1.2/}attachment_url', 'foo')
- link = get_text_tag(item,
- '{http://wordpress.org/export/1.2/}link', 'foo')
- path = urlparse(url).path
- dst_path = os.path.join(*(['new_site', 'files']
- + list(path.split('/'))))
- dst_dir = os.path.dirname(dst_path)
- if not os.path.isdir(dst_dir):
- os.makedirs(dst_dir)
- print "Downloading %s => %s" % (url, dst_path)
- with open(dst_path, 'wb+') as fd:
- fd.write(urlopen(url).read())
- dst_url = '/'.join(dst_path.split(os.sep)[2:])
- links[link] = '/' + dst_url
- links[url] = '/' + dst_url
- return
-
-
-def import_item(item):
- """Takes an item from the feed and creates a post file."""
- title = get_text_tag(item, 'title', 'NO TITLE')
- # link is something like http://foo.com/2012/09/01/hello-world/
- # So, take the path, utils.slugify it, and that's our slug
- slug = utils.slugify(urlparse(get_text_tag(item, 'link', None)).path)
- description = get_text_tag(item, 'description', '')
- post_date = get_text_tag(item,
- '{http://wordpress.org/export/1.2/}post_date', None)
- post_type = get_text_tag(item,
- '{http://wordpress.org/export/1.2/}post_type', 'post')
- status = get_text_tag(item,
- '{http://wordpress.org/export/1.2/}status', 'publish')
- content = get_text_tag(item,
- '{http://purl.org/rss/1.0/modules/content/}encoded', '')
-
- tags = []
- if status != 'publish':
- tags.append('draft')
- for tag in item.findall('category'):
- text = tag.text
- if text == 'Uncategorized':
- continue
- tags.append(text)
-
- if post_type == 'attachment':
- return
- elif post_type == 'post':
- out_folder = 'posts'
- else:
- out_folder = 'stories'
- # Write metadata
- with codecs.open(os.path.join('new_site', out_folder, slug + '.meta'),
- "w+", "utf8") as fd:
- fd.write(u'%s\n' % title)
- fd.write(u'%s\n' % slug)
- fd.write(u'%s\n' % post_date)
- fd.write(u'%s\n' % ','.join(tags))
- fd.write(u'\n')
- fd.write(u'%s\n' % description)
- with open(os.path.join(
- 'new_site', out_folder, slug + '.wp'), "wb+") as fd:
- if content.strip():
- try:
- doc = html.document_fromstring(content)
- doc.rewrite_links(replacer)
- fd.write(html.tostring(doc, encoding='utf8'))
- except:
- import pdb
- pdb.set_trace()
diff --git a/nikola/plugins/command_init.py b/nikola/plugins/command_init.py
index a032370..0b56482 100644
--- a/nikola/plugins/command_init.py
+++ b/nikola/plugins/command_init.py
@@ -1,6 +1,34 @@
+# 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
from optparse import OptionParser
import os
import shutil
+import codecs
+
+from mako.template import Template
import nikola
from nikola.plugin_categories import Command
@@ -17,18 +45,50 @@ That will create a sample site in the specified folder.
The destination folder must not exist.
"""
+ SAMPLE_CONF = {
+ 'BLOG_AUTHOR': "Your Name",
+ 'BLOG_TITLE': "Demo Site",
+ 'BLOG_URL': "http://nikola.ralsina.com.ar",
+ 'BLOG_EMAIL': "joe@demo.site",
+ 'BLOG_DESCRIPTION': "This is a demo site for Nikola.",
+ 'DEFAULT_LANG': "en",
+
+ 'POST_PAGES': """(
+ ("posts/*.txt", "posts", "post.tmpl", True),
+ ("stories/*.txt", "stories", "story.tmpl", False),
+)""",
+
+ 'POST_COMPILERS': """{
+ "rest": ('.txt', '.rst'),
+ "markdown": ('.md', '.mdown', '.markdown'),
+ "html": ('.html', '.htm')
+ }""",
+ 'REDIRECTIONS': '[]',
+ }
+
def run(self, *args):
"""Create a new site."""
parser = OptionParser(usage=self.usage)
(options, args) = parser.parse_args(list(args))
+ if not args:
+ print("Usage: nikola init folder [options]")
+ return
target = args[0]
if target is None:
- print self.usage
+ print(self.usage)
else:
- src = os.path.join(os.path.dirname(nikola.__file__),
- 'data', 'samplesite')
+ # copy sample data
+ lib_path = os.path.dirname(nikola.__file__)
+ src = os.path.join(lib_path, 'data', 'samplesite')
shutil.copytree(src, target)
- print "A new site with some sample data has been created at %s."\
- % target
- print "See README.txt in that folder for more information."
+ # create conf.py
+ template_path = os.path.join(lib_path, 'conf.py.in')
+ conf_template = Template(filename=template_path)
+ conf_path = os.path.join(target, 'conf.py')
+ with codecs.open(conf_path, 'w+', 'utf8') as fd:
+ fd.write(conf_template.render(**self.SAMPLE_CONF))
+
+ print("A new site with some sample data has been created at %s."
+ % target)
+ print("See README.txt in that folder for more information.")
diff --git a/nikola/plugins/command_install_theme.py b/nikola/plugins/command_install_theme.py
index 293ce97..b9ca634 100644
--- a/nikola/plugins/command_install_theme.py
+++ b/nikola/plugins/command_install_theme.py
@@ -1,9 +1,38 @@
+# 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
from optparse import OptionParser
import os
-import urllib2
import json
from io import StringIO
+try:
+ import requests
+except ImportError:
+ requests = None
+
from nikola.plugin_categories import Command
from nikola import utils
@@ -15,7 +44,9 @@ class CommandInstallTheme(Command):
def run(self, *args):
"""Install theme into current site."""
-
+ if requests is None:
+ print('To use the install_theme command, you need to install the "requests" package.')
+ return
parser = OptionParser(usage="nikola %s [options]" % self.name)
parser.add_option("-l", "--list", dest="list",
action="store_true",
@@ -33,15 +64,15 @@ class CommandInstallTheme(Command):
url = options.url
if name is None and not listing:
- print "This command needs either the -n or the -l option."
+ print("This command needs either the -n or the -l option.")
return False
- data = urllib2.urlopen(url).read()
+ data = requests.get(url).text
data = json.loads(data)
if listing:
- print "Themes:"
- print "-------"
+ print("Themes:")
+ print("-------")
for theme in sorted(data.keys()):
- print theme
+ print(theme)
return True
else:
if name in data:
@@ -52,11 +83,11 @@ class CommandInstallTheme(Command):
os.makedirs("themes")
except:
raise OSError("mkdir 'theme' error!")
- print 'Downloading: %s' % data[name]
+ print('Downloading: %s' % data[name])
zip_file = StringIO()
- zip_file.write(urllib2.urlopen(data[name]).read())
- print 'Extracting: %s into themes' % name
+ zip_file.write(requests.get(data[name]).content)
+ print('Extracting: %s into themes' % name)
utils.extract_all(zip_file)
else:
- print "Can't find theme %s" % name
+ print("Can't find theme %s" % name)
return False
diff --git a/nikola/plugins/command_new_post.py b/nikola/plugins/command_new_post.py
index 574df5f..36026be 100644
--- a/nikola/plugins/command_new_post.py
+++ b/nikola/plugins/command_new_post.py
@@ -1,3 +1,28 @@
+# 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 unicode_literals, print_function
import codecs
import datetime
from optparse import OptionParser
@@ -47,12 +72,13 @@ class CommandNewPost(Command):
else:
path = self.site.config['post_pages'][0][0]
- print "Creating New Post"
- print "-----------------\n"
+ print("Creating New Post")
+ print("-----------------\n")
if title is None:
- title = raw_input("Enter title: ").decode(sys.stdin.encoding)
+ print("Enter title: ")
+ title = sys.stdin.readline().decode(sys.stdin.encoding).strip()
else:
- print "Title: ", title
+ print("Title: ", title)
slug = utils.slugify(title)
date = datetime.datetime.now().strftime('%Y/%m/%d %H:%M:%S')
data = [
@@ -72,12 +98,12 @@ class CommandNewPost(Command):
if (not onefile and os.path.isfile(meta_path)) or \
os.path.isfile(txt_path):
- print "The title already exists!"
+ print("The title already exists!")
exit()
if onefile:
if post_format not in ('rest', 'markdown'):
- print "ERROR: Unknown post format %s" % post_format
+ print("ERROR: Unknown post format %s" % post_format)
return
with codecs.open(txt_path, "wb+", "utf8") as fd:
if post_format == 'markdown':
@@ -90,11 +116,11 @@ class CommandNewPost(Command):
fd.write('.. description: \n')
if post_format == 'markdown':
fd.write('-->\n')
- fd.write(u"Write your post here.")
+ fd.write("\nWrite your post here.")
else:
with codecs.open(meta_path, "wb+", "utf8") as fd:
fd.write(data)
with codecs.open(txt_path, "wb+", "utf8") as fd:
- fd.write(u"Write your post here.")
- print "Your post's metadata is at: ", meta_path
- print "Your post's text is at: ", txt_path
+ fd.write("Write your post here.")
+ print("Your post's metadata is at: ", meta_path)
+ print("Your post's text is at: ", txt_path)
diff --git a/nikola/plugins/command_serve.py b/nikola/plugins/command_serve.py
index 626b117..628bba0 100644
--- a/nikola/plugins/command_serve.py
+++ b/nikola/plugins/command_serve.py
@@ -1,7 +1,36 @@
+# 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
from optparse import OptionParser
import os
-from BaseHTTPServer import HTTPServer
-from SimpleHTTPServer import SimpleHTTPRequestHandler
+try:
+ from BaseHTTPServer import HTTPServer
+ from SimpleHTTPServer import SimpleHTTPRequestHandler
+except ImportError:
+ from http.server import HTTPServer
+ from http.server import SimpleHTTPRequestHandler
from nikola.plugin_categories import Command
@@ -25,13 +54,13 @@ class CommandBuild(Command):
out_dir = self.site.config['OUTPUT_FOLDER']
if not os.path.isdir(out_dir):
- print "Error: Missing '%s' folder?" % out_dir
+ print("Error: Missing '%s' folder?" % out_dir)
else:
os.chdir(out_dir)
httpd = HTTPServer((options.address, options.port),
OurHTTPRequestHandler)
sa = httpd.socket.getsockname()
- print "Serving HTTP on", sa[0], "port", sa[1], "..."
+ print("Serving HTTP on", sa[0], "port", sa[1], "...")
httpd.serve_forever()
diff --git a/nikola/plugins/compile_html.py b/nikola/plugins/compile_html.py
index 8241030..24e01fb 100644
--- a/nikola/plugins/compile_html.py
+++ b/nikola/plugins/compile_html.py
@@ -1,3 +1,27 @@
+# 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.
+
"""Implementation of compile_html based on markdown."""
import os
diff --git a/nikola/plugins/compile_markdown/__init__.py b/nikola/plugins/compile_markdown/__init__.py
index 958cfa3..1a58a98 100644
--- a/nikola/plugins/compile_markdown/__init__.py
+++ b/nikola/plugins/compile_markdown/__init__.py
@@ -1,20 +1,49 @@
+# 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.
+
"""Implementation of compile_html based on markdown."""
import codecs
import os
import re
-from markdown import markdown
+try:
+ from markdown import markdown
+except ImportError:
+ markdown = None
from nikola.plugin_categories import PageCompiler
class CompileMarkdown(PageCompiler):
- """Compile reSt into HTML."""
+ """Compile markdown into HTML."""
name = "markdown"
def compile_html(self, source, dest):
+ if markdown is None:
+ raise Exception('To build this site, you need to install the "markdown" package.')
try:
os.makedirs(os.path.dirname(dest))
except:
diff --git a/nikola/plugins/compile_rest/__init__.py b/nikola/plugins/compile_rest/__init__.py
index 0a25a06..0e677e1 100644
--- a/nikola/plugins/compile_rest/__init__.py
+++ b/nikola/plugins/compile_rest/__init__.py
@@ -1,3 +1,28 @@
+# 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 unicode_literals
import codecs
import os
@@ -5,17 +30,16 @@ import docutils.core
import docutils.io
from docutils.parsers.rst import directives
-from pygments_code_block_directive import (
+from .pygments_code_block_directive import (
code_block_directive,
listings_directive)
directives.register_directive('code-block', code_block_directive)
directives.register_directive('listing', listings_directive)
-import pygments_code_block_directive
-# Below is to make pyflakes happy (sigh)
-pygments_code_block_directive
-from youtube import youtube
+from .youtube import youtube
directives.register_directive('youtube', youtube)
+from .slides import slides
+directives.register_directive('slides', slides)
from nikola.plugin_categories import PageCompiler
diff --git a/nikola/plugins/compile_rest/pygments_code_block_directive.py b/nikola/plugins/compile_rest/pygments_code_block_directive.py
index ac91f3c..a83098f 100644
--- a/nikola/plugins/compile_rest/pygments_code_block_directive.py
+++ b/nikola/plugins/compile_rest/pygments_code_block_directive.py
@@ -26,6 +26,7 @@
"""Define and register a code-block directive using pygments"""
+from __future__ import unicode_literals
# Requirements
# ------------
@@ -34,7 +35,10 @@
import codecs
from copy import copy
import os
-import urlparse
+try:
+ from urlparse import urlparse, urlunsplit
+except ImportError:
+ from urllib.parse import urlparse, urlunsplit
from docutils import nodes, core
from docutils.parsers.rst import directives
@@ -90,7 +94,7 @@ class DocutilsInterface(object):
def lex(self):
"""Get lexer for language (use text as fallback)"""
try:
- if self.language and unicode(self.language).lower() != 'none':
+ if self.language and str(self.language).lower() != 'none':
lexer = get_lexer_by_name(self.language.lower(),
**self.custom_args
)
@@ -105,7 +109,7 @@ class DocutilsInterface(object):
"""join subsequent tokens of same token-type
"""
tokens = iter(tokens)
- (lasttype, lastval) = tokens.next()
+ (lasttype, lastval) = next(tokens)
for ttype, value in tokens:
if ttype is lasttype:
lastval += value
@@ -143,7 +147,7 @@ def code_block_directive(name, arguments, options, content, lineno,
content = codecs.open(
options['include'], 'r', encoding).read().rstrip()
except (IOError, UnicodeError): # no file or problem reading it
- content = u''
+ content = ''
line_offset = 0
if content:
# here we define the start-at and end-at options
@@ -163,8 +167,22 @@ def code_block_directive(name, arguments, options, content, lineno,
if after_index < 0:
raise state_machine.reporter.severe(
'Problem with "start-at" option of "%s" '
- 'code-block directive:\nText not found.' %
- options['start-at'])
+ 'code-block directive:\nText not found.'
+ % options['start-at'])
+ # patch mmueller start
+ # Move the after_index to the beginning of the line with the
+ # match.
+ for char in content[after_index:0:-1]:
+ # codecs always opens binary. This works with '\n',
+ # '\r' and '\r\n'. We are going backwards, so
+ # '\n' is found first in '\r\n'.
+ # Going with .splitlines() seems more appropriate
+ # but needs a few more changes.
+ if char == '\n' or char == '\r':
+ break
+ after_index -= 1
+ # patch mmueller end
+
content = content[after_index:]
line_offset = len(content[:after_index].splitlines())
@@ -208,7 +226,7 @@ def code_block_directive(name, arguments, options, content, lineno,
content = content[:before_index]
else:
- content = u'\n'.join(content)
+ content = '\n'.join(content)
if 'tabsize' in options:
tabw = options['tabsize']
@@ -240,7 +258,7 @@ def code_block_directive(name, arguments, options, content, lineno,
else:
# The [:-1] is because pygments adds a trailing \n which looks bad
l = list(DocutilsInterface(content, language, options))
- if l[-1] == ('', u'\n'):
+ if l[-1] == ('', '\n'):
l = l[:-1]
for cls, value in l:
if withln and "\n" in value:
@@ -249,7 +267,7 @@ def code_block_directive(name, arguments, options, content, lineno,
# The first piece, pass as-is
code_block += nodes.Text(values[0], values[0])
# On the second and later pieces, insert \n and linenos
- linenos = range(lineno, lineno + len(values))
+ linenos = list(range(lineno, lineno + len(values)))
for chunk, ln in zip(values, linenos)[1:]:
if ln <= total_lines:
code_block += nodes.inline(fstr % ln, fstr % ln,
@@ -319,7 +337,7 @@ def listings_directive(name, arguments, options, content, lineno,
content_offset, block_text, state, state_machine):
fname = arguments[0]
options['include'] = os.path.join('listings', fname)
- target = urlparse.urlunsplit(("link", 'listing', fname, '', ''))
+ target = urlunsplit(("link", 'listing', fname, '', ''))
generated_nodes = [core.publish_doctree('`%s <%s>`_' % (fname, target))[0]]
generated_nodes += code_block_directive(name, [arguments[1]],
options, content, lineno, content_offset, block_text,
diff --git a/nikola/plugins/compile_rest/slides.py b/nikola/plugins/compile_rest/slides.py
new file mode 100644
index 0000000..942a7d4
--- /dev/null
+++ b/nikola/plugins/compile_rest/slides.py
@@ -0,0 +1,89 @@
+# 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
+
+from docutils import nodes
+from docutils.parsers.rst import Directive, directives
+
+class slides(Directive):
+
+ """ Restructured text extension for inserting slideshows."""
+
+ has_content = True
+ option_spec = {
+ "preload": directives.flag,
+ "preloadImage": directives.uri,
+ "container": directives.unchanged,
+ "generateNextPrev": directives.flag,
+ "next": directives.unchanged,
+ "prev": directives.unchanged,
+ "pagination": directives.flag,
+ "generatePagination": directives.flag,
+ "paginationClass": directives.unchanged,
+ "currentClass": directives.unchanged,
+ "fadeSpeed": directives.positive_int,
+ "fadeEasing": directives.unchanged,
+ "slideSpeed": directives.positive_int,
+ "slideEasing": directives.unchanged,
+ "start": directives.positive_int,
+ "effect": directives.unchanged,
+ "crossfade": directives.flag,
+ "randomize": directives.flag,
+ "play": directives.positive_int,
+ "pause": directives.positive_int,
+ "hoverPause": directives.flag,
+ "autoHeight": directives.flag,
+ "autoHeightSpeed": directives.positive_int,
+ "bigTarget": directives.flag,
+ "animationStart": directives.unchanged,
+ "animationComplete": directives.unchanged,
+ }
+
+ def run(self):
+ if len(self.content) == 0:
+ return
+ for opt in ("preload", "generateNextPrev", "pagination", "generatePagination",
+ "crossfade", "randomize", "hoverPause", "autoHeight", "bigTarget"):
+ if opt in self.options:
+ self.options[opt] = True
+ options = {
+ "autoHeight": True,
+ "bigTarget": True,
+ "paginationClass": "pager",
+ "currentClass": "slide-current"
+ }
+ options.update(self.options)
+ options = json.dumps(options)
+ output = []
+ output.append("""<script> $(function(){ $("#slides").slides(%s); }); </script>""" % options)
+ output.append("""<div id="slides" class="slides"><div class="slides_container">""")
+ for image in self.content:
+ output.append("""<div><img src="%s"></div>""" % image)
+ output.append("""</div></div>""")
+
+ return [nodes.raw('', '\n'.join(output), format='html')]
+
+
+directives.register_directive('slides', slides)
diff --git a/nikola/plugins/compile_rest/youtube.py b/nikola/plugins/compile_rest/youtube.py
index 584160b..0765158 100644
--- a/nikola/plugins/compile_rest/youtube.py
+++ b/nikola/plugins/compile_rest/youtube.py
@@ -1,3 +1,27 @@
+# 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 docutils import nodes
from docutils.parsers.rst import directives
diff --git a/nikola/plugins/task_archive.py b/nikola/plugins/task_archive.py
index 4c97101..cafb7e3 100644
--- a/nikola/plugins/task_archive.py
+++ b/nikola/plugins/task_archive.py
@@ -1,3 +1,27 @@
+# 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 os
from nikola.plugin_categories import Task
@@ -18,39 +42,36 @@ class Archive(Task):
}
self.site.scan_posts()
# TODO add next/prev links for years
- template_name = "list.tmpl"
+ template_name = "list_post.tmpl"
# TODO: posts_per_year is global, kill it
- for year, posts in self.site.posts_per_year.items():
+ for year, posts in list(self.site.posts_per_year.items()):
for lang in kw["translations"]:
output_name = os.path.join(
kw['output_folder'], self.site.path("archive", year, lang))
post_list = [self.site.global_data[post] for post in posts]
- post_list.sort(cmp=lambda a, b: cmp(a.date, b.date))
+ post_list.sort(key=lambda a: a.date)
post_list.reverse()
context = {}
context["lang"] = lang
- context["items"] = [("[%s] %s" %
- (post.date, post.title(lang)), post.permalink(lang))
- for post in post_list]
+ context["posts"] = post_list
context["permalink"] = self.site.link("archive", year, lang)
context["title"] = kw["messages"][lang]["Posts for year %s"]\
% year
- for task in self.site.generic_post_list_renderer(
+ task = self.site.generic_post_list_renderer(
lang,
post_list,
output_name,
template_name,
kw['filters'],
context,
- ):
- task['uptodate'] = [config_changed({
- 1: task['uptodate'][0].config,
- 2: kw})]
- task['basename'] = self.name
- yield task
+ )
+ task_cfg = {1: task['uptodate'][0].config, 2: kw}
+ task['uptodate'] = [config_changed(task_cfg)]
+ task['basename'] = self.name
+ yield task
# And global "all your years" page
- years = self.site.posts_per_year.keys()
+ years = list(self.site.posts_per_year.keys())
years.sort(reverse=True)
template_name = "list.tmpl"
kw['years'] = years
@@ -62,16 +83,15 @@ class Archive(Task):
context["items"] = [(year, self.site.link("archive", year, lang))
for year in years]
context["permalink"] = self.site.link("archive", None, lang)
- for task in self.site.generic_post_list_renderer(
+ task = self.site.generic_post_list_renderer(
lang,
[],
output_name,
template_name,
kw['filters'],
context,
- ):
- task['uptodate'] = [config_changed({
- 1: task['uptodate'][0].config,
- 2: kw})]
- task['basename'] = self.name
- yield task
+ )
+ task_cfg = {1: task['uptodate'][0].config, 2: kw}
+ task['uptodate'] = [config_changed(task_cfg)]
+ task['basename'] = self.name
+ yield task
diff --git a/nikola/plugins/task_copy_assets.py b/nikola/plugins/task_copy_assets.py
index ac31fd7..6b9c6a5 100644
--- a/nikola/plugins/task_copy_assets.py
+++ b/nikola/plugins/task_copy_assets.py
@@ -1,3 +1,27 @@
+# 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 os
from nikola.plugin_categories import Task
@@ -21,7 +45,7 @@ class CopyAssets(Task):
"output_folder": self.site.config['OUTPUT_FOLDER'],
"filters": self.site.config['FILTERS'],
}
-
+ flag = True
tasks = {}
for theme_name in kw['themes']:
src = os.path.join(utils.get_theme_path(theme_name), 'assets')
@@ -32,4 +56,13 @@ class CopyAssets(Task):
tasks[task['name']] = task
task['uptodate'] = [utils.config_changed(kw)]
task['basename'] = self.name
+ flag = False
yield utils.apply_filters(task, kw['filters'])
+
+ if flag:
+ yield {
+ 'basename': self.name,
+ 'name': 'None',
+ 'uptodate': [True],
+ 'actions': [],
+ }
diff --git a/nikola/plugins/task_copy_files.py b/nikola/plugins/task_copy_files.py
index a053905..f8d761d 100644
--- a/nikola/plugins/task_copy_files.py
+++ b/nikola/plugins/task_copy_files.py
@@ -1,3 +1,27 @@
+# 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 os
from nikola.plugin_categories import Task
diff --git a/nikola/plugins/task_create_bundles.py b/nikola/plugins/task_create_bundles.py
index ebca0b7..4903699 100644
--- a/nikola/plugins/task_create_bundles.py
+++ b/nikola/plugins/task_create_bundles.py
@@ -1,3 +1,27 @@
+# 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 os
try:
@@ -25,6 +49,7 @@ class BuildBundles(LateTask):
kw = {
'filters': self.site.config['FILTERS'],
'output_folder': self.site.config['OUTPUT_FOLDER'],
+ 'cache_folder': self.site.config['CACHE_FOLDER'],
'theme_bundles': get_theme_bundles(self.site.THEMES),
}
@@ -32,7 +57,7 @@ class BuildBundles(LateTask):
out_dir = os.path.join(kw['output_folder'], os.path.dirname(output))
inputs = [i for i in inputs if os.path.isfile(
os.path.join(out_dir, i))]
- cache_dir = os.path.join('cache', 'webassets')
+ cache_dir = os.path.join(kw['cache_folder'], 'webassets')
if not os.path.isdir(cache_dir):
os.makedirs(cache_dir)
env = webassets.Environment(out_dir, os.path.dirname(output),
diff --git a/nikola/plugins/task_indexes.py b/nikola/plugins/task_indexes.py
index 2311ef3..6f54145 100644
--- a/nikola/plugins/task_indexes.py
+++ b/nikola/plugins/task_indexes.py
@@ -1,3 +1,27 @@
+# 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 os
from nikola.plugin_categories import Task
@@ -66,16 +90,15 @@ class Indexes(Task):
context["permalink"] = self.site.link("index", i, lang)
output_name = os.path.join(
kw['output_folder'], self.site.path("index", i, lang))
- for task in self.site.generic_post_list_renderer(
+ task = self.site.generic_post_list_renderer(
lang,
post_list,
output_name,
template_name,
kw['filters'],
context,
- ):
- task['uptodate'] = [config_changed({
- 1: task['uptodate'][0].config,
- 2: kw})]
- task['basename'] = 'render_indexes'
- yield task
+ )
+ task_cfg = {1: task['uptodate'][0].config, 2: kw}
+ task['uptodate'] = [config_changed(task_cfg)]
+ task['basename'] = 'render_indexes'
+ yield task
diff --git a/nikola/plugins/task_redirect.py b/nikola/plugins/task_redirect.py
index 7c2ccb1..d7117ec 100644
--- a/nikola/plugins/task_redirect.py
+++ b/nikola/plugins/task_redirect.py
@@ -1,3 +1,27 @@
+# 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 codecs
import os
@@ -33,7 +57,7 @@ class Redirect(Task):
src_path = os.path.join(kw["output_folder"], src)
yield {
'basename': self.name,
- 'name': src_path,
+ 'name': src_path.encode('utf8'),
'targets': [src_path],
'actions': [(create_redirect, (src_path, dst))],
'clean': True,
@@ -42,6 +66,10 @@ class Redirect(Task):
def create_redirect(src, dst):
+ try:
+ os.makedirs(os.path.dirname(src))
+ except:
+ pass
with codecs.open(src, "wb+", "utf8") as fd:
fd.write(('<head>' +
'<meta HTTP-EQUIV="REFRESH" content="0; url=%s">' +
diff --git a/nikola/plugins/task_render_galleries.py b/nikola/plugins/task_render_galleries.py
index 27e13ea..72d0581 100644
--- a/nikola/plugins/task_render_galleries.py
+++ b/nikola/plugins/task_render_galleries.py
@@ -1,3 +1,28 @@
+# 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 unicode_literals
import codecs
import datetime
import glob
@@ -23,7 +48,7 @@ from nikola import utils
class Galleries(Task):
"""Copy theme assets into output."""
- name = "render_galleries"
+ name = str("render_galleries")
dates = {}
def gen_tasks(self):
@@ -33,6 +58,7 @@ class Galleries(Task):
'thumbnail_size': self.site.config['THUMBNAIL_SIZE'],
'max_image_size': self.site.config['MAX_IMAGE_SIZE'],
'output_folder': self.site.config['OUTPUT_FOLDER'],
+ 'cache_folder': self.site.config['CACHE_FOLDER'],
'default_lang': self.site.config['DEFAULT_LANG'],
'blog_description': self.site.config['BLOG_DESCRIPTION'],
'use_filename_as_title': self.site.config['USE_FILENAME_AS_TITLE'],
@@ -48,7 +74,7 @@ class Galleries(Task):
gallery_list.append(root)
if not gallery_list:
yield {
- 'basename': 'render_galleries',
+ 'basename': str('render_galleries'),
'actions': [],
}
return
@@ -66,8 +92,8 @@ class Galleries(Task):
self.site.path("gallery", gallery_name, None)))
if not os.path.isdir(output_gallery):
yield {
- 'basename': 'render_galleries',
- 'name': output_gallery,
+ 'basename': str('render_galleries'),
+ 'name': str(output_gallery),
'actions': [(os.makedirs, (output_gallery,))],
'targets': [output_gallery],
'clean': True,
@@ -91,8 +117,8 @@ class Galleries(Task):
except IOError:
excluded_image_name_list = []
- excluded_image_list = map(add_gallery_path,
- excluded_image_name_list)
+ excluded_image_list = list(map(add_gallery_path,
+ excluded_image_name_list))
image_set = set(image_list) - set(excluded_image_list)
image_list = list(image_set)
except IOError:
@@ -107,17 +133,16 @@ class Galleries(Task):
# TODO: write this in human
paths = ['/'.join(['..'] * (len(crumbs) - 1 - i)) for i in
range(len(crumbs[:-1]))] + ['#']
- crumbs = zip(paths, crumbs)
+ crumbs = list(zip(paths, crumbs))
image_list = [x for x in image_list if "thumbnail" not in x]
# Sort by date
- image_list.sort(cmp=lambda a, b: cmp(
- self.image_date(a), self.image_date(b)))
+ image_list.sort(key=lambda a: self.image_date(a))
image_name_list = [os.path.basename(x) for x in image_list]
thumbs = []
# Do thumbnails and copy originals
- for img, img_name in zip(image_list, image_name_list):
+ for img, img_name in list(zip(image_list, image_name_list)):
# img is "galleries/name/image_name.jpg"
# img_name is "image_name.jpg"
# fname, ext are "image_name", ".jpg"
@@ -130,8 +155,8 @@ class Galleries(Task):
orig_dest_path = os.path.join(output_gallery, img_name)
thumbs.append(os.path.basename(thumb_path))
yield {
- 'basename': 'render_galleries',
- 'name': thumb_path,
+ 'basename': str('render_galleries'),
+ 'name': thumb_path.encode('utf8'),
'file_dep': [img],
'targets': [thumb_path],
'actions': [
@@ -142,8 +167,8 @@ class Galleries(Task):
'uptodate': [utils.config_changed(kw)],
}
yield {
- 'basename': 'render_galleries',
- 'name': orig_dest_path,
+ 'basename': str('render_galleries'),
+ 'name': orig_dest_path.encode('utf8'),
'file_dep': [img],
'targets': [orig_dest_path],
'actions': [
@@ -165,8 +190,8 @@ class Galleries(Task):
fname + ".thumbnail" + ext)
excluded_dest_path = os.path.join(output_gallery, img_name)
yield {
- 'basename': 'render_galleries',
- 'name': excluded_thumb_dest_path,
+ 'basename': str('render_galleries'),
+ 'name': excluded_thumb_dest_path.encode('utf8'),
'file_dep': [exclude_path],
#'targets': [excluded_thumb_dest_path],
'actions': [
@@ -176,8 +201,8 @@ class Galleries(Task):
'uptodate': [utils.config_changed(kw)],
}
yield {
- 'basename': 'render_galleries',
- 'name': excluded_dest_path,
+ 'basename': str('render_galleries'),
+ 'name': excluded_dest_path.encode('utf8'),
'file_dep': [exclude_path],
#'targets': [excluded_dest_path],
'actions': [
@@ -193,11 +218,11 @@ class Galleries(Task):
context["title"] = os.path.basename(gallery_path)
context["description"] = kw["blog_description"]
if kw['use_filename_as_title']:
- img_titles = ['title="%s"' % utils.unslugify(fn[:-4])
+ img_titles = ['id="%s" alt="%s" title="%s"' % (fn[:-4], fn[:-4], utils.unslugify(fn[:-4]))
for fn in image_name_list]
else:
img_titles = [''] * len(image_name_list)
- context["images"] = zip(image_name_list, thumbs, img_titles)
+ context["images"] = list(zip(image_name_list, thumbs, img_titles))
context["folders"] = folder_list
context["crumbs"] = crumbs
context["permalink"] = self.site.link(
@@ -206,14 +231,14 @@ class Galleries(Task):
# Use galleries/name/index.txt to generate a blurb for
# the gallery, if it exists
index_path = os.path.join(gallery_path, "index.txt")
- cache_dir = os.path.join('cache', 'galleries')
+ cache_dir = os.path.join(kw["cache_folder"], 'galleries')
if not os.path.isdir(cache_dir):
os.makedirs(cache_dir)
- index_dst_path = os.path.join(cache_dir, unicode(uuid.uuid1())+'.html')
+ index_dst_path = os.path.join(cache_dir, str(uuid.uuid1())+'.html')
if os.path.exists(index_path):
compile_html = self.site.get_compiler(index_path)
yield {
- 'basename': 'render_galleries',
+ 'basename': str('render_galleries'),
'name': index_dst_path.encode('utf-8'),
'file_dep': [index_path],
'targets': [index_dst_path],
@@ -236,8 +261,8 @@ class Galleries(Task):
self.site.render_template(template_name, output_name, context)
yield {
- 'basename': 'render_galleries',
- 'name': output_name,
+ 'basename': str('render_galleries'),
+ 'name': output_name.encode('utf8'),
'file_dep': file_dep,
'targets': [output_name],
'actions': [(render_gallery,
@@ -262,7 +287,7 @@ class Galleries(Task):
except Exception:
exif = None
if exif is not None:
- for tag, value in exif.items():
+ for tag, value in list(exif.items()):
decoded = ExifTags.TAGS.get(tag, tag)
if decoded == 'Orientation':
@@ -284,13 +309,13 @@ class Galleries(Task):
def image_date(self, src):
"""Try to figure out the date of the image."""
if src not in self.dates:
- im = Image.open(src)
try:
+ im = Image.open(src)
exif = im._getexif()
except Exception:
exif = None
if exif is not None:
- for tag, value in exif.items():
+ for tag, value in list(exif.items()):
decoded = ExifTags.TAGS.get(tag, tag)
if decoded == 'DateTimeOriginal':
try:
diff --git a/nikola/plugins/task_render_listings.py b/nikola/plugins/task_render_listings.py
index 7ec6e42..e3334c2 100644
--- a/nikola/plugins/task_render_listings.py
+++ b/nikola/plugins/task_render_listings.py
@@ -1,3 +1,27 @@
+# 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 os
from pygments import highlight
diff --git a/nikola/plugins/task_render_pages.py b/nikola/plugins/task_render_pages.py
index 954dc47..1892c13 100644
--- a/nikola/plugins/task_render_pages.py
+++ b/nikola/plugins/task_render_pages.py
@@ -1,3 +1,27 @@
+# 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 nikola.plugin_categories import Task
from nikola.utils import config_changed
diff --git a/nikola/plugins/task_render_posts.py b/nikola/plugins/task_render_posts.py
index 44888f2..48a0384 100644
--- a/nikola/plugins/task_render_posts.py
+++ b/nikola/plugins/task_render_posts.py
@@ -1,3 +1,27 @@
+# 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 copy import copy
import os
diff --git a/nikola/plugins/task_render_rss.py b/nikola/plugins/task_render_rss.py
index bee1192..54b66bf 100644
--- a/nikola/plugins/task_render_rss.py
+++ b/nikola/plugins/task_render_rss.py
@@ -1,3 +1,27 @@
+# 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 os
from nikola import utils
@@ -30,7 +54,7 @@ class RenderRSS(Task):
deps += post.deps(lang)
yield {
'basename': 'render_rss',
- 'name': output_name,
+ 'name': output_name.encode('utf8'),
'file_dep': deps,
'targets': [output_name],
'actions': [(utils.generic_rss_renderer,
diff --git a/nikola/plugins/task_render_sources.py b/nikola/plugins/task_render_sources.py
index ae5ce23..3a05b96 100644
--- a/nikola/plugins/task_render_sources.py
+++ b/nikola/plugins/task_render_sources.py
@@ -1,3 +1,27 @@
+# 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 os
from nikola.plugin_categories import Task
diff --git a/nikola/plugins/task_render_tags.py b/nikola/plugins/task_render_tags.py
index 61629ec..026ba75 100644
--- a/nikola/plugins/task_render_tags.py
+++ b/nikola/plugins/task_render_tags.py
@@ -1,3 +1,29 @@
+# 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 unicode_literals
+import json
import os
from nikola.plugin_categories import Task
@@ -30,151 +56,187 @@ class RenderTags(Task):
if not self.site.posts_per_tag:
yield {
- 'basename': self.name,
+ 'basename': str(self.name),
'actions': [],
}
return
- def page_name(tagname, i, lang):
- """Given tag, n, returns a page name."""
- name = self.site.path("tag", tag, lang)
- if i:
- name = name.replace('.html', '-%s.html' % i)
- return name
-
- for tag, posts in self.site.posts_per_tag.items():
+ for tag, posts in list(self.site.posts_per_tag.items()):
post_list = [self.site.global_data[post] for post in posts]
- post_list.sort(cmp=lambda a, b: cmp(a.date, b.date))
+ post_list.sort(key=lambda a: a.date)
post_list.reverse()
for lang in kw["translations"]:
- #Render RSS
- output_name = os.path.join(kw['output_folder'],
- self.site.path("tag_rss", tag, lang))
- deps = []
- post_list = [self.site.global_data[post] for post in posts
- if self.site.global_data[post].use_in_feeds]
- post_list.sort(cmp=lambda a, b: cmp(a.date, b.date))
- post_list.reverse()
- for post in post_list:
- deps += post.deps(lang)
- yield {
- 'name': output_name.encode('utf8'),
- 'file_dep': deps,
- 'targets': [output_name],
- 'actions': [(utils.generic_rss_renderer,
- (lang, "%s (%s)" % (kw["blog_title"], tag),
- kw["blog_url"], kw["blog_description"],
- post_list, output_name))],
- 'clean': True,
- 'uptodate': [utils.config_changed(kw)],
- 'basename': self.name
- }
+ yield self.tag_rss(tag, lang, posts, kw)
# Render HTML
if kw['tag_pages_are_indexes']:
- # We render a sort of index page collection using only
- # this tag's posts.
-
- # FIXME: deduplicate this with render_indexes
- template_name = "index.tmpl"
- # Split in smaller lists
- lists = []
- while post_list:
- lists.append(post_list[
- :kw["index_display_post_count"]])
- post_list = post_list[
- kw["index_display_post_count"]:]
- num_pages = len(lists)
- for i, post_list in enumerate(lists):
- context = {}
- # On a tag page, the feeds include the tag's feeds
- rss_link = \
- """<link rel="alternate" type="application/rss+xml" """\
- """type="application/rss+xml" title="RSS for tag """\
- """%s (%s)" href="%s">""" % \
- (tag, lang, self.site.link("tag_rss", tag, lang))
- context['rss_link'] = rss_link
- output_name = os.path.join(kw['output_folder'],
- page_name(tag, i, lang))
- context["title"] = kw["messages"][lang][
- u"Posts about %s"] % tag
- context["prevlink"] = None
- context["nextlink"] = None
- context['index_teasers'] = kw['index_teasers']
- if i > 1:
- context["prevlink"] = os.path.basename(
- page_name(tag, i - 1, lang))
- if i == 1:
- context["prevlink"] = os.path.basename(
- page_name(tag, 0, lang))
- if i < num_pages - 1:
- context["nextlink"] = os.path.basename(
- page_name(tag, i + 1, lang))
- context["permalink"] = self.site.link("tag", tag, lang)
- context["tag"] = tag
- for task in self.site.generic_post_list_renderer(
- lang,
- post_list,
- output_name,
- template_name,
- kw['filters'],
- context,
- ):
- task['uptodate'] = [utils.config_changed({
- 1: task['uptodate'][0].config,
- 2: kw})]
- task['basename'] = self.name
- yield task
+ yield self.tag_page_as_index(tag, lang, post_list, kw)
else:
- # We render a single flat link list with this tag's posts
- template_name = "tag.tmpl"
- output_name = os.path.join(kw['output_folder'],
- self.site.path("tag", tag, lang))
- context = {}
- context["lang"] = lang
- context["title"] = kw["messages"][lang][
- u"Posts about %s"] % tag
- context["items"] = [("[%s] %s" % (post.date,
- post.title(lang)),
- post.permalink(lang)) for post in post_list]
- context["permalink"] = self.site.link("tag", tag, lang)
- context["tag"] = tag
- for task in self.site.generic_post_list_renderer(
- lang,
- post_list,
- output_name,
- template_name,
- kw['filters'],
- context,
- ):
- task['uptodate'] = [utils.config_changed({
- 1: task['uptodate'][0].config,
- 2: kw})]
- task['basename'] = self.name
- yield task
-
- # And global "all your tags" page
- tags = self.site.posts_per_tag.keys()
+ yield self.tag_page_as_list(tag, lang, post_list, kw)
+
+ yield self.list_tags_page(kw)
+
+ # Tag cloud json file
+ tag_cloud_data = {}
+ for tag, posts in self.site.posts_per_tag.items():
+ tag_cloud_data[tag] = [len(posts), self.site.link(
+ 'tag', tag, self.site.config['DEFAULT_LANG'])]
+ output_name = os.path.join(kw['output_folder'],
+ 'assets','js','tag_cloud_data.json')
+
+ def write_tag_data(data):
+ try:
+ os.makedirs(os.path.dirname(output_name))
+ except:
+ pass
+ with open(output_name, 'wb+') as fd:
+ fd.write(json.dumps(data))
+
+ task = {
+ 'basename': str(self.name),
+ 'name': str(output_name)
+ }
+ task['uptodate'] = [utils.config_changed(tag_cloud_data)]
+ task['targets'] = [output_name]
+ task['actions'] = [(write_tag_data,[tag_cloud_data])]
+ yield task
+
+ def list_tags_page(self, kw):
+ """a global "all your tags" page for each language"""
+ tags = list(self.site.posts_per_tag.keys())
tags.sort()
template_name = "tags.tmpl"
kw['tags'] = tags
for lang in kw["translations"]:
output_name = os.path.join(
kw['output_folder'], self.site.path('tag_index', None, lang))
+ output_name = output_name.encode('utf8')
context = {}
- context["title"] = kw["messages"][lang][u"Tags"]
+ context["title"] = kw["messages"][lang]["Tags"]
context["items"] = [(tag, self.site.link("tag", tag, lang))
for tag in tags]
context["permalink"] = self.site.link("tag_index", None, lang)
- for task in self.site.generic_post_list_renderer(
+ task = self.site.generic_post_list_renderer(
lang,
[],
output_name,
template_name,
kw['filters'],
context,
- ):
- task['uptodate'] = [utils.config_changed({
- 1: task['uptodate'][0].config,
- 2: kw})]
- yield task
+ )
+ task_cfg = {1: task['uptodate'][0].config, 2: kw}
+ task['uptodate'] = [utils.config_changed(task_cfg)]
+ yield task
+
+
+ def tag_page_as_index(self, tag, lang, post_list, kw):
+ """render a sort of index page collection using only this tag's posts."""
+
+ def page_name(tagname, i, lang):
+ """Given tag, n, returns a page name."""
+ name = self.site.path("tag", tag, lang)
+ if i:
+ name = name.replace('.html', '-%s.html' % i)
+ return name
+
+ # FIXME: deduplicate this with render_indexes
+ template_name = "index.tmpl"
+ # Split in smaller lists
+ lists = []
+ while post_list:
+ lists.append(post_list[:kw["index_display_post_count"]])
+ post_list = post_list[kw["index_display_post_count"]:]
+ num_pages = len(lists)
+ for i, post_list in enumerate(lists):
+ context = {}
+ # On a tag page, the feeds include the tag's feeds
+ rss_link = \
+ """<link rel="alternate" type="application/rss+xml" """\
+ """type="application/rss+xml" title="RSS for tag """\
+ """%s (%s)" href="%s">""" % \
+ (tag, lang, self.site.link("tag_rss", tag, lang))
+ context['rss_link'] = rss_link
+ output_name = os.path.join(kw['output_folder'],
+ page_name(tag, i, lang))
+ output_name = output_name.encode('utf8')
+ context["title"] = kw["messages"][lang][
+ "Posts about %s"] % tag
+ context["prevlink"] = None
+ context["nextlink"] = None
+ context['index_teasers'] = kw['index_teasers']
+ if i > 1:
+ context["prevlink"] = os.path.basename(
+ page_name(tag, i - 1, lang))
+ if i == 1:
+ context["prevlink"] = os.path.basename(
+ page_name(tag, 0, lang))
+ if i < num_pages - 1:
+ context["nextlink"] = os.path.basename(
+ page_name(tag, i + 1, lang))
+ context["permalink"] = self.site.link("tag", tag, lang)
+ context["tag"] = tag
+ task = self.site.generic_post_list_renderer(
+ lang,
+ post_list,
+ output_name,
+ template_name,
+ kw['filters'],
+ context,
+ )
+ task_cfg = {1: task['uptodate'][0].config, 2: kw}
+ task['uptodate'] = [utils.config_changed(task_cfg)]
+ task['basename'] = str(self.name)
+ yield task
+
+
+ def tag_page_as_list(self, tag, lang, post_list, kw):
+ """We render a single flat link list with this tag's posts"""
+ template_name = "tag.tmpl"
+ output_name = os.path.join(kw['output_folder'],
+ self.site.path("tag", tag, lang))
+ output_name = output_name.encode('utf8')
+ context = {}
+ context["lang"] = lang
+ context["title"] = kw["messages"][lang]["Posts about %s"] % tag
+ context["posts"] = post_list
+ context["permalink"] = self.site.link("tag", tag, lang)
+ context["tag"] = tag
+ task = self.site.generic_post_list_renderer(
+ lang,
+ post_list,
+ output_name,
+ template_name,
+ kw['filters'],
+ context,
+ )
+ task_cfg = {1: task['uptodate'][0].config, 2: kw}
+ task['uptodate'] = [utils.config_changed(task_cfg)]
+ task['basename'] = str(self.name)
+ yield task
+
+
+ def tag_rss(self, tag, lang, posts, kw):
+ """RSS for a single tag / language"""
+ #Render RSS
+ output_name = os.path.join(kw['output_folder'],
+ self.site.path("tag_rss", tag, lang))
+ output_name = output_name.encode('utf8')
+ deps = []
+ post_list = [self.site.global_data[post] for post in posts
+ if self.site.global_data[post].use_in_feeds]
+ post_list.sort(key=lambda a: a.date)
+ post_list.reverse()
+ for post in post_list:
+ deps += post.deps(lang)
+ return {
+ 'basename': str(self.name),
+ 'name': output_name,
+ 'file_dep': deps,
+ 'targets': [output_name],
+ 'actions': [(utils.generic_rss_renderer,
+ (lang, "%s (%s)" % (kw["blog_title"], tag),
+ kw["blog_url"], kw["blog_description"],
+ post_list, output_name))],
+ 'clean': True,
+ 'uptodate': [utils.config_changed(kw)],
+ }
diff --git a/nikola/plugins/task_sitemap/__init__.py b/nikola/plugins/task_sitemap/__init__.py
index 87b72bf..1ed6c21 100644
--- a/nikola/plugins/task_sitemap/__init__.py
+++ b/nikola/plugins/task_sitemap/__init__.py
@@ -1,10 +1,34 @@
+# 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 os
import tempfile
from nikola.plugin_categories import LateTask
from nikola.utils import config_changed
-import sitemap_gen as smap
+import sitemap_gen
class Sitemap(LateTask):
@@ -42,15 +66,15 @@ class Sitemap(LateTask):
config_file.close()
# Generate sitemap
- sitemap = smap.CreateSitemapFromFile(config_file.name, True)
+ sitemap = sitemap_gen.CreateSitemapFromFile(config_file.name, True)
if not sitemap:
- smap.output.Log('Configuration file errors -- exiting.', 0)
+ sitemap_gen.output.Log('Configuration file errors -- exiting.', 0)
else:
sitemap.Generate()
- smap.output.Log('Number of errors: %d' %
- smap.output.num_errors, 1)
- smap.output.Log('Number of warnings: %d' %
- smap.output.num_warns, 1)
+ sitemap_gen.output.Log('Number of errors: %d' %
+ sitemap_gen.output.num_errors, 1)
+ sitemap_gen.output.Log('Number of warnings: %d' %
+ sitemap_gen.output.num_warns, 1)
os.unlink(config_file.name)
yield {
diff --git a/nikola/plugins/task_sitemap/sitemap_gen.py b/nikola/plugins/task_sitemap/sitemap_gen.py
index 43e7c32..eef2b0b 100755
--- a/nikola/plugins/task_sitemap/sitemap_gen.py
+++ b/nikola/plugins/task_sitemap/sitemap_gen.py
@@ -40,6 +40,7 @@
#
# http://www.opensource.org/licenses/bsd-license.php
#
+from __future__ import print_function
__usage__ = \
"""A simple script to automatically produce sitemaps for a webserver,
@@ -56,8 +57,8 @@ Usage: python sitemap_gen.py --config=config.xml [--help] [--testing]
# entire file has been parsed.
import sys
if sys.hexversion < 0x02020000:
- print 'This script requires Python 2.2 or later.'
- print 'Currently run with version: %s' % sys.version
+ print('This script requires Python 2.2 or later.')
+ print('Currently run with version: %s' % sys.version)
sys.exit(1)
import fnmatch
@@ -70,16 +71,12 @@ import stat
import time
import types
import urllib
-import urlparse
import xml.sax
-# True and False were introduced in Python2.2.2
try:
- testTrue=True
- del testTrue
-except NameError:
- True=1
- False=0
+ from urlparse import urlparse, urlsplit, urlunsplit
+except ImportError:
+ from urllib.parse import urlparse, urlsplit, urlunsplit
# Text encodings
ENC_ASCII = 'ASCII'
@@ -383,7 +380,7 @@ class Output:
if text:
text = encoder.NarrowText(text, None)
if self._verbose >= level:
- print text
+ print(text)
#end def Log
def Warn(self, text):
@@ -393,7 +390,7 @@ class Output:
hash = hashlib.md5(text).digest()
if not self._warns_shown.has_key(hash):
self._warns_shown[hash] = 1
- print '[WARNING] ' + text
+ print('[WARNING] ' + text)
else:
self.Log('(suppressed) [WARNING] ' + text, 3)
self.num_warns = self.num_warns + 1
@@ -406,7 +403,7 @@ class Output:
hash = hashlib.md5(text).digest()
if not self._errors_shown.has_key(hash):
self._errors_shown[hash] = 1
- print '[ERROR] ' + text
+ print('[ERROR] ' + text)
else:
self.Log('(suppressed) [ERROR] ' + text, 3)
self.num_errors = self.num_errors + 1
@@ -416,9 +413,9 @@ class Output:
""" Output an error and terminate the program. """
if text:
text = encoder.NarrowText(text, None)
- print '[FATAL] ' + text
+ print('[FATAL] ' + text)
else:
- print 'Fatal error.'
+ print('Fatal error.')
sys.exit(1)
#end def Fatal
@@ -475,7 +472,7 @@ class URL(object):
if not loc:
return False
narrow = encoder.NarrowText(loc, None)
- (scheme, netloc, path, query, frag) = urlparse.urlsplit(narrow)
+ (scheme, netloc, path, query, frag) = urlsplit(narrow)
if (not scheme) or (not netloc):
return False
return True
@@ -491,7 +488,7 @@ class URL(object):
narrow = encoder.NarrowText(loc, None)
# Escape components individually
- (scheme, netloc, path, query, frag) = urlparse.urlsplit(narrow)
+ (scheme, netloc, path, query, frag) = urlsplit(narrow)
unr = '-._~'
sub = '!$&\'()*+,;='
netloc = urllib.quote(netloc, unr + sub + '%:@/[]')
@@ -501,7 +498,7 @@ class URL(object):
# Try built-in IDNA encoding on the netloc
try:
- (ignore, widenetloc, ignore, ignore, ignore) = urlparse.urlsplit(loc)
+ (ignore, widenetloc, ignore, ignore, ignore) = urlsplit(loc)
for c in widenetloc:
if c >= unichr(128):
netloc = widenetloc.encode(ENC_IDNA)
@@ -522,7 +519,7 @@ class URL(object):
bad_netloc = True
# Put it all back together
- narrow = urlparse.urlunsplit((scheme, netloc, path, query, frag))
+ narrow = urlunsplit((scheme, netloc, path, query, frag))
# I let '%' through. Fix any that aren't pre-existing escapes.
HEXDIG = '0123456789abcdefABCDEF'
@@ -1459,7 +1456,7 @@ class InputSitemap(xml.sax.handler.ContentHandler):
% path)
except IOError:
output.Error('Cannot read from file "%s"' % path)
- except xml.sax._exceptions.SAXParseException, e:
+ except xml.sax._exceptions.SAXParseException as e:
output.Error('XML error in the file "%s" (line %d, column %d): %s' %
(path, e._linenum, e._colnum, e.getMessage()))
@@ -1488,7 +1485,7 @@ class InputSitemap(xml.sax.handler.ContentHandler):
for url in urllist:
url = URL.Canonicalize(url)
output.Log('Index points to Sitemap file at: %s' % url, 2)
- (scheme, netloc, path, query, frag) = urlparse.urlsplit(url)
+ (scheme, netloc, path, query, frag) = urlsplit(url)
file = os.path.basename(path)
file = urllib.unquote(file)
if wide:
@@ -1679,7 +1676,7 @@ class PerURLStatistics:
def Consume(self, url):
""" Log some stats for the URL. At the moment, that means extension. """
if url and url.loc:
- (scheme, netloc, path, query, frag) = urlparse.urlsplit(url.loc)
+ (scheme, netloc, path, query, frag) = urlsplit(url.loc)
if not path:
return
@@ -1930,7 +1927,7 @@ class Sitemap(xml.sax.handler.ContentHandler):
file = None
except IOError:
output.Fatal('Couldn\'t write out to file: %s' % filename)
- os.chmod(filename, 0644)
+ os.chmod(filename, 0o0644)
# Flush
self._set = []
@@ -1965,7 +1962,7 @@ class Sitemap(xml.sax.handler.ContentHandler):
fd = None
except IOError:
output.Fatal('Couldn\'t write out to file: %s' % filename)
- os.chmod(filename, 0644)
+ os.chmod(filename, 0o0644)
#end def WriteIndex
def NotifySearch(self):
@@ -2011,7 +2008,7 @@ class Sitemap(xml.sax.handler.ContentHandler):
query_attr = ping[5]
query_map[query_attr] = url
query = urllib.urlencode(query_map)
- notify = urlparse.urlunsplit((ping[0], ping[1], ping[2], query, ping[4]))
+ notify = urlunsplit((ping[0], ping[1], ping[2], query, ping[4]))
# Send the notification
output.Log('Notifying: %s' % ping[1], 1)
@@ -2183,7 +2180,7 @@ def CreateSitemapFromFile(configpath, suppress_notify):
xml.sax.parse(configpath, sitemap)
except IOError:
output.Error('Cannot read configuration file: %s' % configpath)
- except xml.sax._exceptions.SAXParseException, e:
+ except xml.sax._exceptions.SAXParseException as e:
output.Error('XML error in the config file (line %d, column %d): %s' %
(e._linenum, e._colnum, e.getMessage()))
except xml.sax._exceptions.SAXReaderNotAvailable:
diff --git a/nikola/plugins/template_jinja.py b/nikola/plugins/template_jinja.py
index 0893cf7..f88b2c0 100644
--- a/nikola/plugins/template_jinja.py
+++ b/nikola/plugins/template_jinja.py
@@ -1,7 +1,34 @@
+# 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.
+
"""Jinja template handlers"""
import os
-import jinja2
+try:
+ import jinja2
+except ImportError:
+ jinja2 = None
from nikola.plugin_categories import TemplateSystem
@@ -12,8 +39,10 @@ class JinjaTemplates(TemplateSystem):
name = "jinja"
lookup = None
- def set_directories(self, directories):
+ def set_directories(self, directories, cache_folder):
"""Createa template lookup."""
+ if jinja2 is None:
+ raise Exception('To use this theme you need to install the "Jinja2" package.')
self.lookup = jinja2.Environment(loader=jinja2.FileSystemLoader(
directories,
encoding='utf-8',
@@ -21,7 +50,8 @@ class JinjaTemplates(TemplateSystem):
def render_template(self, template_name, output_name, context):
"""Render the template into output_name using context."""
-
+ if jinja2 is None:
+ raise Exception('To use this theme you need to install the "Jinja2" package.')
template = self.lookup.get_template(template_name)
output = template.render(**context)
if output_name is not None:
diff --git a/nikola/plugins/template_mako.py b/nikola/plugins/template_mako.py
index 7ab5c43..2f8d52c 100644
--- a/nikola/plugins/template_mako.py
+++ b/nikola/plugins/template_mako.py
@@ -1,3 +1,27 @@
+# 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.
+
"""Mako template handlers"""
import os
@@ -29,9 +53,9 @@ class MakoTemplates(TemplateSystem):
# TODO: include tags are not handled
return deps
- def set_directories(self, directories):
+ def set_directories(self, directories, cache_folder):
"""Createa template lookup."""
- cache_dir = os.path.join('cache', '.mako.tmp')
+ cache_dir = os.path.join(cache_folder, '.mako.tmp')
if os.path.exists(cache_dir):
shutil.rmtree(cache_dir)
self.lookup = TemplateLookup(
diff --git a/nikola/post.py b/nikola/post.py
index f4b0a0e..d5b98f6 100644
--- a/nikola/post.py
+++ b/nikola/post.py
@@ -1,11 +1,34 @@
# -*- 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.
import codecs
import os
import lxml.html
-import utils
+from . import utils
__all__ = ['Post']
@@ -14,7 +37,7 @@ class Post(object):
"""Represents a blog post or web page."""
- def __init__(self, source_path, destination, use_in_feeds,
+ def __init__(self, source_path, cache_folder, destination, use_in_feeds,
translations, default_lang, blog_url, messages):
"""Initialize post.
@@ -32,7 +55,7 @@ class Post(object):
self.source_path = source_path # posts/blah.txt
self.post_name = os.path.splitext(source_path)[0] # posts/blah
# cache/posts/blah.html
- self.base_path = os.path.join('cache', self.post_name + ".html")
+ self.base_path = os.path.join(cache_folder, self.post_name + ".html")
self.metadata_path = self.post_name + ".meta" # posts/blah.meta
self.folder = destination
self.translations = translations
@@ -56,7 +79,7 @@ class Post(object):
self.date = utils.to_datetime(self.date)
self.tags = [x.strip() for x in self.tags.split(',')]
- self.tags = filter(None, self.tags)
+ self.tags = [_f for _f in self.tags if _f]
# While draft comes from the tags, it's not really a tag
self.use_in_feeds = use_in_feeds and "draft" not in self.tags
@@ -123,7 +146,7 @@ class Post(object):
if os.path.isfile(self.metadata_path):
deps.append(self.metadata_path)
if lang != self.default_lang:
- lang_deps = filter(os.path.exists, [x + "." + lang for x in deps])
+ lang_deps = list(filter(os.path.exists, [x + "." + lang for x in deps]))
deps += lang_deps
return deps
@@ -166,7 +189,7 @@ class Post(object):
pieces = list(os.path.split(self.translations[lang]))
pieces += list(os.path.split(self.folder))
pieces += [self.pagenames[lang] + extension]
- pieces = filter(None, pieces)
+ pieces = [_f for _f in pieces if _f]
if absolute:
pieces = [self.blog_url] + pieces
else:
diff --git a/nikola/utils.py b/nikola/utils.py
index e319a6d..18b4646 100644
--- a/nikola/utils.py
+++ b/nikola/utils.py
@@ -1,6 +1,31 @@
+# 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.
+
"""Utility functions."""
-from collections import defaultdict
+from __future__ import print_function
+from collections import defaultdict, Callable
import datetime
import hashlib
import os
@@ -12,11 +37,15 @@ import string
import subprocess
import sys
from zipfile import ZipFile as zip
+try:
+ from imp import reload
+except ImportError:
+ pass
from doit import tools
from unidecode import unidecode
-import PyRSS2Gen as rss
+from . import PyRSS2Gen as rss
__all__ = ['get_theme_path', 'get_theme_chain', 'load_messages', 'copy_tree',
'generic_rss_renderer',
@@ -37,11 +66,11 @@ class config_changed(tools.config_changed):
""" A copy of doit's but using pickle instead of serializing manually."""
def _calc_digest(self):
- if isinstance(self.config, basestring):
+ if isinstance(self.config, str):
return self.config
elif isinstance(self.config, dict):
data = json.dumps(self.config, cls=CustomEncoder)
- if isinstance(data, unicode): # pragma: no cover # python3
+ if isinstance(data, str): # pragma: no cover # python3
byte_data = data.encode("utf-8")
else:
byte_data = data
@@ -68,7 +97,7 @@ def get_theme_path(theme):
'data', 'themes', theme)
if os.path.isdir(dir_name):
return dir_name
- raise Exception(u"Can't find theme '%s'" % theme)
+ raise Exception("Can't find theme '%s'" % theme)
def re_meta(line, match):
@@ -158,25 +187,25 @@ def load_messages(themes, translations):
"""
messages = defaultdict(dict)
warned = []
+ oldpath = sys.path[:]
for theme_name in themes[::-1]:
msg_folder = os.path.join(get_theme_path(theme_name), 'messages')
- oldpath = sys.path
sys.path.insert(0, msg_folder)
- english = __import__('en')
- for lang in translations.keys():
+ english = __import__('messages_en')
+ for lang in list(translations.keys()):
# If we don't do the reload, the module is cached
- translation = __import__(lang)
+ translation = __import__('messages_'+lang)
reload(translation)
if sorted(translation.MESSAGES.keys()) !=\
sorted(english.MESSAGES.keys()) and \
lang not in warned:
# FIXME: get real logging in place
- print "Warning: Incomplete translation for language '%s'." % lang
+ print("Warning: Incomplete translation for language '%s'." % lang)
warned.append(lang)
messages[lang].update(english.MESSAGES)
messages[lang].update(translation.MESSAGES)
del(translation)
- sys.path = oldpath
+ sys.path = oldpath
return messages
@@ -210,7 +239,7 @@ def copy_tree(src, dst, link_cutoff=None):
dst_file = os.path.join(dst_dir, src_name)
src_file = os.path.join(root, src_name)
yield {
- 'name': dst_file,
+ 'name': str(dst_file),
'file_dep': [src_file],
'targets': [dst_file],
'actions': [(copy_file, (src_file, dst_file, link_cutoff))],
@@ -225,9 +254,9 @@ def generic_rss_renderer(lang, title, link, description,
for post in timeline[:10]:
args = {
'title': post.title(lang),
- 'link': post.permalink(lang),
+ 'link': post.permalink(lang, absolute=True),
'description': post.text(lang, teaser_only=True),
- 'guid': post.permalink(lang),
+ 'guid': post.permalink(lang, absolute=True),
'pubDate': post.date,
}
items.append(rss.RSSItem(**args))
@@ -288,7 +317,9 @@ def slugify(value):
From Django's "django/template/defaultfilters.py".
"""
value = unidecode(value)
- value = unicode(_slugify_strip_re.sub('', value).strip().lower())
+ # WARNING: this may not be python2/3 equivalent
+ #value = unicode(_slugify_strip_re.sub('', value).strip().lower())
+ value = str(_slugify_strip_re.sub('', value).strip().lower())
return _slugify_hyphenate_re.sub('-', value)
@@ -312,7 +343,7 @@ class UnsafeZipException(Exception):
def extract_all(zipfile):
pwd = os.getcwd()
os.chdir('themes')
- z = zip(zipfile)
+ z = list(zip(zipfile))
namelist = z.namelist()
for f in namelist:
if f.endswith('/') and '..' in f:
@@ -364,11 +395,11 @@ def apply_filters(task, filters):
"""
def filter_matches(ext):
- for key, value in filters.items():
+ for key, value in list(filters.items()):
if isinstance(key, (tuple, list)):
if ext in key:
return value
- elif isinstance(key, (str, unicode)):
+ elif isinstance(key, (str, bytes)):
if ext == key:
return value
else:
@@ -381,7 +412,7 @@ def apply_filters(task, filters):
for action in filter_:
def unlessLink(action, target):
if not os.path.islink(target):
- if callable(action):
+ if isinstance(action, Callable):
action(target)
else:
subprocess.check_call(action % target, shell=True)
diff --git a/requirements-3.txt b/requirements-3.txt
new file mode 100644
index 0000000..6967942
--- /dev/null
+++ b/requirements-3.txt
@@ -0,0 +1,12 @@
+doit>=0.17
+pygments
+https://github.com/fluggo/Pillow/archive/master.zip
+docutils
+mako>=0.6
+unidecode
+lxml
+yapsy
+mock>=1.0.0
+requests
+markdown
+Jinja2
diff --git a/requirements.txt b/requirements.txt
index 50482e5..c20cf9b 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -7,3 +7,7 @@ unidecode
lxml
configparser
yapsy
+mock>=1.0.0
+requests
+markdown
+Jinja2
diff --git a/scripts/nikola b/scripts/nikola
index a20ac43..2143e9e 100755
--- a/scripts/nikola
+++ b/scripts/nikola
@@ -57,7 +57,7 @@ if __name__ == "__main__":
site = nikola.Nikola(**config)
if len(sys.argv) < 2:
- sys.argv[1:] = 'help'
+ sys.argv.append('help')
cmd_name = sys.argv[1]
if cmd_name in ("help", "--help", "-h"):
diff --git a/setup.py b/setup.py
index 33beae5..a4fb9fc 100644
--- a/setup.py
+++ b/setup.py
@@ -6,12 +6,27 @@
# Licensed under the MIT license:
# http://www.opensource.org/licenses/mit-license.php
+from __future__ import print_function
import os
import subprocess
import sys
from fnmatch import fnmatchcase
-from distutils.util import convert_path
-from distutils.command.install import install
+
+
+try:
+ # Prefer setuptools for the installation to have no problem with the
+ # "--single-version-externally-managed" option that pip uses when
+ # installing packages.
+ from setuptools import setup
+ from setuptools import convert_path
+
+ from setuptools.command.install import install
+except ImportError:
+ print('\n*** setuptools not found! Falling back to distutils\n\n')
+ from distutils.core import setup
+
+ from distutils.command.install import install
+ from distutils.util import convert_path
dependencies = [
'doit>=0.18.1',
@@ -22,9 +37,13 @@ dependencies = [
'unidecode',
'lxml',
'yapsy',
- 'configparser',
+ 'mock>=1.0.0',
]
+if sys.version_info[0] == 2:
+ # in Python 3 this becomes a builtin, for Python 2 we need the backport
+ dependencies.append('configparser')
+
# Provided as an attribute, so you can append to these instead
# of replicating them:
standard_exclude = ('*.pyc', '*$py.class', '*~', '.*', '*.bak')
@@ -32,12 +51,14 @@ standard_exclude_directories = ('.*', 'CVS', '_darcs', './build',
'./dist', 'EGG-INFO', '*.egg-info')
-def install_manpages(prefix):
+def install_manpages(root, prefix):
man_pages = [
('docs/man/nikola.1', 'share/man/man1/nikola.1'),
]
join = os.path.join
normpath = os.path.normpath
+ if root is not None:
+ prefix = os.path.realpath(root) + os.path.sep + prefix
for src, dst in man_pages:
path_dst = join(normpath(prefix), normpath(dst))
try:
@@ -57,7 +78,7 @@ def install_manpages(prefix):
class nikola_install(install):
def run(self):
install.run(self)
- install_manpages(self.prefix)
+ install_manpages(self.root, self.prefix)
def find_package_data(
@@ -65,7 +86,7 @@ def find_package_data(
exclude=standard_exclude,
exclude_directories=standard_exclude_directories,
only_in_packages=True,
- show_ignored=False):
+ show_ignored=False):
"""
Return a dictionary suitable for use in ``package_data``
in a distutils ``setup.py`` file.
@@ -103,7 +124,7 @@ def find_package_data(
bad_name = False
for pattern in exclude_directories:
if (fnmatchcase(name, pattern)
- or fn.lower() == pattern.lower()):
+ or fn.lower() == pattern.lower()):
bad_name = True
if show_ignored:
print >> sys.stderr, (
@@ -113,7 +134,7 @@ def find_package_data(
if bad_name:
continue
if (os.path.isfile(os.path.join(fn, '__init__.py'))
- and not prefix):
+ and not prefix):
if not package:
new_package = name
else:
@@ -121,13 +142,13 @@ def find_package_data(
stack.append((fn, '', new_package, False))
else:
stack.append((fn, prefix + name + '/',
- package, only_in_packages))
+ package, only_in_packages))
elif package or not only_in_packages:
# is a file
bad_name = False
for pattern in exclude:
if (fnmatchcase(name, pattern)
- or fn.lower() == pattern.lower()):
+ or fn.lower() == pattern.lower()):
bad_name = True
if show_ignored:
print >> sys.stderr, (
@@ -142,20 +163,24 @@ def find_package_data(
from distutils.core import setup
setup(name='Nikola',
- version='5',
+ version='5.1',
description='Static blog/website generator',
author='Roberto Alsina and others',
author_email='ralsina@netmanagers.com.ar',
url='http://nikola.ralsina.com.ar/',
- packages=['nikola'],
+ packages=['nikola',
+ 'nikola.plugins',
+ 'nikola.plugins.compile_markdown',
+ 'nikola.plugins.task_sitemap',
+ 'nikola.plugins.compile_rest'],
scripts=['scripts/nikola'],
install_requires=dependencies,
package_data=find_package_data(),
cmdclass={'install': nikola_install},
data_files=[
('share/doc/nikola', [
- 'docs/manual.txt',
- 'docs/theming.txt',
- 'docs/extending.txt']),
+ 'docs/manual.txt',
+ 'docs/theming.txt',
+ 'docs/extending.txt']),
],
- )
+ )
diff --git a/tests/context.py b/tests/context.py
new file mode 100644
index 0000000..f292b79
--- /dev/null
+++ b/tests/context.py
@@ -0,0 +1,9 @@
+# -*- coding: utf-8 -*-
+
+# Path hack as shown by Kenneth Reitz at http://kennethreitz.com/repository-structure-and-python.html
+
+import os
+import sys
+sys.path.insert(0, os.path.abspath('..'))
+
+import nikola
diff --git a/tests/rss-2_0.xsd b/tests/rss-2_0.xsd
new file mode 100644
index 0000000..d7ddaee
--- /dev/null
+++ b/tests/rss-2_0.xsd
@@ -0,0 +1,500 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ XML Schema for RSS v2.0
+ Copyright (C) 2003-2008 Jorgen Thelin
+
+Microsoft Public License (Ms-PL)
+
+This license governs use of the accompanying software.
+If you use the software, you accept this license.
+If you do not accept the license, do not use the software.
+
+1. Definitions
+
+The terms "reproduce," "reproduction," "derivative works," and "distribution" have the same meaning here as under U.S. copyright law.
+
+ A "contribution" is the original software, or any additions or changes to the software.
+
+ A "contributor" is any person that distributes its contribution under this license.
+
+ "Licensed patents" are a contributor's patent claims that read directly on its contribution.
+
+2. Grant of Rights
+
+ (A) Copyright Grant- Subject to the terms of this license, including the license conditions and limitations in section 3, each contributor grants you a non-exclusive, worldwide, royalty-free copyright license to reproduce its contribution, prepare derivative works of its contribution, and distribute its contribution or any derivative works that you create.
+
+ (B) Patent Grant- Subject to the terms of this license, including the license conditions and limitations in section 3, each contributor grants you a non-exclusive, worldwide, royalty-free license under its licensed patents to make, have made, use, sell, offer for sale, import, and/or otherwise dispose of its contribution in the software or derivative works of the contribution in the software.
+
+3. Conditions and Limitations
+
+ (A) No Trademark License- This license does not grant you rights to use any contributors' name, logo, or trademarks.
+
+ (B) If you bring a patent claim against any contributor over patents that you claim are infringed by the software, your patent license from such contributor to the software ends automatically.
+
+ (C) If you distribute any portion of the software, you must retain all copyright, patent, trademark, and attribution notices that are present in the software.
+
+ (D) If you distribute any portion of the software in source code form, you may do so only under this license by including a complete copy of this license with your distribution. If you distribute any portion of the software in compiled or object code form, you may only do so under a license that complies with this license.
+
+ (E) The software is licensed "as-is." You bear the risk of using it. The contributors give no express warranties, guarantees or conditions. You may have additional consumer rights under your local laws which this license cannot change. To the extent permitted under your local laws, the contributors exclude the implied warranties of merchantability, fitness for a particular purpose and non-infringement.
+
+-->
+<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
+ elementFormDefault="unqualified"
+ version="2.0.2.16">
+ <xs:annotation>
+ <xs:documentation>XML Schema for RSS v2.0 feed files.</xs:documentation>
+ <xs:documentation>Project home: http://www.codeplex.com/rss2schema/ </xs:documentation>
+ <xs:documentation>Based on the RSS 2.0 specification document at http://cyber.law.harvard.edu/rss/rss.html </xs:documentation>
+ <xs:documentation>Author: Jorgen Thelin</xs:documentation>
+ <xs:documentation>Revision: 16</xs:documentation>
+ <xs:documentation>Date: 01-Nov-2008</xs:documentation>
+ <xs:documentation>Feedback to: http://www.codeplex.com/rss2schema/WorkItem/List.aspx </xs:documentation>
+ </xs:annotation>
+ <xs:element name="rss">
+ <xs:complexType>
+ <xs:sequence>
+ <xs:element name="channel" type="RssChannel"/>
+ <xs:any namespace="##other" processContents="lax" minOccurs="0" maxOccurs="unbounded"/>
+ </xs:sequence>
+ <xs:attribute name="version" type="xs:decimal" use="required" fixed="2.0"/>
+ <xs:anyAttribute namespace="##any"/>
+ </xs:complexType>
+ </xs:element>
+ <xs:complexType name="RssItem">
+ <xs:annotation>
+ <xs:documentation>An item may represent a "story" -- much like a story in a newspaper or magazine; if so its description is a synopsis of the story, and the link points to the full story. An item may also be complete in itself, if so, the description contains the text (entity-encoded HTML is allowed), and the link and title may be omitted.</xs:documentation>
+ </xs:annotation>
+ <xs:sequence>
+ <xs:choice maxOccurs="unbounded">
+ <xs:element name="title" type="xs:string" minOccurs="0">
+ <xs:annotation>
+ <xs:documentation>The title of the item.</xs:documentation>
+ </xs:annotation>
+ </xs:element>
+ <xs:element name="description" type="xs:string" minOccurs="0">
+ <xs:annotation>
+ <xs:documentation>The item synopsis.</xs:documentation>
+ </xs:annotation>
+ </xs:element>
+ <xs:element name="link" type="xs:anyURI" minOccurs="0">
+ <xs:annotation>
+ <xs:documentation>The URL of the item.</xs:documentation>
+ </xs:annotation>
+ </xs:element>
+ <xs:element name="author" type="EmailAddress" minOccurs="0">
+ <xs:annotation>
+ <xs:documentation>Email address of the author of the item.</xs:documentation>
+ </xs:annotation>
+ </xs:element>
+ <xs:element name="category" type="Category" minOccurs="0">
+ <xs:annotation>
+ <xs:documentation>Includes the item in one or more categories. </xs:documentation>
+ </xs:annotation>
+ </xs:element>
+ <xs:element name="comments" type="xs:anyURI" minOccurs="0">
+ <xs:annotation>
+ <xs:documentation>URL of a page for comments relating to the item.</xs:documentation>
+ </xs:annotation>
+ </xs:element>
+ <xs:element name="enclosure" type="Enclosure" minOccurs="0">
+ <xs:annotation>
+ <xs:documentation>Describes a media object that is attached to the item.</xs:documentation>
+ </xs:annotation>
+ </xs:element>
+ <xs:element name="guid" type="Guid" minOccurs="0">
+ <xs:annotation>
+ <xs:documentation>guid or permalink URL for this entry</xs:documentation>
+ </xs:annotation>
+ </xs:element>
+ <xs:element name="pubDate" type="Rfc822FormatDate" minOccurs="0">
+ <xs:annotation>
+ <xs:documentation>Indicates when the item was published.</xs:documentation>
+ </xs:annotation>
+ </xs:element>
+ <xs:element name="source" type="Source" minOccurs="0">
+ <xs:annotation>
+ <xs:documentation>The RSS channel that the item came from.</xs:documentation>
+ </xs:annotation>
+ </xs:element>
+ <xs:any namespace="##other" processContents="lax" minOccurs="0" maxOccurs="unbounded">
+ <xs:annotation>
+ <xs:documentation>Extensibility element.</xs:documentation>
+ </xs:annotation>
+ </xs:any>
+ </xs:choice>
+ </xs:sequence>
+ <xs:anyAttribute namespace="##any"/>
+ </xs:complexType>
+ <xs:complexType name="RssChannel">
+ <xs:sequence>
+ <xs:choice maxOccurs="unbounded">
+ <xs:element name="title" type="xs:string">
+ <xs:annotation>
+ <xs:documentation>The name of the channel. It's how people refer to your service. If you have an HTML website that contains the same information as your RSS file, the title of your channel should be the same as the title of your website.</xs:documentation>
+ </xs:annotation>
+ </xs:element>
+ <xs:element name="link" type="xs:anyURI">
+ <xs:annotation>
+ <xs:documentation>The URL to the HTML website corresponding to the channel.</xs:documentation>
+ </xs:annotation>
+ </xs:element>
+ <xs:element name="description" type="xs:string">
+ <xs:annotation>
+ <xs:documentation>Phrase or sentence describing the channel.</xs:documentation>
+ </xs:annotation>
+ </xs:element>
+ <xs:element name="language" type="xs:language" minOccurs="0">
+ <xs:annotation>
+ <xs:documentation>The language the channel is written in. This allows aggregators to group all Italian language sites, for example, on a single page. A list of allowable values for this element, as provided by Netscape, is here. You may also use values defined by the W3C.</xs:documentation>
+ </xs:annotation>
+ </xs:element>
+ <xs:element name="copyright" type="xs:string" minOccurs="0">
+ <xs:annotation>
+ <xs:documentation>Copyright notice for content in the channel.</xs:documentation>
+ </xs:annotation>
+ </xs:element>
+ <xs:element name="managingEditor" type="EmailAddress" minOccurs="0">
+ <xs:annotation>
+ <xs:documentation>Email address for person responsible for editorial content.</xs:documentation>
+ </xs:annotation>
+ </xs:element>
+ <xs:element name="webMaster" type="EmailAddress" minOccurs="0">
+ <xs:annotation>
+ <xs:documentation>Email address for person responsible for technical issues relating to channel.</xs:documentation>
+ </xs:annotation>
+ </xs:element>
+ <xs:element name="pubDate" type="Rfc822FormatDate" minOccurs="0">
+ <xs:annotation>
+ <xs:documentation>The publication date for the content in the channel. All date-times in RSS conform to the Date and Time Specification of RFC 822, with the exception that the year may be expressed with two characters or four characters (four preferred).</xs:documentation>
+ </xs:annotation>
+ </xs:element>
+ <xs:element name="lastBuildDate" type="Rfc822FormatDate" minOccurs="0">
+ <xs:annotation>
+ <xs:documentation>The last time the content of the channel changed.</xs:documentation>
+ </xs:annotation>
+ </xs:element>
+ <xs:element name="category" type="Category" minOccurs="0">
+ <xs:annotation>
+ <xs:documentation>Specify one or more categories that the channel belongs to.</xs:documentation>
+ </xs:annotation>
+ </xs:element>
+ <xs:element name="generator" type="xs:string" minOccurs="0">
+ <xs:annotation>
+ <xs:documentation>A string indicating the program used to generate the channel.</xs:documentation>
+ </xs:annotation>
+ </xs:element>
+ <xs:element name="docs" type="xs:anyURI" minOccurs="0">
+ <xs:annotation>
+ <xs:documentation>A URL that points to the documentation for the format used in the RSS file. It's probably a pointer to this page. It's for people who might stumble across an RSS file on a Web server 25 years from now and wonder what it is.</xs:documentation>
+ </xs:annotation>
+ </xs:element>
+ <xs:element name="cloud" type="Cloud" minOccurs="0">
+ <xs:annotation>
+ <xs:documentation>Allows processes to register with a cloud to be notified of updates to the channel, implementing a lightweight publish-subscribe protocol for RSS feeds.</xs:documentation>
+ </xs:annotation>
+ </xs:element>
+ <xs:element name="ttl" type="xs:nonNegativeInteger" minOccurs="0">
+ <xs:annotation>
+ <xs:documentation>ttl stands for time to live. It's a number of minutes that indicates how long a channel can be cached before refreshing from the source.</xs:documentation>
+ </xs:annotation>
+ </xs:element>
+ <xs:element name="image" type="Image" minOccurs="0">
+ <xs:annotation>
+ <xs:documentation>Specifies a GIF, JPEG or PNG image that can be displayed with the channel.</xs:documentation>
+ </xs:annotation>
+ </xs:element>
+ <xs:element name="rating" type="xs:string" minOccurs="0">
+ <xs:annotation>
+ <xs:documentation>The PICS rating for the channel.</xs:documentation>
+ </xs:annotation>
+ </xs:element>
+ <xs:element name="textInput" type="TextInput" minOccurs="0">
+ <xs:annotation>
+ <xs:documentation>Specifies a text input box that can be displayed with the channel.</xs:documentation>
+ </xs:annotation>
+ </xs:element>
+ <xs:element name="skipHours" type="SkipHoursList" minOccurs="0">
+ <xs:annotation>
+ <xs:documentation>A hint for aggregators telling them which hours they can skip.</xs:documentation>
+ </xs:annotation>
+ </xs:element>
+ <xs:element name="skipDays" type="SkipDaysList" minOccurs="0">
+ <xs:annotation>
+ <xs:documentation>A hint for aggregators telling them which days they can skip.</xs:documentation>
+ </xs:annotation>
+ </xs:element>
+ <xs:any namespace="##other" processContents="lax" minOccurs="0" maxOccurs="unbounded">
+ <xs:annotation>
+ <xs:documentation>Extensibility element.</xs:documentation>
+ </xs:annotation>
+ </xs:any>
+ </xs:choice>
+ <xs:element name="item" type="RssItem" minOccurs="1" maxOccurs="unbounded">
+ <!--
+ HACK: According to the RSS 2.0 spec, it should strictly be possible to have zero item elements,
+ but this makes the schema non-deterministic with regard to extensibility elements
+ so for the moment we undid bug-fix 10231 and set minOccurs=1 to work around this problem.
+ -->
+ </xs:element>
+ <xs:any namespace="##other" processContents="lax" minOccurs="0" maxOccurs="unbounded">
+ <xs:annotation>
+ <xs:documentation>Extensibility element.</xs:documentation>
+ </xs:annotation>
+ </xs:any>
+ </xs:sequence>
+ <xs:anyAttribute namespace="##any"/>
+ </xs:complexType>
+ <xs:simpleType name="SkipHour">
+ <xs:annotation>
+ <xs:documentation>A time in GMT when aggregators should not request the channel data. The hour beginning at midnight is hour zero.</xs:documentation>
+ </xs:annotation>
+ <xs:restriction base="xs:nonNegativeInteger">
+ <xs:minInclusive value="0"/>
+ <xs:maxInclusive value="23"/>
+ </xs:restriction>
+ </xs:simpleType>
+ <xs:complexType name="SkipHoursList">
+ <xs:sequence>
+ <xs:element name="hour" type="SkipHour" minOccurs="0" maxOccurs="24"/>
+ </xs:sequence>
+ </xs:complexType>
+ <xs:simpleType name="SkipDay">
+ <xs:annotation>
+ <xs:documentation>A day when aggregators should not request the channel data.</xs:documentation>
+ </xs:annotation>
+ <xs:restriction base="xs:string">
+ <xs:enumeration value="Monday"/>
+ <xs:enumeration value="Tuesday"/>
+ <xs:enumeration value="Wednesday"/>
+ <xs:enumeration value="Thursday"/>
+ <xs:enumeration value="Friday"/>
+ <xs:enumeration value="Saturday"/>
+ <xs:enumeration value="Sunday"/>
+ </xs:restriction>
+ </xs:simpleType>
+ <xs:complexType name="SkipDaysList">
+ <xs:sequence>
+ <xs:element name="day" type="SkipDay" minOccurs="0" maxOccurs="7">
+ <xs:annotation>
+ <xs:documentation>A time in GMT, when aggregators should not request the channel data. The hour beginning at midnight is hour zero.</xs:documentation>
+ </xs:annotation>
+ </xs:element>
+ </xs:sequence>
+ </xs:complexType>
+ <xs:complexType name="Category">
+ <xs:simpleContent>
+ <xs:extension base="xs:string">
+ <xs:attribute name="domain" type="xs:string" use="optional"/>
+ </xs:extension>
+ </xs:simpleContent>
+ </xs:complexType>
+ <xs:complexType name="Image">
+ <xs:all>
+ <xs:element name="url" type="xs:anyURI">
+ <xs:annotation>
+ <xs:documentation>The URL of the image file.</xs:documentation>
+ </xs:annotation>
+ </xs:element>
+ <xs:element name="title" type="xs:string">
+ <xs:annotation>
+ <xs:documentation>Describes the image, it's used in the ALT attribute of the HTML &lt;img&gt; tag when the channel is rendered in HTML.</xs:documentation>
+ </xs:annotation>
+ </xs:element>
+ <xs:element name="link" type="xs:anyURI">
+ <xs:annotation>
+ <xs:documentation>The URL of the site, when the channel is rendered, the image is a link to the site. (Note, in practice the image &lt;title&gt; and &lt;link&gt; should have the same value as the channel's &lt;title&gt; and &lt;link&gt;. </xs:documentation>
+ </xs:annotation>
+ </xs:element>
+ <xs:element name="width" type="ImageWidth" default="88" minOccurs="0">
+ <xs:annotation>
+ <xs:documentation>The width of the image in pixels.</xs:documentation>
+ </xs:annotation>
+ </xs:element>
+ <xs:element name="height" type="ImageHeight" default="31" minOccurs="0">
+ <xs:annotation>
+ <xs:documentation>The height of the image in pixels.</xs:documentation>
+ </xs:annotation>
+ </xs:element>
+ <xs:element name="description" type="xs:string" minOccurs="0">
+ <xs:annotation>
+ <xs:documentation>Text that is included in the TITLE attribute of the link formed around the image in the HTML rendering.</xs:documentation>
+ </xs:annotation>
+ </xs:element>
+ </xs:all>
+ </xs:complexType>
+ <xs:simpleType name="ImageHeight">
+ <xs:annotation>
+ <xs:documentation>The height of the image in pixels.</xs:documentation>
+ </xs:annotation>
+ <xs:restriction base="xs:positiveInteger">
+ <xs:maxInclusive value="400"/>
+ </xs:restriction>
+ </xs:simpleType>
+ <xs:simpleType name="ImageWidth">
+ <xs:annotation>
+ <xs:documentation>The width of the image in pixels.</xs:documentation>
+ </xs:annotation>
+ <xs:restriction base="xs:positiveInteger">
+ <xs:maxInclusive value="144"/>
+ </xs:restriction>
+ </xs:simpleType>
+ <xs:complexType name="Cloud">
+ <xs:annotation>
+ <xs:documentation>Specifies a web service that supports the rssCloud interface which can be implemented in HTTP-POST, XML-RPC or SOAP 1.1. Its purpose is to allow processes to register with a cloud to be notified of updates to the channel, implementing a lightweight publish-subscribe protocol for RSS feeds.</xs:documentation>
+ </xs:annotation>
+ <xs:attribute name="domain" type="xs:string" use="required"/>
+ <xs:attribute name="port" type="xs:positiveInteger" use="required"/>
+ <xs:attribute name="path" type="xs:string" use="required"/>
+ <xs:attribute name="registerProcedure" type="xs:string" use="required"/>
+ <xs:attribute name="protocol" type="CloudProtocol" use="required"/>
+ </xs:complexType>
+ <xs:simpleType name="CloudProtocol">
+ <xs:restriction base="xs:string">
+ <xs:enumeration value="xml-rpc"/>
+ <xs:enumeration value="http-post"/>
+ <xs:enumeration value="soap"/>
+ </xs:restriction>
+ </xs:simpleType>
+ <xs:complexType name="TextInput">
+ <xs:annotation>
+ <xs:documentation>The purpose of this element is something of a mystery! You can use it to specify a search engine box. Or to allow a reader to provide feedback. Most aggregators ignore it.</xs:documentation>
+ </xs:annotation>
+ <xs:all>
+ <xs:element name="title" type="xs:string">
+ <xs:annotation>
+ <xs:documentation>The label of the Submit button in the text input area.</xs:documentation>
+ </xs:annotation>
+ </xs:element>
+ <xs:element name="description" type="xs:string">
+ <xs:annotation>
+ <xs:documentation>Explains the text input area.</xs:documentation>
+ </xs:annotation>
+ </xs:element>
+ <xs:element name="name" type="xs:string">
+ <xs:annotation>
+ <xs:documentation>The name of the text object in the text input area.</xs:documentation>
+ </xs:annotation>
+ </xs:element>
+ <xs:element name="link" type="xs:anyURI">
+ <xs:annotation>
+ <xs:documentation>The URL of the CGI script that processes text input requests.</xs:documentation>
+ </xs:annotation>
+ </xs:element>
+ </xs:all>
+ </xs:complexType>
+ <xs:simpleType name="EmailAddress">
+ <xs:annotation>
+ <xs:documentation>Using the regexp definiton of E-Mail Address by Lucadean from the .NET RegExp Pattern Repository at http://www.3leaf.com/default/NetRegExpRepository.aspx </xs:documentation>
+ </xs:annotation>
+ <xs:restriction base="xs:string">
+ <xs:pattern value="([a-zA-Z0-9_\-])([a-zA-Z0-9_\-\.]*)@(\[((25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])\.){3}|((([a-zA-Z0-9\-]+)\.)+))([a-zA-Z]{2,}|(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])\])"/>
+ </xs:restriction>
+ </xs:simpleType>
+ <xs:simpleType name="Rfc822FormatDate">
+ <xs:annotation>
+ <xs:documentation>A date-time displayed in RFC-822 format.</xs:documentation>
+ <xs:documentation>Using the regexp definiton of rfc-822 date by Sam Ruby at http://www.intertwingly.net/blog/1360.html </xs:documentation>
+ </xs:annotation>
+ <xs:restriction base="xs:string">
+ <xs:pattern value="(((Mon)|(Tue)|(Wed)|(Thu)|(Fri)|(Sat)|(Sun)), *)?\d\d? +((Jan)|(Feb)|(Mar)|(Apr)|(May)|(Jun)|(Jul)|(Aug)|(Sep)|(Oct)|(Nov)|(Dec)) +\d\d(\d\d)? +\d\d:\d\d(:\d\d)? +(([+\-]?\d\d\d\d)|(UT)|(GMT)|(EST)|(EDT)|(CST)|(CDT)|(MST)|(MDT)|(PST)|(PDT)|\w)"/>
+ </xs:restriction>
+ </xs:simpleType>
+ <xs:complexType name="Source">
+ <xs:simpleContent>
+ <xs:extension base="xs:string">
+ <xs:attribute name="url" type="xs:anyURI"/>
+ </xs:extension>
+ </xs:simpleContent>
+ </xs:complexType>
+ <xs:complexType name="Enclosure">
+ <xs:simpleContent>
+ <xs:extension base="xs:string">
+ <xs:attribute name="url" type="xs:anyURI" use="required">
+ <xs:annotation>
+ <xs:documentation>URL where the enclosure is located</xs:documentation>
+ </xs:annotation>
+ </xs:attribute>
+ <xs:attribute name="length" type="xs:nonNegativeInteger" use="required">
+ <xs:annotation>
+ <xs:documentation>Size in bytes</xs:documentation>
+ </xs:annotation>
+ </xs:attribute>
+ <xs:attribute name="type" type="xs:string" use="required">
+ <xs:annotation>
+ <xs:documentation>MIME media-type of the enclosure</xs:documentation>
+ </xs:annotation>
+ </xs:attribute>
+ </xs:extension>
+ </xs:simpleContent>
+ </xs:complexType>
+ <xs:complexType name="Guid">
+ <xs:simpleContent>
+ <xs:extension base="xs:string">
+ <xs:attribute name="isPermaLink" type="xs:boolean" use="optional" default="true"/>
+ </xs:extension>
+ </xs:simpleContent>
+ </xs:complexType>
+
+<!--
+TODO:
+- Need to add regexp pattern for MIME media-type value of tEnclosure/type
+- Need to add regexp pattern for checking contents of guid is a URL when isPermaLink=true"
+- Need to add some form of constraint to check on an item that one, or other, or both of title and description are present.
+ However, I'm not sure it is possible to represent these constraints in XML Schema language alone.
+- Need some way to enforce cardinality constraints preventing repeated elements in channels or items
+- Unfortunately the bug-fix for issue 10231 made this schema non-deterministic with respect to extensibitity elements.
+ We can't tell whether an extension element in tRssChannel is within the choice or after the item elements.
+ Need to reconsider the solution to bug-fix 10231.
+-->
+
+<!--
+Change Log:
+Date Revision Description
+31-Mar-2003 1 Initial version released for comment
+31-Mar-2003 2 Changes based on feedback from Gudge:
+ - Remove targetNamespace="" and use elemenfFormDefault="unqualified" instead
+ - Use namespace="##other" on <any>'s to create a more deterministic data model.
+ - Added missing xs:documentation inside xs:annotation at the schema level.
+ - Use xs:language for ISO Language Codes in <language> element.
+ - Change guid to a single declaration. This loses some of the checking of the
+ URL when the contents of the guid is a permaLink, so we will need to add
+ that back in with a regexp pattern.
+14-Apr-2003 3 Changes to solve some element ordering problems.
+ - Use xs:all in place of xs:sequence to support flexible ordering of elements.
+ Although the ordering constraints for elements is not clear from the
+ original specification, the custom and practice seems to be that
+ element ordering is freeform.
+ - Use elemenfFormDefault="qualified" for explicit intent.
+15-Apr-2003 4 Changes to solve some element ordering problems.
+ - Use xs:choice in place of xs:all as previous usage of <all> was invalid.
+ This creates the problem that unsufficient constraints can be applied
+ by the schema - for example, it can't prevent two title elements for an item.
+ - Use elemenfFormDefault="unqualified" for to get the correct behavious
+ when importing and combining schemas.
+15-Apr-2003 5 Putting the extensibility element inside the repeating choice solves
+ all problems with element ordering.
+15-Apr-2003 6 - skipHours and skipDays should contain a nested list of values,
+ not just a single value.
+ - Added version attribute to schema definition.
+ - Corrected type of the cloud element
+25-Apr-2003 7 - Add regexp for RFC-822 date suggested by Sam Ruby
+ - I had to leave the base type of the tRfc822FormatDate type
+ as xs:string due to the problems with using
+ a pattern with xs:dateTime described at
+ http://www.thearchitect.co.uk/weblog/archives/2003/04/000142.html
+19-Jun-2003 8 - Fixed a bug the Oxygen XML Editor spotted in the regexp for RFC-822 dates
+23-Jun-2003 9 - Added legal boilerplate license text for LGPL.
+ - Minor formatting changes.
+24-Jun-2003 10 - Missing types for item/title and item/description - Spotted by Andreas Schwotzer.
+01-Jan-2008 11 - Copy made available under the Microsoft Public License (MS-PL).
+25-May-2008 12 - Bug fix 10231 from Ken Gruven - channel can contain zero or more items.
+06-Sep-2008 13 - Fixed tab-space whitespace issues. Now always use spaces.
+ - Undid the fix for bug-fix 10231 since it made the schema non-deterministic
+ with respect to extensibility eleemnts in tRssChannel - need to reconsider the fix.
+08-Sep-2008 14 - Removed 't' prefixes from type names to improve class names
+ that get code-generated from the schema.
+22-Sep-2008 15 - Move type def for rss element in-line for improved compativility with Java 1.6 tools.
+01-Nov-2008 16 - Added the missing rating element from the spec to RssChannel.
+-->
+
+</xs:schema>
diff --git a/tests/test_command_import_wordpress.py b/tests/test_command_import_wordpress.py
new file mode 100644
index 0000000..4a30dba
--- /dev/null
+++ b/tests/test_command_import_wordpress.py
@@ -0,0 +1,92 @@
+# -*- coding: utf-8 -*-
+from __future__ import unicode_literals
+
+from context import nikola
+import os
+import unittest
+import mock
+
+
+class CommandImportWordpressTest(unittest.TestCase):
+ def setUp(self):
+ self.import_command = nikola.plugins.command_import_wordpress.CommandImportWordpress()
+ self.import_filename = os.path.abspath(
+ os.path.join(os.path.dirname(__file__),
+ 'wordpress_export_example.xml'))
+
+ def tearDown(self):
+ del self.import_command
+ del self.import_filename
+
+ def test_create_import_work_without_argument(self):
+ # Running this without an argument must not fail.
+ # It should show the proper usage of the command.
+ self.import_command.run()
+
+ def test_create_import(self):
+ data_import = mock.MagicMock()
+ site_generation = mock.MagicMock()
+ write_urlmap = mock.MagicMock()
+ write_configuration = mock.MagicMock()
+
+ with mock.patch('nikola.plugins.command_import_wordpress.CommandImportWordpress.generate_base_site', site_generation):
+ with mock.patch('nikola.plugins.command_import_wordpress.CommandImportWordpress.import_posts', data_import):
+ with mock.patch('nikola.plugins.command_import_wordpress.CommandImportWordpress.write_urlmap_csv', write_urlmap):
+ with mock.patch('nikola.plugins.command_import_wordpress.CommandImportWordpress.write_configuration', write_configuration):
+ self.import_command.run(self.import_filename)
+
+ self.assertTrue(site_generation.called)
+ self.assertTrue(data_import.called)
+
+ def test_populate_context(self):
+ channel = self.import_command.get_channel_from_file(
+ self.import_filename)
+ context = self.import_command.populate_context(channel)
+
+ for required_key in ('POST_PAGES', 'POST_COMPILERS'):
+ self.assertTrue(required_key in context)
+
+ self.assertEqual('de', context['DEFAULT_LANG'])
+ self.assertEqual('Wordpress blog title', context['BLOG_TITLE'])
+ self.assertEqual('Nikola test blog ;) - with moré Ümläüts', context['BLOG_DESCRIPTION'])
+ self.assertEqual('http://some.blog', context['BLOG_URL'])
+ self.assertEqual('mail@some.blog', context['BLOG_EMAIL'])
+ self.assertEqual('Niko', context['BLOG_AUTHOR'])
+
+ def test_importing_posts_and_attachments(self):
+ channel = self.import_command.get_channel_from_file(
+ self.import_filename)
+ self.import_command.context = self.import_command.populate_context(
+ channel)
+ self.import_command.url_map = {} # For testing we use an empty one.
+
+ write_metadata = mock.MagicMock()
+ write_content = mock.MagicMock()
+ download_mock = mock.MagicMock()
+
+ with mock.patch('nikola.plugins.command_import_wordpress.CommandImportWordpress.write_content', write_content):
+ with mock.patch('nikola.plugins.command_import_wordpress.CommandImportWordpress.write_metadata', write_metadata):
+ with mock.patch('nikola.plugins.command_import_wordpress.CommandImportWordpress.download_url_content_to_file', download_mock):
+ with mock.patch('nikola.plugins.command_import_wordpress.os.makedirs'):
+ self.import_command.import_posts(channel)
+
+ self.assertTrue(download_mock.called)
+ download_mock.assert_any_call(u'http://some.blog/wp-content/uploads/2008/07/arzt_und_pfusch-sick-cover.png', u'new_site/files/wp-content/uploads/2008/07/arzt_und_pfusch-sick-cover.png')
+
+ self.assertTrue(write_metadata.called)
+ write_metadata.assert_any_call(u'new_site/stories/kontakt.meta', 'Kontakt', u'kontakt', '2009-07-16 20:20:32', None, [])
+
+ self.assertTrue(write_content.called)
+ write_content.assert_any_call(u'new_site/posts/200704hoert.wp', '...!\n\n\n\n[caption id="attachment_16" align="alignnone" width="739" caption="caption test"]<img class="size-full wp-image-16" title="caption test" src="http://some.blog/wp-content/uploads/2009/07/caption_test.jpg" alt="caption test" width="739" height="517" />[/caption]\n\n\n\nNicht, dass daran jemals Zweifel bestanden.')
+ write_content.assert_any_call(u'new_site/posts/200807arzt-und-pfusch-s-i-c-k.wp', u'<img class="size-full wp-image-10 alignright" title="Arzt+Pfusch - S.I.C.K." src="http://some.blog/wp-content/uploads/2008/07/arzt_und_pfusch-sick-cover.png" alt="Arzt+Pfusch - S.I.C.K." width="210" height="209" />Arzt+Pfusch - S.I.C.K.Gerade bin ich \xfcber das Album <em>S.I.C.K</em> von <a title="Arzt+Pfusch" href="http://www.arztpfusch.com/" target="_blank">Arzt+Pfusch</a> gestolpert, welches Arzt+Pfusch zum Download f\xfcr lau anbieten. Das Album steht unter einer Creative Commons <a href="http://creativecommons.org/licenses/by-nc-nd/3.0/de/">BY-NC-ND</a>-Lizenz.\n\nDie Ladung <em>noisebmstupidevildustrial</em> gibts als MP3s mit <a href="http://www.archive.org/download/dmp005/dmp005_64kb_mp3.zip">64kbps</a> und <a href="http://www.archive.org/download/dmp005/dmp005_vbr_mp3.zip">VBR</a>, als Ogg Vorbis und als FLAC (letztere <a href="http://www.archive.org/details/dmp005">hier</a>). <a href="http://www.archive.org/download/dmp005/dmp005-artwork.zip">Artwork</a> und <a href="http://www.archive.org/download/dmp005/dmp005-lyrics.txt">Lyrics</a> gibts nochmal einzeln zum Download.')
+ write_content.assert_any_call(u'new_site/stories/kontakt.wp', u'<h1>Datenschutz</h1>\n\nIch erhebe und speichere automatisch in meine Server Log Files Informationen, die dein Browser an mich \xfcbermittelt. Dies sind:\n\n<ul>\n\n <li>Browsertyp und -version</li>\n\n <li>verwendetes Betriebssystem</li>\n\n <li>Referrer URL (die zuvor besuchte Seite)</li>\n\n <li>IP Adresse des zugreifenden Rechners</li>\n\n <li>Uhrzeit der Serveranfrage.</li>\n\n</ul>\n\nDiese Daten sind f\xfcr mich nicht bestimmten Personen zuordenbar. Eine Zusammenf\xfchrung dieser Daten mit anderen Datenquellen wird nicht vorgenommen, die Daten werden einzig zu statistischen Zwecken erhoben.')
+
+ self.assertTrue(len(self.import_command.url_map) > 0)
+
+ self.assertEqual(self.import_command.url_map['http://some.blog/2007/04/hoert/'], u'http://some.blog/posts/200704hoert.html')
+ self.assertEqual(self.import_command.url_map['http://some.blog/2008/07/arzt-und-pfusch-s-i-c-k/'], u'http://some.blog/posts/200807arzt-und-pfusch-s-i-c-k.html')
+ self.assertEqual(self.import_command.url_map['http://some.blog/kontakt/'], u'http://some.blog/stories/kontakt.html')
+
+
+if __name__ == '__main__':
+ unittest.main()
diff --git a/tests/test_rss_feeds.py b/tests/test_rss_feeds.py
new file mode 100644
index 0000000..2b48f36
--- /dev/null
+++ b/tests/test_rss_feeds.py
@@ -0,0 +1,104 @@
+# -*- coding: utf-8 -*-
+
+import unittest
+import os
+import re
+from StringIO import StringIO
+
+import mock
+
+from context import nikola
+from lxml import etree
+
+
+class RSSFeedTest(unittest.TestCase):
+ def setUp(self):
+ self.blog_url = "http://some.blog"
+
+ with mock.patch('nikola.nikola.utils.get_meta',
+ mock.Mock(return_value=('post title',
+ 'awesome_article',
+ '2012-10-01 22:41', 'tags',
+ 'link', 'description'))):
+ with mock.patch('nikola.nikola.utils.os.path.isdir',
+ mock.Mock(return_value=True)):
+ with mock.patch('nikola.nikola.Post.text',
+ mock.Mock(return_value='some long text')):
+
+ example_post = nikola.nikola.Post('source.file',
+ 'cache',
+ 'blog_folder',
+ True,
+ {'en': ''},
+ 'en',
+ self.blog_url,
+ 'unused message.')
+
+ opener_mock = mock.mock_open()
+
+ with mock.patch('nikola.nikola.utils.open', opener_mock, create=True):
+ nikola.nikola.utils.generic_rss_renderer('en',
+ "blog_title",
+ self.blog_url,
+ "blog_description",
+ [example_post,
+ ],
+ 'testfeed.rss')
+
+ self.file_content = ''.join(
+ [call[1][0] for call in opener_mock.mock_calls[2:-1]])
+
+ def tearDown(self):
+ pass
+
+ def test_feed_items_have_valid_URLs(self):
+ '''The items in the feed need to have valid urls in link and guid.'''
+ # This validation regex is taken from django.core.validators
+ url_validation_regex = re.compile(r'^(?:http|ftp)s?://' # http:// or https://
+ r'(?:(?:[A-Z0-9](?:[A-Z0-9-]{0,61}[A-Z0-9])?\.)+(?:[A-Z]{2,6}\.?|[A-Z0-9-]{2,}\.?)|' # domain...
+ r'localhost|' # localhost...
+ r'\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}|' # ...or ipv4
+ r'\[?[A-F0-9]*:[A-F0-9:]+\]?)' # ...or ipv6
+ r'(?::\d+)?' # optional port
+ r'(?:/?|[/?]\S+)$', re.IGNORECASE)
+
+ def is_valid_URL(url):
+ return url_validation_regex.match(url) is not None
+
+ et = etree.parse(StringIO(self.file_content))
+ channel = et.find('channel')
+ item = channel.find('item')
+ guid = item.find('guid')
+ link = item.find('link')
+
+ # As stated by W3 FEED Validator: "link must be a full and valid URL"
+ self.assertTrue(is_valid_URL(link.text),
+ 'The following URL is not valid: %s' % link.text)
+ self.assertTrue(self.blog_url in link.text)
+
+ # "guid must be a full URL, unless isPermaLink attribute
+ # is false: /weblog/posts/the-minimal-server.html "
+ self.assertTrue(is_valid_URL(guid.text),
+ 'The following URL is not valid: %s' %
+ guid.text)
+ self.assertTrue(self.blog_url in guid.text)
+
+ def test_feed_is_valid(self):
+ '''
+ A testcase to check if the generated feed is valid.
+
+ Validation can be tested with W3 FEED Validator that can be found
+ at http://feedvalidator.org
+ '''
+ rss_schema_filename = os.path.join(os.path.dirname(__file__),
+ 'rss-2_0.xsd')
+ with open(rss_schema_filename, 'r') as rss_schema_file:
+ xmlschema_doc = etree.parse(rss_schema_file)
+
+ xmlschema = etree.XMLSchema(xmlschema_doc)
+ document = etree.parse(StringIO(self.file_content))
+
+ self.assertTrue(xmlschema.validate(document))
+
+if __name__ == '__main__':
+ unittest.main()
diff --git a/tests/wordpress_export_example.xml b/tests/wordpress_export_example.xml
new file mode 100644
index 0000000..7517193
--- /dev/null
+++ b/tests/wordpress_export_example.xml
@@ -0,0 +1,174 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!-- This is a WordPress eXtended RSS file generated by WordPress as an export of your site. -->
+<rss version="2.0"
+ xmlns:excerpt="http://wordpress.org/export/1.2/excerpt/"
+ xmlns:content="http://purl.org/rss/1.0/modules/content/"
+ xmlns:wfw="http://wellformedweb.org/CommentAPI/"
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:wp="http://wordpress.org/export/1.2/"
+>
+
+<channel>
+ <title>Wordpress blog title</title>
+ <link>http://some.blog</link>
+ <description>Nikola test blog ;) - with moré Ümläüts</description>
+ <pubDate>Wed, 25 Jul 2012 22:31:24 +0000</pubDate>
+ <language>de-DE</language>
+ <wp:wxr_version>1.2</wp:wxr_version>
+ <wp:base_site_url>http://some.blog</wp:base_site_url>
+ <wp:base_blog_url>http://some.blog</wp:base_blog_url>
+
+ <wp:author><wp:author_id>2</wp:author_id><wp:author_login>Niko</wp:author_login><wp:author_email>mail@some.blog</wp:author_email><wp:author_display_name><![CDATA[Niko]]></wp:author_display_name><wp:author_first_name><![CDATA[Niko]]></wp:author_first_name><wp:author_last_name><![CDATA[]]></wp:author_last_name></wp:author>
+
+ <wp:category><wp:term_id>11</wp:term_id><wp:category_nicename>programmierung</wp:category_nicename><wp:category_parent></wp:category_parent><wp:cat_name><![CDATA[c0ding]]></wp:cat_name></wp:category>
+ <wp:tag><wp:term_id>501</wp:term_id><wp:tag_slug>dotnet</wp:tag_slug><wp:tag_name><![CDATA[.Net]]></wp:tag_name></wp:tag>
+
+ <generator>http://wordpress.org/?v=3.4.1</generator>
+
+ <item>
+ <title>Arzt+Pfusch - S.I.C.K.</title>
+ <link>http://some.blog/2008/07/arzt-und-pfusch-s-i-c-k/arzt_und_pfusch-sick-cover/</link>
+ <pubDate>Thu, 16 Jul 2009 19:40:37 +0000</pubDate>
+ <dc:creator>Niko</dc:creator>
+ <guid isPermaLink="false">http://some.blog/wp-content/uploads/2008/07/arzt_und_pfusch-sick-cover.png</guid>
+ <description></description>
+ <content:encoded><![CDATA[Das Cover von Arzt+Pfusch - S.I.C.K.]]></content:encoded>
+ <excerpt:encoded><![CDATA[Arzt+Pfusch - S.I.C.K.]]></excerpt:encoded>
+ <wp:post_id>10</wp:post_id>
+ <wp:post_date>2009-07-16 21:40:37</wp:post_date>
+ <wp:post_date_gmt>2009-07-16 19:40:37</wp:post_date_gmt>
+ <wp:comment_status>open</wp:comment_status>
+ <wp:ping_status>open</wp:ping_status>
+ <wp:post_name>arzt_und_pfusch-sick-cover</wp:post_name>
+ <wp:status>inherit</wp:status>
+ <wp:post_parent>6</wp:post_parent>
+ <wp:menu_order>0</wp:menu_order>
+ <wp:post_type>attachment</wp:post_type>
+ <wp:post_password></wp:post_password>
+ <wp:is_sticky>0</wp:is_sticky>
+ <wp:attachment_url>http://some.blog/wp-content/uploads/2008/07/arzt_und_pfusch-sick-cover.png</wp:attachment_url>
+ <wp:postmeta>
+ <wp:meta_key>_wp_attached_file</wp:meta_key>
+ <wp:meta_value><![CDATA[2008/07/arzt_und_pfusch-sick-cover.png]]></wp:meta_value>
+ </wp:postmeta>
+ <wp:postmeta>
+ <wp:meta_key>_wp_attachment_metadata</wp:meta_key>
+ <wp:meta_value><![CDATA[a:6:{s:5:"width";s:3:"300";s:6:"height";s:3:"299";s:14:"hwstring_small";s:22:"height='96' width='96'";s:4:"file";s:38:"2008/07/arzt_und_pfusch-sick-cover.png";s:5:"sizes";a:1:{s:9:"thumbnail";a:3:{s:4:"file";s:38:"arzt_und_pfusch-sick-cover-150x150.png";s:5:"width";s:3:"150";s:6:"height";s:3:"150";}}s:10:"image_meta";a:10:{s:8:"aperture";s:1:"0";s:6:"credit";s:0:"";s:6:"camera";s:0:"";s:7:"caption";s:0:"";s:17:"created_timestamp";s:1:"0";s:9:"copyright";s:0:"";s:12:"focal_length";s:1:"0";s:3:"iso";s:1:"0";s:13:"shutter_speed";s:1:"0";s:5:"title";s:0:"";}}]]></wp:meta_value>
+ </wp:postmeta>
+ </item>
+
+ <item>
+ <title>Caption test</title>
+ <link>http://some.blog/2007/04/hoert/</link>
+ <pubDate>Fri, 27 Apr 2007 13:02:35 +0000</pubDate>
+ <dc:creator>Niko</dc:creator>
+ <guid isPermaLink="false">http://some.blog/?p=17</guid>
+ <description></description>
+ <content:encoded><![CDATA[...!
+
+[caption id="attachment_16" align="alignnone" width="739" caption="caption test"]<img class="size-full wp-image-16" title="caption test" src="http://some.blog/wp-content/uploads/2009/07/caption_test.jpg" alt="caption test" width="739" height="517" />[/caption]
+
+Nicht, dass daran jemals Zweifel bestanden.]]></content:encoded>
+ <excerpt:encoded><![CDATA[]]></excerpt:encoded>
+ <wp:post_id>17</wp:post_id>
+ <wp:post_date>2007-04-27 15:02:35</wp:post_date>
+ <wp:post_date_gmt>2007-04-27 13:02:35</wp:post_date_gmt>
+ <wp:comment_status>open</wp:comment_status>
+ <wp:ping_status>open</wp:ping_status>
+ <wp:post_name>hoert</wp:post_name>
+ <wp:status>publish</wp:status>
+ <wp:post_parent>0</wp:post_parent>
+ <wp:menu_order>0</wp:menu_order>
+ <wp:post_type>post</wp:post_type>
+ <wp:post_password></wp:post_password>
+ <wp:is_sticky>0</wp:is_sticky>
+ <category domain="post_tag" nicename="bild"><![CDATA[Bild]]></category>
+ <category domain="category" nicename="musik"><![CDATA[Musik]]></category>
+ <category domain="category" nicename="unterhaltung"><![CDATA[Unterhaltung]]></category>
+ <category domain="post_tag" nicename="werbung"><![CDATA[Werbung]]></category>
+ <wp:postmeta>
+ <wp:meta_key>_edit_last</wp:meta_key>
+ <wp:meta_value><![CDATA[2]]></wp:meta_value>
+ </wp:postmeta>
+ </item>
+
+ <item>
+ <title>Arzt+Pfusch - S.I.C.K.</title>
+ <link>http://some.blog/2008/07/arzt-und-pfusch-s-i-c-k/</link>
+ <pubDate>Sat, 12 Jul 2008 19:22:06 +0000</pubDate>
+ <dc:creator>Niko</dc:creator>
+ <guid isPermaLink="false">http://some.blog/?p=6</guid>
+ <description></description>
+ <content:encoded><![CDATA[<img class="size-full wp-image-10 alignright" title="Arzt+Pfusch - S.I.C.K." src="http://some.blog/wp-content/uploads/2008/07/arzt_und_pfusch-sick-cover.png" alt="Arzt+Pfusch - S.I.C.K." width="210" height="209" />Arzt+Pfusch - S.I.C.K.Gerade bin ich über das Album <em>S.I.C.K</em> von <a title="Arzt+Pfusch" href="http://www.arztpfusch.com/" target="_blank">Arzt+Pfusch</a> gestolpert, welches Arzt+Pfusch zum Download für lau anbieten. Das Album steht unter einer Creative Commons <a href="http://creativecommons.org/licenses/by-nc-nd/3.0/de/">BY-NC-ND</a>-Lizenz.
+Die Ladung <em>noisebmstupidevildustrial</em> gibts als MP3s mit <a href="http://www.archive.org/download/dmp005/dmp005_64kb_mp3.zip">64kbps</a> und <a href="http://www.archive.org/download/dmp005/dmp005_vbr_mp3.zip">VBR</a>, als Ogg Vorbis und als FLAC (letztere <a href="http://www.archive.org/details/dmp005">hier</a>). <a href="http://www.archive.org/download/dmp005/dmp005-artwork.zip">Artwork</a> und <a href="http://www.archive.org/download/dmp005/dmp005-lyrics.txt">Lyrics</a> gibts nochmal einzeln zum Download.]]></content:encoded>
+ <excerpt:encoded><![CDATA[]]></excerpt:encoded>
+ <wp:post_id>6</wp:post_id>
+ <wp:post_date>2008-07-12 21:22:06</wp:post_date>
+ <wp:post_date_gmt>2008-07-12 19:22:06</wp:post_date_gmt>
+ <wp:comment_status>open</wp:comment_status>
+ <wp:ping_status>open</wp:ping_status>
+ <wp:post_name>arzt-und-pfusch-s-i-c-k</wp:post_name>
+ <wp:status>publish</wp:status>
+ <wp:post_parent>0</wp:post_parent>
+ <wp:menu_order>0</wp:menu_order>
+ <wp:post_type>post</wp:post_type>
+ <wp:post_password></wp:post_password>
+ <wp:is_sticky>0</wp:is_sticky>
+ <category domain="post_tag" nicename="arzt-und-pfusch"><![CDATA[Arzt+Pfusch]]></category>
+ <category domain="post_tag" nicename="creative-commons"><![CDATA[Creative Commons]]></category>
+ <category domain="post_tag" nicename="download"><![CDATA[Download]]></category>
+ <category domain="post_tag" nicename="electronic-body-music"><![CDATA[EBM]]></category>
+ <category domain="post_tag" nicename="flac"><![CDATA[Free Lossless Audio Codec]]></category>
+ <category domain="post_tag" nicename="gratis"><![CDATA[Gratis]]></category>
+ <category domain="post_tag" nicename="industrial"><![CDATA[Industrial]]></category>
+ <category domain="post_tag" nicename="mp3"><![CDATA[MP3]]></category>
+ <category domain="category" nicename="musik"><![CDATA[Musik]]></category>
+ <category domain="post_tag" nicename="ogg"><![CDATA[Ogg]]></category>
+ <wp:postmeta>
+ <wp:meta_key>_edit_last</wp:meta_key>
+ <wp:meta_value><![CDATA[2]]></wp:meta_value>
+ </wp:postmeta>
+ </item>
+
+ <item>
+ <title>Kontakt</title>
+ <link>http://some.blog/kontakt/</link>
+ <pubDate>Thu, 16 Jul 2009 18:20:32 +0000</pubDate>
+ <dc:creator>Niko</dc:creator>
+ <guid isPermaLink="false">http://some.blog/?page_id=3</guid>
+ <description></description>
+ <content:encoded><![CDATA[<h1>Datenschutz</h1>
+Ich erhebe und speichere automatisch in meine Server Log Files Informationen, die dein Browser an mich übermittelt. Dies sind:
+<ul>
+ <li>Browsertyp und -version</li>
+ <li>verwendetes Betriebssystem</li>
+ <li>Referrer URL (die zuvor besuchte Seite)</li>
+ <li>IP Adresse des zugreifenden Rechners</li>
+ <li>Uhrzeit der Serveranfrage.</li>
+</ul>
+Diese Daten sind für mich nicht bestimmten Personen zuordenbar. Eine Zusammenführung dieser Daten mit anderen Datenquellen wird nicht vorgenommen, die Daten werden einzig zu statistischen Zwecken erhoben.]]></content:encoded>
+ <excerpt:encoded><![CDATA[]]></excerpt:encoded>
+ <wp:post_id>3</wp:post_id>
+ <wp:post_date>2009-07-16 20:20:32</wp:post_date>
+ <wp:post_date_gmt>2009-07-16 18:20:32</wp:post_date_gmt>
+ <wp:comment_status>closed</wp:comment_status>
+ <wp:ping_status>closed</wp:ping_status>
+ <wp:post_name>kontakt</wp:post_name>
+ <wp:status>publish</wp:status>
+ <wp:post_parent>0</wp:post_parent>
+ <wp:menu_order>0</wp:menu_order>
+ <wp:post_type>page</wp:post_type>
+ <wp:post_password></wp:post_password>
+ <wp:is_sticky>0</wp:is_sticky>
+ <wp:postmeta>
+ <wp:meta_key>_edit_last</wp:meta_key>
+ <wp:meta_value><![CDATA[2]]></wp:meta_value>
+ </wp:postmeta>
+ <wp:postmeta>
+ <wp:meta_key>_wp_page_template</wp:meta_key>
+ <wp:meta_value><![CDATA[default]]></wp:meta_value>
+ </wp:postmeta>
+ </item>
+
+</channel>
+</rss>
diff --git a/tests/wordpress_unicode_export.xml b/tests/wordpress_unicode_export.xml
new file mode 100644
index 0000000..b2204fc
--- /dev/null
+++ b/tests/wordpress_unicode_export.xml
@@ -0,0 +1,114 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!-- This is a WordPress eXtended RSS file generated by WordPress as an export of your site. -->
+<!-- It contains information about your site's posts, pages, comments, categories, and other content. -->
+<!-- You may use this file to transfer that content from one site to another. -->
+<!-- This file is not intended to serve as a complete backup of your site. -->
+
+<!-- To import this information into a WordPress site follow these steps: -->
+<!-- 1. Log in to that site as an administrator. -->
+<!-- 2. Go to Tools: Import in the WordPress admin panel. -->
+<!-- 3. Install the "WordPress" importer from the list. -->
+<!-- 4. Activate & Run Importer. -->
+<!-- 5. Upload this file using the form provided on that page. -->
+<!-- 6. You will first be asked to map the authors in this export file to users -->
+<!-- on the site. For each author, you may choose to map to an -->
+<!-- existing user on the site or to create a new user. -->
+<!-- 7. WordPress will then import each of the posts, pages, comments, categories, etc. -->
+<!-- contained in this file into your site. -->
+
+<!-- generator="WordPress.com" created="2012-12-25 21:39"-->
+<rss version="2.0"
+ xmlns:excerpt="http://wordpress.org/export/1.2/excerpt/"
+ xmlns:content="http://purl.org/rss/1.0/modules/content/"
+ xmlns:wfw="http://wellformedweb.org/CommentAPI/"
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:wp="http://wordpress.org/export/1.2/"
+>
+
+<channel>
+ <title>Nikola Unicode Test ͵pó®t</title>
+ <link>http://nikolaunicode.wordpress.com</link>
+ <description>The greatest WordPress.com site in all the land!</description>
+ <pubDate>Tue, 25 Dec 2012 21:39:30 +0000</pubDate>
+ <language>en</language>
+ <wp:wxr_version>1.2</wp:wxr_version>
+ <wp:base_site_url>http://wordpress.com/</wp:base_site_url>
+ <wp:base_blog_url>http://nikolaunicode.wordpress.com</wp:base_blog_url>
+
+ <wp:author><wp:author_id>3804924</wp:author_id><wp:author_login>ralsina</wp:author_login><wp:author_email>roberto.alsina@gmail.com</wp:author_email><wp:author_display_name><![CDATA[ralsina]]></wp:author_display_name><wp:author_first_name><![CDATA[]]></wp:author_first_name><wp:author_last_name><![CDATA[]]></wp:author_last_name></wp:author>
+
+ <wp:category><wp:term_id>1</wp:term_id><wp:category_nicename>uncategorized</wp:category_nicename><wp:category_parent></wp:category_parent><wp:cat_name><![CDATA[Uncategorized]]></wp:cat_name></wp:category>
+ <wp:tag><wp:term_id>132937998</wp:term_id><wp:tag_slug>thag1</wp:tag_slug><wp:tag_name><![CDATA[þág1]]></wp:tag_name></wp:tag>
+ <wp:tag><wp:term_id>132937999</wp:term_id><wp:tag_slug>thag%c2%b2</wp:tag_slug><wp:tag_name><![CDATA[þág²]]></wp:tag_name></wp:tag>
+
+ <generator>http://wordpress.com/</generator>
+<cloud domain='nikolaunicode.wordpress.com' port='80' path='/?rsscloud=notify' registerProcedure='' protocol='http-post' />
+<image>
+ <url>https://s2.wp.com/i/buttonw-com.png</url>
+ <title>Nikola Unicode Test ͵pó®t</title>
+ <link>http://nikolaunicode.wordpress.com</link>
+ </image>
+ <atom:link rel="search" type="application/opensearchdescription+xml" href="http://nikolaunicode.wordpress.com/osd.xml" title="Nikola Unicode Test ͵pó®t" />
+ <atom:link rel='hub' href='http://nikolaunicode.wordpress.com/?pushpress=hub'/>
+
+ <item>
+ <title>About</title>
+ <link>http://nikolaunicode.wordpress.com/about/</link>
+ <pubDate>Tue, 25 Dec 2012 21:36:15 +0000</pubDate>
+ <dc:creator>ralsina</dc:creator>
+ <guid isPermaLink="false">http://nikolaunicode.wordpress.com/?page_id=1</guid>
+ <description></description>
+ <content:encoded><![CDATA[This is an example of a page. Unlike posts, which are displayed on your blog’s front page in the order they’re published, pages are better suited for more timeless content that you want to be easily accessible, like your About or Contact information. Click the Edit link to make changes to this page or <a title="Direct link to Add New in the Admin Dashboard" href="/wp-admin/post-new.php?post_type=page">add another page</a>.]]></content:encoded>
+ <excerpt:encoded><![CDATA[]]></excerpt:encoded>
+ <wp:post_id>1</wp:post_id>
+ <wp:post_date>2012-12-25 21:36:15</wp:post_date>
+ <wp:post_date_gmt>2012-12-25 21:36:15</wp:post_date_gmt>
+ <wp:comment_status>open</wp:comment_status>
+ <wp:ping_status>open</wp:ping_status>
+ <wp:post_name>about</wp:post_name>
+ <wp:status>publish</wp:status>
+ <wp:post_parent>0</wp:post_parent>
+ <wp:menu_order>0</wp:menu_order>
+ <wp:post_type>page</wp:post_type>
+ <wp:post_password></wp:post_password>
+ <wp:is_sticky>0</wp:is_sticky>
+ </item>
+ <item>
+ <title>Úñî©òðé title</title>
+ <link>http://nikolaunicode.wordpress.com/2012/12/25/uniode-title/</link>
+ <pubDate>Tue, 25 Dec 2012 21:38:37 +0000</pubDate>
+ <dc:creator>ralsina</dc:creator>
+ <guid isPermaLink="false">http://nikolaunicode.wordpress.com/?p=3</guid>
+ <description></description>
+ <content:encoded><![CDATA[Mó®é úñí©óðé]]></content:encoded>
+ <excerpt:encoded><![CDATA[]]></excerpt:encoded>
+ <wp:post_id>3</wp:post_id>
+ <wp:post_date>2012-12-25 21:38:37</wp:post_date>
+ <wp:post_date_gmt>2012-12-25 21:38:37</wp:post_date_gmt>
+ <wp:comment_status>open</wp:comment_status>
+ <wp:ping_status>open</wp:ping_status>
+ <wp:post_name>uniode-title</wp:post_name>
+ <wp:status>publish</wp:status>
+ <wp:post_parent>0</wp:post_parent>
+ <wp:menu_order>0</wp:menu_order>
+ <wp:post_type>post</wp:post_type>
+ <wp:post_password></wp:post_password>
+ <wp:is_sticky>0</wp:is_sticky>
+ <category domain="post_tag" nicename="thag1"><![CDATA[þág1]]></category>
+ <category domain="post_tag" nicename="thag%c2%b2"><![CDATA[þág²]]></category>
+ <category domain="category" nicename="uncategorized"><![CDATA[Uncategorized]]></category>
+ <wp:postmeta>
+ <wp:meta_key>_edit_last</wp:meta_key>
+ <wp:meta_value><![CDATA[3804924]]></wp:meta_value>
+ </wp:postmeta>
+ <wp:postmeta>
+ <wp:meta_key>_publicize_pending</wp:meta_key>
+ <wp:meta_value><![CDATA[1]]></wp:meta_value>
+ </wp:postmeta>
+ <wp:postmeta>
+ <wp:meta_key>jabber_published</wp:meta_key>
+ <wp:meta_value><![CDATA[1356471518]]></wp:meta_value>
+ </wp:postmeta>
+ </item>
+</channel>
+</rss>