From 72cbc342af313dba9f332704dbabf58dc7bcbcdf Mon Sep 17 00:00:00 2001 From: Ben Busby <33362396+benbusby@users.noreply.github.com> Date: Wed, 11 Nov 2020 00:40:49 -0500 Subject: [PATCH] Add ability to set temp config in search query Dark mode, country, interface language, and search language configs can now be set in the search query by appending each option as a url parameter. Supported args are: 'dark', 'lang_search', 'lang_interface', and 'ctry' Ex: /search?q=%s&dark=1&lang_search=lang_en... These config settings persist across page navigation and switching result type, but will be reset if the main search bar is used. See #144 --- app/filter.py | 1 - app/models/config.py | 36 ++++++++++++++++++++++++++++++++++++ app/routes.py | 3 +++ app/templates/header.html | 4 ++-- app/utils/routing_utils.py | 10 ++++++++++ test/test_routes.py | 6 ++++++ 6 files changed, 57 insertions(+), 3 deletions(-) diff --git a/app/filter.py b/app/filter.py index 012ed91..cd6feb6 100644 --- a/app/filter.py +++ b/app/filter.py @@ -53,7 +53,6 @@ class Filter: self.fix_question_section() self.update_styling(soup) - for img in [_ for _ in soup.find_all('img') if 'src' in _.attrs]: self.update_element_src(img, 'image/png') diff --git a/app/models/config.py b/app/models/config.py index 6cfe18f..e69e037 100644 --- a/app/models/config.py +++ b/app/models/config.py @@ -325,3 +325,39 @@ class Config: def __contains__(self, name): return hasattr(self, name) + + def is_safe_key(self, key) -> bool: + """Establishes a group of config options that are safe to set + in the url. + + Args: + key (str) -- the key to check against + + Returns: + bool -- True/False depending on if the key is in the "safe" + array + """ + + return key in [ + 'lang_search', + 'lang_interface', + 'ctry', + 'dark' + ] + + def from_params(self, params) -> 'Config': + """Modify user config with search parameters. This is primarily + used for specifying configuration on a search-by-search basis on + public instances. + + Args: + params -- the url arguments (can be any deemed safe by is_safe()) + + Returns: + Config -- a modified config object + """ + for param_key in params.keys(): + if not self.is_safe_key(param_key): + continue + self[param_key] = params.get(param_key) + return self diff --git a/app/routes.py b/app/routes.py index d1734a3..d205283 100644 --- a/app/routes.py +++ b/app/routes.py @@ -161,6 +161,9 @@ def search(): # Reset element counter app.user_elements[session['uuid']] = 0 + # Update user config if specified in search args + g.user_config = g.user_config.from_params(g.request_params) + search_util = RoutingUtils(request, g.user_config, session, cookies_disabled=g.cookies_disabled) query = search_util.new_search_query() diff --git a/app/templates/header.html b/app/templates/header.html index 034b591..e99e38e 100644 --- a/app/templates/header.html +++ b/app/templates/header.html @@ -3,7 +3,7 @@
@@ -24,7 +24,7 @@ {% else %}
diff --git a/app/utils/routing_utils.py b/app/utils/routing_utils.py index a083da2..703b7e0 100644 --- a/app/utils/routing_utils.py +++ b/app/utils/routing_utils.py @@ -81,4 +81,14 @@ class RoutingUtils: return get_first_link(html_soup), 1 else: formatted_results = content_filter.clean(html_soup) + + # Append user config to all search links, if available + param_str = ''.join('&{}={}'.format(k, v) + for k, v in self.request_params.to_dict(flat=True).items() + if self.config.is_safe_key(k)) + for link in formatted_results.find_all('a', href=True): + if 'search?' not in link['href'] or link['href'].index('search?') > 1: + continue + link['href'] += param_str + return formatted_results, content_filter.elements diff --git a/test/test_routes.py b/test/test_routes.py index 995e3c7..ff9e0a2 100644 --- a/test/test_routes.py +++ b/test/test_routes.py @@ -48,6 +48,12 @@ def test_config(client): for key in demo_config.keys(): assert config[key] == demo_config[key] + # Test setting config via search + custom_config = '&dark=1&lang_interface=lang_en' + rv = client.get('/search?q=test' + custom_config) + assert rv._status_code == 200 + assert custom_config.replace('&', '&') in str(rv.data) + def test_opensearch(client): rv = client.get('/opensearch.xml')