We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
HTML_SANITIZATION controllable from config panel
We should maybe make HTML_SANITIZATION controlable from the admin panel so that Admins can decide how they want to deal with HTML. 

Of course this maybe could be done another way, it's just the general idea about the configuration value. 

--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `CTFd/admin/__init__.py`
Content:
```
1 import csv  # noqa: I001
2 import datetime
3 import os
4 from io import StringIO
5 
6 from flask import Blueprint, abort
7 from flask import current_app as app
8 from flask import (
9     jsonify,
10     redirect,
11     render_template,
12     render_template_string,
13     request,
14     send_file,
15     url_for,
16 )
17 
18 admin = Blueprint("admin", __name__)
19 
20 # isort:imports-firstparty
21 from CTFd.admin import challenges  # noqa: F401,I001
22 from CTFd.admin import notifications  # noqa: F401,I001
23 from CTFd.admin import pages  # noqa: F401,I001
24 from CTFd.admin import scoreboard  # noqa: F401,I001
25 from CTFd.admin import statistics  # noqa: F401,I001
26 from CTFd.admin import submissions  # noqa: F401,I001
27 from CTFd.admin import teams  # noqa: F401,I001
28 from CTFd.admin import users  # noqa: F401,I001
29 from CTFd.cache import (
30     cache,
31     clear_challenges,
32     clear_config,
33     clear_pages,
34     clear_standings,
35 )
36 from CTFd.models import (
37     Awards,
38     Challenges,
39     Configs,
40     Notifications,
41     Pages,
42     Solves,
43     Submissions,
44     Teams,
45     Tracking,
46     Unlocks,
47     Users,
48     db,
49 )
50 from CTFd.utils import config as ctf_config
51 from CTFd.utils import get_config, set_config
52 from CTFd.utils.csv import dump_csv, load_challenges_csv, load_teams_csv, load_users_csv
53 from CTFd.utils.decorators import admins_only
54 from CTFd.utils.exports import background_import_ctf
55 from CTFd.utils.exports import export_ctf as export_ctf_util
56 from CTFd.utils.security.auth import logout_user
57 from CTFd.utils.uploads import delete_file
58 from CTFd.utils.user import is_admin
59 
60 
61 @admin.route("/admin", methods=["GET"])
62 def view():
63     if is_admin():
64         return redirect(url_for("admin.statistics"))
65     return redirect(url_for("auth.login"))
66 
67 
68 @admin.route("/admin/plugins/<plugin>", methods=["GET", "POST"])
69 @admins_only
70 def plugin(plugin):
71     if request.method == "GET":
72         plugins_path = os.path.join(app.root_path, "plugins")
73 
74         config_html_plugins = [
75             name
76             for name in os.listdir(plugins_path)
77             if os.path.isfile(os.path.join(plugins_path, name, "config.html"))
78         ]
79 
80         if plugin in config_html_plugins:
81             config_html = open(
82                 os.path.join(app.root_path, "plugins", plugin, "config.html")
83             ).read()
84             return render_template_string(config_html)
85         abort(404)
86     elif request.method == "POST":
87         for k, v in request.form.items():
88             if k == "nonce":
89                 continue
90             set_config(k, v)
91         with app.app_context():
92             clear_config()
93         return "1"
94 
95 
96 @admin.route("/admin/import", methods=["GET", "POST"])
97 @admins_only
98 def import_ctf():
99     if request.method == "GET":
100         start_time = cache.get("import_start_time")
101         end_time = cache.get("import_end_time")
102         import_status = cache.get("import_status")
103         import_error = cache.get("import_error")
104         return render_template(
105             "admin/import.html",
106             start_time=start_time,
107             end_time=end_time,
108             import_status=import_status,
109             import_error=import_error,
110         )
111     elif request.method == "POST":
112         backup = request.files["backup"]
113         background_import_ctf(backup)
114         return redirect(url_for("admin.import_ctf"))
115 
116 
117 @admin.route("/admin/export", methods=["GET", "POST"])
118 @admins_only
119 def export_ctf():
120     backup = export_ctf_util()
121     ctf_name = ctf_config.ctf_name()
122     day = datetime.datetime.now().strftime("%Y-%m-%d_%T")
123     full_name = u"{}.{}.zip".format(ctf_name, day)
124     return send_file(
125         backup, cache_timeout=-1, as_attachment=True, attachment_filename=full_name
126     )
127 
128 
129 @admin.route("/admin/import/csv", methods=["POST"])
130 @admins_only
131 def import_csv():
132     csv_type = request.form["csv_type"]
133     # Try really hard to load data in properly no matter what nonsense Excel gave you
134     raw = request.files["csv_file"].stream.read()
135     try:
136         csvdata = raw.decode("utf-8-sig")
137     except UnicodeDecodeError:
138         try:
139             csvdata = raw.decode("cp1252")
140         except UnicodeDecodeError:
141             csvdata = raw.decode("latin-1")
142     csvfile = StringIO(csvdata)
143 
144     loaders = {
145         "challenges": load_challenges_csv,
146         "users": load_users_csv,
147         "teams": load_teams_csv,
148     }
149 
150     loader = loaders[csv_type]
151     reader = csv.DictReader(csvfile)
152     success = loader(reader)
153     if success is True:
154         return redirect(url_for("admin.config"))
155     else:
156         return jsonify(success), 500
157 
158 
159 @admin.route("/admin/export/csv")
160 @admins_only
161 def export_csv():
162     table = request.args.get("table")
163 
164     output = dump_csv(name=table)
165 
166     return send_file(
167         output,
168         as_attachment=True,
169         cache_timeout=-1,
170         attachment_filename="{name}-{table}.csv".format(
171             name=ctf_config.ctf_name(), table=table
172         ),
173     )
174 
175 
176 @admin.route("/admin/config", methods=["GET", "POST"])
177 @admins_only
178 def config():
179     # Clear the config cache so that we don't get stale values
180     clear_config()
181 
182     configs = Configs.query.all()
183     configs = {c.key: get_config(c.key) for c in configs}
184 
185     themes = ctf_config.get_themes()
186 
187     # Remove current theme but ignore failure
188     try:
189         themes.remove(get_config("ctf_theme"))
190     except ValueError:
191         pass
192 
193     return render_template("admin/config.html", themes=themes, **configs)
194 
195 
196 @admin.route("/admin/reset", methods=["GET", "POST"])
197 @admins_only
198 def reset():
199     if request.method == "POST":
200         require_setup = False
201         logout = False
202         next_url = url_for("admin.statistics")
203 
204         data = request.form
205 
206         if data.get("pages"):
207             _pages = Pages.query.all()
208             for p in _pages:
209                 for f in p.files:
210                     delete_file(file_id=f.id)
211 
212             Pages.query.delete()
213 
214         if data.get("notifications"):
215             Notifications.query.delete()
216 
217         if data.get("challenges"):
218             _challenges = Challenges.query.all()
219             for c in _challenges:
220                 for f in c.files:
221                     delete_file(file_id=f.id)
222             Challenges.query.delete()
223 
224         if data.get("accounts"):
225             Users.query.delete()
226             Teams.query.delete()
227             require_setup = True
228             logout = True
229 
230         if data.get("submissions"):
231             Solves.query.delete()
232             Submissions.query.delete()
233             Awards.query.delete()
234             Unlocks.query.delete()
235             Tracking.query.delete()
236 
237         if require_setup:
238             set_config("setup", False)
239             cache.clear()
240             logout_user()
241             next_url = url_for("views.setup")
242 
243         db.session.commit()
244 
245         clear_pages()
246         clear_standings()
247         clear_challenges()
248         clear_config()
249 
250         if logout is True:
251             cache.clear()
252             logout_user()
253 
254         db.session.close()
255         return redirect(next_url)
256 
257     return render_template("admin/reset.html")
258
```

Path: `CTFd/utils/config/pages.py`
Content:
```
1 from flask import current_app
2 
3 from CTFd.cache import cache
4 from CTFd.models import Pages, db
5 from CTFd.utils import get_config, markdown
6 from CTFd.utils.dates import isoformat, unix_time_to_utc
7 from CTFd.utils.formatters import safe_format
8 from CTFd.utils.security.sanitize import sanitize_html
9 
10 
11 def format_variables(content):
12     ctf_name = get_config("ctf_name")
13     ctf_description = get_config("ctf_description")
14     ctf_start = get_config("start")
15     if ctf_start:
16         ctf_start = isoformat(unix_time_to_utc(int(ctf_start)))
17 
18     ctf_end = get_config("end")
19     if ctf_end:
20         ctf_end = isoformat(unix_time_to_utc(int(ctf_end)))
21 
22     ctf_freeze = get_config("freeze")
23     if ctf_freeze:
24         ctf_freeze = isoformat(unix_time_to_utc(int(ctf_freeze)))
25 
26     content = safe_format(
27         content,
28         ctf_name=ctf_name,
29         ctf_description=ctf_description,
30         ctf_start=ctf_start,
31         ctf_end=ctf_end,
32         ctf_freeze=ctf_freeze,
33     )
34     return content
35 
36 
37 def build_html(html, sanitize=False):
38     html = format_variables(html)
39     if current_app.config["HTML_SANITIZATION"] is True or sanitize is True:
40         html = sanitize_html(html)
41     return html
42 
43 
44 def build_markdown(md, sanitize=False):
45     html = markdown(md)
46     html = format_variables(html)
47     if current_app.config["HTML_SANITIZATION"] is True or sanitize is True:
48         html = sanitize_html(html)
49     return html
50 
51 
52 @cache.memoize()
53 def get_pages():
54     db_pages = Pages.query.filter(
55         Pages.route != "index", Pages.draft.isnot(True), Pages.hidden.isnot(True)
56     ).all()
57     return db_pages
58 
59 
60 @cache.memoize()
61 def get_page(route):
62     page = db.session.execute(
63         Pages.__table__.select()
64         .where(Pages.route == route)
65         .where(Pages.draft.isnot(True))
66     ).fetchone()
67     if page:
68         # Convert the row into a transient ORM object so this change isn't commited accidentally
69         p = Pages(**page)
70         return p
71     return None
72
```
--- 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/CTFd/admin/__init__.py b/CTFd/admin/__init__.py
--- a/CTFd/admin/__init__.py
+++ b/CTFd/admin/__init__.py
@@ -48,7 +48,7 @@
     db,
 )
 from CTFd.utils import config as ctf_config
-from CTFd.utils import get_config, set_config
+from CTFd.utils import get_app_config, get_config, set_config
 from CTFd.utils.csv import dump_csv, load_challenges_csv, load_teams_csv, load_users_csv
 from CTFd.utils.decorators import admins_only
 from CTFd.utils.exports import background_import_ctf
@@ -190,7 +190,14 @@
     except ValueError:
         pass
 
-    return render_template("admin/config.html", themes=themes, **configs)
+    force_html_sanitization = get_app_config("HTML_SANITIZATION")
+
+    return render_template(
+        "admin/config.html",
+        themes=themes,
+        **configs,
+        force_html_sanitization=force_html_sanitization
+    )
 
 
 @admin.route("/admin/reset", methods=["GET", "POST"])
diff --git a/CTFd/utils/config/pages.py b/CTFd/utils/config/pages.py
--- a/CTFd/utils/config/pages.py
+++ b/CTFd/utils/config/pages.py
@@ -36,7 +36,11 @@
 
 def build_html(html, sanitize=False):
     html = format_variables(html)
-    if current_app.config["HTML_SANITIZATION"] is True or sanitize is True:
+    if (
+        current_app.config["HTML_SANITIZATION"] is True
+        or bool(get_config("html_sanitization")) is True
+        or sanitize is True
+    ):
         html = sanitize_html(html)
     return html
 
@@ -44,7 +48,11 @@
 def build_markdown(md, sanitize=False):
     html = markdown(md)
     html = format_variables(html)
-    if current_app.config["HTML_SANITIZATION"] is True or sanitize is True:
+    if (
+        current_app.config["HTML_SANITIZATION"] is True
+        or bool(get_config("html_sanitization")) is True
+        or sanitize is True
+    ):
         html = sanitize_html(html)
     return html

{"golden_diff": "diff --git a/CTFd/admin/__init__.py b/CTFd/admin/__init__.py\n--- a/CTFd/admin/__init__.py\n+++ b/CTFd/admin/__init__.py\n@@ -48,7 +48,7 @@\n     db,\n )\n from CTFd.utils import config as ctf_config\n-from CTFd.utils import get_config, set_config\n+from CTFd.utils import get_app_config, get_config, set_config\n from CTFd.utils.csv import dump_csv, load_challenges_csv, load_teams_csv, load_users_csv\n from CTFd.utils.decorators import admins_only\n from CTFd.utils.exports import background_import_ctf\n@@ -190,7 +190,14 @@\n     except ValueError:\n         pass\n \n-    return render_template(\"admin/config.html\", themes=themes, **configs)\n+    force_html_sanitization = get_app_config(\"HTML_SANITIZATION\")\n+\n+    return render_template(\n+        \"admin/config.html\",\n+        themes=themes,\n+        **configs,\n+        force_html_sanitization=force_html_sanitization\n+    )\n \n \n @admin.route(\"/admin/reset\", methods=[\"GET\", \"POST\"])\ndiff --git a/CTFd/utils/config/pages.py b/CTFd/utils/config/pages.py\n--- a/CTFd/utils/config/pages.py\n+++ b/CTFd/utils/config/pages.py\n@@ -36,7 +36,11 @@\n \n def build_html(html, sanitize=False):\n     html = format_variables(html)\n-    if current_app.config[\"HTML_SANITIZATION\"] is True or sanitize is True:\n+    if (\n+        current_app.config[\"HTML_SANITIZATION\"] is True\n+        or bool(get_config(\"html_sanitization\")) is True\n+        or sanitize is True\n+    ):\n         html = sanitize_html(html)\n     return html\n \n@@ -44,7 +48,11 @@\n def build_markdown(md, sanitize=False):\n     html = markdown(md)\n     html = format_variables(html)\n-    if current_app.config[\"HTML_SANITIZATION\"] is True or sanitize is True:\n+    if (\n+        current_app.config[\"HTML_SANITIZATION\"] is True\n+        or bool(get_config(\"html_sanitization\")) is True\n+        or sanitize is True\n+    ):\n         html = sanitize_html(html)\n     return html\n", "issue": "HTML_SANITIZATION controllable from config panel\nWe should maybe make HTML_SANITIZATION controlable from the admin panel so that Admins can decide how they want to deal with HTML. \r\n\r\nOf course this maybe could be done another way, it's just the general idea about the configuration value. \n", "before_files": [{"content": "import csv  # noqa: I001\nimport datetime\nimport os\nfrom io import StringIO\n\nfrom flask import Blueprint, abort\nfrom flask import current_app as app\nfrom flask import (\n    jsonify,\n    redirect,\n    render_template,\n    render_template_string,\n    request,\n    send_file,\n    url_for,\n)\n\nadmin = Blueprint(\"admin\", __name__)\n\n# isort:imports-firstparty\nfrom CTFd.admin import challenges  # noqa: F401,I001\nfrom CTFd.admin import notifications  # noqa: F401,I001\nfrom CTFd.admin import pages  # noqa: F401,I001\nfrom CTFd.admin import scoreboard  # noqa: F401,I001\nfrom CTFd.admin import statistics  # noqa: F401,I001\nfrom CTFd.admin import submissions  # noqa: F401,I001\nfrom CTFd.admin import teams  # noqa: F401,I001\nfrom CTFd.admin import users  # noqa: F401,I001\nfrom CTFd.cache import (\n    cache,\n    clear_challenges,\n    clear_config,\n    clear_pages,\n    clear_standings,\n)\nfrom CTFd.models import (\n    Awards,\n    Challenges,\n    Configs,\n    Notifications,\n    Pages,\n    Solves,\n    Submissions,\n    Teams,\n    Tracking,\n    Unlocks,\n    Users,\n    db,\n)\nfrom CTFd.utils import config as ctf_config\nfrom CTFd.utils import get_config, set_config\nfrom CTFd.utils.csv import dump_csv, load_challenges_csv, load_teams_csv, load_users_csv\nfrom CTFd.utils.decorators import admins_only\nfrom CTFd.utils.exports import background_import_ctf\nfrom CTFd.utils.exports import export_ctf as export_ctf_util\nfrom CTFd.utils.security.auth import logout_user\nfrom CTFd.utils.uploads import delete_file\nfrom CTFd.utils.user import is_admin\n\n\n@admin.route(\"/admin\", methods=[\"GET\"])\ndef view():\n    if is_admin():\n        return redirect(url_for(\"admin.statistics\"))\n    return redirect(url_for(\"auth.login\"))\n\n\n@admin.route(\"/admin/plugins/<plugin>\", methods=[\"GET\", \"POST\"])\n@admins_only\ndef plugin(plugin):\n    if request.method == \"GET\":\n        plugins_path = os.path.join(app.root_path, \"plugins\")\n\n        config_html_plugins = [\n            name\n            for name in os.listdir(plugins_path)\n            if os.path.isfile(os.path.join(plugins_path, name, \"config.html\"))\n        ]\n\n        if plugin in config_html_plugins:\n            config_html = open(\n                os.path.join(app.root_path, \"plugins\", plugin, \"config.html\")\n            ).read()\n            return render_template_string(config_html)\n        abort(404)\n    elif request.method == \"POST\":\n        for k, v in request.form.items():\n            if k == \"nonce\":\n                continue\n            set_config(k, v)\n        with app.app_context():\n            clear_config()\n        return \"1\"\n\n\n@admin.route(\"/admin/import\", methods=[\"GET\", \"POST\"])\n@admins_only\ndef import_ctf():\n    if request.method == \"GET\":\n        start_time = cache.get(\"import_start_time\")\n        end_time = cache.get(\"import_end_time\")\n        import_status = cache.get(\"import_status\")\n        import_error = cache.get(\"import_error\")\n        return render_template(\n            \"admin/import.html\",\n            start_time=start_time,\n            end_time=end_time,\n            import_status=import_status,\n            import_error=import_error,\n        )\n    elif request.method == \"POST\":\n        backup = request.files[\"backup\"]\n        background_import_ctf(backup)\n        return redirect(url_for(\"admin.import_ctf\"))\n\n\n@admin.route(\"/admin/export\", methods=[\"GET\", \"POST\"])\n@admins_only\ndef export_ctf():\n    backup = export_ctf_util()\n    ctf_name = ctf_config.ctf_name()\n    day = datetime.datetime.now().strftime(\"%Y-%m-%d_%T\")\n    full_name = u\"{}.{}.zip\".format(ctf_name, day)\n    return send_file(\n        backup, cache_timeout=-1, as_attachment=True, attachment_filename=full_name\n    )\n\n\n@admin.route(\"/admin/import/csv\", methods=[\"POST\"])\n@admins_only\ndef import_csv():\n    csv_type = request.form[\"csv_type\"]\n    # Try really hard to load data in properly no matter what nonsense Excel gave you\n    raw = request.files[\"csv_file\"].stream.read()\n    try:\n        csvdata = raw.decode(\"utf-8-sig\")\n    except UnicodeDecodeError:\n        try:\n            csvdata = raw.decode(\"cp1252\")\n        except UnicodeDecodeError:\n            csvdata = raw.decode(\"latin-1\")\n    csvfile = StringIO(csvdata)\n\n    loaders = {\n        \"challenges\": load_challenges_csv,\n        \"users\": load_users_csv,\n        \"teams\": load_teams_csv,\n    }\n\n    loader = loaders[csv_type]\n    reader = csv.DictReader(csvfile)\n    success = loader(reader)\n    if success is True:\n        return redirect(url_for(\"admin.config\"))\n    else:\n        return jsonify(success), 500\n\n\n@admin.route(\"/admin/export/csv\")\n@admins_only\ndef export_csv():\n    table = request.args.get(\"table\")\n\n    output = dump_csv(name=table)\n\n    return send_file(\n        output,\n        as_attachment=True,\n        cache_timeout=-1,\n        attachment_filename=\"{name}-{table}.csv\".format(\n            name=ctf_config.ctf_name(), table=table\n        ),\n    )\n\n\n@admin.route(\"/admin/config\", methods=[\"GET\", \"POST\"])\n@admins_only\ndef config():\n    # Clear the config cache so that we don't get stale values\n    clear_config()\n\n    configs = Configs.query.all()\n    configs = {c.key: get_config(c.key) for c in configs}\n\n    themes = ctf_config.get_themes()\n\n    # Remove current theme but ignore failure\n    try:\n        themes.remove(get_config(\"ctf_theme\"))\n    except ValueError:\n        pass\n\n    return render_template(\"admin/config.html\", themes=themes, **configs)\n\n\n@admin.route(\"/admin/reset\", methods=[\"GET\", \"POST\"])\n@admins_only\ndef reset():\n    if request.method == \"POST\":\n        require_setup = False\n        logout = False\n        next_url = url_for(\"admin.statistics\")\n\n        data = request.form\n\n        if data.get(\"pages\"):\n            _pages = Pages.query.all()\n            for p in _pages:\n                for f in p.files:\n                    delete_file(file_id=f.id)\n\n            Pages.query.delete()\n\n        if data.get(\"notifications\"):\n            Notifications.query.delete()\n\n        if data.get(\"challenges\"):\n            _challenges = Challenges.query.all()\n            for c in _challenges:\n                for f in c.files:\n                    delete_file(file_id=f.id)\n            Challenges.query.delete()\n\n        if data.get(\"accounts\"):\n            Users.query.delete()\n            Teams.query.delete()\n            require_setup = True\n            logout = True\n\n        if data.get(\"submissions\"):\n            Solves.query.delete()\n            Submissions.query.delete()\n            Awards.query.delete()\n            Unlocks.query.delete()\n            Tracking.query.delete()\n\n        if require_setup:\n            set_config(\"setup\", False)\n            cache.clear()\n            logout_user()\n            next_url = url_for(\"views.setup\")\n\n        db.session.commit()\n\n        clear_pages()\n        clear_standings()\n        clear_challenges()\n        clear_config()\n\n        if logout is True:\n            cache.clear()\n            logout_user()\n\n        db.session.close()\n        return redirect(next_url)\n\n    return render_template(\"admin/reset.html\")\n", "path": "CTFd/admin/__init__.py"}, {"content": "from flask import current_app\n\nfrom CTFd.cache import cache\nfrom CTFd.models import Pages, db\nfrom CTFd.utils import get_config, markdown\nfrom CTFd.utils.dates import isoformat, unix_time_to_utc\nfrom CTFd.utils.formatters import safe_format\nfrom CTFd.utils.security.sanitize import sanitize_html\n\n\ndef format_variables(content):\n    ctf_name = get_config(\"ctf_name\")\n    ctf_description = get_config(\"ctf_description\")\n    ctf_start = get_config(\"start\")\n    if ctf_start:\n        ctf_start = isoformat(unix_time_to_utc(int(ctf_start)))\n\n    ctf_end = get_config(\"end\")\n    if ctf_end:\n        ctf_end = isoformat(unix_time_to_utc(int(ctf_end)))\n\n    ctf_freeze = get_config(\"freeze\")\n    if ctf_freeze:\n        ctf_freeze = isoformat(unix_time_to_utc(int(ctf_freeze)))\n\n    content = safe_format(\n        content,\n        ctf_name=ctf_name,\n        ctf_description=ctf_description,\n        ctf_start=ctf_start,\n        ctf_end=ctf_end,\n        ctf_freeze=ctf_freeze,\n    )\n    return content\n\n\ndef build_html(html, sanitize=False):\n    html = format_variables(html)\n    if current_app.config[\"HTML_SANITIZATION\"] is True or sanitize is True:\n        html = sanitize_html(html)\n    return html\n\n\ndef build_markdown(md, sanitize=False):\n    html = markdown(md)\n    html = format_variables(html)\n    if current_app.config[\"HTML_SANITIZATION\"] is True or sanitize is True:\n        html = sanitize_html(html)\n    return html\n\n\n@cache.memoize()\ndef get_pages():\n    db_pages = Pages.query.filter(\n        Pages.route != \"index\", Pages.draft.isnot(True), Pages.hidden.isnot(True)\n    ).all()\n    return db_pages\n\n\n@cache.memoize()\ndef get_page(route):\n    page = db.session.execute(\n        Pages.__table__.select()\n        .where(Pages.route == route)\n        .where(Pages.draft.isnot(True))\n    ).fetchone()\n    if page:\n        # Convert the row into a transient ORM object so this change isn't commited accidentally\n        p = Pages(**page)\n        return p\n    return None\n", "path": "CTFd/utils/config/pages.py"}], "after_files": [{"content": "import csv  # noqa: I001\nimport datetime\nimport os\nfrom io import StringIO\n\nfrom flask import Blueprint, abort\nfrom flask import current_app as app\nfrom flask import (\n    jsonify,\n    redirect,\n    render_template,\n    render_template_string,\n    request,\n    send_file,\n    url_for,\n)\n\nadmin = Blueprint(\"admin\", __name__)\n\n# isort:imports-firstparty\nfrom CTFd.admin import challenges  # noqa: F401,I001\nfrom CTFd.admin import notifications  # noqa: F401,I001\nfrom CTFd.admin import pages  # noqa: F401,I001\nfrom CTFd.admin import scoreboard  # noqa: F401,I001\nfrom CTFd.admin import statistics  # noqa: F401,I001\nfrom CTFd.admin import submissions  # noqa: F401,I001\nfrom CTFd.admin import teams  # noqa: F401,I001\nfrom CTFd.admin import users  # noqa: F401,I001\nfrom CTFd.cache import (\n    cache,\n    clear_challenges,\n    clear_config,\n    clear_pages,\n    clear_standings,\n)\nfrom CTFd.models import (\n    Awards,\n    Challenges,\n    Configs,\n    Notifications,\n    Pages,\n    Solves,\n    Submissions,\n    Teams,\n    Tracking,\n    Unlocks,\n    Users,\n    db,\n)\nfrom CTFd.utils import config as ctf_config\nfrom CTFd.utils import get_app_config, get_config, set_config\nfrom CTFd.utils.csv import dump_csv, load_challenges_csv, load_teams_csv, load_users_csv\nfrom CTFd.utils.decorators import admins_only\nfrom CTFd.utils.exports import background_import_ctf\nfrom CTFd.utils.exports import export_ctf as export_ctf_util\nfrom CTFd.utils.security.auth import logout_user\nfrom CTFd.utils.uploads import delete_file\nfrom CTFd.utils.user import is_admin\n\n\n@admin.route(\"/admin\", methods=[\"GET\"])\ndef view():\n    if is_admin():\n        return redirect(url_for(\"admin.statistics\"))\n    return redirect(url_for(\"auth.login\"))\n\n\n@admin.route(\"/admin/plugins/<plugin>\", methods=[\"GET\", \"POST\"])\n@admins_only\ndef plugin(plugin):\n    if request.method == \"GET\":\n        plugins_path = os.path.join(app.root_path, \"plugins\")\n\n        config_html_plugins = [\n            name\n            for name in os.listdir(plugins_path)\n            if os.path.isfile(os.path.join(plugins_path, name, \"config.html\"))\n        ]\n\n        if plugin in config_html_plugins:\n            config_html = open(\n                os.path.join(app.root_path, \"plugins\", plugin, \"config.html\")\n            ).read()\n            return render_template_string(config_html)\n        abort(404)\n    elif request.method == \"POST\":\n        for k, v in request.form.items():\n            if k == \"nonce\":\n                continue\n            set_config(k, v)\n        with app.app_context():\n            clear_config()\n        return \"1\"\n\n\n@admin.route(\"/admin/import\", methods=[\"GET\", \"POST\"])\n@admins_only\ndef import_ctf():\n    if request.method == \"GET\":\n        start_time = cache.get(\"import_start_time\")\n        end_time = cache.get(\"import_end_time\")\n        import_status = cache.get(\"import_status\")\n        import_error = cache.get(\"import_error\")\n        return render_template(\n            \"admin/import.html\",\n            start_time=start_time,\n            end_time=end_time,\n            import_status=import_status,\n            import_error=import_error,\n        )\n    elif request.method == \"POST\":\n        backup = request.files[\"backup\"]\n        background_import_ctf(backup)\n        return redirect(url_for(\"admin.import_ctf\"))\n\n\n@admin.route(\"/admin/export\", methods=[\"GET\", \"POST\"])\n@admins_only\ndef export_ctf():\n    backup = export_ctf_util()\n    ctf_name = ctf_config.ctf_name()\n    day = datetime.datetime.now().strftime(\"%Y-%m-%d_%T\")\n    full_name = u\"{}.{}.zip\".format(ctf_name, day)\n    return send_file(\n        backup, cache_timeout=-1, as_attachment=True, attachment_filename=full_name\n    )\n\n\n@admin.route(\"/admin/import/csv\", methods=[\"POST\"])\n@admins_only\ndef import_csv():\n    csv_type = request.form[\"csv_type\"]\n    # Try really hard to load data in properly no matter what nonsense Excel gave you\n    raw = request.files[\"csv_file\"].stream.read()\n    try:\n        csvdata = raw.decode(\"utf-8-sig\")\n    except UnicodeDecodeError:\n        try:\n            csvdata = raw.decode(\"cp1252\")\n        except UnicodeDecodeError:\n            csvdata = raw.decode(\"latin-1\")\n    csvfile = StringIO(csvdata)\n\n    loaders = {\n        \"challenges\": load_challenges_csv,\n        \"users\": load_users_csv,\n        \"teams\": load_teams_csv,\n    }\n\n    loader = loaders[csv_type]\n    reader = csv.DictReader(csvfile)\n    success = loader(reader)\n    if success is True:\n        return redirect(url_for(\"admin.config\"))\n    else:\n        return jsonify(success), 500\n\n\n@admin.route(\"/admin/export/csv\")\n@admins_only\ndef export_csv():\n    table = request.args.get(\"table\")\n\n    output = dump_csv(name=table)\n\n    return send_file(\n        output,\n        as_attachment=True,\n        cache_timeout=-1,\n        attachment_filename=\"{name}-{table}.csv\".format(\n            name=ctf_config.ctf_name(), table=table\n        ),\n    )\n\n\n@admin.route(\"/admin/config\", methods=[\"GET\", \"POST\"])\n@admins_only\ndef config():\n    # Clear the config cache so that we don't get stale values\n    clear_config()\n\n    configs = Configs.query.all()\n    configs = {c.key: get_config(c.key) for c in configs}\n\n    themes = ctf_config.get_themes()\n\n    # Remove current theme but ignore failure\n    try:\n        themes.remove(get_config(\"ctf_theme\"))\n    except ValueError:\n        pass\n\n    force_html_sanitization = get_app_config(\"HTML_SANITIZATION\")\n\n    return render_template(\n        \"admin/config.html\",\n        themes=themes,\n        **configs,\n        force_html_sanitization=force_html_sanitization\n    )\n\n\n@admin.route(\"/admin/reset\", methods=[\"GET\", \"POST\"])\n@admins_only\ndef reset():\n    if request.method == \"POST\":\n        require_setup = False\n        logout = False\n        next_url = url_for(\"admin.statistics\")\n\n        data = request.form\n\n        if data.get(\"pages\"):\n            _pages = Pages.query.all()\n            for p in _pages:\n                for f in p.files:\n                    delete_file(file_id=f.id)\n\n            Pages.query.delete()\n\n        if data.get(\"notifications\"):\n            Notifications.query.delete()\n\n        if data.get(\"challenges\"):\n            _challenges = Challenges.query.all()\n            for c in _challenges:\n                for f in c.files:\n                    delete_file(file_id=f.id)\n            Challenges.query.delete()\n\n        if data.get(\"accounts\"):\n            Users.query.delete()\n            Teams.query.delete()\n            require_setup = True\n            logout = True\n\n        if data.get(\"submissions\"):\n            Solves.query.delete()\n            Submissions.query.delete()\n            Awards.query.delete()\n            Unlocks.query.delete()\n            Tracking.query.delete()\n\n        if require_setup:\n            set_config(\"setup\", False)\n            cache.clear()\n            logout_user()\n            next_url = url_for(\"views.setup\")\n\n        db.session.commit()\n\n        clear_pages()\n        clear_standings()\n        clear_challenges()\n        clear_config()\n\n        if logout is True:\n            cache.clear()\n            logout_user()\n\n        db.session.close()\n        return redirect(next_url)\n\n    return render_template(\"admin/reset.html\")\n", "path": "CTFd/admin/__init__.py"}, {"content": "from flask import current_app\n\nfrom CTFd.cache import cache\nfrom CTFd.models import Pages, db\nfrom CTFd.utils import get_config, markdown\nfrom CTFd.utils.dates import isoformat, unix_time_to_utc\nfrom CTFd.utils.formatters import safe_format\nfrom CTFd.utils.security.sanitize import sanitize_html\n\n\ndef format_variables(content):\n    ctf_name = get_config(\"ctf_name\")\n    ctf_description = get_config(\"ctf_description\")\n    ctf_start = get_config(\"start\")\n    if ctf_start:\n        ctf_start = isoformat(unix_time_to_utc(int(ctf_start)))\n\n    ctf_end = get_config(\"end\")\n    if ctf_end:\n        ctf_end = isoformat(unix_time_to_utc(int(ctf_end)))\n\n    ctf_freeze = get_config(\"freeze\")\n    if ctf_freeze:\n        ctf_freeze = isoformat(unix_time_to_utc(int(ctf_freeze)))\n\n    content = safe_format(\n        content,\n        ctf_name=ctf_name,\n        ctf_description=ctf_description,\n        ctf_start=ctf_start,\n        ctf_end=ctf_end,\n        ctf_freeze=ctf_freeze,\n    )\n    return content\n\n\ndef build_html(html, sanitize=False):\n    html = format_variables(html)\n    if (\n        current_app.config[\"HTML_SANITIZATION\"] is True\n        or bool(get_config(\"html_sanitization\")) is True\n        or sanitize is True\n    ):\n        html = sanitize_html(html)\n    return html\n\n\ndef build_markdown(md, sanitize=False):\n    html = markdown(md)\n    html = format_variables(html)\n    if (\n        current_app.config[\"HTML_SANITIZATION\"] is True\n        or bool(get_config(\"html_sanitization\")) is True\n        or sanitize is True\n    ):\n        html = sanitize_html(html)\n    return html\n\n\n@cache.memoize()\ndef get_pages():\n    db_pages = Pages.query.filter(\n        Pages.route != \"index\", Pages.draft.isnot(True), Pages.hidden.isnot(True)\n    ).all()\n    return db_pages\n\n\n@cache.memoize()\ndef get_page(route):\n    page = db.session.execute(\n        Pages.__table__.select()\n        .where(Pages.route == route)\n        .where(Pages.draft.isnot(True))\n    ).fetchone()\n    if page:\n        # Convert the row into a transient ORM object so this change isn't commited accidentally\n        p = Pages(**page)\n        return p\n    return None\n", "path": "CTFd/utils/config/pages.py"}]}