@@ -133,6 +133,10 @@ def _normalize_settings(payload: dict[str, Any]) -> dict[str, Any]:
|
||||
if preferred_home_view not in PREFERRED_HOME_VIEWS:
|
||||
preferred_home_view = "week"
|
||||
|
||||
theme_preference = settings_data.get("theme_preference", "auto")
|
||||
if theme_preference not in {"auto", "dark", "light"}:
|
||||
theme_preference = "auto"
|
||||
|
||||
preferred_month_view_mode = settings_data.get("preferred_month_view_mode", "flat")
|
||||
if preferred_month_view_mode not in PREFERRED_MONTH_VIEWS:
|
||||
preferred_month_view_mode = "flat"
|
||||
@@ -170,6 +174,7 @@ def _normalize_settings(payload: dict[str, Any]) -> dict[str, Any]:
|
||||
return {
|
||||
"weekly_target_minutes": _parse_int(settings_data.get("weekly_target_minutes", 1500), label="Wochenstunden", minimum=1),
|
||||
"preferred_home_view": preferred_home_view,
|
||||
"theme_preference": theme_preference,
|
||||
"preferred_month_view_mode": preferred_month_view_mode,
|
||||
"entry_mode": entry_mode,
|
||||
"working_days": sorted(working_days),
|
||||
@@ -524,6 +529,7 @@ def parse_preview_payload(preview: ImportPreview) -> dict[str, Any]:
|
||||
def _apply_settings_from_backup(*, user: User, settings_data: dict[str, Any]) -> None:
|
||||
user.weekly_target_minutes = settings_data["weekly_target_minutes"]
|
||||
user.preferred_home_view = settings_data["preferred_home_view"]
|
||||
user.theme_preference = settings_data["theme_preference"]
|
||||
user.preferred_month_view_mode = settings_data["preferred_month_view_mode"]
|
||||
user.entry_mode = settings_data["entry_mode"]
|
||||
user.working_days_csv = serialize_working_days(settings_data["working_days"])
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import html
|
||||
import re
|
||||
|
||||
import markdown as markdown_lib
|
||||
import bleach
|
||||
|
||||
@@ -194,13 +197,36 @@ _ALLOWED_ATTRIBUTES = {
|
||||
'a': ['href', 'title', 'rel', 'target'],
|
||||
}
|
||||
_ALLOWED_PROTOCOLS = ['http', 'https', 'mailto']
|
||||
_EMAIL_RE = re.compile(r'(?P<email>[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,})', re.IGNORECASE)
|
||||
_MAILTO_LINK_RE = re.compile(
|
||||
r'<a\b[^>]*href="mailto:(?P<href>[^"]+)"[^>]*>(?P<label>.*?)</a>',
|
||||
re.IGNORECASE | re.DOTALL,
|
||||
)
|
||||
|
||||
|
||||
def default_site_content_markdown(key: str) -> str:
|
||||
return DEFAULT_SITE_CONTENT_MARKDOWN.get(key, '')
|
||||
|
||||
|
||||
def render_safe_markdown(markdown_text: str) -> str:
|
||||
def obfuscate_email_address(email: str) -> str:
|
||||
local_part, _, domain = (email or '').partition('@')
|
||||
if not local_part or not domain:
|
||||
return email
|
||||
return f"{local_part} [at] {domain.replace('.', ' [dot] ')}"
|
||||
|
||||
|
||||
def obfuscate_emails_in_html(html_text: str) -> str:
|
||||
def replace_mailto_link(match: re.Match[str]) -> str:
|
||||
href_email = html.unescape(match.group('href')).strip()
|
||||
label = html.unescape(match.group('label')).strip()
|
||||
email = href_email or label
|
||||
return obfuscate_email_address(email)
|
||||
|
||||
html_text = _MAILTO_LINK_RE.sub(replace_mailto_link, html_text)
|
||||
return _EMAIL_RE.sub(lambda match: obfuscate_email_address(match.group('email')), html_text)
|
||||
|
||||
|
||||
def render_safe_markdown(markdown_text: str, *, obfuscate_emails: bool = False) -> str:
|
||||
raw_html = markdown_lib.markdown(
|
||||
markdown_text or '',
|
||||
extensions=['extra', 'sane_lists'],
|
||||
@@ -213,6 +239,8 @@ def render_safe_markdown(markdown_text: str) -> str:
|
||||
protocols=_ALLOWED_PROTOCOLS,
|
||||
strip=True,
|
||||
)
|
||||
if obfuscate_emails:
|
||||
return obfuscate_emails_in_html(cleaned)
|
||||
return bleach.linkify(cleaned)
|
||||
|
||||
|
||||
|
||||
@@ -17,6 +17,8 @@ def run_startup_migrations(engine: Engine) -> None:
|
||||
statements: list[str] = []
|
||||
if "preferred_home_view" not in user_columns:
|
||||
statements.append("ALTER TABLE users ADD COLUMN preferred_home_view VARCHAR(16) NOT NULL DEFAULT 'week'")
|
||||
if "theme_preference" not in user_columns:
|
||||
statements.append("ALTER TABLE users ADD COLUMN theme_preference VARCHAR(16) NOT NULL DEFAULT 'auto'")
|
||||
if "preferred_month_view_mode" not in user_columns:
|
||||
statements.append("ALTER TABLE users ADD COLUMN preferred_month_view_mode VARCHAR(16) NOT NULL DEFAULT 'flat'")
|
||||
if "entry_mode" not in user_columns:
|
||||
|
||||
Reference in New Issue
Block a user