Não pode escolher mais do que 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.
 
 
 
 
 
 

271 linhas
7.0 KiB

  1. # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
  2. # MIT License. See license.txt
  3. from __future__ import unicode_literals
  4. import frappe
  5. from frappe import _
  6. import frappe.sessions
  7. from frappe.utils import cstr
  8. import mimetypes, json
  9. from six import iteritems
  10. from werkzeug.wrappers import Response
  11. from werkzeug.routing import Map, Rule, NotFound
  12. from frappe.website.context import get_context
  13. from frappe.website.utils import get_home_page, can_cache, delete_page_cache
  14. from frappe.website.router import clear_sitemap
  15. from frappe.translate import guess_language
  16. class PageNotFoundError(Exception): pass
  17. def render(path=None, http_status_code=None):
  18. """render html page"""
  19. path = resolve_path(path or frappe.local.request.path.strip('/ '))
  20. data = None
  21. # if in list of already known 404s, send it
  22. if can_cache() and frappe.cache().hget('website_404', frappe.request.url):
  23. data = render_page('404')
  24. http_status_code = 404
  25. else:
  26. try:
  27. data = render_page_by_language(path)
  28. except frappe.DoesNotExistError as e:
  29. doctype, name = get_doctype_from_path(path)
  30. if doctype and name:
  31. path = "printview"
  32. frappe.local.form_dict.doctype = doctype
  33. frappe.local.form_dict.name = name
  34. elif doctype:
  35. path = "list"
  36. frappe.local.form_dict.doctype = doctype
  37. else:
  38. # 404s are expensive, cache them!
  39. frappe.cache().hset('website_404', frappe.request.url, True)
  40. data = render_page('404')
  41. http_status_code = 404
  42. if not data:
  43. try:
  44. data = render_page(path)
  45. except frappe.PermissionError as e:
  46. data, http_status_code = render_403(e, path)
  47. except frappe.PermissionError as e:
  48. data, http_status_code = render_403(e, path)
  49. except frappe.Redirect as e:
  50. return build_response(path, "", 301, {
  51. "Location": frappe.flags.redirect_location or (frappe.local.response or {}).get('location'),
  52. "Cache-Control": "no-store, no-cache, must-revalidate"
  53. })
  54. except Exception:
  55. path = "error"
  56. data = render_page(path)
  57. http_status_code = 500
  58. data = add_csrf_token(data)
  59. return build_response(path, data, http_status_code or 200)
  60. def build_response(path, data, http_status_code, headers=None):
  61. # build response
  62. response = Response()
  63. response.data = set_content_type(response, data, path)
  64. response.status_code = http_status_code
  65. response.headers[b"X-Page-Name"] = path.encode("utf-8")
  66. response.headers[b"X-From-Cache"] = frappe.local.response.from_cache or False
  67. if headers:
  68. for key, val in iteritems(headers):
  69. response.headers[bytes(key)] = val.encode("utf-8")
  70. return response
  71. def render_page_by_language(path):
  72. translated_languages = frappe.get_hooks("translated_languages_for_website")
  73. user_lang = guess_language(translated_languages)
  74. if translated_languages and user_lang in translated_languages:
  75. try:
  76. if path and path != "index":
  77. lang_path = '{0}/{1}'.format(user_lang, path)
  78. else:
  79. lang_path = user_lang # index
  80. return render_page(lang_path)
  81. except frappe.DoesNotExistError:
  82. return render_page(path)
  83. else:
  84. return render_page(path)
  85. def render_page(path):
  86. """get page html"""
  87. out = None
  88. if can_cache():
  89. # return rendered page
  90. page_cache = frappe.cache().hget("website_page", path)
  91. if page_cache and frappe.local.lang in page_cache:
  92. out = page_cache[frappe.local.lang]
  93. if out:
  94. frappe.local.response.from_cache = True
  95. return out
  96. return build(path)
  97. def build(path):
  98. if not frappe.db:
  99. frappe.connect()
  100. try:
  101. return build_page(path)
  102. except frappe.DoesNotExistError:
  103. hooks = frappe.get_hooks()
  104. if hooks.website_catch_all:
  105. path = hooks.website_catch_all[0]
  106. return build_page(path)
  107. else:
  108. raise
  109. def build_page(path):
  110. if not getattr(frappe.local, "path", None):
  111. frappe.local.path = path
  112. context = get_context(path)
  113. if context.source:
  114. html = frappe.render_template(context.source, context)
  115. elif context.template:
  116. html = frappe.get_template(context.template).render(context)
  117. # html = frappe.get_template(context.base_template_path).render(context)
  118. if can_cache(context.no_cache):
  119. page_cache = frappe.cache().hget("website_page", path) or {}
  120. page_cache[frappe.local.lang] = html
  121. frappe.cache().hset("website_page", path, page_cache)
  122. return html
  123. def resolve_path(path):
  124. if not path:
  125. path = "index"
  126. if path.endswith('.html'):
  127. path = path[:-5]
  128. if path == "index":
  129. path = get_home_page()
  130. frappe.local.path = path
  131. if path != "index":
  132. path = resolve_from_map(path)
  133. return path
  134. def resolve_from_map(path):
  135. m = Map([Rule(r["from_route"], endpoint=r["to_route"], defaults=r.get("defaults"))
  136. for r in get_website_rules()])
  137. urls = m.bind_to_environ(frappe.local.request.environ)
  138. try:
  139. endpoint, args = urls.match("/" + path)
  140. path = endpoint
  141. if args:
  142. # don't cache when there's a query string!
  143. frappe.local.no_cache = 1
  144. frappe.local.form_dict.update(args)
  145. except NotFound:
  146. pass
  147. return path
  148. def get_website_rules():
  149. '''Get website route rules from hooks and DocType route'''
  150. def _get():
  151. rules = frappe.get_hooks("website_route_rules")
  152. for d in frappe.get_all('DocType', 'name, route', dict(has_web_view=1)):
  153. if d.route:
  154. rules.append(dict(from_route = '/' + d.route.strip('/'), to_route=d.name))
  155. return rules
  156. return frappe.cache().get_value('website_route_rules', _get)
  157. def set_content_type(response, data, path):
  158. if isinstance(data, dict):
  159. response.mimetype = 'application/json'
  160. response.charset = 'utf-8'
  161. data = json.dumps(data)
  162. return data
  163. response.mimetype = 'text/html'
  164. response.charset = 'utf-8'
  165. if "." in path:
  166. content_type, encoding = mimetypes.guess_type(path)
  167. if content_type:
  168. response.mimetype = content_type
  169. if encoding:
  170. response.charset = encoding
  171. return data
  172. def clear_cache(path=None):
  173. '''Clear website caches
  174. :param path: (optional) for the given path'''
  175. frappe.cache().delete_value("website_generator_routes")
  176. delete_page_cache(path)
  177. frappe.cache().delete_value("website_404")
  178. if not path:
  179. clear_sitemap()
  180. frappe.clear_cache("Guest")
  181. for key in ('portal_menu_items', 'home_page', 'website_route_rules',
  182. 'doctypes_with_web_view'):
  183. frappe.cache().delete_value(key)
  184. for method in frappe.get_hooks("website_clear_cache"):
  185. frappe.get_attr(method)(path)
  186. def render_403(e, pathname):
  187. frappe.local.message = cstr(e.message)
  188. frappe.local.message_title = _("Not Permitted")
  189. frappe.local.response['context'] = dict(
  190. indicator_color = 'red',
  191. primary_action = '/login',
  192. primary_label = _('Login')
  193. )
  194. return render_page("message"), e.http_status_code
  195. def get_doctype_from_path(path):
  196. doctypes = frappe.db.sql_list("select name from tabDocType")
  197. parts = path.split("/")
  198. doctype = parts[0]
  199. name = parts[1] if len(parts) > 1 else None
  200. if doctype in doctypes:
  201. return doctype, name
  202. # try scrubbed
  203. doctype = doctype.replace("_", " ").title()
  204. if doctype in doctypes:
  205. return doctype, name
  206. return None, None
  207. def add_csrf_token(data):
  208. if frappe.local.session:
  209. return data.replace("<!-- csrf_token -->", '<script>frappe.csrf_token = "{0}";</script>'.format(
  210. frappe.local.session.data.csrf_token))
  211. else:
  212. return data