diff --git a/docs/settings.rst b/docs/settings.rst index d32a9869..bf848eee 100644 --- a/docs/settings.rst +++ b/docs/settings.rst @@ -464,14 +464,29 @@ respectively. 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 = '' 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 = '' 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 = () Substitutions to make prior to stripping out non-alphanumerics when diff --git a/pelican/generators.py b/pelican/generators.py index c78a5484..a6f376b5 100644 --- a/pelican/generators.py +++ b/pelican/generators.py @@ -262,7 +262,7 @@ class TemplatePagesGenerator(Generator): template = self.env.get_template(source) rurls = self.settings['RELATIVE_URLS'] writer.write_file(dest, template, self.context, rurls, - override_output=True) + override_output=True, url='') finally: del self.env.loader.loaders[0] @@ -376,7 +376,7 @@ class ArticlesGenerator(CachingGenerator): write(article.save_as, self.get_template(article.template), self.context, article=article, category=article.category, override_output=hasattr(article, 'override_save_as'), - blog=True) + url=article.url, blog=True) def generate_period_archives(self, write): """Generate per-year, per-month, and per-day archives.""" @@ -391,13 +391,19 @@ class ArticlesGenerator(CachingGenerator): '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 = { 'year': attrgetter('date.year'), 'month': attrgetter('date.year', 'date.month'), '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 `key` and written to `save_as`. """ @@ -409,6 +415,7 @@ class ArticlesGenerator(CachingGenerator): # period archive dates date = archive[0].date save_as = save_as_fmt.format(date=date) + url = url_fmt.format(date=date) context = self.context.copy() if key == period_date_key['year']: @@ -426,13 +433,14 @@ class ArticlesGenerator(CachingGenerator): _period[2]) write(save_as, template, context, - dates=archive, blog=True) + dates=archive, blog=True, url=url) for period in 'year', 'month', 'day': save_as = period_save_as[period] + url = period_url[period] if save_as: 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): """Generate direct templates pages""" @@ -443,12 +451,14 @@ class ArticlesGenerator(CachingGenerator): paginated = {'articles': self.articles, 'dates': self.dates} save_as = self.settings.get("%s_SAVE_AS" % template.upper(), '%s.html' % template) + url = self.settings.get("%s_URL" % template.upper(), + '%s.html' % template) if not save_as: continue write(save_as, self.get_template(template), 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): """Generate Tags pages.""" @@ -457,7 +467,7 @@ class ArticlesGenerator(CachingGenerator): articles.sort(key=attrgetter('date'), reverse=True) dates = [article for article in self.dates if article in articles] 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, page_name=tag.page_name, all_articles=self.articles) @@ -468,7 +478,7 @@ class ArticlesGenerator(CachingGenerator): articles.sort(key=attrgetter('date'), reverse=True) dates = [article for article in self.dates if article in articles] 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, page_name=cat.page_name, all_articles=self.articles) @@ -479,7 +489,7 @@ class ArticlesGenerator(CachingGenerator): articles.sort(key=attrgetter('date'), reverse=True) dates = [article for article in self.dates if article in articles] 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, 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), self.context, article=draft, category=draft.category, 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): """Generate the pages on the disk""" @@ -662,7 +672,8 @@ class PagesGenerator(CachingGenerator): page.save_as, self.get_template(page.template), self.context, page=page, 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) def refresh_metadata_intersite_links(self): diff --git a/pelican/paginator.py b/pelican/paginator.py index 9aca550b..6996cae9 100644 --- a/pelican/paginator.py +++ b/pelican/paginator.py @@ -17,8 +17,9 @@ PaginationRule = namedtuple( class Paginator(object): - def __init__(self, name, object_list, settings): + def __init__(self, name, url, object_list, settings): self.name = name + self.url = url self.object_list = object_list self.settings = settings @@ -37,8 +38,8 @@ class Paginator(object): top = bottom + self.per_page if top + self.orphans >= self.count: top = self.count - return Page(self.name, self.object_list[bottom:top], number, self, - self.settings) + return Page(self.name, self.url, self.object_list[bottom:top], number, + self, self.settings) def _get_count(self): "Returns the total number of objects, across all pages." @@ -65,8 +66,9 @@ class Paginator(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.base_url = url self.object_list = object_list self.number = number self.paginator = paginator @@ -134,6 +136,7 @@ class Page(object): # URL or SAVE_AS is a string, format it with a controlled context context = { 'name': self.name.replace(os.sep, '/'), + 'url': self.base_url, 'object_list': self.object_list, 'number': self.number, 'paginator': self.paginator, diff --git a/pelican/settings.py b/pelican/settings.py index 0d09340b..5b9d4b94 100644 --- a/pelican/settings.py +++ b/pelican/settings.py @@ -93,8 +93,11 @@ DEFAULT_CONFIG = { 'PAGINATION_PATTERNS': [ (0, '{name}{number}{extension}', '{name}{number}{extension}'), ], + 'YEAR_ARCHIVE_URL': '', 'YEAR_ARCHIVE_SAVE_AS': '', + 'MONTH_ARCHIVE_URL': '', 'MONTH_ARCHIVE_SAVE_AS': '', + 'DAY_ARCHIVE_URL': '', 'DAY_ARCHIVE_SAVE_AS': '', 'RELATIVE_URLS': False, 'DEFAULT_LANG': 'en', diff --git a/pelican/tests/test_generators.py b/pelican/tests/test_generators.py index 732dd60a..5165a6c8 100644 --- a/pelican/tests/test_generators.py +++ b/pelican/tests/test_generators.py @@ -254,7 +254,7 @@ class TestArticlesGenerator(unittest.TestCase): self.assertEqual(sorted(categories), sorted(categories_expected)) @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['CACHE_PATH'] = self.temp_cache @@ -265,14 +265,16 @@ class TestArticlesGenerator(unittest.TestCase): generator.generate_direct_templates(write) write.assert_called_with("archives.html", 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') - def test_direct_templates_save_as_modified(self): + def test_direct_templates_save_as_url_modified(self): settings = get_settings() settings['DIRECT_TEMPLATES'] = ['archives'] settings['ARCHIVES_SAVE_AS'] = 'archives/index.html' + settings['ARCHIVES_URL'] = 'archives/' settings['CACHE_PATH'] = self.temp_cache generator = ArticlesGenerator( context=settings, settings=settings, @@ -282,7 +284,8 @@ class TestArticlesGenerator(unittest.TestCase): write.assert_called_with("archives/index.html", generator.get_template("archives"), settings, blog=True, paginated={}, - page_name='archives/index') + page_name='archives/index', + url="archives/") @unittest.skipUnless(MagicMock, 'Needs Mock module') def test_direct_templates_save_as_false(self): @@ -320,6 +323,7 @@ class TestArticlesGenerator(unittest.TestCase): settings = get_settings(filenames={}) settings['YEAR_ARCHIVE_SAVE_AS'] = 'posts/{date:%Y}/index.html' + settings['YEAR_ARCHIVE_URL'] = 'posts/{date:%Y}/' settings['CACHE_PATH'] = self.temp_cache generator = ArticlesGenerator( context=settings, settings=settings, @@ -334,11 +338,13 @@ class TestArticlesGenerator(unittest.TestCase): write.assert_called_with("posts/1970/index.html", generator.get_template("period_archives"), settings, - blog=True, dates=dates) + blog=True, dates=dates, url="posts/1970/") del settings["period"] settings['MONTH_ARCHIVE_SAVE_AS'] = \ 'posts/{date:%Y}/{date:%b}/index.html' + settings['MONTH_ARCHIVE_URL'] = \ + 'posts/{date:%Y}/{date:%b}/' generator = ArticlesGenerator( context=settings, settings=settings, 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", generator.get_template("period_archives"), settings, - blog=True, dates=dates) + blog=True, dates=dates, url="posts/1970/Jan/") del settings["period"] settings['DAY_ARCHIVE_SAVE_AS'] = \ 'posts/{date:%Y}/{date:%b}/{date:%d}/index.html' + settings['DAY_ARCHIVE_URL'] = \ + 'posts/{date:%Y}/{date:%b}/{date:%d}/' generator = ArticlesGenerator( context=settings, settings=settings, 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", generator.get_template("period_archives"), settings, - blog=True, dates=dates) + blog=True, dates=dates, + url="posts/1970/Jan/01/") locale.setlocale(locale.LC_ALL, old_locale) def test_nonexistent_template(self): diff --git a/pelican/tests/test_paginator.py b/pelican/tests/test_paginator.py index 903a0305..12403410 100644 --- a/pelican/tests/test_paginator.py +++ b/pelican/tests/test_paginator.py @@ -54,6 +54,28 @@ class TestPage(unittest.TestCase): self.page_kwargs['metadata']['author'] = Author('Blogger', settings) object_list = [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) 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/') diff --git a/pelican/writers.py b/pelican/writers.py index fef0d5ca..6c8b97d2 100644 --- a/pelican/writers.py +++ b/pelican/writers.py @@ -141,7 +141,7 @@ class Writer(object): return feed 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. :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 output with the same name (and if next files written with the same 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 """ @@ -202,7 +203,7 @@ class Writer(object): if paginated: # 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()} # generated pages, and write