diff --git a/.bzrignore b/.bzrignore index e7618c7b0ce..86a1881981f 100644 --- a/.bzrignore +++ b/.bzrignore @@ -1,21 +1,15 @@ -.*.swp -.bzrignore -.idea -.project -.pydevproject -.ropeproject -.settings -.DS_Store -openerp/addons/* -openerp/filestore* -.Python -*.pyc -*.pyo -bin/* +.* +*.egg-info +*.orig +*.vim build/ -include/ -lib/ -share/ -doc/_build/* -win32/*.bat -win32/meta.py +RE:^bin/ +RE:^dist/ +RE:^include/ + +RE:^share/ +RE:^man/ +RE:^lib/ + +RE:^addons/\w+/doc/_build/ +RE:^.*?/node_modules diff --git a/addons/web/Gruntfile.js b/addons/web/Gruntfile.js new file mode 100644 index 00000000000..cbc650492b6 --- /dev/null +++ b/addons/web/Gruntfile.js @@ -0,0 +1,20 @@ +module.exports = function(grunt) { + + grunt.initConfig({ + jshint: { + src: ['static/src/**/*.js', 'static/test/**/*.js'], + options: { + sub: true, //[] instead of . + evil: true, //eval + laxbreak: true, //unsafe line breaks + }, + } + }); + + grunt.loadNpmTasks('grunt-contrib-jshint'); + + grunt.registerTask('test', ['jshint']); + + grunt.registerTask('default', ['jshint']); + +}; \ No newline at end of file diff --git a/addons/web/controllers/main.py b/addons/web/controllers/main.py index 2536f4d9f2f..da3cc8b8d2a 100644 --- a/addons/web/controllers/main.py +++ b/addons/web/controllers/main.py @@ -88,9 +88,6 @@ def rjsmin(script): db_list = http.db_list -def db_monodb_redirect(): - return http.db_redirect(not config['list_db']) - db_monodb = http.db_monodb def redirect_with_hash(url, code=303): @@ -296,13 +293,13 @@ def manifest_glob(extension, addons=None, db=None, include_remotes=False): r.append((path, fs2web(path[len(addons_path):]))) return r -def manifest_list(extension, mods=None, db=None): +def manifest_list(extension, mods=None, db=None, debug=False): """ list ressources to load specifying either: mods: a comma separated string listing modules db: a database name (return all installed modules in that database) """ files = manifest_glob(extension, addons=mods, db=db, include_remotes=True) - if not request.debug: + if not debug: path = '/web/webclient/' + extension if mods is not None: path += '?' + urllib.urlencode({'mods': mods}) @@ -535,13 +532,24 @@ html_template = """ class Home(http.Controller): @http.route('/', type='http', auth="none") - def index(self, s_action=None, db=None, **kw): - db, redir = db_monodb_redirect() - if redir: - return redirect_with_hash(redir) + def index(self, s_action=None, db=None, debug=False, **kw): + debug = debug != False + if db is not None: + lst = http.db_list(True) + if db in lst and db != request.session.db: + request.session.logout() + request.session.db = db - js = "\n ".join('' % i for i in manifest_list('js', db=db)) - css = "\n ".join('' % i for i in manifest_list('css', db=db)) + if db != request.session.db: + query = dict(urlparse.parse_qsl(request.httprequest.query_string, keep_blank_values=True)) + query.update({'db': request.session.db}) + redirect = request.httprequest.path + '?' + urllib.urlencode(query) + return redirect_with_hash(redirect) + + db = request.session.db + + js = "\n ".join('' % i for i in manifest_list('js', db=db, debug=debug)) + css = "\n ".join('' % i for i in manifest_list('css', db=db, debug=debug)) r = html_template % { 'js': js, @@ -549,7 +557,7 @@ class Home(http.Controller): 'modules': simplejson.dumps(module_boot(db=db)), 'init': 'var wc = new s.web.WebClient();wc.appendTo($(document.body));' } - return r + return request.make_response(r, {'Cache-Control': 'no-cache', 'Content-Type': 'text/html; charset=utf-8'}) @http.route('/login', type='http', auth="user") def login(self, db, login, key): @@ -819,7 +827,7 @@ class Session(http.Controller): @http.route('/web/session/get_session_info', type='json', auth="none") def get_session_info(self): request.uid = request.session.uid - request.db = request.session.db + request.disable_db = False return self.session_info() @http.route('/web/session/authenticate', type='json', auth="none") diff --git a/addons/web/http.py b/addons/web/http.py index d99f2982ec3..687af164e72 100644 --- a/addons/web/http.py +++ b/addons/web/http.py @@ -85,10 +85,6 @@ class WebRequest(object): :class:`~collections.Mapping` of context values for the current request - .. attribute:: debug - - ``bool``, indicates whether the debug mode is active on the client - .. attribute:: db ``str``, the name of the database linked to the current request. Can be ``None`` @@ -105,16 +101,13 @@ class WebRequest(object): self.httpsession = httprequest.session self.session = httprequest.session self.session_id = httprequest.session.sid - self.db = None + self.disable_db = False self.uid = None self.func = None self.auth_method = None self._cr_cm = None self._cr = None self.func_request_type = None - self.debug = self.httprequest.args.get('debug', False) is not False - with set_request(self): - self.db = self.session.db or db_monodb() # set db/uid trackers - they're cleaned up at the WSGI # dispatching phase in openerp.service.wsgi_server.application if self.db: @@ -132,17 +125,16 @@ class WebRequest(object): self.session.logout() raise SessionExpiredException("Session expired for request %s" % self.httprequest) if self.auth_method == "none": - self.db = None + self.disable_db = True self.uid = None elif self.auth_method == "admin": - self.db = self.session.db or db_monodb() + self.disable_db = False if not self.db: raise SessionExpiredException("No valid database for request %s" % self.httprequest) self.uid = openerp.SUPERUSER_ID else: # auth - self.db = self.session.db + self.disable_db = False self.uid = self.session.uid - @property def registry(self): """ @@ -151,6 +143,14 @@ class WebRequest(object): """ return openerp.modules.registry.RegistryManager.get(self.db) if self.db else None + @property + def db(self): + """ + The registry to the database linked to this request. Can be ``None`` if the current request uses the + ``none'' authentication. + """ + return self.session.db if not self.disable_db else None + @property def cr(self): """ @@ -184,7 +184,7 @@ class WebRequest(object): return self.func(*args, **kwargs) finally: # just to be sure no one tries to re-use the request - self.db = None + self.disable_db = True self.uid = None def route(route, type="http", auth="user"): @@ -304,8 +304,6 @@ class JsonRequest(WebRequest): error = None try: - #if _logger.isEnabledFor(logging.DEBUG): - # _logger.debug("--> %s.%s\n%s", func.im_class.__name__, func.__name__, pprint.pformat(self.jsonrequest)) response['id'] = self.jsonrequest.get('id') response["result"] = self._call_function(**self.params) except AuthenticationError, e: @@ -327,9 +325,6 @@ class JsonRequest(WebRequest): if error: response["error"] = error - if _logger.isEnabledFor(logging.DEBUG): - _logger.debug("<--\n%s", pprint.pformat(response)) - if self.jsonp: # If we use jsonp, that's mean we are called from another host # Some browser (IE and Safari) do no allow third party cookies @@ -394,7 +389,7 @@ class HttpRequest(WebRequest): def __init__(self, *args): super(HttpRequest, self).__init__(*args) params = dict(self.httprequest.args) - ex = set(["session_id", "debug"]) + ex = set(["session_id"]) for k in params.keys(): if k in ex: del params[k] @@ -409,7 +404,6 @@ class HttpRequest(WebRequest): akw[key] = value else: akw[key] = type(value) - #_logger.debug("%s --> %s.%s %r", self.httprequest.func, func.im_class.__name__, func.__name__, akw) try: r = self._call_function(**self.params) except werkzeug.exceptions.HTTPException, e: @@ -426,10 +420,6 @@ class HttpRequest(WebRequest): else: if not r: r = werkzeug.wrappers.Response(status=204) # no content - if isinstance(r, (werkzeug.wrappers.BaseResponse, werkzeug.exceptions.HTTPException)): - _logger.debug('<-- %s', r) - else: - _logger.debug("<-- size: %s", len(r)) return r def make_response(self, data, headers=None, cookies=None): @@ -626,8 +616,8 @@ class OpenERPSession(werkzeug.contrib.sessions.Session): self.uid = uid self.login = login self.password = password - request.db = db request.uid = uid + request.disable_db = False if uid: self.get_context() return uid @@ -900,6 +890,8 @@ class Root(object): else: httprequest.session = self.session_store.get(sid) + self._find_db(httprequest) + if not "lang" in httprequest.session.context: lang = httprequest.accept_languages.best or "en_US" lang = babel.core.LOCALE_ALIASES.get(lang, lang).replace('-', '_') @@ -936,6 +928,12 @@ class Root(object): except werkzeug.exceptions.HTTPException, e: return e(environ, start_response) + def _find_db(self, httprequest): + db = db_monodb(httprequest) + if db != httprequest.session.db: + httprequest.session.logout() + httprequest.session.db = db + def _build_request(self, httprequest): if httprequest.args.get('jsonp'): return JsonRequest(httprequest) @@ -1050,43 +1048,16 @@ class Root(object): root = None -def db_list(force=False): - proxy = request.session.proxy("db") - dbs = proxy.list(force) - h = request.httprequest.environ['HTTP_HOST'].split(':')[0] +def db_list(force=False, httprequest=None): + httprequest = httprequest or request.httprequest + dbs = openerp.netsvc.dispatch_rpc("db", "list", [force]) + h = httprequest.environ['HTTP_HOST'].split(':')[0] d = h.split('.')[0] r = openerp.tools.config['dbfilter'].replace('%h', h).replace('%d', d) dbs = [i for i in dbs if re.match(r, i)] return dbs -def db_redirect(match_first_only_if_unique): - db = None - redirect = None - - dbs = db_list(True) - - # 1 try the db in the url - db_url = request.httprequest.args.get('db') - if db_url in dbs: - return (db_url, None) - - # 2 use the database from the cookie if it's listable and still listed - cookie_db = request.httprequest.cookies.get('last_used_database') - if cookie_db in dbs: - db = cookie_db - - # 3 use the first db if user can list databases - if dbs and not db and (not match_first_only_if_unique or len(dbs) == 1): - db = dbs[0] - - # redirect to the chosen db if multiple are available - if db and len(dbs) > 1: - query = dict(urlparse.parse_qsl(request.httprequest.query_string, keep_blank_values=True)) - query.update({'db': db}) - redirect = request.httprequest.path + '?' + urllib.urlencode(query) - return (db, redirect) - -def db_monodb(): +def db_monodb(httprequest=None): """ Magic function to find the current database. @@ -1095,10 +1066,27 @@ def db_monodb(): * Magic * More magic - Return ``None`` if the magic is not magic enough. + Returns ``None`` if the magic is not magic enough. """ - return db_redirect(True)[0] + httprequest = httprequest or request.httprequest + db = None + redirect = None + dbs = db_list(True, httprequest) + + # 1 try the db already in the session + db_session = httprequest.session.db + if db_session in dbs: + return db_session + + # 2 if there is only one db in the db filters, take it + if len(dbs) == 1: + return dbs[0] + + # 3 if there are multiple dbs, take the first one only if we can list them + if len(dbs) > 1 and config['list_db']: + return dbs[0] + return None class CommonController(Controller): diff --git a/addons/web/package.json b/addons/web/package.json new file mode 100644 index 00000000000..f8dfc2879d6 --- /dev/null +++ b/addons/web/package.json @@ -0,0 +1,6 @@ +{ + "devDependencies": { + "grunt": "~0.4.1", + "grunt-contrib-jshint": "~0.6.0" + } +} \ No newline at end of file diff --git a/addons/web/static/src/js/chrome.js b/addons/web/static/src/js/chrome.js index 1c976cb619e..3c8e1e0956a 100644 --- a/addons/web/static/src/js/chrome.js +++ b/addons/web/static/src/js/chrome.js @@ -438,7 +438,7 @@ instance.web.DatabaseManager = instance.web.Widget.extend({ self.$el.html(QWeb.render("DatabaseManager", { widget : self })); $('.oe_user_menu_placeholder').append(QWeb.render("DatabaseManager.user_menu",{ widget : self })); $('.oe_secondary_menus_container').append(QWeb.render("DatabaseManager.menu",{ widget : self })); - $('ul.oe_secondary_submenu > li:first').addClass('oe_active') + $('ul.oe_secondary_submenu > li:first').addClass('oe_active'); $('ul.oe_secondary_submenu > li').bind('click', function (event) { var menuitem = $(this); menuitem.addClass('oe_active').siblings().removeClass('oe_active'); @@ -705,20 +705,9 @@ instance.web.Login = instance.web.Widget.extend({ } return d; }, - remember_last_used_database: function(db) { - // This cookie will be used server side in order to avoid db reloading on first visit - var ttl = 24 * 60 * 60 * 365; - document.cookie = [ - 'last_used_database=' + db, - 'path=/', - 'max-age=' + ttl, - 'expires=' + new Date(new Date().getTime() + ttl * 1000).toGMTString() - ].join(';'); - }, database_selected: function(db) { var params = $.deparam.querystring(); params.db = db; - this.remember_last_used_database(db); this.$('.oe_login_dbpane').empty().text(_t('Loading...')); this.$('[name=login], [name=password]').prop('readonly', true); instance.web.redirect('/?' + $.param(params)); @@ -769,7 +758,6 @@ instance.web.Login = instance.web.Widget.extend({ self.hide_error(); self.$(".oe_login_pane").fadeOut("slow"); return this.session.session_authenticate(db, login, password).then(function() { - self.remember_last_used_database(db); if (self.has_local_storage && self.remember_credentials) { localStorage.setItem(db + '|last_login', login); if (self.session.debug) { @@ -804,10 +792,17 @@ instance.web.redirect = function(url, wait) { instance.client.crashmanager.active = false; } - var wait_server = function() { - instance.session.rpc("/web/webclient/version_info", {}).done(function() { + var load = function() { + var old = "" + window.location; + if (old === url) { + window.location.reload(); + } else { window.location = url; - }).fail(function() { + } + }; + + var wait_server = function() { + instance.session.rpc("/web/webclient/version_info", {}).done(load).fail(function() { setTimeout(wait_server, 250); }); }; @@ -815,7 +810,7 @@ instance.web.redirect = function(url, wait) { if (wait) { setTimeout(wait_server, 1000); } else { - window.location = url; + load(); } }; @@ -830,7 +825,6 @@ instance.web.Reload = function(parent, action) { var l = window.location; var sobj = $.deparam(l.search.substr(1)); - sobj.ts = new Date().getTime(); if (params.url_search) { sobj = _.extend(sobj, params.url_search); } @@ -875,7 +869,7 @@ instance.web.ChangePassword = instance.web.Widget.extend({ $button.appendTo(this.getParent().$buttons); $button.eq(2).click(function(){ self.getParent().close(); - }) + }); $button.eq(0).click(function(){ self.rpc("/web/session/change_password",{ 'fields': $("form[name=change_password_form]").serializeArray() @@ -887,7 +881,7 @@ instance.web.ChangePassword = instance.web.Widget.extend({ instance.webclient.on_logout(); } }); - }) + }); }, display_error: function (error) { return instance.web.dialog($('