From 27d862ab29a768a81d038ab4a6ba17ad412be7ac Mon Sep 17 00:00:00 2001 From: Kevin Chung Date: Fri, 30 Jul 2021 00:03:16 -0400 Subject: [PATCH] Challenge Topics (#1966) * Closes #1897 * Adds Topics to Challenges where Topics are admin-only visible tags about challenges * Adds `/api/v1/topics` and `/api/v1/challenges/[challenge_id]/topics` to API * Challenge comments have been moved into a modal --- CTFd/api/__init__.py | 2 + CTFd/api/v1/challenges.py | 34 +++- CTFd/api/v1/topics.py | 177 +++++++++++++++++ CTFd/models/__init__.py | 26 +++ CTFd/schemas/topics.py | 38 ++++ .../js/components/topics/TopicsList.vue | 188 ++++++++++++++++++ .../themes/admin/assets/js/pages/challenge.js | 15 ++ CTFd/themes/admin/static/js/components.dev.js | 60 ++++++ CTFd/themes/admin/static/js/components.min.js | 2 +- .../admin/static/js/pages/challenge.dev.js | 2 +- .../admin/static/js/pages/challenge.min.js | 2 +- .../admin/templates/challenges/challenge.html | 44 +++- .../templates/modals/challenges/topics.html | 2 + ..._add_topics_and_challenge_topics_tables.py | 46 +++++ tests/api/v1/test_challenges.py | 29 +++ tests/api/v1/test_tags.py | 2 +- tests/api/v1/test_topics.py | 132 ++++++++++++ tests/helpers.py | 13 ++ 18 files changed, 788 insertions(+), 26 deletions(-) create mode 100644 CTFd/api/v1/topics.py create mode 100644 CTFd/schemas/topics.py create mode 100644 CTFd/themes/admin/assets/js/components/topics/TopicsList.vue create mode 100644 CTFd/themes/admin/templates/modals/challenges/topics.html create mode 100644 migrations/versions/ef87d69ec29a_add_topics_and_challenge_topics_tables.py create mode 100644 tests/api/v1/test_topics.py diff --git a/CTFd/api/__init__.py b/CTFd/api/__init__.py index 3c70f839d..aa5084680 100644 --- a/CTFd/api/__init__.py +++ b/CTFd/api/__init__.py @@ -21,6 +21,7 @@ from CTFd.api.v1.tags import tags_namespace from CTFd.api.v1.teams import teams_namespace from CTFd.api.v1.tokens import tokens_namespace +from CTFd.api.v1.topics import topics_namespace from CTFd.api.v1.unlocks import unlocks_namespace from CTFd.api.v1.users import users_namespace @@ -35,6 +36,7 @@ CTFd_API_v1.add_namespace(challenges_namespace, "/challenges") CTFd_API_v1.add_namespace(tags_namespace, "/tags") +CTFd_API_v1.add_namespace(topics_namespace, "/topics") CTFd_API_v1.add_namespace(awards_namespace, "/awards") CTFd_API_v1.add_namespace(hints_namespace, "/hints") CTFd_API_v1.add_namespace(flags_namespace, "/flags") diff --git a/CTFd/api/v1/challenges.py b/CTFd/api/v1/challenges.py index 36c583df9..1b4067d3a 100644 --- a/CTFd/api/v1/challenges.py +++ b/CTFd/api/v1/challenges.py @@ -12,17 +12,9 @@ from CTFd.cache import clear_standings from CTFd.constants import RawEnum from CTFd.models import ChallengeFiles as ChallengeFilesModel -from CTFd.models import ( - Challenges, - Fails, - Flags, - Hints, - HintUnlocks, - Solves, - Submissions, - Tags, - db, -) +from CTFd.models import Challenges +from CTFd.models import ChallengeTopics as ChallengeTopicsModel +from CTFd.models import Fails, Flags, Hints, HintUnlocks, Solves, Submissions, Tags, db from CTFd.plugins.challenges import CHALLENGE_CLASSES, get_chal_class from CTFd.schemas.challenges import ChallengeSchema from CTFd.schemas.flags import FlagSchema @@ -831,6 +823,26 @@ def get(self, challenge_id): return {"success": True, "data": response} +@challenges_namespace.route("//topics") +class ChallengeTopics(Resource): + @admins_only + def get(self, challenge_id): + response = [] + + topics = ChallengeTopicsModel.query.filter_by(challenge_id=challenge_id).all() + + for t in topics: + response.append( + { + "id": t.id, + "challenge_id": t.challenge_id, + "topic_id": t.topic_id, + "value": t.topic.value, + } + ) + return {"success": True, "data": response} + + @challenges_namespace.route("//hints") class ChallengeHints(Resource): @admins_only diff --git a/CTFd/api/v1/topics.py b/CTFd/api/v1/topics.py new file mode 100644 index 000000000..ef50389bd --- /dev/null +++ b/CTFd/api/v1/topics.py @@ -0,0 +1,177 @@ +from typing import List + +from flask import request +from flask_restx import Namespace, Resource + +from CTFd.api.v1.helpers.request import validate_args +from CTFd.api.v1.helpers.schemas import sqlalchemy_to_pydantic +from CTFd.api.v1.schemas import APIDetailedSuccessResponse, APIListSuccessResponse +from CTFd.constants import RawEnum +from CTFd.models import ChallengeTopics, Topics, db +from CTFd.schemas.topics import ChallengeTopicSchema, TopicSchema +from CTFd.utils.decorators import admins_only +from CTFd.utils.helpers.models import build_model_filters + +topics_namespace = Namespace("topics", description="Endpoint to retrieve Topics") + +TopicModel = sqlalchemy_to_pydantic(Topics) + + +class TopicDetailedSuccessResponse(APIDetailedSuccessResponse): + data: TopicModel + + +class TopicListSuccessResponse(APIListSuccessResponse): + data: List[TopicModel] + + +topics_namespace.schema_model( + "TopicDetailedSuccessResponse", TopicDetailedSuccessResponse.apidoc() +) + +topics_namespace.schema_model( + "TopicListSuccessResponse", TopicListSuccessResponse.apidoc() +) + + +@topics_namespace.route("") +class TopicList(Resource): + @admins_only + @topics_namespace.doc( + description="Endpoint to list Topic objects in bulk", + responses={ + 200: ("Success", "TopicListSuccessResponse"), + 400: ( + "An error occured processing the provided or stored data", + "APISimpleErrorResponse", + ), + }, + ) + @validate_args( + { + "value": (str, None), + "q": (str, None), + "field": (RawEnum("TopicFields", {"value": "value"}), None,), + }, + location="query", + ) + def get(self, query_args): + q = query_args.pop("q", None) + field = str(query_args.pop("field", None)) + filters = build_model_filters(model=Topics, query=q, field=field) + + topics = Topics.query.filter_by(**query_args).filter(*filters).all() + schema = TopicSchema(many=True) + response = schema.dump(topics) + + if response.errors: + return {"success": False, "errors": response.errors}, 400 + + return {"success": True, "data": response.data} + + @admins_only + @topics_namespace.doc( + description="Endpoint to create a Topic object", + responses={ + 200: ("Success", "TopicDetailedSuccessResponse"), + 400: ( + "An error occured processing the provided or stored data", + "APISimpleErrorResponse", + ), + }, + ) + def post(self): + req = request.get_json() + value = req.get("value") + + if value: + topic = Topics.query.filter_by(value=value).first() + if topic is None: + schema = TopicSchema() + response = schema.load(req, session=db.session) + + if response.errors: + return {"success": False, "errors": response.errors}, 400 + + topic = response.data + db.session.add(topic) + db.session.commit() + else: + topic_id = req.get("topic_id") + topic = Topics.query.filter_by(id=topic_id).first_or_404() + + req["topic_id"] = topic.id + topic_type = req.get("type") + if topic_type == "challenge": + schema = ChallengeTopicSchema() + response = schema.load(req, session=db.session) + else: + return {"success": False}, 400 + + db.session.add(response.data) + db.session.commit() + + response = schema.dump(response.data) + db.session.close() + + return {"success": True, "data": response.data} + + @admins_only + @topics_namespace.doc( + description="Endpoint to delete a specific Topic object of a specific type", + responses={200: ("Success", "APISimpleSuccessResponse")}, + ) + @validate_args( + {"type": (str, None), "target_id": (int, 0)}, location="query", + ) + def delete(self, query_args): + topic_type = query_args.get("type") + target_id = int(query_args.get("target_id", 0)) + + if topic_type == "challenge": + Model = ChallengeTopics + else: + return {"success": False}, 400 + + topic = Model.query.filter_by(id=target_id).first_or_404() + db.session.delete(topic) + db.session.commit() + db.session.close() + + return {"success": True} + + +@topics_namespace.route("/") +class Topic(Resource): + @admins_only + @topics_namespace.doc( + description="Endpoint to get a specific Topic object", + responses={ + 200: ("Success", "TopicDetailedSuccessResponse"), + 400: ( + "An error occured processing the provided or stored data", + "APISimpleErrorResponse", + ), + }, + ) + def get(self, topic_id): + topic = Topics.query.filter_by(id=topic_id).first_or_404() + response = TopicSchema().dump(topic) + + if response.errors: + return {"success": False, "errors": response.errors}, 400 + + return {"success": True, "data": response.data} + + @admins_only + @topics_namespace.doc( + description="Endpoint to delete a specific Topic object", + responses={200: ("Success", "APISimpleSuccessResponse")}, + ) + def delete(self, topic_id): + topic = Topics.query.filter_by(id=topic_id).first_or_404() + db.session.delete(topic) + db.session.commit() + db.session.close() + + return {"success": True} diff --git a/CTFd/models/__init__.py b/CTFd/models/__init__.py index be7fe695f..b49537b5a 100644 --- a/CTFd/models/__init__.py +++ b/CTFd/models/__init__.py @@ -97,6 +97,7 @@ class Challenges(db.Model): hints = db.relationship("Hints", backref="challenge") flags = db.relationship("Flags", backref="challenge") comments = db.relationship("ChallengeComments", backref="challenge") + topics = db.relationship("ChallengeTopics", backref="challenge") class alt_defaultdict(defaultdict): """ @@ -222,6 +223,31 @@ def __init__(self, *args, **kwargs): super(Tags, self).__init__(**kwargs) +class Topics(db.Model): + __tablename__ = "topics" + id = db.Column(db.Integer, primary_key=True) + value = db.Column(db.String(255), unique=True) + + def __init__(self, *args, **kwargs): + super(Topics, self).__init__(**kwargs) + + +class ChallengeTopics(db.Model): + __tablename__ = "challenge_topics" + id = db.Column(db.Integer, primary_key=True) + challenge_id = db.Column( + db.Integer, db.ForeignKey("challenges.id", ondelete="CASCADE") + ) + topic_id = db.Column(db.Integer, db.ForeignKey("topics.id", ondelete="CASCADE")) + + topic = db.relationship( + "Topics", foreign_keys="ChallengeTopics.topic_id", lazy="select" + ) + + def __init__(self, *args, **kwargs): + super(ChallengeTopics, self).__init__(**kwargs) + + class Files(db.Model): __tablename__ = "files" id = db.Column(db.Integer, primary_key=True) diff --git a/CTFd/schemas/topics.py b/CTFd/schemas/topics.py new file mode 100644 index 000000000..c71e8d906 --- /dev/null +++ b/CTFd/schemas/topics.py @@ -0,0 +1,38 @@ +from CTFd.models import ChallengeTopics, Topics, ma +from CTFd.utils import string_types + + +class TopicSchema(ma.ModelSchema): + class Meta: + model = Topics + include_fk = True + dump_only = ("id",) + + views = {"admin": ["id", "value"]} + + def __init__(self, view=None, *args, **kwargs): + if view: + if isinstance(view, string_types): + kwargs["only"] = self.views[view] + elif isinstance(view, list): + kwargs["only"] = view + + super(TopicSchema, self).__init__(*args, **kwargs) + + +class ChallengeTopicSchema(ma.ModelSchema): + class Meta: + model = ChallengeTopics + include_fk = True + dump_only = ("id",) + + views = {"admin": ["id", "challenge_id", "topic_id"]} + + def __init__(self, view=None, *args, **kwargs): + if view: + if isinstance(view, string_types): + kwargs["only"] = self.views[view] + elif isinstance(view, list): + kwargs["only"] = view + + super(ChallengeTopicSchema, self).__init__(*args, **kwargs) diff --git a/CTFd/themes/admin/assets/js/components/topics/TopicsList.vue b/CTFd/themes/admin/assets/js/components/topics/TopicsList.vue new file mode 100644 index 000000000..8db97bdfc --- /dev/null +++ b/CTFd/themes/admin/assets/js/components/topics/TopicsList.vue @@ -0,0 +1,188 @@ + + + + + diff --git a/CTFd/themes/admin/assets/js/pages/challenge.js b/CTFd/themes/admin/assets/js/pages/challenge.js index 6ae10204e..418e56058 100644 --- a/CTFd/themes/admin/assets/js/pages/challenge.js +++ b/CTFd/themes/admin/assets/js/pages/challenge.js @@ -11,6 +11,7 @@ import Vue from "vue/dist/vue.esm.browser"; import CommentBox from "../components/comments/CommentBox.vue"; import FlagList from "../components/flags/FlagList.vue"; import Requirements from "../components/requirements/Requirements.vue"; +import TopicsList from "../components/topics/TopicsList.vue"; import TagsList from "../components/tags/TagsList.vue"; import ChallengeFilesList from "../components/files/ChallengeFilesList.vue"; import HintsList from "../components/hints/HintsList.vue"; @@ -325,6 +326,10 @@ $(() => { ); }); + $(".comments-challenge").click(function(_event) { + $("#challenge-comments-window").modal(); + }); + $(".delete-challenge").click(function(_e) { ezQuery({ title: "Delete Challenge", @@ -438,6 +443,16 @@ $(() => { }).$mount(vueContainer); } + // Load TopicsList component + if (document.querySelector("#challenge-topics")) { + const topicsList = Vue.extend(TopicsList); + let vueContainer = document.createElement("div"); + document.querySelector("#challenge-topics").appendChild(vueContainer); + new topicsList({ + propsData: { challenge_id: window.CHALLENGE_ID } + }).$mount(vueContainer); + } + // Load TagsList component if (document.querySelector("#challenge-tags")) { const tagList = Vue.extend(TagsList); diff --git a/CTFd/themes/admin/static/js/components.dev.js b/CTFd/themes/admin/static/js/components.dev.js index 620bd4638..05242d5a9 100644 --- a/CTFd/themes/admin/static/js/components.dev.js +++ b/CTFd/themes/admin/static/js/components.dev.js @@ -564,6 +564,42 @@ eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _nod /***/ }), +/***/ "./CTFd/themes/admin/assets/js/components/topics/TopicsList.vue": +/*!**********************************************************************!*\ + !*** ./CTFd/themes/admin/assets/js/components/topics/TopicsList.vue ***! + \**********************************************************************/ +/*! no static exports found */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _TopicsList_vue_vue_type_template_id_6982af81_scoped_true___WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./TopicsList.vue?vue&type=template&id=6982af81&scoped=true& */ \"./CTFd/themes/admin/assets/js/components/topics/TopicsList.vue?vue&type=template&id=6982af81&scoped=true&\");\n/* harmony import */ var _TopicsList_vue_vue_type_script_lang_js___WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./TopicsList.vue?vue&type=script&lang=js& */ \"./CTFd/themes/admin/assets/js/components/topics/TopicsList.vue?vue&type=script&lang=js&\");\n/* harmony reexport (unknown) */ for(var __WEBPACK_IMPORT_KEY__ in _TopicsList_vue_vue_type_script_lang_js___WEBPACK_IMPORTED_MODULE_1__) if(__WEBPACK_IMPORT_KEY__ !== 'default') (function(key) { __webpack_require__.d(__webpack_exports__, key, function() { return _TopicsList_vue_vue_type_script_lang_js___WEBPACK_IMPORTED_MODULE_1__[key]; }) }(__WEBPACK_IMPORT_KEY__));\n/* harmony import */ var _node_modules_vue_loader_lib_runtime_componentNormalizer_js__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../../../../../../../node_modules/vue-loader/lib/runtime/componentNormalizer.js */ \"./node_modules/vue-loader/lib/runtime/componentNormalizer.js\");\n\n\n\n\n\n/* normalize component */\n\nvar component = Object(_node_modules_vue_loader_lib_runtime_componentNormalizer_js__WEBPACK_IMPORTED_MODULE_2__[\"default\"])(\n _TopicsList_vue_vue_type_script_lang_js___WEBPACK_IMPORTED_MODULE_1__[\"default\"],\n _TopicsList_vue_vue_type_template_id_6982af81_scoped_true___WEBPACK_IMPORTED_MODULE_0__[\"render\"],\n _TopicsList_vue_vue_type_template_id_6982af81_scoped_true___WEBPACK_IMPORTED_MODULE_0__[\"staticRenderFns\"],\n false,\n null,\n \"6982af81\",\n null\n \n)\n\n/* hot reload */\nif (false) { var api; }\ncomponent.options.__file = \"CTFd/themes/admin/assets/js/components/topics/TopicsList.vue\"\n/* harmony default export */ __webpack_exports__[\"default\"] = (component.exports);\n\n//# sourceURL=webpack:///./CTFd/themes/admin/assets/js/components/topics/TopicsList.vue?"); + +/***/ }), + +/***/ "./CTFd/themes/admin/assets/js/components/topics/TopicsList.vue?vue&type=script&lang=js&": +/*!***********************************************************************************************!*\ + !*** ./CTFd/themes/admin/assets/js/components/topics/TopicsList.vue?vue&type=script&lang=js& ***! + \***********************************************************************************************/ +/*! no static exports found */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _node_modules_babel_loader_lib_index_js_ref_0_node_modules_vue_loader_lib_index_js_vue_loader_options_TopicsList_vue_vue_type_script_lang_js___WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! -!../../../../../../../node_modules/babel-loader/lib??ref--0!../../../../../../../node_modules/vue-loader/lib??vue-loader-options!./TopicsList.vue?vue&type=script&lang=js& */ \"./node_modules/babel-loader/lib/index.js?!./node_modules/vue-loader/lib/index.js?!./CTFd/themes/admin/assets/js/components/topics/TopicsList.vue?vue&type=script&lang=js&\");\n/* harmony import */ var _node_modules_babel_loader_lib_index_js_ref_0_node_modules_vue_loader_lib_index_js_vue_loader_options_TopicsList_vue_vue_type_script_lang_js___WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(_node_modules_babel_loader_lib_index_js_ref_0_node_modules_vue_loader_lib_index_js_vue_loader_options_TopicsList_vue_vue_type_script_lang_js___WEBPACK_IMPORTED_MODULE_0__);\n/* harmony reexport (unknown) */ for(var __WEBPACK_IMPORT_KEY__ in _node_modules_babel_loader_lib_index_js_ref_0_node_modules_vue_loader_lib_index_js_vue_loader_options_TopicsList_vue_vue_type_script_lang_js___WEBPACK_IMPORTED_MODULE_0__) if(__WEBPACK_IMPORT_KEY__ !== 'default') (function(key) { __webpack_require__.d(__webpack_exports__, key, function() { return _node_modules_babel_loader_lib_index_js_ref_0_node_modules_vue_loader_lib_index_js_vue_loader_options_TopicsList_vue_vue_type_script_lang_js___WEBPACK_IMPORTED_MODULE_0__[key]; }) }(__WEBPACK_IMPORT_KEY__));\n /* harmony default export */ __webpack_exports__[\"default\"] = (_node_modules_babel_loader_lib_index_js_ref_0_node_modules_vue_loader_lib_index_js_vue_loader_options_TopicsList_vue_vue_type_script_lang_js___WEBPACK_IMPORTED_MODULE_0___default.a); \n\n//# sourceURL=webpack:///./CTFd/themes/admin/assets/js/components/topics/TopicsList.vue?"); + +/***/ }), + +/***/ "./CTFd/themes/admin/assets/js/components/topics/TopicsList.vue?vue&type=template&id=6982af81&scoped=true&": +/*!*****************************************************************************************************************!*\ + !*** ./CTFd/themes/admin/assets/js/components/topics/TopicsList.vue?vue&type=template&id=6982af81&scoped=true& ***! + \*****************************************************************************************************************/ +/*! exports provided: render, staticRenderFns */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _node_modules_vue_loader_lib_loaders_templateLoader_js_vue_loader_options_node_modules_vue_loader_lib_index_js_vue_loader_options_TopicsList_vue_vue_type_template_id_6982af81_scoped_true___WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! -!../../../../../../../node_modules/vue-loader/lib/loaders/templateLoader.js??vue-loader-options!../../../../../../../node_modules/vue-loader/lib??vue-loader-options!./TopicsList.vue?vue&type=template&id=6982af81&scoped=true& */ \"./node_modules/vue-loader/lib/loaders/templateLoader.js?!./node_modules/vue-loader/lib/index.js?!./CTFd/themes/admin/assets/js/components/topics/TopicsList.vue?vue&type=template&id=6982af81&scoped=true&\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"render\", function() { return _node_modules_vue_loader_lib_loaders_templateLoader_js_vue_loader_options_node_modules_vue_loader_lib_index_js_vue_loader_options_TopicsList_vue_vue_type_template_id_6982af81_scoped_true___WEBPACK_IMPORTED_MODULE_0__[\"render\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"staticRenderFns\", function() { return _node_modules_vue_loader_lib_loaders_templateLoader_js_vue_loader_options_node_modules_vue_loader_lib_index_js_vue_loader_options_TopicsList_vue_vue_type_template_id_6982af81_scoped_true___WEBPACK_IMPORTED_MODULE_0__[\"staticRenderFns\"]; });\n\n\n\n//# sourceURL=webpack:///./CTFd/themes/admin/assets/js/components/topics/TopicsList.vue?"); + +/***/ }), + /***/ "./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&": /*!*******************************************************************************************************************************************************************************************!*\ !*** ./node_modules/babel-loader/lib??ref--0!./node_modules/vue-loader/lib??vue-loader-options!./CTFd/themes/admin/assets/js/components/comments/CommentBox.vue?vue&type=script&lang=js& ***! @@ -744,6 +780,18 @@ eval("\n\nObject.defineProperty(exports, \"__esModule\", {\n value: true\n});\n /***/ }), +/***/ "./node_modules/babel-loader/lib/index.js?!./node_modules/vue-loader/lib/index.js?!./CTFd/themes/admin/assets/js/components/topics/TopicsList.vue?vue&type=script&lang=js&": +/*!*****************************************************************************************************************************************************************************************!*\ + !*** ./node_modules/babel-loader/lib??ref--0!./node_modules/vue-loader/lib??vue-loader-options!./CTFd/themes/admin/assets/js/components/topics/TopicsList.vue?vue&type=script&lang=js& ***! + \*****************************************************************************************************************************************************************************************/ +/*! no static exports found */ +/***/ (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\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//\nvar _default = {\n props: {\n challenge_id: Number\n },\n data: function data() {\n return {\n topics: [],\n topicValue: \"\",\n searchedTopic: \"\",\n topicResults: [],\n selectedResultIdx: 0,\n awaitingSearch: false\n };\n },\n methods: {\n loadTopics: function loadTopics() {\n var _this = this;\n\n _CTFd[\"default\"].fetch(\"/api/v1/challenges/\".concat(this.$props.challenge_id, \"/topics\"), {\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.topics = response.data;\n }\n });\n },\n searchTopics: function searchTopics() {\n var _this2 = this;\n\n this.selectedResultIdx = 0;\n\n if (this.topicValue == \"\") {\n this.topicResults = [];\n return;\n }\n\n _CTFd[\"default\"].fetch(\"/api/v1/topics?field=value&q=\".concat(this.topicValue), {\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 _this2.topicResults = response.data.slice(0, 10);\n }\n });\n },\n addTopic: function addTopic() {\n var _this3 = this;\n\n var value;\n\n if (this.selectedResultIdx === 0) {\n value = this.topicValue;\n } else {\n var idx = this.selectedResultIdx - 1;\n var topic = this.topicResults[idx];\n value = topic.value;\n }\n\n var params = {\n value: value,\n challenge: this.$props.challenge_id,\n type: \"challenge\"\n };\n\n _CTFd[\"default\"].fetch(\"/api/v1/topics\", {\n method: \"POST\",\n body: JSON.stringify(params)\n }).then(function (response) {\n return response.json();\n }).then(function (response) {\n if (response.success) {\n _this3.topicValue = \"\";\n\n _this3.loadTopics();\n }\n });\n },\n deleteTopic: function deleteTopic(topic_id) {\n var _this4 = this;\n\n _CTFd[\"default\"].fetch(\"/api/v1/topics?type=challenge&target_id=\".concat(topic_id), {\n method: \"DELETE\"\n }).then(function (response) {\n return response.json();\n }).then(function (response) {\n if (response.success) {\n _this4.loadTopics();\n }\n });\n },\n moveCursor: function moveCursor(dir) {\n switch (dir) {\n case \"up\":\n if (this.selectedResultIdx) {\n this.selectedResultIdx -= 1;\n }\n\n break;\n\n case \"down\":\n if (this.selectedResultIdx < this.topicResults.length) {\n this.selectedResultIdx += 1;\n }\n\n break;\n }\n },\n selectTopic: function selectTopic(idx) {\n if (idx === undefined) {\n idx = this.selectedResultIdx;\n }\n\n var topic = this.topicResults[idx];\n this.topicValue = topic.value;\n }\n },\n watch: {\n topicValue: function topicValue(val) {\n var _this5 = this;\n\n if (this.awaitingSearch === false) {\n // 1 second delay after typing\n setTimeout(function () {\n _this5.searchTopics();\n\n _this5.awaitingSearch = false;\n }, 500);\n }\n\n this.awaitingSearch = true;\n }\n },\n created: function created() {\n this.loadTopics();\n }\n};\nexports[\"default\"] = _default;\n\n//# sourceURL=webpack:///./CTFd/themes/admin/assets/js/components/topics/TopicsList.vue?./node_modules/babel-loader/lib??ref--0!./node_modules/vue-loader/lib??vue-loader-options"); + +/***/ }), + /***/ "./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&": /*!**********************************************************************************************************************************************************************************************************************************************************************************!*\ !*** ./node_modules/css-loader/dist/cjs.js!./node_modules/vue-loader/lib/loaders/stylePostLoader.js!./node_modules/vue-loader/lib??vue-loader-options!./CTFd/themes/admin/assets/js/components/comments/CommentBox.vue?vue&type=style&index=0&id=1fd2c08a&scoped=true&lang=css& ***! @@ -946,6 +994,18 @@ eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) * /***/ }), +/***/ "./node_modules/vue-loader/lib/loaders/templateLoader.js?!./node_modules/vue-loader/lib/index.js?!./CTFd/themes/admin/assets/js/components/topics/TopicsList.vue?vue&type=template&id=6982af81&scoped=true&": +/*!***********************************************************************************************************************************************************************************************************************************************!*\ + !*** ./node_modules/vue-loader/lib/loaders/templateLoader.js??vue-loader-options!./node_modules/vue-loader/lib??vue-loader-options!./CTFd/themes/admin/assets/js/components/topics/TopicsList.vue?vue&type=template&id=6982af81&scoped=true& ***! + \***********************************************************************************************************************************************************************************************************************************************/ +/*! exports provided: render, staticRenderFns */ +/***/ (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: \"col-md-12\" }, [\n _c(\n \"div\",\n { staticClass: \"my-3\", attrs: { id: \"challenge-topics\" } },\n _vm._l(_vm.topics, function(topic) {\n return _c(\"h5\", { key: topic.id, staticClass: \"challenge-tag\" }, [\n _c(\"span\", { staticClass: \"mr-1\" }, [_vm._v(_vm._s(topic.value))]),\n _vm._v(\" \"),\n _c(\n \"a\",\n {\n staticClass: \"btn-fa delete-tag\",\n on: {\n click: function($event) {\n return _vm.deleteTopic(topic.id)\n }\n }\n },\n [_vm._v(\" ×\")]\n )\n ])\n }),\n 0\n ),\n _vm._v(\" \"),\n _c(\"div\", { staticClass: \"form-group\" }, [\n _vm._m(0),\n _vm._v(\" \"),\n _c(\"input\", {\n directives: [\n {\n name: \"model\",\n rawName: \"v-model\",\n value: _vm.topicValue,\n expression: \"topicValue\"\n }\n ],\n staticClass: \"form-control\",\n attrs: { id: \"tags-add-input\", maxlength: \"255\", type: \"text\" },\n domProps: { value: _vm.topicValue },\n on: {\n keyup: [\n function($event) {\n if (\n !$event.type.indexOf(\"key\") &&\n _vm._k($event.keyCode, \"down\", 40, $event.key, [\n \"Down\",\n \"ArrowDown\"\n ])\n ) {\n return null\n }\n return _vm.moveCursor(\"down\")\n },\n function($event) {\n if (\n !$event.type.indexOf(\"key\") &&\n _vm._k($event.keyCode, \"up\", 38, $event.key, [\"Up\", \"ArrowUp\"])\n ) {\n return null\n }\n return _vm.moveCursor(\"up\")\n },\n function($event) {\n if (\n !$event.type.indexOf(\"key\") &&\n _vm._k($event.keyCode, \"enter\", 13, $event.key, \"Enter\")\n ) {\n return null\n }\n return _vm.addTopic()\n }\n ],\n input: function($event) {\n if ($event.target.composing) {\n return\n }\n _vm.topicValue = $event.target.value\n }\n }\n })\n ]),\n _vm._v(\" \"),\n _c(\"div\", { staticClass: \"form-group\" }, [\n _c(\n \"ul\",\n { staticClass: \"list-group\" },\n _vm._l(_vm.topicResults, function(topic, idx) {\n return _c(\n \"li\",\n {\n key: topic.id,\n class: {\n \"list-group-item\": true,\n active: idx + 1 === _vm.selectedResultIdx\n },\n on: {\n click: function($event) {\n return _vm.selectTopic(idx)\n }\n }\n },\n [_vm._v(\"\\n \" + _vm._s(topic.value) + \"\\n \")]\n )\n }),\n 0\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(\"label\", [\n _vm._v(\"\\n Topic\\n \"),\n _c(\"br\"),\n _vm._v(\" \"),\n _c(\"small\", { staticClass: \"text-muted\" }, [\n _vm._v(\"Type topic and press Enter\")\n ])\n ])\n }\n]\nrender._withStripped = true\n\n\n\n//# sourceURL=webpack:///./CTFd/themes/admin/assets/js/components/topics/TopicsList.vue?./node_modules/vue-loader/lib/loaders/templateLoader.js??vue-loader-options!./node_modules/vue-loader/lib??vue-loader-options"); + +/***/ }), + /***/ "./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&": /*!******************************************************************************************************************************************************************************************************************************************************************************************************************!*\ !*** ./node_modules/vue-style-loader!./node_modules/css-loader/dist/cjs.js!./node_modules/vue-loader/lib/loaders/stylePostLoader.js!./node_modules/vue-loader/lib??vue-loader-options!./CTFd/themes/admin/assets/js/components/comments/CommentBox.vue?vue&type=style&index=0&id=1fd2c08a&scoped=true&lang=css& ***! diff --git a/CTFd/themes/admin/static/js/components.min.js b/CTFd/themes/admin/static/js/components.min.js index 6010e7f7d..07c6d8072 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(""+_this.editForm+"").find("script").each(function(){eval((0,_jquery.default)(this).html())})},100)})})},updateFlag:function(e){var t=this,s=(0,_jquery.default)(e.target).serializeJSON(!0);_CTFd.default.fetch("/api/v1/flags/".concat(this.$props.flag_id),{method:"PATCH",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)})}},mounted:function(){this.flag_id&&this.loadFlag()},created:function(){this.flag_id&&this.loadFlag()}};exports.default=_default},"./node_modules/babel-loader/lib/index.js?!./node_modules/vue-loader/lib/index.js?!./CTFd/themes/admin/assets/js/components/flags/FlagList.vue?vue&type=script&lang=js&":function(e,t,s){Object.defineProperty(t,"__esModule",{value:!0}),t.default=void 0;var n=l(s("./node_modules/jquery/dist/jquery.js")),i=l(s("./CTFd/themes/core/assets/js/CTFd.js")),a=l(s("./CTFd/themes/admin/assets/js/components/flags/FlagCreationForm.vue")),o=l(s("./CTFd/themes/admin/assets/js/components/flags/FlagEditForm.vue"));function l(e){return e&&e.__esModule?e:{default:e}}var d={components:{FlagCreationForm:a.default,FlagEditForm:o.default},props:{challenge_id:Number},data:function(){return{flags:[],editing_flag_id:null}},methods:{loadFlags:function(){var t=this;i.default.fetch("/api/v1/challenges/".concat(this.$props.challenge_id,"/flags"),{method:"GET",credentials:"same-origin",headers:{Accept:"application/json","Content-Type":"application/json"}}).then(function(e){return e.json()}).then(function(e){e.success&&(t.flags=e.data)})},refreshFlags:function(e){var t;switch(this.loadFlags(),e){case"FlagEditForm":t=this.$refs.FlagEditForm.$el,(0,n.default)(t).modal("hide");break;case"FlagCreationForm":t=this.$refs.FlagCreationForm.$el,(0,n.default)(t).modal("hide")}},addFlag:function(){var e=this.$refs.FlagCreationForm.$el;(0,n.default)(e).modal()},editFlag:function(e){this.editing_flag_id=e;var t=this.$refs.FlagEditForm.$el;(0,n.default)(t).modal()},deleteFlag:function(e){var t=this;confirm("Are you sure you'd like to delete this flag?")&&i.default.fetch("/api/v1/flags/".concat(e),{method:"DELETE"}).then(function(e){return e.json()}).then(function(e){e.success&&t.loadFlags()})}},created:function(){this.loadFlags()}};t.default=d},"./node_modules/babel-loader/lib/index.js?!./node_modules/vue-loader/lib/index.js?!./CTFd/themes/admin/assets/js/components/hints/HintCreationForm.vue?vue&type=script&lang=js&":function(e,t,s){Object.defineProperty(t,"__esModule",{value:!0}),t.default=void 0;var n={name:"HintCreationForm",props:{challenge_id:Number},data:function(){return{cost:0}},methods:{getCost:function(){return this.cost||0},getContent:function(){return this.$refs.content.value},submitHint:function(){var t=this,e={challenge_id:this.$props.challenge_id,content:this.getContent(),cost:this.getCost()};CTFd.fetch("/api/v1/hints",{method:"POST",credentials:"same-origin",headers:{Accept:"application/json","Content-Type":"application/json"},body:JSON.stringify(e)}).then(function(e){return e.json()}).then(function(e){e.success&&t.$emit("refreshHints",t.$options.name)})}}};t.default=n},"./node_modules/babel-loader/lib/index.js?!./node_modules/vue-loader/lib/index.js?!./CTFd/themes/admin/assets/js/components/hints/HintEditForm.vue?vue&type=script&lang=js&":function(e,t,s){Object.defineProperty(t,"__esModule",{value:!0}),t.default=void 0;var n,i=(n=s("./CTFd/themes/core/assets/js/CTFd.js"))&&n.__esModule?n:{default:n},a=s("./CTFd/themes/admin/assets/js/styles.js");var o={name:"HintEditForm",props:{hint_id:Number},data:function(){return{cost:0,content:null}},watch:{hint_id:{immediate:!0,handler:function(e){null!==e&&this.loadHint()}}},methods:{loadHint:function(){var s=this;i.default.fetch("/api/v1/hints/".concat(this.$props.hint_id,"?preview=true"),{method:"GET",credentials:"same-origin",headers:{Accept:"application/json","Content-Type":"application/json"}}).then(function(e){return e.json()}).then(function(e){var t;e.success&&(t=e.data,s.cost=t.cost,s.content=t.content,s.$nextTick(function(){setTimeout(function(){var e=s.$refs.content;(0,a.bindMarkdownEditor)(e),e.mde.codemirror.getDoc().setValue(e.value),e.mde.codemirror.refresh()},100)}))})},getCost:function(){return this.cost||0},getContent:function(){return this.$refs.content.value},updateHint:function(){var t=this,e={challenge_id:this.$props.challenge_id,content:this.getContent(),cost:this.getCost()};i.default.fetch("/api/v1/hints/".concat(this.$props.hint_id),{method:"PATCH",credentials:"same-origin",headers:{Accept:"application/json","Content-Type":"application/json"},body:JSON.stringify(e)}).then(function(e){return e.json()}).then(function(e){e.success&&t.$emit("refreshHints",t.$options.name)})}},mounted:function(){this.hint_id&&this.loadHint()},created:function(){this.hint_id&&this.loadHint()}};t.default=o},"./node_modules/babel-loader/lib/index.js?!./node_modules/vue-loader/lib/index.js?!./CTFd/themes/admin/assets/js/components/hints/HintsList.vue?vue&type=script&lang=js&":function(e,t,s){Object.defineProperty(t,"__esModule",{value:!0}),t.default=void 0;var n=s("./CTFd/themes/core/assets/js/ezq.js"),i=l(s("./CTFd/themes/core/assets/js/CTFd.js")),a=l(s("./CTFd/themes/admin/assets/js/components/hints/HintCreationForm.vue")),o=l(s("./CTFd/themes/admin/assets/js/components/hints/HintEditForm.vue"));function l(e){return e&&e.__esModule?e:{default:e}}var d={components:{HintCreationForm:a.default,HintEditForm:o.default},props:{challenge_id:Number},data:function(){return{hints:[],editing_hint_id:null}},methods:{loadHints:function(){var t=this;i.default.fetch("/api/v1/challenges/".concat(this.$props.challenge_id,"/hints"),{method:"GET",credentials:"same-origin",headers:{Accept:"application/json","Content-Type":"application/json"}}).then(function(e){return e.json()}).then(function(e){e.success&&(t.hints=e.data)})},addHint:function(){var e=this.$refs.HintCreationForm.$el;$(e).modal()},editHint:function(e){this.editing_hint_id=e;var t=this.$refs.HintEditForm.$el;$(t).modal()},refreshHints:function(e){var t;switch(this.loadHints(),e){case"HintCreationForm":t=this.$refs.HintCreationForm.$el,$(t).modal("hide");break;case"HintEditForm":t=this.$refs.HintEditForm.$el,$(t).modal("hide")}},deleteHint:function(e){var t=this;(0,n.ezQuery)({title:"Delete Hint",body:"Are you sure you want to delete this hint?",success:function(){i.default.fetch("/api/v1/hints/".concat(e),{method:"DELETE"}).then(function(e){return e.json()}).then(function(e){e.success&&t.loadHints()})}})}},created:function(){this.loadHints()}};t.default=d},"./node_modules/babel-loader/lib/index.js?!./node_modules/vue-loader/lib/index.js?!./CTFd/themes/admin/assets/js/components/notifications/Notification.vue?vue&type=script&lang=js&":function(e,t,s){Object.defineProperty(t,"__esModule",{value:!0}),t.default=void 0;var n=o(s("./CTFd/themes/core/assets/js/CTFd.js")),i=o(s("./node_modules/dayjs/dayjs.min.js")),a=o(s("./node_modules/highlight.js/lib/index.js"));function o(e){return e&&e.__esModule?e:{default:e}}var l={props:{id:Number,title:String,content:String,html:String,date:String},methods:{localDate:function(){return(0,i.default)(this.date).format("MMMM Do, h:mm:ss A")},deleteNotification:function(){var t=this;confirm("Are you sure you want to delete this notification?")&&n.default.api.delete_notification({notificationId:this.id}).then(function(e){e.success&&(t.$destroy(),t.$el.parentNode.removeChild(t.$el))})}},mounted:function(){this.$el.querySelectorAll("pre code").forEach(function(e){a.default.highlightBlock(e)})}};t.default=l},"./node_modules/babel-loader/lib/index.js?!./node_modules/vue-loader/lib/index.js?!./CTFd/themes/admin/assets/js/components/requirements/Requirements.vue?vue&type=script&lang=js&":function(e,t,s){Object.defineProperty(t,"__esModule",{value:!0}),t.default=void 0;var n,i=(n=s("./CTFd/themes/core/assets/js/CTFd.js"))&&n.__esModule?n:{default:n};var a={props:{challenge_id:Number},data:function(){return{challenges:[],requirements:{},selectedRequirements:[],selectedAnonymize:!1}},computed:{newRequirements:function(){var e=this.requirements.prerequisites||[],t=this.requirements.anonymize||!1,s=JSON.stringify(e.sort())!==JSON.stringify(this.selectedRequirements.sort()),n=t!==this.selectedAnonymize;return s||n},requiredChallenges:function(){var t=this,s=this.requirements.prerequisites||[];return this.challenges.filter(function(e){return e.id!==t.$props.challenge_id&&s.includes(e.id)})},otherChallenges:function(){var t=this,s=this.requirements.prerequisites||[];return this.challenges.filter(function(e){return e.id!==t.$props.challenge_id&&!s.includes(e.id)})}},methods:{loadChallenges:function(){var t=this;i.default.fetch("/api/v1/challenges?view=admin",{method:"GET",credentials:"same-origin",headers:{Accept:"application/json","Content-Type":"application/json"}}).then(function(e){return e.json()}).then(function(e){e.success&&(t.challenges=e.data)})},getChallengeNameById:function(t){var e=this.challenges.find(function(e){return e.id===t});return e?e.name:""},loadRequirements:function(){var t=this;i.default.fetch("/api/v1/challenges/".concat(this.$props.challenge_id,"/requirements"),{method:"GET",credentials:"same-origin",headers:{Accept:"application/json","Content-Type":"application/json"}}).then(function(e){return e.json()}).then(function(e){e.success&&(t.requirements=e.data||{},t.selectedRequirements=t.requirements.prerequisites||[],t.selectedAnonymize=t.requirements.anonymize||!1)})},updateRequirements:function(){var t=this,e={requirements:{prerequisites:this.selectedRequirements}};this.selectedAnonymize&&(e.requirements.anonymize=!0),i.default.fetch("/api/v1/challenges/".concat(this.$props.challenge_id),{method:"PATCH",credentials:"same-origin",headers:{Accept:"application/json","Content-Type":"application/json"},body:JSON.stringify(e)}).then(function(e){return e.json()}).then(function(e){e.success&&t.loadRequirements()})}},created:function(){this.loadChallenges(),this.loadRequirements()}};t.default=a},"./node_modules/babel-loader/lib/index.js?!./node_modules/vue-loader/lib/index.js?!./CTFd/themes/admin/assets/js/components/tags/TagsList.vue?vue&type=script&lang=js&":function(e,t,s){Object.defineProperty(t,"__esModule",{value:!0}),t.default=void 0;i(s("./node_modules/jquery/dist/jquery.js"));var n=i(s("./CTFd/themes/core/assets/js/CTFd.js"));function i(e){return e&&e.__esModule?e:{default:e}}var a={props:{challenge_id:Number},data:function(){return{tags:[],tagValue:""}},methods:{loadTags:function(){var t=this;n.default.fetch("/api/v1/challenges/".concat(this.$props.challenge_id,"/tags"),{method:"GET",credentials:"same-origin",headers:{Accept:"application/json","Content-Type":"application/json"}}).then(function(e){return e.json()}).then(function(e){e.success&&(t.tags=e.data)})},addTag:function(){var t=this,e={value:this.tagValue,challenge:this.$props.challenge_id};n.default.api.post_tag_list({},e).then(function(e){e.success&&(t.tagValue="",t.loadTags())})},deleteTag:function(e){var t=this;n.default.api.delete_tag({tagId:e}).then(function(e){e.success&&t.loadTags()})}},created:function(){this.loadTags()}};t.default=a},"./node_modules/babel-loader/lib/index.js?!./node_modules/vue-loader/lib/index.js?!./CTFd/themes/admin/assets/js/components/teams/UserAddForm.vue?vue&type=script&lang=js&":function(e,t,s){Object.defineProperty(t,"__esModule",{value:!0}),t.default=void 0;var n,i=(n=s("./CTFd/themes/core/assets/js/CTFd.js"))&&n.__esModule?n:{default:n},a=s("./CTFd/themes/core/assets/js/ezq.js"),o=s("./CTFd/themes/core/assets/js/utils.js");var l={name:"UserAddForm",props:{team_id:Number},data:function(){return{searchedName:"",awaitingSearch:!1,emptyResults:!1,userResults:[],selectedResultIdx:0,selectedUsers:[]}},methods:{searchUsers:function(){var t=this;this.selectedResultIdx=0,""!=this.searchedName?i.default.fetch("/api/v1/users?view=admin&field=name&q=".concat(this.searchedName),{method:"GET",credentials:"same-origin",headers:{Accept:"application/json","Content-Type":"application/json"}}).then(function(e){return e.json()}).then(function(e){e.success&&(t.userResults=e.data.slice(0,10))}):this.userResults=[]},moveCursor:function(e){switch(e){case"up":this.selectedResultIdx&&--this.selectedResultIdx;break;case"down":this.selectedResultIdx
".concat(e,"

Are you sure you want to remove them from their current teams and add them to this one?

All of their challenge solves, attempts, awards, and unlocked hints will also be deleted!"),success:function(){t.handleRemoveUsersFromTeams().then(function(e){t.handleAddUsersRequest().then(function(e){window.location.reload()})})}})):this.handleAddUsersRequest().then(function(e){window.location.reload()})}},watch:{searchedName:function(){var e=this;!1===this.awaitingSearch&&setTimeout(function(){e.searchUsers(),e.awaitingSearch=!1},1e3),this.awaitingSearch=!0}}};t.default=l},"./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&":function(e,t,s){(t=s("./node_modules/css-loader/dist/runtime/api.js")(!1)).push([e.i,"\n.card .close[data-v-1fd2c08a] {\n opacity: 0;\n transition: 0.2s;\n}\n.card:hover .close[data-v-1fd2c08a] {\n opacity: 0.5;\n}\n.close[data-v-1fd2c08a]:hover {\n opacity: 0.75 !important;\n}\n.comment-card-leave[data-v-1fd2c08a] {\n max-height: 200px;\n}\n.comment-card-leave-to[data-v-1fd2c08a] {\n max-height: 0;\n}\n.comment-card-active[data-v-1fd2c08a] {\n position: absolute;\n}\n.comment-card-enter-active[data-v-1fd2c08a],\n.comment-card-move[data-v-1fd2c08a],\n.comment-card-leave-active[data-v-1fd2c08a] {\n transition: all 0.3s;\n}\n",""]),e.exports=t},"./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/requirements/Requirements.vue?vue&type=style&index=0&id=f3bc9efa&scoped=true&lang=css&":function(e,t,s){(t=s("./node_modules/css-loader/dist/runtime/api.js")(!1)).push([e.i,"\n.flip-list-move[data-v-f3bc9efa] {\n transition: transform 0.5s ease;\n}\n\n/* https://stackoverflow.com/a/34299947 */\n/* https://dabblet.com/gist/2462915 */\n/* https://lea.verou.me/2012/04/background-attachment-local/ */\n/* magical CSS rules for scrolling indication without scrollbar */\n/* prettier-ignore */\n.scrollbox[data-v-f3bc9efa] {\n\toverflow: auto;\n\tmax-height: 40vh;\n\n\tbackground:\n\t\t/* Shadow covers */\n\t\tlinear-gradient(white 30%, rgba(255,255,255,0)),\n\t\tlinear-gradient(rgba(255,255,255,0), white 70%) 0 100%,\n\n\t\t/* Shadows */\n\t\tradial-gradient(50% 0, farthest-side, rgba(0,0,0,.2), rgba(0,0,0,0)),\n\t\tradial-gradient(50% 100%,farthest-side, rgba(0,0,0,.2), rgba(0,0,0,0)) 0 100%;\n\tbackground:\n\t\t/* Shadow covers */\n\t\tlinear-gradient(white 30%, rgba(255,255,255,0)),\n\t\tlinear-gradient(rgba(255,255,255,0), white 70%) 0 100%,\n\n\t\t/* Shadows */\n\t\tradial-gradient(farthest-side at 50% 0, rgba(0,0,0,.2), rgba(0,0,0,0)),\n\t\tradial-gradient(farthest-side at 50% 100%, rgba(0,0,0,.2), rgba(0,0,0,0)) 0 100%;\n\tbackground-repeat: no-repeat;\n\tbackground-color: white;\n\tbackground-size: 100% 40px, 100% 40px, 100% 14px, 100% 14px;\n\n\t/* Opera doesn't support this in the shorthand */\n\tbackground-attachment: local, local, scroll, scroll;\n}\n",""]),e.exports=t},"./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&":function(e,t,s){var n=s("./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&");"string"==typeof n&&(n=[[e.i,n,""]]),n.locals&&(e.exports=n.locals);(0,s("./node_modules/vue-style-loader/lib/addStylesClient.js").default)("0b5f1745",n,!1,{})},"./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/requirements/Requirements.vue?vue&type=style&index=0&id=f3bc9efa&scoped=true&lang=css&":function(e,t,s){var n=s("./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/requirements/Requirements.vue?vue&type=style&index=0&id=f3bc9efa&scoped=true&lang=css&");"string"==typeof n&&(n=[[e.i,n,""]]),n.locals&&(e.exports=n.locals);(0,s("./node_modules/vue-style-loader/lib/addStylesClient.js").default)("477101d6",n,!1,{})}}]); \ No newline at end of file +(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(""+_this.editForm+"").find("script").each(function(){eval((0,_jquery.default)(this).html())})},100)})})},updateFlag:function(e){var t=this,s=(0,_jquery.default)(e.target).serializeJSON(!0);_CTFd.default.fetch("/api/v1/flags/".concat(this.$props.flag_id),{method:"PATCH",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)})}},mounted:function(){this.flag_id&&this.loadFlag()},created:function(){this.flag_id&&this.loadFlag()}};exports.default=_default},"./node_modules/babel-loader/lib/index.js?!./node_modules/vue-loader/lib/index.js?!./CTFd/themes/admin/assets/js/components/flags/FlagList.vue?vue&type=script&lang=js&":function(e,t,s){Object.defineProperty(t,"__esModule",{value:!0}),t.default=void 0;var n=l(s("./node_modules/jquery/dist/jquery.js")),i=l(s("./CTFd/themes/core/assets/js/CTFd.js")),a=l(s("./CTFd/themes/admin/assets/js/components/flags/FlagCreationForm.vue")),o=l(s("./CTFd/themes/admin/assets/js/components/flags/FlagEditForm.vue"));function l(e){return e&&e.__esModule?e:{default:e}}var d={components:{FlagCreationForm:a.default,FlagEditForm:o.default},props:{challenge_id:Number},data:function(){return{flags:[],editing_flag_id:null}},methods:{loadFlags:function(){var t=this;i.default.fetch("/api/v1/challenges/".concat(this.$props.challenge_id,"/flags"),{method:"GET",credentials:"same-origin",headers:{Accept:"application/json","Content-Type":"application/json"}}).then(function(e){return e.json()}).then(function(e){e.success&&(t.flags=e.data)})},refreshFlags:function(e){var t;switch(this.loadFlags(),e){case"FlagEditForm":t=this.$refs.FlagEditForm.$el,(0,n.default)(t).modal("hide");break;case"FlagCreationForm":t=this.$refs.FlagCreationForm.$el,(0,n.default)(t).modal("hide")}},addFlag:function(){var e=this.$refs.FlagCreationForm.$el;(0,n.default)(e).modal()},editFlag:function(e){this.editing_flag_id=e;var t=this.$refs.FlagEditForm.$el;(0,n.default)(t).modal()},deleteFlag:function(e){var t=this;confirm("Are you sure you'd like to delete this flag?")&&i.default.fetch("/api/v1/flags/".concat(e),{method:"DELETE"}).then(function(e){return e.json()}).then(function(e){e.success&&t.loadFlags()})}},created:function(){this.loadFlags()}};t.default=d},"./node_modules/babel-loader/lib/index.js?!./node_modules/vue-loader/lib/index.js?!./CTFd/themes/admin/assets/js/components/hints/HintCreationForm.vue?vue&type=script&lang=js&":function(e,t,s){Object.defineProperty(t,"__esModule",{value:!0}),t.default=void 0;var n={name:"HintCreationForm",props:{challenge_id:Number},data:function(){return{cost:0}},methods:{getCost:function(){return this.cost||0},getContent:function(){return this.$refs.content.value},submitHint:function(){var t=this,e={challenge_id:this.$props.challenge_id,content:this.getContent(),cost:this.getCost()};CTFd.fetch("/api/v1/hints",{method:"POST",credentials:"same-origin",headers:{Accept:"application/json","Content-Type":"application/json"},body:JSON.stringify(e)}).then(function(e){return e.json()}).then(function(e){e.success&&t.$emit("refreshHints",t.$options.name)})}}};t.default=n},"./node_modules/babel-loader/lib/index.js?!./node_modules/vue-loader/lib/index.js?!./CTFd/themes/admin/assets/js/components/hints/HintEditForm.vue?vue&type=script&lang=js&":function(e,t,s){Object.defineProperty(t,"__esModule",{value:!0}),t.default=void 0;var n,i=(n=s("./CTFd/themes/core/assets/js/CTFd.js"))&&n.__esModule?n:{default:n},a=s("./CTFd/themes/admin/assets/js/styles.js");var o={name:"HintEditForm",props:{hint_id:Number},data:function(){return{cost:0,content:null}},watch:{hint_id:{immediate:!0,handler:function(e){null!==e&&this.loadHint()}}},methods:{loadHint:function(){var s=this;i.default.fetch("/api/v1/hints/".concat(this.$props.hint_id,"?preview=true"),{method:"GET",credentials:"same-origin",headers:{Accept:"application/json","Content-Type":"application/json"}}).then(function(e){return e.json()}).then(function(e){var t;e.success&&(t=e.data,s.cost=t.cost,s.content=t.content,s.$nextTick(function(){setTimeout(function(){var e=s.$refs.content;(0,a.bindMarkdownEditor)(e),e.mde.codemirror.getDoc().setValue(e.value),e.mde.codemirror.refresh()},100)}))})},getCost:function(){return this.cost||0},getContent:function(){return this.$refs.content.value},updateHint:function(){var t=this,e={challenge_id:this.$props.challenge_id,content:this.getContent(),cost:this.getCost()};i.default.fetch("/api/v1/hints/".concat(this.$props.hint_id),{method:"PATCH",credentials:"same-origin",headers:{Accept:"application/json","Content-Type":"application/json"},body:JSON.stringify(e)}).then(function(e){return e.json()}).then(function(e){e.success&&t.$emit("refreshHints",t.$options.name)})}},mounted:function(){this.hint_id&&this.loadHint()},created:function(){this.hint_id&&this.loadHint()}};t.default=o},"./node_modules/babel-loader/lib/index.js?!./node_modules/vue-loader/lib/index.js?!./CTFd/themes/admin/assets/js/components/hints/HintsList.vue?vue&type=script&lang=js&":function(e,t,s){Object.defineProperty(t,"__esModule",{value:!0}),t.default=void 0;var n=s("./CTFd/themes/core/assets/js/ezq.js"),i=l(s("./CTFd/themes/core/assets/js/CTFd.js")),a=l(s("./CTFd/themes/admin/assets/js/components/hints/HintCreationForm.vue")),o=l(s("./CTFd/themes/admin/assets/js/components/hints/HintEditForm.vue"));function l(e){return e&&e.__esModule?e:{default:e}}var d={components:{HintCreationForm:a.default,HintEditForm:o.default},props:{challenge_id:Number},data:function(){return{hints:[],editing_hint_id:null}},methods:{loadHints:function(){var t=this;i.default.fetch("/api/v1/challenges/".concat(this.$props.challenge_id,"/hints"),{method:"GET",credentials:"same-origin",headers:{Accept:"application/json","Content-Type":"application/json"}}).then(function(e){return e.json()}).then(function(e){e.success&&(t.hints=e.data)})},addHint:function(){var e=this.$refs.HintCreationForm.$el;$(e).modal()},editHint:function(e){this.editing_hint_id=e;var t=this.$refs.HintEditForm.$el;$(t).modal()},refreshHints:function(e){var t;switch(this.loadHints(),e){case"HintCreationForm":t=this.$refs.HintCreationForm.$el,$(t).modal("hide");break;case"HintEditForm":t=this.$refs.HintEditForm.$el,$(t).modal("hide")}},deleteHint:function(e){var t=this;(0,n.ezQuery)({title:"Delete Hint",body:"Are you sure you want to delete this hint?",success:function(){i.default.fetch("/api/v1/hints/".concat(e),{method:"DELETE"}).then(function(e){return e.json()}).then(function(e){e.success&&t.loadHints()})}})}},created:function(){this.loadHints()}};t.default=d},"./node_modules/babel-loader/lib/index.js?!./node_modules/vue-loader/lib/index.js?!./CTFd/themes/admin/assets/js/components/notifications/Notification.vue?vue&type=script&lang=js&":function(e,t,s){Object.defineProperty(t,"__esModule",{value:!0}),t.default=void 0;var n=o(s("./CTFd/themes/core/assets/js/CTFd.js")),i=o(s("./node_modules/dayjs/dayjs.min.js")),a=o(s("./node_modules/highlight.js/lib/index.js"));function o(e){return e&&e.__esModule?e:{default:e}}var l={props:{id:Number,title:String,content:String,html:String,date:String},methods:{localDate:function(){return(0,i.default)(this.date).format("MMMM Do, h:mm:ss A")},deleteNotification:function(){var t=this;confirm("Are you sure you want to delete this notification?")&&n.default.api.delete_notification({notificationId:this.id}).then(function(e){e.success&&(t.$destroy(),t.$el.parentNode.removeChild(t.$el))})}},mounted:function(){this.$el.querySelectorAll("pre code").forEach(function(e){a.default.highlightBlock(e)})}};t.default=l},"./node_modules/babel-loader/lib/index.js?!./node_modules/vue-loader/lib/index.js?!./CTFd/themes/admin/assets/js/components/requirements/Requirements.vue?vue&type=script&lang=js&":function(e,t,s){Object.defineProperty(t,"__esModule",{value:!0}),t.default=void 0;var n,i=(n=s("./CTFd/themes/core/assets/js/CTFd.js"))&&n.__esModule?n:{default:n};var a={props:{challenge_id:Number},data:function(){return{challenges:[],requirements:{},selectedRequirements:[],selectedAnonymize:!1}},computed:{newRequirements:function(){var e=this.requirements.prerequisites||[],t=this.requirements.anonymize||!1,s=JSON.stringify(e.sort())!==JSON.stringify(this.selectedRequirements.sort()),n=t!==this.selectedAnonymize;return s||n},requiredChallenges:function(){var t=this,s=this.requirements.prerequisites||[];return this.challenges.filter(function(e){return e.id!==t.$props.challenge_id&&s.includes(e.id)})},otherChallenges:function(){var t=this,s=this.requirements.prerequisites||[];return this.challenges.filter(function(e){return e.id!==t.$props.challenge_id&&!s.includes(e.id)})}},methods:{loadChallenges:function(){var t=this;i.default.fetch("/api/v1/challenges?view=admin",{method:"GET",credentials:"same-origin",headers:{Accept:"application/json","Content-Type":"application/json"}}).then(function(e){return e.json()}).then(function(e){e.success&&(t.challenges=e.data)})},getChallengeNameById:function(t){var e=this.challenges.find(function(e){return e.id===t});return e?e.name:""},loadRequirements:function(){var t=this;i.default.fetch("/api/v1/challenges/".concat(this.$props.challenge_id,"/requirements"),{method:"GET",credentials:"same-origin",headers:{Accept:"application/json","Content-Type":"application/json"}}).then(function(e){return e.json()}).then(function(e){e.success&&(t.requirements=e.data||{},t.selectedRequirements=t.requirements.prerequisites||[],t.selectedAnonymize=t.requirements.anonymize||!1)})},updateRequirements:function(){var t=this,e={requirements:{prerequisites:this.selectedRequirements}};this.selectedAnonymize&&(e.requirements.anonymize=!0),i.default.fetch("/api/v1/challenges/".concat(this.$props.challenge_id),{method:"PATCH",credentials:"same-origin",headers:{Accept:"application/json","Content-Type":"application/json"},body:JSON.stringify(e)}).then(function(e){return e.json()}).then(function(e){e.success&&t.loadRequirements()})}},created:function(){this.loadChallenges(),this.loadRequirements()}};t.default=a},"./node_modules/babel-loader/lib/index.js?!./node_modules/vue-loader/lib/index.js?!./CTFd/themes/admin/assets/js/components/tags/TagsList.vue?vue&type=script&lang=js&":function(e,t,s){Object.defineProperty(t,"__esModule",{value:!0}),t.default=void 0;i(s("./node_modules/jquery/dist/jquery.js"));var n=i(s("./CTFd/themes/core/assets/js/CTFd.js"));function i(e){return e&&e.__esModule?e:{default:e}}var a={props:{challenge_id:Number},data:function(){return{tags:[],tagValue:""}},methods:{loadTags:function(){var t=this;n.default.fetch("/api/v1/challenges/".concat(this.$props.challenge_id,"/tags"),{method:"GET",credentials:"same-origin",headers:{Accept:"application/json","Content-Type":"application/json"}}).then(function(e){return e.json()}).then(function(e){e.success&&(t.tags=e.data)})},addTag:function(){var t=this,e={value:this.tagValue,challenge:this.$props.challenge_id};n.default.api.post_tag_list({},e).then(function(e){e.success&&(t.tagValue="",t.loadTags())})},deleteTag:function(e){var t=this;n.default.api.delete_tag({tagId:e}).then(function(e){e.success&&t.loadTags()})}},created:function(){this.loadTags()}};t.default=a},"./node_modules/babel-loader/lib/index.js?!./node_modules/vue-loader/lib/index.js?!./CTFd/themes/admin/assets/js/components/teams/UserAddForm.vue?vue&type=script&lang=js&":function(e,t,s){Object.defineProperty(t,"__esModule",{value:!0}),t.default=void 0;var n,i=(n=s("./CTFd/themes/core/assets/js/CTFd.js"))&&n.__esModule?n:{default:n},a=s("./CTFd/themes/core/assets/js/ezq.js"),o=s("./CTFd/themes/core/assets/js/utils.js");var l={name:"UserAddForm",props:{team_id:Number},data:function(){return{searchedName:"",awaitingSearch:!1,emptyResults:!1,userResults:[],selectedResultIdx:0,selectedUsers:[]}},methods:{searchUsers:function(){var t=this;this.selectedResultIdx=0,""!=this.searchedName?i.default.fetch("/api/v1/users?view=admin&field=name&q=".concat(this.searchedName),{method:"GET",credentials:"same-origin",headers:{Accept:"application/json","Content-Type":"application/json"}}).then(function(e){return e.json()}).then(function(e){e.success&&(t.userResults=e.data.slice(0,10))}):this.userResults=[]},moveCursor:function(e){switch(e){case"up":this.selectedResultIdx&&--this.selectedResultIdx;break;case"down":this.selectedResultIdx
".concat(e,"

Are you sure you want to remove them from their current teams and add them to this one?

All of their challenge solves, attempts, awards, and unlocked hints will also be deleted!"),success:function(){t.handleRemoveUsersFromTeams().then(function(e){t.handleAddUsersRequest().then(function(e){window.location.reload()})})}})):this.handleAddUsersRequest().then(function(e){window.location.reload()})}},watch:{searchedName:function(){var e=this;!1===this.awaitingSearch&&setTimeout(function(){e.searchUsers(),e.awaitingSearch=!1},1e3),this.awaitingSearch=!0}}};t.default=l},"./node_modules/babel-loader/lib/index.js?!./node_modules/vue-loader/lib/index.js?!./CTFd/themes/admin/assets/js/components/topics/TopicsList.vue?vue&type=script&lang=js&":function(e,t,s){Object.defineProperty(t,"__esModule",{value:!0}),t.default=void 0;var n,i=(n=s("./CTFd/themes/core/assets/js/CTFd.js"))&&n.__esModule?n:{default:n};var a={props:{challenge_id:Number},data:function(){return{topics:[],topicValue:"",searchedTopic:"",topicResults:[],selectedResultIdx:0,awaitingSearch:!1}},methods:{loadTopics:function(){var t=this;i.default.fetch("/api/v1/challenges/".concat(this.$props.challenge_id,"/topics"),{method:"GET",credentials:"same-origin",headers:{Accept:"application/json","Content-Type":"application/json"}}).then(function(e){return e.json()}).then(function(e){e.success&&(t.topics=e.data)})},searchTopics:function(){var t=this;this.selectedResultIdx=0,""!=this.topicValue?i.default.fetch("/api/v1/topics?field=value&q=".concat(this.topicValue),{method:"GET",credentials:"same-origin",headers:{Accept:"application/json","Content-Type":"application/json"}}).then(function(e){return e.json()}).then(function(e){e.success&&(t.topicResults=e.data.slice(0,10))}):this.topicResults=[]},addTopic:function(){var e,t=this,s={value:0===this.selectedResultIdx?this.topicValue:(e=this.selectedResultIdx-1,this.topicResults[e].value),challenge:this.$props.challenge_id,type:"challenge"};i.default.fetch("/api/v1/topics",{method:"POST",body:JSON.stringify(s)}).then(function(e){return e.json()}).then(function(e){e.success&&(t.topicValue="",t.loadTopics())})},deleteTopic:function(e){var t=this;i.default.fetch("/api/v1/topics?type=challenge&target_id=".concat(e),{method:"DELETE"}).then(function(e){return e.json()}).then(function(e){e.success&&t.loadTopics()})},moveCursor:function(e){switch(e){case"up":this.selectedResultIdx&&--this.selectedResultIdx;break;case"down":this.selectedResultIdx\" + (0, _utils.htmlEntities)(window.CHALLENGE_NAME) + \"\"),\n success: function success() {\n _CTFd[\"default\"].fetch(\"/api/v1/challenges/\" + window.CHALLENGE_ID, {\n method: \"DELETE\"\n }).then(function (response) {\n return response.json();\n }).then(function (response) {\n if (response.success) {\n window.location = _CTFd[\"default\"].config.urlRoot + \"/admin/challenges\";\n }\n });\n }\n });\n });\n (0, _jquery[\"default\"])(\"#challenge-update-container > form\").submit(function (e) {\n e.preventDefault();\n var params = (0, _jquery[\"default\"])(e.target).serializeJSON(true);\n\n _CTFd[\"default\"].fetch(\"/api/v1/challenges/\" + window.CHALLENGE_ID + \"/flags\", {\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 var update_challenge = function update_challenge() {\n _CTFd[\"default\"].fetch(\"/api/v1/challenges/\" + window.CHALLENGE_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 (0, _jquery[\"default\"])(\".challenge-state\").text(response.data.state);\n\n switch (response.data.state) {\n case \"visible\":\n (0, _jquery[\"default\"])(\".challenge-state\").removeClass(\"badge-danger\").addClass(\"badge-success\");\n break;\n\n case \"hidden\":\n (0, _jquery[\"default\"])(\".challenge-state\").removeClass(\"badge-success\").addClass(\"badge-danger\");\n break;\n\n default:\n break;\n }\n\n (0, _ezq.ezToast)({\n title: \"Success\",\n body: \"Your challenge has been updated!\"\n });\n } else {\n var body = \"\";\n\n for (var k in response.errors) {\n body += response.errors[k].join(\"\\n\");\n body += \"\\n\";\n }\n\n (0, _ezq.ezAlert)({\n title: \"Error\",\n body: body,\n button: \"OK\"\n });\n }\n });\n }; // Check if the challenge doesn't have any flags before marking visible\n\n\n if (response.data.length === 0 && params.state === \"visible\") {\n (0, _ezq.ezQuery)({\n title: \"Missing Flags\",\n body: \"This challenge does not have any flags meaning it may be unsolveable. Are you sure you'd like to update this challenge?\",\n success: update_challenge\n });\n } else {\n update_challenge();\n }\n });\n });\n (0, _jquery[\"default\"])(\"#challenge-create-options form\").submit(handleChallengeOptions); // Load FlagList component\n\n if (document.querySelector(\"#challenge-flags\")) {\n var flagList = _vueEsm[\"default\"].extend(_FlagList[\"default\"]);\n\n var vueContainer = document.createElement(\"div\");\n document.querySelector(\"#challenge-flags\").appendChild(vueContainer);\n new flagList({\n propsData: {\n challenge_id: window.CHALLENGE_ID\n }\n }).$mount(vueContainer);\n } // Load TagsList component\n\n\n if (document.querySelector(\"#challenge-tags\")) {\n var tagList = _vueEsm[\"default\"].extend(_TagsList[\"default\"]);\n\n var _vueContainer = document.createElement(\"div\");\n\n document.querySelector(\"#challenge-tags\").appendChild(_vueContainer);\n new tagList({\n propsData: {\n challenge_id: window.CHALLENGE_ID\n }\n }).$mount(_vueContainer);\n } // Load Requirements component\n\n\n if (document.querySelector(\"#prerequisite-add-form\")) {\n var reqsComponent = _vueEsm[\"default\"].extend(_Requirements[\"default\"]);\n\n var _vueContainer2 = document.createElement(\"div\");\n\n document.querySelector(\"#prerequisite-add-form\").appendChild(_vueContainer2);\n new reqsComponent({\n propsData: {\n challenge_id: window.CHALLENGE_ID\n }\n }).$mount(_vueContainer2);\n } // Load ChallengeFilesList component\n\n\n if (document.querySelector(\"#challenge-files\")) {\n var challengeFilesList = _vueEsm[\"default\"].extend(_ChallengeFilesList[\"default\"]);\n\n var _vueContainer3 = document.createElement(\"div\");\n\n document.querySelector(\"#challenge-files\").appendChild(_vueContainer3);\n new challengeFilesList({\n propsData: {\n challenge_id: window.CHALLENGE_ID\n }\n }).$mount(_vueContainer3);\n } // Load HintsList component\n\n\n if (document.querySelector(\"#challenge-hints\")) {\n var hintsList = _vueEsm[\"default\"].extend(_HintsList[\"default\"]);\n\n var _vueContainer4 = document.createElement(\"div\");\n\n document.querySelector(\"#challenge-hints\").appendChild(_vueContainer4);\n new hintsList({\n propsData: {\n challenge_id: window.CHALLENGE_ID\n }\n }).$mount(_vueContainer4);\n } // Because this JS is shared by a few pages,\n // we should only insert the CommentBox if it's actually in use\n\n\n if (document.querySelector(\"#comment-box\")) {\n // Insert CommentBox element\n var commentBox = _vueEsm[\"default\"].extend(_CommentBox[\"default\"]);\n\n var _vueContainer5 = document.createElement(\"div\");\n\n document.querySelector(\"#comment-box\").appendChild(_vueContainer5);\n new commentBox({\n propsData: {\n type: \"challenge\",\n id: window.CHALLENGE_ID\n }\n }).$mount(_vueContainer5);\n }\n\n _jquery[\"default\"].get(_CTFd[\"default\"].config.urlRoot + \"/api/v1/challenges/types\", function (response) {\n var data = response.data;\n loadChalTemplate(data[\"standard\"]);\n (0, _jquery[\"default\"])(\"#create-chals-select input[name=type]\").change(function () {\n var challenge = data[this.value];\n loadChalTemplate(challenge);\n });\n });\n});\n\n//# sourceURL=webpack:///./CTFd/themes/admin/assets/js/pages/challenge.js?"); +eval("\n\n__webpack_require__(/*! ./main */ \"./CTFd/themes/admin/assets/js/pages/main.js\");\n\nvar _utils = __webpack_require__(/*! core/utils */ \"./CTFd/themes/core/assets/js/utils.js\");\n\nvar _jquery = _interopRequireDefault(__webpack_require__(/*! jquery */ \"./node_modules/jquery/dist/jquery.js\"));\n\n__webpack_require__(/*! bootstrap/js/dist/tab */ \"./node_modules/bootstrap/js/dist/tab.js\");\n\nvar _CTFd = _interopRequireDefault(__webpack_require__(/*! core/CTFd */ \"./CTFd/themes/core/assets/js/CTFd.js\"));\n\nvar _ezq = __webpack_require__(/*! core/ezq */ \"./CTFd/themes/core/assets/js/ezq.js\");\n\nvar _helpers = _interopRequireDefault(__webpack_require__(/*! core/helpers */ \"./CTFd/themes/core/assets/js/helpers.js\"));\n\nvar _styles = __webpack_require__(/*! ../styles */ \"./CTFd/themes/admin/assets/js/styles.js\");\n\nvar _vueEsm = _interopRequireDefault(__webpack_require__(/*! vue/dist/vue.esm.browser */ \"./node_modules/vue/dist/vue.esm.browser.js\"));\n\nvar _CommentBox = _interopRequireDefault(__webpack_require__(/*! ../components/comments/CommentBox.vue */ \"./CTFd/themes/admin/assets/js/components/comments/CommentBox.vue\"));\n\nvar _FlagList = _interopRequireDefault(__webpack_require__(/*! ../components/flags/FlagList.vue */ \"./CTFd/themes/admin/assets/js/components/flags/FlagList.vue\"));\n\nvar _Requirements = _interopRequireDefault(__webpack_require__(/*! ../components/requirements/Requirements.vue */ \"./CTFd/themes/admin/assets/js/components/requirements/Requirements.vue\"));\n\nvar _TopicsList = _interopRequireDefault(__webpack_require__(/*! ../components/topics/TopicsList.vue */ \"./CTFd/themes/admin/assets/js/components/topics/TopicsList.vue\"));\n\nvar _TagsList = _interopRequireDefault(__webpack_require__(/*! ../components/tags/TagsList.vue */ \"./CTFd/themes/admin/assets/js/components/tags/TagsList.vue\"));\n\nvar _ChallengeFilesList = _interopRequireDefault(__webpack_require__(/*! ../components/files/ChallengeFilesList.vue */ \"./CTFd/themes/admin/assets/js/components/files/ChallengeFilesList.vue\"));\n\nvar _HintsList = _interopRequireDefault(__webpack_require__(/*! ../components/hints/HintsList.vue */ \"./CTFd/themes/admin/assets/js/components/hints/HintsList.vue\"));\n\nvar _highlight = _interopRequireDefault(__webpack_require__(/*! highlight.js */ \"./node_modules/highlight.js/lib/index.js\"));\n\nfunction _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { \"default\": obj }; }\n\nvar displayHint = function displayHint(data) {\n (0, _ezq.ezAlert)({\n title: \"Hint\",\n body: data.html,\n button: \"Got it!\"\n });\n};\n\nvar loadHint = function loadHint(id) {\n _CTFd[\"default\"].api.get_hint({\n hintId: id,\n preview: true\n }).then(function (response) {\n if (response.data.content) {\n displayHint(response.data);\n return;\n } // displayUnlock(id);\n\n });\n};\n\nfunction renderSubmissionResponse(response, cb) {\n var result = response.data;\n var result_message = (0, _jquery[\"default\"])(\"#result-message\");\n var result_notification = (0, _jquery[\"default\"])(\"#result-notification\");\n var answer_input = (0, _jquery[\"default\"])(\"#submission-input\");\n result_notification.removeClass();\n result_message.text(result.message);\n\n if (result.status === \"authentication_required\") {\n window.location = _CTFd[\"default\"].config.urlRoot + \"/login?next=\" + _CTFd[\"default\"].config.urlRoot + window.location.pathname + window.location.hash;\n return;\n } else if (result.status === \"incorrect\") {\n // Incorrect key\n result_notification.addClass(\"alert alert-danger alert-dismissable text-center\");\n result_notification.slideDown();\n answer_input.removeClass(\"correct\");\n answer_input.addClass(\"wrong\");\n setTimeout(function () {\n answer_input.removeClass(\"wrong\");\n }, 3000);\n } else if (result.status === \"correct\") {\n // Challenge Solved\n result_notification.addClass(\"alert alert-success alert-dismissable text-center\");\n result_notification.slideDown();\n (0, _jquery[\"default\"])(\".challenge-solves\").text(parseInt((0, _jquery[\"default\"])(\".challenge-solves\").text().split(\" \")[0]) + 1 + \" Solves\");\n answer_input.val(\"\");\n answer_input.removeClass(\"wrong\");\n answer_input.addClass(\"correct\");\n } else if (result.status === \"already_solved\") {\n // Challenge already solved\n result_notification.addClass(\"alert alert-info alert-dismissable text-center\");\n result_notification.slideDown();\n answer_input.addClass(\"correct\");\n } else if (result.status === \"paused\") {\n // CTF is paused\n result_notification.addClass(\"alert alert-warning alert-dismissable text-center\");\n result_notification.slideDown();\n } else if (result.status === \"ratelimited\") {\n // Keys per minute too high\n result_notification.addClass(\"alert alert-warning alert-dismissable text-center\");\n result_notification.slideDown();\n answer_input.addClass(\"too-fast\");\n setTimeout(function () {\n answer_input.removeClass(\"too-fast\");\n }, 3000);\n }\n\n setTimeout(function () {\n (0, _jquery[\"default\"])(\".alert\").slideUp();\n (0, _jquery[\"default\"])(\"#challenge-submit\").removeClass(\"disabled-button\");\n (0, _jquery[\"default\"])(\"#challenge-submit\").prop(\"disabled\", false);\n }, 3000);\n\n if (cb) {\n cb(result);\n }\n}\n\nfunction loadChalTemplate(challenge) {\n _CTFd[\"default\"]._internal.challenge = {};\n\n _jquery[\"default\"].getScript(_CTFd[\"default\"].config.urlRoot + challenge.scripts.view, function () {\n var template_data = challenge.create;\n (0, _jquery[\"default\"])(\"#create-chal-entry-div\").html(template_data);\n (0, _styles.bindMarkdownEditors)();\n\n _jquery[\"default\"].getScript(_CTFd[\"default\"].config.urlRoot + challenge.scripts.create, function () {\n (0, _jquery[\"default\"])(\"#create-chal-entry-div form\").submit(function (event) {\n event.preventDefault();\n var params = (0, _jquery[\"default\"])(\"#create-chal-entry-div form\").serializeJSON();\n\n _CTFd[\"default\"].fetch(\"/api/v1/challenges\", {\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 (0, _jquery[\"default\"])(\"#challenge-create-options #challenge_id\").val(response.data.id);\n (0, _jquery[\"default\"])(\"#challenge-create-options\").modal();\n } else {\n var body = \"\";\n\n for (var k in response.errors) {\n body += response.errors[k].join(\"\\n\");\n body += \"\\n\";\n }\n\n (0, _ezq.ezAlert)({\n title: \"Error\",\n body: body,\n button: \"OK\"\n });\n }\n });\n });\n });\n });\n}\n\nfunction handleChallengeOptions(event) {\n event.preventDefault();\n var params = (0, _jquery[\"default\"])(event.target).serializeJSON(true);\n var flag_params = {\n challenge_id: params.challenge_id,\n content: params.flag || \"\",\n type: params.flag_type,\n data: params.flag_data ? params.flag_data : \"\"\n }; // Define a save_challenge function\n\n var save_challenge = function save_challenge() {\n _CTFd[\"default\"].fetch(\"/api/v1/challenges/\" + params.challenge_id, {\n method: \"PATCH\",\n credentials: \"same-origin\",\n headers: {\n Accept: \"application/json\",\n \"Content-Type\": \"application/json\"\n },\n body: JSON.stringify({\n state: params.state\n })\n }).then(function (response) {\n return response.json();\n }).then(function (data) {\n if (data.success) {\n setTimeout(function () {\n window.location = _CTFd[\"default\"].config.urlRoot + \"/admin/challenges/\" + params.challenge_id;\n }, 700);\n }\n });\n };\n\n Promise.all([// Save flag\n new Promise(function (resolve, _reject) {\n if (flag_params.content.length == 0) {\n resolve();\n return;\n }\n\n _CTFd[\"default\"].fetch(\"/api/v1/flags\", {\n method: \"POST\",\n credentials: \"same-origin\",\n headers: {\n Accept: \"application/json\",\n \"Content-Type\": \"application/json\"\n },\n body: JSON.stringify(flag_params)\n }).then(function (response) {\n resolve(response.json());\n });\n }), // Upload files\n new Promise(function (resolve, _reject) {\n var form = event.target;\n var data = {\n challenge: params.challenge_id,\n type: \"challenge\"\n };\n var filepath = (0, _jquery[\"default\"])(form.elements[\"file\"]).val();\n\n if (filepath) {\n _helpers[\"default\"].files.upload(form, data);\n }\n\n resolve();\n })]).then(function (_responses) {\n save_challenge();\n });\n}\n\n(0, _jquery[\"default\"])(function () {\n (0, _jquery[\"default\"])(\".preview-challenge\").click(function (_e) {\n _CTFd[\"default\"]._internal.challenge = {};\n\n _jquery[\"default\"].get(_CTFd[\"default\"].config.urlRoot + \"/api/v1/challenges/\" + window.CHALLENGE_ID, function (response) {\n // Preview should not show any solves\n var challenge_data = response.data;\n challenge_data[\"solves\"] = null;\n\n _jquery[\"default\"].getScript(_CTFd[\"default\"].config.urlRoot + challenge_data.type_data.scripts.view, function () {\n var challenge = _CTFd[\"default\"]._internal.challenge; // Inject challenge data into the plugin\n\n challenge.data = response.data;\n (0, _jquery[\"default\"])(\"#challenge-window\").empty(); // Call preRender function in plugin\n\n challenge.preRender();\n (0, _jquery[\"default\"])(\"#challenge-window\").append(challenge_data.view);\n (0, _jquery[\"default\"])(\"#challenge-window #challenge-input\").addClass(\"form-control\");\n (0, _jquery[\"default\"])(\"#challenge-window #challenge-submit\").addClass(\"btn btn-md btn-outline-secondary float-right\");\n (0, _jquery[\"default\"])(\".challenge-solves\").hide();\n (0, _jquery[\"default\"])(\".nav-tabs a\").click(function (e) {\n e.preventDefault();\n (0, _jquery[\"default\"])(this).tab(\"show\");\n }); // Handle modal toggling\n\n (0, _jquery[\"default\"])(\"#challenge-window\").on(\"hide.bs.modal\", function (_event) {\n (0, _jquery[\"default\"])(\"#challenge-input\").removeClass(\"wrong\");\n (0, _jquery[\"default\"])(\"#challenge-input\").removeClass(\"correct\");\n (0, _jquery[\"default\"])(\"#incorrect-key\").slideUp();\n (0, _jquery[\"default\"])(\"#correct-key\").slideUp();\n (0, _jquery[\"default\"])(\"#already-solved\").slideUp();\n (0, _jquery[\"default\"])(\"#too-fast\").slideUp();\n });\n (0, _jquery[\"default\"])(\".load-hint\").on(\"click\", function (_event) {\n loadHint((0, _jquery[\"default\"])(this).data(\"hint-id\"));\n });\n (0, _jquery[\"default\"])(\"#challenge-submit\").click(function (e) {\n e.preventDefault();\n (0, _jquery[\"default\"])(\"#challenge-submit\").addClass(\"disabled-button\");\n (0, _jquery[\"default\"])(\"#challenge-submit\").prop(\"disabled\", true);\n\n _CTFd[\"default\"]._internal.challenge.submit(true).then(renderSubmissionResponse); // Preview passed as true\n\n });\n (0, _jquery[\"default\"])(\"#challenge-input\").keyup(function (event) {\n if (event.keyCode == 13) {\n (0, _jquery[\"default\"])(\"#challenge-submit\").click();\n }\n });\n challenge.postRender();\n (0, _jquery[\"default\"])(\"#challenge-window\").find(\"pre code\").each(function (_idx) {\n _highlight[\"default\"].highlightBlock(this);\n });\n window.location.replace(window.location.href.split(\"#\")[0] + \"#preview\");\n (0, _jquery[\"default\"])(\"#challenge-window\").modal();\n });\n });\n });\n (0, _jquery[\"default\"])(\".comments-challenge\").click(function (_event) {\n (0, _jquery[\"default\"])(\"#challenge-comments-window\").modal();\n });\n (0, _jquery[\"default\"])(\".delete-challenge\").click(function (_e) {\n (0, _ezq.ezQuery)({\n title: \"Delete Challenge\",\n body: \"Are you sure you want to delete {0}\".format(\"\" + (0, _utils.htmlEntities)(window.CHALLENGE_NAME) + \"\"),\n success: function success() {\n _CTFd[\"default\"].fetch(\"/api/v1/challenges/\" + window.CHALLENGE_ID, {\n method: \"DELETE\"\n }).then(function (response) {\n return response.json();\n }).then(function (response) {\n if (response.success) {\n window.location = _CTFd[\"default\"].config.urlRoot + \"/admin/challenges\";\n }\n });\n }\n });\n });\n (0, _jquery[\"default\"])(\"#challenge-update-container > form\").submit(function (e) {\n e.preventDefault();\n var params = (0, _jquery[\"default\"])(e.target).serializeJSON(true);\n\n _CTFd[\"default\"].fetch(\"/api/v1/challenges/\" + window.CHALLENGE_ID + \"/flags\", {\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 var update_challenge = function update_challenge() {\n _CTFd[\"default\"].fetch(\"/api/v1/challenges/\" + window.CHALLENGE_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 (0, _jquery[\"default\"])(\".challenge-state\").text(response.data.state);\n\n switch (response.data.state) {\n case \"visible\":\n (0, _jquery[\"default\"])(\".challenge-state\").removeClass(\"badge-danger\").addClass(\"badge-success\");\n break;\n\n case \"hidden\":\n (0, _jquery[\"default\"])(\".challenge-state\").removeClass(\"badge-success\").addClass(\"badge-danger\");\n break;\n\n default:\n break;\n }\n\n (0, _ezq.ezToast)({\n title: \"Success\",\n body: \"Your challenge has been updated!\"\n });\n } else {\n var body = \"\";\n\n for (var k in response.errors) {\n body += response.errors[k].join(\"\\n\");\n body += \"\\n\";\n }\n\n (0, _ezq.ezAlert)({\n title: \"Error\",\n body: body,\n button: \"OK\"\n });\n }\n });\n }; // Check if the challenge doesn't have any flags before marking visible\n\n\n if (response.data.length === 0 && params.state === \"visible\") {\n (0, _ezq.ezQuery)({\n title: \"Missing Flags\",\n body: \"This challenge does not have any flags meaning it may be unsolveable. Are you sure you'd like to update this challenge?\",\n success: update_challenge\n });\n } else {\n update_challenge();\n }\n });\n });\n (0, _jquery[\"default\"])(\"#challenge-create-options form\").submit(handleChallengeOptions); // Load FlagList component\n\n if (document.querySelector(\"#challenge-flags\")) {\n var flagList = _vueEsm[\"default\"].extend(_FlagList[\"default\"]);\n\n var vueContainer = document.createElement(\"div\");\n document.querySelector(\"#challenge-flags\").appendChild(vueContainer);\n new flagList({\n propsData: {\n challenge_id: window.CHALLENGE_ID\n }\n }).$mount(vueContainer);\n } // Load TopicsList component\n\n\n if (document.querySelector(\"#challenge-topics\")) {\n var topicsList = _vueEsm[\"default\"].extend(_TopicsList[\"default\"]);\n\n var _vueContainer = document.createElement(\"div\");\n\n document.querySelector(\"#challenge-topics\").appendChild(_vueContainer);\n new topicsList({\n propsData: {\n challenge_id: window.CHALLENGE_ID\n }\n }).$mount(_vueContainer);\n } // Load TagsList component\n\n\n if (document.querySelector(\"#challenge-tags\")) {\n var tagList = _vueEsm[\"default\"].extend(_TagsList[\"default\"]);\n\n var _vueContainer2 = document.createElement(\"div\");\n\n document.querySelector(\"#challenge-tags\").appendChild(_vueContainer2);\n new tagList({\n propsData: {\n challenge_id: window.CHALLENGE_ID\n }\n }).$mount(_vueContainer2);\n } // Load Requirements component\n\n\n if (document.querySelector(\"#prerequisite-add-form\")) {\n var reqsComponent = _vueEsm[\"default\"].extend(_Requirements[\"default\"]);\n\n var _vueContainer3 = document.createElement(\"div\");\n\n document.querySelector(\"#prerequisite-add-form\").appendChild(_vueContainer3);\n new reqsComponent({\n propsData: {\n challenge_id: window.CHALLENGE_ID\n }\n }).$mount(_vueContainer3);\n } // Load ChallengeFilesList component\n\n\n if (document.querySelector(\"#challenge-files\")) {\n var challengeFilesList = _vueEsm[\"default\"].extend(_ChallengeFilesList[\"default\"]);\n\n var _vueContainer4 = document.createElement(\"div\");\n\n document.querySelector(\"#challenge-files\").appendChild(_vueContainer4);\n new challengeFilesList({\n propsData: {\n challenge_id: window.CHALLENGE_ID\n }\n }).$mount(_vueContainer4);\n } // Load HintsList component\n\n\n if (document.querySelector(\"#challenge-hints\")) {\n var hintsList = _vueEsm[\"default\"].extend(_HintsList[\"default\"]);\n\n var _vueContainer5 = document.createElement(\"div\");\n\n document.querySelector(\"#challenge-hints\").appendChild(_vueContainer5);\n new hintsList({\n propsData: {\n challenge_id: window.CHALLENGE_ID\n }\n }).$mount(_vueContainer5);\n } // Because this JS is shared by a few pages,\n // we should only insert the CommentBox if it's actually in use\n\n\n if (document.querySelector(\"#comment-box\")) {\n // Insert CommentBox element\n var commentBox = _vueEsm[\"default\"].extend(_CommentBox[\"default\"]);\n\n var _vueContainer6 = document.createElement(\"div\");\n\n document.querySelector(\"#comment-box\").appendChild(_vueContainer6);\n new commentBox({\n propsData: {\n type: \"challenge\",\n id: window.CHALLENGE_ID\n }\n }).$mount(_vueContainer6);\n }\n\n _jquery[\"default\"].get(_CTFd[\"default\"].config.urlRoot + \"/api/v1/challenges/types\", function (response) {\n var data = response.data;\n loadChalTemplate(data[\"standard\"]);\n (0, _jquery[\"default\"])(\"#create-chals-select input[name=type]\").change(function () {\n var challenge = data[this.value];\n loadChalTemplate(challenge);\n });\n });\n});\n\n//# sourceURL=webpack:///./CTFd/themes/admin/assets/js/pages/challenge.js?"); /***/ }) diff --git a/CTFd/themes/admin/static/js/pages/challenge.min.js b/CTFd/themes/admin/static/js/pages/challenge.min.js index 10d7bd586..cb91269e2 100644 --- a/CTFd/themes/admin/static/js/pages/challenge.min.js +++ b/CTFd/themes/admin/static/js/pages/challenge.min.js @@ -1 +1 @@ -!function(d){function e(e){for(var t,n,i=e[0],o=e[1],a=e[2],r=0,s=[];r"+(0,u.htmlEntities)(window.CHALLENGE_NAME)+""),success:function(){m.default.fetch("/api/v1/challenges/"+window.CHALLENGE_ID,{method:"DELETE"}).then(function(e){return e.json()}).then(function(e){e.success&&(window.location=m.default.config.urlRoot+"/admin/challenges")})}})}),(0,f.default)("#challenge-update-container > form").submit(function(e){e.preventDefault();var n=(0,f.default)(e.target).serializeJSON(!0);m.default.fetch("/api/v1/challenges/"+window.CHALLENGE_ID+"/flags",{method:"GET",credentials:"same-origin",headers:{Accept:"application/json","Content-Type":"application/json"}}).then(function(e){return e.json()}).then(function(e){function t(){m.default.fetch("/api/v1/challenges/"+window.CHALLENGE_ID,{method:"PATCH",credentials:"same-origin",headers:{Accept:"application/json","Content-Type":"application/json"},body:JSON.stringify(n)}).then(function(e){return e.json()}).then(function(e){if(e.success){switch((0,f.default)(".challenge-state").text(e.data.state),e.data.state){case"visible":(0,f.default)(".challenge-state").removeClass("badge-danger").addClass("badge-success");break;case"hidden":(0,f.default)(".challenge-state").removeClass("badge-success").addClass("badge-danger")}(0,h.ezToast)({title:"Success",body:"Your challenge has been updated!"})}else{var t,n="";for(t in e.errors)n+=e.errors[t].join("\n"),n+="\n";(0,h.ezAlert)({title:"Error",body:n,button:"OK"})}})}0===e.data.length&&"visible"===n.state?(0,h.ezQuery)({title:"Missing Flags",body:"This challenge does not have any flags meaning it may be unsolveable. Are you sure you'd like to update this challenge?",success:t}):t()})}),(0,f.default)("#challenge-create-options form").submit(I),document.querySelector("#challenge-flags")&&(e=g.default.extend(y.default),t=document.createElement("div"),document.querySelector("#challenge-flags").appendChild(t),new e({propsData:{challenge_id:window.CHALLENGE_ID}}).$mount(t)),document.querySelector("#challenge-tags")&&(n=g.default.extend(_.default),i=document.createElement("div"),document.querySelector("#challenge-tags").appendChild(i),new n({propsData:{challenge_id:window.CHALLENGE_ID}}).$mount(i)),document.querySelector("#prerequisite-add-form")&&(o=g.default.extend(j.default),a=document.createElement("div"),document.querySelector("#prerequisite-add-form").appendChild(a),new o({propsData:{challenge_id:window.CHALLENGE_ID}}).$mount(a)),document.querySelector("#challenge-files")&&(r=g.default.extend(b.default),s=document.createElement("div"),document.querySelector("#challenge-files").appendChild(s),new r({propsData:{challenge_id:window.CHALLENGE_ID}}).$mount(s)),document.querySelector("#challenge-hints")&&(d=g.default.extend(T.default),c=document.createElement("div"),document.querySelector("#challenge-hints").appendChild(c),new d({propsData:{challenge_id:window.CHALLENGE_ID}}).$mount(c)),document.querySelector("#comment-box")&&(l=g.default.extend(v.default),p=document.createElement("div"),document.querySelector("#comment-box").appendChild(p),new l({propsData:{type:"challenge",id:window.CHALLENGE_ID}}).$mount(p)),f.default.get(m.default.config.urlRoot+"/api/v1/challenges/types",function(e){var t=e.data;E(t.standard),(0,f.default)("#create-chals-select input[name=type]").change(function(){E(t[this.value])})})})},"./CTFd/themes/admin/assets/js/pages/main.js":function(e,t,n){var i=f(n("./CTFd/themes/core/assets/js/CTFd.js")),o=f(n("./node_modules/jquery/dist/jquery.js")),a=f(n("./node_modules/dayjs/dayjs.min.js")),r=f(n("./node_modules/dayjs/plugin/advancedFormat.js")),s=f(n("./node_modules/nunjucks/browser/nunjucks.js")),d=n("./node_modules/howler/dist/howler.js"),c=f(n("./CTFd/themes/core/assets/js/events.js")),l=f(n("./CTFd/themes/core/assets/js/times.js")),p=f(n("./CTFd/themes/admin/assets/js/styles.js")),u=f(n("./CTFd/themes/core/assets/js/helpers.js"));function f(e){return e&&e.__esModule?e:{default:e}}a.default.extend(r.default),i.default.init(window.init),window.CTFd=i.default,window.helpers=u.default,window.$=o.default,window.dayjs=a.default,window.nunjucks=s.default,window.Howl=d.Howl,(0,o.default)(function(){(0,p.default)(),(0,l.default)(),(0,c.default)(i.default.config.urlRoot)})},"./CTFd/themes/admin/assets/js/styles.js":function(e,t,n){Object.defineProperty(t,"__esModule",{value:!0}),t.showMediaLibrary=l,t.bindMarkdownEditor=p,t.bindMarkdownEditors=u,t.default=void 0,n("./node_modules/bootstrap/dist/js/bootstrap.bundle.js");var i=n("./CTFd/themes/core/assets/js/utils.js"),o=c(n("./node_modules/jquery/dist/jquery.js")),a=c(n("./node_modules/easymde/src/js/easymde.js")),r=c(n("./node_modules/vue/dist/vue.esm.browser.js")),s=c(n("./CTFd/themes/admin/assets/js/components/files/MediaLibrary.vue")),d=c(n("./node_modules/highlight.js/lib/index.js"));function c(e){return e&&e.__esModule?e:{default:e}}function l(e){var t=r.default.extend(s.default),n=document.createElement("div");document.querySelector("main").appendChild(n);var i=new t({propsData:{editor:e}}).$mount(n);(0,o.default)("#media-modal").on("hidden.bs.modal",function(e){i.$destroy(),(0,o.default)("#media-modal").remove()}),(0,o.default)("#media-modal").modal()}function p(e){var t;!1===e.hasOwnProperty("mde")&&(t=new a.default({autoDownloadFontAwesome:!1,toolbar:["bold","italic","heading","|","quote","unordered-list","ordered-list","|","link","image",{name:"media",action:function(e){l(e)},className:"fas fa-file-upload",title:"Media Library"},"|","preview","guide"],element:e,initialValue:(0,o.default)(e).val(),forceSync:!0,minHeight:"200px",renderingConfig:{codeSyntaxHighlighting:!0,hljs:d.default}}),e.mde=t,e.codemirror=t.codemirror,(0,o.default)(e).on("change keyup paste",function(){t.codemirror.getDoc().setValue((0,o.default)(e).val()),t.codemirror.refresh()}))}function u(){(0,o.default)("textarea.markdown").each(function(e,t){p(t)})}t.default=function(){(0,o.default)(":input").each(function(){(0,o.default)(this).data("initial",(0,o.default)(this).val())}),(0,o.default)(function(){(0,o.default)("tr[data-href], td[data-href]").click(function(){var e;return getSelection().toString()||(e=(0,o.default)(this).attr("data-href"))&&(window.location=e),!1}),(0,o.default)("[data-checkbox]").click(function(e){(0,o.default)(e.target).is("input[type=checkbox]")||(0,o.default)(this).find("input[type=checkbox]").click(),e.stopImmediatePropagation()}),(0,o.default)("[data-checkbox-all]").on("click change",function(e){var t=(0,o.default)(this).prop("checked"),n=(0,o.default)(this).index()+1;(0,o.default)(this).closest("table").find("tr td:nth-child(".concat(n,") input[type=checkbox]")).prop("checked",t),e.stopImmediatePropagation()}),(0,o.default)("tr[data-href] a, tr[data-href] button").click(function(e){(0,o.default)(this).attr("data-dismiss")||e.stopPropagation()}),(0,o.default)(".page-select").change(function(){var e=new URL(window.location);e.searchParams.set("page",this.value),window.location.href=e.toString()}),(0,o.default)('a[data-toggle="tab"]').on("shown.bs.tab",function(e){sessionStorage.setItem("activeTab",(0,o.default)(e.target).attr("href"))});var e,t=sessionStorage.getItem("activeTab");t&&((e=(0,o.default)('.nav-tabs a[href="'.concat(t,'"], .nav-pills a[href="').concat(t,'"]'))).length?e.tab("show"):sessionStorage.removeItem("activeTab")),u(),(0,i.makeSortableTables)(),(0,o.default)('[data-toggle="tooltip"]').tooltip(),document.querySelectorAll("pre code").forEach(function(e){d.default.highlightBlock(e)})})}},"./CTFd/themes/core/assets/js/CTFd.js":function(e,t,n){Object.defineProperty(t,"__esModule",{value:!0}),t.default=void 0;var i=c(n("./CTFd/themes/core/assets/js/fetch.js")),o=c(n("./CTFd/themes/core/assets/js/config.js")),a=n("./CTFd/themes/core/assets/js/api.js");n("./CTFd/themes/core/assets/js/patch.js");var r=c(n("./node_modules/markdown-it/index.js")),s=c(n("./node_modules/jquery/dist/jquery.js")),d=c(n("./CTFd/themes/core/assets/js/ezq.js"));function c(e){return e&&e.__esModule?e:{default:e}}function l(t,e){var n,i=Object.keys(t);return Object.getOwnPropertySymbols&&(n=Object.getOwnPropertySymbols(t),e&&(n=n.filter(function(e){return Object.getOwnPropertyDescriptor(t,e).enumerable})),i.push.apply(i,n)),i}function p(o){for(var e=1;e".concat(e.body,"

")):n.find(".modal-body").append((0,s.default)(e.body));var i=(0,s.default)(p.format(e.button));return e.success&&(0,s.default)(i).click(function(){e.success()}),e.large&&n.find(".modal-dialog").addClass("modal-lg"),n.find(".modal-footer").append(i),n.find("pre code").each(function(e){a.default.highlightBlock(this)}),(0,s.default)("main").append(n),n.modal("show"),(0,s.default)(n).on("hidden.bs.modal",function(){(0,s.default)(this).modal("dispose")}),n}function h(e){(0,s.default)("#ezq--notifications-toast-container").length||(0,s.default)("body").append((0,s.default)("
").attr({id:"ezq--notifications-toast-container"}).css({position:"fixed",bottom:"0",right:"0","min-width":"20%"}));var t,n=d.format(e.title,e.body),i=(0,s.default)(n);e.onclose&&(0,s.default)(i).find("button[data-dismiss=toast]").click(function(){e.onclose()}),e.onclick&&((t=(0,s.default)(i).find(".toast-body")).addClass("cursor-pointer"),t.click(function(){e.onclick()}));var o=!1!==e.autohide,a=!1!==e.animation,r=e.delay||1e4;return(0,s.default)("#ezq--notifications-toast-container").prepend(i),i.toast({autohide:o,delay:r,animation:a}),i.toast("show"),i}function g(e){var t=r.format(e.title),n=(0,s.default)(t);"string"==typeof e.body?n.find(".modal-body").append("

".concat(e.body,"

")):n.find(".modal-body").append((0,s.default)(e.body));var i=(0,s.default)(f),o=(0,s.default)(u);return n.find(".modal-footer").append(o),n.find(".modal-footer").append(i),n.find("pre code").each(function(e){a.default.highlightBlock(this)}),(0,s.default)("main").append(n),(0,s.default)(n).on("hidden.bs.modal",function(){(0,s.default)(this).modal("dispose")}),(0,s.default)(i).click(function(){e.success()}),n.modal("show"),n}function v(e){if(e.target){var t=(0,s.default)(e.target);return t.find(".progress-bar").css("width",e.width+"%"),t}var n=c.format(e.width),i=r.format(e.title),o=(0,s.default)(i);return o.find(".modal-body").append((0,s.default)(n)),(0,s.default)("main").append(o),o.modal("show")}function y(e){var t={success:l,error:o}[e.type].format(e.body);return(0,s.default)(t)}var j={ezAlert:m,ezToast:h,ezQuery:g,ezProgressBar:v,ezBadge:y};t.default=j},"./CTFd/themes/core/assets/js/fetch.js":function(e,t,n){Object.defineProperty(t,"__esModule",{value:!0}),t.default=void 0,n("./node_modules/whatwg-fetch/fetch.js");var i,o=(i=n("./CTFd/themes/core/assets/js/config.js"))&&i.__esModule?i:{default:i};var a=window.fetch;t.default=function(e,t){return void 0===t&&(t={method:"GET",credentials:"same-origin",headers:{}}),e=o.default.urlRoot+e,void 0===t.headers&&(t.headers={}),t.credentials="same-origin",t.headers.Accept="application/json",t.headers["Content-Type"]="application/json",t.headers["CSRF-Token"]=o.default.csrfNonce,a(e,t)}},"./CTFd/themes/core/assets/js/patch.js":function(e,t,n){var i,s=(i=n("./node_modules/q/q.js"))&&i.__esModule?i:{default:i},o=n("./CTFd/themes/core/assets/js/api.js");function r(t,e){var n,i=Object.keys(t);return Object.getOwnPropertySymbols&&(n=Object.getOwnPropertySymbols(t),e&&(n=n.filter(function(e){return Object.getOwnPropertyDescriptor(t,e).enumerable})),i.push.apply(i,n)),i}function a(o){for(var e=1;e").text(e).html()},t.cumulativeSum=function(e){for(var t=e.concat(),n=0;n'),(0,r.default)("th.sort-col").click(function(){var o,e=(0,r.default)(this).parents("table").eq(0),t=e.find("tr:gt(0)").toArray().sort((o=(0,r.default)(this).index(),function(e,t){var n=a(e,o),i=a(t,o);return r.default.isNumeric(n)&&r.default.isNumeric(i)?n-i:n.toString().localeCompare(i)}));this.asc=!this.asc,this.asc||(t=t.reverse());for(var n=0;n"+(0,m.htmlEntities)(window.CHALLENGE_NAME)+""),success:function(){g.default.fetch("/api/v1/challenges/"+window.CHALLENGE_ID,{method:"DELETE"}).then(function(e){return e.json()}).then(function(e){e.success&&(window.location=g.default.config.urlRoot+"/admin/challenges")})}})}),(0,h.default)("#challenge-update-container > form").submit(function(e){e.preventDefault();var n=(0,h.default)(e.target).serializeJSON(!0);g.default.fetch("/api/v1/challenges/"+window.CHALLENGE_ID+"/flags",{method:"GET",credentials:"same-origin",headers:{Accept:"application/json","Content-Type":"application/json"}}).then(function(e){return e.json()}).then(function(e){function t(){g.default.fetch("/api/v1/challenges/"+window.CHALLENGE_ID,{method:"PATCH",credentials:"same-origin",headers:{Accept:"application/json","Content-Type":"application/json"},body:JSON.stringify(n)}).then(function(e){return e.json()}).then(function(e){if(e.success){switch((0,h.default)(".challenge-state").text(e.data.state),e.data.state){case"visible":(0,h.default)(".challenge-state").removeClass("badge-danger").addClass("badge-success");break;case"hidden":(0,h.default)(".challenge-state").removeClass("badge-success").addClass("badge-danger")}(0,v.ezToast)({title:"Success",body:"Your challenge has been updated!"})}else{var t,n="";for(t in e.errors)n+=e.errors[t].join("\n"),n+="\n";(0,v.ezAlert)({title:"Error",body:n,button:"OK"})}})}0===e.data.length&&"visible"===n.state?(0,v.ezQuery)({title:"Missing Flags",body:"This challenge does not have any flags meaning it may be unsolveable. Are you sure you'd like to update this challenge?",success:t}):t()})}),(0,h.default)("#challenge-create-options form").submit(O),document.querySelector("#challenge-flags")&&(e=y.default.extend(_.default),t=document.createElement("div"),document.querySelector("#challenge-flags").appendChild(t),new e({propsData:{challenge_id:window.CHALLENGE_ID}}).$mount(t)),document.querySelector("#challenge-topics")&&(n=y.default.extend(T.default),i=document.createElement("div"),document.querySelector("#challenge-topics").appendChild(i),new n({propsData:{challenge_id:window.CHALLENGE_ID}}).$mount(i)),document.querySelector("#challenge-tags")&&(o=y.default.extend(w.default),a=document.createElement("div"),document.querySelector("#challenge-tags").appendChild(a),new o({propsData:{challenge_id:window.CHALLENGE_ID}}).$mount(a)),document.querySelector("#prerequisite-add-form")&&(r=y.default.extend(b.default),s=document.createElement("div"),document.querySelector("#prerequisite-add-form").appendChild(s),new r({propsData:{challenge_id:window.CHALLENGE_ID}}).$mount(s)),document.querySelector("#challenge-files")&&(d=y.default.extend(C.default),c=document.createElement("div"),document.querySelector("#challenge-files").appendChild(c),new d({propsData:{challenge_id:window.CHALLENGE_ID}}).$mount(c)),document.querySelector("#challenge-hints")&&(l=y.default.extend(q.default),p=document.createElement("div"),document.querySelector("#challenge-hints").appendChild(p),new l({propsData:{challenge_id:window.CHALLENGE_ID}}).$mount(p)),document.querySelector("#comment-box")&&(u=y.default.extend(j.default),f=document.createElement("div"),document.querySelector("#comment-box").appendChild(f),new u({propsData:{type:"challenge",id:window.CHALLENGE_ID}}).$mount(f)),h.default.get(g.default.config.urlRoot+"/api/v1/challenges/types",function(e){var t=e.data;P(t.standard),(0,h.default)("#create-chals-select input[name=type]").change(function(){P(t[this.value])})})})},"./CTFd/themes/admin/assets/js/pages/main.js":function(e,t,n){var i=f(n("./CTFd/themes/core/assets/js/CTFd.js")),o=f(n("./node_modules/jquery/dist/jquery.js")),a=f(n("./node_modules/dayjs/dayjs.min.js")),r=f(n("./node_modules/dayjs/plugin/advancedFormat.js")),s=f(n("./node_modules/nunjucks/browser/nunjucks.js")),d=n("./node_modules/howler/dist/howler.js"),c=f(n("./CTFd/themes/core/assets/js/events.js")),l=f(n("./CTFd/themes/core/assets/js/times.js")),p=f(n("./CTFd/themes/admin/assets/js/styles.js")),u=f(n("./CTFd/themes/core/assets/js/helpers.js"));function f(e){return e&&e.__esModule?e:{default:e}}a.default.extend(r.default),i.default.init(window.init),window.CTFd=i.default,window.helpers=u.default,window.$=o.default,window.dayjs=a.default,window.nunjucks=s.default,window.Howl=d.Howl,(0,o.default)(function(){(0,p.default)(),(0,l.default)(),(0,c.default)(i.default.config.urlRoot)})},"./CTFd/themes/admin/assets/js/styles.js":function(e,t,n){Object.defineProperty(t,"__esModule",{value:!0}),t.showMediaLibrary=l,t.bindMarkdownEditor=p,t.bindMarkdownEditors=u,t.default=void 0,n("./node_modules/bootstrap/dist/js/bootstrap.bundle.js");var i=n("./CTFd/themes/core/assets/js/utils.js"),o=c(n("./node_modules/jquery/dist/jquery.js")),a=c(n("./node_modules/easymde/src/js/easymde.js")),r=c(n("./node_modules/vue/dist/vue.esm.browser.js")),s=c(n("./CTFd/themes/admin/assets/js/components/files/MediaLibrary.vue")),d=c(n("./node_modules/highlight.js/lib/index.js"));function c(e){return e&&e.__esModule?e:{default:e}}function l(e){var t=r.default.extend(s.default),n=document.createElement("div");document.querySelector("main").appendChild(n);var i=new t({propsData:{editor:e}}).$mount(n);(0,o.default)("#media-modal").on("hidden.bs.modal",function(e){i.$destroy(),(0,o.default)("#media-modal").remove()}),(0,o.default)("#media-modal").modal()}function p(e){var t;!1===e.hasOwnProperty("mde")&&(t=new a.default({autoDownloadFontAwesome:!1,toolbar:["bold","italic","heading","|","quote","unordered-list","ordered-list","|","link","image",{name:"media",action:function(e){l(e)},className:"fas fa-file-upload",title:"Media Library"},"|","preview","guide"],element:e,initialValue:(0,o.default)(e).val(),forceSync:!0,minHeight:"200px",renderingConfig:{codeSyntaxHighlighting:!0,hljs:d.default}}),e.mde=t,e.codemirror=t.codemirror,(0,o.default)(e).on("change keyup paste",function(){t.codemirror.getDoc().setValue((0,o.default)(e).val()),t.codemirror.refresh()}))}function u(){(0,o.default)("textarea.markdown").each(function(e,t){p(t)})}t.default=function(){(0,o.default)(":input").each(function(){(0,o.default)(this).data("initial",(0,o.default)(this).val())}),(0,o.default)(function(){(0,o.default)("tr[data-href], td[data-href]").click(function(){var e;return getSelection().toString()||(e=(0,o.default)(this).attr("data-href"))&&(window.location=e),!1}),(0,o.default)("[data-checkbox]").click(function(e){(0,o.default)(e.target).is("input[type=checkbox]")||(0,o.default)(this).find("input[type=checkbox]").click(),e.stopImmediatePropagation()}),(0,o.default)("[data-checkbox-all]").on("click change",function(e){var t=(0,o.default)(this).prop("checked"),n=(0,o.default)(this).index()+1;(0,o.default)(this).closest("table").find("tr td:nth-child(".concat(n,") input[type=checkbox]")).prop("checked",t),e.stopImmediatePropagation()}),(0,o.default)("tr[data-href] a, tr[data-href] button").click(function(e){(0,o.default)(this).attr("data-dismiss")||e.stopPropagation()}),(0,o.default)(".page-select").change(function(){var e=new URL(window.location);e.searchParams.set("page",this.value),window.location.href=e.toString()}),(0,o.default)('a[data-toggle="tab"]').on("shown.bs.tab",function(e){sessionStorage.setItem("activeTab",(0,o.default)(e.target).attr("href"))});var e,t=sessionStorage.getItem("activeTab");t&&((e=(0,o.default)('.nav-tabs a[href="'.concat(t,'"], .nav-pills a[href="').concat(t,'"]'))).length?e.tab("show"):sessionStorage.removeItem("activeTab")),u(),(0,i.makeSortableTables)(),(0,o.default)('[data-toggle="tooltip"]').tooltip(),document.querySelectorAll("pre code").forEach(function(e){d.default.highlightBlock(e)})})}},"./CTFd/themes/core/assets/js/CTFd.js":function(e,t,n){Object.defineProperty(t,"__esModule",{value:!0}),t.default=void 0;var i=c(n("./CTFd/themes/core/assets/js/fetch.js")),o=c(n("./CTFd/themes/core/assets/js/config.js")),a=n("./CTFd/themes/core/assets/js/api.js");n("./CTFd/themes/core/assets/js/patch.js");var r=c(n("./node_modules/markdown-it/index.js")),s=c(n("./node_modules/jquery/dist/jquery.js")),d=c(n("./CTFd/themes/core/assets/js/ezq.js"));function c(e){return e&&e.__esModule?e:{default:e}}function l(t,e){var n,i=Object.keys(t);return Object.getOwnPropertySymbols&&(n=Object.getOwnPropertySymbols(t),e&&(n=n.filter(function(e){return Object.getOwnPropertyDescriptor(t,e).enumerable})),i.push.apply(i,n)),i}function p(o){for(var e=1;e".concat(e.body,"

")):n.find(".modal-body").append((0,s.default)(e.body));var i=(0,s.default)(p.format(e.button));return e.success&&(0,s.default)(i).click(function(){e.success()}),e.large&&n.find(".modal-dialog").addClass("modal-lg"),n.find(".modal-footer").append(i),n.find("pre code").each(function(e){a.default.highlightBlock(this)}),(0,s.default)("main").append(n),n.modal("show"),(0,s.default)(n).on("hidden.bs.modal",function(){(0,s.default)(this).modal("dispose")}),n}function h(e){(0,s.default)("#ezq--notifications-toast-container").length||(0,s.default)("body").append((0,s.default)("
").attr({id:"ezq--notifications-toast-container"}).css({position:"fixed",bottom:"0",right:"0","min-width":"20%"}));var t,n=d.format(e.title,e.body),i=(0,s.default)(n);e.onclose&&(0,s.default)(i).find("button[data-dismiss=toast]").click(function(){e.onclose()}),e.onclick&&((t=(0,s.default)(i).find(".toast-body")).addClass("cursor-pointer"),t.click(function(){e.onclick()}));var o=!1!==e.autohide,a=!1!==e.animation,r=e.delay||1e4;return(0,s.default)("#ezq--notifications-toast-container").prepend(i),i.toast({autohide:o,delay:r,animation:a}),i.toast("show"),i}function g(e){var t=r.format(e.title),n=(0,s.default)(t);"string"==typeof e.body?n.find(".modal-body").append("

".concat(e.body,"

")):n.find(".modal-body").append((0,s.default)(e.body));var i=(0,s.default)(f),o=(0,s.default)(u);return n.find(".modal-footer").append(o),n.find(".modal-footer").append(i),n.find("pre code").each(function(e){a.default.highlightBlock(this)}),(0,s.default)("main").append(n),(0,s.default)(n).on("hidden.bs.modal",function(){(0,s.default)(this).modal("dispose")}),(0,s.default)(i).click(function(){e.success()}),n.modal("show"),n}function v(e){if(e.target){var t=(0,s.default)(e.target);return t.find(".progress-bar").css("width",e.width+"%"),t}var n=c.format(e.width),i=r.format(e.title),o=(0,s.default)(i);return o.find(".modal-body").append((0,s.default)(n)),(0,s.default)("main").append(o),o.modal("show")}function y(e){var t={success:l,error:o}[e.type].format(e.body);return(0,s.default)(t)}var j={ezAlert:m,ezToast:h,ezQuery:g,ezProgressBar:v,ezBadge:y};t.default=j},"./CTFd/themes/core/assets/js/fetch.js":function(e,t,n){Object.defineProperty(t,"__esModule",{value:!0}),t.default=void 0,n("./node_modules/whatwg-fetch/fetch.js");var i,o=(i=n("./CTFd/themes/core/assets/js/config.js"))&&i.__esModule?i:{default:i};var a=window.fetch;t.default=function(e,t){return void 0===t&&(t={method:"GET",credentials:"same-origin",headers:{}}),e=o.default.urlRoot+e,void 0===t.headers&&(t.headers={}),t.credentials="same-origin",t.headers.Accept="application/json",t.headers["Content-Type"]="application/json",t.headers["CSRF-Token"]=o.default.csrfNonce,a(e,t)}},"./CTFd/themes/core/assets/js/patch.js":function(e,t,n){var i,s=(i=n("./node_modules/q/q.js"))&&i.__esModule?i:{default:i},o=n("./CTFd/themes/core/assets/js/api.js");function r(t,e){var n,i=Object.keys(t);return Object.getOwnPropertySymbols&&(n=Object.getOwnPropertySymbols(t),e&&(n=n.filter(function(e){return Object.getOwnPropertyDescriptor(t,e).enumerable})),i.push.apply(i,n)),i}function a(o){for(var e=1;e").text(e).html()},t.cumulativeSum=function(e){for(var t=e.concat(),n=0;n'),(0,r.default)("th.sort-col").click(function(){var o,e=(0,r.default)(this).parents("table").eq(0),t=e.find("tr:gt(0)").toArray().sort((o=(0,r.default)(this).index(),function(e,t){var n=a(e,o),i=a(t,o);return r.default.isNumeric(n)&&r.default.isNumeric(i)?n-i:n.toString().localeCompare(i)}));this.asc=!this.asc,this.asc||(t=t.reverse());for(var n=0;n
+ +

{{ challenge.name }}

@@ -29,6 +50,10 @@

{{ challenge.value }} points

+ + + @@ -41,23 +66,20 @@

{{ challenge.value }} points

-
+
-

Files

- {% include "admin/modals/challenges/files.html" %} +

Topics

+ {% include "admin/modals/challenges/topics.html" %}
diff --git a/CTFd/themes/admin/templates/modals/challenges/topics.html b/CTFd/themes/admin/templates/modals/challenges/topics.html new file mode 100644 index 000000000..8e310979f --- /dev/null +++ b/CTFd/themes/admin/templates/modals/challenges/topics.html @@ -0,0 +1,2 @@ +
+
diff --git a/migrations/versions/ef87d69ec29a_add_topics_and_challenge_topics_tables.py b/migrations/versions/ef87d69ec29a_add_topics_and_challenge_topics_tables.py new file mode 100644 index 000000000..1e87ca8df --- /dev/null +++ b/migrations/versions/ef87d69ec29a_add_topics_and_challenge_topics_tables.py @@ -0,0 +1,46 @@ +"""Add topics and challenge_topics tables + +Revision ID: ef87d69ec29a +Revises: 07dfbe5e1edc +Create Date: 2021-07-29 23:22:39.345426 + +""" +from alembic import op +import sqlalchemy as sa + + +# revision identifiers, used by Alembic. +revision = "ef87d69ec29a" +down_revision = "07dfbe5e1edc" +branch_labels = None +depends_on = None + + +def upgrade(): + # ### commands auto generated by Alembic - please adjust! ### + op.create_table( + "topics", + sa.Column("id", sa.Integer(), nullable=False), + sa.Column("value", sa.String(length=255), nullable=True), + sa.PrimaryKeyConstraint("id"), + sa.UniqueConstraint("value"), + ) + op.create_table( + "challenge_topics", + sa.Column("id", sa.Integer(), nullable=False), + sa.Column("challenge_id", sa.Integer(), nullable=True), + sa.Column("topic_id", sa.Integer(), nullable=True), + sa.ForeignKeyConstraint( + ["challenge_id"], ["challenges.id"], ondelete="CASCADE" + ), + sa.ForeignKeyConstraint(["topic_id"], ["topics.id"], ondelete="CASCADE"), + sa.PrimaryKeyConstraint("id"), + ) + # ### end Alembic commands ### + + +def downgrade(): + # ### commands auto generated by Alembic - please adjust! ### + op.drop_table("challenge_topics") + op.drop_table("topics") + # ### end Alembic commands ### diff --git a/tests/api/v1/test_challenges.py b/tests/api/v1/test_challenges.py index b17571f54..39ecd49a9 100644 --- a/tests/api/v1/test_challenges.py +++ b/tests/api/v1/test_challenges.py @@ -15,6 +15,7 @@ gen_solve, gen_tag, gen_team, + gen_topic, gen_user, login_as_user, register_user, @@ -1313,6 +1314,34 @@ def test_api_challenge_get_tags_admin(): destroy_ctfd(app) +def test_api_challenge_get_topics_non_admin(): + """Can a user get /api/v1/challenges//topics if not admin""" + app = create_ctfd() + with app.app_context(): + gen_challenge(app.db) + gen_topic(app.db, challenge_id=1) + with app.test_client() as client: + r = client.get("/api/v1/challenges/1/topics", json="") + assert r.status_code == 403 + destroy_ctfd(app) + + +def test_api_challenge_get_topics_admin(): + """Can a user get /api/v1/challenges//topics if not admin""" + app = create_ctfd() + with app.app_context(): + gen_challenge(app.db) + gen_topic(app.db, challenge_id=1) + with login_as_user(app, name="admin") as client: + r = client.get("/api/v1/challenges/1/topics", json="") + assert r.status_code == 200 + assert r.get_json() == { + "success": True, + "data": [{"id": 1, "challenge_id": 1, "topic_id": 1, "value": "topic"}], + } + destroy_ctfd(app) + + def test_api_challenge_get_hints_non_admin(): """Can a user get /api/v1/challenges//hints if not admin""" app = create_ctfd() diff --git a/tests/api/v1/test_tags.py b/tests/api/v1/test_tags.py index 2e7286ad6..85d3374d8 100644 --- a/tests/api/v1/test_tags.py +++ b/tests/api/v1/test_tags.py @@ -91,7 +91,7 @@ def test_api_tag_patch_admin(): def test_api_tag_delete_admin(): - """Can a user patch /api/v1/tags/ if admin""" + """Can a user delete /api/v1/tags/ if admin""" app = create_ctfd() with app.app_context(): gen_challenge(app.db) diff --git a/tests/api/v1/test_topics.py b/tests/api/v1/test_topics.py new file mode 100644 index 000000000..48e212434 --- /dev/null +++ b/tests/api/v1/test_topics.py @@ -0,0 +1,132 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +from tests.helpers import ( + create_ctfd, + destroy_ctfd, + gen_challenge, + gen_topic, + login_as_user, + register_user, +) + + +def test_api_topics_non_admin(): + """Can a user interact with /api/v1/topics if not admin""" + app = create_ctfd() + with app.app_context(): + gen_challenge(app.db) + gen_topic(app.db, challenge_id=1) + with app.test_client() as client: + r = client.get("/api/v1/topics", json="") + assert r.status_code == 403 + + """Can a user post /api/v1/topics if not admin""" + r = client.post("/api/v1/topics") + assert r.status_code == 403 + + """Can a user delete /api/v1/topics if not admin""" + r = client.delete("/api/v1/topics") + assert r.status_code == 403 + + """Can a user get /api/v1/topics/ if not admin""" + r = client.get("/api/v1/topics/1", json="") + assert r.status_code == 403 + + """Can a user delete /api/v1/topics/ if not admin""" + r = client.delete("/api/v1/topics/1", json="") + assert r.status_code == 403 + + register_user(app) + with login_as_user(app) as client: + r = client.get("/api/v1/topics", json="") + assert r.status_code == 403 + + """Can a user post /api/v1/topics if not admin""" + r = client.post("/api/v1/topics") + assert r.status_code == 403 + + """Can a user delete /api/v1/topics if not admin""" + r = client.delete("/api/v1/topics") + assert r.status_code == 403 + + """Can a user get /api/v1/topics/ if not admin""" + r = client.get("/api/v1/topics/1", json="") + assert r.status_code == 403 + + """Can a user delete /api/v1/topics/ if not admin""" + r = client.delete("/api/v1/topics/1", json="") + assert r.status_code == 403 + destroy_ctfd(app) + + +def test_api_topics_get_admin(): + """Can a user get /api/v1/topics if admin""" + app = create_ctfd() + with app.app_context(): + gen_challenge(app.db) + gen_topic(app.db, challenge_id=1) + gen_topic(app.db, challenge_id=1, value="topic2") + with login_as_user(app, name="admin") as client: + r = client.get("/api/v1/topics") + assert r.status_code == 200 + assert r.get_json() == { + "success": True, + "data": [{"id": 1, "value": "topic"}, {"id": 2, "value": "topic2"}], + } + destroy_ctfd(app) + + +def test_api_topics_post_admin(): + """Can a user post /api/v1/topics if admin""" + app = create_ctfd() + with app.app_context(): + gen_challenge(app.db) + with login_as_user(app, name="admin") as client: + r = client.post( + "/api/v1/topics", + json={"value": "topic", "type": "challenge", "challenge_id": 1}, + ) + assert r.status_code == 200 + print(r.get_json()) + assert r.get_json() == { + "success": True, + "data": { + "challenge_id": 1, + "challenge": 1, + "topic": 1, + "id": 1, + "topic_id": 1, + }, + } + destroy_ctfd(app) + + +def test_api_topics_delete_admin(): + """Can a user delete /api/v1/topics/ if admin""" + app = create_ctfd() + with app.app_context(): + gen_challenge(app.db) + gen_topic(app.db, challenge_id=1) + with login_as_user(app, "admin") as client: + r = client.delete("/api/v1/topics/1", json="") + assert r.status_code == 200 + resp = r.get_json() + assert resp.get("data") is None + assert resp.get("success") is True + destroy_ctfd(app) + + +def test_api_topics_delete_target_admin(): + """Can a user delete /api/v1/topics if admin""" + app = create_ctfd() + with app.app_context(): + gen_challenge(app.db) + gen_topic(app.db, challenge_id=1) + with login_as_user(app, "admin") as client: + r = client.delete("/api/v1/topics?type=challenge&target_id=1", json="") + assert r.status_code == 200 + resp = r.get_json() + assert resp.get("data") is None + assert resp.get("success") is True + destroy_ctfd(app) diff --git a/tests/helpers.py b/tests/helpers.py index 6ff2ae670..2037dfd4a 100644 --- a/tests/helpers.py +++ b/tests/helpers.py @@ -22,6 +22,7 @@ ChallengeComments, ChallengeFiles, Challenges, + ChallengeTopics, Comments, Fails, Fields, @@ -37,6 +38,7 @@ TeamComments, Teams, Tokens, + Topics, Tracking, Unlocks, UserComments, @@ -353,6 +355,17 @@ def gen_tag(db, challenge_id, value="tag_tag", **kwargs): return tag +def gen_topic(db, challenge_id, value="topic", **kwargs): + topic = Topics(value=value, **kwargs) + db.session.add(topic) + db.session.commit() + + challenge_topic = ChallengeTopics(challenge_id=challenge_id, topic_id=topic.id) + db.session.add(challenge_topic) + db.session.commit() + return challenge_topic + + def gen_file(db, location, challenge_id=None, page_id=None): if challenge_id: f = ChallengeFiles(challenge_id=challenge_id, location=location)