From c5eecd23eb3d7498210701c278cbb9365cfb45ff Mon Sep 17 00:00:00 2001
From: Ross McFarland
Date: Sat, 29 Dec 2012 09:05:40 -0800
Subject: [PATCH 01/10] PAGINATION_URL/PAGINATION_SAVE_AS implementation
allows the use of custom urls for pagination similar to *_URLS
---
pelican/paginator.py | 44 ++++++++++++++++---
pelican/settings.py | 2 +
.../custom/author/alexis-metaireau2.html | 4 +-
.../custom/author/alexis-metaireau3.html | 4 +-
pelican/tests/output/custom/index2.html | 4 +-
pelican/tests/output/custom/index3.html | 4 +-
.../themes/simple/templates/pagination.html | 8 +---
pelican/writers.py | 29 ++++++------
8 files changed, 66 insertions(+), 33 deletions(-)
diff --git a/pelican/paginator.py b/pelican/paginator.py
index 067215c2..5fc6357e 100644
--- a/pelican/paginator.py
+++ b/pelican/paginator.py
@@ -2,14 +2,26 @@
from __future__ import unicode_literals, print_function
# From django.core.paginator
+import functools
+import logging
+
from math import ceil
+logger = logging.getLogger(__name__)
class Paginator(object):
- def __init__(self, object_list, per_page, orphans=0):
+ def __init__(self, name, object_list, settings):
+ self.name = name
self.object_list = object_list
- self.per_page = per_page
- self.orphans = orphans
+ self.settings = settings
+
+ if settings.get('DEFAULT_PAGINATION'):
+ self.per_page = settings.get('DEFAULT_PAGINATION')
+ self.orphans = settings.get('DEFAULT_ORPHANS')
+ else:
+ self.per_page = len(object_list)
+ self.orphans = 0
+
self._num_pages = self._count = None
def page(self, number):
@@ -18,7 +30,8 @@ class Paginator(object):
top = bottom + self.per_page
if top + self.orphans >= self.count:
top = self.count
- return Page(self.object_list[bottom:top], number, self)
+ return Page(self.name, self.object_list[bottom:top], number, self,
+ self.settings)
def _get_count(self):
"Returns the total number of objects, across all pages."
@@ -45,10 +58,12 @@ class Paginator(object):
class Page(object):
- def __init__(self, object_list, number, paginator):
+ def __init__(self, name, object_list, number, paginator, settings):
+ self.name = name
self.object_list = object_list
self.number = number
self.paginator = paginator
+ self.settings = settings
def __repr__(self):
return '' % (self.number, self.paginator.num_pages)
@@ -87,3 +102,22 @@ class Page(object):
if self.number == self.paginator.num_pages:
return self.paginator.count
return self.number * self.paginator.per_page
+
+ def _from_settings(self, key):
+ """Returns URL information as defined in settings. Similar to
+ URLWrapper._from_settings, but specialized to deal with pagination
+ logic."""
+ setting = "%s_%s" % ('PAGINATION', key)
+ value = self.settings[setting]
+ if not isinstance(value, basestring):
+ logger.warning(u'%s is set to %s' % (setting, value))
+ return value
+ else:
+ context = self.__dict__
+ if self.number == 1:
+ # no page numbers on the first page
+ context['number'] = ''
+ return unicode(value).format(**context)
+
+ url = property(functools.partial(_from_settings, key='URL'))
+ save_as = property(functools.partial(_from_settings, key='SAVE_AS'))
diff --git a/pelican/settings.py b/pelican/settings.py
index 01203504..b65a6df7 100644
--- a/pelican/settings.py
+++ b/pelican/settings.py
@@ -73,6 +73,8 @@ DEFAULT_CONFIG = {
'TAG_SAVE_AS': os.path.join('tag', '{slug}.html'),
'AUTHOR_URL': 'author/{slug}.html',
'AUTHOR_SAVE_AS': os.path.join('author', '{slug}.html'),
+ 'PAGINATION_URL': '{name}{number}.html',
+ 'PAGINATION_SAVE_AS': '{name}{number}.html',
'YEAR_ARCHIVE_SAVE_AS': False,
'MONTH_ARCHIVE_SAVE_AS': False,
'DAY_ARCHIVE_SAVE_AS': False,
diff --git a/pelican/tests/output/custom/author/alexis-metaireau2.html b/pelican/tests/output/custom/author/alexis-metaireau2.html
index 9f4a31e8..5d92303c 100644
--- a/pelican/tests/output/custom/author/alexis-metaireau2.html
+++ b/pelican/tests/output/custom/author/alexis-metaireau2.html
@@ -142,8 +142,8 @@ as well as inline markup.
- «
- Page 2 / 3
+ «
+ Page 2 / 3
»
diff --git a/pelican/tests/output/custom/author/alexis-metaireau3.html b/pelican/tests/output/custom/author/alexis-metaireau3.html
index 4eda0b62..64468725 100644
--- a/pelican/tests/output/custom/author/alexis-metaireau3.html
+++ b/pelican/tests/output/custom/author/alexis-metaireau3.html
@@ -59,8 +59,8 @@
- «
- Page 3 / 3
+ «
+ Page 3 / 3
diff --git a/pelican/tests/output/custom/index2.html b/pelican/tests/output/custom/index2.html
index b8e2ac1a..7de87a34 100644
--- a/pelican/tests/output/custom/index2.html
+++ b/pelican/tests/output/custom/index2.html
@@ -140,8 +140,8 @@ YEAH !
- «
- Page 2 / 3
+ «
+ Page 2 / 3
»
diff --git a/pelican/tests/output/custom/index3.html b/pelican/tests/output/custom/index3.html
index cf285ea2..18435df8 100644
--- a/pelican/tests/output/custom/index3.html
+++ b/pelican/tests/output/custom/index3.html
@@ -59,8 +59,8 @@
- «
- Page 3 / 3
+ «
+ Page 3 / 3
diff --git a/pelican/themes/simple/templates/pagination.html b/pelican/themes/simple/templates/pagination.html
index 83c587ac..4219a5c3 100644
--- a/pelican/themes/simple/templates/pagination.html
+++ b/pelican/themes/simple/templates/pagination.html
@@ -1,15 +1,11 @@
{% if DEFAULT_PAGINATION %}
{% 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 %}
{% endif %}
diff --git a/pelican/writers.py b/pelican/writers.py
index fe37b25d..1113a5ba 100644
--- a/pelican/writers.py
+++ b/pelican/writers.py
@@ -150,36 +150,37 @@ class Writer(object):
# check paginated
paginated = paginated or {}
if paginated:
+ name_root, ext = os.path.splitext(name)
+
# pagination needed, init paginators
paginators = {}
for key in paginated.keys():
object_list = paginated[key]
- if self.settings['DEFAULT_PAGINATION']:
- paginators[key] = Paginator(object_list,
- self.settings['DEFAULT_PAGINATION'],
- self.settings['DEFAULT_ORPHANS'])
- else:
- paginators[key] = Paginator(object_list, len(object_list))
+ paginators[key] = Paginator(
+ name_root,
+ object_list,
+ self.settings,
+ )
# generated pages, and write
- name_root, ext = os.path.splitext(name)
for page_num in range(list(paginators.values())[0].num_pages):
paginated_localcontext = localcontext.copy()
for key in paginators.keys():
paginator = paginators[key]
+ previous_page = paginator.page(page_num) \
+ if page_num > 0 else None
page = paginator.page(page_num + 1)
+ next_page = paginator.page(page_num + 2) \
+ if page_num + 1 < paginator.num_pages else None
paginated_localcontext.update(
{'%s_paginator' % key: paginator,
- '%s_page' % key: page})
- if page_num > 0:
- paginated_name = '%s%s%s' % (
- name_root, page_num + 1, ext)
- else:
- paginated_name = name
+ '%s_page' % key: page,
+ '%s_previous_page' % key: previous_page,
+ '%s_next_page' % key: next_page})
_write_file(template, paginated_localcontext, self.output_path,
- paginated_name)
+ page.save_as)
else:
# no pagination
_write_file(template, localcontext, self.output_path, name)
From e07b39dfcb4748066ade21ed75f41295ecd8ac7e Mon Sep 17 00:00:00 2001
From: Ross McFarland
Date: Sat, 29 Dec 2012 17:44:35 -0800
Subject: [PATCH 02/10] more robust PAGINATION_(URL|SAVE_AS) support
- add base_name and number_seperator to context to give more flexibility
when naming things
---
pelican/paginator.py | 9 ++++++++-
pelican/writers.py | 2 +-
2 files changed, 9 insertions(+), 2 deletions(-)
diff --git a/pelican/paginator.py b/pelican/paginator.py
index 5fc6357e..6be64b39 100644
--- a/pelican/paginator.py
+++ b/pelican/paginator.py
@@ -4,6 +4,7 @@ from __future__ import unicode_literals, print_function
# From django.core.paginator
import functools
import logging
+import os
from math import ceil
@@ -114,10 +115,16 @@ class Page(object):
return value
else:
context = self.__dict__
+ context['base_name'] = os.path.dirname(self.name)
+ context['number_sep'] = '/'
if self.number == 1:
# no page numbers on the first page
context['number'] = ''
- return unicode(value).format(**context)
+ context['number_sep'] = ''
+ ret = unicode(value).format(**context)
+ if ret[0] == '/':
+ ret = ret[1:]
+ return ret
url = property(functools.partial(_from_settings, key='URL'))
save_as = property(functools.partial(_from_settings, key='SAVE_AS'))
diff --git a/pelican/writers.py b/pelican/writers.py
index 1113a5ba..25f49aeb 100644
--- a/pelican/writers.py
+++ b/pelican/writers.py
@@ -150,7 +150,7 @@ class Writer(object):
# check paginated
paginated = paginated or {}
if paginated:
- name_root, ext = os.path.splitext(name)
+ name_root = os.path.splitext(name)[0]
# pagination needed, init paginators
paginators = {}
From 0caa101ec73ef392cb2a38d3de0e732b7d544b55 Mon Sep 17 00:00:00 2001
From: Ross McFarland
Date: Sun, 31 Mar 2013 07:41:14 -0700
Subject: [PATCH 03/10] use six.string_types for python 3 compat
---
pelican/paginator.py | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/pelican/paginator.py b/pelican/paginator.py
index 6be64b39..a03ef2af 100644
--- a/pelican/paginator.py
+++ b/pelican/paginator.py
@@ -1,5 +1,6 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals, print_function
+import six
# From django.core.paginator
import functools
@@ -110,7 +111,7 @@ class Page(object):
logic."""
setting = "%s_%s" % ('PAGINATION', key)
value = self.settings[setting]
- if not isinstance(value, basestring):
+ if not isinstance(value, six.string_types):
logger.warning(u'%s is set to %s' % (setting, value))
return value
else:
From 71e83635ea062c7b9cfdaca941f57e70628ba5ae Mon Sep 17 00:00:00 2001
From: Ross McFarland
Date: Sun, 12 May 2013 17:59:44 -0700
Subject: [PATCH 04/10] remove u prefix from string literal, using
unicode_literals
---
pelican/paginator.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/pelican/paginator.py b/pelican/paginator.py
index a03ef2af..e29a32fe 100644
--- a/pelican/paginator.py
+++ b/pelican/paginator.py
@@ -112,7 +112,7 @@ class Page(object):
setting = "%s_%s" % ('PAGINATION', key)
value = self.settings[setting]
if not isinstance(value, six.string_types):
- logger.warning(u'%s is set to %s' % (setting, value))
+ logger.warning('%s is set to %s' % (setting, value))
return value
else:
context = self.__dict__
From 95890a2a611100a833ee727a4a39d5334b017ddf Mon Sep 17 00:00:00 2001
From: Nathan Yergler
Date: Mon, 29 Jul 2013 08:10:28 -0400
Subject: [PATCH 05/10] Allow definition of pagination rules by page index.
---
pelican/paginator.py | 20 ++++++++++++++++++--
pelican/settings.py | 15 +++++++++++++--
2 files changed, 31 insertions(+), 4 deletions(-)
diff --git a/pelican/paginator.py b/pelican/paginator.py
index e29a32fe..b5db2d7d 100644
--- a/pelican/paginator.py
+++ b/pelican/paginator.py
@@ -3,6 +3,7 @@ from __future__ import unicode_literals, print_function
import six
# From django.core.paginator
+from collections import namedtuple
import functools
import logging
import os
@@ -11,6 +12,13 @@ from math import ceil
logger = logging.getLogger(__name__)
+
+PaginationRule = namedtuple(
+ 'PaginationRule',
+ 'min_page URL SAVE_AS',
+)
+
+
class Paginator(object):
def __init__(self, name, object_list, settings):
self.name = name
@@ -109,8 +117,16 @@ class Page(object):
"""Returns URL information as defined in settings. Similar to
URLWrapper._from_settings, but specialized to deal with pagination
logic."""
- setting = "%s_%s" % ('PAGINATION', key)
- value = self.settings[setting]
+
+ rule = None
+
+ # find the last matching pagination rule
+ for p in self.settings['PAGINATION_PATTERNS']:
+ if p.min_page <= self.number:
+ rule = p
+
+ value = getattr(rule, key)
+
if not isinstance(value, six.string_types):
logger.warning('%s is set to %s' % (setting, value))
return value
diff --git a/pelican/settings.py b/pelican/settings.py
index b65a6df7..ff5f4ce4 100644
--- a/pelican/settings.py
+++ b/pelican/settings.py
@@ -73,8 +73,9 @@ DEFAULT_CONFIG = {
'TAG_SAVE_AS': os.path.join('tag', '{slug}.html'),
'AUTHOR_URL': 'author/{slug}.html',
'AUTHOR_SAVE_AS': os.path.join('author', '{slug}.html'),
- 'PAGINATION_URL': '{name}{number}.html',
- 'PAGINATION_SAVE_AS': '{name}{number}.html',
+ 'PAGINATION_PATTERNS': [
+ (0, '{name}{number}.html', '{name}{number}.html'),
+ ],
'YEAR_ARCHIVE_SAVE_AS': False,
'MONTH_ARCHIVE_SAVE_AS': False,
'DAY_ARCHIVE_SAVE_AS': False,
@@ -238,6 +239,16 @@ def configure_settings(settings):
'http://docs.getpelican.com/en/latest/settings.html#timezone '
'for more information')
+ # fix up pagination rules
+ from pelican.paginator import PaginationRule
+ pagination_rules = [
+ PaginationRule(*r) for r in settings['PAGINATION_PATTERNS']
+ ]
+ settings['PAGINATION_PATTERNS'] = sorted(
+ pagination_rules,
+ key=lambda r: r[0],
+ )
+
# Save people from accidentally setting a string rather than a list
path_keys = (
'ARTICLE_EXCLUDES',
From 9abc08ea9f6c8c58edae77a7c4cb9c8364b2729d Mon Sep 17 00:00:00 2001
From: Nathan Yergler
Date: Wed, 31 Jul 2013 18:57:21 -0700
Subject: [PATCH 06/10] Don't assume that PAGINATION_PATTERNS will always be
present.
---
pelican/settings.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/pelican/settings.py b/pelican/settings.py
index ff5f4ce4..59f1354f 100644
--- a/pelican/settings.py
+++ b/pelican/settings.py
@@ -242,7 +242,7 @@ def configure_settings(settings):
# fix up pagination rules
from pelican.paginator import PaginationRule
pagination_rules = [
- PaginationRule(*r) for r in settings['PAGINATION_PATTERNS']
+ PaginationRule(*r) for r in settings.get('PAGINATION_PATTERNS', [])
]
settings['PAGINATION_PATTERNS'] = sorted(
pagination_rules,
From 74c7c72fb3dbe481bd891f447eb1e4056bcbb9a4 Mon Sep 17 00:00:00 2001
From: Nathan Yergler
Date: Wed, 31 Jul 2013 21:31:08 -0700
Subject: [PATCH 07/10] Use six.u instead of unicode.
---
pelican/paginator.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/pelican/paginator.py b/pelican/paginator.py
index b5db2d7d..39ea0e3a 100644
--- a/pelican/paginator.py
+++ b/pelican/paginator.py
@@ -138,7 +138,7 @@ class Page(object):
# no page numbers on the first page
context['number'] = ''
context['number_sep'] = ''
- ret = unicode(value).format(**context)
+ ret = six.u(value).format(**context)
if ret[0] == '/':
ret = ret[1:]
return ret
From 5ffbf907decd2bea80b9b41803b2ac240267da62 Mon Sep 17 00:00:00 2001
From: Nathan Yergler
Date: Sat, 3 Aug 2013 13:49:43 -0700
Subject: [PATCH 08/10] Create new formatting context dict instead of using
self.__dict__
Using self.__dict__ is fine, but when its mutated it changes the
object's state. Creating a new dict avoids needing to think about
that, and doesn't introduce Python 3 issues (ie, where self.number is
accidentally set to '').
---
pelican/paginator.py | 44 ++++++++++++++++++++++++++++----------------
1 file changed, 28 insertions(+), 16 deletions(-)
diff --git a/pelican/paginator.py b/pelican/paginator.py
index 39ea0e3a..df8606ec 100644
--- a/pelican/paginator.py
+++ b/pelican/paginator.py
@@ -125,23 +125,35 @@ class Page(object):
if p.min_page <= self.number:
rule = p
- value = getattr(rule, key)
+ if not rule:
+ return ''
- if not isinstance(value, six.string_types):
- logger.warning('%s is set to %s' % (setting, value))
- return value
- else:
- context = self.__dict__
- context['base_name'] = os.path.dirname(self.name)
- context['number_sep'] = '/'
- if self.number == 1:
- # no page numbers on the first page
- context['number'] = ''
- context['number_sep'] = ''
- ret = six.u(value).format(**context)
- if ret[0] == '/':
- ret = ret[1:]
- return ret
+ prop_value = getattr(rule, key)
+
+ if not isinstance(prop_value, six.string_types):
+ logger.warning('%s is set to %s' % (key, prop_value))
+ return prop_value
+
+ # URL or SAVE_AS is a string, format it with a controlled context
+ context = {
+ 'name': self.name,
+ 'object_list': self.object_list,
+ 'number': self.number,
+ 'paginator': self.paginator,
+ 'settings': self.settings,
+ 'base_name': os.path.dirname(self.name),
+ 'number_sep': '/',
+ }
+
+ if self.number == 1:
+ # no page numbers on the first page
+ context['number'] = ''
+ context['number_sep'] = ''
+
+ ret = prop_value.format(**context)
+ if ret[0] == '/':
+ ret = ret[1:]
+ return ret
url = property(functools.partial(_from_settings, key='URL'))
save_as = property(functools.partial(_from_settings, key='SAVE_AS'))
From 50ff7ce89f7f68c9fe8e4de75f4a06334695794d Mon Sep 17 00:00:00 2001
From: Nathan Yergler
Date: Sat, 3 Aug 2013 14:04:58 -0700
Subject: [PATCH 09/10] Fall back to the default PAGINATION PATTERNS when none
specified.
---
pelican/settings.py | 5 ++++-
1 file changed, 4 insertions(+), 1 deletion(-)
diff --git a/pelican/settings.py b/pelican/settings.py
index 59f1354f..b8e634ca 100644
--- a/pelican/settings.py
+++ b/pelican/settings.py
@@ -242,7 +242,10 @@ def configure_settings(settings):
# fix up pagination rules
from pelican.paginator import PaginationRule
pagination_rules = [
- PaginationRule(*r) for r in settings.get('PAGINATION_PATTERNS', [])
+ PaginationRule(*r) for r in settings.get(
+ 'PAGINATION_PATTERNS',
+ DEFAULT_CONFIG['PAGINATION_PATTERNS'],
+ )
]
settings['PAGINATION_PATTERNS'] = sorted(
pagination_rules,
From d61ef0c66ce6f6db4568f34662f7fd00f8dae6d2 Mon Sep 17 00:00:00 2001
From: Nathan Yergler
Date: Sat, 3 Aug 2013 14:06:16 -0700
Subject: [PATCH 10/10] Adding basic documentation for PAGINATION_PATTERNS.
---
docs/settings.rst | 24 ++++++++++++++++++++++++
1 file changed, 24 insertions(+)
diff --git a/docs/settings.rst b/docs/settings.rst
index eb0d028f..548e9cec 100644
--- a/docs/settings.rst
+++ b/docs/settings.rst
@@ -477,8 +477,32 @@ Setting name (default value) What does it do?
`DEFAULT_PAGINATION` (``False``) The maximum number of articles to include on a
page, not including orphans. False to disable
pagination.
+`PAGINATION_PATTERNS` A set of patterns that are used to determine advanced
+ pagination output.
================================================ =====================================================
+Using Pagination Patterns
+-------------------------
+
+The ``PAGINATION_PATTERNS`` setting can be used to configure where
+subsequent pages are created. The setting is a sequence of three
+element tuples, where each tuple consists of::
+
+ (minimum page, URL setting, SAVE_AS setting,)
+
+For example, if you wanted the first page to just be ``/``, and the
+second (and subsequent) pages to be ``/page/2/``, you would set
+``PAGINATION_PATTERNS`` as follows::
+
+ PAGINATION_PATTERNS = (
+ (1, '{base_name}/', '{base_name}/index.html'),
+ (2, '{base_name}/page/{number}/', '{base_name}/page/{number}/index.html'),
+ )
+
+This would cause the first page to be written to
+``{base_name}/index.html``, and subsequent ones would be written into
+``page/{number}`` directories.
+
Tag cloud
=========