You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

180 lines
4.4 KiB

  1. # Copyright (c) 2013, Web Notes 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 frappe.website.context import get_context
  10. from frappe.website.utils import scrub_relative_urls, get_home_page, can_cache, delete_page_cache
  11. from frappe.website.permissions import clear_permissions
  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. try:
  18. data = render_page(path)
  19. except frappe.DoesNotExistError, e:
  20. doctype, name = get_doctype_from_path(path)
  21. if doctype and name:
  22. path = "print"
  23. frappe.local.form_dict.doctype = doctype
  24. frappe.local.form_dict.name = name
  25. elif doctype:
  26. path = "list"
  27. frappe.local.form_dict.doctype = doctype
  28. else:
  29. path = "404"
  30. http_status_code = e.http_status_code
  31. try:
  32. data = render_page(path)
  33. except frappe.PermissionError, e:
  34. data, http_status_code = render_403(e, path)
  35. except frappe.PermissionError, e:
  36. data, http_status_code = render_403(e, path)
  37. except Exception:
  38. path = "error"
  39. data = render_page(path)
  40. http_status_code = 500
  41. return build_response(path, data, http_status_code or 200)
  42. def render_403(e, pathname):
  43. path = "message"
  44. frappe.local.message = """<p><strong>{error}</strong></p>
  45. <p>
  46. <a href="/login?redirect-to=/{pathname}" class="btn btn-primary>{login}</a>
  47. </p>""".format(error=cstr(e), login=_("Login"), pathname=pathname)
  48. frappe.local.message_title = _("Not Permitted")
  49. return render_page(path), e.http_status_code
  50. def get_doctype_from_path(path):
  51. doctypes = frappe.db.sql_list("select name from tabDocType")
  52. parts = path.split("/")
  53. doctype = parts[0]
  54. name = parts[1] if len(parts) > 1 else None
  55. if doctype in doctypes:
  56. return doctype, name
  57. # try scrubbed
  58. doctype = doctype.replace("_", " ").title()
  59. if doctype in doctypes:
  60. return doctype, name
  61. return None, None
  62. def build_response(path, data, http_status_code):
  63. # build response
  64. response = Response()
  65. response.data = set_content_type(response, data, path)
  66. response.status_code = http_status_code
  67. response.headers[b"X-Page-Name"] = path.encode("utf-8")
  68. response.headers[b"X-From-Cache"] = frappe.local.response.from_cache or False
  69. return response
  70. def render_page(path):
  71. """get page html"""
  72. cache_key = ("page_context:{}" if is_ajax() else "page:{}").format(path)
  73. out = None
  74. # try memcache
  75. if can_cache():
  76. out = frappe.cache().get_value(cache_key)
  77. if out and is_ajax():
  78. out = out.get("data")
  79. if out:
  80. frappe.local.response.from_cache = True
  81. return out
  82. return build(path)
  83. def build(path):
  84. if not frappe.db:
  85. frappe.connect()
  86. build_method = (build_json if is_ajax() else build_page)
  87. try:
  88. return build_method(path)
  89. except frappe.DoesNotExistError:
  90. hooks = frappe.get_hooks()
  91. if hooks.website_catch_all:
  92. path = hooks.website_catch_all[0]
  93. return build_method(path)
  94. else:
  95. raise
  96. def build_json(path):
  97. return get_context(path).data
  98. def build_page(path):
  99. context = get_context(path)
  100. html = frappe.get_template(context.base_template_path).render(context)
  101. html = scrub_relative_urls(html)
  102. if can_cache(context.no_cache):
  103. frappe.cache().set_value("page:" + path, html)
  104. return html
  105. def is_ajax():
  106. return getattr(frappe.local, "is_ajax", False)
  107. def resolve_path(path):
  108. if not path:
  109. path = "index"
  110. if path.endswith('.html'):
  111. path = path[:-5]
  112. if path == "index":
  113. path = get_home_page()
  114. return path
  115. def set_content_type(response, data, path):
  116. if isinstance(data, dict):
  117. response.headers[b"Content-Type"] = b"application/json; charset: utf-8"
  118. data = json.dumps(data)
  119. return data
  120. response.headers[b"Content-Type"] = b"text/html; charset: utf-8"
  121. if "." in path:
  122. content_type, encoding = mimetypes.guess_type(path)
  123. if not content_type:
  124. raise frappe.UnsupportedMediaType("Cannot determine content type of {}".format(path))
  125. response.headers[b"Content-Type"] = content_type.encode("utf-8")
  126. return data
  127. def clear_cache(path=None):
  128. if path:
  129. delete_page_cache(path)
  130. else:
  131. clear_sitemap()
  132. frappe.clear_cache("Guest")
  133. frappe.cache().delete_value("_website_pages")
  134. clear_permissions()
  135. for method in frappe.get_hooks("website_clear_cache"):
  136. frappe.get_attr(method)(path)