[IMP] massive connection deferrization, json works again
authorAntony Lesuisse <al@openerp.com>
Fri, 16 Dec 2011 00:44:02 +0000 (01:44 +0100)
committerAntony Lesuisse <al@openerp.com>
Fri, 16 Dec 2011 00:44:02 +0000 (01:44 +0100)
bzr revid: al@openerp.com-20111216004402-q6v26822swarra7r

addons/web/common/http.py
addons/web/controllers/main.py
addons/web/static/src/js/chrome.js
addons/web/static/src/js/core.js

index 620e3a5..4bdd87a 100644 (file)
@@ -6,12 +6,12 @@ import ast
 import contextlib
 import functools
 import logging
-import urllib
 import os
 import pprint
 import sys
 import threading
 import traceback
+import urllib
 import uuid
 import xmlrpclib
 
@@ -137,20 +137,19 @@ class JsonRequest(WebRequest):
 
         :returns: an utf8 encoded JSON-RPC2 or JSONP reply
         """
-        method = self.httprequest.method
         args = self.httprequest.args
         jsonp = args.get('jsonp', False)
         requestf = None
         request = None
 
-        if jsonp and method == 'POST':
+        if jsonp and self.httprequest.method == 'POST':
             # jsonp 2 steps step1 POST: save call
             self.init(args)
             req.session.jsonp_requests[args.get('id')] = self.httprequest.form['r']
             headers=[('Content-Type', 'text/plain; charset=utf-8')]
             r = werkzeug.wrappers.Response(request_id, headers=headers)
             return r
-        elif args['jsonp'] and args.get('id'):
+        elif jsonp and args.get('id'):
             # jsonp 2 steps step2 GET: run and return result
             self.init(args)
             request = self.session.jsonp_requests.pop(args.get(id), "")
@@ -195,6 +194,8 @@ class JsonRequest(WebRequest):
                 }
             }
         except Exception:
+            logging.getLogger(__name__ + '.JSONRequest.dispatch').exception\
+                ("An error occured while handling a json request")
             error = {
                 'code': 300,
                 'message': "OpenERP WebClient Error",
@@ -205,9 +206,9 @@ class JsonRequest(WebRequest):
             }
         if error:
             response["error"] = error
-            _logger.error("[%s] <--\n%s", rid, pprint.pformat(response))
-        elif _logger.isEnabledFor(logging.DEBUG):
-            _logger.debug("[%s] <--\n%s", rid, pprint.pformat(response))
+
+        if _logger.isEnabledFor(logging.DEBUG):
+            _logger.debug("<--\n%s", pprint.pformat(response))
 
         if jsonp:
             mime = 'application/javascript'
index e745020..9c6edcf 100644 (file)
@@ -10,6 +10,7 @@ import os
 import re
 import simplejson
 import time
+import urllib2
 import xmlrpclib
 import zlib
 from xml.etree import ElementTree
@@ -245,7 +246,7 @@ class WebClient(openerpweb.Controller):
 class Proxy(openerpweb.Controller):
     _cp_path = '/web/proxy'
 
-    @jsonrequest
+    @openerpweb.jsonrequest
     def load(self, req, path):
         #req.config.socket_port
         #if not re.match('^/[^/]+/static/.*', path):
@@ -254,7 +255,7 @@ class Proxy(openerpweb.Controller):
         env = req.httprequest.environ
         port = env['SERVER_PORT']
 
-        o = urllib.urlopen('http://127.0.0.1:%s%s' % (port, path))
+        o = urllib2.urlopen('http://127.0.0.1:%s%s' % (port, path))
         return o.read()
 
 class Database(openerpweb.Controller):
index 70beb7e..cbf475a 100644 (file)
@@ -1004,20 +1004,12 @@ openerp.web.WebClient = openerp.web.Widget.extend(/** @lends openerp.web.WebClie
         this.header.on_logout.add(this.login.on_logout);
         this.header.on_action.add(this.on_menu_action);
 
-        this.session.on_session_invalid.add(this.login.do_ask_login);
-        this.session.on_session_valid.add_last(this.header.do_update);
-        this.session.on_session_invalid.add_last(this.header.do_update);
-        this.session.on_session_valid.add_last(this.on_logged);
-        this.session.on_session_invalid.add_last(this.on_logged_out);
-
-
         this._current_state = null;
-
     },
     start: function() {
         this._super.apply(this, arguments);
         var self = this;
-        openerp.connection.bind("",function() {
+        this.session.bind().then(function() {
             var params = {};
             if (jQuery.param != undefined && jQuery.deparam(jQuery.param.querystring()).kitten != undefined) {
                 this.$element.addClass("kitten-mode-activated");
@@ -1026,17 +1018,25 @@ openerp.web.WebClient = openerp.web.Widget.extend(/** @lends openerp.web.WebClie
                 });
             }
             self.$element.html(QWeb.render("Interface", params));
-            openerp.connection.session_restore();
-
             self.menu = new openerp.web.Menu(self, "oe_menu", "oe_secondary_menu");
             self.menu.on_action.add(self.on_menu_action);
 
-
             self.notification.prependTo(self.$element);
             self.loading.appendTo($('#oe_loading'));
             self.header.appendTo($("#oe_header"));
             self.login.appendTo($('#oe_login'));
             self.menu.start();
+            self.login.on_login_invalid();
+        });
+        this.session.ready.then(function() {
+            self.login.on_login_valid();
+            self.header.do_update();
+            self.menu.do_reload();
+            if(self.action_manager)
+                self.action_manager.stop();
+            self.action_manager = new openerp.web.ActionManager(this);
+            self.action_manager.appendTo($("#oe_app"));
+            self.bind_hashchange();
         });
     },
     do_reload: function() {
@@ -1050,24 +1050,8 @@ openerp.web.WebClient = openerp.web.Widget.extend(/** @lends openerp.web.WebClie
         var n = this.notification;
         n.warn.apply(n, arguments);
     },
-    on_logged: function() {
-        this.menu.do_reload();
-        if(this.action_manager)
-            this.action_manager.stop();
-        this.action_manager = new openerp.web.ActionManager(this);
-        this.action_manager.appendTo($("#oe_app"));
-
-        if (openerp._modules_loaded) { // TODO: find better option than this
-            this.bind_hashchange();
-        } else {
-            this.session.on_modules_loaded.add({        // XXX what about a $.Deferred ?
-                callback: $.proxy(this, 'bind_hashchange'),
-                unique: true,
-                position: 'last'
-            })
-        }
-    },
-    on_logged_out: function() {
+    on_loggued_out: function() {
+        this.header.do_update();
         $(window).unbind('hashchange', this.on_hashchange);
         this.do_push_state({});
         if(this.action_manager)
index 55bce37..f28d0ad 100644 (file)
@@ -352,11 +352,11 @@ openerp.web.Connection = openerp.web.CallbackEnabled.extend( /** @lends openerp.
      */
     init: function() {
         this._super();
-        // TODO: session should have an optional name indicating that they'll
-        //       be saved to (and revived from) cookies
+        // TODO: session store in cookie should be optional
         this.name = openerp._session_id;
     },
     bind: function(host, protocol) {
+        var self = this;
         this.host = (host == undefined) ? location.host : host;
         this.protocol = (protocol == undefined) ? location.protocol : protocol;
         this.prefix = this.protocol + '//' + this.host;
@@ -368,13 +368,13 @@ openerp.web.Connection = openerp.web.CallbackEnabled.extend( /** @lends openerp.
         this.username = false;
         this.user_context= {};
         this.db = false;
-        this.module_loading = $.Deferred();
         this.module_list = [];
         this.module_loaded = {"web": true};
         this.context = {};
         this.shortcuts = [];
         this.active_id = null;
-        this.do_load_qweb(['/web/webclient/qweb'], continuation);
+        this.ready = $.Deferred();
+        return this.session_restore();
     },
     /**
      * Executes an RPC call, registering the provided callbacks.
@@ -523,73 +523,51 @@ openerp.web.Connection = openerp.web.CallbackEnabled.extend( /** @lends openerp.
     on_rpc_error: function(error) {
     },
     /**
-     * The session is validated either by login or by restoration of a previous session
+     * Init a session, reloads from cookie, if it exists
      */
-    on_session_valid: function(continuation) {
-        this.load_modules().then(function() { continuation() } );
-    },
-    on_session_invalid: function(continuation) {
-    },
-    session_is_valid: function() {
-        return this.uid;
-    },
-    session_authenticate: function(db, login, password, success_callback) {
+    session_restore: function () {
         var self = this;
-        var base_location = document.location.protocol + '//' + document.location.host;
-        var params = { db: db, login: login, password: password, base_location: base_location };
-        return this.rpc("/web/session/authenticate", params, function(result) {
+        // TODO: session store in cookie should be optional
+        this.session_id = this.get_cookie('session_id');
+        return this.rpc("/web/session/get_session_info", {}).pipe(function(result) {
+            // If immediately follows a login (triggered by trying to restore
+            // an invalid session or no session at all), refresh session data
+            // (should not change, but just in case...)
             _.extend(self, {
-                session_id: result.session_id,
-                uid: result.uid,
-                user_context: result.context,
                 db: result.db,
-                username: result.login
+                username: result.login,
+                uid: result.uid,
+                user_context: result.context
             });
-            self.session_save();
-            self.on_session_valid(success_callback);
-            return true;
+            var deferred = self.do_load_qweb(['/web/webclient/qweb']);
+            if(self.uid) {
+                return deferred.then(self.load_modules());
+            }
+            return deferred;
         });
     },
     /**
-     * Reloads uid and session_id from local storage, if they exist
+     * The session is validated either by login or by restoration of a previous session
      */
-    session_restore: function (continuation) {
+    session_authenticate: function(db, login, password) {
         var self = this;
-        this.session_id = this.get_cookie('session_id');
-        return this.rpc("/web/session/get_session_info", {}).then(function(result) {
-            // If immediately follows a login (triggered by trying to restore
-            // an invalid session or no session at all), refresh session data
-            // (should not change, but just in case...) but should not call
-            // on_session_valid again as it triggers reloading the menu
-            var already_logged = self.uid;
+        var base_location = document.location.protocol + '//' + document.location.host;
+        var params = { db: db, login: login, password: password, base_location: base_location };
+        return this.rpc("/web/session/authenticate", params).pipe(function(result) {
             _.extend(self, {
-                uid: result.uid,
-                user_context: result.context,
+                session_id: result.session_id,
                 db: result.db,
-                username: result.login
+                username: result.login,
+                uid: result.uid,
+                user_context: result.context
             });
-            if (!already_logged) {
-                if (self.uid) {
-                    self.on_session_valid(continuation);
-                } else {
-                    self.on_session_invalid(continuation);
-                }
-            }
-        }, function() {
-            self.on_session_invalid(continuation);
+            // TODO: session store in cookie should be optional
+            self.set_cookie('session_id', this.session_id);
+            return self.load_modules();
         });
     },
-    /**
-     * Saves the session id and uid locally
-     */
-    session_save: function () {
-        this.set_cookie('session_id', this.session_id);
-    },
-    logout: function() {
+    session_logout: function() {
         this.set_cookie('session_id', '');
-        this.reload_client();
-    },
-    reload_client: function() {
         window.location.reload();
     },
     /**
@@ -640,19 +618,21 @@ openerp.web.Connection = openerp.web.CallbackEnabled.extend( /** @lends openerp.
             self.module_list = result;
             var lang = self.user_context.lang;
             var params = { mods: ["web"].concat(result), lang: lang};
-            self.rpc('/web/webclient/translations',params).then(function(transs) {
+            self.rpc('/web/webclient/translations',params).pipe(function(transs) {
                 openerp.web._t.database.set_bundle(transs);
                 var modules = self.module_list.join(',');
                 var file_list = ["/web/static/lib/datejs/globalization/" +
                     self.user_context.lang.replace("_", "-") + ".js"
                 ];
                 return $.when(
-                    self.rpc('/web/webclient/qweblist', {mods: modules}, self.do_load_qweb),
                     self.rpc('/web/webclient/csslist', {mods: modules}, self.do_load_css),
-                    self.rpc('/web/webclient/jslist', {mods: modules}, function(files) {
+                    self.rpc('/web/webclient/qweblist', {mods: modules}).pipe(self.do_load_qweb),
+                    self.rpc('/web/webclient/jslist', {mods: modules}).pipe(function(files) {
                         self.do_load_js(file_list.concat(files)); 
                     })
-                );
+                ).then(function() {
+                    self.ready.resolve();
+                });
             });
         });
     },
@@ -660,7 +640,7 @@ openerp.web.Connection = openerp.web.CallbackEnabled.extend( /** @lends openerp.
         var self = this;
         _.each(files, function (file) {
             $('head').append($('<link>', {
-                'href': self.get_absolute_url(file),
+                'href': self.get_url(file),
                 'rel': 'stylesheet',
                 'type': 'text/css'
             }));
@@ -668,32 +648,39 @@ openerp.web.Connection = openerp.web.CallbackEnabled.extend( /** @lends openerp.
     },
     do_load_js: function(files) {
         var self = this;
+        var d = $.Deferred();
         if(files.length != 0) {
             var file = files.shift();
             var tag = document.createElement('script');
             tag.type = 'text/javascript';
-            tag.src = self.get_absolute_url(file);
+            tag.src = self.get_url(file);
             tag.onload = tag.onreadystatechange = function() {
                 if ( (tag.readyState && tag.readyState != "loaded" && tag.readyState != "complete") || tag.onload_done )
                     return;
                 tag.onload_done = true;
-                self.do_load_js(files, callback);
+                self.do_load_js(files).then(function () {
+                    d.resolve();
+                });
             };
             var head = document.head || document.getElementsByTagName('head')[0];
             head.appendChild(tag);
         } else {
             self.on_modules_loaded();
+            d.resolve();
         }
+        return d;
     },
     do_load_qweb: function(files) {
         var self = this;
         if (files.length != 0) {
             var file = files.shift();
-            self.rpc('/web/proxy/load', {path: file}, function(xml) {
+            return self.rpc('/web/proxy/load', {path: file}).pipe(function(xml) {
                 openerp.web.qweb.add_template(_.str.trim(xml));
-                self.do_load_qweb(files, callback);
+                return self.do_load_qweb(files);
             });
-        } 
+        } else {
+            return $.when();
+        }
     },
     on_modules_loaded: function() {
         for(var j=0; j<this.module_list.length; j++) {
@@ -728,6 +715,9 @@ openerp.web.Connection = openerp.web.CallbackEnabled.extend( /** @lends openerp.
      * @param {Function} [options.error] callback in case of request error, provided with the error body
      * @param {Function} [options.complete] called after both ``success`` and ``error` callbacks have executed
      */
+    get_url: function (file) {
+        return this.prefix + file;
+    },
     get_file: function (options) {
         // need to detect when the file is done downloading (not used
         // yet, but we'll need it to fix the UI e.g. with a throbber