We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Bootstrap Beta v3
https://getbootstrap.com/docs/4.0/migration/#beta-3-changes

See if anything needs to be changed to support Bootstrap Betav3. Supposedly there won't be breaking changes between v3 and Stable so this is a good issue to look into. 


--- 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/challenges.py`
Content:
```
1 from flask import current_app as app, render_template, request, redirect, jsonify, url_for, Blueprint
2 from CTFd.utils import admins_only, is_admin, cache
3 from CTFd.models import db, Teams, Solves, Awards, Challenges, WrongKeys, Keys, Tags, Files, Tracking, Pages, Config, Hints, Unlocks, DatabaseError
4 from CTFd.plugins.keys import get_key_class, KEY_CLASSES
5 from CTFd.plugins.challenges import get_chal_class, CHALLENGE_CLASSES
6 
7 from CTFd import utils
8 
9 import os
10 
11 admin_challenges = Blueprint('admin_challenges', __name__)
12 
13 
14 @admin_challenges.route('/admin/chal_types', methods=['GET'])
15 @admins_only
16 def admin_chal_types():
17     data = {}
18     for class_id in CHALLENGE_CLASSES:
19         challenge_class = CHALLENGE_CLASSES.get(class_id)
20         data[challenge_class.id] = {
21             'id': challenge_class.id,
22             'name': challenge_class.name,
23             'templates': challenge_class.templates,
24             'scripts': challenge_class.scripts,
25         }
26 
27     return jsonify(data)
28 
29 
30 @admin_challenges.route('/admin/chals', methods=['POST', 'GET'])
31 @admins_only
32 def admin_chals():
33     if request.method == 'POST':
34         chals = Challenges.query.add_columns('id', 'type', 'name', 'value', 'description', 'category', 'hidden', 'max_attempts').order_by(Challenges.value).all()
35 
36         json_data = {'game': []}
37         for x in chals:
38             type_class = CHALLENGE_CLASSES.get(x.type)
39             type_name = type_class.name if type_class else None
40 
41             json_data['game'].append({
42                 'id': x.id,
43                 'name': x.name,
44                 'value': x.value,
45                 'description': x.description,
46                 'category': x.category,
47                 'hidden': x.hidden,
48                 'max_attempts': x.max_attempts,
49                 'type': x.type,
50                 'type_name': type_name,
51                 'type_data': {
52                     'id': type_class.id,
53                     'name': type_class.name,
54                     'templates': type_class.templates,
55                     'scripts': type_class.scripts,
56                 }
57             })
58 
59         db.session.close()
60         return jsonify(json_data)
61     else:
62         challenges = Challenges.query.all()
63         return render_template('admin/challenges.html', challenges=challenges)
64 
65 
66 @admin_challenges.route('/admin/chal/<int:chalid>', methods=['GET', 'POST'])
67 @admins_only
68 def admin_chal_detail(chalid):
69     chal = Challenges.query.filter_by(id=chalid).first_or_404()
70     chal_class = get_chal_class(chal.type)
71 
72     if request.method == 'POST':
73         status, message = chal_class.attempt(chal, request)
74         if status:
75             return jsonify({'status': 1, 'message': message})
76         else:
77             return jsonify({'status': 0, 'message': message})
78     elif request.method == 'GET':
79         obj, data = chal_class.read(chal)
80         return jsonify(data)
81 
82 
83 @admin_challenges.route('/admin/tags/<int:chalid>', methods=['GET', 'POST'])
84 @admins_only
85 def admin_tags(chalid):
86     if request.method == 'GET':
87         tags = Tags.query.filter_by(chal=chalid).all()
88         json_data = {'tags': []}
89         for x in tags:
90             json_data['tags'].append({'id': x.id, 'chal': x.chal, 'tag': x.tag})
91         return jsonify(json_data)
92 
93     elif request.method == 'POST':
94         newtags = request.form.getlist('tags[]')
95         for x in newtags:
96             tag = Tags(chalid, x)
97             db.session.add(tag)
98         db.session.commit()
99         db.session.close()
100         return '1'
101 
102 
103 @admin_challenges.route('/admin/tags/<int:tagid>/delete', methods=['POST'])
104 @admins_only
105 def admin_delete_tags(tagid):
106     if request.method == 'POST':
107         tag = Tags.query.filter_by(id=tagid).first_or_404()
108         db.session.delete(tag)
109         db.session.commit()
110         db.session.close()
111         return '1'
112 
113 
114 @admin_challenges.route('/admin/hints', defaults={'hintid': None}, methods=['POST', 'GET'])
115 @admin_challenges.route('/admin/hints/<int:hintid>', methods=['GET', 'POST', 'DELETE'])
116 @admins_only
117 def admin_hints(hintid):
118     if hintid:
119         hint = Hints.query.filter_by(id=hintid).first_or_404()
120 
121         if request.method == 'POST':
122             hint.hint = request.form.get('hint')
123             hint.chal = int(request.form.get('chal'))
124             hint.cost = int(request.form.get('cost') or 0)
125             db.session.commit()
126 
127         elif request.method == 'DELETE':
128             db.session.delete(hint)
129             db.session.commit()
130             db.session.close()
131             return ('', 204)
132 
133         json_data = {
134             'hint': hint.hint,
135             'type': hint.type,
136             'chal': hint.chal,
137             'cost': hint.cost,
138             'id': hint.id
139         }
140         db.session.close()
141         return jsonify(json_data)
142     else:
143         if request.method == 'GET':
144             hints = Hints.query.all()
145             json_data = []
146             for hint in hints:
147                 json_data.append({
148                     'hint': hint.hint,
149                     'type': hint.type,
150                     'chal': hint.chal,
151                     'cost': hint.cost,
152                     'id': hint.id
153                 })
154             return jsonify({'results': json_data})
155         elif request.method == 'POST':
156             hint = request.form.get('hint')
157             chalid = int(request.form.get('chal'))
158             cost = int(request.form.get('cost') or 0)
159             hint_type = request.form.get('type', 0)
160             hint = Hints(chal=chalid, hint=hint, cost=cost)
161             db.session.add(hint)
162             db.session.commit()
163             json_data = {
164                 'hint': hint.hint,
165                 'type': hint.type,
166                 'chal': hint.chal,
167                 'cost': hint.cost,
168                 'id': hint.id
169             }
170             db.session.close()
171             return jsonify(json_data)
172 
173 
174 @admin_challenges.route('/admin/files/<int:chalid>', methods=['GET', 'POST'])
175 @admins_only
176 def admin_files(chalid):
177     if request.method == 'GET':
178         files = Files.query.filter_by(chal=chalid).all()
179         json_data = {'files': []}
180         for x in files:
181             json_data['files'].append({'id': x.id, 'file': x.location})
182         return jsonify(json_data)
183     if request.method == 'POST':
184         if request.form['method'] == "delete":
185             utils.delete_file(request.form['file'])
186 
187             db.session.commit()
188             db.session.close()
189             return '1'
190         elif request.form['method'] == "upload":
191             files = request.files.getlist('files[]')
192 
193             for f in files:
194                 utils.upload_file(file=f, chalid=chalid)
195 
196             db.session.commit()
197             db.session.close()
198             return '1'
199 
200 
201 @admin_challenges.route('/admin/chal/<int:chalid>/<prop>', methods=['GET'])
202 @admins_only
203 def admin_get_values(chalid, prop):
204     challenge = Challenges.query.filter_by(id=chalid).first_or_404()
205     if prop == 'keys':
206         chal_keys = Keys.query.filter_by(chal=challenge.id).all()
207         json_data = {'keys': []}
208         for x in chal_keys:
209             key_class = get_key_class(x.type)
210             json_data['keys'].append({
211                 'id': x.id,
212                 'key': x.flag,
213                 'type': x.type,
214                 'type_name': key_class.name,
215                 'templates': key_class.templates,
216             })
217         return jsonify(json_data)
218     elif prop == 'tags':
219         tags = Tags.query.filter_by(chal=chalid).all()
220         json_data = {'tags': []}
221         for x in tags:
222             json_data['tags'].append({
223                 'id': x.id,
224                 'chal': x.chal,
225                 'tag': x.tag
226             })
227         return jsonify(json_data)
228     elif prop == 'hints':
229         hints = Hints.query.filter_by(chal=chalid)
230         json_data = {'hints': []}
231         for hint in hints:
232             json_data['hints'].append({
233                 'hint': hint.hint,
234                 'type': hint.type,
235                 'chal': hint.chal,
236                 'cost': hint.cost,
237                 'id': hint.id
238             })
239         return jsonify(json_data)
240 
241 
242 @admin_challenges.route('/admin/chal/new', methods=['GET', 'POST'])
243 @admins_only
244 def admin_create_chal():
245     if request.method == 'POST':
246         chal_type = request.form['chaltype']
247         chal_class = get_chal_class(chal_type)
248         chal_class.create(request)
249         return redirect(url_for('admin_challenges.admin_chals'))
250     else:
251         return render_template('admin/chals/create.html')
252 
253 
254 @admin_challenges.route('/admin/chal/delete', methods=['POST'])
255 @admins_only
256 def admin_delete_chal():
257     challenge = Challenges.query.filter_by(id=request.form['id']).first_or_404()
258     chal_class = get_chal_class(challenge.type)
259     chal_class.delete(challenge)
260     return '1'
261 
262 
263 @admin_challenges.route('/admin/chal/update', methods=['POST'])
264 @admins_only
265 def admin_update_chal():
266     challenge = Challenges.query.filter_by(id=request.form['id']).first_or_404()
267     chal_class = get_chal_class(challenge.type)
268     chal_class.update(challenge, request)
269     return redirect(url_for('admin_challenges.admin_chals'))
270
```
--- 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/challenges.py b/CTFd/admin/challenges.py
--- a/CTFd/admin/challenges.py
+++ b/CTFd/admin/challenges.py
@@ -31,22 +31,31 @@
 @admins_only
 def admin_chals():
     if request.method == 'POST':
-        chals = Challenges.query.add_columns('id', 'type', 'name', 'value', 'description', 'category', 'hidden', 'max_attempts').order_by(Challenges.value).all()
+        chals = Challenges.query.order_by(Challenges.value).all()
 
         json_data = {'game': []}
-        for x in chals:
-            type_class = CHALLENGE_CLASSES.get(x.type)
+        for chal in chals:
+            tags = [tag.tag for tag in Tags.query.add_columns('tag').filter_by(chal=chal.id).all()]
+            files = [str(f.location) for f in Files.query.filter_by(chal=chal.id).all()]
+            hints = []
+            for hint in Hints.query.filter_by(chal=chal.id).all():
+                hints.append({'id': hint.id, 'cost': hint.cost, 'hint': hint.hint})
+
+            type_class = CHALLENGE_CLASSES.get(chal.type)
             type_name = type_class.name if type_class else None
 
             json_data['game'].append({
-                'id': x.id,
-                'name': x.name,
-                'value': x.value,
-                'description': x.description,
-                'category': x.category,
-                'hidden': x.hidden,
-                'max_attempts': x.max_attempts,
-                'type': x.type,
+                'id': chal.id,
+                'name': chal.name,
+                'value': chal.value,
+                'description': chal.description,
+                'category': chal.category,
+                'files': files,
+                'tags': tags,
+                'hints': hints,
+                'hidden': chal.hidden,
+                'max_attempts': chal.max_attempts,
+                'type': chal.type,
                 'type_name': type_name,
                 'type_data': {
                     'id': type_class.id,
@@ -77,6 +86,17 @@
             return jsonify({'status': 0, 'message': message})
     elif request.method == 'GET':
         obj, data = chal_class.read(chal)
+
+        tags = [tag.tag for tag in Tags.query.add_columns('tag').filter_by(chal=chal.id).all()]
+        files = [str(f.location) for f in Files.query.filter_by(chal=chal.id).all()]
+        hints = []
+        for hint in Hints.query.filter_by(chal=chal.id).all():
+            hints.append({'id': hint.id, 'cost': hint.cost, 'hint': hint.hint})
+
+        data['tags'] = tags
+        data['files'] = files
+        data['hints'] = hints
+
         return jsonify(data)

{"golden_diff": "diff --git a/CTFd/admin/challenges.py b/CTFd/admin/challenges.py\n--- a/CTFd/admin/challenges.py\n+++ b/CTFd/admin/challenges.py\n@@ -31,22 +31,31 @@\n @admins_only\n def admin_chals():\n     if request.method == 'POST':\n-        chals = Challenges.query.add_columns('id', 'type', 'name', 'value', 'description', 'category', 'hidden', 'max_attempts').order_by(Challenges.value).all()\n+        chals = Challenges.query.order_by(Challenges.value).all()\n \n         json_data = {'game': []}\n-        for x in chals:\n-            type_class = CHALLENGE_CLASSES.get(x.type)\n+        for chal in chals:\n+            tags = [tag.tag for tag in Tags.query.add_columns('tag').filter_by(chal=chal.id).all()]\n+            files = [str(f.location) for f in Files.query.filter_by(chal=chal.id).all()]\n+            hints = []\n+            for hint in Hints.query.filter_by(chal=chal.id).all():\n+                hints.append({'id': hint.id, 'cost': hint.cost, 'hint': hint.hint})\n+\n+            type_class = CHALLENGE_CLASSES.get(chal.type)\n             type_name = type_class.name if type_class else None\n \n             json_data['game'].append({\n-                'id': x.id,\n-                'name': x.name,\n-                'value': x.value,\n-                'description': x.description,\n-                'category': x.category,\n-                'hidden': x.hidden,\n-                'max_attempts': x.max_attempts,\n-                'type': x.type,\n+                'id': chal.id,\n+                'name': chal.name,\n+                'value': chal.value,\n+                'description': chal.description,\n+                'category': chal.category,\n+                'files': files,\n+                'tags': tags,\n+                'hints': hints,\n+                'hidden': chal.hidden,\n+                'max_attempts': chal.max_attempts,\n+                'type': chal.type,\n                 'type_name': type_name,\n                 'type_data': {\n                     'id': type_class.id,\n@@ -77,6 +86,17 @@\n             return jsonify({'status': 0, 'message': message})\n     elif request.method == 'GET':\n         obj, data = chal_class.read(chal)\n+\n+        tags = [tag.tag for tag in Tags.query.add_columns('tag').filter_by(chal=chal.id).all()]\n+        files = [str(f.location) for f in Files.query.filter_by(chal=chal.id).all()]\n+        hints = []\n+        for hint in Hints.query.filter_by(chal=chal.id).all():\n+            hints.append({'id': hint.id, 'cost': hint.cost, 'hint': hint.hint})\n+\n+        data['tags'] = tags\n+        data['files'] = files\n+        data['hints'] = hints\n+\n         return jsonify(data)\n", "issue": "Bootstrap Beta v3\nhttps://getbootstrap.com/docs/4.0/migration/#beta-3-changes\r\n\r\nSee if anything needs to be changed to support Bootstrap Betav3. Supposedly there won't be breaking changes between v3 and Stable so this is a good issue to look into. \r\n\n", "before_files": [{"content": "from flask import current_app as app, render_template, request, redirect, jsonify, url_for, Blueprint\nfrom CTFd.utils import admins_only, is_admin, cache\nfrom CTFd.models import db, Teams, Solves, Awards, Challenges, WrongKeys, Keys, Tags, Files, Tracking, Pages, Config, Hints, Unlocks, DatabaseError\nfrom CTFd.plugins.keys import get_key_class, KEY_CLASSES\nfrom CTFd.plugins.challenges import get_chal_class, CHALLENGE_CLASSES\n\nfrom CTFd import utils\n\nimport os\n\nadmin_challenges = Blueprint('admin_challenges', __name__)\n\n\n@admin_challenges.route('/admin/chal_types', methods=['GET'])\n@admins_only\ndef admin_chal_types():\n    data = {}\n    for class_id in CHALLENGE_CLASSES:\n        challenge_class = CHALLENGE_CLASSES.get(class_id)\n        data[challenge_class.id] = {\n            'id': challenge_class.id,\n            'name': challenge_class.name,\n            'templates': challenge_class.templates,\n            'scripts': challenge_class.scripts,\n        }\n\n    return jsonify(data)\n\n\n@admin_challenges.route('/admin/chals', methods=['POST', 'GET'])\n@admins_only\ndef admin_chals():\n    if request.method == 'POST':\n        chals = Challenges.query.add_columns('id', 'type', 'name', 'value', 'description', 'category', 'hidden', 'max_attempts').order_by(Challenges.value).all()\n\n        json_data = {'game': []}\n        for x in chals:\n            type_class = CHALLENGE_CLASSES.get(x.type)\n            type_name = type_class.name if type_class else None\n\n            json_data['game'].append({\n                'id': x.id,\n                'name': x.name,\n                'value': x.value,\n                'description': x.description,\n                'category': x.category,\n                'hidden': x.hidden,\n                'max_attempts': x.max_attempts,\n                'type': x.type,\n                'type_name': type_name,\n                'type_data': {\n                    'id': type_class.id,\n                    'name': type_class.name,\n                    'templates': type_class.templates,\n                    'scripts': type_class.scripts,\n                }\n            })\n\n        db.session.close()\n        return jsonify(json_data)\n    else:\n        challenges = Challenges.query.all()\n        return render_template('admin/challenges.html', challenges=challenges)\n\n\n@admin_challenges.route('/admin/chal/<int:chalid>', methods=['GET', 'POST'])\n@admins_only\ndef admin_chal_detail(chalid):\n    chal = Challenges.query.filter_by(id=chalid).first_or_404()\n    chal_class = get_chal_class(chal.type)\n\n    if request.method == 'POST':\n        status, message = chal_class.attempt(chal, request)\n        if status:\n            return jsonify({'status': 1, 'message': message})\n        else:\n            return jsonify({'status': 0, 'message': message})\n    elif request.method == 'GET':\n        obj, data = chal_class.read(chal)\n        return jsonify(data)\n\n\n@admin_challenges.route('/admin/tags/<int:chalid>', methods=['GET', 'POST'])\n@admins_only\ndef admin_tags(chalid):\n    if request.method == 'GET':\n        tags = Tags.query.filter_by(chal=chalid).all()\n        json_data = {'tags': []}\n        for x in tags:\n            json_data['tags'].append({'id': x.id, 'chal': x.chal, 'tag': x.tag})\n        return jsonify(json_data)\n\n    elif request.method == 'POST':\n        newtags = request.form.getlist('tags[]')\n        for x in newtags:\n            tag = Tags(chalid, x)\n            db.session.add(tag)\n        db.session.commit()\n        db.session.close()\n        return '1'\n\n\n@admin_challenges.route('/admin/tags/<int:tagid>/delete', methods=['POST'])\n@admins_only\ndef admin_delete_tags(tagid):\n    if request.method == 'POST':\n        tag = Tags.query.filter_by(id=tagid).first_or_404()\n        db.session.delete(tag)\n        db.session.commit()\n        db.session.close()\n        return '1'\n\n\n@admin_challenges.route('/admin/hints', defaults={'hintid': None}, methods=['POST', 'GET'])\n@admin_challenges.route('/admin/hints/<int:hintid>', methods=['GET', 'POST', 'DELETE'])\n@admins_only\ndef admin_hints(hintid):\n    if hintid:\n        hint = Hints.query.filter_by(id=hintid).first_or_404()\n\n        if request.method == 'POST':\n            hint.hint = request.form.get('hint')\n            hint.chal = int(request.form.get('chal'))\n            hint.cost = int(request.form.get('cost') or 0)\n            db.session.commit()\n\n        elif request.method == 'DELETE':\n            db.session.delete(hint)\n            db.session.commit()\n            db.session.close()\n            return ('', 204)\n\n        json_data = {\n            'hint': hint.hint,\n            'type': hint.type,\n            'chal': hint.chal,\n            'cost': hint.cost,\n            'id': hint.id\n        }\n        db.session.close()\n        return jsonify(json_data)\n    else:\n        if request.method == 'GET':\n            hints = Hints.query.all()\n            json_data = []\n            for hint in hints:\n                json_data.append({\n                    'hint': hint.hint,\n                    'type': hint.type,\n                    'chal': hint.chal,\n                    'cost': hint.cost,\n                    'id': hint.id\n                })\n            return jsonify({'results': json_data})\n        elif request.method == 'POST':\n            hint = request.form.get('hint')\n            chalid = int(request.form.get('chal'))\n            cost = int(request.form.get('cost') or 0)\n            hint_type = request.form.get('type', 0)\n            hint = Hints(chal=chalid, hint=hint, cost=cost)\n            db.session.add(hint)\n            db.session.commit()\n            json_data = {\n                'hint': hint.hint,\n                'type': hint.type,\n                'chal': hint.chal,\n                'cost': hint.cost,\n                'id': hint.id\n            }\n            db.session.close()\n            return jsonify(json_data)\n\n\n@admin_challenges.route('/admin/files/<int:chalid>', methods=['GET', 'POST'])\n@admins_only\ndef admin_files(chalid):\n    if request.method == 'GET':\n        files = Files.query.filter_by(chal=chalid).all()\n        json_data = {'files': []}\n        for x in files:\n            json_data['files'].append({'id': x.id, 'file': x.location})\n        return jsonify(json_data)\n    if request.method == 'POST':\n        if request.form['method'] == \"delete\":\n            utils.delete_file(request.form['file'])\n\n            db.session.commit()\n            db.session.close()\n            return '1'\n        elif request.form['method'] == \"upload\":\n            files = request.files.getlist('files[]')\n\n            for f in files:\n                utils.upload_file(file=f, chalid=chalid)\n\n            db.session.commit()\n            db.session.close()\n            return '1'\n\n\n@admin_challenges.route('/admin/chal/<int:chalid>/<prop>', methods=['GET'])\n@admins_only\ndef admin_get_values(chalid, prop):\n    challenge = Challenges.query.filter_by(id=chalid).first_or_404()\n    if prop == 'keys':\n        chal_keys = Keys.query.filter_by(chal=challenge.id).all()\n        json_data = {'keys': []}\n        for x in chal_keys:\n            key_class = get_key_class(x.type)\n            json_data['keys'].append({\n                'id': x.id,\n                'key': x.flag,\n                'type': x.type,\n                'type_name': key_class.name,\n                'templates': key_class.templates,\n            })\n        return jsonify(json_data)\n    elif prop == 'tags':\n        tags = Tags.query.filter_by(chal=chalid).all()\n        json_data = {'tags': []}\n        for x in tags:\n            json_data['tags'].append({\n                'id': x.id,\n                'chal': x.chal,\n                'tag': x.tag\n            })\n        return jsonify(json_data)\n    elif prop == 'hints':\n        hints = Hints.query.filter_by(chal=chalid)\n        json_data = {'hints': []}\n        for hint in hints:\n            json_data['hints'].append({\n                'hint': hint.hint,\n                'type': hint.type,\n                'chal': hint.chal,\n                'cost': hint.cost,\n                'id': hint.id\n            })\n        return jsonify(json_data)\n\n\n@admin_challenges.route('/admin/chal/new', methods=['GET', 'POST'])\n@admins_only\ndef admin_create_chal():\n    if request.method == 'POST':\n        chal_type = request.form['chaltype']\n        chal_class = get_chal_class(chal_type)\n        chal_class.create(request)\n        return redirect(url_for('admin_challenges.admin_chals'))\n    else:\n        return render_template('admin/chals/create.html')\n\n\n@admin_challenges.route('/admin/chal/delete', methods=['POST'])\n@admins_only\ndef admin_delete_chal():\n    challenge = Challenges.query.filter_by(id=request.form['id']).first_or_404()\n    chal_class = get_chal_class(challenge.type)\n    chal_class.delete(challenge)\n    return '1'\n\n\n@admin_challenges.route('/admin/chal/update', methods=['POST'])\n@admins_only\ndef admin_update_chal():\n    challenge = Challenges.query.filter_by(id=request.form['id']).first_or_404()\n    chal_class = get_chal_class(challenge.type)\n    chal_class.update(challenge, request)\n    return redirect(url_for('admin_challenges.admin_chals'))\n", "path": "CTFd/admin/challenges.py"}], "after_files": [{"content": "from flask import current_app as app, render_template, request, redirect, jsonify, url_for, Blueprint\nfrom CTFd.utils import admins_only, is_admin, cache\nfrom CTFd.models import db, Teams, Solves, Awards, Challenges, WrongKeys, Keys, Tags, Files, Tracking, Pages, Config, Hints, Unlocks, DatabaseError\nfrom CTFd.plugins.keys import get_key_class, KEY_CLASSES\nfrom CTFd.plugins.challenges import get_chal_class, CHALLENGE_CLASSES\n\nfrom CTFd import utils\n\nimport os\n\nadmin_challenges = Blueprint('admin_challenges', __name__)\n\n\n@admin_challenges.route('/admin/chal_types', methods=['GET'])\n@admins_only\ndef admin_chal_types():\n    data = {}\n    for class_id in CHALLENGE_CLASSES:\n        challenge_class = CHALLENGE_CLASSES.get(class_id)\n        data[challenge_class.id] = {\n            'id': challenge_class.id,\n            'name': challenge_class.name,\n            'templates': challenge_class.templates,\n            'scripts': challenge_class.scripts,\n        }\n\n    return jsonify(data)\n\n\n@admin_challenges.route('/admin/chals', methods=['POST', 'GET'])\n@admins_only\ndef admin_chals():\n    if request.method == 'POST':\n        chals = Challenges.query.order_by(Challenges.value).all()\n\n        json_data = {'game': []}\n        for chal in chals:\n            tags = [tag.tag for tag in Tags.query.add_columns('tag').filter_by(chal=chal.id).all()]\n            files = [str(f.location) for f in Files.query.filter_by(chal=chal.id).all()]\n            hints = []\n            for hint in Hints.query.filter_by(chal=chal.id).all():\n                hints.append({'id': hint.id, 'cost': hint.cost, 'hint': hint.hint})\n\n            type_class = CHALLENGE_CLASSES.get(chal.type)\n            type_name = type_class.name if type_class else None\n\n            json_data['game'].append({\n                'id': chal.id,\n                'name': chal.name,\n                'value': chal.value,\n                'description': chal.description,\n                'category': chal.category,\n                'files': files,\n                'tags': tags,\n                'hints': hints,\n                'hidden': chal.hidden,\n                'max_attempts': chal.max_attempts,\n                'type': chal.type,\n                'type_name': type_name,\n                'type_data': {\n                    'id': type_class.id,\n                    'name': type_class.name,\n                    'templates': type_class.templates,\n                    'scripts': type_class.scripts,\n                }\n            })\n\n        db.session.close()\n        return jsonify(json_data)\n    else:\n        challenges = Challenges.query.all()\n        return render_template('admin/challenges.html', challenges=challenges)\n\n\n@admin_challenges.route('/admin/chal/<int:chalid>', methods=['GET', 'POST'])\n@admins_only\ndef admin_chal_detail(chalid):\n    chal = Challenges.query.filter_by(id=chalid).first_or_404()\n    chal_class = get_chal_class(chal.type)\n\n    if request.method == 'POST':\n        status, message = chal_class.attempt(chal, request)\n        if status:\n            return jsonify({'status': 1, 'message': message})\n        else:\n            return jsonify({'status': 0, 'message': message})\n    elif request.method == 'GET':\n        obj, data = chal_class.read(chal)\n\n        tags = [tag.tag for tag in Tags.query.add_columns('tag').filter_by(chal=chal.id).all()]\n        files = [str(f.location) for f in Files.query.filter_by(chal=chal.id).all()]\n        hints = []\n        for hint in Hints.query.filter_by(chal=chal.id).all():\n            hints.append({'id': hint.id, 'cost': hint.cost, 'hint': hint.hint})\n\n        data['tags'] = tags\n        data['files'] = files\n        data['hints'] = hints\n\n        return jsonify(data)\n\n\n@admin_challenges.route('/admin/tags/<int:chalid>', methods=['GET', 'POST'])\n@admins_only\ndef admin_tags(chalid):\n    if request.method == 'GET':\n        tags = Tags.query.filter_by(chal=chalid).all()\n        json_data = {'tags': []}\n        for x in tags:\n            json_data['tags'].append({'id': x.id, 'chal': x.chal, 'tag': x.tag})\n        return jsonify(json_data)\n\n    elif request.method == 'POST':\n        newtags = request.form.getlist('tags[]')\n        for x in newtags:\n            tag = Tags(chalid, x)\n            db.session.add(tag)\n        db.session.commit()\n        db.session.close()\n        return '1'\n\n\n@admin_challenges.route('/admin/tags/<int:tagid>/delete', methods=['POST'])\n@admins_only\ndef admin_delete_tags(tagid):\n    if request.method == 'POST':\n        tag = Tags.query.filter_by(id=tagid).first_or_404()\n        db.session.delete(tag)\n        db.session.commit()\n        db.session.close()\n        return '1'\n\n\n@admin_challenges.route('/admin/hints', defaults={'hintid': None}, methods=['POST', 'GET'])\n@admin_challenges.route('/admin/hints/<int:hintid>', methods=['GET', 'POST', 'DELETE'])\n@admins_only\ndef admin_hints(hintid):\n    if hintid:\n        hint = Hints.query.filter_by(id=hintid).first_or_404()\n\n        if request.method == 'POST':\n            hint.hint = request.form.get('hint')\n            hint.chal = int(request.form.get('chal'))\n            hint.cost = int(request.form.get('cost') or 0)\n            db.session.commit()\n\n        elif request.method == 'DELETE':\n            db.session.delete(hint)\n            db.session.commit()\n            db.session.close()\n            return ('', 204)\n\n        json_data = {\n            'hint': hint.hint,\n            'type': hint.type,\n            'chal': hint.chal,\n            'cost': hint.cost,\n            'id': hint.id\n        }\n        db.session.close()\n        return jsonify(json_data)\n    else:\n        if request.method == 'GET':\n            hints = Hints.query.all()\n            json_data = []\n            for hint in hints:\n                json_data.append({\n                    'hint': hint.hint,\n                    'type': hint.type,\n                    'chal': hint.chal,\n                    'cost': hint.cost,\n                    'id': hint.id\n                })\n            return jsonify({'results': json_data})\n        elif request.method == 'POST':\n            hint = request.form.get('hint')\n            chalid = int(request.form.get('chal'))\n            cost = int(request.form.get('cost') or 0)\n            hint_type = request.form.get('type', 0)\n            hint = Hints(chal=chalid, hint=hint, cost=cost)\n            db.session.add(hint)\n            db.session.commit()\n            json_data = {\n                'hint': hint.hint,\n                'type': hint.type,\n                'chal': hint.chal,\n                'cost': hint.cost,\n                'id': hint.id\n            }\n            db.session.close()\n            return jsonify(json_data)\n\n\n@admin_challenges.route('/admin/files/<int:chalid>', methods=['GET', 'POST'])\n@admins_only\ndef admin_files(chalid):\n    if request.method == 'GET':\n        files = Files.query.filter_by(chal=chalid).all()\n        json_data = {'files': []}\n        for x in files:\n            json_data['files'].append({'id': x.id, 'file': x.location})\n        return jsonify(json_data)\n    if request.method == 'POST':\n        if request.form['method'] == \"delete\":\n            utils.delete_file(request.form['file'])\n\n            db.session.commit()\n            db.session.close()\n            return '1'\n        elif request.form['method'] == \"upload\":\n            files = request.files.getlist('files[]')\n\n            for f in files:\n                utils.upload_file(file=f, chalid=chalid)\n\n            db.session.commit()\n            db.session.close()\n            return '1'\n\n\n@admin_challenges.route('/admin/chal/<int:chalid>/<prop>', methods=['GET'])\n@admins_only\ndef admin_get_values(chalid, prop):\n    challenge = Challenges.query.filter_by(id=chalid).first_or_404()\n    if prop == 'keys':\n        chal_keys = Keys.query.filter_by(chal=challenge.id).all()\n        json_data = {'keys': []}\n        for x in chal_keys:\n            key_class = get_key_class(x.type)\n            json_data['keys'].append({\n                'id': x.id,\n                'key': x.flag,\n                'type': x.type,\n                'type_name': key_class.name,\n                'templates': key_class.templates,\n            })\n        return jsonify(json_data)\n    elif prop == 'tags':\n        tags = Tags.query.filter_by(chal=chalid).all()\n        json_data = {'tags': []}\n        for x in tags:\n            json_data['tags'].append({\n                'id': x.id,\n                'chal': x.chal,\n                'tag': x.tag\n            })\n        return jsonify(json_data)\n    elif prop == 'hints':\n        hints = Hints.query.filter_by(chal=chalid)\n        json_data = {'hints': []}\n        for hint in hints:\n            json_data['hints'].append({\n                'hint': hint.hint,\n                'type': hint.type,\n                'chal': hint.chal,\n                'cost': hint.cost,\n                'id': hint.id\n            })\n        return jsonify(json_data)\n\n\n@admin_challenges.route('/admin/chal/new', methods=['GET', 'POST'])\n@admins_only\ndef admin_create_chal():\n    if request.method == 'POST':\n        chal_type = request.form['chaltype']\n        chal_class = get_chal_class(chal_type)\n        chal_class.create(request)\n        return redirect(url_for('admin_challenges.admin_chals'))\n    else:\n        return render_template('admin/chals/create.html')\n\n\n@admin_challenges.route('/admin/chal/delete', methods=['POST'])\n@admins_only\ndef admin_delete_chal():\n    challenge = Challenges.query.filter_by(id=request.form['id']).first_or_404()\n    chal_class = get_chal_class(challenge.type)\n    chal_class.delete(challenge)\n    return '1'\n\n\n@admin_challenges.route('/admin/chal/update', methods=['POST'])\n@admins_only\ndef admin_update_chal():\n    challenge = Challenges.query.filter_by(id=request.form['id']).first_or_404()\n    chal_class = get_chal_class(challenge.type)\n    chal_class.update(challenge, request)\n    return redirect(url_for('admin_challenges.admin_chals'))\n", "path": "CTFd/admin/challenges.py"}]}