From 23d48f87bbc1772c7f945e17f84a6f93c35bf570 Mon Sep 17 00:00:00 2001 From: Joshua Laymon Date: Sat, 6 Sep 2025 04:16:38 +0000 Subject: [PATCH] Update web/core/context_processors.py --- web/core/context_processors.py | 47 +++++++++++++++++++++++++--------- 1 file changed, 35 insertions(+), 12 deletions(-) diff --git a/web/core/context_processors.py b/web/core/context_processors.py index 1b8cc66..2ae93e9 100644 --- a/web/core/context_processors.py +++ b/web/core/context_processors.py @@ -1,27 +1,39 @@ +from pathlib import Path from django.contrib.staticfiles import finders +from .models_ann import Announcement, AnnouncementDismissal import os +def app_version(request): + """ + Expose APP_VERSION (read from /version.txt). Used in base.html. + """ + version_file = Path(__file__).resolve().parent.parent / "version.txt" + try: + return {"APP_VERSION": version_file.read_text(encoding="utf-8").strip()} + except FileNotFoundError: + return {"APP_VERSION": "v0.0.0"} + def available_themes(request): """ Return clean theme names by scanning /static/themes/*.css. - Works whether finder paths are 'themes/xxx.css' or '.../themes/xxx.css' - and collapses fingerprinted files (e.g., midnight.400a2f.css -> midnight). + Handles both 'themes/foo.css' and '/.../themes/foo.css' paths and + collapses fingerprinted files (e.g., 'midnight.400a2f.css' -> 'midnight'). """ bases = set() + # Try listing the 'themes' directory directly. for finder in finders.get_finders(): - # 1) Try listing the 'themes' directory directly (some storages support this) try: for path, storage in finder.list(['themes']): if path.endswith('.css'): - filename = os.path.basename(path)[:-4] # drop .css - base = filename.split('.', 1)[0] # drop fingerprint + filename = os.path.basename(path)[:-4] # drop .css + base = filename.split('.', 1)[0] # drop fingerprint if base: bases.add(base) except Exception: pass - # 2) Fallback: list everything and filter anything under /themes/ + # Fallback: scan all and filter anything under themes/ try: for path, storage in finder.list([]): if path.endswith('.css') and ('/themes/' in path or path.startswith('themes/')): @@ -32,15 +44,26 @@ def available_themes(request): except Exception: pass - # If scan found nothing (e.g., dev env quirk), propose common themes + # If nothing found, fall back to common names that actually exist. if not bases: - candidates = ["classic", "dawn", "forest", "midnight", "mint", "sandstone"] - for name in candidates: + for name in ["classic", "dawn", "forest", "midnight", "mint", "sandstone", "original"]: if finders.find(f"themes/{name}.css"): bases.add(name) - # Order: preferred list first, then alphabetical for anything else preferred = ["classic", "dawn", "forest", "midnight", "mint", "sandstone", "original"] - names = sorted(bases, key=lambda n: (preferred.index(n) if n in preferred else len(preferred)+1, n)) + names = sorted(bases, key=lambda n: (preferred.index(n) if n in preferred else len(preferred) + 1, n)) + return {"available_themes": names} - return {"available_themes": names} \ No newline at end of file +def pending_announcement(request): + """ + Latest active announcement the user has not dismissed (for Search page). + """ + user = getattr(request, "user", None) + if not (user and user.is_authenticated): + return {"pending_announcement": None} + + current = [a for a in Announcement.objects.all() if a.is_current()] + for a in current: + if not AnnouncementDismissal.objects.filter(user=user, announcement=a).exists(): + return {"pending_announcement": a} + return {"pending_announcement": None} \ No newline at end of file