mirror of
https://github.com/aljazceru/CTFd.git
synced 2026-02-06 06:44:37 +01:00
Add a rough implementation of improving the challenge preview
This commit is contained in:
@@ -71,6 +71,50 @@ def challenges_detail(challenge_id):
|
||||
)
|
||||
|
||||
|
||||
from CTFd.utils.security.signing import serialize
|
||||
from CTFd.utils.user import (
|
||||
get_current_team,
|
||||
get_current_user,
|
||||
)
|
||||
from CTFd.schemas.tags import TagSchema
|
||||
|
||||
@admin.route("/admin/challenges/preview/<int:challenge_id>")
|
||||
@admins_only
|
||||
def challenges_preview(challenge_id):
|
||||
challenge = Challenges.query.filter_by(id=challenge_id).first_or_404()
|
||||
chal_class = get_chal_class(challenge.type)
|
||||
user = get_current_user()
|
||||
team = get_current_team()
|
||||
|
||||
files = []
|
||||
for f in challenge.files:
|
||||
token = {
|
||||
"user_id": user.id,
|
||||
"team_id": team.id if team else None,
|
||||
"file_id": f.id,
|
||||
}
|
||||
files.append(
|
||||
url_for("views.files", path=f.location, token=serialize(token))
|
||||
)
|
||||
|
||||
tags = [
|
||||
tag["value"] for tag in TagSchema("user", many=True).dump(challenge.tags).data
|
||||
]
|
||||
|
||||
content = render_template(
|
||||
chal_class.templates["view"].lstrip("/"),
|
||||
solves=None,
|
||||
solved_by_me=False,
|
||||
files=files,
|
||||
tags=tags,
|
||||
hints=challenge.hints,
|
||||
max_attempts=challenge.max_attempts,
|
||||
attempts=0,
|
||||
challenge=challenge,
|
||||
)
|
||||
return render_template("admin/challenges/preview.html", content=content)
|
||||
|
||||
|
||||
@admin.route("/admin/challenges/new")
|
||||
@admins_only
|
||||
def challenges_new():
|
||||
|
||||
209
CTFd/themes/admin/templates/challenges/preview.html
Normal file
209
CTFd/themes/admin/templates/challenges/preview.html
Normal file
@@ -0,0 +1,209 @@
|
||||
{% extends "page.html" %}
|
||||
|
||||
{% block content %}
|
||||
{{ super() }}
|
||||
<h4 style="text-align: center">
|
||||
Preview may be slightly different from the user-facing implementation
|
||||
</h4>
|
||||
{% endblock %}
|
||||
|
||||
{% block scripts %}
|
||||
{% if "beta" in Configs.ctf_theme %}
|
||||
{{ Assets.js("assets/js/challenges.js") }}
|
||||
{% else %}
|
||||
<script type="module">
|
||||
const displayHint = data => {
|
||||
ezAlert({
|
||||
title: "Hint",
|
||||
body: data.html,
|
||||
button: "Got it!"
|
||||
});
|
||||
};
|
||||
|
||||
const loadHint = id => {
|
||||
CTFd.api.get_hint({ hintId: id, preview: true }).then(response => {
|
||||
if (response.data.content) {
|
||||
displayHint(response.data);
|
||||
return;
|
||||
}
|
||||
// displayUnlock(id);
|
||||
});
|
||||
};
|
||||
|
||||
function renderSubmissionResponse(response) {
|
||||
const result = response.data;
|
||||
|
||||
const result_message = $("#result-message");
|
||||
const result_notification = $("#result-notification");
|
||||
const answer_input = $("#challenge-input");
|
||||
result_notification.removeClass();
|
||||
result_message.text(result.message);
|
||||
|
||||
const next_btn = $(
|
||||
`<div class='col-md-12 pb-3'><button class='btn btn-info w-100'>Next Challenge</button></div>`
|
||||
).click(function () {
|
||||
$("#challenge-window").modal("toggle");
|
||||
setTimeout(function () {
|
||||
loadChal(CTFd._internal.challenge.data.next_id);
|
||||
}, 500);
|
||||
});
|
||||
|
||||
if (result.status === "authentication_required") {
|
||||
window.location =
|
||||
CTFd.config.urlRoot +
|
||||
"/login?next=" +
|
||||
CTFd.config.urlRoot +
|
||||
window.location.pathname +
|
||||
window.location.hash;
|
||||
return;
|
||||
} else if (result.status === "incorrect") {
|
||||
// Incorrect key
|
||||
result_notification.addClass(
|
||||
"alert alert-danger alert-dismissable text-center"
|
||||
);
|
||||
result_notification.slideDown();
|
||||
|
||||
answer_input.removeClass("correct");
|
||||
answer_input.addClass("wrong");
|
||||
setTimeout(function () {
|
||||
answer_input.removeClass("wrong");
|
||||
}, 3000);
|
||||
} else if (result.status === "correct") {
|
||||
// Challenge Solved
|
||||
result_notification.addClass(
|
||||
"alert alert-success alert-dismissable text-center"
|
||||
);
|
||||
result_notification.slideDown();
|
||||
|
||||
if (
|
||||
$(".challenge-solves")
|
||||
.text()
|
||||
.trim()
|
||||
) {
|
||||
// Only try to increment solves if the text isn't hidden
|
||||
$(".challenge-solves").text(
|
||||
parseInt(
|
||||
$(".challenge-solves")
|
||||
.text()
|
||||
.split(" ")[0]
|
||||
) +
|
||||
1 +
|
||||
" Solves"
|
||||
);
|
||||
}
|
||||
|
||||
answer_input.val("");
|
||||
answer_input.removeClass("wrong");
|
||||
answer_input.addClass("correct");
|
||||
|
||||
if (CTFd._internal.challenge.data.next_id) {
|
||||
$(".submit-row").html(next_btn);
|
||||
}
|
||||
} else if (result.status === "already_solved") {
|
||||
// Challenge already solved
|
||||
result_notification.addClass(
|
||||
"alert alert-info alert-dismissable text-center"
|
||||
);
|
||||
result_notification.slideDown();
|
||||
|
||||
answer_input.addClass("correct");
|
||||
|
||||
if (CTFd._internal.challenge.data.next_id) {
|
||||
$(".submit-row").html(next_btn);
|
||||
}
|
||||
} else if (result.status === "paused") {
|
||||
// CTF is paused
|
||||
result_notification.addClass(
|
||||
"alert alert-warning alert-dismissable text-center"
|
||||
);
|
||||
result_notification.slideDown();
|
||||
} else if (result.status === "ratelimited") {
|
||||
// Keys per minute too high
|
||||
result_notification.addClass(
|
||||
"alert alert-warning alert-dismissable text-center"
|
||||
);
|
||||
result_notification.slideDown();
|
||||
|
||||
answer_input.addClass("too-fast");
|
||||
setTimeout(function () {
|
||||
answer_input.removeClass("too-fast");
|
||||
}, 3000);
|
||||
}
|
||||
setTimeout(function () {
|
||||
$(".alert").slideUp();
|
||||
$("#challenge-submit").removeClass("disabled-button");
|
||||
$("#challenge-submit").prop("disabled", false);
|
||||
}, 3000);
|
||||
}
|
||||
|
||||
CTFd._internal.challenge = {};
|
||||
$.get(
|
||||
CTFd.config.urlRoot + "/api/v1/challenges/1",
|
||||
function (response) {
|
||||
// Preview should not show any solves
|
||||
var challenge_data = response.data;
|
||||
challenge_data["solves"] = null;
|
||||
|
||||
$.getScript(
|
||||
CTFd.config.urlRoot + challenge_data.type_data.scripts.view,
|
||||
function () {
|
||||
const challenge = CTFd._internal.challenge;
|
||||
|
||||
// Inject challenge data into the plugin
|
||||
challenge.data = response.data;
|
||||
challenge.preRender();
|
||||
|
||||
$("#challenge-input").addClass("form-control");
|
||||
$("#challenge-submit").addClass(
|
||||
"btn btn-md btn-outline-secondary float-right"
|
||||
);
|
||||
|
||||
$(".challenge-solves").hide();
|
||||
$(".nav-tabs a").click(function (e) {
|
||||
e.preventDefault();
|
||||
$(this).tab("show");
|
||||
});
|
||||
|
||||
// Handle modal toggling
|
||||
$("#challenge-window").on("hide.bs.modal", function (_event) {
|
||||
$("#challenge-input").removeClass("wrong");
|
||||
$("#challenge-input").removeClass("correct");
|
||||
$("#incorrect-key").slideUp();
|
||||
$("#correct-key").slideUp();
|
||||
$("#already-solved").slideUp();
|
||||
$("#too-fast").slideUp();
|
||||
});
|
||||
|
||||
$(".load-hint").on("click", function (_event) {
|
||||
loadHint($(this).data("hint-id"));
|
||||
});
|
||||
|
||||
$("#challenge-submit").click(function (e) {
|
||||
e.preventDefault();
|
||||
$("#challenge-submit").addClass("disabled-button");
|
||||
$("#challenge-submit").prop("disabled", true);
|
||||
CTFd._internal.challenge
|
||||
.submit(true)
|
||||
.then(renderSubmissionResponse);
|
||||
// Preview passed as true
|
||||
});
|
||||
|
||||
$("#challenge-input").keyup(function (event) {
|
||||
if (event.keyCode == 13) {
|
||||
$("#challenge-submit").click();
|
||||
}
|
||||
});
|
||||
|
||||
challenge.postRender();
|
||||
|
||||
$("pre code")
|
||||
.each(function (_idx) {
|
||||
hljs.highlightBlock(this);
|
||||
});
|
||||
}
|
||||
);
|
||||
}
|
||||
);
|
||||
</script>
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
Reference in New Issue
Block a user