mirror of
https://github.com/getpelican/pelican.git
synced 2025-10-15 20:28:56 +02:00
Merge pull request #1756 from avaris/articles_order_by
Fix #1647: Fix ARTICLE_ORDER_BY and add the ability to reverse order
This commit is contained in:
commit
3c8a7c9fd7
5 changed files with 115 additions and 35 deletions
|
|
@ -277,14 +277,6 @@ Setting name (followed by default value, if any) What does it do?
|
||||||
====================================================== ==============================================================
|
====================================================== ==============================================================
|
||||||
``ARTICLE_URL = '{slug}.html'`` The URL to refer to an article.
|
``ARTICLE_URL = '{slug}.html'`` The URL to refer to an article.
|
||||||
``ARTICLE_SAVE_AS = '{slug}.html'`` The place where we will save an article.
|
``ARTICLE_SAVE_AS = '{slug}.html'`` The place where we will save an article.
|
||||||
``ARTICLE_ORDER_BY = 'slug'`` The metadata attribute used to sort articles. By default,
|
|
||||||
the ``articles_page.object_list`` template variable is
|
|
||||||
ordered by slug. If you modify this, make sure all
|
|
||||||
articles contain the attribute you specify. You can also
|
|
||||||
specify a "sorting" function of one argument that is used
|
|
||||||
to extract a comparison key from each article. For example,
|
|
||||||
sorting by title without using the built-in functionality
|
|
||||||
would use the function ``operator.attrgetter('title')``.
|
|
||||||
``ARTICLE_LANG_URL = '{slug}-{lang}.html'`` The URL to refer to an article which doesn't use the
|
``ARTICLE_LANG_URL = '{slug}-{lang}.html'`` The URL to refer to an article which doesn't use the
|
||||||
default language.
|
default language.
|
||||||
``ARTICLE_LANG_SAVE_AS = '{slug}-{lang}.html'`` The place where we will save an article which
|
``ARTICLE_LANG_SAVE_AS = '{slug}-{lang}.html'`` The place where we will save an article which
|
||||||
|
|
@ -299,17 +291,6 @@ Setting name (followed by default value, if any) What does it do?
|
||||||
``PAGE_SAVE_AS = 'pages/{slug}.html'`` The location we will save the page. This value has to be
|
``PAGE_SAVE_AS = 'pages/{slug}.html'`` The location we will save the page. This value has to be
|
||||||
the same as PAGE_URL or you need to use a rewrite in
|
the same as PAGE_URL or you need to use a rewrite in
|
||||||
your server config.
|
your server config.
|
||||||
|
|
||||||
``PAGE_ORDER_BY = 'basename'`` The metadata attribute used to sort pages. By default
|
|
||||||
the ``PAGES`` template variable is ordered by basename
|
|
||||||
(i.e., path not included). Note that the option ``'basename'``
|
|
||||||
is a special option supported in the source code. If
|
|
||||||
you modify this setting, make sure all pages contain
|
|
||||||
the attribute you specify. You can also specify a "sorting"
|
|
||||||
function of one argument that is used to extract a comparison
|
|
||||||
key from each page. For example, the basename function looks
|
|
||||||
similar to
|
|
||||||
``lambda x: os.path.basename(getattr(x, 'source_path', ''))``.
|
|
||||||
``PAGE_LANG_URL = 'pages/{slug}-{lang}.html'`` The URL we will use to link to a page which doesn't
|
``PAGE_LANG_URL = 'pages/{slug}-{lang}.html'`` The URL we will use to link to a page which doesn't
|
||||||
use the default language.
|
use the default language.
|
||||||
``PAGE_LANG_SAVE_AS = 'pages/{slug}-{lang}.html'`` The location we will save the page which doesn't
|
``PAGE_LANG_SAVE_AS = 'pages/{slug}-{lang}.html'`` The location we will save the page which doesn't
|
||||||
|
|
@ -644,14 +625,26 @@ Setting name (followed by default value, if any) What does it do?
|
||||||
Ordering content
|
Ordering content
|
||||||
================
|
================
|
||||||
|
|
||||||
================================================ =====================================================
|
================================================ ==============================================================
|
||||||
Setting name (followed by default value) What does it do?
|
Setting name (followed by default value) What does it do?
|
||||||
================================================ =====================================================
|
================================================ ==============================================================
|
||||||
``NEWEST_FIRST_ARCHIVES = True`` Order archives by newest first by date. (False:
|
``NEWEST_FIRST_ARCHIVES = True`` Order archives by newest first by date. (False:
|
||||||
orders by date with older articles first.)
|
orders by date with older articles first.)
|
||||||
``REVERSE_CATEGORY_ORDER = False`` Reverse the category order. (True: lists by reverse
|
``REVERSE_CATEGORY_ORDER = False`` Reverse the category order. (True: lists by reverse
|
||||||
alphabetical order; default lists alphabetically.)
|
alphabetical order; default lists alphabetically.)
|
||||||
================================================ =====================================================
|
``ARTICLE_ORDER_BY = 'reversed-date'`` Defines how the articles (``articles_page.object_list`` in
|
||||||
|
the template) are sorted. Valid options are: metadata as a
|
||||||
|
string (use ``reversed-`` prefix the reverse the sort order),
|
||||||
|
special option ``'basename'`` which will use the basename of
|
||||||
|
the file (without path) or a custom function to extract the
|
||||||
|
sorting key from articles. The default value,
|
||||||
|
``'reversed-date'``, will sort articles by date in reverse
|
||||||
|
order (i.e. newest article comes first).
|
||||||
|
``PAGE_ORDER_BY = 'basename'`` Defines how the pages (``PAGES`` variable in the template)
|
||||||
|
are sorted. Options are same as ``ARTICLE_ORDER_BY``.
|
||||||
|
The default value, ``'basename'`` will sort pages by their
|
||||||
|
basename.
|
||||||
|
================================================ ==============================================================
|
||||||
|
|
||||||
|
|
||||||
Themes
|
Themes
|
||||||
|
|
|
||||||
|
|
@ -561,8 +561,7 @@ class ArticlesGenerator(CachingGenerator):
|
||||||
self.tags[tag].append(article)
|
self.tags[tag].append(article)
|
||||||
for author in getattr(article, 'authors', []):
|
for author in getattr(article, 'authors', []):
|
||||||
self.authors[author].append(article)
|
self.authors[author].append(article)
|
||||||
# sort the articles by date
|
|
||||||
self.articles.sort(key=attrgetter('date'), reverse=True)
|
|
||||||
self.dates = list(self.articles)
|
self.dates = list(self.articles)
|
||||||
self.dates.sort(key=attrgetter('date'),
|
self.dates.sort(key=attrgetter('date'),
|
||||||
reverse=self.context['NEWEST_FIRST_ARCHIVES'])
|
reverse=self.context['NEWEST_FIRST_ARCHIVES'])
|
||||||
|
|
|
||||||
|
|
@ -65,7 +65,7 @@ DEFAULT_CONFIG = {
|
||||||
'OUTPUT_RETENTION': [],
|
'OUTPUT_RETENTION': [],
|
||||||
'ARTICLE_URL': '{slug}.html',
|
'ARTICLE_URL': '{slug}.html',
|
||||||
'ARTICLE_SAVE_AS': '{slug}.html',
|
'ARTICLE_SAVE_AS': '{slug}.html',
|
||||||
'ARTICLE_ORDER_BY': 'slug',
|
'ARTICLE_ORDER_BY': 'reversed-date',
|
||||||
'ARTICLE_LANG_URL': '{slug}-{lang}.html',
|
'ARTICLE_LANG_URL': '{slug}-{lang}.html',
|
||||||
'ARTICLE_LANG_SAVE_AS': '{slug}-{lang}.html',
|
'ARTICLE_LANG_SAVE_AS': '{slug}-{lang}.html',
|
||||||
'DRAFT_URL': 'drafts/{slug}.html',
|
'DRAFT_URL': 'drafts/{slug}.html',
|
||||||
|
|
|
||||||
|
|
@ -387,6 +387,65 @@ class TestArticlesGenerator(unittest.TestCase):
|
||||||
'パイソン', 'マック'])
|
'パイソン', 'マック'])
|
||||||
self.assertEqual(tags, tags_expected)
|
self.assertEqual(tags, tags_expected)
|
||||||
|
|
||||||
|
def test_article_order_by(self):
|
||||||
|
settings = get_settings(filenames={})
|
||||||
|
settings['DEFAULT_CATEGORY'] = 'Default'
|
||||||
|
settings['DEFAULT_DATE'] = (1970, 1, 1)
|
||||||
|
settings['CACHE_CONTENT'] = False # cache not needed for this logic tests
|
||||||
|
settings['ARTICLE_ORDER_BY'] = 'title'
|
||||||
|
|
||||||
|
generator = ArticlesGenerator(
|
||||||
|
context=settings.copy(), settings=settings,
|
||||||
|
path=CONTENT_DIR, theme=settings['THEME'], output_path=None)
|
||||||
|
generator.generate_context()
|
||||||
|
|
||||||
|
expected = [
|
||||||
|
'An Article With Code Block To Test Typogrify Ignore',
|
||||||
|
'Article title',
|
||||||
|
'Article with Nonconformant HTML meta tags',
|
||||||
|
'Article with markdown and summary metadata multi',
|
||||||
|
'Article with markdown and summary metadata single',
|
||||||
|
'Article with markdown containing footnotes',
|
||||||
|
'Article with template',
|
||||||
|
'Rst with filename metadata',
|
||||||
|
'Test Markdown extensions',
|
||||||
|
'Test markdown File',
|
||||||
|
'Test md File',
|
||||||
|
'Test mdown File',
|
||||||
|
'Test mkd File',
|
||||||
|
'This is a super article !',
|
||||||
|
'This is a super article !',
|
||||||
|
'This is a super article !',
|
||||||
|
'This is a super article !',
|
||||||
|
'This is a super article !',
|
||||||
|
'This is a super article !',
|
||||||
|
'This is an article with category !',
|
||||||
|
'This is an article with multiple authors in lastname, firstname format!',
|
||||||
|
'This is an article with multiple authors in list format!',
|
||||||
|
'This is an article with multiple authors!',
|
||||||
|
'This is an article with multiple authors!',
|
||||||
|
'This is an article without category !',
|
||||||
|
'This is an article without category !',
|
||||||
|
'マックOS X 10.8でパイソンとVirtualenvをインストールと設定']
|
||||||
|
|
||||||
|
articles = [article.title for article in generator.articles]
|
||||||
|
self.assertEqual(articles, expected)
|
||||||
|
|
||||||
|
# reversed title
|
||||||
|
settings = get_settings(filenames={})
|
||||||
|
settings['DEFAULT_CATEGORY'] = 'Default'
|
||||||
|
settings['DEFAULT_DATE'] = (1970, 1, 1)
|
||||||
|
settings['CACHE_CONTENT'] = False # cache not needed for this logic tests
|
||||||
|
settings['ARTICLE_ORDER_BY'] = 'reversed-title'
|
||||||
|
|
||||||
|
generator = ArticlesGenerator(
|
||||||
|
context=settings.copy(), settings=settings,
|
||||||
|
path=CONTENT_DIR, theme=settings['THEME'], output_path=None)
|
||||||
|
generator.generate_context()
|
||||||
|
|
||||||
|
articles = [article.title for article in generator.articles]
|
||||||
|
self.assertEqual(articles, list(reversed(expected)))
|
||||||
|
|
||||||
|
|
||||||
class TestPageGenerator(unittest.TestCase):
|
class TestPageGenerator(unittest.TestCase):
|
||||||
# Note: Every time you want to test for a new field; Make sure the test
|
# Note: Every time you want to test for a new field; Make sure the test
|
||||||
|
|
@ -473,6 +532,23 @@ class TestPageGenerator(unittest.TestCase):
|
||||||
pages = self.distill_pages(generator.pages)
|
pages = self.distill_pages(generator.pages)
|
||||||
self.assertEqual(pages_expected_sorted_by_title, pages)
|
self.assertEqual(pages_expected_sorted_by_title, pages)
|
||||||
|
|
||||||
|
# sort by title reversed
|
||||||
|
pages_expected_sorted_by_title = [
|
||||||
|
['This is a test page with a preset template', 'published',
|
||||||
|
'custom'],
|
||||||
|
['This is a test page', 'published', 'page'],
|
||||||
|
['This is a markdown test page', 'published', 'page'],
|
||||||
|
['Page with a bunch of links', 'published', 'page'],
|
||||||
|
['A Page (Test) for sorting', 'published', 'page'],
|
||||||
|
]
|
||||||
|
settings['PAGE_ORDER_BY'] = 'reversed-title'
|
||||||
|
generator = PagesGenerator(
|
||||||
|
context=settings.copy(), settings=settings,
|
||||||
|
path=CUR_DIR, theme=settings['THEME'], output_path=None)
|
||||||
|
generator.generate_context()
|
||||||
|
pages = self.distill_pages(generator.pages)
|
||||||
|
self.assertEqual(pages_expected_sorted_by_title, pages)
|
||||||
|
|
||||||
def test_tag_and_category_links_on_generated_pages(self):
|
def test_tag_and_category_links_on_generated_pages(self):
|
||||||
"""
|
"""
|
||||||
Test to ensure links of the form {tag}tagname and {category}catname
|
Test to ensure links of the form {tag}tagname and {category}catname
|
||||||
|
|
|
||||||
|
|
@ -541,16 +541,28 @@ def process_translations(content_list, order_by=None):
|
||||||
try:
|
try:
|
||||||
index.sort(key=order_by)
|
index.sort(key=order_by)
|
||||||
except Exception:
|
except Exception:
|
||||||
logger.error('Error sorting with function {}'.format(order_by))
|
logger.error('Error sorting with function %s', order_by)
|
||||||
elif order_by == 'basename':
|
elif isinstance(order_by, six.string_types):
|
||||||
index.sort(key=lambda x: os.path.basename(x.source_path or ''))
|
if order_by.startswith('reversed-'):
|
||||||
elif order_by != 'slug':
|
order_reversed = True
|
||||||
try:
|
order_by = order_by.replace('reversed-', '', 1)
|
||||||
index.sort(key=attrgetter(order_by))
|
else:
|
||||||
except AttributeError:
|
order_reversed = False
|
||||||
error_msg = ('There is no "{}" attribute in the item metadata.'
|
|
||||||
'Defaulting to slug order.')
|
if order_by == 'basename':
|
||||||
logger.warning(error_msg.format(order_by))
|
index.sort(key=lambda x: os.path.basename(x.source_path or ''),
|
||||||
|
reverse=order_reversed)
|
||||||
|
# already sorted by slug, no need to sort again
|
||||||
|
elif not (order_by == 'slug' and not order_reversed):
|
||||||
|
try:
|
||||||
|
index.sort(key=attrgetter(order_by),
|
||||||
|
reverse=order_reversed)
|
||||||
|
except AttributeError:
|
||||||
|
logger.warning('There is no "%s" attribute in the item '
|
||||||
|
'metadata. Defaulting to slug order.', order_by)
|
||||||
|
else:
|
||||||
|
logger.warning('Invalid *_ORDER_BY setting (%s).'
|
||||||
|
'Valid options are strings and functions.', order_by)
|
||||||
|
|
||||||
return index, translations
|
return index, translations
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue