merge with trunk and removed *.po
[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
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         self.session.assert_valid()
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         return openerp.netsvc.dispatch_rpc(service_name, method, args)
89
90     def proxy(self, service):
91         return Service(self, service)
92
93     def bind(self, db, uid, login, password):
94         self._db = db
95         self._uid = uid
96         self._login = login
97         self._password = password
98
99     def authenticate(self, db, login, password, env=None):
100         uid = self.proxy('common').authenticate(db, login, password, env)
101         self.bind(db, uid, login, password)
102
103         if uid: self.get_context()
104         return uid
105
106     def assert_valid(self, force=False):
107         """
108         Ensures this session is valid (logged into the openerp server)
109         """
110         if self._uid and not force:
111             return
112         # TODO use authenticate instead of login
113         self._uid = self.proxy("common").login(self._db, self._login, self._password)
114         if not self._uid:
115             raise AuthenticationError("Authentication failure")
116
117     def ensure_valid(self):
118         if self._uid:
119             try:
120                 self.assert_valid(True)
121             except Exception:
122                 self._uid = None
123
124     def execute(self, model, func, *l, **d):
125         model = self.model(model)
126         r = getattr(model, func)(*l, **d)
127         return r
128
129     def exec_workflow(self, model, id, signal):
130         self.assert_valid()
131         r = self.proxy('object').exec_workflow(self._db, self._uid, self._password, model, signal, id)
132         return r
133
134     def model(self, model):
135         """ Get an RPC proxy for the object ``model``, bound to this session.
136
137         :param model: an OpenERP model name
138         :type model: str
139         :rtype: a model object
140         """
141         if self._db == False:
142             raise SessionExpiredException("Session expired")
143
144         return Model(self, model)
145
146     def get_context(self):
147         """ Re-initializes the current user's session context (based on
148         his preferences) by calling res.users.get_context() with the old
149         context
150
151         :returns: the new context
152         """
153         assert self._uid, "The user needs to be logged-in to initialize his context"
154         self.context = self.model('res.users').context_get() or {}
155         self.context['uid'] = self._uid
156         self._fix_lang(self.context)
157         return self.context
158
159     def _fix_lang(self, context):
160         """ OpenERP provides languages which may not make sense and/or may not
161         be understood by the web client's libraries.
162
163         Fix those here.
164
165         :param dict context: context to fix
166         """
167         lang = context['lang']
168
169         # inane OpenERP locale
170         if lang == 'ar_AR':
171             lang = 'ar'
172
173         # lang to lang_REGION (datejs only handles lang_REGION, no bare langs)
174         if lang in babel.core.LOCALE_ALIASES:
175             lang = babel.core.LOCALE_ALIASES[lang]
176
177         context['lang'] = lang or 'en_US'
178
179 # vim:et:ts=4:sw=4: