From 849fe77446d4c2485f9f0409f02cbbc7c3cfa756 Mon Sep 17 00:00:00 2001
From: Jordan Robinson
Date: Wed, 24 Sep 2025 22:41:41 +0100
Subject: [PATCH] update tags filtering functionality to allow for multiple
tags to be filtered on
---
main/templates/assets/style.css | 5 +
main/templates/main/blog_posts.html | 48 ++++-----
main/templates/main/post_list.html | 51 +++++-----
main/urls.py | 2 -
main/views/general.py | 150 ++++++++++++++++------------
5 files changed, 136 insertions(+), 120 deletions(-)
diff --git a/main/templates/assets/style.css b/main/templates/assets/style.css
index cd876dd038cbe80d62a9d4ac5734b254f1d7b61c..b531b398e03bb47ca328b62443c234fbc97885fd 100644
--- a/main/templates/assets/style.css
+++ b/main/templates/assets/style.css
@@ -320,6 +320,11 @@ footer {
text-decoration: dotted underline;
}
+.active {
+ font-weight: 700;
+ text-decoration: underline;
+}
+
/* mods
* they override specific classes with specific styles */
.type-approve {
diff --git a/main/templates/main/blog_posts.html b/main/templates/main/blog_posts.html
index 308884fda6766e957758b42979ca8fb483ad4570..2c021025c99e4b81f43316a3701e1702c587dd48 100644
--- a/main/templates/main/blog_posts.html
+++ b/main/templates/main/blog_posts.html
@@ -43,12 +43,6 @@
{% if blog_user.posts_page_title %}{{ blog_user.posts_page_title }}{% else %}Posts{% endif %}
- {% if filter_tag %}
-
- Active tag filter: {{ filter_tag }} (clear filter)
-
- {% endif %}
-
{% if request.user.is_authenticated and request.subdomain == request.user.username and drafts %}
@@ -64,25 +58,34 @@
{% endif %}
+ {% if tag_cloud and request.user.show_tags_in_post_list %}
+
+ All tags:
+ {% for tag, url, is_active in tag_cloud %}
+ {{ tag }}
+ {% endfor %}
+
+ {% endif %}
+
- {% for p in posts %}
- {% if p.published_at %}
+ {% for item in posts_with_tag_urls %}
+ {% if item.post.published_at %}
- {{ p.title }}
- {% if p.tag_list and request.user.show_tags_in_post_list %}
-
- —
- Tags:
- {% for tag in p.tag_list %}
- {{ tag }}{% if not forloop.last %}, {% endif %}
- {% endfor %}
-
+ {{ item.post.title }}
+ {% if item.post.tag_list and request.user.show_tags_in_post_list %}
+
+ — Tags:
+ {% for tag, url, is_active in item.tag_urls %}
+ {{ tag }}
+ {% if not forloop.last %}, {% endif %}
+ {% endfor %}
+
{% endif %}
—
Published on:
{% if not p.is_published %}
— SCHEDULED
@@ -91,15 +94,6 @@
{% endif %}
{% endfor %}
-
- {% if all_tags and request.user.show_tags_in_post_list %}
-
-
All tags:
- {% for tag in all_tags %}
- {{ tag }}{% if not forloop.last %}, {% endif %}
- {% endfor %}
-
- {% endif %}
diff --git a/main/templates/main/post_list.html b/main/templates/main/post_list.html
index ca4b268f601fb796a4781e4c44fbac98413ce658..cccc90c2e2c02ba906dafd3f6884165217ae9ce4 100644
--- a/main/templates/main/post_list.html
+++ b/main/templates/main/post_list.html
@@ -9,39 +9,43 @@
Create a new post »
{% if post_list %}
- {% if filter_tag %}
-
- Active tag filter: {{ filter_tag }} (clear filter)
-
+
+ {% if tag_cloud and request.user.show_tags_in_post_list %}
+
+ All tags:
+ {% for tag, url, is_active in tag_cloud %}
+ {{ tag }}
+ {% endfor %}
+
{% endif %}
List of posts:
- {% for post in post_list %}
+ {% for item in posts_with_tag_urls %}
-
- {{ post.title }}
+
+ {{ item.post.title }}
- {% if p.tag_list and request.user.show_tags_in_post_list %}
-
- —
- Tags:
- {% for tag in p.tag_list %}
- {{ tag }}{% if not forloop.last %}, {% endif %}
- {% endfor %}
-
+ {% if item.post.tag_list and request.user.show_tags_in_post_list %}
+
+ — Tags:
+ {% for tag, url, is_active in item.tag_urls %}
+ {{ tag }}
+ {% if not forloop.last %}, {% endif %}
+ {% endfor %}
+
{% endif %}
—
Published on:
- {% if post.is_published %}
-
@@ -49,14 +53,5 @@
{% endfor %}
{% endif %}
-
- {% if all_tags and request.user.show_tags_in_post_list %}
-
-
All tags:
- {% for tag in all_tags %}
- {{ tag }}{% if not forloop.last %}, {% endif %}
- {% endfor %}
-
- {% endif %}
{% endblock content %}
diff --git a/main/urls.py b/main/urls.py
index fcfa5d98f21df27755cb4291ba630486772ba222..6eb13b9a9a79f96c40b4860a0b5f8d057fb2a24a 100644
--- a/main/urls.py
+++ b/main/urls.py
@@ -80,7 +80,6 @@ urlpatterns += [
# blog posts and post snapshots
urlpatterns += [
path("posts-workshop/", general.PostList.as_view(), name="post_list_dashboard"),
- path("posts-workshop/tag//", general.PostList.as_view(), name="post_list_dashboard_filter"),
path(
"post-backups/create/", general.SnapshotCreate.as_view(), name="snapshot_create"
),
@@ -93,7 +92,6 @@ urlpatterns += [
),
path("new/post/", general.PostCreate.as_view(), name="post_create"),
path("posts/", general.post_list, name="post_list"),
- path("posts/tag//", general.post_list_filter, name="post_list_filter"),
path("blog//", general.PostDetail.as_view(), name="post_detail"),
path("posts//", general.post_detail_redir, name="post_detail_redir_a"),
path("post//", general.post_detail_redir, name="post_detail_redir_b"),
diff --git a/main/views/general.py b/main/views/general.py
index bead95ecf043b9740a0c98609b348022f5e5b0f2..41247b19aa76b8834f40b2356dd6d8b3d98d3cc8 100644
--- a/main/views/general.py
+++ b/main/views/general.py
@@ -141,59 +141,44 @@ def post_list(request):
license_url = request.build_absolute_uri(reverse('rsl_license'))
- response = render(
- request,
- "main/blog_posts.html",
- {
- "subdomain": request.subdomain,
- "blog_user": request.blog_user,
- "posts": posts,
- "drafts": drafts,
- "pages": models.Page.objects.filter(
- owner=request.blog_user, is_hidden=False
- ).defer("body"),
- "license_url": license_url,
- "all_tags": models.Post.objects.all_unique_tags()
- },
- )
-
- # add Link header for license if applicable
- obj, created = models.ReallySimpleLicensing.objects.get_or_create(user=request.blog_user)
- if request.blog_user.reallysimplelicensing.license and request.blog_user.reallysimplelicensing.show_http:
- response["Link"] = f'<{license_url}>; rel="license"; type="application/rsl+xml"'
-
- return response
- else:
- return redirect("//" + settings.CANONICAL_HOST + reverse("index"))
- else:
- if request.user.is_authenticated:
- return redirect("post_list_dashboard")
-
- return render(request, "404.html")
-
-def post_list_filter(request, tag = None):
- if hasattr(request, "subdomain"):
- if models.User.objects.filter(username=request.subdomain).exists():
- drafts = []
- if request.user.is_authenticated and request.user == request.blog_user:
- posts = models.Post.objects.filter(Q(tags__regex=rf"(^|,){re.escape(tag)}(,|$)"),owner=request.blog_user).defer(
- "body",
- )
- drafts = models.Post.objects.filter(
- owner=request.blog_user,
- published_at__isnull=True,
- tags__icontains=tag,
- ).defer("body")
- else:
- models.AnalyticPage.objects.create(user=request.blog_user, path="index")
- posts = models.Post.objects.filter(
- Q(tags__regex=rf"(^|,){re.escape(tag)}(,|$)"),
- owner=request.blog_user,
- published_at__isnull=False,
- published_at__lte=timezone.now().date(),
- ).defer("body")
-
- license_url = request.build_absolute_uri(reverse('rsl_license'))
+ # active tags as list
+ active_tags = request.GET.get("tags", "")
+ active_tags = [t.strip() for t in active_tags.split(",") if t.strip()]
+
+ # all unique tags for the user
+ all_tags = models.Post.objects.filter(
+ owner=request.user
+ ).all_unique_tags()
+
+ # generate tag cloud with URLs
+ tag_cloud = []
+ for tag in all_tags:
+ if tag in active_tags:
+ remaining_tags = [t for t in active_tags if t != tag]
+ url = f"{reverse('post_list')}?tags={','.join(remaining_tags)}" if remaining_tags else reverse('post_list')
+ is_active = True
+ else:
+ new_tags = active_tags + [tag]
+ url = f"{reverse('post_list')}?tags={','.join(new_tags)}"
+ is_active = False
+ tag_cloud.append((tag, url, is_active))
+
+ # generate tag URLs for each post
+ posts_with_tag_urls = []
+ for post in posts:
+ tag_urls = []
+ for tag in post.tag_list:
+ if tag in active_tags:
+ remaining = [t for t in active_tags if t != tag]
+ url = f"{reverse('post_list')}?tags={','.join(remaining)}" if remaining else reverse('post_list')
+ tag_urls.append((tag, url, True))
+ else:
+ url = f"{reverse('post_list')}?tags={','.join(active_tags + [tag])}"
+ tag_urls.append((tag, url, False))
+ posts_with_tag_urls.append({
+ "post": post,
+ "tag_urls": tag_urls
+ })
response = render(
request,
@@ -201,14 +186,13 @@ def post_list_filter(request, tag = None):
{
"subdomain": request.subdomain,
"blog_user": request.blog_user,
- "posts": posts,
"drafts": drafts,
"pages": models.Page.objects.filter(
owner=request.blog_user, is_hidden=False
).defer("body"),
"license_url": license_url,
- "filter_tag": tag,
- "all_tags": models.Post.objects.all_unique_tags()
+ "posts_with_tag_urls": posts_with_tag_urls,
+ "tag_cloud": tag_cloud,
},
)
@@ -225,26 +209,66 @@ def post_list_filter(request, tag = None):
return redirect("post_list_dashboard")
return render(request, "404.html")
-
+
class PostList(LoginRequiredMixin, ListView):
model = models.Post
def get_queryset(self):
qs = models.Post.objects.filter(owner=self.request.user)
- #tag = self.request.GET.get("tag")
- tag = self.kwargs.get("tag")
- if tag:
- qs = qs.with_tag(tag)
+ tags = self.request.GET.get("tags")
+ if tags:
+ tag_list = [t.strip() for t in tags.split(",") if t.strip()]
+ for tag in tag_list:
+ qs = qs.with_tag(tag)
return qs
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
- context["filter_tag"] = self.kwargs.get("tag")
- context["all_tags"] = models.Post.objects.filter(
+
+ # active tags as list
+ active_tags = self.request.GET.get("tags", "")
+ active_tags = [t.strip() for t in active_tags.split(",") if t.strip()]
+
+ # all unique tags for the user
+ all_tags = models.Post.objects.filter(
owner=self.request.user
).all_unique_tags()
+
+ # generate tag cloud with URLs
+ tag_cloud = []
+ for tag in all_tags:
+ if tag in active_tags:
+ remaining_tags = [t for t in active_tags if t != tag]
+ url = f"{reverse('post_list_dashboard')}?tags={','.join(remaining_tags)}" if remaining_tags else reverse('post_list_dashboard')
+ is_active = True
+ else:
+ new_tags = active_tags + [tag]
+ url = f"{reverse('post_list_dashboard')}?tags={','.join(new_tags)}"
+ is_active = False
+ tag_cloud.append((tag, url, is_active))
+
+ context["tag_cloud"] = tag_cloud
+
+ # generate tag URLs for each post
+ posts_with_tag_urls = []
+ for post in context['object_list']:
+ tag_urls = []
+ for tag in post.tag_list:
+ if tag in active_tags:
+ remaining = [t for t in active_tags if t != tag]
+ url = f"{reverse('post_list_dashboard')}?tags={','.join(remaining)}" if remaining else reverse('post_list_dashboard')
+ tag_urls.append((tag, url, True))
+ else:
+ url = f"{reverse('post_list_dashboard')}?tags={','.join(active_tags + [tag])}"
+ tag_urls.append((tag, url, False))
+ posts_with_tag_urls.append({
+ "post": post,
+ "tag_urls": tag_urls
+ })
+
+ context["posts_with_tag_urls"] = posts_with_tag_urls
return context
def domain_check(request):