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 ``bool``, indicates whether the debug mode is active on the client
94 ``str``, the name of the database linked to the current request. Can be ``None``
95 if the current request uses the ``none`` authentication.
99 ``int``, the id of the user related to the current request. Can be ``None``
100 if the current request uses the ``none`` authenticatoin.
102 def __init__(self, httprequest):
103 self.httprequest = httprequest
104 self.httpresponse = None
105 self.httpsession = httprequest.session
106 self.session = httprequest.session
107 self.session_id = httprequest.session.sid
111 self.auth_method = None
114 self.func_request_type = None
115 self.debug = self.httprequest.args.get('debug', False) is not False
116 with set_request(self):
117 self.db = self.session.db or db_monodb()
118 # set db/uid trackers - they're cleaned up at the WSGI
119 # dispatching phase in openerp.service.wsgi_server.application
121 threading.current_thread().dbname = self.db
123 threading.current_thread().uid = self.session.uid
124 self.context = self.session.context
125 self.lang = self.context["lang"]
127 def _authenticate(self):
130 self.session.check_security()
131 except SessionExpiredException, e:
132 self.session.logout()
133 raise SessionExpiredException("Session expired for request %s" % self.httprequest)
134 if self.auth_method == "none":
137 elif self.auth_method == "admin":
138 self.db = self.session.db or db_monodb()
140 raise SessionExpiredException("No valid database for request %s" % self.httprequest)
141 self.uid = openerp.SUPERUSER_ID
143 self.db = self.session.db
144 self.uid = self.session.uid
149 The registry to the database linked to this request. Can be ``None`` if the current request uses the
150 ``none'' authentication.
152 return openerp.modules.registry.RegistryManager.get(self.db) if self.db else None
157 The cursor initialized for the current method call. If the current request uses the ``none`` authentication
158 trying to access this property will raise an exception.
160 # some magic to lazy create the cr
162 self._cr_cm = self.registry.cursor()
163 self._cr = self._cr_cm.__enter__()
166 def _call_function(self, *args, **kwargs):
169 # ugly syntax only to get the __exit__ arguments to pass to self._cr
171 class with_obj(object):
174 def __exit__(self, *args):
176 request._cr_cm.__exit__(*args)
177 request._cr_cm = None
181 if self.func_request_type != self._request_type:
182 raise Exception("%s, %s: Function declared as capable of handling request of type '%s' but called with a request of type '%s'" \
183 % (self.func, self.httprequest.path, self.func_request_type, self._request_type))
184 return self.func(*args, **kwargs)
186 # just to be sure no one tries to re-use the request
190 def route(route, type="http", auth="user"):
192 Decorator marking the decorated method as being a handler for requests. The method must be part of a subclass
195 :param route: string or array. The route part that will determine which http requests will match the decorated
196 method. Can be a single string or an array of strings. See werkzeug's routing documentation for the format of
197 route expression ( http://werkzeug.pocoo.org/docs/routing/ ).
198 :param type: The type of request, can be ``'http'`` or ``'json'``.
199 :param auth: The type of authentication method, can on of the following:
201 * ``user``: The user must be authenticated and the current request will perform using the rights of the
203 * ``admin``: The user may not be authenticated and the current request will perform using the admin user.
204 * ``none``: The method is always active, even if there is no database. Mainly used by the framework and
205 authentication modules. There request code will not have any facilities to access the database nor have any
206 configuration indicating the current database nor the current user.
208 assert type in ["http", "json"]
209 assert auth in ["user", "admin", "none"]
211 if isinstance(route, list):
216 if getattr(f, "auth", None) is None:
221 def reject_nonliteral(dct):
224 "Non literal contexts can not be sent to the server anymore (%r)" % (dct,))
227 class JsonRequest(WebRequest):
228 """ JSON-RPC2 over HTTP.
232 --> {"jsonrpc": "2.0",
234 "params": {"context": {},
238 <-- {"jsonrpc": "2.0",
239 "result": { "res1": "val1" },
242 Request producing a error::
244 --> {"jsonrpc": "2.0",
246 "params": {"context": {},
250 <-- {"jsonrpc": "2.0",
252 "message": "End user error message.",
253 "data": {"code": "codestring",
254 "debug": "traceback" } },
258 _request_type = "json"
260 def __init__(self, *args):
261 super(JsonRequest, self).__init__(*args)
263 self.jsonp_handler = None
265 args = self.httprequest.args
266 jsonp = args.get('jsonp')
269 request_id = args.get('id')
271 if jsonp and self.httprequest.method == 'POST':
272 # jsonp 2 steps step1 POST: save call
276 self.session.jsonp_requests[request_id] = self.httprequest.form['r']
277 headers=[('Content-Type', 'text/plain; charset=utf-8')]
278 r = werkzeug.wrappers.Response(request_id, headers=headers)
280 self.jsonp_handler = handler
282 elif jsonp and args.get('r'):
284 request = args.get('r')
285 elif jsonp and request_id:
286 # 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, object_hook=reject_nonliteral)
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 #if _logger.isEnabledFor(logging.DEBUG):
308 # _logger.debug("--> %s.%s\n%s", func.im_class.__name__, func.__name__, pprint.pformat(self.jsonrequest))
309 response['id'] = self.jsonrequest.get('id')
310 response["result"] = self._call_function(**self.params)
311 except AuthenticationError, e:
312 _logger.exception("Exception during JSON request handling.")
313 se = serialize_exception(e)
316 'message': "OpenERP Session Invalid",
320 _logger.exception("Exception during JSON request handling.")
321 se = serialize_exception(e)
324 'message': "OpenERP Server Error",
328 response["error"] = error
330 if _logger.isEnabledFor(logging.DEBUG):
331 _logger.debug("<--\n%s", pprint.pformat(response))
334 # If we use jsonp, that's mean we are called from another host
335 # Some browser (IE and Safari) do no allow third party cookies
336 # We need then to manage http sessions manually.
337 response['session_id'] = self.session_id
338 mime = 'application/javascript'
339 body = "%s(%s);" % (self.jsonp, simplejson.dumps(response),)
341 mime = 'application/json'
342 body = simplejson.dumps(response)
344 r = werkzeug.wrappers.Response(body, headers=[('Content-Type', mime), ('Content-Length', len(body))])
347 def serialize_exception(e):
349 "name": type(e).__module__ + "." + type(e).__name__ if type(e).__module__ else type(e).__name__,
350 "debug": traceback.format_exc(),
351 "message": u"%s" % e,
352 "arguments": to_jsonable(e.args),
354 if isinstance(e, openerp.osv.osv.except_osv):
355 tmp["exception_type"] = "except_osv"
356 elif isinstance(e, openerp.exceptions.Warning):
357 tmp["exception_type"] = "warning"
358 elif isinstance(e, openerp.exceptions.AccessError):
359 tmp["exception_type"] = "access_error"
360 elif isinstance(e, openerp.exceptions.AccessDenied):
361 tmp["exception_type"] = "access_denied"
365 if isinstance(o, str) or isinstance(o,unicode) or isinstance(o, int) or isinstance(o, long) \
366 or isinstance(o, bool) or o is None or isinstance(o, float):
368 if isinstance(o, list) or isinstance(o, tuple):
369 return [to_jsonable(x) for x in o]
370 if isinstance(o, dict):
372 for k, v in o.items():
373 tmp[u"%s" % k] = to_jsonable(v)
381 Use the ``route()`` decorator instead.
385 if f.__name__ == "index":
387 return route([base, os.path.join(base, "<path:_ignored_path>")], type="json", auth="user")(f)
389 class HttpRequest(WebRequest):
390 """ Regular GET/POST request
392 _request_type = "http"
394 def __init__(self, *args):
395 super(HttpRequest, self).__init__(*args)
396 params = dict(self.httprequest.args)
397 ex = set(["session_id", "debug"])
398 for k in params.keys():
401 params.update(self.httprequest.form)
402 params.update(self.httprequest.files)
407 for key, value in self.httprequest.args.iteritems():
408 if isinstance(value, basestring) and len(value) < 1024:
411 akw[key] = type(value)
412 #_logger.debug("%s --> %s.%s %r", self.httprequest.func, func.im_class.__name__, func.__name__, akw)
414 r = self._call_function(**self.params)
415 except werkzeug.exceptions.HTTPException, e:
418 _logger.exception("An exception occured during an http request")
419 se = serialize_exception(e)
422 'message': "OpenERP Server Error",
425 r = werkzeug.exceptions.InternalServerError(cgi.escape(simplejson.dumps(error)))
428 r = werkzeug.wrappers.Response(status=204) # no content
429 if isinstance(r, (werkzeug.wrappers.BaseResponse, werkzeug.exceptions.HTTPException)):
430 _logger.debug('<-- %s', r)
432 _logger.debug("<-- size: %s", len(r))
435 def make_response(self, data, headers=None, cookies=None):
436 """ Helper for non-HTML responses, or HTML responses with custom
437 response headers or cookies.
439 While handlers can just return the HTML markup of a page they want to
440 send as a string if non-HTML data is returned they need to create a
441 complete response object, or the returned data will not be correctly
442 interpreted by the clients.
444 :param basestring data: response body
445 :param headers: HTTP headers to set on the response
446 :type headers: ``[(name, value)]``
447 :param collections.Mapping cookies: cookies to set on the client
449 response = werkzeug.wrappers.Response(data, headers=headers)
451 for k, v in cookies.iteritems():
452 response.set_cookie(k, v)
455 def not_found(self, description=None):
456 """ Helper for 404 response, return its result from the method
458 return werkzeug.exceptions.NotFound(description)
464 Use the ``route()`` decorator instead.
468 if f.__name__ == "index":
470 return route([base, os.path.join(base, "<path:_ignored_path>")], type="http", auth="user")(f)
472 #----------------------------------------------------------
473 # Local storage of requests
474 #----------------------------------------------------------
475 from werkzeug.local import LocalStack
477 _request_stack = LocalStack()
479 def set_request(request):
480 class with_obj(object):
482 _request_stack.push(request)
483 def __exit__(self, *args):
488 A global proxy that always redirect to the current request object.
490 request = _request_stack()
492 #----------------------------------------------------------
493 # Controller registration with a metaclass
494 #----------------------------------------------------------
497 controllers_per_module = {}
499 class ControllerType(type):
500 def __init__(cls, name, bases, attrs):
501 super(ControllerType, cls).__init__(name, bases, attrs)
503 # create wrappers for old-style methods with req as first argument
504 cls._methods_wrapper = {}
505 for k, v in attrs.items():
506 if inspect.isfunction(v):
507 spec = inspect.getargspec(v)
508 first_arg = spec.args[1] if len(spec.args) >= 2 else None
509 if first_arg in ["req", "request"]:
511 return lambda self, *args, **kwargs: nv(self, request, *args, **kwargs)
512 cls._methods_wrapper[k] = build_new(v)
514 # store the controller in the controllers list
515 name_class = ("%s.%s" % (cls.__module__, cls.__name__), cls)
516 class_path = name_class[0].split(".")
517 if not class_path[:2] == ["openerp", "addons"]:
519 # we want to know all modules that have controllers
520 module = class_path[2]
521 controllers_per_module.setdefault(module, [])
522 # but we only store controllers directly inheriting from Controller
523 if not "Controller" in globals() or not Controller in bases:
525 controllers_per_module.setdefault(module, []).append(name_class)
527 class Controller(object):
528 __metaclass__ = ControllerType
530 def get_wrapped_method(self, name):
531 if name in self.__class__._methods_wrapper:
532 return functools.partial(self.__class__._methods_wrapper[name], self)
534 return getattr(self, name)
536 #############################
538 #############################
540 class AuthenticationError(Exception):
543 class SessionExpiredException(Exception):
546 class Service(object):
549 Use ``openerp.netsvc.dispatch_rpc()`` instead.
551 def __init__(self, session, service_name):
552 self.session = session
553 self.service_name = service_name
555 def __getattr__(self, method):
556 def proxy_method(*args):
557 result = openerp.netsvc.dispatch_rpc(self.service_name, method, args)
564 Use the resistry and cursor in ``openerp.addons.web.http.request`` instead.
566 def __init__(self, session, model):
567 self.session = session
569 self.proxy = self.session.proxy('object')
571 def __getattr__(self, method):
572 self.session.assert_valid()
573 def proxy(*args, **kw):
574 # Can't provide any retro-compatibility for this case, so we check it and raise an Exception
575 # to tell the programmer to adapt his code
576 if not request.db or not request.uid or self.session.db != request.db \
577 or self.session.uid != request.uid:
578 raise Exception("Trying to use Model with badly configured database or user.")
580 mod = request.registry.get(self.model)
581 meth = getattr(mod, method)
583 result = meth(cr, request.uid, *args, **kw)
586 if isinstance(result, list) and len(result) > 0 and "id" in result[0]:
590 result = [index[x] for x in args[0] if x in index]
594 class OpenERPSession(werkzeug.contrib.sessions.Session):
595 def __init__(self, *args, **kwargs):
597 self.modified = False
598 super(OpenERPSession, self).__init__(*args, **kwargs)
600 self._default_values()
601 self.modified = False
603 def __getattr__(self, attr):
604 return self.get(attr, None)
605 def __setattr__(self, k, v):
606 if getattr(self, "inited", False):
608 object.__getattribute__(self, k)
610 return self.__setitem__(k, v)
611 object.__setattr__(self, k, v)
613 def authenticate(self, db, login=None, password=None, env=None, uid=None):
615 Authenticate the current user with the given db, login and password. If successful, store
616 the authentication parameters in the current session and request.
618 :param uid: If not None, that user id will be used instead the login to authenticate the user.
622 uid = openerp.netsvc.dispatch_rpc('common', 'authenticate', [db, login, password, env])
624 security.check(db, uid, password)
628 self.password = password
632 if uid: self.get_context()
635 def check_security(self):
637 Chech the current authentication parameters to know if those are still valid. This method
638 should be called at each request. If the authentication fails, a ``SessionExpiredException``
641 if not self.db or not self.uid:
642 raise SessionExpiredException("Session expired")
643 security.check(self.db, self.uid, self.password)
646 for k in self.keys():
648 self._default_values()
650 def _default_values(self):
651 self.setdefault("db", None)
652 self.setdefault("uid", None)
653 self.setdefault("login", None)
654 self.setdefault("password", None)
655 self.setdefault("context", {'tz': "UTC", "uid": None})
656 self.setdefault("jsonp_requests", {})
658 def get_context(self):
660 Re-initializes the current user's session context (based on
661 his preferences) by calling res.users.get_context() with the old
664 :returns: the new context
666 assert self.uid, "The user needs to be logged-in to initialize his context"
667 self.context = request.registry.get('res.users').context_get(request.cr, request.uid) or {}
668 self.context['uid'] = self.uid
669 self._fix_lang(self.context)
672 def _fix_lang(self, context):
673 """ OpenERP provides languages which may not make sense and/or may not
674 be understood by the web client's libraries.
678 :param dict context: context to fix
680 lang = context['lang']
682 # inane OpenERP locale
686 # lang to lang_REGION (datejs only handles lang_REGION, no bare langs)
687 if lang in babel.core.LOCALE_ALIASES:
688 lang = babel.core.LOCALE_ALIASES[lang]
690 context['lang'] = lang or 'en_US'
693 Damn properties for retro-compatibility. All of that is deprecated, all
700 def _db(self, value):
706 def _uid(self, value):
712 def _login(self, value):
718 def _password(self, value):
719 self.password = value
721 def send(self, service_name, method, *args):
724 Use ``openerp.netsvc.dispatch_rpc()`` instead.
726 return openerp.netsvc.dispatch_rpc(service_name, method, args)
728 def proxy(self, service):
731 Use ``openerp.netsvc.dispatch_rpc()`` instead.
733 return Service(self, service)
735 def assert_valid(self, force=False):
738 Use ``check_security()`` instead.
740 Ensures this session is valid (logged into the openerp server)
742 if self.uid and not force:
744 # TODO use authenticate instead of login
745 self.uid = self.proxy("common").login(self.db, self.login, self.password)
747 raise AuthenticationError("Authentication failure")
749 def ensure_valid(self):
752 Use ``check_security()`` instead.
756 self.assert_valid(True)
760 def execute(self, model, func, *l, **d):
763 Use the resistry and cursor in ``openerp.addons.web.http.request`` instead.
765 model = self.model(model)
766 r = getattr(model, func)(*l, **d)
769 def exec_workflow(self, model, id, signal):
772 Use the resistry and cursor in ``openerp.addons.web.http.request`` instead.
775 r = self.proxy('object').exec_workflow(self.db, self.uid, self.password, model, signal, id)
778 def model(self, model):
781 Use the resistry and cursor in ``openerp.addons.web.http.request`` instead.
783 Get an RPC proxy for the object ``model``, bound to this session.
785 :param model: an OpenERP model name
787 :rtype: a model object
790 raise SessionExpiredException("Session expired")
792 return Model(self, model)
794 def session_gc(session_store):
795 if random.random() < 0.001:
796 # we keep session one week
797 last_week = time.time() - 60*60*24*7
798 for fname in os.listdir(session_store.path):
799 path = os.path.join(session_store.path, fname)
801 if os.path.getmtime(path) < last_week:
806 #----------------------------------------------------------
808 #----------------------------------------------------------
809 # Add potentially missing (older ubuntu) font mime types
810 mimetypes.add_type('application/font-woff', '.woff')
811 mimetypes.add_type('application/vnd.ms-fontobject', '.eot')
812 mimetypes.add_type('application/x-font-ttf', '.ttf')
814 class DisableCacheMiddleware(object):
815 def __init__(self, app):
817 def __call__(self, environ, start_response):
818 def start_wrapped(status, headers):
819 referer = environ.get('HTTP_REFERER', '')
820 parsed = urlparse.urlparse(referer)
821 debug = parsed.query.count('debug') >= 1
824 unwanted_keys = ['Last-Modified']
826 new_headers = [('Cache-Control', 'no-cache')]
827 unwanted_keys += ['Expires', 'Etag', 'Cache-Control']
830 if k not in unwanted_keys:
831 new_headers.append((k, v))
833 start_response(status, new_headers)
834 return self.app(environ, start_wrapped)
839 username = pwd.getpwuid(os.geteuid()).pw_name
842 username = getpass.getuser()
845 path = os.path.join(tempfile.gettempdir(), "oe-sessions-" + username)
848 except OSError as exc:
849 if exc.errno == errno.EEXIST:
850 # directory exists: ensure it has the correct permissions
851 # this will fail if the directory is not owned by the current user
858 """Root WSGI application for the OpenERP Web Client.
865 self.db_routers_lock = threading.Lock()
869 # Setup http sessions
870 path = session_path()
871 self.session_store = werkzeug.contrib.sessions.FilesystemSessionStore(path, session_class=OpenERPSession)
872 _logger.debug('HTTP sessions stored in: %s', path)
875 def __call__(self, environ, start_response):
876 """ Handle a WSGI request
878 return self.dispatch(environ, start_response)
880 def dispatch(self, environ, start_response):
882 Performs the actual WSGI dispatching for the application.
885 httprequest = werkzeug.wrappers.Request(environ)
886 httprequest.parameter_storage_class = werkzeug.datastructures.ImmutableDict
887 httprequest.app = self
889 session_gc(self.session_store)
891 sid = httprequest.args.get('session_id')
892 explicit_session = True
894 sid = httprequest.headers.get("X-Openerp-Session-Id")
896 sid = httprequest.cookies.get('session_id')
897 explicit_session = False
899 httprequest.session = self.session_store.new()
901 httprequest.session = self.session_store.get(sid)
903 if not "lang" in httprequest.session.context:
904 lang = httprequest.accept_languages.best or "en_US"
905 lang = babel.core.LOCALE_ALIASES.get(lang, lang).replace('-', '_')
906 httprequest.session.context["lang"] = lang
908 request = self._build_request(httprequest)
912 updated = openerp.modules.registry.RegistryManager.check_registry_signaling(db)
914 with self.db_routers_lock:
915 del self.db_routers[db]
917 with set_request(request):
919 result = request.dispatch()
922 openerp.modules.registry.RegistryManager.signal_caches_change(db)
924 if isinstance(result, basestring):
925 headers=[('Content-Type', 'text/html; charset=utf-8'), ('Content-Length', len(result))]
926 response = werkzeug.wrappers.Response(result, headers=headers)
930 if httprequest.session.should_save:
931 self.session_store.save(httprequest.session)
932 if not explicit_session and hasattr(response, 'set_cookie'):
933 response.set_cookie('session_id', httprequest.session.sid)
935 return response(environ, start_response)
936 except werkzeug.exceptions.HTTPException, e:
937 return e(environ, start_response)
939 def _build_request(self, httprequest):
940 if httprequest.args.get('jsonp'):
941 return JsonRequest(httprequest)
943 content = httprequest.stream.read()
945 httprequest.stream = cStringIO.StringIO(content)
947 simplejson.loads(content)
948 return JsonRequest(httprequest)
950 return HttpRequest(httprequest)
952 def load_addons(self):
953 """ Load all addons from addons patch containg static files and
954 controllers and configure them. """
956 for addons_path in openerp.modules.module.ad_paths:
957 for module in sorted(os.listdir(str(addons_path))):
958 if module not in addons_module:
959 manifest_path = os.path.join(addons_path, module, '__openerp__.py')
960 path_static = os.path.join(addons_path, module, 'static')
961 if os.path.isfile(manifest_path) and os.path.isdir(path_static):
962 manifest = ast.literal_eval(open(manifest_path).read())
963 manifest['addons_path'] = addons_path
964 _logger.debug("Loading %s", module)
965 if 'openerp.addons' in sys.modules:
966 m = __import__('openerp.addons.' + module)
968 m = __import__(module)
969 addons_module[module] = m
970 addons_manifest[module] = manifest
971 self.statics['/%s/static' % module] = path_static
973 app = werkzeug.wsgi.SharedDataMiddleware(self.dispatch, self.statics)
974 self.dispatch = DisableCacheMiddleware(app)
976 def _build_router(self, db):
977 _logger.info("Generating routing configuration for database %s" % db)
978 routing_map = routing.Map()
980 def gen(modules, nodb_only):
981 for module in modules:
982 for v in controllers_per_module[module]:
985 subclasses = cls.__subclasses__()
986 subclasses = [c for c in subclasses if c.__module__.split(".")[:2] == ["openerp", "addons"] and \
987 cls.__module__.split(".")[2] in modules]
989 name = "%s (extended by %s)" % (cls.__name__, ', '.join(sub.__name__ for sub in subclasses))
990 cls = type(name, tuple(reversed(subclasses)), {})
993 members = inspect.getmembers(o)
994 for mk, mv in members:
995 if inspect.ismethod(mv) and getattr(mv, 'exposed', False) and \
996 nodb_only == (getattr(mv, "auth", None) == "none"):
997 function = (o.get_wrapped_method(mk), mv)
998 for url in mv.routes:
999 if getattr(mv, "combine", False):
1000 url = os.path.join(o._cp_path, url)
1001 if url.endswith("/") and len(url) > 1:
1003 routing_map.add(routing.Rule(url, endpoint=function))
1005 modules_set = set(controllers_per_module.keys())
1006 modules_set -= set("web")
1007 # building all none methods
1008 gen(["web"] + sorted(modules_set), True)
1012 registry = openerp.modules.registry.RegistryManager.get(db)
1013 with registry.cursor() as cr:
1014 m = registry.get('ir.module.module')
1015 ids = m.search(cr, openerp.SUPERUSER_ID, [('state','=','installed')])
1016 installed = set([x['name'] for x in m.read(cr, 1, ids, ['name'])])
1017 modules_set = modules_set.intersection(set(installed))
1018 modules = ["web"] + sorted(modules_set)
1019 # building all other methods
1020 gen(["web"] + sorted(modules_set), False)
1024 def get_db_router(self, db):
1025 with self.db_routers_lock:
1026 router = self.db_routers.get(db)
1028 router = self._build_router(db)
1029 with self.db_routers_lock:
1030 self.db_routers[db] = router
1033 def find_handler(self):
1035 Tries to discover the controller handling the request for the path specified in the request.
1037 path = request.httprequest.path
1038 urls = self.get_db_router(request.db).bind("")
1039 matched, arguments = urls.match(path)
1040 arguments = dict([(k, v) for k, v in arguments.items() if not k.startswith("_ignored_")])
1041 func, original = matched
1043 def nfunc(*args, **kwargs):
1044 kwargs.update(arguments)
1045 return func(*args, **kwargs)
1047 request.func = nfunc
1048 request.auth_method = getattr(original, "auth", "user")
1049 request.func_request_type = original.exposed
1053 def db_list(force=False):
1054 proxy = request.session.proxy("db")
1055 dbs = proxy.list(force)
1056 h = request.httprequest.environ['HTTP_HOST'].split(':')[0]
1058 r = openerp.tools.config['dbfilter'].replace('%h', h).replace('%d', d)
1059 dbs = [i for i in dbs if re.match(r, i)]
1062 def db_redirect(match_first_only_if_unique):
1068 # 1 try the db in the url
1069 db_url = request.httprequest.args.get('db')
1071 return (db_url, None)
1073 # 2 use the database from the cookie if it's listable and still listed
1074 cookie_db = request.httprequest.cookies.get('last_used_database')
1075 if cookie_db in dbs:
1078 # 3 use the first db if user can list databases
1079 if dbs and not db and (not match_first_only_if_unique or len(dbs) == 1):
1082 # redirect to the chosen db if multiple are available
1083 if db and len(dbs) > 1:
1084 query = dict(urlparse.parse_qsl(request.httprequest.query_string, keep_blank_values=True))
1085 query.update({'db': db})
1086 redirect = request.httprequest.path + '?' + urllib.urlencode(query)
1087 return (db, redirect)
1091 Magic function to find the current database.
1093 Implementation details:
1098 Return ``None`` if the magic is not magic enough.
1100 return db_redirect(True)[0]
1103 class CommonController(Controller):
1105 @route('/jsonrpc', type='json', auth="none")
1106 def jsonrpc(self, service, method, args):
1107 """ Method used by client APIs to contact OpenERP. """
1108 return openerp.netsvc.dispatch_rpc(service, method, args)
1110 @route('/gen_session_id', type='json', auth="none")
1111 def gen_session_id(self):
1112 nsession = root.session_store.new()
1115 def wsgi_postload():
1118 openerp.wsgi.register_wsgi_handler(root)