diff --git a/addons/web/__init__.py b/addons/web/__init__.py index e01969ddcca..5a0289cef6d 100644 --- a/addons/web/__init__.py +++ b/addons/web/__init__.py @@ -1,4 +1,10 @@ -import http +import sys + +# Mock deprecated openerp.addons.web.http module +import openerp.http +sys.modules['openerp.addons.web.http'] = openerp.http +http = openerp.http + import controllers import cli diff --git a/addons/web/controllers/main.py b/addons/web/controllers/main.py index 5ed0c8326f4..e9a6aa29f65 100644 --- a/addons/web/controllers/main.py +++ b/addons/web/controllers/main.py @@ -33,10 +33,9 @@ import openerp import openerp.modules.registry from openerp.tools.translate import _ from openerp.tools import config +from openerp import http -from .. import http - -from openerp.addons.web.http import request +from openerp.http import request #---------------------------------------------------------- # OpenERP Web helpers @@ -91,13 +90,16 @@ db_list = http.db_list db_monodb = http.db_monodb def redirect_with_hash(url, code=303): + redirect_code = "
" % url if request.httprequest.user_agent.browser == 'msie': try: version = float(request.httprequest.user_agent.version) if version < 10: - return "" % url + return redirect_code except Exception: pass + elif request.httprequest.user_agent.browser == 'safari': + return redirect_code return werkzeug.utils.redirect(url, code) def module_topological_sort(modules): diff --git a/addons/web/controllers/testing.py b/addons/web/controllers/testing.py index b51b12fc53d..0160f0f28cd 100644 --- a/addons/web/controllers/testing.py +++ b/addons/web/controllers/testing.py @@ -9,11 +9,10 @@ import os from mako.template import Template from openerp.modules import module +from openerp import http +from openerp.http import request from .main import module_topological_sort -from .. import http - -from ..http import request NOMODULE_TEMPLATE = Template(u""" @@ -171,4 +170,4 @@ class TestRunnerController(http.Controller): @http.route('/web/tests/get_session_value', type='json', auth="none") def get_session_value(self): - return request.session.some_test_value \ No newline at end of file + return request.session.some_test_value diff --git a/addons/web/doc/web_controllers.rst b/addons/web/doc/web_controllers.rst index 71ff9fb8416..442d2b0caa7 100644 --- a/addons/web/doc/web_controllers.rst +++ b/addons/web/doc/web_controllers.rst @@ -35,8 +35,8 @@ Now you can put the following content in ``controllers/my_controllers.py``: :: - import openerp.addons.web.http as http - from openerp.addons.web.http import request + import openerp.http as http + from openerp.http import request Controller Declaration diff --git a/addons/web/http.py b/addons/web/http.py deleted file mode 100644 index 7bad66cb7c4..00000000000 --- a/addons/web/http.py +++ /dev/null @@ -1,1116 +0,0 @@ -# -*- coding: utf-8 -*- -#---------------------------------------------------------- -# OpenERP Web HTTP layer -#---------------------------------------------------------- -import ast -import cgi -import contextlib -import functools -import getpass -import logging -import mimetypes -import os -import pprint -import random -import sys -import tempfile -import threading -import time -import traceback -import urlparse -import uuid -import errno -import re -import warnings - -import babel.core -import simplejson -import werkzeug.contrib.sessions -import werkzeug.datastructures -import werkzeug.exceptions -import werkzeug.utils -import werkzeug.wrappers -import werkzeug.wsgi -import werkzeug.routing as routing -import urllib -import urllib2 - -import openerp -from openerp.service import security, model as service_model -from openerp.tools import config - -import inspect -import functools - -_logger = logging.getLogger(__name__) - -#---------------------------------------------------------- -# RequestHandler -#---------------------------------------------------------- -class WebRequest(object): - """ Parent class for all OpenERP Web request types, mostly deals with - initialization and setup of the request object (the dispatching itself has - to be handled by the subclasses) - - :param request: a wrapped werkzeug Request object - :type request: :class:`werkzeug.wrappers.BaseRequest` - - .. attribute:: httprequest - - the original :class:`werkzeug.wrappers.Request` object provided to the - request - - .. attribute:: httpsession - - .. deprecated:: 8.0 - - Use ``self.session`` instead. - - .. attribute:: params - - :class:`~collections.Mapping` of request parameters, not generally - useful as they're provided directly to the handler method as keyword - arguments - - .. attribute:: session_id - - opaque identifier for the :class:`session.OpenERPSession` instance of - the current request - - .. attribute:: session - - a :class:`OpenERPSession` holding the HTTP session data for the - current http session - - .. attribute:: context - - :class:`~collections.Mapping` of context values for the current request - - .. attribute:: db - - ``str``, the name of the database linked to the current request. Can be ``None`` - if the current request uses the ``none`` authentication. - - .. attribute:: uid - - ``int``, the id of the user related to the current request. Can be ``None`` - if the current request uses the ``none`` authenticatoin. - """ - def __init__(self, httprequest): - self.httprequest = httprequest - self.httpresponse = None - self.httpsession = httprequest.session - self.session = httprequest.session - self.session_id = httprequest.session.sid - self.disable_db = False - self.uid = None - self.func = None - self.auth_method = None - self._cr_cm = None - self._cr = None - self.func_request_type = None - # set db/uid trackers - they're cleaned up at the WSGI - # dispatching phase in openerp.service.wsgi_server.application - if self.db: - threading.current_thread().dbname = self.db - if self.session.uid: - threading.current_thread().uid = self.session.uid - self.context = dict(self.session.context) - self.lang = self.context["lang"] - - def _authenticate(self): - if self.session.uid: - try: - self.session.check_security() - except SessionExpiredException, e: - self.session.logout() - raise SessionExpiredException("Session expired for request %s" % self.httprequest) - auth_methods[self.auth_method]() - @property - def registry(self): - """ - The registry to the database linked to this request. Can be ``None`` if the current request uses the - ``none'' authentication. - """ - return openerp.modules.registry.RegistryManager.get(self.db) if self.db else None - - @property - def db(self): - """ - The registry to the database linked to this request. Can be ``None`` if the current request uses the - ``none'' authentication. - """ - return self.session.db if not self.disable_db else None - - @property - def cr(self): - """ - The cursor initialized for the current method call. If the current request uses the ``none`` authentication - trying to access this property will raise an exception. - """ - # some magic to lazy create the cr - if not self._cr_cm: - self._cr_cm = self.registry.cursor() - self._cr = self._cr_cm.__enter__() - return self._cr - - def _call_function(self, *args, **kwargs): - self._authenticate() - try: - # ugly syntax only to get the __exit__ arguments to pass to self._cr - request = self - class with_obj(object): - def __enter__(self): - pass - def __exit__(self, *args): - if request._cr_cm: - request._cr_cm.__exit__(*args) - request._cr_cm = None - request._cr = None - - with with_obj(): - if self.func_request_type != self._request_type: - raise Exception("%s, %s: Function declared as capable of handling request of type '%s' but called with a request of type '%s'" \ - % (self.func, self.httprequest.path, self.func_request_type, self._request_type)) - return self.func(*args, **kwargs) - finally: - # just to be sure no one tries to re-use the request - self.disable_db = True - self.uid = None - - @property - def debug(self): - return 'debug' in self.httprequest.args - - @contextlib.contextmanager - def registry_cr(self): - warnings.warn('please use request.registry and request.cr directly', DeprecationWarning) - yield (self.registry, self.cr) - -def auth_method_user(): - request.uid = request.session.uid - if not request.uid: - raise SessionExpiredException("Session expired") - -def auth_method_admin(): - if not request.db: - raise SessionExpiredException("No valid database for request %s" % request.httprequest) - request.uid = openerp.SUPERUSER_ID - -def auth_method_none(): - request.disable_db = True - request.uid = None - -auth_methods = { - "user": auth_method_user, - "admin": auth_method_admin, - "none": auth_method_none, -} - -def route(route, type="http", auth="user"): - """ - Decorator marking the decorated method as being a handler for requests. The method must be part of a subclass - of ``Controller``. - - :param route: string or array. The route part that will determine which http requests will match the decorated - method. Can be a single string or an array of strings. See werkzeug's routing documentation for the format of - route expression ( http://werkzeug.pocoo.org/docs/routing/ ). - :param type: The type of request, can be ``'http'`` or ``'json'``. - :param auth: The type of authentication method, can on of the following: - - * ``user``: The user must be authenticated and the current request will perform using the rights of the - user. - * ``admin``: The user may not be authenticated and the current request will perform using the admin user. - * ``none``: The method is always active, even if there is no database. Mainly used by the framework and - authentication modules. There request code will not have any facilities to access the database nor have any - configuration indicating the current database nor the current user. - """ - assert type in ["http", "json"] - assert auth in auth_methods.keys() - def decorator(f): - if isinstance(route, list): - f.routes = route - else: - f.routes = [route] - f.exposed = type - if getattr(f, "auth", None) is None: - f.auth = auth - return f - return decorator - -def reject_nonliteral(dct): - if '__ref' in dct: - raise ValueError( - "Non literal contexts can not be sent to the server anymore (%r)" % (dct,)) - return dct - -class JsonRequest(WebRequest): - """ JSON-RPC2 over HTTP. - - Sucessful request:: - - --> {"jsonrpc": "2.0", - "method": "call", - "params": {"context": {}, - "arg1": "val1" }, - "id": null} - - <-- {"jsonrpc": "2.0", - "result": { "res1": "val1" }, - "id": null} - - Request producing a error:: - - --> {"jsonrpc": "2.0", - "method": "call", - "params": {"context": {}, - "arg1": "val1" }, - "id": null} - - <-- {"jsonrpc": "2.0", - "error": {"code": 1, - "message": "End user error message.", - "data": {"code": "codestring", - "debug": "traceback" } }, - "id": null} - - """ - _request_type = "json" - - def __init__(self, *args): - super(JsonRequest, self).__init__(*args) - - self.jsonp_handler = None - - args = self.httprequest.args - jsonp = args.get('jsonp') - self.jsonp = jsonp - request = None - request_id = args.get('id') - - if jsonp and self.httprequest.method == 'POST': - # jsonp 2 steps step1 POST: save call - def handler(): - self.session.jsonp_requests[request_id] = self.httprequest.form['r'] - self.session.modified = True - headers=[('Content-Type', 'text/plain; charset=utf-8')] - r = werkzeug.wrappers.Response(request_id, headers=headers) - return r - self.jsonp_handler = handler - return - elif jsonp and args.get('r'): - # jsonp method GET - request = args.get('r') - elif jsonp and request_id: - # jsonp 2 steps step2 GET: run and return result - request = self.session.jsonp_requests.pop(request_id, "") - else: - # regular jsonrpc2 - request = self.httprequest.stream.read() - - # Read POST content or POST Form Data named "request" - self.jsonrequest = simplejson.loads(request, object_hook=reject_nonliteral) - self.params = dict(self.jsonrequest.get("params", {})) - self.context = self.params.pop('context', self.session.context) - - def dispatch(self): - """ Calls the method asked for by the JSON-RPC2 or JSONP request - """ - if self.jsonp_handler: - return self.jsonp_handler() - response = {"jsonrpc": "2.0" } - error = None - - try: - response['id'] = self.jsonrequest.get('id') - response["result"] = self._call_function(**self.params) - except AuthenticationError, e: - _logger.exception("Exception during JSON request handling.") - se = serialize_exception(e) - error = { - 'code': 100, - 'message': "OpenERP Session Invalid", - 'data': se - } - except Exception, e: - _logger.exception("Exception during JSON request handling.") - se = serialize_exception(e) - error = { - 'code': 200, - 'message': "OpenERP Server Error", - 'data': se - } - if error: - response["error"] = error - - if self.jsonp: - # If we use jsonp, that's mean we are called from another host - # Some browser (IE and Safari) do no allow third party cookies - # We need then to manage http sessions manually. - response['session_id'] = self.session_id - mime = 'application/javascript' - body = "%s(%s);" % (self.jsonp, simplejson.dumps(response),) - else: - mime = 'application/json' - body = simplejson.dumps(response) - - r = werkzeug.wrappers.Response(body, headers=[('Content-Type', mime), ('Content-Length', len(body))]) - return r - -def serialize_exception(e): - tmp = { - "name": type(e).__module__ + "." + type(e).__name__ if type(e).__module__ else type(e).__name__, - "debug": traceback.format_exc(), - "message": u"%s" % e, - "arguments": to_jsonable(e.args), - } - if isinstance(e, openerp.osv.osv.except_osv): - tmp["exception_type"] = "except_osv" - elif isinstance(e, openerp.exceptions.Warning): - tmp["exception_type"] = "warning" - elif isinstance(e, openerp.exceptions.AccessError): - tmp["exception_type"] = "access_error" - elif isinstance(e, openerp.exceptions.AccessDenied): - tmp["exception_type"] = "access_denied" - return tmp - -def to_jsonable(o): - if isinstance(o, str) or isinstance(o,unicode) or isinstance(o, int) or isinstance(o, long) \ - or isinstance(o, bool) or o is None or isinstance(o, float): - return o - if isinstance(o, list) or isinstance(o, tuple): - return [to_jsonable(x) for x in o] - if isinstance(o, dict): - tmp = {} - for k, v in o.items(): - tmp[u"%s" % k] = to_jsonable(v) - return tmp - return u"%s" % o - -def jsonrequest(f): - """ - .. deprecated:: 8.0 - - Use the ``route()`` decorator instead. - """ - f.combine = True - base = f.__name__.lstrip('/') - if f.__name__ == "index": - base = "" - return route([base, base + "/