1 # -*- coding: utf-8 -*-
2 #----------------------------------------------------------
4 #----------------------------------------------------------
27 import werkzeug.contrib.sessions
28 import werkzeug.datastructures
29 import werkzeug.exceptions
30 import werkzeug.wrappers
32 import werkzeug.routing as routing
35 from openerp.service import security, model as service_model
36 from openerp.tools import config
38 _logger = logging.getLogger(__name__)
40 #----------------------------------------------------------
42 #----------------------------------------------------------
43 class WebRequest(object):
44 """ Parent class for all OpenERP Web request types, mostly deals with
45 initialization and setup of the request object (the dispatching itself has
46 to be handled by the subclasses)
48 :param request: a wrapped werkzeug Request object
49 :type request: :class:`werkzeug.wrappers.BaseRequest`
51 .. attribute:: httprequest
53 the original :class:`werkzeug.wrappers.Request` object provided to the
56 .. attribute:: httpsession
60 Use ``self.session`` instead.
64 :class:`~collections.Mapping` of request parameters, not generally
65 useful as they're provided directly to the handler method as keyword
68 .. attribute:: session_id
70 opaque identifier for the :class:`session.OpenERPSession` instance of
73 .. attribute:: session
75 a :class:`OpenERPSession` holding the HTTP session data for the
78 .. attribute:: context
80 :class:`~collections.Mapping` of context values for the current request
84 ``str``, the name of the database linked to the current request. Can be ``None``
85 if the current request uses the ``none`` authentication.
89 ``int``, the id of the user related to the current request. Can be ``None``
90 if the current request uses the ``none`` authenticatoin.
92 def __init__(self, httprequest):
93 self.httprequest = httprequest
94 self.httpresponse = None
95 self.httpsession = httprequest.session
96 self.session = httprequest.session
97 self.session_id = httprequest.session.sid
98 self.disable_db = False
101 self.auth_method = None
104 self.func_request_type = None
105 # set db/uid trackers - they're cleaned up at the WSGI
106 # dispatching phase in openerp.service.wsgi_server.application
108 threading.current_thread().dbname = self.db
110 threading.current_thread().uid = self.session.uid
111 self.context = dict(self.session.context)
112 self.lang = self.context["lang"]
114 def _authenticate(self):
117 self.session.check_security()
118 except SessionExpiredException, e:
119 self.session.logout()
120 raise SessionExpiredException("Session expired for request %s" % self.httprequest)
121 auth_methods[self.auth_method]()
125 The registry to the database linked to this request. Can be ``None`` if the current request uses the
126 ``none'' authentication.
128 return openerp.modules.registry.RegistryManager.get(self.db) if self.db else None
133 The registry to the database linked to this request. Can be ``None`` if the current request uses the
134 ``none'' authentication.
136 return self.session.db if not self.disable_db else None
141 The cursor initialized for the current method call. If the current request uses the ``none`` authentication
142 trying to access this property will raise an exception.
144 # some magic to lazy create the cr
146 self._cr_cm = self.registry.cursor()
147 self._cr = self._cr_cm.__enter__()
150 def _call_function(self, *args, **kwargs):
153 # ugly syntax only to get the __exit__ arguments to pass to self._cr
155 class with_obj(object):
158 def __exit__(self, *args):
160 request._cr_cm.__exit__(*args)
161 request._cr_cm = None
165 if self.func_request_type != self._request_type:
166 raise Exception("%s, %s: Function declared as capable of handling request of type '%s' but called with a request of type '%s'" \
167 % (self.func, self.httprequest.path, self.func_request_type, self._request_type))
168 return self.func(*args, **kwargs)
170 # just to be sure no one tries to re-use the request
171 self.disable_db = True
176 return 'debug' in self.httprequest.args
178 @contextlib.contextmanager
179 def registry_cr(self):
180 warnings.warn('please use request.registry and request.cr directly', DeprecationWarning)
181 yield (self.registry, self.cr)
183 def auth_method_user():
184 request.uid = request.session.uid
186 raise SessionExpiredException("Session expired")
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="user")(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 params.update(self.httprequest.form)
404 params.update(self.httprequest.files)
405 params.pop('session_id', None)
410 r = self._call_function(**self.params)
411 except werkzeug.exceptions.HTTPException, e:
414 _logger.exception("An exception occured during an http request")
415 se = serialize_exception(e)
418 'message': "OpenERP Server Error",
421 r = werkzeug.exceptions.InternalServerError(cgi.escape(simplejson.dumps(error)))
424 r = werkzeug.wrappers.Response(status=204) # no content
427 def make_response(self, data, headers=None, cookies=None):
428 """ Helper for non-HTML responses, or HTML responses with custom
429 response headers or cookies.
431 While handlers can just return the HTML markup of a page they want to
432 send as a string if non-HTML data is returned they need to create a
433 complete response object, or the returned data will not be correctly
434 interpreted by the clients.
436 :param basestring data: response body
437 :param headers: HTTP headers to set on the response
438 :type headers: ``[(name, value)]``
439 :param collections.Mapping cookies: cookies to set on the client
441 response = werkzeug.wrappers.Response(data, headers=headers)
443 for k, v in cookies.iteritems():
444 response.set_cookie(k, v)
447 def not_found(self, description=None):
448 """ Helper for 404 response, return its result from the method
450 return werkzeug.exceptions.NotFound(description)
456 Use the ``route()`` decorator instead.
459 base = f.__name__.lstrip('/')
460 if f.__name__ == "index":
462 return route([base, base + "/<path:_ignored_path>"], type="http", auth="user")(f)
464 #----------------------------------------------------------
465 # Thread local global request object
466 #----------------------------------------------------------
467 from werkzeug.local import LocalStack
469 _request_stack = LocalStack()
471 request = _request_stack()
473 A global proxy that always redirect to the current request object.
476 @contextlib.contextmanager
477 def set_request(req):
478 _request_stack.push(req)
484 #----------------------------------------------------------
485 # Controller metaclass registration
486 #----------------------------------------------------------
489 controllers_per_module = {}
491 class ControllerType(type):
492 def __init__(cls, name, bases, attrs):
493 super(ControllerType, cls).__init__(name, bases, attrs)
495 # flag old-style methods with req as first argument
496 for k, v in attrs.items():
497 if inspect.isfunction(v):
498 spec = inspect.getargspec(v)
499 first_arg = spec.args[1] if len(spec.args) >= 2 else None
500 if first_arg in ["req", "request"]:
501 v._first_arg_is_req = True
503 # store the controller in the controllers list
504 name_class = ("%s.%s" % (cls.__module__, cls.__name__), cls)
505 class_path = name_class[0].split(".")
506 if not class_path[:2] == ["openerp", "addons"]:
509 # we want to know all modules that have controllers
510 module = class_path[2]
511 # but we only store controllers directly inheriting from Controller
512 if not "Controller" in globals() or not Controller in bases:
514 controllers_per_module.setdefault(module, []).append(name_class)
516 class Controller(object):
517 __metaclass__ = ControllerType
519 #----------------------------------------------------------
521 #----------------------------------------------------------
522 class AuthenticationError(Exception):
525 class SessionExpiredException(Exception):
528 class Service(object):
531 Use ``openerp.netsvc.dispatch_rpc()`` instead.
533 def __init__(self, session, service_name):
534 self.session = session
535 self.service_name = service_name
537 def __getattr__(self, method):
538 def proxy_method(*args):
539 result = openerp.netsvc.dispatch_rpc(self.service_name, method, args)
546 Use the resistry and cursor in ``openerp.addons.web.http.request`` instead.
548 def __init__(self, session, model):
549 self.session = session
551 self.proxy = self.session.proxy('object')
553 def __getattr__(self, method):
554 self.session.assert_valid()
555 def proxy(*args, **kw):
556 # Can't provide any retro-compatibility for this case, so we check it and raise an Exception
557 # to tell the programmer to adapt his code
558 if not request.db or not request.uid or self.session.db != request.db \
559 or self.session.uid != request.uid:
560 raise Exception("Trying to use Model with badly configured database or user.")
562 mod = request.registry.get(self.model)
563 if method.startswith('_'):
564 raise Exception("Access denied")
565 meth = getattr(mod, method)
567 result = meth(cr, request.uid, *args, **kw)
570 if isinstance(result, list) and len(result) > 0 and "id" in result[0]:
574 result = [index[x] for x in args[0] if x in index]
578 class OpenERPSession(werkzeug.contrib.sessions.Session):
579 def __init__(self, *args, **kwargs):
581 self.modified = False
582 super(OpenERPSession, self).__init__(*args, **kwargs)
584 self._default_values()
585 self.modified = False
587 def __getattr__(self, attr):
588 return self.get(attr, None)
589 def __setattr__(self, k, v):
590 if getattr(self, "inited", False):
592 object.__getattribute__(self, k)
594 return self.__setitem__(k, v)
595 object.__setattr__(self, k, v)
597 def authenticate(self, db, login=None, password=None, uid=None):
599 Authenticate the current user with the given db, login and password. If successful, store
600 the authentication parameters in the current session and request.
602 :param uid: If not None, that user id will be used instead the login to authenticate the user.
606 wsgienv = request.httprequest.environ
608 base_location=request.httprequest.url_root.rstrip('/'),
609 HTTP_HOST=wsgienv['HTTP_HOST'],
610 REMOTE_ADDR=wsgienv['REMOTE_ADDR'],
612 uid = openerp.netsvc.dispatch_rpc('common', 'authenticate', [db, login, password, env])
614 security.check(db, uid, password)
618 self.password = password
620 request.disable_db = False
622 if uid: self.get_context()
625 def check_security(self):
627 Chech the current authentication parameters to know if those are still valid. This method
628 should be called at each request. If the authentication fails, a ``SessionExpiredException``
631 if not self.db or not self.uid:
632 raise SessionExpiredException("Session expired")
633 security.check(self.db, self.uid, self.password)
636 for k in self.keys():
638 self._default_values()
640 def _default_values(self):
641 self.setdefault("db", None)
642 self.setdefault("uid", None)
643 self.setdefault("login", None)
644 self.setdefault("password", None)
645 self.setdefault("context", {'tz': "UTC", "uid": None})
646 self.setdefault("jsonp_requests", {})
648 def get_context(self):
650 Re-initializes the current user's session context (based on
651 his preferences) by calling res.users.get_context() with the old
654 :returns: the new context
656 assert self.uid, "The user needs to be logged-in to initialize his context"
657 self.context = request.registry.get('res.users').context_get(request.cr, request.uid) or {}
658 self.context['uid'] = self.uid
659 self._fix_lang(self.context)
662 def _fix_lang(self, context):
663 """ OpenERP provides languages which may not make sense and/or may not
664 be understood by the web client's libraries.
668 :param dict context: context to fix
670 lang = context['lang']
672 # inane OpenERP locale
676 # lang to lang_REGION (datejs only handles lang_REGION, no bare langs)
677 if lang in babel.core.LOCALE_ALIASES:
678 lang = babel.core.LOCALE_ALIASES[lang]
680 context['lang'] = lang or 'en_US'
683 Damn properties for retro-compatibility. All of that is deprecated, all
690 def _db(self, value):
696 def _uid(self, value):
702 def _login(self, value):
708 def _password(self, value):
709 self.password = value
711 def send(self, service_name, method, *args):
714 Use ``openerp.netsvc.dispatch_rpc()`` instead.
716 return openerp.netsvc.dispatch_rpc(service_name, method, args)
718 def proxy(self, service):
721 Use ``openerp.netsvc.dispatch_rpc()`` instead.
723 return Service(self, service)
725 def assert_valid(self, force=False):
728 Use ``check_security()`` instead.
730 Ensures this session is valid (logged into the openerp server)
732 if self.uid and not force:
734 # TODO use authenticate instead of login
735 self.uid = self.proxy("common").login(self.db, self.login, self.password)
737 raise AuthenticationError("Authentication failure")
739 def ensure_valid(self):
742 Use ``check_security()`` instead.
746 self.assert_valid(True)
750 def execute(self, model, func, *l, **d):
753 Use the resistry and cursor in ``openerp.addons.web.http.request`` instead.
755 model = self.model(model)
756 r = getattr(model, func)(*l, **d)
759 def exec_workflow(self, model, id, signal):
762 Use the resistry and cursor in ``openerp.addons.web.http.request`` instead.
765 r = self.proxy('object').exec_workflow(self.db, self.uid, self.password, model, signal, id)
768 def model(self, model):
771 Use the resistry and cursor in ``openerp.addons.web.http.request`` instead.
773 Get an RPC proxy for the object ``model``, bound to this session.
775 :param model: an OpenERP model name
777 :rtype: a model object
780 raise SessionExpiredException("Session expired")
782 return Model(self, model)
784 def session_gc(session_store):
785 if random.random() < 0.001:
786 # we keep session one week
787 last_week = time.time() - 60*60*24*7
788 for fname in os.listdir(session_store.path):
789 path = os.path.join(session_store.path, fname)
791 if os.path.getmtime(path) < last_week:
796 #----------------------------------------------------------
798 #----------------------------------------------------------
799 # Add potentially missing (older ubuntu) font mime types
800 mimetypes.add_type('application/font-woff', '.woff')
801 mimetypes.add_type('application/vnd.ms-fontobject', '.eot')
802 mimetypes.add_type('application/x-font-ttf', '.ttf')
804 class DisableCacheMiddleware(object):
805 def __init__(self, app):
807 def __call__(self, environ, start_response):
808 def start_wrapped(status, headers):
809 referer = environ.get('HTTP_REFERER', '')
810 parsed = urlparse.urlparse(referer)
811 debug = parsed.query.count('debug') >= 1
814 unwanted_keys = ['Last-Modified']
816 new_headers = [('Cache-Control', 'no-cache')]
817 unwanted_keys += ['Expires', 'Etag', 'Cache-Control']
820 if k not in unwanted_keys:
821 new_headers.append((k, v))
823 start_response(status, new_headers)
824 return self.app(environ, start_wrapped)
829 username = pwd.getpwuid(os.geteuid()).pw_name
832 username = getpass.getuser()
835 path = os.path.join(tempfile.gettempdir(), "oe-sessions-" + username)
838 except OSError as exc:
839 if exc.errno == errno.EEXIST:
840 # directory exists: ensure it has the correct permissions
841 # this will fail if the directory is not owned by the current user
848 """Root WSGI application for the OpenERP Web Client.
854 self.no_db_router = None
858 # Setup http sessions
859 path = session_path()
860 self.session_store = werkzeug.contrib.sessions.FilesystemSessionStore(path, session_class=OpenERPSession)
861 _logger.debug('HTTP sessions stored in: %s', path)
864 def __call__(self, environ, start_response):
865 """ Handle a WSGI request
867 return self.dispatch(environ, start_response)
869 def dispatch(self, environ, start_response):
871 Performs the actual WSGI dispatching for the application.
874 httprequest = werkzeug.wrappers.Request(environ)
875 httprequest.parameter_storage_class = werkzeug.datastructures.ImmutableDict
876 httprequest.app = self
878 session_gc(self.session_store)
880 sid = httprequest.args.get('session_id')
881 explicit_session = True
883 sid = httprequest.headers.get("X-Openerp-Session-Id")
885 sid = httprequest.cookies.get('session_id')
886 explicit_session = False
888 httprequest.session = self.session_store.new()
890 httprequest.session = self.session_store.get(sid)
892 self._find_db(httprequest)
894 if not "lang" in httprequest.session.context:
895 lang = httprequest.accept_languages.best or "en_US"
896 lang = babel.core.LOCALE_ALIASES.get(lang, lang).replace('-', '_')
897 httprequest.session.context["lang"] = lang
899 request = self._build_request(httprequest)
903 openerp.modules.registry.RegistryManager.check_registry_signaling(db)
905 with set_request(request):
907 result = request.dispatch()
910 openerp.modules.registry.RegistryManager.signal_caches_change(db)
912 if isinstance(result, basestring):
913 headers=[('Content-Type', 'text/html; charset=utf-8'), ('Content-Length', len(result))]
914 response = werkzeug.wrappers.Response(result, headers=headers)
918 if httprequest.session.should_save:
919 self.session_store.save(httprequest.session)
920 # We must not set the cookie if the session id was specified using a http header or a GET parameter.
921 # There are two reasons to this:
922 # - When using one of those two means we consider that we are overriding the cookie, which means creating a new
923 # session on top of an already existing session and we don't want to create a mess with the 'normal' session
924 # (the one using the cookie). That is a special feature of the Session Javascript class.
925 # - It could allow session fixation attacks.
926 if not explicit_session and hasattr(response, 'set_cookie'):
927 response.set_cookie('session_id', httprequest.session.sid, max_age=90 * 24 * 60 * 60)
929 return response(environ, start_response)
930 except werkzeug.exceptions.HTTPException, e:
931 return e(environ, start_response)
933 def _find_db(self, httprequest):
934 db = db_monodb(httprequest)
935 if db != httprequest.session.db:
936 httprequest.session.logout()
937 httprequest.session.db = db
939 def _build_request(self, httprequest):
940 if httprequest.args.get('jsonp'):
941 return JsonRequest(httprequest)
943 if httprequest.mimetype == "application/json":
944 return JsonRequest(httprequest)
946 return HttpRequest(httprequest)
948 def load_addons(self):
949 """ Load all addons from addons patch containg static files and
950 controllers and configure them. """
952 for addons_path in openerp.modules.module.ad_paths:
953 for module in sorted(os.listdir(str(addons_path))):
954 if module not in addons_module:
955 manifest_path = os.path.join(addons_path, module, '__openerp__.py')
956 path_static = os.path.join(addons_path, module, 'static')
957 if os.path.isfile(manifest_path) and os.path.isdir(path_static):
958 manifest = ast.literal_eval(open(manifest_path).read())
959 manifest['addons_path'] = addons_path
960 _logger.debug("Loading %s", module)
961 if 'openerp.addons' in sys.modules:
962 m = __import__('openerp.addons.' + module)
964 m = __import__(module)
965 addons_module[module] = m
966 addons_manifest[module] = manifest
967 self.statics['/%s/static' % module] = path_static
969 app = werkzeug.wsgi.SharedDataMiddleware(self.dispatch, self.statics)
970 self.dispatch = DisableCacheMiddleware(app)
972 def _build_router(self, db):
973 _logger.info("Generating routing configuration for database %s" % db)
974 routing_map = routing.Map(strict_slashes=False)
976 def gen(modules, nodb_only):
977 for module in modules:
978 for v in controllers_per_module[module]:
981 subclasses = cls.__subclasses__()
982 subclasses = [c for c in subclasses if c.__module__.startswith('openerp.addons.') and
983 c.__module__.split(".")[2] in modules]
985 name = "%s (extended by %s)" % (cls.__name__, ', '.join(sub.__name__ for sub in subclasses))
986 cls = type(name, tuple(reversed(subclasses)), {})
989 members = inspect.getmembers(o)
990 for mk, mv in members:
991 if inspect.ismethod(mv) and getattr(mv, 'exposed', False) and \
992 nodb_only == (getattr(mv, "auth", "none") == "none"):
993 for url in mv.routes:
994 if getattr(mv, "combine", False):
995 url = o._cp_path.rstrip('/') + '/' + url.lstrip('/')
996 if url.endswith("/") and len(url) > 1:
998 routing_map.add(routing.Rule(url, endpoint=mv))
1000 modules_set = set(controllers_per_module.keys()) - set(['', 'web'])
1001 # building all none methods
1002 gen(['', "web"] + sorted(modules_set), True)
1006 registry = openerp.modules.registry.RegistryManager.get(db)
1007 with registry.cursor() as cr:
1008 m = registry.get('ir.module.module')
1009 ids = m.search(cr, openerp.SUPERUSER_ID, [('state', '=', 'installed'), ('name', '!=', 'web')])
1010 installed = set(x['name'] for x in m.read(cr, 1, ids, ['name']))
1011 modules_set = modules_set & installed
1013 # building all other methods
1014 gen(['', "web"] + sorted(modules_set), False)
1018 def get_db_router(self, db):
1020 router = self.no_db_router
1022 router = getattr(openerp.modules.registry.RegistryManager.get(db), "werkzeug_http_router", None)
1024 router = self._build_router(db)
1026 self.no_db_router = router
1028 openerp.modules.registry.RegistryManager.get(db).werkzeug_http_router = router
1031 def find_handler(self):
1033 Tries to discover the controller handling the request for the path specified in the request.
1035 path = request.httprequest.path
1036 urls = self.get_db_router(request.db).bind_to_environ(request.httprequest.environ)
1037 func, arguments = urls.match(path)
1038 arguments = dict([(k, v) for k, v in arguments.items() if not k.startswith("_ignored_")])
1040 @service_model.check
1041 def checked_call(dbname, *a, **kw):
1042 return func(*a, **kw)
1044 def nfunc(*args, **kwargs):
1045 kwargs.update(arguments)
1046 if getattr(func, '_first_arg_is_req', False):
1047 args = (request,) + args
1050 return checked_call(request.db, *args, **kwargs)
1051 return func(*args, **kwargs)
1053 request.func = nfunc
1054 request.auth_method = getattr(func, "auth", "user")
1055 request.func_request_type = func.exposed
1057 def db_list(force=False, httprequest=None):
1058 httprequest = httprequest or request.httprequest
1059 dbs = openerp.netsvc.dispatch_rpc("db", "list", [force])
1060 h = httprequest.environ['HTTP_HOST'].split(':')[0]
1062 r = openerp.tools.config['dbfilter'].replace('%h', h).replace('%d', d)
1063 dbs = [i for i in dbs if re.match(r, i)]
1066 def db_monodb(httprequest=None):
1068 Magic function to find the current database.
1070 Implementation details:
1075 Returns ``None`` if the magic is not magic enough.
1077 httprequest = httprequest or request.httprequest
1081 dbs = db_list(True, httprequest)
1083 # try the db already in the session
1084 db_session = httprequest.session.db
1085 if db_session in dbs:
1088 # if dbfilters was specified when launching the server and there is
1089 # only one possible db, we take that one
1090 if openerp.tools.config['dbfilter'] != ".*" and len(dbs) == 1:
1094 class CommonController(Controller):
1096 @route('/jsonrpc', type='json', auth="none")
1097 def jsonrpc(self, service, method, args):
1098 """ Method used by client APIs to contact OpenERP. """
1099 return openerp.netsvc.dispatch_rpc(service, method, args)
1101 @route('/gen_session_id', type='json', auth="none")
1102 def gen_session_id(self):
1103 nsession = root.session_store.new()
1108 def wsgi_postload():
1111 openerp.wsgi.register_wsgi_handler(root)