Navigation Menu

Skip to content

Commit

Permalink
Merge pull request #2546 from getnikola/allow-html-fragment-output
Browse files Browse the repository at this point in the history
Allowing to write HTML fragments instead of only whole documents.
  • Loading branch information
felixfontein committed Oct 26, 2016
2 parents 163f873 + 3a3cd7c commit 9a09d5d
Show file tree
Hide file tree
Showing 2 changed files with 22 additions and 7 deletions.
2 changes: 2 additions & 0 deletions CHANGES.txt
Expand Up @@ -10,6 +10,8 @@ Bugfixes
Features
--------

* ``render_template`` and ``generic_renderer`` can now create HTML
fragments.
* Allow posts to set custom ``URL_TYPE`` by using the ``url_type``
meta tag (useful for HTML fragments inserted using JavaScript)
* Plugins can depend on other plugins being installed (Issue #2533)
Expand Down
27 changes: 20 additions & 7 deletions nikola/nikola.py
Expand Up @@ -1287,7 +1287,7 @@ def get_compiler(self, source_name):

return compiler

def render_template(self, template_name, output_name, context, url_type=None):
def render_template(self, template_name, output_name, context, url_type=None, is_fragment=False):
"""Render a template with the global context.
If ``output_name`` is None, will return a string and all URL
Expand All @@ -1298,6 +1298,9 @@ def render_template(self, template_name, output_name, context, url_type=None):
The argument ``url_type`` allows to override the ``URL_TYPE``
configuration.
If ``is_fragment`` is set to ``True``, a HTML fragment will
be rendered and not a whole HTML document.
"""
local_context = {}
local_context["template_name"] = template_name
Expand Down Expand Up @@ -1334,9 +1337,18 @@ def render_template(self, template_name, output_name, context, url_type=None):

utils.makedirs(os.path.dirname(output_name))
parser = lxml.html.HTMLParser(remove_blank_text=True)
doc = lxml.html.document_fromstring(data, parser)
if is_fragment:
doc = lxml.html.fragment_fromstring(data, parser)
else:
doc = lxml.html.document_fromstring(data, parser)
self.rewrite_links(doc, src, context['lang'], url_type)
data = b'<!DOCTYPE html>\n' + lxml.html.tostring(doc, encoding='utf8', method='html', pretty_print=True)
if is_fragment:
# doc.text contains text before the first HTML, or None if there was no text
# The text after HTML elements is added by tostring() (because its implicit
# argument with_tail has default value True).
data = (doc.text or '').encode('utf-8') + b''.join([lxml.html.tostring(child, encoding='utf-8', method='html') for child in doc.iterchildren()])
else:
data = lxml.html.tostring(doc, encoding='utf8', method='html', pretty_print=True, doctype='<!DOCTYPE html>')
with open(output_name, "wb+") as post_file:
post_file.write(data)

Expand All @@ -1346,7 +1358,7 @@ def rewrite_links(self, doc, src, lang, url_type=None):
doc.rewrite_links(lambda dst: self.url_replacer(src, dst, lang, url_type), resolve_base_href=False)

# lxml ignores srcset in img and source elements, so do that by hand
objs = list(doc.xpath('(*//img|*//source)'))
objs = list(doc.xpath('(//img|//source)'))
for obj in objs:
if 'srcset' in obj.attrib:
urls = [u.strip() for u in obj.attrib['srcset'].split(',')]
Expand Down Expand Up @@ -2004,7 +2016,7 @@ def scan_posts(self, really=False, ignore_quit=False, quiet=False):
sys.exit(1)
signal('scanned').send(self)

def generic_renderer(self, lang, output_name, template_name, filters, file_deps=None, uptodate_deps=None, context=None, context_deps_remove=None, post_deps_dict=None, url_type=None):
def generic_renderer(self, lang, output_name, template_name, filters, file_deps=None, uptodate_deps=None, context=None, context_deps_remove=None, post_deps_dict=None, url_type=None, is_fragment=False):
"""Helper function for rendering pages and post lists and other related pages.
lang is the current language.
Expand All @@ -2016,7 +2028,8 @@ def generic_renderer(self, lang, output_name, template_name, filters, file_deps=
context (optional) a dict used as a basis for the template context. The lang parameter will always be added.
context_deps_remove (optional) is a list of keys to remove from the context after using it as an uptodate dependency. This should name all keys containing non-trivial Python objects; they can be replaced by adding JSON-style dicts in post_deps_dict.
post_deps_dict (optional) is a dict merged into the copy of context which is used as an uptodate dependency.
url_type (optional) allows to override the ``URL_TYPE`` configuration
url_type (optional) allows to override the ``URL_TYPE`` configuration.
is_fragment (optional) allows to write a HTML fragment instead of a HTML document.
"""
utils.LocaleBorg().set_locale(lang)

Expand Down Expand Up @@ -2050,7 +2063,7 @@ def generic_renderer(self, lang, output_name, template_name, filters, file_deps=
'targets': [output_name],
'file_dep': file_deps,
'actions': [(self.render_template, [template_name, output_name,
context, url_type])],
context, url_type, is_fragment])],
'clean': True,
'uptodate': [config_changed(deps_dict, 'nikola.nikola.Nikola.generic_renderer')] + ([] if uptodate_deps is None else uptodate_deps)
}
Expand Down

0 comments on commit 9a09d5d

Please sign in to comment.