Skip to content

Commit c5102b6

Browse files
fixed challenge type not changable bug in user challenges
1 parent 3c176a0 commit c5102b6

16 files changed

Lines changed: 232 additions & 401 deletions

CTFd/plugins/userchallenge/api_calls/challenges.py

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11

2+
from flask import abort, render_template, request, url_for
3+
from sqlalchemy.sql import and_
4+
25
from CTFd.cache import clear_challenges, clear_standings
3-
from CTFd.constants import config
46
from CTFd.models import Challenges, Hints, HintUnlocks, Solves, Submissions, db
57
from CTFd.plugins.challenges import get_chal_class
68
from CTFd.plugins.LuaUtils import run_after_route, run_before_route
@@ -14,6 +16,7 @@
1416
)
1517
from CTFd.schemas.challenges import ChallengeSchema
1618
from CTFd.schemas.tags import TagSchema
19+
from CTFd.utils import config
1720
from CTFd.utils.challenges import (
1821
get_solve_counts_for_challenges,
1922
get_solve_ids_for_user_id,
@@ -27,8 +30,6 @@
2730
from CTFd.utils.decorators import admins_only
2831
from CTFd.utils.security.signing import serialize
2932
from CTFd.utils.user import authed, get_current_team, get_current_user, is_admin
30-
from flask import abort, render_template, request, url_for
31-
from sqlalchemy.sql import and_
3233

3334

3435
def load(app):
@@ -96,7 +97,7 @@ def getChallenges():
9697
requirements = challenge.requirements.get("prerequisites", [])
9798
anonymize = challenge.requirements.get("anonymize")
9899
prereqs = set(requirements).intersection(all_challenge_ids)
99-
if user_solves >= prereqs or admin_view:
100+
if user_solves >= prereqs or is_admin():
100101
pass
101102
else:
102103
if anonymize:

CTFd/plugins/userchallenge/assets/js/userChallenge.js

Lines changed: 99 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,21 @@
1-
import "./main";
2-
import $ from "jquery";
3-
import "./compat/json";
1+
import { htmlEntities } from "@ctfdio/ctfd-js/utils/html";
42
import "bootstrap/js/dist/tab";
3+
import $ from "jquery";
4+
import Vue from "vue";
55
import CTFd from "./compat/CTFd";
6-
import { htmlEntities } from "@ctfdio/ctfd-js/utils/html";
7-
import { ezQuery, ezAlert, ezToast } from "./compat/ezq";
6+
import { ezAlert, ezQuery, ezToast } from "./compat/ezq";
87
import { default as helpers } from "./compat/helpers";
9-
import { bindMarkdownEditors } from "./styles";
10-
import Vue from "vue";
8+
import "./compat/json";
119
import CommentBox from "./components/comments/CommentBox.vue";
12-
import FlagList from "./components/flags/FlagList.vue";
13-
import Requirements from "./components/requirements/Requirements.vue";
14-
import TopicsList from "./components/topics/TopicsList.vue";
15-
import TagsList from "./components/tags/TagsList.vue";
1610
import ChallengeFilesList from "./components/files/ChallengeFilesList.vue";
11+
import FlagList from "./components/flags/FlagList.vue";
1712
import HintsList from "./components/hints/HintsList.vue";
1813
import NextChallenge from "./components/next/NextChallenge.vue";
14+
import Requirements from "./components/requirements/Requirements.vue";
15+
import TagsList from "./components/tags/TagsList.vue";
16+
import TopicsList from "./components/topics/TopicsList.vue";
17+
import "./main";
18+
import { bindMarkdownEditors } from "./styles";
1919

2020
function loadChalTemplate(challenge) {
2121
CTFd._internal.challenge = {};
@@ -251,6 +251,94 @@ $(() => {
251251
});
252252

253253
$("#challenge-create-options form").submit(handleChallengeOptions);
254+
255+
$(".chal-function")
256+
.change(function () {
257+
const selectedFunction = $(this).val();
258+
const initialFormGroup = $(".chal-initial").closest(".form-group");
259+
const decayFormGroup = $(".chal-decay").closest(".form-group");
260+
const minimumFormGroup = $(".chal-minimum").closest(".form-group");
261+
const initialInput = $(".chal-initial");
262+
const decayInput = $(".chal-decay");
263+
const minimumInput = $(".chal-minimum");
264+
const valueInput = $(".chal-value");
265+
266+
if (selectedFunction === "static") {
267+
// Save current values to data attributes before clearing them
268+
if (initialInput.val()) {
269+
initialInput.data("saved-value", initialInput.val());
270+
}
271+
if (decayInput.val()) {
272+
decayInput.data("saved-value", decayInput.val());
273+
}
274+
if (minimumInput.val()) {
275+
minimumInput.data("saved-value", minimumInput.val());
276+
}
277+
278+
// Clear the input values to prevent validation errors on hidden fields
279+
initialInput.val("");
280+
decayInput.val("");
281+
minimumInput.val("");
282+
283+
// Hide initial, decay, and minimum form groups for static function
284+
initialFormGroup.hide();
285+
decayFormGroup.hide();
286+
minimumFormGroup.hide();
287+
288+
// Remove name attributes so they won't be included in serializeJSON
289+
initialInput.removeAttr("name").data("original-name", "initial");
290+
decayInput.removeAttr("name").data("original-name", "decay");
291+
minimumInput.removeAttr("name").data("original-name", "minimum");
292+
293+
// Remove required attribute for static function
294+
initialInput.removeAttr("required");
295+
decayInput.removeAttr("required");
296+
minimumInput.removeAttr("required");
297+
298+
// Make value input enabled and required for static function
299+
valueInput.prop("disabled", false).prop("required", true);
300+
} else if (
301+
selectedFunction === "linear" ||
302+
selectedFunction === "logarithmic"
303+
) {
304+
// Show initial, decay, and minimum form groups for linear and logarithmic functions
305+
initialFormGroup.show();
306+
decayFormGroup.show();
307+
minimumFormGroup.show();
308+
309+
// Restore name attributes so they will be included in serializeJSON
310+
initialInput.attr(
311+
"name",
312+
initialInput.data("original-name") || "initial",
313+
);
314+
decayInput.attr("name", decayInput.data("original-name") || "decay");
315+
minimumInput.attr(
316+
"name",
317+
minimumInput.data("original-name") || "minimum",
318+
);
319+
320+
// Restore saved values from data attributes
321+
if (initialInput.data("saved-value")) {
322+
initialInput.val(initialInput.data("saved-value"));
323+
}
324+
if (decayInput.data("saved-value")) {
325+
decayInput.val(decayInput.data("saved-value"));
326+
}
327+
if (minimumInput.data("saved-value")) {
328+
minimumInput.val(minimumInput.data("saved-value"));
329+
}
330+
331+
// Add required attribute for dynamic functions
332+
initialInput.prop("required", true);
333+
decayInput.prop("required", true);
334+
minimumInput.prop("required", true);
335+
336+
// Make value input disabled and not required for dynamic functions
337+
valueInput.prop("disabled", true).prop("required", false);
338+
}
339+
})
340+
.trigger("change"); // Trigger change event on page load to set initial state
341+
254342

255343
// Load FlagList component
256344
if (document.querySelector("#challenge-flags")) {

CTFd/plugins/userchallenge/assets/js/userChallengeRead.js

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -252,6 +252,94 @@ $(() => {
252252

253253
$("#challenge-create-options form").submit(handleChallengeOptions);
254254

255+
$(".chal-function")
256+
.change(function () {
257+
const selectedFunction = $(this).val();
258+
const initialFormGroup = $(".chal-initial").closest(".form-group");
259+
const decayFormGroup = $(".chal-decay").closest(".form-group");
260+
const minimumFormGroup = $(".chal-minimum").closest(".form-group");
261+
const initialInput = $(".chal-initial");
262+
const decayInput = $(".chal-decay");
263+
const minimumInput = $(".chal-minimum");
264+
const valueInput = $(".chal-value");
265+
266+
if (selectedFunction === "static") {
267+
// Save current values to data attributes before clearing them
268+
if (initialInput.val()) {
269+
initialInput.data("saved-value", initialInput.val());
270+
}
271+
if (decayInput.val()) {
272+
decayInput.data("saved-value", decayInput.val());
273+
}
274+
if (minimumInput.val()) {
275+
minimumInput.data("saved-value", minimumInput.val());
276+
}
277+
278+
// Clear the input values to prevent validation errors on hidden fields
279+
initialInput.val("");
280+
decayInput.val("");
281+
minimumInput.val("");
282+
283+
// Hide initial, decay, and minimum form groups for static function
284+
initialFormGroup.hide();
285+
decayFormGroup.hide();
286+
minimumFormGroup.hide();
287+
288+
// Remove name attributes so they won't be included in serializeJSON
289+
initialInput.removeAttr("name").data("original-name", "initial");
290+
decayInput.removeAttr("name").data("original-name", "decay");
291+
minimumInput.removeAttr("name").data("original-name", "minimum");
292+
293+
// Remove required attribute for static function
294+
initialInput.removeAttr("required");
295+
decayInput.removeAttr("required");
296+
minimumInput.removeAttr("required");
297+
298+
// Make value input enabled and required for static function
299+
valueInput.prop("disabled", false).prop("required", true);
300+
} else if (
301+
selectedFunction === "linear" ||
302+
selectedFunction === "logarithmic"
303+
) {
304+
// Show initial, decay, and minimum form groups for linear and logarithmic functions
305+
initialFormGroup.show();
306+
decayFormGroup.show();
307+
minimumFormGroup.show();
308+
309+
// Restore name attributes so they will be included in serializeJSON
310+
initialInput.attr(
311+
"name",
312+
initialInput.data("original-name") || "initial",
313+
);
314+
decayInput.attr("name", decayInput.data("original-name") || "decay");
315+
minimumInput.attr(
316+
"name",
317+
minimumInput.data("original-name") || "minimum",
318+
);
319+
320+
// Restore saved values from data attributes
321+
if (initialInput.data("saved-value")) {
322+
initialInput.val(initialInput.data("saved-value"));
323+
}
324+
if (decayInput.data("saved-value")) {
325+
decayInput.val(decayInput.data("saved-value"));
326+
}
327+
if (minimumInput.data("saved-value")) {
328+
minimumInput.val(minimumInput.data("saved-value"));
329+
}
330+
331+
// Add required attribute for dynamic functions
332+
initialInput.prop("required", true);
333+
decayInput.prop("required", true);
334+
minimumInput.prop("required", true);
335+
336+
// Make value input disabled and not required for dynamic functions
337+
valueInput.prop("disabled", true).prop("required", false);
338+
}
339+
})
340+
.trigger("change"); // Trigger change event on page load to set initial state
341+
342+
255343
// Load FlagList component
256344
if (document.querySelector("#challenge-flags")) {
257345
const flagList = Vue.extend(FlagList);

0 commit comments

Comments
 (0)