diff --git a/addons/base/controllers/main.py b/addons/base/controllers/main.py index 30794733f44..0b7a8db1db8 100644 --- a/addons/base/controllers/main.py +++ b/addons/base/controllers/main.py @@ -1,7 +1,6 @@ # -*- coding: utf-8 -*- import base64 import glob, os -import pprint from xml.etree import ElementTree from cStringIO import StringIO @@ -397,9 +396,10 @@ class DataSet(openerpweb.Controller): req.session.eval_context(req.context))} @openerpweb.jsonrequest - def search_read(self, request, model, fields=False, offset=0, limit=False, domain=None, context=None, sort=None): - return self.do_search_read(request, model, fields, offset, limit, domain, context, sort) - def do_search_read(self, request, model, fields=False, offset=0, limit=False, domain=None, context=None, sort=None): + def search_read(self, request, model, fields=False, offset=0, limit=False, domain=None, sort=None): + return self.do_search_read(request, model, fields, offset, limit, domain, sort) + def do_search_read(self, request, model, fields=False, offset=0, limit=False, domain=None + , sort=None): """ Performs a search() followed by a read() (if needed) using the provided search criteria @@ -412,22 +412,33 @@ class DataSet(openerpweb.Controller): :param int limit: the maximum number of records to return :param list domain: the search domain for the query :param list sort: sorting directives - :returns: a list of result records + :returns: A structure (dict) with two keys: ids (all the ids matching + the (domain, context) pair) and records (paginated records + matching fields selection set) :rtype: list """ Model = request.session.model(model) - context, domain = eval_context_and_domain(request.session, request.context, domain) - - ids = Model.search(domain, offset or 0, limit or False, - sort or False, context) + context, domain = eval_context_and_domain( + request.session, request.context, domain) + ids = Model.search(domain, 0, False, sort or False, context) + # need to fill the dataset with all ids for the (domain, context) pair, + # so search un-paginated and paginate manually before reading + paginated_ids = ids[offset:(offset + limit if limit else None)] if fields and fields == ['id']: # shortcut read if we only want the ids - return map(lambda id: {'id': id}, ids) + return { + 'ids': ids, + 'records': map(lambda id: {'id': id}, paginated_ids) + } + + records = Model.read(paginated_ids, fields or False, context) + records.sort(key=lambda obj: ids.index(obj['id'])) + return { + 'ids': ids, + 'records': records + } - reads = Model.read(ids, fields or False, context) - reads.sort(key=lambda obj: ids.index(obj['id'])) - return reads @openerpweb.jsonrequest def get(self, request, model, ids, fields=False): diff --git a/addons/base/static/src/css/base.css b/addons/base/static/src/css/base.css index 1d5dd7b6314..d03abe34d5c 100644 --- a/addons/base/static/src/css/base.css +++ b/addons/base/static/src/css/base.css @@ -607,6 +607,9 @@ background: linear-gradient(top, #ffffff 0%,#d8d8d8 11%,#afafaf 86%,#333333 91%, vertical-align: middle; text-align: left; } +.openerp .oe-listview td.oe-record-delete { + text-align: right; +} .openerp .oe-listview th.oe-sortable, .openerp .oe-listview th.oe-sortable .ui-icon { cursor: pointer; @@ -637,6 +640,17 @@ background: linear-gradient(top, #ffffff 0%,#d8d8d8 11%,#afafaf 86%,#333333 91%, .openerp .oe-listview th.oe-list-pager { text-align: right; } +.openerp .oe-list-pager .oe-pager-state { + cursor: pointer; +} + +.openerp .oe-listview .oe-group-name { + padding-right: 1em; +} +.openerp .oe-listview .oe-group-name, +.openerp .oe-listview .oe-group-pagination { + white-space: nowrap; +} .openerp .oe-listview tfoot td { padding: 3px 3px 0; diff --git a/addons/base/static/src/js/chrome.js b/addons/base/static/src/js/chrome.js index 12ea496a3e6..06153f53cb6 100644 --- a/addons/base/static/src/js/chrome.js +++ b/addons/base/static/src/js/chrome.js @@ -355,15 +355,12 @@ openerp.base.Session = openerp.base.BasicController.extend( /** @lends openerp.b * @param {Object} params call parameters * @param {Function} success_callback function to execute on RPC call success * @param {Function} error_callback function to execute on RPC call failure - * one * @returns {jQuery.Deferred} jquery-provided ajax deferred */ rpc: function(url, params, success_callback, error_callback) { var self = this; // Construct a JSON-RPC2 request, method is currently unused params.session_id = this.session_id; - // niv: wtf? - //params.context = typeof(params.context) != "undefined" ? params.context : this.context; // Call using the rpc_mode var deferred = $.Deferred(); @@ -919,18 +916,15 @@ openerp.base.Database = openerp.base.Controller.extend({ }); openerp.base.Login = openerp.base.Controller.extend({ + remember_creditentials: true, init: function(session, element_id) { this._super(session, element_id); this.has_local_storage = typeof(localStorage) != 'undefined'; this.selected_db = null; this.selected_login = null; - this.selected_password = null; - this.remember = false; - if (this.has_local_storage && localStorage.getItem('remember_creditentials') === 'true') { - this.remember = true; + if (this.has_local_storage && this.remember_creditentials) { this.selected_db = localStorage.getItem('last_db_login_success'); this.selected_login = localStorage.getItem('last_login_login_success'); - this.selected_password = localStorage.getItem('last_password_login_success'); } }, start: function() { @@ -959,22 +953,17 @@ openerp.base.Login = openerp.base.Controller.extend({ var db = $e.find("form [name=db]").val(); var login = $e.find("form input[name=login]").val(); var password = $e.find("form input[name=password]").val(); - var remember = $e.find("form input[name=remember]").attr('checked'); //$e.hide(); // Should hide then call callback this.session.session_login(db, login, password, function() { if(self.session.session_is_valid()) { if (self.has_local_storage) { - if(remember) { - localStorage.setItem('remember_creditentials', 'true'); + if(self.remember_creditentials) { localStorage.setItem('last_db_login_success', db); localStorage.setItem('last_login_login_success', login); - localStorage.setItem('last_password_login_success', password); } else { - localStorage.setItem('remember_creditentials', ''); localStorage.setItem('last_db_login_success', ''); localStorage.setItem('last_login_login_success', ''); - localStorage.setItem('last_password_login_success', ''); } } self.on_login_valid(); diff --git a/addons/base/static/src/js/data.js b/addons/base/static/src/js/data.js index f7fc1a24dc6..3df9679e1c5 100644 --- a/addons/base/static/src/js/data.js +++ b/addons/base/static/src/js/data.js @@ -135,7 +135,7 @@ openerp.base.ContainerDataGroup = openerp.base.DataGroup.extend( domain: this.domain, group_by_fields: this.group_by }, function () { }).then(function (response) { - var data_groups = _(response.result).map( + var data_groups = _(response).map( _.bind(self.transform_group, self)); self.groups = data_groups; d.resolveWith(self, [data_groups]); @@ -196,7 +196,9 @@ openerp.base.GrouplessDataGroup = openerp.base.DataGroup.extend( this._super(session, model, domain, context, null, level); }, list: function (ifGroups, ifRecords) { - ifRecords(new openerp.base.DataSetSearch(this.session, this.model, this.context, this.domain)); + ifRecords(_.extend( + new openerp.base.DataSetSearch(this.session, this.model), + {domain: this.domain, context: this.context})); } }); @@ -429,13 +431,10 @@ openerp.base.DataSetSearch = openerp.base.DataSet.extend({ sort: this.sort(), offset: offset, limit: limit - }, function (records) { - self.ids.splice(0, self.ids.length); + }, function (result) { + self.ids = result.ids; self.offset = offset; - for (var i=0; i < records.length; i++ ) { - self.ids.push(records[i].id); - } - callback(records); + callback(result.records); }); }, /** @@ -491,7 +490,6 @@ openerp.base.BufferedDataSet = openerp.base.DataSetStatic.extend({ var cached = {id:_.uniqueId(this.virtual_id_prefix), values: data}; this.to_create.push(cached); this.cache.push(cached); - this.on_change(); var to_return = $.Deferred().then(callback); setTimeout(function() {to_return.resolve({result: cached.id});}, 0); return to_return.promise(); diff --git a/addons/base/static/src/js/form.js b/addons/base/static/src/js/form.js index bfa62f102cc..0c2ec73af81 100644 --- a/addons/base/static/src/js/form.js +++ b/addons/base/static/src/js/form.js @@ -166,20 +166,24 @@ openerp.base.FormView = openerp.base.View.extend( /** @lends openerp.base.FormV this.ready = false; var onchange = _.trim(widget.node.attrs.on_change); var call = onchange.match(/^\s?(.*?)\((.*?)\)\s?$/); - console.log("Onchange triggered for field '%s' -> %s", widget.name, onchange); if (call) { var method = call[1], args = []; + var context_index = null; var argument_replacement = { - 'False' : false, - 'True' : true, - 'None' : null, - 'context': widget.build_context ? widget.build_context() : {} + 'False' : function() {return false;}, + 'True' : function() {return true;}, + 'None' : function() {return null;}, + 'context': function(i) { + context_index = i; + var ctx = widget.build_context ? widget.build_context() : {}; + return ctx; + } } var parent_fields = null; - _.each(call[2].split(','), function(a) { + _.each(call[2].split(','), function(a, i) { var field = _.trim(a); if (field in argument_replacement) { - args.push(argument_replacement[field]); + args.push(argument_replacement[field](i)); return; } else if (self.fields[field]) { var value = self.fields[field].get_on_change_value(); @@ -193,7 +197,7 @@ openerp.base.FormView = openerp.base.View.extend( /** @lends openerp.base.FormV } var p_val = parent_fields[_.trim(splitted[1])]; if (p_val !== undefined) { - args.push(value ? value : false); + args.push(p_val == null ? false : p_val); return; } } @@ -208,7 +212,8 @@ openerp.base.FormView = openerp.base.View.extend( /** @lends openerp.base.FormV return this.rpc(ajax, { model: this.dataset.model, method: method, - args: [(this.datarecord.id == null ? [] : [this.datarecord.id])].concat(args) + args: [(this.datarecord.id == null ? [] : [this.datarecord.id])].concat(args), + context_id: context_index === null ? null : context_index + 1 }, function(response) { self.on_processed_onchange(response, processed); }); @@ -220,14 +225,12 @@ openerp.base.FormView = openerp.base.View.extend( /** @lends openerp.base.FormV on_processed_onchange: function(response, processed) { var result = response.result; if (result.value) { - console.log(" |-> Onchange Response :", result.value); for (var f in result.value) { var field = this.fields[f]; if (field) { var value = result.value[f]; processed.push(field.name); if (field.get_value() != value) { - console.log(" |-> Onchange Action : change '%s' value from '%s' to '%s'", field.name, field.get_value(), value); field.set_value(value); field.touched = true; if (_.indexOf(processed, field.name) < 0) { @@ -236,12 +239,11 @@ openerp.base.FormView = openerp.base.View.extend( /** @lends openerp.base.FormV } } else { // this is a common case, the normal behavior should be to ignore it - this.log("on_processed_onchange can't find field " + f, result); } } this.on_form_changed(); } - if (result.warning) { + if (!_.isEmpty(result.warning)) { $(QWeb.render("DialogWarning", result.warning)).dialog({ modal: true, buttons: { @@ -390,12 +392,13 @@ openerp.base.FormView = openerp.base.View.extend( /** @lends openerp.base.FormV this.on_attachments_loaded([]); } else { // TODO fme: modify this so it doesn't try to load attachments when there is not sidebar - /*this.rpc('/base/dataset/search_read', { - model: 'ir.attachment', - fields: ['name', 'url', 'type'], - domain: [['res_model', '=', this.dataset.model], ['res_id', '=', this.datarecord.id], ['type', 'in', ['binary', 'url']]], - context: this.dataset.get_context() - }, this.on_attachments_loaded);*/ + /*(new openerp.base.DataSetSearch( + this.session, 'ir.attachment', this.dataset.get_context(), + [['res_model', '=', this.dataset.model], + ['res_id', '=', this.datarecord.id], + ['type', 'in', ['binary', 'url']]])).read_slice( + ['name', 'url', 'type'], false, false, + this.on_attachments_loaded);*/ } }, on_attachments_loaded: function(attachments) { @@ -437,7 +440,8 @@ openerp.base.FormView = openerp.base.View.extend( /** @lends openerp.base.FormV get_fields_values: function() { var values = {}; _.each(this.fields, function(value, key) { - values[key] = value.get_value(); + var val = value.get_value(); + values[key] = val; }); return values; } @@ -928,7 +932,7 @@ openerp.base.form.FieldDatetime = openerp.base.form.Field.extend({ }, set_value: function(value) { this._super.apply(this, arguments); - if (value == null || value == false) { + if (!value) { this.$element.find('input').val(''); } else { this.$element.find('input').unbind('change'); @@ -1079,7 +1083,7 @@ openerp.base.form.FieldProgressBar = openerp.base.form.Field.extend({ set_value: function(value) { this._super.apply(this, arguments); var show_value = Number(value); - if (show_value === NaN) { + if (isNaN(show_value)) { show_value = 0; } this.$element.find('div').progressbar('option', 'value', show_value).find('span').html(show_value + '%'); @@ -1298,7 +1302,7 @@ openerp.base.form.FieldMany2One = openerp.base.form.Field.extend({ // create... values.push({label: " Create and Edit...", action: function() { self._change_int_value(null); - self._search_create_popup("form"); + self._search_create_popup("form", undefined, {"default_name": search_val}); }}); response(values); @@ -1437,6 +1441,7 @@ var commands = { } }; openerp.base.form.FieldOne2Many = openerp.base.form.Field.extend({ + multi_selection: false, init: function(view, node) { this._super(view, node); this.template = "FieldOne2Many"; @@ -1464,6 +1469,7 @@ openerp.base.form.FieldOne2Many = openerp.base.form.Field.extend({ } if(view.view_type === "list") { view.options = { + 'selectable': self.multi_selection }; } views.push(view); @@ -1585,26 +1591,31 @@ openerp.base.form.One2ManyDataSet = openerp.base.BufferedDataSet.extend({ openerp.base.form.One2ManyListView = openerp.base.ListView.extend({ do_add_record: function () { - var self = this; - var pop = new openerp.base.form.SelectCreatePopup(null, self.o2m.view.session); - pop.select_element(self.o2m.field.relation,{ - initial_view: "form", - alternative_form_view: self.o2m.field.views ? self.o2m.field.views["form"] : undefined, - auto_create: false, - parent_view: self.o2m.view - }, self.o2m.build_domain(), self.o2m.build_context()); - pop.on_create.add(function(data) { - self.o2m.dataset.create(data, function(r) { - self.o2m.dataset.set_ids(self.o2m.dataset.ids.concat([r.result])); - self.o2m.dataset.on_change(); - pop.stop(); - self.o2m.reload_current_view(); + if (this.options.editable) { + this._super.apply(this, arguments); + } else { + var self = this; + var pop = new openerp.base.form.SelectCreatePopup(null, self.o2m.view.session); + pop.select_element(self.o2m.field.relation,{ + initial_view: "form", + alternative_form_view: self.o2m.field.views ? self.o2m.field.views["form"] : undefined, + auto_create: false, + parent_view: self.o2m.view + }, self.o2m.build_domain(), self.o2m.build_context()); + pop.on_create.add(function(data) { + self.o2m.dataset.create(data, function(r) { + self.o2m.dataset.set_ids(self.o2m.dataset.ids.concat([r.result])); + self.o2m.dataset.on_change(); + pop.stop(); + self.o2m.reload_current_view(); + }); }); - }); + } } }); openerp.base.form.FieldMany2Many = openerp.base.form.Field.extend({ + multi_selection: false, init: function(view, node) { this._super(view, node); this.template = "FieldMany2Many"; @@ -1625,7 +1636,8 @@ openerp.base.form.FieldMany2Many = openerp.base.form.Field.extend({ this.list_view = new openerp.base.form.Many2ManyListView( null, this.view.session, this.list_id, this.dataset, false, { - 'addable': 'Add' + 'addable': 'Add', + 'selectable': self.multi_selection }); this.list_view.m2m_field = this; this.list_view.on_loaded.add_last(function() { @@ -1666,7 +1678,9 @@ openerp.base.form.Many2ManyListView = openerp.base.ListView.extend({ do_add_record: function () { var pop = new openerp.base.form.SelectCreatePopup( null, this.m2m_field.view.session); - pop.select_element(this.model, {}, this.m2m_field.build_domain(), this.m2m_field.build_context()); + pop.select_element(this.model, {}, + new openerp.base.CompoundDomain(this.m2m_field.build_domain(), ["!", ["id", "in", this.m2m_field.dataset.ids]]), + this.m2m_field.build_context()); var self = this; pop.on_select_elements.add(function(element_ids) { _.each(element_ids, function(element_id) { @@ -1719,7 +1733,7 @@ openerp.base.form.SelectCreatePopup = openerp.base.BaseWidget.extend({ start: function() { this._super(); this.dataset = new openerp.base.ReadOnlyDataSetSearch(this.session, this.model, - this.context, this.domain); + this.context); this.dataset.parent_view = this.options.parent_view; if (this.options.initial_view == "search") { this.setup_search_view(); @@ -1739,11 +1753,11 @@ openerp.base.form.SelectCreatePopup = openerp.base.BaseWidget.extend({ }); this.searchview.on_search.add(function(domains, contexts, groupbys) { if (self.initial_ids) { - self.view_list.do_search.call(self, domains.concat([[["id", "in", self.initial_ids]]]), + self.view_list.do_search.call(self, domains.concat([[["id", "in", self.initial_ids]], self.domain]), contexts, groupbys); self.initial_ids = undefined; } else { - self.view_list.do_search.call(self, domains, contexts, groupbys); + self.view_list.do_search.call(self, domains.concat([self.domain]), contexts, groupbys); } }); this.searchview.on_loaded.add_last(function () { diff --git a/addons/base/static/src/js/list-editable.js b/addons/base/static/src/js/list-editable.js index b97a35d0612..8b4b16faf04 100644 --- a/addons/base/static/src/js/list-editable.js +++ b/addons/base/static/src/js/list-editable.js @@ -116,6 +116,23 @@ openerp.base.list.editable = function (openerp) { delete this.edition_index; delete this.edition; }, + /** + * Adapts this list's view description to be suitable to the inner form view of a row being edited. + * + * @returns {Object} fields_view_get's view section suitable for putting into form view of editable rows. + */ + get_form_fields_view: function () { + // deep copy of view + var view = $.extend(true, {}, this.group.view.fields_view); + _(view.arch.children).each(function (widget) { + widget.attrs.nolabel = true; + if (widget.tag === 'button') { + delete widget.attrs.string; + } + }); + view.arch.attrs.col = 2 * view.arch.children.length; + return view; + }, render_row_as_form: function (row) { this.cancel_pending_edition(); @@ -161,7 +178,7 @@ openerp.base.list.editable = function (openerp) { template: 'ListView.row.form', registry: openerp.base.list.form.widgets }); - $.when(this.edition_form.on_loaded({fields_view: this.get_fields_view()})).then(function () { + $.when(this.edition_form.on_loaded({fields_view: this.get_form_fields_view()})).then(function () { // put in $.when just in case FormView.on_loaded becomes asynchronous $new_row.find('td') .addClass('oe-field-cell') diff --git a/addons/base/static/src/js/list.js b/addons/base/static/src/js/list.js index 9c68e4f9b78..aa3fe3f658b 100644 --- a/addons/base/static/src/js/list.js +++ b/addons/base/static/src/js/list.js @@ -59,6 +59,24 @@ openerp.base.ListView = openerp.base.View.extend( /** @lends openerp.base.ListVi if (this.dataset instanceof openerp.base.DataSetStatic) { this.groups.datagroup = new openerp.base.StaticDataGroup(this.dataset); } + + this.page = 0; + }, + /** + * Retrieves the view's number of records per page (|| section) + * + * options > defaults > view_manager.action.limit > indefinite + * + * @returns {Number|null} + */ + limit: function () { + if (this._limit === undefined) { + this._limit = (this.options.limit + || this.defaults.limit + || (this.view_manager.action || {}).limit + || null); + } + return this._limit; }, /** * Set a custom Group construct as the root of the List View. @@ -134,10 +152,10 @@ openerp.base.ListView = openerp.base.View.extend( /** @lends openerp.base.ListVi this.$element.html(QWeb.render("ListView", this)); // Head hook - this.$element.find('#oe-list-add') + this.$element.find('.oe-list-add') .click(this.do_add_record) .attr('disabled', grouped && this.options.editable); - this.$element.find('#oe-list-delete') + this.$element.find('.oe-list-delete') .attr('disabled', true) .click(this.do_delete_selected); this.$element.find('thead').delegate('th[data-id]', 'click', function (e) { @@ -149,8 +167,75 @@ openerp.base.ListView = openerp.base.View.extend( /** @lends openerp.base.ListVi self.reload_view(); }); + this.$element.find('.oe-list-pager') + .delegate('button', 'click', function () { + var $this = $(this); + switch ($this.data('pager-action')) { + case 'first': + self.page = 0; break; + case 'last': + self.page = Math.floor( + self.dataset.ids.length / self.limit()); + break; + case 'next': + self.page += 1; break; + case 'previous': + self.page -= 1; break; + } + self.reload_content(); + }).find('.oe-pager-state') + .click(function (e) { + e.stopPropagation(); + var $this = $(this); + + var $select = $('') + .appendTo($this.empty()) + .click(function (e) {e.stopPropagation();}) + .append('80' + + '100' + + '200' + + '500' + + 'Unlimited') + .change(function () { + var val = parseInt($select.val(), 10); + self._limit = (isNaN(val) ? null : val); + self.page = 0; + self.reload_content(); + }) + .val(self._limit || 'NaN'); + }); + this.view_manager.sidebar.set_toolbar(data.fields_view.toolbar); }, + /** + * Configures the ListView pager based on the provided dataset's information + * + * Horrifying side-effect: sets the dataset's data on this.dataset? + * + * @param {openerp.base.DataSet} dataset + */ + configure_pager: function (dataset) { + this.dataset.ids = dataset.ids; + + var limit = this.limit(), + total = dataset.ids.length, + first = (this.page * limit), + last; + if (!limit || (total - first) < limit) { + last = total; + } else { + last = first + limit; + } + this.$element.find('span.oe-pager-state').empty().text(_.sprintf( + "[%d to %d] of %d", first + 1, last, total)); + + this.$element + .find('button[data-pager-action=first], button[data-pager-action=previous]') + .attr('disabled', this.page === 0) + .end() + .find('button[data-pager-action=last], button[data-pager-action=next]') + .attr('disabled', last === total); + }, /** * Sets up the listview's columns: merges view and fields data, move * grouped-by columns to the front of the columns list and make them all @@ -182,7 +267,7 @@ openerp.base.ListView = openerp.base.View.extend( /** @lends openerp.base.ListVi } return column; }; - + this.columns.splice(0, this.columns.length); this.columns.push.apply( this.columns, @@ -264,10 +349,8 @@ openerp.base.ListView = openerp.base.View.extend( /** @lends openerp.base.ListVi */ reload_view: function (grouped) { var self = this; - this.dataset.offset = 0; - this.dataset.limit = false; var callback = function (field_view_get) { - self.on_loaded(field_view_get, grouped); + self.on_loaded(field_view_get, grouped); }; if (this.embedded_view) { return $.Deferred().then(callback).resolve({fields_view: this.embedded_view}); @@ -346,7 +429,7 @@ openerp.base.ListView = openerp.base.View.extend( /** @lends openerp.base.ListVi * @param {Array} records selected record values */ do_select: function (ids, records) { - this.$element.find('#oe-list-delete') + this.$element.find('.oe-list-delete') .attr('disabled', !ids.length); if (!records.length) { @@ -505,6 +588,7 @@ openerp.base.ListView.List = Class.extend( /** @lends openerp.base.ListView.List init: function (group, opts) { var self = this; this.group = group; + this.view = group.view; this.options = opts.options; this.columns = opts.columns; @@ -555,18 +639,6 @@ openerp.base.ListView.List = Class.extend( /** @lends openerp.base.ListView.List this.$current = this.$_element.clone(true); this.$current.empty().append(QWeb.render('ListView.rows', this)); }, - get_fields_view: function () { - // deep copy of view - var view = $.extend(true, {}, this.group.view.fields_view); - _(view.arch.children).each(function (widget) { - widget.attrs.nolabel = true; - if (widget.tag === 'button') { - delete widget.attrs.string; - } - }); - view.arch.attrs.col = 2 * view.arch.children.length; - return view; - }, /** * Gets the ids of all currently selected records, if any * @returns {Object} object with the keys ``ids`` and ``records``, holding respectively the ids of all selected records and the records themselves. @@ -715,13 +787,10 @@ openerp.base.ListView.Groups = Class.extend( /** @lends openerp.base.ListView.Gr this.columns = view.columns; this.datagroup = null; - this.sections = []; + this.$row = null; this.children = {}; - }, - pad: function ($row) { - if (this.options.selectable) { - $row.append(''); - } + + this.page = 0; }, make_fragment: function () { return document.createDocumentFragment(); @@ -753,10 +822,35 @@ openerp.base.ListView.Groups = Class.extend( /** @lends openerp.base.ListView.Gr } return red_letter_tboday; }, + make_paginator: function () { + var self = this; + var $prev = $('<') + .click(function (e) { + e.stopPropagation(); + self.page -= 1; + + self.$row.closest('tbody').next() + .replaceWith(self.render()); + }); + var $next = $('>') + .click(function (e) { + e.stopPropagation(); + self.page += 1; + + self.$row.closest('tbody').next() + .replaceWith(self.render()); + }); + this.$row.children().last() + .append($prev) + .append('') + .append($next); + }, open: function (point_insertion) { this.render().insertAfter(point_insertion); + this.make_paginator(); }, close: function () { + this.$row.children().last().empty(); this.apoptosis(); }, /** @@ -788,7 +882,7 @@ openerp.base.ListView.Groups = Class.extend( /** @lends openerp.base.ListView.Gr self.bind_child_events(child); child.datagroup = group; - var $row = $(''); + var $row = child.$row = $(''); if (group.openable) { $row.click(function (e) { if (!$row.data('open')) { @@ -808,7 +902,7 @@ openerp.base.ListView.Groups = Class.extend( /** @lends openerp.base.ListView.Gr } placeholder.appendChild($row[0]); - var $group_column = $('').appendTo($row); + var $group_column = $('').appendTo($row); if (group.grouped_on) { // Don't fill this if group_by_no_leaf but no group_by $group_column @@ -822,8 +916,10 @@ openerp.base.ListView.Groups = Class.extend( /** @lends openerp.base.ListView.Gr self.indent($group_column, group.level); // count column $('').text(group.length).appendTo($row); - - self.pad($row); + + if (self.options.selectable) { + $row.append(''); + } _(self.columns).chain() .filter(function (column) {return !column.invisible;}) .each(function (column) { @@ -844,6 +940,9 @@ openerp.base.ListView.Groups = Class.extend( /** @lends openerp.base.ListView.Gr $row.append(''); } }); + if (self.options.deletable) { + $row.append(''); + } }); return placeholder; }, @@ -867,6 +966,7 @@ openerp.base.ListView.Groups = Class.extend( /** @lends openerp.base.ListView.Gr }, render_dataset: function (dataset) { var rows = [], + self = this, list = new openerp.base.ListView.List(this, { options: this.options, columns: this.columns, @@ -875,11 +975,30 @@ openerp.base.ListView.Groups = Class.extend( /** @lends openerp.base.ListView.Gr }); this.bind_child_events(list); - var d = new $.Deferred(); + var view = this.view, + limit = view.limit(), + d = new $.Deferred(), + page = this.datagroup.openable ? this.page : view.page; + dataset.read_slice( _.filter(_.pluck(_.select(this.columns, function(x) {return x.tag == "field";}), 'name'), _.identity), - 0, false, + page * limit, limit, function (records) { + if (!self.datagroup.openable) { + view.configure_pager(dataset); + } else { + var pages = Math.ceil(dataset.ids.length / limit); + self.$row + .find('.oe-pager-state') + .text(_.sprintf('%d/%d', page + 1, pages)) + .end() + .find('button[data-pager-action=previous]') + .attr('disabled', page === 0) + .end() + .find('button[data-pager-action=next]') + .attr('disabled', page === pages - 1); + } + var form_records = _(records).map( $.proxy(list, 'transform_record')); diff --git a/addons/base/static/src/js/search.js b/addons/base/static/src/js/search.js index 6efa6988fe8..d8b33e3d9a6 100644 --- a/addons/base/static/src/js/search.js +++ b/addons/base/static/src/js/search.js @@ -736,6 +736,9 @@ openerp.base.search.ExtendedSearch = openerp.base.BaseWidget.extend({ }, start: function () { this._super(); + if (!this.$element) { + return; // not a logical state but sometimes it happens + } this.$element.closest("table.oe-searchview-render-line").css("display", "none"); var self = this; this.rpc("/base/searchview/fields_get", @@ -751,6 +754,9 @@ openerp.base.search.ExtendedSearch = openerp.base.BaseWidget.extend({ return null; }, get_domain: function() { + if (!this.$element) { + return null; // not a logical state but sometimes it happens + } if(this.$element.closest("table.oe-searchview-render-line").css("display") == "none") { return null; } diff --git a/addons/base/static/src/xml/base.xml b/addons/base/static/src/xml/base.xml index 96c05f2595e..f6faf413b5c 100644 --- a/addons/base/static/src/xml/base.xml +++ b/addons/base/static/src/xml/base.xml @@ -86,19 +86,7 @@ Password: - - - - Remember password: - - - - - - - - + @@ -255,11 +243,11 @@ - - Delete @@ -268,16 +256,19 @@ - First - First + << - 1 - to 1 - of 1 + + - >> - Last + >> + Last @@ -541,9 +532,9 @@ - Search - Create New - Open + Search... + Create New... + Open... @@ -803,8 +794,8 @@ - All the following conditions must match Any of the following conditions must match + All the following conditions must match None of the following conditions must match - d.html_error = context.engine.tools.html_escape(d.error.data.fault_code).replace(/\n/g, ''); + var message = d.message ? d.message : d.error.data.fault_code; + d.html_error = context.engine.tools.html_escape(message) + .replace(/\n/g, ''); diff --git a/addons/base/static/test/list.js b/addons/base/static/test/list.js index 1a2b1719086..3d441f190db 100644 --- a/addons/base/static/test/list.js +++ b/addons/base/static/test/list.js @@ -149,7 +149,7 @@ $(document).ready(function () { listview.$element.find('tbody th input:eq(1)') .attr('checked', true); - listview.$element.find('#oe-list-delete').click(); + listview.$element.find('.oe-list-delete').click(); deepEqual(deleted, [2, 3]); }); }); diff --git a/addons/base_gantt/static/src/js/gantt.js b/addons/base_gantt/static/src/js/gantt.js index 7f7aec85b63..336454f0bd1 100644 --- a/addons/base_gantt/static/src/js/gantt.js +++ b/addons/base_gantt/static/src/js/gantt.js @@ -262,21 +262,13 @@ init: function(view_manager, session, element_id, dataset, view_id) { throw "Unrecognized date/time format"; }, - reload_gantt: function(domain) { + reload_gantt: function() { var self = this; - var ajax = { - url: '/base/dataset/search_read', - async: false - }; - this.rpc(ajax, { - model: this.dataset.model, - domain: self.dataset.domain, - context :self.dataset.get_context() - }, function(response) { - ganttChartControl.clearAll(); - jQuery("#GanttDiv").children().remove(); - self.load_event(response); - }); + this.dataset.read_slice(false, false, false, function(response) { + ganttChartControl.clearAll(); + jQuery("#GanttDiv").children().remove(); + self.load_event(response); + }); }, do_search: function (domains, contexts, groupbys) { @@ -290,7 +282,7 @@ init: function(view_manager, session, element_id, dataset, view_id) { }, function (results) { self.dataset.context = results.context; self.dataset.domain = results.domain; - return self.reload_gantt(self.dataset.domain); + self.reload_gantt(); }); } diff --git a/addons/web_mobile/static/src/js/web_mobile.js b/addons/web_mobile/static/src/js/web_mobile.js index 08d9e81f4be..059c374d7d4 100644 --- a/addons/web_mobile/static/src/js/web_mobile.js +++ b/addons/web_mobile/static/src/js/web_mobile.js @@ -90,11 +90,8 @@ openerp.web_mobile.ListView = openerp.base.Controller.extend({ on_action: function(action) { var self = this; var view_id = action.views[0][0]; - var model = action.res_model; - - self.rpc('/base/dataset/search_read', { - model: model - },function(result){ + (new openerp.base.DataSetSearch(this.session, action.res_model, null, null)) + .read_slice(false, false, false, function(result){ this.listview = new openerp.web_mobile.ListView(this.session, "oe_app"); self.$element.html(QWeb.render("ListView", {'records' : result})); });
- d.html_error = context.engine.tools.html_escape(d.error.data.fault_code).replace(/\n/g, ''); + var message = d.message ? d.message : d.error.data.fault_code; + d.html_error = context.engine.tools.html_escape(message) + .replace(/\n/g, '');