diff options
Diffstat (limited to 'nikola/plugins')
34 files changed, 685 insertions, 508 deletions
diff --git a/nikola/plugins/__init__.py b/nikola/plugins/__init__.py index 2d9b1b2..b1de7f1 100644 --- a/nikola/plugins/__init__.py +++ b/nikola/plugins/__init__.py @@ -1,4 +1,3 @@ from __future__ import absolute_import from . import command_import_wordpress # NOQA -from . import command_build # NOQA diff --git a/nikola/plugins/command_bootswatch_theme.py b/nikola/plugins/command_bootswatch_theme.py index 6c1061f..8400c9f 100644 --- a/nikola/plugins/command_bootswatch_theme.py +++ b/nikola/plugins/command_bootswatch_theme.py @@ -23,7 +23,6 @@ # SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. from __future__ import print_function -from optparse import OptionParser import os try: @@ -38,37 +37,53 @@ class CommandBootswatchTheme(Command): """Given a swatch name and a parent theme, creates a custom theme.""" name = "bootswatch_theme" + doc_usage = "[options]" + doc_purpose = "Given a swatch name and a parent theme, creates a custom"\ + " theme." + cmd_options = [ + { + 'name': 'name', + 'short': 'n', + 'long': 'name', + 'default': 'custom', + 'type': str, + 'help': 'New theme name (default: custom)', + }, + { + 'name': 'swatch', + 'short': 's', + 'default': 'slate', + 'type': str, + 'help': 'Name of the swatch from bootswatch.com.' + }, + { + 'name': 'parent', + 'short': 'p', + 'long': 'parent', + 'default': 'site', + 'help': 'Parent theme name (default: site)', + }, + ] - def run(self, *args): + def _execute(self, options, 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') - parser.add_option("-s", "--swatch", dest="swatch", - help="Name of the swatch from bootswatch.com " - "(default: slate)", default='slate') - parser.add_option("-p", "--parent", dest="parent", - help="Parent theme name (default: site)", - default='site') - (options, args) = parser.parse_args(list(args)) - name = options.name - swatch = options.swatch - parent = options.parent + name = options['name'] + swatch = options['swatch'] + parent = options['parent'] - print("Creating '%s' theme from '%s' and '%s'" % ( - name, swatch, parent)) + print("Creating '{0}' theme from '{1}' and '{2}'".format(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) + url = '/'.join(('http://bootswatch.com', swatch, fname)) print("Downloading: ", url) data = requests.get(url).text with open(os.path.join('themes', name, 'assets', 'css', fname), @@ -77,5 +92,5 @@ class CommandBootswatchTheme(Command): 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 "{0}" to use ' + 'it.'.format(name)) diff --git a/nikola/plugins/command_build.plugin b/nikola/plugins/command_build.plugin deleted file mode 100644 index 7d029a7..0000000 --- a/nikola/plugins/command_build.plugin +++ /dev/null @@ -1,10 +0,0 @@ -[Core] -Name = build -Module = command_build - -[Documentation] -Author = Roberto Alsina -Version = 0.1 -Website = http://nikola.ralsina.com.ar -Description = Build the site. - diff --git a/nikola/plugins/command_build.py b/nikola/plugins/command_build.py deleted file mode 100644 index 8a1de5f..0000000 --- a/nikola/plugins/command_build.py +++ /dev/null @@ -1,67 +0,0 @@ -# Copyright (c) 2012 Roberto Alsina y otros. - -# Permission is hereby granted, free of charge, to any -# person obtaining a copy of this software and associated -# documentation files (the "Software"), to deal in the -# Software without restriction, including without limitation -# the rights to use, copy, modify, merge, publish, -# distribute, sublicense, and/or sell copies of the -# Software, and to permit persons to whom the Software is -# furnished to do so, subject to the following conditions: -# -# The above copyright notice and this permission notice -# shall be included in all copies or substantial portions of -# the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY -# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE -# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR -# PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS -# OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR -# OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -# OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE -# SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - -from __future__ import unicode_literals -import os -import tempfile - -from nikola.plugin_categories import Command - - -class CommandBuild(Command): - """Build the site.""" - - name = "build" - - def run(self, *args): - """Build the site using doit.""" - - # FIXME: this is crap, do it right - with tempfile.NamedTemporaryFile(suffix='.py', delete=False) as self.dodo: - self.dodo.write(b''' -import sys -sys.path.insert(0, '.') -from doit.reporter import ExecutedOnlyReporter -DOIT_CONFIG = { - 'reporter': ExecutedOnlyReporter, - 'default_tasks': ['render_site'], -} -from nikola import Nikola -import conf -SITE = Nikola(**conf.__dict__) - - -def task_render_site(): - return SITE.gen_tasks() - ''') - self.dodo.flush() - 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, self.dodo.name, - ''.join(args))) - os.unlink(self.dodo.name) diff --git a/nikola/plugins/command_check.py b/nikola/plugins/command_check.py index ae19c41..a396f63 100644 --- a/nikola/plugins/command_check.py +++ b/nikola/plugins/command_check.py @@ -23,9 +23,7 @@ # SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. from __future__ import print_function -from optparse import OptionParser import os -import sys try: from urllib import unquote from urlparse import urlparse @@ -42,25 +40,48 @@ class CommandCheck(Command): name = "check" - def run(self, *args): + doc_usage = "-l [--find-sources] | -f" + doc_purpose = "Check links and files in the generated site." + cmd_options = [ + { + 'name': 'links', + 'short': 'l', + 'long': 'check-links', + 'type': bool, + 'default': False, + 'help': 'Check for dangling links', + }, + { + 'name': 'files', + 'short': 'f', + 'long': 'check-files', + 'type': bool, + 'default': False, + 'help': 'Check for unknown files', + }, + { + 'name': 'find_sources', + 'long': 'find-sources', + 'type': bool, + 'default': False, + 'help': 'List possible source files for files with broken links.', + }, + ] + + def _execute(self, options, args): """Check the generated site.""" - parser = OptionParser(usage="nikola %s [options]" % self.name) - parser.add_option('-l', '--check-links', dest='links', - action='store_true', - help='Check for dangling links.') - parser.add_option('-f', '--check-files', dest='files', - action='store_true', help='Check for unknown files.') - - (options, args) = parser.parse_args(list(args)) - if options.links: - scan_links() - if options.files: + if not options['links'] and not options['files']: + print(self.help()) + return False + if options['links']: + scan_links(options['find_sources']) + if options['files']: scan_files() existing_targets = set([]) -def analize(task): +def analize(task, find_sources=False): try: filename = task.split(":")[-1] d = lxml.html.fromstring(open(filename).read()) @@ -79,26 +100,26 @@ def analize(task): if os.path.exists(target_filename): existing_targets.add(target_filename) else: - print("In %s broken link: " % filename, target) - if '--find-sources' in sys.argv: + print("Broken link in {0}: ".format(filename), target) + if find_sources: print("Possible sources:") - print(os.popen( - 'nikola build list --deps %s' % task, 'r').read()) + print(os.popen('nikola list --deps ' + task, + 'r').read()) print("===============================\n") except Exception as exc: print("Error with:", filename, exc) -def scan_links(): +def scan_links(find_sources=False): print("Checking Links:\n===============\n") - for task in os.popen('nikola build list --all', 'r').readlines(): + for task in os.popen('nikola list --all', 'r').readlines(): task = task.strip() if task.split(':')[0] in ('render_tags', 'render_archive', 'render_galleries', 'render_indexes', 'render_pages', 'render_site') and '.html' in task: - analize(task) + analize(task, find_sources) def scan_files(): @@ -106,7 +127,7 @@ def scan_files(): task_fnames = set([]) real_fnames = set([]) # First check that all targets are generated in the right places - for task in os.popen('nikola build list --all', 'r').readlines(): + for task in os.popen('nikola list --all', 'r').readlines(): task = task.strip() if 'output' in task and ':' in task: fname = task.split(':')[-1] diff --git a/nikola/plugins/command_console.py b/nikola/plugins/command_console.py index 7a009fd..4af759f 100644 --- a/nikola/plugins/command_console.py +++ b/nikola/plugins/command_console.py @@ -22,6 +22,8 @@ # OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE # SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +from __future__ import print_function, unicode_literals + import os from nikola.plugin_categories import Command @@ -31,5 +33,31 @@ class Deploy(Command): """Start debugging console.""" name = "console" - def run(self, *args): - os.system('python -i -c "from nikola.console import *"') + def _execute(self, options, args): + """Start the console.""" + from nikola import Nikola + try: + import conf + SITE = Nikola(**conf.__dict__) + SITE.scan_posts() + print("You can now access your configuration as conf and your " + "site engine as SITE.") + except ImportError: + print("No configuration found.") + import code + try: + import readline + except ImportError: + pass + else: + import rlcompleter + readline.set_completer(rlcompleter.Completer(globals()).complete) + readline.parse_and_bind("tab:complete") + + pythonrc = os.environ.get("PYTHONSTARTUP") + if pythonrc and os.path.isfile(pythonrc): + try: + execfile(pythonrc) # NOQA + except NameError: + pass + code.interact(local=globals()) diff --git a/nikola/plugins/command_deploy.py b/nikola/plugins/command_deploy.py index 48d6e91..ffa86ab 100644 --- a/nikola/plugins/command_deploy.py +++ b/nikola/plugins/command_deploy.py @@ -23,7 +23,6 @@ # SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. from __future__ import print_function -from optparse import OptionParser import os from nikola.plugin_categories import Command @@ -33,9 +32,10 @@ class Deploy(Command): """Deploy site. """ name = "deploy" - def run(self, *args): - parser = OptionParser(usage="nikola %s [options]" % self.name) - (options, args) = parser.parse_args(list(args)) + doc_usage = "" + doc_purpose = "Deploy the site." + + def _execute(self, command, args): for command in self.site.config['DEPLOY_COMMANDS']: print("==>", command) os.system(command) diff --git a/nikola/plugins/command_import_blogger.py b/nikola/plugins/command_import_blogger.py index aea210a..35a702e 100644 --- a/nikola/plugins/command_import_blogger.py +++ b/nikola/plugins/command_import_blogger.py @@ -27,7 +27,6 @@ import codecs import csv import datetime import os -from optparse import OptionParser import time try: @@ -52,9 +51,63 @@ class CommandImportBlogger(Command): """Import a blogger dump.""" name = "import_blogger" + needs_config = False + doc_usage = "[options] blogger_export_file" + doc_purpose = "Import a blogger dump." + cmd_options = [ + { + 'name': 'output_folder', + 'long': 'output-folder', + 'short': 'o', + 'default': 'new_site', + 'help': 'Location to write imported content.' + }, + { + 'name': 'exclude_drafts', + 'long': 'no-drafts', + 'short': 'd', + 'default': False, + 'type': bool, + 'help': "Don't import drafts", + }, + ] + + def _execute(self, options, args): + """Import a Wordpress blog from an export file into a Nikola site.""" + + # Parse the data + if feedparser is None: + print('To use the import_blogger command,' + ' you have to install the "feedparser" package.') + return + + if not args: + print(self.help()) + return + + options['filename'] = args[0] + self.blogger_export_file = options['filename'] + self.output_folder = options['output_folder'] + self.import_into_existing_site = False + self.exclude_drafts = options['exclude_drafts'] + self.url_map = {} + channel = self.get_channel_from_file(self.blogger_export_file) + self.context = self.populate_context(channel) + conf_template = self.generate_base_site() + self.context['REDIRECTIONS'] = self.configure_redirections( + self.url_map) + + self.import_posts(channel) + self.write_urlmap_csv( + os.path.join(self.output_folder, 'url_map.csv'), self.url_map) + + self.write_configuration(self.get_configuration_output_path( + ), conf_template.render(**self.context)) @classmethod def get_channel_from_file(cls, filename): + if not os.path.isfile(filename): + raise Exception("Missing file: %s" % filename) return feedparser.parse(filename) @staticmethod @@ -65,7 +118,7 @@ class CommandImportBlogger(Command): src = (urlparse(k).path + 'index.html')[1:] dst = (urlparse(v).path) if src == 'index.html': - print("Can't do a redirect for: %r" % k) + print("Can't do a redirect for: {0!r}".format(k)) else: redirections.append((src, dst)) @@ -73,11 +126,11 @@ class CommandImportBlogger(Command): def generate_base_site(self): if not os.path.exists(self.output_folder): - os.system('nikola init --empty %s' % (self.output_folder, )) + os.system('nikola init --empty ' + self.output_folder) else: self.import_into_existing_site = True - print('The folder %s already exists - assuming that this is a ' - 'already existing nikola site.' % self.output_folder) + print('The folder {0} already exists - assuming that this is a ' + 'already existing nikola site.'.format(self.output_folder)) conf_template = Template(filename=os.path.join( os.path.dirname(utils.__file__), 'conf.py.in')) @@ -92,7 +145,7 @@ class CommandImportBlogger(Command): context['BLOG_TITLE'] = channel.feed.title context['BLOG_DESCRIPTION'] = '' # Missing in the dump - context['BLOG_URL'] = channel.feed.link.rstrip('/') + context['SITE_URL'] = channel.feed.link.rstrip('/') context['BLOG_EMAIL'] = channel.feed.author_detail.email context['BLOG_AUTHOR'] = channel.feed.author_detail.name context['POST_PAGES'] = '''( @@ -124,12 +177,8 @@ class CommandImportBlogger(Command): @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) + fd.write('\n'.join((title, slug, post_date, ','.join(tags), '', + description))) def import_item(self, item, out_folder=None): """Takes an item from the feed and creates a post file.""" @@ -145,8 +194,8 @@ class CommandImportBlogger(Command): # blogger supports empty titles, which Nikola doesn't if not title: - print("Warning: Empty title in post with URL %s. Using NO_TITLE " - "as placeholder, please fix." % link) + print("Warning: Empty title in post with URL {0}. Using NO_TITLE " + "as placeholder, please fix.".format(link)) title = "NO_TITLE" if link_path.lower().endswith('.html'): @@ -179,11 +228,11 @@ class CommandImportBlogger(Command): else: is_draft = False - self.url_map[link] = self.context['BLOG_URL'] + '/' + \ + self.url_map[link] = self.context['SITE_URL'] + '/' + \ out_folder + '/' + slug + '.html' if is_draft and self.exclude_drafts: - print('Draft "%s" will not be imported.' % (title, )) + print('Draft "{0}" will not be imported.'.format(title)) elif content.strip(): # If no content is found, no files are written. content = self.transform_content(content) @@ -195,8 +244,8 @@ class CommandImportBlogger(Command): os.path.join(self.output_folder, out_folder, slug + '.html'), content) else: - print('Not going to import "%s" because it seems to contain' - ' no content.' % (title, )) + print('Not going to import "{0}" because it seems to contain' + ' no content.'.format(title)) def process_item(self, item): post_type = item.tags[0].term @@ -235,10 +284,10 @@ class CommandImportBlogger(Command): if not self.import_into_existing_site: filename = 'conf.py' else: - filename = 'conf.py.wordpress_import-%s' % datetime.datetime.now( - ).strftime('%Y%m%d_%H%M%s') + filename = 'conf.py.wordpress_import-{0}'.format( + datetime.datetime.now().strftime('%Y%m%d_%H%M%s')) config_output_path = os.path.join(self.output_folder, filename) - print('Configuration will be written to: %s' % config_output_path) + print('Configuration will be written to: ' + config_output_path) return config_output_path @@ -247,54 +296,6 @@ class CommandImportBlogger(Command): with codecs.open(filename, 'w+', 'utf8') as fd: fd.write(rendered_template) - def run(self, *arguments): - """Import a Wordpress blog from an export file into a Nikola site.""" - # Parse the data - if feedparser is None: - print('To use the import_blogger command,' - ' you have to install the "feedparser" package.') - return - - parser = OptionParser( - usage="nikola %s [options] blogger_export_file" % self.name) - parser.add_option('-f', '--filename', dest='filename', - help='Blogger export file from which the import is ' - 'made.') - parser.add_option('-o', '--output-folder', dest='output_folder', - default='new_site', - help='The location into which the imported content ' - 'will be written') - parser.add_option('-d', '--no-drafts', dest='exclude_drafts', - default=False, action="store_true", help='Do not ' - 'import drafts.') - - (options, args) = parser.parse_args(list(arguments)) - - if not options.filename and args: - options.filename = args[0] - - if not options.filename: - parser.print_usage() - return - - self.blogger_export_file = options.filename - self.output_folder = options.output_folder - self.import_into_existing_site = False - self.exclude_drafts = options.exclude_drafts - self.url_map = {} - channel = self.get_channel_from_file(self.blogger_export_file) - self.context = self.populate_context(channel) - conf_template = self.generate_base_site() - self.context['REDIRECTIONS'] = self.configure_redirections( - self.url_map) - - self.import_posts(channel) - self.write_urlmap_csv( - os.path.join(self.output_folder, 'url_map.csv'), self.url_map) - - self.write_configuration(self.get_configuration_output_path( - ), conf_template.render(**self.context)) - def replacer(dst): return links.get(dst, dst) diff --git a/nikola/plugins/command_import_wordpress.py b/nikola/plugins/command_import_wordpress.py index 07028d8..e7ecca0 100644 --- a/nikola/plugins/command_import_wordpress.py +++ b/nikola/plugins/command_import_wordpress.py @@ -28,7 +28,6 @@ import csv import datetime import os import re -from optparse import OptionParser try: from urlparse import urlparse @@ -53,9 +52,104 @@ class CommandImportWordpress(Command): """Import a wordpress dump.""" name = "import_wordpress" + needs_config = False + doc_usage = "[options] wordpress_export_file" + doc_purpose = "Import a wordpress dump." + cmd_options = [ + { + 'name': 'output_folder', + 'long': 'output-folder', + 'short': 'o', + 'default': 'new_site', + 'help': 'Location to write imported content.' + }, + { + 'name': 'exclude_drafts', + 'long': 'no-drafts', + 'short': 'd', + 'default': False, + 'type': bool, + 'help': "Don't import drafts", + }, + { + 'name': 'squash_newlines', + 'long': 'squash-newlines', + 'default': False, + 'type': bool, + 'help': "Shorten multiple newlines in a row to only two newlines", + }, + { + 'name': 'no_downloads', + 'long': 'no-downloads', + 'default': False, + 'type': bool, + 'help': "Do not try to download files for the import", + }, + ] + + def _execute(self, options={}, args=[]): + """Import a Wordpress blog from an export file into a Nikola site.""" + # Parse the data + print(options, args) + if requests is None: + print('To use the import_wordpress command,' + ' you have to install the "requests" package.') + return - @staticmethod - def read_xml_file(filename): + if not args: + print(self.help()) + return + + options['filename'] = args[0] + + if len(args) > 1: + options['output_folder'] = args[1] + + self.wordpress_export_file = options['filename'] + self.squash_newlines = options.get('squash_newlines', False) + self.no_downloads = options.get('no_downloads', False) + self.output_folder = options.get('output_folder', 'new_site') + self.import_into_existing_site = False + self.exclude_drafts = options.get('exclude_drafts', False) + self.url_map = {} + channel = self.get_channel_from_file(self.wordpress_export_file) + self.context = self.populate_context(channel) + conf_template = self.generate_base_site() + + self.import_posts(channel) + + self.context['REDIRECTIONS'] = self.configure_redirections( + self.url_map) + self.write_urlmap_csv( + os.path.join(self.output_folder, 'url_map.csv'), self.url_map) + rendered_template = conf_template.render(**self.context) + rendered_template = re.sub('# REDIRECTIONS = ', 'REDIRECTIONS = ', + rendered_template) + self.write_configuration(self.get_configuration_output_path(), + rendered_template) + + @classmethod + def _glue_xml_lines(cls, xml): + new_xml = xml[0] + previous_line_ended_in_newline = new_xml.endswith(b'\n') + previous_line_was_indentet = False + for line in xml[1:]: + if (re.match(b'^[ \t]+', line) and previous_line_ended_in_newline): + new_xml = b''.join((new_xml, line)) + previous_line_was_indentet = True + elif previous_line_was_indentet: + new_xml = b''.join((new_xml, line)) + previous_line_was_indentet = False + else: + new_xml = b'\n'.join((new_xml, line)) + previous_line_was_indentet = False + + previous_line_ended_in_newline = line.endswith(b'\n') + + return new_xml + + @classmethod + def read_xml_file(cls, filename): xml = [] with open(filename, 'rb') as fd: @@ -64,9 +158,8 @@ class CommandImportWordpress(Command): if b'<atom:link rel=' in line: continue xml.append(line) - xml = b'\n'.join(xml) - return xml + return cls._glue_xml_lines(xml) @classmethod def get_channel_from_file(cls, filename): @@ -82,7 +175,7 @@ class CommandImportWordpress(Command): src = (urlparse(k).path + 'index.html')[1:] dst = (urlparse(v).path) if src == 'index.html': - print("Can't do a redirect for: %r" % k) + print("Can't do a redirect for: {0!r}".format(k)) else: redirections.append((src, dst)) @@ -90,11 +183,11 @@ class CommandImportWordpress(Command): def generate_base_site(self): if not os.path.exists(self.output_folder): - os.system('nikola init --empty %s' % (self.output_folder, )) + os.system('nikola init ' + self.output_folder) else: self.import_into_existing_site = True - print('The folder %s already exists - assuming that this is a ' - 'already existing nikola site.' % self.output_folder) + print('The folder {0} already exists - assuming that this is a ' + 'already existing nikola site.'.format(self.output_folder)) conf_template = Template(filename=os.path.join( os.path.dirname(utils.__file__), 'conf.py.in')) @@ -111,15 +204,16 @@ class CommandImportWordpress(Command): '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('{%s}author' % wordpress_namespace) + context['SITE_URL'] = get_text_tag(channel, 'link', '#') + context['BASE_URL'] = get_text_tag(channel, 'link', '#') + author = channel.find('{{{0}}}author'.format(wordpress_namespace)) context['BLOG_EMAIL'] = get_text_tag( author, - '{%s}author_email' % wordpress_namespace, + '{{{0}}}author_email'.format(wordpress_namespace), "joe@example.com") context['BLOG_AUTHOR'] = get_text_tag( author, - '{%s}author_display_name' % wordpress_namespace, + '{{{0}}}author_display_name'.format(wordpress_namespace), "Joe Example") context['POST_PAGES'] = '''( ("posts/*.wp", "posts", "post.tmpl", True), @@ -134,25 +228,29 @@ class CommandImportWordpress(Command): return context - @staticmethod - def download_url_content_to_file(url, dst_path): + def download_url_content_to_file(self, url, dst_path): + if self.no_downloads: + return + try: with open(dst_path, 'wb+') as fd: fd.write(requests.get(url).content) except requests.exceptions.ConnectionError as err: - print("Downloading %s to %s failed: %s" % (url, dst_path, err)) + print("Downloading {0} to {1} failed: {2}".format(url, dst_path, + err)) 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') + item, '{{{0}}}attachment_url'.format(wordpress_namespace), 'foo') + link = get_text_tag(item, '{{{0}}}link'.format(wordpress_namespace), + 'foo') path = urlparse(url).path dst_path = os.path.join(*([self.output_folder, '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)) + print("Downloading {0} => {1}".format(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 @@ -173,10 +271,18 @@ class CommandImportWordpress(Command): return new_caption - @classmethod - def transform_content(cls, content): - new_content = cls.transform_sourcecode(content) - return cls.transform_caption(new_content) + def transform_multiple_newlines(self, content): + """Replaces multiple newlines with only two.""" + if self.squash_newlines: + return re.sub(r'\n{3,}', r'\n\n', content) + else: + return content + + def transform_content(self, content): + new_content = self.transform_sourcecode(content) + new_content = self.transform_caption(new_content) + new_content = self.transform_multiple_newlines(new_content) + return new_content @classmethod def write_content(cls, filename, content): @@ -188,13 +294,16 @@ class CommandImportWordpress(Command): @staticmethod def write_metadata(filename, title, slug, post_date, description, tags): + if not description: + description = "" + with codecs.open(filename, "w+", "utf8") as fd: - fd.write('%s\n' % title) - fd.write('%s\n' % slug) - fd.write('%s\n' % post_date) - fd.write('%s\n' % ','.join(tags)) + fd.write('{0}\n'.format(title)) + fd.write('{0}\n'.format(slug)) + fd.write('{0}\n'.format(post_date)) + fd.write('{0}\n'.format(','.join(tags))) fd.write('\n') - fd.write('%s\n' % description) + fd.write('{0}\n'.format(description)) def import_item(self, item, wordpress_namespace, out_folder=None): """Takes an item from the feed and creates a post file.""" @@ -208,19 +317,19 @@ class CommandImportWordpress(Command): 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) + item, '{{{0}}}post_name'.format(wordpress_namespace), None) if not slug: # it *may* happen slug = get_text_tag( - item, '{%s}post_id' % wordpress_namespace, None) + item, '{{{0}}}post_id'.format(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) + item, '{{{0}}}post_date'.format(wordpress_namespace), None) status = get_text_tag( - item, '{%s}status' % wordpress_namespace, 'publish') + item, '{{{0}}}status'.format(wordpress_namespace), 'publish') content = get_text_tag( item, '{http://purl.org/rss/1.0/modules/content/}encoded', '') @@ -237,13 +346,13 @@ class CommandImportWordpress(Command): continue tags.append(text) - self.url_map[link] = self.context['BLOG_URL'] + '/' + \ - out_folder + '/' + slug + '.html' - if is_draft and self.exclude_drafts: - print('Draft "%s" will not be imported.' % (title, )) + print('Draft "{0}" will not be imported.'.format(title)) elif content.strip(): # If no content is found, no files are written. + self.url_map[link] = self.context['SITE_URL'] + '/' + \ + out_folder + '/' + slug + '.html' + content = self.transform_content(content) self.write_metadata(os.path.join(self.output_folder, out_folder, @@ -253,15 +362,15 @@ class CommandImportWordpress(Command): os.path.join(self.output_folder, out_folder, slug + '.wp'), content) else: - print('Not going to import "%s" because it seems to contain' - ' no content.' % (title, )) + print('Not going to import "{0}" because it seems to contain' + ' no content.'.format(title)) 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') + item, '{{{0}}}post_type'.format(wordpress_namespace), 'post') if post_type == 'attachment': self.import_attachment(item, wordpress_namespace) @@ -285,10 +394,10 @@ class CommandImportWordpress(Command): if not self.import_into_existing_site: filename = 'conf.py' else: - filename = 'conf.py.wordpress_import-%s' % datetime.datetime.now( - ).strftime('%Y%m%d_%H%M%s') + filename = 'conf.py.wordpress_import-{0}'.format( + datetime.datetime.now().strftime('%Y%m%d_%H%M%s')) config_output_path = os.path.join(self.output_folder, filename) - print('Configuration will be written to: %s' % config_output_path) + print('Configuration will be written to:', config_output_path) return config_output_path @@ -297,53 +406,6 @@ class CommandImportWordpress(Command): with codecs.open(filename, 'w+', 'utf8') as fd: fd.write(rendered_template) - def run(self, *arguments): - """Import a Wordpress blog from an export file into a Nikola site.""" - # Parse the data - if requests is None: - print('To use the import_wordpress command,' - ' you have to install the "requests" package.') - return - - parser = OptionParser(usage="nikola %s [options] " - "wordpress_export_file" % self.name) - parser.add_option('-f', '--filename', dest='filename', - help='WordPress export file from which the import ' - 'made.') - parser.add_option('-o', '--output-folder', dest='output_folder', - default='new_site', help='The location into which ' - 'the imported content will be written') - parser.add_option('-d', '--no-drafts', dest='exclude_drafts', - default=False, action="store_true", help='Do not ' - 'import drafts.') - - (options, args) = parser.parse_args(list(arguments)) - - if not options.filename and args: - options.filename = args[0] - - if not options.filename: - parser.print_usage() - return - - self.wordpress_export_file = options.filename - self.output_folder = options.output_folder - self.import_into_existing_site = False - self.exclude_drafts = options.exclude_drafts - self.url_map = {} - channel = self.get_channel_from_file(self.wordpress_export_file) - self.context = self.populate_context(channel) - conf_template = self.generate_base_site() - self.context['REDIRECTIONS'] = self.configure_redirections( - self.url_map) - - self.import_posts(channel) - self.write_urlmap_csv( - os.path.join(self.output_folder, 'url_map.csv'), self.url_map) - - self.write_configuration(self.get_configuration_output_path( - ), conf_template.render(**self.context)) - def replacer(dst): return links.get(dst, dst) diff --git a/nikola/plugins/command_init.py b/nikola/plugins/command_init.py index e9bd001..bc36266 100644 --- a/nikola/plugins/command_init.py +++ b/nikola/plugins/command_init.py @@ -23,7 +23,6 @@ # SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. from __future__ import print_function -from optparse import OptionParser, OptionGroup import os import shutil import codecs @@ -39,16 +38,23 @@ class CommandInit(Command): name = "init" - usage = """Usage: nikola init folder [options]. - -That will create a sample site in the specified folder. -The destination folder must not exist. -""" + doc_usage = "[--demo] folder" + needs_config = False + doc_purpose = """Create a Nikola site in the specified folder.""" + cmd_options = [ + { + 'name': 'demo', + 'long': 'demo', + 'default': False, + 'type': bool, + 'help': "Create a site filled with example data.", + } + ] SAMPLE_CONF = { 'BLOG_AUTHOR': "Your Name", 'BLOG_TITLE': "Demo Site", - 'BLOG_URL': "http://nikola.ralsina.com.ar", + 'SITE_URL': "http://nikola.ralsina.com.ar", 'BLOG_EMAIL': "joe@demo.site", 'BLOG_DESCRIPTION': "This is a demo site for Nikola.", 'DEFAULT_LANG': "en", @@ -67,7 +73,7 @@ The destination folder must not exist. "wiki": ('.wiki',), "ipynb": ('.ipynb',), "html": ('.html', '.htm') - }""", +}""", 'REDIRECTIONS': '[]', } @@ -95,32 +101,22 @@ The destination folder must not exist. def get_path_to_nikola_modules(): return os.path.dirname(nikola.__file__) - def run(self, *args): + def _execute(self, options={}, args=None): """Create a new site.""" - parser = OptionParser(usage=self.usage) - group = OptionGroup(parser, "Site Options") - group.add_option( - "--empty", action="store_true", dest='empty', default=True, - help="Create an empty site with only a config.") - group.add_option("--demo", action="store_false", dest='empty', - help="Create a site filled with example data.") - parser.add_option_group(group) - (options, args) = parser.parse_args(list(args)) - if not args: print("Usage: nikola init folder [options]") - return + return False target = args[0] if target is None: print(self.usage) else: - if options.empty: + if not options or not options.get('demo'): self.create_empty_site(target) - print('Created empty site at %s.' % target) + print('Created empty site at {0}.'.format(target)) else: self.copy_sample_site(target) - print("A new site with example data has been created at %s." - % target) + print("A new site with example data has been created at " + "{0}.".format(target)) print("See README.txt in that folder for more information.") self.create_configuration(target) diff --git a/nikola/plugins/command_install_theme.py b/nikola/plugins/command_install_theme.py index 0dc000b..04a2cce 100644 --- a/nikola/plugins/command_install_theme.py +++ b/nikola/plugins/command_install_theme.py @@ -23,7 +23,6 @@ # SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. from __future__ import print_function -from optparse import OptionParser import os import json from io import BytesIO @@ -41,31 +40,39 @@ class CommandInstallTheme(Command): """Start test server.""" name = "install_theme" + doc_usage = "[[-u] theme_name] | [[-u] -l]" + doc_purpose = "Install theme into current site." + cmd_options = [ + { + 'name': 'list', + 'short': 'l', + 'long': 'list', + 'type': bool, + 'default': False, + 'help': 'Show list of available themes.' + }, + { + 'name': 'url', + 'short': 'u', + 'long': 'url', + 'type': str, + 'help': "URL for the theme repository (default: " + "http://nikola.ralsina.com.ar/themes/index.json)", + 'default': 'http://nikola.ralsina.com.ar/themes/index.json' + }, + ] - def run(self, *args): + def _execute(self, options, 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", - help="Show list of available themes.") - parser.add_option("-n", "--name", dest="name", help="Theme name", - default=None) - parser.add_option("-u", "--url", dest="url", help="URL for the theme " - "repository" "(default: " - "http://nikola.ralsina.com.ar/themes/index.json)", - default='http://nikola.ralsina.com.ar/themes/' - 'index.json') - (options, args) = parser.parse_args(list(args)) - - listing = options.list - name = options.name - url = options.url + listing = options['list'] + url = options['url'] + if args: + name = args[0] + else: + name = None if name is None and not listing: - print("This command needs either the -n or the -l option.") + print("This command needs either a theme name or the -l option.") return False data = requests.get(url).text data = json.loads(data) @@ -84,11 +91,11 @@ class CommandInstallTheme(Command): os.makedirs("themes") except: raise OSError("mkdir 'theme' error!") - print('Downloading: %s' % data[name]) + print('Downloading: ' + data[name]) zip_file = BytesIO() zip_file.write(requests.get(data[name]).content) - print('Extracting: %s into themes' % name) + print('Extracting: {0} into themes'.format(name)) utils.extract_all(zip_file) else: - print("Can't find theme %s" % name) + print("Can't find theme " + name) return False diff --git a/nikola/plugins/command_new_post.py b/nikola/plugins/command_new_post.py index 9b6397b..a823da3 100644 --- a/nikola/plugins/command_new_post.py +++ b/nikola/plugins/command_new_post.py @@ -25,7 +25,6 @@ from __future__ import unicode_literals, print_function import codecs import datetime -from optparse import OptionParser import os import sys @@ -51,9 +50,9 @@ def filter_post_pages(compiler, is_post, post_compilers, post_pages): if not filtered: type_name = "post" if is_post else "page" raise Exception("Can't find a way, using your configuration, to create" - "a %s in format %s. You may want to tweak " - "post_compilers or post_pages in conf.py" % - (type_name, compiler)) + "a {0} in format {1}. You may want to tweak " + "post_compilers or post_pages in conf.py".format( + type_name, compiler)) return filtered[0] @@ -61,42 +60,88 @@ class CommandNewPost(Command): """Create a new post.""" name = "new_post" - - def run(self, *args): - """Create a new post.""" + doc_usage = "[options] [path]" + doc_purpose = "Create a new blog post or site page." + cmd_options = [ + { + 'name': 'is_page', + 'short': 'p', + 'long': 'page', + 'type': bool, + 'default': False, + 'help': 'Create a page instead of a blog post.' + }, + { + 'name': 'title', + 'short': 't', + 'long': 'title', + 'type': str, + 'default': '', + 'help': 'Title for the page/post.' + }, + { + 'name': 'tags', + 'long': 'tags', + 'type': str, + 'default': '', + 'help': 'Comma-separated tags for the page/post.' + }, + { + 'name': 'onefile', + 'short': '1', + 'type': bool, + 'default': False, + 'help': 'Create post with embedded metadata (single file format)' + }, + { + 'name': 'twofile', + 'short': '2', + 'type': bool, + 'default': False, + 'help': 'Create post with separate metadata (two file format)' + }, + { + 'name': 'post_format', + 'short': 'f', + 'long': 'format', + 'type': str, + 'default': 'rest', + 'help': 'Markup format for post, one of rest, markdown, wiki, ' + 'bbcode, html, textile, txt2tags', + } + ] + + def _execute(self, options, args): + """Create a new post or page.""" compiler_names = [p.name for p in self.site.plugin_manager.getPluginsOfCategory( "PageCompiler")] - parser = OptionParser(usage="nikola %s [options]" % self.name) - parser.add_option('-p', '--page', dest='is_post', action='store_false', - default=True, help='Create a page instead of a blog ' - 'post.') - parser.add_option('-t', '--title', dest='title', help='Title for the ' - 'page/post.', default=None) - parser.add_option('--tags', dest='tags', help='Comma-separated tags ' - 'for the page/post.', default='') - parser.add_option('-1', dest='onefile', action='store_true', - help='Create post with embedded metadata (single ' - 'file format).', - default=self.site.config.get('ONE_FILE_POSTS', True)) - parser.add_option('-2', dest='onefile', action='store_false', - help='Create post with separate metadata (two file ' - 'format).', - default=self.site.config.get('ONE_FILE_POSTS', True)) - parser.add_option('-f', '--format', dest='post_format', default='rest', - help='Format for post (one of %s)' % - ','.join(compiler_names)) - (options, args) = parser.parse_args(list(args)) - - is_post = options.is_post - title = options.title - tags = options.tags - onefile = options.onefile - post_format = options.post_format + if len(args) > 1: + print(self.help()) + return False + elif args: + path = args[0] + else: + path = None + + is_page = options.get('is_page', False) + is_post = not is_page + title = options['title'] or None + tags = options['tags'] + onefile = options['onefile'] + twofile = options['twofile'] + + if twofile: + onefile = False + if not onefile and not twofile: + onefile = self.site.config.get('ONE_FILE_POSTS', True) + + post_format = options['post_format'] + if post_format not in compiler_names: - print("ERROR: Unknown post format %s" % post_format) + print("ERROR: Unknown post format " + post_format) return compiler_plugin = self.site.plugin_manager.getPluginByName( post_format, "PageCompiler").plugin_object @@ -118,19 +163,29 @@ class CommandNewPost(Command): if isinstance(title, bytes): title = title.decode(sys.stdin.encoding) title = title.strip() - slug = utils.slugify(title) + if not path: + slug = utils.slugify(title) + else: + slug = utils.slugify(os.path.splitext(os.path.basename(path))[0]) date = datetime.datetime.now().strftime('%Y/%m/%d %H:%M:%S') data = [title, slug, date, tags] output_path = os.path.dirname(entry[0]) meta_path = os.path.join(output_path, slug + ".meta") pattern = os.path.basename(entry[0]) suffix = pattern[1:] - txt_path = os.path.join(output_path, slug + suffix) + if not path: + txt_path = os.path.join(output_path, slug + suffix) + else: + txt_path = path if (not onefile and os.path.isfile(meta_path)) or \ os.path.isfile(txt_path): print("The title already exists!") exit() + + d_name = os.path.dirname(txt_path) + if not os.path.exists(d_name): + os.makedirs(d_name) compiler_plugin.create_post(txt_path, onefile, title, slug, date, tags) if not onefile: # write metadata file diff --git a/nikola/plugins/command_serve.py b/nikola/plugins/command_serve.py index 75e07a9..64efe7d 100644 --- a/nikola/plugins/command_serve.py +++ b/nikola/plugins/command_serve.py @@ -23,7 +23,6 @@ # SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. from __future__ import print_function -from optparse import OptionParser import os try: from BaseHTTPServer import HTTPServer @@ -39,26 +38,39 @@ class CommandBuild(Command): """Start test server.""" name = "serve" + doc_usage = "[options]" + doc_purpose = "Start the test webserver." - def run(self, *args): - """Start test server.""" - - parser = OptionParser(usage="nikola %s [options]" % self.name) - parser.add_option("-p", "--port", dest="port", help="Port numer " - "(default: 8000)", default=8000, type="int") - parser.add_option("-a", "--address", dest="address", help="Address to " - "bind (default: 127.0.0.1)", default='127.0.0.1') - (options, args) = parser.parse_args(list(args)) + cmd_options = ( + { + 'name': 'port', + 'short': 'p', + 'long': 'port', + 'default': 8000, + 'type': int, + 'help': 'Port nummber (default: 8000)', + }, + { + 'name': 'address', + 'short': 'a', + 'long': '--address', + 'type': str, + 'default': '127.0.0.1', + 'help': 'Address to bind (default: 127.0.0.1)', + }, + ) + def _execute(self, options, args): + """Start test server.""" out_dir = self.site.config['OUTPUT_FOLDER'] if not os.path.isdir(out_dir): - print("Error: Missing '%s' folder?" % out_dir) + print("Error: Missing '{0}' folder?".format(out_dir)) else: os.chdir(out_dir) - httpd = HTTPServer((options.address, options.port), + httpd = HTTPServer((options['address'], options['port']), OurHTTPRequestHandler) sa = httpd.socket.getsockname() - print("Serving HTTP on {0[0]} port {0[1]}...".format(sa)) + print("Serving HTTP on", sa[0], "port", sa[1], "...") httpd.serve_forever() diff --git a/nikola/plugins/compile_bbcode.py b/nikola/plugins/compile_bbcode.py index fd7fe1a..26de727 100644 --- a/nikola/plugins/compile_bbcode.py +++ b/nikola/plugins/compile_bbcode.py @@ -68,10 +68,10 @@ class CompileTextile(PageCompiler): with codecs.open(path, "wb+", "utf8") as fd: if onefile: fd.write('[note]<!--\n') - fd.write('.. title: %s\n' % title) - fd.write('.. slug: %s\n' % slug) - fd.write('.. date: %s\n' % date) - fd.write('.. tags: %s\n' % tags) + fd.write('.. title: {0}\n'.format(title)) + fd.write('.. slug: {0}\n'.format(slug)) + fd.write('.. date: {0}\n'.format(date)) + fd.write('.. tags: {0}\n'.format(tags)) fd.write('.. link: \n') fd.write('.. description: \n') fd.write('-->[/note]\n\n') diff --git a/nikola/plugins/compile_html.py b/nikola/plugins/compile_html.py index 850a3e5..6c1c381 100644 --- a/nikola/plugins/compile_html.py +++ b/nikola/plugins/compile_html.py @@ -51,10 +51,10 @@ class CompileHtml(PageCompiler): with codecs.open(path, "wb+", "utf8") as fd: if onefile: fd.write('<!-- \n') - fd.write('.. title: %s\n' % title) - fd.write('.. slug: %s\n' % slug) - fd.write('.. date: %s\n' % date) - fd.write('.. tags: %s\n' % tags) + fd.write('.. title: {0}\n'.format(title)) + fd.write('.. slug: {0}\n'.format(slug)) + fd.write('.. date: {0}\n'.format(date)) + fd.write('.. tags: {0}\n'.format(tags)) fd.write('.. link: \n') fd.write('.. description: \n') fd.write('-->\n\n') diff --git a/nikola/plugins/compile_markdown/__init__.py b/nikola/plugins/compile_markdown/__init__.py index 5eb25c8..7aa03a9 100644 --- a/nikola/plugins/compile_markdown/__init__.py +++ b/nikola/plugins/compile_markdown/__init__.py @@ -55,8 +55,10 @@ class CompileMarkdown(PageCompiler): output = markdown(data, ['fenced_code', 'codehilite']) # h1 is reserved for the title so increment all header levels for n in reversed(range(1, 9)): - output = re.sub('<h%i>' % n, '<h%i>' % (n + 1), output) - output = re.sub('</h%i>' % n, '</h%i>' % (n + 1), output) + output = re.sub('<h{0}>'.format(n), '<h{0}>'.format(n + 1), + output) + output = re.sub('</h{0}>'.format(n), '</h{0}>'.format(n + 1), + output) # python-markdown's highlighter uses the class 'codehilite' to wrap # code, # instead of the standard 'code'. None of the standard # pygments stylesheets use this class, so swap it to be 'code' @@ -69,10 +71,10 @@ class CompileMarkdown(PageCompiler): with codecs.open(path, "wb+", "utf8") as fd: if onefile: fd.write('<!-- \n') - fd.write('.. title: %s\n' % title) - fd.write('.. slug: %s\n' % slug) - fd.write('.. date: %s\n' % date) - fd.write('.. tags: %s\n' % tags) + fd.write('.. title: {0}\n'.format(title)) + fd.write('.. slug: {0}\n'.format(slug)) + fd.write('.. date: {0}\n'.format(date)) + fd.write('.. tags: {0}\n'.format(tags)) fd.write('.. link: \n') fd.write('.. description: \n') fd.write('-->\n\n') diff --git a/nikola/plugins/compile_rest/__init__.py b/nikola/plugins/compile_rest/__init__.py index 4191add..b0a0c00 100644 --- a/nikola/plugins/compile_rest/__init__.py +++ b/nikola/plugins/compile_rest/__init__.py @@ -44,6 +44,8 @@ from .slides import slides directives.register_directive('slides', slides) from .gist_directive import GitHubGist directives.register_directive('gist', GitHubGist) +from .soundcloud import soundcloud +directives.register_directive('soundcloud', soundcloud) from nikola.plugin_categories import PageCompiler @@ -75,10 +77,10 @@ class CompileRest(PageCompiler): tags=""): with codecs.open(path, "wb+", "utf8") as fd: if onefile: - fd.write('.. title: %s\n' % title) - fd.write('.. slug: %s\n' % slug) - fd.write('.. date: %s\n' % date) - fd.write('.. tags: %s\n' % tags) + fd.write('.. title: {0}\n'.format(title)) + fd.write('.. slug: {0}\n'.format(slug)) + fd.write('.. date: {0}\n'.format(date)) + fd.write('.. tags: {0}\n'.format(tags)) fd.write('.. link: \n') fd.write('.. description: \n\n') fd.write("\nWrite your post here.") diff --git a/nikola/plugins/compile_rest/gist_directive.py b/nikola/plugins/compile_rest/gist_directive.py index 3bfe818..0ea8f23 100644 --- a/nikola/plugins/compile_rest/gist_directive.py +++ b/nikola/plugins/compile_rest/gist_directive.py @@ -24,11 +24,11 @@ class GitHubGist(Directive): has_content = False def get_raw_gist_with_filename(self, gistID, filename): - url = "https://raw.github.com/gist/%s/%s" % (gistID, filename) + url = '/'.join(("https://raw.github.com/gist", gistID, filename)) return requests.get(url).text def get_raw_gist(self, gistID): - url = "https://raw.github.com/gist/%s/" % (gistID) + url = "https://raw.github.com/gist/{0}/".format(gistID) return requests.get(url).text def run(self): @@ -43,12 +43,12 @@ class GitHubGist(Directive): if 'file' in self.options: filename = self.options['file'] rawGist = (self.get_raw_gist_with_filename(gistID, filename)) - embedHTML = ('<script src="https://gist.github.com/%s.js?file=%s">' - '</script>') % (gistID, filename) + embedHTML = ('<script src="https://gist.github.com/{0}.js' + '?file={1}"></script>').format(gistID, filename) else: rawGist = (self.get_raw_gist(gistID)) - embedHTML = ('<script src="https://gist.github.com/%s.js">' - '</script>') % gistID + embedHTML = ('<script src="https://gist.github.com/{0}.js">' + '</script>').format(gistID) return [nodes.raw('', embedHTML, format='html'), nodes.raw('', '<noscript>', format='html'), diff --git a/nikola/plugins/compile_rest/pygments_code_block_directive.py b/nikola/plugins/compile_rest/pygments_code_block_directive.py index f858427..79bada2 100644 --- a/nikola/plugins/compile_rest/pygments_code_block_directive.py +++ b/nikola/plugins/compile_rest/pygments_code_block_directive.py @@ -165,9 +165,9 @@ def code_block_directive(name, arguments, options, content, lineno, after_index = content.find(after_text)
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'])
+ 'Problem with "start-at" option of "{0}" '
+ 'code-block directive:\nText not found.'.format(
+ options['start-at']))
# patch mmueller start
# Move the after_index to the beginning of the line with the
# match.
@@ -192,9 +192,9 @@ def code_block_directive(name, arguments, options, content, lineno, after_index = content.find(after_text)
if after_index < 0:
raise state_machine.reporter.severe(
- 'Problem with "start-after" option of "%s" '
- 'code-block directive:\nText not found.' %
- options['start-after'])
+ 'Problem with "start-after" option of "{0}" '
+ 'code-block directive:\nText not found.'.format(
+ options['start-after']))
line_offset = len(content[:after_index +
len(after_text)].splitlines())
content = content[after_index + len(after_text):]
@@ -207,9 +207,9 @@ def code_block_directive(name, arguments, options, content, lineno, before_index = content.find(before_text)
if before_index < 0:
raise state_machine.reporter.severe(
- 'Problem with "end-at" option of "%s" '
- 'code-block directive:\nText not found.' %
- options['end-at'])
+ 'Problem with "end-at" option of "{0}" '
+ 'code-block directive:\nText not found.'.format(
+ options['end-at']))
content = content[:before_index + len(before_text)]
before_text = options.get('end-before', None)
@@ -219,9 +219,9 @@ def code_block_directive(name, arguments, options, content, lineno, before_index = content.find(before_text)
if before_index < 0:
raise state_machine.reporter.severe(
- 'Problem with "end-before" option of "%s" '
- 'code-block directive:\nText not found.' %
- options['end-before'])
+ 'Problem with "end-before" option of "{0}" '
+ 'code-block directive:\nText not found.'.format(
+ options['end-before']))
content = content[:before_index]
else:
@@ -246,8 +246,9 @@ def code_block_directive(name, arguments, options, content, lineno, lineno = 1 + line_offset
total_lines = content.count('\n') + 1 + line_offset
lnwidth = len(str(total_lines))
- fstr = "\n%%%dd " % lnwidth
- code_block += nodes.inline(fstr[1:] % lineno, fstr[1:] % lineno,
+ fstr = "\n%{0}d ".format(lnwidth)
+ code_block += nodes.inline(fstr[1:].format(lineno),
+ fstr[1:].format(lineno),
classes=['linenumber'])
# parse content with pygments and add to code_block element
@@ -272,7 +273,8 @@ def code_block_directive(name, arguments, options, content, lineno, 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,
+ code_block += nodes.inline(fstr.format(ln),
+ fstr.format(ln),
classes=['linenumber'])
code_block += nodes.Text(chunk, chunk)
lineno += len(values) - 1
@@ -319,8 +321,8 @@ def string_bool(argument): elif argument.lower() == 'false':
return False
else:
- raise ValueError('"%s" unknown; choose from "True" or "False"' %
- argument)
+ raise ValueError('"{0}" unknown; choose from "True" or "False"'.format(
+ argument))
def csharp_unicodelevel(argument):
@@ -340,7 +342,8 @@ def listings_directive(name, arguments, options, content, lineno, fname = arguments[0]
options['include'] = os.path.join('listings', fname)
target = urlunsplit(("link", 'listing', fname, '', ''))
- generated_nodes = [core.publish_doctree('`%s <%s>`_' % (fname, target))[0]]
+ generated_nodes = [core.publish_doctree('`{0} <{1}>`_'.format(fname,
+ target))[0]]
generated_nodes += code_block_directive(name, [arguments[1]], options,
content, lineno, content_offset,
block_text, state, state_machine)
diff --git a/nikola/plugins/compile_rest/slides.py b/nikola/plugins/compile_rest/slides.py index c9d55f3..f9901f5 100644 --- a/nikola/plugins/compile_rest/slides.py +++ b/nikola/plugins/compile_rest/slides.py @@ -77,12 +77,13 @@ class slides(Directive): options.update(self.options) options = json.dumps(options) output = [] - output.append('<script> $(function(){ $("#slides").slides(%s); });' - '</script>' % options) + output.append('<script> $(function(){ $("#slides").slides(' + options + + '); });' + '</script>') 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><img src="{0}"></div>""".format(image)) output.append("""</div></div>""") return [nodes.raw('', '\n'.join(output), format='html')] diff --git a/nikola/plugins/compile_rest/soundcloud.py b/nikola/plugins/compile_rest/soundcloud.py new file mode 100644 index 0000000..d47bebf --- /dev/null +++ b/nikola/plugins/compile_rest/soundcloud.py @@ -0,0 +1,32 @@ +from docutils import nodes +from docutils.parsers.rst import directives + +CODE = ("""<iframe width="{width}" height="{height}" +scrolling="no" frameborder="no" +src="https://w.soundcloud.com/player/?url=http://api.soundcloud.com/tracks/""" + """{sid}"> +</iframe>""") + + +def soundcloud(name, args, options, content, lineno, + contentOffset, blockText, state, stateMachine): + """ Restructured text extension for inserting SoundCloud embedded music """ + string_vars = { + 'sid': content[0], + 'width': 600, + 'height': 160, + 'extra': '' + } + extra_args = content[1:] # Because content[0] is ID + extra_args = [ea.strip().split("=") for ea in extra_args] # key=value + extra_args = [ea for ea in extra_args if len(ea) == 2] # drop bad lines + extra_args = dict(extra_args) + if 'width' in extra_args: + string_vars['width'] = extra_args.pop('width') + if 'height' in extra_args: + string_vars['height'] = extra_args.pop('height') + + return [nodes.raw('', CODE.format(**string_vars), format='html')] + +soundcloud.content = True +directives.register_directive('soundcloud', soundcloud) diff --git a/nikola/plugins/compile_rest/vimeo.py b/nikola/plugins/compile_rest/vimeo.py index 3eefcc4..34f2a50 100644 --- a/nikola/plugins/compile_rest/vimeo.py +++ b/nikola/plugins/compile_rest/vimeo.py @@ -37,8 +37,8 @@ except ImportError: except ImportError: json = None -CODE = """<iframe src="http://player.vimeo.com/video/%(vimeo_id)s" -width="%(width)s" height="%(height)s" +CODE = """<iframe src="http://player.vimeo.com/video/{vimeo_id}" +width="{width}" height="{height}" frameborder="0" webkitAllowFullScreen mozallowfullscreen allowFullScreen> </iframe> """ @@ -76,8 +76,8 @@ def vimeo(name, args, options, content, lineno, contentOffset, blockText, if json: # we can attempt to retrieve video attributes from vimeo try: - url = ('http://vimeo.com/api/v2/video/%(vimeo_id)s.json' % - string_vars) + url = ('http://vimeo.com/api/v2/video/{vimeo_id}' + '.json'.format(**string_vars)) data = requests.get(url).text video_attributes = json.loads(data) string_vars['height'] = video_attributes['height'] @@ -86,7 +86,7 @@ def vimeo(name, args, options, content, lineno, contentOffset, blockText, # fall back to the defaults pass - return [nodes.raw('', CODE % string_vars, format='html')] + return [nodes.raw('', CODE.format(**string_vars), format='html')] vimeo.content = True directives.register_directive('vimeo', vimeo) diff --git a/nikola/plugins/compile_rest/youtube.py b/nikola/plugins/compile_rest/youtube.py index fe3b28b..30ac000 100644 --- a/nikola/plugins/compile_rest/youtube.py +++ b/nikola/plugins/compile_rest/youtube.py @@ -26,9 +26,9 @@ from docutils import nodes from docutils.parsers.rst import directives CODE = """\ -<iframe width="%(width)s" -height="%(height)s" -src="http://www.youtube.com/embed/%(yid)s?rel=0&hd=1&wmode=transparent" +<iframe width="{width}" +height="{height}" +src="http://www.youtube.com/embed/{yid}?rel=0&hd=1&wmode=transparent" ></iframe>""" @@ -51,6 +51,6 @@ def youtube(name, args, options, content, lineno, string_vars['width'] = extra_args.pop('width') if 'height' in extra_args: string_vars['height'] = extra_args.pop('height') - return [nodes.raw('', CODE % (string_vars), format='html')] + return [nodes.raw('', CODE.format(**string_vars), format='html')] youtube.content = True directives.register_directive('youtube', youtube) diff --git a/nikola/plugins/compile_textile.py b/nikola/plugins/compile_textile.py index 7fa4e3f..3ca370d 100644 --- a/nikola/plugins/compile_textile.py +++ b/nikola/plugins/compile_textile.py @@ -62,10 +62,10 @@ class CompileTextile(PageCompiler): with codecs.open(path, "wb+", "utf8") as fd: if onefile: fd.write('<notextile> <!--\n') - fd.write('.. title: %s\n' % title) - fd.write('.. slug: %s\n' % slug) - fd.write('.. date: %s\n' % date) - fd.write('.. tags: %s\n' % tags) + fd.write('.. title: {0}\n'.format(title)) + fd.write('.. slug: {0}\n'.format(slug)) + fd.write('.. date: {0}\n'.format(date)) + fd.write('.. tags: {0}\n'.format(tags)) fd.write('.. link: \n') fd.write('.. description: \n') fd.write('--></notextile>\n\n') diff --git a/nikola/plugins/compile_txt2tags.py b/nikola/plugins/compile_txt2tags.py index 2446dfd..90372bd 100644 --- a/nikola/plugins/compile_txt2tags.py +++ b/nikola/plugins/compile_txt2tags.py @@ -65,10 +65,10 @@ class CompileTextile(PageCompiler): with codecs.open(path, "wb+", "utf8") as fd: if onefile: fd.write("\n'''\n<!--\n") - fd.write('.. title: %s\n' % title) - fd.write('.. slug: %s\n' % slug) - fd.write('.. date: %s\n' % date) - fd.write('.. tags: %s\n' % tags) + fd.write('.. title: {0}\n'.format(title)) + fd.write('.. slug: {0}\n'.format(slug)) + fd.write('.. date: {0}\n'.format(date)) + fd.write('.. tags: {0}\n'.format(tags)) fd.write('.. link: \n') fd.write('.. description: \n') fd.write("-->\n'''\n") diff --git a/nikola/plugins/task_create_bundles.py b/nikola/plugins/task_create_bundles.py index 95f10c2..ad670e1 100644 --- a/nikola/plugins/task_create_bundles.py +++ b/nikola/plugins/task_create_bundles.py @@ -77,7 +77,8 @@ class BuildBundles(LateTask): output_path = os.path.join(kw['output_folder'], name) dname = os.path.dirname(name) file_dep = [get_asset_path( - os.path.join(dname, fname), kw['themes'], kw['files_folders']) + os.path.join(dname, fname), kw['themes'], + kw['files_folders']) for fname in files ] file_dep = filter(None, file_dep) # removes missing files @@ -108,16 +109,17 @@ def get_asset_path(path, themes, files_folders={'files': ''}): If the asset is not provided by a theme, then it will be checked for in the FILES_FOLDERS - >>> get_asset_path('assets/css/rst.css', ['site','default']) + >>> get_asset_path('assets/css/rst.css', ['site', 'default']) 'nikola/data/themes/default/assets/css/rst.css' - >>> get_asset_path('assets/css/theme.css', ['site','default']) + >>> get_asset_path('assets/css/theme.css', ['site', 'default']) 'nikola/data/themes/site/assets/css/theme.css' - >>> get_asset_path('nikola.py',['site','default'],{'nikola':''}) + >>> get_asset_path('nikola.py', ['site', 'default'], {'nikola': ''}) 'nikola/nikola.py' - >>> get_asset_path('nikola/nikola.py',['site','default'],{'nikola':'nikola'}) + >>> get_asset_path('nikola/nikola.py', ['site', 'default'], + ... {'nikola':'nikola'}) 'nikola/nikola.py' """ diff --git a/nikola/plugins/task_indexes.py b/nikola/plugins/task_indexes.py index 757998e..7baf660 100644 --- a/nikola/plugins/task_indexes.py +++ b/nikola/plugins/task_indexes.py @@ -79,11 +79,11 @@ class Indexes(Task): context["nextlink"] = None context['index_teasers'] = kw['index_teasers'] if i > 1: - context["prevlink"] = "index-%s.html" % (i - 1) + context["prevlink"] = "index-{0}.html".format(i - 1) if i == 1: context["prevlink"] = "index.html" if i < num_pages - 1: - context["nextlink"] = "index-%s.html" % (i + 1) + context["nextlink"] = "index-{0}.html".format(i + 1) context["permalink"] = self.site.link("index", i, lang) output_name = os.path.join( kw['output_folder'], self.site.path("index", i, diff --git a/nikola/plugins/task_redirect.py b/nikola/plugins/task_redirect.py index b133948..e440c30 100644 --- a/nikola/plugins/task_redirect.py +++ b/nikola/plugins/task_redirect.py @@ -71,5 +71,6 @@ def create_redirect(src, dst): except: pass with codecs.open(src, "wb+", "utf8") as fd: - fd.write('<head><meta http-equiv="refresh" content="0; ' - 'url=%s"></head>' % dst) + fd.write('<!DOCTYPE html><head><title>Redirecting...</title>' + '<meta http-equiv="refresh" content="0; ' + 'url={0}"></head>'.format(dst)) diff --git a/nikola/plugins/task_render_galleries.py b/nikola/plugins/task_render_galleries.py index e69a457..0880e3e 100644 --- a/nikola/plugins/task_render_galleries.py +++ b/nikola/plugins/task_render_galleries.py @@ -136,7 +136,6 @@ class Galleries(Task): # Sort by date 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 list(zip(image_list, image_name_list)): @@ -187,7 +186,7 @@ class Galleries(Task): output_gallery, ".thumbnail".join([fname, ext])) excluded_dest_path = os.path.join(output_gallery, img_name) yield { - 'basename': str('render_galleries'), + 'basename': str('render_galleries_clean'), 'name': excluded_thumb_dest_path.encode('utf8'), 'file_dep': [exclude_path], #'targets': [excluded_thumb_dest_path], @@ -198,7 +197,7 @@ class Galleries(Task): 'uptodate': [utils.config_changed(kw)], } yield { - 'basename': str('render_galleries'), + 'basename': str('render_galleries_clean'), 'name': excluded_dest_path.encode('utf8'), 'file_dep': [exclude_path], #'targets': [excluded_dest_path], @@ -214,9 +213,9 @@ class Galleries(Task): context["title"] = os.path.basename(gallery_path) context["description"] = kw["blog_description"] if kw['use_filename_as_title']: - img_titles = ['id="%s" alt="%s" title="%s"' % - (fn[:-4], fn[:-4], utils.unslugify(fn[:-4])) - for fn in image_name_list] + img_titles = ['id="{0}" alt="{1}" title="{2}"'.format( + fn[:-4], fn[:-4], utils.unslugify(fn[:-4])) for fn + in image_name_list] else: img_titles = [''] * len(image_name_list) context["images"] = list(zip(image_name_list, thumbs, img_titles)) diff --git a/nikola/plugins/task_render_listings.py b/nikola/plugins/task_render_listings.py index a899f10..b115a2f 100644 --- a/nikola/plugins/task_render_listings.py +++ b/nikola/plugins/task_render_listings.py @@ -50,29 +50,32 @@ class Listings(Task): # Things to ignore in listings ignored_extensions = (".pyc",) - def render_listing(in_name, out_name): - with open(in_name, 'r') as fd: - try: - lexer = get_lexer_for_filename(in_name) - except: - lexer = TextLexer() - code = highlight(fd.read(), lexer, - HtmlFormatter(cssclass='code', - linenos="table", nowrap=False, - lineanchors=utils.slugify(f), - anchorlinenos=True)) - title = os.path.basename(in_name) - print("CRUMBSINOUT", in_name, out_name) - #crumbs = out_name.split(os.sep)[1:-1] + [title] - # TODO: write this in human - #paths = ['/'.join(['..'] * (len(crumbs) - 2 - i)) for i in - #range(len(crumbs[:-2]))] + ['.', '#'] - crumbs = utils.get_crumbs(os.path.relpath(out_name, kw['output_folder']), is_file=True) + def render_listing(in_name, out_name, folders=[], files=[]): + if in_name: + with open(in_name, 'r') as fd: + try: + lexer = get_lexer_for_filename(in_name) + except: + lexer = TextLexer() + code = highlight(fd.read(), lexer, + HtmlFormatter(cssclass='code', + linenos="table", nowrap=False, + lineanchors=utils.slugify(f), + anchorlinenos=True)) + title = os.path.basename(in_name) + else: + code = '' + title = '' + crumbs = utils.get_crumbs(os.path.relpath(out_name, + kw['output_folder']), + is_file=True) context = { 'code': code, 'title': title, 'crumbs': crumbs, 'lang': kw['default_lang'], + 'folders': folders, + 'files': files, 'description': title, } self.site.render_template('listing.tmpl', out_name.encode('utf8'), @@ -80,12 +83,27 @@ class Listings(Task): flag = True template_deps = self.site.template_system.template_deps('listing.tmpl') for root, dirs, files in os.walk(kw['listings_folder']): + flag = False # Render all files + out_name = os.path.join( + kw['output_folder'], + root, 'index.html' + ) + yield { + 'basename': self.name, + 'name': out_name.encode('utf8'), + 'file_dep': template_deps, + 'targets': [out_name], + 'actions': [(render_listing, [None, out_name, dirs, files])], + # This is necessary to reflect changes in blog title, + # sidebar links, etc. + 'uptodate': [utils.config_changed( + self.site.config['GLOBAL_CONTEXT'])] + } for f in files: ext = os.path.splitext(f)[-1] if ext in ignored_extensions: continue - flag = False in_name = os.path.join(root, f) out_name = os.path.join( kw['output_folder'], diff --git a/nikola/plugins/task_render_rss.py b/nikola/plugins/task_render_rss.py index fb35843..9ce1d63 100644 --- a/nikola/plugins/task_render_rss.py +++ b/nikola/plugins/task_render_rss.py @@ -39,7 +39,7 @@ class RenderRSS(Task): "translations": self.site.config["TRANSLATIONS"], "filters": self.site.config["FILTERS"], "blog_title": self.site.config["BLOG_TITLE"], - "blog_url": self.site.config["BLOG_URL"], + "site_url": self.site.config["SITE_URL"], "blog_description": self.site.config["BLOG_DESCRIPTION"], "output_folder": self.site.config["OUTPUT_FOLDER"], "rss_teasers": self.site.config["RSS_TEASERS"], @@ -59,7 +59,7 @@ class RenderRSS(Task): 'file_dep': deps, 'targets': [output_name], 'actions': [(utils.generic_rss_renderer, - (lang, kw["blog_title"], kw["blog_url"], + (lang, kw["blog_title"], kw["site_url"], kw["blog_description"], posts, output_name, kw["rss_teasers"]))], 'clean': True, diff --git a/nikola/plugins/task_render_tags.py b/nikola/plugins/task_render_tags.py index a561a81..744f0cb 100644 --- a/nikola/plugins/task_render_tags.py +++ b/nikola/plugins/task_render_tags.py @@ -22,7 +22,7 @@ # 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 +from __future__ import unicode_literals import codecs import json import os @@ -42,7 +42,7 @@ class RenderTags(Task): kw = { "translations": self.site.config["TRANSLATIONS"], "blog_title": self.site.config["BLOG_TITLE"], - "blog_url": self.site.config["BLOG_URL"], + "site_url": self.site.config["SITE_URL"], "blog_description": self.site.config["BLOG_DESCRIPTION"], "messages": self.site.MESSAGES, "output_folder": self.site.config['OUTPUT_FOLDER'], @@ -56,6 +56,8 @@ class RenderTags(Task): self.site.scan_posts() + yield self.list_tags_page(kw) + if not self.site.posts_per_tag: yield {'basename': str(self.name), 'actions': []} return @@ -73,8 +75,6 @@ class RenderTags(Task): else: 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(): @@ -136,7 +136,7 @@ class RenderTags(Task): """Given tag, n, returns a page name.""" name = self.site.path("tag", tag, lang) if i: - name = name.replace('.html', '-%s.html' % i) + name = name.replace('.html', '-{0}.html'.format(i)) return name # FIXME: deduplicate this with render_indexes @@ -152,11 +152,11 @@ class RenderTags(Task): # 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))) + """{0} ({1})" href="{2}">""".format( + 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 = 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 @@ -231,8 +231,8 @@ class RenderTags(Task): '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, + (lang, "{0} ({1})".format(kw["blog_title"], tag), + kw["site_url"], kw["blog_description"], post_list, output_name, kw["rss_teasers"]))], 'clean': True, 'uptodate': [utils.config_changed(kw)], diff --git a/nikola/plugins/task_sitemap/__init__.py b/nikola/plugins/task_sitemap/__init__.py index 96b9dbd..9d89070 100644 --- a/nikola/plugins/task_sitemap/__init__.py +++ b/nikola/plugins/task_sitemap/__init__.py @@ -49,7 +49,8 @@ class Sitemap(LateTask): return """Generate Google sitemap.""" kw = { - "blog_url": self.site.config["BLOG_URL"], + "base_url": self.site.config["BASE_URL"], + "site_url": self.site.config["SITE_URL"], "output_folder": self.site.config["OUTPUT_FOLDER"], } output_path = os.path.abspath(kw['output_folder']) @@ -59,18 +60,14 @@ class Sitemap(LateTask): # Generate config config_data = """<?xml version="1.0" encoding="UTF-8"?> <site - base_url="%s" - store_into="%s" + base_url="{0}" + store_into="{1}" verbose="1" > - <directory path="%s" url="%s" /> + <directory path="{2}" url="{3}" /> <filter action="drop" type="wildcard" pattern="*~" /> <filter action="drop" type="regexp" pattern="/\.[^/]*" /> - </site>""" % ( - kw["blog_url"], - sitemap_path, - output_path, - kw["blog_url"], - ) + </site>""".format(kw["site_url"], sitemap_path, output_path, + kw["base_url"]) config_file = tempfile.NamedTemporaryFile(delete=False) config_file.write(config_data.encode('utf8')) config_file.close() @@ -82,14 +79,15 @@ class Sitemap(LateTask): 0) else: sitemap.Generate() - 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) + sitemap_gen.output.Log('Number of errors: {0}'.format( + sitemap_gen.output.num_errors), 1) + sitemap_gen.output.Log('Number of warnings: {0}'.format( + sitemap_gen.output.num_warns), 1) os.unlink(config_file.name) yield { "basename": "sitemap", + "name": os.path.join(kw['output_folder'], "sitemap.xml.gz"), "targets": [sitemap_path], "actions": [(sitemap,)], "uptodate": [config_changed(kw)], diff --git a/nikola/plugins/task_sitemap/sitemap_gen.py b/nikola/plugins/task_sitemap/sitemap_gen.py index a877c24..898325a 100644 --- a/nikola/plugins/task_sitemap/sitemap_gen.py +++ b/nikola/plugins/task_sitemap/sitemap_gen.py @@ -90,7 +90,7 @@ if sys.version_info[0] == 3: unichr = chr else: bytes_str = str - unicode_str = unicode + unicode_str = unicode # NOQA # Text encodings ENC_ASCII = 'ASCII' |
