summaryrefslogtreecommitdiffstats
path: root/nikola/plugins/compile
diff options
context:
space:
mode:
Diffstat (limited to 'nikola/plugins/compile')
-rw-r--r--nikola/plugins/compile/__init__.py2
-rw-r--r--nikola/plugins/compile/html.plugin2
-rw-r--r--nikola/plugins/compile/html.py8
-rw-r--r--nikola/plugins/compile/ipynb.plugin8
-rw-r--r--nikola/plugins/compile/ipynb.py150
-rw-r--r--nikola/plugins/compile/ipynb/README.txt44
-rw-r--r--nikola/plugins/compile/ipynb/__init__.py97
-rw-r--r--nikola/plugins/compile/markdown.plugin2
-rw-r--r--nikola/plugins/compile/markdown/__init__.py12
-rw-r--r--nikola/plugins/compile/markdown/mdx_gist.py48
-rw-r--r--nikola/plugins/compile/markdown/mdx_nikola.py4
-rw-r--r--nikola/plugins/compile/markdown/mdx_podcast.py2
-rw-r--r--nikola/plugins/compile/pandoc.plugin2
-rw-r--r--nikola/plugins/compile/pandoc.py11
-rw-r--r--nikola/plugins/compile/php.plugin2
-rw-r--r--nikola/plugins/compile/php.py3
-rw-r--r--nikola/plugins/compile/rest.plugin2
-rw-r--r--nikola/plugins/compile/rest/__init__.py114
-rw-r--r--nikola/plugins/compile/rest/chart.py2
-rw-r--r--nikola/plugins/compile/rest/doc.py2
-rw-r--r--nikola/plugins/compile/rest/gist.py20
-rw-r--r--nikola/plugins/compile/rest/listing.py112
-rw-r--r--nikola/plugins/compile/rest/media.py2
-rw-r--r--nikola/plugins/compile/rest/post_list.py20
-rw-r--r--nikola/plugins/compile/rest/slides.py2
-rw-r--r--nikola/plugins/compile/rest/thumbnail.plugin9
-rw-r--r--nikola/plugins/compile/rest/thumbnail.py69
-rw-r--r--nikola/plugins/compile/rest/vimeo.py12
-rw-r--r--nikola/plugins/compile/rest/youtube.py2
29 files changed, 467 insertions, 298 deletions
diff --git a/nikola/plugins/compile/__init__.py b/nikola/plugins/compile/__init__.py
index 6ad8bac..a1d17a6 100644
--- a/nikola/plugins/compile/__init__.py
+++ b/nikola/plugins/compile/__init__.py
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
-# Copyright © 2012-2014 Roberto Alsina and others.
+# Copyright © 2012-2015 Roberto Alsina and others.
# Permission is hereby granted, free of charge, to any
# person obtaining a copy of this software and associated
diff --git a/nikola/plugins/compile/html.plugin b/nikola/plugins/compile/html.plugin
index 21dd338..66623b2 100644
--- a/nikola/plugins/compile/html.plugin
+++ b/nikola/plugins/compile/html.plugin
@@ -4,7 +4,7 @@ Module = html
[Documentation]
Author = Roberto Alsina
-Version = 0.1
+Version = 1.0
Website = http://getnikola.com
Description = Compile HTML into HTML (just copy)
diff --git a/nikola/plugins/compile/html.py b/nikola/plugins/compile/html.py
index 24bf385..ab0c2f6 100644
--- a/nikola/plugins/compile/html.py
+++ b/nikola/plugins/compile/html.py
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
-# Copyright © 2012-2014 Roberto Alsina and others.
+# Copyright © 2012-2015 Roberto Alsina and others.
# Permission is hereby granted, free of charge, to any
# person obtaining a copy of this software and associated
@@ -29,18 +29,16 @@
from __future__ import unicode_literals
import os
-import re
import io
from nikola.plugin_categories import PageCompiler
from nikola.utils import makedirs, write_metadata
-_META_SEPARATOR = '(' + os.linesep * 2 + '|' + ('\n' * 2) + '|' + ("\r\n" * 2) + ')'
-
class CompileHtml(PageCompiler):
"""Compile HTML into HTML."""
name = "html"
+ friendly_name = "HTML"
def compile_html(self, source, dest, is_two_file=True):
makedirs(os.path.dirname(dest))
@@ -48,7 +46,7 @@ class CompileHtml(PageCompiler):
with io.open(source, "r", encoding="utf8") as in_file:
data = in_file.read()
if not is_two_file:
- data = re.split(_META_SEPARATOR, data, maxsplit=1)[-1]
+ _, data = self.split_metadata(data)
out_file.write(data)
return True
diff --git a/nikola/plugins/compile/ipynb.plugin b/nikola/plugins/compile/ipynb.plugin
index e258d8a..efe6702 100644
--- a/nikola/plugins/compile/ipynb.plugin
+++ b/nikola/plugins/compile/ipynb.plugin
@@ -3,8 +3,8 @@ Name = ipynb
Module = ipynb
[Documentation]
-Author = Damian Avila
-Version = 1.0
-Website = http://www.oquanta.info
-Description = Compile IPython notebooks into HTML
+Author = Damian Avila, Chris Warrick and others
+Version = 2.0.0
+Website = http://www.damian.oquanta.info/
+Description = Compile IPython notebooks into Nikola posts
diff --git a/nikola/plugins/compile/ipynb.py b/nikola/plugins/compile/ipynb.py
new file mode 100644
index 0000000..82b76c8
--- /dev/null
+++ b/nikola/plugins/compile/ipynb.py
@@ -0,0 +1,150 @@
+# -*- coding: utf-8 -*-
+
+# Copyright © 2013-2015 Damián Avila, Chris Warrick and others.
+
+# Permission is hereby granted, free of charge, to any
+# person obtaining a copy of this software and associated
+# documentation files (the "Software"), to deal in the
+# Software without restriction, including without limitation
+# the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the
+# Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice
+# shall be included in all copies or substantial portions of
+# the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
+# PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS
+# OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+# OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+# OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+# SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+"""Implementation of compile_html based on nbconvert."""
+
+from __future__ import unicode_literals, print_function
+import io
+import os
+import sys
+
+try:
+ import IPython
+ from IPython.nbconvert.exporters import HTMLExporter
+ if IPython.version_info[0] >= 3: # API changed with 3.0.0
+ from IPython import nbformat
+ current_nbformat = nbformat.current_nbformat
+ from IPython.kernel import kernelspec
+ else:
+ import IPython.nbformat.current as nbformat
+ current_nbformat = 'json'
+ kernelspec = None
+
+ from IPython.config import Config
+ flag = True
+except ImportError:
+ flag = None
+
+from nikola.plugin_categories import PageCompiler
+from nikola.utils import makedirs, req_missing, get_logger
+
+
+class CompileIPynb(PageCompiler):
+ """Compile IPynb into HTML."""
+
+ name = "ipynb"
+ friendly_name = "Jupyter/IPython Notebook"
+ demote_headers = True
+ default_kernel = 'python2' if sys.version_info[0] == 2 else 'python3'
+
+ def set_site(self, site):
+ self.logger = get_logger('compile_ipynb', site.loghandlers)
+ super(CompileIPynb, self).set_site(site)
+
+ def compile_html(self, source, dest, is_two_file=True):
+ if flag is None:
+ req_missing(['ipython[notebook]>=2.0.0'], 'build this site (compile ipynb)')
+ makedirs(os.path.dirname(dest))
+ HTMLExporter.default_template = 'basic'
+ c = Config(self.site.config['IPYNB_CONFIG'])
+ exportHtml = HTMLExporter(config=c)
+ with io.open(dest, "w+", encoding="utf8") as out_file:
+ with io.open(source, "r", encoding="utf8") as in_file:
+ nb_json = nbformat.read(in_file, current_nbformat)
+ (body, resources) = exportHtml.from_notebook_node(nb_json)
+ out_file.write(body)
+
+ def read_metadata(self, post, file_metadata_regexp=None, unslugify_titles=False, lang=None):
+ """read metadata directly from ipynb file.
+
+ As ipynb file support arbitrary metadata as json, the metadata used by Nikola
+ will be assume to be in the 'nikola' subfield.
+ """
+ if flag is None:
+ req_missing(['ipython[notebook]>=2.0.0'], 'build this site (compile ipynb)')
+ source = post.source_path
+ with io.open(source, "r", encoding="utf8") as in_file:
+ nb_json = nbformat.read(in_file, current_nbformat)
+ # Metadata might not exist in two-file posts or in hand-crafted
+ # .ipynb files.
+ return nb_json.get('metadata', {}).get('nikola', {})
+
+ def create_post(self, path, **kw):
+ if flag is None:
+ req_missing(['ipython[notebook]>=2.0.0'], 'build this site (compile ipynb)')
+ content = kw.pop('content', None)
+ onefile = kw.pop('onefile', False)
+ kernel = kw.pop('ipython_kernel', None)
+ # is_page is not needed to create the file
+ kw.pop('is_page', False)
+
+ metadata = {}
+ metadata.update(self.default_metadata)
+ metadata.update(kw)
+
+ makedirs(os.path.dirname(path))
+
+ if content.startswith("{"):
+ # imported .ipynb file, guaranteed to start with "{" because it’s JSON.
+ nb = nbformat.reads(content, current_nbformat)
+ else:
+ if IPython.version_info[0] >= 3:
+ nb = nbformat.v4.new_notebook()
+ nb["cells"] = [nbformat.v4.new_markdown_cell(content)]
+ else:
+ nb = nbformat.new_notebook()
+ nb["worksheets"] = [nbformat.new_worksheet(cells=[nbformat.new_text_cell('markdown', [content])])]
+
+ if kernelspec is not None:
+ if kernel is None:
+ kernel = self.default_kernel
+ self.logger.notice('No kernel specified, assuming "{0}".'.format(kernel))
+
+ IPYNB_KERNELS = {}
+ ksm = kernelspec.KernelSpecManager()
+ for k in ksm.find_kernel_specs():
+ IPYNB_KERNELS[k] = ksm.get_kernel_spec(k).to_dict()
+ IPYNB_KERNELS[k]['name'] = k
+ del IPYNB_KERNELS[k]['argv']
+
+ if kernel not in IPYNB_KERNELS:
+ self.logger.error('Unknown kernel "{0}". Maybe you mispelled it?'.format(kernel))
+ self.logger.info("Available kernels: {0}".format(", ".join(sorted(IPYNB_KERNELS))))
+ raise Exception('Unknown kernel "{0}"'.format(kernel))
+
+ nb["metadata"]["kernelspec"] = IPYNB_KERNELS[kernel]
+ else:
+ # Older IPython versions don’t need kernelspecs.
+ pass
+
+ if onefile:
+ nb["metadata"]["nikola"] = metadata
+
+ with io.open(path, "w+", encoding="utf8") as fd:
+ if IPython.version_info[0] >= 3:
+ nbformat.write(nb, fd, 4)
+ else:
+ nbformat.write(nb, fd, 'ipynb')
diff --git a/nikola/plugins/compile/ipynb/README.txt b/nikola/plugins/compile/ipynb/README.txt
deleted file mode 100644
index 0a7d6db..0000000
--- a/nikola/plugins/compile/ipynb/README.txt
+++ /dev/null
@@ -1,44 +0,0 @@
-To make this work...
-
-1- You can install the "jinja-site-ipython" theme using this command:
-
-$ nikola install_theme -n jinja-site-ipython
-
-(or xkcd-site-ipython, if you want xkcd styling)
-
-More info here about themes:
-http://getnikola.com/handbook.html#getting-more-themes
-
-OR
-
-You can to download the "jinja-site-ipython" theme from here:
-https://github.com/damianavila/jinja-site-ipython-theme-for-Nikola
-and copy the "site-ipython" folder inside the "themes" folder of your site.
-
-
-2- Then, just add:
-
-post_pages = (
- ("posts/*.ipynb", "posts", "post.tmpl", True),
- ("stories/*.ipynb", "stories", "story.tmpl", False),
-)
-
-and
-
-THEME = 'jinja-site-ipython' (or 'xkcd-site-ipython', if you want xkcd styling)
-
-to your conf.py.
-Finally... to use it:
-
-$nikola new_page -f ipynb
-
-**NOTE**: Just IGNORE the "-1" and "-2" options in nikola new_page command, by default this compiler
-create one metadata file and the corresponding naive IPython notebook.
-
-$nikola build
-
-And deploy the output folder... to see it locally: $nikola serve
-If you have any doubts, just ask: @damianavila
-
-Cheers.
-Damián
diff --git a/nikola/plugins/compile/ipynb/__init__.py b/nikola/plugins/compile/ipynb/__init__.py
deleted file mode 100644
index 7dde279..0000000
--- a/nikola/plugins/compile/ipynb/__init__.py
+++ /dev/null
@@ -1,97 +0,0 @@
-# -*- coding: utf-8 -*-
-
-# Copyright © 2013-2014 Damián Avila and others.
-
-# Permission is hereby granted, free of charge, to any
-# person obtaining a copy of this software and associated
-# documentation files (the "Software"), to deal in the
-# Software without restriction, including without limitation
-# the rights to use, copy, modify, merge, publish,
-# distribute, sublicense, and/or sell copies of the
-# Software, and to permit persons to whom the Software is
-# furnished to do so, subject to the following conditions:
-#
-# The above copyright notice and this permission notice
-# shall be included in all copies or substantial portions of
-# the Software.
-#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
-# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
-# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
-# PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS
-# OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
-# OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
-# OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
-# SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-
-"""Implementation of compile_html based on nbconvert."""
-
-from __future__ import unicode_literals, print_function
-import io
-import os
-
-try:
- from IPython.nbconvert.exporters import HTMLExporter
- from IPython.nbformat import current as nbformat
- from IPython.config import Config
- flag = True
-except ImportError:
- flag = None
-
-from nikola.plugin_categories import PageCompiler
-from nikola.utils import makedirs, req_missing
-
-
-class CompileIPynb(PageCompiler):
- """Compile IPynb into HTML."""
-
- name = "ipynb"
- supports_onefile = False
- demote_headers = True
-
- def compile_html(self, source, dest, is_two_file=True):
- if flag is None:
- req_missing(['ipython>=1.1.0'], 'build this site (compile ipynb)')
- makedirs(os.path.dirname(dest))
- HTMLExporter.default_template = 'basic'
- c = Config(self.site.config['IPYNB_CONFIG'])
- exportHtml = HTMLExporter(config=c)
- with io.open(dest, "w+", encoding="utf8") as out_file:
- with io.open(source, "r", encoding="utf8") as in_file:
- nb = in_file.read()
- nb_json = nbformat.reads_json(nb)
- (body, resources) = exportHtml.from_notebook_node(nb_json)
- out_file.write(body)
-
- def create_post(self, path, **kw):
- # content and onefile are ignored by ipynb.
- kw.pop('content', None)
- onefile = kw.pop('onefile', False)
- kw.pop('is_page', False)
-
- makedirs(os.path.dirname(path))
- if onefile:
- raise Exception('The one-file format is not supported by this compiler.')
- with io.open(path, "w+", encoding="utf8") as fd:
- fd.write("""{
- "metadata": {
- "name": ""
- },
- "nbformat": 3,
- "nbformat_minor": 0,
- "worksheets": [
- {
- "cells": [
- {
- "cell_type": "code",
- "collapsed": false,
- "input": [],
- "language": "python",
- "metadata": {},
- "outputs": []
- }
- ],
- "metadata": {}
- }
- ]
-}""")
diff --git a/nikola/plugins/compile/markdown.plugin b/nikola/plugins/compile/markdown.plugin
index 157579a..a44b798 100644
--- a/nikola/plugins/compile/markdown.plugin
+++ b/nikola/plugins/compile/markdown.plugin
@@ -4,7 +4,7 @@ Module = markdown
[Documentation]
Author = Roberto Alsina
-Version = 0.1
+Version = 1.0
Website = http://getnikola.com
Description = Compile Markdown into HTML
diff --git a/nikola/plugins/compile/markdown/__init__.py b/nikola/plugins/compile/markdown/__init__.py
index 47c7c9b..fbe049d 100644
--- a/nikola/plugins/compile/markdown/__init__.py
+++ b/nikola/plugins/compile/markdown/__init__.py
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
-# Copyright © 2012-2014 Roberto Alsina and others.
+# Copyright © 2012-2015 Roberto Alsina and others.
# Permission is hereby granted, free of charge, to any
# person obtaining a copy of this software and associated
@@ -30,7 +30,6 @@ from __future__ import unicode_literals
import io
import os
-import re
try:
from markdown import markdown
@@ -45,24 +44,27 @@ from nikola.utils import makedirs, req_missing, write_metadata
class CompileMarkdown(PageCompiler):
- """Compile markdown into HTML."""
+ """Compile Markdown into HTML."""
name = "markdown"
+ friendly_name = "Markdown"
demote_headers = True
extensions = []
site = None
def set_site(self, site):
+ self.config_dependencies = []
for plugin_info in site.plugin_manager.getPluginsOfCategory("MarkdownExtension"):
if plugin_info.name in site.config['DISABLED_PLUGINS']:
site.plugin_manager.removePluginFromCategory(plugin_info, "MarkdownExtension")
continue
-
+ self.config_dependencies.append(plugin_info.name)
site.plugin_manager.activatePluginByName(plugin_info.name)
plugin_info.plugin_object.set_site(site)
self.extensions.append(plugin_info.plugin_object)
plugin_info.plugin_object.short_help = plugin_info.description
+ self.config_dependencies.append(str(sorted(site.config.get("MARKDOWN_EXTENSIONS"))))
return super(CompileMarkdown, self).set_site(site)
def compile_html(self, source, dest, is_two_file=True):
@@ -74,7 +76,7 @@ class CompileMarkdown(PageCompiler):
with io.open(source, "r", encoding="utf8") as in_file:
data = in_file.read()
if not is_two_file:
- data = re.split('(\n\n|\r\n\r\n)', data, maxsplit=1)[-1]
+ _, data = self.split_metadata(data)
output = markdown(data, self.extensions)
out_file.write(output)
diff --git a/nikola/plugins/compile/markdown/mdx_gist.py b/nikola/plugins/compile/markdown/mdx_gist.py
index 4209bdd..70e7394 100644
--- a/nikola/plugins/compile/markdown/mdx_gist.py
+++ b/nikola/plugins/compile/markdown/mdx_gist.py
@@ -203,14 +203,11 @@ except ImportError:
Extension = Pattern = object
from nikola.plugin_categories import MarkdownExtension
-from nikola.utils import get_logger, req_missing, STDERR_HANDLER
+from nikola.utils import get_logger, STDERR_HANDLER
-LOGGER = get_logger('compile_markdown.mdx_gist', STDERR_HANDLER)
+import requests
-try:
- import requests
-except ImportError:
- requests = None # NOQA
+LOGGER = get_logger('compile_markdown.mdx_gist', STDERR_HANDLER)
GIST_JS_URL = "https://gist.github.com/{0}.js"
GIST_FILE_JS_URL = "https://gist.github.com/{0}.js?file={1}"
@@ -261,32 +258,27 @@ class GistPattern(Pattern):
gist_elem.set('class', 'gist')
script_elem = etree.SubElement(gist_elem, 'script')
- if requests:
- noscript_elem = etree.SubElement(gist_elem, 'noscript')
-
- try:
- if gist_file:
- script_elem.set('src', GIST_FILE_JS_URL.format(
- gist_id, gist_file))
- raw_gist = (self.get_raw_gist_with_filename(
- gist_id, gist_file))
+ noscript_elem = etree.SubElement(gist_elem, 'noscript')
- else:
- script_elem.set('src', GIST_JS_URL.format(
- gist_id))
- raw_gist = (self.get_raw_gist(gist_id))
+ try:
+ if gist_file:
+ script_elem.set('src', GIST_FILE_JS_URL.format(
+ gist_id, gist_file))
+ raw_gist = (self.get_raw_gist_with_filename(
+ gist_id, gist_file))
- # Insert source as <pre/> within <noscript>
- pre_elem = etree.SubElement(noscript_elem, 'pre')
- pre_elem.text = AtomicString(raw_gist)
+ else:
+ script_elem.set('src', GIST_JS_URL.format(gist_id))
+ raw_gist = (self.get_raw_gist(gist_id))
- except GistFetchException as e:
- LOGGER.warn(e.message)
- warning_comment = etree.Comment(' WARNING: {0} '.format(e.message))
- noscript_elem.append(warning_comment)
+ # Insert source as <pre/> within <noscript>
+ pre_elem = etree.SubElement(noscript_elem, 'pre')
+ pre_elem.text = AtomicString(raw_gist)
- else:
- req_missing('requests', 'have inline gist source', optional=True)
+ except GistFetchException as e:
+ LOGGER.warn(e.message)
+ warning_comment = etree.Comment(' WARNING: {0} '.format(e.message))
+ noscript_elem.append(warning_comment)
return gist_elem
diff --git a/nikola/plugins/compile/markdown/mdx_nikola.py b/nikola/plugins/compile/markdown/mdx_nikola.py
index ca67511..a03547f 100644
--- a/nikola/plugins/compile/markdown/mdx_nikola.py
+++ b/nikola/plugins/compile/markdown/mdx_nikola.py
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
-# Copyright © 2012-2014 Roberto Alsina and others.
+# Copyright © 2012-2015 Roberto Alsina and others.
# Permission is hereby granted, free of charge, to any
# person obtaining a copy of this software and associated
@@ -37,7 +37,6 @@ except ImportError:
from nikola.plugin_categories import MarkdownExtension
-# FIXME: duplicated with listings.py
CODERE = re.compile('<div class="codehilite"><pre>(.*?)</pre></div>', flags=re.MULTILINE | re.DOTALL)
@@ -47,6 +46,7 @@ class NikolaPostProcessor(Postprocessor):
# python-markdown's highlighter uses <div class="codehilite"><pre>
# for code. We switch it to reST's <pre class="code">.
+ # TODO: monkey-patch for CodeHilite that uses nikola.utils.NikolaPygmentsHTML
output = CODERE.sub('<pre class="code literal-block">\\1</pre>', output)
return output
diff --git a/nikola/plugins/compile/markdown/mdx_podcast.py b/nikola/plugins/compile/markdown/mdx_podcast.py
index 9a67910..670973a 100644
--- a/nikola/plugins/compile/markdown/mdx_podcast.py
+++ b/nikola/plugins/compile/markdown/mdx_podcast.py
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
#
-# Copyright © 2013-2014 Michael Rabbitt, Roberto Alsina and others.
+# Copyright © 2013-2015 Michael Rabbitt, Roberto Alsina and others.
#
# Permission is hereby granted, free of charge, to any person obtaining a
# copy of this software and associated documentation files (the
diff --git a/nikola/plugins/compile/pandoc.plugin b/nikola/plugins/compile/pandoc.plugin
index 157b694..ad54b3b 100644
--- a/nikola/plugins/compile/pandoc.plugin
+++ b/nikola/plugins/compile/pandoc.plugin
@@ -4,7 +4,7 @@ Module = pandoc
[Documentation]
Author = Roberto Alsina
-Version = 0.1
+Version = 1.0
Website = http://getnikola.com
Description = Compile markups into HTML using pandoc
diff --git a/nikola/plugins/compile/pandoc.py b/nikola/plugins/compile/pandoc.py
index ada8035..361f158 100644
--- a/nikola/plugins/compile/pandoc.py
+++ b/nikola/plugins/compile/pandoc.py
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
-# Copyright © 2012-2014 Roberto Alsina and others.
+# Copyright © 2012-2015 Roberto Alsina and others.
# Permission is hereby granted, free of charge, to any
# person obtaining a copy of this software and associated
@@ -30,6 +30,8 @@ You will need, of course, to install pandoc
"""
+from __future__ import unicode_literals
+
import io
import os
import subprocess
@@ -42,11 +44,16 @@ class CompilePandoc(PageCompiler):
"""Compile markups into HTML using pandoc."""
name = "pandoc"
+ friendly_name = "pandoc"
+
+ def set_site(self, site):
+ self.config_dependencies = [str(site.config['PANDOC_OPTIONS'])]
+ super(CompilePandoc, self).set_site(site)
def compile_html(self, source, dest, is_two_file=True):
makedirs(os.path.dirname(dest))
try:
- subprocess.check_call(('pandoc', '-o', dest, source))
+ subprocess.check_call(['pandoc', '-o', dest, source] + self.site.config['PANDOC_OPTIONS'])
except OSError as e:
if e.strreror == 'No such file or directory':
req_missing(['pandoc'], 'build this site (compile with pandoc)', python=False)
diff --git a/nikola/plugins/compile/php.plugin b/nikola/plugins/compile/php.plugin
index ac25259..d6623b5 100644
--- a/nikola/plugins/compile/php.plugin
+++ b/nikola/plugins/compile/php.plugin
@@ -4,7 +4,7 @@ Module = php
[Documentation]
Author = Roberto Alsina
-Version = 0.1
+Version = 1.0
Website = http://getnikola.com
Description = Compile PHP into HTML (just copy and name the file .php)
diff --git a/nikola/plugins/compile/php.py b/nikola/plugins/compile/php.py
index 77344fb..bb436e5 100644
--- a/nikola/plugins/compile/php.py
+++ b/nikola/plugins/compile/php.py
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
-# Copyright © 2012-2014 Roberto Alsina and others.
+# Copyright © 2012-2015 Roberto Alsina and others.
# Permission is hereby granted, free of charge, to any
# person obtaining a copy of this software and associated
@@ -40,6 +40,7 @@ class CompilePhp(PageCompiler):
"""Compile PHP into PHP."""
name = "php"
+ friendly_name = "PHP"
def compile_html(self, source, dest, is_two_file=True):
makedirs(os.path.dirname(dest))
diff --git a/nikola/plugins/compile/rest.plugin b/nikola/plugins/compile/rest.plugin
index 55e9c59..f144809 100644
--- a/nikola/plugins/compile/rest.plugin
+++ b/nikola/plugins/compile/rest.plugin
@@ -4,7 +4,7 @@ Module = rest
[Documentation]
Author = Roberto Alsina
-Version = 0.1
+Version = 1.0
Website = http://getnikola.com
Description = Compile reSt into HTML
diff --git a/nikola/plugins/compile/rest/__init__.py b/nikola/plugins/compile/rest/__init__.py
index 98c7151..d446fe8 100644
--- a/nikola/plugins/compile/rest/__init__.py
+++ b/nikola/plugins/compile/rest/__init__.py
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
-# Copyright © 2012-2014 Roberto Alsina and others.
+# Copyright © 2012-2015 Roberto Alsina and others.
# Permission is hereby granted, free of charge, to any
# person obtaining a copy of this software and associated
@@ -27,66 +27,78 @@
from __future__ import unicode_literals
import io
import os
-import re
-
-try:
- import docutils.core
- import docutils.nodes
- import docutils.utils
- import docutils.io
- import docutils.readers.standalone
- import docutils.writers.html4css1
- has_docutils = True
-except ImportError:
- has_docutils = False
+
+import docutils.core
+import docutils.nodes
+import docutils.utils
+import docutils.io
+import docutils.readers.standalone
+import docutils.writers.html4css1
from nikola.plugin_categories import PageCompiler
-from nikola.utils import get_logger, makedirs, req_missing, write_metadata
+from nikola.utils import unicode_str, get_logger, makedirs, write_metadata
class CompileRest(PageCompiler):
- """Compile reSt into HTML."""
+ """Compile reStructuredText into HTML."""
name = "rest"
+ friendly_name = "reStructuredText"
demote_headers = True
logger = None
- def compile_html(self, source, dest, is_two_file=True):
- """Compile reSt into HTML."""
+ def _read_extra_deps(self, post):
+ """Reads contents of .dep file and returns them as a list"""
+ dep_path = post.base_path + '.dep'
+ if os.path.isfile(dep_path):
+ with io.open(dep_path, 'r+', encoding='utf8') as depf:
+ deps = [l.strip() for l in depf.readlines()]
+ return deps
+ return []
+
+ def register_extra_dependencies(self, post):
+ """Adds dependency to post object to check .dep file."""
+ post.add_dependency(lambda: self._read_extra_deps(post), 'fragment')
+
+ def compile_html_string(self, data, source_path=None, is_two_file=True):
+ """Compile reSt into HTML strings."""
+ # If errors occur, this will be added to the line number reported by
+ # docutils so the line number matches the actual line number (off by
+ # 7 with default metadata, could be more or less depending on the post).
+ add_ln = 0
+ if not is_two_file:
+ m_data, data = self.split_metadata(data)
+ add_ln = len(m_data.splitlines()) + 1
+
+ default_template_path = os.path.join(os.path.dirname(__file__), 'template.txt')
+ output, error_level, deps = rst2html(
+ data, settings_overrides={
+ 'initial_header_level': 1,
+ 'record_dependencies': True,
+ 'stylesheet_path': None,
+ 'link_stylesheet': True,
+ 'syntax_highlight': 'short',
+ 'math_output': 'mathjax',
+ 'template': default_template_path,
+ }, logger=self.logger, source_path=source_path, l_add_ln=add_ln, transforms=self.site.rst_transforms)
+ if not isinstance(output, unicode_str):
+ # To prevent some weird bugs here or there.
+ # Original issue: empty files. `output` became a bytestring.
+ output = output.decode('utf-8')
+ return output, error_level, deps
- if not has_docutils:
- req_missing(['docutils'], 'build this site (compile reStructuredText)')
+ def compile_html(self, source, dest, is_two_file=True):
+ """Compile reSt into HTML files."""
makedirs(os.path.dirname(dest))
error_level = 100
with io.open(dest, "w+", encoding="utf8") as out_file:
with io.open(source, "r", encoding="utf8") as in_file:
data = in_file.read()
- add_ln = 0
- if not is_two_file:
- spl = re.split('(\n\n|\r\n\r\n)', data, maxsplit=1)
- data = spl[-1]
- if len(spl) != 1:
- # If errors occur, this will be added to the line
- # number reported by docutils so the line number
- # matches the actual line number (off by 7 with default
- # metadata, could be more or less depending on the post
- # author).
- add_ln = len(spl[0].splitlines()) + 1
-
- default_template_path = os.path.join(os.path.dirname(__file__), 'template.txt')
- output, error_level, deps = rst2html(
- data, settings_overrides={
- 'initial_header_level': 1,
- 'record_dependencies': True,
- 'stylesheet_path': None,
- 'link_stylesheet': True,
- 'syntax_highlight': 'short',
- 'math_output': 'mathjax',
- 'template': default_template_path,
- }, logger=self.logger, source_path=source, l_add_ln=add_ln)
+ output, error_level, deps = self.compile_html_string(data, source, is_two_file)
out_file.write(output)
deps_path = dest + '.dep'
if deps.list:
+ deps.list = [p for p in deps.list if p != dest] # Don't depend on yourself (#1671)
with io.open(deps_path, "w+", encoding="utf8") as deps_file:
deps_file.write('\n'.join(deps.list))
else:
@@ -111,15 +123,18 @@ class CompileRest(PageCompiler):
with io.open(path, "w+", encoding="utf8") as fd:
if onefile:
fd.write(write_metadata(metadata))
- fd.write('\n' + content)
+ fd.write('\n')
+ fd.write(content)
def set_site(self, site):
+ self.config_dependencies = []
for plugin_info in site.plugin_manager.getPluginsOfCategory("RestExtension"):
if plugin_info.name in site.config['DISABLED_PLUGINS']:
site.plugin_manager.removePluginFromCategory(plugin_info, "RestExtension")
continue
site.plugin_manager.activatePluginByName(plugin_info.name)
+ self.config_dependencies.append(plugin_info.name)
plugin_info.plugin_object.set_site(site)
plugin_info.plugin_object.short_help = plugin_info.description
@@ -160,6 +175,13 @@ def get_observer(settings):
class NikolaReader(docutils.readers.standalone.Reader):
+ def __init__(self, *args, **kwargs):
+ self.transforms = kwargs.pop('transforms', [])
+ docutils.readers.standalone.Reader.__init__(self, *args, **kwargs)
+
+ def get_transforms(self):
+ return docutils.readers.standalone.Reader(self).get_transforms() + self.transforms
+
def new_document(self):
"""Create and return a new empty document tree (root node)."""
document = docutils.utils.new_document(self.source.source_path, self.settings)
@@ -199,7 +221,7 @@ def add_node(node, visit_function=None, depart_function=None):
def depart_Math(self, node):
self.body.append('</math>')
- For full example, you can refer to `Microdata plugin <http://plugins.getnikola.com/#microdata>`_
+ For full example, you can refer to `Microdata plugin <https://plugins.getnikola.com/#microdata>`_
"""
docutils.nodes._add_node_class_names([node.__name__])
if visit_function:
@@ -213,7 +235,7 @@ def rst2html(source, source_path=None, source_class=docutils.io.StringInput,
parser=None, parser_name='restructuredtext', writer=None,
writer_name='html', settings=None, settings_spec=None,
settings_overrides=None, config_section=None,
- enable_exit_status=None, logger=None, l_add_ln=0):
+ enable_exit_status=None, logger=None, l_add_ln=0, transforms=None):
"""
Set up & run a `Publisher`, and return a dictionary of document parts.
Dictionary keys are the names of parts, and values are Unicode strings;
@@ -231,7 +253,7 @@ def rst2html(source, source_path=None, source_class=docutils.io.StringInput,
reStructuredText syntax errors.
"""
if reader is None:
- reader = NikolaReader()
+ reader = NikolaReader(transforms=transforms)
# For our custom logging, we have special needs and special settings we
# specify here.
# logger a logger from Nikola
diff --git a/nikola/plugins/compile/rest/chart.py b/nikola/plugins/compile/rest/chart.py
index 55ddf5c..59b9dc7 100644
--- a/nikola/plugins/compile/rest/chart.py
+++ b/nikola/plugins/compile/rest/chart.py
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
-# Copyright © 2012-2014 Roberto Alsina and others.
+# Copyright © 2012-2015 Roberto Alsina and others.
# Permission is hereby granted, free of charge, to any
# person obtaining a copy of this software and associated
diff --git a/nikola/plugins/compile/rest/doc.py b/nikola/plugins/compile/rest/doc.py
index 6143606..703c234 100644
--- a/nikola/plugins/compile/rest/doc.py
+++ b/nikola/plugins/compile/rest/doc.py
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
-# Copyright © 2012-2014 Roberto Alsina and others.
+# Copyright © 2012-2015 Roberto Alsina and others.
# Permission is hereby granted, free of charge, to any
# person obtaining a copy of this software and associated
diff --git a/nikola/plugins/compile/rest/gist.py b/nikola/plugins/compile/rest/gist.py
index 65189b5..ab4d56d 100644
--- a/nikola/plugins/compile/rest/gist.py
+++ b/nikola/plugins/compile/rest/gist.py
@@ -1,16 +1,11 @@
# -*- coding: utf-8 -*-
# This file is public domain according to its author, Brian Hsu
+import requests
from docutils.parsers.rst import Directive, directives
from docutils import nodes
-try:
- import requests
-except ImportError:
- requests = None # NOQA
-
from nikola.plugin_categories import RestExtension
-from nikola.utils import req_missing
class Plugin(RestExtension):
@@ -64,22 +59,15 @@ class GitHubGist(Directive):
if 'file' in self.options:
filename = self.options['file']
- if requests is not None:
- rawGist = (self.get_raw_gist_with_filename(gistID, filename))
+ rawGist = (self.get_raw_gist_with_filename(gistID, filename))
embedHTML = ('<script src="https://gist.github.com/{0}.js'
'?file={1}"></script>').format(gistID, filename)
else:
- if requests is not None:
- rawGist = (self.get_raw_gist(gistID))
+ rawGist = (self.get_raw_gist(gistID))
embedHTML = ('<script src="https://gist.github.com/{0}.js">'
'</script>').format(gistID)
- if requests is None:
- reqnode = nodes.raw(
- '', req_missing('requests', 'have inline gist source',
- optional=True), format='html')
- else:
- reqnode = nodes.literal_block('', rawGist)
+ reqnode = nodes.literal_block('', rawGist)
return [nodes.raw('', embedHTML, format='html'),
nodes.raw('', '<noscript>', format='html'),
diff --git a/nikola/plugins/compile/rest/listing.py b/nikola/plugins/compile/rest/listing.py
index 23ec254..b8340cf 100644
--- a/nikola/plugins/compile/rest/listing.py
+++ b/nikola/plugins/compile/rest/listing.py
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
-# Copyright © 2012-2014 Roberto Alsina and others.
+# Copyright © 2012-2015 Roberto Alsina and others.
# Permission is hereby granted, free of charge, to any
# person obtaining a copy of this software and associated
@@ -31,43 +31,95 @@
from __future__ import unicode_literals
import io
import os
+import uuid
try:
from urlparse import urlunsplit
except ImportError:
from urllib.parse import urlunsplit # NOQA
+import docutils.parsers.rst.directives.body
+import docutils.parsers.rst.directives.misc
from docutils import core
from docutils import nodes
from docutils.parsers.rst import Directive, directives
+from docutils.parsers.rst.roles import set_classes
from docutils.parsers.rst.directives.misc import Include
-try:
- from docutils.parsers.rst.directives.body import CodeBlock
-except ImportError: # docutils < 0.9 (Debian Sid For The Loss)
- class CodeBlock(Directive):
- required_arguments = 1
- has_content = True
- option_spec = {}
- CODE = '<pre>{0}</pre>'
-
- def run(self):
- """ Required by the Directive interface. Create docutils nodes """
- return [nodes.raw('', self.CODE.format('\n'.join(self.content)), format='html')]
- directives.register_directive('code', CodeBlock)
+from pygments.lexers import get_lexer_by_name
+import pygments
+import pygments.util
+from nikola import utils
from nikola.plugin_categories import RestExtension
-# Add sphinx compatibility option
-CodeBlock.option_spec['linenos'] = directives.unchanged
-
-class FlexibleCodeBlock(CodeBlock):
+# A sanitized version of docutils.parsers.rst.directives.body.CodeBlock.
+class CodeBlock(Directive):
+ """Parse and mark up content of a code block."""
+ optional_arguments = 1
+ option_spec = {'class': directives.class_option,
+ 'name': directives.unchanged,
+ 'number-lines': directives.unchanged, # integer or None
+ 'linenos': directives.unchanged,
+ 'tab-width': directives.nonnegative_int}
+ has_content = True
def run(self):
+ self.assert_has_content()
+
if 'linenos' in self.options:
self.options['number-lines'] = self.options['linenos']
- return super(FlexibleCodeBlock, self).run()
-CodeBlock = FlexibleCodeBlock
+ if 'tab-width' in self.options:
+ self.content = [x.replace('\t', ' ' * self.options['tab-width']) for x in self.content]
+
+ if self.arguments:
+ language = self.arguments[0]
+ else:
+ language = 'text'
+ set_classes(self.options)
+ classes = ['code']
+ if language:
+ classes.append(language)
+ if 'classes' in self.options:
+ classes.extend(self.options['classes'])
+
+ code = '\n'.join(self.content)
+
+ try:
+ lexer = get_lexer_by_name(language)
+ except pygments.util.ClassNotFound:
+ raise self.error('Cannot find pygments lexer for language "{0}"'.format(language))
+
+ if 'number-lines' in self.options:
+ linenos = 'table'
+ # optional argument `startline`, defaults to 1
+ try:
+ linenostart = int(self.options['number-lines'] or 1)
+ except ValueError:
+ raise self.error(':number-lines: with non-integer start value')
+ else:
+ linenos = False
+ linenostart = 1 # actually unused
+
+ if self.site.invariant: # for testing purposes
+ anchor_ref = 'rest_code_' + 'fixedvaluethatisnotauuid'
+ else:
+ anchor_ref = 'rest_code_' + uuid.uuid4().hex
+
+ formatter = utils.NikolaPygmentsHTML(anchor_ref=anchor_ref, classes=classes, linenos=linenos, linenostart=linenostart)
+ out = pygments.highlight(code, lexer, formatter)
+ node = nodes.raw('', out, format='html')
+
+ self.add_name(node)
+ # if called from "include", set the source
+ if 'source' in self.options:
+ node.attributes['source'] = self.options['source']
+
+ return [node]
+
+# Monkey-patch: replace insane docutils CodeBlock with our implementation.
+docutils.parsers.rst.directives.body.CodeBlock = CodeBlock
+docutils.parsers.rst.directives.misc.CodeBlock = CodeBlock
class Plugin(RestExtension):
@@ -79,11 +131,15 @@ class Plugin(RestExtension):
# Even though listings don't use CodeBlock anymore, I am
# leaving these to make the code directive work with
# docutils < 0.9
+ CodeBlock.site = site
+ directives.register_directive('code', CodeBlock)
directives.register_directive('code-block', CodeBlock)
directives.register_directive('sourcecode', CodeBlock)
directives.register_directive('listing', Listing)
+ Listing.folders = site.config['LISTINGS_FOLDERS']
return super(Plugin, self).set_site(site)
+
# Add sphinx compatibility option
listing_spec = Include.option_spec
listing_spec['linenos'] = directives.unchanged
@@ -104,9 +160,17 @@ class Listing(Include):
option_spec = listing_spec
def run(self):
- fname = self.arguments.pop(0)
+ _fname = self.arguments.pop(0)
+ fname = _fname.replace('/', os.sep)
lang = self.arguments.pop(0)
- fpath = os.path.join('listings', fname)
+ if len(self.folders) == 1:
+ listings_folder = next(iter(self.folders.keys()))
+ if fname.startswith(listings_folder):
+ fpath = os.path.join(fname) # new syntax: specify folder name
+ else:
+ fpath = os.path.join(listings_folder, fname) # old syntax: don't specify folder name
+ else:
+ fpath = os.path.join(fname) # must be new syntax: specify folder name
self.arguments.insert(0, fpath)
self.options['code'] = lang
if 'linenos' in self.options:
@@ -114,9 +178,9 @@ class Listing(Include):
with io.open(fpath, 'r+', encoding='utf8') as fileobject:
self.content = fileobject.read().splitlines()
self.state.document.settings.record_dependencies.add(fpath)
- target = urlunsplit(("link", 'listing', fname, '', ''))
+ target = urlunsplit(("link", 'listing', fpath.replace('\\', '/'), '', ''))
generated_nodes = (
- [core.publish_doctree('`{0} <{1}>`_'.format(fname, target))[0]])
+ [core.publish_doctree('`{0} <{1}>`_'.format(_fname, target))[0]])
generated_nodes += self.get_code_from_file(fileobject)
return generated_nodes
diff --git a/nikola/plugins/compile/rest/media.py b/nikola/plugins/compile/rest/media.py
index ccda559..0363d28 100644
--- a/nikola/plugins/compile/rest/media.py
+++ b/nikola/plugins/compile/rest/media.py
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
-# Copyright © 2012-2014 Roberto Alsina and others.
+# Copyright © 2012-2015 Roberto Alsina and others.
# Permission is hereby granted, free of charge, to any
# person obtaining a copy of this software and associated
diff --git a/nikola/plugins/compile/rest/post_list.py b/nikola/plugins/compile/rest/post_list.py
index f719e31..ddbd82d 100644
--- a/nikola/plugins/compile/rest/post_list.py
+++ b/nikola/plugins/compile/rest/post_list.py
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
-# Copyright © 2013-2014 Udo Spallek, Roberto Alsina and others.
+# Copyright © 2013-2015 Udo Spallek, Roberto Alsina and others.
# Permission is hereby granted, free of charge, to any
# person obtaining a copy of this software and associated
@@ -25,7 +25,9 @@
# SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
from __future__ import unicode_literals
+import os
import uuid
+import natsort
from docutils import nodes
from docutils.parsers.rst import Directive, directives
@@ -52,7 +54,7 @@ class PostList(Directive):
Post List
=========
:Directive Arguments: None.
- :Directive Options: lang, start, stop, reverse, tags, template, id
+ :Directive Options: lang, start, stop, reverse, sort, tags, template, id
:Directive Content: None.
Provides a reStructuredText directive to create a list of posts.
@@ -77,6 +79,10 @@ class PostList(Directive):
Reverse the order of the post-list.
Defaults is to not reverse the order of posts.
+ ``sort``: string
+ Sort post list by one of each post's attributes, usually ``title`` or a
+ custom ``priority``. Defaults to None (chronological sorting).
+
``tags`` : string [, string...]
Filter posts to show only posts having at least one of the ``tags``.
Defaults to None.
@@ -105,6 +111,7 @@ class PostList(Directive):
'start': int,
'stop': int,
'reverse': directives.flag,
+ 'sort': directives.unchanged,
'tags': directives.unchanged,
'slugs': directives.unchanged,
'all': directives.flag,
@@ -124,6 +131,7 @@ class PostList(Directive):
show_all = self.options.get('all', False)
lang = self.options.get('lang', utils.LocaleBorg().current_lang)
template = self.options.get('template', 'post_list_directive.tmpl')
+ sort = self.options.get('sort')
if self.site.invariant: # for testing purposes
post_list_id = self.options.get('id', 'post_list_' + 'fixedvaluethatisnotauuid')
else:
@@ -150,6 +158,9 @@ class PostList(Directive):
filtered_timeline.append(post)
+ if sort:
+ filtered_timeline = natsort.natsorted(filtered_timeline, key=lambda post: post.meta[lang][sort], alg=natsort.ns.F | natsort.ns.IC)
+
for post in filtered_timeline[start:stop:step]:
if slugs:
cont = True
@@ -160,10 +171,15 @@ class PostList(Directive):
if cont:
continue
+ bp = post.translated_base_path(lang)
+ if os.path.exists(bp):
+ self.state.document.settings.record_dependencies.add(bp)
+
posts += [post]
if not posts:
return []
+ self.state.document.settings.record_dependencies.add("####MAGIC####TIMELINE")
template_data = {
'lang': lang,
diff --git a/nikola/plugins/compile/rest/slides.py b/nikola/plugins/compile/rest/slides.py
index ea8e413..7826f6a 100644
--- a/nikola/plugins/compile/rest/slides.py
+++ b/nikola/plugins/compile/rest/slides.py
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
-# Copyright © 2012-2014 Roberto Alsina and others.
+# Copyright © 2012-2015 Roberto Alsina and others.
# Permission is hereby granted, free of charge, to any
# person obtaining a copy of this software and associated
diff --git a/nikola/plugins/compile/rest/thumbnail.plugin b/nikola/plugins/compile/rest/thumbnail.plugin
new file mode 100644
index 0000000..3b73340
--- /dev/null
+++ b/nikola/plugins/compile/rest/thumbnail.plugin
@@ -0,0 +1,9 @@
+[Core]
+Name = rest_thumbnail
+Module = thumbnail
+
+[Documentation]
+Author = Pelle Nilsson
+Version = 0.1
+Website = http://getnikola.com
+Description = reST directive to facilitate enlargeable images with thumbnails
diff --git a/nikola/plugins/compile/rest/thumbnail.py b/nikola/plugins/compile/rest/thumbnail.py
new file mode 100644
index 0000000..5388d8d
--- /dev/null
+++ b/nikola/plugins/compile/rest/thumbnail.py
@@ -0,0 +1,69 @@
+# -*- coding: utf-8 -*-
+
+# Copyright © 2014-2015 Pelle Nilsson and others.
+
+# Permission is hereby granted, free of charge, to any
+# person obtaining a copy of this software and associated
+# documentation files (the "Software"), to deal in the
+# Software without restriction, including without limitation
+# the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the
+# Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice
+# shall be included in all copies or substantial portions of
+# the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
+# PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS
+# OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+# OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+# OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+# SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+import os
+
+from docutils.parsers.rst import directives
+from docutils.parsers.rst.directives.images import Image, Figure
+
+from nikola.plugin_categories import RestExtension
+
+
+class Plugin(RestExtension):
+
+ name = "rest_thumbnail"
+
+ def set_site(self, site):
+ self.site = site
+ directives.register_directive('thumbnail', Thumbnail)
+ return super(Plugin, self).set_site(site)
+
+
+class Thumbnail(Figure):
+
+ def align(argument):
+ return directives.choice(argument, Image.align_values)
+
+ def figwidth_value(argument):
+ if argument.lower() == 'image':
+ return 'image'
+ else:
+ return directives.length_or_percentage_or_unitless(argument, 'px')
+
+ option_spec = Image.option_spec.copy()
+ option_spec['figwidth'] = figwidth_value
+ option_spec['figclass'] = directives.class_option
+ has_content = True
+
+ def run(self):
+ uri = directives.uri(self.arguments[0])
+ self.options['target'] = uri
+ self.arguments[0] = '.thumbnail'.join(os.path.splitext(uri))
+ if self.content:
+ (node,) = Figure.run(self)
+ else:
+ (node,) = Image.run(self)
+ return [node]
diff --git a/nikola/plugins/compile/rest/vimeo.py b/nikola/plugins/compile/rest/vimeo.py
index 4b34dfe..bc44b0e 100644
--- a/nikola/plugins/compile/rest/vimeo.py
+++ b/nikola/plugins/compile/rest/vimeo.py
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
-# Copyright © 2012-2014 Roberto Alsina and others.
+# Copyright © 2012-2015 Roberto Alsina and others.
# Permission is hereby granted, free of charge, to any
# person obtaining a copy of this software and associated
@@ -28,15 +28,11 @@
from docutils import nodes
from docutils.parsers.rst import Directive, directives
-try:
- import requests
-except ImportError:
- requests = None # NOQA
+import requests
import json
from nikola.plugin_categories import RestExtension
-from nikola.utils import req_missing
class Plugin(RestExtension):
@@ -94,10 +90,6 @@ class Vimeo(Directive):
return [nodes.raw('', CODE.format(**options), format='html')]
def check_modules(self):
- msg = None
- if requests is None:
- msg = req_missing(['requests'], 'use the vimeo directive', optional=True)
- return [nodes.raw('', '<div class="text-error">{0}</div>'.format(msg), format='html')]
return None
def set_video_size(self):
diff --git a/nikola/plugins/compile/rest/youtube.py b/nikola/plugins/compile/rest/youtube.py
index b32e77a..7c6bba1 100644
--- a/nikola/plugins/compile/rest/youtube.py
+++ b/nikola/plugins/compile/rest/youtube.py
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
-# Copyright © 2012-2014 Roberto Alsina and others.
+# Copyright © 2012-2015 Roberto Alsina and others.
# Permission is hereby granted, free of charge, to any
# person obtaining a copy of this software and associated