Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add option require_all_tags to post-list directive. #2665

Merged
merged 3 commits into from Feb 16, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
3 changes: 3 additions & 0 deletions CHANGES.txt
Expand Up @@ -10,6 +10,9 @@ Features
``OPTIPNG_EXECUTABLE``, ``JPEGOPTIM_EXECUTABLE`` and
``HTML_TIDY_EXECUTABLE`` to configure executables for built-in filters.
(Issue #2615)
* Add a ``require_all_tags`` parameter to the ``post-list`` directive to
show only posts that have all specified tags.


Bugfixes
--------
Expand Down
4 changes: 4 additions & 0 deletions docs/manual.txt
Expand Up @@ -2355,6 +2355,10 @@ The following options are recognized:
Filter posts to show only posts having at least one of the ``tags``.
Defaults to None.

* ``require_all_tags`` : flag
Change tag filter behaviour to show only posts that have all specified ``tags``.
Defaults to False.

* ``categories`` : string [, string...]
Filter posts to show only posts having one of the ``categories``.
Defaults to None.
Expand Down
36 changes: 21 additions & 15 deletions nikola/plugins/compile/rest/post_list.py
Expand Up @@ -31,6 +31,7 @@
import os
import uuid
import natsort
import operator

from docutils import nodes
from docutils.parsers.rst import Directive, directives
Expand Down Expand Up @@ -104,6 +105,10 @@ class PostList(Directive):
Filter posts to show only posts having at least one of the ``tags``.
Defaults to None.

``require_all_tags`` : flag
Change tag filter behaviour to show only posts that have all specified ``tags``.
Defaults to False.

``categories`` : string [, string...]
Filter posts to show only posts having one of the ``categories``.
Defaults to None.
Expand Down Expand Up @@ -143,6 +148,7 @@ class PostList(Directive):
'reverse': directives.flag,
'sort': directives.unchanged,
'tags': directives.unchanged,
'require_all_tags': directives.flag,
'categories': directives.unchanged,
'sections': directives.unchanged,
'slugs': directives.unchanged,
Expand All @@ -161,6 +167,7 @@ def run(self):
stop = self.options.get('stop')
reverse = self.options.get('reverse', False)
tags = self.options.get('tags')
require_all_tags = not self.options.get('require_all_tags', True) # a flag returns None if declared
categories = self.options.get('categories')
sections = self.options.get('sections')
slugs = self.options.get('slugs')
Expand All @@ -172,7 +179,7 @@ def run(self):
sort = self.options.get('sort')
date = self.options.get('date')

output, deps = _do_post_list(start, stop, reverse, tags, categories, sections, slugs, post_type, type,
output, deps = _do_post_list(start, stop, reverse, tags, require_all_tags, categories, sections, slugs, post_type, type,
all, lang, template, sort, state=self.state, site=self.site, date=date)
self.state.document.settings.record_dependencies.add("####MAGIC####TIMELINE")
for d in deps:
Expand All @@ -183,7 +190,7 @@ def run(self):
return []


def _do_post_list(start=None, stop=None, reverse=False, tags=None, categories=None,
def _do_post_list(start=None, stop=None, reverse=False, tags=None, require_all_tags=False, categories=None,
sections=None, slugs=None, post_type='post', type=False, all=False,
lang=None, template='post_list_directive.tmpl', sort=None,
id=None, data=None, state=None, site=None, date=None, filename=None, post=None):
Expand All @@ -210,7 +217,6 @@ def _do_post_list(start=None, stop=None, reverse=False, tags=None, categories=No
stop = int(stop)

# Parse tags/categories/sections/slugs (input is strings)
tags = [t.strip().lower() for t in tags.split(',')] if tags else []
categories = [c.strip().lower() for c in categories.split(',')] if categories else []
sections = [s.strip().lower() for s in sections.split(',')] if sections else []
slugs = [s.strip() for s in slugs.split(',')] if slugs else []
Expand Down Expand Up @@ -246,18 +252,18 @@ def _do_post_list(start=None, stop=None, reverse=False, tags=None, categories=No
if sections:
timeline = [p for p in timeline if p.section_name(lang).lower() in sections]

for post in timeline:
if tags:
cont = True
tags_lower = [t.lower() for t in post.tags]
for tag in tags:
if tag in tags_lower:
cont = False

if cont:
continue

filtered_timeline.append(post)
if tags:
tags = {t.strip().lower() for t in tags.split(',')}
if require_all_tags:
compare = set.issubset
else:
compare = operator.and_
for post in timeline:
post_tags = {t.lower() for t in post.tags}
if compare(tags, post_tags):
filtered_timeline.append(post)
else:
filtered_timeline = timeline

if sort:
filtered_timeline = natsort.natsorted(filtered_timeline, key=lambda post: post.meta[lang][sort], alg=natsort.ns.F | natsort.ns.IC)
Expand Down