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 params.update(self.httprequest.form)
404 params.update(self.httprequest.files)
405 ex = set(["session_id"])
406 for k in params.keys():
413 for key, value in self.httprequest.args.iteritems():
414 if isinstance(value, basestring) and len(value) < 1024:
417 akw[key] = type(value)
419 r = self._call_function(**self.params)
420 except werkzeug.exceptions.HTTPException, e:
423 _logger.exception("An exception occured during an http request")
424 se = serialize_exception(e)
427 'message': "OpenERP Server Error",
430 r = werkzeug.exceptions.InternalServerError(cgi.escape(simplejson.dumps(error)))
433 r = werkzeug.wrappers.Response(status=204) # no content
436 def make_response(self, data, headers=None, cookies=None):
437 """ Helper for non-HTML responses, or HTML responses with custom
438 response headers or cookies.
440 While handlers can just return the HTML markup of a page they want to
441 send as a string if non-HTML data is returned they need to create a
442 complete response object, or the returned data will not be correctly
443 interpreted by the clients.
445 :param basestring data: response body
446 :param headers: HTTP headers to set on the response
447 :type headers: ``[(name, value)]``
448 :param collections.Mapping cookies: cookies to set on the client
450 response = werkzeug.wrappers.Response(data, headers=headers)
452 for k, v in cookies.iteritems():
453 response.set_cookie(k, v)
456 def not_found(self, description=None):
457 """ Helper for 404 response, return its result from the method
459 return werkzeug.exceptions.NotFound(description)
465 Use the ``route()`` decorator instead.
468 base = f.__name__.lstrip('/')
469 if f.__name__ == "index":
471 return route([base, base + "/<path:_ignored_path>"], type="http", auth="none")(f)
473 #----------------------------------------------------------
474 # Local storage of requests
475 #----------------------------------------------------------
476 from werkzeug.local import LocalStack
478 _request_stack = LocalStack()
480 def set_request(request):
481 class with_obj(object):
483 _request_stack.push(request)
484 def __exit__(self, *args):
489 A global proxy that always redirect to the current request object.
491 request = _request_stack()
493 #----------------------------------------------------------
494 # Controller registration with a metaclass
495 #----------------------------------------------------------
498 controllers_per_module = {}
500 class ControllerType(type):
501 def __init__(cls, name, bases, attrs):
502 super(ControllerType, cls).__init__(name, bases, attrs)
504 # flag old-style methods with req as first argument
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"]:
510 v._first_arg_is_req = True
512 # store the controller in the controllers list
513 name_class = ("%s.%s" % (cls.__module__, cls.__name__), cls)
514 class_path = name_class[0].split(".")
515 if not class_path[:2] == ["openerp", "addons"]:
517 # we want to know all modules that have controllers
518 module = class_path[2]
519 # but we only store controllers directly inheriting from Controller
520 if not "Controller" in globals() or not Controller in bases:
522 controllers_per_module.setdefault(module, []).append(name_class)
524 class Controller(object):
525 __metaclass__ = ControllerType
527 #############################
529 #############################
531 class AuthenticationError(Exception):
534 class SessionExpiredException(Exception):
537 class Service(object):
540 Use ``openerp.netsvc.dispatch_rpc()`` instead.
542 def __init__(self, session, service_name):
543 self.session = session
544 self.service_name = service_name
546 def __getattr__(self, method):
547 def proxy_method(*args):
548 result = openerp.netsvc.dispatch_rpc(self.service_name, method, args)
555 Use the resistry and cursor in ``openerp.addons.web.http.request`` instead.
557 def __init__(self, session, model):
558 self.session = session
560 self.proxy = self.session.proxy('object')
562 def __getattr__(self, method):
563 self.session.assert_valid()
564 def proxy(*args, **kw):
565 # Can't provide any retro-compatibility for this case, so we check it and raise an Exception
566 # to tell the programmer to adapt his code
567 if not request.db or not request.uid or self.session.db != request.db \
568 or self.session.uid != request.uid:
569 raise Exception("Trying to use Model with badly configured database or user.")
571 mod = request.registry.get(self.model)
572 if method.startswith('_'):
573 raise Exception("Access denied")
574 meth = getattr(mod, method)
576 result = meth(cr, request.uid, *args, **kw)
579 if isinstance(result, list) and len(result) > 0 and "id" in result[0]:
583 result = [index[x] for x in args[0] if x in index]
587 class OpenERPSession(werkzeug.contrib.sessions.Session):
588 def __init__(self, *args, **kwargs):
590 self.modified = False
591 super(OpenERPSession, self).__init__(*args, **kwargs)
593 self._default_values()
594 self.modified = False
596 def __getattr__(self, attr):
597 return self.get(attr, None)
598 def __setattr__(self, k, v):
599 if getattr(self, "inited", False):
601 object.__getattribute__(self, k)
603 return self.__setitem__(k, v)
604 object.__setattr__(self, k, v)
606 def authenticate(self, db, login=None, password=None, uid=None):
608 Authenticate the current user with the given db, login and password. If successful, store
609 the authentication parameters in the current session and request.
611 :param uid: If not None, that user id will be used instead the login to authenticate the user.
615 wsgienv = request.httprequest.environ
617 base_location=request.httprequest.url_root.rstrip('/'),
618 HTTP_HOST=wsgienv['HTTP_HOST'],
619 REMOTE_ADDR=wsgienv['REMOTE_ADDR'],
621 uid = openerp.netsvc.dispatch_rpc('common', 'authenticate', [db, login, password, env])
623 security.check(db, uid, password)
627 self.password = password
629 request.disable_db = False
631 if uid: self.get_context()
634 def check_security(self):
636 Chech the current authentication parameters to know if those are still valid. This method
637 should be called at each request. If the authentication fails, a ``SessionExpiredException``
640 if not self.db or not self.uid:
641 raise SessionExpiredException("Session expired")
642 security.check(self.db, self.uid, self.password)
645 for k in self.keys():
647 self._default_values()
649 def _default_values(self):
650 self.setdefault("db", None)
651 self.setdefault("uid", None)
652 self.setdefault("login", None)
653 self.setdefault("password", None)
654 self.setdefault("context", {'tz': "UTC", "uid": None})
655 self.setdefault("jsonp_requests", {})
657 def get_context(self):
659 Re-initializes the current user's session context (based on
660 his preferences) by calling res.users.get_context() with the old
663 :returns: the new context
665 assert self.uid, "The user needs to be logged-in to initialize his context"
666 self.context = request.registry.get('res.users').context_get(request.cr, request.uid) or {}
667 self.context['uid'] = self.uid
668 self._fix_lang(self.context)
671 def _fix_lang(self, context):
672 """ OpenERP provides languages which may not make sense and/or may not
673 be understood by the web client's libraries.
677 :param dict context: context to fix
679 lang = context['lang']
681 # inane OpenERP locale
685 # lang to lang_REGION (datejs only handles lang_REGION, no bare langs)
686 if lang in babel.core.LOCALE_ALIASES:
687 lang = babel.core.LOCALE_ALIASES[lang]
689 context['lang'] = lang or 'en_US'
692 Damn properties for retro-compatibility. All of that is deprecated, all
699 def _db(self, value):
705 def _uid(self, value):
711 def _login(self, value):
717 def _password(self, value):
718 self.password = value
720 def send(self, service_name, method, *args):
723 Use ``openerp.netsvc.dispatch_rpc()`` instead.
725 return openerp.netsvc.dispatch_rpc(service_name, method, args)
727 def proxy(self, service):
730 Use ``openerp.netsvc.dispatch_rpc()`` instead.
732 return Service(self, service)
734 def assert_valid(self, force=False):
737 Use ``check_security()`` instead.
739 Ensures this session is valid (logged into the openerp server)
741 if self.uid and not force:
743 # TODO use authenticate instead of login
744 self.uid = self.proxy("common").login(self.db, self.login, self.password)
746 raise AuthenticationError("Authentication failure")
748 def ensure_valid(self):
751 Use ``check_security()`` instead.
755 self.assert_valid(True)
759 def execute(self, model, func, *l, **d):
762 Use the resistry and cursor in ``openerp.addons.web.http.request`` instead.
764 model = self.model(model)
765 r = getattr(model, func)(*l, **d)
768 def exec_workflow(self, model, id, signal):
771 Use the resistry and cursor in ``openerp.addons.web.http.request`` instead.
774 r = self.proxy('object').exec_workflow(self.db, self.uid, self.password, model, signal, id)
777 def model(self, model):
780 Use the resistry and cursor in ``openerp.addons.web.http.request`` instead.
782 Get an RPC proxy for the object ``model``, bound to this session.
784 :param model: an OpenERP model name
786 :rtype: a model object
789 raise SessionExpiredException("Session expired")
791 return Model(self, model)
793 def session_gc(session_store):
794 if random.random() < 0.001:
795 # we keep session one week
796 last_week = time.time() - 60*60*24*7
797 for fname in os.listdir(session_store.path):
798 path = os.path.join(session_store.path, fname)
800 if os.path.getmtime(path) < last_week:
805 #----------------------------------------------------------
807 #----------------------------------------------------------
808 # Add potentially missing (older ubuntu) font mime types
809 mimetypes.add_type('application/font-woff', '.woff')
810 mimetypes.add_type('application/vnd.ms-fontobject', '.eot')
811 mimetypes.add_type('application/x-font-ttf', '.ttf')
813 class DisableCacheMiddleware(object):
814 def __init__(self, app):
816 def __call__(self, environ, start_response):
817 def start_wrapped(status, headers):
818 referer = environ.get('HTTP_REFERER', '')
819 parsed = urlparse.urlparse(referer)
820 debug = parsed.query.count('debug') >= 1
823 unwanted_keys = ['Last-Modified']
825 new_headers = [('Cache-Control', 'no-cache')]
826 unwanted_keys += ['Expires', 'Etag', 'Cache-Control']
829 if k not in unwanted_keys:
830 new_headers.append((k, v))
832 start_response(status, new_headers)
833 return self.app(environ, start_wrapped)
838 username = pwd.getpwuid(os.geteuid()).pw_name
841 username = getpass.getuser()
844 path = os.path.join(tempfile.gettempdir(), "oe-sessions-" + username)
847 except OSError as exc:
848 if exc.errno == errno.EEXIST:
849 # directory exists: ensure it has the correct permissions
850 # this will fail if the directory is not owned by the current user
857 """Root WSGI application for the OpenERP Web Client.
864 self.db_routers_lock = threading.Lock()
868 # Setup http sessions
869 path = session_path()
870 self.session_store = werkzeug.contrib.sessions.FilesystemSessionStore(path, session_class=OpenERPSession)
871 _logger.debug('HTTP sessions stored in: %s', path)
874 def __call__(self, environ, start_response):
875 """ Handle a WSGI request
877 return self.dispatch(environ, start_response)
879 def dispatch(self, environ, start_response):
881 Performs the actual WSGI dispatching for the application.
884 httprequest = werkzeug.wrappers.Request(environ)
885 httprequest.parameter_storage_class = werkzeug.datastructures.ImmutableDict
886 httprequest.app = self
888 session_gc(self.session_store)
890 sid = httprequest.args.get('session_id')
891 explicit_session = True
893 sid = httprequest.headers.get("X-Openerp-Session-Id")
895 sid = httprequest.cookies.get('session_id')
896 explicit_session = False
898 httprequest.session = self.session_store.new()
900 httprequest.session = self.session_store.get(sid)
902 self._find_db(httprequest)
904 if not "lang" in httprequest.session.context:
905 lang = httprequest.accept_languages.best or "en_US"
906 lang = babel.core.LOCALE_ALIASES.get(lang, lang).replace('-', '_')
907 httprequest.session.context["lang"] = lang
909 request = self._build_request(httprequest)
913 updated = openerp.modules.registry.RegistryManager.check_registry_signaling(db)
915 with self.db_routers_lock:
916 del self.db_routers[db]
918 with set_request(request):
920 result = request.dispatch()
923 openerp.modules.registry.RegistryManager.signal_caches_change(db)
925 if isinstance(result, basestring):
926 headers=[('Content-Type', 'text/html; charset=utf-8'), ('Content-Length', len(result))]
927 response = werkzeug.wrappers.Response(result, headers=headers)
931 if httprequest.session.should_save:
932 self.session_store.save(httprequest.session)
933 if not explicit_session and hasattr(response, 'set_cookie'):
934 response.set_cookie('session_id', httprequest.session.sid, max_age=90 * 24 * 60 * 60)
936 return response(environ, start_response)
937 except werkzeug.exceptions.HTTPException, e:
938 return e(environ, start_response)
940 def _find_db(self, httprequest):
941 db = db_monodb(httprequest)
942 if db != httprequest.session.db:
943 httprequest.session.logout()
944 httprequest.session.db = db
946 def _build_request(self, httprequest):
947 if httprequest.args.get('jsonp'):
948 return JsonRequest(httprequest)
950 if httprequest.mimetype == "application/json":
951 return JsonRequest(httprequest)
953 return HttpRequest(httprequest)
955 def load_addons(self):
956 """ Load all addons from addons patch containg static files and
957 controllers and configure them. """
959 for addons_path in openerp.modules.module.ad_paths:
960 for module in sorted(os.listdir(str(addons_path))):
961 if module not in addons_module:
962 manifest_path = os.path.join(addons_path, module, '__openerp__.py')
963 path_static = os.path.join(addons_path, module, 'static')
964 if os.path.isfile(manifest_path) and os.path.isdir(path_static):
965 manifest = ast.literal_eval(open(manifest_path).read())
966 manifest['addons_path'] = addons_path
967 _logger.debug("Loading %s", module)
968 if 'openerp.addons' in sys.modules:
969 m = __import__('openerp.addons.' + module)
971 m = __import__(module)
972 addons_module[module] = m
973 addons_manifest[module] = manifest
974 self.statics['/%s/static' % module] = path_static
976 app = werkzeug.wsgi.SharedDataMiddleware(self.dispatch, self.statics)
977 self.dispatch = DisableCacheMiddleware(app)
979 def _build_router(self, db):
980 _logger.info("Generating routing configuration for database %s" % db)
981 routing_map = routing.Map()
983 def gen(modules, nodb_only):
984 for module in modules:
985 for v in controllers_per_module[module]:
988 subclasses = cls.__subclasses__()
989 subclasses = [c for c in subclasses if c.__module__.startswith('openerp.addons.') and
990 c.__module__.split(".")[2] in modules]
992 name = "%s (extended by %s)" % (cls.__name__, ', '.join(sub.__name__ for sub in subclasses))
993 cls = type(name, tuple(reversed(subclasses)), {})
996 members = inspect.getmembers(o)
997 for mk, mv in members:
998 if inspect.ismethod(mv) and getattr(mv, 'exposed', False) and \
999 nodb_only == (getattr(mv, "auth", "none") == "none"):
1000 for url in mv.routes:
1001 if getattr(mv, "combine", False):
1002 url = o._cp_path.rstrip('/') + '/' + url.lstrip('/')
1003 if url.endswith("/") and len(url) > 1:
1005 routing_map.add(routing.Rule(url, endpoint=mv))
1007 modules_set = set(controllers_per_module.keys()) - set(['web'])
1008 # building all none methods
1009 gen(["web"] + sorted(modules_set), True)
1013 registry = openerp.modules.registry.RegistryManager.get(db)
1014 with registry.cursor() as cr:
1015 m = registry.get('ir.module.module')
1016 ids = m.search(cr, openerp.SUPERUSER_ID, [('state', '=', 'installed'), ('name', '!=', 'web')])
1017 installed = set([x['name'] for x in m.read(cr, 1, ids, ['name'])])
1018 modules_set = modules_set.intersection(set(installed))
1019 modules = ["web"] + sorted(modules_set)
1020 # building all other methods
1025 def get_db_router(self, db):
1026 with self.db_routers_lock:
1027 router = self.db_routers.get(db)
1029 router = self._build_router(db)
1030 with self.db_routers_lock:
1031 self.db_routers[db] = router
1034 def find_handler(self):
1036 Tries to discover the controller handling the request for the path specified in the request.
1038 path = request.httprequest.path
1039 urls = self.get_db_router(request.db).bind("")
1040 func, arguments = urls.match(path)
1041 arguments = dict([(k, v) for k, v in arguments.items() if not k.startswith("_ignored_")])
1043 def nfunc(*args, **kwargs):
1044 kwargs.update(arguments)
1045 if getattr(func, '_first_arg_is_req', False):
1046 args = (request,) + args
1047 return func(*args, **kwargs)
1049 request.func = nfunc
1050 request.auth_method = getattr(func, "auth", "user")
1051 request.func_request_type = func.exposed
1055 def db_list(force=False, httprequest=None):
1056 httprequest = httprequest or request.httprequest
1057 dbs = openerp.netsvc.dispatch_rpc("db", "list", [force])
1058 h = httprequest.environ['HTTP_HOST'].split(':')[0]
1060 r = openerp.tools.config['dbfilter'].replace('%h', h).replace('%d', d)
1061 dbs = [i for i in dbs if re.match(r, i)]
1064 def db_monodb(httprequest=None):
1066 Magic function to find the current database.
1068 Implementation details:
1073 Returns ``None`` if the magic is not magic enough.
1075 httprequest = httprequest or request.httprequest
1079 dbs = db_list(True, httprequest)
1081 # try the db already in the session
1082 db_session = httprequest.session.db
1083 if db_session in dbs:
1086 # if dbfilters was specified when launching the server and there is
1087 # only one possible db, we take that one
1088 if openerp.tools.config['dbfilter'] != ".*" and len(dbs) == 1:
1092 class CommonController(Controller):
1094 @route('/jsonrpc', type='json', auth="none")
1095 def jsonrpc(self, service, method, args):
1096 """ Method used by client APIs to contact OpenERP. """
1097 return openerp.netsvc.dispatch_rpc(service, method, args)
1099 @route('/gen_session_id', type='json', auth="none")
1100 def gen_session_id(self):
1101 nsession = root.session_store.new()
1104 def wsgi_postload():
1107 openerp.wsgi.register_wsgi_handler(root)