From 6d9d03e35e21e10520995c09486dc83deb5a3140 Mon Sep 17 00:00:00 2001 From: Kevin Chung Date: Sat, 3 Jun 2017 14:25:31 -0400 Subject: [PATCH] Pages functionality improved (#267) * Pages now support Markdown * Pages now have a preview tab * Adding a media library to Pages --- CTFd/admin/pages.py | 22 +++ CTFd/templates/admin/editor.html | 239 +++++++++++++++++++++++++++++-- CTFd/utils.py | 4 +- CTFd/views.py | 4 +- requirements.txt | 1 + tests/helpers.py | 7 + tests/test_user_facing.py | 15 +- 7 files changed, 280 insertions(+), 12 deletions(-) diff --git a/CTFd/admin/pages.py b/CTFd/admin/pages.py index 863f6dc9..4e06697a 100644 --- a/CTFd/admin/pages.py +++ b/CTFd/admin/pages.py @@ -53,6 +53,28 @@ def admin_pages_view(route): return render_template('admin/pages.html', routes=pages, css=utils.get_config('css')) +@admin_pages.route('/admin/media', methods=['GET', 'POST', 'DELETE']) +@admins_only +def admin_pages_media(): + if request.method == 'POST': + files = request.files.getlist('files[]') + + uploaded = [] + for f in files: + data = utils.upload_file(file=f, chalid=None) + if data: + uploaded.append({'id': data[0], 'location': data[1]}) + return jsonify({'results': uploaded}) + elif request.method == 'DELETE': + file_ids = request.form.getlist('file_ids[]') + for file_id in file_ids: + utils.delete_file(file_id) + return True + else: + files = [{'id': f.id, 'location': f.location} for f in Files.query.filter_by(chal=None).all()] + return jsonify({'results': files}) + + @admin_pages.route('/admin/page//delete', methods=['POST']) @admins_only def delete_page(pageroute): diff --git a/CTFd/templates/admin/editor.html b/CTFd/templates/admin/editor.html index 7a75a7ff..f1024916 100644 --- a/CTFd/templates/admin/editor.html +++ b/CTFd/templates/admin/editor.html @@ -4,10 +4,66 @@ {% endblock %} {% block content %} + + +
{% for error in errors %} @@ -32,25 +88,157 @@
+

Content:

This is the HTML content of your page

-
- + + + +
+
+
+
+
+
+ +
+
+
+
+ +
+
+
+
+
+
+
{% endblock %} {% block scripts %} + {% endblock %} diff --git a/CTFd/utils.py b/CTFd/utils.py index 99cc2471..5bd02fc0 100644 --- a/CTFd/utils.py +++ b/CTFd/utils.py @@ -5,6 +5,7 @@ import hashlib import json import logging import logging.handlers +import mistune import os import re import requests @@ -32,6 +33,7 @@ from CTFd.models import db, WrongKeys, Pages, Config, Tracking, Teams, Files, Co cache = Cache() migrate = Migrate() +markdown = mistune.Markdown() def init_logs(app): @@ -348,7 +350,7 @@ def upload_file(file, chalid): db_f = Files(chalid, (md5hash + '/' + filename)) db.session.add(db_f) db.session.commit() - return True + return db_f.id, (md5hash + '/' + filename) def delete_file(file_id): diff --git a/CTFd/views.py b/CTFd/views.py index de221ed6..c30d2b80 100644 --- a/CTFd/views.py +++ b/CTFd/views.py @@ -6,7 +6,7 @@ from jinja2.exceptions import TemplateNotFound from passlib.hash import bcrypt_sha256 from CTFd.models import db, Teams, Solves, Awards, Files, Pages -from CTFd.utils import cache +from CTFd.utils import cache, markdown from CTFd import utils views = Blueprint('views', __name__) @@ -118,7 +118,7 @@ def static_html(template): return render_template('%s.html' % template) except TemplateNotFound: page = Pages.query.filter_by(route=template).first_or_404() - return render_template('page.html', content=page.html) + return render_template('page.html', content=markdown(page.html)) @views.route('/teams', defaults={'page': '1'}) diff --git a/requirements.txt b/requirements.txt index 0f7ff303..17887661 100644 --- a/requirements.txt +++ b/requirements.txt @@ -13,3 +13,4 @@ requests==2.13.0 PyMySQL==0.7.10 gunicorn==19.7.0 dataset==0.8.0 +mistune==0.7.4 diff --git a/tests/helpers.py b/tests/helpers.py index 5e9a92cf..d3164387 100644 --- a/tests/helpers.py +++ b/tests/helpers.py @@ -119,3 +119,10 @@ def gen_tracking(db, ip, team): db.session.add(tracking) db.session.commit() return tracking + + +def gen_page(db, route, html): + page = Pages(route, html) + db.session.add(page) + db.session.commit() + return page diff --git a/tests/test_user_facing.py b/tests/test_user_facing.py index 5a1bc8de..c4c22b80 100644 --- a/tests/test_user_facing.py +++ b/tests/test_user_facing.py @@ -250,7 +250,6 @@ def test_submitting_incorrect_flag(): def test_submitting_unicode_flag(): """Test that users can submit a unicode flag""" - print("Test that users can submit a flag") app = create_ctfd() with app.app_context(): register_user(app) @@ -266,3 +265,17 @@ def test_submitting_unicode_flag(): assert r.status_code == 200 resp = json.loads(r.data.decode('utf8')) assert resp.get('status') == 1 and resp.get('message') == "Correct" + + +def test_pages_routing_and_rendering(): + """Test that pages are routing and rendering""" + app = create_ctfd() + with app.app_context(): + html = '''##The quick brown fox jumped over the lazy dog''' + route = 'test' + page = gen_page(app.db, route, html) + + with app.test_client() as client: + r = client.get('/test') + output = r.get_data(as_text=True) + assert "

The quick brown fox jumped over the lazy dog

" in output