Refactored handler, catch exceptions, response codesversion-14
@@ -16,6 +16,7 @@ import json | |||
import semantic_version | |||
from frappe.core.doctype.print_format.print_format import get_html as get_print_html | |||
from .exceptions import * | |||
local = Local() | |||
@@ -71,7 +72,6 @@ form = form_dict = local("form_dict") | |||
request = local("request") | |||
request_method = local("request_method") | |||
response = local("response") | |||
_response = local("_response") | |||
session = local("session") | |||
user = local("user") | |||
flags = local("flags") | |||
@@ -149,21 +149,6 @@ def cache(): | |||
_memc = MClient(['localhost:11211']) | |||
return _memc | |||
class DuplicateEntryError(Exception): pass | |||
class ValidationError(Exception): pass | |||
class AuthenticationError(Exception): pass | |||
class PermissionError(Exception): pass | |||
class DataError(Exception): pass | |||
class UnknownDomainError(Exception): pass | |||
class SessionStopped(Exception): pass | |||
class MappingMismatchError(ValidationError): pass | |||
class InvalidStatusError(ValidationError): pass | |||
class DoesNotExistError(ValidationError): pass | |||
class MandatoryError(ValidationError): pass | |||
class InvalidSignatureError(ValidationError): pass | |||
class RateLimitExceededError(ValidationError): pass | |||
class OutgoingEmailError(Exception): pass | |||
def get_traceback(): | |||
import utils | |||
return utils.get_traceback() | |||
@@ -562,8 +547,6 @@ def repsond_as_web_page(title, html): | |||
local.response['type'] = 'page' | |||
local.response['page_name'] = 'message.html' | |||
return obj | |||
def build_match_conditions(doctype, as_condition=True): | |||
import frappe.widgets.reportview | |||
return frappe.widgets.reportview.build_match_conditions(doctype, as_condition) | |||
@@ -5,7 +5,7 @@ import frappe | |||
import frappe.handler | |||
import frappe.client | |||
import frappe.widgets.reportview | |||
from frappe.utils.response import build_response, report_error | |||
from frappe.utils.response import build_response | |||
def handle(): | |||
""" | |||
@@ -35,63 +35,56 @@ def handle(): | |||
if len(parts) > 3: | |||
name = parts[3] | |||
try: | |||
if call=="method": | |||
frappe.local.form_dict.cmd = doctype | |||
frappe.handler.handle() | |||
return | |||
elif call=="resource": | |||
if "run_method" in frappe.local.form_dict: | |||
bean = frappe.bean(doctype, name) | |||
if call=="method": | |||
frappe.local.form_dict.cmd = doctype | |||
return frappe.handler.handle() | |||
elif call=="resource": | |||
if "run_method" in frappe.local.form_dict: | |||
bean = frappe.bean(doctype, name) | |||
if frappe.local.request.method=="GET": | |||
if not bean.has_permission("read"): | |||
frappe.throw("No Permission", frappe.PermissionError) | |||
bean.run_method(frappe.local.form_dict.run_method, **frappe.local.form_dict) | |||
if frappe.local.request.method=="POST": | |||
if not bean.has_permission("write"): | |||
frappe.throw("No Permission", frappe.PermissionError) | |||
bean.run_method(frappe.local.form_dict.run_method, **frappe.local.form_dict) | |||
frappe.db.commit() | |||
else: | |||
if name: | |||
if frappe.local.request.method=="GET": | |||
if not bean.has_permission("read"): | |||
frappe.throw("No Permission", frappe.PermissionError) | |||
bean.run_method(frappe.local.form_dict.run_method, **frappe.local.form_dict) | |||
frappe.local.response.update({ | |||
"doclist": frappe.client.get(doctype, | |||
name)}) | |||
if frappe.local.request.method=="POST": | |||
if not bean.has_permission("write"): | |||
frappe.throw("No Permission", frappe.PermissionError) | |||
bean.run_method(frappe.local.form_dict.run_method, **frappe.local.form_dict) | |||
frappe.local.response.update({ | |||
"doclist": frappe.client.insert(frappe.local.form_dict.doclist)}) | |||
frappe.db.commit() | |||
else: | |||
if name: | |||
if frappe.local.request.method=="GET": | |||
frappe.local.response.update({ | |||
"doclist": frappe.client.get(doctype, | |||
name)}) | |||
if frappe.local.request.method=="POST": | |||
frappe.local.response.update({ | |||
"doclist": frappe.client.insert(frappe.local.form_dict.doclist)}) | |||
frappe.db.commit() | |||
if frappe.local.request.method=="PUT": | |||
frappe.local.response.update({ | |||
"doclist":frappe.client.save(frappe.local.form_dict.doclist)}) | |||
frappe.db.commit() | |||
if frappe.local.request.method=="DELETE": | |||
frappe.client.delete(doctype, name) | |||
frappe.local.response.message = "ok" | |||
elif doctype: | |||
if frappe.local.request.method=="GET": | |||
frappe.local.response.update({ | |||
"data": frappe.call(frappe.widgets.reportview.execute, | |||
doctype, **frappe.local.form_dict)}) | |||
else: | |||
raise frappe.DoesNotExistError | |||
else: | |||
raise frappe.DoesNotExistError | |||
except frappe.DoesNotExistError, e: | |||
report_error(404) | |||
except Exception, e: | |||
report_error(500) | |||
if frappe.local.request.method=="PUT": | |||
frappe.local.response.update({ | |||
"doclist":frappe.client.save(frappe.local.form_dict.doclist)}) | |||
frappe.db.commit() | |||
if frappe.local.request.method=="DELETE": | |||
frappe.client.delete(doctype, name) | |||
frappe.local.response.message = "ok" | |||
elif doctype: | |||
if frappe.local.request.method=="GET": | |||
frappe.local.response.update({ | |||
"data": frappe.call(frappe.widgets.reportview.execute, | |||
doctype, **frappe.local.form_dict)}) | |||
else: | |||
raise frappe.DoesNotExistError | |||
else: | |||
raise frappe.DoesNotExistError | |||
build_response() | |||
return build_response("json") |
@@ -25,65 +25,82 @@ local_manager = LocalManager([frappe.local]) | |||
_site = None | |||
_sites_path = os.environ.get("SITES_PATH", ".") | |||
def handle_session_stopped(): | |||
res = Response("""<html> | |||
<body style="background-color: #EEE;"> | |||
<h3 style="width: 900px; background-color: #FFF; border: 2px solid #AAA; padding: 20px; font-family: Arial; margin: 20px auto"> | |||
Updating. | |||
We will be back in a few moments... | |||
</h3> | |||
</body> | |||
</html>""") | |||
res.status_code = 503 | |||
res.content_type = 'text/html' | |||
return res | |||
@Request.application | |||
def application(request): | |||
frappe.local.request = request | |||
response = None | |||
try: | |||
site = _site or get_site_name(request.host) | |||
frappe.init(site=site, sites_path=_sites_path) | |||
rollback = True | |||
if not frappe.local.conf: | |||
# site does not exist | |||
raise NotFound | |||
init_site(request) | |||
make_form_dict(request) | |||
frappe.local.http_request = frappe.auth.HTTPRequest() | |||
frappe.local.form_dict = frappe._dict({ k:v[0] if isinstance(v, (list, tuple)) else v \ | |||
for k, v in (request.form or request.args).iteritems() }) | |||
frappe.local._response = Response() | |||
frappe.http_request = frappe.auth.HTTPRequest() | |||
if frappe.local.form_dict.cmd: | |||
frappe.handler.handle() | |||
response = frappe.handler.handle() | |||
elif frappe.request.path.startswith("/api/"): | |||
frappe.api.handle() | |||
response = frappe.api.handle() | |||
elif frappe.request.path.startswith('/backups'): | |||
frappe.utils.response.download_backup(request.path) | |||
response = frappe.utils.response.download_backup(request.path) | |||
elif frappe.local.request.method in ('GET', 'HEAD'): | |||
frappe.website.render.render(frappe.request.path[1:]) | |||
response = frappe.website.render.render(request.path) | |||
else: | |||
raise NotFound | |||
except HTTPException, e: | |||
return e | |||
except frappe.AuthenticationError, e: | |||
frappe._response.status_code=401 | |||
except frappe.SessionStopped, e: | |||
frappe.local._response = handle_session_stopped() | |||
response = frappe.utils.response.handle_session_stopped() | |||
except (frappe.AuthenticationError, | |||
frappe.PermissionError, | |||
frappe.DoesNotExistError, | |||
frappe.DuplicateEntryError, | |||
frappe.OutgoingEmailError, | |||
frappe.ValidationError), e: | |||
response = frappe.utils.response.report_error(e.http_status_code) | |||
if e.__class__ == frappe.AuthenticationError: | |||
frappe.local.login_manager.clear_cookies() | |||
else: | |||
if frappe.local.request.method in ("POST", "PUT") and frappe.db: | |||
frappe.db.commit() | |||
rollback = False | |||
finally: | |||
_response = frappe.local._response | |||
if frappe.local.request.method in ("POST", "PUT") and frappe.db and rollback: | |||
frappe.db.rollback() | |||
# set cookies | |||
if response: | |||
frappe.local.cookie_manager.flush_cookies(response=response) | |||
frappe.destroy() | |||
return response | |||
def init_site(request): | |||
site = _site or get_site_name(request.host) | |||
frappe.init(site=site, sites_path=_sites_path) | |||
if not frappe.local.conf: | |||
# site does not exist | |||
raise NotFound | |||
def make_form_dict(request): | |||
frappe.local.form_dict = frappe._dict({ k:v[0] if isinstance(v, (list, tuple)) else v \ | |||
for k, v in (request.form or request.args).iteritems() }) | |||
return _response | |||
application = local_manager.make_middleware(application) | |||
def serve(port=8000, profile=False, site=None, sites_path='.'): | |||
global application, _site, _sites_path | |||
_site = site | |||
@@ -102,6 +119,6 @@ def serve(port=8000, profile=False, site=None, sites_path='.'): | |||
application = StaticDataMiddleware(application, { | |||
'/files': os.path.abspath(sites_path) | |||
}) | |||
run_simple('0.0.0.0', int(port), application, use_reloader=True, | |||
use_debugger=True, use_evalex=True) |
@@ -2,13 +2,15 @@ | |||
# MIT License. See license.txt | |||
from __future__ import unicode_literals | |||
import datetime | |||
import frappe | |||
import frappe.database | |||
import frappe.utils | |||
import frappe.utils.user | |||
from frappe import conf | |||
from frappe.sessions import Session | |||
from frappe.modules.patch_handler import check_session_stopped | |||
class HTTPRequest: | |||
def __init__(self): | |||
@@ -33,11 +35,12 @@ class HTTPRequest: | |||
# login | |||
frappe.local.login_manager = LoginManager() | |||
# write out latest cookies | |||
frappe.local.cookie_manager.init_cookies() | |||
# check status | |||
if frappe.db.get_global("__session_status")=='stop': | |||
frappe.msgprint(frappe.db.get_global("__session_status_message")) | |||
raise frappe.SessionStopped('Session Stopped') | |||
check_session_stopped() | |||
# load user | |||
self.setup_user() | |||
@@ -106,20 +109,23 @@ class LoginManager: | |||
self.set_user_info() | |||
def set_user_info(self): | |||
# set sid again | |||
frappe.local.cookie_manager.init_cookies() | |||
info = frappe.db.get_value("User", self.user, | |||
["user_type", "first_name", "last_name", "user_image"], as_dict=1) | |||
if info.user_type=="Website User": | |||
frappe.local._response.set_cookie("system_user", "no") | |||
frappe.local.cookie_manager.set_cookie("system_user", "no") | |||
frappe.local.response["message"] = "No App" | |||
else: | |||
frappe.local._response.set_cookie("system_user", "yes") | |||
frappe.local.cookie_manager.set_cookie("system_user", "yes") | |||
frappe.local.response['message'] = 'Logged In' | |||
full_name = " ".join(filter(None, [info.first_name, info.last_name])) | |||
frappe.response["full_name"] = full_name | |||
frappe._response.set_cookie("full_name", full_name) | |||
frappe._response.set_cookie("user_id", self.user) | |||
frappe._response.set_cookie("user_image", info.user_image or "") | |||
frappe.local.cookie_manager.set_cookie("full_name", full_name) | |||
frappe.local.cookie_manager.set_cookie("user_id", self.user) | |||
frappe.local.cookie_manager.set_cookie("user_image", info.user_image or "") | |||
def make_session(self, resume=False): | |||
# start session | |||
@@ -161,7 +167,7 @@ class LoginManager: | |||
def run_trigger(self, event='on_login'): | |||
for method in frappe.get_hooks().get(event, []): | |||
frappe.get_attr(method)(self) | |||
frappe.call(frappe.get_attr(method), login_manager=self) | |||
def validate_ip_address(self): | |||
"""check if IP Address is valid""" | |||
@@ -212,44 +218,45 @@ class LoginManager: | |||
clear_sessions(user) | |||
if user == frappe.session.user: | |||
frappe.session.sid = "" | |||
frappe.local._response.delete_cookie("full_name") | |||
frappe.local._response.delete_cookie("user_id") | |||
frappe.local._response.delete_cookie("sid") | |||
frappe.local._response.set_cookie("full_name", "") | |||
frappe.local._response.set_cookie("user_id", "") | |||
frappe.local._response.set_cookie("sid", "") | |||
self.clear_cookies() | |||
def clear_cookies(self): | |||
frappe.session.sid = "" | |||
frappe.local.cookie_manager.delete_cookie(["full_name", "user_id", "sid", "user_image", "system_user"]) | |||
class CookieManager: | |||
def __init__(self): | |||
pass | |||
self.cookies = {} | |||
self.to_delete = [] | |||
def set_cookies(self): | |||
def init_cookies(self): | |||
if not frappe.local.session.get('sid'): return | |||
import datetime | |||
# sid expires in 3 days | |||
expires = datetime.datetime.now() + datetime.timedelta(days=3) | |||
if frappe.session.sid: | |||
frappe.local._response.set_cookie("sid", frappe.session.sid, expires = expires) | |||
self.cookies["sid"] = {"value": frappe.session.sid, "expires": expires} | |||
if frappe.session.session_country: | |||
frappe.local._response.set_cookie('country', frappe.session.get("session_country")) | |||
self.cookies["country"] = {"value": frappe.session.get("session_country")} | |||
def set_cookie(self, key, value, expires=None): | |||
self.cookies[key] = {"value": value, "expires": expires} | |||
def delete_cookie(self, to_delete): | |||
if not isinstance(to_delete, (list, tuple)): | |||
to_delete = [to_delete] | |||
def set_remember_me(self): | |||
from frappe.utils import cint | |||
self.to_delete.extend(to_delete) | |||
if not cint(frappe.form_dict.get('remember_me')): return | |||
def flush_cookies(self, response): | |||
for key, opts in self.cookies.items(): | |||
response.set_cookie(key, opts.get("value"), expires=opts.get("expires")) | |||
# expires yesterday! | |||
expires = datetime.datetime.now() + datetime.timedelta(days=-1) | |||
for key in set(self.to_delete): | |||
response.set_cookie(key, "", expires=expires) | |||
remember_days = frappe.db.get_value('Control Panel', None, | |||
'remember_for_days') or 7 | |||
import datetime | |||
expires = datetime.datetime.now() + \ | |||
datetime.timedelta(days=remember_days) | |||
frappe.local._response.set_cookie["remember_me"] = 1 | |||
def _update_password(user, password): | |||
frappe.db.sql("""insert into __Auth (user, `password`) | |||
values (%s, password(%s)) | |||
@@ -129,10 +129,16 @@ class DocType: | |||
self.doc.doctype, self.doc.name), scrub(self.doc.name) + '.py') | |||
if not os.path.exists(pypath): | |||
# get app publisher for copyright | |||
app = frappe.local.module_app[frappe.scrub(self.doc.module)] | |||
if not app: | |||
frappe.throw("App not found!") | |||
app_publisher = frappe.get_hooks(hook="app_publisher", app_name=app)[0] | |||
with open(pypath, 'w') as pyfile: | |||
with open(os.path.join(get_module_path("core"), "doctype", "doctype", | |||
"doctype_template.py"), 'r') as srcfile: | |||
pyfile.write(srcfile.read()) | |||
pyfile.write(srcfile.read().format(app_publisher=app_publisher)) | |||
def make_amendable(self): | |||
""" | |||
@@ -1,5 +1,5 @@ | |||
# Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors | |||
# GNU General Public License. See license.txt | |||
# Copyright (c) 2013, {app_publisher} | |||
# For license information, please see license.txt | |||
from __future__ import unicode_literals | |||
import frappe | |||
@@ -0,0 +1,35 @@ | |||
# Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors | |||
# MIT License. See license.txt | |||
from __future__ import unicode_literals | |||
# BEWARE don't put anything in this file except exceptions | |||
class ValidationError(Exception): | |||
http_status_code = 417 | |||
class AuthenticationError(Exception): | |||
http_status_code = 401 | |||
class PermissionError(Exception): | |||
http_status_code = 403 | |||
class DoesNotExistError(ValidationError): | |||
http_status_code = 404 | |||
class DuplicateEntryError(Exception): | |||
http_status_code = 409 | |||
class OutgoingEmailError(Exception): | |||
http_status_code = 501 | |||
class SessionStopped(Exception): | |||
http_status_code = 503 | |||
class DataError(Exception): pass | |||
class UnknownDomainError(Exception): pass | |||
class MappingMismatchError(ValidationError): pass | |||
class InvalidStatusError(ValidationError): pass | |||
class MandatoryError(ValidationError): pass | |||
class InvalidSignatureError(ValidationError): pass | |||
class RateLimitExceededError(ValidationError): pass |
@@ -8,7 +8,7 @@ import frappe.utils | |||
import frappe.sessions | |||
import frappe.utils.file_manager | |||
import frappe.widgets.form.run_method | |||
from frappe.utils.response import build_response, report_error | |||
from frappe.utils.response import build_response | |||
@frappe.whitelist(allow_guest=True) | |||
def startup(): | |||
@@ -64,33 +64,9 @@ def handle(): | |||
cmd = frappe.local.form_dict.cmd | |||
if cmd!='login': | |||
status_codes = { | |||
frappe.PermissionError: 403, | |||
frappe.AuthenticationError: 401, | |||
frappe.DoesNotExistError: 404, | |||
frappe.DuplicateEntryError: 409, | |||
frappe.SessionStopped: 503, | |||
frappe.OutgoingEmailError: 501 | |||
} | |||
try: | |||
execute_cmd(cmd) | |||
except Exception, e: | |||
report_error(status_codes.get(e.__class__, 500)) | |||
else: | |||
if frappe.local.request.method in ("POST", "PUT") and frappe.db: | |||
frappe.db.commit() | |||
else: | |||
# commit for login | |||
if frappe.local.request.method in ("POST", "PUT") and frappe.db: | |||
frappe.db.commit() | |||
execute_cmd(cmd) | |||
build_response() | |||
if frappe.db: | |||
frappe.db.close() | |||
if frappe._memc: | |||
frappe._memc.disconnect_all() | |||
return build_response("json") | |||
def execute_cmd(cmd): | |||
"""execute a request as python module""" | |||
@@ -102,7 +78,6 @@ def execute_cmd(cmd): | |||
raise frappe.PermissionError('Not Allowed, %s' % str(method)) | |||
else: | |||
if not method in frappe.whitelisted: | |||
frappe._response.status_code = 403 | |||
frappe.msgprint('Not Allowed, %s' % str(method)) | |||
raise frappe.PermissionError('Not Allowed, %s' % str(method)) | |||
@@ -1,6 +1,6 @@ | |||
app_name = frappe | |||
app_title = Frappe Framework | |||
app_publisher = Web Notes Technologies | |||
app_publisher = Web Notes Technologies Pvt. Ltd. and Contributors | |||
app_description = Full Stack Web Application Framwork in Python | |||
app_icon = assets/frappe/images/frappe.svg | |||
app_version = 4.0.0-wip | |||
@@ -114,6 +114,11 @@ def block_user(block): | |||
frappe.db.set_global('__session_status', block and 'stop' or None) | |||
frappe.db.set_global('__session_status_message', block and msg or None) | |||
frappe.db.commit() | |||
def check_session_stopped(): | |||
if frappe.db.get_global("__session_status")=='stop': | |||
frappe.msgprint(frappe.db.get_global("__session_status_message")) | |||
raise frappe.SessionStopped('Session Stopped') | |||
def setup(): | |||
frappe.db.sql("""CREATE TABLE IF NOT EXISTS `__PatchLog` ( | |||
@@ -30,10 +30,10 @@ frappe.avatar = function(user, large, title) { | |||
}); | |||
} | |||
frappe.ui.set_user_background = function(src) { | |||
if(!src) src = "assets/frappe/images/ui/random-polygons.jpg"; | |||
frappe.ui.set_user_background = function(src, size) { | |||
if(!src) src = "assets/frappe/images/ui/wallpaper.jpg"; | |||
frappe.dom.set_style(repl('#page-desktop { \ | |||
background: url("%(src)s") center center fixed; \ | |||
background: url("%(src)s") center center; \ | |||
}', {src:src})) | |||
} | |||
@@ -59,7 +59,7 @@ def clear_sessions(user=None, keep_current=False): | |||
user = frappe.session.user | |||
for sid in frappe.db.sql("""select sid from tabSessions where user=%s""", (user,)): | |||
if keep_current and frappe.session.sid==sid[0]: | |||
pass | |||
continue | |||
else: | |||
frappe.cache().delete_value("session:" + sid[0]) | |||
frappe.db.sql("""delete from tabSessions where sid=%s""", (sid[0],)) | |||
@@ -104,9 +104,6 @@ class Session: | |||
# set local session | |||
frappe.local.session = self.data | |||
# write out latest cookies | |||
frappe.local.cookie_manager.set_cookies() | |||
def start(self): | |||
"""start a new session""" | |||
# generate sid | |||
@@ -102,7 +102,11 @@ login.login_handlers = { | |||
} | |||
}, | |||
401: function(xhr, data) { | |||
frappe.msgprint("Invalid Login"); | |||
if(xhr.responseJSON) { | |||
data = xhr.responseJSON; | |||
} | |||
var message = data._server_messages ? JSON.parse(data._server_messages).join("\n") : "Invalid Login"; | |||
frappe.msgprint(message); | |||
} | |||
} | |||
@@ -33,7 +33,7 @@ oauth2_providers = { | |||
"redirect_uri": "/api/method/frappe.templates.pages.login.login_via_google", | |||
"auth_url_data": { | |||
"scope": "https://www.googleapis.com/auth/userinfo.user https://www.googleapis.com/auth/userinfo.email", | |||
"scope": "https://www.googleapis.com/auth/userinfo.profile https://www.googleapis.com/auth/userinfo.email", | |||
"response_type": "code" | |||
}, | |||
@@ -12,6 +12,7 @@ from frappe import _ | |||
import frappe.utils | |||
import frappe.sessions | |||
import frappe.model.utils | |||
import werkzeug.utils | |||
from werkzeug.local import LocalProxy | |||
from werkzeug.wsgi import wrap_file | |||
from werkzeug.wrappers import Response | |||
@@ -20,61 +21,49 @@ from werkzeug.exceptions import NotFound, Forbidden | |||
def report_error(status_code): | |||
if status_code!=404 or frappe.conf.logging: | |||
frappe.errprint(frappe.utils.get_traceback()) | |||
frappe._response.status_code = status_code | |||
if frappe.request_method == "POST": | |||
frappe.db.rollback() | |||
response = build_response("json") | |||
response.status_code = status_code | |||
return response | |||
def build_response(): | |||
print_map = { | |||
'csv': print_csv, | |||
'download': print_raw, | |||
'json': print_json, | |||
'page': print_page, | |||
def build_response(response_type=None): | |||
response_type_map = { | |||
'csv': as_csv, | |||
'download': as_raw, | |||
'json': as_json, | |||
'page': as_page, | |||
'redirect': redirect | |||
} | |||
print_map.get(frappe.response.get('type'), print_json)() | |||
def print_page(): | |||
"""print web page""" | |||
from frappe.website.render import render | |||
render(frappe.response['page_name']) | |||
def print_json(): | |||
make_logs() | |||
cleanup_docs() | |||
frappe._response.headers["Content-Type"] = "text/json; charset: utf-8" | |||
print_zip(json.dumps(frappe.local.response, default=json_handler, separators=(',',':'))) | |||
def redirect(): | |||
frappe._response.data = """<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN"> | |||
<title>Redirecting...</title> | |||
<h1>Redirecting...</h1> | |||
<p>You should be redirected automatically to target URL: <a href="{location}">/</a>. | |||
If not click the link.'""".format(location=frappe.response.location) | |||
frappe._response.headers["Content-Type"] = "text/html; charset: utf-8" | |||
frappe._response.status_code = frappe.response.status_code or 302 | |||
frappe._response.location = frappe.response.location | |||
return response_type_map[frappe.response.get('type') or response_type]() | |||
def cleanup_docs(): | |||
if frappe.response.get('docs') and type(frappe.response['docs'])!=dict: | |||
frappe.response['docs'] = frappe.model.utils.compress(frappe.response['docs']) | |||
def print_csv(): | |||
frappe._response.headers["Content-Type"] = \ | |||
def as_csv(): | |||
response = Response() | |||
response.headers["Content-Type"] = \ | |||
"text/csv; charset: utf-8" | |||
frappe._response.headers["Content-Disposition"] = \ | |||
response.headers["Content-Disposition"] = \ | |||
"attachment; filename=%s.csv" % frappe.response['doctype'].replace(' ', '_') | |||
frappe._response.data = frappe.response['result'] | |||
response.data = frappe.response['result'] | |||
return response | |||
def print_raw(): | |||
frappe._response.headers["Content-Type"] = \ | |||
def as_raw(): | |||
response = Response() | |||
response.headers["Content-Type"] = \ | |||
mimetypes.guess_type(frappe.response['filename'])[0] or "application/unknown" | |||
frappe._response.headers["Content-Disposition"] = \ | |||
response.headers["Content-Disposition"] = \ | |||
"filename=%s" % frappe.response['filename'].replace(' ', '_') | |||
frappe._response.data = frappe.response['filecontent'] | |||
response.data = frappe.response['filecontent'] | |||
return response | |||
def as_json(): | |||
make_logs() | |||
cleanup_docs() | |||
response = Response() | |||
response.headers["Content-Type"] = "text/json; charset: utf-8" | |||
response = gzip(json.dumps(frappe.local.response, default=json_handler, separators=(',',':')), | |||
response=response) | |||
return response | |||
def make_logs(): | |||
"""make strings for msgprint and errprint""" | |||
if frappe.error_log: | |||
@@ -87,15 +76,31 @@ def make_logs(): | |||
if frappe.debug_log and frappe.conf.get("logging") or False: | |||
frappe.response['_debug_messages'] = json.dumps(frappe.local.debug_log) | |||
def print_zip(response): | |||
response = response.encode('utf-8') | |||
orig_len = len(response) | |||
def cleanup_docs(): | |||
if frappe.response.get('docs') and type(frappe.response['docs'])!=dict: | |||
frappe.response['docs'] = frappe.model.utils.compress(frappe.response['docs']) | |||
def gzip(data, response): | |||
data = data.encode('utf-8') | |||
orig_len = len(data) | |||
if accept_gzip() and orig_len>512: | |||
response = compressBuf(response) | |||
frappe._response.headers["Content-Encoding"] = "gzip" | |||
data = compressBuf(data) | |||
response.headers["Content-Encoding"] = "gzip" | |||
frappe._response.headers["Content-Length"] = str(len(response)) | |||
frappe._response.data = response | |||
response.headers["Content-Length"] = str(len(data)) | |||
response.data = data | |||
return response | |||
def accept_gzip(): | |||
if "gzip" in frappe.get_request_header("HTTP_ACCEPT_ENCODING", ""): | |||
return True | |||
def compressBuf(buf): | |||
zbuf = cStringIO.StringIO() | |||
zfile = gzip.GzipFile(mode = 'wb', fileobj = zbuf, compresslevel = 5) | |||
zfile.write(buf) | |||
zfile.close() | |||
return zbuf.getvalue() | |||
def json_handler(obj): | |||
"""serialize non-serializable data for json""" | |||
@@ -108,31 +113,30 @@ def json_handler(obj): | |||
else: | |||
raise TypeError, """Object of type %s with value of %s is not JSON serializable""" % \ | |||
(type(obj), repr(obj)) | |||
def accept_gzip(): | |||
if "gzip" in frappe.get_request_header("HTTP_ACCEPT_ENCODING", ""): | |||
return True | |||
def compressBuf(buf): | |||
zbuf = cStringIO.StringIO() | |||
zfile = gzip.GzipFile(mode = 'wb', fileobj = zbuf, compresslevel = 5) | |||
zfile.write(buf) | |||
zfile.close() | |||
return zbuf.getvalue() | |||
def as_page(): | |||
"""print web page""" | |||
from frappe.website.render import render | |||
return render(frappe.response['page_name']) | |||
def redirect(): | |||
return werkzeug.utils.redirect(frappe.response.location) | |||
def download_backup(path): | |||
try: | |||
frappe.only_for(("System Manager", "Administrator")) | |||
except frappe.PermissionError: | |||
raise Forbidden(_("You need to be logged in and have System Manager Role to be able to access backups.")) | |||
send_private_file(path) | |||
return send_private_file(path) | |||
def send_private_file(path): | |||
path = os.path.join(frappe.local.conf.get('private_path', 'private'), path.strip("/")) | |||
if frappe.local.request.headers.get('X-Use-X-Accel-Redirect'): | |||
path = '/' + path | |||
frappe.local._response.headers['X-Accel-Redirect'] = path | |||
response = Response() | |||
response.headers['X-Accel-Redirect'] = path | |||
else: | |||
filename = os.path.basename(path) | |||
filepath = frappe.utils.get_site_path(path) | |||
@@ -140,6 +144,22 @@ def send_private_file(path): | |||
f = open(filepath, 'rb') | |||
except IOError: | |||
raise NotFound | |||
frappe.local._response = Response(wrap_file(frappe.local.request.environ, f)) | |||
frappe.local._response.headers.add('Content-Disposition', 'attachment', filename=filename) | |||
frappe.local._response.headers['Content-Type'] = mimetypes.guess_type(filename)[0] or 'application/octet-stream' | |||
response = Response(wrap_file(frappe.local.request.environ, f)) | |||
response.headers.add('Content-Disposition', 'attachment', filename=filename) | |||
response.headers['Content-Type'] = mimetypes.guess_type(filename)[0] or 'application/octet-stream' | |||
return response | |||
def handle_session_stopped(): | |||
response = Response("""<html> | |||
<body style="background-color: #EEE;"> | |||
<h3 style="width: 900px; background-color: #FFF; border: 2px solid #AAA; padding: 20px; font-family: Arial; margin: 20px auto"> | |||
Updating. | |||
We will be back in a few moments... | |||
</h3> | |||
</body> | |||
</html>""") | |||
response.status_code = 503 | |||
response.content_type = 'text/html' | |||
return res |
@@ -4,17 +4,18 @@ | |||
from __future__ import unicode_literals | |||
import frappe | |||
import mimetypes, json | |||
from werkzeug.wrappers import Response | |||
from frappe.website.context import get_context | |||
from frappe.website.utils import scrub_relative_urls, get_home_page, can_cache | |||
from frappe.website.permissions import get_access, clear_permissions | |||
class PageNotFoundError(Exception): pass | |||
def render(path): | |||
"""render html page""" | |||
path = resolve_path(path) | |||
frappe.local.is_ajax = frappe.get_request_header("X-Requested-With")=="XMLHttpRequest" | |||
path = resolve_path(path.lstrip("/")) | |||
try: | |||
data = render_page(path) | |||
@@ -22,9 +23,12 @@ def render(path): | |||
path = "error" | |||
data = render_page(path) | |||
data = set_content_type(data, path) | |||
frappe._response.data = data | |||
frappe._response.headers[b"X-Page-Name"] = path.encode("utf-8") | |||
# build response | |||
response = Response() | |||
response.data = set_content_type(response, data, path) | |||
response.headers[b"X-Page-Name"] = path.encode("utf-8") | |||
response.headers[b"X-From-Cache"] = frappe.local.response.from_cache or False | |||
return response | |||
def render_page(path): | |||
"""get page html""" | |||
@@ -39,9 +43,7 @@ def render_page(path): | |||
out = out.get("data") | |||
if out: | |||
if hasattr(frappe, "_response"): | |||
frappe._response.headers[b"X-From-Cache"] = True | |||
frappe.local.response.from_cache = True | |||
return out | |||
return build(path) | |||
@@ -75,8 +77,7 @@ def build_page(path): | |||
return html | |||
def is_ajax(): | |||
return (frappe.get_request_header("X-Requested-With")=="XMLHttpRequest" | |||
if hasattr(frappe.local, "_response") else False) | |||
return getattr(frappe.local, "is_ajax") | |||
def resolve_path(path): | |||
if not path: | |||
@@ -90,17 +91,17 @@ def resolve_path(path): | |||
return path | |||
def set_content_type(data, path): | |||
def set_content_type(response, data, path): | |||
if isinstance(data, dict): | |||
frappe._response.headers[b"Content-Type"] = b"application/json; charset: utf-8" | |||
response.headers[b"Content-Type"] = b"application/json; charset: utf-8" | |||
data = json.dumps(data) | |||
return data | |||
frappe._response.headers[b"Content-Type"] = b"text/html; charset: utf-8" | |||
response.headers[b"Content-Type"] = b"text/html; charset: utf-8" | |||
if "." in path and not path.endswith(".html"): | |||
content_type, encoding = mimetypes.guess_type(path) | |||
frappe._response.headers[b"Content-Type"] = content_type.encode("utf-8") | |||
response.headers[b"Content-Type"] = content_type.encode("utf-8") | |||
return data | |||