refactor: Jinja hooks

- Rename hook from "jenv" to "jinja"
- You can now pass the path to the module and all of the methods in that
module will be added as methods
- You can also pass module path of a method

BREAKING CHANGE: Previous use of "jenv" hook won't work anymore
This commit is contained in:
Faris Ansari 2021-04-17 07:31:24 +05:30
parent 85ad358a83
commit 1fd08d3960
3 changed files with 102 additions and 84 deletions

View file

@ -130,6 +130,10 @@ has_website_permission = {
"Address": "frappe.contacts.doctype.address.address.has_website_permission"
}
jinja = {
"methods": "frappe.utils.jinja_globals"
}
standard_queries = {
"User": "frappe.core.doctype.user.user.user_query"
}

View file

@ -18,13 +18,10 @@ def get_jenv():
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,
'web_block': web_block
})
methods, filters = get_jinja_hooks('methods')
jenv.globals.update(methods or {})
jenv.filters.update(filters or {})
frappe.local.jenv = jenv
@ -143,88 +140,34 @@ def set_filters(jenv):
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'''
def get_jinja_hooks():
"""Returns a tuple of (methods, filters) each containing a dict of method name and method definition pair."""
import frappe
out = {}
if not getattr(frappe.local, "site", None):
return (None, None)
from types import FunctionType, ModuleType
from inspect import getmembers, isfunction
def get_obj_dict_from_paths(object_paths):
out = {}
for obj_path in object_paths:
obj = frappe.get_attr(obj_path)
if isinstance(obj, ModuleType):
functions = getmembers(obj, isfunction)
for function_name, function in functions:
out[function_name] = function
elif isinstance(obj, FunctionType):
function_name = obj.__name__
out[function_name] = obj
return out
values = frappe.get_hooks("jenv", {}).get(customization_type)
if not values:
return out
values = frappe.get_hooks("jinja")
methods, filters = values.get("methods", []), values.get("filters", [])
for value in values:
fn_name, fn_string = value.split(":")
out[fn_name] = frappe.get_attr(fn_string)
method_dict = get_obj_dict_from_paths(methods)
filter_dict = get_obj_dict_from_paths(filters)
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_block(template, values=None, **kwargs):
options = {"template": template, "values": values}
options.update(kwargs)
return web_blocks([options])
def web_blocks(blocks):
from frappe import throw, _dict
from frappe.website.doctype.web_page.web_page import get_web_blocks_html
web_blocks = []
for block in blocks:
if not block.get('template'):
throw('Web Template is not specified')
doc = _dict({
'doctype': 'Web Page Block',
'web_template': block['template'],
'web_template_values': block.get('values', {}),
'add_top_padding': 1,
'add_bottom_padding': 1,
'add_container': 1,
'hide_block': 0,
'css_class': ''
})
doc.update(block)
web_blocks.append(doc)
out = get_web_blocks_html(web_blocks)
html = out.html
for script in out.scripts:
html += '<script>{}</script>'.format(script)
return html
return method_dict, filter_dict

View file

@ -0,0 +1,71 @@
# Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and Contributors
# MIT License. See license.txt
from __future__ import unicode_literals
from frappe.utils.jinja import get_jenv
import frappe
def resolve_class(classes):
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:
return ""
return get_jenv().from_string(html).render(context)
def web_block(template, values=None, **kwargs):
options = {"template": template, "values": values}
options.update(kwargs)
return web_blocks([options])
def web_blocks(blocks):
from frappe import throw, _dict
from frappe.website.doctype.web_page.web_page import get_web_blocks_html
web_blocks = []
for block in blocks:
if not block.get("template"):
throw("Web Template is not specified")
doc = _dict(
{
"doctype": "Web Page Block",
"web_template": block["template"],
"web_template_values": block.get("values", {}),
"add_top_padding": 1,
"add_bottom_padding": 1,
"add_container": 1,
"hide_block": 0,
"css_class": "",
}
)
doc.update(block)
web_blocks.append(doc)
out = get_web_blocks_html(web_blocks)
html = out.html
for script in out.scripts:
html += "<script>{}</script>".format(script)
return html