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() translator = Translator()
def get_request_language(request: Request): def get_request_language(request: Request) -> str:
if request.user.is_authenticated(): """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 return request.user.LangPreference
default_lang = aurweb.config.get("options", "default_lang") elif cookie_lang and cookie_lang in SUPPORTED_LANGUAGES:
return request.cookies.get("AURLANG", default_lang) return cookie_lang
return aurweb.config.get_with_fallback("options", "default_lang", "en")
def get_raw_translator_for_request(request: Request): def get_raw_translator_for_request(request: Request):

View file

@ -3,7 +3,6 @@ import functools
import os import os
from http import HTTPStatus from http import HTTPStatus
from typing import Callable from typing import Callable
from zoneinfo import ZoneInfoNotFoundError
import jinja2 import jinja2
from fastapi import Request 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. # Shorten commit_hash to a short Git hash.
commit_hash = commit_hash[:7] commit_hash = commit_hash[:7]
try: timezone = time.get_request_timezone(request)
timezone = time.get_request_timezone(request)
except ZoneInfoNotFoundError:
timezone = DEFAULT_TIMEZONE
language = l10n.get_request_language(request) language = l10n.get_request_language(request)
return { return {
"request": request, "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(): for k, v in to_copy.items():
if k == "timezone": if k not in context:
context[k] = v if v in time.SUPPORTED_TIMEZONES else DEFAULT_TIMEZONE
elif k not in context:
context[k] = v context[k] = v
context["q"] = dict(request.query_params) context["q"] = dict(request.query_params)

View file

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

View file

@ -1,5 +1,5 @@
""" Test our l10n module. """ """ Test our l10n module. """
from aurweb import filters, l10n from aurweb import config, filters, l10n
from aurweb.testing.requests import Request from aurweb.testing.requests import Request
@ -10,13 +10,55 @@ def test_translator():
def test_get_request_language(): 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() 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" request.cookies["AURLANG"] = "de"
assert l10n.get_request_language(request) == "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(): def test_get_raw_translator_for_request():
"""Make sure that get_raw_translator_for_request is giving us """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(): def test_request_timezone():
request = Request() 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(): # Timezone from query params
# Modify a fake request to be authenticated with the request.query_params = {"timezone": "Europe/Berlin"}
# America/Los_Angeles timezone. assert get_request_timezone(request) == "Europe/Berlin"
request = Request()
# Timezone from authenticated user.
request.query_params = {}
request.user.authenticated = True request.user.authenticated = True
request.user.Timezone = "America/Los_Angeles" 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. # Timezone from authenticated user with query param
tz = get_request_timezone(request) # Query param should have precedence
assert tz == request.user.Timezone request.query_params = {"timezone": "Europe/Berlin"}
assert tz == "America/Los_Angeles" assert get_request_timezone(request) == "Europe/Berlin"