feat: Support timezone and language query params

Support setting the timezone as well as the language via query params:
The timezone parameter previously only worked on certain pages.
While we're at it, let's also add the language as a param.
Refactor code for timezone and language functions.
Remove unused AURTZ cookie.

Signed-off-by: moson <moson@archlinux.org>
This commit is contained in:
moson 2023-10-19 18:41:06 +02:00
parent 933654fcbb
commit 5d302ae00c
No known key found for this signature in database
GPG key ID: 4A4760AB4EE15296
5 changed files with 89 additions and 33 deletions

View file

@ -64,11 +64,24 @@ class Translator:
translator = Translator()
def get_request_language(request: Request):
if request.user.is_authenticated():
def get_request_language(request: Request) -> str:
"""Get a request's language from either query param, user setting or
cookie. We use the configuration's [options] default_lang otherwise.
@param request FastAPI request
"""
request_lang = request.query_params.get("language")
cookie_lang = request.cookies.get("AURLANG")
if request_lang and request_lang in SUPPORTED_LANGUAGES:
return request_lang
elif (
request.user.is_authenticated()
and request.user.LangPreference in SUPPORTED_LANGUAGES
):
return request.user.LangPreference
default_lang = aurweb.config.get("options", "default_lang")
return request.cookies.get("AURLANG", default_lang)
elif cookie_lang and cookie_lang in SUPPORTED_LANGUAGES:
return cookie_lang
return aurweb.config.get_with_fallback("options", "default_lang", "en")
def get_raw_translator_for_request(request: Request):

View file

@ -3,7 +3,6 @@ import functools
import os
from http import HTTPStatus
from typing import Callable
from zoneinfo import ZoneInfoNotFoundError
import jinja2
from fastapi import Request
@ -75,10 +74,7 @@ def make_context(request: Request, title: str, next: str = None):
# Shorten commit_hash to a short Git hash.
commit_hash = commit_hash[:7]
try:
timezone = time.get_request_timezone(request)
except ZoneInfoNotFoundError:
timezone = DEFAULT_TIMEZONE
timezone = time.get_request_timezone(request)
language = l10n.get_request_language(request)
return {
"request": request,
@ -110,9 +106,7 @@ async def make_variable_context(request: Request, title: str, next: str = None):
)
for k, v in to_copy.items():
if k == "timezone":
context[k] = v if v in time.SUPPORTED_TIMEZONES else DEFAULT_TIMEZONE
elif k not in context:
if k not in context:
context[k] = v
context["q"] = dict(request.query_params)

View file

@ -1,7 +1,6 @@
import zoneinfo
from collections import OrderedDict
from datetime import datetime
from urllib.parse import unquote
from zoneinfo import ZoneInfo
from fastapi import Request
@ -58,16 +57,20 @@ SUPPORTED_TIMEZONES = OrderedDict(
)
def get_request_timezone(request: Request):
"""Get a request's timezone by its AURTZ cookie. We use the
configuration's [options] default_timezone otherwise.
def get_request_timezone(request: Request) -> str:
"""Get a request's timezone from either query param or user settings.
We use the configuration's [options] default_timezone otherwise.
@param request FastAPI request
"""
default_tz = aurweb.config.get("options", "default_timezone")
if request.user.is_authenticated():
default_tz = request.user.Timezone
return unquote(request.cookies.get("AURTZ", default_tz))
request_tz = request.query_params.get("timezone")
if request_tz and request_tz in SUPPORTED_TIMEZONES:
return request_tz
elif (
request.user.is_authenticated() and request.user.Timezone in SUPPORTED_TIMEZONES
):
return request.user.Timezone
return aurweb.config.get_with_fallback("options", "default_timezone", "UTC")
def now(timezone: str) -> datetime:

View file

@ -1,5 +1,5 @@
""" Test our l10n module. """
from aurweb import filters, l10n
from aurweb import config, filters, l10n
from aurweb.testing.requests import Request
@ -10,13 +10,55 @@ def test_translator():
def test_get_request_language():
"""First, tests default_lang, then tests a modified AURLANG cookie."""
"""Test getting the language setting from a request."""
# Default language
default_lang = config.get("options", "default_lang")
request = Request()
assert l10n.get_request_language(request) == "en"
assert l10n.get_request_language(request) == default_lang
# Language setting from cookie: de
request.cookies["AURLANG"] = "de"
assert l10n.get_request_language(request) == "de"
# Language setting from cookie: nonsense
# Should fallback to default lang
request.cookies["AURLANG"] = "nonsense"
assert l10n.get_request_language(request) == default_lang
# Language setting from query param: de
request.cookies = {}
request.query_params = {"language": "de"}
assert l10n.get_request_language(request) == "de"
# Language setting from query param: nonsense
# Should fallback to default lang
request.query_params = {"language": "nonsense"}
assert l10n.get_request_language(request) == default_lang
# Language setting from query param: de and cookie
# Query param should have precedence
request.query_params = {"language": "de"}
request.cookies["AURLANG"] = "fr"
assert l10n.get_request_language(request) == "de"
# Language setting from authenticated user
request.cookies = {}
request.query_params = {}
request.user.authenticated = True
request.user.LangPreference = "de"
assert l10n.get_request_language(request) == "de"
# Language setting from authenticated user with query param
# Query param should have precedence
request.query_params = {"language": "fr"}
assert l10n.get_request_language(request) == "fr"
# Language setting from authenticated user with cookie
# DB setting should have precedence
request.query_params = {}
request.cookies["AURLANG"] = "fr"
assert l10n.get_request_language(request) == "de"
def test_get_raw_translator_for_request():
"""Make sure that get_raw_translator_for_request is giving us

View file

@ -15,18 +15,22 @@ def test_tz_offset_mst():
def test_request_timezone():
request = Request()
tz = get_request_timezone(request)
assert tz == aurweb.config.get("options", "default_timezone")
# Default timezone
dtz = aurweb.config.get("options", "default_timezone")
assert get_request_timezone(request) == dtz
def test_authenticated_request_timezone():
# Modify a fake request to be authenticated with the
# America/Los_Angeles timezone.
request = Request()
# Timezone from query params
request.query_params = {"timezone": "Europe/Berlin"}
assert get_request_timezone(request) == "Europe/Berlin"
# Timezone from authenticated user.
request.query_params = {}
request.user.authenticated = True
request.user.Timezone = "America/Los_Angeles"
assert get_request_timezone(request) == "America/Los_Angeles"
# Get the request's timezone, it should be America/Los_Angeles.
tz = get_request_timezone(request)
assert tz == request.user.Timezone
assert tz == "America/Los_Angeles"
# Timezone from authenticated user with query param
# Query param should have precedence
request.query_params = {"timezone": "Europe/Berlin"}
assert get_request_timezone(request) == "Europe/Berlin"