Commits

Mike Verdone  committed 78b8b6a

Add show_previous_and_next_all_the_time parameter to Page.pager method.

  • Participants
  • Parent commits acfb178

Comments (0)

Files changed (2)

File tests/test_paginate.py

 from webhelpers.util import update_params
 
 
+def url_generator(**kw):
+    return update_params("/content", **kw)
+
+
+
 def test_empty_list():
     """Test whether an empty list is handled correctly."""
     items = []
     assert page.pager() == ''
     assert page.pager(show_if_single_page=True) == ''
 
+
+def test_show_previous_and_next_all_the_time():
+    """
+    Verify that symbol_previous and symbol_next appear as non-links
+    when show_previous_and_next_all_the_time is True.
+    """
+    items = range(20)
+    sym_prev = "previous_symbol"
+    sym_next = "next_symbol"
+    page = paginate.Page(items, page=0, items_per_page=5, url=url_generator)
+    pager = page.pager(
+        format="$link_previous $link_next",
+        show_previous_and_next_all_the_time=True,
+        symbol_previous=sym_prev,
+        symbol_next=sym_next)
+    assert sym_prev in pager
+    assert sym_next in pager
+
+
 def test_one_page():
     """Test that we fit 10 items on a single 10-item page."""
     items = range(10)
     assert page.pager() == ''
     assert page.pager(show_if_single_page=True) == '<span class="pager_curpage">1</span>'
 
-def url_generator(**kw):
-    return update_params("/content", **kw)
-
 def test_many_pages():
     """Test that 100 items fit on seven 15-item pages."""
     items = range(100)
 class UnsliceableSequence(object):
    def __init__(self, seq):
       self.l = seq
-   
+
    def __iter__(self):
        for i in self.l:
            yield i
         insert = notes.insert()
         records = [{"id": x} for x in range(1, 101)]
         engine.execute(insert, records)
-            
+
     def tearDown(self):
         import sqlalchemy as sa
         import sqlalchemy.orm as orm

File webhelpers/paginate.py

 What is pagination?
 ---------------------
 
-This module helps dividing large lists of items into pages. The user 
-is shown one page at a time and can navigate to other pages. Imagine you 
-are offering a company phonebook and let the user search the entries. If 
-the search result contains 23 entries but you may want to display no 
-more than 10 entries at once. The first page contains entries 1-10, the 
-second 11-20 and the third 21-23. See the documentation of the "Page" 
-class for more information. 
+This module helps dividing large lists of items into pages. The user
+is shown one page at a time and can navigate to other pages. Imagine you
+are offering a company phonebook and let the user search the entries. If
+the search result contains 23 entries but you may want to display no
+more than 10 entries at once. The first page contains entries 1-10, the
+second 11-20 and the third 21-23. See the documentation of the "Page"
+class for more information.
 
 How do I use it?
 ------------------
 
 If the page number is in the URL path, you'll have to use a framework-specific
 URL generator. For instance, in Pyramid if the current route is
-"/articles/{id}/page/{page}" and the current URL is 
+"/articles/{id}/page/{page}" and the current URL is
 "/articles/ABC/page/3?print=1", you can use Pyramid's "current_route_url"
 function as follows::
 
 This overrides the 'page' path variable, while leaving the 'id' variable and
 the query string intact.
 
-The callback API is simple. 
+The callback API is simple.
 
 1. It must accept an integer argument 'page', which will be passed by name.
 
-2. It should return the URL for that page.  
+2. It should return the URL for that page.
 
 3. If you're using AJAX 'partial' functionality described in the ``Page.pager``
    docstring, the callback should also accept a 'partial' argument and, if
     """A list/iterator of items representing one page in a larger
     collection.
 
-    An instance of the "Page" class is created from a collection of things. 
-    The instance works as an iterator running from the first item to the 
+    An instance of the "Page" class is created from a collection of things.
+    The instance works as an iterator running from the first item to the
     last item on the given page. The collection can be:
 
     - a sequence
     - an SQLAlchemy query - e.g.: Session.query(MyModel)
     - an SQLAlchemy select - e.g.: sqlalchemy.select([my_table])
 
-    A "Page" instance maintains pagination logic associated with each 
-    page, where it begins, what the first/last item on the page is, etc. 
+    A "Page" instance maintains pagination logic associated with each
+    page, where it begins, what the first/last item on the page is, etc.
     The pager() method creates a link list allowing the user to go to
     other pages.
 
-    **WARNING:** Unless you pass in an item_count, a count will be 
-    performed on the collection every time a Page instance is created. 
-    If using an ORM, it's advised to pass in the number of items in the 
+    **WARNING:** Unless you pass in an item_count, a count will be
+    performed on the collection every time a Page instance is created.
+    If using an ORM, it's advised to pass in the number of items in the
     collection if that number is known.
 
     Instance attributes:
 
     last_item
         Index of last item on the current page
-        
+
     """
     def __init__(self, collection, page=1, items_per_page=20,
         item_count=None, sqlalchemy_session=None, presliced_list=False,
             If this parameter is not given then the paginator will count
             the number of elements in the collection every time a "Page"
             is created. Giving this parameter will speed up things.
-        
+
         presliced_list (optional)
             Indicates whether the collection, when a list, has already
             been sliced for the current viewing page, and thus should
         symbol_first='<<', symbol_last='>>',
         symbol_previous='<', symbol_next='>',
         link_attr={'class':'pager_link'}, curpage_attr={'class':'pager_curpage'},
-        dotdot_attr={'class':'pager_dotdot'}, **kwargs):
+        dotdot_attr={'class':'pager_dotdot'},
+        show_previous_and_next_all_the_time=False, **kwargs):
         """
         Return string with links to other pages (e.g. "1 2 [3] 4 5 6 7").
 
             - $link_previous: link to previous page (unless this is first page)
             - $link_next: link to next page (unless this is last page)
 
-            To render a range of pages the token '~3~' can be used. The 
+            To render a range of pages the token '~3~' can be used. The
             number sets the radius of pages around the current page.
             Example for a range with radius 3:
 
             Default: '~2~'
 
         symbol_first
-            String to be displayed as the text for the %(link_first)s 
+            String to be displayed as the text for the %(link_first)s
             link above.
 
             Default: '<<'
 
         symbol_last
-            String to be displayed as the text for the %(link_last)s 
+            String to be displayed as the text for the %(link_last)s
             link above.
 
             Default: '>>'
 
         symbol_previous
-            String to be displayed as the text for the %(link_previous)s 
+            String to be displayed as the text for the %(link_previous)s
             link above.
 
             Default: '<'
 
         symbol_next
-            String to be displayed as the text for the %(link_next)s 
+            String to be displayed as the text for the %(link_next)s
             link above.
 
             Default: '>'
 
         separator:
-            String that is used to separate page links/numbers in the 
+            String that is used to separate page links/numbers in the
             above range of pages.
 
             Default: ' '
 
         page_param:
-            The name of the parameter that will carry the number of the 
-            page the user just clicked on. The parameter will be passed 
-            to a url_for() call so if you stay with the default 
-            ':controller/:action/:id' routing and set page_param='id' then 
-            the :id part of the URL will be changed. If you set 
-            page_param='page' then url_for() will make it an extra 
-            parameters like ':controller/:action/:id?page=1'. 
-            You need the page_param in your action to determine the page 
-            number the user wants to see. If you do not specify anything 
+            The name of the parameter that will carry the number of the
+            page the user just clicked on. The parameter will be passed
+            to a url_for() call so if you stay with the default
+            ':controller/:action/:id' routing and set page_param='id' then
+            the :id part of the URL will be changed. If you set
+            page_param='page' then url_for() will make it an extra
+            parameters like ':controller/:action/:id?page=1'.
+            You need the page_param in your action to determine the page
+            number the user wants to see. If you do not specify anything
             else the default will be a parameter called 'page'.
 
             Note: If you set this argument and are using a URL generator
             of 'partial'.
 
         show_if_single_page:
-            if True the navigator will be shown even if there is only 
+            if True the navigator will be shown even if there is only
             one page
-            
+
             Default: False
 
+        show_previous_and_next_all_the_time:
+            if True the navigator previous and next symbols will show
+            even if there is no previous or next page. The symbols
+            will not be links in this case.
+
         link_attr (optional)
-            A dictionary of attributes that get added to A-HREF links 
-            pointing to other pages. Can be used to define a CSS style 
+            A dictionary of attributes that get added to A-HREF links
+            pointing to other pages. Can be used to define a CSS style
             or class to customize the look of links.
 
             Example: { 'style':'border: 1px solid green' }
             Default: { 'class':'pager_link' }
 
         curpage_attr (optional)
-            A dictionary of attributes that get added to the current 
+            A dictionary of attributes that get added to the current
             page number in the pager (which is obviously not a link).
             If this dictionary is not empty then the elements
             will be wrapped in a SPAN tag with the given attributes.
 
         dotdot_attr (optional)
             A dictionary of attributes that get added to the '..' string
-            in the pager (which is obviously not a link). If this 
-            dictionary is not empty then the elements will be wrapped in 
+            in the pager (which is obviously not a link). If this
+            dictionary is not empty then the elements will be wrapped in
             a SPAN tag with the given attributes.
 
             Example: { 'style':'color: #808080' }
         onclick (optional)
             This paramter is a string containing optional Javascript code
             that will be used as the 'onclick' action of each pager link.
-            It can be used to enhance your pager with AJAX actions loading another 
-            page into a DOM object. 
+            It can be used to enhance your pager with AJAX actions loading another
+            page into a DOM object.
 
             In this string the variable '$partial_url' will be replaced by
             the URL linking to the desired page with an added 'partial=1'
             In addition the '$page' variable gets replaced by the
             respective page number.
 
-            Note that the URL to the destination page contains a 'partial_param' 
-            parameter so that you can distinguish between AJAX requests (just 
-            refreshing the paginated area of your page) and full requests (loading 
+            Note that the URL to the destination page contains a 'partial_param'
+            parameter so that you can distinguish between AJAX requests (just
+            refreshing the paginated area of your page) and full requests (loading
             the whole new page).
 
             [Backward compatibility: you can use '%s' instead of '$partial_url']
 
             ExtJS example:
                 "Ext.get('#my-page-area').load({url:'$partial_url'}); return false;"
-            
+
             Custom example:
                 "my_load_page($page)"
 
         Additional keyword arguments are used as arguments in the links.
-        Otherwise the link will be created with url_for() which points 
+        Otherwise the link will be created with url_for() which points
         to the page you are currently displaying.
         """
         self.curpage_attr = curpage_attr
         self.onclick = onclick
         self.link_attr = link_attr
         self.dotdot_attr = dotdot_attr
+        self.show_previous_and_next_all_the_time = show_previous_and_next_all_the_time
 
         # 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):
         # Replace ~...~ in token format by range of pages
         result = re.sub(r'~(\d+)~', self._range, format)
 
+        if self.previous_page:
+            link_previous = self._pagerlink(self.previous_page, symbol_previous)
+        elif self.show_previous_and_next_all_the_time:
+            link_previous = symbol_previous
+        else:
+            link_previous = ''
+
+        if self.next_page:
+            link_next = self._pagerlink(self.next_page, symbol_next)
+        elif self.show_previous_and_next_all_the_time:
+            link_next = symbol_next
+        else:
+            link_next = ''
+
         # Interpolate '%' variables
         result = Template(result).safe_substitute({
             'first_page': self.first_page,
                     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 '',
-            'link_next': self.next_page and \
-                    self._pagerlink(self.next_page, symbol_next) or ''
+            'link_previous': link_previous,
+            'link_next': link_next,
         })
 
         return literal(result)
         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 
+        This function is supposed to be called as a callable in
         re.sub.
-        
+
         """
         radius = int(regexp_match.group(1))
 
         Create a URL that links to another page using url_for().
 
         Parameters:
-            
+
         page
             Number of the page that the link points to
 
         params.sort()
     qs = urllib.urlencode(params, True)
     return "%s?%s" % (path, qs)
-    
+
 class PageURL(object):
     """A simple page URL generator for any framework."""
 
 
 class PageURL_WebOb(object):
     """A page URL generator for WebOb-compatible Request objects.
-    
+
     I derive new URLs based on the current URL but overriding the 'page'
     query parameter.
 
     framework whose Request object has 'application_url', 'path', and 'GET'
     attributes that behave the same way as ``webob.Request``'s.
     """
-    
+
     def __init__(self, request, qualified=False):
         """
         ``request`` is a WebOb-compatible ``Request`` object.
         path and query string. If true, the "scheme://host" prefix will be
         included. The default is false to match traditional usage, and to avoid
         generating unuseable URLs behind reverse proxies (e.g., Apache's
-        mod_proxy). 
+        mod_proxy).
         """
         self.request = request
         self.qualified = qualified