Merge branch 'master' of git://github.com/callowayproject/django-pagination
authorZygmunt Krynicki <zygmunt.krynicki@linaro.org>
Tue, 14 Jun 2011 09:21:36 +0000 (11:21 +0200)
committerZygmunt Krynicki <zygmunt.krynicki@linaro.org>
Tue, 14 Jun 2011 09:21:36 +0000 (11:21 +0200)
Conflicts:
pagination/templatetags/pagination_tags.py
setup.py

1  2 
linaro_django_pagination/templates/pagination/pagination.html
linaro_django_pagination/templatetags/pagination_tags.py
setup.py

index fd925f0,0000000..1bd9ab5
mode 100644,000000..100644
--- /dev/null
@@@ -1,1 -1,0 +1,38 @@@
- {% include pagination_template %}
++{% if is_paginated %}
++{% load i18n %}
++<div class="pagination">
++  {% block previouslink %}
++  {% if page_obj.has_previous %}
++  <a href="?page{{ page_suffix }}={{ page_obj.previous_page_number }}{{ getvars }}" class="prev">{{ previous_link_decorator|safe }}{% trans "previous" %}</a>
++  {% else %}
++  {% if display_disabled_previous_link %}
++  <span class="disabled prev">{{ previous_link_decorator|safe }}{% trans "previous" %}</span>
++  {% endif %}
++  {% endif %}
++  {% endblock previouslink %}
++  {% block pagelinks %}
++  {% if display_page_links %}
++  {% for page in pages %}
++  {% if page %}
++  {% ifequal page page_obj.number %}
++  <span class="current page">{{ page }}</span>
++  {% else %}
++  <a href="?page{{ page_suffix }}={{ page }}{{ getvars }}" class="page">{{ page }}</a>
++  {% endifequal %}
++  {% else %}
++  ...
++  {% endif %}
++  {% endfor %}
++  {% endif %}
++  {% endblock pagelinks %}
++  {% block nextlink %}
++  {% if page_obj.has_next %}
++  <a href="?page{{ page_suffix }}={{ page_obj.next_page_number }}{{ getvars }}" class="next">{% trans "next" %}{{ next_link_decorator|safe }}</a>
++  {% else %}
++  {% if display_disabled_next_link %}
++  <span class="disabled next">{% trans "next" %}{{ next_link_decorator|safe }}</span>
++  {% endif %}
++  {% endif %}
++  {% endblock nextlink %}
++</div>
++{% endif %}
index 1da25f4,0000000..a78c790
mode 100644,000000..100644
--- /dev/null
@@@ -1,296 -1,0 +1,331 @@@
- from django import template
 +# Copyright (c) 2008, Eric Florenzano
 +# Copyright (c) 2010, 2011 Linaro Limited
 +# All rights reserved.
 +# 
 +# Redistribution and use in source and binary forms, with or without
 +# modification, are permitted provided that the following conditions are
 +# met:
 +# 
 +#     * Redistributions of source code must retain the above copyright
 +#       notice, this list of conditions and the following disclaimer.
 +#     * Redistributions in binary form must reproduce the above
 +#       copyright notice, this list of conditions and the following
 +#       disclaimer in the documentation and/or other materials provided
 +#       with the distribution.
 +#     * Neither the name of the author nor the names of other
 +#       contributors may be used to endorse or promote products derived
 +#       from this software without specific prior written permission.
 +# 
 +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 +
 +
- from django.template import TOKEN_BLOCK
 +from django.conf import settings
 +from django.core.exceptions import ImproperlyConfigured
 +from django.core.paginator import Paginator, InvalidPage
 +from django.http import Http404
-         raise template.TemplateSyntaxError(
++from django.template import (
++    Context,
++    Library,
++    Node,
++    TOKEN_BLOCK,
++    TemplateSyntaxError,
++    Variable,
++)
++from django.template.loader import select_template
 +
 +# TODO, import this normally later on
 +from linaro_django_pagination.settings import *
 +
 +
 +def do_autopaginate(parser, token):
 +    """
 +    Splits the arguments to the autopaginate tag and formats them correctly.
 +
 +    Syntax is:
 +
 +        autopaginate QUERYSET [PAGINATE_BY] [ORPHANS] [as NAME]
 +    """
 +    # Check whether there are any other autopaginations are later in this template
 +    expr = lambda obj: (obj.token_type == TOKEN_BLOCK and \
 +        len(obj.split_contents()) > 0 and obj.split_contents()[0] == "autopaginate")
 +    multiple_paginations = len(filter(expr, parser.tokens)) > 0
 +
 +    i = iter(token.split_contents())
 +    paginate_by = None
 +    queryset_var = None
 +    context_var = None
 +    orphans = None
 +    word = None
 +    try:
 +        word = i.next()
 +        assert word == "autopaginate"
 +        queryset_var = i.next()
 +        word = i.next()
 +        if word != "as":
 +            paginate_by = word
 +            try:
 +                paginate_by = int(paginate_by)
 +            except ValueError:
 +                pass
 +            word = i.next()
 +        if word != "as":
 +            orphans = word
 +            try:
 +                orphans = int(orphans)
 +            except ValueError:
 +                pass
 +            word = i.next()
 +        assert word == "as"
 +        context_var = i.next()
 +    except StopIteration:
 +        pass
 +    if queryset_var is None:
- class AutoPaginateNode(template.Node):
++        raise TemplateSyntaxError(
 +            "Invalid syntax. Proper usage of this tag is: "
 +            "{%% autopaginate QUERYSET [PAGINATE_BY] [ORPHANS]"
 +            " [as CONTEXT_VAR_NAME] %%}"
 +        )
 +    return AutoPaginateNode(queryset_var, multiple_paginations, paginate_by, orphans, context_var)
 +
 +
-         self.queryset_var = template.Variable(queryset_var)
++class AutoPaginateNode(Node):
 +    """
 +    Emits the required objects to allow for Digg-style pagination.
 +    
 +    First, it looks in the current context for the variable specified, and using
 +    that object, it emits a simple ``Paginator`` and the current page object 
 +    into the context names ``paginator`` and ``page_obj``, respectively.
 +    
 +    It will then replace the variable specified with only the objects for the
 +    current page.
 +    
 +    .. note::
 +        
 +        It is recommended to use *{% paginate %}* after using the autopaginate
 +        tag.  If you choose not to use *{% paginate %}*, make sure to display the
 +        list of available pages, or else the application may seem to be buggy.
 +    """
 +    def __init__(self, queryset_var,  multiple_paginations, paginate_by=None,
 +                 orphans=None, context_var=None):
 +        if paginate_by is None:
 +            paginate_by = DEFAULT_PAGINATION
 +        if orphans is None:
 +            orphans = DEFAULT_ORPHANS
-             self.paginate_by = template.Variable(paginate_by)
++        self.queryset_var = Variable(queryset_var)
 +        if isinstance(paginate_by, int):
 +            self.paginate_by = paginate_by
 +        else:
-             self.orphans = template.Variable(orphans)
++            self.paginate_by = Variable(paginate_by)
 +        if isinstance(orphans, int):
 +            self.orphans = orphans
 +        else:
-     
-     ``pagination_template``
-         A custom template to include in place of the default ``pagination/default.html`` 
-         contents.
++            self.orphans = Variable(orphans)
 +        self.context_var = context_var
 +        self.multiple_paginations = multiple_paginations
 +
 +    def render(self, context):
 +        if self.multiple_paginations or "paginator" in context:
 +            page_suffix = '_%s' % self.queryset_var
 +        else:
 +            page_suffix = ''
 +        
 +        key = self.queryset_var.var
 +        value = self.queryset_var.resolve(context)
 +        if isinstance(self.paginate_by, int):
 +            paginate_by = self.paginate_by
 +        else:
 +            paginate_by = self.paginate_by.resolve(context)
 +        if isinstance(self.orphans, int):
 +            orphans = self.orphans
 +        else:
 +            orphans = self.orphans.resolve(context)
 +        paginator = Paginator(value, paginate_by, orphans)
 +        try:
 +            request = context['request']
 +        except KeyError:
 +            raise ImproperlyConfigured(
 +                "You need to enable 'django.core.context_processors.request'."
 +                " See linaro-django-pagination/README file for TEMPLATE_CONTEXT_PROCESSORS details")
 +        try:
 +            page_obj = paginator.page(request.page(page_suffix))
 +        except InvalidPage:
 +            if INVALID_PAGE_RAISES_404:
 +                raise Http404('Invalid page requested.  If DEBUG were set to ' +
 +                    'False, an HTTP 404 page would have been shown instead.')
 +            context[key] = []
 +            context['invalid_page'] = True
 +            return u''
 +        if self.context_var is not None:
 +            context[self.context_var] = page_obj.object_list
 +        else:
 +            context[key] = page_obj.object_list
 +        context['paginator'] = paginator
 +        context['page_obj'] = page_obj
 +        context['page_suffix'] = page_suffix
 +        return u''
 +
 +
++class PaginateNode(Node):
++
++    def __init__(self, template=None):
++        self.template = template
++        
++    def render(self, context):
++        template_list = ['pagination/pagination.html']
++        to_return = paginate(context)
++        if self.template:
++            template_list.insert(0, self.template)
++        t = select_template(template_list)
++        if not t: 
++            return None
++        context = Context(to_return)
++        return t.render(context)
++        
++
++def do_paginate(parser, token):
++    """
++    {% paginate [using] [template] %}
++    
++    {% paginate %}
++    {% paginate using paginations/custom_pagination.html %}
++    """
++    argv = token.contents.split()
++    argc = len(argv)
++    if argc > 3:
++        raise TemplateSyntaxError("Tag %s takes at most 2 argument." % argv[0])
++    if argc == 1:
++        return PaginateNode()
++    if argc == 3 and argv[1] == 'using':
++        return PaginateNode(template=argv[2])
++    raise TemplateSyntaxError("Tag %s is invalid. Please check the syntax" % argv[0])
++
++
 +def paginate(context, window=DEFAULT_WINDOW, margin=DEFAULT_MARGIN):
 +    """
 +    Renders the ``pagination/pagination.html`` template, resulting in a
 +    Digg-like display of the available pages, given the current page.  If there
 +    are too many pages to be displayed before and after the current page, then
 +    elipses will be used to indicate the undisplayed gap between page numbers.
 +    
 +    Requires one argument, ``context``, which should be a dictionary-like data
 +    structure and must contain the following keys:
 +    
 +    ``paginator``
 +        A ``Paginator`` or ``QuerySetPaginator`` object.
 +    
 +    ``page_obj``
 +        This should be the result of calling the page method on the 
 +        aforementioned ``Paginator`` or ``QuerySetPaginator`` object, given
 +        the current page.
 +    
 +    This same ``context`` dictionary-like data structure may also include:
 +    
 +    ``getvars``
 +        A dictionary of all of the **GET** parameters in the current request.
 +        This is useful to maintain certain types of state, even when requesting
 +        a different page.
-         pagination_template = context.get('pagination_template', 'pagination/default.html')
 +        
 +    Argument ``window`` is number to pages before/after current page. If window
 +    exceeds pagination border (1 and end), window is moved to left or right.
 +
 +    Argument ``margin``` is number of pages on start/end of pagination. 
 +    Example:
 +        window=2, margin=1, current=6     1 ... 4 5 [6] 7 8 ... 11 
 +        window=2, margin=0, current=1     [1] 2 3 4 5 ...
 +        window=2, margin=0, current=5     ... 3 4 [5] 6 7 ...
 +        window=2, margin=0, current=11     ... 7 8 9 10 [11]
 +        """
 +
 +    if window < 0:
 +        raise ValueError('Parameter "window" cannot be less than zero')
 +    if margin < 0:
 +        raise ValueError('Parameter "margin" cannot be less than zero')
 +    try:
 +        paginator = context['paginator']
 +        page_obj = context['page_obj']
 +        page_suffix = context.get('page_suffix', '')
 +        page_range = paginator.page_range
-             'pagination_template': pagination_template,
 +        # Calculate the record range in the current page for display.
 +        records = {'first': 1 + (page_obj.number - 1) * paginator.per_page}
 +        records['last'] = records['first'] + paginator.per_page - 1
 +        if records['last'] + paginator.orphans >= paginator.count:
 +            records['last'] = paginator.count
 +
 +        # figure window
 +        window_start = page_obj.number - window - 1
 +        window_end = page_obj.number + window
 +
 +        # solve if window exceeded page range
 +        if window_start < 0:
 +            window_end = window_end - window_start
 +            window_start = 0
 +        if window_end > paginator.num_pages:
 +            window_start = window_start - (window_end - paginator.num_pages)
 +            window_end = paginator.num_pages
 +        pages = page_range[window_start:window_end]
 +
 +        # figure margin and add elipses
 +        if margin > 0:
 +            # figure margin
 +            tmp_pages = set(pages)
 +            tmp_pages = tmp_pages.union(page_range[:margin])
 +            tmp_pages = tmp_pages.union(page_range[-margin:])
 +            tmp_pages = list(tmp_pages)
 +            tmp_pages.sort()
 +            pages = []
 +            pages.append(tmp_pages[0])
 +            for i in range(1, len(tmp_pages)):
 +                # figure gap size => add elipses or fill in gap
 +                gap = tmp_pages[i] - tmp_pages[i - 1]
 +                if gap >= 3:
 +                    pages.append(None)
 +                elif gap == 2:
 +                    pages.append(tmp_pages[i] - 1)
 +                pages.append(tmp_pages[i])
 +        else:
 +            if pages[0] != 1:
 +                pages.insert(0, None)
 +            if pages[-1] != paginator.num_pages:
 +                pages.append(None)
 +
 +        to_return = {
 +            'MEDIA_URL': settings.MEDIA_URL,
 +            'STATIC_URL': getattr(settings, "STATIC_URL", None),
 +            'display_disabled_next_link': DISPLAY_DISABLED_NEXT_LINK,
 +            'display_disabled_previous_link': DISPLAY_DISABLED_PREVIOUS_LINK,
 +            'display_page_links': DISPLAY_PAGE_LINKS,
 +            'is_paginated': paginator.count > paginator.per_page,
 +            'next_link_decorator': NEXT_LINK_DECORATOR,
 +            'page_obj': page_obj,
 +            'page_suffix': page_suffix,
 +            'pages': pages,
- register = template.Library()
- register.inclusion_tag(
-     'pagination/pagination.html', takes_context=True)(paginate)
 +            'paginator': paginator,
 +            'previous_link_decorator': PREVIOUS_LINK_DECORATOR,
 +            'records': records,
 +        }
 +        if 'request' in context:
 +            getvars = context['request'].GET.copy()
 +            if 'page%s' % page_suffix in getvars:
 +                del getvars['page%s' % page_suffix]
 +            if len(getvars.keys()) > 0:
 +                to_return['getvars'] = "&%s" % getvars.urlencode()
 +            else:
 +                to_return['getvars'] = ''
 +        return to_return
 +    except (KeyError, AttributeError):
 +        return {}
 +
 +
++register = Library()
++register.tag('paginate', do_paginate)
 +register.tag('autopaginate', do_autopaginate)
diff --cc setup.py
index c653024,7a4de48..2c23e80
mode 100755,100644..100755
+++ b/setup.py
 -from setuptools import setup, find_packages
 -
 -version = '1.1.0'
 -
 -LONG_DESCRIPTION = """
 -How to use django-pagination
 -----------------------------
 -
 -``django-pagination`` allows for easy Digg-style pagination without modifying
 -your views.
 -
 -There are really 5 steps to setting it up with your projects (not including 
 -installation, which is covered in INSTALL.txt in this same directory.)
 -
 -1. List this application in the ``INSTALLED_APPS`` portion of your settings
 -   file.  Your settings file might look something like::
 -   
 -       INSTALLED_APPS = (
 -           # ...
 -           'pagination',
 -       )
 -
 -
 -2. Install the pagination middleware.  Your settings file might look something
 -   like::
 -   
 -       MIDDLEWARE_CLASSES = (
 -           # ...
 -           'pagination.middleware.PaginationMiddleware',
 -       )
 -
 -3. If it's not already added in your setup, add the request context processor.
 -   Note that context processors are set by default implicitly, so to set them
 -   explicitly, you need to copy and paste this code into your under
 -   the value TEMPLATE_CONTEXT_PROCESSORS::
 -   
 -        ("django.core.context_processors.auth",
 -        "django.core.context_processors.debug",
 -        "django.core.context_processors.i18n",
 -        "django.core.context_processors.media",
 -        "django.core.context_processors.request")
 -
 -4. Add this line at the top of your template to load the pagination tags:
 -
 -       {% load pagination_tags %}
 -
 -
 -5. Decide on a variable that you would like to paginate, and use the
 -   autopaginate tag on that variable before iterating over it.  This could 
 -   take one of two forms (using the canonical ``object_list`` as an example
 -   variable):
 -   
 -       {% autopaginate object_list %}
 -       
 -   This assumes that you would like to have the default 20 results per page.
 -   If you would like to specify your own amount of results per page, you can
 -   specify that like so:
 -   
 -       {% autopaginate object_list 10 %}
 -   
 -   Note that this replaces ``object_list`` with the list for the current page, so
 -   you can iterate over the ``object_list`` like you normally would.
 -   
 -
 -6. Now you want to display the current page and the available pages, so
 -   somewhere after having used autopaginate, use the paginate inclusion tag:
 -   
 -       {% paginate %}
 -   
 -   This does not take any arguments, but does assume that you have already
 -   called autopaginate, so make sure to do so first.
 -
 +#!/usr/bin/env python
 +# Copyright (c) 2008, Eric Florenzano
 +# Copyright (c) 2010, 2011 Linaro Limited
 +# All rights reserved.
 +# 
 +# Redistribution and use in source and binary forms, with or without
 +# modification, are permitted provided that the following conditions are
 +# met:
 +# 
 +#     * Redistributions of source code must retain the above copyright
 +#       notice, this list of conditions and the following disclaimer.
 +#     * Redistributions in binary form must reproduce the above
 +#       copyright notice, this list of conditions and the following
 +#       disclaimer in the documentation and/or other materials provided
 +#       with the distribution.
 +#     * Neither the name of the author nor the names of other
 +#       contributors may be used to endorse or promote products derived
 +#       from this software without specific prior written permission.
 +# 
 +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  
 -That's it!  You have now paginated ``object_list`` and given users of the site
 -a way to navigate between the different pages--all without touching your views.
 -
 -
 -Optional Settings
 -------------------
 -
 -In django-pagination, there are no required settings.  There are, however, a
 -small set of optional settings useful for changing the default behavior of the
 -pagination tags.  Here's an overview:
--
 -``PAGINATION_DEFAULT_PAGINATION``
 -    The default amount of items to show on a page if no number is specified.
 +from setuptools import setup, find_packages
  
 -``PAGINATION_DEFAULT_WINDOW``
 -    The number of items to the left and to the right of the current page to
 -    display (accounting for ellipses).
  
 -``PAGINATION_DEFAULT_ORPHANS``
 -    The number of orphans allowed.  According to the Django documentation,
 -    orphans are defined as::
 -    
 -        The minimum number of items allowed on the last page, defaults to zero.
 +version = "1.1"
  
 -``PAGINATION_INVALID_PAGE_RAISES_404``
 -    Determines whether an invalid page raises an ``Http404`` or just sets the
 -    ``invalid_page`` context variable.  ``True`` does the former and ``False``
 -    does the latter.
 -"""
  
  setup(
 -    name='django-pagination',
 +    name='linaro-django-pagination',
      version=version,
 -    description="django-pagination",
 -    long_description=LONG_DESCRIPTION,
 -    classifiers=[
 -        "Programming Language :: Python",
 -        "Topic :: Software Development :: Libraries :: Python Modules",
 -        "Framework :: Django",
 -        "Environment :: Web Environment",
 -    ],
 +    author='Zygmunt Krynicki',
 +    author_email='zygmunt.krynicki@linaro.org',
 +    description="linaro-django-pagination",
 +    long_description=open("README").read(),
      keywords='pagination,django',
 -    author='Eric Florenzano',
 -    author_email='floguy@gmail.com',
 -    url='http://django-pagination.googlecode.com/',
 +    url='https://github.com/zyga/django-pagination',
 +    test_suite='linaro_django_pagination.test_project.tests.run_tests',
      license='BSD',
      packages=find_packages(),
 +    classifiers=[
 +        "Development Status :: 4 - Beta",
 +        "Environment :: Web Environment",
 +        "Framework :: Django",
 +        "Intended Audience :: Developers",
 +        "License :: OSI Approved :: BSD License",
 +        "Operating System :: OS Independent",
 +        "Programming Language :: Python :: 2.6",
 +        "Programming Language :: Python :: 2.7",
 +    ],
 +    install_requires=[
 +        'django >= 1.2',
 +    ],
 +    tests_require=[
 +        'django-testproject >= 0.1',
 +    ],
      include_package_data=True,
 -    zip_safe=False,
  )