diff options
Diffstat (limited to 'nikola/plugins')
35 files changed, 1470 insertions, 397 deletions
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( |
