Coverage for an_website/settings/settings.py: 92.857%

42 statements  

« prev     ^ index     » next       coverage.py v7.8.0, created at 2025-05-07 13:44 +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/>. 

13 

14"""The settings page used to change settings.""" 

15 

16from __future__ import annotations 

17 

18import contextlib 

19import types 

20from base64 import b64encode 

21from collections.abc import Awaitable, Mapping 

22 

23from tornado.web import HTTPError 

24 

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) 

34 

35 

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 ) 

50 

51 

52class SettingsPage(HTMLRequestHandler): 

53 """The request handler for the settings page.""" 

54 

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 ) 

65 

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 ) 

76 

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 ) 

93 

94 replace_url_with = self.fix_url( 

95 self.request.full_url(), 

96 access_token=None, 

97 advanced_settings=None, 

98 save_in_cookie=None, 

99 **dict.fromkeys(self.user_settings.iter_option_names()), 

100 ) 

101 else: 

102 replace_url_with = self.fix_url( 

103 self.request.full_url(), 

104 access_token=access_token, 

105 advanced_settings=advanced_settings, 

106 save_in_cookie=False, 

107 **self.user_settings.as_dict_with_str_values( 

108 include_cookie=False, include_query_argument=False 

109 ), 

110 ) 

111 

112 if replace_url_with != self.request.full_url(): 

113 return self.redirect(replace_url_with) 

114 

115 await self.render_settings( 

116 advanced_settings=advanced_settings, 

117 save_in_cookie=save_in_cookie, 

118 show_token_input=bool( 

119 token or self.get_bool_argument("auth", False) 

120 ), 

121 token=token, 

122 kwargs={ 

123 option.name: option.get_value( 

124 self, include_cookie=False, include_query_argument=False 

125 ) 

126 for option in self.user_settings.iter_options() 

127 }, 

128 ) 

129 

130 def render_settings( # pylint: disable=too-many-arguments 

131 self, 

132 *, 

133 save_in_cookie: bool, 

134 show_token_input: bool, 

135 token: str, 

136 advanced_settings: bool, 

137 kwargs: Mapping[str, object] = types.MappingProxyType({}), 

138 ) -> Awaitable[None]: 

139 """Render the settings page.""" 

140 return self.render( 

141 "pages/settings.html", 

142 advanced_settings=advanced_settings, 

143 ask_before_leaving_default=( 

144 Options.ask_before_leaving.get_default_value(self) 

145 ), 

146 bumpscosity_values=BUMPSCOSITY_VALUES, 

147 no_3rd_party_default=Options.no_3rd_party.get_default_value(self), 

148 save_in_cookie=save_in_cookie, 

149 show_token_input=show_token_input, 

150 themes=THEMES, 

151 colour_schemes=COLOUR_SCHEMES, 

152 token=token, 

153 **kwargs, 

154 ) 

155 

156 def show_advanced_settings(self) -> bool: 

157 """Whether advanced settings should be shown.""" 

158 if arg := self.get_argument("advanced_settings", ""): 

159 with contextlib.suppress(ValueError): 

160 return str_to_bool(arg) 

161 return str_to_bool(self.get_cookie("advanced_settings", ""), False)