diff --git a/docs/settings.rst b/docs/settings.rst index e08291f0..202fc45f 100644 --- a/docs/settings.rst +++ b/docs/settings.rst @@ -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_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 default language. ``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 the same as PAGE_URL or you need to use a rewrite in 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 use the default language. ``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 ================ -================================================ ===================================================== +================================================ ============================================================== Setting name (followed by default value) What does it do? -================================================ ===================================================== +================================================ ============================================================== ``NEWEST_FIRST_ARCHIVES = True`` Order archives by newest first by date. (False: orders by date with older articles first.) ``REVERSE_CATEGORY_ORDER = False`` Reverse the category order. (True: lists by reverse 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 diff --git a/pelican/generators.py b/pelican/generators.py index 3692ef0b..a29555a5 100644 --- a/pelican/generators.py +++ b/pelican/generators.py @@ -561,8 +561,7 @@ class ArticlesGenerator(CachingGenerator): self.tags[tag].append(article) for author in getattr(article, 'authors', []): 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.sort(key=attrgetter('date'), reverse=self.context['NEWEST_FIRST_ARCHIVES']) diff --git a/pelican/settings.py b/pelican/settings.py index 82955277..c1a902cd 100644 --- a/pelican/settings.py +++ b/pelican/settings.py @@ -65,7 +65,7 @@ DEFAULT_CONFIG = { 'OUTPUT_RETENTION': [], 'ARTICLE_URL': '{slug}.html', 'ARTICLE_SAVE_AS': '{slug}.html', - 'ARTICLE_ORDER_BY': 'slug', + 'ARTICLE_ORDER_BY': 'reversed-date', 'ARTICLE_LANG_URL': '{slug}-{lang}.html', 'ARTICLE_LANG_SAVE_AS': '{slug}-{lang}.html', 'DRAFT_URL': 'drafts/{slug}.html', diff --git a/pelican/tests/test_generators.py b/pelican/tests/test_generators.py index 7aac27a2..e93c8c14 100644 --- a/pelican/tests/test_generators.py +++ b/pelican/tests/test_generators.py @@ -387,6 +387,65 @@ class TestArticlesGenerator(unittest.TestCase): 'パイソン', 'マック']) 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): # 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) 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): """ Test to ensure links of the form {tag}tagname and {category}catname diff --git a/pelican/utils.py b/pelican/utils.py index f973a6ad..6ad4de24 100644 --- a/pelican/utils.py +++ b/pelican/utils.py @@ -541,16 +541,28 @@ def process_translations(content_list, order_by=None): try: index.sort(key=order_by) except Exception: - logger.error('Error sorting with function {}'.format(order_by)) - elif order_by == 'basename': - index.sort(key=lambda x: os.path.basename(x.source_path or '')) - elif order_by != 'slug': - try: - index.sort(key=attrgetter(order_by)) - except AttributeError: - error_msg = ('There is no "{}" attribute in the item metadata.' - 'Defaulting to slug order.') - logger.warning(error_msg.format(order_by)) + logger.error('Error sorting with function %s', order_by) + elif isinstance(order_by, six.string_types): + if order_by.startswith('reversed-'): + order_reversed = True + order_by = order_by.replace('reversed-', '', 1) + else: + order_reversed = False + + if order_by == 'basename': + 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