diff options
| author | Markus Heiser <markus.heiser@darmarIT.de> | 2020-04-29 12:55:13 +0000 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2020-04-29 12:55:13 +0000 |
| commit | 4bae1a9eabd33ee095002c0392d26c45e8319159 (patch) | |
| tree | 43601cb54beca64d63457f66a46b1633ffb522c6 /searx/webapp.py | |
| parent | ceceee546b5273d9a1ebce6638ab98c7c34ed58f (diff) | |
| parent | 7342806987aec05c50f12e149683609640ba66a0 (diff) | |
Merge branch 'master' into fix/manage.sh
Diffstat (limited to 'searx/webapp.py')
| -rwxr-xr-x[-rw-r--r--] | searx/webapp.py | 202 |
1 files changed, 149 insertions, 53 deletions
diff --git a/searx/webapp.py b/searx/webapp.py index 7cf4106d3..8c3531069 100644..100755 --- a/searx/webapp.py +++ b/searx/webapp.py @@ -45,15 +45,20 @@ try: from cgi import escape except: from html import escape +from six import next from datetime import datetime, timedelta from time import time -from werkzeug.contrib.fixers import ProxyFix +from werkzeug.middleware.proxy_fix import ProxyFix from flask import ( Flask, request, render_template, url_for, Response, make_response, redirect, send_from_directory ) +from babel.support import Translations +import flask_babel from flask_babel import Babel, gettext, format_date, format_decimal +from flask.ctx import has_request_context from flask.json import jsonify +from searx import brand from searx import settings, searx_dir, searx_debug from searx.exceptions import SearxParameterException from searx.engines import ( @@ -95,6 +100,8 @@ if sys.version_info[0] == 3: PY3 = True else: PY3 = False + logger.warning('\033[1;31m *** Deprecation Warning ***\033[0m') + logger.warning('\033[1;31m Python2 is deprecated\033[0m') # serve pages with HTTP/1.1 from werkzeug.serving import WSGIRequestHandler @@ -154,21 +161,54 @@ _category_names = (gettext('files'), outgoing_proxies = settings['outgoing'].get('proxies') or None +_flask_babel_get_translations = flask_babel.get_translations + + +# monkey patch for flask_babel.get_translations +def _get_translations(): + if has_request_context() and request.form.get('use-translation') == 'oc': + babel_ext = flask_babel.current_app.extensions['babel'] + return Translations.load(next(babel_ext.translation_directories), 'oc') + + return _flask_babel_get_translations() + + +flask_babel.get_translations = _get_translations + + +def _get_browser_language(request, lang_list): + for lang in request.headers.get("Accept-Language", "en").split(","): + if ';' in lang: + lang = lang.split(';')[0] + locale = match_language(lang, lang_list, fallback=None) + if locale is not None: + return locale + return settings['search']['default_lang'] or 'en' + @babel.localeselector def get_locale(): + locale = _get_browser_language(request, settings['locales'].keys()) + + logger.debug("default locale from browser info is `%s`", locale) + + if request.preferences.get_value('locale') != '': + locale = request.preferences.get_value('locale') + if 'locale' in request.form\ and request.form['locale'] in settings['locales']: - return request.form['locale'] + locale = request.form['locale'] - if 'locale' in request.args\ - and request.args['locale'] in settings['locales']: - return request.args['locale'] + if locale == 'zh_TW': + locale = 'zh_Hant_TW' - if request.preferences.get_value('locale') != '': - return request.preferences.get_value('locale') + if locale == 'oc': + request.form['use-translation'] = 'oc' + locale = 'fr_FR' - return request.accept_languages.best_match(settings['locales'].keys()) + logger.debug("selected locale is `%s`", locale) + + return locale # code-highlighter @@ -346,7 +386,9 @@ def render(template_name, override_theme=None, **kwargs): if 'autocomplete' not in kwargs: kwargs['autocomplete'] = request.preferences.get_value('autocomplete') - if get_locale() in rtl_locales and 'rtl' not in kwargs: + locale = request.preferences.get_value('locale') + + if locale in rtl_locales and 'rtl' not in kwargs: kwargs['rtl'] = True kwargs['searx_version'] = VERSION_STRING @@ -358,8 +400,7 @@ def render(template_name, override_theme=None, **kwargs): kwargs['language_codes'] = languages if 'current_language' not in kwargs: kwargs['current_language'] = match_language(request.preferences.get_value('language'), - LANGUAGE_CODES, - fallback=settings['search']['language']) + LANGUAGE_CODES) # override url_for function in templates kwargs['url_for'] = url_for_theme @@ -386,6 +427,8 @@ def render(template_name, override_theme=None, **kwargs): kwargs['preferences'] = request.preferences + kwargs['brand'] = brand + kwargs['scripts'] = set() for plugin in request.user_plugins: for script in plugin.js_dependencies: @@ -429,6 +472,12 @@ def pre_request(): logger.exception('invalid settings') request.errors.append(gettext('Invalid settings')) + # init search language and locale + if not preferences.get_value("language"): + preferences.parse_dict({"language": _get_browser_language(request, LANGUAGE_CODES)}) + if not preferences.get_value("locale"): + preferences.parse_dict({"locale": get_locale()}) + # request.user_plugins request.user_plugins = [] allowed_plugins = preferences.plugins.get_enabled() @@ -577,25 +626,38 @@ def index(): 'corrections': list(result_container.corrections), 'infoboxes': result_container.infoboxes, 'suggestions': list(result_container.suggestions), - 'unresponsive_engines': list(result_container.unresponsive_engines)}, + 'unresponsive_engines': __get_translated_errors(result_container.unresponsive_engines)}, # noqa default=lambda item: list(item) if isinstance(item, set) else item), mimetype='application/json') elif output_format == 'csv': csv = UnicodeWriter(StringIO()) - keys = ('title', 'url', 'content', 'host', 'engine', 'score') + keys = ('title', 'url', 'content', 'host', 'engine', 'score', 'type') csv.writerow(keys) for row in results: row['host'] = row['parsed_url'].netloc + row['type'] = 'result' + csv.writerow([row.get(key, '') for key in keys]) + for a in result_container.answers: + row = {'title': a, 'type': 'answer'} + csv.writerow([row.get(key, '') for key in keys]) + for a in result_container.suggestions: + row = {'title': a, 'type': 'suggestion'} + csv.writerow([row.get(key, '') for key in keys]) + for a in result_container.corrections: + row = {'title': a, 'type': 'correction'} csv.writerow([row.get(key, '') for key in keys]) csv.stream.seek(0) response = Response(csv.stream.read(), mimetype='application/csv') - cont_disp = 'attachment;Filename=searx_-_{0}.csv'.format(search_query.query) + cont_disp = 'attachment;Filename=searx_-_{0}.csv'.format(search_query.query.decode('utf-8')) response.headers.add('Content-Disposition', cont_disp) return response elif output_format == 'rss': response_rss = render( 'opensearch_response_rss.xml', results=results, + answers=result_container.answers, + corrections=result_container.corrections, + suggestions=result_container.suggestions, q=request.form['q'], number_of_results=number_of_results, base_url=get_base_url(), @@ -606,11 +668,11 @@ def index(): # HTML output format # suggestions: use RawTextQuery to get the suggestion URLs with the same bang - suggestion_urls = map(lambda suggestion: { - 'url': raw_text_query.changeSearchQuery(suggestion).getFullQuery(), - 'title': suggestion - }, - result_container.suggestions) + suggestion_urls = list(map(lambda suggestion: { + 'url': raw_text_query.changeSearchQuery(suggestion).getFullQuery(), + 'title': suggestion + }, + result_container.suggestions)) correction_urls = list(map(lambda correction: { 'url': raw_text_query.changeSearchQuery(correction).getFullQuery(), @@ -632,10 +694,10 @@ def index(): corrections=correction_urls, infoboxes=result_container.infoboxes, paging=result_container.paging, - unresponsive_engines=result_container.unresponsive_engines, + unresponsive_engines=__get_translated_errors(result_container.unresponsive_engines), current_language=match_language(search_query.lang, LANGUAGE_CODES, - fallback=settings['search']['language']), + fallback=request.preferences.get_value("language")), base_url=get_base_url(), theme=get_current_theme_name(), favicons=global_favicons[themes.index(get_current_theme_name())], @@ -643,6 +705,16 @@ def index(): ) +def __get_translated_errors(unresponsive_engines): + translated_errors = [] + for unresponsive_engine in unresponsive_engines: + error_msg = gettext(unresponsive_engine[1]) + if unresponsive_engine[2]: + error_msg = "{} {}".format(error_msg, unresponsive_engine[2]) + translated_errors.append((unresponsive_engine[0], error_msg)) + return translated_errors + + @app.route('/about', methods=['GET']) def about(): """Render about page""" @@ -729,8 +801,13 @@ def preferences(): # stats for preferences page stats = {} + engines_by_category = {} for c in categories: + engines_by_category[c] = [] for e in categories[c]: + if not request.preferences.validate_token(e): + continue + stats[e.name] = {'time': None, 'warn_timeout': False, 'warn_time': False} @@ -738,9 +815,11 @@ def preferences(): stats[e.name]['warn_timeout'] = True stats[e.name]['supports_selected_language'] = _is_selected_language_supported(e, request.preferences) + engines_by_category[c].append(e) + # get first element [0], the engine time, # and then the second element [1] : the time (the first one is the label) - for engine_stat in get_engines_stats()[0][1]: + for engine_stat in get_engines_stats(request.preferences)[0][1]: stats[engine_stat.get('name')]['time'] = round(engine_stat.get('avg'), 3) if engine_stat.get('avg') > settings['outgoing']['request_timeout']: stats[engine_stat.get('name')]['warn_time'] = True @@ -748,9 +827,9 @@ def preferences(): return render('preferences.html', locales=settings['locales'], - current_locale=get_locale(), + current_locale=request.preferences.get_value("locale"), image_proxy=image_proxy, - engines_by_category=categories, + engines_by_category=engines_by_category, stats=stats, answerers=[{'info': a.self_info(), 'keywords': a.keywords} for a in answerers], disabled_engines=disabled_engines, @@ -826,7 +905,7 @@ def image_proxy(): @app.route('/stats', methods=['GET']) def stats(): """Render engine statistics page.""" - stats = get_engines_stats() + stats = get_engines_stats(request.preferences) return render( 'stats.html', stats=stats, @@ -888,34 +967,51 @@ def clear_cookies(): @app.route('/config') def config(): - return jsonify({'categories': list(categories.keys()), - 'engines': [{'name': engine_name, - 'categories': engine.categories, - 'shortcut': engine.shortcut, - 'enabled': not engine.disabled, - 'paging': engine.paging, - 'language_support': engine.language_support, - 'supported_languages': - list(engine.supported_languages.keys()) - if isinstance(engine.supported_languages, dict) - else engine.supported_languages, - 'safesearch': engine.safesearch, - 'time_range_support': engine.time_range_support, - 'timeout': engine.timeout} - for engine_name, engine in engines.items()], - 'plugins': [{'name': plugin.name, - 'enabled': plugin.default_on} - for plugin in plugins], - 'instance_name': settings['general']['instance_name'], - 'locales': settings['locales'], - 'default_locale': settings['ui']['default_locale'], - 'autocomplete': settings['search']['autocomplete'], - 'safe_search': settings['search']['safe_search'], - 'default_theme': settings['ui']['default_theme'], - 'version': VERSION_STRING, - 'doi_resolvers': [r for r in settings['doi_resolvers']], - 'default_doi_resolver': settings['default_doi_resolver'], - }) + """Return configuration in JSON format.""" + _engines = [] + for name, engine in engines.items(): + if not request.preferences.validate_token(engine): + continue + + supported_languages = engine.supported_languages + if isinstance(engine.supported_languages, dict): + supported_languages = list(engine.supported_languages.keys()) + + _engines.append({ + 'name': name, + 'categories': engine.categories, + 'shortcut': engine.shortcut, + 'enabled': not engine.disabled, + 'paging': engine.paging, + 'language_support': engine.language_support, + 'supported_languages': supported_languages, + 'safesearch': engine.safesearch, + 'time_range_support': engine.time_range_support, + 'timeout': engine.timeout + }) + + _plugins = [] + for _ in plugins: + _plugins.append({'name': _.name, 'enabled': _.default_on}) + + return jsonify({ + 'categories': list(categories.keys()), + 'engines': _engines, + 'plugins': _plugins, + 'instance_name': settings['general']['instance_name'], + 'locales': settings['locales'], + 'default_locale': settings['ui']['default_locale'], + 'autocomplete': settings['search']['autocomplete'], + 'safe_search': settings['search']['safe_search'], + 'default_theme': settings['ui']['default_theme'], + 'version': VERSION_STRING, + 'brand': { + 'GIT_URL': brand.GIT_URL, + 'DOCS_URL': brand.DOCS_URL + }, + 'doi_resolvers': [r for r in settings['doi_resolvers']], + 'default_doi_resolver': settings['default_doi_resolver'], + }) @app.errorhandler(404) |