Xavier (Open ERP) has proposed merging 
lp:~openerp-dev/openerp-web/trunk-listview-pagination-xmo into lp:openerp-web.

Requested reviews:
  OpenERP R&D Web Team (openerp-dev-web)

For more details, see:
https://code.launchpad.net/~openerp-dev/openerp-web/trunk-listview-pagination-xmo/+merge/65791

Handling of pagination for list views:


* Pagination of list to some page size instead of fetching everything
* Page switcher with prev/next and first/last, displays current record range
* Ability to change page size from (action-provided) default
* Pagination within groups when grouped
  - only prev/next buttons
  - displays page count, instead of records range
-- 
https://code.launchpad.net/~openerp-dev/openerp-web/trunk-listview-pagination-xmo/+merge/65791
Your team OpenERP R&D Team is subscribed to branch 
lp:~openerp-dev/openerp-web/trunk-listview-pagination-xmo.
=== modified file 'addons/base/controllers/main.py'
--- addons/base/controllers/main.py	2011-06-23 15:59:46 +0000
+++ addons/base/controllers/main.py	2011-06-24 13:45:48 +0000
@@ -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 @@
                                                               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,22 +367,45 @@
         :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
+<<<<<<< TREE
             return map(lambda id: {'id': id}, ids)
 
         reads = Model.read(ids, fields or False, context)
         reads.sort(key=lambda obj: ids.index(obj['id']))
         return reads
+=======
+            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
+        }
+
+    @openerpweb.jsonrequest
+    def read(self, request, model, ids, fields=False):
+        return self.do_search_read(request, model, ids, fields,
+                                   request.session.eval_context(request.context))
+>>>>>>> MERGE-SOURCE
 
     @openerpweb.jsonrequest
     def get(self, request, model, ids, fields=False):

=== modified file 'addons/base/static/src/css/base.css'
--- addons/base/static/src/css/base.css	2011-06-22 14:57:57 +0000
+++ addons/base/static/src/css/base.css	2011-06-24 13:45:48 +0000
@@ -613,6 +613,9 @@
     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;
@@ -643,6 +646,17 @@
 .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;

=== modified file 'addons/base/static/src/js/data.js'
--- addons/base/static/src/js/data.js	2011-06-23 16:57:17 +0000
+++ addons/base/static/src/js/data.js	2011-06-24 13:45:48 +0000
@@ -406,14 +406,11 @@
             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);
         });
     },
     /**

=== modified file 'addons/base/static/src/js/form.js'
--- addons/base/static/src/js/form.js	2011-06-23 17:27:37 +0000
+++ addons/base/static/src/js/form.js	2011-06-24 13:45:48 +0000
@@ -377,6 +377,7 @@
         if (!this.datarecord.id) {
             this.on_attachments_loaded([]);
         } else {
+<<<<<<< TREE
             // 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',
@@ -384,6 +385,15 @@
                 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);
+>>>>>>> MERGE-SOURCE
         }
     },
     on_attachments_loaded: function(attachments) {

=== modified file 'addons/base/static/src/js/list-editable.js'
--- addons/base/static/src/js/list-editable.js	2011-06-21 15:57:05 +0000
+++ addons/base/static/src/js/list-editable.js	2011-06-24 13:45:48 +0000
@@ -116,6 +116,23 @@
             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 @@
                 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')

=== modified file 'addons/base/static/src/js/list.js'
--- addons/base/static/src/js/list.js	2011-06-23 10:42:22 +0000
+++ addons/base/static/src/js/list.js	2011-06-24 13:45:48 +0000
@@ -59,6 +59,24 @@
         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 @@
         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,9 +167,76 @@
             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 = $('<select>')
+                        .appendTo($this.empty())
+                        .click(function (e) {e.stopPropagation();})
+                        .append('<option value="80">80</option>' +
+                                '<option value="100">100</option>' +
+                                '<option value="200">200</option>' +
+                                '<option value="500">500</option>' +
+                                '<option value="NaN">Unlimited</option>')
+                        .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
      * visible.
@@ -182,7 +267,7 @@
             }
             return column;
         };
-        
+
         this.columns.splice(0, this.columns.length);
         this.columns.push.apply(
                 this.columns,
@@ -264,10 +349,8 @@
      */
     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 @@
      * @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 @@
     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 @@
         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 @@
         this.columns = view.columns;
         this.datagroup = null;
 
-        this.sections = [];
+        this.$row = null;
         this.children = {};
-    },
-    pad: function ($row) {
-        if (this.options.selectable) {
-            $row.append('<td>');
-        }
+
+        this.page = 0;
     },
     make_fragment: function () {
         return document.createDocumentFragment();
@@ -753,10 +822,35 @@
         }
         return red_letter_tboday;
     },
+    make_paginator: function () {
+        var self = this;
+        var $prev = $('<button type="button" data-pager-action="previous">&lt;</button>')
+            .click(function (e) {
+                e.stopPropagation();
+                self.page -= 1;
+
+                self.$row.closest('tbody').next()
+                    .replaceWith(self.render());
+            });
+        var $next = $('<button type="button" data-pager-action="next">&gt;</button>')
+            .click(function (e) {
+                e.stopPropagation();
+                self.page += 1;
+
+                self.$row.closest('tbody').next()
+                    .replaceWith(self.render());
+            });
+        this.$row.children().last()
+            .append($prev)
+            .append('<span class="oe-pager-state"></span>')
+            .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 @@
             self.bind_child_events(child);
             child.datagroup = group;
 
-            var $row = $('<tr>');
+            var $row = child.$row = $('<tr>');
             if (group.openable) {
                 $row.click(function (e) {
                     if (!$row.data('open')) {
@@ -808,7 +902,7 @@
             }
             placeholder.appendChild($row[0]);
 
-            var $group_column = $('<th>').appendTo($row);
+            var $group_column = $('<th class="oe-group-name">').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 @@
             self.indent($group_column, group.level);
             // count column
             $('<td>').text(group.length).appendTo($row);
-                    
-            self.pad($row);
+
+            if (self.options.selectable) {
+                $row.append('<td>');
+            }
             _(self.columns).chain()
                 .filter(function (column) {return !column.invisible;})
                 .each(function (column) {
@@ -844,6 +940,9 @@
                         $row.append('<td>');
                     }
                 });
+            if (self.options.deletable) {
+                $row.append('<td class="oe-group-pagination">');
+            }
         });
         return placeholder;
     },
@@ -867,6 +966,7 @@
     },
     render_dataset: function (dataset) {
         var rows = [],
+            self = this,
             list = new openerp.base.ListView.List(this, {
                 options: this.options,
                 columns: this.columns,
@@ -875,11 +975,35 @@
             });
         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(
+<<<<<<< TREE
             _.filter(_.pluck(_.select(this.columns, function(x) {return x.tag == "field";}), 'name'), _.identity),
             0, false,
+=======
+            _.filter(_.pluck(this.columns, 'name'), _.identity),
+            page * limit, limit,
+>>>>>>> MERGE-SOURCE
             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'));
 

=== modified file 'addons/base/static/src/xml/base.xml'
--- addons/base/static/src/xml/base.xml	2011-06-23 15:55:09 +0000
+++ addons/base/static/src/xml/base.xml	2011-06-24 13:45:48 +0000
@@ -255,11 +255,11 @@
             <th t-if="actions_span" t-att-colspan="actions_span"
                 class="oe-actions">
                 <t t-if="flags.action_buttons !== false">
-                    <button type="button" id="oe-list-add"
+                    <button type="button" class="oe-list-add"
                             t-if="options.addable">
                         <t t-esc="options.addable"/>
                     </button>
-                    <button type="button" id="oe-list-delete"
+                    <button type="button" class="oe-list-delete"
                             t-if="options.selectable and options.deletable">
                         Delete
                     </button>
@@ -268,16 +268,19 @@
             <th t-att-colspan="columns_count - actions_span"
                 class="oe-list-pager">
                 <t t-if="flags.pager !== false">
-                    <button type="button" data-pager-action="first">First</button>
-                    <button type="button" data-pager-action="previous"
+                    <button type="button" disabled="disabled"
+                            data-pager-action="first">First</button>
+                    <button type="button" disabled="disabled"
+                            data-pager-action="previous"
                             >&lt;&lt;</button>
 
-                    <span class="oe-pager-first">1</span>
-                    to <span class="oe-pager-last">1</span>
-                    of <span class="oe-pager-total">1</span>
+                    <span class="oe-pager-state">
+                    </span>
 
-                    <button type="button" data-pager-action="next">&gt;&gt;</button>
-                    <button type="button" data-pager-action="last">Last</button>
+                    <button type="button" disabled="disabled"
+                            data-pager-action="next">&gt;&gt;</button>
+                    <button type="button" disabled="disabled"
+                            data-pager-action="last">Last</button>
                 </t>
             </th>
         </tr>

=== modified file 'addons/base/static/test/list.js'
--- addons/base/static/test/list.js	2011-05-24 14:00:55 +0000
+++ addons/base/static/test/list.js	2011-06-24 13:45:48 +0000
@@ -149,7 +149,7 @@
         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]);
     });
 });

=== modified file 'addons/base_gantt/static/src/js/gantt.js'
--- addons/base_gantt/static/src/js/gantt.js	2011-05-20 06:16:50 +0000
+++ addons/base_gantt/static/src/js/gantt.js	2011-06-24 13:45:48 +0000
@@ -262,21 +262,13 @@
         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 @@
         }, function (results) {
             self.dataset.context = results.context;
             self.dataset.domain = results.domain;
-            return self.reload_gantt(self.dataset.domain);
+            self.reload_gantt();
         });
     }
 

=== modified file 'addons/web_mobile/static/src/js/web_mobile.js'
--- addons/web_mobile/static/src/js/web_mobile.js	2011-06-14 12:26:26 +0000
+++ addons/web_mobile/static/src/js/web_mobile.js	2011-06-24 13:45:48 +0000
@@ -90,11 +90,8 @@
     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}));
             });

_______________________________________________
Mailing list: https://launchpad.net/~openerp-dev-gtk
Post to     : [email protected]
Unsubscribe : https://launchpad.net/~openerp-dev-gtk
More help   : https://help.launchpad.net/ListHelp

Reply via email to