aboutsummaryrefslogtreecommitdiffstats
path: root/nikola/plugins
diff options
context:
space:
mode:
Diffstat (limited to 'nikola/plugins')
-rw-r--r--nikola/plugins/__init__.py4
-rw-r--r--nikola/plugins/command_bootswatch_theme.py47
-rw-r--r--nikola/plugins/command_build.py36
-rw-r--r--nikola/plugins/command_check.py58
-rw-r--r--nikola/plugins/command_console.plugin9
-rw-r--r--nikola/plugins/command_console.py35
-rw-r--r--nikola/plugins/command_deploy.py27
-rw-r--r--nikola/plugins/command_import_wordpress.py330
-rw-r--r--nikola/plugins/command_init.py72
-rw-r--r--nikola/plugins/command_install_theme.py53
-rw-r--r--nikola/plugins/command_new_post.py46
-rw-r--r--nikola/plugins/command_serve.py37
-rw-r--r--nikola/plugins/compile_html.py24
-rw-r--r--nikola/plugins/compile_markdown/__init__.py33
-rw-r--r--nikola/plugins/compile_rest/__init__.py34
-rw-r--r--nikola/plugins/compile_rest/pygments_code_block_directive.py38
-rw-r--r--nikola/plugins/compile_rest/slides.py89
-rw-r--r--nikola/plugins/compile_rest/youtube.py24
-rw-r--r--nikola/plugins/task_archive.py62
-rw-r--r--nikola/plugins/task_copy_assets.py35
-rw-r--r--nikola/plugins/task_copy_files.py24
-rw-r--r--nikola/plugins/task_create_bundles.py27
-rw-r--r--nikola/plugins/task_indexes.py37
-rw-r--r--nikola/plugins/task_redirect.py30
-rw-r--r--nikola/plugins/task_render_galleries.py81
-rw-r--r--nikola/plugins/task_render_listings.py24
-rw-r--r--nikola/plugins/task_render_pages.py24
-rw-r--r--nikola/plugins/task_render_posts.py24
-rw-r--r--nikola/plugins/task_render_rss.py26
-rw-r--r--nikola/plugins/task_render_sources.py24
-rw-r--r--nikola/plugins/task_render_tags.py304
-rw-r--r--nikola/plugins/task_sitemap/__init__.py38
-rwxr-xr-xnikola/plugins/task_sitemap/sitemap_gen.py47
-rw-r--r--nikola/plugins/template_jinja.py36
-rw-r--r--nikola/plugins/template_mako.py28
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(