1 # -*- coding: utf-8 -*-
2 #----------------------------------------------------------
3 # OpenERP Web HTTP layer
4 #----------------------------------------------------------
27 import werkzeug.contrib.sessions
28 import werkzeug.datastructures
29 import werkzeug.exceptions
31 import werkzeug.wrappers
33 import werkzeug.routing as routing
38 import openerp.service.security as security
39 from openerp.tools import config
44 _logger = logging.getLogger(__name__)
46 #----------------------------------------------------------
48 #----------------------------------------------------------
49 class WebRequest(object):
50 """ Parent class for all OpenERP Web request types, mostly deals with
51 initialization and setup of the request object (the dispatching itself has
52 to be handled by the subclasses)
54 :param request: a wrapped werkzeug Request object
55 :type request: :class:`werkzeug.wrappers.BaseRequest`
57 .. attribute:: httprequest
59 the original :class:`werkzeug.wrappers.Request` object provided to the
62 .. attribute:: httpsession
66 Use ``self.session`` instead.
70 :class:`~collections.Mapping` of request parameters, not generally
71 useful as they're provided directly to the handler method as keyword
74 .. attribute:: session_id
76 opaque identifier for the :class:`session.OpenERPSession` instance of
79 .. attribute:: session
81 a :class:`OpenERPSession` holding the HTTP session data for the
84 .. attribute:: context
86 :class:`~collections.Mapping` of context values for the current request
90 ``str``, the name of the database linked to the current request. Can be ``None``
91 if the current request uses the ``none`` authentication.
95 ``int``, the id of the user related to the current request. Can be ``None``
96 if the current request uses the ``none`` authenticatoin.
98 def __init__(self, httprequest):
99 self.httprequest = httprequest
100 self.httpresponse = None
101 self.httpsession = httprequest.session
102 self.session = httprequest.session
103 self.session_id = httprequest.session.sid
104 self.disable_db = False
107 self.auth_method = None
110 self.func_request_type = None
111 # set db/uid trackers - they're cleaned up at the WSGI
112 # dispatching phase in openerp.service.wsgi_server.application
114 threading.current_thread().dbname = self.db
116 threading.current_thread().uid = self.session.uid
117 self.context = dict(self.session.context)
118 self.lang = self.context["lang"]
120 def _authenticate(self):
123 self.session.check_security()
124 except SessionExpiredException, e:
125 self.session.logout()
126 raise SessionExpiredException("Session expired for request %s" % self.httprequest)
127 auth_methods[self.auth_method]()
131 The registry to the database linked to this request. Can be ``None`` if the current request uses the
132 ``none'' authentication.
134 return openerp.modules.registry.RegistryManager.get(self.db) if self.db else None
139 The registry to the database linked to this request. Can be ``None`` if the current request uses the
140 ``none'' authentication.
142 return self.session.db if not self.disable_db else None
147 The cursor initialized for the current method call. If the current request uses the ``none`` authentication
148 trying to access this property will raise an exception.
150 # some magic to lazy create the cr
152 self._cr_cm = self.registry.cursor()
153 self._cr = self._cr_cm.__enter__()
156 def _call_function(self, *args, **kwargs):
159 # ugly syntax only to get the __exit__ arguments to pass to self._cr
161 class with_obj(object):
164 def __exit__(self, *args):
166 request._cr_cm.__exit__(*args)
167 request._cr_cm = None
171 if self.func_request_type != self._request_type:
172 raise Exception("%s, %s: Function declared as capable of handling request of type '%s' but called with a request of type '%s'" \
173 % (self.func, self.httprequest.path, self.func_request_type, self._request_type))
174 return self.func(*args, **kwargs)
176 # just to be sure no one tries to re-use the request
177 self.disable_db = True
182 return 'debug' in self.httprequest.args
185 def auth_method_user():
186 request.uid = request.session.uid
188 def auth_method_admin():
190 raise SessionExpiredException("No valid database for request %s" % request.httprequest)
191 request.uid = openerp.SUPERUSER_ID
193 def auth_method_none():
194 request.disable_db = True
198 "user": auth_method_user,
199 "admin": auth_method_admin,
200 "none": auth_method_none,
203 def route(route, type="http", auth="user"):
205 Decorator marking the decorated method as being a handler for requests. The method must be part of a subclass
208 :param route: string or array. The route part that will determine which http requests will match the decorated
209 method. Can be a single string or an array of strings. See werkzeug's routing documentation for the format of
210 route expression ( http://werkzeug.pocoo.org/docs/routing/ ).
211 :param type: The type of request, can be ``'http'`` or ``'json'``.
212 :param auth: The type of authentication method, can on of the following:
214 * ``user``: The user must be authenticated and the current request will perform using the rights of the
216 * ``admin``: The user may not be authenticated and the current request will perform using the admin user.
217 * ``none``: The method is always active, even if there is no database. Mainly used by the framework and
218 authentication modules. There request code will not have any facilities to access the database nor have any
219 configuration indicating the current database nor the current user.
221 assert type in ["http", "json"]
222 assert auth in auth_methods.keys()
224 if isinstance(route, list):
229 if getattr(f, "auth", None) is None:
234 def reject_nonliteral(dct):
237 "Non literal contexts can not be sent to the server anymore (%r)" % (dct,))
240 class JsonRequest(WebRequest):
241 """ JSON-RPC2 over HTTP.
245 --> {"jsonrpc": "2.0",
247 "params": {"context": {},
251 <-- {"jsonrpc": "2.0",
252 "result": { "res1": "val1" },
255 Request producing a error::
257 --> {"jsonrpc": "2.0",
259 "params": {"context": {},
263 <-- {"jsonrpc": "2.0",
265 "message": "End user error message.",
266 "data": {"code": "codestring",
267 "debug": "traceback" } },
271 _request_type = "json"
273 def __init__(self, *args):
274 super(JsonRequest, self).__init__(*args)
276 self.jsonp_handler = None
278 args = self.httprequest.args
279 jsonp = args.get('jsonp')
282 request_id = args.get('id')
284 if jsonp and self.httprequest.method == 'POST':
285 # jsonp 2 steps step1 POST: save call
287 self.session.jsonp_requests[request_id] = self.httprequest.form['r']
288 self.session.modified = True
289 headers=[('Content-Type', 'text/plain; charset=utf-8')]
290 r = werkzeug.wrappers.Response(request_id, headers=headers)
292 self.jsonp_handler = handler
294 elif jsonp and args.get('r'):
296 request = args.get('r')
297 elif jsonp and request_id:
298 # jsonp 2 steps step2 GET: run and return result
299 request = self.session.jsonp_requests.pop(request_id, "")
302 request = self.httprequest.stream.read()
304 # Read POST content or POST Form Data named "request"
305 self.jsonrequest = simplejson.loads(request, object_hook=reject_nonliteral)
306 self.params = dict(self.jsonrequest.get("params", {}))
307 self.context = self.params.pop('context', self.session.context)
310 """ Calls the method asked for by the JSON-RPC2 or JSONP request
312 if self.jsonp_handler:
313 return self.jsonp_handler()
314 response = {"jsonrpc": "2.0" }
318 response['id'] = self.jsonrequest.get('id')
319 response["result"] = self._call_function(**self.params)
320 except AuthenticationError, e:
321 _logger.exception("Exception during JSON request handling.")
322 se = serialize_exception(e)
325 'message': "OpenERP Session Invalid",
329 _logger.exception("Exception during JSON request handling.")
330 se = serialize_exception(e)
333 'message': "OpenERP Server Error",
337 response["error"] = error
340 # If we use jsonp, that's mean we are called from another host
341 # Some browser (IE and Safari) do no allow third party cookies
342 # We need then to manage http sessions manually.
343 response['session_id'] = self.session_id
344 mime = 'application/javascript'
345 body = "%s(%s);" % (self.jsonp, simplejson.dumps(response),)
347 mime = 'application/json'
348 body = simplejson.dumps(response)
350 r = werkzeug.wrappers.Response(body, headers=[('Content-Type', mime), ('Content-Length', len(body))])
353 def serialize_exception(e):
355 "name": type(e).__module__ + "." + type(e).__name__ if type(e).__module__ else type(e).__name__,
356 "debug": traceback.format_exc(),
357 "message": u"%s" % e,
358 "arguments": to_jsonable(e.args),
360 if isinstance(e, openerp.osv.osv.except_osv):
361 tmp["exception_type"] = "except_osv"
362 elif isinstance(e, openerp.exceptions.Warning):
363 tmp["exception_type"] = "warning"
364 elif isinstance(e, openerp.exceptions.AccessError):
365 tmp["exception_type"] = "access_error"
366 elif isinstance(e, openerp.exceptions.AccessDenied):
367 tmp["exception_type"] = "access_denied"
371 if isinstance(o, str) or isinstance(o,unicode) or isinstance(o, int) or isinstance(o, long) \
372 or isinstance(o, bool) or o is None or isinstance(o, float):
374 if isinstance(o, list) or isinstance(o, tuple):
375 return [to_jsonable(x) for x in o]
376 if isinstance(o, dict):
378 for k, v in o.items():
379 tmp[u"%s" % k] = to_jsonable(v)
387 Use the ``route()`` decorator instead.
390 base = f.__name__.lstrip('/')
391 if f.__name__ == "index":
393 return route([base, base + "/<path:_ignored_path>"], type="json", auth="none")(f)
395 class HttpRequest(WebRequest):
396 """ Regular GET/POST request
398 _request_type = "http"
400 def __init__(self, *args):
401 super(HttpRequest, self).__init__(*args)
402 params = dict(self.httprequest.args)
403 ex = set(["session_id"])
404 for k in params.keys():
407 params.update(self.httprequest.form)
408 params.update(self.httprequest.files)
413 r = self._call_function(**self.params)
414 except werkzeug.exceptions.HTTPException, e:
417 _logger.exception("An exception occured during an http request")
418 se = serialize_exception(e)
421 'message': "OpenERP Server Error",
424 r = werkzeug.exceptions.InternalServerError(cgi.escape(simplejson.dumps(error)))
427 r = werkzeug.wrappers.Response(status=204) # no content
430 def make_response(self, data, headers=None, cookies=None):
431 """ Helper for non-HTML responses, or HTML responses with custom
432 response headers or cookies.
434 While handlers can just return the HTML markup of a page they want to
435 send as a string if non-HTML data is returned they need to create a
436 complete response object, or the returned data will not be correctly
437 interpreted by the clients.
439 :param basestring data: response body
440 :param headers: HTTP headers to set on the response
441 :type headers: ``[(name, value)]``
442 :param collections.Mapping cookies: cookies to set on the client
444 response = werkzeug.wrappers.Response(data, headers=headers)
446 for k, v in cookies.iteritems():
447 response.set_cookie(k, v)
450 def not_found(self, description=None):
451 """ Helper for 404 response, return its result from the method
453 return werkzeug.exceptions.NotFound(description)
459 Use the ``route()`` decorator instead.
462 base = f.__name__.lstrip('/')
463 if f.__name__ == "index":
465 return route([base, base + "/<path:_ignored_path>"], type="http", auth="none")(f)
467 #----------------------------------------------------------
468 # Local storage of requests
469 #----------------------------------------------------------
470 from werkzeug.local import LocalStack
472 _request_stack = LocalStack()
474 def set_request(request):
475 class with_obj(object):
477 _request_stack.push(request)
478 def __exit__(self, *args):
483 A global proxy that always redirect to the current request object.
485 request = _request_stack()
487 #----------------------------------------------------------
488 # Controller registration with a metaclass
489 #----------------------------------------------------------
492 controllers_per_module = {}
494 class ControllerType(type):
495 def __init__(cls, name, bases, attrs):
496 super(ControllerType, cls).__init__(name, bases, attrs)
498 # flag old-style methods with req as first argument
499 for k, v in attrs.items():
500 if inspect.isfunction(v):
501 spec = inspect.getargspec(v)
502 first_arg = spec.args[1] if len(spec.args) >= 2 else None
503 if first_arg in ["req", "request"]:
504 v._first_arg_is_req = True
506 # store the controller in the controllers list
507 name_class = ("%s.%s" % (cls.__module__, cls.__name__), cls)
508 class_path = name_class[0].split(".")
509 if not class_path[:2] == ["openerp", "addons"]:
511 # we want to know all modules that have controllers
512 module = class_path[2]
513 # but we only store controllers directly inheriting from Controller
514 if not "Controller" in globals() or not Controller in bases:
516 controllers_per_module.setdefault(module, []).append(name_class)
518 class Controller(object):
519 __metaclass__ = ControllerType
521 #############################
523 #############################
525 class AuthenticationError(Exception):
528 class SessionExpiredException(Exception):
531 class Service(object):
534 Use ``openerp.netsvc.dispatch_rpc()`` instead.
536 def __init__(self, session, service_name):
537 self.session = session
538 self.service_name = service_name
540 def __getattr__(self, method):
541 def proxy_method(*args):
542 result = openerp.netsvc.dispatch_rpc(self.service_name, method, args)
549 Use the resistry and cursor in ``openerp.addons.web.http.request`` instead.
551 def __init__(self, session, model):
552 self.session = session
554 self.proxy = self.session.proxy('object')
556 def __getattr__(self, method):
557 self.session.assert_valid()
558 def proxy(*args, **kw):
559 # Can't provide any retro-compatibility for this case, so we check it and raise an Exception
560 # to tell the programmer to adapt his code
561 if not request.db or not request.uid or self.session.db != request.db \
562 or self.session.uid != request.uid:
563 raise Exception("Trying to use Model with badly configured database or user.")
565 mod = request.registry.get(self.model)
566 if method.startswith('_'):
567 raise Exception("Access denied")
568 meth = getattr(mod, method)
570 result = meth(cr, request.uid, *args, **kw)
573 if isinstance(result, list) and len(result) > 0 and "id" in result[0]:
577 result = [index[x] for x in args[0] if x in index]
581 class OpenERPSession(werkzeug.contrib.sessions.Session):
582 def __init__(self, *args, **kwargs):
584 self.modified = False
585 super(OpenERPSession, self).__init__(*args, **kwargs)
587 self._default_values()
588 self.modified = False
590 def __getattr__(self, attr):
591 return self.get(attr, None)
592 def __setattr__(self, k, v):
593 if getattr(self, "inited", False):
595 object.__getattribute__(self, k)
597 return self.__setitem__(k, v)
598 object.__setattr__(self, k, v)
600 def authenticate(self, db, login=None, password=None, uid=None):
602 Authenticate the current user with the given db, login and password. If successful, store
603 the authentication parameters in the current session and request.
605 :param uid: If not None, that user id will be used instead the login to authenticate the user.
609 wsgienv = request.httprequest.environ
611 base_location=request.httprequest.url_root.rstrip('/'),
612 HTTP_HOST=wsgienv['HTTP_HOST'],
613 REMOTE_ADDR=wsgienv['REMOTE_ADDR'],
615 uid = openerp.netsvc.dispatch_rpc('common', 'authenticate', [db, login, password, env])
617 security.check(db, uid, password)
621 self.password = password
623 request.disable_db = False
625 if uid: self.get_context()
628 def check_security(self):
630 Chech the current authentication parameters to know if those are still valid. This method
631 should be called at each request. If the authentication fails, a ``SessionExpiredException``
634 if not self.db or not self.uid:
635 raise SessionExpiredException("Session expired")
636 security.check(self.db, self.uid, self.password)
639 for k in self.keys():
641 self._default_values()
643 def _default_values(self):
644 self.setdefault("db", None)
645 self.setdefault("uid", None)
646 self.setdefault("login", None)
647 self.setdefault("password", None)
648 self.setdefault("context", {'tz': "UTC", "uid": None})
649 self.setdefault("jsonp_requests", {})
651 def get_context(self):
653 Re-initializes the current user's session context (based on
654 his preferences) by calling res.users.get_context() with the old
657 :returns: the new context
659 assert self.uid, "The user needs to be logged-in to initialize his context"
660 self.context = request.registry.get('res.users').context_get(request.cr, request.uid) or {}
661 self.context['uid'] = self.uid
662 self._fix_lang(self.context)
665 def _fix_lang(self, context):
666 """ OpenERP provides languages which may not make sense and/or may not
667 be understood by the web client's libraries.
671 :param dict context: context to fix
673 lang = context['lang']
675 # inane OpenERP locale
679 # lang to lang_REGION (datejs only handles lang_REGION, no bare langs)
680 if lang in babel.core.LOCALE_ALIASES:
681 lang = babel.core.LOCALE_ALIASES[lang]
683 context['lang'] = lang or 'en_US'
686 Damn properties for retro-compatibility. All of that is deprecated, all
693 def _db(self, value):
699 def _uid(self, value):
705 def _login(self, value):
711 def _password(self, value):
712 self.password = value
714 def send(self, service_name, method, *args):
717 Use ``openerp.netsvc.dispatch_rpc()`` instead.
719 return openerp.netsvc.dispatch_rpc(service_name, method, args)
721 def proxy(self, service):
724 Use ``openerp.netsvc.dispatch_rpc()`` instead.
726 return Service(self, service)
728 def assert_valid(self, force=False):
731 Use ``check_security()`` instead.
733 Ensures this session is valid (logged into the openerp server)
735 if self.uid and not force:
737 # TODO use authenticate instead of login
738 self.uid = self.proxy("common").login(self.db, self.login, self.password)
740 raise AuthenticationError("Authentication failure")
742 def ensure_valid(self):
745 Use ``check_security()`` instead.
749 self.assert_valid(True)
753 def execute(self, model, func, *l, **d):
756 Use the resistry and cursor in ``openerp.addons.web.http.request`` instead.
758 model = self.model(model)
759 r = getattr(model, func)(*l, **d)
762 def exec_workflow(self, model, id, signal):
765 Use the resistry and cursor in ``openerp.addons.web.http.request`` instead.
768 r = self.proxy('object').exec_workflow(self.db, self.uid, self.password, model, signal, id)
771 def model(self, model):
774 Use the resistry and cursor in ``openerp.addons.web.http.request`` instead.
776 Get an RPC proxy for the object ``model``, bound to this session.
778 :param model: an OpenERP model name
780 :rtype: a model object
783 raise SessionExpiredException("Session expired")
785 return Model(self, model)
787 def session_gc(session_store):
788 if random.random() < 0.001:
789 # we keep session one week
790 last_week = time.time() - 60*60*24*7
791 for fname in os.listdir(session_store.path):
792 path = os.path.join(session_store.path, fname)
794 if os.path.getmtime(path) < last_week:
799 #----------------------------------------------------------
801 #----------------------------------------------------------
802 # Add potentially missing (older ubuntu) font mime types
803 mimetypes.add_type('application/font-woff', '.woff')
804 mimetypes.add_type('application/vnd.ms-fontobject', '.eot')
805 mimetypes.add_type('application/x-font-ttf', '.ttf')
807 class DisableCacheMiddleware(object):
808 def __init__(self, app):
810 def __call__(self, environ, start_response):
811 def start_wrapped(status, headers):
812 referer = environ.get('HTTP_REFERER', '')
813 parsed = urlparse.urlparse(referer)
814 debug = parsed.query.count('debug') >= 1
817 unwanted_keys = ['Last-Modified']
819 new_headers = [('Cache-Control', 'no-cache')]
820 unwanted_keys += ['Expires', 'Etag', 'Cache-Control']
823 if k not in unwanted_keys:
824 new_headers.append((k, v))
826 start_response(status, new_headers)
827 return self.app(environ, start_wrapped)
832 username = pwd.getpwuid(os.geteuid()).pw_name
835 username = getpass.getuser()
838 path = os.path.join(tempfile.gettempdir(), "oe-sessions-" + username)
841 except OSError as exc:
842 if exc.errno == errno.EEXIST:
843 # directory exists: ensure it has the correct permissions
844 # this will fail if the directory is not owned by the current user
851 """Root WSGI application for the OpenERP Web Client.
857 self.no_db_router = None
861 # Setup http sessions
862 path = session_path()
863 self.session_store = werkzeug.contrib.sessions.FilesystemSessionStore(path, session_class=OpenERPSession)
864 _logger.debug('HTTP sessions stored in: %s', path)
867 def __call__(self, environ, start_response):
868 """ Handle a WSGI request
870 return self.dispatch(environ, start_response)
872 def dispatch(self, environ, start_response):
874 Performs the actual WSGI dispatching for the application.
877 httprequest = werkzeug.wrappers.Request(environ)
878 httprequest.parameter_storage_class = werkzeug.datastructures.ImmutableDict
879 httprequest.app = self
881 session_gc(self.session_store)
883 sid = httprequest.args.get('session_id')
884 explicit_session = True
886 sid = httprequest.headers.get("X-Openerp-Session-Id")
888 sid = httprequest.cookies.get('session_id')
889 explicit_session = False
891 httprequest.session = self.session_store.new()
893 httprequest.session = self.session_store.get(sid)
895 self._find_db(httprequest)
897 if not "lang" in httprequest.session.context:
898 lang = httprequest.accept_languages.best or "en_US"
899 lang = babel.core.LOCALE_ALIASES.get(lang, lang).replace('-', '_')
900 httprequest.session.context["lang"] = lang
902 request = self._build_request(httprequest)
906 openerp.modules.registry.RegistryManager.check_registry_signaling(db)
908 with set_request(request):
910 result = request.dispatch()
913 openerp.modules.registry.RegistryManager.signal_caches_change(db)
915 if isinstance(result, basestring):
916 headers=[('Content-Type', 'text/html; charset=utf-8'), ('Content-Length', len(result))]
917 response = werkzeug.wrappers.Response(result, headers=headers)
921 if httprequest.session.should_save:
922 self.session_store.save(httprequest.session)
923 if not explicit_session and hasattr(response, 'set_cookie'):
924 response.set_cookie('session_id', httprequest.session.sid, max_age=90 * 24 * 60 * 60)
926 return response(environ, start_response)
927 except werkzeug.exceptions.HTTPException, e:
928 return e(environ, start_response)
930 def _find_db(self, httprequest):
931 db = db_monodb(httprequest)
932 if db != httprequest.session.db:
933 httprequest.session.logout()
934 httprequest.session.db = db
936 def _build_request(self, httprequest):
937 if httprequest.args.get('jsonp'):
938 return JsonRequest(httprequest)
940 if httprequest.mimetype == "application/json":
941 return JsonRequest(httprequest)
943 return HttpRequest(httprequest)
945 def load_addons(self):
946 """ Load all addons from addons patch containg static files and
947 controllers and configure them. """
949 for addons_path in openerp.modules.module.ad_paths:
950 for module in sorted(os.listdir(str(addons_path))):
951 if module not in addons_module:
952 manifest_path = os.path.join(addons_path, module, '__openerp__.py')
953 path_static = os.path.join(addons_path, module, 'static')
954 if os.path.isfile(manifest_path) and os.path.isdir(path_static):
955 manifest = ast.literal_eval(open(manifest_path).read())
956 manifest['addons_path'] = addons_path
957 _logger.debug("Loading %s", module)
958 if 'openerp.addons' in sys.modules:
959 m = __import__('openerp.addons.' + module)
961 m = __import__(module)
962 addons_module[module] = m
963 addons_manifest[module] = manifest
964 self.statics['/%s/static' % module] = path_static
966 app = werkzeug.wsgi.SharedDataMiddleware(self.dispatch, self.statics)
967 self.dispatch = DisableCacheMiddleware(app)
969 def _build_router(self, db):
970 _logger.info("Generating routing configuration for database %s" % db)
971 routing_map = routing.Map()
973 def gen(modules, nodb_only):
974 for module in modules:
975 for v in controllers_per_module[module]:
978 subclasses = cls.__subclasses__()
979 subclasses = [c for c in subclasses if c.__module__.startswith('openerp.addons.') and
980 c.__module__.split(".")[2] in modules]
982 name = "%s (extended by %s)" % (cls.__name__, ', '.join(sub.__name__ for sub in subclasses))
983 cls = type(name, tuple(reversed(subclasses)), {})
986 members = inspect.getmembers(o)
987 for mk, mv in members:
988 if inspect.ismethod(mv) and getattr(mv, 'exposed', False) and \
989 nodb_only == (getattr(mv, "auth", "none") == "none"):
990 for url in mv.routes:
991 if getattr(mv, "combine", False):
992 url = o._cp_path.rstrip('/') + '/' + url.lstrip('/')
993 if url.endswith("/") and len(url) > 1:
995 routing_map.add(routing.Rule(url, endpoint=mv))
997 modules_set = set(controllers_per_module.keys()) - set(['web'])
998 # building all none methods
999 gen(["web"] + sorted(modules_set), True)
1003 registry = openerp.modules.registry.RegistryManager.get(db)
1004 with registry.cursor() as cr:
1005 m = registry.get('ir.module.module')
1006 ids = m.search(cr, openerp.SUPERUSER_ID, [('state', '=', 'installed'), ('name', '!=', 'web')])
1007 installed = set([x['name'] for x in m.read(cr, 1, ids, ['name'])])
1008 modules_set = modules_set.intersection(set(installed))
1009 modules = ["web"] + sorted(modules_set)
1010 # building all other methods
1015 def get_db_router(self, db):
1017 router = self.no_db_router
1019 router = getattr(openerp.modules.registry.RegistryManager.get(db), "werkzeug_http_router", None)
1021 router = self._build_router(db)
1023 self.no_db_router = router
1025 openerp.modules.registry.RegistryManager.get(db).werkzeug_http_router = router
1028 def find_handler(self):
1030 Tries to discover the controller handling the request for the path specified in the request.
1032 path = request.httprequest.path
1033 urls = self.get_db_router(request.db).bind("")
1034 func, arguments = urls.match(path)
1035 arguments = dict([(k, v) for k, v in arguments.items() if not k.startswith("_ignored_")])
1037 def nfunc(*args, **kwargs):
1038 kwargs.update(arguments)
1039 if getattr(func, '_first_arg_is_req', False):
1040 args = (request,) + args
1041 return func(*args, **kwargs)
1043 request.func = nfunc
1044 request.auth_method = getattr(func, "auth", "user")
1045 request.func_request_type = func.exposed
1049 def db_list(force=False, httprequest=None):
1050 httprequest = httprequest or request.httprequest
1051 dbs = openerp.netsvc.dispatch_rpc("db", "list", [force])
1052 h = httprequest.environ['HTTP_HOST'].split(':')[0]
1054 r = openerp.tools.config['dbfilter'].replace('%h', h).replace('%d', d)
1055 dbs = [i for i in dbs if re.match(r, i)]
1058 def db_monodb(httprequest=None):
1060 Magic function to find the current database.
1062 Implementation details:
1067 Returns ``None`` if the magic is not magic enough.
1069 httprequest = httprequest or request.httprequest
1073 dbs = db_list(True, httprequest)
1075 # try the db already in the session
1076 db_session = httprequest.session.db
1077 if db_session in dbs:
1080 # if dbfilters was specified when launching the server and there is
1081 # only one possible db, we take that one
1082 if openerp.tools.config['dbfilter'] != ".*" and len(dbs) == 1:
1086 class CommonController(Controller):
1088 @route('/jsonrpc', type='json', auth="none")
1089 def jsonrpc(self, service, method, args):
1090 """ Method used by client APIs to contact OpenERP. """
1091 return openerp.netsvc.dispatch_rpc(service, method, args)
1093 @route('/gen_session_id', type='json', auth="none")
1094 def gen_session_id(self):
1095 nsession = root.session_store.new()
1098 def wsgi_postload():
1101 openerp.wsgi.register_wsgi_handler(root)