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