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 %} + 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):