Commits

Karol Fuksiewicz  committed e2f1d4d

Patch for pagination by Alex_Zorg

  • Participants
  • Parent commits 316e44e

Comments (0)

Files changed (2)

File pagination.patch

+# HG changeset patch
+# Parent 94a043d3fea4c7d4b3a2504a64876ea0dca7444f
+diff -r 94a043d3fea4 -r f8ae73c56d1b djangobb/djangobb_forum/pagination.py
+--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
++++ b/djangobb/djangobb_forum/pagination.py	Mon Feb 07 19:57:24 2011 +0100
+@@ -0,0 +1,319 @@
++# -*- coding: UTF-8 -*-
++#
++# Pretty Paginator for usage in Django projects
++# Version: 0.1
++# File: "pagination.py"
++# Copyright (C) 2011, Alex Zorg <zlexzorg (at) mail dot ru>
++# Licensed by BSD License
++# Last update: 2011.02.06
++
++#------------------------------------------------------------------------------
++#  get pretty paginator ranges
++#  return 3 ranges of indexes [1...num_pages] and 2 dots flags
++#  (head_range, dots1, locality_range, dots2, tail_range)
++def pagination_ranges(
++    num_pages,       # sumary page numbers
++    page,            # current page number [1...num_pages] or <=0 if none
++    reverse=False,   # reverse enumeration
++    show_all=False,  # show all pages
++    head=1,          # pages in head (>=0)
++    locality=5,      # locality of current page (>=0)
++    tail=1,          # pages in tail (>=0)
++    stick=1):        # stick interval (>=0)
++    """
++    get pretty paginator ranges
++    return 3 ranges of indexes [1...num_pages] and 2 dots flags
++    (head_range, dots1, locality_range, dots2, tail_range)
++
++    Example #1 (num_pages=20, page=10, reverse=False):
++        1 2 3 ... 8 9 10 11 12 ... 17 18 19 20
++        ^^^^^  |  ^^^^^^^^^^^^  |  ^^^^^^^^^^^
++        head=3  |  locality=5    |  tail=4
++        dots1=True       dots2=True
++
++    Example #2 (num_pages=10, page=0 or locality=0, reverse=True):
++        10 9 8 7 ... 3 2 1
++        ^^^^^^^^  |  ^^^^^
++        head=4    |  tail=3
++            dots1=True
++            dots2=False
++
++    Example #3 (num_pages=10, reverse=False):
++        1 2 3 4 5 6 7 8 9 10
++        ^^^^^^^^^^^^^^^^^^^^
++        head=10
++        dots1=dots2=False, locality=tail=0
++    """
++    if not stick or stick < 0:
++        stick = 0
++
++    if head + tail + stick >= num_pages or show_all:
++        # `head` cross `tail`, show all pages
++        head = num_pages
++        locality = start = finish = tail = 0
++    else:
++        if reverse:
++            (head, tail) = (tail, head)
++
++        # transform (`page`, `locality`) to (`start`, `finish`)
++        if page >= 1 and page <= num_pages and locality > 0:
++            if not reverse:
++                start  = page - (locality - 1) / 2
++                finish = page + locality / 2
++            else:
++                start  = page - locality / 2
++                finish = page + (locality - 1) / 2
++            if start < 1:
++                start = 1
++            if finish > num_pages:
++                finish = num_pages
++            locality = finish + 1 - start
++        else:
++            locality = start = finish = 0
++
++        # check head and locality overlay
++        if head >= finish:
++            # `head` cover `locality` => avoid locality
++            locality = start = finish = 0
++        elif head + stick + 1 >= start:
++            # `head` cross `locality` => combine them to `head`
++            head = finish
++            locality = start = finish = 0
++
++        # check locality and tail overlay
++        if head + tail + stick >= num_pages:
++            # `head` cross `tail` => show all pages as `head`
++            head = num_pages
++            locality = start = finish = tail = 0
++        elif start > num_pages - tail:
++            # `tail` cover `locality` => avoid locality
++            locality = start = finish = 0
++        elif finish + stick >= num_pages - tail:
++            # `tail` cross `locality` => combine them to `tail`
++            tail = num_pages - start + 1
++            locality = start = finish = 0
++
++        if head + tail + stick >= num_pages:
++            # `head` cross `tail` => show all pages as `head`
++            head = num_pages
++            locality = start = finish = tail = 0
++
++    if not reverse:
++        head_range     = range(1, head + 1)
++        locality_range = range(start, start + locality)
++        tail_range     = range(num_pages + 1 - tail, num_pages + 1)
++    else:
++        head_range     = range(num_pages, num_pages - tail, -1)
++        locality_range = range(finish, finish - locality, -1)
++        tail_range     = range(head, 0, -1)
++
++    dots1 = bool(head_range and (locality_range or tail_range))
++    dots2 = bool(locality_range and tail_range)
++    return head_range, dots1, locality_range, dots2, tail_range
++
++#------------------------------------------------------------------------------
++# return string of A tags with links to pages and correct redirection path
++def pagination_urls(
++    request_GET,    # HTTP request GET QueryDict
++    template_name,  # template name to render A tags
++    num_pages,      # sumary page numbers
++    page=0,         # current page number [1...num_pages] or <=0 if none
++    page_size=0,    # page size (items per page) or 0 for default
++    base_url='',    # base URL like '/foo/' or 'http://qqq.com/' or ''
++    reverse=False,  # reverse enumeration
++    show_all=False, # show all pages
++    head=1,         # pages in head (>=0)
++    locality=5,     # locality of current page (>=0)
++    tail=1,         # pages in tail (>=0)
++    stick=1):       # stick interval (>=0)
++    """
++    return string of A tags with links to pages and correct redirection path
++    """
++
++    # create pagination indexes
++    head_range, dots1, locality_range, dots2, tail_range = pagination_ranges(
++        num_pages, # sumary page numbers
++        page,      # current page number [1...num_pages] or <=0 if none
++        reverse,   # reverse enumeration
++        show_all,  # show all pages
++        head,      # pages in head (>=0)
++        locality,  # locality of current page (>=0)
++        tail,      # pages in tail (>=0)
++        stick)     # stick interval (>=0)
++
++    # check first/last and previous/next page
++    first_page = last_page = previous_page = next_page = None
++    if num_pages > 1:
++        if page > 1 and page < num_pages:
++            first_page    = 1
++            last_page     = num_pages
++            previous_page = page - 1
++            next_page     = page + 1
++        elif page == 1:
++            last_page = num_pages
++            next_page = 2
++        elif page == num_pages:
++            first_page    = 1
++            previous_page = num_pages - 1
++
++    # check revers page enumeration
++    if reverse:
++        (previous_page, next_page) = (next_page, previous_page)
++        (first_page, last_page)    = (last_page, first_page)
++
++    # exclude from GET `page` and `page_size` variable
++    url_params = base_url + '?'
++    params = ['%s=%s' % (x[0], x[1]) for x in request_GET.iteritems()
++             if (x[0] != 'page' and x[0] != 'page_size')]
++    if params:
++        url_params += '&'.join(params) + '&'
++
++    # create correct redirection path to current page
++    redirect_to = base_url + '?'
++    if num_pages > 1 and page > 0 and page <= num_pages:
++        params.append('page=%i' % page)
++    if page_size:
++        params.append('page_size=%s' % str(page_size))
++    if params:
++        redirect_to += '&'.join(params)
++
++    from django.template.loader import render_to_string
++    urls = render_to_string(
++        template_name,
++        {
++        'num_pages':      num_pages,
++        'page':           page,
++        'page_size':      page_size,
++        'first_page':     first_page,
++        'last_page':      last_page,
++        'previous_page':  previous_page,
++        'next_page':      next_page,
++        'head_range':     head_range,
++        'dots1':          dots1,
++        'locality_range': locality_range,
++        'dots2':          dots2,
++        'tail_range':     tail_range,
++        'url_params':     url_params,
++        'redirect_to':    redirect_to # for debug
++        })
++
++    return urls, redirect_to
++
++#------------------------------------------------------------------------------
++# paginate object list and render string of A tags with links to pages
++def pagination(
++    request_GET,           # HTTP request GET QueryDict
++    object_list,           # object list to paginate
++    template_name,         # template name to render A tags
++    default_page_size,     # default page size (items per page)
++    page_size_limit=1000,  # limit of items per page
++    base_url='',           # base URL like '/foo/' or 'http://qqq.com/' or ''
++    reverse=False,         # reverse enumeration
++    show_all=False,        # show all pages
++    head=1,                # pages in head (>=0)
++    locality=5,            # locality of current page (>=0)
++    tail=1,                # pages in tail (>=0)
++    stick=1):              # stick interval (>=0)
++    """
++    paginate object list and render string of A tags with links to pages
++    return:
++        (object_list, # croped object_list
++        urls,        # string of A tags with links to pages {{ pagination }}
++        redirect,    # True if request_GET has out of range values
++        redirect_to) # correct redirection path to current page
++    """
++    redirect = False
++
++    # get page size
++    get_page_size = request_GET.get('page_size', '')
++    if get_page_size in ('all', 'max', 'full', 'large', 'big'):
++        # show all items in one or more large pages
++        page_size = page_size_limit
++    else:
++        try:
++            page_size = int(get_page_size)
++            if page_size < 1:
++                get_page_size = ''
++                page_size = default_page_size
++                redirect = True
++            elif page_size > page_size_limit:
++                get_page_size = page_size = page_size_limit
++                redirect = True
++        except ValueError:
++            # page_size in GET request is not integer value
++            if get_page_size:
++                get_page_size = ''
++                redirect = True
++            page_size = default_page_size
++
++    # create Django Paginator object
++    from django.core.paginator import Paginator, EmptyPage, InvalidPage
++    paginator = Paginator(object_list, page_size)
++    num_pages = paginator.num_pages
++
++    # default page number if value from GET request out of range
++    default_page = 1
++    if reverse:
++        default_page = num_pages
++
++    # get current page number
++    try:
++        page = int(request_GET.get('page', default_page))
++        if page < 1 or page > num_pages:
++            page = default_page
++            redirect = True
++    except ValueError:
++        page = default_page
++        redirect = True
++
++    # check reverse flag
++    if not reverse:
++        paginator_page = page
++    else:
++        paginator_page = num_pages - page + 1
++
++    # select objects of page
++    try:
++        object_list = paginator.page(paginator_page).object_list
++    except (InvalidPage, EmptyPage):
++        from django.http import Http404
++        raise Http404
++
++    # create list of A tags
++    urls, redirect_to = pagination_urls(
++        request_GET,    # HTTP request GET QueryDict
++        template_name,  # template name to render A tags
++        num_pages,      # sumary page numbers
++        page,           # current page number [1...num_pages] or <=0 if none
++        get_page_size,  # page size (items per page) or 0 if none
++        base_url,       # base URL like '/foo/' or 'http://qqq.com/' or ''
++        reverse,        # reverse enumeration
++        show_all,       # show all pages
++        head,           # pages in head (>=0)
++        locality,       # locality of current page (>=0)
++        tail,           # pages in tail (>=0)
++        stick)          # stick interval (>=0)
++
++    return object_list, urls, redirect, redirect_to
++
++#------------------------------------------------------------------------------
++# simple example of enumeration
++if __name__ == '__main__':
++    head, dots1, locality, dots2, tail = pagination_ranges(
++        20,  # sumary page numbers
++        10,  # current page number [1...num_pages] or <=0 if none
++        0,   # reverse enumeration
++        0,   # show all pages
++        1,   # pages in head (>=0)
++        7,   # locality of current page (>=0)
++        1,   # pages in tail (>=0)
++        2)   # stick interval (>=0)
++
++    # very simple debug show
++    if head:     print head,
++    if dots1:    print "...",
++    if locality: print locality,
++    if dots2:    print "...",
++    if tail:     print tail
++
++#*** end of "pagination.py" file ***#
+diff -r 94a043d3fea4 -r f8ae73c56d1b djangobb/djangobb_forum/settings.py
+--- a/djangobb/djangobb_forum/settings.py	Sun Feb 06 22:45:22 2011 +0200
++++ b/djangobb/djangobb_forum/settings.py	Mon Feb 07 19:57:24 2011 +0100
+@@ -4,6 +4,16 @@
+ def get(key, default):
+     return getattr(settings, key, default)
+ 
++# default pagination settings
++PAGINATION_TEMPLATE = "forum/pagination.html" # default template
++PAGINATION_LIMIT    = 1000  # limit of items per page
++PAGINATION_REVERSE  = False # reverse enumeration
++PAGINATION_SHOW_ALL = False # show all pages
++PAGINATION_HEAD     = 1     # pages in head (>=0)
++PAGINATION_LOCALITY = 7     # locality of current page (>=0)
++PAGINATION_TAIL     = 1     # pages in tail (>=0)
++PAGINATION_STICK    = 1     # stick interval (>=0)
++
+ # FORUM Settings
+ FORUM_BASE_TITLE = get('DJANGOBB_FORUM_BASE_TITLE', 'Django Bulletin Board')
+ FORUM_META_DESCRIPTION = get('DJANGOBB_FORUM_META_DESCRIPTION', '')
+@@ -86,4 +96,4 @@
+           (r':rolleyes:', EMOTION_ROLL),
+           (r':cool:', EMOTION_COOL)
+          )
+-SMILES = get('DJANGOBB_SMILES', SMILES)
+\ No newline at end of file
++SMILES = get('DJANGOBB_SMILES', SMILES)
+diff -r 94a043d3fea4 -r f8ae73c56d1b djangobb/djangobb_forum/templates/forum/pagination.html
+--- a/djangobb/djangobb_forum/templates/forum/pagination.html	Sun Feb 06 22:45:22 2011 +0200
++++ b/djangobb/djangobb_forum/templates/forum/pagination.html	Mon Feb 07 19:57:24 2011 +0100
+@@ -1,21 +1,56 @@
+ {% load i18n %}
+-{% if pages != 1 %}
+-	{% trans "Pages" %}: 
+-	{% if lower_page %}
+-		<a href="{{ get_params }}page={{ lower_page }}{#&per_page={{ per_page }}#}">&laquo;</a>
+-	{% endif %}
+-	{% for number in page_list %}
+-		{% if number == "." %}
+-		{% else %}
+-			{% if number == page %}
+-				<strong>{{ number }}</strong> 
+-			{% endif %}
+-			{% if number != page %}
+-				<a href="{{ get_params }}page={{ number }}{#&per_page={{ per_page }}#}">{{ number }}</a>
+-			{% endif %}
+-		{% endif %}
+-	{% endfor %}
+-	{% if higher_page %} 
+-		<a href="{{ get_params }}page={{ higher_page }}{#&per_page={{ per_page }}#}">&raquo;</a>
+-	{% endif %}
+-{% endif %}
++{% if num_pages > 1 %}
++    {% trans "Pages: " %}
++
++    {% for i in head_range %}
++        {% if i != page %}
++            <a href="{{ url_params }}page={{ i }}{% if page_size %}&page_size={{ page_size }}{% endif %}">{{ i }}</a>
++        {% else %}
++            <strong>{{ i }}</strong>
++        {% endif %}
++    {% endfor %}
++
++    {% if dots1 %}&hellip;{% endif %}
++
++    {% for i in locality_range %}
++        {% if i != page %}
++            <a href="{{ url_params }}page={{ i }}{% if page_size %}&page_size={{ page_size }}{% endif %}">{{ i }}</a>
++        {% else %}
++            <strong>{{ i }}</strong>
++        {% endif %}
++    {% endfor %}
++
++    {% if dots2 %}&hellip;{% endif %}
++
++    {% for i in tail_range %}
++        {% if i != page %}
++            <a href="{{ url_params }}page={{ i }}{% if page_size %}&page_size={{ page_size }}{% endif %}">{{ i }}</a>
++        {% else %}
++            <strong>{{ i }}</strong>
++        {% endif %}
++    {% endfor %}
++
++    <br />
++
++    {% if previous_page %}
++        <a href="{{ url_params }}page={{ previous_page }}{% if page_size %}&page_size={{ page_size }}{% endif %}">
++            {% trans "&larr; Prev" %}
++        </a>
++    {% else %}
++        <strong>
++            {% trans "&larr; Prev" %}
++        </strong>
++    {% endif %}
++
++    &nbsp;
++
++    {% if next_page %}
++        <a href="{{ url_params }}page={{ next_page }}{% if page_size %}&page_size={{ page_size }}{% endif %}">
++            {% trans "Next &rarr;" %}
++        </a>
++    {% else %}
++        <strong>
++            {% trans "Next &rarr;" %}
++        </strong>
++    {% endif %}
++{% endif %}
+\ No newline at end of file
+diff -r 94a043d3fea4 -r f8ae73c56d1b djangobb/djangobb_forum/util.py
+--- a/djangobb/djangobb_forum/util.py	Sun Feb 06 22:45:22 2011 +0200
++++ b/djangobb/djangobb_forum/util.py	Mon Feb 07 19:57:24 2011 +0100
+@@ -21,12 +21,92 @@
+ from django.conf import settings
+ 
+ from djangobb_forum import settings as forum_settings
++from djangobb_forum.pagination import pagination
+ from djangobb_forum.markups import bbmarkup
+ 
+ #compile smiles regexp
+ _SMILES = [(re.compile(smile_re), path) for smile_re, path in forum_settings.SMILES]
+ 
+ 
++# decorator for Django views, used under `render_to` to paginate data
++def add_pagination(
++    paged_list_name,           # name of paged (filtered) data
++    default_page_size,         # default page size (items per page)
++    pagination_template='pagination.html', # template name to render A tags
++    page_size_limit=1000,      # limit of items per page
++    base_url='',               # base URL like '/foo/' or 'http://qqq.com/' or ''
++    pagination_reverse=False,  # reverse enumeration
++    pagination_show_all=False, # show all pages
++    pagination_head=1,         # pages in head (>=0)
++    pagination_locality=5,     # locality of current page (>=0)
++    pagination_tail=1,         # pages in tail (>=0)
++    pagination_stick=1):       # stick interval (>=0)
++    """
++    decorator for Django views, used under `render_to` to paginate data
++    """
++    def decorator(func):
++        def wrapper(request, *args, **kwargs):
++            # do nothing befor
++            to_return = func(request, *args, **kwargs)
++            if not isinstance(to_return, dict):
++                # return HttpResponse object directly
++                return to_return
++
++            # import settings
++            def get(key, default):
++                return getattr(forum_settings, key, default)
++            template_name = get('PAGINATION_TEMPLATE', pagination_template)
++            size_limit    = get('PAGINATION_LIMIT',    page_size_limit)
++            reverse       = get('PAGINATION_REVERSE',  pagination_reverse)
++            show_all      = get('PAGINATION_SHOW_ALL', pagination_show_all)
++            head          = get('PAGINATION_HEAD',     pagination_head)
++            locality      = get('PAGINATION_LOCALITY', pagination_locality)
++            tail          = get('PAGINATION_TAIL',     pagination_tail)
++            stick         = get('PAGINATION_STICK',    pagination_stick)
++
++            # try to overrde default settings
++            list_name     = to_return.pop('PAGINATION_NAME',     paged_list_name)
++            page_size     = to_return.pop('PAGINATION_SIZE',     default_page_size)
++            template_name = to_return.pop('PAGINATION_TEMPLATE', template_name)
++            size_limit    = to_return.pop('PAGINATION_LIMIT',    size_limit)
++            url_params    = to_return.pop('PAGINATION_BASE_URL', base_url)
++            reverse       = to_return.pop('PAGINATION_REVERSE',  reverse)
++            show_all      = to_return.pop('PAGINATION_SHOW_ALL', show_all)
++            head          = to_return.pop('PAGINATION_HEAD',     head)
++            locality      = to_return.pop('PAGINATION_LOCALITY', locality)
++            tail          = to_return.pop('PAGINATION_TAIL',     tail)
++            stick         = to_return.pop('PAGINATION_STICK',    stick)
++
++            # paginate object list and render string of of A tags with links to pages
++            object_list = to_return[list_name]
++            object_list, pagination_urls, redirect, redirect_to = pagination(
++                request.GET,   # HTTP request GET QueryDict
++                object_list,   # object list to paginate
++                template_name, # template name to render A tags
++                page_size,     # default page size (items per page)
++                size_limit,    # limit of items per page
++                url_params,    # base URL like '/foo/' or 'http://qqq.com/' or ''
++                reverse,       # reverse enumeration
++                show_all,      # show all pages
++                head,          # pages in head (>=0)
++                locality,      # locality of current page (>=0)
++                tail,          # pages in tail (>=0)
++                stick)         # stick interval (>=0)
++
++            # redirection if some errors in GET request fixed
++            if redirect:
++                return HttpResponseRedirect(redirect_to)
++
++            # update (filter) result and add `pagination`
++            if pagination_urls:
++                to_return[list_name] = object_list
++                to_return['pagination'] = pagination_urls
++
++            return to_return
++        return wrapper
++
++    return decorator
++
+ def render_to(template):
+     """
+     Decorator for Django views that sends returned dict to render_to_response function.
+@@ -43,14 +123,14 @@
+ 
+     @render_to('template.html')
+     def foo(request):
+-        bar = Bar.object.all()  
++        bar = Bar.object.all()
+         return {'bar': bar}
+ 
+-    # equals to 
++    # equals to
+     def foo(request):
+-        bar = Bar.object.all()  
+-        return render_to_response('template.html', 
+-                                  {'bar': bar}, 
++        bar = Bar.object.all()
++        return render_to_response('template.html',
++                                  {'bar': bar},
+                                   context_instance=RequestContext(request))
+ 
+     # 2. Template name as TEMPLATE item value in return dictionary
+@@ -59,12 +139,12 @@
+     def foo(request, category):
+         template_name = '%s.html' % category
+         return {'bar': bar, 'TEMPLATE': template_name}
+-    
++
+     #equals to
+     def foo(request, category):
+         template_name = '%s.html' % category
+-        return render_to_response(template_name, 
+-                                  {'bar': bar}, 
++        return render_to_response(template_name,
++                                  {'bar': bar},
+                                   context_instance=RequestContext(request))
+     """
+ 
+@@ -208,7 +288,7 @@
+             self.html.append(data)
+ 
+         def handle_startendtag(self, tag, attrs):
+-            self.html.append('<%s%s/>' % (tag, self.__html_attrs(attrs))) 
++            self.html.append('<%s%s/>' % (tag, self.__html_attrs(attrs)))
+ 
+         def handle_endtag(self, tag):
+             self.is_ignored = False
+@@ -238,7 +318,7 @@
+ def urlize(data):
+     """
+     Urlize plain text links in the HTML contents.
+-   
++
+     Do not urlize content of A and CODE tags.
+     """
+ 
+@@ -276,7 +356,7 @@
+         paged_list_name = paginator.page(page_number).object_list
+     except (InvalidPage, EmptyPage):
+         raise Http404
+-    return pages, paginator, paged_list_name 
++    return pages, paginator, paged_list_name
+ 
+ def set_language(request, language):
+     """
+@@ -287,12 +367,12 @@
+         if hasattr(request, 'session'):
+             request.session['django_language'] = language
+         else:
+-            response.set_cookie(settings.LANGUAGE_COOKIE_NAME, language) 
++            response.set_cookie(settings.LANGUAGE_COOKIE_NAME, language)
+ 
+ def convert_text_to_html(text, markup):
+     if markup == 'bbcode':
+         text = bbmarkup.bbcode(text)
+-    elif markup == 'markdown':            
++    elif markup == 'markdown':
+         text = markdown.markdown(text, safe_mode='escape')
+     else:
+         raise Exception('Invalid markup property: %s' % markup)
+diff -r 94a043d3fea4 -r f8ae73c56d1b djangobb/djangobb_forum/views.py
+--- a/djangobb/djangobb_forum/views.py	Sun Feb 06 22:45:22 2011 +0200
++++ b/djangobb/djangobb_forum/views.py	Mon Feb 07 19:57:24 2011 +0100
+@@ -714,7 +714,7 @@
+ 
+ 
+ @render_to('forum/users.html')
+-@paged('users', forum_settings.USERS_PAGE_SIZE)
++@add_pagination('users', forum_settings.USERS_PAGE_SIZE)
+ def users(request):
+     users = User.objects.filter(forum_profile__post_count__gte=forum_settings.POST_USER_SEARCH).order_by('username')
+     form = UserSearchForm(request.GET)
+pagination.patch
 # Placed by Bitbucket