1 # -*- coding: utf-8 -*-
2 #----------------------------------------------------------
4 #----------------------------------------------------------
28 import werkzeug.contrib.sessions
29 import werkzeug.datastructures
30 import werkzeug.exceptions
32 import werkzeug.routing
33 import werkzeug.wrappers
37 from openerp.service import security, model as service_model
38 from openerp.tools import config
40 _logger = logging.getLogger(__name__)
42 #----------------------------------------------------------
44 #----------------------------------------------------------
45 class WebRequest(object):
46 """ Parent class for all OpenERP Web request types, mostly deals with
47 initialization and setup of the request object (the dispatching itself has
48 to be handled by the subclasses)
50 :param request: a wrapped werkzeug Request object
51 :type request: :class:`werkzeug.wrappers.BaseRequest`
53 .. attribute:: httprequest
55 the original :class:`werkzeug.wrappers.Request` object provided to the
58 .. attribute:: httpsession
62 Use ``self.session`` instead.
66 :class:`~collections.Mapping` of request parameters, not generally
67 useful as they're provided directly to the handler method as keyword
70 .. attribute:: session_id
72 opaque identifier for the :class:`session.OpenERPSession` instance of
75 .. attribute:: session
77 a :class:`OpenERPSession` holding the HTTP session data for the
80 .. attribute:: context
82 :class:`~collections.Mapping` of context values for the current request
86 ``str``, the name of the database linked to the current request. Can be ``None``
87 if the current request uses the ``none`` authentication.
91 ``int``, the id of the user related to the current request. Can be ``None``
92 if the current request uses the ``none`` authenticatoin.
94 def __init__(self, httprequest):
95 self.httprequest = httprequest
96 self.httpresponse = None
97 self.httpsession = httprequest.session
98 self.session = httprequest.session
99 self.session_id = httprequest.session.sid
100 self.disable_db = False
103 self.func_arguments = {}
104 self.auth_method = None
107 self.func_request_type = None
108 # set db/uid trackers - they're cleaned up at the WSGI
109 # dispatching phase in openerp.service.wsgi_server.application
111 threading.current_thread().dbname = self.db
113 threading.current_thread().uid = self.session.uid
114 self.context = dict(self.session.context)
115 self.lang = self.context["lang"]
120 The registry to the database linked to this request. Can be ``None`` if the current request uses the
121 ``none'' authentication.
123 return openerp.modules.registry.RegistryManager.get(self.db) if self.db else None
128 The registry to the database linked to this request. Can be ``None`` if the current request uses the
129 ``none'' authentication.
131 return self.session.db if not self.disable_db else None
136 The cursor initialized for the current method call. If the current request uses the ``none`` authentication
137 trying to access this property will raise an exception.
139 # some magic to lazy create the cr
141 self._cr_cm = self.registry.cursor()
142 self._cr = self._cr_cm.__enter__()
145 def set_handler(self, func, arguments, auth):
147 arguments = dict((k, v) for k, v in arguments.iteritems()
148 if not k.startswith("_ignored_"))
151 self.func_request_type = func.exposed
152 self.func_arguments = arguments
153 self.auth_method = auth
155 def _call_function(self, *args, **kwargs):
157 # ugly syntax only to get the __exit__ arguments to pass to self._cr
159 class with_obj(object):
162 def __exit__(self, *args):
164 request._cr_cm.__exit__(*args)
165 request._cr_cm = None
169 if self.func_request_type != self._request_type:
170 raise Exception("%s, %s: Function declared as capable of handling request of type '%s' but called with a request of type '%s'" \
171 % (self.func, self.httprequest.path, self.func_request_type, self._request_type))
173 kwargs.update(self.func_arguments)
176 if getattr(self.func, '_first_arg_is_req', False):
177 args = (request,) + args
178 # Correct exception handling and concurency retry
180 def checked_call(dbname, *a, **kw):
181 return self.func(*a, **kw)
183 return checked_call(self.db, *args, **kwargs)
184 return self.func(*args, **kwargs)
186 # just to be sure no one tries to re-use the request
187 self.disable_db = True
192 return 'debug' in self.httprequest.args
194 @contextlib.contextmanager
195 def registry_cr(self):
196 warnings.warn('please use request.registry and request.cr directly', DeprecationWarning)
197 yield (self.registry, self.cr)
199 def route(route, type="http", auth="user"):
201 Decorator marking the decorated method as being a handler for requests. The method must be part of a subclass
204 :param route: string or array. The route part that will determine which http requests will match the decorated
205 method. Can be a single string or an array of strings. See werkzeug's routing documentation for the format of
206 route expression ( http://werkzeug.pocoo.org/docs/routing/ ).
207 :param type: The type of request, can be ``'http'`` or ``'json'``.
208 :param auth: The type of authentication method, can on of the following:
210 * ``user``: The user must be authenticated and the current request will perform using the rights of the
212 * ``admin``: The user may not be authenticated and the current request will perform using the admin user.
213 * ``none``: The method is always active, even if there is no database. Mainly used by the framework and
214 authentication modules. There request code will not have any facilities to access the database nor have any
215 configuration indicating the current database nor the current user.
217 assert type in ["http", "json"]
219 if isinstance(route, list):
224 if getattr(f, "auth", None) is None:
229 class JsonRequest(WebRequest):
230 """ JSON-RPC2 over HTTP.
234 --> {"jsonrpc": "2.0",
236 "params": {"context": {},
240 <-- {"jsonrpc": "2.0",
241 "result": { "res1": "val1" },
244 Request producing a error::
246 --> {"jsonrpc": "2.0",
248 "params": {"context": {},
252 <-- {"jsonrpc": "2.0",
254 "message": "End user error message.",
255 "data": {"code": "codestring",
256 "debug": "traceback" } },
260 _request_type = "json"
262 def __init__(self, *args):
263 super(JsonRequest, self).__init__(*args)
265 self.jsonp_handler = None
267 args = self.httprequest.args
268 jsonp = args.get('jsonp')
271 request_id = args.get('id')
273 if jsonp and self.httprequest.method == 'POST':
274 # jsonp 2 steps step1 POST: save call
276 self.session.jsonp_requests[request_id] = self.httprequest.form['r']
277 self.session.modified = True
278 headers=[('Content-Type', 'text/plain; charset=utf-8')]
279 r = werkzeug.wrappers.Response(request_id, headers=headers)
281 self.jsonp_handler = handler
283 elif jsonp and args.get('r'):
285 request = args.get('r')
286 elif jsonp and request_id:
287 # jsonp 2 steps step2 GET: run and return result
288 request = self.session.jsonp_requests.pop(request_id, "")
291 request = self.httprequest.stream.read()
293 # Read POST content or POST Form Data named "request"
294 self.jsonrequest = simplejson.loads(request)
295 self.params = dict(self.jsonrequest.get("params", {}))
296 self.context = self.params.pop('context', self.session.context)
299 """ Calls the method asked for by the JSON-RPC2 or JSONP request
301 if self.jsonp_handler:
302 return self.jsonp_handler()
303 response = {"jsonrpc": "2.0" }
307 response['id'] = self.jsonrequest.get('id')
308 response["result"] = self._call_function(**self.params)
309 except AuthenticationError, e:
310 _logger.exception("Exception during JSON request handling.")
311 se = serialize_exception(e)
314 'message': "OpenERP Session Invalid",
318 _logger.exception("Exception during JSON request handling.")
319 se = serialize_exception(e)
322 'message': "OpenERP Server Error",
326 response["error"] = error
329 # If we use jsonp, that's mean we are called from another host
330 # Some browser (IE and Safari) do no allow third party cookies
331 # We need then to manage http sessions manually.
332 response['session_id'] = self.session_id
333 mime = 'application/javascript'
334 body = "%s(%s);" % (self.jsonp, simplejson.dumps(response),)
336 mime = 'application/json'
337 body = simplejson.dumps(response)
339 r = werkzeug.wrappers.Response(body, headers=[('Content-Type', mime), ('Content-Length', len(body))])
342 def serialize_exception(e):
344 "name": type(e).__module__ + "." + type(e).__name__ if type(e).__module__ else type(e).__name__,
345 "debug": traceback.format_exc(),
346 "message": u"%s" % e,
347 "arguments": to_jsonable(e.args),
349 if isinstance(e, openerp.osv.osv.except_osv):
350 tmp["exception_type"] = "except_osv"
351 elif isinstance(e, openerp.exceptions.Warning):
352 tmp["exception_type"] = "warning"
353 elif isinstance(e, openerp.exceptions.AccessError):
354 tmp["exception_type"] = "access_error"
355 elif isinstance(e, openerp.exceptions.AccessDenied):
356 tmp["exception_type"] = "access_denied"
360 if isinstance(o, str) or isinstance(o,unicode) or isinstance(o, int) or isinstance(o, long) \
361 or isinstance(o, bool) or o is None or isinstance(o, float):
363 if isinstance(o, list) or isinstance(o, tuple):
364 return [to_jsonable(x) for x in o]
365 if isinstance(o, dict):
367 for k, v in o.items():
368 tmp[u"%s" % k] = to_jsonable(v)
376 Use the ``route()`` decorator instead.
379 base = f.__name__.lstrip('/')
380 if f.__name__ == "index":
382 return route([base, base + "/<path:_ignored_path>"], type="json", auth="user")(f)
384 class HttpRequest(WebRequest):
385 """ Regular GET/POST request
387 _request_type = "http"
389 def __init__(self, *args):
390 super(HttpRequest, self).__init__(*args)
391 params = dict(self.httprequest.args)
392 params.update(self.httprequest.form)
393 params.update(self.httprequest.files)
394 params.pop('session_id', None)
399 r = self._call_function(**self.params)
400 except werkzeug.exceptions.HTTPException, e:
403 _logger.exception("An exception occured during an http request")
404 se = serialize_exception(e)
407 'message': "OpenERP Server Error",
410 r = werkzeug.exceptions.InternalServerError(cgi.escape(simplejson.dumps(error)))
413 r = werkzeug.wrappers.Response(status=204) # no content
416 def make_response(self, data, headers=None, cookies=None):
417 """ Helper for non-HTML responses, or HTML responses with custom
418 response headers or cookies.
420 While handlers can just return the HTML markup of a page they want to
421 send as a string if non-HTML data is returned they need to create a
422 complete response object, or the returned data will not be correctly
423 interpreted by the clients.
425 :param basestring data: response body
426 :param headers: HTTP headers to set on the response
427 :type headers: ``[(name, value)]``
428 :param collections.Mapping cookies: cookies to set on the client
430 response = werkzeug.wrappers.Response(data, headers=headers)
432 for k, v in cookies.iteritems():
433 response.set_cookie(k, v)
436 def not_found(self, description=None):
437 """ Helper for 404 response, return its result from the method
439 return werkzeug.exceptions.NotFound(description)
445 Use the ``route()`` decorator instead.
448 base = f.__name__.lstrip('/')
449 if f.__name__ == "index":
451 return route([base, base + "/<path:_ignored_path>"], type="http", auth="user")(f)
453 #----------------------------------------------------------
454 # Thread local global request object
455 #----------------------------------------------------------
456 _request_stack = werkzeug.local.LocalStack()
458 request = _request_stack()
460 A global proxy that always redirect to the current request object.
463 @contextlib.contextmanager
464 def set_request(req):
465 _request_stack.push(req)
471 #----------------------------------------------------------
472 # Controller and route registration
473 #----------------------------------------------------------
476 controllers_per_module = collections.defaultdict(list)
478 class ControllerType(type):
479 def __init__(cls, name, bases, attrs):
480 super(ControllerType, cls).__init__(name, bases, attrs)
482 # flag old-style methods with req as first argument
483 for k, v in attrs.items():
484 if inspect.isfunction(v):
485 spec = inspect.getargspec(v)
486 first_arg = spec.args[1] if len(spec.args) >= 2 else None
487 if first_arg in ["req", "request"]:
488 v._first_arg_is_req = True
490 # store the controller in the controllers list
491 name_class = ("%s.%s" % (cls.__module__, cls.__name__), cls)
492 class_path = name_class[0].split(".")
493 if not class_path[:2] == ["openerp", "addons"]:
496 # we want to know all modules that have controllers
497 module = class_path[2]
498 # but we only store controllers directly inheriting from Controller
499 if not "Controller" in globals() or not Controller in bases:
501 controllers_per_module[module].append(name_class)
503 class Controller(object):
504 __metaclass__ = ControllerType
506 def routing_map(modules, nodb_only, converters=None):
507 routing_map = werkzeug.routing.Map(strict_slashes=False, converters=converters)
508 for module in modules:
509 if module not in controllers_per_module:
512 for _, cls in controllers_per_module[module]:
513 subclasses = cls.__subclasses__()
514 subclasses = [c for c in subclasses if c.__module__.startswith('openerp.addons.') and c.__module__.split(".")[2] in modules]
516 name = "%s (extended by %s)" % (cls.__name__, ', '.join(sub.__name__ for sub in subclasses))
517 cls = type(name, tuple(reversed(subclasses)), {})
520 members = inspect.getmembers(o)
521 for mk, mv in members:
522 if inspect.ismethod(mv) and getattr(mv, 'exposed', False) and (not nodb_only or nodb_only == (mv.auth == "none")):
523 for url in mv.routes:
524 if getattr(mv, "combine", False):
525 url = o._cp_path.rstrip('/') + '/' + url.lstrip('/')
526 if url.endswith("/") and len(url) > 1:
528 routing_map.add(werkzeug.routing.Rule(url, endpoint=mv))
531 #----------------------------------------------------------
533 #----------------------------------------------------------
534 class AuthenticationError(Exception):
537 class SessionExpiredException(Exception):
540 class Service(object):
543 Use ``openerp.netsvc.dispatch_rpc()`` instead.
545 def __init__(self, session, service_name):
546 self.session = session
547 self.service_name = service_name
549 def __getattr__(self, method):
550 def proxy_method(*args):
551 result = openerp.netsvc.dispatch_rpc(self.service_name, method, args)
558 Use the resistry and cursor in ``openerp.addons.web.http.request`` instead.
560 def __init__(self, session, model):
561 self.session = session
563 self.proxy = self.session.proxy('object')
565 def __getattr__(self, method):
566 self.session.assert_valid()
567 def proxy(*args, **kw):
568 # Can't provide any retro-compatibility for this case, so we check it and raise an Exception
569 # to tell the programmer to adapt his code
570 if not request.db or not request.uid or self.session.db != request.db \
571 or self.session.uid != request.uid:
572 raise Exception("Trying to use Model with badly configured database or user.")
574 mod = request.registry.get(self.model)
575 if method.startswith('_'):
576 raise Exception("Access denied")
577 meth = getattr(mod, method)
579 result = meth(cr, request.uid, *args, **kw)
582 if isinstance(result, list) and len(result) > 0 and "id" in result[0]:
586 result = [index[x] for x in args[0] if x in index]
590 class OpenERPSession(werkzeug.contrib.sessions.Session):
591 def __init__(self, *args, **kwargs):
593 self.modified = False
594 super(OpenERPSession, self).__init__(*args, **kwargs)
596 self._default_values()
597 self.modified = False
599 def __getattr__(self, attr):
600 return self.get(attr, None)
601 def __setattr__(self, k, v):
602 if getattr(self, "inited", False):
604 object.__getattribute__(self, k)
606 return self.__setitem__(k, v)
607 object.__setattr__(self, k, v)
609 def authenticate(self, db, login=None, password=None, uid=None):
611 Authenticate the current user with the given db, login and password. If successful, store
612 the authentication parameters in the current session and request.
614 :param uid: If not None, that user id will be used instead the login to authenticate the user.
618 wsgienv = request.httprequest.environ
620 base_location=request.httprequest.url_root.rstrip('/'),
621 HTTP_HOST=wsgienv['HTTP_HOST'],
622 REMOTE_ADDR=wsgienv['REMOTE_ADDR'],
624 uid = openerp.netsvc.dispatch_rpc('common', 'authenticate', [db, login, password, env])
626 security.check(db, uid, password)
630 self.password = password
632 request.disable_db = False
634 if uid: self.get_context()
637 def check_security(self):
639 Chech the current authentication parameters to know if those are still valid. This method
640 should be called at each request. If the authentication fails, a ``SessionExpiredException``
643 if not self.db or not self.uid:
644 raise SessionExpiredException("Session expired")
645 security.check(self.db, self.uid, self.password)
648 for k in self.keys():
650 self._default_values()
652 def _default_values(self):
653 self.setdefault("db", None)
654 self.setdefault("uid", None)
655 self.setdefault("login", None)
656 self.setdefault("password", None)
657 self.setdefault("context", {'tz': "UTC", "uid": None})
658 self.setdefault("jsonp_requests", {})
660 def get_context(self):
662 Re-initializes the current user's session context (based on
663 his preferences) by calling res.users.get_context() with the old
666 :returns: the new context
668 assert self.uid, "The user needs to be logged-in to initialize his context"
669 self.context = request.registry.get('res.users').context_get(request.cr, request.uid) or {}
670 self.context['uid'] = self.uid
671 self._fix_lang(self.context)
674 def _fix_lang(self, context):
675 """ OpenERP provides languages which may not make sense and/or may not
676 be understood by the web client's libraries.
680 :param dict context: context to fix
682 lang = context['lang']
684 # inane OpenERP locale
688 # lang to lang_REGION (datejs only handles lang_REGION, no bare langs)
689 if lang in babel.core.LOCALE_ALIASES:
690 lang = babel.core.LOCALE_ALIASES[lang]
692 context['lang'] = lang or 'en_US'
694 # Deprecated to be removed in 9
697 Damn properties for retro-compatibility. All of that is deprecated, all
704 def _db(self, value):
710 def _uid(self, value):
716 def _login(self, value):
722 def _password(self, value):
723 self.password = value
725 def send(self, service_name, method, *args):
728 Use ``openerp.netsvc.dispatch_rpc()`` instead.
730 return openerp.netsvc.dispatch_rpc(service_name, method, args)
732 def proxy(self, service):
735 Use ``openerp.netsvc.dispatch_rpc()`` instead.
737 return Service(self, service)
739 def assert_valid(self, force=False):
742 Use ``check_security()`` instead.
744 Ensures this session is valid (logged into the openerp server)
746 if self.uid and not force:
748 # TODO use authenticate instead of login
749 self.uid = self.proxy("common").login(self.db, self.login, self.password)
751 raise AuthenticationError("Authentication failure")
753 def ensure_valid(self):
756 Use ``check_security()`` instead.
760 self.assert_valid(True)
764 def execute(self, model, func, *l, **d):
767 Use the resistry and cursor in ``openerp.addons.web.http.request`` instead.
769 model = self.model(model)
770 r = getattr(model, func)(*l, **d)
773 def exec_workflow(self, model, id, signal):
776 Use the resistry and cursor in ``openerp.addons.web.http.request`` instead.
779 r = self.proxy('object').exec_workflow(self.db, self.uid, self.password, model, signal, id)
782 def model(self, model):
785 Use the resistry and cursor in ``openerp.addons.web.http.request`` instead.
787 Get an RPC proxy for the object ``model``, bound to this session.
789 :param model: an OpenERP model name
791 :rtype: a model object
794 raise SessionExpiredException("Session expired")
796 return Model(self, model)
798 def session_gc(session_store):
799 if random.random() < 0.001:
800 # we keep session one week
801 last_week = time.time() - 60*60*24*7
802 for fname in os.listdir(session_store.path):
803 path = os.path.join(session_store.path, fname)
805 if os.path.getmtime(path) < last_week:
810 #----------------------------------------------------------
812 #----------------------------------------------------------
813 # Add potentially missing (older ubuntu) font mime types
814 mimetypes.add_type('application/font-woff', '.woff')
815 mimetypes.add_type('application/vnd.ms-fontobject', '.eot')
816 mimetypes.add_type('application/x-font-ttf', '.ttf')
818 class DisableCacheMiddleware(object):
819 def __init__(self, app):
821 def __call__(self, environ, start_response):
822 def start_wrapped(status, headers):
823 referer = environ.get('HTTP_REFERER', '')
824 parsed = urlparse.urlparse(referer)
825 debug = parsed.query.count('debug') >= 1
828 unwanted_keys = ['Last-Modified']
830 new_headers = [('Cache-Control', 'no-cache')]
831 unwanted_keys += ['Expires', 'Etag', 'Cache-Control']
834 if k not in unwanted_keys:
835 new_headers.append((k, v))
837 start_response(status, new_headers)
838 return self.app(environ, start_wrapped)
843 username = pwd.getpwuid(os.geteuid()).pw_name
846 username = getpass.getuser()
849 path = os.path.join(tempfile.gettempdir(), "oe-sessions-" + username)
852 except OSError as exc:
853 if exc.errno == errno.EEXIST:
854 # directory exists: ensure it has the correct permissions
855 # this will fail if the directory is not owned by the current user
862 """Root WSGI application for the OpenERP Web Client.
865 # Setup http sessions
866 path = session_path()
867 _logger.debug('HTTP sessions stored in: %s', path)
868 self.session_store = werkzeug.contrib.sessions.FilesystemSessionStore(path, session_class=OpenERPSession)
870 # TODO should we move this to ir.http so that only configured modules are served ?
871 _logger.info("HTTP Configuring static files")
874 _logger.info("Generating nondb routing")
875 self.nodb_routing_map = routing_map(['', "web"], True)
877 def __call__(self, environ, start_response):
878 """ Handle a WSGI request
880 return self.dispatch(environ, start_response)
882 def load_addons(self):
883 """ Load all addons from addons patch containg static files and
884 controllers and configure them. """
887 for addons_path in openerp.modules.module.ad_paths:
888 for module in sorted(os.listdir(str(addons_path))):
889 if module not in addons_module:
890 manifest_path = os.path.join(addons_path, module, '__openerp__.py')
891 path_static = os.path.join(addons_path, module, 'static')
892 if os.path.isfile(manifest_path) and os.path.isdir(path_static):
893 manifest = ast.literal_eval(open(manifest_path).read())
894 manifest['addons_path'] = addons_path
895 _logger.debug("Loading %s", module)
896 if 'openerp.addons' in sys.modules:
897 m = __import__('openerp.addons.' + module)
898 addons_module[module] = m
899 addons_manifest[module] = manifest
900 statics['/%s/static' % module] = path_static
902 app = werkzeug.wsgi.SharedDataMiddleware(self.dispatch, statics)
903 self.dispatch = DisableCacheMiddleware(app)
905 def setup_session(self, httprequest):
906 # recover or create session
907 session_gc(self.session_store)
909 sid = httprequest.args.get('session_id')
910 explicit_session = True
912 sid = httprequest.headers.get("X-Openerp-Session-Id")
914 sid = httprequest.cookies.get('session_id')
915 explicit_session = False
917 httprequest.session = self.session_store.new()
919 httprequest.session = self.session_store.get(sid)
920 return explicit_session
922 def setup_db(self, httprequest):
923 # if no db is found on the session try to deduce it from the domain
924 db = db_monodb(httprequest)
925 if db != httprequest.session.db:
926 httprequest.session.logout()
927 httprequest.session.db = db
929 def setup_lang(self, httprequest):
930 if not "lang" in httprequest.session.context:
931 lang = httprequest.accept_languages.best or "en_US"
932 lang = babel.core.LOCALE_ALIASES.get(lang, lang).replace('-', '_')
933 httprequest.session.context["lang"] = lang
935 def get_request(self, httprequest):
936 # deduce type of request
937 if httprequest.args.get('jsonp'):
938 return JsonRequest(httprequest)
939 if httprequest.mimetype == "application/json":
940 return JsonRequest(httprequest)
942 return HttpRequest(httprequest)
944 def get_response(self, httprequest, result, explicit_session):
945 if isinstance(result, basestring):
946 response = werkzeug.wrappers.Response(result, mimetype='text/html')
950 if httprequest.session.should_save:
951 self.session_store.save(httprequest.session)
952 # We must not set the cookie if the session id was specified using a http header or a GET parameter.
953 # There are two reasons to this:
954 # - When using one of those two means we consider that we are overriding the cookie, which means creating a new
955 # session on top of an already existing session and we don't want to create a mess with the 'normal' session
956 # (the one using the cookie). That is a special feature of the Session Javascript class.
957 # - It could allow session fixation attacks.
958 if not explicit_session and hasattr(response, 'set_cookie'):
959 response.set_cookie('session_id', httprequest.session.sid, max_age=90 * 24 * 60 * 60)
963 def dispatch(self, environ, start_response):
965 Performs the actual WSGI dispatching for the application.
968 httprequest = werkzeug.wrappers.Request(environ)
969 httprequest.parameter_storage_class = werkzeug.datastructures.ImmutableDict
970 httprequest.app = self
972 explicit_session = self.setup_session(httprequest)
973 self.setup_db(httprequest)
974 self.setup_lang(httprequest)
976 request = self.get_request(httprequest)
978 with set_request(request):
979 db = request.session.db
981 openerp.modules.registry.RegistryManager.check_registry_signaling(db)
982 result = request.registry['ir.http']._dispatch()
983 openerp.modules.registry.RegistryManager.signal_caches_change(db)
985 # fallback to non-db handlers
986 func, arguments = self.nodb_routing_map.bind_to_environ(request.httprequest.environ).match()
987 request.set_handler(func, arguments, "none")
988 result = request.dispatch()
989 response = self.get_response(httprequest, result, explicit_session)
990 return response(environ, start_response)
992 except werkzeug.exceptions.HTTPException, e:
993 return e(environ, start_response)
995 def get_db_router(self, db):
997 return self.nodb_routing_map
998 return request.registry['ir.http'].routing_map()
1000 def db_list(force=False, httprequest=None):
1001 httprequest = httprequest or request.httprequest
1002 dbs = openerp.netsvc.dispatch_rpc("db", "list", [force])
1003 h = httprequest.environ['HTTP_HOST'].split(':')[0]
1005 r = openerp.tools.config['dbfilter'].replace('%h', h).replace('%d', d)
1006 dbs = [i for i in dbs if re.match(r, i)]
1009 def db_monodb(httprequest=None):
1011 Magic function to find the current database.
1013 Implementation details:
1018 Returns ``None`` if the magic is not magic enough.
1020 httprequest = httprequest or request.httprequest
1024 dbs = db_list(True, httprequest)
1026 # try the db already in the session
1027 db_session = httprequest.session.db
1028 if db_session in dbs:
1031 # if dbfilters was specified when launching the server and there is
1032 # only one possible db, we take that one
1033 if openerp.tools.config['dbfilter'] != ".*" and len(dbs) == 1:
1037 #----------------------------------------------------------
1039 #----------------------------------------------------------
1040 class CommonController(Controller):
1042 @route('/jsonrpc', type='json', auth="none")
1043 def jsonrpc(self, service, method, args):
1044 """ Method used by client APIs to contact OpenERP. """
1045 return openerp.netsvc.dispatch_rpc(service, method, args)
1047 @route('/gen_session_id', type='json', auth="none")
1048 def gen_session_id(self):
1049 nsession = root.session_store.new()
1054 def wsgi_postload():
1057 openerp.wsgi.register_wsgi_handler(root)