diff --git a/frappe/__init__.py b/frappe/__init__.py index 8d02238975..8bbb452ab0 100644 --- a/frappe/__init__.py +++ b/frappe/__init__.py @@ -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) diff --git a/frappe/api.py b/frappe/api.py index e8a9358c18..9d306f1280 100644 --- a/frappe/api.py +++ b/frappe/api.py @@ -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") diff --git a/frappe/app.py b/frappe/app.py index 5c0a75210c..95ec261e44 100644 --- a/frappe/app.py +++ b/frappe/app.py @@ -25,65 +25,82 @@ local_manager = LocalManager([frappe.local]) _site = None _sites_path = os.environ.get("SITES_PATH", ".") -def handle_session_stopped(): - res = Response(""" - -

- Updating. - We will be back in a few moments... -

- - """) - 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) diff --git a/frappe/auth.py b/frappe/auth.py index f7be90a07f..a26a0017fa 100644 --- a/frappe/auth.py +++ b/frappe/auth.py @@ -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)) diff --git a/frappe/core/doctype/doctype/doctype.py b/frappe/core/doctype/doctype/doctype.py index 2f41b6e923..35ba142486 100644 --- a/frappe/core/doctype/doctype/doctype.py +++ b/frappe/core/doctype/doctype/doctype.py @@ -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): """ diff --git a/frappe/core/doctype/doctype/doctype_template.py b/frappe/core/doctype/doctype/doctype_template.py index 7207ead2bf..bb4f563ecf 100644 --- a/frappe/core/doctype/doctype/doctype_template.py +++ b/frappe/core/doctype/doctype/doctype_template.py @@ -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 diff --git a/frappe/exceptions.py b/frappe/exceptions.py new file mode 100644 index 0000000000..ecd9957531 --- /dev/null +++ b/frappe/exceptions.py @@ -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 diff --git a/frappe/handler.py b/frappe/handler.py index 84b0c85e07..c15c56f000 100755 --- a/frappe/handler.py +++ b/frappe/handler.py @@ -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)) diff --git a/frappe/hooks.txt b/frappe/hooks.txt index 8888baacc9..45cc4912da 100644 --- a/frappe/hooks.txt +++ b/frappe/hooks.txt @@ -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 diff --git a/frappe/modules/patch_handler.py b/frappe/modules/patch_handler.py index 0ff2978246..851bf7b7b5 100644 --- a/frappe/modules/patch_handler.py +++ b/frappe/modules/patch_handler.py @@ -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` ( diff --git a/frappe/public/images/ui/field.jpg b/frappe/public/images/ui/field.jpg deleted file mode 100644 index fd3498eb51..0000000000 Binary files a/frappe/public/images/ui/field.jpg and /dev/null differ diff --git a/frappe/public/images/ui/random-polygons.jpg b/frappe/public/images/ui/random-polygons.jpg deleted file mode 100755 index 146382fe81..0000000000 Binary files a/frappe/public/images/ui/random-polygons.jpg and /dev/null differ diff --git a/frappe/public/images/ui/wallpaper.jpg b/frappe/public/images/ui/wallpaper.jpg new file mode 100644 index 0000000000..205c2dde5b Binary files /dev/null and b/frappe/public/images/ui/wallpaper.jpg differ diff --git a/frappe/public/js/frappe/misc/user.js b/frappe/public/js/frappe/misc/user.js index 0b52c29511..fa1d2a12c1 100644 --- a/frappe/public/js/frappe/misc/user.js +++ b/frappe/public/js/frappe/misc/user.js @@ -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})) } diff --git a/frappe/sessions.py b/frappe/sessions.py index 0442333742..fdc19b29db 100644 --- a/frappe/sessions.py +++ b/frappe/sessions.py @@ -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 diff --git a/frappe/templates/includes/login.js b/frappe/templates/includes/login.js index 039a15488b..9ec8add689 100644 --- a/frappe/templates/includes/login.js +++ b/frappe/templates/includes/login.js @@ -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); } } diff --git a/frappe/templates/pages/login.py b/frappe/templates/pages/login.py index b4637d6eeb..f98015af41 100644 --- a/frappe/templates/pages/login.py +++ b/frappe/templates/pages/login.py @@ -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" }, diff --git a/frappe/utils/response.py b/frappe/utils/response.py index 7bccb724f3..f137145d42 100644 --- a/frappe/utils/response.py +++ b/frappe/utils/response.py @@ -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 = """ - Redirecting... -

Redirecting...

-

You should be redirected automatically to target URL: /. - 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(""" + +

+ Updating. + We will be back in a few moments... +

+ + """) + response.status_code = 503 + response.content_type = 'text/html' + return res diff --git a/frappe/website/render.py b/frappe/website/render.py index 03b1bb1722..c82d6a8d98 100644 --- a/frappe/website/render.py +++ b/frappe/website/render.py @@ -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