We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Robots.txt can no longer be easily customised
**CKAN version**

2.9

**Describe the bug**

`robots.txt` was moved back to the `public` directory as part of #4801. However, this reverts the implementation of https://github.com/ckan/ideas-and-roadmap/issues/178 and makes it harder to customise the file (it can still be overridden with a different version, but not using Jinja syntax).


--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `ckan/views/home.py`
Content:
```
1 # encoding: utf-8
2 
3 from __future__ import annotations
4 
5 from urllib.parse import urlencode
6 from typing import Any, Optional, cast, List, Tuple
7 
8 from flask import Blueprint, abort, redirect, request
9 
10 import ckan.model as model
11 import ckan.logic as logic
12 import ckan.lib.base as base
13 import ckan.lib.search as search
14 import ckan.lib.helpers as h
15 
16 from ckan.common import g, config, current_user, _
17 from ckan.types import Context
18 
19 
20 CACHE_PARAMETERS = [u'__cache', u'__no_cache__']
21 
22 
23 home = Blueprint(u'home', __name__)
24 
25 
26 @home.before_request
27 def before_request() -> None:
28     u'''set context and check authorization'''
29     try:
30         context = cast(Context, {
31             u'model': model,
32             u'user': current_user.name,
33             u'auth_user_obj': current_user})
34         logic.check_access(u'site_read', context)
35     except logic.NotAuthorized:
36         abort(403)
37 
38 
39 def index() -> str:
40     u'''display home page'''
41     try:
42         context = cast(Context, {
43             u'model': model,
44             u'session': model.Session,
45             u'user': current_user.name,
46             u'auth_user_obj': current_user
47             }
48         )
49 
50         data_dict: dict[str, Any] = {
51             u'q': u'*:*',
52             u'facet.field': h.facets(),
53             u'rows': 4,
54             u'start': 0,
55             u'sort': u'view_recent desc',
56             u'fq': u'capacity:"public"'}
57         query = logic.get_action(u'package_search')(context, data_dict)
58         g.package_count = query['count']
59         g.datasets = query['results']
60 
61         org_label = h.humanize_entity_type(
62             u'organization',
63             h.default_group_type(u'organization'),
64             u'facet label') or _(u'Organizations')
65 
66         group_label = h.humanize_entity_type(
67             u'group',
68             h.default_group_type(u'group'),
69             u'facet label') or _(u'Groups')
70 
71         g.facet_titles = {
72             u'organization': org_label,
73             u'groups': group_label,
74             u'tags': _(u'Tags'),
75             u'res_format': _(u'Formats'),
76             u'license': _(u'Licenses'),
77         }
78 
79     except search.SearchError:
80         g.package_count = 0
81 
82     if current_user.is_authenticated and not current_user.email:
83         url = h.url_for('user.edit')
84         msg = _(u'Please <a href="%s">update your profile</a>'
85                 u' and add your email address. ') % url + \
86             _(u'%s uses your email address'
87                 u' if you need to reset your password.') \
88             % config.get_value(u'ckan.site_title')
89         h.flash_notice(msg, allow_html=True)
90     return base.render(u'home/index.html', extra_vars={})
91 
92 
93 def about() -> str:
94     u''' display about page'''
95     return base.render(u'home/about.html', extra_vars={})
96 
97 
98 def redirect_locale(target_locale: str, path: Optional[str] = None) -> Any:
99 
100     target = f'/{target_locale}/{path}' if path else f'/{target_locale}'
101 
102     if request.args:
103         target += f'?{urlencode(request.args)}'
104 
105     return redirect(target, code=308)
106 
107 
108 util_rules: List[Tuple[str, Any]] = [
109     (u'/', index),
110     (u'/about', about)
111 ]
112 for rule, view_func in util_rules:
113     home.add_url_rule(rule, view_func=view_func)
114 
115 locales_mapping: List[Tuple[str, str]] = [
116     ('zh_TW', 'zh_Hant_TW'),
117     ('zh_CN', 'zh_Hans_CN'),
118     ('no', 'nb_NO'),
119 ]
120 
121 for locale in locales_mapping:
122 
123     legacy_locale = locale[0]
124     new_locale = locale[1]
125 
126     home.add_url_rule(
127         f'/{legacy_locale}/',
128         view_func=redirect_locale,
129         defaults={'target_locale': new_locale}
130     )
131 
132     home.add_url_rule(
133         f'/{legacy_locale}/<path:path>',
134         view_func=redirect_locale,
135         defaults={'target_locale': new_locale}
136     )
137
```
--- 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/ckan/views/home.py b/ckan/views/home.py
--- a/ckan/views/home.py
+++ b/ckan/views/home.py
@@ -5,7 +5,7 @@
 from urllib.parse import urlencode
 from typing import Any, Optional, cast, List, Tuple
 
-from flask import Blueprint, abort, redirect, request
+from flask import Blueprint, make_response, abort, redirect, request
 
 import ckan.model as model
 import ckan.logic as logic
@@ -14,7 +14,7 @@
 import ckan.lib.helpers as h
 
 from ckan.common import g, config, current_user, _
-from ckan.types import Context
+from ckan.types import Context, Response
 
 
 CACHE_PARAMETERS = [u'__cache', u'__no_cache__']
@@ -95,6 +95,13 @@
     return base.render(u'home/about.html', extra_vars={})
 
 
+def robots_txt() -> Response:
+    '''display robots.txt'''
+    resp = make_response(base.render('home/robots.txt'))
+    resp.headers['Content-Type'] = "text/plain; charset=utf-8"
+    return resp
+
+
 def redirect_locale(target_locale: str, path: Optional[str] = None) -> Any:
 
     target = f'/{target_locale}/{path}' if path else f'/{target_locale}'
@@ -107,7 +114,8 @@
 
 util_rules: List[Tuple[str, Any]] = [
     (u'/', index),
-    (u'/about', about)
+    (u'/about', about),
+    (u'/robots.txt', robots_txt)
 ]
 for rule, view_func in util_rules:
     home.add_url_rule(rule, view_func=view_func)

{"golden_diff": "diff --git a/ckan/views/home.py b/ckan/views/home.py\n--- a/ckan/views/home.py\n+++ b/ckan/views/home.py\n@@ -5,7 +5,7 @@\n from urllib.parse import urlencode\n from typing import Any, Optional, cast, List, Tuple\n \n-from flask import Blueprint, abort, redirect, request\n+from flask import Blueprint, make_response, abort, redirect, request\n \n import ckan.model as model\n import ckan.logic as logic\n@@ -14,7 +14,7 @@\n import ckan.lib.helpers as h\n \n from ckan.common import g, config, current_user, _\n-from ckan.types import Context\n+from ckan.types import Context, Response\n \n \n CACHE_PARAMETERS = [u'__cache', u'__no_cache__']\n@@ -95,6 +95,13 @@\n     return base.render(u'home/about.html', extra_vars={})\n \n \n+def robots_txt() -> Response:\n+    '''display robots.txt'''\n+    resp = make_response(base.render('home/robots.txt'))\n+    resp.headers['Content-Type'] = \"text/plain; charset=utf-8\"\n+    return resp\n+\n+\n def redirect_locale(target_locale: str, path: Optional[str] = None) -> Any:\n \n     target = f'/{target_locale}/{path}' if path else f'/{target_locale}'\n@@ -107,7 +114,8 @@\n \n util_rules: List[Tuple[str, Any]] = [\n     (u'/', index),\n-    (u'/about', about)\n+    (u'/about', about),\n+    (u'/robots.txt', robots_txt)\n ]\n for rule, view_func in util_rules:\n     home.add_url_rule(rule, view_func=view_func)\n", "issue": "Robots.txt can no longer be easily customised\n**CKAN version**\r\n\r\n2.9\r\n\r\n**Describe the bug**\r\n\r\n`robots.txt` was moved back to the `public` directory as part of #4801. However, this reverts the implementation of https://github.com/ckan/ideas-and-roadmap/issues/178 and makes it harder to customise the file (it can still be overridden with a different version, but not using Jinja syntax).\r\n\n", "before_files": [{"content": "# encoding: utf-8\n\nfrom __future__ import annotations\n\nfrom urllib.parse import urlencode\nfrom typing import Any, Optional, cast, List, Tuple\n\nfrom flask import Blueprint, abort, redirect, request\n\nimport ckan.model as model\nimport ckan.logic as logic\nimport ckan.lib.base as base\nimport ckan.lib.search as search\nimport ckan.lib.helpers as h\n\nfrom ckan.common import g, config, current_user, _\nfrom ckan.types import Context\n\n\nCACHE_PARAMETERS = [u'__cache', u'__no_cache__']\n\n\nhome = Blueprint(u'home', __name__)\n\n\n@home.before_request\ndef before_request() -> None:\n    u'''set context and check authorization'''\n    try:\n        context = cast(Context, {\n            u'model': model,\n            u'user': current_user.name,\n            u'auth_user_obj': current_user})\n        logic.check_access(u'site_read', context)\n    except logic.NotAuthorized:\n        abort(403)\n\n\ndef index() -> str:\n    u'''display home page'''\n    try:\n        context = cast(Context, {\n            u'model': model,\n            u'session': model.Session,\n            u'user': current_user.name,\n            u'auth_user_obj': current_user\n            }\n        )\n\n        data_dict: dict[str, Any] = {\n            u'q': u'*:*',\n            u'facet.field': h.facets(),\n            u'rows': 4,\n            u'start': 0,\n            u'sort': u'view_recent desc',\n            u'fq': u'capacity:\"public\"'}\n        query = logic.get_action(u'package_search')(context, data_dict)\n        g.package_count = query['count']\n        g.datasets = query['results']\n\n        org_label = h.humanize_entity_type(\n            u'organization',\n            h.default_group_type(u'organization'),\n            u'facet label') or _(u'Organizations')\n\n        group_label = h.humanize_entity_type(\n            u'group',\n            h.default_group_type(u'group'),\n            u'facet label') or _(u'Groups')\n\n        g.facet_titles = {\n            u'organization': org_label,\n            u'groups': group_label,\n            u'tags': _(u'Tags'),\n            u'res_format': _(u'Formats'),\n            u'license': _(u'Licenses'),\n        }\n\n    except search.SearchError:\n        g.package_count = 0\n\n    if current_user.is_authenticated and not current_user.email:\n        url = h.url_for('user.edit')\n        msg = _(u'Please <a href=\"%s\">update your profile</a>'\n                u' and add your email address. ') % url + \\\n            _(u'%s uses your email address'\n                u' if you need to reset your password.') \\\n            % config.get_value(u'ckan.site_title')\n        h.flash_notice(msg, allow_html=True)\n    return base.render(u'home/index.html', extra_vars={})\n\n\ndef about() -> str:\n    u''' display about page'''\n    return base.render(u'home/about.html', extra_vars={})\n\n\ndef redirect_locale(target_locale: str, path: Optional[str] = None) -> Any:\n\n    target = f'/{target_locale}/{path}' if path else f'/{target_locale}'\n\n    if request.args:\n        target += f'?{urlencode(request.args)}'\n\n    return redirect(target, code=308)\n\n\nutil_rules: List[Tuple[str, Any]] = [\n    (u'/', index),\n    (u'/about', about)\n]\nfor rule, view_func in util_rules:\n    home.add_url_rule(rule, view_func=view_func)\n\nlocales_mapping: List[Tuple[str, str]] = [\n    ('zh_TW', 'zh_Hant_TW'),\n    ('zh_CN', 'zh_Hans_CN'),\n    ('no', 'nb_NO'),\n]\n\nfor locale in locales_mapping:\n\n    legacy_locale = locale[0]\n    new_locale = locale[1]\n\n    home.add_url_rule(\n        f'/{legacy_locale}/',\n        view_func=redirect_locale,\n        defaults={'target_locale': new_locale}\n    )\n\n    home.add_url_rule(\n        f'/{legacy_locale}/<path:path>',\n        view_func=redirect_locale,\n        defaults={'target_locale': new_locale}\n    )\n", "path": "ckan/views/home.py"}], "after_files": [{"content": "# encoding: utf-8\n\nfrom __future__ import annotations\n\nfrom urllib.parse import urlencode\nfrom typing import Any, Optional, cast, List, Tuple\n\nfrom flask import Blueprint, make_response, abort, redirect, request\n\nimport ckan.model as model\nimport ckan.logic as logic\nimport ckan.lib.base as base\nimport ckan.lib.search as search\nimport ckan.lib.helpers as h\n\nfrom ckan.common import g, config, current_user, _\nfrom ckan.types import Context, Response\n\n\nCACHE_PARAMETERS = [u'__cache', u'__no_cache__']\n\n\nhome = Blueprint(u'home', __name__)\n\n\n@home.before_request\ndef before_request() -> None:\n    u'''set context and check authorization'''\n    try:\n        context = cast(Context, {\n            u'model': model,\n            u'user': current_user.name,\n            u'auth_user_obj': current_user})\n        logic.check_access(u'site_read', context)\n    except logic.NotAuthorized:\n        abort(403)\n\n\ndef index() -> str:\n    u'''display home page'''\n    try:\n        context = cast(Context, {\n            u'model': model,\n            u'session': model.Session,\n            u'user': current_user.name,\n            u'auth_user_obj': current_user\n            }\n        )\n\n        data_dict: dict[str, Any] = {\n            u'q': u'*:*',\n            u'facet.field': h.facets(),\n            u'rows': 4,\n            u'start': 0,\n            u'sort': u'view_recent desc',\n            u'fq': u'capacity:\"public\"'}\n        query = logic.get_action(u'package_search')(context, data_dict)\n        g.package_count = query['count']\n        g.datasets = query['results']\n\n        org_label = h.humanize_entity_type(\n            u'organization',\n            h.default_group_type(u'organization'),\n            u'facet label') or _(u'Organizations')\n\n        group_label = h.humanize_entity_type(\n            u'group',\n            h.default_group_type(u'group'),\n            u'facet label') or _(u'Groups')\n\n        g.facet_titles = {\n            u'organization': org_label,\n            u'groups': group_label,\n            u'tags': _(u'Tags'),\n            u'res_format': _(u'Formats'),\n            u'license': _(u'Licenses'),\n        }\n\n    except search.SearchError:\n        g.package_count = 0\n\n    if current_user.is_authenticated and not current_user.email:\n        url = h.url_for('user.edit')\n        msg = _(u'Please <a href=\"%s\">update your profile</a>'\n                u' and add your email address. ') % url + \\\n            _(u'%s uses your email address'\n                u' if you need to reset your password.') \\\n            % config.get_value(u'ckan.site_title')\n        h.flash_notice(msg, allow_html=True)\n    return base.render(u'home/index.html', extra_vars={})\n\n\ndef about() -> str:\n    u''' display about page'''\n    return base.render(u'home/about.html', extra_vars={})\n\n\ndef robots_txt() -> Response:\n    '''display robots.txt'''\n    resp = make_response(base.render('home/robots.txt'))\n    resp.headers['Content-Type'] = \"text/plain; charset=utf-8\"\n    return resp\n\n\ndef redirect_locale(target_locale: str, path: Optional[str] = None) -> Any:\n\n    target = f'/{target_locale}/{path}' if path else f'/{target_locale}'\n\n    if request.args:\n        target += f'?{urlencode(request.args)}'\n\n    return redirect(target, code=308)\n\n\nutil_rules: List[Tuple[str, Any]] = [\n    (u'/', index),\n    (u'/about', about),\n    (u'/robots.txt', robots_txt)\n]\nfor rule, view_func in util_rules:\n    home.add_url_rule(rule, view_func=view_func)\n\nlocales_mapping: List[Tuple[str, str]] = [\n    ('zh_TW', 'zh_Hant_TW'),\n    ('zh_CN', 'zh_Hans_CN'),\n    ('no', 'nb_NO'),\n]\n\nfor locale in locales_mapping:\n\n    legacy_locale = locale[0]\n    new_locale = locale[1]\n\n    home.add_url_rule(\n        f'/{legacy_locale}/',\n        view_func=redirect_locale,\n        defaults={'target_locale': new_locale}\n    )\n\n    home.add_url_rule(\n        f'/{legacy_locale}/<path:path>',\n        view_func=redirect_locale,\n        defaults={'target_locale': new_locale}\n    )\n", "path": "ckan/views/home.py"}]}