Coverage for an_website/settings/settings.py: 92.857%
42 statements
« prev ^ index » next coverage.py v7.10.6, created at 2025-10-04 17:54 +0000
« prev ^ index » next coverage.py v7.10.6, created at 2025-10-04 17:54 +0000
1# This program is free software: you can redistribute it and/or modify
2# it under the terms of the GNU Affero General Public License as
3# published by the Free Software Foundation, either version 3 of the
4# License, or (at your option) any later version.
5#
6# This program is distributed in the hope that it will be useful,
7# but WITHOUT ANY WARRANTY; without even the implied warranty of
8# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
9# GNU Affero General Public License for more details.
10#
11# You should have received a copy of the GNU Affero General Public License
12# along with this program. If not, see <https://www.gnu.org/licenses/>.
14"""The settings page used to change settings."""
16from __future__ import annotations
18import contextlib
19import types
20from base64 import b64encode
21from collections.abc import Awaitable, Mapping
23from tornado.web import HTTPError
25from ..utils.options import COLOUR_SCHEMES, Options
26from ..utils.request_handler import HTMLRequestHandler
27from ..utils.themes import THEMES
28from ..utils.utils import (
29 BUMPSCOSITY_VALUES,
30 ModuleInfo,
31 bool_to_str,
32 str_to_bool,
33)
36def get_module_info() -> ModuleInfo:
37 """Create and return the ModuleInfo for this module."""
38 return ModuleInfo(
39 handlers=((r"/einstellungen", SettingsPage),),
40 name="Einstellungen",
41 description="Stelle wichtige Sachen ein",
42 path="/einstellungen",
43 keywords=(
44 "Einstellungen",
45 "Config",
46 "Settings",
47 ),
48 aliases=("/config", "/settings"),
49 )
52class SettingsPage(HTMLRequestHandler):
53 """The request handler for the settings page."""
55 async def get(self, *, head: bool = False) -> None:
56 """Handle GET requests to the settings page."""
57 if head:
58 return
59 await self.render_settings( # nosec: B106
60 save_in_cookie=self.get_bool_argument("save_in_cookie", True),
61 show_token_input=self.get_bool_argument("auth", False),
62 token="",
63 advanced_settings=self.show_advanced_settings(),
64 )
66 async def post(self) -> None:
67 """Handle POST requests to the settings page."""
68 if not self.request.body.strip():
69 raise HTTPError(400, "No body provided")
70 advanced_settings = self.get_bool_argument("advanced_settings", False)
71 save_in_cookie: bool = self.get_bool_argument("save_in_cookie", False)
72 token = self.get_argument("access_token", "")
73 access_token: None | str = (
74 b64encode(token.encode("UTF-8")).decode("ASCII") if token else None
75 )
77 if save_in_cookie:
78 if access_token:
79 self.set_cookie("access_token", access_token)
80 self.set_cookie("advanced_settings", bool_to_str(advanced_settings))
81 for option in self.user_settings.iter_options():
82 self.set_cookie(
83 option.name,
84 option.value_to_string(
85 option.get_value(
86 self,
87 include_cookie=False,
88 include_query_argument=False,
89 )
90 ),
91 httponly=option.httponly,
92 )
94 replace_url_with = self.fix_url(
95 self.request.full_url(),
96 include_protocol_and_host=True,
97 query_args={
98 "access_token": None,
99 "advanced_settings": None,
100 "save_in_cookie": None,
101 **dict.fromkeys(self.user_settings.iter_option_names()),
102 },
103 )
104 else:
105 replace_url_with = self.fix_url(
106 self.request.full_url(),
107 include_protocol_and_host=True,
108 query_args={
109 "access_token": access_token,
110 "advanced_settings": advanced_settings,
111 "save_in_cookie": False,
112 **self.user_settings.as_dict_with_str_values(
113 include_cookie=False, include_query_argument=False
114 ),
115 },
116 )
118 if replace_url_with != self.request.full_url():
119 return self.redirect(replace_url_with)
121 await self.render_settings(
122 advanced_settings=advanced_settings,
123 save_in_cookie=save_in_cookie,
124 show_token_input=bool(
125 token or self.get_bool_argument("auth", False)
126 ),
127 token=token,
128 kwargs={
129 option.name: option.get_value(
130 self, include_cookie=False, include_query_argument=False
131 )
132 for option in self.user_settings.iter_options()
133 },
134 )
136 def render_settings( # pylint: disable=too-many-arguments
137 self,
138 *,
139 save_in_cookie: bool,
140 show_token_input: bool,
141 token: str,
142 advanced_settings: bool,
143 kwargs: Mapping[str, object] = types.MappingProxyType({}),
144 ) -> Awaitable[None]:
145 """Render the settings page."""
146 return self.render(
147 "pages/settings.html",
148 advanced_settings=advanced_settings,
149 ask_before_leaving_default=(
150 Options.ask_before_leaving.get_default_value(self)
151 ),
152 bumpscosity_values=BUMPSCOSITY_VALUES,
153 no_3rd_party_default=Options.no_3rd_party.get_default_value(self),
154 save_in_cookie=save_in_cookie,
155 show_token_input=show_token_input,
156 themes=THEMES,
157 colour_schemes=COLOUR_SCHEMES,
158 token=token,
159 **kwargs,
160 )
162 def show_advanced_settings(self) -> bool:
163 """Whether advanced settings should be shown."""
164 if arg := self.get_argument("advanced_settings", ""):
165 with contextlib.suppress(ValueError):
166 return str_to_bool(arg)
167 return str_to_bool(self.get_cookie("advanced_settings", ""), False)