From 121f55facfd7e5f1c70f293ee0698fe351793bc4 Mon Sep 17 00:00:00 2001 From: Xavier Morel Date: Wed, 22 Jun 2011 12:57:39 +0200 Subject: [PATCH 01/12] [FIX] mobile so it goes through DataSetSearch instead of calling search_read directly bzr revid: xmo@openerp.com-20110622105739-5cpe77ptruvkthkq --- addons/web_mobile/static/src/js/web_mobile.js | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) 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})); }); From ec24a587a7293caf16011af87060c0c637acc353 Mon Sep 17 00:00:00 2001 From: Xavier Morel Date: Wed, 22 Jun 2011 12:59:20 +0200 Subject: [PATCH 02/12] [FIX] gantt so it goes through DataSetSearch instead of calling search_read directly bzr revid: xmo@openerp.com-20110622105920-gl0dyx8tkaau0qxq --- addons/base_gantt/static/src/js/gantt.js | 22 +++++++--------------- 1 file changed, 7 insertions(+), 15 deletions(-) diff --git a/addons/base_gantt/static/src/js/gantt.js b/addons/base_gantt/static/src/js/gantt.js index b8ca9a49e9e..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.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(); }); } From c0e985352ccedbe8a9b69bf8691ebeaf6ac1ca7e Mon Sep 17 00:00:00 2001 From: Xavier Morel Date: Wed, 22 Jun 2011 13:00:26 +0200 Subject: [PATCH 03/12] [FIX] form's attachments loading so it goes through DataSetSearch instead of calling search_read directly bzr revid: xmo@openerp.com-20110622110026-s7t7kf7mhaamhc77 --- addons/base/static/src/js/form.js | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/addons/base/static/src/js/form.js b/addons/base/static/src/js/form.js index 90357325928..5f685c22e88 100644 --- a/addons/base/static/src/js/form.js +++ b/addons/base/static/src/js/form.js @@ -351,12 +351,13 @@ openerp.base.FormView = openerp.base.View.extend( /** @lends openerp.base.FormV if (!this.datarecord.id) { this.on_attachments_loaded([]); } else { - 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.context - }, this.on_attachments_loaded); + (new openerp.base.DataSetSearch( + this.session, 'ir.attachment', this.dataset.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) { From 1e0fc68cade83eeb59208072fea57f411588589d Mon Sep 17 00:00:00 2001 From: Xavier Morel Date: Wed, 22 Jun 2011 16:55:59 +0200 Subject: [PATCH 04/12] [IMP] rpc search_read and internal managment of DataSetSearch.read_slice to better handle pagination * /base/dataset/search_read now returns a dict of {ids, records} where ids are *not* paginated and is an ordered sequence of all the ids matching the unpaginated search criteria (domain, context, sort) records is the old toplevel result: an ordered, paginated sequence of the records read (with fields correctly selected and everything) * Removed context param to search_read since context comes from request object bzr revid: xmo@openerp.com-20110622145559-3r2jszyddvbsz3hq --- addons/base/controllers/main.py | 38 +++++++++++++++++++------------ addons/base/static/src/js/data.js | 11 ++++----- 2 files changed, 28 insertions(+), 21 deletions(-) diff --git a/addons/base/controllers/main.py b/addons/base/controllers/main.py index 3f519f30962..094f6c76492 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 @@ -352,9 +351,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 @@ -367,23 +367,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 read(self, request, model, ids, fields=False): return self.do_search_read(request, model, ids, fields, diff --git a/addons/base/static/src/js/data.js b/addons/base/static/src/js/data.js index 47e6d4fed3d..8162dca4e2c 100644 --- a/addons/base/static/src/js/data.js +++ b/addons/base/static/src/js/data.js @@ -404,14 +404,11 @@ 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; - self.count = records.length; // TODO: get real count - for (var i=0; i < records.length; i++ ) { - self.ids.push(records[i].id); - } - callback(records); + self.count = result.ids.length; + callback(result.records); }); }, /** From e8526ae9f19550675633e63b3a9c2fe3d6da20f4 Mon Sep 17 00:00:00 2001 From: Xavier Morel Date: Wed, 22 Jun 2011 17:36:26 +0200 Subject: [PATCH 05/12] [ADD] load lists paginated bzr revid: xmo@openerp.com-20110622153626-zscyr6natsmvb726 --- addons/base/static/src/js/list.js | 28 ++++++++++++++++++++++++---- 1 file changed, 24 insertions(+), 4 deletions(-) diff --git a/addons/base/static/src/js/list.js b/addons/base/static/src/js/list.js index 38162849e08..c52f7ddc3b7 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) { + 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. @@ -182,7 +200,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, @@ -505,6 +523,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; @@ -822,7 +841,7 @@ 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); _(self.columns).chain() .filter(function (column) {return !column.invisible;}) @@ -875,10 +894,11 @@ openerp.base.ListView.Groups = Class.extend( /** @lends openerp.base.ListView.Gr }); this.bind_child_events(list); - var d = new $.Deferred(); + var limit = this.view.limit(), + d = new $.Deferred(); dataset.read_slice( _.filter(_.pluck(this.columns, 'name'), _.identity), - 0, false, + this.view.page * limit, limit, function (records) { var form_records = _(records).map( $.proxy(list, 'transform_record')); From 3099c3022135d21c21ac9098e3d4220e314557cc Mon Sep 17 00:00:00 2001 From: Xavier Morel Date: Thu, 23 Jun 2011 12:50:02 +0200 Subject: [PATCH 06/12] [IMP] move and rename ListView.List.get_fields_view: its actual role is to transmogrify the listview's view descriptor so that we can use it in a form view, so it's only for editable lists, and it should reflect its actual task in its name. Also add some doc. bzr revid: xmo@openerp.com-20110623105002-vnznqit790p5gv48 --- addons/base/static/src/js/list-editable.js | 19 ++++++++++++++++++- addons/base/static/src/js/list.js | 12 ------------ 2 files changed, 18 insertions(+), 13 deletions(-) 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 c52f7ddc3b7..3ecda4d5354 100644 --- a/addons/base/static/src/js/list.js +++ b/addons/base/static/src/js/list.js @@ -574,18 +574,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. From d530b966c96f9e4dbf2f029a430c9b89de9f6fe6 Mon Sep 17 00:00:00 2001 From: Xavier Morel Date: Thu, 23 Jun 2011 12:54:04 +0200 Subject: [PATCH 07/12] [FIX] convert fixed ids in list view to classes (ids should either be generated or not used since each view can be instantiated several times in the same page) bzr revid: xmo@openerp.com-20110623105404-baomtfarfflb5kd7 --- addons/base/static/src/js/list.js | 6 +++--- addons/base/static/src/xml/base.xml | 4 ++-- addons/base/static/test/list.js | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/addons/base/static/src/js/list.js b/addons/base/static/src/js/list.js index 3ecda4d5354..41a76d7c66f 100644 --- a/addons/base/static/src/js/list.js +++ b/addons/base/static/src/js/list.js @@ -152,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) { @@ -364,7 +364,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) { diff --git a/addons/base/static/src/xml/base.xml b/addons/base/static/src/xml/base.xml index d059ae84864..8bb9e2f3e76 100644 --- a/addons/base/static/src/xml/base.xml +++ b/addons/base/static/src/xml/base.xml @@ -255,11 +255,11 @@ - - 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]); }); }); From 52a6119cb4c1bc0f4b18507f25385ba03d7c7d3e Mon Sep 17 00:00:00 2001 From: Xavier Morel Date: Thu, 23 Jun 2011 14:46:41 +0200 Subject: [PATCH 08/12] [ADD] list-level pagination on list views bzr revid: xmo@openerp.com-20110623124641-kzvkmx7fxe0xwn6w --- addons/base/static/src/js/list.js | 59 ++++++++++++++++++++++++++--- addons/base/static/src/xml/base.xml | 17 +++++---- 2 files changed, 63 insertions(+), 13 deletions(-) diff --git a/addons/base/static/src/js/list.js b/addons/base/static/src/js/list.js index 41a76d7c66f..e0bc3b5d340 100644 --- a/addons/base/static/src/js/list.js +++ b/addons/base/static/src/js/list.js @@ -167,8 +167,54 @@ 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(); + // TODO: pagination setup thingie + }); + 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 = ((total - first) < limit) ? total : first + limit; + this.$element.find('span.oe-pager-state').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 @@ -282,10 +328,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}); @@ -882,12 +926,15 @@ openerp.base.ListView.Groups = Class.extend( /** @lends openerp.base.ListView.Gr }); this.bind_child_events(list); - var limit = this.view.limit(), - d = new $.Deferred(); + var view = this.view, + limit = view.limit(), + d = new $.Deferred(); dataset.read_slice( _.filter(_.pluck(this.columns, 'name'), _.identity), - this.view.page * limit, limit, + view.page * limit, limit, function (records) { + view.configure_pager(dataset); + var form_records = _(records).map( $.proxy(list, 'transform_record')); diff --git a/addons/base/static/src/xml/base.xml b/addons/base/static/src/xml/base.xml index 8bb9e2f3e76..1b077fa8aff 100644 --- a/addons/base/static/src/xml/base.xml +++ b/addons/base/static/src/xml/base.xml @@ -268,16 +268,19 @@ - - + - 1 - to 1 - of 1 + + - - + + From 5f339ee830e75632a4585128a7d3445ab5eae190 Mon Sep 17 00:00:00 2001 From: Xavier Morel Date: Thu, 23 Jun 2011 15:49:20 +0200 Subject: [PATCH 09/12] [IMP] page size switcher for list views, fix handling and display of pagination in case of unlimited page size (limit = (0 | undefined | null)) bzr revid: xmo@openerp.com-20110623134920-i8rfy4ggcb7kt7h4 --- addons/base/static/src/css/base.css | 3 +++ addons/base/static/src/js/list.js | 29 +++++++++++++++++++++++++---- 2 files changed, 28 insertions(+), 4 deletions(-) diff --git a/addons/base/static/src/css/base.css b/addons/base/static/src/css/base.css index a921f320b3d..6f679d4b125 100644 --- a/addons/base/static/src/css/base.css +++ b/addons/base/static/src/css/base.css @@ -643,6 +643,9 @@ 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 tfoot td { padding: 3px 3px 0; diff --git a/addons/base/static/src/js/list.js b/addons/base/static/src/js/list.js index e0bc3b5d340..060e5221d69 100644 --- a/addons/base/static/src/js/list.js +++ b/addons/base/static/src/js/list.js @@ -70,7 +70,7 @@ openerp.base.ListView = openerp.base.View.extend( /** @lends openerp.base.ListVi * @returns {Number|null} */ limit: function () { - if (!this._limit) { + if (this._limit === undefined) { this._limit = (this.options.limit || this.defaults.limit || (this.view_manager.action || {}).limit @@ -186,7 +186,23 @@ openerp.base.ListView = openerp.base.View.extend( /** @lends openerp.base.ListVi }).find('.oe-pager-state') .click(function (e) { e.stopPropagation(); - // TODO: pagination setup thingie + var $this = $(this); + + var $select = $('