mirror of
https://github.com/getpelican/pelican.git
synced 2025-10-15 20:28:56 +02:00
Merge pull request #979 from nyergler/pagination_conf
Support flexible pagination configuration
This commit is contained in:
commit
bea03bb4bb
9 changed files with 140 additions and 33 deletions
|
|
@ -479,8 +479,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
|
||||
=========
|
||||
|
||||
|
|
|
|||
|
|
@ -1,15 +1,37 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
from __future__ import unicode_literals, print_function
|
||||
import six
|
||||
|
||||
# From django.core.paginator
|
||||
from collections import namedtuple
|
||||
import functools
|
||||
import logging
|
||||
import os
|
||||
|
||||
from math import ceil
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
PaginationRule = namedtuple(
|
||||
'PaginationRule',
|
||||
'min_page URL SAVE_AS',
|
||||
)
|
||||
|
||||
|
||||
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 +40,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 +68,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 '<Page %s of %s>' % (self.number, self.paginator.num_pages)
|
||||
|
|
@ -87,3 +112,48 @@ 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."""
|
||||
|
||||
rule = None
|
||||
|
||||
# find the last matching pagination rule
|
||||
for p in self.settings['PAGINATION_PATTERNS']:
|
||||
if p.min_page <= self.number:
|
||||
rule = p
|
||||
|
||||
if not rule:
|
||||
return ''
|
||||
|
||||
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'))
|
||||
|
|
|
|||
|
|
@ -74,6 +74,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_PATTERNS': [
|
||||
(0, '{name}{number}.html', '{name}{number}.html'),
|
||||
],
|
||||
'YEAR_ARCHIVE_SAVE_AS': False,
|
||||
'MONTH_ARCHIVE_SAVE_AS': False,
|
||||
'DAY_ARCHIVE_SAVE_AS': False,
|
||||
|
|
@ -237,6 +240,19 @@ 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.get(
|
||||
'PAGINATION_PATTERNS',
|
||||
DEFAULT_CONFIG['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',
|
||||
|
|
|
|||
|
|
@ -142,8 +142,8 @@ as well as <strong>inline markup</strong>.</p>
|
|||
</article></li>
|
||||
</ol><!-- /#posts-list -->
|
||||
<p class="paginator">
|
||||
<a href="../author/alexis-metaireau.html">«</a>
|
||||
Page 2 / 3
|
||||
<a href="../author/alexis-metaireau.html">«</a>
|
||||
Page 2 / 3
|
||||
<a href="../author/alexis-metaireau3.html">»</a>
|
||||
</p>
|
||||
</section><!-- /#content -->
|
||||
|
|
|
|||
|
|
@ -59,8 +59,8 @@
|
|||
</article></li>
|
||||
</ol><!-- /#posts-list -->
|
||||
<p class="paginator">
|
||||
<a href="../author/alexis-metaireau2.html">«</a>
|
||||
Page 3 / 3
|
||||
<a href="../author/alexis-metaireau2.html">«</a>
|
||||
Page 3 / 3
|
||||
</p>
|
||||
</section><!-- /#content -->
|
||||
<section id="extras" class="body">
|
||||
|
|
|
|||
|
|
@ -140,8 +140,8 @@ YEAH !</p>
|
|||
</article></li>
|
||||
</ol><!-- /#posts-list -->
|
||||
<p class="paginator">
|
||||
<a href="./index.html">«</a>
|
||||
Page 2 / 3
|
||||
<a href="./index.html">«</a>
|
||||
Page 2 / 3
|
||||
<a href="./index3.html">»</a>
|
||||
</p>
|
||||
</section><!-- /#content -->
|
||||
|
|
|
|||
|
|
@ -59,8 +59,8 @@
|
|||
</article></li>
|
||||
</ol><!-- /#posts-list -->
|
||||
<p class="paginator">
|
||||
<a href="./index2.html">«</a>
|
||||
Page 3 / 3
|
||||
<a href="./index2.html">«</a>
|
||||
Page 3 / 3
|
||||
</p>
|
||||
</section><!-- /#content -->
|
||||
<section id="extras" class="body">
|
||||
|
|
|
|||
|
|
@ -1,15 +1,11 @@
|
|||
{% if DEFAULT_PAGINATION %}
|
||||
<p class="paginator">
|
||||
{% if articles_page.has_previous() %}
|
||||
{% if articles_page.previous_page_number() == 1 %}
|
||||
<a href="{{ SITEURL }}/{{ page_name }}.html">«</a>
|
||||
{% else %}
|
||||
<a href="{{ SITEURL }}/{{ page_name }}{{ articles_page.previous_page_number() }}.html">«</a>
|
||||
{% endif %}
|
||||
<a href="{{ SITEURL }}/{{ articles_previous_page.url }}">«</a>
|
||||
{% endif %}
|
||||
Page {{ articles_page.number }} / {{ articles_paginator.num_pages }}
|
||||
{% if articles_page.has_next() %}
|
||||
<a href="{{ SITEURL }}/{{ page_name }}{{ articles_page.next_page_number() }}.html">»</a>
|
||||
<a href="{{ SITEURL }}/{{ articles_next_page.url }}">»</a>
|
||||
{% endif %}
|
||||
</p>
|
||||
{% endif %}
|
||||
|
|
|
|||
|
|
@ -150,36 +150,37 @@ class Writer(object):
|
|||
# check paginated
|
||||
paginated = paginated or {}
|
||||
if paginated:
|
||||
name_root = os.path.splitext(name)[0]
|
||||
|
||||
# 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)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue