From 4111acd1c136b77ec11e2b8962089502ee4c9bba Mon Sep 17 00:00:00 2001 From: Laureline Guerin Date: Mon, 14 Feb 2011 16:24:54 +0100 Subject: [PATCH] Pagination added for index and tag/category pages --- pelican/generators.py | 52 ++++++++++-- pelican/paginator.py | 85 +++++++++++++++++++ pelican/settings.py | 3 + pelican/themes/notmyidea/templates/index.html | 18 +++- .../themes/notmyidea/templates/paginator.html | 13 +++ samples/pelican.conf.py | 1 + 6 files changed, 164 insertions(+), 8 deletions(-) create mode 100644 pelican/paginator.py create mode 100644 pelican/themes/notmyidea/templates/paginator.html diff --git a/pelican/generators.py b/pelican/generators.py index c50b55e1..088a7271 100755 --- a/pelican/generators.py +++ b/pelican/generators.py @@ -12,10 +12,12 @@ from jinja2.exceptions import TemplateNotFound from pelican.utils import copytree, get_relative_path, process_translations, open from pelican.contents import Article, Page, is_valid_content from pelican.readers import read_file +from pelican.paginator import Paginator _TEMPLATES = ('index', 'tag', 'tags', 'article', 'category', 'categories', 'archives', 'page') _DIRECT_TEMPLATES = ('index', 'tags', 'categories', 'archives') +_PAGINATED_DIRECT_TEMPLATES = ('index', 'tag', 'category') class Generator(object): @@ -141,18 +143,58 @@ class ArticlesGenerator(Generator): category=article.category) for template in _DIRECT_TEMPLATES: - write('%s.html' % template, templates[template], self.context, - blog=True) + if self.settings.get('WITH_PAGINATION') and template in _PAGINATED_DIRECT_TEMPLATES: + articles_paginator = Paginator(self.articles, + self.settings.get('DEFAULT_PAGINATION'), + self.settings.get('DEFAULT_ORPHANS')) + dates_paginator = Paginator(self.dates, + self.settings.get('DEFAULT_PAGINATION'), + self.settings.get('DEFAULT_ORPHANS')) + for page_num in range(articles_paginator.num_pages): + # update context with paginator object and current page + self.context.update({'articles_paginator': articles_paginator, + 'articles_page': articles_paginator.page(page_num+1), + 'dates_paginator': dates_paginator, + 'dates_page': dates_paginator.page(page_num+1), + 'page_name': 'index'}) + write('%s%s.html' % (template, '%s' % (page_num > 0 and page_num+1 or '')), + templates[template], self.context, blog=True) + else: + write('%s.html' % template, templates[template], self.context, + blog=True) # and subfolders after that for tag, articles in self.tags.items(): - for article in articles: + if self.settings.get('WITH_PAGINATION') and 'tag' in _PAGINATED_DIRECT_TEMPLATES: + articles_paginator = Paginator(articles, + self.settings.get('DEFAULT_PAGINATION'), + self.settings.get('DEFAULT_ORPHANS')) + for page_num in range(articles_paginator.num_pages): + # update context with paginator object and current page + self.context.update({'articles_paginator': articles_paginator, + 'articles_page': articles_paginator.page(page_num+1), + 'page_name': 'tag/%s' % tag}) + write('tag/%s%s.html' % (tag, '%s' % (page_num > 0 and page_num+1 or '')), + templates['tag'], self.context, blog=True) + else: write('tag/%s.html' % tag, templates['tag'], self.context, tag=tag, articles=articles) for cat, articles in self.categories: - write('category/%s.html' % cat, templates['category'], self.context, - category=cat, articles=articles) + if self.settings.get('WITH_PAGINATION') and 'tag' in _PAGINATED_DIRECT_TEMPLATES: + articles_paginator = Paginator(articles, + self.settings.get('DEFAULT_PAGINATION'), + self.settings.get('DEFAULT_ORPHANS')) + for page_num in range(articles_paginator.num_pages): + # update context with paginator object and current page + self.context.update({'articles_paginator': articles_paginator, + 'articles_page': articles_paginator.page(page_num+1), + 'page_name': 'category/%s' % cat}) + write('category/%s%s.html' % (cat, '%s' % (page_num > 0 and page_num+1 or '')), + templates['category'], self.context, blog=True) + else: + write('category/%s.html' % cat, templates['category'], self.context, + category=cat, articles=articles) def generate_context(self): """change the context""" diff --git a/pelican/paginator.py b/pelican/paginator.py new file mode 100644 index 00000000..a2a452b9 --- /dev/null +++ b/pelican/paginator.py @@ -0,0 +1,85 @@ +# From django.core.paginator +from math import ceil + +class Paginator(object): + def __init__(self, object_list, per_page, orphans=0): + self.object_list = object_list + self.per_page = per_page + self.orphans = orphans + self._num_pages = self._count = None + + def page(self, number): + "Returns a Page object for the given 1-based page number." + bottom = (number - 1) * self.per_page + top = bottom + self.per_page + if top + self.orphans >= self.count: + top = self.count + return Page(self.object_list[bottom:top], number, self) + + def _get_count(self): + "Returns the total number of objects, across all pages." + if self._count is None: + self._count = len(self.object_list) + return self._count + count = property(_get_count) + + def _get_num_pages(self): + "Returns the total number of pages." + if self._num_pages is None: + hits = max(1, self.count - self.orphans) + self._num_pages = int(ceil(hits / float(self.per_page))) + return self._num_pages + num_pages = property(_get_num_pages) + + def _get_page_range(self): + """ + Returns a 1-based range of pages for iterating through within + a template for loop. + """ + return range(1, self.num_pages + 1) + page_range = property(_get_page_range) + +class Page(object): + def __init__(self, object_list, number, paginator): + self.object_list = object_list + self.number = number + self.paginator = paginator + + def __repr__(self): + return '' % (self.number, self.paginator.num_pages) + + def has_next(self): + return self.number < self.paginator.num_pages + + def has_previous(self): + return self.number > 1 + + def has_other_pages(self): + return self.has_previous() or self.has_next() + + def next_page_number(self): + return self.number + 1 + + def previous_page_number(self): + return self.number - 1 + + def start_index(self): + """ + Returns the 1-based index of the first object on this page, + relative to total objects in the paginator. + """ + # Special case, return zero if no items. + if self.paginator.count == 0: + return 0 + return (self.paginator.per_page * (self.number - 1)) + 1 + + def end_index(self): + """ + Returns the 1-based index of the last object on this page, + relative to total objects found (hits). + """ + # Special case for the last page because there can be orphans. + if self.number == self.paginator.num_pages: + return self.paginator.count + return self.number * self.paginator.per_page + diff --git a/pelican/settings.py b/pelican/settings.py index d84178fe..dbd230d4 100644 --- a/pelican/settings.py +++ b/pelican/settings.py @@ -30,6 +30,9 @@ _DEFAULT_CONFIG = {'PATH': None, 'DATE_FORMATS': {}, 'JINJA_EXTENSIONS': [], 'LOCALE': '', # default to user locale + 'WITH_PAGINATION': True, + 'DEFAULT_PAGINATION': 5, + 'DEFAULT_ORPHANS': 0, } def read_settings(filename): diff --git a/pelican/themes/notmyidea/templates/index.html b/pelican/themes/notmyidea/templates/index.html index 5969e6c8..57b7c1a7 100644 --- a/pelican/themes/notmyidea/templates/index.html +++ b/pelican/themes/notmyidea/templates/index.html @@ -2,8 +2,8 @@ {% block content_title %}{% endblock %} {% block content %} {% if articles %} -{% for article in articles %} - {% if loop.index == 1 %} +{% for article in articles_page.object_list %} + {% if loop.first and not articles_page.has_previous() %} - {% if loop.length > 1 %} + {% if loop.length > 1 %}

Other articles


    {% endif %} {% else %} + {% if loop.first and articles_page.has_previous %} +
    +
      + {% endif %}
    1. {% endif %} + {% if loop.last and (articles_page.has_previous() or not articles_page.has_previous() and loop.length > 1) %} + {% include 'paginator.html' %} + {% endif %} {% endfor %}
    +{% if loop.length > 1 %}
    +{% endif %} {% else %}

    Pages

    diff --git a/pelican/themes/notmyidea/templates/paginator.html b/pelican/themes/notmyidea/templates/paginator.html new file mode 100644 index 00000000..9cce0237 --- /dev/null +++ b/pelican/themes/notmyidea/templates/paginator.html @@ -0,0 +1,13 @@ +

    + {% if articles_page.has_previous() %} + {% if articles_page.previous_page_number() == 1 %} + « + {% else %} + « + {% endif %} + {% endif %} + Page {{ articles_page.number }} / {{ articles_paginator.num_pages }} + {% if articles_page.has_next() %} + » + {% endif %} +

    diff --git a/samples/pelican.conf.py b/samples/pelican.conf.py index 10d5497a..07c49d01 100755 --- a/samples/pelican.conf.py +++ b/samples/pelican.conf.py @@ -8,6 +8,7 @@ DISQUS_SITENAME = "blog-notmyidea" PDF_GENERATOR = False REVERSE_CATEGORY_ORDER = True LOCALE = 'fr_FR.utf8' +DEFAULT_PAGINATION = 2 FEED_RSS = 'feeds/all.rss.xml' CATEGORY_FEED_RSS = 'feeds/%s.rss.xml'