changeset 6252:e33b17db388d

helpers: move Page/RepoPage to a separate file page.py The Page and RepoPage classes are not used from templates and thus do not belong in helpers. They could have been put in utils.py, but because they are completely standalone we can easily split them to a separate file.
author Thomas De Schampheleire <thomas.de.schampheleire@gmail.com>
date Sat, 08 Oct 2016 22:59:50 +0200
parents d3930bd0c14a
children 48f8c73a94f2
files kallithea/controllers/admin/admin.py kallithea/controllers/admin/gists.py kallithea/controllers/admin/notifications.py kallithea/controllers/changelog.py kallithea/controllers/followers.py kallithea/controllers/forks.py kallithea/controllers/journal.py kallithea/controllers/pullrequests.py kallithea/controllers/search.py kallithea/lib/helpers.py kallithea/lib/page.py
diffstat 11 files changed, 267 insertions(+), 250 deletions(-) [+]
line wrap: on
line diff
--- a/kallithea/controllers/admin/admin.py	Thu Sep 22 21:06:44 2016 +0200
+++ b/kallithea/controllers/admin/admin.py	Sat Oct 08 22:59:50 2016 +0200
@@ -41,7 +41,7 @@
 from kallithea.lib.base import BaseController, render
 from kallithea.lib.utils2 import safe_int, remove_prefix, remove_suffix
 from kallithea.lib.indexers import JOURNAL_SCHEMA
-from kallithea.lib.helpers import Page
+from kallithea.lib.page import Page
 
 
 log = logging.getLogger(__name__)
--- a/kallithea/controllers/admin/gists.py	Thu Sep 22 21:06:44 2016 +0200
+++ b/kallithea/controllers/admin/gists.py	Sat Oct 08 22:59:50 2016 +0200
@@ -44,7 +44,7 @@
 from kallithea.lib.auth import LoginRequired, NotAnonymous
 from kallithea.lib.utils import jsonify
 from kallithea.lib.utils2 import safe_int, safe_unicode, time_to_datetime
-from kallithea.lib.helpers import Page
+from kallithea.lib.page import Page
 from sqlalchemy.sql.expression import or_
 from kallithea.lib.vcs.exceptions import VCSError, NodeNotChangedError
 
--- a/kallithea/controllers/admin/notifications.py	Thu Sep 22 21:06:44 2016 +0200
+++ b/kallithea/controllers/admin/notifications.py	Sat Oct 08 22:59:50 2016 +0200
@@ -38,7 +38,7 @@
 from kallithea.lib.auth import LoginRequired, NotAnonymous
 from kallithea.lib.base import BaseController, render
 from kallithea.lib import helpers as h
-from kallithea.lib.helpers import Page
+from kallithea.lib.page import Page
 from kallithea.lib.utils2 import safe_int
 
 
--- a/kallithea/controllers/changelog.py	Thu Sep 22 21:06:44 2016 +0200
+++ b/kallithea/controllers/changelog.py	Sat Oct 08 22:59:50 2016 +0200
@@ -36,9 +36,9 @@
 from kallithea.config.routing import url
 from kallithea.lib.auth import LoginRequired, HasRepoPermissionAnyDecorator
 from kallithea.lib.base import BaseRepoController, render
-from kallithea.lib.helpers import RepoPage
 from kallithea.lib.compat import json
 from kallithea.lib.graphmod import graph_data
+from kallithea.lib.page import RepoPage
 from kallithea.lib.vcs.exceptions import RepositoryError, ChangesetDoesNotExistError, \
     ChangesetError, NodeDoesNotExistError, EmptyRepositoryError
 from kallithea.lib.utils2 import safe_int, safe_str
--- a/kallithea/controllers/followers.py	Thu Sep 22 21:06:44 2016 +0200
+++ b/kallithea/controllers/followers.py	Sat Oct 08 22:59:50 2016 +0200
@@ -29,11 +29,11 @@
 
 from pylons import tmpl_context as c, request
 
-from kallithea.lib.helpers import Page
 from kallithea.lib.auth import LoginRequired, HasRepoPermissionAnyDecorator
 from kallithea.lib.base import BaseRepoController, render
+from kallithea.lib.page import Page
+from kallithea.lib.utils2 import safe_int
 from kallithea.model.db import UserFollowing
-from kallithea.lib.utils2 import safe_int
 
 log = logging.getLogger(__name__)
 
--- a/kallithea/controllers/forks.py	Thu Sep 22 21:06:44 2016 +0200
+++ b/kallithea/controllers/forks.py	Sat Oct 08 22:59:50 2016 +0200
@@ -37,15 +37,15 @@
 import kallithea.lib.helpers as h
 
 from kallithea.config.routing import url
-from kallithea.lib.helpers import Page
 from kallithea.lib.auth import LoginRequired, HasRepoPermissionAnyDecorator, \
     NotAnonymous, HasRepoPermissionAny, HasPermissionAnyDecorator, HasPermissionAny
 from kallithea.lib.base import BaseRepoController, render
+from kallithea.lib.page import Page
+from kallithea.lib.utils2 import safe_int
 from kallithea.model.db import Repository, UserFollowing, User, Ui
 from kallithea.model.repo import RepoModel
 from kallithea.model.forms import RepoForkForm
 from kallithea.model.scm import ScmModel, AvailableRepoGroupChoices
-from kallithea.lib.utils2 import safe_int
 
 log = logging.getLogger(__name__)
 
--- a/kallithea/controllers/journal.py	Thu Sep 22 21:06:44 2016 +0200
+++ b/kallithea/controllers/journal.py	Sat Oct 08 22:59:50 2016 +0200
@@ -46,11 +46,11 @@
 from kallithea.model.meta import Session
 from kallithea.model.repo import RepoModel
 import kallithea.lib.helpers as h
-from kallithea.lib.helpers import Page
 from kallithea.lib.auth import LoginRequired, NotAnonymous
 from kallithea.lib.base import BaseController, render
+from kallithea.lib.compat import json
+from kallithea.lib.page import Page
 from kallithea.lib.utils2 import safe_int, AttributeDict
-from kallithea.lib.compat import json
 
 log = logging.getLogger(__name__)
 
--- a/kallithea/controllers/pullrequests.py	Thu Sep 22 21:06:44 2016 +0200
+++ b/kallithea/controllers/pullrequests.py	Sat Oct 08 22:59:50 2016 +0200
@@ -35,19 +35,19 @@
 from webob.exc import HTTPFound, HTTPNotFound, HTTPForbidden, HTTPBadRequest
 
 from kallithea.config.routing import url
-from kallithea.lib.vcs.utils.hgcompat import unionrepo
-from kallithea.lib.compat import json, OrderedDict
-from kallithea.lib.base import BaseRepoController, render
+from kallithea.lib import helpers as h
+from kallithea.lib import diffs
 from kallithea.lib.auth import LoginRequired, HasRepoPermissionAnyDecorator, \
     NotAnonymous
-from kallithea.lib.helpers import Page
-from kallithea.lib import helpers as h
-from kallithea.lib import diffs
+from kallithea.lib.base import BaseRepoController, render
+from kallithea.lib.compat import json, OrderedDict
+from kallithea.lib.diffs import LimitedDiffContainer
 from kallithea.lib.exceptions import UserInvalidException
+from kallithea.lib.page import Page
 from kallithea.lib.utils import action_logger, jsonify
+from kallithea.lib.vcs.exceptions import EmptyRepositoryError, ChangesetDoesNotExistError
 from kallithea.lib.vcs.utils import safe_str
-from kallithea.lib.vcs.exceptions import EmptyRepositoryError, ChangesetDoesNotExistError
-from kallithea.lib.diffs import LimitedDiffContainer
+from kallithea.lib.vcs.utils.hgcompat import unionrepo
 from kallithea.model.db import PullRequest, ChangesetStatus, ChangesetComment, \
     PullRequestReviewers, User
 from kallithea.model.pull_request import PullRequestModel
--- a/kallithea/controllers/search.py	Thu Sep 22 21:06:44 2016 +0200
+++ b/kallithea/controllers/search.py	Sat Oct 08 22:59:50 2016 +0200
@@ -40,9 +40,9 @@
 from kallithea.lib.base import BaseRepoController, render
 from kallithea.lib.indexers import CHGSETS_SCHEMA, SCHEMA, CHGSET_IDX_NAME, \
     IDX_NAME, WhooshResultWrapper
-from kallithea.model.repo import RepoModel
+from kallithea.lib.page import Page
 from kallithea.lib.utils2 import safe_str, safe_int
-from kallithea.lib.helpers import Page
+from kallithea.model.repo import RepoModel
 
 log = logging.getLogger(__name__)
 
--- a/kallithea/lib/helpers.py	Thu Sep 22 21:06:44 2016 +0200
+++ b/kallithea/lib/helpers.py	Sat Oct 08 22:59:50 2016 +0200
@@ -19,7 +19,6 @@
 """
 import hashlib
 import StringIO
-import math
 import logging
 import re
 import urlparse
@@ -47,7 +46,6 @@
     convert_misc_entities, lchop, plural, rchop, remove_formatting, \
     replace_whitespace, urlify, truncate, wrap_paragraphs
 from webhelpers.date import time_ago_in_words
-from webhelpers.paginate import Page as _Page
 from webhelpers.html.tags import _set_input_attrs, _set_id_attr, \
     convert_boolean_attrs, NotGiven, _make_safe_id_component
 
@@ -885,234 +883,6 @@
                .replace('{size}', safe_str(size))
     return url
 
-class Page(_Page):
-    """
-    Custom pager to match rendering style with YUI paginator
-    """
-
-    def __init__(self, *args, **kwargs):
-        kwargs.setdefault('url', url.current)
-        _Page.__init__(self, *args, **kwargs)
-
-    def _get_pos(self, cur_page, max_page, items):
-        edge = (items / 2) + 1
-        if (cur_page <= edge):
-            radius = max(items / 2, items - cur_page)
-        elif (max_page - cur_page) < edge:
-            radius = (items - 1) - (max_page - cur_page)
-        else:
-            radius = items / 2
-
-        left = max(1, (cur_page - (radius)))
-        right = min(max_page, cur_page + (radius))
-        return left, cur_page, right
-
-    def _range(self, regexp_match):
-        """
-        Return range of linked pages (e.g. '1 2 [3] 4 5 6 7 8').
-
-        Arguments:
-
-        regexp_match
-            A "re" (regular expressions) match object containing the
-            radius of linked pages around the current page in
-            regexp_match.group(1) as a string
-
-        This function is supposed to be called as a callable in
-        re.sub.
-
-        """
-        radius = int(regexp_match.group(1))
-
-        # Compute the first and last page number within the radius
-        # e.g. '1 .. 5 6 [7] 8 9 .. 12'
-        # -> leftmost_page  = 5
-        # -> rightmost_page = 9
-        leftmost_page, _cur, rightmost_page = self._get_pos(self.page,
-                                                            self.last_page,
-                                                            (radius * 2) + 1)
-        nav_items = []
-
-        # Create a link to the first page (unless we are on the first page
-        # or there would be no need to insert '..' spacers)
-        if self.page != self.first_page and self.first_page < leftmost_page:
-            nav_items.append(self._pagerlink(self.first_page, self.first_page))
-
-        # Insert dots if there are pages between the first page
-        # and the currently displayed page range
-        if leftmost_page - self.first_page > 1:
-            # Wrap in a SPAN tag if nolink_attr is set
-            text_ = '..'
-            if self.dotdot_attr:
-                text_ = HTML.span(c=text_, **self.dotdot_attr)
-            nav_items.append(text_)
-
-        for thispage in xrange(leftmost_page, rightmost_page + 1):
-            # Highlight the current page number and do not use a link
-            text_ = str(thispage)
-            if thispage == self.page:
-                # Wrap in a SPAN tag if nolink_attr is set
-                if self.curpage_attr:
-                    text_ = HTML.span(c=text_, **self.curpage_attr)
-                nav_items.append(text_)
-            # Otherwise create just a link to that page
-            else:
-                nav_items.append(self._pagerlink(thispage, text_))
-
-        # Insert dots if there are pages between the displayed
-        # page numbers and the end of the page range
-        if self.last_page - rightmost_page > 1:
-            text_ = '..'
-            # Wrap in a SPAN tag if nolink_attr is set
-            if self.dotdot_attr:
-                text_ = HTML.span(c=text_, **self.dotdot_attr)
-            nav_items.append(text_)
-
-        # Create a link to the very last page (unless we are on the last
-        # page or there would be no need to insert '..' spacers)
-        if self.page != self.last_page and rightmost_page < self.last_page:
-            nav_items.append(self._pagerlink(self.last_page, self.last_page))
-
-        #_page_link = url.current()
-        #nav_items.append(literal('<link rel="prerender" href="%s?page=%s">' % (_page_link, str(int(self.page)+1))))
-        #nav_items.append(literal('<link rel="prefetch" href="%s?page=%s">' % (_page_link, str(int(self.page)+1))))
-        return self.separator.join(nav_items)
-
-    def pager(self, format='~2~', page_param='page', partial_param='partial',
-        show_if_single_page=False, separator=' ', onclick=None,
-        symbol_first='<<', symbol_last='>>',
-        symbol_previous='<', symbol_next='>',
-        link_attr=None,
-        curpage_attr=None,
-        dotdot_attr=None, **kwargs):
-        self.curpage_attr = curpage_attr or {'class': 'pager_curpage'}
-        self.separator = separator
-        self.pager_kwargs = kwargs
-        self.page_param = page_param
-        self.partial_param = partial_param
-        self.onclick = onclick
-        self.link_attr = link_attr or {'class': 'pager_link', 'rel': 'prerender'}
-        self.dotdot_attr = dotdot_attr or {'class': 'pager_dotdot'}
-
-        # Don't show navigator if there is no more than one page
-        if self.page_count == 0 or (self.page_count == 1 and not show_if_single_page):
-            return ''
-
-        from string import Template
-        # Replace ~...~ in token format by range of pages
-        result = re.sub(r'~(\d+)~', self._range, format)
-
-        # Interpolate '%' variables
-        result = Template(result).safe_substitute({
-            'first_page': self.first_page,
-            'last_page': self.last_page,
-            'page': self.page,
-            'page_count': self.page_count,
-            'items_per_page': self.items_per_page,
-            'first_item': self.first_item,
-            'last_item': self.last_item,
-            'item_count': self.item_count,
-            'link_first': self.page > self.first_page and \
-                    self._pagerlink(self.first_page, symbol_first) or '',
-            'link_last': self.page < self.last_page and \
-                    self._pagerlink(self.last_page, symbol_last) or '',
-            'link_previous': self.previous_page and \
-                    self._pagerlink(self.previous_page, symbol_previous) \
-                    or HTML.span(symbol_previous, class_="yui-pg-previous"),
-            'link_next': self.next_page and \
-                    self._pagerlink(self.next_page, symbol_next) \
-                    or HTML.span(symbol_next, class_="yui-pg-next")
-        })
-
-        return literal(result)
-
-
-#==============================================================================
-# REPO PAGER, PAGER FOR REPOSITORY
-#==============================================================================
-class RepoPage(Page):
-
-    def __init__(self, collection, page=1, items_per_page=20,
-                 item_count=None, **kwargs):
-
-        """Create a "RepoPage" instance. special pager for paging
-        repository
-        """
-        # TODO: call baseclass __init__
-        self._url_generator = kwargs.pop('url', url.current)
-
-        # Safe the kwargs class-wide so they can be used in the pager() method
-        self.kwargs = kwargs
-
-        # Save a reference to the collection
-        self.original_collection = collection
-
-        self.collection = collection
-
-        # The self.page is the number of the current page.
-        # The first page has the number 1!
-        try:
-            self.page = int(page)  # make it int() if we get it as a string
-        except (ValueError, TypeError):
-            self.page = 1
-
-        self.items_per_page = items_per_page
-
-        # Unless the user tells us how many items the collections has
-        # we calculate that ourselves.
-        if item_count is not None:
-            self.item_count = item_count
-        else:
-            self.item_count = len(self.collection)
-
-        # Compute the number of the first and last available page
-        if self.item_count > 0:
-            self.first_page = 1
-            self.page_count = int(math.ceil(float(self.item_count) /
-                                            self.items_per_page))
-            self.last_page = self.first_page + self.page_count - 1
-
-            # Make sure that the requested page number is the range of
-            # valid pages
-            if self.page > self.last_page:
-                self.page = self.last_page
-            elif self.page < self.first_page:
-                self.page = self.first_page
-
-            # Note: the number of items on this page can be less than
-            #       items_per_page if the last page is not full
-            self.first_item = max(0, (self.item_count) - (self.page *
-                                                          items_per_page))
-            self.last_item = ((self.item_count - 1) - items_per_page *
-                              (self.page - 1))
-
-            self.items = list(self.collection[self.first_item:self.last_item + 1])
-
-            # Links to previous and next page
-            if self.page > self.first_page:
-                self.previous_page = self.page - 1
-            else:
-                self.previous_page = None
-
-            if self.page < self.last_page:
-                self.next_page = self.page + 1
-            else:
-                self.next_page = None
-
-        # No items available
-        else:
-            self.first_page = None
-            self.page_count = 0
-            self.last_page = None
-            self.first_item = None
-            self.last_item = None
-            self.previous_page = None
-            self.next_page = None
-            self.items = []
-
-        # This is a subclass of the 'list' type. Initialise the list now.
-        list.__init__(self, reversed(self.items))
-
 
 def changed_tooltip(nodes):
     """
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/kallithea/lib/page.py	Sat Oct 08 22:59:50 2016 +0200
@@ -0,0 +1,247 @@
+# -*- coding: utf-8 -*-
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+"""
+Custom paging classes
+"""
+
+import math
+import re
+from kallithea.config.routing import url
+from webhelpers.html import literal, HTML
+from webhelpers.paginate import Page as _Page
+
+class Page(_Page):
+    """
+    Custom pager to match rendering style with YUI paginator
+    """
+
+    def __init__(self, *args, **kwargs):
+        kwargs.setdefault('url', url.current)
+        _Page.__init__(self, *args, **kwargs)
+
+    def _get_pos(self, cur_page, max_page, items):
+        edge = (items / 2) + 1
+        if (cur_page <= edge):
+            radius = max(items / 2, items - cur_page)
+        elif (max_page - cur_page) < edge:
+            radius = (items - 1) - (max_page - cur_page)
+        else:
+            radius = items / 2
+
+        left = max(1, (cur_page - (radius)))
+        right = min(max_page, cur_page + (radius))
+        return left, cur_page, right
+
+    def _range(self, regexp_match):
+        """
+        Return range of linked pages (e.g. '1 2 [3] 4 5 6 7 8').
+
+        Arguments:
+
+        regexp_match
+            A "re" (regular expressions) match object containing the
+            radius of linked pages around the current page in
+            regexp_match.group(1) as a string
+
+        This function is supposed to be called as a callable in
+        re.sub.
+
+        """
+        radius = int(regexp_match.group(1))
+
+        # Compute the first and last page number within the radius
+        # e.g. '1 .. 5 6 [7] 8 9 .. 12'
+        # -> leftmost_page  = 5
+        # -> rightmost_page = 9
+        leftmost_page, _cur, rightmost_page = self._get_pos(self.page,
+                                                            self.last_page,
+                                                            (radius * 2) + 1)
+        nav_items = []
+
+        # Create a link to the first page (unless we are on the first page
+        # or there would be no need to insert '..' spacers)
+        if self.page != self.first_page and self.first_page < leftmost_page:
+            nav_items.append(self._pagerlink(self.first_page, self.first_page))
+
+        # Insert dots if there are pages between the first page
+        # and the currently displayed page range
+        if leftmost_page - self.first_page > 1:
+            # Wrap in a SPAN tag if nolink_attr is set
+            text_ = '..'
+            if self.dotdot_attr:
+                text_ = HTML.span(c=text_, **self.dotdot_attr)
+            nav_items.append(text_)
+
+        for thispage in xrange(leftmost_page, rightmost_page + 1):
+            # Highlight the current page number and do not use a link
+            text_ = str(thispage)
+            if thispage == self.page:
+                # Wrap in a SPAN tag if nolink_attr is set
+                if self.curpage_attr:
+                    text_ = HTML.span(c=text_, **self.curpage_attr)
+                nav_items.append(text_)
+            # Otherwise create just a link to that page
+            else:
+                nav_items.append(self._pagerlink(thispage, text_))
+
+        # Insert dots if there are pages between the displayed
+        # page numbers and the end of the page range
+        if self.last_page - rightmost_page > 1:
+            text_ = '..'
+            # Wrap in a SPAN tag if nolink_attr is set
+            if self.dotdot_attr:
+                text_ = HTML.span(c=text_, **self.dotdot_attr)
+            nav_items.append(text_)
+
+        # Create a link to the very last page (unless we are on the last
+        # page or there would be no need to insert '..' spacers)
+        if self.page != self.last_page and rightmost_page < self.last_page:
+            nav_items.append(self._pagerlink(self.last_page, self.last_page))
+
+        #_page_link = url.current()
+        #nav_items.append(literal('<link rel="prerender" href="%s?page=%s">' % (_page_link, str(int(self.page)+1))))
+        #nav_items.append(literal('<link rel="prefetch" href="%s?page=%s">' % (_page_link, str(int(self.page)+1))))
+        return self.separator.join(nav_items)
+
+    def pager(self, format='~2~', page_param='page', partial_param='partial',
+        show_if_single_page=False, separator=' ', onclick=None,
+        symbol_first='<<', symbol_last='>>',
+        symbol_previous='<', symbol_next='>',
+        link_attr=None,
+        curpage_attr=None,
+        dotdot_attr=None, **kwargs):
+        self.curpage_attr = curpage_attr or {'class': 'pager_curpage'}
+        self.separator = separator
+        self.pager_kwargs = kwargs
+        self.page_param = page_param
+        self.partial_param = partial_param
+        self.onclick = onclick
+        self.link_attr = link_attr or {'class': 'pager_link', 'rel': 'prerender'}
+        self.dotdot_attr = dotdot_attr or {'class': 'pager_dotdot'}
+
+        # Don't show navigator if there is no more than one page
+        if self.page_count == 0 or (self.page_count == 1 and not show_if_single_page):
+            return ''
+
+        from string import Template
+        # Replace ~...~ in token format by range of pages
+        result = re.sub(r'~(\d+)~', self._range, format)
+
+        # Interpolate '%' variables
+        result = Template(result).safe_substitute({
+            'first_page': self.first_page,
+            'last_page': self.last_page,
+            'page': self.page,
+            'page_count': self.page_count,
+            'items_per_page': self.items_per_page,
+            'first_item': self.first_item,
+            'last_item': self.last_item,
+            'item_count': self.item_count,
+            'link_first': self.page > self.first_page and \
+                    self._pagerlink(self.first_page, symbol_first) or '',
+            'link_last': self.page < self.last_page and \
+                    self._pagerlink(self.last_page, symbol_last) or '',
+            'link_previous': self.previous_page and \
+                    self._pagerlink(self.previous_page, symbol_previous) \
+                    or HTML.span(symbol_previous, class_="yui-pg-previous"),
+            'link_next': self.next_page and \
+                    self._pagerlink(self.next_page, symbol_next) \
+                    or HTML.span(symbol_next, class_="yui-pg-next")
+        })
+
+        return literal(result)
+
+
+class RepoPage(Page):
+
+    def __init__(self, collection, page=1, items_per_page=20,
+                 item_count=None, **kwargs):
+
+        """Create a "RepoPage" instance. special pager for paging
+        repository
+        """
+        # TODO: call baseclass __init__
+        self._url_generator = kwargs.pop('url', url.current)
+
+        # Safe the kwargs class-wide so they can be used in the pager() method
+        self.kwargs = kwargs
+
+        # Save a reference to the collection
+        self.original_collection = collection
+
+        self.collection = collection
+
+        # The self.page is the number of the current page.
+        # The first page has the number 1!
+        try:
+            self.page = int(page)  # make it int() if we get it as a string
+        except (ValueError, TypeError):
+            self.page = 1
+
+        self.items_per_page = items_per_page
+
+        # Unless the user tells us how many items the collections has
+        # we calculate that ourselves.
+        if item_count is not None:
+            self.item_count = item_count
+        else:
+            self.item_count = len(self.collection)
+
+        # Compute the number of the first and last available page
+        if self.item_count > 0:
+            self.first_page = 1
+            self.page_count = int(math.ceil(float(self.item_count) /
+                                            self.items_per_page))
+            self.last_page = self.first_page + self.page_count - 1
+
+            # Make sure that the requested page number is the range of
+            # valid pages
+            if self.page > self.last_page:
+                self.page = self.last_page
+            elif self.page < self.first_page:
+                self.page = self.first_page
+
+            # Note: the number of items on this page can be less than
+            #       items_per_page if the last page is not full
+            self.first_item = max(0, (self.item_count) - (self.page *
+                                                          items_per_page))
+            self.last_item = ((self.item_count - 1) - items_per_page *
+                              (self.page - 1))
+
+            self.items = list(self.collection[self.first_item:self.last_item + 1])
+
+            # Links to previous and next page
+            if self.page > self.first_page:
+                self.previous_page = self.page - 1
+            else:
+                self.previous_page = None
+
+            if self.page < self.last_page:
+                self.next_page = self.page + 1
+            else:
+                self.next_page = None
+
+        # No items available
+        else:
+            self.first_page = None
+            self.page_count = 0
+            self.last_page = None
+            self.first_item = None
+            self.last_item = None
+            self.previous_page = None
+            self.next_page = None
+            self.items = []
+
+        # This is a subclass of the 'list' type. Initialise the list now.
+        list.__init__(self, reversed(self.items))