render_markdown_path: Handle absolute template paths properly.

One disadvantage of relying on Jinja2 to load all templates is that it
only searches a finite set of pre-configured template directories.
Unfortunately, that breaks when someone tries to enable a custom
privacy or terms page and has the corresponding template in a
directory outside of Jinja2's recognized directories (for instance, it
won't find `/etc/zulip/terms.md`, the recommended path).

This commit makes it so that render_markdown_path can be more
sensible about pure Markdown files and load templates with
absolute paths directly without relying on Jinja2, if need be.
This commit is contained in:
Eeshan Garg 2018-08-08 22:16:32 -02:30 committed by Tim Abbott
parent 3e885e52b2
commit 0a99fa2fd6
3 changed files with 30 additions and 7 deletions

View File

@ -21,7 +21,7 @@
<div class="padded-content">
<div class="inner-content">
{% if privacy_policy %}
{{ render_markdown_path(privacy_policy) }}
{{ render_markdown_path(privacy_policy, pure_markdown=True) }}
{% else %}
{% trans %}
This installation of Zulip does not have a configured privacy policy.

View File

@ -20,7 +20,7 @@
<div class="padded-content">
<div class="inner-content">
{% if terms_of_service %}
{{ render_markdown_path(terms_of_service) }}
{{ render_markdown_path(terms_of_service, pure_markdown=True) }}
{% else %}
{% trans %}
This installation of Zulip does not have a configured terms of service.

View File

@ -10,6 +10,7 @@ import markdown_include.include
from django.conf import settings
from django.template import Library, engines, loader
from django.utils.safestring import mark_safe
from jinja2.exceptions import TemplateNotFound
import zerver.lib.bugdown.fenced_code
import zerver.lib.bugdown.api_arguments_table_generator
@ -71,7 +72,9 @@ docs_without_macros = [
# the results when called if none of the arguments are unhashable.
@ignore_unhashable_lru_cache(512)
@register.filter(name='render_markdown_path', is_safe=True)
def render_markdown_path(markdown_file_path: str, context: Optional[Dict[Any, Any]]=None) -> str:
def render_markdown_path(markdown_file_path: str,
context: Optional[Dict[Any, Any]]=None,
pure_markdown: Optional[bool]=False) -> str:
"""Given a path to a markdown file, return the rendered html.
Note that this assumes that any HTML in the markdown file is
@ -114,7 +117,27 @@ def render_markdown_path(markdown_file_path: str, context: Optional[Dict[Any, An
md_engine.reset()
jinja = engines['Jinja2']
markdown_string = jinja.env.loader.get_source(jinja.env, markdown_file_path)[0]
html = md_engine.convert(markdown_string)
html_template = jinja.from_string(html)
return mark_safe(html_template.render(context))
if pure_markdown:
# For files such as /etc/zulip/terms.md where we don't intend
# to use Jinja2 template variables, we still try to load the
# template using Jinja2 (in case the file path isn't absolute
# and does happen to be in Jinja's recognized template
# directories), and if that fails, we try to load it directly
# from disk.
try:
markdown_string = jinja.env.loader.get_source(jinja.env, markdown_file_path)[0]
except TemplateNotFound:
with open(markdown_file_path) as fp:
markdown_string = fp.read()
rendered_html = md_engine.convert(markdown_string)
else:
# By default, we do both Jinja2 templating and markdown
# processing on the file, to make it easy to use both Jinja2
# context variables and markdown includes in the file.
markdown_string = jinja.env.loader.get_source(jinja.env, markdown_file_path)[0]
html = md_engine.convert(markdown_string)
rendered_html = jinja.from_string(html).render(context)
return mark_safe(rendered_html)