M main/templates/assets/style.css => main/templates/assets/style.css +5 -0
@@ 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 {
M main/templates/main/blog_posts.html => main/templates/main/blog_posts.html +21 -27
@@ 43,12 43,6 @@
<h2 itemprop="name">{% if blog_user.posts_page_title %}{{ blog_user.posts_page_title }}{% else %}Posts{% endif %}</h2>
- {% if filter_tag %}
- <p>
- Active tag filter: <strong>{{ filter_tag }}</strong> (<a href="{% url 'post_list' %}">clear filter</a>)
- </p>
- {% endif %}
-
{% if request.user.is_authenticated and request.subdomain == request.user.username and drafts %}
<div class="drafts">
<strong>
@@ 64,25 58,34 @@
</div>
{% endif %}
+ {% if tag_cloud and request.user.show_tags_in_post_list %}
+ <div class="tag-cloud">
+ All tags:
+ {% for tag, url, is_active in tag_cloud %}
+ <a href="{{ url }}" class="tag {% if is_active %}active{% endif %}">{{ tag }}</a>
+ {% endfor %}
+ </div>
+ {% endif %}
+
<ul class="posts">
- {% for p in posts %}
- {% if p.published_at %}
+ {% for item in posts_with_tag_urls %}
+ {% if item.post.published_at %}
<li>
- <a href="{% url 'post_detail' p.slug %}">{{ p.title }}</a>
- {% if p.tag_list and request.user.show_tags_in_post_list %}
- <small>
- —
- <b>Tags</b>:
- {% for tag in p.tag_list %}
- <a href="{% url 'post_list_filter' tag %}" class="tag">{{ tag }}</a>{% if not forloop.last %}, {% endif %}
- {% endfor %}
- </small>
+ <a href="{% url 'post_detail' item.post.slug %}">{{ item.post.title }}</a>
+ {% if item.post.tag_list and request.user.show_tags_in_post_list %}
+ <small>
+ — <b>Tags</b>:
+ {% for tag, url, is_active in item.tag_urls %}
+ <a href="{{ url }}" class="tag {% if is_active %}active{% endif %}">{{ tag }}</a>
+ {% if not forloop.last %}, {% endif %}
+ {% endfor %}
+ </small>
{% endif %}
<small>
—
<b>Published on</b>:
<time datetime="{{ p.published_at|date:'Y-m-d' }}" itemprop="datePublished">
- {{ p.published_at|date:'F j, Y' }}
+ {{ item.post.published_at|date:'F j, Y' }}
</time>
{% if not p.is_published %}
— SCHEDULED
@@ 91,15 94,6 @@
</li>
{% endif %}
{% endfor %}
-
- {% if all_tags and request.user.show_tags_in_post_list %}
- <hr>
- <p><strong>All tags:</strong>
- {% for tag in all_tags %}
- <a href="{% url 'post_list_filter' tag %}" class="tag{% if tag == tag %} active{% endif %}">{{ tag }}</a>{% if not forloop.last %}, {% endif %}
- {% endfor %}
- </p>
- {% endif %}
</ul>
</main>
M main/templates/main/post_list.html => main/templates/main/post_list.html +23 -28
@@ 9,39 9,43 @@
<a href="{% url 'post_create' %}">Create a new post »</a>
</p>
{% if post_list %}
- {% if filter_tag %}
- <p>
- Active tag filter: <strong>{{ filter_tag }}</strong> (<a href="{% url 'post_list' %}">clear filter</a>)
- </p>
+
+ {% if tag_cloud and request.user.show_tags_in_post_list %}
+ <div class="tag-cloud">
+ All tags:
+ {% for tag, url, is_active in tag_cloud %}
+ <a href="{{ url }}" class="tag {% if is_active %}active{% endif %}">{{ tag }}</a>
+ {% endfor %}
+ </div>
{% endif %}
<p>
List of posts:
</p>
<ul>
- {% for post in post_list %}
+ {% for item in posts_with_tag_urls %}
<li>
- <a href="{% url 'post_detail' post.slug %}">
- {{ post.title }}
+ <a href="{% url 'post_detail' item.post.slug %}">
+ {{ item.post.title }}
</a>
- {% if p.tag_list and request.user.show_tags_in_post_list %}
- <small>
- —
- <b>Tags</b>:
- {% for tag in p.tag_list %}
- <a href="{% url 'post_list_filter' tag %}" class="tag">{{ tag }}</a>{% if not forloop.last %}, {% endif %}
- {% endfor %}
- </small>
+ {% if item.post.tag_list and request.user.show_tags_in_post_list %}
+ <small>
+ — <b>Tags</b>:
+ {% for tag, url, is_active in item.tag_urls %}
+ <a href="{{ url }}" class="tag {% if is_active %}active{% endif %}">{{ tag }}</a>
+ {% if not forloop.last %}, {% endif %}
+ {% endfor %}
+ </small>
{% endif %}
<small>
—
<b>Published on</b>:
- {% if post.is_published %}
- <time datetime="{{ post.published_at|date:'Y-m-d' }}" itemprop="datePublished">
- — {{ post.published_at|date:'F j, Y' }}
+ {% if item.post.is_published %}
+ <time datetime="{{ item.itempost.published_at|date:'Y-m-d' }}" itemprop="datePublished">
+ — {{ item.post.published_at|date:'F j, Y' }}
</time>
{% endif %}
- {% if not post.is_published %}
+ {% if not item.post.is_published %}
— DRAFT/SCHEDULED
{% endif %}
</small>
@@ 49,14 53,5 @@
{% endfor %}
</ul>
{% endif %}
-
- {% if all_tags and request.user.show_tags_in_post_list %}
- <hr>
- <p><strong>All tags:</strong>
- {% for tag in all_tags %}
- <a href="{% url 'post_list_dashboard_filter' tag %}" class="tag{% if filter_tag == tag %} active{% endif %}">{{ tag }}</a>{% if not forloop.last %}, {% endif %}
- {% endfor %}
- </p>
- {% endif %}
</main>
{% endblock content %}
M main/urls.py => main/urls.py +0 -2
@@ 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/<slug: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/<slug:tag>/", general.post_list_filter, name="post_list_filter"),
path("blog/<slug:slug>/", general.PostDetail.as_view(), name="post_detail"),
path("posts/<slug:slug>/", general.post_detail_redir, name="post_detail_redir_a"),
path("post/<slug:slug>/", general.post_detail_redir, name="post_detail_redir_b"),
M main/views/general.py => main/views/general.py +87 -63
@@ 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):