special-gallery: scope image prev/next/back to special galleries; add tests

This commit is contained in:
2026-03-27 21:04:18 -04:00
parent 0e48a5d9bd
commit 21a3ab40c5
6 changed files with 145 additions and 63 deletions

View File

@@ -64,15 +64,15 @@ def append_query(url, query_dict):
return url + "?" + urlencode(query_dict)
def gallery_url(path_obj=None, is_dir=False, query_dict=None):
def gallery_url(path_obj=None, is_dir=False, query_dict=None, special=None):
if query_dict is None:
query_dict = {}
if path_obj is None:
base_url = "/gallery/"
base_url = f"/gallery/{special}/" if special is not None else "/gallery/"
else:
path_text = str(path_obj).replace("\\", "/")
base_url = "/gallery/" + path_text
base_url = (f"/gallery/{special}/" if special is not None else "/gallery/") + path_text
if is_dir and not base_url.endswith("/"):
base_url += "/"

View File

@@ -122,7 +122,7 @@ def render_directory(request, path_text, full_path, special=None):
image_data = []
for image in images:
rel_path = image.relative_to(settings.GALLERY_ROOT)
image_url = gallery_url(rel_path, False, query_state)
image_url = gallery_url(rel_path, False, query_state, special)
thumbnail = None
try:

View File

@@ -115,11 +115,22 @@ def render_image(request, path_text, full_path, special=None):
next_url = None
if prev_path is not None:
rel = prev_path.relative_to(settings.GALLERY_ROOT)
prev_url = gallery_url(rel, False, query_state)
# When viewing from a special gallery, route prev/next through the
# special gallery URL so subsequent navigation preserves the special
# gallery context. Otherwise use the normal gallery URL for the file.
if special is not None:
rel_text = str(rel).replace("\\", "/")
prev_url = append_query(f"/gallery/{special}/{rel_text}/", query_state)
else:
prev_url = gallery_url(rel, False, query_state)
if next_path is not None:
rel = next_path.relative_to(settings.GALLERY_ROOT)
next_url = gallery_url(rel, False, query_state)
if special is not None:
rel_text = str(rel).replace("\\", "/")
next_url = append_query(f"/gallery/{special}/{rel_text}/", query_state)
else:
next_url = gallery_url(rel, False, query_state)
# Back (directory) and Home (root) links and thumbnails
dir_rel = None

View File

@@ -49,9 +49,11 @@
<a href="/gallery/most-visited/" class="sidebar-link {% if active_special == 'most-visited' %}active-sort{% endif %}">Most visited</a>
<a href="/gallery/recent/" class="sidebar-link {% if active_special == 'recent' %}active-sort{% endif %}">Recently visited</a>
{% if path != '' %}
{% if path != '' or is_special %}
<hr>
{% endif %}
{% if path != '' %}
<a href="{{ back_url }}" class="subdir-item">
{% if back_thumb %}
<img src="{{ back_thumb }}" class="subdir-thumb" alt="back thumb">
@@ -60,7 +62,9 @@
{% endif %}
<span>Back</span>
</a>
{% endif %}
{% if path != '' or is_special %}
<a href="{{ home_url }}" class="subdir-item">
{% if home_thumb %}
<img src="{{ home_thumb }}" class="subdir-thumb" alt="home thumb">
@@ -125,13 +129,15 @@
<hr>
<a href="/gallery/favorites/" class="sidebar-link">Favorites</a>
<a href="/gallery/most-visited/" class="sidebar-link">Most visited</a>
<a href="/gallery/recent/" class="sidebar-link">Recently visited</a>
<a href="/gallery/favorites/" class="sidebar-link {% if active_special == 'favorites' %}active-sort{% endif %}">Favorites</a>
<a href="/gallery/most-visited/" class="sidebar-link {% if active_special == 'most-visited' %}active-sort{% endif %}">Most visited</a>
<a href="/gallery/recent/" class="sidebar-link {% if active_special == 'recent' %}active-sort{% endif %}">Recently visited</a>
{% if path != '' or is_special %}
<hr>
{% endif %}
{% if path != '' %}
<hr>
<a href="{{ back_url }}" class="subdir-item">
{% if back_thumb %}
<img src="{{ back_thumb }}" class="subdir-thumb" alt="back thumb">
@@ -140,7 +146,9 @@
{% endif %}
<span>Back</span>
</a>
{% endif %}
{% if path != '' or is_special %}
<a href="{{ home_url }}" class="subdir-item">
{% if home_thumb %}
<img src="{{ home_thumb }}" class="subdir-thumb" alt="home thumb">
@@ -265,13 +273,15 @@
<hr>
<a href="#" class="sidebar-link">Favorites</a>
<a href="#" class="sidebar-link">Most visited</a>
<a href="#" class="sidebar-link">Recently visited</a>
<a href="/gallery/favorites/" class="sidebar-link {% if active_special == 'favorites' %}active-sort{% endif %}">Favorites</a>
<a href="/gallery/most-visited/" class="sidebar-link {% if active_special == 'most-visited' %}active-sort{% endif %}">Most visited</a>
<a href="/gallery/recent/" class="sidebar-link {% if active_special == 'recent' %}active-sort{% endif %}">Recently visited</a>
{% if path != '' or is_special %}
<hr>
{% endif %}
{% if path != '' %}
<hr>
<a href="{{ back_url }}" class="subdir-item">
{% if back_thumb %}
<img src="{{ back_thumb }}" class="subdir-thumb" alt="back thumb">
@@ -280,7 +290,9 @@
{% endif %}
<span>Back</span>
</a>
{% endif %}
{% if path != '' or is_special %}
<a href="{{ home_url }}" class="subdir-item">
{% if home_thumb %}
<img src="{{ home_thumb }}" class="subdir-thumb" alt="home thumb">

View File

@@ -36,46 +36,59 @@
<hr>
<div class="sidebar-scroll flex-grow-1">
{% if prev_url %}
<a href="{{ prev_url }}" class="subdir-item">
{% if prev_thumb %}
<img src="{{ prev_thumb }}" class="subdir-thumb" alt="prev thumb">
{% else %}
<span class="subdir-fallback"><i class="fa-solid fa-image"></i></span>
{% endif %}
<span>Previous</span>
</a>
{% else %}
<div class="subdir-item text-muted">
<span class="subdir-fallback"><i class="fa-solid fa-image"></i></span>
<span>Previous</span>
</div>
{% endif %}
{% if next_url %}
<a href="{{ next_url }}" class="subdir-item">
{% if next_thumb %}
<img src="{{ next_thumb }}" class="subdir-thumb" alt="next thumb">
{% else %}
<span class="subdir-fallback"><i class="fa-solid fa-image"></i></span>
{% endif %}
<span>Next</span>
</a>
{% else %}
<div class="subdir-item text-muted">
<span class="subdir-fallback"><i class="fa-solid fa-image"></i></span>
<span>Next</span>
</div>
{% endif %}
<a href="{{ back_url }}" class="subdir-item">
{% if back_thumb %}
<img src="{{ back_thumb }}" class="subdir-thumb" alt="back thumb">
{% if not is_special or is_special and 'favorites' in breadcrumbs.0.path %}
{% if prev_url %}
<a href="{{ prev_url }}" class="subdir-item">
{% if prev_thumb %}
<img src="{{ prev_thumb }}" class="subdir-thumb" alt="prev thumb">
{% else %}
<span class="subdir-fallback"><i class="fa-solid fa-image"></i></span>
{% endif %}
<span>Previous</span>
</a>
{% else %}
<span class="subdir-fallback"><i class="fa-solid fa-image"></i></span>
<div class="subdir-item text-muted">
<span class="subdir-fallback"><i class="fa-solid fa-image"></i></span>
<span>Previous</span>
</div>
{% endif %}
<span>Back</span>
</a>
{% if next_url %}
<a href="{{ next_url }}" class="subdir-item">
{% if next_thumb %}
<img src="{{ next_thumb }}" class="subdir-thumb" alt="next thumb">
{% else %}
<span class="subdir-fallback"><i class="fa-solid fa-image"></i></span>
{% endif %}
<span>Next</span>
</a>
{% else %}
<div class="subdir-item text-muted">
<span class="subdir-fallback"><i class="fa-solid fa-image"></i></span>
<span>Next</span>
</div>
{% endif %}
{% endif %}
{% if is_special %}
<a href="{{ breadcrumbs.0.path }}" class="subdir-item">
{% if back_thumb %}
<img src="{{ back_thumb }}" class="subdir-thumb" alt="back thumb">
{% else %}
<span class="subdir-fallback"><i class="fa-solid fa-image"></i></span>
{% endif %}
<span>Back</span>
</a>
{% else %}
<a href="{{ back_url }}" class="subdir-item">
{% if back_thumb %}
<img src="{{ back_thumb }}" class="subdir-thumb" alt="back thumb">
{% else %}
<span class="subdir-fallback"><i class="fa-solid fa-image"></i></span>
{% endif %}
<span>Back</span>
</a>
{% endif %}
<a href="{{ home_url }}" class="subdir-item">
{% if home_thumb %}
@@ -221,14 +234,25 @@
</a>
{% endif %}
<a href="{{ back_url }}" class="subdir-item">
{% if back_thumb %}
<img src="{{ back_thumb }}" class="subdir-thumb" alt="back thumb">
{% else %}
<span class="subdir-fallback"><i class="fa-solid fa-image"></i></span>
{% endif %}
<span>Back</span>
</a>
{% if is_special %}
<a href="{{ breadcrumbs.0.path }}" class="subdir-item">
{% if back_thumb %}
<img src="{{ back_thumb }}" class="subdir-thumb" alt="back thumb">
{% else %}
<span class="subdir-fallback"><i class="fa-solid fa-image"></i></span>
{% endif %}
<span>Back</span>
</a>
{% else %}
<a href="{{ back_url }}" class="subdir-item">
{% if back_thumb %}
<img src="{{ back_thumb }}" class="subdir-thumb" alt="back thumb">
{% else %}
<span class="subdir-fallback"><i class="fa-solid fa-image"></i></span>
{% endif %}
<span>Back</span>
</a>
{% endif %}
<a href="{{ home_url }}" class="subdir-item">
{% if home_thumb %}

View File

@@ -91,6 +91,41 @@ class SpecialGalleriesTests(TestCase):
# Next should be c.jpg (since favorites are a.jpg and c.jpg sorted)
self.assertIn("c.jpg", ctx.get("next"))
def test_favorites_image_view_prev_next_urls_and_back_link(self):
# Image view under favorites should produce prev/next URLs scoped to favorites
resp = self.client.get("/gallery/favorites/a.jpg/")
self.assertEqual(resp.status_code, 200)
ctx = resp.context
self.assertTrue(ctx.get("is_special"))
crumbs = ctx.get("breadcrumbs")
# back link for template should point to the special root
self.assertTrue(crumbs[0]["path"].startswith("/gallery/favorites/"))
# prev_url should be None for first favorite; next_url should be inside favorites
self.assertIsNone(ctx.get("prev_url"))
next_url = ctx.get("next_url")
self.assertIsNotNone(next_url)
self.assertTrue(next_url.startswith("/gallery/favorites/"))
self.assertIn("c.jpg", next_url)
def test_most_visited_image_view_prev_next_urls(self):
# most-visited image view should scope prev/next URLs to the most-visited gallery
resp = self.client.get("/gallery/most-visited/b.jpg/")
self.assertEqual(resp.status_code, 200)
ctx = resp.context
self.assertTrue(ctx.get("is_special"))
crumbs = ctx.get("breadcrumbs")
self.assertTrue(crumbs[0]["path"].startswith("/gallery/most-visited/"))
# b.jpg is the top most-visited image in fixtures => prev_url None, next_url points to a.jpg
self.assertIsNone(ctx.get("prev_url"))
next_url = ctx.get("next_url")
self.assertIsNotNone(next_url)
self.assertTrue(next_url.startswith("/gallery/most-visited/"))
self.assertIn("a.jpg", next_url)
def test_most_visited_and_recent_directory_views(self):
# most-visited should list images ordered by visits desc
resp = self.client.get("/gallery/most-visited/")