diff --git a/CTFd/admin/__init__.py b/CTFd/admin/__init__.py index 51aa5078..20f96ef6 100644 --- a/CTFd/admin/__init__.py +++ b/CTFd/admin/__init__.py @@ -45,9 +45,8 @@ from CTFd.utils import config as ctf_config from CTFd.utils import get_config, set_config from CTFd.utils.csv import dump_csv, load_challenges_csv, load_teams_csv, load_users_csv from CTFd.utils.decorators import admins_only +from CTFd.utils.exports import background_import_ctf from CTFd.utils.exports import export_ctf as export_ctf_util -from CTFd.utils.exports import import_ctf as import_ctf_util -from CTFd.utils.helpers import get_errors from CTFd.utils.security.auth import logout_user from CTFd.utils.uploads import delete_file from CTFd.utils.user import is_admin @@ -88,21 +87,25 @@ def plugin(plugin): return "1" -@admin.route("/admin/import", methods=["POST"]) +@admin.route("/admin/import", methods=["GET", "POST"]) @admins_only def import_ctf(): - backup = request.files["backup"] - errors = get_errors() - try: - import_ctf_util(backup) - except Exception as e: - print(e) - errors.append(repr(e)) - - if errors: - return errors[0], 500 - else: - return redirect(url_for("admin.config")) + if request.method == "GET": + start_time = cache.get("import_start_time") + end_time = cache.get("import_end_time") + import_status = cache.get("import_status") + import_error = cache.get("import_error") + return render_template( + "admin/import.html", + start_time=start_time, + end_time=end_time, + import_status=import_status, + import_error=import_error, + ) + elif request.method == "POST": + backup = request.files["backup"] + background_import_ctf(backup) + return redirect(url_for("admin.import_ctf")) @admin.route("/admin/export", methods=["GET", "POST"]) diff --git a/CTFd/themes/admin/assets/js/pages/configs.js b/CTFd/themes/admin/assets/js/pages/configs.js index b0d2e489..5bc4be76 100644 --- a/CTFd/themes/admin/assets/js/pages/configs.js +++ b/CTFd/themes/admin/assets/js/pages/configs.js @@ -359,12 +359,7 @@ function importConfig(event) { target: pg, width: 100 }); - setTimeout(function() { - pg.modal("hide"); - }, 500); - setTimeout(function() { - window.location.reload(); - }, 700); + location.href = CTFd.config.urlRoot + "/admin/import"; } }); } diff --git a/CTFd/themes/admin/static/js/pages/configs.dev.js b/CTFd/themes/admin/static/js/pages/configs.dev.js index f91b0600..8492b61b 100644 --- a/CTFd/themes/admin/static/js/pages/configs.dev.js +++ b/CTFd/themes/admin/static/js/pages/configs.dev.js @@ -162,7 +162,7 @@ /***/ (function(module, exports, __webpack_require__) { ; -eval("\n\n__webpack_require__(/*! ./main */ \"./CTFd/themes/admin/assets/js/pages/main.js\");\n\n__webpack_require__(/*! core/utils */ \"./CTFd/themes/core/assets/js/utils.js\");\n\n__webpack_require__(/*! bootstrap/js/dist/tab */ \"./node_modules/bootstrap/js/dist/tab.js\");\n\nvar _dayjs = _interopRequireDefault(__webpack_require__(/*! dayjs */ \"./node_modules/dayjs/dayjs.min.js\"));\n\nvar _advancedFormat = _interopRequireDefault(__webpack_require__(/*! dayjs/plugin/advancedFormat */ \"./node_modules/dayjs/plugin/advancedFormat.js\"));\n\nvar _utc = _interopRequireDefault(__webpack_require__(/*! dayjs/plugin/utc */ \"./node_modules/dayjs/plugin/utc.js\"));\n\nvar _timezone = _interopRequireDefault(__webpack_require__(/*! dayjs/plugin/timezone */ \"./node_modules/dayjs/plugin/timezone.js\"));\n\nvar _timezones = _interopRequireDefault(__webpack_require__(/*! ../timezones */ \"./CTFd/themes/admin/assets/js/timezones.js\"));\n\nvar _CTFd = _interopRequireDefault(__webpack_require__(/*! core/CTFd */ \"./CTFd/themes/core/assets/js/CTFd.js\"));\n\nvar _helpers = _interopRequireDefault(__webpack_require__(/*! core/helpers */ \"./CTFd/themes/core/assets/js/helpers.js\"));\n\nvar _jquery = _interopRequireDefault(__webpack_require__(/*! jquery */ \"./node_modules/jquery/dist/jquery.js\"));\n\nvar _ezq = __webpack_require__(/*! core/ezq */ \"./CTFd/themes/core/assets/js/ezq.js\");\n\nvar _codemirror = _interopRequireDefault(__webpack_require__(/*! codemirror */ \"./node_modules/codemirror/lib/codemirror.js\"));\n\n__webpack_require__(/*! codemirror/mode/htmlmixed/htmlmixed.js */ \"./node_modules/codemirror/mode/htmlmixed/htmlmixed.js\");\n\nvar _vueEsm = _interopRequireDefault(__webpack_require__(/*! vue/dist/vue.esm.browser */ \"./node_modules/vue/dist/vue.esm.browser.js\"));\n\nvar _FieldList = _interopRequireDefault(__webpack_require__(/*! ../components/configs/fields/FieldList.vue */ \"./CTFd/themes/admin/assets/js/components/configs/fields/FieldList.vue\"));\n\nfunction _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { \"default\": obj }; }\n\n_dayjs[\"default\"].extend(_advancedFormat[\"default\"]);\n\n_dayjs[\"default\"].extend(_utc[\"default\"]);\n\n_dayjs[\"default\"].extend(_timezone[\"default\"]);\n\nfunction loadTimestamp(place, timestamp) {\n if (typeof timestamp == \"string\") {\n timestamp = parseInt(timestamp, 10) * 1000;\n }\n\n var d = (0, _dayjs[\"default\"])(timestamp);\n (0, _jquery[\"default\"])(\"#\" + place + \"-month\").val(d.month() + 1); // Months are zero indexed (https://day.js.org/docs/en/get-set/month)\n\n (0, _jquery[\"default\"])(\"#\" + place + \"-day\").val(d.date());\n (0, _jquery[\"default\"])(\"#\" + place + \"-year\").val(d.year());\n (0, _jquery[\"default\"])(\"#\" + place + \"-hour\").val(d.hour());\n (0, _jquery[\"default\"])(\"#\" + place + \"-minute\").val(d.minute());\n loadDateValues(place);\n}\n\nfunction loadDateValues(place) {\n var month = (0, _jquery[\"default\"])(\"#\" + place + \"-month\").val();\n var day = (0, _jquery[\"default\"])(\"#\" + place + \"-day\").val();\n var year = (0, _jquery[\"default\"])(\"#\" + place + \"-year\").val();\n var hour = (0, _jquery[\"default\"])(\"#\" + place + \"-hour\").val();\n var minute = (0, _jquery[\"default\"])(\"#\" + place + \"-minute\").val();\n var timezone_string = (0, _jquery[\"default\"])(\"#\" + place + \"-timezone\").val();\n var utc = convertDateToMoment(month, day, year, hour, minute);\n\n if (utc.unix() && month && day && year && hour && minute) {\n (0, _jquery[\"default\"])(\"#\" + place).val(utc.unix());\n (0, _jquery[\"default\"])(\"#\" + place + \"-local\").val(utc.format(\"dddd, MMMM Do YYYY, h:mm:ss a z (zzz)\"));\n (0, _jquery[\"default\"])(\"#\" + place + \"-zonetime\").val(utc.tz(timezone_string).format(\"dddd, MMMM Do YYYY, h:mm:ss a z (zzz)\"));\n } else {\n (0, _jquery[\"default\"])(\"#\" + place).val(\"\");\n (0, _jquery[\"default\"])(\"#\" + place + \"-local\").val(\"\");\n (0, _jquery[\"default\"])(\"#\" + place + \"-zonetime\").val(\"\");\n }\n}\n\nfunction convertDateToMoment(month, day, year, hour, minute) {\n var month_num = month.toString();\n\n if (month_num.length == 1) {\n month_num = \"0\" + month_num;\n }\n\n var day_str = day.toString();\n\n if (day_str.length == 1) {\n day_str = \"0\" + day_str;\n }\n\n var hour_str = hour.toString();\n\n if (hour_str.length == 1) {\n hour_str = \"0\" + hour_str;\n }\n\n var min_str = minute.toString();\n\n if (min_str.length == 1) {\n min_str = \"0\" + min_str;\n } // 2013-02-08 24:00\n\n\n var date_string = year.toString() + \"-\" + month_num + \"-\" + day_str + \" \" + hour_str + \":\" + min_str + \":00\";\n return (0, _dayjs[\"default\"])(date_string);\n}\n\nfunction updateConfigs(event) {\n event.preventDefault();\n var obj = (0, _jquery[\"default\"])(this).serializeJSON();\n var params = {};\n\n if (obj.mail_useauth === false) {\n obj.mail_username = null;\n obj.mail_password = null;\n } else {\n if (obj.mail_username === \"\") {\n delete obj.mail_username;\n }\n\n if (obj.mail_password === \"\") {\n delete obj.mail_password;\n }\n }\n\n Object.keys(obj).forEach(function (x) {\n if (obj[x] === \"true\") {\n params[x] = true;\n } else if (obj[x] === \"false\") {\n params[x] = false;\n } else {\n params[x] = obj[x];\n }\n });\n\n _CTFd[\"default\"].api.patch_config_list({}, params).then(function (_response) {\n if (_response.success) {\n window.location.reload();\n } else {\n var errors = _response.errors.value.join(\"\\n\");\n\n (0, _ezq.ezAlert)({\n title: \"Error!\",\n body: errors,\n button: \"Okay\"\n });\n }\n });\n}\n\nfunction uploadLogo(event) {\n event.preventDefault();\n var form = event.target;\n\n _helpers[\"default\"].files.upload(form, {}, function (response) {\n var f = response.data[0];\n var params = {\n value: f.location\n };\n\n _CTFd[\"default\"].fetch(\"/api/v1/configs/ctf_logo\", {\n method: \"PATCH\",\n body: JSON.stringify(params)\n }).then(function (response) {\n return response.json();\n }).then(function (response) {\n if (response.success) {\n window.location.reload();\n } else {\n (0, _ezq.ezAlert)({\n title: \"Error!\",\n body: \"Logo uploading failed!\",\n button: \"Okay\"\n });\n }\n });\n });\n}\n\nfunction switchUserMode(event) {\n event.preventDefault();\n\n if (confirm(\"Are you sure you'd like to switch user modes?\\n\\nAll user submissions, awards, unlocks, and tracking will be deleted!\")) {\n var formData = new FormData();\n formData.append(\"submissions\", true);\n formData.append(\"nonce\", _CTFd[\"default\"].config.csrfNonce);\n fetch(_CTFd[\"default\"].config.urlRoot + \"/admin/reset\", {\n method: \"POST\",\n credentials: \"same-origin\",\n body: formData\n }); // Bind `this` so that we can reuse the updateConfigs function\n\n var binded = updateConfigs.bind(this);\n binded(event);\n }\n}\n\nfunction removeLogo() {\n (0, _ezq.ezQuery)({\n title: \"Remove logo\",\n body: \"Are you sure you'd like to remove the CTF logo?\",\n success: function success() {\n var params = {\n value: null\n };\n\n _CTFd[\"default\"].api.patch_config({\n configKey: \"ctf_logo\"\n }, params).then(function (_response) {\n window.location.reload();\n });\n }\n });\n}\n\nfunction smallIconUpload(event) {\n event.preventDefault();\n var form = event.target;\n\n _helpers[\"default\"].files.upload(form, {}, function (response) {\n var f = response.data[0];\n var params = {\n value: f.location\n };\n\n _CTFd[\"default\"].fetch(\"/api/v1/configs/ctf_small_icon\", {\n method: \"PATCH\",\n body: JSON.stringify(params)\n }).then(function (response) {\n return response.json();\n }).then(function (response) {\n if (response.success) {\n window.location.reload();\n } else {\n (0, _ezq.ezAlert)({\n title: \"Error!\",\n body: \"Icon uploading failed!\",\n button: \"Okay\"\n });\n }\n });\n });\n}\n\nfunction removeSmallIcon() {\n (0, _ezq.ezQuery)({\n title: \"Remove logo\",\n body: \"Are you sure you'd like to remove the small site icon?\",\n success: function success() {\n var params = {\n value: null\n };\n\n _CTFd[\"default\"].api.patch_config({\n configKey: \"ctf_small_icon\"\n }, params).then(function (_response) {\n window.location.reload();\n });\n }\n });\n}\n\nfunction importCSV(event) {\n event.preventDefault();\n var csv_file = document.getElementById(\"import-csv-file\").files[0];\n var csv_type = document.getElementById(\"import-csv-type\").value;\n var form_data = new FormData();\n form_data.append(\"csv_file\", csv_file);\n form_data.append(\"csv_type\", csv_type);\n form_data.append(\"nonce\", _CTFd[\"default\"].config.csrfNonce);\n var pg = (0, _ezq.ezProgressBar)({\n width: 0,\n title: \"Upload Progress\"\n });\n\n _jquery[\"default\"].ajax({\n url: _CTFd[\"default\"].config.urlRoot + \"/admin/import/csv\",\n type: \"POST\",\n data: form_data,\n processData: false,\n contentType: false,\n statusCode: {\n 500: function _(resp) {\n // Normalize errors\n var errors = JSON.parse(resp.responseText);\n var errorText = \"\";\n errors.forEach(function (element) {\n errorText += \"Line \".concat(element[0], \": \").concat(JSON.stringify(element[1]), \"\\n\");\n }); // Show errors\n\n alert(errorText); // Hide progress modal if its there\n\n pg = (0, _ezq.ezProgressBar)({\n target: pg,\n width: 100\n });\n setTimeout(function () {\n pg.modal(\"hide\");\n }, 500);\n }\n },\n xhr: function xhr() {\n var xhr = _jquery[\"default\"].ajaxSettings.xhr();\n\n xhr.upload.onprogress = function (e) {\n if (e.lengthComputable) {\n var width = e.loaded / e.total * 100;\n pg = (0, _ezq.ezProgressBar)({\n target: pg,\n width: width\n });\n }\n };\n\n return xhr;\n },\n success: function success(_data) {\n pg = (0, _ezq.ezProgressBar)({\n target: pg,\n width: 100\n });\n setTimeout(function () {\n pg.modal(\"hide\");\n }, 500);\n setTimeout(function () {\n window.location.reload();\n }, 700);\n }\n });\n}\n\nfunction importConfig(event) {\n event.preventDefault();\n var import_file = document.getElementById(\"import-file\").files[0];\n var form_data = new FormData();\n form_data.append(\"backup\", import_file);\n form_data.append(\"nonce\", _CTFd[\"default\"].config.csrfNonce);\n var pg = (0, _ezq.ezProgressBar)({\n width: 0,\n title: \"Upload Progress\"\n });\n\n _jquery[\"default\"].ajax({\n url: _CTFd[\"default\"].config.urlRoot + \"/admin/import\",\n type: \"POST\",\n data: form_data,\n processData: false,\n contentType: false,\n statusCode: {\n 500: function _(resp) {\n alert(resp.responseText);\n }\n },\n xhr: function xhr() {\n var xhr = _jquery[\"default\"].ajaxSettings.xhr();\n\n xhr.upload.onprogress = function (e) {\n if (e.lengthComputable) {\n var width = e.loaded / e.total * 100;\n pg = (0, _ezq.ezProgressBar)({\n target: pg,\n width: width\n });\n }\n };\n\n return xhr;\n },\n success: function success(_data) {\n pg = (0, _ezq.ezProgressBar)({\n target: pg,\n width: 100\n });\n setTimeout(function () {\n pg.modal(\"hide\");\n }, 500);\n setTimeout(function () {\n window.location.reload();\n }, 700);\n }\n });\n}\n\nfunction exportConfig(event) {\n event.preventDefault();\n window.location.href = (0, _jquery[\"default\"])(this).attr(\"href\");\n}\n\nfunction insertTimezones(target) {\n var current = (0, _jquery[\"default\"])(\"\").text(_dayjs[\"default\"].tz.guess());\n (0, _jquery[\"default\"])(target).append(current);\n var tz_names = _timezones[\"default\"];\n\n for (var i = 0; i < tz_names.length; i++) {\n var tz = (0, _jquery[\"default\"])(\"\").text(tz_names[i]);\n (0, _jquery[\"default\"])(target).append(tz);\n }\n}\n\n(0, _jquery[\"default\"])(function () {\n var theme_header_editor = _codemirror[\"default\"].fromTextArea(document.getElementById(\"theme-header\"), {\n lineNumbers: true,\n lineWrapping: true,\n mode: \"htmlmixed\",\n htmlMode: true\n });\n\n var theme_footer_editor = _codemirror[\"default\"].fromTextArea(document.getElementById(\"theme-footer\"), {\n lineNumbers: true,\n lineWrapping: true,\n mode: \"htmlmixed\",\n htmlMode: true\n });\n\n var theme_settings_editor = _codemirror[\"default\"].fromTextArea(document.getElementById(\"theme-settings\"), {\n lineNumbers: true,\n lineWrapping: true,\n readOnly: true,\n mode: {\n name: \"javascript\",\n json: true\n }\n }); // Handle refreshing codemirror when switching tabs.\n // Better than the autorefresh approach b/c there's no flicker\n\n\n (0, _jquery[\"default\"])(\"a[href='#theme']\").on(\"shown.bs.tab\", function (_e) {\n theme_header_editor.refresh();\n theme_footer_editor.refresh();\n theme_settings_editor.refresh();\n });\n (0, _jquery[\"default\"])(\"a[href='#legal'], a[href='#tos-config'], a[href='#privacy-policy-config']\").on(\"shown.bs.tab\", function (_e) {\n (0, _jquery[\"default\"])(\"#tos-config .CodeMirror\").each(function (i, el) {\n el.CodeMirror.refresh();\n });\n (0, _jquery[\"default\"])(\"#privacy-policy-config .CodeMirror\").each(function (i, el) {\n el.CodeMirror.refresh();\n });\n });\n (0, _jquery[\"default\"])(\"#theme-settings-modal form\").submit(function (e) {\n e.preventDefault();\n theme_settings_editor.getDoc().setValue(JSON.stringify((0, _jquery[\"default\"])(this).serializeJSON(), null, 2));\n (0, _jquery[\"default\"])(\"#theme-settings-modal\").modal(\"hide\");\n });\n (0, _jquery[\"default\"])(\"#theme-settings-button\").click(function () {\n var form = (0, _jquery[\"default\"])(\"#theme-settings-modal form\");\n var data; // Ignore invalid JSON data\n\n try {\n data = JSON.parse(theme_settings_editor.getValue());\n } catch (e) {\n data = {};\n }\n\n _jquery[\"default\"].each(data, function (key, value) {\n var ctrl = form.find(\"[name='\".concat(key, \"']\"));\n\n switch (ctrl.prop(\"type\")) {\n case \"radio\":\n case \"checkbox\":\n ctrl.each(function () {\n if ((0, _jquery[\"default\"])(this).attr(\"value\") == value) {\n (0, _jquery[\"default\"])(this).attr(\"checked\", value);\n }\n });\n break;\n\n default:\n ctrl.val(value);\n }\n });\n\n (0, _jquery[\"default\"])(\"#theme-settings-modal\").modal();\n });\n insertTimezones((0, _jquery[\"default\"])(\"#start-timezone\"));\n insertTimezones((0, _jquery[\"default\"])(\"#end-timezone\"));\n insertTimezones((0, _jquery[\"default\"])(\"#freeze-timezone\"));\n (0, _jquery[\"default\"])(\".config-section > form:not(.form-upload, .custom-config-form)\").submit(updateConfigs);\n (0, _jquery[\"default\"])(\"#logo-upload\").submit(uploadLogo);\n (0, _jquery[\"default\"])(\"#user-mode-form\").submit(switchUserMode);\n (0, _jquery[\"default\"])(\"#remove-logo\").click(removeLogo);\n (0, _jquery[\"default\"])(\"#ctf-small-icon-upload\").submit(smallIconUpload);\n (0, _jquery[\"default\"])(\"#remove-small-icon\").click(removeSmallIcon);\n (0, _jquery[\"default\"])(\"#export-button\").click(exportConfig);\n (0, _jquery[\"default\"])(\"#import-button\").click(importConfig);\n (0, _jquery[\"default\"])(\"#import-csv-form\").submit(importCSV);\n (0, _jquery[\"default\"])(\"#config-color-update\").click(function () {\n var hex_code = (0, _jquery[\"default\"])(\"#config-color-picker\").val();\n var user_css = theme_header_editor.getValue();\n var new_css;\n\n if (user_css.length) {\n var css_vars = \"theme-color: \".concat(hex_code, \";\");\n new_css = user_css.replace(/theme-color: (.*);/, css_vars);\n } else {\n new_css = \"\\n\";\n }\n\n theme_header_editor.getDoc().setValue(new_css);\n });\n (0, _jquery[\"default\"])(\".start-date\").change(function () {\n loadDateValues(\"start\");\n });\n (0, _jquery[\"default\"])(\".end-date\").change(function () {\n loadDateValues(\"end\");\n });\n (0, _jquery[\"default\"])(\".freeze-date\").change(function () {\n loadDateValues(\"freeze\");\n });\n var start = (0, _jquery[\"default\"])(\"#start\").val();\n var end = (0, _jquery[\"default\"])(\"#end\").val();\n var freeze = (0, _jquery[\"default\"])(\"#freeze\").val();\n\n if (start) {\n loadTimestamp(\"start\", start);\n }\n\n if (end) {\n loadTimestamp(\"end\", end);\n }\n\n if (freeze) {\n loadTimestamp(\"freeze\", freeze);\n } // Toggle username and password based on stored value\n\n\n (0, _jquery[\"default\"])(\"#mail_useauth\").change(function () {\n (0, _jquery[\"default\"])(\"#mail_username_password\").toggle(this.checked);\n }).change(); // Insert FieldList element for users\n\n var fieldList = _vueEsm[\"default\"].extend(_FieldList[\"default\"]);\n\n var userVueContainer = document.createElement(\"div\");\n document.querySelector(\"#user-field-list\").appendChild(userVueContainer);\n new fieldList({\n propsData: {\n type: \"user\"\n }\n }).$mount(userVueContainer); // Insert FieldList element for teams\n\n var teamVueContainer = document.createElement(\"div\");\n document.querySelector(\"#team-field-list\").appendChild(teamVueContainer);\n new fieldList({\n propsData: {\n type: \"team\"\n }\n }).$mount(teamVueContainer);\n});\n\n//# sourceURL=webpack:///./CTFd/themes/admin/assets/js/pages/configs.js?"); +eval("\n\n__webpack_require__(/*! ./main */ \"./CTFd/themes/admin/assets/js/pages/main.js\");\n\n__webpack_require__(/*! core/utils */ \"./CTFd/themes/core/assets/js/utils.js\");\n\n__webpack_require__(/*! bootstrap/js/dist/tab */ \"./node_modules/bootstrap/js/dist/tab.js\");\n\nvar _dayjs = _interopRequireDefault(__webpack_require__(/*! dayjs */ \"./node_modules/dayjs/dayjs.min.js\"));\n\nvar _advancedFormat = _interopRequireDefault(__webpack_require__(/*! dayjs/plugin/advancedFormat */ \"./node_modules/dayjs/plugin/advancedFormat.js\"));\n\nvar _utc = _interopRequireDefault(__webpack_require__(/*! dayjs/plugin/utc */ \"./node_modules/dayjs/plugin/utc.js\"));\n\nvar _timezone = _interopRequireDefault(__webpack_require__(/*! dayjs/plugin/timezone */ \"./node_modules/dayjs/plugin/timezone.js\"));\n\nvar _timezones = _interopRequireDefault(__webpack_require__(/*! ../timezones */ \"./CTFd/themes/admin/assets/js/timezones.js\"));\n\nvar _CTFd = _interopRequireDefault(__webpack_require__(/*! core/CTFd */ \"./CTFd/themes/core/assets/js/CTFd.js\"));\n\nvar _helpers = _interopRequireDefault(__webpack_require__(/*! core/helpers */ \"./CTFd/themes/core/assets/js/helpers.js\"));\n\nvar _jquery = _interopRequireDefault(__webpack_require__(/*! jquery */ \"./node_modules/jquery/dist/jquery.js\"));\n\nvar _ezq = __webpack_require__(/*! core/ezq */ \"./CTFd/themes/core/assets/js/ezq.js\");\n\nvar _codemirror = _interopRequireDefault(__webpack_require__(/*! codemirror */ \"./node_modules/codemirror/lib/codemirror.js\"));\n\n__webpack_require__(/*! codemirror/mode/htmlmixed/htmlmixed.js */ \"./node_modules/codemirror/mode/htmlmixed/htmlmixed.js\");\n\nvar _vueEsm = _interopRequireDefault(__webpack_require__(/*! vue/dist/vue.esm.browser */ \"./node_modules/vue/dist/vue.esm.browser.js\"));\n\nvar _FieldList = _interopRequireDefault(__webpack_require__(/*! ../components/configs/fields/FieldList.vue */ \"./CTFd/themes/admin/assets/js/components/configs/fields/FieldList.vue\"));\n\nfunction _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { \"default\": obj }; }\n\n_dayjs[\"default\"].extend(_advancedFormat[\"default\"]);\n\n_dayjs[\"default\"].extend(_utc[\"default\"]);\n\n_dayjs[\"default\"].extend(_timezone[\"default\"]);\n\nfunction loadTimestamp(place, timestamp) {\n if (typeof timestamp == \"string\") {\n timestamp = parseInt(timestamp, 10) * 1000;\n }\n\n var d = (0, _dayjs[\"default\"])(timestamp);\n (0, _jquery[\"default\"])(\"#\" + place + \"-month\").val(d.month() + 1); // Months are zero indexed (https://day.js.org/docs/en/get-set/month)\n\n (0, _jquery[\"default\"])(\"#\" + place + \"-day\").val(d.date());\n (0, _jquery[\"default\"])(\"#\" + place + \"-year\").val(d.year());\n (0, _jquery[\"default\"])(\"#\" + place + \"-hour\").val(d.hour());\n (0, _jquery[\"default\"])(\"#\" + place + \"-minute\").val(d.minute());\n loadDateValues(place);\n}\n\nfunction loadDateValues(place) {\n var month = (0, _jquery[\"default\"])(\"#\" + place + \"-month\").val();\n var day = (0, _jquery[\"default\"])(\"#\" + place + \"-day\").val();\n var year = (0, _jquery[\"default\"])(\"#\" + place + \"-year\").val();\n var hour = (0, _jquery[\"default\"])(\"#\" + place + \"-hour\").val();\n var minute = (0, _jquery[\"default\"])(\"#\" + place + \"-minute\").val();\n var timezone_string = (0, _jquery[\"default\"])(\"#\" + place + \"-timezone\").val();\n var utc = convertDateToMoment(month, day, year, hour, minute);\n\n if (utc.unix() && month && day && year && hour && minute) {\n (0, _jquery[\"default\"])(\"#\" + place).val(utc.unix());\n (0, _jquery[\"default\"])(\"#\" + place + \"-local\").val(utc.format(\"dddd, MMMM Do YYYY, h:mm:ss a z (zzz)\"));\n (0, _jquery[\"default\"])(\"#\" + place + \"-zonetime\").val(utc.tz(timezone_string).format(\"dddd, MMMM Do YYYY, h:mm:ss a z (zzz)\"));\n } else {\n (0, _jquery[\"default\"])(\"#\" + place).val(\"\");\n (0, _jquery[\"default\"])(\"#\" + place + \"-local\").val(\"\");\n (0, _jquery[\"default\"])(\"#\" + place + \"-zonetime\").val(\"\");\n }\n}\n\nfunction convertDateToMoment(month, day, year, hour, minute) {\n var month_num = month.toString();\n\n if (month_num.length == 1) {\n month_num = \"0\" + month_num;\n }\n\n var day_str = day.toString();\n\n if (day_str.length == 1) {\n day_str = \"0\" + day_str;\n }\n\n var hour_str = hour.toString();\n\n if (hour_str.length == 1) {\n hour_str = \"0\" + hour_str;\n }\n\n var min_str = minute.toString();\n\n if (min_str.length == 1) {\n min_str = \"0\" + min_str;\n } // 2013-02-08 24:00\n\n\n var date_string = year.toString() + \"-\" + month_num + \"-\" + day_str + \" \" + hour_str + \":\" + min_str + \":00\";\n return (0, _dayjs[\"default\"])(date_string);\n}\n\nfunction updateConfigs(event) {\n event.preventDefault();\n var obj = (0, _jquery[\"default\"])(this).serializeJSON();\n var params = {};\n\n if (obj.mail_useauth === false) {\n obj.mail_username = null;\n obj.mail_password = null;\n } else {\n if (obj.mail_username === \"\") {\n delete obj.mail_username;\n }\n\n if (obj.mail_password === \"\") {\n delete obj.mail_password;\n }\n }\n\n Object.keys(obj).forEach(function (x) {\n if (obj[x] === \"true\") {\n params[x] = true;\n } else if (obj[x] === \"false\") {\n params[x] = false;\n } else {\n params[x] = obj[x];\n }\n });\n\n _CTFd[\"default\"].api.patch_config_list({}, params).then(function (_response) {\n if (_response.success) {\n window.location.reload();\n } else {\n var errors = _response.errors.value.join(\"\\n\");\n\n (0, _ezq.ezAlert)({\n title: \"Error!\",\n body: errors,\n button: \"Okay\"\n });\n }\n });\n}\n\nfunction uploadLogo(event) {\n event.preventDefault();\n var form = event.target;\n\n _helpers[\"default\"].files.upload(form, {}, function (response) {\n var f = response.data[0];\n var params = {\n value: f.location\n };\n\n _CTFd[\"default\"].fetch(\"/api/v1/configs/ctf_logo\", {\n method: \"PATCH\",\n body: JSON.stringify(params)\n }).then(function (response) {\n return response.json();\n }).then(function (response) {\n if (response.success) {\n window.location.reload();\n } else {\n (0, _ezq.ezAlert)({\n title: \"Error!\",\n body: \"Logo uploading failed!\",\n button: \"Okay\"\n });\n }\n });\n });\n}\n\nfunction switchUserMode(event) {\n event.preventDefault();\n\n if (confirm(\"Are you sure you'd like to switch user modes?\\n\\nAll user submissions, awards, unlocks, and tracking will be deleted!\")) {\n var formData = new FormData();\n formData.append(\"submissions\", true);\n formData.append(\"nonce\", _CTFd[\"default\"].config.csrfNonce);\n fetch(_CTFd[\"default\"].config.urlRoot + \"/admin/reset\", {\n method: \"POST\",\n credentials: \"same-origin\",\n body: formData\n }); // Bind `this` so that we can reuse the updateConfigs function\n\n var binded = updateConfigs.bind(this);\n binded(event);\n }\n}\n\nfunction removeLogo() {\n (0, _ezq.ezQuery)({\n title: \"Remove logo\",\n body: \"Are you sure you'd like to remove the CTF logo?\",\n success: function success() {\n var params = {\n value: null\n };\n\n _CTFd[\"default\"].api.patch_config({\n configKey: \"ctf_logo\"\n }, params).then(function (_response) {\n window.location.reload();\n });\n }\n });\n}\n\nfunction smallIconUpload(event) {\n event.preventDefault();\n var form = event.target;\n\n _helpers[\"default\"].files.upload(form, {}, function (response) {\n var f = response.data[0];\n var params = {\n value: f.location\n };\n\n _CTFd[\"default\"].fetch(\"/api/v1/configs/ctf_small_icon\", {\n method: \"PATCH\",\n body: JSON.stringify(params)\n }).then(function (response) {\n return response.json();\n }).then(function (response) {\n if (response.success) {\n window.location.reload();\n } else {\n (0, _ezq.ezAlert)({\n title: \"Error!\",\n body: \"Icon uploading failed!\",\n button: \"Okay\"\n });\n }\n });\n });\n}\n\nfunction removeSmallIcon() {\n (0, _ezq.ezQuery)({\n title: \"Remove logo\",\n body: \"Are you sure you'd like to remove the small site icon?\",\n success: function success() {\n var params = {\n value: null\n };\n\n _CTFd[\"default\"].api.patch_config({\n configKey: \"ctf_small_icon\"\n }, params).then(function (_response) {\n window.location.reload();\n });\n }\n });\n}\n\nfunction importCSV(event) {\n event.preventDefault();\n var csv_file = document.getElementById(\"import-csv-file\").files[0];\n var csv_type = document.getElementById(\"import-csv-type\").value;\n var form_data = new FormData();\n form_data.append(\"csv_file\", csv_file);\n form_data.append(\"csv_type\", csv_type);\n form_data.append(\"nonce\", _CTFd[\"default\"].config.csrfNonce);\n var pg = (0, _ezq.ezProgressBar)({\n width: 0,\n title: \"Upload Progress\"\n });\n\n _jquery[\"default\"].ajax({\n url: _CTFd[\"default\"].config.urlRoot + \"/admin/import/csv\",\n type: \"POST\",\n data: form_data,\n processData: false,\n contentType: false,\n statusCode: {\n 500: function _(resp) {\n // Normalize errors\n var errors = JSON.parse(resp.responseText);\n var errorText = \"\";\n errors.forEach(function (element) {\n errorText += \"Line \".concat(element[0], \": \").concat(JSON.stringify(element[1]), \"\\n\");\n }); // Show errors\n\n alert(errorText); // Hide progress modal if its there\n\n pg = (0, _ezq.ezProgressBar)({\n target: pg,\n width: 100\n });\n setTimeout(function () {\n pg.modal(\"hide\");\n }, 500);\n }\n },\n xhr: function xhr() {\n var xhr = _jquery[\"default\"].ajaxSettings.xhr();\n\n xhr.upload.onprogress = function (e) {\n if (e.lengthComputable) {\n var width = e.loaded / e.total * 100;\n pg = (0, _ezq.ezProgressBar)({\n target: pg,\n width: width\n });\n }\n };\n\n return xhr;\n },\n success: function success(_data) {\n pg = (0, _ezq.ezProgressBar)({\n target: pg,\n width: 100\n });\n setTimeout(function () {\n pg.modal(\"hide\");\n }, 500);\n setTimeout(function () {\n window.location.reload();\n }, 700);\n }\n });\n}\n\nfunction importConfig(event) {\n event.preventDefault();\n var import_file = document.getElementById(\"import-file\").files[0];\n var form_data = new FormData();\n form_data.append(\"backup\", import_file);\n form_data.append(\"nonce\", _CTFd[\"default\"].config.csrfNonce);\n var pg = (0, _ezq.ezProgressBar)({\n width: 0,\n title: \"Upload Progress\"\n });\n\n _jquery[\"default\"].ajax({\n url: _CTFd[\"default\"].config.urlRoot + \"/admin/import\",\n type: \"POST\",\n data: form_data,\n processData: false,\n contentType: false,\n statusCode: {\n 500: function _(resp) {\n alert(resp.responseText);\n }\n },\n xhr: function xhr() {\n var xhr = _jquery[\"default\"].ajaxSettings.xhr();\n\n xhr.upload.onprogress = function (e) {\n if (e.lengthComputable) {\n var width = e.loaded / e.total * 100;\n pg = (0, _ezq.ezProgressBar)({\n target: pg,\n width: width\n });\n }\n };\n\n return xhr;\n },\n success: function success(_data) {\n pg = (0, _ezq.ezProgressBar)({\n target: pg,\n width: 100\n });\n location.href = _CTFd[\"default\"].config.urlRoot + \"/admin/import\";\n }\n });\n}\n\nfunction exportConfig(event) {\n event.preventDefault();\n window.location.href = (0, _jquery[\"default\"])(this).attr(\"href\");\n}\n\nfunction insertTimezones(target) {\n var current = (0, _jquery[\"default\"])(\"\").text(_dayjs[\"default\"].tz.guess());\n (0, _jquery[\"default\"])(target).append(current);\n var tz_names = _timezones[\"default\"];\n\n for (var i = 0; i < tz_names.length; i++) {\n var tz = (0, _jquery[\"default\"])(\"\").text(tz_names[i]);\n (0, _jquery[\"default\"])(target).append(tz);\n }\n}\n\n(0, _jquery[\"default\"])(function () {\n var theme_header_editor = _codemirror[\"default\"].fromTextArea(document.getElementById(\"theme-header\"), {\n lineNumbers: true,\n lineWrapping: true,\n mode: \"htmlmixed\",\n htmlMode: true\n });\n\n var theme_footer_editor = _codemirror[\"default\"].fromTextArea(document.getElementById(\"theme-footer\"), {\n lineNumbers: true,\n lineWrapping: true,\n mode: \"htmlmixed\",\n htmlMode: true\n });\n\n var theme_settings_editor = _codemirror[\"default\"].fromTextArea(document.getElementById(\"theme-settings\"), {\n lineNumbers: true,\n lineWrapping: true,\n readOnly: true,\n mode: {\n name: \"javascript\",\n json: true\n }\n }); // Handle refreshing codemirror when switching tabs.\n // Better than the autorefresh approach b/c there's no flicker\n\n\n (0, _jquery[\"default\"])(\"a[href='#theme']\").on(\"shown.bs.tab\", function (_e) {\n theme_header_editor.refresh();\n theme_footer_editor.refresh();\n theme_settings_editor.refresh();\n });\n (0, _jquery[\"default\"])(\"a[href='#legal'], a[href='#tos-config'], a[href='#privacy-policy-config']\").on(\"shown.bs.tab\", function (_e) {\n (0, _jquery[\"default\"])(\"#tos-config .CodeMirror\").each(function (i, el) {\n el.CodeMirror.refresh();\n });\n (0, _jquery[\"default\"])(\"#privacy-policy-config .CodeMirror\").each(function (i, el) {\n el.CodeMirror.refresh();\n });\n });\n (0, _jquery[\"default\"])(\"#theme-settings-modal form\").submit(function (e) {\n e.preventDefault();\n theme_settings_editor.getDoc().setValue(JSON.stringify((0, _jquery[\"default\"])(this).serializeJSON(), null, 2));\n (0, _jquery[\"default\"])(\"#theme-settings-modal\").modal(\"hide\");\n });\n (0, _jquery[\"default\"])(\"#theme-settings-button\").click(function () {\n var form = (0, _jquery[\"default\"])(\"#theme-settings-modal form\");\n var data; // Ignore invalid JSON data\n\n try {\n data = JSON.parse(theme_settings_editor.getValue());\n } catch (e) {\n data = {};\n }\n\n _jquery[\"default\"].each(data, function (key, value) {\n var ctrl = form.find(\"[name='\".concat(key, \"']\"));\n\n switch (ctrl.prop(\"type\")) {\n case \"radio\":\n case \"checkbox\":\n ctrl.each(function () {\n if ((0, _jquery[\"default\"])(this).attr(\"value\") == value) {\n (0, _jquery[\"default\"])(this).attr(\"checked\", value);\n }\n });\n break;\n\n default:\n ctrl.val(value);\n }\n });\n\n (0, _jquery[\"default\"])(\"#theme-settings-modal\").modal();\n });\n insertTimezones((0, _jquery[\"default\"])(\"#start-timezone\"));\n insertTimezones((0, _jquery[\"default\"])(\"#end-timezone\"));\n insertTimezones((0, _jquery[\"default\"])(\"#freeze-timezone\"));\n (0, _jquery[\"default\"])(\".config-section > form:not(.form-upload, .custom-config-form)\").submit(updateConfigs);\n (0, _jquery[\"default\"])(\"#logo-upload\").submit(uploadLogo);\n (0, _jquery[\"default\"])(\"#user-mode-form\").submit(switchUserMode);\n (0, _jquery[\"default\"])(\"#remove-logo\").click(removeLogo);\n (0, _jquery[\"default\"])(\"#ctf-small-icon-upload\").submit(smallIconUpload);\n (0, _jquery[\"default\"])(\"#remove-small-icon\").click(removeSmallIcon);\n (0, _jquery[\"default\"])(\"#export-button\").click(exportConfig);\n (0, _jquery[\"default\"])(\"#import-button\").click(importConfig);\n (0, _jquery[\"default\"])(\"#import-csv-form\").submit(importCSV);\n (0, _jquery[\"default\"])(\"#config-color-update\").click(function () {\n var hex_code = (0, _jquery[\"default\"])(\"#config-color-picker\").val();\n var user_css = theme_header_editor.getValue();\n var new_css;\n\n if (user_css.length) {\n var css_vars = \"theme-color: \".concat(hex_code, \";\");\n new_css = user_css.replace(/theme-color: (.*);/, css_vars);\n } else {\n new_css = \"\\n\";\n }\n\n theme_header_editor.getDoc().setValue(new_css);\n });\n (0, _jquery[\"default\"])(\".start-date\").change(function () {\n loadDateValues(\"start\");\n });\n (0, _jquery[\"default\"])(\".end-date\").change(function () {\n loadDateValues(\"end\");\n });\n (0, _jquery[\"default\"])(\".freeze-date\").change(function () {\n loadDateValues(\"freeze\");\n });\n var start = (0, _jquery[\"default\"])(\"#start\").val();\n var end = (0, _jquery[\"default\"])(\"#end\").val();\n var freeze = (0, _jquery[\"default\"])(\"#freeze\").val();\n\n if (start) {\n loadTimestamp(\"start\", start);\n }\n\n if (end) {\n loadTimestamp(\"end\", end);\n }\n\n if (freeze) {\n loadTimestamp(\"freeze\", freeze);\n } // Toggle username and password based on stored value\n\n\n (0, _jquery[\"default\"])(\"#mail_useauth\").change(function () {\n (0, _jquery[\"default\"])(\"#mail_username_password\").toggle(this.checked);\n }).change(); // Insert FieldList element for users\n\n var fieldList = _vueEsm[\"default\"].extend(_FieldList[\"default\"]);\n\n var userVueContainer = document.createElement(\"div\");\n document.querySelector(\"#user-field-list\").appendChild(userVueContainer);\n new fieldList({\n propsData: {\n type: \"user\"\n }\n }).$mount(userVueContainer); // Insert FieldList element for teams\n\n var teamVueContainer = document.createElement(\"div\");\n document.querySelector(\"#team-field-list\").appendChild(teamVueContainer);\n new fieldList({\n propsData: {\n type: \"team\"\n }\n }).$mount(teamVueContainer);\n});\n\n//# sourceURL=webpack:///./CTFd/themes/admin/assets/js/pages/configs.js?"); /***/ }), diff --git a/CTFd/themes/admin/static/js/pages/configs.min.js b/CTFd/themes/admin/static/js/pages/configs.min.js index 567dd9a5..06d1b29e 100644 --- a/CTFd/themes/admin/static/js/pages/configs.min.js +++ b/CTFd/themes/admin/static/js/pages/configs.min.js @@ -1 +1 @@ -!function(c){function e(e){for(var t,a,i=e[0],o=e[1],n=e[2],r=0,s=[];r").text(l.default.tz.guess());(0,d.default)(e).append(t);for(var a=r.default,i=0;i").text(a[i]);(0,d.default)(e).append(o)}}l.default.extend(i.default),l.default.extend(o.default),l.default.extend(n.default),(0,d.default)(function(){var o=p.default.fromTextArea(document.getElementById("theme-header"),{lineNumbers:!0,lineWrapping:!0,mode:"htmlmixed",htmlMode:!0}),t=p.default.fromTextArea(document.getElementById("theme-footer"),{lineNumbers:!0,lineWrapping:!0,mode:"htmlmixed",htmlMode:!0}),a=p.default.fromTextArea(document.getElementById("theme-settings"),{lineNumbers:!0,lineWrapping:!0,readOnly:!0,mode:{name:"javascript",json:!0}});(0,d.default)("a[href='#theme']").on("shown.bs.tab",function(e){o.refresh(),t.refresh(),a.refresh()}),(0,d.default)("a[href='#legal'], a[href='#tos-config'], a[href='#privacy-policy-config']").on("shown.bs.tab",function(e){(0,d.default)("#tos-config .CodeMirror").each(function(e,t){t.CodeMirror.refresh()}),(0,d.default)("#privacy-policy-config .CodeMirror").each(function(e,t){t.CodeMirror.refresh()})}),(0,d.default)("#theme-settings-modal form").submit(function(e){e.preventDefault(),a.getDoc().setValue(JSON.stringify((0,d.default)(this).serializeJSON(),null,2)),(0,d.default)("#theme-settings-modal").modal("hide")}),(0,d.default)("#theme-settings-button").click(function(){var t,i=(0,d.default)("#theme-settings-modal form");try{t=JSON.parse(a.getValue())}catch(e){t={}}d.default.each(t,function(e,t){var a=i.find("[name='".concat(e,"']"));switch(a.prop("type")){case"radio":case"checkbox":a.each(function(){(0,d.default)(this).attr("value")==t&&(0,d.default)(this).attr("checked",t)});break;default:a.val(t)}}),(0,d.default)("#theme-settings-modal").modal()}),I((0,d.default)("#start-timezone")),I((0,d.default)("#end-timezone")),I((0,d.default)("#freeze-timezone")),(0,d.default)(".config-section > form:not(.form-upload, .custom-config-form)").submit(A),(0,d.default)("#logo-upload").submit(y),(0,d.default)("#user-mode-form").submit(_),(0,d.default)("#remove-logo").click(j),(0,d.default)("#ctf-small-icon-upload").submit(T),(0,d.default)("#remove-small-icon").click(b),(0,d.default)("#export-button").click(w),(0,d.default)("#import-button").click(C),(0,d.default)("#import-csv-form").submit(E),(0,d.default)("#config-color-update").click(function(){var e,t,a=(0,d.default)("#config-color-picker").val(),i=o.getValue();t=i.length?(e="theme-color: ".concat(a,";"),i.replace(/theme-color: (.*);/,e)):'\n",o.getDoc().setValue(t)}),(0,d.default)(".start-date").change(function(){v("start")}),(0,d.default)(".end-date").change(function(){v("end")}),(0,d.default)(".freeze-date").change(function(){v("freeze")});var e=(0,d.default)("#start").val(),i=(0,d.default)("#end").val(),n=(0,d.default)("#freeze").val();e&&g("start",e),i&&g("end",i),n&&g("freeze",n),(0,d.default)("#mail_useauth").change(function(){(0,d.default)("#mail_username_password").toggle(this.checked)}).change();var r=f.default.extend(m.default),s=document.createElement("div");document.querySelector("#user-field-list").appendChild(s),new r({propsData:{type:"user"}}).$mount(s);var c=document.createElement("div");document.querySelector("#team-field-list").appendChild(c),new r({propsData:{type:"team"}}).$mount(c)})},"./CTFd/themes/admin/assets/js/pages/main.js":function(e,t,a){var i=f(a("./CTFd/themes/core/assets/js/CTFd.js")),o=f(a("./node_modules/jquery/dist/jquery.js")),n=f(a("./node_modules/dayjs/dayjs.min.js")),r=f(a("./node_modules/dayjs/plugin/advancedFormat.js")),s=f(a("./node_modules/nunjucks/browser/nunjucks.js")),c=a("./node_modules/howler/dist/howler.js"),d=f(a("./CTFd/themes/core/assets/js/events.js")),l=f(a("./CTFd/themes/core/assets/js/times.js")),u=f(a("./CTFd/themes/admin/assets/js/styles.js")),p=f(a("./CTFd/themes/core/assets/js/helpers.js"));function f(e){return e&&e.__esModule?e:{default:e}}n.default.extend(r.default),i.default.init(window.init),window.CTFd=i.default,window.helpers=p.default,window.$=o.default,window.dayjs=n.default,window.nunjucks=s.default,window.Howl=c.Howl,(0,o.default)(function(){(0,u.default)(),(0,l.default)(),(0,d.default)(i.default.config.urlRoot)})},"./CTFd/themes/admin/assets/js/styles.js":function(e,t,a){Object.defineProperty(t,"__esModule",{value:!0}),t.showMediaLibrary=l,t.bindMarkdownEditor=u,t.bindMarkdownEditors=p,t.default=void 0,a("./node_modules/bootstrap/dist/js/bootstrap.bundle.js");var i=a("./CTFd/themes/core/assets/js/utils.js"),o=d(a("./node_modules/jquery/dist/jquery.js")),n=d(a("./node_modules/easymde/src/js/easymde.js")),r=d(a("./node_modules/vue/dist/vue.esm.browser.js")),s=d(a("./CTFd/themes/admin/assets/js/components/files/MediaLibrary.vue")),c=d(a("./node_modules/highlight.js/lib/index.js"));function d(e){return e&&e.__esModule?e:{default:e}}function l(e){var t=r.default.extend(s.default),a=document.createElement("div");document.querySelector("main").appendChild(a);var i=new t({propsData:{editor:e}}).$mount(a);(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 u(e){var t;!1===e.hasOwnProperty("mde")&&(t=new n.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:c.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 p(){(0,o.default)("textarea.markdown").each(function(e,t){u(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"),a=(0,o.default)(this).index()+1;(0,o.default)(this).closest("table").find("tr td:nth-child(".concat(a,") 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")),p(),(0,i.makeSortableTables)(),(0,o.default)('[data-toggle="tooltip"]').tooltip(),document.querySelectorAll("pre code").forEach(function(e){c.default.highlightBlock(e)})})}},"./CTFd/themes/admin/assets/js/timezones.js":function(e,t,a){Object.defineProperty(t,"__esModule",{value:!0}),t.default=void 0;var i=["Africa/Abidjan","Africa/Accra","Africa/Addis_Ababa","Africa/Algiers","Africa/Asmara","Africa/Asmera","Africa/Bamako","Africa/Bangui","Africa/Banjul","Africa/Bissau","Africa/Blantyre","Africa/Brazzaville","Africa/Bujumbura","Africa/Cairo","Africa/Casablanca","Africa/Ceuta","Africa/Conakry","Africa/Dakar","Africa/Dar_es_Salaam","Africa/Djibouti","Africa/Douala","Africa/El_Aaiun","Africa/Freetown","Africa/Gaborone","Africa/Harare","Africa/Johannesburg","Africa/Juba","Africa/Kampala","Africa/Khartoum","Africa/Kigali","Africa/Kinshasa","Africa/Lagos","Africa/Libreville","Africa/Lome","Africa/Luanda","Africa/Lubumbashi","Africa/Lusaka","Africa/Malabo","Africa/Maputo","Africa/Maseru","Africa/Mbabane","Africa/Mogadishu","Africa/Monrovia","Africa/Nairobi","Africa/Ndjamena","Africa/Niamey","Africa/Nouakchott","Africa/Ouagadougou","Africa/Porto-Novo","Africa/Sao_Tome","Africa/Timbuktu","Africa/Tripoli","Africa/Tunis","Africa/Windhoek","America/Adak","America/Anchorage","America/Anguilla","America/Antigua","America/Araguaina","America/Argentina/Buenos_Aires","America/Argentina/Catamarca","America/Argentina/ComodRivadavia","America/Argentina/Cordoba","America/Argentina/Jujuy","America/Argentina/La_Rioja","America/Argentina/Mendoza","America/Argentina/Rio_Gallegos","America/Argentina/Salta","America/Argentina/San_Juan","America/Argentina/San_Luis","America/Argentina/Tucuman","America/Argentina/Ushuaia","America/Aruba","America/Asuncion","America/Atikokan","America/Atka","America/Bahia","America/Bahia_Banderas","America/Barbados","America/Belem","America/Belize","America/Blanc-Sablon","America/Boa_Vista","America/Bogota","America/Boise","America/Buenos_Aires","America/Cambridge_Bay","America/Campo_Grande","America/Cancun","America/Caracas","America/Catamarca","America/Cayenne","America/Cayman","America/Chicago","America/Chihuahua","America/Coral_Harbour","America/Cordoba","America/Costa_Rica","America/Creston","America/Cuiaba","America/Curacao","America/Danmarkshavn","America/Dawson","America/Dawson_Creek","America/Denver","America/Detroit","America/Dominica","America/Edmonton","America/Eirunepe","America/El_Salvador","America/Ensenada","America/Fort_Nelson","America/Fort_Wayne","America/Fortaleza","America/Glace_Bay","America/Godthab","America/Goose_Bay","America/Grand_Turk","America/Grenada","America/Guadeloupe","America/Guatemala","America/Guayaquil","America/Guyana","America/Halifax","America/Havana","America/Hermosillo","America/Indiana/Indianapolis","America/Indiana/Knox","America/Indiana/Marengo","America/Indiana/Petersburg","America/Indiana/Tell_City","America/Indiana/Vevay","America/Indiana/Vincennes","America/Indiana/Winamac","America/Indianapolis","America/Inuvik","America/Iqaluit","America/Jamaica","America/Jujuy","America/Juneau","America/Kentucky/Louisville","America/Kentucky/Monticello","America/Knox_IN","America/Kralendijk","America/La_Paz","America/Lima","America/Los_Angeles","America/Louisville","America/Lower_Princes","America/Maceio","America/Managua","America/Manaus","America/Marigot","America/Martinique","America/Matamoros","America/Mazatlan","America/Mendoza","America/Menominee","America/Merida","America/Metlakatla","America/Mexico_City","America/Miquelon","America/Moncton","America/Monterrey","America/Montevideo","America/Montreal","America/Montserrat","America/Nassau","America/New_York","America/Nipigon","America/Nome","America/Noronha","America/North_Dakota/Beulah","America/North_Dakota/Center","America/North_Dakota/New_Salem","America/Nuuk","America/Ojinaga","America/Panama","America/Pangnirtung","America/Paramaribo","America/Phoenix","America/Port-au-Prince","America/Port_of_Spain","America/Porto_Acre","America/Porto_Velho","America/Puerto_Rico","America/Punta_Arenas","America/Rainy_River","America/Rankin_Inlet","America/Recife","America/Regina","America/Resolute","America/Rio_Branco","America/Rosario","America/Santa_Isabel","America/Santarem","America/Santiago","America/Santo_Domingo","America/Sao_Paulo","America/Scoresbysund","America/Shiprock","America/Sitka","America/St_Barthelemy","America/St_Johns","America/St_Kitts","America/St_Lucia","America/St_Thomas","America/St_Vincent","America/Swift_Current","America/Tegucigalpa","America/Thule","America/Thunder_Bay","America/Tijuana","America/Toronto","America/Tortola","America/Vancouver","America/Virgin","America/Whitehorse","America/Winnipeg","America/Yakutat","America/Yellowknife","Antarctica/Casey","Antarctica/Davis","Antarctica/DumontDUrville","Antarctica/Macquarie","Antarctica/Mawson","Antarctica/McMurdo","Antarctica/Palmer","Antarctica/Rothera","Antarctica/South_Pole","Antarctica/Syowa","Antarctica/Troll","Antarctica/Vostok","Arctic/Longyearbyen","Asia/Aden","Asia/Almaty","Asia/Amman","Asia/Anadyr","Asia/Aqtau","Asia/Aqtobe","Asia/Ashgabat","Asia/Ashkhabad","Asia/Atyrau","Asia/Baghdad","Asia/Bahrain","Asia/Baku","Asia/Bangkok","Asia/Barnaul","Asia/Beirut","Asia/Bishkek","Asia/Brunei","Asia/Calcutta","Asia/Chita","Asia/Choibalsan","Asia/Chongqing","Asia/Chungking","Asia/Colombo","Asia/Dacca","Asia/Damascus","Asia/Dhaka","Asia/Dili","Asia/Dubai","Asia/Dushanbe","Asia/Famagusta","Asia/Gaza","Asia/Harbin","Asia/Hebron","Asia/Ho_Chi_Minh","Asia/Hong_Kong","Asia/Hovd","Asia/Irkutsk","Asia/Istanbul","Asia/Jakarta","Asia/Jayapura","Asia/Jerusalem","Asia/Kabul","Asia/Kamchatka","Asia/Karachi","Asia/Kashgar","Asia/Kathmandu","Asia/Katmandu","Asia/Khandyga","Asia/Kolkata","Asia/Krasnoyarsk","Asia/Kuala_Lumpur","Asia/Kuching","Asia/Kuwait","Asia/Macao","Asia/Macau","Asia/Magadan","Asia/Makassar","Asia/Manila","Asia/Muscat","Asia/Nicosia","Asia/Novokuznetsk","Asia/Novosibirsk","Asia/Omsk","Asia/Oral","Asia/Phnom_Penh","Asia/Pontianak","Asia/Pyongyang","Asia/Qatar","Asia/Qostanay","Asia/Qyzylorda","Asia/Rangoon","Asia/Riyadh","Asia/Saigon","Asia/Sakhalin","Asia/Samarkand","Asia/Seoul","Asia/Shanghai","Asia/Singapore","Asia/Srednekolymsk","Asia/Taipei","Asia/Tashkent","Asia/Tbilisi","Asia/Tehran","Asia/Tel_Aviv","Asia/Thimbu","Asia/Thimphu","Asia/Tokyo","Asia/Tomsk","Asia/Ujung_Pandang","Asia/Ulaanbaatar","Asia/Ulan_Bator","Asia/Urumqi","Asia/Ust-Nera","Asia/Vientiane","Asia/Vladivostok","Asia/Yakutsk","Asia/Yangon","Asia/Yekaterinburg","Asia/Yerevan","Atlantic/Azores","Atlantic/Bermuda","Atlantic/Canary","Atlantic/Cape_Verde","Atlantic/Faeroe","Atlantic/Faroe","Atlantic/Jan_Mayen","Atlantic/Madeira","Atlantic/Reykjavik","Atlantic/South_Georgia","Atlantic/St_Helena","Atlantic/Stanley","Australia/ACT","Australia/Adelaide","Australia/Brisbane","Australia/Broken_Hill","Australia/Canberra","Australia/Currie","Australia/Darwin","Australia/Eucla","Australia/Hobart","Australia/LHI","Australia/Lindeman","Australia/Lord_Howe","Australia/Melbourne","Australia/NSW","Australia/North","Australia/Perth","Australia/Queensland","Australia/South","Australia/Sydney","Australia/Tasmania","Australia/Victoria","Australia/West","Australia/Yancowinna","Brazil/Acre","Brazil/DeNoronha","Brazil/East","Brazil/West","CET","CST6CDT","Canada/Atlantic","Canada/Central","Canada/Eastern","Canada/Mountain","Canada/Newfoundland","Canada/Pacific","Canada/Saskatchewan","Canada/Yukon","Chile/Continental","Chile/EasterIsland","Cuba","EET","EST","EST5EDT","Egypt","Eire","Etc/GMT","Etc/GMT+0","Etc/GMT+1","Etc/GMT+10","Etc/GMT+11","Etc/GMT+12","Etc/GMT+2","Etc/GMT+3","Etc/GMT+4","Etc/GMT+5","Etc/GMT+6","Etc/GMT+7","Etc/GMT+8","Etc/GMT+9","Etc/GMT-0","Etc/GMT-1","Etc/GMT-10","Etc/GMT-11","Etc/GMT-12","Etc/GMT-13","Etc/GMT-14","Etc/GMT-2","Etc/GMT-3","Etc/GMT-4","Etc/GMT-5","Etc/GMT-6","Etc/GMT-7","Etc/GMT-8","Etc/GMT-9","Etc/GMT0","Etc/Greenwich","Etc/UCT","Etc/UTC","Etc/Universal","Etc/Zulu","Europe/Amsterdam","Europe/Andorra","Europe/Astrakhan","Europe/Athens","Europe/Belfast","Europe/Belgrade","Europe/Berlin","Europe/Bratislava","Europe/Brussels","Europe/Bucharest","Europe/Budapest","Europe/Busingen","Europe/Chisinau","Europe/Copenhagen","Europe/Dublin","Europe/Gibraltar","Europe/Guernsey","Europe/Helsinki","Europe/Isle_of_Man","Europe/Istanbul","Europe/Jersey","Europe/Kaliningrad","Europe/Kiev","Europe/Kirov","Europe/Lisbon","Europe/Ljubljana","Europe/London","Europe/Luxembourg","Europe/Madrid","Europe/Malta","Europe/Mariehamn","Europe/Minsk","Europe/Monaco","Europe/Moscow","Europe/Nicosia","Europe/Oslo","Europe/Paris","Europe/Podgorica","Europe/Prague","Europe/Riga","Europe/Rome","Europe/Samara","Europe/San_Marino","Europe/Sarajevo","Europe/Saratov","Europe/Simferopol","Europe/Skopje","Europe/Sofia","Europe/Stockholm","Europe/Tallinn","Europe/Tirane","Europe/Tiraspol","Europe/Ulyanovsk","Europe/Uzhgorod","Europe/Vaduz","Europe/Vatican","Europe/Vienna","Europe/Vilnius","Europe/Volgograd","Europe/Warsaw","Europe/Zagreb","Europe/Zaporozhye","Europe/Zurich","GB","GB-Eire","GMT","GMT+0","GMT-0","GMT0","Greenwich","HST","Hongkong","Iceland","Indian/Antananarivo","Indian/Chagos","Indian/Christmas","Indian/Cocos","Indian/Comoro","Indian/Kerguelen","Indian/Mahe","Indian/Maldives","Indian/Mauritius","Indian/Mayotte","Indian/Reunion","Iran","Israel","Jamaica","Japan","Kwajalein","Libya","MET","MST","MST7MDT","Mexico/BajaNorte","Mexico/BajaSur","Mexico/General","NZ","NZ-CHAT","Navajo","PRC","PST8PDT","Pacific/Apia","Pacific/Auckland","Pacific/Bougainville","Pacific/Chatham","Pacific/Chuuk","Pacific/Easter","Pacific/Efate","Pacific/Enderbury","Pacific/Fakaofo","Pacific/Fiji","Pacific/Funafuti","Pacific/Galapagos","Pacific/Gambier","Pacific/Guadalcanal","Pacific/Guam","Pacific/Honolulu","Pacific/Johnston","Pacific/Kiritimati","Pacific/Kosrae","Pacific/Kwajalein","Pacific/Majuro","Pacific/Marquesas","Pacific/Midway","Pacific/Nauru","Pacific/Niue","Pacific/Norfolk","Pacific/Noumea","Pacific/Pago_Pago","Pacific/Palau","Pacific/Pitcairn","Pacific/Pohnpei","Pacific/Ponape","Pacific/Port_Moresby","Pacific/Rarotonga","Pacific/Saipan","Pacific/Samoa","Pacific/Tahiti","Pacific/Tarawa","Pacific/Tongatapu","Pacific/Truk","Pacific/Wake","Pacific/Wallis","Pacific/Yap","Poland","Portugal","ROC","ROK","Singapore","Turkey","UCT","US/Alaska","US/Aleutian","US/Arizona","US/Central","US/East-Indiana","US/Eastern","US/Hawaii","US/Indiana-Starke","US/Michigan","US/Mountain","US/Pacific","US/Pacific-New","US/Samoa","UTC","Universal","W-SU","WET","Zulu"];t.default=i},"./CTFd/themes/core/assets/js/CTFd.js":function(e,t,a){Object.defineProperty(t,"__esModule",{value:!0}),t.default=void 0;var i=d(a("./CTFd/themes/core/assets/js/fetch.js")),o=d(a("./CTFd/themes/core/assets/js/config.js")),n=a("./CTFd/themes/core/assets/js/api.js");a("./CTFd/themes/core/assets/js/patch.js");var r=d(a("./node_modules/markdown-it/index.js")),s=d(a("./node_modules/jquery/dist/jquery.js")),c=d(a("./CTFd/themes/core/assets/js/ezq.js"));function d(e){return e&&e.__esModule?e:{default:e}}function l(t,e){var a,i=Object.keys(t);return Object.getOwnPropertySymbols&&(a=Object.getOwnPropertySymbols(t),e&&(a=a.filter(function(e){return Object.getOwnPropertyDescriptor(t,e).enumerable})),i.push.apply(i,a)),i}function u(o){for(var e=1;e {0} × {1}',d=' ',o='\n Error:\n {0}\n ×\n',l='\n Success!\n {0}\n ×\n',u='{0}',p='No',f='Yes';function m(e){var t=r.format(e.title),a=(0,s.default)(t);"string"==typeof e.body?a.find(".modal-body").append("".concat(e.body,"")):a.find(".modal-body").append((0,s.default)(e.body));var i=(0,s.default)(u.format(e.button));return e.success&&(0,s.default)(i).click(function(){e.success()}),e.large&&a.find(".modal-dialog").addClass("modal-lg"),a.find(".modal-footer").append(i),a.find("pre code").each(function(e){n.default.highlightBlock(this)}),(0,s.default)("main").append(a),a.modal("show"),(0,s.default)(a).on("hidden.bs.modal",function(){(0,s.default)(this).modal("dispose")}),a}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,a=c.format(e.title,e.body),i=(0,s.default)(a);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,n=!1!==e.animation,r=e.delay||1e4;return(0,s.default)("#ezq--notifications-toast-container").prepend(i),i.toast({autohide:o,delay:r,animation:n}),i.toast("show"),i}function g(e){var t=r.format(e.title),a=(0,s.default)(t);"string"==typeof e.body?a.find(".modal-body").append("".concat(e.body,"")):a.find(".modal-body").append((0,s.default)(e.body));var i=(0,s.default)(f),o=(0,s.default)(p);return a.find(".modal-footer").append(o),a.find(".modal-footer").append(i),a.find("pre code").each(function(e){n.default.highlightBlock(this)}),(0,s.default)("main").append(a),(0,s.default)(a).on("hidden.bs.modal",function(){(0,s.default)(this).modal("dispose")}),(0,s.default)(i).click(function(){e.success()}),a.modal("show"),a}function v(e){if(e.target){var t=(0,s.default)(e.target);return t.find(".progress-bar").css("width",e.width+"%"),t}var a=d.format(e.width),i=r.format(e.title),o=(0,s.default)(i);return o.find(".modal-body").append((0,s.default)(a)),(0,s.default)("main").append(o),o.modal("show")}function A(e){var t={success:l,error:o}[e.type].format(e.body);return(0,s.default)(t)}var y={ezAlert:m,ezToast:h,ezQuery:g,ezProgressBar:v,ezBadge:A};t.default=y},"./CTFd/themes/core/assets/js/fetch.js":function(e,t,a){Object.defineProperty(t,"__esModule",{value:!0}),t.default=void 0,a("./node_modules/whatwg-fetch/fetch.js");var i,o=(i=a("./CTFd/themes/core/assets/js/config.js"))&&i.__esModule?i:{default:i};var n=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,n(e,t)}},"./CTFd/themes/core/assets/js/patch.js":function(e,t,a){var i,s=(i=a("./node_modules/q/q.js"))&&i.__esModule?i:{default:i},o=a("./CTFd/themes/core/assets/js/api.js");function r(t,e){var a,i=Object.keys(t);return Object.getOwnPropertySymbols&&(a=Object.getOwnPropertySymbols(t),e&&(a=a.filter(function(e){return Object.getOwnPropertyDescriptor(t,e).enumerable})),i.push.apply(i,a)),i}function n(o){for(var e=1;e").text(e).html()},t.cumulativeSum=function(e){for(var t=e.concat(),a=0;a'),(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 a=n(e,o),i=n(t,o);return r.default.isNumeric(a)&&r.default.isNumeric(i)?a-i:a.toString().localeCompare(i)}));this.asc=!this.asc,this.asc||(t=t.reverse());for(var a=0;a").text(l.default.tz.guess());(0,d.default)(e).append(t);for(var a=r.default,i=0;i").text(a[i]);(0,d.default)(e).append(o)}}l.default.extend(i.default),l.default.extend(o.default),l.default.extend(n.default),(0,d.default)(function(){var o=p.default.fromTextArea(document.getElementById("theme-header"),{lineNumbers:!0,lineWrapping:!0,mode:"htmlmixed",htmlMode:!0}),t=p.default.fromTextArea(document.getElementById("theme-footer"),{lineNumbers:!0,lineWrapping:!0,mode:"htmlmixed",htmlMode:!0}),a=p.default.fromTextArea(document.getElementById("theme-settings"),{lineNumbers:!0,lineWrapping:!0,readOnly:!0,mode:{name:"javascript",json:!0}});(0,d.default)("a[href='#theme']").on("shown.bs.tab",function(e){o.refresh(),t.refresh(),a.refresh()}),(0,d.default)("a[href='#legal'], a[href='#tos-config'], a[href='#privacy-policy-config']").on("shown.bs.tab",function(e){(0,d.default)("#tos-config .CodeMirror").each(function(e,t){t.CodeMirror.refresh()}),(0,d.default)("#privacy-policy-config .CodeMirror").each(function(e,t){t.CodeMirror.refresh()})}),(0,d.default)("#theme-settings-modal form").submit(function(e){e.preventDefault(),a.getDoc().setValue(JSON.stringify((0,d.default)(this).serializeJSON(),null,2)),(0,d.default)("#theme-settings-modal").modal("hide")}),(0,d.default)("#theme-settings-button").click(function(){var t,i=(0,d.default)("#theme-settings-modal form");try{t=JSON.parse(a.getValue())}catch(e){t={}}d.default.each(t,function(e,t){var a=i.find("[name='".concat(e,"']"));switch(a.prop("type")){case"radio":case"checkbox":a.each(function(){(0,d.default)(this).attr("value")==t&&(0,d.default)(this).attr("checked",t)});break;default:a.val(t)}}),(0,d.default)("#theme-settings-modal").modal()}),I((0,d.default)("#start-timezone")),I((0,d.default)("#end-timezone")),I((0,d.default)("#freeze-timezone")),(0,d.default)(".config-section > form:not(.form-upload, .custom-config-form)").submit(A),(0,d.default)("#logo-upload").submit(y),(0,d.default)("#user-mode-form").submit(_),(0,d.default)("#remove-logo").click(j),(0,d.default)("#ctf-small-icon-upload").submit(T),(0,d.default)("#remove-small-icon").click(b),(0,d.default)("#export-button").click(w),(0,d.default)("#import-button").click(C),(0,d.default)("#import-csv-form").submit(E),(0,d.default)("#config-color-update").click(function(){var e,t,a=(0,d.default)("#config-color-picker").val(),i=o.getValue();t=i.length?(e="theme-color: ".concat(a,";"),i.replace(/theme-color: (.*);/,e)):'\n",o.getDoc().setValue(t)}),(0,d.default)(".start-date").change(function(){v("start")}),(0,d.default)(".end-date").change(function(){v("end")}),(0,d.default)(".freeze-date").change(function(){v("freeze")});var e=(0,d.default)("#start").val(),i=(0,d.default)("#end").val(),n=(0,d.default)("#freeze").val();e&&g("start",e),i&&g("end",i),n&&g("freeze",n),(0,d.default)("#mail_useauth").change(function(){(0,d.default)("#mail_username_password").toggle(this.checked)}).change();var r=f.default.extend(m.default),s=document.createElement("div");document.querySelector("#user-field-list").appendChild(s),new r({propsData:{type:"user"}}).$mount(s);var c=document.createElement("div");document.querySelector("#team-field-list").appendChild(c),new r({propsData:{type:"team"}}).$mount(c)})},"./CTFd/themes/admin/assets/js/pages/main.js":function(e,t,a){var i=f(a("./CTFd/themes/core/assets/js/CTFd.js")),o=f(a("./node_modules/jquery/dist/jquery.js")),n=f(a("./node_modules/dayjs/dayjs.min.js")),r=f(a("./node_modules/dayjs/plugin/advancedFormat.js")),s=f(a("./node_modules/nunjucks/browser/nunjucks.js")),c=a("./node_modules/howler/dist/howler.js"),d=f(a("./CTFd/themes/core/assets/js/events.js")),l=f(a("./CTFd/themes/core/assets/js/times.js")),u=f(a("./CTFd/themes/admin/assets/js/styles.js")),p=f(a("./CTFd/themes/core/assets/js/helpers.js"));function f(e){return e&&e.__esModule?e:{default:e}}n.default.extend(r.default),i.default.init(window.init),window.CTFd=i.default,window.helpers=p.default,window.$=o.default,window.dayjs=n.default,window.nunjucks=s.default,window.Howl=c.Howl,(0,o.default)(function(){(0,u.default)(),(0,l.default)(),(0,d.default)(i.default.config.urlRoot)})},"./CTFd/themes/admin/assets/js/styles.js":function(e,t,a){Object.defineProperty(t,"__esModule",{value:!0}),t.showMediaLibrary=l,t.bindMarkdownEditor=u,t.bindMarkdownEditors=p,t.default=void 0,a("./node_modules/bootstrap/dist/js/bootstrap.bundle.js");var i=a("./CTFd/themes/core/assets/js/utils.js"),o=d(a("./node_modules/jquery/dist/jquery.js")),n=d(a("./node_modules/easymde/src/js/easymde.js")),r=d(a("./node_modules/vue/dist/vue.esm.browser.js")),s=d(a("./CTFd/themes/admin/assets/js/components/files/MediaLibrary.vue")),c=d(a("./node_modules/highlight.js/lib/index.js"));function d(e){return e&&e.__esModule?e:{default:e}}function l(e){var t=r.default.extend(s.default),a=document.createElement("div");document.querySelector("main").appendChild(a);var i=new t({propsData:{editor:e}}).$mount(a);(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 u(e){var t;!1===e.hasOwnProperty("mde")&&(t=new n.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:c.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 p(){(0,o.default)("textarea.markdown").each(function(e,t){u(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"),a=(0,o.default)(this).index()+1;(0,o.default)(this).closest("table").find("tr td:nth-child(".concat(a,") 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")),p(),(0,i.makeSortableTables)(),(0,o.default)('[data-toggle="tooltip"]').tooltip(),document.querySelectorAll("pre code").forEach(function(e){c.default.highlightBlock(e)})})}},"./CTFd/themes/admin/assets/js/timezones.js":function(e,t,a){Object.defineProperty(t,"__esModule",{value:!0}),t.default=void 0;var i=["Africa/Abidjan","Africa/Accra","Africa/Addis_Ababa","Africa/Algiers","Africa/Asmara","Africa/Asmera","Africa/Bamako","Africa/Bangui","Africa/Banjul","Africa/Bissau","Africa/Blantyre","Africa/Brazzaville","Africa/Bujumbura","Africa/Cairo","Africa/Casablanca","Africa/Ceuta","Africa/Conakry","Africa/Dakar","Africa/Dar_es_Salaam","Africa/Djibouti","Africa/Douala","Africa/El_Aaiun","Africa/Freetown","Africa/Gaborone","Africa/Harare","Africa/Johannesburg","Africa/Juba","Africa/Kampala","Africa/Khartoum","Africa/Kigali","Africa/Kinshasa","Africa/Lagos","Africa/Libreville","Africa/Lome","Africa/Luanda","Africa/Lubumbashi","Africa/Lusaka","Africa/Malabo","Africa/Maputo","Africa/Maseru","Africa/Mbabane","Africa/Mogadishu","Africa/Monrovia","Africa/Nairobi","Africa/Ndjamena","Africa/Niamey","Africa/Nouakchott","Africa/Ouagadougou","Africa/Porto-Novo","Africa/Sao_Tome","Africa/Timbuktu","Africa/Tripoli","Africa/Tunis","Africa/Windhoek","America/Adak","America/Anchorage","America/Anguilla","America/Antigua","America/Araguaina","America/Argentina/Buenos_Aires","America/Argentina/Catamarca","America/Argentina/ComodRivadavia","America/Argentina/Cordoba","America/Argentina/Jujuy","America/Argentina/La_Rioja","America/Argentina/Mendoza","America/Argentina/Rio_Gallegos","America/Argentina/Salta","America/Argentina/San_Juan","America/Argentina/San_Luis","America/Argentina/Tucuman","America/Argentina/Ushuaia","America/Aruba","America/Asuncion","America/Atikokan","America/Atka","America/Bahia","America/Bahia_Banderas","America/Barbados","America/Belem","America/Belize","America/Blanc-Sablon","America/Boa_Vista","America/Bogota","America/Boise","America/Buenos_Aires","America/Cambridge_Bay","America/Campo_Grande","America/Cancun","America/Caracas","America/Catamarca","America/Cayenne","America/Cayman","America/Chicago","America/Chihuahua","America/Coral_Harbour","America/Cordoba","America/Costa_Rica","America/Creston","America/Cuiaba","America/Curacao","America/Danmarkshavn","America/Dawson","America/Dawson_Creek","America/Denver","America/Detroit","America/Dominica","America/Edmonton","America/Eirunepe","America/El_Salvador","America/Ensenada","America/Fort_Nelson","America/Fort_Wayne","America/Fortaleza","America/Glace_Bay","America/Godthab","America/Goose_Bay","America/Grand_Turk","America/Grenada","America/Guadeloupe","America/Guatemala","America/Guayaquil","America/Guyana","America/Halifax","America/Havana","America/Hermosillo","America/Indiana/Indianapolis","America/Indiana/Knox","America/Indiana/Marengo","America/Indiana/Petersburg","America/Indiana/Tell_City","America/Indiana/Vevay","America/Indiana/Vincennes","America/Indiana/Winamac","America/Indianapolis","America/Inuvik","America/Iqaluit","America/Jamaica","America/Jujuy","America/Juneau","America/Kentucky/Louisville","America/Kentucky/Monticello","America/Knox_IN","America/Kralendijk","America/La_Paz","America/Lima","America/Los_Angeles","America/Louisville","America/Lower_Princes","America/Maceio","America/Managua","America/Manaus","America/Marigot","America/Martinique","America/Matamoros","America/Mazatlan","America/Mendoza","America/Menominee","America/Merida","America/Metlakatla","America/Mexico_City","America/Miquelon","America/Moncton","America/Monterrey","America/Montevideo","America/Montreal","America/Montserrat","America/Nassau","America/New_York","America/Nipigon","America/Nome","America/Noronha","America/North_Dakota/Beulah","America/North_Dakota/Center","America/North_Dakota/New_Salem","America/Nuuk","America/Ojinaga","America/Panama","America/Pangnirtung","America/Paramaribo","America/Phoenix","America/Port-au-Prince","America/Port_of_Spain","America/Porto_Acre","America/Porto_Velho","America/Puerto_Rico","America/Punta_Arenas","America/Rainy_River","America/Rankin_Inlet","America/Recife","America/Regina","America/Resolute","America/Rio_Branco","America/Rosario","America/Santa_Isabel","America/Santarem","America/Santiago","America/Santo_Domingo","America/Sao_Paulo","America/Scoresbysund","America/Shiprock","America/Sitka","America/St_Barthelemy","America/St_Johns","America/St_Kitts","America/St_Lucia","America/St_Thomas","America/St_Vincent","America/Swift_Current","America/Tegucigalpa","America/Thule","America/Thunder_Bay","America/Tijuana","America/Toronto","America/Tortola","America/Vancouver","America/Virgin","America/Whitehorse","America/Winnipeg","America/Yakutat","America/Yellowknife","Antarctica/Casey","Antarctica/Davis","Antarctica/DumontDUrville","Antarctica/Macquarie","Antarctica/Mawson","Antarctica/McMurdo","Antarctica/Palmer","Antarctica/Rothera","Antarctica/South_Pole","Antarctica/Syowa","Antarctica/Troll","Antarctica/Vostok","Arctic/Longyearbyen","Asia/Aden","Asia/Almaty","Asia/Amman","Asia/Anadyr","Asia/Aqtau","Asia/Aqtobe","Asia/Ashgabat","Asia/Ashkhabad","Asia/Atyrau","Asia/Baghdad","Asia/Bahrain","Asia/Baku","Asia/Bangkok","Asia/Barnaul","Asia/Beirut","Asia/Bishkek","Asia/Brunei","Asia/Calcutta","Asia/Chita","Asia/Choibalsan","Asia/Chongqing","Asia/Chungking","Asia/Colombo","Asia/Dacca","Asia/Damascus","Asia/Dhaka","Asia/Dili","Asia/Dubai","Asia/Dushanbe","Asia/Famagusta","Asia/Gaza","Asia/Harbin","Asia/Hebron","Asia/Ho_Chi_Minh","Asia/Hong_Kong","Asia/Hovd","Asia/Irkutsk","Asia/Istanbul","Asia/Jakarta","Asia/Jayapura","Asia/Jerusalem","Asia/Kabul","Asia/Kamchatka","Asia/Karachi","Asia/Kashgar","Asia/Kathmandu","Asia/Katmandu","Asia/Khandyga","Asia/Kolkata","Asia/Krasnoyarsk","Asia/Kuala_Lumpur","Asia/Kuching","Asia/Kuwait","Asia/Macao","Asia/Macau","Asia/Magadan","Asia/Makassar","Asia/Manila","Asia/Muscat","Asia/Nicosia","Asia/Novokuznetsk","Asia/Novosibirsk","Asia/Omsk","Asia/Oral","Asia/Phnom_Penh","Asia/Pontianak","Asia/Pyongyang","Asia/Qatar","Asia/Qostanay","Asia/Qyzylorda","Asia/Rangoon","Asia/Riyadh","Asia/Saigon","Asia/Sakhalin","Asia/Samarkand","Asia/Seoul","Asia/Shanghai","Asia/Singapore","Asia/Srednekolymsk","Asia/Taipei","Asia/Tashkent","Asia/Tbilisi","Asia/Tehran","Asia/Tel_Aviv","Asia/Thimbu","Asia/Thimphu","Asia/Tokyo","Asia/Tomsk","Asia/Ujung_Pandang","Asia/Ulaanbaatar","Asia/Ulan_Bator","Asia/Urumqi","Asia/Ust-Nera","Asia/Vientiane","Asia/Vladivostok","Asia/Yakutsk","Asia/Yangon","Asia/Yekaterinburg","Asia/Yerevan","Atlantic/Azores","Atlantic/Bermuda","Atlantic/Canary","Atlantic/Cape_Verde","Atlantic/Faeroe","Atlantic/Faroe","Atlantic/Jan_Mayen","Atlantic/Madeira","Atlantic/Reykjavik","Atlantic/South_Georgia","Atlantic/St_Helena","Atlantic/Stanley","Australia/ACT","Australia/Adelaide","Australia/Brisbane","Australia/Broken_Hill","Australia/Canberra","Australia/Currie","Australia/Darwin","Australia/Eucla","Australia/Hobart","Australia/LHI","Australia/Lindeman","Australia/Lord_Howe","Australia/Melbourne","Australia/NSW","Australia/North","Australia/Perth","Australia/Queensland","Australia/South","Australia/Sydney","Australia/Tasmania","Australia/Victoria","Australia/West","Australia/Yancowinna","Brazil/Acre","Brazil/DeNoronha","Brazil/East","Brazil/West","CET","CST6CDT","Canada/Atlantic","Canada/Central","Canada/Eastern","Canada/Mountain","Canada/Newfoundland","Canada/Pacific","Canada/Saskatchewan","Canada/Yukon","Chile/Continental","Chile/EasterIsland","Cuba","EET","EST","EST5EDT","Egypt","Eire","Etc/GMT","Etc/GMT+0","Etc/GMT+1","Etc/GMT+10","Etc/GMT+11","Etc/GMT+12","Etc/GMT+2","Etc/GMT+3","Etc/GMT+4","Etc/GMT+5","Etc/GMT+6","Etc/GMT+7","Etc/GMT+8","Etc/GMT+9","Etc/GMT-0","Etc/GMT-1","Etc/GMT-10","Etc/GMT-11","Etc/GMT-12","Etc/GMT-13","Etc/GMT-14","Etc/GMT-2","Etc/GMT-3","Etc/GMT-4","Etc/GMT-5","Etc/GMT-6","Etc/GMT-7","Etc/GMT-8","Etc/GMT-9","Etc/GMT0","Etc/Greenwich","Etc/UCT","Etc/UTC","Etc/Universal","Etc/Zulu","Europe/Amsterdam","Europe/Andorra","Europe/Astrakhan","Europe/Athens","Europe/Belfast","Europe/Belgrade","Europe/Berlin","Europe/Bratislava","Europe/Brussels","Europe/Bucharest","Europe/Budapest","Europe/Busingen","Europe/Chisinau","Europe/Copenhagen","Europe/Dublin","Europe/Gibraltar","Europe/Guernsey","Europe/Helsinki","Europe/Isle_of_Man","Europe/Istanbul","Europe/Jersey","Europe/Kaliningrad","Europe/Kiev","Europe/Kirov","Europe/Lisbon","Europe/Ljubljana","Europe/London","Europe/Luxembourg","Europe/Madrid","Europe/Malta","Europe/Mariehamn","Europe/Minsk","Europe/Monaco","Europe/Moscow","Europe/Nicosia","Europe/Oslo","Europe/Paris","Europe/Podgorica","Europe/Prague","Europe/Riga","Europe/Rome","Europe/Samara","Europe/San_Marino","Europe/Sarajevo","Europe/Saratov","Europe/Simferopol","Europe/Skopje","Europe/Sofia","Europe/Stockholm","Europe/Tallinn","Europe/Tirane","Europe/Tiraspol","Europe/Ulyanovsk","Europe/Uzhgorod","Europe/Vaduz","Europe/Vatican","Europe/Vienna","Europe/Vilnius","Europe/Volgograd","Europe/Warsaw","Europe/Zagreb","Europe/Zaporozhye","Europe/Zurich","GB","GB-Eire","GMT","GMT+0","GMT-0","GMT0","Greenwich","HST","Hongkong","Iceland","Indian/Antananarivo","Indian/Chagos","Indian/Christmas","Indian/Cocos","Indian/Comoro","Indian/Kerguelen","Indian/Mahe","Indian/Maldives","Indian/Mauritius","Indian/Mayotte","Indian/Reunion","Iran","Israel","Jamaica","Japan","Kwajalein","Libya","MET","MST","MST7MDT","Mexico/BajaNorte","Mexico/BajaSur","Mexico/General","NZ","NZ-CHAT","Navajo","PRC","PST8PDT","Pacific/Apia","Pacific/Auckland","Pacific/Bougainville","Pacific/Chatham","Pacific/Chuuk","Pacific/Easter","Pacific/Efate","Pacific/Enderbury","Pacific/Fakaofo","Pacific/Fiji","Pacific/Funafuti","Pacific/Galapagos","Pacific/Gambier","Pacific/Guadalcanal","Pacific/Guam","Pacific/Honolulu","Pacific/Johnston","Pacific/Kiritimati","Pacific/Kosrae","Pacific/Kwajalein","Pacific/Majuro","Pacific/Marquesas","Pacific/Midway","Pacific/Nauru","Pacific/Niue","Pacific/Norfolk","Pacific/Noumea","Pacific/Pago_Pago","Pacific/Palau","Pacific/Pitcairn","Pacific/Pohnpei","Pacific/Ponape","Pacific/Port_Moresby","Pacific/Rarotonga","Pacific/Saipan","Pacific/Samoa","Pacific/Tahiti","Pacific/Tarawa","Pacific/Tongatapu","Pacific/Truk","Pacific/Wake","Pacific/Wallis","Pacific/Yap","Poland","Portugal","ROC","ROK","Singapore","Turkey","UCT","US/Alaska","US/Aleutian","US/Arizona","US/Central","US/East-Indiana","US/Eastern","US/Hawaii","US/Indiana-Starke","US/Michigan","US/Mountain","US/Pacific","US/Pacific-New","US/Samoa","UTC","Universal","W-SU","WET","Zulu"];t.default=i},"./CTFd/themes/core/assets/js/CTFd.js":function(e,t,a){Object.defineProperty(t,"__esModule",{value:!0}),t.default=void 0;var i=d(a("./CTFd/themes/core/assets/js/fetch.js")),o=d(a("./CTFd/themes/core/assets/js/config.js")),n=a("./CTFd/themes/core/assets/js/api.js");a("./CTFd/themes/core/assets/js/patch.js");var r=d(a("./node_modules/markdown-it/index.js")),s=d(a("./node_modules/jquery/dist/jquery.js")),c=d(a("./CTFd/themes/core/assets/js/ezq.js"));function d(e){return e&&e.__esModule?e:{default:e}}function l(t,e){var a,i=Object.keys(t);return Object.getOwnPropertySymbols&&(a=Object.getOwnPropertySymbols(t),e&&(a=a.filter(function(e){return Object.getOwnPropertyDescriptor(t,e).enumerable})),i.push.apply(i,a)),i}function u(o){for(var e=1;e {0} × {1}',d=' ',o='\n Error:\n {0}\n ×\n',l='\n Success!\n {0}\n ×\n',u='{0}',p='No',f='Yes';function m(e){var t=r.format(e.title),a=(0,s.default)(t);"string"==typeof e.body?a.find(".modal-body").append("".concat(e.body,"")):a.find(".modal-body").append((0,s.default)(e.body));var i=(0,s.default)(u.format(e.button));return e.success&&(0,s.default)(i).click(function(){e.success()}),e.large&&a.find(".modal-dialog").addClass("modal-lg"),a.find(".modal-footer").append(i),a.find("pre code").each(function(e){n.default.highlightBlock(this)}),(0,s.default)("main").append(a),a.modal("show"),(0,s.default)(a).on("hidden.bs.modal",function(){(0,s.default)(this).modal("dispose")}),a}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,a=c.format(e.title,e.body),i=(0,s.default)(a);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,n=!1!==e.animation,r=e.delay||1e4;return(0,s.default)("#ezq--notifications-toast-container").prepend(i),i.toast({autohide:o,delay:r,animation:n}),i.toast("show"),i}function g(e){var t=r.format(e.title),a=(0,s.default)(t);"string"==typeof e.body?a.find(".modal-body").append("".concat(e.body,"")):a.find(".modal-body").append((0,s.default)(e.body));var i=(0,s.default)(f),o=(0,s.default)(p);return a.find(".modal-footer").append(o),a.find(".modal-footer").append(i),a.find("pre code").each(function(e){n.default.highlightBlock(this)}),(0,s.default)("main").append(a),(0,s.default)(a).on("hidden.bs.modal",function(){(0,s.default)(this).modal("dispose")}),(0,s.default)(i).click(function(){e.success()}),a.modal("show"),a}function v(e){if(e.target){var t=(0,s.default)(e.target);return t.find(".progress-bar").css("width",e.width+"%"),t}var a=d.format(e.width),i=r.format(e.title),o=(0,s.default)(i);return o.find(".modal-body").append((0,s.default)(a)),(0,s.default)("main").append(o),o.modal("show")}function A(e){var t={success:l,error:o}[e.type].format(e.body);return(0,s.default)(t)}var y={ezAlert:m,ezToast:h,ezQuery:g,ezProgressBar:v,ezBadge:A};t.default=y},"./CTFd/themes/core/assets/js/fetch.js":function(e,t,a){Object.defineProperty(t,"__esModule",{value:!0}),t.default=void 0,a("./node_modules/whatwg-fetch/fetch.js");var i,o=(i=a("./CTFd/themes/core/assets/js/config.js"))&&i.__esModule?i:{default:i};var n=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,n(e,t)}},"./CTFd/themes/core/assets/js/patch.js":function(e,t,a){var i,s=(i=a("./node_modules/q/q.js"))&&i.__esModule?i:{default:i},o=a("./CTFd/themes/core/assets/js/api.js");function r(t,e){var a,i=Object.keys(t);return Object.getOwnPropertySymbols&&(a=Object.getOwnPropertySymbols(t),e&&(a=a.filter(function(e){return Object.getOwnPropertyDescriptor(t,e).enumerable})),i.push.apply(i,a)),i}function n(o){for(var e=1;e").text(e).html()},t.cumulativeSum=function(e){for(var t=e.concat(),a=0;a'),(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 a=n(e,o),i=n(t,o);return r.default.isNumeric(a)&&r.default.isNumeric(i)?a-i:a.toString().localeCompare(i)}));this.asc=!this.asc,this.asc||(t=t.reverse());for(var a=0;a + + Import Status + + + + + + + Start Time: {{ start_time }} + + {% if end_time %} + + End Time: {{ end_time }} + + {% endif %} + {% if import_error %} + + Import Error: {{ import_error }} + + {% else %} + + Current Status: {{ import_status }} + + {% endif %} + + + +{% endblock %} + +{% block scripts %} + +{% endblock %} \ No newline at end of file diff --git a/CTFd/utils/__init__.py b/CTFd/utils/__init__.py index 0a9d2eaf..d9b9b8fd 100644 --- a/CTFd/utils/__init__.py +++ b/CTFd/utils/__init__.py @@ -81,3 +81,14 @@ def set_config(key, value): cache.delete_memoized(_get_config, key) return config + + +def import_in_progress(): + import_status = cache.get(key="import_status") + import_error = cache.get(key="import_error") + if import_error: + return False + elif import_status: + return True + else: + return False diff --git a/CTFd/utils/exports/__init__.py b/CTFd/utils/exports/__init__.py index 089077d9..ac5c4218 100644 --- a/CTFd/utils/exports/__init__.py +++ b/CTFd/utils/exports/__init__.py @@ -5,8 +5,10 @@ import re import tempfile import zipfile from io import BytesIO +from multiprocessing import Process import dataset +from flask import copy_current_request_context from flask import current_app as app from flask_migrate import upgrade as migration_upgrade from sqlalchemy.engine.url import make_url @@ -21,6 +23,7 @@ from CTFd.plugins import get_plugin_names from CTFd.plugins.migrations import current as plugin_current from CTFd.plugins.migrations import upgrade as plugin_upgrade from CTFd.utils import get_app_config, set_config, string_types +from CTFd.utils.dates import unix_time from CTFd.utils.exports.freeze import freeze_export from CTFd.utils.migrations import ( create_database, @@ -82,7 +85,18 @@ def export_ctf(): def import_ctf(backup, erase=True): + cache_timeout = 604800 # 604800 is 1 week in seconds + + def set_error(val): + cache.set(key="import_error", value=val, timeout=cache_timeout) + print(val) + + def set_status(val): + cache.set(key="import_status", value=val, timeout=cache_timeout) + print(val) + if not zipfile.is_zipfile(backup): + set_error("zipfile.BadZipfile: zipfile is invalid") raise zipfile.BadZipfile backup = zipfile.ZipFile(backup) @@ -92,15 +106,18 @@ def import_ctf(backup, erase=True): for f in members: if f.startswith("/") or ".." in f: # Abort on malicious zip files + set_error("zipfile.BadZipfile: zipfile is malicious") raise zipfile.BadZipfile info = backup.getinfo(f) if max_content_length: if info.file_size > max_content_length: + set_error("zipfile.LargeZipFile: zipfile is too large") raise zipfile.LargeZipFile # Get list of directories in zipfile member_dirs = [os.path.split(m)[0] for m in members if "/" in m] if "db" not in member_dirs: + set_error("Exception: db folder is missing") raise Exception( 'CTFd couldn\'t find the "db" folder in this backup. ' "The backup may be malformed or corrupted and the import process cannot continue." @@ -110,6 +127,7 @@ def import_ctf(backup, erase=True): alembic_version = json.loads(backup.open("db/alembic_version.json").read()) alembic_version = alembic_version["results"][0]["version_num"] except Exception: + set_error("Exception: Could not determine appropriate database version") raise Exception( "Could not determine appropriate database version. This backup cannot be automatically imported." ) @@ -130,15 +148,26 @@ def import_ctf(backup, erase=True): "dab615389702", "e62fd69bd417", ): + set_error( + "Exception: The version of CTFd that this backup is from is too old to be automatically imported." + ) raise Exception( "The version of CTFd that this backup is from is too old to be automatically imported." ) + start_time = unix_time(datetime.datetime.utcnow()) + + cache.set(key="import_start_time", value=start_time, timeout=cache_timeout) + cache.set(key="import_end_time", value=None, timeout=cache_timeout) + + set_status("started") + sqlite = get_app_config("SQLALCHEMY_DATABASE_URI").startswith("sqlite") postgres = get_app_config("SQLALCHEMY_DATABASE_URI").startswith("postgres") mysql = get_app_config("SQLALCHEMY_DATABASE_URI").startswith("mysql") if erase: + set_status("erasing") # Clear out existing connections to release any locks db.session.close() db.engine.dispose() @@ -165,10 +194,12 @@ def import_ctf(backup, erase=True): create_database() # We explicitly do not want to upgrade or stamp here. # The import will have this information. + set_status("erased") side_db = dataset.connect(get_app_config("SQLALCHEMY_DATABASE_URI")) try: + set_status("disabling foreign key checks") if postgres: side_db.query("SET session_replication_role=replica;") else: @@ -213,6 +244,7 @@ def import_ctf(backup, erase=True): # insertion between official database tables and plugin tables def insertion(table_filenames): for member in table_filenames: + set_status(f"inserting {member}") if member.startswith("db/"): table_name = member[3:-5] @@ -226,7 +258,9 @@ def import_ctf(backup, erase=True): table = side_db[table_name] saved = json.loads(data) - for entry in saved["results"]: + count = saved["count"] + for i, entry in enumerate(saved["results"]): + set_status(f"inserting {member} {i}/{count}") # This is a hack to get SQLite to properly accept datetime values from dataset # See Issue #246 if sqlite: @@ -295,6 +329,9 @@ def import_ctf(backup, erase=True): ) side_db.engine.execute(query) else: + set_error( + f"Exception: Table name {table_name} contains quotes" + ) raise Exception( "Table name {table_name} contains quotes".format( table_name=table_name @@ -302,12 +339,15 @@ def import_ctf(backup, erase=True): ) # Insert data from official tables + set_status("inserting tables") insertion(first) # Create tables created by plugins # Run plugin migrations + set_status("inserting plugins") plugins = get_plugin_names() for plugin in plugins: + set_status(f"inserting plugin {plugin}") revision = plugin_current(plugin_name=plugin) plugin_upgrade(plugin_name=plugin, revision=revision, lower=None) @@ -320,6 +360,7 @@ def import_ctf(backup, erase=True): plugin_upgrade(plugin_name=plugin) # Extracting files + set_status("uploading files") files = [f for f in backup.namelist() if f.startswith("uploads/")] uploader = get_uploader() for f in files: @@ -335,6 +376,7 @@ def import_ctf(backup, erase=True): uploader.store(fileobj=source, filename=filename) # Alembic sqlite support is lacking so we should just create_all anyway + set_status("running head migrations") if sqlite: app.db.create_all() stamp_latest_revision() @@ -345,6 +387,7 @@ def import_ctf(backup, erase=True): app.db.create_all() try: + set_status("reenabling foreign key checks") if postgres: side_db.query("SET session_replication_role=DEFAULT;") else: @@ -353,8 +396,26 @@ def import_ctf(backup, erase=True): print("Failed to enable foreign key checks. Continuing.") # Invalidate all cached data + set_status("clearing caches") cache.clear() # Set default theme in case the current instance or the import does not provide it set_config("ctf_theme", DEFAULT_THEME) set_config("ctf_version", CTFD_VERSION) + + # Set config variables to mark import completed + cache.set(key="import_start_time", value=start_time, timeout=cache_timeout) + cache.set( + key="import_end_time", + value=unix_time(datetime.datetime.utcnow()), + timeout=cache_timeout, + ) + + +def background_import_ctf(backup): + @copy_current_request_context + def ctx_bridge(): + import_ctf(backup) + + p = Process(target=ctx_bridge) + p.start() diff --git a/CTFd/utils/initialization/__init__.py b/CTFd/utils/initialization/__init__.py index 96664b92..a7ac22f1 100644 --- a/CTFd/utils/initialization/__init__.py +++ b/CTFd/utils/initialization/__init__.py @@ -10,7 +10,7 @@ from werkzeug.middleware.dispatcher import DispatcherMiddleware from CTFd.cache import clear_user_recent_ips from CTFd.exceptions import UserNotFoundException, UserTokenExpiredException from CTFd.models import Tracking, db -from CTFd.utils import config, get_config, markdown +from CTFd.utils import config, get_config, import_in_progress, markdown from CTFd.utils.config import ( can_send_mail, ctf_logo, @@ -208,6 +208,12 @@ def init_request_processors(app): if request.endpoint == "views.themes": return + if import_in_progress(): + if request.endpoint == "admin.import_ctf": + return + else: + abort(403, description="Import currently in progress") + if authed(): user_ips = get_current_user_recent_ips() ip = get_ip()
".concat(e.body,"
+ Start Time: {{ start_time }} +
+ End Time: {{ end_time }} +
+ Import Error: {{ import_error }} +
+ Current Status: {{ import_status }} +