Merge lp:~openerp-dev/openerp-web/trunk-bug-1220203-jar into lp:openerp-web
- trunk-bug-1220203-jar
- Merge into trunk
Proposed by
Jaydeep Barot(OpenERP)
Status: | Rejected | ||||
---|---|---|---|---|---|
Rejected by: | Thibault Delavallée (OpenERP) | ||||
Proposed branch: | lp:~openerp-dev/openerp-web/trunk-bug-1220203-jar | ||||
Merge into: | lp:openerp-web | ||||
Diff against target: |
1122 lines (+1118/-0) 1 file modified
addons/web/http.py.THIS (+1118/-0) |
||||
To merge this branch: | bzr merge lp:~openerp-dev/openerp-web/trunk-bug-1220203-jar | ||||
Related bugs: |
|
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Thibault Delavallée (OpenERP) (community) | Needs Fixing | ||
Atul Patel(OpenERP) (community) | Approve | ||
Review via email: mp+184968@code.launchpad.net |
Commit message
Description of the change
Hello,
[trunk] Fix upload attachment file in chatter view.
Thanks,
Jaydeep Barot.
To post a comment you must log in.
- 3835. By Mehul Mehta(OpenERP)
-
[Merge] with main main web
Revision history for this message
Thibault Delavallée (OpenERP) (tde-openerp) wrote : | # |
Hello,
Could you re-sync your branch with web, now that http moved to server ?
review:
Needs Fixing
Revision history for this message
Thibault Delavallée (OpenERP) (tde-openerp) wrote : | # |
The related bug is already fixed. I therefore reject this branch, as there is no need to work furthuer on it.
Unmerged revisions
- 3835. By Mehul Mehta(OpenERP)
-
[Merge] with main main web
- 3834. By Jaydeep Barot(OpenERP)
-
[FIX] fix upload attachment in chatter
Preview Diff
[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1 | === added file 'addons/web/http.py.THIS' | |||
2 | --- addons/web/http.py.THIS 1970-01-01 00:00:00 +0000 | |||
3 | +++ addons/web/http.py.THIS 2013-10-29 08:41:10 +0000 | |||
4 | @@ -0,0 +1,1118 @@ | |||
5 | 1 | # -*- coding: utf-8 -*- | ||
6 | 2 | #---------------------------------------------------------- | ||
7 | 3 | # OpenERP Web HTTP layer | ||
8 | 4 | #---------------------------------------------------------- | ||
9 | 5 | import ast | ||
10 | 6 | import cgi | ||
11 | 7 | import contextlib | ||
12 | 8 | import functools | ||
13 | 9 | import getpass | ||
14 | 10 | import logging | ||
15 | 11 | import mimetypes | ||
16 | 12 | import os | ||
17 | 13 | import pprint | ||
18 | 14 | import random | ||
19 | 15 | import sys | ||
20 | 16 | import tempfile | ||
21 | 17 | import threading | ||
22 | 18 | import time | ||
23 | 19 | import traceback | ||
24 | 20 | import urlparse | ||
25 | 21 | import uuid | ||
26 | 22 | import errno | ||
27 | 23 | import re | ||
28 | 24 | |||
29 | 25 | import babel.core | ||
30 | 26 | import simplejson | ||
31 | 27 | import werkzeug.contrib.sessions | ||
32 | 28 | import werkzeug.datastructures | ||
33 | 29 | import werkzeug.exceptions | ||
34 | 30 | import werkzeug.utils | ||
35 | 31 | import werkzeug.wrappers | ||
36 | 32 | import werkzeug.wsgi | ||
37 | 33 | import werkzeug.routing as routing | ||
38 | 34 | import urllib | ||
39 | 35 | import urllib2 | ||
40 | 36 | |||
41 | 37 | import openerp | ||
42 | 38 | import openerp.service.security as security | ||
43 | 39 | from openerp.tools import config | ||
44 | 40 | |||
45 | 41 | import inspect | ||
46 | 42 | import functools | ||
47 | 43 | |||
48 | 44 | _logger = logging.getLogger(__name__) | ||
49 | 45 | |||
50 | 46 | #---------------------------------------------------------- | ||
51 | 47 | # RequestHandler | ||
52 | 48 | #---------------------------------------------------------- | ||
53 | 49 | class WebRequest(object): | ||
54 | 50 | """ Parent class for all OpenERP Web request types, mostly deals with | ||
55 | 51 | initialization and setup of the request object (the dispatching itself has | ||
56 | 52 | to be handled by the subclasses) | ||
57 | 53 | |||
58 | 54 | :param request: a wrapped werkzeug Request object | ||
59 | 55 | :type request: :class:`werkzeug.wrappers.BaseRequest` | ||
60 | 56 | |||
61 | 57 | .. attribute:: httprequest | ||
62 | 58 | |||
63 | 59 | the original :class:`werkzeug.wrappers.Request` object provided to the | ||
64 | 60 | request | ||
65 | 61 | |||
66 | 62 | .. attribute:: httpsession | ||
67 | 63 | |||
68 | 64 | .. deprecated:: 8.0 | ||
69 | 65 | |||
70 | 66 | Use ``self.session`` instead. | ||
71 | 67 | |||
72 | 68 | .. attribute:: params | ||
73 | 69 | |||
74 | 70 | :class:`~collections.Mapping` of request parameters, not generally | ||
75 | 71 | useful as they're provided directly to the handler method as keyword | ||
76 | 72 | arguments | ||
77 | 73 | |||
78 | 74 | .. attribute:: session_id | ||
79 | 75 | |||
80 | 76 | opaque identifier for the :class:`session.OpenERPSession` instance of | ||
81 | 77 | the current request | ||
82 | 78 | |||
83 | 79 | .. attribute:: session | ||
84 | 80 | |||
85 | 81 | a :class:`OpenERPSession` holding the HTTP session data for the | ||
86 | 82 | current http session | ||
87 | 83 | |||
88 | 84 | .. attribute:: context | ||
89 | 85 | |||
90 | 86 | :class:`~collections.Mapping` of context values for the current request | ||
91 | 87 | |||
92 | 88 | .. attribute:: db | ||
93 | 89 | |||
94 | 90 | ``str``, the name of the database linked to the current request. Can be ``None`` | ||
95 | 91 | if the current request uses the ``none`` authentication. | ||
96 | 92 | |||
97 | 93 | .. attribute:: uid | ||
98 | 94 | |||
99 | 95 | ``int``, the id of the user related to the current request. Can be ``None`` | ||
100 | 96 | if the current request uses the ``none`` authenticatoin. | ||
101 | 97 | """ | ||
102 | 98 | def __init__(self, httprequest): | ||
103 | 99 | self.httprequest = httprequest | ||
104 | 100 | self.httpresponse = None | ||
105 | 101 | self.httpsession = httprequest.session | ||
106 | 102 | self.session = httprequest.session | ||
107 | 103 | self.session_id = httprequest.session.sid | ||
108 | 104 | self.disable_db = False | ||
109 | 105 | self.uid = None | ||
110 | 106 | self.func = None | ||
111 | 107 | self.auth_method = None | ||
112 | 108 | self._cr_cm = None | ||
113 | 109 | self._cr = None | ||
114 | 110 | self.func_request_type = None | ||
115 | 111 | # set db/uid trackers - they're cleaned up at the WSGI | ||
116 | 112 | # dispatching phase in openerp.service.wsgi_server.application | ||
117 | 113 | if self.db: | ||
118 | 114 | threading.current_thread().dbname = self.db | ||
119 | 115 | if self.session.uid: | ||
120 | 116 | threading.current_thread().uid = self.session.uid | ||
121 | 117 | self.context = dict(self.session.context) | ||
122 | 118 | self.lang = self.context["lang"] | ||
123 | 119 | |||
124 | 120 | def _authenticate(self): | ||
125 | 121 | if self.session.uid: | ||
126 | 122 | try: | ||
127 | 123 | self.session.check_security() | ||
128 | 124 | except SessionExpiredException, e: | ||
129 | 125 | self.session.logout() | ||
130 | 126 | raise SessionExpiredException("Session expired for request %s" % self.httprequest) | ||
131 | 127 | auth_methods[self.auth_method]() | ||
132 | 128 | @property | ||
133 | 129 | def registry(self): | ||
134 | 130 | """ | ||
135 | 131 | The registry to the database linked to this request. Can be ``None`` if the current request uses the | ||
136 | 132 | ``none'' authentication. | ||
137 | 133 | """ | ||
138 | 134 | return openerp.modules.registry.RegistryManager.get(self.db) if self.db else None | ||
139 | 135 | |||
140 | 136 | @property | ||
141 | 137 | def db(self): | ||
142 | 138 | """ | ||
143 | 139 | The registry to the database linked to this request. Can be ``None`` if the current request uses the | ||
144 | 140 | ``none'' authentication. | ||
145 | 141 | """ | ||
146 | 142 | return self.session.db if not self.disable_db else None | ||
147 | 143 | |||
148 | 144 | @property | ||
149 | 145 | def cr(self): | ||
150 | 146 | """ | ||
151 | 147 | The cursor initialized for the current method call. If the current request uses the ``none`` authentication | ||
152 | 148 | trying to access this property will raise an exception. | ||
153 | 149 | """ | ||
154 | 150 | # some magic to lazy create the cr | ||
155 | 151 | if not self._cr_cm: | ||
156 | 152 | self._cr_cm = self.registry.cursor() | ||
157 | 153 | self._cr = self._cr_cm.__enter__() | ||
158 | 154 | return self._cr | ||
159 | 155 | |||
160 | 156 | def _call_function(self, *args, **kwargs): | ||
161 | 157 | self._authenticate() | ||
162 | 158 | try: | ||
163 | 159 | # ugly syntax only to get the __exit__ arguments to pass to self._cr | ||
164 | 160 | request = self | ||
165 | 161 | class with_obj(object): | ||
166 | 162 | def __enter__(self): | ||
167 | 163 | pass | ||
168 | 164 | def __exit__(self, *args): | ||
169 | 165 | if request._cr_cm: | ||
170 | 166 | request._cr_cm.__exit__(*args) | ||
171 | 167 | request._cr_cm = None | ||
172 | 168 | request._cr = None | ||
173 | 169 | |||
174 | 170 | with with_obj(): | ||
175 | 171 | if self.func_request_type != self._request_type: | ||
176 | 172 | raise Exception("%s, %s: Function declared as capable of handling request of type '%s' but called with a request of type '%s'" \ | ||
177 | 173 | % (self.func, self.httprequest.path, self.func_request_type, self._request_type)) | ||
178 | 174 | return self.func(*args, **kwargs) | ||
179 | 175 | finally: | ||
180 | 176 | # just to be sure no one tries to re-use the request | ||
181 | 177 | self.disable_db = True | ||
182 | 178 | self.uid = None | ||
183 | 179 | |||
184 | 180 | def auth_method_user(): | ||
185 | 181 | request.uid = request.session.uid | ||
186 | 182 | |||
187 | 183 | def auth_method_admin(): | ||
188 | 184 | if not request.db: | ||
189 | 185 | raise SessionExpiredException("No valid database for request %s" % request.httprequest) | ||
190 | 186 | request.uid = openerp.SUPERUSER_ID | ||
191 | 187 | |||
192 | 188 | def auth_method_none(): | ||
193 | 189 | request.disable_db = True | ||
194 | 190 | request.uid = None | ||
195 | 191 | |||
196 | 192 | auth_methods = { | ||
197 | 193 | "user": auth_method_user, | ||
198 | 194 | "admin": auth_method_admin, | ||
199 | 195 | "none": auth_method_none, | ||
200 | 196 | } | ||
201 | 197 | |||
202 | 198 | def route(route, type="http", auth="user"): | ||
203 | 199 | """ | ||
204 | 200 | Decorator marking the decorated method as being a handler for requests. The method must be part of a subclass | ||
205 | 201 | of ``Controller``. | ||
206 | 202 | |||
207 | 203 | :param route: string or array. The route part that will determine which http requests will match the decorated | ||
208 | 204 | method. Can be a single string or an array of strings. See werkzeug's routing documentation for the format of | ||
209 | 205 | route expression ( http://werkzeug.pocoo.org/docs/routing/ ). | ||
210 | 206 | :param type: The type of request, can be ``'http'`` or ``'json'``. | ||
211 | 207 | :param auth: The type of authentication method, can on of the following: | ||
212 | 208 | |||
213 | 209 | * ``user``: The user must be authenticated and the current request will perform using the rights of the | ||
214 | 210 | user. | ||
215 | 211 | * ``admin``: The user may not be authenticated and the current request will perform using the admin user. | ||
216 | 212 | * ``none``: The method is always active, even if there is no database. Mainly used by the framework and | ||
217 | 213 | authentication modules. There request code will not have any facilities to access the database nor have any | ||
218 | 214 | configuration indicating the current database nor the current user. | ||
219 | 215 | """ | ||
220 | 216 | assert type in ["http", "json"] | ||
221 | 217 | assert auth in auth_methods.keys() | ||
222 | 218 | def decorator(f): | ||
223 | 219 | if isinstance(route, list): | ||
224 | 220 | f.routes = route | ||
225 | 221 | else: | ||
226 | 222 | f.routes = [route] | ||
227 | 223 | f.exposed = type | ||
228 | 224 | if getattr(f, "auth", None) is None: | ||
229 | 225 | f.auth = auth | ||
230 | 226 | return f | ||
231 | 227 | return decorator | ||
232 | 228 | |||
233 | 229 | def reject_nonliteral(dct): | ||
234 | 230 | if '__ref' in dct: | ||
235 | 231 | raise ValueError( | ||
236 | 232 | "Non literal contexts can not be sent to the server anymore (%r)" % (dct,)) | ||
237 | 233 | return dct | ||
238 | 234 | |||
239 | 235 | class JsonRequest(WebRequest): | ||
240 | 236 | """ JSON-RPC2 over HTTP. | ||
241 | 237 | |||
242 | 238 | Sucessful request:: | ||
243 | 239 | |||
244 | 240 | --> {"jsonrpc": "2.0", | ||
245 | 241 | "method": "call", | ||
246 | 242 | "params": {"context": {}, | ||
247 | 243 | "arg1": "val1" }, | ||
248 | 244 | "id": null} | ||
249 | 245 | |||
250 | 246 | <-- {"jsonrpc": "2.0", | ||
251 | 247 | "result": { "res1": "val1" }, | ||
252 | 248 | "id": null} | ||
253 | 249 | |||
254 | 250 | Request producing a error:: | ||
255 | 251 | |||
256 | 252 | --> {"jsonrpc": "2.0", | ||
257 | 253 | "method": "call", | ||
258 | 254 | "params": {"context": {}, | ||
259 | 255 | "arg1": "val1" }, | ||
260 | 256 | "id": null} | ||
261 | 257 | |||
262 | 258 | <-- {"jsonrpc": "2.0", | ||
263 | 259 | "error": {"code": 1, | ||
264 | 260 | "message": "End user error message.", | ||
265 | 261 | "data": {"code": "codestring", | ||
266 | 262 | "debug": "traceback" } }, | ||
267 | 263 | "id": null} | ||
268 | 264 | |||
269 | 265 | """ | ||
270 | 266 | _request_type = "json" | ||
271 | 267 | |||
272 | 268 | def __init__(self, *args): | ||
273 | 269 | super(JsonRequest, self).__init__(*args) | ||
274 | 270 | |||
275 | 271 | self.jsonp_handler = None | ||
276 | 272 | |||
277 | 273 | args = self.httprequest.args | ||
278 | 274 | jsonp = args.get('jsonp') | ||
279 | 275 | self.jsonp = jsonp | ||
280 | 276 | request = None | ||
281 | 277 | request_id = args.get('id') | ||
282 | 278 | |||
283 | 279 | if jsonp and self.httprequest.method == 'POST': | ||
284 | 280 | # jsonp 2 steps step1 POST: save call | ||
285 | 281 | def handler(): | ||
286 | 282 | self.session.jsonp_requests[request_id] = self.httprequest.form['r'] | ||
287 | 283 | self.session.modified = True | ||
288 | 284 | headers=[('Content-Type', 'text/plain; charset=utf-8')] | ||
289 | 285 | r = werkzeug.wrappers.Response(request_id, headers=headers) | ||
290 | 286 | return r | ||
291 | 287 | self.jsonp_handler = handler | ||
292 | 288 | return | ||
293 | 289 | elif jsonp and args.get('r'): | ||
294 | 290 | # jsonp method GET | ||
295 | 291 | request = args.get('r') | ||
296 | 292 | elif jsonp and request_id: | ||
297 | 293 | # jsonp 2 steps step2 GET: run and return result | ||
298 | 294 | request = self.session.jsonp_requests.pop(request_id, "") | ||
299 | 295 | else: | ||
300 | 296 | # regular jsonrpc2 | ||
301 | 297 | request = self.httprequest.stream.read() | ||
302 | 298 | |||
303 | 299 | # Read POST content or POST Form Data named "request" | ||
304 | 300 | self.jsonrequest = simplejson.loads(request, object_hook=reject_nonliteral) | ||
305 | 301 | self.params = dict(self.jsonrequest.get("params", {})) | ||
306 | 302 | self.context = self.params.pop('context', self.session.context) | ||
307 | 303 | |||
308 | 304 | def dispatch(self): | ||
309 | 305 | """ Calls the method asked for by the JSON-RPC2 or JSONP request | ||
310 | 306 | """ | ||
311 | 307 | if self.jsonp_handler: | ||
312 | 308 | return self.jsonp_handler() | ||
313 | 309 | response = {"jsonrpc": "2.0" } | ||
314 | 310 | error = None | ||
315 | 311 | |||
316 | 312 | try: | ||
317 | 313 | response['id'] = self.jsonrequest.get('id') | ||
318 | 314 | response["result"] = self._call_function(**self.params) | ||
319 | 315 | except AuthenticationError, e: | ||
320 | 316 | _logger.exception("Exception during JSON request handling.") | ||
321 | 317 | se = serialize_exception(e) | ||
322 | 318 | error = { | ||
323 | 319 | 'code': 100, | ||
324 | 320 | 'message': "OpenERP Session Invalid", | ||
325 | 321 | 'data': se | ||
326 | 322 | } | ||
327 | 323 | except Exception, e: | ||
328 | 324 | _logger.exception("Exception during JSON request handling.") | ||
329 | 325 | se = serialize_exception(e) | ||
330 | 326 | error = { | ||
331 | 327 | 'code': 200, | ||
332 | 328 | 'message': "OpenERP Server Error", | ||
333 | 329 | 'data': se | ||
334 | 330 | } | ||
335 | 331 | if error: | ||
336 | 332 | response["error"] = error | ||
337 | 333 | |||
338 | 334 | if self.jsonp: | ||
339 | 335 | # If we use jsonp, that's mean we are called from another host | ||
340 | 336 | # Some browser (IE and Safari) do no allow third party cookies | ||
341 | 337 | # We need then to manage http sessions manually. | ||
342 | 338 | response['session_id'] = self.session_id | ||
343 | 339 | mime = 'application/javascript' | ||
344 | 340 | body = "%s(%s);" % (self.jsonp, simplejson.dumps(response),) | ||
345 | 341 | else: | ||
346 | 342 | mime = 'application/json' | ||
347 | 343 | body = simplejson.dumps(response) | ||
348 | 344 | |||
349 | 345 | r = werkzeug.wrappers.Response(body, headers=[('Content-Type', mime), ('Content-Length', len(body))]) | ||
350 | 346 | return r | ||
351 | 347 | |||
352 | 348 | def serialize_exception(e): | ||
353 | 349 | tmp = { | ||
354 | 350 | "name": type(e).__module__ + "." + type(e).__name__ if type(e).__module__ else type(e).__name__, | ||
355 | 351 | "debug": traceback.format_exc(), | ||
356 | 352 | "message": u"%s" % e, | ||
357 | 353 | "arguments": to_jsonable(e.args), | ||
358 | 354 | } | ||
359 | 355 | if isinstance(e, openerp.osv.osv.except_osv): | ||
360 | 356 | tmp["exception_type"] = "except_osv" | ||
361 | 357 | elif isinstance(e, openerp.exceptions.Warning): | ||
362 | 358 | tmp["exception_type"] = "warning" | ||
363 | 359 | elif isinstance(e, openerp.exceptions.AccessError): | ||
364 | 360 | tmp["exception_type"] = "access_error" | ||
365 | 361 | elif isinstance(e, openerp.exceptions.AccessDenied): | ||
366 | 362 | tmp["exception_type"] = "access_denied" | ||
367 | 363 | return tmp | ||
368 | 364 | |||
369 | 365 | def to_jsonable(o): | ||
370 | 366 | if isinstance(o, str) or isinstance(o,unicode) or isinstance(o, int) or isinstance(o, long) \ | ||
371 | 367 | or isinstance(o, bool) or o is None or isinstance(o, float): | ||
372 | 368 | return o | ||
373 | 369 | if isinstance(o, list) or isinstance(o, tuple): | ||
374 | 370 | return [to_jsonable(x) for x in o] | ||
375 | 371 | if isinstance(o, dict): | ||
376 | 372 | tmp = {} | ||
377 | 373 | for k, v in o.items(): | ||
378 | 374 | tmp[u"%s" % k] = to_jsonable(v) | ||
379 | 375 | return tmp | ||
380 | 376 | return u"%s" % o | ||
381 | 377 | |||
382 | 378 | def jsonrequest(f): | ||
383 | 379 | """ | ||
384 | 380 | .. deprecated:: 8.0 | ||
385 | 381 | |||
386 | 382 | Use the ``route()`` decorator instead. | ||
387 | 383 | """ | ||
388 | 384 | f.combine = True | ||
389 | 385 | base = f.__name__ | ||
390 | 386 | if f.__name__ == "index": | ||
391 | 387 | base = "" | ||
392 | 388 | return route([base, os.path.join(base, "<path:_ignored_path>")], type="json", auth="user")(f) | ||
393 | 389 | |||
394 | 390 | class HttpRequest(WebRequest): | ||
395 | 391 | """ Regular GET/POST request | ||
396 | 392 | """ | ||
397 | 393 | _request_type = "http" | ||
398 | 394 | |||
399 | 395 | def __init__(self, *args): | ||
400 | 396 | super(HttpRequest, self).__init__(*args) | ||
401 | 397 | params = dict(self.httprequest.args) | ||
402 | 398 | params.update(self.httprequest.form) | ||
403 | 399 | params.update(self.httprequest.files) | ||
404 | 400 | ex = set(["session_id"]) | ||
405 | 401 | for k in params.keys(): | ||
406 | 402 | if k in ex: | ||
407 | 403 | del params[k] | ||
408 | 404 | self.params = params | ||
409 | 405 | |||
410 | 406 | def dispatch(self): | ||
411 | 407 | akw = {} | ||
412 | 408 | for key, value in self.httprequest.args.iteritems(): | ||
413 | 409 | if isinstance(value, basestring) and len(value) < 1024: | ||
414 | 410 | akw[key] = value | ||
415 | 411 | else: | ||
416 | 412 | akw[key] = type(value) | ||
417 | 413 | try: | ||
418 | 414 | r = self._call_function(**self.params) | ||
419 | 415 | except werkzeug.exceptions.HTTPException, e: | ||
420 | 416 | r = e | ||
421 | 417 | except Exception, e: | ||
422 | 418 | _logger.exception("An exception occured during an http request") | ||
423 | 419 | se = serialize_exception(e) | ||
424 | 420 | error = { | ||
425 | 421 | 'code': 200, | ||
426 | 422 | 'message': "OpenERP Server Error", | ||
427 | 423 | 'data': se | ||
428 | 424 | } | ||
429 | 425 | r = werkzeug.exceptions.InternalServerError(cgi.escape(simplejson.dumps(error))) | ||
430 | 426 | else: | ||
431 | 427 | if not r: | ||
432 | 428 | r = werkzeug.wrappers.Response(status=204) # no content | ||
433 | 429 | return r | ||
434 | 430 | |||
435 | 431 | def make_response(self, data, headers=None, cookies=None): | ||
436 | 432 | """ Helper for non-HTML responses, or HTML responses with custom | ||
437 | 433 | response headers or cookies. | ||
438 | 434 | |||
439 | 435 | While handlers can just return the HTML markup of a page they want to | ||
440 | 436 | send as a string if non-HTML data is returned they need to create a | ||
441 | 437 | complete response object, or the returned data will not be correctly | ||
442 | 438 | interpreted by the clients. | ||
443 | 439 | |||
444 | 440 | :param basestring data: response body | ||
445 | 441 | :param headers: HTTP headers to set on the response | ||
446 | 442 | :type headers: ``[(name, value)]`` | ||
447 | 443 | :param collections.Mapping cookies: cookies to set on the client | ||
448 | 444 | """ | ||
449 | 445 | response = werkzeug.wrappers.Response(data, headers=headers) | ||
450 | 446 | if cookies: | ||
451 | 447 | for k, v in cookies.iteritems(): | ||
452 | 448 | response.set_cookie(k, v) | ||
453 | 449 | return response | ||
454 | 450 | |||
455 | 451 | def not_found(self, description=None): | ||
456 | 452 | """ Helper for 404 response, return its result from the method | ||
457 | 453 | """ | ||
458 | 454 | return werkzeug.exceptions.NotFound(description) | ||
459 | 455 | |||
460 | 456 | def httprequest(f): | ||
461 | 457 | """ | ||
462 | 458 | .. deprecated:: 8.0 | ||
463 | 459 | |||
464 | 460 | Use the ``route()`` decorator instead. | ||
465 | 461 | """ | ||
466 | 462 | f.combine = True | ||
467 | 463 | base = f.__name__ | ||
468 | 464 | if f.__name__ == "index": | ||
469 | 465 | base = "" | ||
470 | 466 | return route([base, os.path.join(base, "<path:_ignored_path>")], type="http", auth="user")(f) | ||
471 | 467 | |||
472 | 468 | #---------------------------------------------------------- | ||
473 | 469 | # Local storage of requests | ||
474 | 470 | #---------------------------------------------------------- | ||
475 | 471 | from werkzeug.local import LocalStack | ||
476 | 472 | |||
477 | 473 | _request_stack = LocalStack() | ||
478 | 474 | |||
479 | 475 | def set_request(request): | ||
480 | 476 | class with_obj(object): | ||
481 | 477 | def __enter__(self): | ||
482 | 478 | _request_stack.push(request) | ||
483 | 479 | def __exit__(self, *args): | ||
484 | 480 | _request_stack.pop() | ||
485 | 481 | return with_obj() | ||
486 | 482 | |||
487 | 483 | """ | ||
488 | 484 | A global proxy that always redirect to the current request object. | ||
489 | 485 | """ | ||
490 | 486 | request = _request_stack() | ||
491 | 487 | |||
492 | 488 | #---------------------------------------------------------- | ||
493 | 489 | # Controller registration with a metaclass | ||
494 | 490 | #---------------------------------------------------------- | ||
495 | 491 | addons_module = {} | ||
496 | 492 | addons_manifest = {} | ||
497 | 493 | controllers_per_module = {} | ||
498 | 494 | |||
499 | 495 | class ControllerType(type): | ||
500 | 496 | def __init__(cls, name, bases, attrs): | ||
501 | 497 | super(ControllerType, cls).__init__(name, bases, attrs) | ||
502 | 498 | |||
503 | 499 | # create wrappers for old-style methods with req as first argument | ||
504 | 500 | cls._methods_wrapper = {} | ||
505 | 501 | for k, v in attrs.items(): | ||
506 | 502 | if inspect.isfunction(v): | ||
507 | 503 | spec = inspect.getargspec(v) | ||
508 | 504 | first_arg = spec.args[1] if len(spec.args) >= 2 else None | ||
509 | 505 | if first_arg in ["req", "request"]: | ||
510 | 506 | def build_new(nv): | ||
511 | 507 | return lambda self, *args, **kwargs: nv(self, request, *args, **kwargs) | ||
512 | 508 | cls._methods_wrapper[k] = build_new(v) | ||
513 | 509 | |||
514 | 510 | # store the controller in the controllers list | ||
515 | 511 | name_class = ("%s.%s" % (cls.__module__, cls.__name__), cls) | ||
516 | 512 | class_path = name_class[0].split(".") | ||
517 | 513 | if not class_path[:2] == ["openerp", "addons"]: | ||
518 | 514 | return | ||
519 | 515 | # we want to know all modules that have controllers | ||
520 | 516 | module = class_path[2] | ||
521 | 517 | controllers_per_module.setdefault(module, []) | ||
522 | 518 | # but we only store controllers directly inheriting from Controller | ||
523 | 519 | if not "Controller" in globals() or not Controller in bases: | ||
524 | 520 | return | ||
525 | 521 | controllers_per_module.setdefault(module, []).append(name_class) | ||
526 | 522 | |||
527 | 523 | class Controller(object): | ||
528 | 524 | __metaclass__ = ControllerType | ||
529 | 525 | |||
530 | 526 | def get_wrapped_method(self, name): | ||
531 | 527 | if name in self.__class__._methods_wrapper: | ||
532 | 528 | return functools.partial(self.__class__._methods_wrapper[name], self) | ||
533 | 529 | else: | ||
534 | 530 | return getattr(self, name) | ||
535 | 531 | |||
536 | 532 | ############################# | ||
537 | 533 | # OpenERP Sessions # | ||
538 | 534 | ############################# | ||
539 | 535 | |||
540 | 536 | class AuthenticationError(Exception): | ||
541 | 537 | pass | ||
542 | 538 | |||
543 | 539 | class SessionExpiredException(Exception): | ||
544 | 540 | pass | ||
545 | 541 | |||
546 | 542 | class Service(object): | ||
547 | 543 | """ | ||
548 | 544 | .. deprecated:: 8.0 | ||
549 | 545 | Use ``openerp.netsvc.dispatch_rpc()`` instead. | ||
550 | 546 | """ | ||
551 | 547 | def __init__(self, session, service_name): | ||
552 | 548 | self.session = session | ||
553 | 549 | self.service_name = service_name | ||
554 | 550 | |||
555 | 551 | def __getattr__(self, method): | ||
556 | 552 | def proxy_method(*args): | ||
557 | 553 | result = openerp.netsvc.dispatch_rpc(self.service_name, method, args) | ||
558 | 554 | return result | ||
559 | 555 | return proxy_method | ||
560 | 556 | |||
561 | 557 | class Model(object): | ||
562 | 558 | """ | ||
563 | 559 | .. deprecated:: 8.0 | ||
564 | 560 | Use the resistry and cursor in ``openerp.addons.web.http.request`` instead. | ||
565 | 561 | """ | ||
566 | 562 | def __init__(self, session, model): | ||
567 | 563 | self.session = session | ||
568 | 564 | self.model = model | ||
569 | 565 | self.proxy = self.session.proxy('object') | ||
570 | 566 | |||
571 | 567 | def __getattr__(self, method): | ||
572 | 568 | self.session.assert_valid() | ||
573 | 569 | def proxy(*args, **kw): | ||
574 | 570 | # Can't provide any retro-compatibility for this case, so we check it and raise an Exception | ||
575 | 571 | # to tell the programmer to adapt his code | ||
576 | 572 | if not request.db or not request.uid or self.session.db != request.db \ | ||
577 | 573 | or self.session.uid != request.uid: | ||
578 | 574 | raise Exception("Trying to use Model with badly configured database or user.") | ||
579 | 575 | |||
580 | 576 | mod = request.registry.get(self.model) | ||
581 | 577 | if method.startswith('_'): | ||
582 | 578 | raise Exception("Access denied") | ||
583 | 579 | meth = getattr(mod, method) | ||
584 | 580 | cr = request.cr | ||
585 | 581 | result = meth(cr, request.uid, *args, **kw) | ||
586 | 582 | # reorder read | ||
587 | 583 | if method == "read": | ||
588 | 584 | if isinstance(result, list) and len(result) > 0 and "id" in result[0]: | ||
589 | 585 | index = {} | ||
590 | 586 | for r in result: | ||
591 | 587 | index[r['id']] = r | ||
592 | 588 | result = [index[x] for x in args[0] if x in index] | ||
593 | 589 | return result | ||
594 | 590 | return proxy | ||
595 | 591 | |||
596 | 592 | class OpenERPSession(werkzeug.contrib.sessions.Session): | ||
597 | 593 | def __init__(self, *args, **kwargs): | ||
598 | 594 | self.inited = False | ||
599 | 595 | self.modified = False | ||
600 | 596 | super(OpenERPSession, self).__init__(*args, **kwargs) | ||
601 | 597 | self.inited = True | ||
602 | 598 | self._default_values() | ||
603 | 599 | self.modified = False | ||
604 | 600 | |||
605 | 601 | def __getattr__(self, attr): | ||
606 | 602 | return self.get(attr, None) | ||
607 | 603 | def __setattr__(self, k, v): | ||
608 | 604 | if getattr(self, "inited", False): | ||
609 | 605 | try: | ||
610 | 606 | object.__getattribute__(self, k) | ||
611 | 607 | except: | ||
612 | 608 | return self.__setitem__(k, v) | ||
613 | 609 | object.__setattr__(self, k, v) | ||
614 | 610 | |||
615 | 611 | def authenticate(self, db, login=None, password=None, uid=None): | ||
616 | 612 | """ | ||
617 | 613 | Authenticate the current user with the given db, login and password. If successful, store | ||
618 | 614 | the authentication parameters in the current session and request. | ||
619 | 615 | |||
620 | 616 | :param uid: If not None, that user id will be used instead the login to authenticate the user. | ||
621 | 617 | """ | ||
622 | 618 | |||
623 | 619 | if uid is None: | ||
624 | 620 | wsgienv = request.httprequest.environ | ||
625 | 621 | env = dict( | ||
626 | 622 | base_location=request.httprequest.url_root.rstrip('/'), | ||
627 | 623 | HTTP_HOST=wsgienv['HTTP_HOST'], | ||
628 | 624 | REMOTE_ADDR=wsgienv['REMOTE_ADDR'], | ||
629 | 625 | ) | ||
630 | 626 | uid = openerp.netsvc.dispatch_rpc('common', 'authenticate', [db, login, password, env]) | ||
631 | 627 | else: | ||
632 | 628 | security.check(db, uid, password) | ||
633 | 629 | self.db = db | ||
634 | 630 | self.uid = uid | ||
635 | 631 | self.login = login | ||
636 | 632 | self.password = password | ||
637 | 633 | request.uid = uid | ||
638 | 634 | request.disable_db = False | ||
639 | 635 | |||
640 | 636 | if uid: self.get_context() | ||
641 | 637 | return uid | ||
642 | 638 | |||
643 | 639 | def check_security(self): | ||
644 | 640 | """ | ||
645 | 641 | Chech the current authentication parameters to know if those are still valid. This method | ||
646 | 642 | should be called at each request. If the authentication fails, a ``SessionExpiredException`` | ||
647 | 643 | is raised. | ||
648 | 644 | """ | ||
649 | 645 | if not self.db or not self.uid: | ||
650 | 646 | raise SessionExpiredException("Session expired") | ||
651 | 647 | security.check(self.db, self.uid, self.password) | ||
652 | 648 | |||
653 | 649 | def logout(self): | ||
654 | 650 | for k in self.keys(): | ||
655 | 651 | del self[k] | ||
656 | 652 | self._default_values() | ||
657 | 653 | |||
658 | 654 | def _default_values(self): | ||
659 | 655 | self.setdefault("db", None) | ||
660 | 656 | self.setdefault("uid", None) | ||
661 | 657 | self.setdefault("login", None) | ||
662 | 658 | self.setdefault("password", None) | ||
663 | 659 | self.setdefault("context", {'tz': "UTC", "uid": None}) | ||
664 | 660 | self.setdefault("jsonp_requests", {}) | ||
665 | 661 | |||
666 | 662 | def get_context(self): | ||
667 | 663 | """ | ||
668 | 664 | Re-initializes the current user's session context (based on | ||
669 | 665 | his preferences) by calling res.users.get_context() with the old | ||
670 | 666 | context. | ||
671 | 667 | |||
672 | 668 | :returns: the new context | ||
673 | 669 | """ | ||
674 | 670 | assert self.uid, "The user needs to be logged-in to initialize his context" | ||
675 | 671 | self.context = request.registry.get('res.users').context_get(request.cr, request.uid) or {} | ||
676 | 672 | self.context['uid'] = self.uid | ||
677 | 673 | self._fix_lang(self.context) | ||
678 | 674 | return self.context | ||
679 | 675 | |||
680 | 676 | def _fix_lang(self, context): | ||
681 | 677 | """ OpenERP provides languages which may not make sense and/or may not | ||
682 | 678 | be understood by the web client's libraries. | ||
683 | 679 | |||
684 | 680 | Fix those here. | ||
685 | 681 | |||
686 | 682 | :param dict context: context to fix | ||
687 | 683 | """ | ||
688 | 684 | lang = context['lang'] | ||
689 | 685 | |||
690 | 686 | # inane OpenERP locale | ||
691 | 687 | if lang == 'ar_AR': | ||
692 | 688 | lang = 'ar' | ||
693 | 689 | |||
694 | 690 | # lang to lang_REGION (datejs only handles lang_REGION, no bare langs) | ||
695 | 691 | if lang in babel.core.LOCALE_ALIASES: | ||
696 | 692 | lang = babel.core.LOCALE_ALIASES[lang] | ||
697 | 693 | |||
698 | 694 | context['lang'] = lang or 'en_US' | ||
699 | 695 | |||
700 | 696 | """ | ||
701 | 697 | Damn properties for retro-compatibility. All of that is deprecated, all | ||
702 | 698 | of that. | ||
703 | 699 | """ | ||
704 | 700 | @property | ||
705 | 701 | def _db(self): | ||
706 | 702 | return self.db | ||
707 | 703 | @_db.setter | ||
708 | 704 | def _db(self, value): | ||
709 | 705 | self.db = value | ||
710 | 706 | @property | ||
711 | 707 | def _uid(self): | ||
712 | 708 | return self.uid | ||
713 | 709 | @_uid.setter | ||
714 | 710 | def _uid(self, value): | ||
715 | 711 | self.uid = value | ||
716 | 712 | @property | ||
717 | 713 | def _login(self): | ||
718 | 714 | return self.login | ||
719 | 715 | @_login.setter | ||
720 | 716 | def _login(self, value): | ||
721 | 717 | self.login = value | ||
722 | 718 | @property | ||
723 | 719 | def _password(self): | ||
724 | 720 | return self.password | ||
725 | 721 | @_password.setter | ||
726 | 722 | def _password(self, value): | ||
727 | 723 | self.password = value | ||
728 | 724 | |||
729 | 725 | def send(self, service_name, method, *args): | ||
730 | 726 | """ | ||
731 | 727 | .. deprecated:: 8.0 | ||
732 | 728 | Use ``openerp.netsvc.dispatch_rpc()`` instead. | ||
733 | 729 | """ | ||
734 | 730 | return openerp.netsvc.dispatch_rpc(service_name, method, args) | ||
735 | 731 | |||
736 | 732 | def proxy(self, service): | ||
737 | 733 | """ | ||
738 | 734 | .. deprecated:: 8.0 | ||
739 | 735 | Use ``openerp.netsvc.dispatch_rpc()`` instead. | ||
740 | 736 | """ | ||
741 | 737 | return Service(self, service) | ||
742 | 738 | |||
743 | 739 | def assert_valid(self, force=False): | ||
744 | 740 | """ | ||
745 | 741 | .. deprecated:: 8.0 | ||
746 | 742 | Use ``check_security()`` instead. | ||
747 | 743 | |||
748 | 744 | Ensures this session is valid (logged into the openerp server) | ||
749 | 745 | """ | ||
750 | 746 | if self.uid and not force: | ||
751 | 747 | return | ||
752 | 748 | # TODO use authenticate instead of login | ||
753 | 749 | self.uid = self.proxy("common").login(self.db, self.login, self.password) | ||
754 | 750 | if not self.uid: | ||
755 | 751 | raise AuthenticationError("Authentication failure") | ||
756 | 752 | |||
757 | 753 | def ensure_valid(self): | ||
758 | 754 | """ | ||
759 | 755 | .. deprecated:: 8.0 | ||
760 | 756 | Use ``check_security()`` instead. | ||
761 | 757 | """ | ||
762 | 758 | if self.uid: | ||
763 | 759 | try: | ||
764 | 760 | self.assert_valid(True) | ||
765 | 761 | except Exception: | ||
766 | 762 | self.uid = None | ||
767 | 763 | |||
768 | 764 | def execute(self, model, func, *l, **d): | ||
769 | 765 | """ | ||
770 | 766 | .. deprecated:: 8.0 | ||
771 | 767 | Use the resistry and cursor in ``openerp.addons.web.http.request`` instead. | ||
772 | 768 | """ | ||
773 | 769 | model = self.model(model) | ||
774 | 770 | r = getattr(model, func)(*l, **d) | ||
775 | 771 | return r | ||
776 | 772 | |||
777 | 773 | def exec_workflow(self, model, id, signal): | ||
778 | 774 | """ | ||
779 | 775 | .. deprecated:: 8.0 | ||
780 | 776 | Use the resistry and cursor in ``openerp.addons.web.http.request`` instead. | ||
781 | 777 | """ | ||
782 | 778 | self.assert_valid() | ||
783 | 779 | r = self.proxy('object').exec_workflow(self.db, self.uid, self.password, model, signal, id) | ||
784 | 780 | return r | ||
785 | 781 | |||
786 | 782 | def model(self, model): | ||
787 | 783 | """ | ||
788 | 784 | .. deprecated:: 8.0 | ||
789 | 785 | Use the resistry and cursor in ``openerp.addons.web.http.request`` instead. | ||
790 | 786 | |||
791 | 787 | Get an RPC proxy for the object ``model``, bound to this session. | ||
792 | 788 | |||
793 | 789 | :param model: an OpenERP model name | ||
794 | 790 | :type model: str | ||
795 | 791 | :rtype: a model object | ||
796 | 792 | """ | ||
797 | 793 | if not self.db: | ||
798 | 794 | raise SessionExpiredException("Session expired") | ||
799 | 795 | |||
800 | 796 | return Model(self, model) | ||
801 | 797 | |||
802 | 798 | def session_gc(session_store): | ||
803 | 799 | if random.random() < 0.001: | ||
804 | 800 | # we keep session one week | ||
805 | 801 | last_week = time.time() - 60*60*24*7 | ||
806 | 802 | for fname in os.listdir(session_store.path): | ||
807 | 803 | path = os.path.join(session_store.path, fname) | ||
808 | 804 | try: | ||
809 | 805 | if os.path.getmtime(path) < last_week: | ||
810 | 806 | os.unlink(path) | ||
811 | 807 | except OSError: | ||
812 | 808 | pass | ||
813 | 809 | |||
814 | 810 | #---------------------------------------------------------- | ||
815 | 811 | # WSGI Application | ||
816 | 812 | #---------------------------------------------------------- | ||
817 | 813 | # Add potentially missing (older ubuntu) font mime types | ||
818 | 814 | mimetypes.add_type('application/font-woff', '.woff') | ||
819 | 815 | mimetypes.add_type('application/vnd.ms-fontobject', '.eot') | ||
820 | 816 | mimetypes.add_type('application/x-font-ttf', '.ttf') | ||
821 | 817 | |||
822 | 818 | class DisableCacheMiddleware(object): | ||
823 | 819 | def __init__(self, app): | ||
824 | 820 | self.app = app | ||
825 | 821 | def __call__(self, environ, start_response): | ||
826 | 822 | def start_wrapped(status, headers): | ||
827 | 823 | referer = environ.get('HTTP_REFERER', '') | ||
828 | 824 | parsed = urlparse.urlparse(referer) | ||
829 | 825 | debug = parsed.query.count('debug') >= 1 | ||
830 | 826 | |||
831 | 827 | new_headers = [] | ||
832 | 828 | unwanted_keys = ['Last-Modified'] | ||
833 | 829 | if debug: | ||
834 | 830 | new_headers = [('Cache-Control', 'no-cache')] | ||
835 | 831 | unwanted_keys += ['Expires', 'Etag', 'Cache-Control'] | ||
836 | 832 | |||
837 | 833 | for k, v in headers: | ||
838 | 834 | if k not in unwanted_keys: | ||
839 | 835 | new_headers.append((k, v)) | ||
840 | 836 | |||
841 | 837 | start_response(status, new_headers) | ||
842 | 838 | return self.app(environ, start_wrapped) | ||
843 | 839 | |||
844 | 840 | def session_path(): | ||
845 | 841 | try: | ||
846 | 842 | import pwd | ||
847 | 843 | username = pwd.getpwuid(os.geteuid()).pw_name | ||
848 | 844 | except ImportError: | ||
849 | 845 | try: | ||
850 | 846 | username = getpass.getuser() | ||
851 | 847 | except Exception: | ||
852 | 848 | username = "unknown" | ||
853 | 849 | path = os.path.join(tempfile.gettempdir(), "oe-sessions-" + username) | ||
854 | 850 | try: | ||
855 | 851 | os.mkdir(path, 0700) | ||
856 | 852 | except OSError as exc: | ||
857 | 853 | if exc.errno == errno.EEXIST: | ||
858 | 854 | # directory exists: ensure it has the correct permissions | ||
859 | 855 | # this will fail if the directory is not owned by the current user | ||
860 | 856 | os.chmod(path, 0700) | ||
861 | 857 | else: | ||
862 | 858 | raise | ||
863 | 859 | return path | ||
864 | 860 | |||
865 | 861 | class Root(object): | ||
866 | 862 | """Root WSGI application for the OpenERP Web Client. | ||
867 | 863 | """ | ||
868 | 864 | def __init__(self): | ||
869 | 865 | self.addons = {} | ||
870 | 866 | self.statics = {} | ||
871 | 867 | |||
872 | 868 | self.db_routers = {} | ||
873 | 869 | self.db_routers_lock = threading.Lock() | ||
874 | 870 | |||
875 | 871 | self.load_addons() | ||
876 | 872 | |||
877 | 873 | # Setup http sessions | ||
878 | 874 | path = session_path() | ||
879 | 875 | self.session_store = werkzeug.contrib.sessions.FilesystemSessionStore(path, session_class=OpenERPSession) | ||
880 | 876 | _logger.debug('HTTP sessions stored in: %s', path) | ||
881 | 877 | |||
882 | 878 | |||
883 | 879 | def __call__(self, environ, start_response): | ||
884 | 880 | """ Handle a WSGI request | ||
885 | 881 | """ | ||
886 | 882 | return self.dispatch(environ, start_response) | ||
887 | 883 | |||
888 | 884 | def dispatch(self, environ, start_response): | ||
889 | 885 | """ | ||
890 | 886 | Performs the actual WSGI dispatching for the application. | ||
891 | 887 | """ | ||
892 | 888 | try: | ||
893 | 889 | httprequest = werkzeug.wrappers.Request(environ) | ||
894 | 890 | httprequest.parameter_storage_class = werkzeug.datastructures.ImmutableDict | ||
895 | 891 | httprequest.app = self | ||
896 | 892 | |||
897 | 893 | session_gc(self.session_store) | ||
898 | 894 | |||
899 | 895 | sid = httprequest.args.get('session_id') | ||
900 | 896 | explicit_session = True | ||
901 | 897 | if not sid: | ||
902 | 898 | sid = httprequest.headers.get("X-Openerp-Session-Id") | ||
903 | 899 | if not sid: | ||
904 | 900 | sid = httprequest.cookies.get('session_id') | ||
905 | 901 | explicit_session = False | ||
906 | 902 | if sid is None: | ||
907 | 903 | httprequest.session = self.session_store.new() | ||
908 | 904 | else: | ||
909 | 905 | httprequest.session = self.session_store.get(sid) | ||
910 | 906 | |||
911 | 907 | self._find_db(httprequest) | ||
912 | 908 | |||
913 | 909 | if not "lang" in httprequest.session.context: | ||
914 | 910 | lang = httprequest.accept_languages.best or "en_US" | ||
915 | 911 | lang = babel.core.LOCALE_ALIASES.get(lang, lang).replace('-', '_') | ||
916 | 912 | httprequest.session.context["lang"] = lang | ||
917 | 913 | |||
918 | 914 | request = self._build_request(httprequest) | ||
919 | 915 | db = request.db | ||
920 | 916 | |||
921 | 917 | if db: | ||
922 | 918 | updated = openerp.modules.registry.RegistryManager.check_registry_signaling(db) | ||
923 | 919 | if updated: | ||
924 | 920 | with self.db_routers_lock: | ||
925 | 921 | del self.db_routers[db] | ||
926 | 922 | |||
927 | 923 | with set_request(request): | ||
928 | 924 | self.find_handler() | ||
929 | 925 | result = request.dispatch() | ||
930 | 926 | |||
931 | 927 | if db: | ||
932 | 928 | openerp.modules.registry.RegistryManager.signal_caches_change(db) | ||
933 | 929 | |||
934 | 930 | if isinstance(result, basestring): | ||
935 | 931 | headers=[('Content-Type', 'text/html; charset=utf-8'), ('Content-Length', len(result))] | ||
936 | 932 | response = werkzeug.wrappers.Response(result, headers=headers) | ||
937 | 933 | else: | ||
938 | 934 | response = result | ||
939 | 935 | |||
940 | 936 | if httprequest.session.should_save: | ||
941 | 937 | self.session_store.save(httprequest.session) | ||
942 | 938 | if not explicit_session and hasattr(response, 'set_cookie'): | ||
943 | 939 | response.set_cookie('session_id', httprequest.session.sid, max_age=90 * 24 * 60 * 60) | ||
944 | 940 | |||
945 | 941 | return response(environ, start_response) | ||
946 | 942 | except werkzeug.exceptions.HTTPException, e: | ||
947 | 943 | return e(environ, start_response) | ||
948 | 944 | |||
949 | 945 | def _find_db(self, httprequest): | ||
950 | 946 | db = db_monodb(httprequest) | ||
951 | 947 | if db != httprequest.session.db: | ||
952 | 948 | httprequest.session.logout() | ||
953 | 949 | httprequest.session.db = db | ||
954 | 950 | |||
955 | 951 | def _build_request(self, httprequest): | ||
956 | 952 | if httprequest.args.get('jsonp'): | ||
957 | 953 | return JsonRequest(httprequest) | ||
958 | 954 | |||
959 | 955 | if httprequest.mimetype == "application/json": | ||
960 | 956 | return JsonRequest(httprequest) | ||
961 | 957 | else: | ||
962 | 958 | return HttpRequest(httprequest) | ||
963 | 959 | |||
964 | 960 | def load_addons(self): | ||
965 | 961 | """ Load all addons from addons patch containg static files and | ||
966 | 962 | controllers and configure them. """ | ||
967 | 963 | |||
968 | 964 | for addons_path in openerp.modules.module.ad_paths: | ||
969 | 965 | for module in sorted(os.listdir(str(addons_path))): | ||
970 | 966 | if module not in addons_module: | ||
971 | 967 | manifest_path = os.path.join(addons_path, module, '__openerp__.py') | ||
972 | 968 | path_static = os.path.join(addons_path, module, 'static') | ||
973 | 969 | if os.path.isfile(manifest_path) and os.path.isdir(path_static): | ||
974 | 970 | manifest = ast.literal_eval(open(manifest_path).read()) | ||
975 | 971 | manifest['addons_path'] = addons_path | ||
976 | 972 | _logger.debug("Loading %s", module) | ||
977 | 973 | if 'openerp.addons' in sys.modules: | ||
978 | 974 | m = __import__('openerp.addons.' + module) | ||
979 | 975 | else: | ||
980 | 976 | m = __import__(module) | ||
981 | 977 | addons_module[module] = m | ||
982 | 978 | addons_manifest[module] = manifest | ||
983 | 979 | self.statics['/%s/static' % module] = path_static | ||
984 | 980 | |||
985 | 981 | app = werkzeug.wsgi.SharedDataMiddleware(self.dispatch, self.statics) | ||
986 | 982 | self.dispatch = DisableCacheMiddleware(app) | ||
987 | 983 | |||
988 | 984 | def _build_router(self, db): | ||
989 | 985 | _logger.info("Generating routing configuration for database %s" % db) | ||
990 | 986 | routing_map = routing.Map() | ||
991 | 987 | |||
992 | 988 | def gen(modules, nodb_only): | ||
993 | 989 | for module in modules: | ||
994 | 990 | for v in controllers_per_module[module]: | ||
995 | 991 | cls = v[1] | ||
996 | 992 | |||
997 | 993 | subclasses = cls.__subclasses__() | ||
998 | 994 | subclasses = [c for c in subclasses if c.__module__.split(".")[:2] == ["openerp", "addons"] and \ | ||
999 | 995 | cls.__module__.split(".")[2] in modules] | ||
1000 | 996 | if subclasses: | ||
1001 | 997 | name = "%s (extended by %s)" % (cls.__name__, ', '.join(sub.__name__ for sub in subclasses)) | ||
1002 | 998 | cls = type(name, tuple(reversed(subclasses)), {}) | ||
1003 | 999 | |||
1004 | 1000 | o = cls() | ||
1005 | 1001 | members = inspect.getmembers(o) | ||
1006 | 1002 | for mk, mv in members: | ||
1007 | 1003 | if inspect.ismethod(mv) and getattr(mv, 'exposed', False) and \ | ||
1008 | 1004 | nodb_only == (getattr(mv, "auth", None) == "none"): | ||
1009 | 1005 | function = (o.get_wrapped_method(mk), mv) | ||
1010 | 1006 | for url in mv.routes: | ||
1011 | 1007 | if getattr(mv, "combine", False): | ||
1012 | 1008 | url = os.path.join(o._cp_path, url) | ||
1013 | 1009 | if url.endswith("/") and len(url) > 1: | ||
1014 | 1010 | url = url[: -1] | ||
1015 | 1011 | routing_map.add(routing.Rule(url, endpoint=function)) | ||
1016 | 1012 | |||
1017 | 1013 | modules_set = set(controllers_per_module.keys()) | ||
1018 | 1014 | modules_set -= set("web") | ||
1019 | 1015 | # building all none methods | ||
1020 | 1016 | gen(["web"] + sorted(modules_set), True) | ||
1021 | 1017 | if not db: | ||
1022 | 1018 | return routing_map | ||
1023 | 1019 | |||
1024 | 1020 | registry = openerp.modules.registry.RegistryManager.get(db) | ||
1025 | 1021 | with registry.cursor() as cr: | ||
1026 | 1022 | m = registry.get('ir.module.module') | ||
1027 | 1023 | ids = m.search(cr, openerp.SUPERUSER_ID, [('state','=','installed')]) | ||
1028 | 1024 | installed = set([x['name'] for x in m.read(cr, 1, ids, ['name'])]) | ||
1029 | 1025 | modules_set = modules_set.intersection(set(installed)) | ||
1030 | 1026 | modules = ["web"] + sorted(modules_set) | ||
1031 | 1027 | # building all other methods | ||
1032 | 1028 | gen(["web"] + sorted(modules_set), False) | ||
1033 | 1029 | |||
1034 | 1030 | return routing_map | ||
1035 | 1031 | |||
1036 | 1032 | def get_db_router(self, db): | ||
1037 | 1033 | with self.db_routers_lock: | ||
1038 | 1034 | router = self.db_routers.get(db) | ||
1039 | 1035 | if not router: | ||
1040 | 1036 | router = self._build_router(db) | ||
1041 | 1037 | with self.db_routers_lock: | ||
1042 | 1038 | self.db_routers[db] = router | ||
1043 | 1039 | return router | ||
1044 | 1040 | |||
1045 | 1041 | def find_handler(self): | ||
1046 | 1042 | """ | ||
1047 | 1043 | Tries to discover the controller handling the request for the path specified in the request. | ||
1048 | 1044 | """ | ||
1049 | 1045 | path = request.httprequest.path | ||
1050 | 1046 | urls = self.get_db_router(request.db).bind("") | ||
1051 | 1047 | matched, arguments = urls.match(path) | ||
1052 | 1048 | arguments = dict([(k, v) for k, v in arguments.items() if not k.startswith("_ignored_")]) | ||
1053 | 1049 | func, original = matched | ||
1054 | 1050 | |||
1055 | 1051 | def nfunc(*args, **kwargs): | ||
1056 | 1052 | kwargs.update(arguments) | ||
1057 | 1053 | return func(*args, **kwargs) | ||
1058 | 1054 | |||
1059 | 1055 | request.func = nfunc | ||
1060 | 1056 | request.auth_method = getattr(original, "auth", "user") | ||
1061 | 1057 | request.func_request_type = original.exposed | ||
1062 | 1058 | |||
1063 | 1059 | root = None | ||
1064 | 1060 | |||
1065 | 1061 | def db_list(force=False, httprequest=None): | ||
1066 | 1062 | httprequest = httprequest or request.httprequest | ||
1067 | 1063 | dbs = openerp.netsvc.dispatch_rpc("db", "list", [force]) | ||
1068 | 1064 | h = httprequest.environ['HTTP_HOST'].split(':')[0] | ||
1069 | 1065 | d = h.split('.')[0] | ||
1070 | 1066 | r = openerp.tools.config['dbfilter'].replace('%h', h).replace('%d', d) | ||
1071 | 1067 | dbs = [i for i in dbs if re.match(r, i)] | ||
1072 | 1068 | return dbs | ||
1073 | 1069 | |||
1074 | 1070 | def db_monodb(httprequest=None): | ||
1075 | 1071 | """ | ||
1076 | 1072 | Magic function to find the current database. | ||
1077 | 1073 | |||
1078 | 1074 | Implementation details: | ||
1079 | 1075 | |||
1080 | 1076 | * Magic | ||
1081 | 1077 | * More magic | ||
1082 | 1078 | |||
1083 | 1079 | Returns ``None`` if the magic is not magic enough. | ||
1084 | 1080 | """ | ||
1085 | 1081 | httprequest = httprequest or request.httprequest | ||
1086 | 1082 | db = None | ||
1087 | 1083 | redirect = None | ||
1088 | 1084 | |||
1089 | 1085 | dbs = db_list(True, httprequest) | ||
1090 | 1086 | |||
1091 | 1087 | # 1 try the db already in the session | ||
1092 | 1088 | db_session = httprequest.session.db | ||
1093 | 1089 | if db_session in dbs: | ||
1094 | 1090 | return db_session | ||
1095 | 1091 | |||
1096 | 1092 | # 2 if there is only one db in the db filters, take it | ||
1097 | 1093 | if len(dbs) == 1: | ||
1098 | 1094 | return dbs[0] | ||
1099 | 1095 | |||
1100 | 1096 | # 3 if there are multiple dbs, take the first one only if we can list them | ||
1101 | 1097 | if len(dbs) > 1 and config['list_db']: | ||
1102 | 1098 | return dbs[0] | ||
1103 | 1099 | return None | ||
1104 | 1100 | |||
1105 | 1101 | class CommonController(Controller): | ||
1106 | 1102 | |||
1107 | 1103 | @route('/jsonrpc', type='json', auth="none") | ||
1108 | 1104 | def jsonrpc(self, service, method, args): | ||
1109 | 1105 | """ Method used by client APIs to contact OpenERP. """ | ||
1110 | 1106 | return openerp.netsvc.dispatch_rpc(service, method, args) | ||
1111 | 1107 | |||
1112 | 1108 | @route('/gen_session_id', type='json', auth="none") | ||
1113 | 1109 | def gen_session_id(self): | ||
1114 | 1110 | nsession = root.session_store.new() | ||
1115 | 1111 | return nsession.sid | ||
1116 | 1112 | |||
1117 | 1113 | def wsgi_postload(): | ||
1118 | 1114 | global root | ||
1119 | 1115 | root = Root() | ||
1120 | 1116 | openerp.wsgi.register_wsgi_handler(root) | ||
1121 | 1117 | |||
1122 | 1118 | # vim:et:ts=4:sw=4: |
Hello,
it seems ok for me.
THanks