Você não pode selecionar mais de 25 tópicos Os tópicos devem começar com uma letra ou um número, podem incluir traços ('-') e podem ter até 35 caracteres.
 
 
 
 
 
 

167 linhas
4.5 KiB

  1. # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
  2. # License: MIT. See LICENSE
  3. def get_jenv():
  4. import frappe
  5. from frappe.utils.safe_exec import get_safe_globals
  6. if not getattr(frappe.local, 'jenv', None):
  7. from jinja2 import DebugUndefined
  8. from jinja2.sandbox import SandboxedEnvironment
  9. # frappe will be loaded last, so app templates will get precedence
  10. jenv = SandboxedEnvironment(
  11. loader=get_jloader(),
  12. undefined=DebugUndefined
  13. )
  14. set_filters(jenv)
  15. jenv.globals.update(get_safe_globals())
  16. methods, filters = get_jinja_hooks()
  17. jenv.globals.update(methods or {})
  18. jenv.filters.update(filters or {})
  19. frappe.local.jenv = jenv
  20. return frappe.local.jenv
  21. def get_template(path):
  22. return get_jenv().get_template(path)
  23. def get_email_from_template(name, args):
  24. from jinja2 import TemplateNotFound
  25. args = args or {}
  26. try:
  27. message = get_template('templates/emails/' + name + '.html').render(args)
  28. except TemplateNotFound as e:
  29. raise e
  30. try:
  31. text_content = get_template('templates/emails/' + name + '.txt').render(args)
  32. except TemplateNotFound:
  33. text_content = None
  34. return (message, text_content)
  35. def validate_template(html):
  36. """Throws exception if there is a syntax error in the Jinja Template"""
  37. import frappe
  38. from jinja2 import TemplateSyntaxError
  39. if not html:
  40. return
  41. jenv = get_jenv()
  42. try:
  43. jenv.from_string(html)
  44. except TemplateSyntaxError as e:
  45. frappe.msgprint('Line {}: {}'.format(e.lineno, e.message))
  46. frappe.throw(frappe._("Syntax error in template"))
  47. def render_template(template, context, is_path=None, safe_render=True):
  48. '''Render a template using Jinja
  49. :param template: path or HTML containing the jinja template
  50. :param context: dict of properties to pass to the template
  51. :param is_path: (optional) assert that the `template` parameter is a path
  52. :param safe_render: (optional) prevent server side scripting via jinja templating
  53. '''
  54. from frappe import _, get_traceback, throw
  55. from jinja2 import TemplateError
  56. if not template:
  57. return ""
  58. if (is_path or guess_is_path(template)):
  59. return get_jenv().get_template(template).render(context)
  60. else:
  61. if safe_render and ".__" in template:
  62. throw(_("Illegal template"))
  63. try:
  64. return get_jenv().from_string(template).render(context)
  65. except TemplateError:
  66. throw(title="Jinja Template Error", msg="<pre>{template}</pre><pre>{tb}</pre>".format(template=template, tb=get_traceback()))
  67. def guess_is_path(template):
  68. # template can be passed as a path or content
  69. # if its single line and ends with a html, then its probably a path
  70. if '\n' not in template and '.' in template:
  71. extn = template.rsplit('.')[-1]
  72. if extn in ('html', 'css', 'scss', 'py', 'md', 'json', 'js', 'xml'):
  73. return True
  74. return False
  75. def get_jloader():
  76. import frappe
  77. if not getattr(frappe.local, 'jloader', None):
  78. from jinja2 import ChoiceLoader, PackageLoader, PrefixLoader
  79. apps = frappe.get_hooks('template_apps')
  80. if not apps:
  81. apps = frappe.local.flags.web_pages_apps or frappe.get_installed_apps(sort=True)
  82. apps.reverse()
  83. if "frappe" not in apps:
  84. apps.append('frappe')
  85. frappe.local.jloader = ChoiceLoader(
  86. # search for something like app/templates/...
  87. [PrefixLoader(dict(
  88. (app, PackageLoader(app, ".")) for app in apps
  89. ))]
  90. # search for something like templates/...
  91. + [PackageLoader(app, ".") for app in apps]
  92. )
  93. return frappe.local.jloader
  94. def set_filters(jenv):
  95. import frappe
  96. from frappe.utils import cint, cstr, flt
  97. jenv.filters.update({
  98. "json": frappe.as_json,
  99. "len": len,
  100. "int": cint,
  101. "str": cstr,
  102. "flt": flt,
  103. })
  104. def get_jinja_hooks():
  105. """Returns a tuple of (methods, filters) each containing a dict of method name and method definition pair."""
  106. import frappe
  107. if not getattr(frappe.local, "site", None):
  108. return (None, None)
  109. from types import FunctionType, ModuleType
  110. from inspect import getmembers, isfunction
  111. def get_obj_dict_from_paths(object_paths):
  112. out = {}
  113. for obj_path in object_paths:
  114. try:
  115. obj = frappe.get_module(obj_path)
  116. except ModuleNotFoundError:
  117. obj = frappe.get_attr(obj_path)
  118. if isinstance(obj, ModuleType):
  119. functions = getmembers(obj, isfunction)
  120. for function_name, function in functions:
  121. out[function_name] = function
  122. elif isinstance(obj, FunctionType):
  123. function_name = obj.__name__
  124. out[function_name] = obj
  125. return out
  126. values = frappe.get_hooks("jinja")
  127. methods, filters = values.get("methods", []), values.get("filters", [])
  128. method_dict = get_obj_dict_from_paths(methods)
  129. filter_dict = get_obj_dict_from_paths(filters)
  130. return method_dict, filter_dict