summaryrefslogtreecommitdiffstats
path: root/nikola/plugins
diff options
context:
space:
mode:
Diffstat (limited to 'nikola/plugins')
-rw-r--r--nikola/plugins/basic_import.py8
-rw-r--r--nikola/plugins/command/auto.py18
-rw-r--r--nikola/plugins/command/bootswatch_theme.py9
-rw-r--r--nikola/plugins/command/check.py56
-rw-r--r--nikola/plugins/command/console.py2
-rw-r--r--nikola/plugins/command/deploy.py29
-rw-r--r--nikola/plugins/command/import_blogger.py9
-rw-r--r--nikola/plugins/command/import_feed.py13
-rw-r--r--nikola/plugins/command/import_wordpress.py139
-rw-r--r--nikola/plugins/command/init.py110
-rw-r--r--nikola/plugins/command/install_plugin.py15
-rw-r--r--nikola/plugins/command/install_theme.py11
-rw-r--r--nikola/plugins/command/new_page.plugin9
-rw-r--r--nikola/plugins/command/new_page.py80
-rw-r--r--nikola/plugins/command/new_post.py61
-rw-r--r--nikola/plugins/command/planetoid/__init__.py14
-rw-r--r--nikola/plugins/command/serve.py19
-rw-r--r--nikola/plugins/compile/asciidoc.py11
-rw-r--r--nikola/plugins/compile/bbcode.py11
-rw-r--r--nikola/plugins/compile/html.py11
-rw-r--r--nikola/plugins/compile/ipynb/__init__.py11
-rw-r--r--nikola/plugins/compile/markdown/__init__.py11
-rw-r--r--nikola/plugins/compile/misaka.py11
-rw-r--r--nikola/plugins/compile/pandoc.py11
-rw-r--r--nikola/plugins/compile/php.py11
-rw-r--r--nikola/plugins/compile/rest/__init__.py43
-rw-r--r--nikola/plugins/compile/rest/listing.py19
-rw-r--r--nikola/plugins/compile/textile.py11
-rw-r--r--nikola/plugins/compile/txt2tags.py11
-rw-r--r--nikola/plugins/compile/wiki.py11
-rw-r--r--nikola/plugins/loghandler/stderr.py4
-rw-r--r--nikola/plugins/task/build_less.py16
-rw-r--r--nikola/plugins/task/build_sass.py19
-rw-r--r--nikola/plugins/task/bundles.py4
-rw-r--r--nikola/plugins/task/copy_assets.py4
-rw-r--r--nikola/plugins/task/galleries.py6
-rw-r--r--nikola/plugins/task/listings.py2
-rw-r--r--[-rwxr-xr-x]nikola/plugins/task/localsearch/files/assets/css/img/search.pngbin315 -> 315 bytes
-rw-r--r--[-rwxr-xr-x]nikola/plugins/task/localsearch/files/assets/css/tipuesearch.css0
-rw-r--r--[-rwxr-xr-x]nikola/plugins/task/localsearch/files/tipue_search.html0
-rw-r--r--nikola/plugins/task/rss.py5
-rw-r--r--nikola/plugins/task/sitemap/__init__.py19
-rw-r--r--nikola/plugins/task/tags.py14
-rw-r--r--nikola/plugins/template/jinja.py5
-rw-r--r--nikola/plugins/template/mako.py8
45 files changed, 615 insertions, 276 deletions
diff --git a/nikola/plugins/basic_import.py b/nikola/plugins/basic_import.py
index 0d94d16..27c0eb4 100644
--- a/nikola/plugins/basic_import.py
+++ b/nikola/plugins/basic_import.py
@@ -48,8 +48,8 @@ class ImportMixin(object):
name = "import_mixin"
needs_config = False
- doc_usage = "[options] wordpress_export_file"
- doc_purpose = "import a wordpress dump."
+ doc_usage = "[options] export_file"
+ doc_purpose = "import a dump from a different engine."
cmd_options = [
{
'name': 'output_folder',
@@ -93,7 +93,7 @@ class ImportMixin(object):
else:
self.import_into_existing_site = True
utils.LOGGER.notice('The folder {0} already exists - assuming that this is a '
- 'already existing nikola site.'.format(self.output_folder))
+ 'already existing Nikola site.'.format(self.output_folder))
filename = os.path.join(os.path.dirname(utils.__file__), 'conf.py.in')
# The 'strict_undefined=True' will give the missing symbol name if any,
@@ -151,7 +151,7 @@ class ImportMixin(object):
time=datetime.datetime.now().strftime('%Y%m%d_%H%M%S'),
name=self.name)
config_output_path = os.path.join(self.output_folder, filename)
- utils.LOGGER.notice('Configuration will be written to: {0}'.format(config_output_path))
+ utils.LOGGER.info('Configuration will be written to: {0}'.format(config_output_path))
return config_output_path
diff --git a/nikola/plugins/command/auto.py b/nikola/plugins/command/auto.py
index 01116d1..d707d53 100644
--- a/nikola/plugins/command/auto.py
+++ b/nikola/plugins/command/auto.py
@@ -34,7 +34,7 @@ from nikola.plugin_categories import Command
from nikola.utils import req_missing
-class Auto(Command):
+class CommandAuto(Command):
"""Start debugging console."""
name = "auto"
doc_purpose = "automatically detect site changes, rebuild and optionally refresh a browser"
@@ -61,26 +61,26 @@ class Auto(Command):
try:
from livereload import Server
except ImportError:
- req_missing(['livereload>=2.0.0'], 'use the "auto" command')
+ req_missing(['livereload==2.1.0'], 'use the "auto" command')
return
- # Run an initial build so we are uptodate
+ # Run an initial build so we are up-to-date
subprocess.call(("nikola", "build"))
port = options and options.get('port')
server = Server()
- server.watch('conf.py')
- server.watch('themes/')
- server.watch('templates/')
+ server.watch('conf.py', 'nikola build')
+ server.watch('themes/', 'nikola build')
+ server.watch('templates/', 'nikola build')
server.watch(self.site.config['GALLERY_PATH'])
for item in self.site.config['post_pages']:
- server.watch(os.path.dirname(item[0]))
+ server.watch(os.path.dirname(item[0]), 'nikola build')
for item in self.site.config['FILES_FOLDERS']:
- server.watch(os.path.dirname(item))
+ server.watch(os.path.dirname(item), 'nikola build')
out_folder = self.site.config['OUTPUT_FOLDER']
if options and options.get('browser'):
webbrowser.open('http://localhost:{0}'.format(port))
- server.serve(port, out_folder)
+ server.serve(port, None, out_folder)
diff --git a/nikola/plugins/command/bootswatch_theme.py b/nikola/plugins/command/bootswatch_theme.py
index 94f37f2..82c47d2 100644
--- a/nikola/plugins/command/bootswatch_theme.py
+++ b/nikola/plugins/command/bootswatch_theme.py
@@ -86,12 +86,14 @@ class CommandBootswatchTheme(Command):
version = '2'
elif 'bootstrap' not in themes:
LOGGER.warn('"bootswatch_theme" only makes sense for themes that use bootstrap')
+ elif 'bootstrap3-gradients' in themes or 'bootstrap3-gradients-jinja' in themes:
+ LOGGER.warn('"bootswatch_theme" doesn\'t work well with the bootstrap3-gradients family')
- LOGGER.notice("Creating '{0}' theme from '{1}' and '{2}'".format(name, swatch, parent))
+ LOGGER.info("Creating '{0}' theme from '{1}' and '{2}'".format(name, swatch, parent))
utils.makedirs(os.path.join('themes', name, 'assets', 'css'))
for fname in ('bootstrap.min.css', 'bootstrap.css'):
url = '/'.join(('http://bootswatch.com', version, swatch, fname))
- LOGGER.notice("Downloading: " + url)
+ LOGGER.info("Downloading: " + url)
data = requests.get(url).text
with open(os.path.join('themes', name, 'assets', 'css', fname),
'wb+') as output:
@@ -99,5 +101,4 @@ class CommandBootswatchTheme(Command):
with open(os.path.join('themes', name, 'parent'), 'wb+') as output:
output.write(parent.encode('utf-8'))
- LOGGER.notice('Theme created. Change the THEME setting to "{0}" to use '
- 'it.'.format(name))
+ LOGGER.notice('Theme created. Change the THEME setting to "{0}" to use it.'.format(name))
diff --git a/nikola/plugins/command/check.py b/nikola/plugins/command/check.py
index a7e8c13..26db321 100644
--- a/nikola/plugins/command/check.py
+++ b/nikola/plugins/command/check.py
@@ -102,6 +102,14 @@ class CommandCheck(Command):
'default': False,
'help': 'List possible source files for files with broken links.',
},
+ {
+ 'name': 'verbose',
+ 'long': 'verbose',
+ 'short': 'v',
+ 'type': bool,
+ 'default': False,
+ 'help': 'Be more verbose.',
+ },
]
def _execute(self, options, args):
@@ -112,6 +120,10 @@ class CommandCheck(Command):
if not options['links'] and not options['files'] and not options['clean']:
print(self.help())
return False
+ if options['verbose']:
+ self.logger.level = 1
+ else:
+ self.logger.level = 4
if options['links']:
failure = self.scan_links(options['find_sources'])
if options['files']:
@@ -126,6 +138,8 @@ class CommandCheck(Command):
def analyze(self, task, find_sources=False):
rv = False
self.whitelist = [re.compile(x) for x in self.site.config['LINK_CHECK_WHITELIST']]
+ base_url = urlparse(self.site.config['BASE_URL'])
+ url_type = self.site.config['URL_TYPE']
try:
filename = task.split(":")[-1]
d = lxml.html.fromstring(open(filename).read())
@@ -134,31 +148,51 @@ class CommandCheck(Command):
if target == "#":
continue
parsed = urlparse(target)
- if parsed.scheme or target.startswith('//'):
+
+ # Absolute links when using only paths, skip.
+ if (parsed.scheme or target.startswith('//')) and url_type in ('rel_path', 'full_path'):
continue
+
+ # Absolute links to other domains, skip
+ if (parsed.scheme or target.startswith('//')) and parsed.netloc != base_url.netloc:
+ continue
+
if parsed.fragment:
target = target.split('#')[0]
- target_filename = os.path.abspath(
- os.path.join(os.path.dirname(filename), unquote(target)))
+ if url_type == 'rel_path':
+ target_filename = os.path.abspath(
+ os.path.join(os.path.dirname(filename), unquote(target)))
+
+ elif url_type in ('full_path', 'absolute'):
+ target_filename = os.path.abspath(
+ os.path.join(os.path.dirname(filename), parsed.path))
+ if parsed.path.endswith('/'): # abspath removes trailing slashes
+ target_filename += '/{0}'.format(self.site.config['INDEX_FILE'])
+ if target_filename.startswith(base_url.path):
+ target_filename = target_filename[len(base_url.path):]
+ target_filename = os.path.join(self.site.config['OUTPUT_FOLDER'], target_filename)
+
if any(re.match(x, target_filename) for x in self.whitelist):
continue
elif target_filename not in self.existing_targets:
if os.path.exists(target_filename):
+ self.logger.notice("Good link {0} => {1}".format(target, target_filename))
self.existing_targets.add(target_filename)
else:
rv = True
- self.logger.warn("Broken link in {0}: ".format(filename), target)
+ self.logger.warn("Broken link in {0}: {1}".format(filename, target))
if find_sources:
self.logger.warn("Possible sources:")
self.logger.warn(os.popen('nikola list --deps ' + task, 'r').read())
self.logger.warn("===============================\n")
except Exception as exc:
- self.logger.error("Error with:", filename, exc)
+ self.logger.error("Error with: {0} {1}".format(filename, exc))
return rv
def scan_links(self, find_sources=False):
- self.logger.notice("Checking Links:")
- self.logger.notice("===============")
+ self.logger.info("Checking Links:")
+ self.logger.info("===============\n")
+ self.logger.notice("{0} mode".format(self.site.config['URL_TYPE']))
failure = False
for task in os.popen('nikola list --all', 'r').readlines():
task = task.strip()
@@ -170,13 +204,13 @@ class CommandCheck(Command):
if self.analyze(task, find_sources):
failure = True
if not failure:
- self.logger.notice("All links checked.")
+ self.logger.info("All links checked.")
return failure
def scan_files(self):
failure = False
- self.logger.notice("Checking Files:")
- self.logger.notice("===============\n")
+ self.logger.info("Checking Files:")
+ self.logger.info("===============\n")
only_on_output, only_on_input = real_scan_files(self.site)
# Ignore folders
@@ -195,7 +229,7 @@ class CommandCheck(Command):
for f in only_on_input:
self.logger.warn(f)
if not failure:
- self.logger.notice("All files checked.")
+ self.logger.info("All files checked.")
return failure
def clean_files(self):
diff --git a/nikola/plugins/command/console.py b/nikola/plugins/command/console.py
index e66b650..b0a8958 100644
--- a/nikola/plugins/command/console.py
+++ b/nikola/plugins/command/console.py
@@ -35,7 +35,7 @@ from nikola.utils import get_logger, STDERR_HANDLER
LOGGER = get_logger('console', STDERR_HANDLER)
-class Console(Command):
+class CommandConsole(Command):
"""Start debugging console."""
name = "console"
shells = ['ipython', 'bpython', 'plain']
diff --git a/nikola/plugins/command/deploy.py b/nikola/plugins/command/deploy.py
index eb5787e..bd1c15f 100644
--- a/nikola/plugins/command/deploy.py
+++ b/nikola/plugins/command/deploy.py
@@ -25,7 +25,6 @@
# SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
from __future__ import print_function
-from ast import literal_eval
import codecs
from datetime import datetime
import os
@@ -40,8 +39,8 @@ from nikola.plugin_categories import Command
from nikola.utils import remove_file, get_logger
-class Deploy(Command):
- """Deploy site. """
+class CommandDeploy(Command):
+ """Deploy site."""
name = "deploy"
doc_usage = ""
@@ -76,7 +75,7 @@ class Deploy(Command):
undeployed_posts.append(post)
for command in self.site.config['DEPLOY_COMMANDS']:
- self.logger.notice("==> {0}".format(command))
+ self.logger.info("==> {0}".format(command))
try:
subprocess.check_call(command, shell=True)
except subprocess.CalledProcessError as e:
@@ -84,26 +83,22 @@ class Deploy(Command):
'returned {1}'.format(e.cmd, e.returncode))
sys.exit(e.returncode)
- self.logger.notice("Successful deployment")
- tzinfo = pytz.timezone(self.site.config['TIMEZONE'])
+ self.logger.info("Successful deployment")
try:
- with open(timestamp_path, 'rb') as inf:
- last_deploy = literal_eval(inf.read().strip())
- if tzinfo:
- last_deploy = last_deploy.replace(tzinfo=tzinfo)
+ with codecs.open(timestamp_path, 'rb', 'utf8') as inf:
+ last_deploy = datetime.strptime(inf.read().strip(), "%Y-%m-%dT%H:%M:%S.%f")
clean = False
- except Exception:
+ except (IOError, Exception) as e:
+ self.logger.debug("Problem when reading `{0}`: {1}".format(timestamp_path, e))
last_deploy = datetime(1970, 1, 1)
- if tzinfo:
- last_deploy = last_deploy.replace(tzinfo=tzinfo)
clean = True
- new_deploy = datetime.now()
+ new_deploy = datetime.utcnow()
self._emit_deploy_event(last_deploy, new_deploy, clean, undeployed_posts)
# Store timestamp of successful deployment
with codecs.open(timestamp_path, 'wb+', 'utf8') as outf:
- outf.write(repr(new_deploy))
+ outf.write(new_deploy.isoformat())
def _emit_deploy_event(self, last_deploy, new_deploy, clean=False, undeployed=None):
""" Emit events for all timeline entries newer than last deploy.
@@ -129,9 +124,11 @@ class Deploy(Command):
'undeployed': undeployed
}
+ tzinfo = pytz.timezone(self.site.config['TIMEZONE'])
+
deployed = [
entry for entry in self.site.timeline
- if entry.date > last_deploy and entry not in undeployed
+ if entry.date > (last_deploy.replace(tzinfo=tzinfo) if tzinfo else last_deploy) and entry not in undeployed
]
event['deployed'] = deployed
diff --git a/nikola/plugins/command/import_blogger.py b/nikola/plugins/command/import_blogger.py
index ea12b4a..dd629c4 100644
--- a/nikola/plugins/command/import_blogger.py
+++ b/nikola/plugins/command/import_blogger.py
@@ -43,6 +43,7 @@ from nikola.plugin_categories import Command
from nikola import utils
from nikola.utils import req_missing
from nikola.plugins.basic_import import ImportMixin
+from nikola.plugins.command.init import SAMPLE_CONF, prepare_config
LOGGER = utils.get_logger('import_blogger', utils.STDERR_HANDLER)
@@ -95,8 +96,8 @@ class CommandImportBlogger(Command, ImportMixin):
conf_out_path = self.get_configuration_output_path()
# if it tracebacks here, look a comment in
# basic_import.Import_Mixin.generate_base_site
- conf_termplate_render = conf_template.render(**self.context)
- self.write_configuration(conf_out_path, conf_termplate_render)
+ conf_template_render = conf_template.render(**prepare_config(self.context))
+ self.write_configuration(conf_out_path, conf_template_render)
@classmethod
def get_channel_from_file(cls, filename):
@@ -106,8 +107,7 @@ class CommandImportBlogger(Command, ImportMixin):
@staticmethod
def populate_context(channel):
- # may need changes when the template conf.py.in changes
- context = {}
+ context = SAMPLE_CONF.copy()
context['DEFAULT_LANG'] = 'en' # blogger doesn't include the language
# in the dump
context['BLOG_TITLE'] = channel.feed.title
@@ -131,7 +131,6 @@ class CommandImportBlogger(Command, ImportMixin):
"html": ('.html', '.htm')
}
'''
- context['THEME'] = 'bootstrap3'
return context
diff --git a/nikola/plugins/command/import_feed.py b/nikola/plugins/command/import_feed.py
index 70a5cd5..ee59277 100644
--- a/nikola/plugins/command/import_feed.py
+++ b/nikola/plugins/command/import_feed.py
@@ -43,6 +43,7 @@ from nikola.plugin_categories import Command
from nikola import utils
from nikola.utils import req_missing
from nikola.plugins.basic_import import ImportMixin
+from nikola.plugins.command.init import SAMPLE_CONF, prepare_config
LOGGER = utils.get_logger('import_feed', utils.STDERR_HANDLER)
@@ -82,7 +83,7 @@ class CommandImportFeed(Command, ImportMixin):
self.import_posts(channel)
self.write_configuration(self.get_configuration_output_path(
- ), conf_template.render(**self.context))
+ ), conf_template.render(**prepare_config(self.context)))
@classmethod
def get_channel_from_file(cls, filename):
@@ -90,7 +91,7 @@ class CommandImportFeed(Command, ImportMixin):
@staticmethod
def populate_context(channel):
- context = {}
+ context = SAMPLE_CONF.copy()
context['DEFAULT_LANG'] = channel.feed.title_detail.language \
if channel.feed.title_detail.language else 'en'
context['BLOG_TITLE'] = channel.feed.title
@@ -100,9 +101,11 @@ class CommandImportFeed(Command, ImportMixin):
context['BLOG_EMAIL'] = channel.feed.author_detail.get('email', '') if 'author_detail' in channel.feed else ''
context['BLOG_AUTHOR'] = channel.feed.author_detail.get('name', '') if 'author_detail' in channel.feed else ''
- context['POST_PAGES'] = '''(
- ("posts/*.html", "posts", "post.tmpl", True),
- ("stories/*.html", "stories", "story.tmpl", False),
+ context['POSTS'] = '''(
+ ("posts/*.html", "posts", "post.tmpl"),
+ )'''
+ context['PAGES'] = '''(
+ ("stories/*.html", "stories", "story.tmpl"),
)'''
context['COMPILERS'] = '''{
"rest": ('.txt', '.rst'),
diff --git a/nikola/plugins/command/import_wordpress.py b/nikola/plugins/command/import_wordpress.py
index 0c9915a..b567c77 100644
--- a/nikola/plugins/command/import_wordpress.py
+++ b/nikola/plugins/command/import_wordpress.py
@@ -50,6 +50,8 @@ from nikola.plugin_categories import Command
from nikola import utils
from nikola.utils import req_missing
from nikola.plugins.basic_import import ImportMixin, links
+from nikola.nikola import DEFAULT_TRANSLATIONS_PATTERN
+from nikola.plugins.command.init import SAMPLE_CONF, prepare_config
LOGGER = utils.get_logger('import_wordpress', utils.STDERR_HANDLER)
@@ -84,6 +86,23 @@ class CommandImportWordpress(Command, ImportMixin):
'type': bool,
'help': "Do not try to download files for the import",
},
+ {
+ 'name': 'separate_qtranslate_content',
+ 'long': 'qtranslate',
+ 'default': False,
+ 'type': bool,
+ 'help': "Look for translations generated by qtranslate plugin",
+ # WARNING: won't recover translated titles that actually
+ # don't seem to be part of the wordpress XML export at the
+ # time of writing :(
+ },
+ {
+ 'name': 'translations_pattern',
+ 'long': 'translations_pattern',
+ 'default': None,
+ 'type': str,
+ 'help': "The pattern for translation files names",
+ },
]
def _execute(self, options={}, args=[]):
@@ -114,6 +133,9 @@ class CommandImportWordpress(Command, ImportMixin):
self.exclude_drafts = options.get('exclude_drafts', False)
self.no_downloads = options.get('no_downloads', False)
+ self.separate_qtranslate_content = options.get('separate_qtranslate_content')
+ self.translations_pattern = options.get('translations_pattern')
+
if not self.no_downloads:
def show_info_about_mising_module(modulename):
LOGGER.error(
@@ -135,15 +157,21 @@ class CommandImportWordpress(Command, ImportMixin):
self.context = self.populate_context(channel)
conf_template = self.generate_base_site()
+ # If user has specified a custom pattern for translation files we
+ # need to fix the config
+ if self.translations_pattern:
+ self.context['TRANSLATIONS_PATTERN'] = self.translations_pattern
+
self.import_posts(channel)
self.context['REDIRECTIONS'] = self.configure_redirections(
self.url_map)
self.write_urlmap_csv(
os.path.join(self.output_folder, 'url_map.csv'), self.url_map)
- rendered_template = conf_template.render(**self.context)
+ rendered_template = conf_template.render(**prepare_config(self.context))
rendered_template = re.sub('# REDIRECTIONS = ', 'REDIRECTIONS = ',
rendered_template)
+
if self.timezone:
rendered_template = re.sub('# TIMEZONE = \'UTC\'',
'TIMEZONE = \'' + self.timezone + '\'',
@@ -194,8 +222,9 @@ class CommandImportWordpress(Command, ImportMixin):
def populate_context(channel):
wordpress_namespace = channel.nsmap['wp']
- context = {}
+ context = SAMPLE_CONF.copy()
context['DEFAULT_LANG'] = get_text_tag(channel, 'language', 'en')[:2]
+ context['TRANSLATIONS_PATTERN'] = DEFAULT_TRANSLATIONS_PATTERN
context['BLOG_TITLE'] = get_text_tag(channel, 'title',
'PUT TITLE HERE')
context['BLOG_DESCRIPTION'] = get_text_tag(
@@ -205,9 +234,10 @@ class CommandImportWordpress(Command, ImportMixin):
base_site_url = channel.find('{{{0}}}author'.format(wordpress_namespace))
context['BASE_URL'] = get_text_tag(base_site_url,
None,
- "http://foo.com")
+ "http://foo.com/")
+ if not context['BASE_URL'].endswith('/'):
+ context['BASE_URL'] += '/'
context['SITE_URL'] = context['BASE_URL']
- context['THEME'] = 'bootstrap3'
author = channel.find('{{{0}}}author'.format(wordpress_namespace))
context['BLOG_EMAIL'] = get_text_tag(
@@ -253,7 +283,7 @@ class CommandImportWordpress(Command, ImportMixin):
+ list(path.split('/'))))
dst_dir = os.path.dirname(dst_path)
utils.makedirs(dst_dir)
- LOGGER.notice("Downloading {0} => {1}".format(url, dst_path))
+ LOGGER.info("Downloading {0} => {1}".format(url, dst_path))
self.download_url_content_to_file(url, dst_path)
dst_url = '/'.join(dst_path.split(os.sep)[2:])
links[link] = '/' + dst_url
@@ -288,7 +318,7 @@ class CommandImportWordpress(Command, ImportMixin):
# your blogging into another site or system its not.
# Why don't they just use JSON?
if sys.version_info[0] == 2:
- metadata = phpserialize.loads(meta_value.text)
+ metadata = phpserialize.loads(utils.sys_encode(meta_value.text))
size_key = 'sizes'
file_key = 'file'
else:
@@ -307,7 +337,7 @@ class CommandImportWordpress(Command, ImportMixin):
+ list(path.split('/'))))
dst_dir = os.path.dirname(dst_path)
utils.makedirs(dst_dir)
- LOGGER.notice("Downloading {0} => {1}".format(url, dst_path))
+ LOGGER.info("Downloading {0} => {1}".format(url, dst_path))
self.download_url_content_to_file(url, dst_path)
dst_url = '/'.join(dst_path.split(os.sep)[2:])
links[url] = '/' + dst_url
@@ -350,14 +380,17 @@ class CommandImportWordpress(Command, ImportMixin):
# 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)
- path = unquote(urlparse(link).path)
+ path = unquote(urlparse(link).path.strip('/'))
# In python 2, path is a str. slug requires a unicode
# object. According to wikipedia, unquoted strings will
# usually be UTF8
if isinstance(path, utils.bytes_str):
path = path.decode('utf8')
- slug = utils.slugify(path)
+ pathlist = path.split('/')
+ if len(pathlist) > 1:
+ out_folder = os.path.join(*([out_folder] + pathlist[:-1]))
+ slug = utils.slugify(pathlist[-1])
if not slug: # it happens if the post has no "nice" URL
slug = get_text_tag(
item, '{{{0}}}post_name'.format(wordpress_namespace), None)
@@ -395,21 +428,43 @@ class CommandImportWordpress(Command, ImportMixin):
continue
tags.append(text)
+ if '$latex' in content:
+ tags.append('mathjax')
+
if is_draft and self.exclude_drafts:
LOGGER.notice('Draft "{0}" will not be imported.'.format(title))
elif content.strip():
# If no content is found, no files are written.
- self.url_map[link] = self.context['SITE_URL'] + '/' + \
- out_folder + '/' + slug + '.html'
-
- content = self.transform_content(content)
-
- self.write_metadata(os.path.join(self.output_folder, out_folder,
- slug + '.meta'),
- title, slug, post_date, description, tags)
- self.write_content(
- os.path.join(self.output_folder, out_folder, slug + '.wp'),
- content)
+ self.url_map[link] = (self.context['SITE_URL'] + out_folder + '/'
+ + slug + '.html')
+ if hasattr(self, "separate_qtranslate_content") \
+ and self.separate_qtranslate_content:
+ content_translations = separate_qtranslate_content(content)
+ else:
+ content_translations = {"": content}
+ default_language = self.context["DEFAULT_LANG"]
+ for lang, content in content_translations.items():
+ if lang:
+ out_meta_filename = slug + '.meta'
+ if lang == default_language:
+ out_content_filename = slug + '.wp'
+ else:
+ out_content_filename \
+ = utils.get_translation_candidate(self.context,
+ slug + ".wp", lang)
+ meta_slug = slug
+ else:
+ out_meta_filename = slug + '.meta'
+ out_content_filename = slug + '.wp'
+ meta_slug = slug
+ content = self.transform_content(content)
+ self.write_metadata(os.path.join(self.output_folder, out_folder,
+ out_meta_filename),
+ title, meta_slug, post_date, description, tags)
+ self.write_content(
+ os.path.join(self.output_folder,
+ out_folder, out_content_filename),
+ content)
else:
LOGGER.warn('Not going to import "{0}" because it seems to contain'
' no content.'.format(title))
@@ -441,3 +496,47 @@ def get_text_tag(tag, name, default):
return t.text
else:
return default
+
+
+def separate_qtranslate_content(text):
+ """Parse the content of a wordpress post or page and separate
+ the various language specific contents when they are delimited
+ with qtranslate tags: <!--:LL-->blabla<!--:-->"""
+ # TODO: uniformize qtranslate tags <!--/en--> => <!--:-->
+ qt_start = "<!--:"
+ qt_end = "-->"
+ qt_end_with_lang_len = 5
+ qt_chunks = text.split(qt_start)
+ content_by_lang = {}
+ common_txt_list = []
+ for c in qt_chunks:
+ if not c.strip():
+ continue
+ if c.startswith(qt_end):
+ # just after the end of a language specific section, there may
+ # be some piece of common text or tags, or just nothing
+ lang = "" # default language
+ c = c.lstrip(qt_end)
+ if not c:
+ continue
+ elif c[2:].startswith(qt_end):
+ # a language specific section (with language code at the begining)
+ lang = c[:2]
+ c = c[qt_end_with_lang_len:]
+ else:
+ # nowhere specific (maybe there is no language section in the
+ # currently parsed content)
+ lang = "" # default language
+ if not lang:
+ common_txt_list.append(c)
+ for l in content_by_lang.keys():
+ content_by_lang[l].append(c)
+ else:
+ content_by_lang[lang] = content_by_lang.get(lang, common_txt_list) + [c]
+ # in case there was no language specific section, just add the text
+ if common_txt_list and not content_by_lang:
+ content_by_lang[""] = common_txt_list
+ # Format back the list to simple text
+ for l in content_by_lang.keys():
+ content_by_lang[l] = " ".join(content_by_lang[l])
+ return content_by_lang
diff --git a/nikola/plugins/command/init.py b/nikola/plugins/command/init.py
index 96caad8..d7eeed7 100644
--- a/nikola/plugins/command/init.py
+++ b/nikola/plugins/command/init.py
@@ -28,18 +28,71 @@ from __future__ import print_function
import os
import shutil
import codecs
+import json
from mako.template import Template
import nikola
+from nikola.nikola import DEFAULT_TRANSLATIONS_PATTERN
from nikola.plugin_categories import Command
from nikola.utils import get_logger, makedirs, STDERR_HANDLER
from nikola.winutils import fix_git_symlinked
LOGGER = get_logger('init', STDERR_HANDLER)
+SAMPLE_CONF = {
+ 'BLOG_AUTHOR': "Your Name",
+ 'BLOG_TITLE': "Demo Site",
+ 'SITE_URL': "http://getnikola.com/",
+ 'BLOG_EMAIL': "joe@demo.site",
+ 'BLOG_DESCRIPTION': "This is a demo site for Nikola.",
+ 'DEFAULT_LANG': "en",
+ 'THEME': 'bootstrap3',
+ 'COMMENT_SYSTEM': 'disqus',
+ 'COMMENT_SYSTEM_ID': 'nikolademo',
+ 'TRANSLATIONS_PATTERN': DEFAULT_TRANSLATIONS_PATTERN,
+ 'POSTS': """(
+("posts/*.rst", "posts", "post.tmpl"),
+("posts/*.txt", "posts", "post.tmpl"),
+)""",
+ 'PAGES': """(
+("stories/*.rst", "stories", "story.tmpl"),
+("stories/*.txt", "stories", "story.tmpl"),
+)""",
+ 'COMPILERS': """{
+"rest": ('.rst', '.txt'),
+"markdown": ('.md', '.mdown', '.markdown'),
+"textile": ('.textile',),
+"txt2tags": ('.t2t',),
+"bbcode": ('.bb',),
+"wiki": ('.wiki',),
+"ipynb": ('.ipynb',),
+"html": ('.html', '.htm'),
+# PHP files are rendered the usual way (i.e. with the full templates).
+# The resulting files have .php extensions, making it possible to run
+# them without reconfiguring your server to recognize them.
+"php": ('.php',),
+# Pandoc detects the input from the source filename
+# but is disabled by default as it would conflict
+# with many of the others.
+# "pandoc": ('.rst', '.md', '.txt'),
+}""",
+ 'REDIRECTIONS': [],
+}
+
+
+# In order to ensure proper escaping, all variables but the three
+# pre-formatted ones are handled by json.dumps().
+def prepare_config(config):
+ """Parse sample config with JSON."""
+ p = config.copy()
+ p.update(dict((k, json.dumps(v)) for k, v in p.items()
+ if k not in ('POSTS', 'PAGES', 'COMPILERS')))
+ return p
+
class CommandInit(Command):
+
"""Create a new site."""
name = "init"
@@ -57,40 +110,6 @@ class CommandInit(Command):
}
]
- SAMPLE_CONF = {
- 'BLOG_AUTHOR': "Your Name",
- 'BLOG_TITLE': "Demo Site",
- 'SITE_URL': "http://getnikola.com/",
- 'BLOG_EMAIL': "joe@demo.site",
- 'BLOG_DESCRIPTION': "This is a demo site for Nikola.",
- 'DEFAULT_LANG': "en",
- 'THEME': 'bootstrap3',
-
- 'POSTS': """(
- ("posts/*.rst", "posts", "post.tmpl"),
- ("posts/*.txt", "posts", "post.tmpl"),
-)""",
- 'PAGES': """(
- ("stories/*.rst", "stories", "story.tmpl"),
- ("stories/*.txt", "stories", "story.tmpl"),
-)""",
- 'COMPILERS': """{
- "rest": ('.rst', '.txt'),
- "markdown": ('.md', '.mdown', '.markdown'),
- "textile": ('.textile',),
- "txt2tags": ('.t2t',),
- "bbcode": ('.bb',),
- "wiki": ('.wiki',),
- "ipynb": ('.ipynb',),
- "html": ('.html', '.htm'),
- # Pandoc detects the input from the source filename
- # but is disabled by default as it would conflict
- # with many of the others.
- # "pandoc": ('.rst', '.md', '.txt'),
-}""",
- 'REDIRECTIONS': '[]',
- }
-
@classmethod
def copy_sample_site(cls, target):
lib_path = cls.get_path_to_nikola_modules()
@@ -105,7 +124,7 @@ class CommandInit(Command):
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(**cls.SAMPLE_CONF))
+ fd.write(conf_template.render(**prepare_config(SAMPLE_CONF)))
@classmethod
def create_empty_site(cls, target):
@@ -122,16 +141,13 @@ class CommandInit(Command):
print("Usage: nikola init folder [options]")
return False
target = args[0]
- if target is None:
- print(self.usage)
+ if not options or not options.get('demo'):
+ self.create_empty_site(target)
+ LOGGER.info('Created empty site at {0}.'.format(target))
else:
- if not options or not options.get('demo'):
- self.create_empty_site(target)
- LOGGER.notice('Created empty site at {0}.'.format(target))
- else:
- self.copy_sample_site(target)
- LOGGER.notice("A new site with example data has been created at "
- "{0}.".format(target))
- LOGGER.notice("See README.txt in that folder for more information.")
-
- self.create_configuration(target)
+ self.copy_sample_site(target)
+ LOGGER.info("A new site with example data has been created at "
+ "{0}.".format(target))
+ LOGGER.info("See README.txt in that folder for more information.")
+
+ self.create_configuration(target)
diff --git a/nikola/plugins/command/install_plugin.py b/nikola/plugins/command/install_plugin.py
index 1d6584d..34223c0 100644
--- a/nikola/plugins/command/install_plugin.py
+++ b/nikola/plugins/command/install_plugin.py
@@ -27,7 +27,6 @@
from __future__ import print_function
import codecs
import os
-import sys
import json
import shutil
import subprocess
@@ -123,10 +122,10 @@ class CommandInstallPlugin(Command):
def do_install(self, name, data):
if name in data:
utils.makedirs(self.output_dir)
- LOGGER.notice('Downloading: ' + data[name])
+ LOGGER.info('Downloading: ' + data[name])
zip_file = BytesIO()
zip_file.write(requests.get(data[name]).content)
- LOGGER.notice('Extracting: {0} into plugins'.format(name))
+ LOGGER.info('Extracting: {0} into plugins'.format(name))
utils.extract_all(zip_file, 'plugins')
dest_path = os.path.join('plugins', name)
else:
@@ -142,13 +141,13 @@ class CommandInstallPlugin(Command):
LOGGER.error("{0} is already installed".format(name))
return False
- LOGGER.notice('Copying {0} into plugins'.format(plugin_path))
+ LOGGER.info('Copying {0} into plugins'.format(plugin_path))
shutil.copytree(plugin_path, dest_path)
reqpath = os.path.join(dest_path, 'requirements.txt')
if os.path.exists(reqpath):
LOGGER.notice('This plugin has Python dependencies.')
- LOGGER.notice('Installing dependencies with pip...')
+ LOGGER.info('Installing dependencies with pip...')
try:
subprocess.check_call(('pip', 'install', '-r', reqpath))
except subprocess.CalledProcessError:
@@ -159,7 +158,7 @@ class CommandInstallPlugin(Command):
print('You have to install those yourself or through a '
'package manager.')
else:
- LOGGER.notice('Dependency installation succeeded.')
+ LOGGER.info('Dependency installation succeeded.')
reqnpypath = os.path.join(dest_path, 'requirements-nonpy.txt')
if os.path.exists(reqnpypath):
LOGGER.notice('This plugin has third-party '
@@ -177,10 +176,10 @@ class CommandInstallPlugin(Command):
'manager.')
confpypath = os.path.join(dest_path, 'conf.py.sample')
if os.path.exists(confpypath):
- LOGGER.notice('This plugin has a sample config file.')
+ LOGGER.notice('This plugin has a sample config file. Integrate it with yours in order to make this plugin work!')
print('Contents of the conf.py.sample file:\n')
with codecs.open(confpypath, 'rb', 'utf-8') as fh:
- if sys.platform == 'win32':
+ if self.site.colorful:
print(indent(pygments.highlight(
fh.read(), PythonLexer(), TerminalFormatter()),
4 * ' '))
diff --git a/nikola/plugins/command/install_theme.py b/nikola/plugins/command/install_theme.py
index 569397b..47c73b4 100644
--- a/nikola/plugins/command/install_theme.py
+++ b/nikola/plugins/command/install_theme.py
@@ -26,7 +26,6 @@
from __future__ import print_function
import os
-import sys
import codecs
import json
import shutil
@@ -137,10 +136,10 @@ class CommandInstallTheme(Command):
def do_install(self, name, data):
if name in data:
utils.makedirs(self.output_dir)
- LOGGER.notice('Downloading: ' + data[name])
+ LOGGER.info('Downloading: ' + data[name])
zip_file = BytesIO()
zip_file.write(requests.get(data[name]).content)
- LOGGER.notice('Extracting: {0} into themes'.format(name))
+ LOGGER.info('Extracting: {0} into themes'.format(name))
utils.extract_all(zip_file)
dest_path = os.path.join('themes', name)
else:
@@ -156,14 +155,14 @@ class CommandInstallTheme(Command):
LOGGER.error("{0} is already installed".format(name))
return False
- LOGGER.notice('Copying {0} into themes'.format(theme_path))
+ LOGGER.info('Copying {0} into themes'.format(theme_path))
shutil.copytree(theme_path, dest_path)
confpypath = os.path.join(dest_path, 'conf.py.sample')
if os.path.exists(confpypath):
- LOGGER.notice('This plugin has a sample config file. Integrate it with yours in order to make this theme work!')
+ LOGGER.notice('This theme has a sample config file. Integrate it with yours in order to make this theme work!')
print('Contents of the conf.py.sample file:\n')
with codecs.open(confpypath, 'rb', 'utf-8') as fh:
- if sys.platform == 'win32':
+ if self.site.colorful:
print(indent(pygments.highlight(
fh.read(), PythonLexer(), TerminalFormatter()),
4 * ' '))
diff --git a/nikola/plugins/command/new_page.plugin b/nikola/plugins/command/new_page.plugin
new file mode 100644
index 0000000..1f1c84c
--- /dev/null
+++ b/nikola/plugins/command/new_page.plugin
@@ -0,0 +1,9 @@
+[Core]
+Name = new_page
+Module = new_page
+
+[Documentation]
+Author = Roberto Alsina, Chris Warrick
+Version = 0.1
+Website = http://getnikola.com
+Description = Create a new page.
diff --git a/nikola/plugins/command/new_page.py b/nikola/plugins/command/new_page.py
new file mode 100644
index 0000000..39c0c1d
--- /dev/null
+++ b/nikola/plugins/command/new_page.py
@@ -0,0 +1,80 @@
+# -*- coding: utf-8 -*-
+
+# Copyright © 2012-2014 Roberto Alsina, Chris Warrick and others.
+
+# Permission is hereby granted, free of charge, to any
+# person obtaining a copy of this software and associated
+# documentation files (the "Software"), to deal in the
+# Software without restriction, including without limitation
+# the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the
+# Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice
+# shall be included in all copies or substantial portions of
+# the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
+# PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS
+# OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+# OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+# OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+# SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+from __future__ import unicode_literals, print_function
+
+from nikola.plugin_categories import Command
+
+
+class CommandNewPage(Command):
+ """Create a new page."""
+
+ name = "new_page"
+ doc_usage = "[options] [path]"
+ doc_purpose = "create a new page in the site"
+ cmd_options = [
+ {
+ 'name': 'title',
+ 'short': 't',
+ 'long': 'title',
+ 'type': str,
+ 'default': '',
+ 'help': 'Title for the page.'
+ },
+ {
+ 'name': 'onefile',
+ 'short': '1',
+ 'type': bool,
+ 'default': False,
+ 'help': 'Create the page with embedded metadata (single file format)'
+ },
+ {
+ 'name': 'twofile',
+ 'short': '2',
+ 'type': bool,
+ 'default': False,
+ 'help': 'Create the page with separate metadata (two file format)'
+ },
+ {
+ 'name': 'content_format',
+ 'short': 'f',
+ 'long': 'format',
+ 'type': str,
+ 'default': '',
+ 'help': 'Markup format for the page, one of rest, markdown, wiki, '
+ 'bbcode, html, textile, txt2tags',
+ },
+ ]
+
+ def _execute(self, options, args):
+ """Create a new page."""
+ options['tags'] = ''
+ options['schedule'] = False
+ options['is_page'] = True
+ # Even though stuff was split into `new_page`, it’s easier to do it
+ # there not to duplicate the code.
+ p = self.site.plugin_manager.getPluginByName('new_post', 'Command').plugin_object
+ return p.execute(options, args)
diff --git a/nikola/plugins/command/new_post.py b/nikola/plugins/command/new_post.py
index a5c551d..cd37a75 100644
--- a/nikola/plugins/command/new_post.py
+++ b/nikola/plugins/command/new_post.py
@@ -35,7 +35,9 @@ from blinker import signal
from nikola.plugin_categories import Command
from nikola import utils
-LOGGER = utils.get_logger('new_post', utils.STDERR_HANDLER)
+POSTLOGGER = utils.get_logger('new_post', utils.STDERR_HANDLER)
+PAGELOGGER = utils.get_logger('new_page', utils.STDERR_HANDLER)
+LOGGER = POSTLOGGER
def filter_post_pages(compiler, is_post, compilers, post_pages):
@@ -57,8 +59,8 @@ def filter_post_pages(compiler, is_post, compilers, post_pages):
type_name = "post" if is_post else "page"
raise Exception("Can't find a way, using your configuration, to create "
"a {0} in format {1}. You may want to tweak "
- "COMPILERS or POSTS/PAGES in conf.py".format(
- type_name, compiler))
+ "COMPILERS or {2}S in conf.py".format(
+ type_name, compiler, type_name.upper()))
return filtered[0]
@@ -134,7 +136,7 @@ class CommandNewPost(Command):
'long': 'page',
'type': bool,
'default': False,
- 'help': 'Create a page instead of a blog post.'
+ 'help': 'Create a page instead of a blog post. (see also: `nikola new_page`)'
},
{
'name': 'title',
@@ -142,36 +144,36 @@ class CommandNewPost(Command):
'long': 'title',
'type': str,
'default': '',
- 'help': 'Title for the page/post.'
+ 'help': 'Title for the post.'
},
{
'name': 'tags',
'long': 'tags',
'type': str,
'default': '',
- 'help': 'Comma-separated tags for the page/post.'
+ 'help': 'Comma-separated tags for the post.'
},
{
'name': 'onefile',
'short': '1',
'type': bool,
'default': False,
- 'help': 'Create post with embedded metadata (single file format)'
+ 'help': 'Create the post with embedded metadata (single file format)'
},
{
'name': 'twofile',
'short': '2',
'type': bool,
'default': False,
- 'help': 'Create post with separate metadata (two file format)'
+ 'help': 'Create the post with separate metadata (two file format)'
},
{
- 'name': 'post_format',
+ 'name': 'content_format',
'short': 'f',
'long': 'format',
'type': str,
'default': '',
- 'help': 'Markup format for post, one of rest, markdown, wiki, '
+ 'help': 'Markup format for the post, one of rest, markdown, wiki, '
'bbcode, html, textile, txt2tags',
},
{
@@ -179,13 +181,14 @@ class CommandNewPost(Command):
'short': 's',
'type': bool,
'default': False,
- 'help': 'Schedule post based on recurrence rule'
+ 'help': 'Schedule the post based on recurrence rule'
},
]
def _execute(self, options, args):
"""Create a new post or page."""
+ global LOGGER
compiler_names = [p.name for p in
self.site.plugin_manager.getPluginsOfCategory(
"PageCompiler")]
@@ -198,38 +201,46 @@ class CommandNewPost(Command):
else:
path = None
+ # Even though stuff was split into `new_page`, it’s easier to do it
+ # here not to duplicate the code.
is_page = options.get('is_page', False)
is_post = not is_page
+ content_type = 'page' if is_page else 'post'
title = options['title'] or None
tags = options['tags']
onefile = options['onefile']
twofile = options['twofile']
+ if is_page:
+ LOGGER = PAGELOGGER
+ else:
+ LOGGER = POSTLOGGER
+
if twofile:
onefile = False
if not onefile and not twofile:
onefile = self.site.config.get('ONE_FILE_POSTS', True)
- post_format = options['post_format']
+ content_format = options['content_format']
- if not post_format: # Issue #400
- post_format = get_default_compiler(
+ if not content_format: # Issue #400
+ content_format = get_default_compiler(
is_post,
self.site.config['COMPILERS'],
self.site.config['post_pages'])
- if post_format not in compiler_names:
- LOGGER.error("Unknown post format " + post_format)
+ if content_format not in compiler_names:
+ LOGGER.error("Unknown {0} format {1}".format(content_type, content_format))
return
compiler_plugin = self.site.plugin_manager.getPluginByName(
- post_format, "PageCompiler").plugin_object
+ content_format, "PageCompiler").plugin_object
# Guess where we should put this
- entry = filter_post_pages(post_format, is_post,
+ entry = filter_post_pages(content_format, is_post,
self.site.config['COMPILERS'],
self.site.config['post_pages'])
- print("Creating New Post")
+ print("Creating New {0}".format(content_type.title()))
print("-----------------\n")
if title is None:
print("Enter title: ", end='')
@@ -247,7 +258,7 @@ class CommandNewPost(Command):
if isinstance(path, utils.bytes_str):
path = path.decode(sys.stdin.encoding)
slug = utils.slugify(os.path.splitext(os.path.basename(path))[0])
- # Calculate the date to use for the post
+ # Calculate the date to use for the content
schedule = options['schedule'] or self.site.config['SCHEDULE_ALL']
rule = self.site.config['SCHEDULE_RULE']
force_today = self.site.config['SCHEDULE_FORCE_TODAY']
@@ -275,7 +286,7 @@ class CommandNewPost(Command):
metadata = self.site.config['ADDITIONAL_METADATA']
compiler_plugin.create_post(
txt_path, onefile, title=title,
- slug=slug, date=date, tags=tags, **metadata)
+ slug=slug, date=date, tags=tags, is_page=is_page, **metadata)
event = dict(path=txt_path)
@@ -283,9 +294,9 @@ class CommandNewPost(Command):
with codecs.open(meta_path, "wb+", "utf8") as fd:
fd.write('\n'.join(data))
with codecs.open(txt_path, "wb+", "utf8") as fd:
- fd.write("Write your post here.")
- LOGGER.notice("Your post's metadata is at: {0}".format(meta_path))
+ fd.write("Write your {0} here.".format(content_type))
+ LOGGER.info("Your {0}'s metadata is at: {1}".format(content_type, meta_path))
event['meta_path'] = meta_path
- LOGGER.notice("Your post's text is at: {0}".format(txt_path))
+ LOGGER.info("Your {0}'s text is at: {1}".format(content_type, txt_path))
- signal('new_post').send(self, **event)
+ signal('new_' + content_type).send(self, **event)
diff --git a/nikola/plugins/command/planetoid/__init__.py b/nikola/plugins/command/planetoid/__init__.py
index ff5dd13..fe1a59b 100644
--- a/nikola/plugins/command/planetoid/__init__.py
+++ b/nikola/plugins/command/planetoid/__init__.py
@@ -162,12 +162,12 @@ class Planetoid(Command, Task):
# TODO: log failure
return
if parsed.feed.get('title'):
- LOGGER.notice(parsed.feed.title)
+ LOGGER.info(parsed.feed.title)
else:
- LOGGER.notice(feed.url)
+ LOGGER.info(feed.url)
feed.etag = parsed.get('etag', 'foo')
modified = tuple(parsed.get('date_parsed', (1970, 1, 1)))[:6]
- LOGGER.notice("==========>", modified)
+ LOGGER.info("==========>", modified)
modified = datetime.datetime(*modified)
feed.last_modified = modified
feed.save()
@@ -176,14 +176,14 @@ class Planetoid(Command, Task):
# TODO log failure
return
for entry_data in parsed.entries:
- LOGGER.notice("=========================================")
+ LOGGER.info("=========================================")
date = entry_data.get('published_parsed', None)
if date is None:
date = entry_data.get('updated_parsed', None)
if date is None:
LOGGER.error("Can't parse date from:\n", entry_data)
return False
- LOGGER.notice("DATE:===>", date)
+ LOGGER.info("DATE:===>", date)
date = datetime.datetime(*(date[:6]))
title = "%s: %s" % (feed.name, entry_data.get('title', 'Sin título'))
content = entry_data.get('content', None)
@@ -195,9 +195,9 @@ class Planetoid(Command, Task):
content = entry_data.get('summary', 'Sin contenido')
guid = str(entry_data.get('guid', entry_data.link))
link = entry_data.link
- LOGGER.notice(repr([date, title]))
+ LOGGER.info(repr([date, title]))
e = list(Entry.select().where(Entry.guid == guid))
- LOGGER.notice(
+ LOGGER.info(
repr(dict(
date=date,
title=title,
diff --git a/nikola/plugins/command/serve.py b/nikola/plugins/command/serve.py
index 2dd15c1..f27d1f7 100644
--- a/nikola/plugins/command/serve.py
+++ b/nikola/plugins/command/serve.py
@@ -26,6 +26,7 @@
from __future__ import print_function
import os
+import webbrowser
try:
from BaseHTTPServer import HTTPServer
from SimpleHTTPServer import SimpleHTTPRequestHandler
@@ -37,7 +38,7 @@ from nikola.plugin_categories import Command
from nikola.utils import get_logger
-class CommandBuild(Command):
+class CommandServe(Command):
"""Start test server."""
name = "serve"
@@ -57,11 +58,19 @@ class CommandBuild(Command):
{
'name': 'address',
'short': 'a',
- 'long': '--address',
+ 'long': 'address',
'type': str,
'default': '127.0.0.1',
'help': 'Address to bind (default: 127.0.0.1)',
},
+ {
+ 'name': 'browser',
+ 'short': 'b',
+ 'long': 'browser',
+ 'type': bool,
+ 'default': False,
+ 'help': 'Open the test server in a web browser',
+ }
)
def _execute(self, options, args):
@@ -75,7 +84,11 @@ class CommandBuild(Command):
httpd = HTTPServer((options['address'], options['port']),
OurHTTPRequestHandler)
sa = httpd.socket.getsockname()
- self.logger.notice("Serving HTTP on {0} port {1} ...".format(*sa))
+ self.logger.info("Serving HTTP on {0} port {1} ...".format(*sa))
+ if options['browser']:
+ server_url = "http://{0}:{1}/".format(options['address'], options['port'])
+ self.logger.info("Opening {0} in the default web browser ...".format(server_url))
+ webbrowser.open(server_url)
httpd.serve_forever()
diff --git a/nikola/plugins/compile/asciidoc.py b/nikola/plugins/compile/asciidoc.py
index 12cb4bf..68f96d9 100644
--- a/nikola/plugins/compile/asciidoc.py
+++ b/nikola/plugins/compile/asciidoc.py
@@ -40,7 +40,7 @@ from nikola.utils import makedirs, req_missing
try:
from collections import OrderedDict
except ImportError:
- OrderedDict = None # NOQA
+ OrderedDict = dict # NOQA
class CompileAsciiDoc(PageCompiler):
@@ -57,11 +57,8 @@ class CompileAsciiDoc(PageCompiler):
if e.strreror == 'No such file or directory':
req_missing(['asciidoc'], 'build this site (compile with asciidoc)', python=False)
- def create_post(self, path, onefile=False, **kw):
- if OrderedDict is not None:
- metadata = OrderedDict()
- else:
- metadata = {}
+ def create_post(self, path, onefile=False, is_page=False, **kw):
+ metadata = OrderedDict()
metadata.update(self.default_metadata)
metadata.update(kw)
makedirs(os.path.dirname(path))
@@ -71,4 +68,4 @@ class CompileAsciiDoc(PageCompiler):
for k, v in metadata.items():
fd.write('.. {0}: {1}\n'.format(k, v))
fd.write("/////////////////////////////////////////////\n")
- fd.write("\nWrite your post here.")
+ fd.write("\nWrite your {0} here.".format('page' if is_page else 'post'))
diff --git a/nikola/plugins/compile/bbcode.py b/nikola/plugins/compile/bbcode.py
index 5345be3..0961ffe 100644
--- a/nikola/plugins/compile/bbcode.py
+++ b/nikola/plugins/compile/bbcode.py
@@ -40,7 +40,7 @@ from nikola.utils import makedirs, req_missing
try:
from collections import OrderedDict
except ImportError:
- OrderedDict = None # NOQA
+ OrderedDict = dict # NOQA
class CompileBbcode(PageCompiler):
@@ -66,11 +66,8 @@ class CompileBbcode(PageCompiler):
output = self.parser.format(data)
out_file.write(output)
- def create_post(self, path, onefile=False, **kw):
- if OrderedDict is not None:
- metadata = OrderedDict()
- else:
- metadata = {}
+ def create_post(self, path, onefile=False, is_page=False, **kw):
+ metadata = OrderedDict()
metadata.update(self.default_metadata)
metadata.update(kw)
makedirs(os.path.dirname(path))
@@ -80,4 +77,4 @@ class CompileBbcode(PageCompiler):
for k, v in metadata.items():
fd.write('.. {0}: {1}\n'.format(k, v))
fd.write('-->[/note]\n\n')
- fd.write("Write your post here.")
+ fd.write("Write your {0} here.".format('page' if is_page else 'post'))
diff --git a/nikola/plugins/compile/html.py b/nikola/plugins/compile/html.py
index 5352f00..09a9756 100644
--- a/nikola/plugins/compile/html.py
+++ b/nikola/plugins/compile/html.py
@@ -36,7 +36,7 @@ from nikola.utils import makedirs
try:
from collections import OrderedDict
except ImportError:
- OrderedDict = None # NOQA
+ OrderedDict = dict # NOQA
_META_SEPARATOR = '(' + os.linesep * 2 + '|' + ('\n' * 2) + '|' + ("\r\n" * 2) + ')'
@@ -56,11 +56,8 @@ class CompileHtml(PageCompiler):
out_file.write(data)
return True
- def create_post(self, path, onefile=False, **kw):
- if OrderedDict is not None:
- metadata = OrderedDict()
- else:
- metadata = {}
+ def create_post(self, path, onefile=False, is_page=False, **kw):
+ metadata = OrderedDict()
metadata.update(self.default_metadata)
metadata.update(kw)
makedirs(os.path.dirname(path))
@@ -70,4 +67,4 @@ class CompileHtml(PageCompiler):
for k, v in metadata.items():
fd.write('.. {0}: {1}\n'.format(k, v))
fd.write('-->\n\n')
- fd.write("\n<p>Write your post here.</p>\n")
+ fd.write("\n<p>Write your {0} here.</p>\n".format('page' if is_page else 'post'))
diff --git a/nikola/plugins/compile/ipynb/__init__.py b/nikola/plugins/compile/ipynb/__init__.py
index 5f2f0b3..2b1fd28 100644
--- a/nikola/plugins/compile/ipynb/__init__.py
+++ b/nikola/plugins/compile/ipynb/__init__.py
@@ -44,7 +44,7 @@ from nikola.utils import makedirs, req_missing
try:
from collections import OrderedDict
except ImportError:
- OrderedDict = None # NOQA
+ OrderedDict = dict # NOQA
class CompileIPynb(PageCompiler):
@@ -66,11 +66,8 @@ class CompileIPynb(PageCompiler):
(body, resources) = exportHtml.from_notebook_node(nb_json)
out_file.write(body)
- def create_post(self, path, onefile=False, **kw):
- if OrderedDict is not None:
- metadata = OrderedDict()
- else:
- metadata = {}
+ def create_post(self, path, onefile=False, is_page=False, **kw):
+ metadata = OrderedDict()
metadata.update(self.default_metadata)
metadata.update(kw)
d_name = os.path.dirname(path)
@@ -81,7 +78,7 @@ class CompileIPynb(PageCompiler):
metadata['date'], metadata['tags'],
metadata['link'],
metadata['description'], metadata['type'])))
- print("Your post's metadata is at: ", meta_path)
+ print("Your {0}'s metadata is at: {1}".format('page' if is_page else 'post', meta_path))
with codecs.open(path, "wb+", "utf8") as fd:
fd.write("""{
"metadata": {
diff --git a/nikola/plugins/compile/markdown/__init__.py b/nikola/plugins/compile/markdown/__init__.py
index 1376b11..d0fa66a 100644
--- a/nikola/plugins/compile/markdown/__init__.py
+++ b/nikola/plugins/compile/markdown/__init__.py
@@ -54,7 +54,7 @@ except ImportError:
try:
from collections import OrderedDict
except ImportError:
- OrderedDict = None # NOQA
+ OrderedDict = dict # NOQA
from nikola.plugin_categories import PageCompiler
from nikola.utils import makedirs, req_missing
@@ -81,11 +81,8 @@ class CompileMarkdown(PageCompiler):
output = markdown(data, self.extensions)
out_file.write(output)
- def create_post(self, path, onefile=False, **kw):
- if OrderedDict is not None:
- metadata = OrderedDict()
- else:
- metadata = {}
+ def create_post(self, path, onefile=False, is_page=False, **kw):
+ metadata = OrderedDict()
metadata.update(self.default_metadata)
metadata.update(kw)
makedirs(os.path.dirname(path))
@@ -95,4 +92,4 @@ class CompileMarkdown(PageCompiler):
for k, v in metadata.items():
fd.write('.. {0}: {1}\n'.format(k, v))
fd.write('-->\n\n')
- fd.write("Write your post here.")
+ fd.write("Write your {0} here.".format('page' if is_page else 'post'))
diff --git a/nikola/plugins/compile/misaka.py b/nikola/plugins/compile/misaka.py
index 8777ffc..4951c9f 100644
--- a/nikola/plugins/compile/misaka.py
+++ b/nikola/plugins/compile/misaka.py
@@ -40,7 +40,7 @@ except ImportError:
try:
from collections import OrderedDict
except ImportError:
- OrderedDict = None # NOQA
+ OrderedDict = dict # NOQA
gist_extension = None
podcast_extension = None
@@ -73,11 +73,8 @@ class CompileMisaka(PageCompiler):
output = misaka.html(data, extensions=self.ext)
out_file.write(output)
- def create_post(self, path, onefile=False, **kw):
- if OrderedDict is not None:
- metadata = OrderedDict()
- else:
- metadata = {}
+ def create_post(self, path, onefile=False, is_page=False, **kw):
+ metadata = OrderedDict()
metadata.update(self.default_metadata)
metadata.update(kw)
makedirs(os.path.dirname(path))
@@ -87,4 +84,4 @@ class CompileMisaka(PageCompiler):
for k, v in metadata.items():
fd.write('.. {0}: {1}\n'.format(k, v))
fd.write('-->\n\n')
- fd.write("\nWrite your post here.")
+ fd.write("\nWrite your {0} here.".format('page' if is_page else 'post'))
diff --git a/nikola/plugins/compile/pandoc.py b/nikola/plugins/compile/pandoc.py
index 57c7d71..654c7c8 100644
--- a/nikola/plugins/compile/pandoc.py
+++ b/nikola/plugins/compile/pandoc.py
@@ -40,7 +40,7 @@ from nikola.utils import req_missing, makedirs
try:
from collections import OrderedDict
except ImportError:
- OrderedDict = None # NOQA
+ OrderedDict = dict # NOQA
class CompilePandoc(PageCompiler):
@@ -56,11 +56,8 @@ class CompilePandoc(PageCompiler):
if e.strreror == 'No such file or directory':
req_missing(['pandoc'], 'build this site (compile with pandoc)', python=False)
- def create_post(self, path, onefile=False, **kw):
- if OrderedDict is not None:
- metadata = OrderedDict()
- else:
- metadata = {}
+ def create_post(self, path, onefile=False, is_page=False, **kw):
+ metadata = OrderedDict()
metadata.update(self.default_metadata)
metadata.update(kw)
makedirs(os.path.dirname(path))
@@ -70,4 +67,4 @@ class CompilePandoc(PageCompiler):
for k, v in metadata.items():
fd.write('.. {0}: {1}\n'.format(k, v))
fd.write('-->\n\n')
- fd.write("Write your post here.")
+ fd.write("Write your {0} here.".format('page' if is_page else 'post'))
diff --git a/nikola/plugins/compile/php.py b/nikola/plugins/compile/php.py
index 14b80e8..0a652a6 100644
--- a/nikola/plugins/compile/php.py
+++ b/nikola/plugins/compile/php.py
@@ -38,7 +38,7 @@ from nikola.utils import makedirs
try:
from collections import OrderedDict
except ImportError:
- OrderedDict = None # NOQA
+ OrderedDict = dict # NOQA
class CompilePhp(PageCompiler):
@@ -50,11 +50,8 @@ class CompilePhp(PageCompiler):
makedirs(os.path.dirname(dest))
shutil.copyfile(source, dest)
- def create_post(self, path, onefile=False, **kw):
- if OrderedDict is not None:
- metadata = OrderedDict()
- else:
- metadata = {}
+ def create_post(self, path, onefile=False, is_page=False, **kw):
+ metadata = OrderedDict()
metadata.update(self.default_metadata)
metadata.update(kw)
os.makedirs(os.path.dirname(path))
@@ -64,7 +61,7 @@ class CompilePhp(PageCompiler):
for k, v in metadata.items():
fd.write('.. {0}: {1}\n'.format(k, v))
fd.write('-->\n\n')
- fd.write("\n<p>Write your post here.</p>")
+ fd.write("\n<p>Write your {0} here.</p>".format('page' if is_page else 'post'))
def extension(self):
return ".php"
diff --git a/nikola/plugins/compile/rest/__init__.py b/nikola/plugins/compile/rest/__init__.py
index 50b37cf..9a4e19b 100644
--- a/nikola/plugins/compile/rest/__init__.py
+++ b/nikola/plugins/compile/rest/__init__.py
@@ -43,7 +43,7 @@ except ImportError:
try:
from collections import OrderedDict
except ImportError:
- OrderedDict = None # NOQA
+ OrderedDict = dict # NOQA
from nikola.plugin_categories import PageCompiler
from nikola.utils import get_logger, makedirs, req_missing
@@ -102,11 +102,8 @@ class CompileRest(PageCompiler):
else:
return False
- def create_post(self, path, onefile=False, **kw):
- if OrderedDict is not None:
- metadata = OrderedDict()
- else:
- metadata = {}
+ def create_post(self, path, onefile=False, is_page=False, **kw):
+ metadata = OrderedDict()
metadata.update(self.default_metadata)
metadata.update(kw)
makedirs(os.path.dirname(path))
@@ -114,7 +111,7 @@ class CompileRest(PageCompiler):
if onefile:
for k, v in metadata.items():
fd.write('.. {0}: {1}\n'.format(k, v))
- fd.write("\nWrite your post here.")
+ fd.write("\nWrite your {0} here.".format('page' if is_page else 'post'))
def set_site(self, site):
for plugin_info in site.plugin_manager.getPluginsOfCategory("RestExtension"):
@@ -174,6 +171,38 @@ class NikolaReader(docutils.readers.standalone.Reader):
def add_node(node, visit_function=None, depart_function=None):
+ """
+ Register a Docutils node class.
+ This function is completely optional. It is a same concept as
+ `Sphinx add_node function <http://sphinx-doc.org/ext/appapi.html#sphinx.application.Sphinx.add_node>`_.
+
+ For example::
+
+ class Plugin(RestExtension):
+
+ name = "rest_math"
+
+ def set_site(self, site):
+ self.site = site
+ directives.register_directive('math', MathDirective)
+ add_node(MathBlock, visit_Math, depart_Math)
+ return super(Plugin, self).set_site(site)
+
+ class MathDirective(Directive):
+ def run(self):
+ node = MathBlock()
+ return [node]
+
+ class Math(docutils.nodes.Element): pass
+
+ def visit_Math(self, node):
+ self.body.append(self.starttag(node, 'math'))
+
+ def depart_Math(self, node):
+ self.body.append('</math>')
+
+ For full example, you can refer to `Microdata plugin <http://plugins.getnikola.com/#microdata>`_
+ """
docutils.nodes._add_node_class_names([node.__name__])
if visit_function:
setattr(docutils.writers.html4css1.HTMLTranslator, 'visit_' + node.__name__, visit_function)
diff --git a/nikola/plugins/compile/rest/listing.py b/nikola/plugins/compile/rest/listing.py
index ecf885f..d70e02d 100644
--- a/nikola/plugins/compile/rest/listing.py
+++ b/nikola/plugins/compile/rest/listing.py
@@ -56,6 +56,18 @@ except ImportError: # docutils < 0.9 (Debian Sid For The Loss)
from nikola.plugin_categories import RestExtension
+# Add sphinx compatibility option
+CodeBlock.option_spec['linenos'] = directives.unchanged
+
+
+class FlexibleCodeBlock(CodeBlock):
+
+ def run(self):
+ if 'linenos' in self.options:
+ self.options['number-lines'] = self.options['linenos']
+ return super(FlexibleCodeBlock, self).run()
+CodeBlock = FlexibleCodeBlock
+
class Plugin(RestExtension):
@@ -71,6 +83,10 @@ class Plugin(RestExtension):
directives.register_directive('listing', Listing)
return super(Plugin, self).set_site(site)
+# Add sphinx compatibility option
+listing_spec = Include.option_spec
+listing_spec['linenos'] = directives.unchanged
+
class Listing(Include):
""" listing directive: create a highlighted block of code from a file in listings/
@@ -84,6 +100,7 @@ class Listing(Include):
has_content = False
required_arguments = 1
optional_arguments = 1
+ option_spec = listing_spec
def run(self):
fname = self.arguments.pop(0)
@@ -91,6 +108,8 @@ class Listing(Include):
fpath = os.path.join('listings', fname)
self.arguments.insert(0, fpath)
self.options['code'] = lang
+ if 'linenos' in self.options:
+ self.options['number-lines'] = self.options['linenos']
with codecs_open(fpath, 'rb+', 'utf8') as fileobject:
self.content = fileobject.read().splitlines()
self.state.document.settings.record_dependencies.add(fpath)
diff --git a/nikola/plugins/compile/textile.py b/nikola/plugins/compile/textile.py
index 73f35c0..1679831 100644
--- a/nikola/plugins/compile/textile.py
+++ b/nikola/plugins/compile/textile.py
@@ -41,7 +41,7 @@ from nikola.utils import makedirs, req_missing
try:
from collections import OrderedDict
except ImportError:
- OrderedDict = None # NOQA
+ OrderedDict = dict # NOQA
class CompileTextile(PageCompiler):
@@ -62,11 +62,8 @@ class CompileTextile(PageCompiler):
output = textile(data, head_offset=1)
out_file.write(output)
- def create_post(self, path, onefile=False, **kw):
- if OrderedDict is not None:
- metadata = OrderedDict()
- else:
- metadata = {}
+ def create_post(self, path, onefile=False, is_page=False, **kw):
+ metadata = OrderedDict()
metadata.update(self.default_metadata)
metadata.update(kw)
makedirs(os.path.dirname(path))
@@ -76,4 +73,4 @@ class CompileTextile(PageCompiler):
for k, v in metadata.items():
fd.write('.. {0}: {1}\n'.format(k, v))
fd.write('--></notextile>\n\n')
- fd.write("\nWrite your post here.")
+ fd.write("\nWrite your {0} here.".format('page' if is_page else 'post'))
diff --git a/nikola/plugins/compile/txt2tags.py b/nikola/plugins/compile/txt2tags.py
index 8c9724e..bb6afa5 100644
--- a/nikola/plugins/compile/txt2tags.py
+++ b/nikola/plugins/compile/txt2tags.py
@@ -43,7 +43,7 @@ except ImportError:
try:
from collections import OrderedDict
except ImportError:
- OrderedDict = None # NOQA
+ OrderedDict = dict # NOQA
from nikola.plugin_categories import PageCompiler
from nikola.utils import makedirs, req_missing
@@ -62,11 +62,8 @@ class CompileTxt2tags(PageCompiler):
cmd = ["-t", "html", "--no-headers", "--outfile", dest, source]
txt2tags(cmd)
- def create_post(self, path, onefile=False, **kw):
- if OrderedDict is not None:
- metadata = OrderedDict()
- else:
- metadata = {}
+ def create_post(self, path, onefile=False, is_page=False, **kw):
+ metadata = OrderedDict()
metadata.update(self.default_metadata)
metadata.update(kw)
makedirs(os.path.dirname(path))
@@ -76,4 +73,4 @@ class CompileTxt2tags(PageCompiler):
for k, v in metadata.items():
fd.write('.. {0}: {1}\n'.format(k, v))
fd.write("-->\n'''\n")
- fd.write("\nWrite your post here.")
+ fd.write("\nWrite your {0} here.".format('page' if is_page else 'post'))
diff --git a/nikola/plugins/compile/wiki.py b/nikola/plugins/compile/wiki.py
index 9a365fa..f4858c7 100644
--- a/nikola/plugins/compile/wiki.py
+++ b/nikola/plugins/compile/wiki.py
@@ -40,7 +40,7 @@ from nikola.plugin_categories import PageCompiler
try:
from collections import OrderedDict
except ImportError:
- OrderedDict = None # NOQA
+ OrderedDict = dict # NOQA
from nikola.utils import makedirs, req_missing
@@ -62,11 +62,8 @@ class CompileWiki(PageCompiler):
output = HtmlEmitter(document).emit()
out_file.write(output)
- def create_post(self, path, onefile=False, **kw):
- if OrderedDict is not None:
- metadata = OrderedDict()
- else:
- metadata = {}
+ def create_post(self, path, onefile=False, is_page=False, **kw):
+ metadata = OrderedDict()
metadata.update(self.default_metadata)
metadata.update(kw)
makedirs(os.path.dirname(path))
@@ -75,4 +72,4 @@ class CompileWiki(PageCompiler):
'one-file format is not possible, use the -2 '
'option.')
with codecs.open(path, "wb+", "utf8") as fd:
- fd.write("Write your post here.")
+ fd.write("Write your {0} here.".format('page' if is_page else 'post'))
diff --git a/nikola/plugins/loghandler/stderr.py b/nikola/plugins/loghandler/stderr.py
index 75acffc..fdc892e 100644
--- a/nikola/plugins/loghandler/stderr.py
+++ b/nikola/plugins/loghandler/stderr.py
@@ -26,10 +26,10 @@
from nikola.plugin_categories import SignalHandler
from blinker import signal
-import logbook
import os
from nikola import DEBUG
+from nikola.utils import ColorfulStderrHandler
class StderrHandler(SignalHandler):
@@ -40,7 +40,7 @@ class StderrHandler(SignalHandler):
"""Attach the handler to the logger."""
conf = self.site.config.get('LOGGING_HANDLERS').get('stderr')
if conf or os.getenv('NIKOLA_DEBUG'):
- self.site.loghandlers.append(logbook.StderrHandler(
+ self.site.loghandlers.append(ColorfulStderrHandler(
level='DEBUG' if DEBUG else conf.get('loglevel', 'WARNING').upper(),
format_string=u'[{record.time:%Y-%m-%dT%H:%M:%SZ}] {record.level_name}: {record.channel}: {record.message}'
))
diff --git a/nikola/plugins/task/build_less.py b/nikola/plugins/task/build_less.py
index 14a53f9..a672282 100644
--- a/nikola/plugins/task/build_less.py
+++ b/nikola/plugins/task/build_less.py
@@ -46,22 +46,34 @@ class BuildLess(Task):
def gen_tasks(self):
"""Generate CSS out of LESS sources."""
self.compiler_name = self.site.config['LESS_COMPILER']
+ self.compiler_options = self.site.config['LESS_OPTIONS']
kw = {
'cache_folder': self.site.config['CACHE_FOLDER'],
'themes': self.site.THEMES,
}
+ tasks = {}
# Find where in the theme chain we define the LESS targets
# There can be many *.less in the folder, but we only will build
# the ones listed in less/targets
- targets_path = utils.get_asset_path(os.path.join(self.sources_folder, "targets"), self.site.THEMES)
+ if os.path.isfile(os.path.join(self.sources_folder, "targets")):
+ targets_path = os.path.join(self.sources_folder, "targets")
+ else:
+ targets_path = utils.get_asset_path(os.path.join(self.sources_folder, "targets"), self.site.THEMES)
try:
with codecs.open(targets_path, "rb", "utf-8") as inf:
targets = [x.strip() for x in inf.readlines()]
except Exception:
targets = []
+ for task in utils.copy_tree(self.sources_folder, os.path.join(kw['cache_folder'], self.sources_folder)):
+ if task['name'] in tasks:
+ continue
+ task['basename'] = 'prepare_less_sources'
+ tasks[task['name']] = task
+ yield task
+
for theme_name in kw['themes']:
src = os.path.join(utils.get_theme_path(theme_name), self.sources_folder)
for task in utils.copy_tree(src, os.path.join(kw['cache_folder'], self.sources_folder)):
@@ -82,7 +94,7 @@ class BuildLess(Task):
src = os.path.join(kw['cache_folder'], self.sources_folder, target)
run_in_shell = sys.platform == 'win32'
try:
- compiled = subprocess.check_output([self.compiler_name, src], shell=run_in_shell)
+ compiled = subprocess.check_output([self.compiler_name] + self.compiler_options + [src], shell=run_in_shell)
except OSError:
utils.req_missing([self.compiler_name],
'build LESS files (and use this theme)',
diff --git a/nikola/plugins/task/build_sass.py b/nikola/plugins/task/build_sass.py
index 7575505..becc843 100644
--- a/nikola/plugins/task/build_sass.py
+++ b/nikola/plugins/task/build_sass.py
@@ -47,26 +47,41 @@ class BuildSass(Task):
"""Generate CSS out of Sass sources."""
self.logger = utils.get_logger('build_sass', self.site.loghandlers)
self.compiler_name = self.site.config['SASS_COMPILER']
+ self.compiler_options = self.site.config['SASS_OPTIONS']
kw = {
'cache_folder': self.site.config['CACHE_FOLDER'],
'themes': self.site.THEMES,
}
+ tasks = {}
# Find where in the theme chain we define the Sass targets
# There can be many *.sass/*.scss in the folder, but we only
# will build the ones listed in sass/targets
- targets_path = utils.get_asset_path(os.path.join(self.sources_folder, "targets"), self.site.THEMES)
+ if os.path.isfile(os.path.join(self.sources_folder, "targets")):
+ targets_path = os.path.join(self.sources_folder, "targets")
+ else:
+ targets_path = utils.get_asset_path(os.path.join(self.sources_folder, "targets"), self.site.THEMES)
try:
with codecs.open(targets_path, "rb", "utf-8") as inf:
targets = [x.strip() for x in inf.readlines()]
except Exception:
targets = []
+ for task in utils.copy_tree(self.sources_folder, os.path.join(kw['cache_folder'], self.sources_folder)):
+ if task['name'] in tasks:
+ continue
+ task['basename'] = 'prepare_sass_sources'
+ tasks[task['name']] = task
+ yield task
+
for theme_name in kw['themes']:
src = os.path.join(utils.get_theme_path(theme_name), self.sources_folder)
for task in utils.copy_tree(src, os.path.join(kw['cache_folder'], self.sources_folder)):
+ if task['name'] in tasks:
+ continue
task['basename'] = 'prepare_sass_sources'
+ tasks[task['name']] = task
yield task
# Build targets and write CSS files
@@ -83,7 +98,7 @@ class BuildSass(Task):
run_in_shell = sys.platform == 'win32'
src = os.path.join(kw['cache_folder'], self.sources_folder, target)
try:
- compiled = subprocess.check_output([self.compiler_name, src], shell=run_in_shell)
+ compiled = subprocess.check_output([self.compiler_name] + self.compiler_options + [src], shell=run_in_shell)
except OSError:
utils.req_missing([self.compiler_name],
'build Sass files (and use this theme)',
diff --git a/nikola/plugins/task/bundles.py b/nikola/plugins/task/bundles.py
index b035b97..fcfaf42 100644
--- a/nikola/plugins/task/bundles.py
+++ b/nikola/plugins/task/bundles.py
@@ -87,8 +87,8 @@ class BuildBundles(LateTask):
output_path = os.path.join(kw['output_folder'], name)
dname = os.path.dirname(name)
file_dep = [os.path.join(kw['output_folder'], dname, fname)
- for fname in files]
- file_dep = filter(os.path.isfile, file_dep) # removes missing files
+ for fname in files if
+ utils.get_asset_path(fname, self.site.THEMES, self.site.config['FILES_FOLDERS'])]
task = {
'file_dep': list(file_dep),
'task_dep': ['copy_assets'],
diff --git a/nikola/plugins/task/copy_assets.py b/nikola/plugins/task/copy_assets.py
index 21f1f85..93b7fb3 100644
--- a/nikola/plugins/task/copy_assets.py
+++ b/nikola/plugins/task/copy_assets.py
@@ -75,8 +75,8 @@ class CopyAssets(Task):
formatter = get_formatter_by_name('html', style=kw["code_color_scheme"])
utils.makedirs(os.path.dirname(code_css_path))
with codecs.open(code_css_path, 'wb+', 'utf8') as outf:
- outf.write(formatter.get_style_defs('.code'))
- outf.write("table.codetable { width: 100%;} td.linenos {text-align: right; width: 4em;}")
+ outf.write(formatter.get_style_defs(['pre.code', 'div.code pre']))
+ outf.write("\ntable.codetable { width: 100%;} td.linenos {text-align: right; width: 4em;}\n")
task = {
'basename': self.name,
diff --git a/nikola/plugins/task/galleries.py b/nikola/plugins/task/galleries.py
index 6977eab..880d47c 100644
--- a/nikola/plugins/task/galleries.py
+++ b/nikola/plugins/task/galleries.py
@@ -176,6 +176,7 @@ class Galleries(Task):
thumbs = ['.thumbnail'.join(os.path.splitext(p)) for p in image_list]
thumbs = [os.path.join(self.kw['output_folder'], t) for t in thumbs]
+ dest_img_list = [os.path.join(self.kw['output_folder'], t) for t in image_list]
folders = []
@@ -193,7 +194,8 @@ class Galleries(Task):
context["folders"] = folders
context["crumbs"] = crumbs
context["permalink"] = self.site.link(
- "gallery", os.path.basename(gallery), lang)
+ "gallery", os.path.basename(
+ os.path.relpath(gallery, self.kw['gallery_path'])), lang)
# FIXME: use kw
context["enable_comments"] = (
self.site.config["COMMENTS_IN_GALLERIES"])
@@ -219,7 +221,7 @@ class Galleries(Task):
template_name,
dst,
context,
- image_list,
+ dest_img_list,
thumbs,
file_dep))],
'clean': True,
diff --git a/nikola/plugins/task/listings.py b/nikola/plugins/task/listings.py
index d8ed43b..86be6c4 100644
--- a/nikola/plugins/task/listings.py
+++ b/nikola/plugins/task/listings.py
@@ -55,7 +55,7 @@ class Listings(Task):
}
# Things to ignore in listings
- ignored_extensions = (".pyc",)
+ ignored_extensions = (".pyc", ".pyo")
def render_listing(in_name, out_name, folders=[], files=[]):
if in_name:
diff --git a/nikola/plugins/task/localsearch/files/assets/css/img/search.png b/nikola/plugins/task/localsearch/files/assets/css/img/search.png
index 9ab0f2c..9ab0f2c 100755..100644
--- a/nikola/plugins/task/localsearch/files/assets/css/img/search.png
+++ b/nikola/plugins/task/localsearch/files/assets/css/img/search.png
Binary files differ
diff --git a/nikola/plugins/task/localsearch/files/assets/css/tipuesearch.css b/nikola/plugins/task/localsearch/files/assets/css/tipuesearch.css
index 2230193..2230193 100755..100644
--- a/nikola/plugins/task/localsearch/files/assets/css/tipuesearch.css
+++ b/nikola/plugins/task/localsearch/files/assets/css/tipuesearch.css
diff --git a/nikola/plugins/task/localsearch/files/tipue_search.html b/nikola/plugins/task/localsearch/files/tipue_search.html
index 789fbe5..789fbe5 100755..100644
--- a/nikola/plugins/task/localsearch/files/tipue_search.html
+++ b/nikola/plugins/task/localsearch/files/tipue_search.html
diff --git a/nikola/plugins/task/rss.py b/nikola/plugins/task/rss.py
index e5f7548..9e4204c 100644
--- a/nikola/plugins/task/rss.py
+++ b/nikola/plugins/task/rss.py
@@ -58,6 +58,11 @@ class GenerateRSS(Task):
"feed_length": self.site.config['FEED_LENGTH'],
}
self.site.scan_posts()
+ # Check for any changes in the state of use_in_feeds for any post.
+ # Issue #934
+ kw['use_in_feeds_status'] = ''.join(
+ ['T' if x.use_in_feeds else 'F' for x in self.site.timeline]
+ )
yield self.group_task()
for lang in kw["translations"]:
output_name = os.path.join(kw['output_folder'],
diff --git a/nikola/plugins/task/sitemap/__init__.py b/nikola/plugins/task/sitemap/__init__.py
index 0164000..147bd50 100644
--- a/nikola/plugins/task/sitemap/__init__.py
+++ b/nikola/plugins/task/sitemap/__init__.py
@@ -144,26 +144,35 @@ class Sitemap(LateTask):
def write_sitemap():
# Have to rescan, because files may have been added between
# task dep scanning and task execution
- scan_locs()
with codecs.open(sitemap_path, 'wb+', 'utf8') as outf:
outf.write(header)
for k in sorted(locs.keys()):
outf.write(locs[k])
outf.write("</urlset>")
- # Other tasks can depend on this output, instead of having
- # to scan locations.
+
+ # Yield a task to calculate the dependencies of the sitemap
+ # Other tasks can depend on this output, instead of having
+ # to scan locations.
+ def scan_locs_task():
+ scan_locs()
return {'locations': list(locs.keys())}
- scan_locs()
+ yield {
+ "basename": "_scan_locs",
+ "name": "sitemap",
+ "actions": [(scan_locs_task)]
+ }
+
yield self.group_task()
task = {
"basename": "sitemap",
"name": sitemap_path,
"targets": [sitemap_path],
"actions": [(write_sitemap,)],
- "uptodate": [config_changed({1: kw, 2: locs})],
+ "uptodate": [config_changed(kw)],
"clean": True,
"task_dep": ["render_site"],
+ "calc_dep": ["_scan_locs:sitemap"],
}
yield task
diff --git a/nikola/plugins/task/tags.py b/nikola/plugins/task/tags.py
index a2444ec..f6b8234 100644
--- a/nikola/plugins/task/tags.py
+++ b/nikola/plugins/task/tags.py
@@ -311,9 +311,17 @@ class RenderTags(Task):
self.site.config['INDEX_FILE']] if _f]
def tag_path(self, name, lang):
- return [_f for _f in [self.site.config['TRANSLATIONS'][lang],
- self.site.config['TAG_PATH'], self.slugify_name(name) + ".html"] if
- _f]
+ if self.site.config['PRETTY_URLS']:
+ return [_f for _f in [
+ self.site.config['TRANSLATIONS'][lang],
+ self.site.config['TAG_PATH'],
+ self.slugify_name(name),
+ self.site.config['INDEX_FILE']] if _f]
+ else:
+ return [_f for _f in [
+ self.site.config['TRANSLATIONS'][lang],
+ self.site.config['TAG_PATH'],
+ self.slugify_name(name) + ".html"] if _f]
def tag_rss_path(self, name, lang):
return [_f for _f in [self.site.config['TRANSLATIONS'][lang],
diff --git a/nikola/plugins/template/jinja.py b/nikola/plugins/template/jinja.py
index 17c33d4..f14adfe 100644
--- a/nikola/plugins/template/jinja.py
+++ b/nikola/plugins/template/jinja.py
@@ -61,6 +61,11 @@ class JinjaTemplates(TemplateSystem):
self.lookup.loader = jinja2.FileSystemLoader(directories,
encoding='utf-8')
+ def set_site(self, site):
+ """Sets the site."""
+ self.site = site
+ self.lookup.filters.update(self.site.config['TEMPLATE_FILTERS'])
+
def render_template(self, template_name, output_name, context):
"""Render the template into output_name using context."""
if jinja2 is None:
diff --git a/nikola/plugins/template/mako.py b/nikola/plugins/template/mako.py
index 45f4335..5a23230 100644
--- a/nikola/plugins/template/mako.py
+++ b/nikola/plugins/template/mako.py
@@ -49,6 +49,7 @@ class MakoTemplates(TemplateSystem):
lookup = None
cache = {}
+ filters = {}
def get_deps(self, filename):
text = util.read_file(filename)
@@ -81,6 +82,11 @@ class MakoTemplates(TemplateSystem):
module_directory=cache_dir,
output_encoding='utf-8')
+ def set_site(self, site):
+ """Sets the site."""
+ self.site = site
+ self.filters.update(self.site.config['TEMPLATE_FILTERS'])
+
def render_template(self, template_name, output_name, context):
"""Render the template into output_name using context."""
context['striphtml'] = striphtml
@@ -95,6 +101,8 @@ class MakoTemplates(TemplateSystem):
def render_template_to_string(self, template, context):
""" Render template to a string using context. """
+ context = context.update(self.filters)
+
return Template(template).render(**context)
def template_deps(self, template_name):