1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
"""Shared Jinja2 environment and page rendering for pygitweb templates."""
from collections.abc import Iterable
from fastapi.responses import HTMLResponse
from jinja2 import Environment, PackageLoader, select_autoescape
from markupsafe import Markup
env = Environment(
loader=PackageLoader("pygitweb", "templates"),
autoescape=select_autoescape(enabled_extensions=("html", "xml"), default_for_string=True),
trim_blocks=True,
lstrip_blocks=True,
)
# A table cell is plain text, which the template escapes, or Markup, which it does not.
Cell = str | Markup
_ESCAPE_TEMPLATE = env.from_string("{{ value|e }}")
def render_page(name: str, /, **ctx: object) -> HTMLResponse:
"""Render a page template. Templates extend base.html; site_name defaults from settings."""
from pygitweb.config import settings
page_ctx = dict(ctx)
page_ctx.setdefault("site_name", settings.SITE_NAME)
return HTMLResponse(env.get_template(name).render(**page_ctx))
def jinja_escape(value: str | None) -> str | None:
if value is None:
return None
return _ESCAPE_TEMPLATE.render(value=value)
def html_link(href: str, text: str, **attrs: str) -> Markup:
"""Anchor tag. href, text, and attribute values are escaped."""
attr_html = Markup("").join(
Markup(' {}="{}"').format(name.replace("_", "-"), value) for name, value in attrs.items()
)
return Markup('<a href="{}"{}>{}</a>').format(href, attr_html, text)
def html_format(template: str, /, *args: object, **kwargs: object) -> Markup:
"""Format an HTML snippet, escaping interpolated values. The template itself is trusted."""
return Markup(template).format(*args, **kwargs)
def html_join(parts: Iterable[str | Markup], sep: str = "") -> Markup:
"""Join fragments. Plain strings are escaped; Markup is inserted as-is."""
return Markup(sep).join(parts)
def trusted_html(value: str) -> Markup:
"""Mark HTML that a template or plugin already escaped. Do not pass raw text."""
return Markup(value)
def initials(name: str) -> str:
parts = [part for part in name.replace("_", " ").split() if part]
if len(parts) >= 2:
return (parts[0][0] + parts[-1][0]).upper()
if parts:
return parts[0][:2].upper()
return "?"
_STATUS_BADGE = {
"TODO": "secondary",
"IN_PROGRESS": "blue",
"IN_REVIEW": "yellow",
"DONE": "green",
"CANCELLED": "red",
}
_PRIORITY_BADGE = {
"LOW": "secondary",
"MEDIUM": "azure",
"HIGH": "orange",
"CRITICAL": "red",
}
def status_badge(status: str) -> Markup:
key = (status or "TODO").upper()
color = _STATUS_BADGE.get(key, "secondary")
label = key.replace("_", " ").title()
return html_format('<span class="badge bg-{}-lt">{}</span>', color, label)
def priority_badge(priority: str) -> Markup:
key = (priority or "LOW").upper()
color = _PRIORITY_BADGE.get(key, "secondary")
label = key.replace("_", " ").title()
return html_format('<span class="badge bg-{}-lt">{}</span>', color, label)
def person_label(name: str) -> Markup:
label = (name or "").strip() or "Unassigned"
return html_format(
'<span class="d-inline-flex align-items-center gap-1"><span class="avatar avatar-xs">{}</span>{}</span>',
initials(label),
label,
)
env.filters["initials"] = initials