diff --git a/addons/web/controllers/main.py b/addons/web/controllers/main.py index 9584c53ca5b..04570b68ccf 100644 --- a/addons/web/controllers/main.py +++ b/addons/web/controllers/main.py @@ -91,13 +91,16 @@ db_list = http.db_list db_monodb = http.db_monodb def redirect_with_hash(url, code=303): + redirect_code = "" % url if request.httprequest.user_agent.browser == 'msie': try: version = float(request.httprequest.user_agent.version) if version < 10: - return "" % url + return redirect_code except Exception: pass + elif request.httprequest.user_agent.browser == 'safari': + return redirect_code return werkzeug.utils.redirect(url, code) def module_topological_sort(modules): @@ -529,14 +532,12 @@ class Home(http.Controller): def index(self, s_action=None, db=None, debug=False, **kw): debug = debug != False - try: - lst = http.db_list() - except openerp.exceptions.AccessDenied: - lst = [] - + lst = http.db_list(True) if db not in lst: db = None guessed_db = http.db_monodb(request.httprequest) + if guessed_db is None and len(lst) > 0: + guessed_db = lst[0] def redirect(db): query = dict(urlparse.parse_qsl(request.httprequest.query_string, keep_blank_values=True)) @@ -544,11 +545,8 @@ class Home(http.Controller): redirect = request.httprequest.path + '?' + urllib.urlencode(query) return redirect_with_hash(redirect) - if guessed_db is None and db is None and lst: - return redirect(lst[0]) - - if guessed_db is None and lst: - guessed_db = lst[0] + if db is None and guessed_db is not None: + return redirect(guessed_db) if db is not None and db != guessed_db: request.session.logout() diff --git a/addons/web/http.py b/addons/web/http.py index 4bda58117d4..90dfca1615e 100644 --- a/addons/web/http.py +++ b/addons/web/http.py @@ -1043,7 +1043,7 @@ class Root(object): Tries to discover the controller handling the request for the path specified in the request. """ path = request.httprequest.path - urls = self.get_db_router(request.db).bind("") + urls = self.get_db_router(request.db).bind_to_environ(request.httprequest.environ) func, arguments = urls.match(path) arguments = dict([(k, v) for k, v in arguments.items() if not k.startswith("_ignored_")]) diff --git a/addons/web/static/src/js/openerpframework.js b/addons/web/static/src/js/openerpframework.js index 027452b42fb..1d688505a06 100644 --- a/addons/web/static/src/js/openerpframework.js +++ b/addons/web/static/src/js/openerpframework.js @@ -1265,7 +1265,7 @@ openerp.str_to_datetime = function(str) { if ( !res ) { throw new Error("'" + str + "' is not a valid datetime"); } - var tmp = new Date(); + var tmp = new Date(0); tmp.setUTCFullYear(parseFloat(res[1])); tmp.setUTCMonth(parseFloat(res[2]) - 1); tmp.setUTCDate(parseFloat(res[3])); @@ -1297,7 +1297,7 @@ openerp.str_to_date = function(str) { if ( !res ) { throw new Error("'" + str + "' is not a valid date"); } - var tmp = new Date(); + var tmp = new Date(0); tmp.setFullYear(parseFloat(res[1])); tmp.setMonth(parseFloat(res[2]) - 1); tmp.setDate(parseFloat(res[3])); @@ -1424,4 +1424,4 @@ if (typeof(define) !== "undefined") { // amd window.openerp = declare($, _, QWeb2); } -})(); \ No newline at end of file +})(); diff --git a/addons/web/static/src/js/view_list.js b/addons/web/static/src/js/view_list.js index ba7c58b3820..99e87725dce 100644 --- a/addons/web/static/src/js/view_list.js +++ b/addons/web/static/src/js/view_list.js @@ -501,7 +501,7 @@ instance.web.ListView = instance.web.View.extend( /** @lends instance.web.ListVi * * @returns {$.Deferred} promise to content reloading */ - reload_content: function () { + reload_content: synchronized(function () { var self = this; self.$el.find('.oe_list_record_selector').prop('checked', false); this.records.reset(); @@ -525,7 +525,7 @@ instance.web.ListView = instance.web.View.extend( /** @lends instance.web.ListVi limit: this._limit }); return reloaded.promise(); - }, + }), reload: function () { return this.reload_content(); }, @@ -1367,6 +1367,9 @@ instance.web.ListView.Groups = instance.web.Class.extend( /** @lends instance.we .removeClass('ui-icon-triangle-1-s') .addClass('ui-icon-triangle-1-e'); child.close(); + // force recompute the selection as closing group reset properties + var selection = self.get_selection(); + $(self).trigger('selected', [selection.ids, this.records]); } }); } @@ -1377,22 +1380,29 @@ instance.web.ListView.Groups = instance.web.Class.extend( /** @lends instance.we if (group.grouped_on) { var row_data = {}; row_data[group.grouped_on] = group; + var group_label = _t("Undefined"); var group_column = _(self.columns).detect(function (column) { return column.id === group.grouped_on; }); - if (! group_column) { - throw new Error(_.str.sprintf( - _t("Grouping on field '%s' is not possible because that field does not appear in the list view."), - group.grouped_on)); - } - var group_label; - try { - group_label = group_column.format(row_data, { - value_if_empty: _t("Undefined"), - process_modifiers: false - }); - } catch (e) { - group_label = _.str.escapeHTML(row_data[group_column.id].value); + if (group_column) { + try { + group_label = group_column.format(row_data, { + value_if_empty: _t("Undefined"), + process_modifiers: false + }); + } catch (e) { + group_label = _.str.escapeHTML(row_data[group_column.id].value); + } + } else { + group_label = group.value; + if (group_label instanceof Array) { + group_label = group_label[1]; + } + if (group_label === false) { + group_label = _t('Undefined'); + } + group_label = _.str.escapeHTML(group_label); } + // group_label is html-clean (through format or explicit // escaping if format failed), can inject straight into HTML $group_column.html(_.str.sprintf(_t("%s (%d)"), @@ -1637,6 +1647,22 @@ instance.web.ListView.Groups = instance.web.Class.extend( /** @lends instance.we } }); +/** + * Serializes concurrent calls to this asynchronous method. The method must + * return a deferred or promise. + * + * Current-implementation is class-serialized (the mutex is common to all + * instances of the list view). Can be switched to instance-serialized if + * having concurrent list views becomes possible and common. + */ +function synchronized(fn) { + var fn_mutex = new $.Mutex(); + return function () { + var args = _.toArray(arguments); + args.unshift(this); + return fn_mutex.exec(fn.bind.apply(fn, args)); + }; +} var DataGroup = instance.web.Class.extend({ init: function(parent, model, domain, context, group_by, level) { this.model = new instance.web.Model(model, context, domain); @@ -1648,6 +1674,10 @@ var DataGroup = instance.web.Class.extend({ }, list: function (fields, ifGroups, ifRecords) { var self = this; + if (!_.isEmpty(this.group_by)) { + // ensure group_by fields are read. + fields = _.unique((fields || []).concat(this.group_by)); + } var query = this.model.query(fields).order_by(this.sort).group_by(this.group_by); $.when(query).done(function (querygroups) { // leaf node diff --git a/addons/web/static/test/framework.js b/addons/web/static/test/framework.js index 3619ab87e65..237bdc195ca 100644 --- a/addons/web/static/test/framework.js +++ b/addons/web/static/test/framework.js @@ -427,12 +427,55 @@ ropenerp.testing.section('server-formats', { date3.getUTCHours(), date3.getUTCMinutes(), date3.getUTCSeconds(), date3.getUTCMilliseconds()], [2009, 5 - 1, 4, 12, 34, 23, 845]); }); + test('Parse server datetime on 31', {asserts: 1}, function() { + var wDate = window.Date; + var s = ropenerp.testing.Stack(); + return s.push(function() { + window.Date = function(v) { + if (_.isUndefined(v)) { + v = '2013-10-31 12:34:56'; + } + return new wDate(v); + }; + }, function() { + window.Date = wDate; + }).execute(function() { + return openerp.str_to_datetime('2013-11-11 02:45:21'); + }).then(function(date) { + deepEqual( + [date.getUTCFullYear(), date.getUTCMonth(), date.getUTCDate(), + date.getUTCHours(), date.getUTCMinutes(), date.getUTCSeconds()], + [2013, 11 - 1, 11, 2, 45, 21]); + }); + + }); test('Parse server date', function () { var date = openerp.str_to_date("2009-05-04"); deepEqual( [date.getFullYear(), date.getMonth(), date.getDate()], [2009, 5 - 1, 4]); }); + test('Parse server date on 31', {asserts: 1}, function() { + var wDate = window.Date; + var s = ropenerp.testing.Stack(); + return s.push(function() { + window.Date = function(v) { + if (_.isUndefined(v)) { + v = '2013-10-31 12:34:56'; + } + return new wDate(v); + }; + }, function() { + window.Date = wDate; + }).execute(function() { + return openerp.str_to_date('2013-11-21'); + }).then(function(date) { + deepEqual( + [date.getFullYear(), date.getMonth(), date.getDate()], + [2013, 11 - 1, 21]); + }); + + }); test('Parse server time', function () { var date = openerp.str_to_time("12:34:23"); deepEqual( diff --git a/addons/web_kanban/static/src/js/kanban.js b/addons/web_kanban/static/src/js/kanban.js index 6f47f9c128d..1de3df275c6 100644 --- a/addons/web_kanban/static/src/js/kanban.js +++ b/addons/web_kanban/static/src/js/kanban.js @@ -239,7 +239,11 @@ instance.web_kanban.KanbanView = instance.web.View.extend({ self.$buttons.find('.oe_alternative').toggle(self.grouped_by_m2o); self.$el.toggleClass('oe_kanban_grouped_by_m2o', self.grouped_by_m2o); var grouping_fields = self.group_by ? [self.group_by].concat(_.keys(self.aggregates)) : undefined; - var grouping = new instance.web.Model(self.dataset.model, context, domain).query().group_by(grouping_fields); + if (!_.isEmpty(grouping_fields)) { + // ensure group_by fields are read. + self.fields_keys = _.unique(self.fields_keys.concat(grouping_fields)); + } + var grouping = new instance.web.Model(self.dataset.model, context, domain).query(self.fields_keys).group_by(grouping_fields); return self.alive($.when(grouping)).done(function(groups) { self.remove_no_result(); if (groups) {