blog-browse #40

Merged
PlexSheep merged 17 commits from blog-browse into devel 2023-10-08 14:46:09 +02:00
4 changed files with 168 additions and 61 deletions
Showing only changes of commit 21f3b19111 - Show all commits

View File

@ -32,11 +32,17 @@
</div>
<div class="col-sm-3">
<div class="container-fluid w-100 h-100 p-2">
<form class="w-100 h-100" role="search" action="" method="GET" novalidate>
<form class="w-100 h-100"
role="search"
action=""
method="GET"
novalidate
id="filter-form">
<div class="py-2">
<input type="search"
name="search"
class="form-control flex-fill py-2"
defaultValue=""
aria-label="Search"
placeholder="{% trans "Search" %}"
required=""
@ -46,6 +52,7 @@
<div class="py-2">
<select class="form-select py-2"
aria-label="Large select example"
defaultValue=""
name="category">
<option value="">{% trans "select category" %}</option>
{% for category in categories %}
@ -57,6 +64,7 @@
<div class="py-2">
<input class="tagify"
name="keywords"
defaultValue=""
placeholder="{% trans "Keywords" %}"
{% if filters.keywords %} value="{% for keyword in filters.keywords %}{{ keyword }} {% endfor %}
"
@ -65,9 +73,11 @@
<div class="row">
<div class="col">
<div class="py-2">
<input id="clear-button"
type="reset"
class="btn bg-primary fw-bold py-2 float-end w-100">
<!-- will reset to the filters encoded in
the url by GET attributes, not to the empty
form. -->
<input type="reset"
class="btn bg-primary fw-bold py-2 float-end w-100 reset-empty-button">
</div>
</div>
<div class="col">
@ -91,61 +101,69 @@
<div class="row mb-5">
<div class="container-fluid p-0 w-100 h-100">
<div class="row gap-3">
{# TODO: display some info if the queryset is empty #}
{# TODO: pagination #}
{% for post in posts %}
<div class="card col mx-auto my-2 py-2" style="min-width: 400px;">
<a class="text-reset link-offset-2 link-underline link-underline-opacity-0"
href=" {% url 'blog:post' post.category.slug post.slug %}">
<img src="{{ post.thumbnail.url }}"
class="card-img-top img-fluid mx-auto d-block"
style="max-height: 150px;
width: auto;
padding-top: 8px"
alt="thumbnail" />
<div class="card-body" style="height: 100px">
{% if LANGUAGE_CODE == "de" %}
<h5 class="card-title">
{{ post.title_de }}<small class="text-body-secondary">{{ post.subtitle }}</small>
</h5>
<p class="card-text">{{ post.desc_de }}</p>
{% elif LANGUAGE_CODE == "en" %}
<h5 class="card-title">
{{ post.title_en }}<small class="text-body-secondary">{{ post.subtitle }}</small>
</h5>
<p class="card-text">{{ post.desc_en }}</p>
{% else %}
<h5 class="card-title">
{{ post.title_en }}<small class="text-body-secondary">{{ post.subtitle }}</small>
</h5>
<p class="card-text">{{ post.desc_en }}</p>
{% endif %}
</div>
<div class="container pt-5">
<ul class="list-group list-group-flush">
<li class="list-group-item">
{% translate "category" %}: <b>{{ post.category.name }}</b>
</li>
{% for keyword in post.keywords.all %}
{% if LANGUAGE_CODE == "de" %}
<li class="list-group-item">{{ keyword.text_de }}</li>
{% elif LANGUAGE_CODE == "en" %}
<li class="list-group-item">{{ keyword.text_en }}</li>
{% else %}
<li class="list-group-item">{{ keyword.text_en }}</li>
{% endif %}
{% endfor %}
</ul>
</div>
<div class="container p-1 text-center" style="border-top: solid">
<li class="list-group-item">
{% format_time post.date "%F" as date %}
{% trans "published" %}: {{ date }}
</li>
</div>
</a>
{% if is_empty %}
<div class="text-center">
<img src="/media/img/http/404.svg"
class="img-fluid pb-5 pt-2 darkmode-invert"
style="max-height: 650px;"
alt="404" />
<h2 class="display-5">{% trans "No posts found for your filters." %}</h2>
</div>
{% endfor %}
{% else %}
{% for post in posts %}
<div class="card col mx-auto my-2 py-2" style="min-width: 400px;">
<a class="text-reset link-offset-2 link-underline link-underline-opacity-0"
href=" {% url 'blog:post' post.category.slug post.slug %}">
<img src="{{ post.thumbnail.url }}"
class="card-img-top img-fluid mx-auto d-block"
style="max-height: 150px;
width: auto;
padding-top: 8px"
alt="thumbnail" />
<div class="card-body" style="height: 100px">
{% if LANGUAGE_CODE == "de" %}
<h5 class="card-title">
{{ post.title_de }}<small class="text-body-secondary">{{ post.subtitle }}</small>
</h5>
<p class="card-text">{{ post.desc_de }}</p>
{% elif LANGUAGE_CODE == "en" %}
<h5 class="card-title">
{{ post.title_en }}<small class="text-body-secondary">{{ post.subtitle }}</small>
</h5>
<p class="card-text">{{ post.desc_en }}</p>
{% else %}
<h5 class="card-title">
{{ post.title_en }}<small class="text-body-secondary">{{ post.subtitle }}</small>
</h5>
<p class="card-text">{{ post.desc_en }}</p>
{% endif %}
</div>
<div class="container pt-5">
<ul class="list-group list-group-flush">
<li class="list-group-item">
{% translate "category" %}: <b>{{ post.category.name }}</b>
</li>
{% for keyword in post.keywords.all %}
{% if LANGUAGE_CODE == "de" %}
<li class="list-group-item">{{ keyword.text_de }}</li>
{% elif LANGUAGE_CODE == "en" %}
<li class="list-group-item">{{ keyword.text_en }}</li>
{% else %}
<li class="list-group-item">{{ keyword.text_en }}</li>
{% endif %}
{% endfor %}
</ul>
</div>
<div class="container p-1 text-center" style="border-top: solid">
<li class="list-group-item">
{% format_time post.date "%F" as date %}
{% trans "published" %}: {{ date }}
</li>
</div>
</a>
</div>
{% endfor %}
{% endif %}
</div>
</div>
</div>

View File

@ -1,4 +1,8 @@
from django.shortcuts import Http404, HttpResponse, get_object_or_404, render
import json
import ast
from django.shortcuts import get_object_or_404, render
from django.http.response import Http404, HttpResponse
from django.http.request import HttpRequest
from django.utils.translation import get_language
from django.views.generic import TemplateView, DetailView, ListView, View
from django.db.models import Q
@ -9,8 +13,6 @@ from start.views import SearchableView
import logging
logger = logging.getLogger(__name__)
import ast
import json
class Index(TemplateView, SearchableView):
"""
@ -55,6 +57,7 @@ class Browse(ListView):
model = BlogPost
template_name = "blog/browse.html"
context_object_name = "posts"
allow_empty = False # but we have a special get method
def get_queryset(self):
objects = BlogPost.objects.all()
@ -123,3 +126,27 @@ class Browse(ListView):
keywords = keywords.split('+')
context["filters"]["keywords"] = keywords
return context
def get(self, request: HttpRequest, *args, **kwargs) -> HttpResponse:
self.object_list = self.get_queryset()
allow_empty = self.get_allow_empty()
if not allow_empty:
# When pagination is enabled and object_list is a queryset,
# it's better to do a cheap query than to load the unpaginated
# queryset in memory.
if self.get_paginate_by(self.object_list) is not None and hasattr(
self.object_list, 'exists'):
is_empty = not self.object_list.exists()
else:
is_empty = not self.object_list
else:
is_empty = False
context = self.get_context_data()
context["is_empty"] = is_empty
response = self.render_to_response(context)
if is_empty:
response.status_code = 404
return response

View File

@ -0,0 +1,50 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
width="557.15094"
height="210.8916"
viewBox="0 0 557.15094 210.8916"
version="1.1"
id="svg1"
inkscape:export-filename="404.svg"
inkscape:export-xdpi="96"
inkscape:export-ydpi="96"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<sodipodi:namedview
id="namedview1"
pagecolor="#ffffff"
bordercolor="#000000"
borderopacity="0.25"
inkscape:showpageshadow="2"
inkscape:pageopacity="0.0"
inkscape:pagecheckerboard="0"
inkscape:deskcolor="#d1d1d1"
inkscape:document-units="px" />
<defs
id="defs1">
<rect
x="292.2478"
y="154.0531"
width="462.1593"
height="240.14159"
id="rect1" />
</defs>
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1"
transform="translate(-239.50485,-280.19422)">
<text
xml:space="preserve"
id="text1"
style="font-style:italic;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:24.5524px;font-family:'Source Code Pro';-inkscape-font-specification:'Source Code Pro, Bold Italic';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;white-space:pre;shape-inside:url(#rect1);fill:#3b3b3b;fill-opacity:1"
transform="matrix(13.034066,0,0,13.034066,-3571.9156,-1800.7033)"><tspan
x="292.24805"
y="175.53608"
id="tspan2">404</tspan></text>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.7 KiB

View File

@ -2,6 +2,18 @@
// dark theme stuff
const setTheme = (theme) => {
document.documentElement.setAttribute("data-bs-theme", theme);
if (theme === "dark") {
var to_invert = document.querySelectorAll('.darkmode-invert');
to_invert.forEach(element => {
element.style.filter = "invert(100%)";
});
}
if (theme === "light") {
var to_invert = document.querySelectorAll('.darkmode-invert');
to_invert.forEach(element => {
element.style.filter = "invert(0%)";
});
}
};
setTheme(localStorage.getItem("theme"));
document.getElementById("toggleThemeButton").onclick = function () {