@@ -56,7 +56,7 @@ from searx.engines import (
5656from searx.utils import (
5757 UnicodeWriter, highlight_content, html_to_text, get_themes,
5858 get_static_files, get_result_templates, gen_useragent, dict_subset,
59− prettify_url, get_blocked_engines
59+ prettify_url
6060)
6161from searx.version import VERSION_STRING
6262from searx.languages import language_codes
@@ -64,6 +64,7 @@ from searx.search import Search
6464from searx.query import Query
6565from searx.autocomplete import searx_bang, backends as autocomplete_backends
6666from searx.plugins import plugins
67+from searx.preferences import Preferences
6768
6869# check if the pyopenssl, ndg-httpsclient, pyasn1 packages are installed.
6970# They are needed for SSL connection without trouble, see #298
@@ -109,8 +110,6 @@ for indice, theme in enumerate(themes):
109110 for (dirpath, dirnames, filenames) in os.walk(theme_img_path):
110111 global_favicons[indice].extend(filenames)
111112
112−cookie_max_age = 60 * 60 * 24 * 365 * 5 # 5 years
113−
114113_category_names = (gettext('files'),
115114 gettext('general'),
116115 gettext('music'),
@@ -222,9 +221,7 @@ def get_current_theme_name(override=None):
222221
223222 if override and override in themes:
224223 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'))
228225 if theme_name not in themes:
229226 theme_name = default_theme
230227 return theme_name
@@ -262,12 +259,8 @@ def image_proxify(url):
262259
263260
264261def 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')
271264
272265 nonblocked_categories = set(category for engine_name in engines
273266 for category in engines[engine_name].categories
@@ -295,7 +288,7 @@ def render(template_name, override_theme=None, **kwargs):
295288 kwargs['selected_categories'].append(c)
296289
297290 if not kwargs['selected_categories']:
298− cookie_categories = request.cookies.get('categories', '').split(',')
291+ cookie_categories = request.preferences.get_value('categories')
299292 for ccateg in cookie_categories:
300293 if ccateg in categories:
301294 kwargs['selected_categories'].append(ccateg)
@@ -311,9 +304,9 @@ def render(template_name, override_theme=None, **kwargs):
311304
312305 kwargs['searx_version'] = VERSION_STRING
313306
314− kwargs['method'] = request.cookies.get('method', 'POST')
307+ kwargs['method'] = request.preferences.get_value('method')
315308
316− kwargs['safesearch'] = request.cookies.get('safesearch', str(settings['search']['safe_search']))
309+ kwargs['safesearch'] = str(request.preferences.get_value('safesearch'))
317310
318311 # override url_for function in templates
319312 kwargs['url_for'] = url_for_theme
@@ -347,14 +340,18 @@ def render(template_name, override_theme=None, **kwargs):
347340@app.before_request
348341def pre_request():
349342 # merge GET, POST vars
343+ preferences = Preferences(themes, categories.keys(), engines, plugins)
344+ preferences.parse_cookies(request.cookies)
345+ request.preferences = preferences
346+
350347 request.form = dict(request.form.items())
351348 for k, v in request.args.items():
352349 if k not in request.form:
353350 request.form[k] = v
354351
355352 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()
358355 for plugin in plugins:
359356 if ((plugin.default_on and plugin.id not in disabled_plugins)
360357 or plugin.id in allowed_plugins):
@@ -486,7 +483,7 @@ def autocompleter():
486483 request_data = request.args
487484
488485 # set blocked engines
489− blocked_engines = get_blocked_engines(engines, request.cookies)
486+ blocked_engines = request.preferences.engines.get_disabled()
490487
491488 # parse query
492489 query = Query(request_data.get('q', '').encode('utf-8'), blocked_engines)
@@ -496,8 +493,8 @@ def autocompleter():
496493 if not query.getSearchQuery():
497494 return '', 400
498495
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'))
501498
502499 # parse searx specific autocompleter results like !bang
503500 raw_results = searx_bang(query)
@@ -532,117 +529,23 @@ def autocompleter():
532529
533530@app.route('/preferences', methods=['GET', 'POST'])
534531def 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"""
605533
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()
646549
647550 # stats for preferences page
648551 stats = {}
@@ -664,7 +567,7 @@ def preferences():
664567 return render('preferences.html',
665568 locales=settings['locales'],
666569 current_locale=get_locale(),
667− current_language=lang or 'all',
570+ current_language=lang,
668571 image_proxy=image_proxy,
669572 language_codes=language_codes,
670573 engines_by_category=categories,
@@ -674,7 +577,7 @@ def preferences():
674577 shortcuts={y: x for x, y in engine_shortcuts.items()},
675578 themes=themes,
676579 plugins=plugins,
677− allowed_plugins=[plugin.id for plugin in request.user_plugins],
580+ allowed_plugins=allowed_plugins,
678581 theme=get_current_theme_name())
679582
680583
@@ -750,7 +653,7 @@ Disallow: /preferences
750653def opensearch():
751654 method = 'post'
752655
753− if request.cookies.get('method', 'POST') == 'GET':
656+ if request.preferences.get_value('method') == 'GET':
754657 method = 'get'
755658
756659 # chrome/chromium only supports HTTP GET....