mirror of
https://github.com/searxng/searxng
synced 2024-01-01 19:24:07 +01:00
192 lines
6.2 KiB
Python
192 lines
6.2 KiB
Python
import os
|
|
from typing import Optional
|
|
from urllib.parse import parse_qs, urlencode, urlsplit
|
|
|
|
|
|
import jinja2
|
|
import babel.support
|
|
|
|
from pygments import highlight
|
|
from pygments.lexers import get_lexer_by_name
|
|
from pygments.formatters import HtmlFormatter # pylint: disable=no-name-in-module
|
|
|
|
from starlette.requests import Request
|
|
from starlette.templating import Jinja2Templates
|
|
from starlette_context import context
|
|
from starlette.routing import NoMatchFound
|
|
from starlette_i18n import i18n
|
|
|
|
from searx import logger, settings
|
|
from searx.webutils import (
|
|
get_static_files,
|
|
get_result_templates,
|
|
get_themes,
|
|
)
|
|
|
|
|
|
# about static
|
|
logger.debug('static directory is %s', settings['ui']['static_path'])
|
|
static_files = get_static_files(settings['ui']['static_path'])
|
|
|
|
# about templates
|
|
logger.debug('templates directory is %s', settings['ui']['templates_path'])
|
|
default_theme = settings['ui']['default_theme']
|
|
templates_path = settings['ui']['templates_path']
|
|
themes = get_themes(templates_path)
|
|
result_templates = get_result_templates(templates_path)
|
|
global_favicons = []
|
|
for indice, theme in enumerate(themes):
|
|
global_favicons.append([])
|
|
theme_img_path = os.path.join(settings['ui']['static_path'], 'themes', theme, 'img', 'icons')
|
|
for (dirpath, dirnames, filenames) in os.walk(theme_img_path):
|
|
global_favicons[indice].extend(filenames)
|
|
|
|
|
|
def get_current_theme_name(request: Request, override: Optional[str] =None) -> str:
|
|
"""Returns theme name.
|
|
|
|
Checks in this order:
|
|
1. override
|
|
2. cookies
|
|
3. settings"""
|
|
|
|
if override and (override in themes or override == '__common__'):
|
|
return override
|
|
theme_name = request.query_params.get('theme', context.preferences.get_value('theme')) # pylint: disable=no-member
|
|
if theme_name not in themes:
|
|
theme_name = default_theme
|
|
return theme_name
|
|
|
|
|
|
def get_result_template(theme_name: str, template_name: str) -> str:
|
|
themed_path = theme_name + '/result_templates/' + template_name
|
|
if themed_path in result_templates:
|
|
return themed_path
|
|
return 'result_templates/' + template_name
|
|
|
|
|
|
# code-highlighter
|
|
def code_highlighter(codelines, language=None):
|
|
if not language:
|
|
language = 'text'
|
|
|
|
try:
|
|
# find lexer by programing language
|
|
lexer = get_lexer_by_name(language, stripall=True)
|
|
|
|
except Exception as e: # pylint: disable=broad-except
|
|
logger.exception(e, exc_info=True)
|
|
# if lexer is not found, using default one
|
|
lexer = get_lexer_by_name('text', stripall=True)
|
|
|
|
html_code = ''
|
|
tmp_code = ''
|
|
last_line = None
|
|
|
|
# parse lines
|
|
for line, code in codelines:
|
|
if not last_line:
|
|
line_code_start = line
|
|
|
|
# new codeblock is detected
|
|
if last_line is not None and\
|
|
last_line + 1 != line:
|
|
|
|
# highlight last codepart
|
|
formatter = HtmlFormatter(
|
|
linenos='inline', linenostart=line_code_start, cssclass="code-highlight"
|
|
)
|
|
html_code = html_code + highlight(tmp_code, lexer, formatter)
|
|
|
|
# reset conditions for next codepart
|
|
tmp_code = ''
|
|
line_code_start = line
|
|
|
|
# add codepart
|
|
tmp_code += code + '\n'
|
|
|
|
# update line
|
|
last_line = line
|
|
|
|
# highlight last codepart
|
|
formatter = HtmlFormatter(linenos='inline', linenostart=line_code_start, cssclass="code-highlight")
|
|
html_code = html_code + highlight(tmp_code, lexer, formatter)
|
|
|
|
return html_code
|
|
|
|
|
|
class I18NTemplates(Jinja2Templates):
|
|
"""Custom Jinja2Templates with i18n support
|
|
"""
|
|
|
|
@staticmethod
|
|
def url_for_theme(endpoint: str, override_theme=None, **values):
|
|
request = context.request # pylint: disable=no-member
|
|
|
|
# starlette migration
|
|
if '_external' in values:
|
|
del values['_external']
|
|
if 'filename' in values:
|
|
values['path'] = values['filename']
|
|
del values['filename']
|
|
|
|
#
|
|
if endpoint == 'static' and values.get('path'):
|
|
theme_name = get_current_theme_name(request, override=override_theme)
|
|
filename_with_theme = "themes/{}/{}".format(theme_name, values['path'])
|
|
if filename_with_theme in static_files:
|
|
values['path'] = filename_with_theme
|
|
return request.url_for(endpoint, **values)
|
|
try:
|
|
url_for_args = {}
|
|
for k in ('path', 'filename'):
|
|
if k in values:
|
|
v = values.pop(k)
|
|
url_for_args[k] = v
|
|
url = request.url_for(endpoint, **url_for_args)
|
|
_url = urlsplit(url)
|
|
_query = parse_qs(_url.query)
|
|
_query.update(values)
|
|
querystr = urlencode(_query, doseq=True)
|
|
return _url._replace(query=querystr).geturl()
|
|
# if anchor is not None:
|
|
# rv += f"#{url_quote(anchor)}"
|
|
except NoMatchFound as e:
|
|
error_message = "url_for, endpoint='%s' not found (values=%s)" % (endpoint, str(values))
|
|
logger.error(error_message)
|
|
context.errors.append(error_message) # pylint: disable=no-member
|
|
raise e
|
|
|
|
@staticmethod
|
|
def ugettext(message):
|
|
translations = i18n.get_locale().translations
|
|
if isinstance(message, babel.support.LazyProxy):
|
|
message = message.value
|
|
return translations.ugettext(message)
|
|
|
|
@staticmethod
|
|
def ungettext(*args):
|
|
translations = i18n.get_locale().translations
|
|
return translations.ungettext(*args)
|
|
|
|
def _create_env(self, directory: str) -> "jinja2.Environment":
|
|
loader = jinja2.FileSystemLoader(directory)
|
|
env = jinja2.Environment(
|
|
loader=loader,
|
|
autoescape=True,
|
|
trim_blocks=True,
|
|
lstrip_blocks=True,
|
|
auto_reload=False,
|
|
extensions=[
|
|
'jinja2.ext.loopcontrols',
|
|
'jinja2.ext.i18n'
|
|
],
|
|
)
|
|
env.filters["code_highlighter"] = code_highlighter
|
|
env.globals["url_for"] = I18NTemplates.url_for_theme
|
|
env.install_gettext_callables( # pylint: disable=no-member
|
|
I18NTemplates.ugettext,
|
|
I18NTemplates.ungettext,
|
|
newstyle=True
|
|
)
|
|
return env
|