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