from pathlib import Path from django.contrib.staticfiles import finders from .models_ann import Announcement, AnnouncementDismissal import os def app_version(request): 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"} # core/context_processors.py from pathlib import Path from django.contrib.staticfiles import finders from django.conf import settings import os def available_themes(request): """ Return theme names by scanning /static/themes/*.css across both STATICFILES_DIRS and app static folders. Filter out hashed/backup files (anything with an extra dot before .css). """ seen = set() # 1) Check project-level STATICFILES_DIRS for root in getattr(settings, "STATICFILES_DIRS", []): dirpath = os.path.join(root, "themes") if os.path.isdir(dirpath): for fname in os.listdir(dirpath): if not fname.endswith(".css"): continue base = fname[:-4] # strip .css # Ignore hashed/backup or hidden files if not base or base.startswith("_") or "." in base: continue seen.add(base) # 2) Check app static folders discovered by staticfiles finders for finder in finders.get_finders(): # list() with the 'themes' prefix limits the walk for path, storage in finder.list(["themes"]): # Expect paths like 'themes/.css' if not (path.startswith("themes/") and path.endswith(".css")): continue base = os.path.basename(path)[:-4] if not base or base.startswith("_") or "." in base: continue seen.add(base) # Nice ordering: keep 'classic' first, then alphabetical names = sorted(seen) if "classic" in names: names = ["classic"] + [n for n in names if n != "classic"] return {"available_themes": names} def pending_announcement(request): 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}