from __future__ import annotations import hashlib import re from pathlib import Path from quart import Quart, g, url_for from shared.config import config from shared.utils import host_url from shared.browser.app.csrf import generate_csrf_token from shared.browser.app.authz import has_access from shared.browser.app.filters import register as register_filters from .urls import coop_url, market_url, cart_url, events_url, login_url, page_cart_url, market_product_url def setup_jinja(app: Quart) -> None: app.jinja_env.add_extension("jinja2.ext.do") # --- template globals --- app.add_template_global(generate_csrf_token, "csrf_token") app.add_template_global(has_access, "has_access") def level(): if not hasattr(g, "_level_counter"): g._level_counter = 0 return g._level_counter def level_up(): if not hasattr(g, "_level_counter"): g._level_counter = 0 g._level_counter += 1 return "" app.jinja_env.globals["level"] = level app.jinja_env.globals["level_up"] = level_up app.jinja_env.globals["menu_colour"] = "sky" nav_button = """justify-center cursor-pointer flex flex-row items-center gap-2 rounded bg-stone-200 text-black [.hover-capable_&]:hover:bg-yellow-300 aria-selected:bg-stone-500 aria-selected:text-white [.hover-capable_&[aria-selected=true]:hover]:bg-orange-500""" styles = { "pill": """ inline-flex items-center px-3 py-1 rounded-full bg-stone-200 text-stone-700 text-sm hover:bg-stone-300 hover:text-stone-900 focus:outline-none focus-visible:ring-2 focus-visible:ring-stone-400 """, "tr": "odd:bg-slate-50 even:bg-white hover:bg-slate-100", "action_button": "px-2 py-1 border rounded text-sm bg-sky-300 hover:bg-sky-400 flex gap-1 items-center", "pre_action_button": "px-2 py-1 border rounded text-sm bg-green-200 hover:bg-green-300", "cancel_button": "px-3 py-1.5 rounded-full text-sm border border-stone-300 text-stone-700 hover:bg-stone-100", "list_container": "border border-stone-200 rounded-lg p-3 mb-3 bg-white space-y-3 bg-yellow-200", "nav_button": f"{nav_button} p-3", "nav_button_less_pad": f"{nav_button} p-2", } app.jinja_env.globals["styles"] = styles def _asset_url(path: str) -> str: def squash_double_slashes(url: str) -> str: m = re.match(r"(?:[A-Za-z][\w+.-]*:)?//", url) prefix = m.group(0) if m else "" rest = re.sub(r"/+", "/", url[len(prefix):]) return prefix + rest file_path = Path("static") / path try: digest = hashlib.md5(file_path.read_bytes()).hexdigest()[:8] except Exception: digest = "dev" return squash_double_slashes( f"{g.scheme}://{g.host}{g.root}/{url_for('static', filename=path, v=digest)}" ) app.jinja_env.globals["asset_url"] = _asset_url def site(): return { "url": host_url(), "logo": _asset_url("img/logo.jpg"), "default_image": _asset_url("img/logo.jpg"), "title": config()["title"], } app.jinja_env.globals["site"] = site # cross-app URL helpers available in all templates app.jinja_env.globals["coop_url"] = coop_url app.jinja_env.globals["market_url"] = market_url app.jinja_env.globals["cart_url"] = cart_url app.jinja_env.globals["events_url"] = events_url app.jinja_env.globals["login_url"] = login_url app.jinja_env.globals["page_cart_url"] = page_cart_url app.jinja_env.globals["market_product_url"] = market_product_url # register jinja filters register_filters(app)