seitime-frappe/frappe/utils/jinja.py
2020-05-20 19:07:05 +05:30

219 lines
5.6 KiB
Python

# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
# MIT License. See license.txt
from __future__ import unicode_literals
def get_jenv():
import frappe
from frappe.utils.safe_exec import get_safe_globals
if not getattr(frappe.local, 'jenv', None):
from jinja2 import DebugUndefined
from jinja2.sandbox import SandboxedEnvironment
# frappe will be loaded last, so app templates will get precedence
jenv = SandboxedEnvironment(
loader=get_jloader(),
undefined=DebugUndefined
)
set_filters(jenv)
jenv.globals.update(get_safe_globals())
jenv.globals.update(get_jenv_customization('methods'))
jenv.globals.update({
'resolve_class': resolve_class,
'inspect': inspect,
'web_blocks': web_blocks
})
frappe.local.jenv = jenv
return frappe.local.jenv
def get_template(path):
return get_jenv().get_template(path)
def get_email_from_template(name, args):
from jinja2 import TemplateNotFound
args = args or {}
try:
message = get_template('templates/emails/' + name + '.html').render(args)
except TemplateNotFound as e:
raise e
try:
text_content = get_template('templates/emails/' + name + '.txt').render(args)
except TemplateNotFound:
text_content = None
return (message, text_content)
def validate_template(html):
"""Throws exception if there is a syntax error in the Jinja Template"""
import frappe
from jinja2 import TemplateSyntaxError
jenv = get_jenv()
try:
jenv.from_string(html)
except TemplateSyntaxError as e:
frappe.msgprint('Line {}: {}'.format(e.lineno, e.message))
frappe.throw(frappe._("Syntax error in template"))
def render_template(template, context, is_path=None, safe_render=True):
'''Render a template using Jinja
:param template: path or HTML containing the jinja template
:param context: dict of properties to pass to the template
:param is_path: (optional) assert that the `template` parameter is a path
:param safe_render: (optional) prevent server side scripting via jinja templating
'''
from frappe import get_traceback, throw
from jinja2 import TemplateError
if not template:
return ""
if (is_path or guess_is_path(template)):
return get_jenv().get_template(template).render(context)
else:
if safe_render and ".__" in template:
throw("Illegal template")
try:
return get_jenv().from_string(template).render(context)
except TemplateError:
throw(title="Jinja Template Error", msg="<pre>{template}</pre><pre>{tb}</pre>".format(template=template, tb=get_traceback()))
def guess_is_path(template):
# template can be passed as a path or content
# if its single line and ends with a html, then its probably a path
if not '\n' in template and '.' in template:
extn = template.rsplit('.')[-1]
if extn in ('html', 'css', 'scss', 'py'):
return True
return False
def get_jloader():
import frappe
if not getattr(frappe.local, 'jloader', None):
from jinja2 import ChoiceLoader, PackageLoader, PrefixLoader
if frappe.local.flags.in_setup_help:
apps = ['frappe']
else:
apps = frappe.get_hooks('template_apps')
if not apps:
apps = frappe.local.flags.web_pages_apps or frappe.get_installed_apps(sort=True)
apps.reverse()
if not "frappe" in apps:
apps.append('frappe')
frappe.local.jloader = ChoiceLoader(
# search for something like app/templates/...
[PrefixLoader(dict(
(app, PackageLoader(app, ".")) for app in apps
))]
# search for something like templates/...
+ [PackageLoader(app, ".") for app in apps]
)
return frappe.local.jloader
def set_filters(jenv):
import frappe
from frappe.utils import global_date_format, cint, cstr, flt, markdown
from frappe.website.utils import get_shade, abs_url
jenv.filters["global_date_format"] = global_date_format
jenv.filters["markdown"] = markdown
jenv.filters["json"] = frappe.as_json
jenv.filters["get_shade"] = get_shade
jenv.filters["len"] = len
jenv.filters["int"] = cint
jenv.filters["str"] = cstr
jenv.filters["flt"] = flt
jenv.filters["abs_url"] = abs_url
if frappe.flags.in_setup_help:
return
jenv.filters.update(get_jenv_customization('filters'))
def get_jenv_customization(customization_type):
'''Returns a dict with filter/method name as key and definition as value'''
import frappe
out = {}
if not getattr(frappe.local, "site", None):
return out
values = frappe.get_hooks("jenv", {}).get(customization_type)
if not values:
return out
for value in values:
fn_name, fn_string = value.split(":")
out[fn_name] = frappe.get_attr(fn_string)
return out
def resolve_class(classes):
import frappe
if classes is None:
return ''
if isinstance(classes, frappe.string_types):
return classes
if isinstance(classes, (list, tuple)):
return ' '.join([resolve_class(c) for c in classes]).strip()
if isinstance(classes, dict):
return ' '.join([classname for classname in classes if classes[classname]]).strip()
return classes
def inspect(var, render=True):
context = { "var": var }
if render:
html = "<pre>{{ var | pprint | e }}</pre>"
else:
html = ""
return get_jenv().from_string(html).render(context)
def web_blocks(blocks):
from frappe import get_doc
from frappe.website.doctype.web_page.web_page import get_web_blocks_html
web_blocks = []
for block in blocks:
doc = {
'doctype': 'Web Page Block',
'web_template': block['template'],
'web_template_values': block['values'],
'add_top_padding': 1,
'add_bottom_padding': 1,
'add_container': 1,
'hide_block': 0,
'css_class': ''
}
doc.update(block)
web_blocks.append(get_doc(doc))
out = get_web_blocks_html(web_blocks)
html = out.html
for script in out.scripts:
html += '<script>{}</script>'.format(script)
return html