diff options
| author | 2013-11-20 16:58:50 -0300 | |
|---|---|---|
| committer | 2013-11-20 16:58:50 -0300 | |
| commit | ca94afc07df55cb7fc6fe3b4f3011877b7881195 (patch) | |
| tree | d81e1f275aa77545f33740723f307a83dde2e0b4 /nikola/plugins/compile/rest | |
| parent | f794eee787e9cde54e6b8f53e45d69c9ddc9936a (diff) | |
Imported Upstream version 6.2.1upstream/6.2.1
Diffstat (limited to 'nikola/plugins/compile/rest')
21 files changed, 1286 insertions, 0 deletions
diff --git a/nikola/plugins/compile/rest/__init__.py b/nikola/plugins/compile/rest/__init__.py new file mode 100644 index 0000000..c71a5f8 --- /dev/null +++ b/nikola/plugins/compile/rest/__init__.py @@ -0,0 +1,200 @@ +# -*- coding: utf-8 -*- + +# Copyright © 2012-2013 Roberto Alsina and others. + +# Permission is hereby granted, free of charge, to any +# person obtaining a copy of this software and associated +# documentation files (the "Software"), to deal in the +# Software without restriction, including without limitation +# the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the +# Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice +# shall be included in all copies or substantial portions of +# the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR +# PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS +# OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR +# OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +# OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +# SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +from __future__ import unicode_literals +import codecs +import os +import re + +try: + import docutils.core + import docutils.nodes + import docutils.utils + import docutils.io + import docutils.readers.standalone + has_docutils = True +except ImportError: + has_docutils = False + +from nikola.plugin_categories import PageCompiler +from nikola.utils import get_logger, makedirs, req_missing + + +class CompileRest(PageCompiler): + """Compile reSt into HTML.""" + + name = "rest" + logger = None + + def compile_html(self, source, dest, is_two_file=True): + """Compile reSt into HTML.""" + + if not has_docutils: + req_missing(['docutils'], 'build this site (compile reStructuredText)') + makedirs(os.path.dirname(dest)) + error_level = 100 + with codecs.open(dest, "w+", "utf8") as out_file: + with codecs.open(source, "r", "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 + + output, error_level, deps = rst2html( + data, settings_overrides={ + 'initial_header_level': 2, + 'record_dependencies': True, + 'stylesheet_path': None, + 'link_stylesheet': True, + 'syntax_highlight': 'short', + 'math_output': 'mathjax', + }, logger=self.logger, l_source=source, l_add_ln=add_ln) + out_file.write(output) + deps_path = dest + '.dep' + if deps.list: + with codecs.open(deps_path, "wb+", "utf8") as deps_file: + deps_file.write('\n'.join(deps.list)) + else: + if os.path.isfile(deps_path): + os.unlink(deps_path) + if error_level < 3: + return True + else: + return False + + def create_post(self, path, onefile=False, **kw): + metadata = {} + metadata.update(self.default_metadata) + metadata.update(kw) + makedirs(os.path.dirname(path)) + with codecs.open(path, "wb+", "utf8") as fd: + if onefile: + for k, v in metadata.items(): + fd.write('.. {0}: {1}\n'.format(k, v)) + fd.write("\nWrite your post here.") + + def set_site(self, site): + for plugin_info in site.plugin_manager.getPluginsOfCategory("RestExtension"): + if (plugin_info.name in site.config['DISABLED_PLUGINS'] + or (plugin_info.name in site.EXTRA_PLUGINS and + plugin_info.name not in site.config['ENABLED_EXTRAS'])): + site.plugin_manager.removePluginFromCategory(plugin_info, "RestExtension") + continue + + site.plugin_manager.activatePluginByName(plugin_info.name) + plugin_info.plugin_object.set_site(site) + plugin_info.plugin_object.short_help = plugin_info.description + + self.logger = get_logger('compile_rest', site.loghandlers) + return super(CompileRest, self).set_site(site) + + +def get_observer(settings): + """Return an observer for the docutils Reporter.""" + def observer(msg): + """Report docutils/rest messages to a Nikola user. + + Error code mapping: + + +------+---------+------+----------+ + | dNUM | dNAME | lNUM | lNAME | d = docutils, l = logbook + +------+---------+------+----------+ + | 0 | DEBUG | 1 | DEBUG | + | 1 | INFO | 2 | INFO | + | 2 | WARNING | 4 | WARNING | + | 3 | ERROR | 5 | ERROR | + | 4 | SEVERE | 6 | CRITICAL | + +------+---------+------+----------+ + """ + errormap = {0: 1, 1: 2, 2: 4, 3: 5, 4: 6} + text = docutils.nodes.Element.astext(msg) + out = '[{source}:{line}] {text}'.format(source=settings['source'], line=msg['line'] + settings['add_ln'], text=text) + settings['logger'].log(errormap[msg['level']], out) + + return observer + + +class NikolaReader(docutils.readers.standalone.Reader): + + 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) + document.reporter.stream = False + document.reporter.attach_observer(get_observer(self.l_settings)) + return document + + +def rst2html(source, source_path=None, source_class=docutils.io.StringInput, + destination_path=None, reader=None, + 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_source='', l_add_ln=0): + """ + Set up & run a `Publisher`, and return a dictionary of document parts. + Dictionary keys are the names of parts, and values are Unicode strings; + encoding is up to the client. For programmatic use with string I/O. + + For encoded string input, be sure to set the 'input_encoding' setting to + the desired encoding. Set it to 'unicode' for unencoded Unicode string + input. Here's how:: + + publish_parts(..., settings_overrides={'input_encoding': 'unicode'}) + + Parameters: see `publish_programmatically`. + + WARNING: `reader` should be None (or NikolaReader()) if you want Nikola to report + reStructuredText syntax errors. + """ + if reader is None: + reader = NikolaReader() + # For our custom logging, we have special needs and special settings we + # specify here. + # logger a logger from Nikola + # source source filename (docutils gets a string) + # add_ln amount of metadata lines (see comment in compile_html above) + reader.l_settings = {'logger': logger, 'source': l_source, + 'add_ln': l_add_ln} + + pub = docutils.core.Publisher(reader, parser, writer, settings=settings, + source_class=source_class, + destination_class=docutils.io.StringOutput) + pub.set_components(None, parser_name, writer_name) + pub.process_programmatic_settings( + settings_spec, settings_overrides, config_section) + pub.set_source(source, source_path) + pub.set_destination(None, destination_path) + pub.publish(enable_exit_status=enable_exit_status) + + return pub.writer.parts['docinfo'] + pub.writer.parts['fragment'], pub.document.reporter.max_level, pub.settings.record_dependencies diff --git a/nikola/plugins/compile/rest/chart.plugin b/nikola/plugins/compile/rest/chart.plugin new file mode 100644 index 0000000..3e27a25 --- /dev/null +++ b/nikola/plugins/compile/rest/chart.plugin @@ -0,0 +1,10 @@ +[Core] +Name = rest_chart +Module = chart + +[Documentation] +Author = Roberto Alsina +Version = 0.1 +Website = http://getnikola.com +Description = Chart directive based in PyGal + diff --git a/nikola/plugins/compile/rest/chart.py b/nikola/plugins/compile/rest/chart.py new file mode 100644 index 0000000..ee917b9 --- /dev/null +++ b/nikola/plugins/compile/rest/chart.py @@ -0,0 +1,150 @@ +# -*- coding: utf-8 -*- + +# Copyright © 2012-2013 Roberto Alsina and others. + +# Permission is hereby granted, free of charge, to any +# person obtaining a copy of this software and associated +# documentation files (the "Software"), to deal in the +# Software without restriction, including without limitation +# the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the +# Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice +# shall be included in all copies or substantial portions of +# the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR +# PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS +# OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR +# OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +# OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +# SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +from ast import literal_eval + +from docutils import nodes +from docutils.parsers.rst import Directive, directives + +try: + import pygal +except ImportError: + pygal = None # NOQA + +from nikola.plugin_categories import RestExtension +from nikola.utils import req_missing + + +class Plugin(RestExtension): + + name = "rest_chart" + + def set_site(self, site): + self.site = site + directives.register_directive('chart', Chart) + return super(Plugin, self).set_site(site) + + +class Chart(Directive): + """ Restructured text extension for inserting charts as SVG + + Usage: + .. chart:: Bar + :title: 'Browser usage evolution (in %)' + :x_labels: ["2002", "2003", "2004", "2005", "2006", "2007"] + + 'Firefox', [None, None, 0, 16.6, 25, 31] + 'Chrome', [None, None, None, None, None, None] + 'IE', [85.8, 84.6, 84.7, 74.5, 66, 58.6] + 'Others', [14.2, 15.4, 15.3, 8.9, 9, 10.4] + """ + + has_content = True + required_arguments = 1 + option_spec = { + "copy": directives.unchanged, + "css": directives.unchanged, + "disable_xml_declaration": directives.unchanged, + "dots_size": directives.unchanged, + "explicit_size": directives.unchanged, + "fill": directives.unchanged, + "font_sizes": directives.unchanged, + "height": directives.unchanged, + "human_readable": directives.unchanged, + "include_x_axis": directives.unchanged, + "interpolate": directives.unchanged, + "interpolation_parameters": directives.unchanged, + "interpolation_precision": directives.unchanged, + "js": directives.unchanged, + "label_font_size": directives.unchanged, + "legend_at_bottom": directives.unchanged, + "legend_box_size": directives.unchanged, + "legend_font_size": directives.unchanged, + "logarithmic": directives.unchanged, + "major_label_font_size": directives.unchanged, + "margin": directives.unchanged, + "no_data_font_size": directives.unchanged, + "no_data_text": directives.unchanged, + "no_prefix": directives.unchanged, + "order_min": directives.unchanged, + "pretty_print": directives.unchanged, + "print_values": directives.unchanged, + "print_zeroes": directives.unchanged, + "range": directives.unchanged, + "rounded_bars": directives.unchanged, + "show_dots": directives.unchanged, + "show_legend": directives.unchanged, + "show_minor_x_labels": directives.unchanged, + "show_y_labels": directives.unchanged, + "spacing": directives.unchanged, + "strict": directives.unchanged, + "stroke": directives.unchanged, + "style": directives.unchanged, + "title": directives.unchanged, + "title_font_size": directives.unchanged, + "to_dict": directives.unchanged, + "tooltip_border_radius": directives.unchanged, + "tooltip_font_size": directives.unchanged, + "truncate_label": directives.unchanged, + "truncate_legend": directives.unchanged, + "value_font_size": directives.unchanged, + "value_formatter": directives.unchanged, + "width": directives.unchanged, + "x_label_rotation": directives.unchanged, + "x_labels": directives.unchanged, + "x_labels_major": directives.unchanged, + "x_labels_major_count": directives.unchanged, + "x_labels_major_every": directives.unchanged, + "x_title": directives.unchanged, + "y_label_rotation": directives.unchanged, + "y_labels": directives.unchanged, + "y_title": directives.unchanged, + "zero": directives.unchanged, + } + + def run(self): + if pygal is None: + msg = req_missing(['pygal'], 'use the Chart directive', optional=True) + return [nodes.raw('', '<div class="text-error">{0}</div>'.format(msg), format='html')] + options = {} + if 'style' in self.options: + style_name = self.options.pop('style') + else: + style_name = 'BlueStyle' + if '(' in style_name: # Parametric style + style = eval('pygal.style.' + style_name) + else: + style = getattr(pygal.style, style_name) + for k, v in self.options.items(): + options[k] = literal_eval(v) + + chart = getattr(pygal, self.arguments[0])(style=style) + chart.config(**options) + for line in self.content: + label, series = literal_eval('({0})'.format(line)) + chart.add(label, series) + + return [nodes.raw('', chart.render().decode('utf8'), format='html')] diff --git a/nikola/plugins/compile/rest/doc.plugin b/nikola/plugins/compile/rest/doc.plugin new file mode 100644 index 0000000..1984f52 --- /dev/null +++ b/nikola/plugins/compile/rest/doc.plugin @@ -0,0 +1,10 @@ +[Core] +Name = rest_doc +Module = doc + +[Documentation] +Author = Manuel Kaufmann +Version = 0.1 +Website = http://getnikola.com +Description = Role to link another page / post from the blog + diff --git a/nikola/plugins/compile/rest/doc.py b/nikola/plugins/compile/rest/doc.py new file mode 100644 index 0000000..915a7e1 --- /dev/null +++ b/nikola/plugins/compile/rest/doc.py @@ -0,0 +1,88 @@ +# -*- coding: utf-8 -*- + +# Copyright © 2012-2013 Roberto Alsina and others. + +# Permission is hereby granted, free of charge, to any +# person obtaining a copy of this software and associated +# documentation files (the "Software"), to deal in the +# Software without restriction, including without limitation +# the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the +# Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice +# shall be included in all copies or substantial portions of +# the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR +# PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS +# OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR +# OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +# OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +# SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + +from docutils import nodes +from docutils.parsers.rst import roles + +from nikola.utils import split_explicit_title +from nikola.plugin_categories import RestExtension + + +class Plugin(RestExtension): + + name = 'rest_doc' + + def set_site(self, site): + self.site = site + roles.register_canonical_role('doc', doc_role) + doc_role.site = site + return super(Plugin, self).set_site(site) + + +def doc_role(name, rawtext, text, lineno, inliner, + options={}, content=[]): + + # split link's text and post's slug in role content + has_explicit_title, title, slug = split_explicit_title(text) + + # check if the slug given is part of our blog posts/pages + twin_slugs = False + post = None + for p in doc_role.site.timeline: + if p.meta('slug') == slug: + if post is None: + post = p + else: + twin_slugs = True + break + + try: + if post is None: + raise ValueError + except ValueError: + msg = inliner.reporter.error( + '"{0}" slug doesn\'t exist.'.format(slug), + line=lineno) + prb = inliner.problematic(rawtext, rawtext, msg) + return [prb], [msg] + + if not has_explicit_title: + # use post's title as link's text + title = post.title() + + permalink = post.permalink() + if twin_slugs: + msg = inliner.reporter.warning( + 'More than one post with the same slug. Using "{0}"'.format(permalink)) + + node = make_link_node(rawtext, title, permalink, options) + return [node], [] + + +def make_link_node(rawtext, text, url, options): + node = nodes.reference(rawtext, text, refuri=url, *options) + return node diff --git a/nikola/plugins/compile/rest/gist.plugin b/nikola/plugins/compile/rest/gist.plugin new file mode 100644 index 0000000..8f498ec --- /dev/null +++ b/nikola/plugins/compile/rest/gist.plugin @@ -0,0 +1,10 @@ +[Core] +Name = rest_gist +Module = gist + +[Documentation] +Author = Roberto Alsina +Version = 0.1 +Website = http://getnikola.com +Description = Gist directive + diff --git a/nikola/plugins/compile/rest/gist.py b/nikola/plugins/compile/rest/gist.py new file mode 100644 index 0000000..e09ed76 --- /dev/null +++ b/nikola/plugins/compile/rest/gist.py @@ -0,0 +1,84 @@ +# -*- coding: utf-8 -*- +# This file is public domain according to its author, Brian Hsu + +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): + + name = "rest_gist" + + def set_site(self, site): + self.site = site + directives.register_directive('gist', GitHubGist) + return super(Plugin, self).set_site(site) + + +class GitHubGist(Directive): + """ Embed GitHub Gist. + + Usage: + + .. gist:: GIST_ID + + or + + .. gist:: GIST_URL + + + """ + + required_arguments = 1 + optional_arguments = 1 + option_spec = {'file': directives.unchanged} + final_argument_whitespace = True + has_content = False + + def get_raw_gist_with_filename(self, gistID, filename): + url = '/'.join(("https://gist.github.com/raw", gistID, filename)) + return requests.get(url).text + + def get_raw_gist(self, gistID): + url = "https://gist.github.com/raw/{0}".format(gistID) + return requests.get(url).text + + def run(self): + if 'https://' in self.arguments[0]: + gistID = self.arguments[0].split('/')[-1].strip() + else: + gistID = self.arguments[0].strip() + embedHTML = "" + rawGist = "" + + if 'file' in self.options: + filename = self.options['file'] + if requests is not None: + 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)) + 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) + + return [nodes.raw('', embedHTML, format='html'), + nodes.raw('', '<noscript>', format='html'), + reqnode, + nodes.raw('', '</noscript>', format='html')] diff --git a/nikola/plugins/compile/rest/listing.plugin b/nikola/plugins/compile/rest/listing.plugin new file mode 100644 index 0000000..4c9883e --- /dev/null +++ b/nikola/plugins/compile/rest/listing.plugin @@ -0,0 +1,10 @@ +[Core] +Name = rest_listing +Module = listing + +[Documentation] +Author = Roberto Alsina +Version = 0.1 +Website = http://getnikola.com +Description = Extension for source listings + diff --git a/nikola/plugins/compile/rest/listing.py b/nikola/plugins/compile/rest/listing.py new file mode 100644 index 0000000..31975bb --- /dev/null +++ b/nikola/plugins/compile/rest/listing.py @@ -0,0 +1,109 @@ +# -*- coding: utf-8 -*- + +# Copyright © 2012-2013 Roberto Alsina and others. + +# Permission is hereby granted, free of charge, to any +# person obtaining a copy of this software and associated +# documentation files (the "Software"), to deal in the +# Software without restriction, including without limitation +# the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the +# Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice +# shall be included in all copies or substantial portions of +# the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR +# PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS +# OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR +# OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +# OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +# SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + +""" Define and register a listing directive using the existing CodeBlock """ + + +from __future__ import unicode_literals +from codecs import open as codecs_open # for patching purposes +import os +try: + from urlparse import urlunsplit +except ImportError: + from urllib.parse import urlunsplit # NOQA + +from docutils import core +from docutils import nodes +from docutils.parsers.rst import Directive, directives +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 + 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 nikola.plugin_categories import RestExtension + + +class Plugin(RestExtension): + + name = "rest_listing" + + def set_site(self, site): + self.site = site + # Even though listings don't use CodeBlock anymore, I am + # leaving these to make the code directive work with + # docutils < 0.9 + directives.register_directive('code-block', CodeBlock) + directives.register_directive('sourcecode', CodeBlock) + directives.register_directive('listing', Listing) + return super(Plugin, self).set_site(site) + + +class Listing(Include): + """ listing directive: create a highlighted block of code from a file in listings/ + + Usage: + + .. listing:: nikola.py python + :number-lines: + + """ + has_content = False + required_arguments = 1 + optional_arguments = 1 + + def run(self): + fname = self.arguments.pop(0) + lang = self.arguments.pop(0) + fpath = os.path.join('listings', fname) + self.arguments.insert(0, fpath) + self.options['code'] = lang + with codecs_open(fpath, 'rb+', 'utf8') as fileobject: + self.content = fileobject.read().splitlines() + self.state.document.settings.record_dependencies.add(fpath) + target = urlunsplit(("link", 'listing', fname, '', '')) + generated_nodes = ( + [core.publish_doctree('`{0} <{1}>`_'.format(fname, target))[0]]) + generated_nodes += self.get_code_from_file(fileobject) + return generated_nodes + + def get_code_from_file(self, data): + """ Create CodeBlock nodes from file object content """ + return super(Listing, self).run() + + def assert_has_content(self): + """ Listing has no content, override check from superclass """ + pass diff --git a/nikola/plugins/compile/rest/media.plugin b/nikola/plugins/compile/rest/media.plugin new file mode 100644 index 0000000..5f5276b --- /dev/null +++ b/nikola/plugins/compile/rest/media.plugin @@ -0,0 +1,10 @@ +[Core] +Name = rest_media +Module = media + +[Documentation] +Author = Roberto Alsina +Version = 0.1 +Website = http://getnikola.com +Description = Directive to support oembed via micawber + diff --git a/nikola/plugins/compile/rest/media.py b/nikola/plugins/compile/rest/media.py new file mode 100644 index 0000000..d1930dd --- /dev/null +++ b/nikola/plugins/compile/rest/media.py @@ -0,0 +1,63 @@ +# -*- coding: utf-8 -*- + +# Copyright © 2012-2013 Roberto Alsina and others. + +# Permission is hereby granted, free of charge, to any +# person obtaining a copy of this software and associated +# documentation files (the "Software"), to deal in the +# Software without restriction, including without limitation +# the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the +# Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice +# shall be included in all copies or substantial portions of +# the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR +# PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS +# OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR +# OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +# OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +# SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + +from docutils import nodes +from docutils.parsers.rst import Directive, directives + +try: + import micawber +except ImportError: + micawber = None # NOQA + + +from nikola.plugin_categories import RestExtension +from nikola.utils import req_missing + + +class Plugin(RestExtension): + + name = "rest_media" + + def set_site(self, site): + self.site = site + directives.register_directive('media', Media) + return super(Plugin, self).set_site(site) + + +class Media(Directive): + """ Restructured text extension for inserting any sort of media using micawber.""" + has_content = False + required_arguments = 1 + optional_arguments = 999 + + def run(self): + if micawber is None: + msg = req_missing(['micawber'], 'use the media directive', optional=True) + return [nodes.raw('', '<div class="text-error">{0}</div>'.format(msg), format='html')] + + providers = micawber.bootstrap_basic() + return [nodes.raw('', micawber.parse_text(" ".join(self.arguments), providers), format='html')] diff --git a/nikola/plugins/compile/rest/post_list.plugin b/nikola/plugins/compile/rest/post_list.plugin new file mode 100644 index 0000000..82450a0 --- /dev/null +++ b/nikola/plugins/compile/rest/post_list.plugin @@ -0,0 +1,9 @@ +[Core] +Name = rest_post_list +Module = post_list + +[Documentation] +Author = Udo Spallek +Version = 0.1 +Website = http://getnikola.com +Description = Includes a list of posts with tag and slide based filters. diff --git a/nikola/plugins/compile/rest/post_list.py b/nikola/plugins/compile/rest/post_list.py new file mode 100644 index 0000000..eae4016 --- /dev/null +++ b/nikola/plugins/compile/rest/post_list.py @@ -0,0 +1,165 @@ +# -*- coding: utf-8 -*- + +# Copyright © 2013 Udo Spallek, Roberto Alsina and others. + +# Permission is hereby granted, free of charge, to any +# person obtaining a copy of this software and associated +# documentation files (the "Software"), to deal in the +# Software without restriction, including without limitation +# the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the +# Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice +# shall be included in all copies or substantial portions of +# the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR +# PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS +# OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR +# OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +# OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +# SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +from __future__ import unicode_literals + +import uuid + +from docutils import nodes +from docutils.parsers.rst import Directive, directives + +from nikola import utils +from nikola.plugin_categories import RestExtension + + +class Plugin(RestExtension): + name = "rest_post_list" + + def set_site(self, site): + self.site = site + directives.register_directive('post-list', PostList) + PostList.site = site + return super(Plugin, self).set_site(site) + + +class PostList(Directive): + """ + Post List + ========= + :Directive Arguments: None. + :Directive Options: lang, start, stop, reverse, tags, template, id + :Directive Content: None. + + Provides a reStructuredText directive to create a list of posts. + The posts appearing in the list can be filtered by options. + *List slicing* is provided with the *start*, *stop* and *reverse* options. + + The following not required options are recognized: + + ``start`` : integer + The index of the first post to show. + A negative value like ``-3`` will show the *last* three posts in the + post-list. + Defaults to None. + + ``stop`` : integer + The index of the last post to show. + A value negative value like ``-1`` will show every post, but not the + *last* in the post-list. + Defaults to None. + + ``reverse`` : flag + Reverse the order of the post-list. + Defaults is to not reverse the order of posts. + + ``tags`` : string [, string...] + Filter posts to show only posts having at least one of the ``tags``. + Defaults to None. + + ``slugs`` : string [, string...] + Filter posts to show only posts having at least one of the ``slugs``. + Defaults to None. + + ``all`` : flag + Shows all posts and pages in the post list. + Defaults to show only posts with set *use_in_feeds*. + + ``lang`` : string + The language of post *titles* and *links*. + Defaults to default language. + + ``template`` : string + The name of an alternative template to render the post-list. + Defaults to ``post_list_directive.tmpl`` + + ``id`` : string + A manual id for the post list. + Defaults to a random name composed by 'post_list_' + uuid.uuid4().hex. + """ + option_spec = { + 'start': int, + 'stop': int, + 'reverse': directives.flag, + 'tags': directives.unchanged, + 'slugs': directives.unchanged, + 'all': directives.flag, + 'lang': directives.unchanged, + 'template': directives.path, + 'id': directives.unchanged, + } + + def run(self): + start = self.options.get('start') + stop = self.options.get('stop') + reverse = self.options.get('reverse', False) + tags = self.options.get('tags') + tags = [t.strip().lower() for t in tags.split(',')] if tags else [] + slugs = self.options.get('slugs') + slugs = [s.strip() for s in slugs.split(',')] if slugs else [] + 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') + post_list_id = self.options.get('id', 'post_list_' + uuid.uuid4().hex) + + posts = [] + step = -1 if reverse is None else None + if show_all is None: + timeline = [p for p in self.site.timeline] + else: + timeline = [p for p in self.site.timeline if p.use_in_feeds] + + for post in timeline[start:stop:step]: + if tags: + cont = True + for tag in tags: + if tag in [t.lower() for t in post.tags]: + cont = False + + if cont: + continue + + if slugs: + cont = True + for slug in slugs: + if slug == post.meta('slug'): + cont = False + + if cont: + continue + + posts += [post] + + if not posts: + return [] + + template_data = { + 'lang': lang, + 'posts': posts, + 'date_format': self.site.GLOBAL_CONTEXT.get('date_format'), + 'post_list_id': post_list_id, + } + output = self.site.template_system.render_template( + template, None, template_data) + return [nodes.raw('', output, format='html')] diff --git a/nikola/plugins/compile/rest/slides.plugin b/nikola/plugins/compile/rest/slides.plugin new file mode 100644 index 0000000..cee4b06 --- /dev/null +++ b/nikola/plugins/compile/rest/slides.plugin @@ -0,0 +1,10 @@ +[Core] +Name = rest_slides +Module = slides + +[Documentation] +Author = Roberto Alsina +Version = 0.1 +Website = http://getnikola.com +Description = Slides directive + diff --git a/nikola/plugins/compile/rest/slides.py b/nikola/plugins/compile/rest/slides.py new file mode 100644 index 0000000..41c3314 --- /dev/null +++ b/nikola/plugins/compile/rest/slides.py @@ -0,0 +1,67 @@ +# -*- coding: utf-8 -*- + +# Copyright © 2012-2013 Roberto Alsina and others. + +# Permission is hereby granted, free of charge, to any +# person obtaining a copy of this software and associated +# documentation files (the "Software"), to deal in the +# Software without restriction, including without limitation +# the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the +# Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice +# shall be included in all copies or substantial portions of +# the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR +# PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS +# OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR +# OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +# OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +# SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +from __future__ import unicode_literals + +import uuid + +from docutils import nodes +from docutils.parsers.rst import Directive, directives + +from nikola.plugin_categories import RestExtension + + +class Plugin(RestExtension): + + name = "rest_slides" + + def set_site(self, site): + self.site = site + directives.register_directive('slides', Slides) + Slides.site = site + return super(Plugin, self).set_site(site) + + +class Slides(Directive): + """ Restructured text extension for inserting slideshows.""" + has_content = True + + def run(self): + if len(self.content) == 0: + return + + output = self.site.template_system.render_template( + 'slides.tmpl', + None, + { + 'content': self.content, + 'carousel_id': 'slides_' + uuid.uuid4().hex, + } + ) + return [nodes.raw('', output, format='html')] + + +directives.register_directive('slides', Slides) diff --git a/nikola/plugins/compile/rest/soundcloud.plugin b/nikola/plugins/compile/rest/soundcloud.plugin new file mode 100644 index 0000000..1d31a8f --- /dev/null +++ b/nikola/plugins/compile/rest/soundcloud.plugin @@ -0,0 +1,10 @@ +[Core] +Name = rest_soundcloud +Module = soundcloud + +[Documentation] +Author = Roberto Alsina +Version = 0.1 +Website = http://getnikola.com +Description = Soundcloud directive + diff --git a/nikola/plugins/compile/rest/soundcloud.py b/nikola/plugins/compile/rest/soundcloud.py new file mode 100644 index 0000000..6fb3e99 --- /dev/null +++ b/nikola/plugins/compile/rest/soundcloud.py @@ -0,0 +1,60 @@ +# -*- coding: utf-8 -*- + + +from docutils import nodes +from docutils.parsers.rst import Directive, directives + + +from nikola.plugin_categories import RestExtension + + +class Plugin(RestExtension): + + name = "rest_soundcloud" + + def set_site(self, site): + self.site = site + directives.register_directive('soundcloud', SoundCloud) + return super(Plugin, self).set_site(site) + + +CODE = ("""<iframe width="{width}" height="{height}" +scrolling="no" frameborder="no" +src="https://w.soundcloud.com/player/?url=http://api.soundcloud.com/tracks/""" + """{sid}"> +</iframe>""") + + +class SoundCloud(Directive): + """ Restructured text extension for inserting SoundCloud embedded music + + Usage: + .. soundcloud:: <sound id> + :height: 400 + :width: 600 + + """ + has_content = True + required_arguments = 1 + option_spec = { + 'width': directives.positive_int, + 'height': directives.positive_int, + } + + def run(self): + """ Required by the Directive interface. Create docutils nodes """ + self.check_content() + options = { + 'sid': self.arguments[0], + 'width': 600, + 'height': 160, + } + options.update(self.options) + return [nodes.raw('', CODE.format(**options), format='html')] + + def check_content(self): + """ Emit a deprecation warning if there is content """ + if self.content: + raise self.warning("This directive does not accept content. The " + "'key=value' format for options is deprecated, " + "use ':key: value' instead") diff --git a/nikola/plugins/compile/rest/vimeo.plugin b/nikola/plugins/compile/rest/vimeo.plugin new file mode 100644 index 0000000..e0ff3f1 --- /dev/null +++ b/nikola/plugins/compile/rest/vimeo.plugin @@ -0,0 +1,7 @@ +[Core] +Name = rest_vimeo +Module = vimeo + +[Documentation] +Description = Vimeo directive + diff --git a/nikola/plugins/compile/rest/vimeo.py b/nikola/plugins/compile/rest/vimeo.py new file mode 100644 index 0000000..6d66648 --- /dev/null +++ b/nikola/plugins/compile/rest/vimeo.py @@ -0,0 +1,125 @@ +# -*- coding: utf-8 -*- + +# Copyright © 2012-2013 Roberto Alsina and others. + +# Permission is hereby granted, free of charge, to any +# person obtaining a copy of this software and associated +# documentation files (the "Software"), to deal in the +# Software without restriction, including without limitation +# the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the +# Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice +# shall be included in all copies or substantial portions of +# the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR +# PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS +# OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR +# OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +# OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +# SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + +from docutils import nodes +from docutils.parsers.rst import Directive, directives + +try: + import requests +except ImportError: + requests = None # NOQA +import json + + +from nikola.plugin_categories import RestExtension +from nikola.utils import req_missing + + +class Plugin(RestExtension): + + name = "rest_vimeo" + + def set_site(self, site): + self.site = site + directives.register_directive('vimeo', Vimeo) + return super(Plugin, self).set_site(site) + + +CODE = """<iframe src="http://player.vimeo.com/video/{vimeo_id}" +width="{width}" height="{height}" +frameborder="0" webkitAllowFullScreen mozallowfullscreen allowFullScreen> +</iframe> +""" + +VIDEO_DEFAULT_HEIGHT = 500 +VIDEO_DEFAULT_WIDTH = 281 + + +class Vimeo(Directive): + """ Restructured text extension for inserting vimeo embedded videos + + Usage: + .. vimeo:: 20241459 + :height: 400 + :width: 600 + + """ + has_content = True + required_arguments = 1 + option_spec = { + "width": directives.positive_int, + "height": directives.positive_int, + } + + # set to False for not querying the vimeo api for size + request_size = True + + def run(self): + self.check_content() + options = { + 'vimeo_id': self.arguments[0], + 'width': VIDEO_DEFAULT_WIDTH, + 'height': VIDEO_DEFAULT_HEIGHT, + } + if self.request_size: + err = self.check_modules() + if err: + return err + self.set_video_size() + options.update(self.options) + 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): + # Only need to make a connection if width and height aren't provided + if 'height' not in self.options or 'width' not in self.options: + self.options['height'] = VIDEO_DEFAULT_HEIGHT + self.options['width'] = VIDEO_DEFAULT_WIDTH + + if json: # we can attempt to retrieve video attributes from vimeo + try: + url = ('http://vimeo.com/api/v2/video/{0}' + '.json'.format(self.arguments[0])) + data = requests.get(url).text + video_attributes = json.loads(data)[0] + self.options['height'] = video_attributes['height'] + self.options['width'] = video_attributes['width'] + except Exception: + # fall back to the defaults + pass + + def check_content(self): + if self.content: + raise self.warning("This directive does not accept content. The " + "'key=value' format for options is deprecated, " + "use ':key: value' instead") diff --git a/nikola/plugins/compile/rest/youtube.plugin b/nikola/plugins/compile/rest/youtube.plugin new file mode 100644 index 0000000..01275be --- /dev/null +++ b/nikola/plugins/compile/rest/youtube.plugin @@ -0,0 +1,8 @@ +[Core] +Name = rest_youtube +Module = youtube + +[Documentation] +Version = 0.1 +Description = Youtube directive + diff --git a/nikola/plugins/compile/rest/youtube.py b/nikola/plugins/compile/rest/youtube.py new file mode 100644 index 0000000..3d4bdd3 --- /dev/null +++ b/nikola/plugins/compile/rest/youtube.py @@ -0,0 +1,81 @@ +# -*- coding: utf-8 -*- + +# Copyright © 2012-2013 Roberto Alsina and others. + +# Permission is hereby granted, free of charge, to any +# person obtaining a copy of this software and associated +# documentation files (the "Software"), to deal in the +# Software without restriction, including without limitation +# the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the +# Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice +# shall be included in all copies or substantial portions of +# the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR +# PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS +# OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR +# OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +# OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +# SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +from docutils import nodes +from docutils.parsers.rst import Directive, directives + + +from nikola.plugin_categories import RestExtension + + +class Plugin(RestExtension): + + name = "rest_youtube" + + def set_site(self, site): + self.site = site + directives.register_directive('youtube', Youtube) + return super(Plugin, self).set_site(site) + + +CODE = """\ +<iframe width="{width}" +height="{height}" +src="http://www.youtube.com/embed/{yid}?rel=0&hd=1&wmode=transparent" +></iframe>""" + + +class Youtube(Directive): + """ Restructured text extension for inserting youtube embedded videos + + Usage: + .. youtube:: lyViVmaBQDg + :height: 400 + :width: 600 + + """ + has_content = True + required_arguments = 1 + option_spec = { + "width": directives.positive_int, + "height": directives.positive_int, + } + + def run(self): + self.check_content() + options = { + 'yid': self.arguments[0], + 'width': 425, + 'height': 344, + } + options.update(self.options) + return [nodes.raw('', CODE.format(**options), format='html')] + + def check_content(self): + if self.content: + raise self.warning("This directive does not accept content. The " + "'key=value' format for options is deprecated, " + "use ':key: value' instead") |
