We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
move donation page to docs.searxng.org and link to it from instances
<!-- PLEASE FILL THESE FIELDS, IT REALLY HELPS THE MAINTAINERS OF SearXNG -->

**Version of SearXNG, commit number if you are using on master branch and stipulate if you forked SearXNG**
```
3a75d3c1ccbf979a483b4e5b209a59db9876ba33; searxng master
``` 

**How did you install SearXNG?**

with docker

**What happened?**
There is a donation page on the instance.

**Expected behavior**
The donation page should be in the official documentation and linked to by instances instead of having it on the instance. Also: There should be more information about who is receiving the money and what it is used for.

**Screenshots & Logs**
<!-- If applicable, add screenshots, logs to help explain your problem. -->

**Additional context**
Suggestion in matrix, since donation page on instances could mean legal trouble.


--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `searx/infopage/__init__.py`
Content:
```
1 # SPDX-License-Identifier: AGPL-3.0-or-later
2 # lint: pylint
3 # pyright: basic
4 """Render SearXNG instance documentation.
5 
6 Usage in a Flask app route:
7 
8 .. code:: python
9 
10   from searx import infopage
11 
12   _INFO_PAGES = infopage.InfoPageSet(infopage.MistletoePage)
13 
14   @app.route('/info/<pagename>', methods=['GET'])
15   def info(pagename):
16 
17       locale = request.preferences.get_value('locale')
18       page = _INFO_PAGES.get_page(pagename, locale)
19 
20 """
21 
22 __all__ = ['InfoPage', 'InfoPageSet']
23 
24 import os
25 import os.path
26 import logging
27 import typing
28 
29 import urllib.parse
30 import jinja2
31 from flask.helpers import url_for
32 from markdown_it import MarkdownIt
33 
34 from .. import get_setting
35 from ..compat import cached_property
36 from ..version import GIT_URL
37 from ..locales import LOCALE_NAMES
38 
39 
40 logger = logging.getLogger('searx.infopage')
41 _INFO_FOLDER = os.path.abspath(os.path.dirname(__file__))
42 
43 
44 class InfoPage:
45     """A page of the :py:obj:`online documentation <InfoPageSet>`."""
46 
47     def __init__(self, fname):
48         self.fname = fname
49 
50     @cached_property
51     def raw_content(self):
52         """Raw content of the page (without any jinja rendering)"""
53         with open(self.fname, 'r', encoding='utf-8') as f:
54             return f.read()
55 
56     @cached_property
57     def content(self):
58         """Content of the page (rendered in a Jinja conntext)"""
59         ctx = self.get_ctx()
60         template = jinja2.Environment().from_string(self.raw_content)
61         return template.render(**ctx)
62 
63     @cached_property
64     def title(self):
65         """Title of the content (without any markup)"""
66         t = ""
67         for l in self.raw_content.split('\n'):
68             if l.startswith('# '):
69                 t = l.strip('# ')
70         return t
71 
72     @cached_property
73     def html(self):
74         """Render Markdown (CommonMark_) to HTML by using markdown-it-py_.
75 
76         .. _CommonMark: https://commonmark.org/
77         .. _markdown-it-py: https://github.com/executablebooks/markdown-it-py
78 
79         """
80         return (
81             MarkdownIt("commonmark", {"typographer": True}).enable(["replacements", "smartquotes"]).render(self.content)
82         )
83 
84     def get_ctx(self):
85         """Jinja context to render :py:obj:`InfoPage.content`"""
86 
87         def _md_link(name, url):
88             url = url_for(url, _external=True)
89             return "[%s](%s)" % (name, url)
90 
91         def _md_search(query):
92             url = '%s?q=%s' % (url_for('search', _external=True), urllib.parse.quote(query))
93             return '[%s](%s)' % (query, url)
94 
95         ctx = {}
96         ctx['GIT_URL'] = GIT_URL
97         ctx['get_setting'] = get_setting
98         ctx['link'] = _md_link
99         ctx['search'] = _md_search
100 
101         return ctx
102 
103     def __repr__(self):
104         return f'<{self.__class__.__name__} fname={self.fname!r}>'
105 
106 
107 class InfoPageSet:  # pylint: disable=too-few-public-methods
108     """Cached rendering of the online documentation a SearXNG instance has.
109 
110     :param page_class: render online documentation by :py:obj:`InfoPage` parser.
111     :type page_class: :py:obj:`InfoPage`
112 
113     :param info_folder: information directory
114     :type info_folder: str
115     """
116 
117     def __init__(
118         self, page_class: typing.Optional[typing.Type[InfoPage]] = None, info_folder: typing.Optional[str] = None
119     ):
120         self.page_class = page_class or InfoPage
121         self.folder: str = info_folder or _INFO_FOLDER
122         """location of the Markdwon files"""
123 
124         self.CACHE: typing.Dict[tuple, typing.Optional[InfoPage]] = {}
125 
126         self.locale_default: str = 'en'
127         """default language"""
128 
129         self.locales: typing.List[str] = [
130             locale.replace('_', '-') for locale in os.listdir(_INFO_FOLDER) if locale.replace('_', '-') in LOCALE_NAMES
131         ]
132         """list of supported languages (aka locales)"""
133 
134         self.toc: typing.List[str] = [
135             'search-syntax',
136             'about',
137             'donate',
138         ]
139         """list of articles in the online documentation"""
140 
141     def get_page(self, pagename: str, locale: typing.Optional[str] = None):
142         """Return ``pagename`` instance of :py:obj:`InfoPage`
143 
144         :param pagename: name of the page, a value from :py:obj:`InfoPageSet.toc`
145         :type pagename: str
146 
147         :param locale: language of the page, e.g. ``en``, ``zh_Hans_CN``
148                        (default: :py:obj:`InfoPageSet.i18n_origin`)
149         :type locale: str
150 
151         """
152         locale = locale or self.locale_default
153 
154         if pagename not in self.toc:
155             return None
156         if locale not in self.locales:
157             return None
158 
159         cache_key = (pagename, locale)
160         page = self.CACHE.get(cache_key)
161 
162         if page is not None:
163             return page
164 
165         # not yet instantiated
166 
167         fname = os.path.join(self.folder, locale.replace('-', '_'), pagename) + '.md'
168         if not os.path.exists(fname):
169             logger.info('file %s does not exists', fname)
170             self.CACHE[cache_key] = None
171             return None
172 
173         page = self.page_class(fname)
174         self.CACHE[cache_key] = page
175         return page
176 
177     def iter_pages(self, locale: typing.Optional[str] = None, fallback_to_default=False):
178         """Iterate over all pages of the TOC"""
179         locale = locale or self.locale_default
180         for page_name in self.toc:
181             page_locale = locale
182             page = self.get_page(page_name, locale)
183             if fallback_to_default and page is None:
184                 page_locale = self.locale_default
185                 page = self.get_page(page_name, self.locale_default)
186             yield page_name, page_locale, page
187
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/searx/infopage/__init__.py b/searx/infopage/__init__.py
--- a/searx/infopage/__init__.py
+++ b/searx/infopage/__init__.py
@@ -157,10 +157,9 @@
             return None
 
         cache_key = (pagename, locale)
-        page = self.CACHE.get(cache_key)
 
-        if page is not None:
-            return page
+        if cache_key in self.CACHE:
+            return self.CACHE[cache_key]
 
         # not yet instantiated
 
@@ -183,4 +182,6 @@
             if fallback_to_default and page is None:
                 page_locale = self.locale_default
                 page = self.get_page(page_name, self.locale_default)
-            yield page_name, page_locale, page
+            if page is not None:
+                # page is None if the page was deleted by the administrator
+                yield page_name, page_locale, page

{"golden_diff": "diff --git a/searx/infopage/__init__.py b/searx/infopage/__init__.py\n--- a/searx/infopage/__init__.py\n+++ b/searx/infopage/__init__.py\n@@ -157,10 +157,9 @@\n             return None\n \n         cache_key = (pagename, locale)\n-        page = self.CACHE.get(cache_key)\n \n-        if page is not None:\n-            return page\n+        if cache_key in self.CACHE:\n+            return self.CACHE[cache_key]\n \n         # not yet instantiated\n \n@@ -183,4 +182,6 @@\n             if fallback_to_default and page is None:\n                 page_locale = self.locale_default\n                 page = self.get_page(page_name, self.locale_default)\n-            yield page_name, page_locale, page\n+            if page is not None:\n+                # page is None if the page was deleted by the administrator\n+                yield page_name, page_locale, page\n", "issue": "move donation page to docs.searxng.org and link to it from instances\n<!-- PLEASE FILL THESE FIELDS, IT REALLY HELPS THE MAINTAINERS OF SearXNG -->\r\n\r\n**Version of SearXNG, commit number if you are using on master branch and stipulate if you forked SearXNG**\r\n```\r\n3a75d3c1ccbf979a483b4e5b209a59db9876ba33; searxng master\r\n``` \r\n\r\n**How did you install SearXNG?**\r\n\r\nwith docker\r\n\r\n**What happened?**\r\nThere is a donation page on the instance.\r\n\r\n**Expected behavior**\r\nThe donation page should be in the official documentation and linked to by instances instead of having it on the instance. Also: There should be more information about who is receiving the money and what it is used for.\r\n\r\n**Screenshots & Logs**\r\n<!-- If applicable, add screenshots, logs to help explain your problem. -->\r\n\r\n**Additional context**\r\nSuggestion in matrix, since donation page on instances could mean legal trouble.\r\n\n", "before_files": [{"content": "# SPDX-License-Identifier: AGPL-3.0-or-later\n# lint: pylint\n# pyright: basic\n\"\"\"Render SearXNG instance documentation.\n\nUsage in a Flask app route:\n\n.. code:: python\n\n  from searx import infopage\n\n  _INFO_PAGES = infopage.InfoPageSet(infopage.MistletoePage)\n\n  @app.route('/info/<pagename>', methods=['GET'])\n  def info(pagename):\n\n      locale = request.preferences.get_value('locale')\n      page = _INFO_PAGES.get_page(pagename, locale)\n\n\"\"\"\n\n__all__ = ['InfoPage', 'InfoPageSet']\n\nimport os\nimport os.path\nimport logging\nimport typing\n\nimport urllib.parse\nimport jinja2\nfrom flask.helpers import url_for\nfrom markdown_it import MarkdownIt\n\nfrom .. import get_setting\nfrom ..compat import cached_property\nfrom ..version import GIT_URL\nfrom ..locales import LOCALE_NAMES\n\n\nlogger = logging.getLogger('searx.infopage')\n_INFO_FOLDER = os.path.abspath(os.path.dirname(__file__))\n\n\nclass InfoPage:\n    \"\"\"A page of the :py:obj:`online documentation <InfoPageSet>`.\"\"\"\n\n    def __init__(self, fname):\n        self.fname = fname\n\n    @cached_property\n    def raw_content(self):\n        \"\"\"Raw content of the page (without any jinja rendering)\"\"\"\n        with open(self.fname, 'r', encoding='utf-8') as f:\n            return f.read()\n\n    @cached_property\n    def content(self):\n        \"\"\"Content of the page (rendered in a Jinja conntext)\"\"\"\n        ctx = self.get_ctx()\n        template = jinja2.Environment().from_string(self.raw_content)\n        return template.render(**ctx)\n\n    @cached_property\n    def title(self):\n        \"\"\"Title of the content (without any markup)\"\"\"\n        t = \"\"\n        for l in self.raw_content.split('\\n'):\n            if l.startswith('# '):\n                t = l.strip('# ')\n        return t\n\n    @cached_property\n    def html(self):\n        \"\"\"Render Markdown (CommonMark_) to HTML by using markdown-it-py_.\n\n        .. _CommonMark: https://commonmark.org/\n        .. _markdown-it-py: https://github.com/executablebooks/markdown-it-py\n\n        \"\"\"\n        return (\n            MarkdownIt(\"commonmark\", {\"typographer\": True}).enable([\"replacements\", \"smartquotes\"]).render(self.content)\n        )\n\n    def get_ctx(self):\n        \"\"\"Jinja context to render :py:obj:`InfoPage.content`\"\"\"\n\n        def _md_link(name, url):\n            url = url_for(url, _external=True)\n            return \"[%s](%s)\" % (name, url)\n\n        def _md_search(query):\n            url = '%s?q=%s' % (url_for('search', _external=True), urllib.parse.quote(query))\n            return '[%s](%s)' % (query, url)\n\n        ctx = {}\n        ctx['GIT_URL'] = GIT_URL\n        ctx['get_setting'] = get_setting\n        ctx['link'] = _md_link\n        ctx['search'] = _md_search\n\n        return ctx\n\n    def __repr__(self):\n        return f'<{self.__class__.__name__} fname={self.fname!r}>'\n\n\nclass InfoPageSet:  # pylint: disable=too-few-public-methods\n    \"\"\"Cached rendering of the online documentation a SearXNG instance has.\n\n    :param page_class: render online documentation by :py:obj:`InfoPage` parser.\n    :type page_class: :py:obj:`InfoPage`\n\n    :param info_folder: information directory\n    :type info_folder: str\n    \"\"\"\n\n    def __init__(\n        self, page_class: typing.Optional[typing.Type[InfoPage]] = None, info_folder: typing.Optional[str] = None\n    ):\n        self.page_class = page_class or InfoPage\n        self.folder: str = info_folder or _INFO_FOLDER\n        \"\"\"location of the Markdwon files\"\"\"\n\n        self.CACHE: typing.Dict[tuple, typing.Optional[InfoPage]] = {}\n\n        self.locale_default: str = 'en'\n        \"\"\"default language\"\"\"\n\n        self.locales: typing.List[str] = [\n            locale.replace('_', '-') for locale in os.listdir(_INFO_FOLDER) if locale.replace('_', '-') in LOCALE_NAMES\n        ]\n        \"\"\"list of supported languages (aka locales)\"\"\"\n\n        self.toc: typing.List[str] = [\n            'search-syntax',\n            'about',\n            'donate',\n        ]\n        \"\"\"list of articles in the online documentation\"\"\"\n\n    def get_page(self, pagename: str, locale: typing.Optional[str] = None):\n        \"\"\"Return ``pagename`` instance of :py:obj:`InfoPage`\n\n        :param pagename: name of the page, a value from :py:obj:`InfoPageSet.toc`\n        :type pagename: str\n\n        :param locale: language of the page, e.g. ``en``, ``zh_Hans_CN``\n                       (default: :py:obj:`InfoPageSet.i18n_origin`)\n        :type locale: str\n\n        \"\"\"\n        locale = locale or self.locale_default\n\n        if pagename not in self.toc:\n            return None\n        if locale not in self.locales:\n            return None\n\n        cache_key = (pagename, locale)\n        page = self.CACHE.get(cache_key)\n\n        if page is not None:\n            return page\n\n        # not yet instantiated\n\n        fname = os.path.join(self.folder, locale.replace('-', '_'), pagename) + '.md'\n        if not os.path.exists(fname):\n            logger.info('file %s does not exists', fname)\n            self.CACHE[cache_key] = None\n            return None\n\n        page = self.page_class(fname)\n        self.CACHE[cache_key] = page\n        return page\n\n    def iter_pages(self, locale: typing.Optional[str] = None, fallback_to_default=False):\n        \"\"\"Iterate over all pages of the TOC\"\"\"\n        locale = locale or self.locale_default\n        for page_name in self.toc:\n            page_locale = locale\n            page = self.get_page(page_name, locale)\n            if fallback_to_default and page is None:\n                page_locale = self.locale_default\n                page = self.get_page(page_name, self.locale_default)\n            yield page_name, page_locale, page\n", "path": "searx/infopage/__init__.py"}], "after_files": [{"content": "# SPDX-License-Identifier: AGPL-3.0-or-later\n# lint: pylint\n# pyright: basic\n\"\"\"Render SearXNG instance documentation.\n\nUsage in a Flask app route:\n\n.. code:: python\n\n  from searx import infopage\n\n  _INFO_PAGES = infopage.InfoPageSet(infopage.MistletoePage)\n\n  @app.route('/info/<pagename>', methods=['GET'])\n  def info(pagename):\n\n      locale = request.preferences.get_value('locale')\n      page = _INFO_PAGES.get_page(pagename, locale)\n\n\"\"\"\n\n__all__ = ['InfoPage', 'InfoPageSet']\n\nimport os\nimport os.path\nimport logging\nimport typing\n\nimport urllib.parse\nimport jinja2\nfrom flask.helpers import url_for\nfrom markdown_it import MarkdownIt\n\nfrom .. import get_setting\nfrom ..compat import cached_property\nfrom ..version import GIT_URL\nfrom ..locales import LOCALE_NAMES\n\n\nlogger = logging.getLogger('searx.infopage')\n_INFO_FOLDER = os.path.abspath(os.path.dirname(__file__))\n\n\nclass InfoPage:\n    \"\"\"A page of the :py:obj:`online documentation <InfoPageSet>`.\"\"\"\n\n    def __init__(self, fname):\n        self.fname = fname\n\n    @cached_property\n    def raw_content(self):\n        \"\"\"Raw content of the page (without any jinja rendering)\"\"\"\n        with open(self.fname, 'r', encoding='utf-8') as f:\n            return f.read()\n\n    @cached_property\n    def content(self):\n        \"\"\"Content of the page (rendered in a Jinja conntext)\"\"\"\n        ctx = self.get_ctx()\n        template = jinja2.Environment().from_string(self.raw_content)\n        return template.render(**ctx)\n\n    @cached_property\n    def title(self):\n        \"\"\"Title of the content (without any markup)\"\"\"\n        t = \"\"\n        for l in self.raw_content.split('\\n'):\n            if l.startswith('# '):\n                t = l.strip('# ')\n        return t\n\n    @cached_property\n    def html(self):\n        \"\"\"Render Markdown (CommonMark_) to HTML by using markdown-it-py_.\n\n        .. _CommonMark: https://commonmark.org/\n        .. _markdown-it-py: https://github.com/executablebooks/markdown-it-py\n\n        \"\"\"\n        return (\n            MarkdownIt(\"commonmark\", {\"typographer\": True}).enable([\"replacements\", \"smartquotes\"]).render(self.content)\n        )\n\n    def get_ctx(self):\n        \"\"\"Jinja context to render :py:obj:`InfoPage.content`\"\"\"\n\n        def _md_link(name, url):\n            url = url_for(url, _external=True)\n            return \"[%s](%s)\" % (name, url)\n\n        def _md_search(query):\n            url = '%s?q=%s' % (url_for('search', _external=True), urllib.parse.quote(query))\n            return '[%s](%s)' % (query, url)\n\n        ctx = {}\n        ctx['GIT_URL'] = GIT_URL\n        ctx['get_setting'] = get_setting\n        ctx['link'] = _md_link\n        ctx['search'] = _md_search\n\n        return ctx\n\n    def __repr__(self):\n        return f'<{self.__class__.__name__} fname={self.fname!r}>'\n\n\nclass InfoPageSet:  # pylint: disable=too-few-public-methods\n    \"\"\"Cached rendering of the online documentation a SearXNG instance has.\n\n    :param page_class: render online documentation by :py:obj:`InfoPage` parser.\n    :type page_class: :py:obj:`InfoPage`\n\n    :param info_folder: information directory\n    :type info_folder: str\n    \"\"\"\n\n    def __init__(\n        self, page_class: typing.Optional[typing.Type[InfoPage]] = None, info_folder: typing.Optional[str] = None\n    ):\n        self.page_class = page_class or InfoPage\n        self.folder: str = info_folder or _INFO_FOLDER\n        \"\"\"location of the Markdwon files\"\"\"\n\n        self.CACHE: typing.Dict[tuple, typing.Optional[InfoPage]] = {}\n\n        self.locale_default: str = 'en'\n        \"\"\"default language\"\"\"\n\n        self.locales: typing.List[str] = [\n            locale.replace('_', '-') for locale in os.listdir(_INFO_FOLDER) if locale.replace('_', '-') in LOCALE_NAMES\n        ]\n        \"\"\"list of supported languages (aka locales)\"\"\"\n\n        self.toc: typing.List[str] = [\n            'search-syntax',\n            'about',\n            'donate',\n        ]\n        \"\"\"list of articles in the online documentation\"\"\"\n\n    def get_page(self, pagename: str, locale: typing.Optional[str] = None):\n        \"\"\"Return ``pagename`` instance of :py:obj:`InfoPage`\n\n        :param pagename: name of the page, a value from :py:obj:`InfoPageSet.toc`\n        :type pagename: str\n\n        :param locale: language of the page, e.g. ``en``, ``zh_Hans_CN``\n                       (default: :py:obj:`InfoPageSet.i18n_origin`)\n        :type locale: str\n\n        \"\"\"\n        locale = locale or self.locale_default\n\n        if pagename not in self.toc:\n            return None\n        if locale not in self.locales:\n            return None\n\n        cache_key = (pagename, locale)\n\n        if cache_key in self.CACHE:\n            return self.CACHE[cache_key]\n\n        # not yet instantiated\n\n        fname = os.path.join(self.folder, locale.replace('-', '_'), pagename) + '.md'\n        if not os.path.exists(fname):\n            logger.info('file %s does not exists', fname)\n            self.CACHE[cache_key] = None\n            return None\n\n        page = self.page_class(fname)\n        self.CACHE[cache_key] = page\n        return page\n\n    def iter_pages(self, locale: typing.Optional[str] = None, fallback_to_default=False):\n        \"\"\"Iterate over all pages of the TOC\"\"\"\n        locale = locale or self.locale_default\n        for page_name in self.toc:\n            page_locale = locale\n            page = self.get_page(page_name, locale)\n            if fallback_to_default and page is None:\n                page_locale = self.locale_default\n                page = self.get_page(page_name, self.locale_default)\n            if page is not None:\n                # page is None if the page was deleted by the administrator\n                yield page_name, page_locale, page\n", "path": "searx/infopage/__init__.py"}]}