25'ten fazla konu seçemezsiniz Konular bir harf veya rakamla başlamalı, kısa çizgiler ('-') içerebilir ve en fazla 35 karakter uzunluğunda olabilir.
 
 
 
 
 
 

223 satır
5.7 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. from frappe.utils import cstr
  7. import mimetypes, json
  8. from werkzeug.wrappers import Response
  9. from werkzeug.routing import Map, Rule, NotFound
  10. from frappe.website.context import get_context
  11. from frappe.website.utils import get_home_page, can_cache, delete_page_cache
  12. from frappe.website.router import clear_sitemap
  13. class PageNotFoundError(Exception): pass
  14. def render(path, http_status_code=None):
  15. """render html page"""
  16. path = resolve_path(path.strip("/ "))
  17. set_lang()
  18. try:
  19. data = render_page(path)
  20. except frappe.DoesNotExistError, e:
  21. doctype, name = get_doctype_from_path(path)
  22. if doctype and name:
  23. path = "print"
  24. frappe.local.form_dict.doctype = doctype
  25. frappe.local.form_dict.name = name
  26. elif doctype:
  27. path = "list"
  28. frappe.local.form_dict.doctype = doctype
  29. else:
  30. path = "404"
  31. http_status_code = e.http_status_code
  32. try:
  33. data = render_page(path)
  34. except frappe.PermissionError, e:
  35. data, http_status_code = render_403(e, path)
  36. except frappe.PermissionError, e:
  37. data, http_status_code = render_403(e, path)
  38. except frappe.Redirect, e:
  39. return build_response(path, "", 301, {
  40. "Location": frappe.flags.redirect_location,
  41. "Cache-Control": "no-store, no-cache, must-revalidate"
  42. })
  43. except Exception:
  44. path = "error"
  45. data = render_page(path)
  46. http_status_code = 500
  47. return build_response(path, data, http_status_code or 200)
  48. def set_lang():
  49. """Set user's lang if not Guest or use default lang"""
  50. frappe.local.lang = getattr(frappe.local, "user_lang", None) or frappe.db.get_default("lang")
  51. def render_403(e, pathname):
  52. path = "message"
  53. frappe.local.message = """<p><strong>{error}</strong></p>
  54. <p>
  55. <a href="/login?redirect-to=/{pathname}" class="btn btn-primary">{login}</a>
  56. </p>""".format(error=cstr(e.message), login=_("Login"), pathname=frappe.local.path)
  57. frappe.local.message_title = _("Not Permitted")
  58. return render_page(path), e.http_status_code
  59. def get_doctype_from_path(path):
  60. doctypes = frappe.db.sql_list("select name from tabDocType")
  61. parts = path.split("/")
  62. doctype = parts[0]
  63. name = parts[1] if len(parts) > 1 else None
  64. if doctype in doctypes:
  65. return doctype, name
  66. # try scrubbed
  67. doctype = doctype.replace("_", " ").title()
  68. if doctype in doctypes:
  69. return doctype, name
  70. return None, None
  71. def build_response(path, data, http_status_code, headers=None):
  72. # build response
  73. response = Response()
  74. response.data = set_content_type(response, data, path)
  75. response.status_code = http_status_code
  76. response.headers[b"X-Page-Name"] = path.encode("utf-8")
  77. response.headers[b"X-From-Cache"] = frappe.local.response.from_cache or False
  78. if headers:
  79. for key, val in headers.iteritems():
  80. response.headers[bytes(key)] = val.encode("utf-8")
  81. return response
  82. def render_page(path):
  83. """get page html"""
  84. out = None
  85. if can_cache():
  86. if is_ajax():
  87. # ajax, send context
  88. context_cache = frappe.cache().hget("page_context", path)
  89. if context_cache and frappe.local.lang in context_cache:
  90. out = context_cache[frappe.local.lang].get("data")
  91. else:
  92. # return rendered page
  93. page_cache = frappe.cache().hget("website_page", path)
  94. if page_cache and frappe.local.lang in page_cache:
  95. out = page_cache[frappe.local.lang]
  96. if out:
  97. frappe.local.response.from_cache = True
  98. return out
  99. return build(path)
  100. def build(path):
  101. if not frappe.db:
  102. frappe.connect()
  103. build_method = (build_json if is_ajax() else build_page)
  104. try:
  105. return build_method(path)
  106. except frappe.DoesNotExistError:
  107. hooks = frappe.get_hooks()
  108. if hooks.website_catch_all:
  109. path = hooks.website_catch_all[0]
  110. return build_method(path)
  111. else:
  112. raise
  113. def build_json(path):
  114. return get_context(path).data
  115. def build_page(path):
  116. if not getattr(frappe.local, "path", None):
  117. frappe.local.path = path
  118. context = get_context(path)
  119. html = frappe.get_template(context.base_template_path).render(context)
  120. if can_cache(context.no_cache):
  121. page_cache = frappe.cache().hget("website_page", path) or {}
  122. page_cache[frappe.local.lang] = html
  123. frappe.cache().hset("website_page", path, page_cache)
  124. return html
  125. def is_ajax():
  126. return getattr(frappe.local, "is_ajax", False)
  127. def resolve_path(path):
  128. if not path:
  129. path = "index"
  130. if path.endswith('.html'):
  131. path = path[:-5]
  132. if path == "index":
  133. path = get_home_page()
  134. frappe.local.path = path
  135. if path != "index":
  136. path = resolve_from_map(path)
  137. return path
  138. def resolve_from_map(path):
  139. m = Map([Rule(r["from_route"], endpoint=r["to_route"], defaults=r.get("defaults"))
  140. for r in frappe.get_hooks("website_route_rules")])
  141. urls = m.bind_to_environ(frappe.local.request.environ)
  142. try:
  143. endpoint, args = urls.match("/" + path)
  144. path = endpoint
  145. if args:
  146. # don't cache when there's a query string!
  147. frappe.local.no_cache = 1
  148. frappe.local.form_dict.update(args)
  149. except NotFound:
  150. pass
  151. return path
  152. def set_content_type(response, data, path):
  153. if isinstance(data, dict):
  154. response.headers[b"Content-Type"] = b"application/json; charset: utf-8"
  155. data = json.dumps(data)
  156. return data
  157. response.headers[b"Content-Type"] = b"text/html; charset: utf-8"
  158. if "." in path:
  159. content_type, encoding = mimetypes.guess_type(path)
  160. if not content_type:
  161. content_type = "text/html; charset: utf-8"
  162. response.headers[b"Content-Type"] = content_type.encode("utf-8")
  163. return data
  164. def clear_cache(path=None):
  165. frappe.cache().delete_value("website_generator_routes")
  166. delete_page_cache(path)
  167. if not path:
  168. clear_sitemap()
  169. frappe.clear_cache("Guest")
  170. frappe.cache().delete_value("_website_pages")
  171. for method in frappe.get_hooks("website_clear_cache"):
  172. frappe.get_attr(method)(path)