[FIX] Inject user context in all domain and context evaluation
[odoo/odoo.git] / addons / web / session.py
1 #!/usr/bin/python
2 import datetime
3 import babel
4 import dateutil.relativedelta
5 import logging
6 import time
7 import traceback
8 import sys
9 import xmlrpclib
10
11 import openerp
12
13 _logger = logging.getLogger(__name__)
14
15 #----------------------------------------------------------
16 # OpenERPSession RPC openerp backend access
17 #----------------------------------------------------------
18 class AuthenticationError(Exception):
19     pass
20
21 class SessionExpiredException(Exception):
22     pass
23
24 class Service(object):
25     def __init__(self, session, service_name):
26         self.session = session
27         self.service_name = service_name
28
29     def __getattr__(self, method):
30         def proxy_method(*args):
31             result = self.session.send(self.service_name, method, *args)
32             return result
33         return proxy_method
34
35 class Model(object):
36     def __init__(self, session, model):
37         self.session = session
38         self.model = model
39         self.proxy = self.session.proxy('object')
40
41     def __getattr__(self, method):
42         def proxy(*args, **kw):
43             result = self.proxy.execute_kw(self.session._db, self.session._uid, self.session._password, self.model, method, args, kw)
44             # reorder read
45             if method == "read":
46                 if isinstance(result, list) and len(result) > 0 and "id" in result[0]:
47                     index = {}
48                     for r in result:
49                         index[r['id']] = r
50                     result = [index[x] for x in args[0] if x in index]
51             return result
52         return proxy
53
54     def search_read(self, domain=None, fields=None, offset=0, limit=None, order=None, context=None):
55         record_ids = self.search(domain or [], offset, limit or False, order or False, context or {})
56         if not record_ids: return []
57         records = self.read(record_ids, fields or [], context or {})
58         return records
59
60 class OpenERPSession(object):
61     """
62     An OpenERP RPC session, a given user can own multiple such sessions
63     in a web session.
64
65     .. attribute:: context
66
67         The session context, a ``dict``. Can be reloaded by calling
68         :meth:`openerpweb.openerpweb.OpenERPSession.get_context`
69
70     .. attribute:: domains_store
71
72         A ``dict`` matching domain keys to evaluable (but non-literal) domains.
73
74         Used to store references to non-literal domains which need to be
75         round-tripped to the client browser.
76     """
77     def __init__(self):
78         self._creation_time = time.time()
79         self._db = False
80         self._uid = False
81         self._login = False
82         self._password = False
83         self._suicide = False
84         self.context = {}
85         self.jsonp_requests = {}     # FIXME use a LRU
86
87     def send(self, service_name, method, *args):
88         code_string = u"warning -- %s\n\n%s"
89         try:
90             return openerp.netsvc.dispatch_rpc(service_name, method, args)
91         except openerp.osv.osv.except_osv, e:
92             raise xmlrpclib.Fault(code_string % (e.name, e.value), '')
93         except openerp.exceptions.Warning, e:
94             raise xmlrpclib.Fault(code_string % ("Warning", e), '')
95         except openerp.exceptions.AccessError, e:
96             raise xmlrpclib.Fault(code_string % ("AccessError", e), '')
97         except openerp.exceptions.AccessDenied, e:
98             raise xmlrpclib.Fault('AccessDenied', openerp.tools.ustr(e))
99         except openerp.exceptions.DeferredException, e:
100             formatted_info = "".join(traceback.format_exception(*e.traceback))
101             raise xmlrpclib.Fault(openerp.tools.ustr(e), formatted_info)
102         except Exception, e:
103             formatted_info = "".join(traceback.format_exception(*(sys.exc_info())))
104             raise xmlrpclib.Fault(openerp.tools.ustr(e), formatted_info)
105
106     def proxy(self, service):
107         return Service(self, service)
108
109     def bind(self, db, uid, login, password):
110         self._db = db
111         self._uid = uid
112         self._login = login
113         self._password = password
114
115     def authenticate(self, db, login, password, env=None):
116         uid = self.proxy('common').authenticate(db, login, password, env)
117         self.bind(db, uid, login, password)
118
119         if uid: self.get_context()
120         return uid
121
122     def assert_valid(self, force=False):
123         """
124         Ensures this session is valid (logged into the openerp server)
125         """
126         if self._uid and not force:
127             return
128         # TODO use authenticate instead of login
129         uid = self.proxy("common").login(self._db, self._login, self._password)
130         if not uid:
131             raise AuthenticationError("Authentication failure")
132
133     def ensure_valid(self):
134         if self._uid:
135             try:
136                 self.assert_valid(True)
137             except Exception:
138                 self._uid = None
139
140     def execute(self, model, func, *l, **d):
141         self.assert_valid()
142         model = self.model(model)
143         r = getattr(model, func)(*l, **d)
144         return r
145
146     def exec_workflow(self, model, id, signal):
147         self.assert_valid()
148         r = self.proxy('object').exec_workflow(self._db, self._uid, self._password, model, signal, id)
149         return r
150
151     def model(self, model):
152         """ Get an RPC proxy for the object ``model``, bound to this session.
153
154         :param model: an OpenERP model name
155         :type model: str
156         :rtype: a model object
157         """
158         if self._db == False:
159             raise SessionExpiredException("Session expired")
160
161         return Model(self, model)
162
163     def get_context(self):
164         """ Re-initializes the current user's session context (based on
165         his preferences) by calling res.users.get_context() with the old
166         context
167
168         :returns: the new context
169         """
170         assert self._uid, "The user needs to be logged-in to initialize his context"
171         self.context = self.model('res.users').context_get() or {}
172         self.context['uid'] = self._uid
173         self._fix_lang(self.context)
174         return self.context
175
176     def _fix_lang(self, context):
177         """ OpenERP provides languages which may not make sense and/or may not
178         be understood by the web client's libraries.
179
180         Fix those here.
181
182         :param dict context: context to fix
183         """
184         lang = context['lang']
185
186         # inane OpenERP locale
187         if lang == 'ar_AR':
188             lang = 'ar'
189
190         # lang to lang_REGION (datejs only handles lang_REGION, no bare langs)
191         if lang in babel.core.LOCALE_ALIASES:
192             lang = babel.core.LOCALE_ALIASES[lang]
193
194         context['lang'] = lang or 'en_US'
195
196 # vim:et:ts=4:sw=4: