diff --git a/CTFd/plugins/__init__.py b/CTFd/plugins/__init__.py index 60559e62..223832cb 100644 --- a/CTFd/plugins/__init__.py +++ b/CTFd/plugins/__init__.py @@ -3,20 +3,19 @@ import importlib import os from collections import namedtuple -from flask.helpers import safe_join from flask import current_app as app, send_file, send_from_directory, abort from CTFd.utils.decorators import admins_only as admins_only_wrapper from CTFd.utils.plugins import ( override_template as utils_override_template, register_script as utils_register_plugin_script, register_stylesheet as utils_register_plugin_stylesheet, + register_admin_script as utils_register_admin_plugin_script, + register_admin_stylesheet as utils_register_admin_plugin_stylesheet ) from CTFd.utils.config.pages import get_pages Menu = namedtuple('Menu', ['title', 'route']) -ADMIN_PLUGIN_MENU_BAR = [] -USER_PAGE_MENU_BAR = [] def register_plugin_assets_directory(app, base_path, admins_only=False): @@ -83,6 +82,26 @@ def register_plugin_stylesheet(*args, **kwargs): utils_register_plugin_stylesheet(*args, **kwargs) +def register_admin_plugin_script(*args, **kwargs): + """ + Adds a given script to the base.html of the admin theme which all admin pages inherit from + :param args: + :param kwargs: + :return: + """ + utils_register_admin_plugin_script(*args, **kwargs) + + +def register_admin_plugin_stylesheet(*args, **kwargs): + """ + Adds a given stylesheet to the base.html of the admin theme which all admin pages inherit from + :param args: + :param kwargs: + :return: + """ + utils_register_admin_plugin_stylesheet(*args, **kwargs) + + def register_admin_plugin_menu_bar(title, route): """ Registers links on the Admin Panel menubar/navbar @@ -92,7 +111,7 @@ def register_admin_plugin_menu_bar(title, route): :return: """ am = Menu(title=title, route=route) - ADMIN_PLUGIN_MENU_BAR.append(am) + app.admin_plugin_menu_bar.append(am) def get_admin_plugin_menu_bar(): @@ -101,7 +120,7 @@ def get_admin_plugin_menu_bar(): :return: Returns a list of Menu namedtuples. They have name, and route attributes. """ - return ADMIN_PLUGIN_MENU_BAR + return app.admin_plugin_menu_bar def register_user_page_menu_bar(title, route): @@ -113,7 +132,7 @@ def register_user_page_menu_bar(title, route): :return: """ p = Menu(title=title, route=route) - USER_PAGE_MENU_BAR.append(p) + app.plugin_menu_bar.append(p) def get_user_page_menu_bar(): @@ -122,7 +141,7 @@ def get_user_page_menu_bar(): :return: Returns a list of Menu namedtuples. They have name, and route attributes. """ - return get_pages() + USER_PAGE_MENU_BAR + return get_pages() + app.plugin_menu_bar def bypass_csrf_protection(f): @@ -146,6 +165,14 @@ def init_plugins(app): :param app: A CTFd application :return: """ + app.admin_plugin_scripts = [] + app.admin_plugin_stylesheets = [] + app.plugin_scripts = [] + app.plugin_stylesheets = [] + + app.admin_plugin_menu_bar = [] + app.plugin_menu_bar = [] + if app.config.get('SAFE_MODE', False) is False: modules = sorted(glob.glob(os.path.dirname(__file__) + "/*")) blacklist = {'__pycache__'} diff --git a/CTFd/themes/admin/templates/base.html b/CTFd/themes/admin/templates/base.html index f06677a8..4d8cd56a 100644 --- a/CTFd/themes/admin/templates/base.html +++ b/CTFd/themes/admin/templates/base.html @@ -25,6 +25,15 @@ var user_mode = "{{ get_config('user_mode') }}"; {% block stylesheets %} {% endblock %} + {% for stylesheet in get_registered_admin_stylesheets() %} + {% if stylesheet.startswith('http') %} + + {% elif request.script_root %} + + {% else %} + + {% endif %} + {% endfor %} @@ -135,6 +144,16 @@ {% block scripts %} {% endblock %} + + {% for script in get_registered_admin_scripts() %} + {% if script.startswith('http') %} + + {% elif request.script_root %} + + {% else %} + + {% endif %} + {% endfor %} diff --git a/CTFd/utils/initialization/__init__.py b/CTFd/utils/initialization/__init__.py index a45dbcad..647a499d 100644 --- a/CTFd/utils/initialization/__init__.py +++ b/CTFd/utils/initialization/__init__.py @@ -8,7 +8,13 @@ from CTFd.utils import config from CTFd.utils.config import can_send_mail, ctf_logo, ctf_name, ctf_theme from CTFd.utils.config.pages import get_pages -from CTFd.utils.plugins import get_registered_stylesheets, get_registered_scripts, get_configurable_plugins +from CTFd.utils.plugins import ( + get_registered_stylesheets, + get_registered_scripts, + get_configurable_plugins, + get_registered_admin_scripts, + get_registered_admin_stylesheets +) from CTFd.utils.countries import get_countries, lookup_country_code from CTFd.utils.user import authed, get_ip, get_current_user, get_current_team @@ -45,6 +51,8 @@ def init_template_globals(app): app.jinja_env.globals.update(get_configurable_plugins=get_configurable_plugins) app.jinja_env.globals.update(get_registered_scripts=get_registered_scripts) app.jinja_env.globals.update(get_registered_stylesheets=get_registered_stylesheets) + app.jinja_env.globals.update(get_registered_admin_scripts=get_registered_admin_scripts) + app.jinja_env.globals.update(get_registered_admin_stylesheets=get_registered_admin_stylesheets) app.jinja_env.globals.update(get_config=get_config) app.jinja_env.globals.update(generate_account_url=generate_account_url) app.jinja_env.globals.update(get_countries=get_countries) diff --git a/CTFd/utils/plugins/__init__.py b/CTFd/utils/plugins/__init__.py index 770fc534..8781a3fb 100644 --- a/CTFd/utils/plugins/__init__.py +++ b/CTFd/utils/plugins/__init__.py @@ -4,24 +4,36 @@ import os import json -SCRIPTS = [] -STYLESHEETS = [] - - def register_script(url): - SCRIPTS.append(url) + app.plugin_scripts.append(url) def register_stylesheet(url): - STYLESHEETS.append(url) + app.plugin_stylesheets.append(url) + + +def register_admin_script(url): + app.admin_plugin_scripts.append(url) + + +def register_admin_stylesheet(url): + app.admin_plugin_stylesheets.append(url) def get_registered_scripts(): - return SCRIPTS + return app.plugin_scripts def get_registered_stylesheets(): - return STYLESHEETS + return app.plugin_stylesheets + + +def get_registered_admin_scripts(): + return app.admin_plugin_scripts + + +def get_registered_admin_stylesheets(): + return app.admin_plugin_stylesheets def override_template(template, html): diff --git a/tests/test_plugin_utils.py b/tests/test_plugin_utils.py index 8bee2d6d..0e28b3a3 100644 --- a/tests/test_plugin_utils.py +++ b/tests/test_plugin_utils.py @@ -7,6 +7,8 @@ from CTFd.plugins import ( register_plugin_asset, register_plugin_script, register_plugin_stylesheet, + register_admin_plugin_script, + register_admin_plugin_stylesheet, override_template, register_admin_plugin_menu_bar, get_admin_plugin_menu_bar, @@ -77,7 +79,7 @@ def test_admin_override_template(): def test_register_plugin_script(): - '''Test that register_plugin_script adds script paths to the core theme when used from a plugin''' + """Test that register_plugin_script adds script paths to the core theme when used from a plugin""" app = create_ctfd() with app.app_context(): register_plugin_script('/fake/script/path.js') @@ -91,7 +93,7 @@ def test_register_plugin_script(): def test_register_plugin_stylesheet(): - '''Test that register_plugin_stylesheet adds stylesheet paths to the core theme when used from a plugin''' + """Test that register_plugin_stylesheet adds stylesheet paths to the core theme when used from a plugin""" app = create_ctfd() with app.app_context(): register_plugin_script('/fake/stylesheet/path.css') @@ -104,6 +106,34 @@ def test_register_plugin_stylesheet(): destroy_ctfd(app) +def test_register_admin_plugin_script(): + """Test that register_admin_plugin_script adds script paths to the admin theme when used from a plugin""" + app = create_ctfd() + with app.app_context(): + register_admin_plugin_script('/fake/script/path.js') + register_admin_plugin_script('http://ctfd.io/fake/script/path.js') + with login_as_user(app, name="admin") as client: + r = client.get('/admin/statistics') + output = r.get_data(as_text=True) + assert '/fake/script/path.js' in output + assert 'http://ctfd.io/fake/script/path.js' in output + destroy_ctfd(app) + + +def test_register_admin_plugin_stylesheet(): + """Test that register_admin_plugin_stylesheet adds stylesheet paths to the admin theme when used from a plugin""" + app = create_ctfd() + with app.app_context(): + register_admin_plugin_stylesheet('/fake/stylesheet/path.css') + register_admin_plugin_stylesheet('http://ctfd.io/fake/stylesheet/path.css') + with login_as_user(app, name="admin") as client: + r = client.get('/admin/statistics') + output = r.get_data(as_text=True) + assert '/fake/stylesheet/path.css' in output + assert 'http://ctfd.io/fake/stylesheet/path.css' in output + destroy_ctfd(app) + + def test_register_admin_plugin_menu_bar(): """ Test that register_admin_plugin_menu_bar() properly inserts into HTML and get_admin_plugin_menu_bar()