From 244ffef3151f94b2d3fd00b54fff16a4bd4970ab Mon Sep 17 00:00:00 2001 From: Martin Fischer Date: Mon, 31 Jan 2022 13:46:16 +0100 Subject: [PATCH] [help] substitute variables with string.Template Previously help pages could link to instance-specific pages with e.g. [search engines][url_for:preferences] which was possible because searx.user_help was prefixing the Markdown: [url_for:preferences]: /preferences There are two problems with this: 1. typos in the Markdown files could go by unnoticed (since Markdown treats [foo][doesntexist] as regular text) 2. it doesn't let Markdown append their own query string (which would however be handy to link example search queries to the search page) This commit addresses both of these problems by using string.Template from the Python standard library. Why don't we use Jinja2 for this? Well Jinja2 would be overkill. We don't need any logic in the user documentation. And in the future we might outsource the user documentation translation to Weblate, in which case not permitting any logic in them is more secure. --- searx/help/en/about.md | 14 +++++++------- searx/user_help.py | 28 ++++++++++++++++++++-------- 2 files changed, 27 insertions(+), 15 deletions(-) diff --git a/searx/help/en/about.md b/searx/help/en/about.md index dd7cd4515..865e104fa 100644 --- a/searx/help/en/about.md +++ b/searx/help/en/about.md @@ -1,12 +1,12 @@ # About SearXNG SearXNG is a fork from the well-known [searx] [metasearch engine], aggregating -the results of other [search engines][url_for:preferences] while not storing +the results of other [search engines]($preferences) while not storing information about its users. More about SearXNG ... -* [SearXNG sources][brand.git_url] +* [SearXNG sources]($brand.git_url) * [weblate] --- @@ -20,7 +20,7 @@ More about SearXNG ... with a third party, and it can't be used to compromise you. * SearXNG is free software, the code is 100% open and you can help - to make it better. See more on [SearXNG sources][brand.git_url]. + to make it better. See more on [SearXNG sources]($brand.git_url). If you do care about privacy, want to be a conscious user, or otherwise believe in digital freedom, make SearXNG your default search engine or run @@ -49,20 +49,20 @@ search engine, see your browser's documentation: ## Where to find anonymous usage statistics of this instance ? -[Stats page][url_for:stats] contains some useful data about the engines used. +[Stats page]($stats) contains some useful data about the engines used. ## How can I make it my own? SearXNG appreciates your concern regarding logs, so take the code from -the [SearXNG project][brand.git_url] and run it yourself! +the [SearXNG project]($brand.git_url) and run it yourself! -Add your instance to this [list of public instances][brand.public_instances] to +Add your instance to this [list of public instances]($brand.public_instances) to help other people reclaim their privacy and make the Internet freer! The more decentralized the Internet is, the more freedom we have! ## Where are the docs & code of this instance? -See the [SearXNG docs][brand.docs_url] and [SearXNG sources][brand.git_url] +See the [SearXNG docs]($brand.docs_url) and [SearXNG sources]($brand.git_url) [searx]: https://github.com/searx/searx [metasearch engine]: https://en.wikipedia.org/wiki/Metasearch_engine diff --git a/searx/user_help.py b/searx/user_help.py index 1914c7c84..446c52ca8 100644 --- a/searx/user_help.py +++ b/searx/user_help.py @@ -1,6 +1,8 @@ # pyright: basic from typing import Dict, NamedTuple import pkg_resources +import string +import sys import flask from flask.helpers import url_for @@ -22,6 +24,10 @@ PAGES: Dict[str, HelpPage] = {} """ Maps a filename under help/ without the file extension to the rendered page. """ +class Template(string.Template): + idpattern = '(:?[a-z._:]+)' + + def render(app: flask.Flask): """ Renders the user documentation. Must be called after all Flask routes have been @@ -30,7 +36,7 @@ def render(app: flask.Flask): We render the user documentation once on startup to improve performance. """ - link_targets = { + variables = { 'brand.git_url': GIT_URL, 'brand.public_instances': get_setting('brand.public_instances'), 'brand.docs_url': get_setting('brand.docs_url'), @@ -40,15 +46,21 @@ def render(app: flask.Flask): # we specify base_url so that url_for works for base_urls that have a non-root path with app.test_request_context(base_url=base_url): - link_targets['url_for:index'] = url_for('index') - link_targets['url_for:preferences'] = url_for('preferences') - link_targets['url_for:stats'] = url_for('stats') - - define_link_targets = ''.join(f'[{name}]: {url}\n' for name, url in link_targets.items()) + variables['index'] = url_for('index') + variables['preferences'] = url_for('preferences') + variables['stats'] = url_for('stats') for pagename in _TOC: - file_content = pkg_resources.resource_string(__name__, 'help/en/' + pagename + '.md').decode() - markdown = define_link_targets + file_content + filename = 'help/en/' + pagename + '.md' + file_content = pkg_resources.resource_string(__name__, filename).decode() + try: + markdown = Template(file_content).substitute(variables) + except KeyError as e: + print('[FATAL ERROR] undefined variable ${} in {}'.format(e.args[0], filename)) + print('available variables are:') + for key in variables: + print('\t' + key) + sys.exit(1) assert file_content.startswith('# ') title = file_content.split('\n', maxsplit=1)[0].strip('# ') content: str = mistletoe.markdown(markdown)