Merge pull request #2235 from mosra/paginator-pattern-url

Allow using page URL in pagination patterns
This commit is contained in:
Justin Mayer 2018-02-10 11:27:02 -08:00 committed by GitHub
commit 59fc1d02a7
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 89 additions and 25 deletions

View file

@ -464,14 +464,29 @@ respectively.
The location to save per-year archives of your posts. The location to save per-year archives of your posts.
.. data:: YEAR_ARCHIVE_URL = ''
The URL to use for per-year archives of your posts. Used only if you have
the ``{url}`` placeholder in ``PAGINATION_PATTERNS``.
.. data:: MONTH_ARCHIVE_SAVE_AS = '' .. data:: MONTH_ARCHIVE_SAVE_AS = ''
The location to save per-month archives of your posts. The location to save per-month archives of your posts.
.. data:: MONTH_ARCHIVE_URL = ''
The URL to use for per-month archives of your posts. Used only if you have
the ``{url}`` placeholder in ``PAGINATION_PATTERNS``.
.. data:: DAY_ARCHIVE_SAVE_AS = '' .. data:: DAY_ARCHIVE_SAVE_AS = ''
The location to save per-day archives of your posts. The location to save per-day archives of your posts.
.. data:: DAY_ARCHIVE_URL = ''
The URL to use for per-day archives of your posts. Used only if you have the
``{url}`` placeholder in ``PAGINATION_PATTERNS``.
.. data:: SLUG_SUBSTITUTIONS = () .. data:: SLUG_SUBSTITUTIONS = ()
Substitutions to make prior to stripping out non-alphanumerics when Substitutions to make prior to stripping out non-alphanumerics when

View file

@ -262,7 +262,7 @@ class TemplatePagesGenerator(Generator):
template = self.env.get_template(source) template = self.env.get_template(source)
rurls = self.settings['RELATIVE_URLS'] rurls = self.settings['RELATIVE_URLS']
writer.write_file(dest, template, self.context, rurls, writer.write_file(dest, template, self.context, rurls,
override_output=True) override_output=True, url='')
finally: finally:
del self.env.loader.loaders[0] del self.env.loader.loaders[0]
@ -376,7 +376,7 @@ class ArticlesGenerator(CachingGenerator):
write(article.save_as, self.get_template(article.template), write(article.save_as, self.get_template(article.template),
self.context, article=article, category=article.category, self.context, article=article, category=article.category,
override_output=hasattr(article, 'override_save_as'), override_output=hasattr(article, 'override_save_as'),
blog=True) url=article.url, blog=True)
def generate_period_archives(self, write): def generate_period_archives(self, write):
"""Generate per-year, per-month, and per-day archives.""" """Generate per-year, per-month, and per-day archives."""
@ -391,13 +391,19 @@ class ArticlesGenerator(CachingGenerator):
'day': self.settings['DAY_ARCHIVE_SAVE_AS'], 'day': self.settings['DAY_ARCHIVE_SAVE_AS'],
} }
period_url = {
'year': self.settings['YEAR_ARCHIVE_URL'],
'month': self.settings['MONTH_ARCHIVE_URL'],
'day': self.settings['DAY_ARCHIVE_URL'],
}
period_date_key = { period_date_key = {
'year': attrgetter('date.year'), 'year': attrgetter('date.year'),
'month': attrgetter('date.year', 'date.month'), 'month': attrgetter('date.year', 'date.month'),
'day': attrgetter('date.year', 'date.month', 'date.day') 'day': attrgetter('date.year', 'date.month', 'date.day')
} }
def _generate_period_archives(dates, key, save_as_fmt): def _generate_period_archives(dates, key, save_as_fmt, url_fmt):
"""Generate period archives from `dates`, grouped by """Generate period archives from `dates`, grouped by
`key` and written to `save_as`. `key` and written to `save_as`.
""" """
@ -409,6 +415,7 @@ class ArticlesGenerator(CachingGenerator):
# period archive dates # period archive dates
date = archive[0].date date = archive[0].date
save_as = save_as_fmt.format(date=date) save_as = save_as_fmt.format(date=date)
url = url_fmt.format(date=date)
context = self.context.copy() context = self.context.copy()
if key == period_date_key['year']: if key == period_date_key['year']:
@ -426,13 +433,14 @@ class ArticlesGenerator(CachingGenerator):
_period[2]) _period[2])
write(save_as, template, context, write(save_as, template, context,
dates=archive, blog=True) dates=archive, blog=True, url=url)
for period in 'year', 'month', 'day': for period in 'year', 'month', 'day':
save_as = period_save_as[period] save_as = period_save_as[period]
url = period_url[period]
if save_as: if save_as:
key = period_date_key[period] key = period_date_key[period]
_generate_period_archives(self.dates, key, save_as) _generate_period_archives(self.dates, key, save_as, url)
def generate_direct_templates(self, write): def generate_direct_templates(self, write):
"""Generate direct templates pages""" """Generate direct templates pages"""
@ -443,12 +451,14 @@ class ArticlesGenerator(CachingGenerator):
paginated = {'articles': self.articles, 'dates': self.dates} paginated = {'articles': self.articles, 'dates': self.dates}
save_as = self.settings.get("%s_SAVE_AS" % template.upper(), save_as = self.settings.get("%s_SAVE_AS" % template.upper(),
'%s.html' % template) '%s.html' % template)
url = self.settings.get("%s_URL" % template.upper(),
'%s.html' % template)
if not save_as: if not save_as:
continue continue
write(save_as, self.get_template(template), write(save_as, self.get_template(template),
self.context, blog=True, paginated=paginated, self.context, blog=True, paginated=paginated,
page_name=os.path.splitext(save_as)[0]) page_name=os.path.splitext(save_as)[0], url=url)
def generate_tags(self, write): def generate_tags(self, write):
"""Generate Tags pages.""" """Generate Tags pages."""
@ -457,7 +467,7 @@ class ArticlesGenerator(CachingGenerator):
articles.sort(key=attrgetter('date'), reverse=True) articles.sort(key=attrgetter('date'), reverse=True)
dates = [article for article in self.dates if article in articles] dates = [article for article in self.dates if article in articles]
write(tag.save_as, tag_template, self.context, tag=tag, write(tag.save_as, tag_template, self.context, tag=tag,
articles=articles, dates=dates, url=tag.url, articles=articles, dates=dates,
paginated={'articles': articles, 'dates': dates}, blog=True, paginated={'articles': articles, 'dates': dates}, blog=True,
page_name=tag.page_name, all_articles=self.articles) page_name=tag.page_name, all_articles=self.articles)
@ -468,7 +478,7 @@ class ArticlesGenerator(CachingGenerator):
articles.sort(key=attrgetter('date'), reverse=True) articles.sort(key=attrgetter('date'), reverse=True)
dates = [article for article in self.dates if article in articles] dates = [article for article in self.dates if article in articles]
write(cat.save_as, category_template, self.context, write(cat.save_as, category_template, self.context,
category=cat, articles=articles, dates=dates, url=cat.url, category=cat, articles=articles, dates=dates,
paginated={'articles': articles, 'dates': dates}, blog=True, paginated={'articles': articles, 'dates': dates}, blog=True,
page_name=cat.page_name, all_articles=self.articles) page_name=cat.page_name, all_articles=self.articles)
@ -479,7 +489,7 @@ class ArticlesGenerator(CachingGenerator):
articles.sort(key=attrgetter('date'), reverse=True) articles.sort(key=attrgetter('date'), reverse=True)
dates = [article for article in self.dates if article in articles] dates = [article for article in self.dates if article in articles]
write(aut.save_as, author_template, self.context, write(aut.save_as, author_template, self.context,
author=aut, articles=articles, dates=dates, url=aut.url, author=aut, articles=articles, dates=dates,
paginated={'articles': articles, 'dates': dates}, blog=True, paginated={'articles': articles, 'dates': dates}, blog=True,
page_name=aut.page_name, all_articles=self.articles) page_name=aut.page_name, all_articles=self.articles)
@ -489,7 +499,7 @@ class ArticlesGenerator(CachingGenerator):
write(draft.save_as, self.get_template(draft.template), write(draft.save_as, self.get_template(draft.template),
self.context, article=draft, category=draft.category, self.context, article=draft, category=draft.category,
override_output=hasattr(draft, 'override_save_as'), override_output=hasattr(draft, 'override_save_as'),
blog=True, all_articles=self.articles) blog=True, all_articles=self.articles, url=draft.url)
def generate_pages(self, writer): def generate_pages(self, writer):
"""Generate the pages on the disk""" """Generate the pages on the disk"""
@ -662,7 +672,8 @@ class PagesGenerator(CachingGenerator):
page.save_as, self.get_template(page.template), page.save_as, self.get_template(page.template),
self.context, page=page, self.context, page=page,
relative_urls=self.settings['RELATIVE_URLS'], relative_urls=self.settings['RELATIVE_URLS'],
override_output=hasattr(page, 'override_save_as')) override_output=hasattr(page, 'override_save_as'),
url=page.url)
signals.page_writer_finalized.send(self, writer=writer) signals.page_writer_finalized.send(self, writer=writer)
def refresh_metadata_intersite_links(self): def refresh_metadata_intersite_links(self):

View file

@ -17,8 +17,9 @@ PaginationRule = namedtuple(
class Paginator(object): class Paginator(object):
def __init__(self, name, object_list, settings): def __init__(self, name, url, object_list, settings):
self.name = name self.name = name
self.url = url
self.object_list = object_list self.object_list = object_list
self.settings = settings self.settings = settings
@ -37,8 +38,8 @@ class Paginator(object):
top = bottom + self.per_page top = bottom + self.per_page
if top + self.orphans >= self.count: if top + self.orphans >= self.count:
top = self.count top = self.count
return Page(self.name, self.object_list[bottom:top], number, self, return Page(self.name, self.url, self.object_list[bottom:top], number,
self.settings) self, self.settings)
def _get_count(self): def _get_count(self):
"Returns the total number of objects, across all pages." "Returns the total number of objects, across all pages."
@ -65,8 +66,9 @@ class Paginator(object):
class Page(object): class Page(object):
def __init__(self, name, object_list, number, paginator, settings): def __init__(self, name, url, object_list, number, paginator, settings):
self.name, self.extension = os.path.splitext(name) self.name, self.extension = os.path.splitext(name)
self.base_url = url
self.object_list = object_list self.object_list = object_list
self.number = number self.number = number
self.paginator = paginator self.paginator = paginator
@ -134,6 +136,7 @@ class Page(object):
# URL or SAVE_AS is a string, format it with a controlled context # URL or SAVE_AS is a string, format it with a controlled context
context = { context = {
'name': self.name.replace(os.sep, '/'), 'name': self.name.replace(os.sep, '/'),
'url': self.base_url,
'object_list': self.object_list, 'object_list': self.object_list,
'number': self.number, 'number': self.number,
'paginator': self.paginator, 'paginator': self.paginator,

View file

@ -93,8 +93,11 @@ DEFAULT_CONFIG = {
'PAGINATION_PATTERNS': [ 'PAGINATION_PATTERNS': [
(0, '{name}{number}{extension}', '{name}{number}{extension}'), (0, '{name}{number}{extension}', '{name}{number}{extension}'),
], ],
'YEAR_ARCHIVE_URL': '',
'YEAR_ARCHIVE_SAVE_AS': '', 'YEAR_ARCHIVE_SAVE_AS': '',
'MONTH_ARCHIVE_URL': '',
'MONTH_ARCHIVE_SAVE_AS': '', 'MONTH_ARCHIVE_SAVE_AS': '',
'DAY_ARCHIVE_URL': '',
'DAY_ARCHIVE_SAVE_AS': '', 'DAY_ARCHIVE_SAVE_AS': '',
'RELATIVE_URLS': False, 'RELATIVE_URLS': False,
'DEFAULT_LANG': 'en', 'DEFAULT_LANG': 'en',

View file

@ -254,7 +254,7 @@ class TestArticlesGenerator(unittest.TestCase):
self.assertEqual(sorted(categories), sorted(categories_expected)) self.assertEqual(sorted(categories), sorted(categories_expected))
@unittest.skipUnless(MagicMock, 'Needs Mock module') @unittest.skipUnless(MagicMock, 'Needs Mock module')
def test_direct_templates_save_as_default(self): def test_direct_templates_save_as_url_default(self):
settings = get_settings(filenames={}) settings = get_settings(filenames={})
settings['CACHE_PATH'] = self.temp_cache settings['CACHE_PATH'] = self.temp_cache
@ -265,14 +265,16 @@ class TestArticlesGenerator(unittest.TestCase):
generator.generate_direct_templates(write) generator.generate_direct_templates(write)
write.assert_called_with("archives.html", write.assert_called_with("archives.html",
generator.get_template("archives"), settings, generator.get_template("archives"), settings,
blog=True, paginated={}, page_name='archives') blog=True, paginated={}, page_name='archives',
url="archives.html")
@unittest.skipUnless(MagicMock, 'Needs Mock module') @unittest.skipUnless(MagicMock, 'Needs Mock module')
def test_direct_templates_save_as_modified(self): def test_direct_templates_save_as_url_modified(self):
settings = get_settings() settings = get_settings()
settings['DIRECT_TEMPLATES'] = ['archives'] settings['DIRECT_TEMPLATES'] = ['archives']
settings['ARCHIVES_SAVE_AS'] = 'archives/index.html' settings['ARCHIVES_SAVE_AS'] = 'archives/index.html'
settings['ARCHIVES_URL'] = 'archives/'
settings['CACHE_PATH'] = self.temp_cache settings['CACHE_PATH'] = self.temp_cache
generator = ArticlesGenerator( generator = ArticlesGenerator(
context=settings, settings=settings, context=settings, settings=settings,
@ -282,7 +284,8 @@ class TestArticlesGenerator(unittest.TestCase):
write.assert_called_with("archives/index.html", write.assert_called_with("archives/index.html",
generator.get_template("archives"), settings, generator.get_template("archives"), settings,
blog=True, paginated={}, blog=True, paginated={},
page_name='archives/index') page_name='archives/index',
url="archives/")
@unittest.skipUnless(MagicMock, 'Needs Mock module') @unittest.skipUnless(MagicMock, 'Needs Mock module')
def test_direct_templates_save_as_false(self): def test_direct_templates_save_as_false(self):
@ -320,6 +323,7 @@ class TestArticlesGenerator(unittest.TestCase):
settings = get_settings(filenames={}) settings = get_settings(filenames={})
settings['YEAR_ARCHIVE_SAVE_AS'] = 'posts/{date:%Y}/index.html' settings['YEAR_ARCHIVE_SAVE_AS'] = 'posts/{date:%Y}/index.html'
settings['YEAR_ARCHIVE_URL'] = 'posts/{date:%Y}/'
settings['CACHE_PATH'] = self.temp_cache settings['CACHE_PATH'] = self.temp_cache
generator = ArticlesGenerator( generator = ArticlesGenerator(
context=settings, settings=settings, context=settings, settings=settings,
@ -334,11 +338,13 @@ class TestArticlesGenerator(unittest.TestCase):
write.assert_called_with("posts/1970/index.html", write.assert_called_with("posts/1970/index.html",
generator.get_template("period_archives"), generator.get_template("period_archives"),
settings, settings,
blog=True, dates=dates) blog=True, dates=dates, url="posts/1970/")
del settings["period"] del settings["period"]
settings['MONTH_ARCHIVE_SAVE_AS'] = \ settings['MONTH_ARCHIVE_SAVE_AS'] = \
'posts/{date:%Y}/{date:%b}/index.html' 'posts/{date:%Y}/{date:%b}/index.html'
settings['MONTH_ARCHIVE_URL'] = \
'posts/{date:%Y}/{date:%b}/'
generator = ArticlesGenerator( generator = ArticlesGenerator(
context=settings, settings=settings, context=settings, settings=settings,
path=CONTENT_DIR, theme=settings['THEME'], output_path=None) path=CONTENT_DIR, theme=settings['THEME'], output_path=None)
@ -353,11 +359,13 @@ class TestArticlesGenerator(unittest.TestCase):
write.assert_called_with("posts/1970/Jan/index.html", write.assert_called_with("posts/1970/Jan/index.html",
generator.get_template("period_archives"), generator.get_template("period_archives"),
settings, settings,
blog=True, dates=dates) blog=True, dates=dates, url="posts/1970/Jan/")
del settings["period"] del settings["period"]
settings['DAY_ARCHIVE_SAVE_AS'] = \ settings['DAY_ARCHIVE_SAVE_AS'] = \
'posts/{date:%Y}/{date:%b}/{date:%d}/index.html' 'posts/{date:%Y}/{date:%b}/{date:%d}/index.html'
settings['DAY_ARCHIVE_URL'] = \
'posts/{date:%Y}/{date:%b}/{date:%d}/'
generator = ArticlesGenerator( generator = ArticlesGenerator(
context=settings, settings=settings, context=settings, settings=settings,
path=CONTENT_DIR, theme=settings['THEME'], output_path=None) path=CONTENT_DIR, theme=settings['THEME'], output_path=None)
@ -376,7 +384,8 @@ class TestArticlesGenerator(unittest.TestCase):
write.assert_called_with("posts/1970/Jan/01/index.html", write.assert_called_with("posts/1970/Jan/01/index.html",
generator.get_template("period_archives"), generator.get_template("period_archives"),
settings, settings,
blog=True, dates=dates) blog=True, dates=dates,
url="posts/1970/Jan/01/")
locale.setlocale(locale.LC_ALL, old_locale) locale.setlocale(locale.LC_ALL, old_locale)
def test_nonexistent_template(self): def test_nonexistent_template(self):

View file

@ -54,6 +54,28 @@ class TestPage(unittest.TestCase):
self.page_kwargs['metadata']['author'] = Author('Blogger', settings) self.page_kwargs['metadata']['author'] = Author('Blogger', settings)
object_list = [Article(**self.page_kwargs), object_list = [Article(**self.page_kwargs),
Article(**self.page_kwargs)] Article(**self.page_kwargs)]
paginator = Paginator('foobar.foo', object_list, settings) paginator = Paginator('foobar.foo', 'foobar/foo', object_list,
settings)
page = paginator.page(1) page = paginator.page(1)
self.assertEqual(page.save_as, 'foobar.foo') self.assertEqual(page.save_as, 'foobar.foo')
def test_custom_pagination_pattern(self):
from pelican.paginator import PaginationRule
settings = get_settings()
settings['PAGINATION_PATTERNS'] = [PaginationRule(*r) for r in [
(1, '/{url}', '{base_name}/index.html'),
(2, '/{url}{number}/', '{base_name}/{number}/index.html')
]]
settings['DEFAULT_PAGINATION'] = 1
self.page_kwargs['metadata']['author'] = Author('Blogger', settings)
object_list = [Article(**self.page_kwargs),
Article(**self.page_kwargs)]
paginator = Paginator('blog/index.html', '//blog.my.site/',
object_list, settings)
page1 = paginator.page(1)
self.assertEqual(page1.save_as, 'blog/index.html')
self.assertEqual(page1.url, '//blog.my.site/')
page2 = paginator.page(2)
self.assertEqual(page2.save_as, 'blog/2/index.html')
self.assertEqual(page2.url, '//blog.my.site/2/')

View file

@ -141,7 +141,7 @@ class Writer(object):
return feed return feed
def write_file(self, name, template, context, relative_urls=False, def write_file(self, name, template, context, relative_urls=False,
paginated=None, override_output=False, **kwargs): paginated=None, override_output=False, url=None, **kwargs):
"""Render the template and write the file. """Render the template and write the file.
:param name: name of the file to output :param name: name of the file to output
@ -153,6 +153,7 @@ class Writer(object):
:param override_output: boolean telling if we can override previous :param override_output: boolean telling if we can override previous
output with the same name (and if next files written with the same output with the same name (and if next files written with the same
name should be skipped to keep that one) name should be skipped to keep that one)
:param url: url of the file (needed by the paginator)
:param **kwargs: additional variables to pass to the templates :param **kwargs: additional variables to pass to the templates
""" """
@ -202,7 +203,7 @@ class Writer(object):
if paginated: if paginated:
# pagination needed, init paginators # pagination needed, init paginators
paginators = {key: Paginator(name, val, self.settings) paginators = {key: Paginator(name, url, val, self.settings)
for key, val in paginated.items()} for key, val in paginated.items()}
# generated pages, and write # generated pages, and write