diff --git a/CTFd/api/v1/hints.py b/CTFd/api/v1/hints.py
index 3a876316..10d39f91 100644
--- a/CTFd/api/v1/hints.py
+++ b/CTFd/api/v1/hints.py
@@ -120,6 +120,33 @@ class Hint(Resource):
user = get_current_user()
hint = Hints.query.filter_by(id=hint_id).first_or_404()
+ if hint.requirements:
+ requirements = hint.requirements.get("prerequisites", [])
+
+ # Get the IDs of all hints that the user has unlocked
+ all_unlocks = HintUnlocks.query.filter_by(account_id=user.account_id).all()
+ unlock_ids = {unlock.id for unlock in all_unlocks}
+
+ # Filter out hint IDs that don't exist
+ all_hint_ids = {h.id for h in Hints.query.with_entities(Hints.id).all()}
+ prereqs = set(requirements).intersection(all_hint_ids)
+
+ # If the user has the necessary unlocks or is admin we should allow them to view
+ if unlock_ids >= prereqs or is_admin():
+ pass
+ else:
+ return (
+ {
+ "success": False,
+ "errors": {
+ "requirements": [
+ "You must unlock other hints before accessing this hint"
+ ]
+ },
+ },
+ 403,
+ )
+
view = "unlocked"
if hint.cost:
view = "locked"
diff --git a/CTFd/themes/admin/assets/js/components/hints/HintCreationForm.vue b/CTFd/themes/admin/assets/js/components/hints/HintCreationForm.vue
index 5807f355..55f9319f 100644
--- a/CTFd/themes/admin/assets/js/components/hints/HintCreationForm.vue
+++ b/CTFd/themes/admin/assets/js/components/hints/HintCreationForm.vue
@@ -25,7 +25,7 @@
-
+
Hint
Markdown & HTML are supported
@@ -50,6 +50,31 @@
v-model.lazy="cost"
/>
+
+
@@ -74,11 +99,13 @@
export default {
name: "HintCreationForm",
props: {
- challenge_id: Number
+ challenge_id: Number,
+ hints: Array
},
data: function() {
return {
- cost: 0
+ cost: 0,
+ selectedHints: []
};
},
methods: {
@@ -89,11 +116,11 @@ export default {
return this.$refs.content.value;
},
submitHint: function() {
- console.log(this.co);
let params = {
challenge_id: this.$props.challenge_id,
content: this.getContent(),
- cost: this.getCost()
+ cost: this.getCost(),
+ requirements: { prerequisites: this.selectedHints }
};
CTFd.fetch("/api/v1/hints", {
method: "POST",
diff --git a/CTFd/themes/admin/assets/js/components/hints/HintEditForm.vue b/CTFd/themes/admin/assets/js/components/hints/HintEditForm.vue
index 781f5ee9..51d6cbb2 100644
--- a/CTFd/themes/admin/assets/js/components/hints/HintEditForm.vue
+++ b/CTFd/themes/admin/assets/js/components/hints/HintEditForm.vue
@@ -25,7 +25,7 @@
-
+
Hint
Markdown & HTML are supported
@@ -52,6 +52,31 @@
v-model.lazy="cost"
/>
+
+
@@ -78,14 +103,25 @@ import { bindMarkdownEditor } from "../../styles";
export default {
name: "HintEditForm",
props: {
- hint_id: Number
+ challenge_id: Number,
+ hint_id: Number,
+ hints: Array
},
data: function() {
return {
cost: 0,
- content: null
+ content: null,
+ selectedHints: []
};
},
+ computed: {
+ // Get all hints besides the current one
+ otherHints: function() {
+ return this.hints.filter(hint => {
+ return hint.id !== this.$props.hint_id;
+ });
+ }
+ },
watch: {
hint_id: {
immediate: true,
@@ -114,6 +150,7 @@ export default {
let hint = response.data;
this.cost = hint.cost;
this.content = hint.content;
+ this.selectedHints = hint.requirements?.prerequisites || [];
// Wait for Vue to update the DOM
this.$nextTick(() => {
// Wait a little longer because we need the modal to appear.
@@ -138,7 +175,8 @@ export default {
let params = {
challenge_id: this.$props.challenge_id,
content: this.getContent(),
- cost: this.getCost()
+ cost: this.getCost(),
+ requirements: { prerequisites: this.selectedHints }
};
CTFd.fetch(`/api/v1/hints/${this.$props.hint_id}`, {
diff --git a/CTFd/themes/admin/assets/js/components/hints/HintsList.vue b/CTFd/themes/admin/assets/js/components/hints/HintsList.vue
index 160b9c23..7ef19589 100644
--- a/CTFd/themes/admin/assets/js/components/hints/HintsList.vue
+++ b/CTFd/themes/admin/assets/js/components/hints/HintsList.vue
@@ -4,6 +4,7 @@
@@ -11,7 +12,9 @@
diff --git a/CTFd/themes/admin/static/js/components.dev.js b/CTFd/themes/admin/static/js/components.dev.js
index 05242d5a..17aa20c0 100644
--- a/CTFd/themes/admin/static/js/components.dev.js
+++ b/CTFd/themes/admin/static/js/components.dev.js
@@ -704,7 +704,7 @@ eval("\n\nObject.defineProperty(exports, \"__esModule\", {\n value: true\n});\n
/***/ (function(module, exports, __webpack_require__) {
;
-eval("\n\nObject.defineProperty(exports, \"__esModule\", {\n value: true\n});\nexports[\"default\"] = void 0;\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\nvar _default = {\n name: \"HintCreationForm\",\n props: {\n challenge_id: Number\n },\n data: function data() {\n return {\n cost: 0\n };\n },\n methods: {\n getCost: function getCost() {\n return this.cost || 0;\n },\n getContent: function getContent() {\n return this.$refs.content.value;\n },\n submitHint: function submitHint() {\n var _this = this;\n\n console.log(this.co);\n var params = {\n challenge_id: this.$props.challenge_id,\n content: this.getContent(),\n cost: this.getCost()\n };\n CTFd.fetch(\"/api/v1/hints\", {\n method: \"POST\",\n credentials: \"same-origin\",\n headers: {\n Accept: \"application/json\",\n \"Content-Type\": \"application/json\"\n },\n body: JSON.stringify(params)\n }).then(function (response) {\n return response.json();\n }).then(function (response) {\n if (response.success) {\n _this.$emit(\"refreshHints\", _this.$options.name);\n }\n });\n }\n }\n};\nexports[\"default\"] = _default;\n\n//# sourceURL=webpack:///./CTFd/themes/admin/assets/js/components/hints/HintCreationForm.vue?./node_modules/babel-loader/lib??ref--0!./node_modules/vue-loader/lib??vue-loader-options");
+eval("\n\nObject.defineProperty(exports, \"__esModule\", {\n value: true\n});\nexports[\"default\"] = void 0;\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\nvar _default = {\n name: \"HintCreationForm\",\n props: {\n challenge_id: Number,\n hints: Array\n },\n data: function data() {\n return {\n cost: 0,\n selectedHints: []\n };\n },\n methods: {\n getCost: function getCost() {\n return this.cost || 0;\n },\n getContent: function getContent() {\n return this.$refs.content.value;\n },\n submitHint: function submitHint() {\n var _this = this;\n\n var params = {\n challenge_id: this.$props.challenge_id,\n content: this.getContent(),\n cost: this.getCost(),\n requirements: {\n prerequisites: this.selectedHints\n }\n };\n CTFd.fetch(\"/api/v1/hints\", {\n method: \"POST\",\n credentials: \"same-origin\",\n headers: {\n Accept: \"application/json\",\n \"Content-Type\": \"application/json\"\n },\n body: JSON.stringify(params)\n }).then(function (response) {\n return response.json();\n }).then(function (response) {\n if (response.success) {\n _this.$emit(\"refreshHints\", _this.$options.name);\n }\n });\n }\n }\n};\nexports[\"default\"] = _default;\n\n//# sourceURL=webpack:///./CTFd/themes/admin/assets/js/components/hints/HintCreationForm.vue?./node_modules/babel-loader/lib??ref--0!./node_modules/vue-loader/lib??vue-loader-options");
/***/ }),
@@ -716,7 +716,7 @@ eval("\n\nObject.defineProperty(exports, \"__esModule\", {\n value: true\n});\n
/***/ (function(module, exports, __webpack_require__) {
;
-eval("\n\nObject.defineProperty(exports, \"__esModule\", {\n value: true\n});\nexports[\"default\"] = void 0;\n\nvar _CTFd = _interopRequireDefault(__webpack_require__(/*! core/CTFd */ \"./CTFd/themes/core/assets/js/CTFd.js\"));\n\nvar _styles = __webpack_require__(/*! ../../styles */ \"./CTFd/themes/admin/assets/js/styles.js\");\n\nfunction _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { \"default\": obj }; }\n\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\nvar _default = {\n name: \"HintEditForm\",\n props: {\n hint_id: Number\n },\n data: function data() {\n return {\n cost: 0,\n content: null\n };\n },\n watch: {\n hint_id: {\n immediate: true,\n handler: function handler(val, oldVal) {\n if (val !== null) {\n this.loadHint();\n }\n }\n }\n },\n methods: {\n loadHint: function loadHint() {\n var _this = this;\n\n _CTFd[\"default\"].fetch(\"/api/v1/hints/\".concat(this.$props.hint_id, \"?preview=true\"), {\n method: \"GET\",\n credentials: \"same-origin\",\n headers: {\n Accept: \"application/json\",\n \"Content-Type\": \"application/json\"\n }\n }).then(function (response) {\n return response.json();\n }).then(function (response) {\n if (response.success) {\n var hint = response.data;\n _this.cost = hint.cost;\n _this.content = hint.content; // Wait for Vue to update the DOM\n\n _this.$nextTick(function () {\n // Wait a little longer because we need the modal to appear.\n // Kinda nasty but not really avoidable without polling the DOM via CodeMirror\n setTimeout(function () {\n var editor = _this.$refs.content;\n (0, _styles.bindMarkdownEditor)(editor);\n editor.mde.codemirror.getDoc().setValue(editor.value);\n editor.mde.codemirror.refresh();\n }, 100);\n });\n }\n });\n },\n getCost: function getCost() {\n return this.cost || 0;\n },\n getContent: function getContent() {\n return this.$refs.content.value;\n },\n updateHint: function updateHint() {\n var _this2 = this;\n\n var params = {\n challenge_id: this.$props.challenge_id,\n content: this.getContent(),\n cost: this.getCost()\n };\n\n _CTFd[\"default\"].fetch(\"/api/v1/hints/\".concat(this.$props.hint_id), {\n method: \"PATCH\",\n credentials: \"same-origin\",\n headers: {\n Accept: \"application/json\",\n \"Content-Type\": \"application/json\"\n },\n body: JSON.stringify(params)\n }).then(function (response) {\n return response.json();\n }).then(function (response) {\n if (response.success) {\n _this2.$emit(\"refreshHints\", _this2.$options.name);\n }\n });\n }\n },\n mounted: function mounted() {\n if (this.hint_id) {\n this.loadHint();\n }\n },\n created: function created() {\n if (this.hint_id) {\n this.loadHint();\n }\n }\n};\nexports[\"default\"] = _default;\n\n//# sourceURL=webpack:///./CTFd/themes/admin/assets/js/components/hints/HintEditForm.vue?./node_modules/babel-loader/lib??ref--0!./node_modules/vue-loader/lib??vue-loader-options");
+eval("\n\nObject.defineProperty(exports, \"__esModule\", {\n value: true\n});\nexports[\"default\"] = void 0;\n\nvar _CTFd = _interopRequireDefault(__webpack_require__(/*! core/CTFd */ \"./CTFd/themes/core/assets/js/CTFd.js\"));\n\nvar _styles = __webpack_require__(/*! ../../styles */ \"./CTFd/themes/admin/assets/js/styles.js\");\n\nfunction _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { \"default\": obj }; }\n\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\nvar _default = {\n name: \"HintEditForm\",\n props: {\n challenge_id: Number,\n hint_id: Number,\n hints: Array\n },\n data: function data() {\n return {\n cost: 0,\n content: null,\n selectedHints: []\n };\n },\n computed: {\n // Get all hints besides the current one\n otherHints: function otherHints() {\n var _this = this;\n\n return this.hints.filter(function (hint) {\n return hint.id !== _this.$props.hint_id;\n });\n }\n },\n watch: {\n hint_id: {\n immediate: true,\n handler: function handler(val, oldVal) {\n if (val !== null) {\n this.loadHint();\n }\n }\n }\n },\n methods: {\n loadHint: function loadHint() {\n var _this2 = this;\n\n _CTFd[\"default\"].fetch(\"/api/v1/hints/\".concat(this.$props.hint_id, \"?preview=true\"), {\n method: \"GET\",\n credentials: \"same-origin\",\n headers: {\n Accept: \"application/json\",\n \"Content-Type\": \"application/json\"\n }\n }).then(function (response) {\n return response.json();\n }).then(function (response) {\n if (response.success) {\n var _hint$requirements;\n\n var hint = response.data;\n _this2.cost = hint.cost;\n _this2.content = hint.content;\n _this2.selectedHints = ((_hint$requirements = hint.requirements) === null || _hint$requirements === void 0 ? void 0 : _hint$requirements.prerequisites) || []; // Wait for Vue to update the DOM\n\n _this2.$nextTick(function () {\n // Wait a little longer because we need the modal to appear.\n // Kinda nasty but not really avoidable without polling the DOM via CodeMirror\n setTimeout(function () {\n var editor = _this2.$refs.content;\n (0, _styles.bindMarkdownEditor)(editor);\n editor.mde.codemirror.getDoc().setValue(editor.value);\n editor.mde.codemirror.refresh();\n }, 100);\n });\n }\n });\n },\n getCost: function getCost() {\n return this.cost || 0;\n },\n getContent: function getContent() {\n return this.$refs.content.value;\n },\n updateHint: function updateHint() {\n var _this3 = this;\n\n var params = {\n challenge_id: this.$props.challenge_id,\n content: this.getContent(),\n cost: this.getCost(),\n requirements: {\n prerequisites: this.selectedHints\n }\n };\n\n _CTFd[\"default\"].fetch(\"/api/v1/hints/\".concat(this.$props.hint_id), {\n method: \"PATCH\",\n credentials: \"same-origin\",\n headers: {\n Accept: \"application/json\",\n \"Content-Type\": \"application/json\"\n },\n body: JSON.stringify(params)\n }).then(function (response) {\n return response.json();\n }).then(function (response) {\n if (response.success) {\n _this3.$emit(\"refreshHints\", _this3.$options.name);\n }\n });\n }\n },\n mounted: function mounted() {\n if (this.hint_id) {\n this.loadHint();\n }\n },\n created: function created() {\n if (this.hint_id) {\n this.loadHint();\n }\n }\n};\nexports[\"default\"] = _default;\n\n//# sourceURL=webpack:///./CTFd/themes/admin/assets/js/components/hints/HintEditForm.vue?./node_modules/babel-loader/lib??ref--0!./node_modules/vue-loader/lib??vue-loader-options");
/***/ }),
@@ -728,7 +728,7 @@ eval("\n\nObject.defineProperty(exports, \"__esModule\", {\n value: true\n});\n
/***/ (function(module, exports, __webpack_require__) {
;
-eval("\n\nObject.defineProperty(exports, \"__esModule\", {\n value: true\n});\nexports[\"default\"] = void 0;\n\nvar _ezq = __webpack_require__(/*! core/ezq */ \"./CTFd/themes/core/assets/js/ezq.js\");\n\nvar _CTFd = _interopRequireDefault(__webpack_require__(/*! core/CTFd */ \"./CTFd/themes/core/assets/js/CTFd.js\"));\n\nvar _HintCreationForm = _interopRequireDefault(__webpack_require__(/*! ./HintCreationForm.vue */ \"./CTFd/themes/admin/assets/js/components/hints/HintCreationForm.vue\"));\n\nvar _HintEditForm = _interopRequireDefault(__webpack_require__(/*! ./HintEditForm.vue */ \"./CTFd/themes/admin/assets/js/components/hints/HintEditForm.vue\"));\n\nfunction _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { \"default\": obj }; }\n\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\nvar _default = {\n components: {\n HintCreationForm: _HintCreationForm[\"default\"],\n HintEditForm: _HintEditForm[\"default\"]\n },\n props: {\n challenge_id: Number\n },\n data: function data() {\n return {\n hints: [],\n editing_hint_id: null\n };\n },\n methods: {\n loadHints: function loadHints() {\n var _this = this;\n\n _CTFd[\"default\"].fetch(\"/api/v1/challenges/\".concat(this.$props.challenge_id, \"/hints\"), {\n method: \"GET\",\n credentials: \"same-origin\",\n headers: {\n Accept: \"application/json\",\n \"Content-Type\": \"application/json\"\n }\n }).then(function (response) {\n return response.json();\n }).then(function (response) {\n if (response.success) {\n _this.hints = response.data;\n }\n });\n },\n addHint: function addHint() {\n var modal = this.$refs.HintCreationForm.$el;\n $(modal).modal();\n },\n editHint: function editHint(hintId) {\n this.editing_hint_id = hintId;\n var modal = this.$refs.HintEditForm.$el;\n $(modal).modal();\n },\n refreshHints: function refreshHints(caller) {\n this.loadHints();\n var modal;\n\n switch (caller) {\n case \"HintCreationForm\":\n modal = this.$refs.HintCreationForm.$el;\n console.log(modal);\n $(modal).modal(\"hide\");\n break;\n\n case \"HintEditForm\":\n modal = this.$refs.HintEditForm.$el;\n $(modal).modal(\"hide\");\n break;\n\n default:\n break;\n }\n },\n deleteHint: function deleteHint(hintId) {\n var _this2 = this;\n\n (0, _ezq.ezQuery)({\n title: \"Delete Hint\",\n body: \"Are you sure you want to delete this hint?\",\n success: function success() {\n _CTFd[\"default\"].fetch(\"/api/v1/hints/\".concat(hintId), {\n method: \"DELETE\"\n }).then(function (response) {\n return response.json();\n }).then(function (data) {\n if (data.success) {\n _this2.loadHints();\n }\n });\n }\n });\n }\n },\n created: function created() {\n this.loadHints();\n }\n};\nexports[\"default\"] = _default;\n\n//# sourceURL=webpack:///./CTFd/themes/admin/assets/js/components/hints/HintsList.vue?./node_modules/babel-loader/lib??ref--0!./node_modules/vue-loader/lib??vue-loader-options");
+eval("\n\nObject.defineProperty(exports, \"__esModule\", {\n value: true\n});\nexports[\"default\"] = void 0;\n\nvar _ezq = __webpack_require__(/*! core/ezq */ \"./CTFd/themes/core/assets/js/ezq.js\");\n\nvar _CTFd = _interopRequireDefault(__webpack_require__(/*! core/CTFd */ \"./CTFd/themes/core/assets/js/CTFd.js\"));\n\nvar _HintCreationForm = _interopRequireDefault(__webpack_require__(/*! ./HintCreationForm.vue */ \"./CTFd/themes/admin/assets/js/components/hints/HintCreationForm.vue\"));\n\nvar _HintEditForm = _interopRequireDefault(__webpack_require__(/*! ./HintEditForm.vue */ \"./CTFd/themes/admin/assets/js/components/hints/HintEditForm.vue\"));\n\nfunction _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { \"default\": obj }; }\n\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\nvar _default = {\n components: {\n HintCreationForm: _HintCreationForm[\"default\"],\n HintEditForm: _HintEditForm[\"default\"]\n },\n props: {\n challenge_id: Number\n },\n data: function data() {\n return {\n hints: [],\n editing_hint_id: null\n };\n },\n methods: {\n loadHints: function loadHints() {\n var _this = this;\n\n _CTFd[\"default\"].fetch(\"/api/v1/challenges/\".concat(this.$props.challenge_id, \"/hints\"), {\n method: \"GET\",\n credentials: \"same-origin\",\n headers: {\n Accept: \"application/json\",\n \"Content-Type\": \"application/json\"\n }\n }).then(function (response) {\n return response.json();\n }).then(function (response) {\n if (response.success) {\n _this.hints = response.data;\n }\n });\n },\n addHint: function addHint() {\n var modal = this.$refs.HintCreationForm.$el;\n $(modal).modal();\n },\n editHint: function editHint(hintId) {\n this.editing_hint_id = hintId;\n var modal = this.$refs.HintEditForm.$el;\n $(modal).modal();\n },\n refreshHints: function refreshHints(caller) {\n this.loadHints();\n var modal;\n\n switch (caller) {\n case \"HintCreationForm\":\n modal = this.$refs.HintCreationForm.$el;\n console.log(modal);\n $(modal).modal(\"hide\");\n break;\n\n case \"HintEditForm\":\n modal = this.$refs.HintEditForm.$el;\n $(modal).modal(\"hide\");\n break;\n\n default:\n break;\n }\n },\n deleteHint: function deleteHint(hintId) {\n var _this2 = this;\n\n (0, _ezq.ezQuery)({\n title: \"Delete Hint\",\n body: \"Are you sure you want to delete this hint?\",\n success: function success() {\n _CTFd[\"default\"].fetch(\"/api/v1/hints/\".concat(hintId), {\n method: \"DELETE\"\n }).then(function (response) {\n return response.json();\n }).then(function (data) {\n if (data.success) {\n _this2.loadHints();\n }\n });\n }\n });\n }\n },\n created: function created() {\n this.loadHints();\n }\n};\nexports[\"default\"] = _default;\n\n//# sourceURL=webpack:///./CTFd/themes/admin/assets/js/components/hints/HintsList.vue?./node_modules/babel-loader/lib??ref--0!./node_modules/vue-loader/lib??vue-loader-options");
/***/ }),
@@ -918,7 +918,7 @@ eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) *
/***/ (function(module, __webpack_exports__, __webpack_require__) {
;
-eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"render\", function() { return render; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"staticRenderFns\", function() { return staticRenderFns; });\nvar render = function() {\n var _vm = this\n var _h = _vm.$createElement\n var _c = _vm._self._c || _h\n return _c(\"div\", { staticClass: \"modal fade\", attrs: { tabindex: \"-1\" } }, [\n _c(\"div\", { staticClass: \"modal-dialog\" }, [\n _c(\"div\", { staticClass: \"modal-content\" }, [\n _vm._m(0),\n _vm._v(\" \"),\n _c(\n \"form\",\n {\n attrs: { method: \"POST\" },\n on: {\n submit: function($event) {\n $event.preventDefault()\n return _vm.submitHint($event)\n }\n }\n },\n [\n _c(\"div\", { staticClass: \"modal-body\" }, [\n _c(\"div\", { staticClass: \"container\" }, [\n _c(\"div\", { staticClass: \"row\" }, [\n _c(\"div\", { staticClass: \"col-md-12\" }, [\n _c(\"div\", { staticClass: \"form-group\" }, [\n _vm._m(1),\n _vm._v(\" \"),\n _c(\"textarea\", {\n ref: \"content\",\n staticClass: \"form-control markdown\",\n attrs: { type: \"text\", name: \"content\", rows: \"7\" }\n })\n ]),\n _vm._v(\" \"),\n _c(\"div\", { staticClass: \"form-group\" }, [\n _vm._m(2),\n _vm._v(\" \"),\n _c(\"input\", {\n directives: [\n {\n name: \"model\",\n rawName: \"v-model.lazy\",\n value: _vm.cost,\n expression: \"cost\",\n modifiers: { lazy: true }\n }\n ],\n staticClass: \"form-control\",\n attrs: { type: \"number\", name: \"cost\" },\n domProps: { value: _vm.cost },\n on: {\n change: function($event) {\n _vm.cost = $event.target.value\n }\n }\n })\n ]),\n _vm._v(\" \"),\n _c(\"input\", {\n attrs: {\n type: \"hidden\",\n id: \"hint-id-for-hint\",\n name: \"id\"\n }\n })\n ])\n ])\n ])\n ]),\n _vm._v(\" \"),\n _vm._m(3)\n ]\n )\n ])\n ])\n ])\n}\nvar staticRenderFns = [\n function() {\n var _vm = this\n var _h = _vm.$createElement\n var _c = _vm._self._c || _h\n return _c(\"div\", { staticClass: \"modal-header text-center\" }, [\n _c(\"div\", { staticClass: \"container\" }, [\n _c(\"div\", { staticClass: \"row\" }, [\n _c(\"div\", { staticClass: \"col-md-12\" }, [_c(\"h3\", [_vm._v(\"Hint\")])])\n ])\n ]),\n _vm._v(\" \"),\n _c(\n \"button\",\n {\n staticClass: \"close\",\n attrs: {\n type: \"button\",\n \"data-dismiss\": \"modal\",\n \"aria-label\": \"Close\"\n }\n },\n [_c(\"span\", { attrs: { \"aria-hidden\": \"true\" } }, [_vm._v(\"×\")])]\n )\n ])\n },\n function() {\n var _vm = this\n var _h = _vm.$createElement\n var _c = _vm._self._c || _h\n return _c(\"label\", { staticClass: \"text-muted\" }, [\n _vm._v(\"\\n Hint\"),\n _c(\"br\"),\n _vm._v(\" \"),\n _c(\"small\", [_vm._v(\"Markdown & HTML are supported\")])\n ])\n },\n function() {\n var _vm = this\n var _h = _vm.$createElement\n var _c = _vm._self._c || _h\n return _c(\"label\", [\n _vm._v(\"\\n Cost\"),\n _c(\"br\"),\n _vm._v(\" \"),\n _c(\"small\", [_vm._v(\"How many points it costs to see your hint.\")])\n ])\n },\n function() {\n var _vm = this\n var _h = _vm.$createElement\n var _c = _vm._self._c || _h\n return _c(\"div\", { staticClass: \"modal-footer\" }, [\n _c(\"div\", { staticClass: \"container\" }, [\n _c(\"div\", { staticClass: \"row\" }, [\n _c(\"div\", { staticClass: \"col-md-12\" }, [\n _c(\"button\", { staticClass: \"btn btn-primary float-right\" }, [\n _vm._v(\"Submit\")\n ])\n ])\n ])\n ])\n ])\n }\n]\nrender._withStripped = true\n\n\n\n//# sourceURL=webpack:///./CTFd/themes/admin/assets/js/components/hints/HintCreationForm.vue?./node_modules/vue-loader/lib/loaders/templateLoader.js??vue-loader-options!./node_modules/vue-loader/lib??vue-loader-options");
+eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"render\", function() { return render; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"staticRenderFns\", function() { return staticRenderFns; });\nvar render = function() {\n var _vm = this\n var _h = _vm.$createElement\n var _c = _vm._self._c || _h\n return _c(\"div\", { staticClass: \"modal fade\", attrs: { tabindex: \"-1\" } }, [\n _c(\"div\", { staticClass: \"modal-dialog\" }, [\n _c(\"div\", { staticClass: \"modal-content\" }, [\n _vm._m(0),\n _vm._v(\" \"),\n _c(\n \"form\",\n {\n attrs: { method: \"POST\" },\n on: {\n submit: function($event) {\n $event.preventDefault()\n return _vm.submitHint($event)\n }\n }\n },\n [\n _c(\"div\", { staticClass: \"modal-body\" }, [\n _c(\"div\", { staticClass: \"container\" }, [\n _c(\"div\", { staticClass: \"row\" }, [\n _c(\"div\", { staticClass: \"col-md-12\" }, [\n _c(\"div\", { staticClass: \"form-group\" }, [\n _vm._m(1),\n _vm._v(\" \"),\n _c(\"textarea\", {\n ref: \"content\",\n staticClass: \"form-control markdown\",\n attrs: { type: \"text\", name: \"content\", rows: \"7\" }\n })\n ]),\n _vm._v(\" \"),\n _c(\"div\", { staticClass: \"form-group\" }, [\n _vm._m(2),\n _vm._v(\" \"),\n _c(\"input\", {\n directives: [\n {\n name: \"model\",\n rawName: \"v-model.lazy\",\n value: _vm.cost,\n expression: \"cost\",\n modifiers: { lazy: true }\n }\n ],\n staticClass: \"form-control\",\n attrs: { type: \"number\", name: \"cost\" },\n domProps: { value: _vm.cost },\n on: {\n change: function($event) {\n _vm.cost = $event.target.value\n }\n }\n })\n ]),\n _vm._v(\" \"),\n _c(\n \"div\",\n { staticClass: \"form-group\" },\n [\n _vm._m(3),\n _vm._v(\" \"),\n _vm._l(_vm.hints, function(hint) {\n return _c(\n \"div\",\n { key: hint.id, staticClass: \"form-check\" },\n [\n _c(\n \"label\",\n {\n staticClass: \"form-check-label cursor-pointer\"\n },\n [\n _c(\"input\", {\n directives: [\n {\n name: \"model\",\n rawName: \"v-model\",\n value: _vm.selectedHints,\n expression: \"selectedHints\"\n }\n ],\n staticClass: \"form-check-input\",\n attrs: { type: \"checkbox\" },\n domProps: {\n value: hint.id,\n checked: Array.isArray(_vm.selectedHints)\n ? _vm._i(_vm.selectedHints, hint.id) >\n -1\n : _vm.selectedHints\n },\n on: {\n change: function($event) {\n var $$a = _vm.selectedHints,\n $$el = $event.target,\n $$c = $$el.checked ? true : false\n if (Array.isArray($$a)) {\n var $$v = hint.id,\n $$i = _vm._i($$a, $$v)\n if ($$el.checked) {\n $$i < 0 &&\n (_vm.selectedHints = $$a.concat([\n $$v\n ]))\n } else {\n $$i > -1 &&\n (_vm.selectedHints = $$a\n .slice(0, $$i)\n .concat($$a.slice($$i + 1)))\n }\n } else {\n _vm.selectedHints = $$c\n }\n }\n }\n }),\n _vm._v(\n \"\\n \" +\n _vm._s(hint.cost) +\n \" - \" +\n _vm._s(hint.id) +\n \"\\n \"\n )\n ]\n )\n ]\n )\n })\n ],\n 2\n ),\n _vm._v(\" \"),\n _c(\"input\", {\n attrs: {\n type: \"hidden\",\n id: \"hint-id-for-hint\",\n name: \"id\"\n }\n })\n ])\n ])\n ])\n ]),\n _vm._v(\" \"),\n _vm._m(4)\n ]\n )\n ])\n ])\n ])\n}\nvar staticRenderFns = [\n function() {\n var _vm = this\n var _h = _vm.$createElement\n var _c = _vm._self._c || _h\n return _c(\"div\", { staticClass: \"modal-header text-center\" }, [\n _c(\"div\", { staticClass: \"container\" }, [\n _c(\"div\", { staticClass: \"row\" }, [\n _c(\"div\", { staticClass: \"col-md-12\" }, [_c(\"h3\", [_vm._v(\"Hint\")])])\n ])\n ]),\n _vm._v(\" \"),\n _c(\n \"button\",\n {\n staticClass: \"close\",\n attrs: {\n type: \"button\",\n \"data-dismiss\": \"modal\",\n \"aria-label\": \"Close\"\n }\n },\n [_c(\"span\", { attrs: { \"aria-hidden\": \"true\" } }, [_vm._v(\"×\")])]\n )\n ])\n },\n function() {\n var _vm = this\n var _h = _vm.$createElement\n var _c = _vm._self._c || _h\n return _c(\"label\", [\n _vm._v(\"\\n Hint\"),\n _c(\"br\"),\n _vm._v(\" \"),\n _c(\"small\", [_vm._v(\"Markdown & HTML are supported\")])\n ])\n },\n function() {\n var _vm = this\n var _h = _vm.$createElement\n var _c = _vm._self._c || _h\n return _c(\"label\", [\n _vm._v(\"\\n Cost\"),\n _c(\"br\"),\n _vm._v(\" \"),\n _c(\"small\", [_vm._v(\"How many points it costs to see your hint.\")])\n ])\n },\n function() {\n var _vm = this\n var _h = _vm.$createElement\n var _c = _vm._self._c || _h\n return _c(\"label\", [\n _vm._v(\"\\n Requirements\"),\n _c(\"br\"),\n _vm._v(\" \"),\n _c(\"small\", [\n _vm._v(\n \"Hints that must be unlocked before unlocking this\\n hint\"\n )\n ])\n ])\n },\n function() {\n var _vm = this\n var _h = _vm.$createElement\n var _c = _vm._self._c || _h\n return _c(\"div\", { staticClass: \"modal-footer\" }, [\n _c(\"div\", { staticClass: \"container\" }, [\n _c(\"div\", { staticClass: \"row\" }, [\n _c(\"div\", { staticClass: \"col-md-12\" }, [\n _c(\"button\", { staticClass: \"btn btn-primary float-right\" }, [\n _vm._v(\"Submit\")\n ])\n ])\n ])\n ])\n ])\n }\n]\nrender._withStripped = true\n\n\n\n//# sourceURL=webpack:///./CTFd/themes/admin/assets/js/components/hints/HintCreationForm.vue?./node_modules/vue-loader/lib/loaders/templateLoader.js??vue-loader-options!./node_modules/vue-loader/lib??vue-loader-options");
/***/ }),
@@ -930,7 +930,7 @@ eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) *
/***/ (function(module, __webpack_exports__, __webpack_require__) {
;
-eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"render\", function() { return render; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"staticRenderFns\", function() { return staticRenderFns; });\nvar render = function() {\n var _vm = this\n var _h = _vm.$createElement\n var _c = _vm._self._c || _h\n return _c(\"div\", { staticClass: \"modal fade\", attrs: { tabindex: \"-1\" } }, [\n _c(\"div\", { staticClass: \"modal-dialog\" }, [\n _c(\"div\", { staticClass: \"modal-content\" }, [\n _vm._m(0),\n _vm._v(\" \"),\n _c(\n \"form\",\n {\n attrs: { method: \"POST\" },\n on: {\n submit: function($event) {\n $event.preventDefault()\n return _vm.updateHint($event)\n }\n }\n },\n [\n _c(\"div\", { staticClass: \"modal-body\" }, [\n _c(\"div\", { staticClass: \"container\" }, [\n _c(\"div\", { staticClass: \"row\" }, [\n _c(\"div\", { staticClass: \"col-md-12\" }, [\n _c(\"div\", { staticClass: \"form-group\" }, [\n _vm._m(1),\n _vm._v(\" \"),\n _c(\"textarea\", {\n ref: \"content\",\n staticClass: \"form-control\",\n attrs: { type: \"text\", name: \"content\", rows: \"7\" },\n domProps: { value: this.content }\n })\n ]),\n _vm._v(\" \"),\n _c(\"div\", { staticClass: \"form-group\" }, [\n _vm._m(2),\n _vm._v(\" \"),\n _c(\"input\", {\n directives: [\n {\n name: \"model\",\n rawName: \"v-model.lazy\",\n value: _vm.cost,\n expression: \"cost\",\n modifiers: { lazy: true }\n }\n ],\n staticClass: \"form-control\",\n attrs: { type: \"number\", name: \"cost\" },\n domProps: { value: _vm.cost },\n on: {\n change: function($event) {\n _vm.cost = $event.target.value\n }\n }\n })\n ])\n ])\n ])\n ])\n ]),\n _vm._v(\" \"),\n _vm._m(3)\n ]\n )\n ])\n ])\n ])\n}\nvar staticRenderFns = [\n function() {\n var _vm = this\n var _h = _vm.$createElement\n var _c = _vm._self._c || _h\n return _c(\"div\", { staticClass: \"modal-header text-center\" }, [\n _c(\"div\", { staticClass: \"container\" }, [\n _c(\"div\", { staticClass: \"row\" }, [\n _c(\"div\", { staticClass: \"col-md-12\" }, [_c(\"h3\", [_vm._v(\"Hint\")])])\n ])\n ]),\n _vm._v(\" \"),\n _c(\n \"button\",\n {\n staticClass: \"close\",\n attrs: {\n type: \"button\",\n \"data-dismiss\": \"modal\",\n \"aria-label\": \"Close\"\n }\n },\n [_c(\"span\", { attrs: { \"aria-hidden\": \"true\" } }, [_vm._v(\"×\")])]\n )\n ])\n },\n function() {\n var _vm = this\n var _h = _vm.$createElement\n var _c = _vm._self._c || _h\n return _c(\"label\", { staticClass: \"text-muted\" }, [\n _vm._v(\"\\n Hint\"),\n _c(\"br\"),\n _vm._v(\" \"),\n _c(\"small\", [_vm._v(\"Markdown & HTML are supported\")])\n ])\n },\n function() {\n var _vm = this\n var _h = _vm.$createElement\n var _c = _vm._self._c || _h\n return _c(\"label\", [\n _vm._v(\"\\n Cost\"),\n _c(\"br\"),\n _vm._v(\" \"),\n _c(\"small\", [_vm._v(\"How many points it costs to see your hint.\")])\n ])\n },\n function() {\n var _vm = this\n var _h = _vm.$createElement\n var _c = _vm._self._c || _h\n return _c(\"div\", { staticClass: \"modal-footer\" }, [\n _c(\"div\", { staticClass: \"container\" }, [\n _c(\"div\", { staticClass: \"row\" }, [\n _c(\"div\", { staticClass: \"col-md-12\" }, [\n _c(\"button\", { staticClass: \"btn btn-primary float-right\" }, [\n _vm._v(\"Submit\")\n ])\n ])\n ])\n ])\n ])\n }\n]\nrender._withStripped = true\n\n\n\n//# sourceURL=webpack:///./CTFd/themes/admin/assets/js/components/hints/HintEditForm.vue?./node_modules/vue-loader/lib/loaders/templateLoader.js??vue-loader-options!./node_modules/vue-loader/lib??vue-loader-options");
+eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"render\", function() { return render; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"staticRenderFns\", function() { return staticRenderFns; });\nvar render = function() {\n var _vm = this\n var _h = _vm.$createElement\n var _c = _vm._self._c || _h\n return _c(\"div\", { staticClass: \"modal fade\", attrs: { tabindex: \"-1\" } }, [\n _c(\"div\", { staticClass: \"modal-dialog\" }, [\n _c(\"div\", { staticClass: \"modal-content\" }, [\n _vm._m(0),\n _vm._v(\" \"),\n _c(\n \"form\",\n {\n attrs: { method: \"POST\" },\n on: {\n submit: function($event) {\n $event.preventDefault()\n return _vm.updateHint($event)\n }\n }\n },\n [\n _c(\"div\", { staticClass: \"modal-body\" }, [\n _c(\"div\", { staticClass: \"container\" }, [\n _c(\"div\", { staticClass: \"row\" }, [\n _c(\"div\", { staticClass: \"col-md-12\" }, [\n _c(\"div\", { staticClass: \"form-group\" }, [\n _vm._m(1),\n _vm._v(\" \"),\n _c(\"textarea\", {\n ref: \"content\",\n staticClass: \"form-control\",\n attrs: { type: \"text\", name: \"content\", rows: \"7\" },\n domProps: { value: this.content }\n })\n ]),\n _vm._v(\" \"),\n _c(\"div\", { staticClass: \"form-group\" }, [\n _vm._m(2),\n _vm._v(\" \"),\n _c(\"input\", {\n directives: [\n {\n name: \"model\",\n rawName: \"v-model.lazy\",\n value: _vm.cost,\n expression: \"cost\",\n modifiers: { lazy: true }\n }\n ],\n staticClass: \"form-control\",\n attrs: { type: \"number\", name: \"cost\" },\n domProps: { value: _vm.cost },\n on: {\n change: function($event) {\n _vm.cost = $event.target.value\n }\n }\n })\n ]),\n _vm._v(\" \"),\n _c(\n \"div\",\n { staticClass: \"form-group\" },\n [\n _vm._m(3),\n _vm._v(\" \"),\n _vm._l(_vm.otherHints, function(hint) {\n return _c(\n \"div\",\n { key: hint.id, staticClass: \"form-check\" },\n [\n _c(\n \"label\",\n {\n staticClass: \"form-check-label cursor-pointer\"\n },\n [\n _c(\"input\", {\n directives: [\n {\n name: \"model\",\n rawName: \"v-model\",\n value: _vm.selectedHints,\n expression: \"selectedHints\"\n }\n ],\n staticClass: \"form-check-input\",\n attrs: { type: \"checkbox\" },\n domProps: {\n value: hint.id,\n checked: Array.isArray(_vm.selectedHints)\n ? _vm._i(_vm.selectedHints, hint.id) >\n -1\n : _vm.selectedHints\n },\n on: {\n change: function($event) {\n var $$a = _vm.selectedHints,\n $$el = $event.target,\n $$c = $$el.checked ? true : false\n if (Array.isArray($$a)) {\n var $$v = hint.id,\n $$i = _vm._i($$a, $$v)\n if ($$el.checked) {\n $$i < 0 &&\n (_vm.selectedHints = $$a.concat([\n $$v\n ]))\n } else {\n $$i > -1 &&\n (_vm.selectedHints = $$a\n .slice(0, $$i)\n .concat($$a.slice($$i + 1)))\n }\n } else {\n _vm.selectedHints = $$c\n }\n }\n }\n }),\n _vm._v(\n \"\\n \" +\n _vm._s(hint.content) +\n \" - \" +\n _vm._s(hint.cost) +\n \"\\n \"\n )\n ]\n )\n ]\n )\n })\n ],\n 2\n )\n ])\n ])\n ])\n ]),\n _vm._v(\" \"),\n _vm._m(4)\n ]\n )\n ])\n ])\n ])\n}\nvar staticRenderFns = [\n function() {\n var _vm = this\n var _h = _vm.$createElement\n var _c = _vm._self._c || _h\n return _c(\"div\", { staticClass: \"modal-header text-center\" }, [\n _c(\"div\", { staticClass: \"container\" }, [\n _c(\"div\", { staticClass: \"row\" }, [\n _c(\"div\", { staticClass: \"col-md-12\" }, [_c(\"h3\", [_vm._v(\"Hint\")])])\n ])\n ]),\n _vm._v(\" \"),\n _c(\n \"button\",\n {\n staticClass: \"close\",\n attrs: {\n type: \"button\",\n \"data-dismiss\": \"modal\",\n \"aria-label\": \"Close\"\n }\n },\n [_c(\"span\", { attrs: { \"aria-hidden\": \"true\" } }, [_vm._v(\"×\")])]\n )\n ])\n },\n function() {\n var _vm = this\n var _h = _vm.$createElement\n var _c = _vm._self._c || _h\n return _c(\"label\", [\n _vm._v(\"\\n Hint\"),\n _c(\"br\"),\n _vm._v(\" \"),\n _c(\"small\", [_vm._v(\"Markdown & HTML are supported\")])\n ])\n },\n function() {\n var _vm = this\n var _h = _vm.$createElement\n var _c = _vm._self._c || _h\n return _c(\"label\", [\n _vm._v(\"\\n Cost\"),\n _c(\"br\"),\n _vm._v(\" \"),\n _c(\"small\", [_vm._v(\"How many points it costs to see your hint.\")])\n ])\n },\n function() {\n var _vm = this\n var _h = _vm.$createElement\n var _c = _vm._self._c || _h\n return _c(\"label\", [\n _vm._v(\"\\n Requirements\"),\n _c(\"br\"),\n _vm._v(\" \"),\n _c(\"small\", [\n _vm._v(\n \"Hints that must be unlocked before unlocking this\\n hint\"\n )\n ])\n ])\n },\n function() {\n var _vm = this\n var _h = _vm.$createElement\n var _c = _vm._self._c || _h\n return _c(\"div\", { staticClass: \"modal-footer\" }, [\n _c(\"div\", { staticClass: \"container\" }, [\n _c(\"div\", { staticClass: \"row\" }, [\n _c(\"div\", { staticClass: \"col-md-12\" }, [\n _c(\"button\", { staticClass: \"btn btn-primary float-right\" }, [\n _vm._v(\"Submit\")\n ])\n ])\n ])\n ])\n ])\n }\n]\nrender._withStripped = true\n\n\n\n//# sourceURL=webpack:///./CTFd/themes/admin/assets/js/components/hints/HintEditForm.vue?./node_modules/vue-loader/lib/loaders/templateLoader.js??vue-loader-options!./node_modules/vue-loader/lib??vue-loader-options");
/***/ }),
@@ -942,7 +942,7 @@ eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) *
/***/ (function(module, __webpack_exports__, __webpack_require__) {
;
-eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"render\", function() { return render; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"staticRenderFns\", function() { return staticRenderFns; });\nvar render = function() {\n var _vm = this\n var _h = _vm.$createElement\n var _c = _vm._self._c || _h\n return _c(\"div\", [\n _c(\n \"div\",\n [\n _c(\"HintCreationForm\", {\n ref: \"HintCreationForm\",\n attrs: { challenge_id: _vm.challenge_id },\n on: { refreshHints: _vm.refreshHints }\n })\n ],\n 1\n ),\n _vm._v(\" \"),\n _c(\n \"div\",\n [\n _c(\"HintEditForm\", {\n ref: \"HintEditForm\",\n attrs: { hint_id: _vm.editing_hint_id },\n on: { refreshHints: _vm.refreshHints }\n })\n ],\n 1\n ),\n _vm._v(\" \"),\n _c(\"table\", { staticClass: \"table table-striped\" }, [\n _vm._m(0),\n _vm._v(\" \"),\n _c(\n \"tbody\",\n _vm._l(_vm.hints, function(hint) {\n return _c(\"tr\", { key: hint.id }, [\n _c(\"td\", { staticClass: \"text-center\" }, [\n _vm._v(_vm._s(hint.type))\n ]),\n _vm._v(\" \"),\n _c(\"td\", { staticClass: \"text-break\" }, [\n _c(\"pre\", [_vm._v(_vm._s(hint.content))])\n ]),\n _vm._v(\" \"),\n _c(\"td\", { staticClass: \"text-center\" }, [\n _vm._v(_vm._s(hint.cost))\n ]),\n _vm._v(\" \"),\n _c(\"td\", { staticClass: \"text-center\" }, [\n _c(\"i\", {\n staticClass: \"btn-fa fas fa-edit\",\n attrs: { role: \"button\" },\n on: {\n click: function($event) {\n return _vm.editHint(hint.id)\n }\n }\n }),\n _vm._v(\" \"),\n _c(\"i\", {\n staticClass: \"btn-fa fas fa-times\",\n attrs: { role: \"button\" },\n on: {\n click: function($event) {\n return _vm.deleteHint(hint.id)\n }\n }\n })\n ])\n ])\n }),\n 0\n )\n ]),\n _vm._v(\" \"),\n _c(\"div\", { staticClass: \"col-md-12\" }, [\n _c(\n \"button\",\n {\n staticClass: \"btn btn-success float-right\",\n on: { click: _vm.addHint }\n },\n [_vm._v(\"\\n Create Hint\\n \")]\n )\n ])\n ])\n}\nvar staticRenderFns = [\n function() {\n var _vm = this\n var _h = _vm.$createElement\n var _c = _vm._self._c || _h\n return _c(\"thead\", [\n _c(\"tr\", [\n _c(\"td\", { staticClass: \"text-center\" }, [_c(\"b\", [_vm._v(\"ID\")])]),\n _vm._v(\" \"),\n _c(\"td\", { staticClass: \"text-center\" }, [_c(\"b\", [_vm._v(\"Hint\")])]),\n _vm._v(\" \"),\n _c(\"td\", { staticClass: \"text-center\" }, [_c(\"b\", [_vm._v(\"Cost\")])]),\n _vm._v(\" \"),\n _c(\"td\", { staticClass: \"text-center\" }, [\n _c(\"b\", [_vm._v(\"Settings\")])\n ])\n ])\n ])\n }\n]\nrender._withStripped = true\n\n\n\n//# sourceURL=webpack:///./CTFd/themes/admin/assets/js/components/hints/HintsList.vue?./node_modules/vue-loader/lib/loaders/templateLoader.js??vue-loader-options!./node_modules/vue-loader/lib??vue-loader-options");
+eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"render\", function() { return render; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"staticRenderFns\", function() { return staticRenderFns; });\nvar render = function() {\n var _vm = this\n var _h = _vm.$createElement\n var _c = _vm._self._c || _h\n return _c(\"div\", [\n _c(\n \"div\",\n [\n _c(\"HintCreationForm\", {\n ref: \"HintCreationForm\",\n attrs: { challenge_id: _vm.challenge_id, hints: _vm.hints },\n on: { refreshHints: _vm.refreshHints }\n })\n ],\n 1\n ),\n _vm._v(\" \"),\n _c(\n \"div\",\n [\n _c(\"HintEditForm\", {\n ref: \"HintEditForm\",\n attrs: {\n challenge_id: _vm.challenge_id,\n hint_id: _vm.editing_hint_id,\n hints: _vm.hints\n },\n on: { refreshHints: _vm.refreshHints }\n })\n ],\n 1\n ),\n _vm._v(\" \"),\n _c(\"table\", { staticClass: \"table table-striped\" }, [\n _vm._m(0),\n _vm._v(\" \"),\n _c(\n \"tbody\",\n _vm._l(_vm.hints, function(hint) {\n return _c(\"tr\", { key: hint.id }, [\n _c(\"td\", { staticClass: \"text-center\" }, [\n _vm._v(_vm._s(hint.type))\n ]),\n _vm._v(\" \"),\n _c(\"td\", { staticClass: \"text-break\" }, [\n _c(\"pre\", [_vm._v(_vm._s(hint.content))])\n ]),\n _vm._v(\" \"),\n _c(\"td\", { staticClass: \"text-center\" }, [\n _vm._v(_vm._s(hint.cost))\n ]),\n _vm._v(\" \"),\n _c(\"td\", { staticClass: \"text-center\" }, [\n _c(\"i\", {\n staticClass: \"btn-fa fas fa-edit\",\n attrs: { role: \"button\" },\n on: {\n click: function($event) {\n return _vm.editHint(hint.id)\n }\n }\n }),\n _vm._v(\" \"),\n _c(\"i\", {\n staticClass: \"btn-fa fas fa-times\",\n attrs: { role: \"button\" },\n on: {\n click: function($event) {\n return _vm.deleteHint(hint.id)\n }\n }\n })\n ])\n ])\n }),\n 0\n )\n ]),\n _vm._v(\" \"),\n _c(\"div\", { staticClass: \"col-md-12\" }, [\n _c(\n \"button\",\n {\n staticClass: \"btn btn-success float-right\",\n on: { click: _vm.addHint }\n },\n [_vm._v(\"\\n Create Hint\\n \")]\n )\n ])\n ])\n}\nvar staticRenderFns = [\n function() {\n var _vm = this\n var _h = _vm.$createElement\n var _c = _vm._self._c || _h\n return _c(\"thead\", [\n _c(\"tr\", [\n _c(\"td\", { staticClass: \"text-center\" }, [_c(\"b\", [_vm._v(\"ID\")])]),\n _vm._v(\" \"),\n _c(\"td\", { staticClass: \"text-center\" }, [_c(\"b\", [_vm._v(\"Hint\")])]),\n _vm._v(\" \"),\n _c(\"td\", { staticClass: \"text-center\" }, [_c(\"b\", [_vm._v(\"Cost\")])]),\n _vm._v(\" \"),\n _c(\"td\", { staticClass: \"text-center\" }, [\n _c(\"b\", [_vm._v(\"Settings\")])\n ])\n ])\n ])\n }\n]\nrender._withStripped = true\n\n\n\n//# sourceURL=webpack:///./CTFd/themes/admin/assets/js/components/hints/HintsList.vue?./node_modules/vue-loader/lib/loaders/templateLoader.js??vue-loader-options!./node_modules/vue-loader/lib??vue-loader-options");
/***/ }),
diff --git a/CTFd/themes/admin/static/js/components.min.js b/CTFd/themes/admin/static/js/components.min.js
index 07c6d807..015d7882 100644
--- a/CTFd/themes/admin/static/js/components.min.js
+++ b/CTFd/themes/admin/static/js/components.min.js
@@ -1 +1 @@
-(window.webpackJsonp=window.webpackJsonp||[]).push([[0],{"./CTFd/themes/admin/assets/js/components/comments/CommentBox.vue":function(e,t,s){s.r(t);var n,i=s("./CTFd/themes/admin/assets/js/components/comments/CommentBox.vue?vue&type=template&id=1fd2c08a&scoped=true&"),a=s("./CTFd/themes/admin/assets/js/components/comments/CommentBox.vue?vue&type=script&lang=js&");for(n in a)"default"!==n&&function(e){s.d(t,e,function(){return a[e]})}(n);s("./CTFd/themes/admin/assets/js/components/comments/CommentBox.vue?vue&type=style&index=0&id=1fd2c08a&scoped=true&lang=css&");var o=s("./node_modules/vue-loader/lib/runtime/componentNormalizer.js"),l=Object(o.a)(a.default,i.a,i.b,!1,null,"1fd2c08a",null);l.options.__file="CTFd/themes/admin/assets/js/components/comments/CommentBox.vue",t.default=l.exports},"./CTFd/themes/admin/assets/js/components/comments/CommentBox.vue?vue&type=script&lang=js&":function(e,t,s){s.r(t);var n,i=s("./node_modules/babel-loader/lib/index.js?!./node_modules/vue-loader/lib/index.js?!./CTFd/themes/admin/assets/js/components/comments/CommentBox.vue?vue&type=script&lang=js&"),a=s.n(i);for(n in i)"default"!==n&&function(e){s.d(t,e,function(){return i[e]})}(n);t.default=a.a},"./CTFd/themes/admin/assets/js/components/comments/CommentBox.vue?vue&type=style&index=0&id=1fd2c08a&scoped=true&lang=css&":function(e,t,s){var n=s("./node_modules/vue-style-loader/index.js!./node_modules/css-loader/dist/cjs.js!./node_modules/vue-loader/lib/loaders/stylePostLoader.js!./node_modules/vue-loader/lib/index.js?!./CTFd/themes/admin/assets/js/components/comments/CommentBox.vue?vue&type=style&index=0&id=1fd2c08a&scoped=true&lang=css&");s.n(n).a},"./CTFd/themes/admin/assets/js/components/comments/CommentBox.vue?vue&type=template&id=1fd2c08a&scoped=true&":function(e,t,s){function n(){var s=this,e=s.$createElement,n=s._self._c||e;return n("div",[n("div",{staticClass:"row mb-3"},[n("div",{staticClass:"col-md-12"},[n("div",{staticClass:"comment"},[n("textarea",{directives:[{name:"model",rawName:"v-model.lazy",value:s.comment,expression:"comment",modifiers:{lazy:!0}}],staticClass:"form-control mb-2",attrs:{rows:"2",id:"comment-input",placeholder:"Add comment"},domProps:{value:s.comment},on:{change:function(e){s.comment=e.target.value}}}),s._v(" "),n("button",{staticClass:"btn btn-sm btn-success btn-outlined float-right",attrs:{type:"submit"},on:{click:function(e){return s.submitComment()}}},[s._v("\n Comment\n ")])])])]),s._v(" "),1>>\n ")])])]),s._v(" "),n("div",{staticClass:"col-md-12"},[n("div",{staticClass:"text-center"},[n("small",{staticClass:"text-muted"},[s._v("Page "+s._s(s.page)+" of "+s._s(s.total)+" comments")])])])]):s._e(),s._v(" "),n("div",{staticClass:"comments"},[n("transition-group",{attrs:{name:"comment-card"}},s._l(s.comments,function(t){return n("div",{key:t.id,staticClass:"comment-card card mb-2"},[n("div",{staticClass:"card-body pl-0 pb-0 pt-2 pr-2"},[n("button",{staticClass:"close float-right",attrs:{type:"button","aria-label":"Close"},on:{click:function(e){return s.deleteComment(t.id)}}},[n("span",{attrs:{"aria-hidden":"true"}},[s._v("×")])])]),s._v(" "),n("div",{staticClass:"card-body"},[n("div",{staticClass:"card-text",domProps:{innerHTML:s._s(t.html)}}),s._v(" "),n("small",{staticClass:"text-muted float-left"},[n("span",[n("a",{attrs:{href:s.urlRoot+"/admin/users/"+t.author_id}},[s._v(s._s(t.author.name))])])]),s._v(" "),n("small",{staticClass:"text-muted float-right"},[n("span",{staticClass:"float-right"},[s._v(s._s(s.toLocalTime(t.date)))])])])])}),0)],1),s._v(" "),1>>\n ")])])]),s._v(" "),n("div",{staticClass:"col-md-12"},[n("div",{staticClass:"text-center"},[n("small",{staticClass:"text-muted"},[s._v("Page "+s._s(s.page)+" of "+s._s(s.total)+" comments")])])])]):s._e()])}var i=[];n._withStripped=!0,s.d(t,"a",function(){return n}),s.d(t,"b",function(){return i})},"./CTFd/themes/admin/assets/js/components/configs/fields/Field.vue":function(e,t,s){s.r(t);var n,i=s("./CTFd/themes/admin/assets/js/components/configs/fields/Field.vue?vue&type=template&id=30e0f744&scoped=true&"),a=s("./CTFd/themes/admin/assets/js/components/configs/fields/Field.vue?vue&type=script&lang=js&");for(n in a)"default"!==n&&function(e){s.d(t,e,function(){return a[e]})}(n);var o=s("./node_modules/vue-loader/lib/runtime/componentNormalizer.js"),l=Object(o.a)(a.default,i.a,i.b,!1,null,"30e0f744",null);l.options.__file="CTFd/themes/admin/assets/js/components/configs/fields/Field.vue",t.default=l.exports},"./CTFd/themes/admin/assets/js/components/configs/fields/Field.vue?vue&type=script&lang=js&":function(e,t,s){s.r(t);var n,i=s("./node_modules/babel-loader/lib/index.js?!./node_modules/vue-loader/lib/index.js?!./CTFd/themes/admin/assets/js/components/configs/fields/Field.vue?vue&type=script&lang=js&"),a=s.n(i);for(n in i)"default"!==n&&function(e){s.d(t,e,function(){return i[e]})}(n);t.default=a.a},"./CTFd/themes/admin/assets/js/components/configs/fields/Field.vue?vue&type=template&id=30e0f744&scoped=true&":function(e,t,s){function n(){var o=this,e=o.$createElement,t=o._self._c||e;return t("div",{staticClass:"border-bottom"},[t("div",[t("button",{staticClass:"close float-right",attrs:{type:"button","aria-label":"Close"},on:{click:function(e){return o.deleteField()}}},[t("span",{attrs:{"aria-hidden":"true"}},[o._v("×")])])]),o._v(" "),t("div",{staticClass:"row"},[t("div",{staticClass:"col-md-3"},[t("div",{staticClass:"form-group"},[t("label",[o._v("Field Type")]),o._v(" "),t("select",{directives:[{name:"model",rawName:"v-model.lazy",value:o.field.field_type,expression:"field.field_type",modifiers:{lazy:!0}}],staticClass:"form-control custom-select",on:{change:function(e){var t=Array.prototype.filter.call(e.target.options,function(e){return e.selected}).map(function(e){return"_value"in e?e._value:e.value});o.$set(o.field,"field_type",e.target.multiple?t:t[0])}}},[t("option",{attrs:{value:"text"}},[o._v("Text Field")]),o._v(" "),t("option",{attrs:{value:"boolean"}},[o._v("Checkbox")])]),o._v(" "),t("small",{staticClass:"form-text text-muted"},[o._v("Type of field shown to the user")])])]),o._v(" "),t("div",{staticClass:"col-md-9"},[t("div",{staticClass:"form-group"},[t("label",[o._v("Field Name")]),o._v(" "),t("input",{directives:[{name:"model",rawName:"v-model.lazy",value:o.field.name,expression:"field.name",modifiers:{lazy:!0}}],staticClass:"form-control",attrs:{type:"text"},domProps:{value:o.field.name},on:{change:function(e){return o.$set(o.field,"name",e.target.value)}}}),o._v(" "),t("small",{staticClass:"form-text text-muted"},[o._v("Field name")])])]),o._v(" "),t("div",{staticClass:"col-md-12"},[t("div",{staticClass:"form-group"},[t("label",[o._v("Field Description")]),o._v(" "),t("input",{directives:[{name:"model",rawName:"v-model.lazy",value:o.field.description,expression:"field.description",modifiers:{lazy:!0}}],staticClass:"form-control",attrs:{type:"text"},domProps:{value:o.field.description},on:{change:function(e){return o.$set(o.field,"description",e.target.value)}}}),o._v(" "),t("small",{staticClass:"form-text text-muted",attrs:{id:"emailHelp"}},[o._v("Field Description")])])]),o._v(" "),t("div",{staticClass:"col-md-12"},[t("div",{staticClass:"form-check"},[t("label",{staticClass:"form-check-label"},[t("input",{directives:[{name:"model",rawName:"v-model.lazy",value:o.field.editable,expression:"field.editable",modifiers:{lazy:!0}}],staticClass:"form-check-input",attrs:{type:"checkbox"},domProps:{checked:Array.isArray(o.field.editable)?-1"+_this.createForm+"").find("script").each(function(){eval((0,_jquery.default)(this).html())})},100)})},loadTypes:function(){var t=this;_CTFd.default.fetch("/api/v1/flags/types",{method:"GET"}).then(function(e){return e.json()}).then(function(e){t.types=e.data})},submitFlag:function(e){var t=this,s=(0,_jquery.default)(e.target).serializeJSON(!0);s.challenge=this.$props.challenge_id,_CTFd.default.fetch("/api/v1/flags",{method:"POST",credentials:"same-origin",headers:{Accept:"application/json","Content-Type":"application/json"},body:JSON.stringify(s)}).then(function(e){return e.json()}).then(function(e){t.$emit("refreshFlags",t.$options.name)})}},created:function(){this.loadTypes()}};exports.default=_default},"./node_modules/babel-loader/lib/index.js?!./node_modules/vue-loader/lib/index.js?!./CTFd/themes/admin/assets/js/components/flags/FlagEditForm.vue?vue&type=script&lang=js&":function(module,exports,__webpack_require__){Object.defineProperty(exports,"__esModule",{value:!0}),exports.default=void 0;var _jquery=_interopRequireDefault(__webpack_require__("./node_modules/jquery/dist/jquery.js")),_CTFd=_interopRequireDefault(__webpack_require__("./CTFd/themes/core/assets/js/CTFd.js")),_nunjucks=_interopRequireDefault(__webpack_require__("./node_modules/nunjucks/browser/nunjucks.js"));function _interopRequireDefault(e){return e&&e.__esModule?e:{default:e}}var _default={name:"FlagEditForm",props:{flag_id:Number},data:function(){return{flag:{},editForm:""}},watch:{flag_id:{immediate:!0,handler:function(e){null!==e&&this.loadFlag()}}},methods:{loadFlag:function loadFlag(){var _this=this;_CTFd.default.fetch("/api/v1/flags/".concat(this.$props.flag_id),{method:"GET"}).then(function(e){return e.json()}).then(function(response){_this.flag=response.data;var editFormURL=_this.flag.templates.update;_jquery.default.get(_CTFd.default.config.urlRoot+editFormURL,function(template_data){var template=_nunjucks.default.compile(template_data);_this.editForm=template.render(_this.flag),_this.editForm.includes("