mirror of
https://github.com/aljazceru/CTFd.git
synced 2025-12-17 22:14:25 +01:00
1329 theme settings (#1485)
* Adds `window.init.theme_settings` which is a JSON blob that's passed by CTFd and configurable from the Admin Panel * Adds `config.html` which should be a form which can be loaded into the Admin Panel and able to emit a JSON blob which can be used as `window.init.theme_settings`. * Closes #1329
This commit is contained in:
@@ -1,3 +1,5 @@
|
||||
import json
|
||||
|
||||
from CTFd.utils import get_config
|
||||
from CTFd.utils.helpers import markup
|
||||
|
||||
@@ -18,5 +20,9 @@ class _ConfigsWrapper:
|
||||
def theme_footer(self):
|
||||
return markup(get_config("theme_footer", default=""))
|
||||
|
||||
@property
|
||||
def theme_settings(self):
|
||||
return json.loads(get_config("theme_settings", default="null"))
|
||||
|
||||
|
||||
Configs = _ConfigsWrapper()
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
@import "~codemirror/lib/codemirror.css";
|
||||
.CodeMirror {
|
||||
font-size: 12px;
|
||||
border: 1px solid lightgray;
|
||||
}
|
||||
|
||||
@@ -251,6 +251,52 @@ $(() => {
|
||||
}
|
||||
);
|
||||
|
||||
const theme_settings_editor = CodeMirror.fromTextArea(
|
||||
document.getElementById("theme-settings"),
|
||||
{
|
||||
lineNumbers: true,
|
||||
lineWrapping: true,
|
||||
mode: { name: "javascript", json: true }
|
||||
}
|
||||
);
|
||||
|
||||
// Handle refreshing codemirror when switching tabs.
|
||||
// Better than the autorefresh approach b/c there's no flicker
|
||||
$("a[href='#theme']").on("shown.bs.tab", function(e) {
|
||||
theme_header_editor.refresh();
|
||||
theme_footer_editor.refresh();
|
||||
theme_settings_editor.refresh();
|
||||
});
|
||||
|
||||
$("#theme-settings-modal form").submit(function(e) {
|
||||
e.preventDefault();
|
||||
theme_settings_editor
|
||||
.getDoc()
|
||||
.setValue(JSON.stringify($(this).serializeJSON(), null, 2));
|
||||
$("#theme-settings-modal").modal("hide");
|
||||
});
|
||||
|
||||
$("#theme-settings-button").click(function() {
|
||||
let form = $("#theme-settings-modal form");
|
||||
let data = JSON.parse(theme_settings_editor.getValue());
|
||||
$.each(data, function(key, value) {
|
||||
var ctrl = form.find(`[name='${key}']`);
|
||||
switch (ctrl.prop("type")) {
|
||||
case "radio":
|
||||
case "checkbox":
|
||||
ctrl.each(function() {
|
||||
if ($(this).attr("value") == value) {
|
||||
$(this).attr("checked", value);
|
||||
}
|
||||
});
|
||||
break;
|
||||
default:
|
||||
ctrl.val(value);
|
||||
}
|
||||
});
|
||||
$("#theme-settings-modal").modal();
|
||||
});
|
||||
|
||||
insertTimezones($("#start-timezone"));
|
||||
insertTimezones($("#end-timezone"));
|
||||
insertTimezones($("#freeze-timezone"));
|
||||
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -17,6 +17,9 @@
|
||||
<li class="nav-item">
|
||||
<a class="nav-link rounded-0 active" href="#appearance" role="tab" data-toggle="tab">Appearance</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link rounded-0" href="#theme" role="tab" data-toggle="tab">Theme</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link rounded-0" href="#accounts" role="tab" data-toggle="tab">Accounts</a>
|
||||
</li>
|
||||
@@ -54,6 +57,8 @@
|
||||
<div class="tab-content">
|
||||
{% include "admin/configs/appearance.html" %}
|
||||
|
||||
{% include "admin/configs/theme.html" %}
|
||||
|
||||
{% include "admin/configs/accounts.html" %}
|
||||
|
||||
{% include "admin/configs/mlc.html" %}
|
||||
|
||||
@@ -76,56 +76,6 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="ctf_theme">
|
||||
Theme
|
||||
<small class="form-text text-muted">
|
||||
Switch themes to change CTFd's aesthetics
|
||||
</small>
|
||||
</label>
|
||||
<select class="form-control custom-select" id="ctf_theme" name="ctf_theme">
|
||||
<option>{{ ctf_theme }}</option>
|
||||
{% for theme in themes %}
|
||||
<option>{{ theme }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label>
|
||||
Theme Color
|
||||
<small class="form-text text-muted">
|
||||
Color used by theme to control aesthetics. Requires theme support.
|
||||
</small>
|
||||
</label>
|
||||
<div class="d-inline-block">
|
||||
<input type="color" id="config-color-picker">
|
||||
<button type="button" class="btn-sm btn-primary" id="config-color-update">Update CSS</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label>
|
||||
Theme Header
|
||||
<small class="form-text text-muted">
|
||||
Theme headers are inserted just before the <code></head></code> tag on all public facing pages.
|
||||
Requires theme support.
|
||||
</small>
|
||||
</label>
|
||||
<textarea class="form-control" id="theme-header" name="theme_header" rows="7">{{ theme_header or "" }}</textarea>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label>
|
||||
Theme Footer
|
||||
<small class="form-text text-muted">
|
||||
Theme footers are inserted just before the <code></body></code> tag on all public facing pages.
|
||||
Requires theme support.
|
||||
</small>
|
||||
</label>
|
||||
<textarea class="form-control" id="theme-footer" name="theme_footer" rows="7">{{ theme_footer or "" }}</textarea>
|
||||
</div>
|
||||
|
||||
<button type="submit" class="btn btn-md btn-primary float-right">Update</button>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
83
CTFd/themes/admin/templates/configs/theme.html
Normal file
83
CTFd/themes/admin/templates/configs/theme.html
Normal file
@@ -0,0 +1,83 @@
|
||||
<div role="tabpanel" class="tab-pane config-section" id="theme">
|
||||
<div class="modal fade" role="document" id="theme-settings-modal">
|
||||
<div class="modal-dialog modal-lg">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title">Theme Settings</h5>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
{% include "config.html" ignore missing %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<form method="POST" autocomplete="off" class="w-100">
|
||||
<div class="form-group">
|
||||
<label for="ctf_theme">
|
||||
Theme
|
||||
<small class="form-text text-muted">
|
||||
Switch themes to change CTFd's aesthetics
|
||||
</small>
|
||||
</label>
|
||||
<select class="form-control custom-select" id="ctf_theme" name="ctf_theme">
|
||||
<option>{{ ctf_theme }}</option>
|
||||
{% for theme in themes %}
|
||||
<option>{{ theme }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label>
|
||||
Theme Color
|
||||
<small class="form-text text-muted">
|
||||
Color used by theme to control aesthetics. Requires theme support.
|
||||
</small>
|
||||
</label>
|
||||
<div class="d-inline-block">
|
||||
<input type="color" id="config-color-picker">
|
||||
<button type="button" class="btn-sm btn-primary" id="config-color-update">Update CSS</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label>
|
||||
Theme Header
|
||||
<small class="form-text text-muted">
|
||||
Theme headers are inserted just before the <code></head></code> tag on all public facing pages.
|
||||
Requires theme support.
|
||||
</small>
|
||||
</label>
|
||||
<textarea class="form-control" id="theme-header" name="theme_header" rows="7">{{ theme_header or "" }}</textarea>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label>
|
||||
Theme Footer
|
||||
<small class="form-text text-muted">
|
||||
Theme footers are inserted just before the <code></body></code> tag on all public facing pages.
|
||||
Requires theme support.
|
||||
</small>
|
||||
</label>
|
||||
<textarea class="form-control" id="theme-footer" name="theme_footer" rows="7">{{ theme_footer or "" }}</textarea>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="ctf_theme">
|
||||
Theme Settings
|
||||
</label>
|
||||
<div class="d-block pb-2">
|
||||
<button type="button" class="btn-sm btn-primary" id="theme-settings-button">
|
||||
Settings Editor
|
||||
</button>
|
||||
</div>
|
||||
<textarea class="form-control" id="theme-settings" name="theme_settings" rows="7">{{ theme_settings or "" }}</textarea>
|
||||
<small class="form-text text-muted">
|
||||
Settings specific to the theme
|
||||
</small>
|
||||
</div>
|
||||
|
||||
<button type="submit" class="btn btn-md btn-primary float-right">Update</button>
|
||||
</form>
|
||||
</div>
|
||||
@@ -62,6 +62,26 @@ const displayChal = chal => {
|
||||
|
||||
$("#challenge-window").append(template.render(challenge_data));
|
||||
|
||||
let modal = $("#challenge-window").find(".modal-dialog");
|
||||
if (
|
||||
window.init.theme_settings &&
|
||||
window.init.theme_settings.challenge_window_size
|
||||
) {
|
||||
switch (window.init.theme_settings.challenge_window_size) {
|
||||
case "sm":
|
||||
modal.addClass("modal-sm");
|
||||
break;
|
||||
case "lg":
|
||||
modal.addClass("modal-lg");
|
||||
break;
|
||||
case "xl":
|
||||
modal.addClass("modal-xl");
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
$(".challenge-solves").click(function(event) {
|
||||
getSolves($("#challenge-id").val());
|
||||
});
|
||||
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -20,6 +20,7 @@
|
||||
'userId': {{ Session.id }},
|
||||
'start': {{ Configs.start | tojson }},
|
||||
'end': {{ Configs.end | tojson }},
|
||||
'theme_settings': {{ Configs.theme_settings | tojson }}
|
||||
}
|
||||
</script>
|
||||
{{ Configs.theme_header }}
|
||||
|
||||
12
CTFd/themes/core/templates/config.html
Normal file
12
CTFd/themes/core/templates/config.html
Normal file
@@ -0,0 +1,12 @@
|
||||
<form>
|
||||
<div class="form-group">
|
||||
<label>Challenge Window Size</label>
|
||||
<select class="form-control custom-select" name="challenge_window_size">
|
||||
<option value="sm">Small</option>
|
||||
<option value="norm">Normal</option>
|
||||
<option value="lg">Large</option>
|
||||
<option value="xl">Extra Large</option>
|
||||
</select>
|
||||
</div>
|
||||
<button type="submit" class="btn btn-primary float-right">Update</button>
|
||||
</form>
|
||||
Reference in New Issue
Block a user