浏览代码

new preferences handling

Preferences class was introduced in order to handle user preferences. Right now
it parses cookies and the form in preferences. Also it can retrieve settings
based on the name of the setting.

ATTENTION
Please note that engine preferences are handled differently from now on. So it
introduces incompatible changes. Every user who has saved preferences should reset and
save his/her settings again.

This change was needed, because everytime a default disabled engine was
added saved user preferences would broke. Now engine setting tracking is
fixed.
Noemi Vanyi 9 年前
父节点
当前提交
fe691a0988
共有 6 个文件被更改,包括 316 次插入168 次删除
  1. 269
    0
      searx/preferences.py
  2. 5
    10
      searx/search.py
  3. 1
    1
      searx/settings_robot.yml
  4. 0
    23
      searx/utils.py
  5. 36
    133
      searx/webapp.py
  6. 5
    1
      tests/unit/test_webapp.py

+ 269
- 0
searx/preferences.py 查看文件

@@ -0,0 +1,269 @@
1
+from searx import settings, autocomplete
2
+from searx.languages import language_codes as languages
3
+
4
+
5
+COOKIE_MAX_AGE = 60 * 60 * 24 * 365 * 5  # 5 years
6
+LANGUAGE_CODES = [l[0] for l in languages]
7
+LANGUAGE_CODES.append('all')
8
+DISABLED = 0
9
+ENABLED = 1
10
+
11
+
12
+class MissingArgumentException(Exception):
13
+    pass
14
+
15
+
16
+class ValidationException(Exception):
17
+    pass
18
+
19
+
20
+class Setting(object):
21
+    """Base class of user settings"""
22
+
23
+    def __init__(self, default_value, **kwargs):
24
+        super(Setting, self).__init__()
25
+        self.value = default_value
26
+        for key, value in kwargs.iteritems():
27
+            setattr(self, key, value)
28
+
29
+        self._post_init()
30
+
31
+    def _post_init(self):
32
+        pass
33
+
34
+    def parse(self, data):
35
+        self.value = data
36
+
37
+    def get_value(self):
38
+        return self.value
39
+
40
+    def save(self, name, resp):
41
+        resp.set_cookie(name, bytes(self.value), max_age=COOKIE_MAX_AGE)
42
+
43
+
44
+class StringSetting(Setting):
45
+    """Setting of plain string values"""
46
+    pass
47
+
48
+
49
+class EnumStringSetting(Setting):
50
+    """Setting of a value which can only come from the given choices"""
51
+
52
+    def _post_init(self):
53
+        if not hasattr(self, 'choices'):
54
+            raise MissingArgumentException('Missing argument: choices')
55
+
56
+        if self.value != '' and self.value not in self.choices:
57
+            raise ValidationException('Invalid default value: {0}'.format(self.value))
58
+
59
+    def parse(self, data):
60
+        if data not in self.choices and data != self.value:
61
+            raise ValidationException('Invalid choice: {0}'.format(data))
62
+        self.value = data
63
+
64
+
65
+class MultipleChoiceSetting(EnumStringSetting):
66
+    """Setting of values which can only come from the given choices"""
67
+
68
+    def _post_init(self):
69
+        if not hasattr(self, 'choices'):
70
+            raise MissingArgumentException('Missing argument: choices')
71
+        for item in self.value:
72
+            if item not in self.choices:
73
+                raise ValidationException('Invalid default value: {0}'.format(self.value))
74
+
75
+    def parse(self, data):
76
+        if data == '':
77
+            self.value = []
78
+            return
79
+
80
+        elements = data.split(',')
81
+        for item in elements:
82
+            if item not in self.choices:
83
+                raise ValidationException('Invalid choice: {0}'.format(item))
84
+        self.value = elements
85
+
86
+    def parse_form(self, data):
87
+        self.value = []
88
+        for choice in data:
89
+            if choice in self.choices and choice not in self.value:
90
+                self.value.append(choice)
91
+
92
+    def save(self, name, resp):
93
+        resp.set_cookie(name, ','.join(self.value), max_age=COOKIE_MAX_AGE)
94
+
95
+
96
+class MapSetting(Setting):
97
+    """Setting of a value that has to be translated in order to be storable"""
98
+
99
+    def _post_init(self):
100
+        if not hasattr(self, 'map'):
101
+            raise MissingArgumentException('missing argument: map')
102
+        if self.value not in self.map.values():
103
+            raise ValidationException('Invalid default value')
104
+
105
+    def parse(self, data):
106
+        if data not in self.map:
107
+            raise ValidationException('Invalid choice: {0}'.format(data))
108
+        self.value = self.map[data]
109
+        self.key = data
110
+
111
+    def save(self, name, resp):
112
+        resp.set_cookie(name, bytes(self.key), max_age=COOKIE_MAX_AGE)
113
+
114
+
115
+class SwitchableSetting(Setting):
116
+    """ Base class for settings that can be turned on && off"""
117
+
118
+    def _post_init(self):
119
+        self.disabled = set()
120
+        self.enabled = set()
121
+        if not hasattr(self, 'choices'):
122
+            raise MissingArgumentException('missing argument: choices')
123
+
124
+    def transform_form_items(self, items):
125
+        return items
126
+
127
+    def transform_values(self, values):
128
+        return values
129
+
130
+    def parse_cookie(self, data):
131
+        if data[DISABLED] != '':
132
+            self.disabled = set(data[DISABLED].split(','))
133
+        if data[ENABLED] != '':
134
+            self.enabled = set(data[ENABLED].split(','))
135
+
136
+    def parse_form(self, items):
137
+        items = self.transform_form_items(items)
138
+
139
+        self.disabled = set()
140
+        self.enabled = set()
141
+        for choice in self.choices:
142
+            if choice['default_on']:
143
+                if choice['id'] in items:
144
+                    self.disabled.add(choice['id'])
145
+            else:
146
+                if choice['id'] not in items:
147
+                    self.enabled.add(choice['id'])
148
+
149
+    def save(self, resp):
150
+        resp.set_cookie('disabled_{0}'.format(self.value), ','.join(self.disabled), max_age=COOKIE_MAX_AGE)
151
+        resp.set_cookie('enabled_{0}'.format(self.value), ','.join(self.enabled), max_age=COOKIE_MAX_AGE)
152
+
153
+    def get_disabled(self):
154
+        disabled = self.disabled
155
+        for choice in self.choices:
156
+            if not choice['default_on'] and choice['id'] not in self.enabled:
157
+                disabled.add(choice['id'])
158
+        return self.transform_values(disabled)
159
+
160
+    def get_enabled(self):
161
+        enabled = self.enabled
162
+        for choice in self.choices:
163
+            if choice['default_on'] and choice['id'] not in self.disabled:
164
+                enabled.add(choice['id'])
165
+        return self.transform_values(enabled)
166
+
167
+
168
+class EnginesSetting(SwitchableSetting):
169
+    def _post_init(self):
170
+        super(EnginesSetting, self)._post_init()
171
+        transformed_choices = []
172
+        for engine_name, engine in self.choices.iteritems():
173
+            for category in engine.categories:
174
+                transformed_choice = dict()
175
+                transformed_choice['default_on'] = not engine.disabled
176
+                transformed_choice['id'] = '{}__{}'.format(engine_name, category)
177
+                transformed_choices.append(transformed_choice)
178
+        self.choices = transformed_choices
179
+
180
+    def transform_form_items(self, items):
181
+        return [item[len('engine_'):].replace('_', ' ').replace('  ', '__') for item in items]
182
+
183
+    def transform_values(self, values):
184
+        if len(values) == 1 and values[0] == '':
185
+            return list()
186
+        transformed_values = []
187
+        for value in values:
188
+            engine, category = value.split('__')
189
+            transformed_values.append((engine, category))
190
+        return transformed_values
191
+
192
+
193
+class PluginsSetting(SwitchableSetting):
194
+    def _post_init(self):
195
+        super(PluginsSetting, self)._post_init()
196
+        transformed_choices = []
197
+        for plugin in self.choices:
198
+            transformed_choice = dict()
199
+            transformed_choice['default_on'] = plugin.default_on
200
+            transformed_choice['id'] = plugin.id
201
+            transformed_choices.append(transformed_choice)
202
+        self.choices = transformed_choices
203
+
204
+    def transform_form_items(self, items):
205
+        return [item[len('plugin_'):] for item in items]
206
+
207
+
208
+class Preferences(object):
209
+    """Stores, validates and saves preferences to cookies"""
210
+
211
+    def __init__(self, themes, categories, engines, plugins):
212
+        super(Preferences, self).__init__()
213
+
214
+        self.key_value_settings = {'categories': MultipleChoiceSetting(['general'], choices=categories),
215
+                                   'language': EnumStringSetting('all', choices=LANGUAGE_CODES),
216
+                                   'locale': EnumStringSetting(settings['ui']['default_locale'],
217
+                                                               choices=settings['locales'].keys()),
218
+                                   'autocomplete': EnumStringSetting(settings['search']['autocomplete'],
219
+                                                                     choices=autocomplete.backends.keys()),
220
+                                   'image_proxy': MapSetting(settings['server']['image_proxy'],
221
+                                                             map={'': settings['server']['image_proxy'],
222
+                                                                  '0': False,
223
+                                                                  '1': True}),
224
+                                   'method': EnumStringSetting('POST', choices=('GET', 'POST')),
225
+                                   'safesearch': MapSetting(settings['search']['safe_search'], map={'0': 0,
226
+                                                                                                    '1': 1,
227
+                                                                                                    '2': 2}),
228
+                                   'theme': EnumStringSetting(settings['ui']['default_theme'], choices=themes)}
229
+
230
+        self.engines = EnginesSetting('engines', choices=engines)
231
+        self.plugins = PluginsSetting('plugins', choices=plugins)
232
+
233
+    def parse_cookies(self, input_data):
234
+        for user_setting_name, user_setting in input_data.iteritems():
235
+            if user_setting_name in self.key_value_settings:
236
+                self.key_value_settings[user_setting_name].parse(user_setting)
237
+            elif user_setting_name == 'disabled_engines':
238
+                self.engines.parse_cookie([input_data['disabled_engines'], input_data['enabled_engines']])
239
+            elif user_setting_name == 'disabled_plugins':
240
+                self.plugins.parse_cookie([input_data['disabled_plugins'], input_data['enabled_plugins']])
241
+
242
+    def parse_form(self, input_data):
243
+        disabled_engines = []
244
+        enabled_categories = []
245
+        disabled_plugins = []
246
+        for user_setting_name, user_setting in input_data.iteritems():
247
+            if user_setting_name in self.key_value_settings:
248
+                self.key_value_settings[user_setting_name].parse(user_setting)
249
+            elif user_setting_name.startswith('engine_'):
250
+                disabled_engines.append(user_setting_name)
251
+            elif user_setting_name.startswith('category_'):
252
+                enabled_categories.append(user_setting_name[len('category_'):])
253
+            elif user_setting_name.startswith('plugin_'):
254
+                disabled_plugins.append(user_setting_name)
255
+        self.key_value_settings['categories'].parse_form(enabled_categories)
256
+        self.engines.parse_form(disabled_engines)
257
+        self.plugins.parse_form(disabled_plugins)
258
+
259
+    # cannot be used in case of engines or plugins
260
+    def get_value(self, user_setting_name):
261
+        if user_setting_name in self.key_value_settings:
262
+            return self.key_value_settings[user_setting_name].get_value()
263
+
264
+    def save(self, resp):
265
+        for user_setting_name, user_setting in self.key_value_settings.iteritems():
266
+            user_setting.save(user_setting_name, resp)
267
+        self.engines.save(resp)
268
+        self.plugins.save(resp)
269
+        return resp

+ 5
- 10
searx/search.py 查看文件

@@ -23,7 +23,7 @@ from searx.engines import (
23 23
     categories, engines
24 24
 )
25 25
 from searx.languages import language_codes
26
-from searx.utils import gen_useragent, get_blocked_engines
26
+from searx.utils import gen_useragent
27 27
 from searx.query import Query
28 28
 from searx.results import ResultContainer
29 29
 from searx import logger
@@ -140,15 +140,13 @@ class Search(object):
140 140
         self.lang = 'all'
141 141
 
142 142
         # set blocked engines
143
-        self.blocked_engines = get_blocked_engines(engines, request.cookies)
143
+        self.blocked_engines = request.preferences.engines.get_disabled()
144 144
 
145 145
         self.result_container = ResultContainer()
146 146
         self.request_data = {}
147 147
 
148 148
         # set specific language if set
149
-        if request.cookies.get('language')\
150
-           and request.cookies['language'] in (x[0] for x in language_codes):
151
-            self.lang = request.cookies['language']
149
+        self.lang = request.preferences.get_value('language')
152 150
 
153 151
         # set request method
154 152
         if request.method == 'POST':
@@ -294,11 +292,8 @@ class Search(object):
294 292
             else:
295 293
                 request_params['language'] = self.lang
296 294
 
297
-            try:
298
-                # 0 = None, 1 = Moderate, 2 = Strict
299
-                request_params['safesearch'] = int(request.cookies.get('safesearch'))
300
-            except Exception:
301
-                request_params['safesearch'] = settings['search']['safe_search']
295
+            # 0 = None, 1 = Moderate, 2 = Strict
296
+            request_params['safesearch'] = request.preferences.get_value('safesearch')
302 297
 
303 298
             # update request parameters dependent on
304 299
             # search-engine (contained in engines folder)

+ 1
- 1
searx/settings_robot.yml 查看文件

@@ -4,7 +4,7 @@ general:
4 4
 
5 5
 search:
6 6
     safe_search : 0
7
-    autocomplete : 0
7
+    autocomplete : ""
8 8
 
9 9
 server:
10 10
     port : 11111

+ 0
- 23
searx/utils.py 查看文件

@@ -230,26 +230,3 @@ def list_get(a_list, index, default=None):
230 230
         return a_list[index]
231 231
     else:
232 232
         return default
233
-
234
-
235
-def get_blocked_engines(engines, cookies):
236
-    if 'blocked_engines' not in cookies:
237
-        return [(engine_name, category) for engine_name in engines
238
-                for category in engines[engine_name].categories if engines[engine_name].disabled]
239
-
240
-    blocked_engine_strings = cookies.get('blocked_engines', '').split(',')
241
-    blocked_engines = []
242
-
243
-    if not blocked_engine_strings:
244
-        return blocked_engines
245
-
246
-    for engine_string in blocked_engine_strings:
247
-        if engine_string.find('__') > -1:
248
-            engine, category = engine_string.split('__', 1)
249
-            if engine in engines and category in engines[engine].categories:
250
-                blocked_engines.append((engine, category))
251
-        elif engine_string in engines:
252
-            for category in engines[engine_string].categories:
253
-                blocked_engines.append((engine_string, category))
254
-
255
-    return blocked_engines

+ 36
- 133
searx/webapp.py 查看文件

@@ -56,7 +56,7 @@ from searx.engines import (
56 56
 from searx.utils import (
57 57
     UnicodeWriter, highlight_content, html_to_text, get_themes,
58 58
     get_static_files, get_result_templates, gen_useragent, dict_subset,
59
-    prettify_url, get_blocked_engines
59
+    prettify_url
60 60
 )
61 61
 from searx.version import VERSION_STRING
62 62
 from searx.languages import language_codes
@@ -64,6 +64,7 @@ from searx.search import Search
64 64
 from searx.query import Query
65 65
 from searx.autocomplete import searx_bang, backends as autocomplete_backends
66 66
 from searx.plugins import plugins
67
+from searx.preferences import Preferences
67 68
 
68 69
 # check if the pyopenssl, ndg-httpsclient, pyasn1 packages are installed.
69 70
 # They are needed for SSL connection without trouble, see #298
@@ -109,8 +110,6 @@ for indice, theme in enumerate(themes):
109 110
     for (dirpath, dirnames, filenames) in os.walk(theme_img_path):
110 111
         global_favicons[indice].extend(filenames)
111 112
 
112
-cookie_max_age = 60 * 60 * 24 * 365 * 5  # 5 years
113
-
114 113
 _category_names = (gettext('files'),
115 114
                    gettext('general'),
116 115
                    gettext('music'),
@@ -222,9 +221,7 @@ def get_current_theme_name(override=None):
222 221
 
223 222
     if override and override in themes:
224 223
         return override
225
-    theme_name = request.args.get('theme',
226
-                                  request.cookies.get('theme',
227
-                                                      default_theme))
224
+    theme_name = request.args.get('theme', request.preferences.get_value('theme'))
228 225
     if theme_name not in themes:
229 226
         theme_name = default_theme
230 227
     return theme_name
@@ -262,12 +259,8 @@ def image_proxify(url):
262 259
 
263 260
 
264 261
 def render(template_name, override_theme=None, **kwargs):
265
-    blocked_engines = get_blocked_engines(engines, request.cookies)
266
-
267
-    autocomplete = request.cookies.get('autocomplete', settings['search']['autocomplete'])
268
-
269
-    if autocomplete not in autocomplete_backends:
270
-        autocomplete = None
262
+    blocked_engines = request.preferences.engines.get_disabled()
263
+    autocomplete = request.preferences.get_value('autocomplete')
271 264
 
272 265
     nonblocked_categories = set(category for engine_name in engines
273 266
                                 for category in engines[engine_name].categories
@@ -295,7 +288,7 @@ def render(template_name, override_theme=None, **kwargs):
295 288
                     kwargs['selected_categories'].append(c)
296 289
 
297 290
     if not kwargs['selected_categories']:
298
-        cookie_categories = request.cookies.get('categories', '').split(',')
291
+        cookie_categories = request.preferences.get_value('categories')
299 292
         for ccateg in cookie_categories:
300 293
             if ccateg in categories:
301 294
                 kwargs['selected_categories'].append(ccateg)
@@ -311,9 +304,9 @@ def render(template_name, override_theme=None, **kwargs):
311 304
 
312 305
     kwargs['searx_version'] = VERSION_STRING
313 306
 
314
-    kwargs['method'] = request.cookies.get('method', 'POST')
307
+    kwargs['method'] = request.preferences.get_value('method')
315 308
 
316
-    kwargs['safesearch'] = request.cookies.get('safesearch', str(settings['search']['safe_search']))
309
+    kwargs['safesearch'] = str(request.preferences.get_value('safesearch'))
317 310
 
318 311
     # override url_for function in templates
319 312
     kwargs['url_for'] = url_for_theme
@@ -347,14 +340,18 @@ def render(template_name, override_theme=None, **kwargs):
347 340
 @app.before_request
348 341
 def pre_request():
349 342
     # merge GET, POST vars
343
+    preferences = Preferences(themes, categories.keys(), engines, plugins)
344
+    preferences.parse_cookies(request.cookies)
345
+    request.preferences = preferences
346
+
350 347
     request.form = dict(request.form.items())
351 348
     for k, v in request.args.items():
352 349
         if k not in request.form:
353 350
             request.form[k] = v
354 351
 
355 352
     request.user_plugins = []
356
-    allowed_plugins = request.cookies.get('allowed_plugins', '').split(',')
357
-    disabled_plugins = request.cookies.get('disabled_plugins', '').split(',')
353
+    allowed_plugins = preferences.plugins.get_enabled()
354
+    disabled_plugins = preferences.plugins.get_disabled()
358 355
     for plugin in plugins:
359 356
         if ((plugin.default_on and plugin.id not in disabled_plugins)
360 357
                 or plugin.id in allowed_plugins):
@@ -486,7 +483,7 @@ def autocompleter():
486 483
         request_data = request.args
487 484
 
488 485
     # set blocked engines
489
-    blocked_engines = get_blocked_engines(engines, request.cookies)
486
+    blocked_engines = request.preferences.engines.get_disabled()
490 487
 
491 488
     # parse query
492 489
     query = Query(request_data.get('q', '').encode('utf-8'), blocked_engines)
@@ -496,8 +493,8 @@ def autocompleter():
496 493
     if not query.getSearchQuery():
497 494
         return '', 400
498 495
 
499
-    # get autocompleter
500
-    completer = autocomplete_backends.get(request.cookies.get('autocomplete', settings['search']['autocomplete']))
496
+    # run autocompleter
497
+    completer = autocomplete_backends.get(request.preferences.get_value('autocomplete'))
501 498
 
502 499
     # parse searx specific autocompleter results like !bang
503 500
     raw_results = searx_bang(query)
@@ -532,117 +529,23 @@ def autocompleter():
532 529
 
533 530
 @app.route('/preferences', methods=['GET', 'POST'])
534 531
 def preferences():
535
-    """Render preferences page.
536
-
537
-    Settings that are going to be saved as cookies."""
538
-    lang = None
539
-    image_proxy = request.cookies.get('image_proxy', settings['server'].get('image_proxy'))
540
-
541
-    if request.cookies.get('language')\
542
-       and request.cookies['language'] in (x[0] for x in language_codes):
543
-        lang = request.cookies['language']
544
-
545
-    blocked_engines = []
546
-
547
-    resp = make_response(redirect(urljoin(settings['server']['base_url'], url_for('index'))))
548
-
549
-    if request.method == 'GET':
550
-        blocked_engines = get_blocked_engines(engines, request.cookies)
551
-    else:  # on save
552
-        selected_categories = []
553
-        post_disabled_plugins = []
554
-        locale = None
555
-        autocomplete = ''
556
-        method = 'POST'
557
-        safesearch = settings['search']['safe_search']
558
-        for pd_name, pd in request.form.items():
559
-            if pd_name.startswith('category_'):
560
-                category = pd_name[9:]
561
-                if category not in categories:
562
-                    continue
563
-                selected_categories.append(category)
564
-            elif pd_name == 'locale' and pd in settings['locales']:
565
-                locale = pd
566
-            elif pd_name == 'image_proxy':
567
-                image_proxy = pd
568
-            elif pd_name == 'autocomplete':
569
-                autocomplete = pd
570
-            elif pd_name == 'language' and (pd == 'all' or
571
-                                            pd in (x[0] for
572
-                                                   x in language_codes)):
573
-                lang = pd
574
-            elif pd_name == 'method':
575
-                method = pd
576
-            elif pd_name == 'safesearch':
577
-                safesearch = pd
578
-            elif pd_name.startswith('engine_'):
579
-                if pd_name.find('__') > -1:
580
-                    # TODO fix underscore vs space
581
-                    engine_name, category = [x.replace('_', ' ') for x in
582
-                                             pd_name.replace('engine_', '', 1).split('__', 1)]
583
-                    if engine_name in engines and category in engines[engine_name].categories:
584
-                        blocked_engines.append((engine_name, category))
585
-            elif pd_name == 'theme':
586
-                theme = pd if pd in themes else default_theme
587
-            elif pd_name.startswith('plugin_'):
588
-                plugin_id = pd_name.replace('plugin_', '', 1)
589
-                if not any(plugin.id == plugin_id for plugin in plugins):
590
-                    continue
591
-                post_disabled_plugins.append(plugin_id)
592
-            else:
593
-                resp.set_cookie(pd_name, pd, max_age=cookie_max_age)
594
-
595
-        disabled_plugins = []
596
-        allowed_plugins = []
597
-        for plugin in plugins:
598
-            if plugin.default_on:
599
-                if plugin.id in post_disabled_plugins:
600
-                    disabled_plugins.append(plugin.id)
601
-            elif plugin.id not in post_disabled_plugins:
602
-                allowed_plugins.append(plugin.id)
603
-
604
-        resp.set_cookie('disabled_plugins', ','.join(disabled_plugins), max_age=cookie_max_age)
532
+    """Render preferences page && save user preferences"""
605 533
 
606
-        resp.set_cookie('allowed_plugins', ','.join(allowed_plugins), max_age=cookie_max_age)
607
-
608
-        resp.set_cookie(
609
-            'blocked_engines', ','.join('__'.join(e) for e in blocked_engines),
610
-            max_age=cookie_max_age
611
-        )
612
-
613
-        if locale:
614
-            resp.set_cookie(
615
-                'locale', locale,
616
-                max_age=cookie_max_age
617
-            )
618
-
619
-        if lang:
620
-            resp.set_cookie(
621
-                'language', lang,
622
-                max_age=cookie_max_age
623
-            )
624
-
625
-        if selected_categories:
626
-            # cookie max age: 4 weeks
627
-            resp.set_cookie(
628
-                'categories', ','.join(selected_categories),
629
-                max_age=cookie_max_age
630
-            )
631
-
632
-            resp.set_cookie(
633
-                'autocomplete', autocomplete,
634
-                max_age=cookie_max_age
635
-            )
636
-
637
-        resp.set_cookie('method', method, max_age=cookie_max_age)
638
-
639
-        resp.set_cookie('safesearch', str(safesearch), max_age=cookie_max_age)
640
-
641
-        resp.set_cookie('image_proxy', image_proxy, max_age=cookie_max_age)
642
-
643
-        resp.set_cookie('theme', theme, max_age=cookie_max_age)
644
-
645
-        return resp
534
+    # save preferences
535
+    if request.method == 'POST':
536
+        resp = make_response(redirect(urljoin(settings['server']['base_url'], url_for('index'))))
537
+        try:
538
+            request.preferences.parse_form(request.form)
539
+        except ValidationException:
540
+            # TODO use flash feature of flask
541
+            return resp
542
+        return request.preferences.save(resp)
543
+
544
+    # render preferences
545
+    image_proxy = request.preferences.get_value('image_proxy')
546
+    lang = request.preferences.get_value('language')
547
+    blocked_engines = request.preferences.engines.get_disabled()
548
+    allowed_plugins = request.preferences.plugins.get_enabled()
646 549
 
647 550
     # stats for preferences page
648 551
     stats = {}
@@ -664,7 +567,7 @@ def preferences():
664 567
     return render('preferences.html',
665 568
                   locales=settings['locales'],
666 569
                   current_locale=get_locale(),
667
-                  current_language=lang or 'all',
570
+                  current_language=lang,
668 571
                   image_proxy=image_proxy,
669 572
                   language_codes=language_codes,
670 573
                   engines_by_category=categories,
@@ -674,7 +577,7 @@ def preferences():
674 577
                   shortcuts={y: x for x, y in engine_shortcuts.items()},
675 578
                   themes=themes,
676 579
                   plugins=plugins,
677
-                  allowed_plugins=[plugin.id for plugin in request.user_plugins],
580
+                  allowed_plugins=allowed_plugins,
678 581
                   theme=get_current_theme_name())
679 582
 
680 583
 
@@ -750,7 +653,7 @@ Disallow: /preferences
750 653
 def opensearch():
751 654
     method = 'post'
752 655
 
753
-    if request.cookies.get('method', 'POST') == 'GET':
656
+    if request.preferences.get_value('method') == 'GET':
754 657
         method = 'get'
755 658
 
756 659
     # chrome/chromium only supports HTTP GET....

+ 5
- 1
tests/unit/test_webapp.py 查看文件

@@ -12,7 +12,6 @@ class ViewsTestCase(SearxTestCase):
12 12
     def setUp(self):
13 13
         webapp.app.config['TESTING'] = True  # to get better error messages
14 14
         self.app = webapp.app.test_client()
15
-        webapp.default_theme = 'default'
16 15
 
17 16
         # set some defaults
18 17
         self.test_results = [
@@ -43,6 +42,11 @@ class ViewsTestCase(SearxTestCase):
43 42
 
44 43
         webapp.Search.search = search_mock
45 44
 
45
+        def get_current_theme_name_mock(override=None):
46
+            return 'default'
47
+
48
+        webapp.get_current_theme_name = get_current_theme_name_mock
49
+
46 50
         self.maxDiff = None  # to see full diffs
47 51
 
48 52
     def test_index_empty(self):