Adding sortable columns to admin panel (#1337)

* Columns can opt-in to being sortable with the `sort-col` class on `th` elements
* Add checkboxes to main admin panels and convert per-line actions into bulk actions
This commit is contained in:
Kevin Chung
2020-04-23 13:35:23 -04:00
committed by GitHub
58 changed files with 933 additions and 202 deletions

View File

@@ -66,3 +66,11 @@ tbody tr:hover {
tr[data-href] { tr[data-href] {
cursor: pointer; cursor: pointer;
} }
.sort-col {
cursor: pointer;
}
input[type="checkbox"] {
cursor: pointer;
}

View File

@@ -0,0 +1,80 @@
import "./main";
import CTFd from "core/CTFd";
import $ from "jquery";
import { ezAlert, ezQuery } from "core/ezq";
function deleteSelectedChallenges(event) {
let challengeIDs = $("input[data-challenge-id]:checked").map(function() {
return $(this).data("challenge-id");
});
let target = challengeIDs.length === 1 ? "challenge" : "challenges";
ezQuery({
title: "Delete Challenges",
body: `Are you sure you want to delete ${challengeIDs.length} ${target}?`,
success: function() {
const reqs = [];
for (var chalID of challengeIDs) {
reqs.push(
CTFd.fetch(`/api/v1/challenges/${chalID}`, {
method: "DELETE"
})
);
}
Promise.all(reqs).then(responses => {
window.location.reload();
});
}
});
}
function bulkEditChallenges(event) {
let challengeIDs = $("input[data-challenge-id]:checked").map(function() {
return $(this).data("challenge-id");
});
ezAlert({
title: "Edit Challenges",
body: $(`
<form id="challenges-bulk-edit">
<div class="form-group">
<label>Category</label>
<input type="text" name="category" data-initial="" value="">
</div>
<div class="form-group">
<label>Value</label>
<input type="number" name="value" data-initial="" value="">
</div>
<div class="form-group">
<label>State</label>
<select name="state" data-initial="">
<option value="">--</option>
<option value="visible">Visible</option>
<option value="hidden">Hidden</option>
</select>
</div>
</form>
`),
button: "Submit",
success: function() {
let data = $("#challenges-bulk-edit").serializeJSON(true);
const reqs = [];
for (var chalID of challengeIDs) {
reqs.push(
CTFd.fetch(`/api/v1/challenges/${chalID}`, {
method: "PATCH",
body: JSON.stringify(data)
})
);
}
Promise.all(reqs).then(responses => {
window.location.reload();
});
}
});
}
$(() => {
$("#challenges-delete-button").click(deleteSelectedChallenges);
$("#challenges-edit-button").click(bulkEditChallenges);
});

View File

@@ -1,37 +1,33 @@
import "./main"; import "./main";
import CTFd from "core/CTFd"; import CTFd from "core/CTFd";
import $ from "jquery"; import $ from "jquery";
import { htmlEntities } from "core/utils";
import { ezQuery } from "core/ezq"; import { ezQuery } from "core/ezq";
function deletePage(event) { function deleteSelectedUsers(event) {
const elem = $(this); let pageIDs = $("input[data-page-id]:checked").map(function() {
const name = elem.attr("page-route"); return $(this).data("page-id");
const page_id = elem.attr("page-id"); });
let target = pageIDs.length === 1 ? "page" : "pages";
ezQuery({ ezQuery({
title: "Delete " + htmlEntities(name), title: "Delete Pages",
body: "Are you sure you want to delete {0}?".format( body: `Are you sure you want to delete ${pageIDs.length} ${target}?`,
"<strong>" + htmlEntities(name) + "</strong>"
),
success: function() { success: function() {
CTFd.fetch("/api/v1/pages/" + page_id, { const reqs = [];
method: "DELETE" for (var pageID of pageIDs) {
}) reqs.push(
.then(function(response) { CTFd.fetch(`/api/v1/pages/${pageID}`, {
return response.json(); method: "DELETE"
}) })
.then(function(response) { );
if (response.success) { }
elem Promise.all(reqs).then(responses => {
.parent() window.location.reload();
.parent() });
.remove();
}
});
} }
}); });
} }
$(() => { $(() => {
$(".delete-page").click(deletePage); $("#pages-delete-button").click(deleteSelectedUsers);
}); });

View File

@@ -1,6 +1,7 @@
import "./main"; import "./main";
import CTFd from "core/CTFd"; import CTFd from "core/CTFd";
import $ from "jquery"; import $ from "jquery";
import { ezAlert, ezQuery } from "core/ezq";
const api_func = { const api_func = {
users: (x, y) => CTFd.api.patch_user_public({ userId: x }, y), users: (x, y) => CTFd.api.patch_user_public({ userId: x }, y),
@@ -37,6 +38,48 @@ function toggleAccount() {
}); });
} }
function toggleSelectedAccounts(accountIDs, action) {
const params = {
hidden: action === "hidden" ? true : false
};
const reqs = [];
for (var accId of accountIDs) {
reqs.push(api_func[CTFd.config.userMode](accId, params));
}
Promise.all(reqs).then(responses => {
window.location.reload();
});
}
function bulkToggleAccounts(event) {
let accountIDs = $("input[data-account-id]:checked").map(function() {
return $(this).data("account-id");
});
ezAlert({
title: "Toggle Visibility",
body: $(`
<form id="scoreboard-bulk-edit">
<div class="form-group">
<label>Visibility</label>
<select name="visibility" data-initial="">
<option value="">--</option>
<option value="visible">Visible</option>
<option value="hidden">Hidden</option>
</select>
</div>
</form>
`),
button: "Submit",
success: function() {
let data = $("#scoreboard-bulk-edit").serializeJSON(true);
let state = data.visibility;
toggleSelectedAccounts(accountIDs, state);
}
});
}
$(() => { $(() => {
$(".scoreboard-toggle").click(toggleAccount); $(".scoreboard-toggle").click(toggleAccount);
$("#scoreboard-edit-button").click(bulkToggleAccounts);
}); });

View File

@@ -40,6 +40,28 @@ function deleteCorrectSubmission(event) {
}); });
} }
function deleteSelectedSubmissions(event) {
let submissionIDs = $("input[data-submission-id]:checked").map(function() {
return $(this).data("submission-id");
});
let target = submissionIDs.length === 1 ? "submission" : "submissions";
ezQuery({
title: "Delete Submissions",
body: `Are you sure you want to delete ${submissionIDs.length} ${target}?`,
success: function() {
const reqs = [];
for (var subId of submissionIDs) {
reqs.push(CTFd.api.delete_submission({ submissionId: subId }));
}
Promise.all(reqs).then(responses => {
window.location.reload();
});
}
});
}
$(() => { $(() => {
$(".delete-correct-submission").click(deleteCorrectSubmission); $(".delete-correct-submission").click(deleteCorrectSubmission);
$("#submission-delete-button").click(deleteSelectedSubmissions);
}); });

View File

@@ -0,0 +1,80 @@
import "./main";
import CTFd from "core/CTFd";
import $ from "jquery";
import { ezAlert, ezQuery } from "core/ezq";
function deleteSelectedTeams(event) {
let teamIDs = $("input[data-team-id]:checked").map(function() {
return $(this).data("team-id");
});
let target = teamIDs.length === 1 ? "team" : "teams";
ezQuery({
title: "Delete Teams",
body: `Are you sure you want to delete ${teamIDs.length} ${target}?`,
success: function() {
const reqs = [];
for (var teamID of teamIDs) {
reqs.push(
CTFd.fetch(`/api/v1/teams/${teamID}`, {
method: "DELETE"
})
);
}
Promise.all(reqs).then(responses => {
window.location.reload();
});
}
});
}
function bulkEditTeams(event) {
let teamIDs = $("input[data-team-id]:checked").map(function() {
return $(this).data("team-id");
});
ezAlert({
title: "Edit Teams",
body: $(`
<form id="teams-bulk-edit">
<div class="form-group">
<label>Banned</label>
<select name="banned" data-initial="">
<option value="">--</option>
<option value="true">True</option>
<option value="false">False</option>
</select>
</div>
<div class="form-group">
<label>Hidden</label>
<select name="hidden" data-initial="">
<option value="">--</option>
<option value="true">True</option>
<option value="false">False</option>
</select>
</div>
</form>
`),
button: "Submit",
success: function() {
let data = $("#teams-bulk-edit").serializeJSON(true);
const reqs = [];
for (var teamID of teamIDs) {
reqs.push(
CTFd.fetch(`/api/v1/teams/${teamID}`, {
method: "PATCH",
body: JSON.stringify(data)
})
);
}
Promise.all(reqs).then(responses => {
window.location.reload();
});
}
});
}
$(() => {
$("#teams-delete-button").click(deleteSelectedTeams);
$("#teams-edit-button").click(bulkEditTeams);
});

View File

@@ -1 +1,88 @@
import "./main"; import "./main";
import CTFd from "core/CTFd";
import $ from "jquery";
import { ezAlert, ezQuery } from "core/ezq";
function deleteSelectedUsers(event) {
let userIDs = $("input[data-user-id]:checked").map(function() {
return $(this).data("user-id");
});
let target = userIDs.length === 1 ? "user" : "users";
ezQuery({
title: "Delete Users",
body: `Are you sure you want to delete ${userIDs.length} ${target}?`,
success: function() {
const reqs = [];
for (var userID of userIDs) {
reqs.push(
CTFd.fetch(`/api/v1/users/${userID}`, {
method: "DELETE"
})
);
}
Promise.all(reqs).then(responses => {
window.location.reload();
});
}
});
}
function bulkEditUsers(event) {
let userIDs = $("input[data-user-id]:checked").map(function() {
return $(this).data("user-id");
});
ezAlert({
title: "Edit Users",
body: $(`
<form id="users-bulk-edit">
<div class="form-group">
<label>Verified</label>
<select name="verified" data-initial="">
<option value="">--</option>
<option value="true">True</option>
<option value="false">False</option>
</select>
</div>
<div class="form-group">
<label>Banned</label>
<select name="banned" data-initial="">
<option value="">--</option>
<option value="true">True</option>
<option value="false">False</option>
</select>
</div>
<div class="form-group">
<label>Hidden</label>
<select name="hidden" data-initial="">
<option value="">--</option>
<option value="true">True</option>
<option value="false">False</option>
</select>
</div>
</form>
`),
button: "Submit",
success: function() {
let data = $("#users-bulk-edit").serializeJSON(true);
const reqs = [];
for (var userID of userIDs) {
reqs.push(
CTFd.fetch(`/api/v1/users/${userID}`, {
method: "PATCH",
body: JSON.stringify(data)
})
);
}
Promise.all(reqs).then(responses => {
window.location.reload();
});
}
});
}
$(() => {
$("#users-delete-button").click(deleteSelectedUsers);
$("#users-edit-button").click(bulkEditUsers);
});

View File

@@ -1,4 +1,5 @@
import "bootstrap/dist/js/bootstrap.bundle"; import "bootstrap/dist/js/bootstrap.bundle";
import { makeSortableTables } from "core/utils";
import $ from "jquery"; import $ from "jquery";
export default () => { export default () => {
@@ -45,6 +46,27 @@ export default () => {
return false; return false;
}); });
$("[data-checkbox]").click(function(e) {
if ($(e.target).is("input[type=checkbox]")) {
e.stopImmediatePropagation();
return;
}
let checkbox = $(this).find("input[type=checkbox]");
// Doing it this way with an event allows data-checkbox-all to work
checkbox.click();
e.stopImmediatePropagation();
});
$("[data-checkbox-all]").on("click change", function(e) {
const checked = $(this).prop("checked");
const idx = $(this).index() + 1;
$(this)
.closest("table")
.find(`tr td:nth-child(${idx}) input[type=checkbox]`)
.prop("checked", checked);
e.stopImmediatePropagation();
});
$("tr[data-href] a, tr[data-href] button").click(function(e) { $("tr[data-href] a, tr[data-href] button").click(function(e) {
// TODO: This is a hack to allow modal close buttons to work // TODO: This is a hack to allow modal close buttons to work
if (!$(this).attr("data-dismiss")) { if (!$(this).attr("data-dismiss")) {
@@ -52,6 +74,7 @@ export default () => {
} }
}); });
makeSortableTables();
$('[data-toggle="tooltip"]').tooltip(); $('[data-toggle="tooltip"]').tooltip();
}); });
}; };

View File

@@ -1,4 +1,4 @@
html{position:relative;min-height:100%}body{margin-bottom:60px}.footer{position:absolute;bottom:1px;width:100%;height:60px;line-height:normal !important;z-index:-20} html{position:relative;min-height:100%}body{margin-bottom:60px}.footer{position:absolute;bottom:1px;width:100%;height:60px;line-height:normal !important;z-index:-20}
#score-graph{height:450px;display:block;clear:both}#solves-graph{display:block;height:350px}#keys-pie-graph{height:400px;display:block}#categories-pie-graph{height:400px;display:block}#solve-percentages-graph{height:400px;display:block}.no-decoration{color:inherit !important;text-decoration:none !important}.no-decoration:hover{color:inherit !important;text-decoration:none !important}.table td,.table th{vertical-align:inherit}pre{white-space:pre-wrap;margin:0;padding:0}.form-control{position:relative;display:block;border-radius:0;font-weight:400;font-family:"Avenir Next", "Helvetica Neue", Helvetica, Arial, sans-serif;-webkit-appearance:none}tbody tr:hover{background-color:rgba(0,0,0,0.1) !important}tr[data-href]{cursor:pointer} #score-graph{height:450px;display:block;clear:both}#solves-graph{display:block;height:350px}#keys-pie-graph{height:400px;display:block}#categories-pie-graph{height:400px;display:block}#solve-percentages-graph{height:400px;display:block}.no-decoration{color:inherit !important;text-decoration:none !important}.no-decoration:hover{color:inherit !important;text-decoration:none !important}.table td,.table th{vertical-align:inherit}pre{white-space:pre-wrap;margin:0;padding:0}.form-control{position:relative;display:block;border-radius:0;font-weight:400;font-family:"Avenir Next", "Helvetica Neue", Helvetica, Arial, sans-serif;-webkit-appearance:none}tbody tr:hover{background-color:rgba(0,0,0,0.1) !important}tr[data-href]{cursor:pointer}.sort-col{cursor:pointer}input[type="checkbox"]{cursor:pointer}

View File

@@ -1 +1 @@
html{position:relative;min-height:100%}body{margin-bottom:60px}.footer{position:absolute;bottom:1px;width:100%;height:60px;line-height:normal!important;z-index:-20}#score-graph{height:450px;display:block;clear:both}#solves-graph{display:block;height:350px}#categories-pie-graph,#keys-pie-graph,#solve-percentages-graph{height:400px;display:block}.no-decoration,.no-decoration:hover{color:inherit!important;text-decoration:none!important}.table td,.table th{vertical-align:inherit}pre{white-space:pre-wrap;margin:0;padding:0}.form-control{position:relative;display:block;border-radius:0;font-weight:400;font-family:Avenir Next,Helvetica Neue,Helvetica,Arial,sans-serif;-webkit-appearance:none}tbody tr:hover{background-color:rgba(0,0,0,.1)!important}tr[data-href]{cursor:pointer} html{position:relative;min-height:100%}body{margin-bottom:60px}.footer{position:absolute;bottom:1px;width:100%;height:60px;line-height:normal!important;z-index:-20}#score-graph{height:450px;display:block;clear:both}#solves-graph{display:block;height:350px}#categories-pie-graph,#keys-pie-graph,#solve-percentages-graph{height:400px;display:block}.no-decoration,.no-decoration:hover{color:inherit!important;text-decoration:none!important}.table td,.table th{vertical-align:inherit}pre{white-space:pre-wrap;margin:0;padding:0}.form-control{position:relative;display:block;border-radius:0;font-weight:400;font-family:Avenir Next,Helvetica Neue,Helvetica,Arial,sans-serif;-webkit-appearance:none}tbody tr:hover{background-color:rgba(0,0,0,.1)!important}.sort-col,input[type=checkbox],tr[data-href]{cursor:pointer}

File diff suppressed because one or more lines are too long

View File

@@ -147,7 +147,7 @@
/******/ /******/
/******/ /******/
/******/ // add entry module to deferred list /******/ // add entry module to deferred list
/******/ deferredModules.push(["./CTFd/themes/admin/assets/js/pages/challenge.js","helpers","vendor","default~pages/challenge~pages/configs~pages/editor~pages/main~pages/notifications~pages/pages~pages/~0fc9fcae"]); /******/ deferredModules.push(["./CTFd/themes/admin/assets/js/pages/challenge.js","helpers","vendor","default~pages/challenge~pages/challenges~pages/configs~pages/editor~pages/main~pages/notifications~p~d5a3cc0a"]);
/******/ // run deferred modules when ready /******/ // run deferred modules when ready
/******/ return checkDeferredModules(); /******/ return checkDeferredModules();
/******/ }) /******/ })

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,169 @@
/******/ (function(modules) { // webpackBootstrap
/******/ // install a JSONP callback for chunk loading
/******/ function webpackJsonpCallback(data) {
/******/ var chunkIds = data[0];
/******/ var moreModules = data[1];
/******/ var executeModules = data[2];
/******/
/******/ // add "moreModules" to the modules object,
/******/ // then flag all "chunkIds" as loaded and fire callback
/******/ var moduleId, chunkId, i = 0, resolves = [];
/******/ for(;i < chunkIds.length; i++) {
/******/ chunkId = chunkIds[i];
/******/ if(installedChunks[chunkId]) {
/******/ resolves.push(installedChunks[chunkId][0]);
/******/ }
/******/ installedChunks[chunkId] = 0;
/******/ }
/******/ for(moduleId in moreModules) {
/******/ if(Object.prototype.hasOwnProperty.call(moreModules, moduleId)) {
/******/ modules[moduleId] = moreModules[moduleId];
/******/ }
/******/ }
/******/ if(parentJsonpFunction) parentJsonpFunction(data);
/******/
/******/ while(resolves.length) {
/******/ resolves.shift()();
/******/ }
/******/
/******/ // add entry modules from loaded chunk to deferred list
/******/ deferredModules.push.apply(deferredModules, executeModules || []);
/******/
/******/ // run deferred modules when all chunks ready
/******/ return checkDeferredModules();
/******/ };
/******/ function checkDeferredModules() {
/******/ var result;
/******/ for(var i = 0; i < deferredModules.length; i++) {
/******/ var deferredModule = deferredModules[i];
/******/ var fulfilled = true;
/******/ for(var j = 1; j < deferredModule.length; j++) {
/******/ var depId = deferredModule[j];
/******/ if(installedChunks[depId] !== 0) fulfilled = false;
/******/ }
/******/ if(fulfilled) {
/******/ deferredModules.splice(i--, 1);
/******/ result = __webpack_require__(__webpack_require__.s = deferredModule[0]);
/******/ }
/******/ }
/******/ return result;
/******/ }
/******/
/******/ // The module cache
/******/ var installedModules = {};
/******/
/******/ // object to store loaded and loading chunks
/******/ // undefined = chunk not loaded, null = chunk preloaded/prefetched
/******/ // Promise = chunk loading, 0 = chunk loaded
/******/ var installedChunks = {
/******/ "pages/challenges": 0
/******/ };
/******/
/******/ var deferredModules = [];
/******/
/******/ // The require function
/******/ function __webpack_require__(moduleId) {
/******/
/******/ // Check if module is in cache
/******/ if(installedModules[moduleId]) {
/******/ return installedModules[moduleId].exports;
/******/ }
/******/ // Create a new module (and put it into the cache)
/******/ var module = installedModules[moduleId] = {
/******/ i: moduleId,
/******/ l: false,
/******/ exports: {}
/******/ };
/******/
/******/ // Execute the module function
/******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
/******/
/******/ // Flag the module as loaded
/******/ module.l = true;
/******/
/******/ // Return the exports of the module
/******/ return module.exports;
/******/ }
/******/
/******/
/******/ // expose the modules object (__webpack_modules__)
/******/ __webpack_require__.m = modules;
/******/
/******/ // expose the module cache
/******/ __webpack_require__.c = installedModules;
/******/
/******/ // define getter function for harmony exports
/******/ __webpack_require__.d = function(exports, name, getter) {
/******/ if(!__webpack_require__.o(exports, name)) {
/******/ Object.defineProperty(exports, name, { enumerable: true, get: getter });
/******/ }
/******/ };
/******/
/******/ // define __esModule on exports
/******/ __webpack_require__.r = function(exports) {
/******/ if(typeof Symbol !== 'undefined' && Symbol.toStringTag) {
/******/ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
/******/ }
/******/ Object.defineProperty(exports, '__esModule', { value: true });
/******/ };
/******/
/******/ // create a fake namespace object
/******/ // mode & 1: value is a module id, require it
/******/ // mode & 2: merge all properties of value into the ns
/******/ // mode & 4: return value when already ns object
/******/ // mode & 8|1: behave like require
/******/ __webpack_require__.t = function(value, mode) {
/******/ if(mode & 1) value = __webpack_require__(value);
/******/ if(mode & 8) return value;
/******/ if((mode & 4) && typeof value === 'object' && value && value.__esModule) return value;
/******/ var ns = Object.create(null);
/******/ __webpack_require__.r(ns);
/******/ Object.defineProperty(ns, 'default', { enumerable: true, value: value });
/******/ if(mode & 2 && typeof value != 'string') for(var key in value) __webpack_require__.d(ns, key, function(key) { return value[key]; }.bind(null, key));
/******/ return ns;
/******/ };
/******/
/******/ // getDefaultExport function for compatibility with non-harmony modules
/******/ __webpack_require__.n = function(module) {
/******/ var getter = module && module.__esModule ?
/******/ function getDefault() { return module['default']; } :
/******/ function getModuleExports() { return module; };
/******/ __webpack_require__.d(getter, 'a', getter);
/******/ return getter;
/******/ };
/******/
/******/ // Object.prototype.hasOwnProperty.call
/******/ __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };
/******/
/******/ // __webpack_public_path__
/******/ __webpack_require__.p = "/themes/admin/static/js";
/******/
/******/ var jsonpArray = window["webpackJsonp"] = window["webpackJsonp"] || [];
/******/ var oldJsonpFunction = jsonpArray.push.bind(jsonpArray);
/******/ jsonpArray.push = webpackJsonpCallback;
/******/ jsonpArray = jsonpArray.slice();
/******/ for(var i = 0; i < jsonpArray.length; i++) webpackJsonpCallback(jsonpArray[i]);
/******/ var parentJsonpFunction = oldJsonpFunction;
/******/
/******/
/******/ // add entry module to deferred list
/******/ deferredModules.push(["./CTFd/themes/admin/assets/js/pages/challenges.js","helpers","vendor","default~pages/challenge~pages/challenges~pages/configs~pages/editor~pages/main~pages/notifications~p~d5a3cc0a"]);
/******/ // run deferred modules when ready
/******/ return checkDeferredModules();
/******/ })
/************************************************************************/
/******/ ({
/***/ "./CTFd/themes/admin/assets/js/pages/challenges.js":
/*!*********************************************************!*\
!*** ./CTFd/themes/admin/assets/js/pages/challenges.js ***!
\*********************************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {
;
eval("\n\n__webpack_require__(/*! ./main */ \"./CTFd/themes/admin/assets/js/pages/main.js\");\n\nvar _CTFd = _interopRequireDefault(__webpack_require__(/*! core/CTFd */ \"./CTFd/themes/core/assets/js/CTFd.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\nfunction _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }\n\nfunction deleteSelectedChallenges(event) {\n var challengeIDs = (0, _jquery.default)(\"input[data-challenge-id]:checked\").map(function () {\n return (0, _jquery.default)(this).data(\"challenge-id\");\n });\n var target = challengeIDs.length === 1 ? \"challenge\" : \"challenges\";\n (0, _ezq.ezQuery)({\n title: \"Delete Challenges\",\n body: \"Are you sure you want to delete \".concat(challengeIDs.length, \" \").concat(target, \"?\"),\n success: function success() {\n var reqs = [];\n var _iteratorNormalCompletion = true;\n var _didIteratorError = false;\n var _iteratorError = undefined;\n\n try {\n for (var _iterator = challengeIDs[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {\n var chalID = _step.value;\n reqs.push(_CTFd.default.fetch(\"/api/v1/challenges/\".concat(chalID), {\n method: \"DELETE\"\n }));\n }\n } catch (err) {\n _didIteratorError = true;\n _iteratorError = err;\n } finally {\n try {\n if (!_iteratorNormalCompletion && _iterator.return != null) {\n _iterator.return();\n }\n } finally {\n if (_didIteratorError) {\n throw _iteratorError;\n }\n }\n }\n\n Promise.all(reqs).then(function (responses) {\n window.location.reload();\n });\n }\n });\n}\n\nfunction bulkEditChallenges(event) {\n var challengeIDs = (0, _jquery.default)(\"input[data-challenge-id]:checked\").map(function () {\n return (0, _jquery.default)(this).data(\"challenge-id\");\n });\n (0, _ezq.ezAlert)({\n title: \"Edit Challenges\",\n body: (0, _jquery.default)(\"\\n <form id=\\\"challenges-bulk-edit\\\">\\n <div class=\\\"form-group\\\">\\n <label>Category</label>\\n <input type=\\\"text\\\" name=\\\"category\\\" data-initial=\\\"\\\" value=\\\"\\\">\\n </div>\\n <div class=\\\"form-group\\\">\\n <label>Value</label>\\n <input type=\\\"number\\\" name=\\\"value\\\" data-initial=\\\"\\\" value=\\\"\\\">\\n </div>\\n <div class=\\\"form-group\\\">\\n <label>State</label>\\n <select name=\\\"state\\\" data-initial=\\\"\\\">\\n <option value=\\\"\\\">--</option>\\n <option value=\\\"visible\\\">Visible</option>\\n <option value=\\\"hidden\\\">Hidden</option>\\n </select>\\n </div>\\n </form>\\n \"),\n button: \"Submit\",\n success: function success() {\n var data = (0, _jquery.default)(\"#challenges-bulk-edit\").serializeJSON(true);\n var reqs = [];\n var _iteratorNormalCompletion2 = true;\n var _didIteratorError2 = false;\n var _iteratorError2 = undefined;\n\n try {\n for (var _iterator2 = challengeIDs[Symbol.iterator](), _step2; !(_iteratorNormalCompletion2 = (_step2 = _iterator2.next()).done); _iteratorNormalCompletion2 = true) {\n var chalID = _step2.value;\n reqs.push(_CTFd.default.fetch(\"/api/v1/challenges/\".concat(chalID), {\n method: \"PATCH\",\n body: JSON.stringify(data)\n }));\n }\n } catch (err) {\n _didIteratorError2 = true;\n _iteratorError2 = err;\n } finally {\n try {\n if (!_iteratorNormalCompletion2 && _iterator2.return != null) {\n _iterator2.return();\n }\n } finally {\n if (_didIteratorError2) {\n throw _iteratorError2;\n }\n }\n }\n\n Promise.all(reqs).then(function (responses) {\n window.location.reload();\n });\n }\n });\n}\n\n(0, _jquery.default)(function () {\n (0, _jquery.default)(\"#challenges-delete-button\").click(deleteSelectedChallenges);\n (0, _jquery.default)(\"#challenges-edit-button\").click(bulkEditChallenges);\n});\n\n//# sourceURL=webpack:///./CTFd/themes/admin/assets/js/pages/challenges.js?");
/***/ })
/******/ });

File diff suppressed because one or more lines are too long

View File

@@ -147,7 +147,7 @@
/******/ /******/
/******/ /******/
/******/ // add entry module to deferred list /******/ // add entry module to deferred list
/******/ deferredModules.push(["./CTFd/themes/admin/assets/js/pages/configs.js","helpers","vendor","default~pages/challenge~pages/configs~pages/editor~pages/main~pages/notifications~pages/pages~pages/~0fc9fcae"]); /******/ deferredModules.push(["./CTFd/themes/admin/assets/js/pages/configs.js","helpers","vendor","default~pages/challenge~pages/challenges~pages/configs~pages/editor~pages/main~pages/notifications~p~d5a3cc0a"]);
/******/ // run deferred modules when ready /******/ // run deferred modules when ready
/******/ return checkDeferredModules(); /******/ return checkDeferredModules();
/******/ }) /******/ })

File diff suppressed because one or more lines are too long

View File

@@ -147,7 +147,7 @@
/******/ /******/
/******/ /******/
/******/ // add entry module to deferred list /******/ // add entry module to deferred list
/******/ deferredModules.push(["./CTFd/themes/admin/assets/js/pages/editor.js","helpers","vendor","default~pages/challenge~pages/configs~pages/editor~pages/main~pages/notifications~pages/pages~pages/~0fc9fcae"]); /******/ deferredModules.push(["./CTFd/themes/admin/assets/js/pages/editor.js","helpers","vendor","default~pages/challenge~pages/challenges~pages/configs~pages/editor~pages/main~pages/notifications~p~d5a3cc0a"]);
/******/ // run deferred modules when ready /******/ // run deferred modules when ready
/******/ return checkDeferredModules(); /******/ return checkDeferredModules();
/******/ }) /******/ })

File diff suppressed because one or more lines are too long

View File

@@ -147,7 +147,7 @@
/******/ /******/
/******/ /******/
/******/ // add entry module to deferred list /******/ // add entry module to deferred list
/******/ deferredModules.push(["./CTFd/themes/admin/assets/js/pages/main.js","helpers","vendor","default~pages/challenge~pages/configs~pages/editor~pages/main~pages/notifications~pages/pages~pages/~0fc9fcae"]); /******/ deferredModules.push(["./CTFd/themes/admin/assets/js/pages/main.js","helpers","vendor","default~pages/challenge~pages/challenges~pages/configs~pages/editor~pages/main~pages/notifications~p~d5a3cc0a"]);
/******/ // run deferred modules when ready /******/ // run deferred modules when ready
/******/ return checkDeferredModules(); /******/ return checkDeferredModules();
/******/ }) /******/ })

File diff suppressed because one or more lines are too long

View File

@@ -147,7 +147,7 @@
/******/ /******/
/******/ /******/
/******/ // add entry module to deferred list /******/ // add entry module to deferred list
/******/ deferredModules.push(["./CTFd/themes/admin/assets/js/pages/notifications.js","helpers","vendor","default~pages/challenge~pages/configs~pages/editor~pages/main~pages/notifications~pages/pages~pages/~0fc9fcae"]); /******/ deferredModules.push(["./CTFd/themes/admin/assets/js/pages/notifications.js","helpers","vendor","default~pages/challenge~pages/challenges~pages/configs~pages/editor~pages/main~pages/notifications~p~d5a3cc0a"]);
/******/ // run deferred modules when ready /******/ // run deferred modules when ready
/******/ return checkDeferredModules(); /******/ return checkDeferredModules();
/******/ }) /******/ })

File diff suppressed because one or more lines are too long

View File

@@ -147,7 +147,7 @@
/******/ /******/
/******/ /******/
/******/ // add entry module to deferred list /******/ // add entry module to deferred list
/******/ deferredModules.push(["./CTFd/themes/admin/assets/js/pages/pages.js","helpers","vendor","default~pages/challenge~pages/configs~pages/editor~pages/main~pages/notifications~pages/pages~pages/~0fc9fcae"]); /******/ deferredModules.push(["./CTFd/themes/admin/assets/js/pages/pages.js","helpers","vendor","default~pages/challenge~pages/challenges~pages/configs~pages/editor~pages/main~pages/notifications~p~d5a3cc0a"]);
/******/ // run deferred modules when ready /******/ // run deferred modules when ready
/******/ return checkDeferredModules(); /******/ return checkDeferredModules();
/******/ }) /******/ })
@@ -162,7 +162,7 @@
/***/ (function(module, exports, __webpack_require__) { /***/ (function(module, exports, __webpack_require__) {
; ;
eval("\n\n__webpack_require__(/*! ./main */ \"./CTFd/themes/admin/assets/js/pages/main.js\");\n\nvar _CTFd = _interopRequireDefault(__webpack_require__(/*! core/CTFd */ \"./CTFd/themes/core/assets/js/CTFd.js\"));\n\nvar _jquery = _interopRequireDefault(__webpack_require__(/*! jquery */ \"./node_modules/jquery/dist/jquery.js\"));\n\nvar _utils = __webpack_require__(/*! core/utils */ \"./CTFd/themes/core/assets/js/utils.js\");\n\nvar _ezq = __webpack_require__(/*! core/ezq */ \"./CTFd/themes/core/assets/js/ezq.js\");\n\nfunction _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }\n\nfunction deletePage(event) {\n var elem = (0, _jquery.default)(this);\n var name = elem.attr(\"page-route\");\n var page_id = elem.attr(\"page-id\");\n (0, _ezq.ezQuery)({\n title: \"Delete \" + (0, _utils.htmlEntities)(name),\n body: \"Are you sure you want to delete {0}?\".format(\"<strong>\" + (0, _utils.htmlEntities)(name) + \"</strong>\"),\n success: function success() {\n _CTFd.default.fetch(\"/api/v1/pages/\" + page_id, {\n method: \"DELETE\"\n }).then(function (response) {\n return response.json();\n }).then(function (response) {\n if (response.success) {\n elem.parent().parent().remove();\n }\n });\n }\n });\n}\n\n(0, _jquery.default)(function () {\n (0, _jquery.default)(\".delete-page\").click(deletePage);\n});\n\n//# sourceURL=webpack:///./CTFd/themes/admin/assets/js/pages/pages.js?"); eval("\n\n__webpack_require__(/*! ./main */ \"./CTFd/themes/admin/assets/js/pages/main.js\");\n\nvar _CTFd = _interopRequireDefault(__webpack_require__(/*! core/CTFd */ \"./CTFd/themes/core/assets/js/CTFd.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\nfunction _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }\n\nfunction deleteSelectedUsers(event) {\n var pageIDs = (0, _jquery.default)(\"input[data-page-id]:checked\").map(function () {\n return (0, _jquery.default)(this).data(\"page-id\");\n });\n var target = pageIDs.length === 1 ? \"page\" : \"pages\";\n (0, _ezq.ezQuery)({\n title: \"Delete Pages\",\n body: \"Are you sure you want to delete \".concat(pageIDs.length, \" \").concat(target, \"?\"),\n success: function success() {\n var reqs = [];\n var _iteratorNormalCompletion = true;\n var _didIteratorError = false;\n var _iteratorError = undefined;\n\n try {\n for (var _iterator = pageIDs[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {\n var pageID = _step.value;\n reqs.push(_CTFd.default.fetch(\"/api/v1/pages/\".concat(pageID), {\n method: \"DELETE\"\n }));\n }\n } catch (err) {\n _didIteratorError = true;\n _iteratorError = err;\n } finally {\n try {\n if (!_iteratorNormalCompletion && _iterator.return != null) {\n _iterator.return();\n }\n } finally {\n if (_didIteratorError) {\n throw _iteratorError;\n }\n }\n }\n\n Promise.all(reqs).then(function (responses) {\n window.location.reload();\n });\n }\n });\n}\n\n(0, _jquery.default)(function () {\n (0, _jquery.default)(\"#pages-delete-button\").click(deleteSelectedUsers);\n});\n\n//# sourceURL=webpack:///./CTFd/themes/admin/assets/js/pages/pages.js?");
/***/ }) /***/ })

File diff suppressed because one or more lines are too long

View File

@@ -147,7 +147,7 @@
/******/ /******/
/******/ /******/
/******/ // add entry module to deferred list /******/ // add entry module to deferred list
/******/ deferredModules.push(["./CTFd/themes/admin/assets/js/pages/reset.js","helpers","vendor","default~pages/challenge~pages/configs~pages/editor~pages/main~pages/notifications~pages/pages~pages/~0fc9fcae"]); /******/ deferredModules.push(["./CTFd/themes/admin/assets/js/pages/reset.js","helpers","vendor","default~pages/challenge~pages/challenges~pages/configs~pages/editor~pages/main~pages/notifications~p~d5a3cc0a"]);
/******/ // run deferred modules when ready /******/ // run deferred modules when ready
/******/ return checkDeferredModules(); /******/ return checkDeferredModules();
/******/ }) /******/ })

File diff suppressed because one or more lines are too long

View File

@@ -147,7 +147,7 @@
/******/ /******/
/******/ /******/
/******/ // add entry module to deferred list /******/ // add entry module to deferred list
/******/ deferredModules.push(["./CTFd/themes/admin/assets/js/pages/scoreboard.js","helpers","vendor","default~pages/challenge~pages/configs~pages/editor~pages/main~pages/notifications~pages/pages~pages/~0fc9fcae"]); /******/ deferredModules.push(["./CTFd/themes/admin/assets/js/pages/scoreboard.js","helpers","vendor","default~pages/challenge~pages/challenges~pages/configs~pages/editor~pages/main~pages/notifications~p~d5a3cc0a"]);
/******/ // run deferred modules when ready /******/ // run deferred modules when ready
/******/ return checkDeferredModules(); /******/ return checkDeferredModules();
/******/ }) /******/ })
@@ -162,7 +162,7 @@
/***/ (function(module, exports, __webpack_require__) { /***/ (function(module, exports, __webpack_require__) {
; ;
eval("\n\n__webpack_require__(/*! ./main */ \"./CTFd/themes/admin/assets/js/pages/main.js\");\n\nvar _CTFd = _interopRequireDefault(__webpack_require__(/*! core/CTFd */ \"./CTFd/themes/core/assets/js/CTFd.js\"));\n\nvar _jquery = _interopRequireDefault(__webpack_require__(/*! jquery */ \"./node_modules/jquery/dist/jquery.js\"));\n\nfunction _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }\n\nvar api_func = {\n users: function users(x, y) {\n return _CTFd.default.api.patch_user_public({\n userId: x\n }, y);\n },\n teams: function teams(x, y) {\n return _CTFd.default.api.patch_team_public({\n teamId: x\n }, y);\n }\n};\n\nfunction toggleAccount() {\n var $btn = (0, _jquery.default)(this);\n var id = $btn.data(\"account-id\");\n var state = $btn.data(\"state\");\n var hidden = undefined;\n\n if (state === \"visible\") {\n hidden = true;\n } else if (state === \"hidden\") {\n hidden = false;\n }\n\n var params = {\n hidden: hidden\n };\n\n api_func[_CTFd.default.config.userMode](id, params).then(function (response) {\n if (response.success) {\n if (hidden) {\n $btn.data(\"state\", \"hidden\");\n $btn.addClass(\"btn-danger\").removeClass(\"btn-success\");\n $btn.text(\"Hidden\");\n } else {\n $btn.data(\"state\", \"visible\");\n $btn.addClass(\"btn-success\").removeClass(\"btn-danger\");\n $btn.text(\"Visible\");\n }\n }\n });\n}\n\n(0, _jquery.default)(function () {\n (0, _jquery.default)(\".scoreboard-toggle\").click(toggleAccount);\n});\n\n//# sourceURL=webpack:///./CTFd/themes/admin/assets/js/pages/scoreboard.js?"); eval("\n\n__webpack_require__(/*! ./main */ \"./CTFd/themes/admin/assets/js/pages/main.js\");\n\nvar _CTFd = _interopRequireDefault(__webpack_require__(/*! core/CTFd */ \"./CTFd/themes/core/assets/js/CTFd.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\nfunction _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }\n\nvar api_func = {\n users: function users(x, y) {\n return _CTFd.default.api.patch_user_public({\n userId: x\n }, y);\n },\n teams: function teams(x, y) {\n return _CTFd.default.api.patch_team_public({\n teamId: x\n }, y);\n }\n};\n\nfunction toggleAccount() {\n var $btn = (0, _jquery.default)(this);\n var id = $btn.data(\"account-id\");\n var state = $btn.data(\"state\");\n var hidden = undefined;\n\n if (state === \"visible\") {\n hidden = true;\n } else if (state === \"hidden\") {\n hidden = false;\n }\n\n var params = {\n hidden: hidden\n };\n\n api_func[_CTFd.default.config.userMode](id, params).then(function (response) {\n if (response.success) {\n if (hidden) {\n $btn.data(\"state\", \"hidden\");\n $btn.addClass(\"btn-danger\").removeClass(\"btn-success\");\n $btn.text(\"Hidden\");\n } else {\n $btn.data(\"state\", \"visible\");\n $btn.addClass(\"btn-success\").removeClass(\"btn-danger\");\n $btn.text(\"Visible\");\n }\n }\n });\n}\n\nfunction toggleSelectedAccounts(accountIDs, action) {\n var params = {\n hidden: action === \"hidden\" ? true : false\n };\n var reqs = [];\n var _iteratorNormalCompletion = true;\n var _didIteratorError = false;\n var _iteratorError = undefined;\n\n try {\n for (var _iterator = accountIDs[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {\n var accId = _step.value;\n reqs.push(api_func[_CTFd.default.config.userMode](accId, params));\n }\n } catch (err) {\n _didIteratorError = true;\n _iteratorError = err;\n } finally {\n try {\n if (!_iteratorNormalCompletion && _iterator.return != null) {\n _iterator.return();\n }\n } finally {\n if (_didIteratorError) {\n throw _iteratorError;\n }\n }\n }\n\n Promise.all(reqs).then(function (responses) {\n window.location.reload();\n });\n}\n\nfunction bulkToggleAccounts(event) {\n var accountIDs = (0, _jquery.default)(\"input[data-account-id]:checked\").map(function () {\n return (0, _jquery.default)(this).data(\"account-id\");\n });\n (0, _ezq.ezAlert)({\n title: \"Toggle Visibility\",\n body: (0, _jquery.default)(\"\\n <form id=\\\"scoreboard-bulk-edit\\\">\\n <div class=\\\"form-group\\\">\\n <label>Visibility</label>\\n <select name=\\\"visibility\\\" data-initial=\\\"\\\">\\n <option value=\\\"\\\">--</option>\\n <option value=\\\"visible\\\">Visible</option>\\n <option value=\\\"hidden\\\">Hidden</option>\\n </select>\\n </div>\\n </form>\\n \"),\n button: \"Submit\",\n success: function success() {\n var data = (0, _jquery.default)(\"#scoreboard-bulk-edit\").serializeJSON(true);\n var state = data.visibility;\n toggleSelectedAccounts(accountIDs, state);\n }\n });\n}\n\n(0, _jquery.default)(function () {\n (0, _jquery.default)(\".scoreboard-toggle\").click(toggleAccount);\n (0, _jquery.default)(\"#scoreboard-edit-button\").click(bulkToggleAccounts);\n});\n\n//# sourceURL=webpack:///./CTFd/themes/admin/assets/js/pages/scoreboard.js?");
/***/ }) /***/ })

File diff suppressed because one or more lines are too long

View File

@@ -147,7 +147,7 @@
/******/ /******/
/******/ /******/
/******/ // add entry module to deferred list /******/ // add entry module to deferred list
/******/ deferredModules.push(["./CTFd/themes/admin/assets/js/pages/statistics.js","helpers","graphs","plotly","vendor","default~pages/challenge~pages/configs~pages/editor~pages/main~pages/notifications~pages/pages~pages/~0fc9fcae"]); /******/ deferredModules.push(["./CTFd/themes/admin/assets/js/pages/statistics.js","helpers","graphs","plotly","vendor","default~pages/challenge~pages/challenges~pages/configs~pages/editor~pages/main~pages/notifications~p~d5a3cc0a"]);
/******/ // run deferred modules when ready /******/ // run deferred modules when ready
/******/ return checkDeferredModules(); /******/ return checkDeferredModules();
/******/ }) /******/ })

File diff suppressed because one or more lines are too long

View File

@@ -147,7 +147,7 @@
/******/ /******/
/******/ /******/
/******/ // add entry module to deferred list /******/ // add entry module to deferred list
/******/ deferredModules.push(["./CTFd/themes/admin/assets/js/pages/submissions.js","helpers","vendor","default~pages/challenge~pages/configs~pages/editor~pages/main~pages/notifications~pages/pages~pages/~0fc9fcae"]); /******/ deferredModules.push(["./CTFd/themes/admin/assets/js/pages/submissions.js","helpers","vendor","default~pages/challenge~pages/challenges~pages/configs~pages/editor~pages/main~pages/notifications~p~d5a3cc0a"]);
/******/ // run deferred modules when ready /******/ // run deferred modules when ready
/******/ return checkDeferredModules(); /******/ return checkDeferredModules();
/******/ }) /******/ })
@@ -162,7 +162,7 @@
/***/ (function(module, exports, __webpack_require__) { /***/ (function(module, exports, __webpack_require__) {
; ;
eval("\n\n__webpack_require__(/*! ./main */ \"./CTFd/themes/admin/assets/js/pages/main.js\");\n\nvar _CTFd = _interopRequireDefault(__webpack_require__(/*! core/CTFd */ \"./CTFd/themes/core/assets/js/CTFd.js\"));\n\nvar _jquery = _interopRequireDefault(__webpack_require__(/*! jquery */ \"./node_modules/jquery/dist/jquery.js\"));\n\nvar _utils = __webpack_require__(/*! core/utils */ \"./CTFd/themes/core/assets/js/utils.js\");\n\nvar _ezq = __webpack_require__(/*! core/ezq */ \"./CTFd/themes/core/assets/js/ezq.js\");\n\nfunction _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }\n\nfunction deleteCorrectSubmission(event) {\n var key_id = (0, _jquery.default)(this).data(\"submission-id\");\n var $elem = (0, _jquery.default)(this).parent().parent();\n var chal_name = $elem.find(\".chal\").text().trim();\n var team_name = $elem.find(\".team\").text().trim();\n var row = (0, _jquery.default)(this).parent().parent();\n (0, _ezq.ezQuery)({\n title: \"Delete Submission\",\n body: \"Are you sure you want to delete correct submission from {0} for challenge {1}\".format(\"<strong>\" + (0, _utils.htmlEntities)(team_name) + \"</strong>\", \"<strong>\" + (0, _utils.htmlEntities)(chal_name) + \"</strong>\"),\n success: function success() {\n _CTFd.default.api.delete_submission({\n submissionId: key_id\n }).then(function (response) {\n if (response.success) {\n row.remove();\n }\n });\n }\n });\n}\n\n(0, _jquery.default)(function () {\n (0, _jquery.default)(\".delete-correct-submission\").click(deleteCorrectSubmission);\n});\n\n//# sourceURL=webpack:///./CTFd/themes/admin/assets/js/pages/submissions.js?"); eval("\n\n__webpack_require__(/*! ./main */ \"./CTFd/themes/admin/assets/js/pages/main.js\");\n\nvar _CTFd = _interopRequireDefault(__webpack_require__(/*! core/CTFd */ \"./CTFd/themes/core/assets/js/CTFd.js\"));\n\nvar _jquery = _interopRequireDefault(__webpack_require__(/*! jquery */ \"./node_modules/jquery/dist/jquery.js\"));\n\nvar _utils = __webpack_require__(/*! core/utils */ \"./CTFd/themes/core/assets/js/utils.js\");\n\nvar _ezq = __webpack_require__(/*! core/ezq */ \"./CTFd/themes/core/assets/js/ezq.js\");\n\nfunction _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }\n\nfunction deleteCorrectSubmission(event) {\n var key_id = (0, _jquery.default)(this).data(\"submission-id\");\n var $elem = (0, _jquery.default)(this).parent().parent();\n var chal_name = $elem.find(\".chal\").text().trim();\n var team_name = $elem.find(\".team\").text().trim();\n var row = (0, _jquery.default)(this).parent().parent();\n (0, _ezq.ezQuery)({\n title: \"Delete Submission\",\n body: \"Are you sure you want to delete correct submission from {0} for challenge {1}\".format(\"<strong>\" + (0, _utils.htmlEntities)(team_name) + \"</strong>\", \"<strong>\" + (0, _utils.htmlEntities)(chal_name) + \"</strong>\"),\n success: function success() {\n _CTFd.default.api.delete_submission({\n submissionId: key_id\n }).then(function (response) {\n if (response.success) {\n row.remove();\n }\n });\n }\n });\n}\n\nfunction deleteSelectedSubmissions(event) {\n var submissionIDs = (0, _jquery.default)(\"input[data-submission-id]:checked\").map(function () {\n return (0, _jquery.default)(this).data(\"submission-id\");\n });\n var target = submissionIDs.length === 1 ? \"submission\" : \"submissions\";\n (0, _ezq.ezQuery)({\n title: \"Delete Submissions\",\n body: \"Are you sure you want to delete \".concat(submissionIDs.length, \" \").concat(target, \"?\"),\n success: function success() {\n var reqs = [];\n var _iteratorNormalCompletion = true;\n var _didIteratorError = false;\n var _iteratorError = undefined;\n\n try {\n for (var _iterator = submissionIDs[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {\n var subId = _step.value;\n reqs.push(_CTFd.default.api.delete_submission({\n submissionId: subId\n }));\n }\n } catch (err) {\n _didIteratorError = true;\n _iteratorError = err;\n } finally {\n try {\n if (!_iteratorNormalCompletion && _iterator.return != null) {\n _iterator.return();\n }\n } finally {\n if (_didIteratorError) {\n throw _iteratorError;\n }\n }\n }\n\n Promise.all(reqs).then(function (responses) {\n window.location.reload();\n });\n }\n });\n}\n\n(0, _jquery.default)(function () {\n (0, _jquery.default)(\".delete-correct-submission\").click(deleteCorrectSubmission);\n (0, _jquery.default)(\"#submission-delete-button\").click(deleteSelectedSubmissions);\n});\n\n//# sourceURL=webpack:///./CTFd/themes/admin/assets/js/pages/submissions.js?");
/***/ }) /***/ })

File diff suppressed because one or more lines are too long

View File

@@ -147,7 +147,7 @@
/******/ /******/
/******/ /******/
/******/ // add entry module to deferred list /******/ // add entry module to deferred list
/******/ deferredModules.push(["./CTFd/themes/admin/assets/js/pages/team.js","helpers","graphs","plotly","vendor","default~pages/challenge~pages/configs~pages/editor~pages/main~pages/notifications~pages/pages~pages/~0fc9fcae"]); /******/ deferredModules.push(["./CTFd/themes/admin/assets/js/pages/team.js","helpers","graphs","plotly","vendor","default~pages/challenge~pages/challenges~pages/configs~pages/editor~pages/main~pages/notifications~p~d5a3cc0a"]);
/******/ // run deferred modules when ready /******/ // run deferred modules when ready
/******/ return checkDeferredModules(); /******/ return checkDeferredModules();
/******/ }) /******/ })

File diff suppressed because one or more lines are too long

View File

@@ -1,7 +1,66 @@
/******/ (function(modules) { // webpackBootstrap /******/ (function(modules) { // webpackBootstrap
/******/ // install a JSONP callback for chunk loading
/******/ function webpackJsonpCallback(data) {
/******/ var chunkIds = data[0];
/******/ var moreModules = data[1];
/******/ var executeModules = data[2];
/******/
/******/ // add "moreModules" to the modules object,
/******/ // then flag all "chunkIds" as loaded and fire callback
/******/ var moduleId, chunkId, i = 0, resolves = [];
/******/ for(;i < chunkIds.length; i++) {
/******/ chunkId = chunkIds[i];
/******/ if(installedChunks[chunkId]) {
/******/ resolves.push(installedChunks[chunkId][0]);
/******/ }
/******/ installedChunks[chunkId] = 0;
/******/ }
/******/ for(moduleId in moreModules) {
/******/ if(Object.prototype.hasOwnProperty.call(moreModules, moduleId)) {
/******/ modules[moduleId] = moreModules[moduleId];
/******/ }
/******/ }
/******/ if(parentJsonpFunction) parentJsonpFunction(data);
/******/
/******/ while(resolves.length) {
/******/ resolves.shift()();
/******/ }
/******/
/******/ // add entry modules from loaded chunk to deferred list
/******/ deferredModules.push.apply(deferredModules, executeModules || []);
/******/
/******/ // run deferred modules when all chunks ready
/******/ return checkDeferredModules();
/******/ };
/******/ function checkDeferredModules() {
/******/ var result;
/******/ for(var i = 0; i < deferredModules.length; i++) {
/******/ var deferredModule = deferredModules[i];
/******/ var fulfilled = true;
/******/ for(var j = 1; j < deferredModule.length; j++) {
/******/ var depId = deferredModule[j];
/******/ if(installedChunks[depId] !== 0) fulfilled = false;
/******/ }
/******/ if(fulfilled) {
/******/ deferredModules.splice(i--, 1);
/******/ result = __webpack_require__(__webpack_require__.s = deferredModule[0]);
/******/ }
/******/ }
/******/ return result;
/******/ }
/******/
/******/ // The module cache /******/ // The module cache
/******/ var installedModules = {}; /******/ var installedModules = {};
/******/ /******/
/******/ // object to store loaded and loading chunks
/******/ // undefined = chunk not loaded, null = chunk preloaded/prefetched
/******/ // Promise = chunk loading, 0 = chunk loaded
/******/ var installedChunks = {
/******/ "pages/teams": 0
/******/ };
/******/
/******/ var deferredModules = [];
/******/
/******/ // The require function /******/ // The require function
/******/ function __webpack_require__(moduleId) { /******/ function __webpack_require__(moduleId) {
/******/ /******/
@@ -79,9 +138,18 @@
/******/ // __webpack_public_path__ /******/ // __webpack_public_path__
/******/ __webpack_require__.p = "/themes/admin/static/js"; /******/ __webpack_require__.p = "/themes/admin/static/js";
/******/ /******/
/******/ var jsonpArray = window["webpackJsonp"] = window["webpackJsonp"] || [];
/******/ var oldJsonpFunction = jsonpArray.push.bind(jsonpArray);
/******/ jsonpArray.push = webpackJsonpCallback;
/******/ jsonpArray = jsonpArray.slice();
/******/ for(var i = 0; i < jsonpArray.length; i++) webpackJsonpCallback(jsonpArray[i]);
/******/ var parentJsonpFunction = oldJsonpFunction;
/******/ /******/
/******/ // Load entry module and return exports /******/
/******/ return __webpack_require__(__webpack_require__.s = "./CTFd/themes/admin/assets/js/pages/teams.js"); /******/ // add entry module to deferred list
/******/ deferredModules.push(["./CTFd/themes/admin/assets/js/pages/teams.js","helpers","vendor","default~pages/challenge~pages/challenges~pages/configs~pages/editor~pages/main~pages/notifications~p~d5a3cc0a"]);
/******/ // run deferred modules when ready
/******/ return checkDeferredModules();
/******/ }) /******/ })
/************************************************************************/ /************************************************************************/
/******/ ({ /******/ ({
@@ -94,7 +162,7 @@
/***/ (function(module, exports, __webpack_require__) { /***/ (function(module, exports, __webpack_require__) {
; ;
eval("\n\n//# sourceURL=webpack:///./CTFd/themes/admin/assets/js/pages/teams.js?"); eval("\n\n__webpack_require__(/*! ./main */ \"./CTFd/themes/admin/assets/js/pages/main.js\");\n\nvar _CTFd = _interopRequireDefault(__webpack_require__(/*! core/CTFd */ \"./CTFd/themes/core/assets/js/CTFd.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\nfunction _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }\n\nfunction deleteSelectedTeams(event) {\n var teamIDs = (0, _jquery.default)(\"input[data-team-id]:checked\").map(function () {\n return (0, _jquery.default)(this).data(\"team-id\");\n });\n var target = teamIDs.length === 1 ? \"team\" : \"teams\";\n (0, _ezq.ezQuery)({\n title: \"Delete Teams\",\n body: \"Are you sure you want to delete \".concat(teamIDs.length, \" \").concat(target, \"?\"),\n success: function success() {\n var reqs = [];\n var _iteratorNormalCompletion = true;\n var _didIteratorError = false;\n var _iteratorError = undefined;\n\n try {\n for (var _iterator = teamIDs[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {\n var teamID = _step.value;\n reqs.push(_CTFd.default.fetch(\"/api/v1/teams/\".concat(teamID), {\n method: \"DELETE\"\n }));\n }\n } catch (err) {\n _didIteratorError = true;\n _iteratorError = err;\n } finally {\n try {\n if (!_iteratorNormalCompletion && _iterator.return != null) {\n _iterator.return();\n }\n } finally {\n if (_didIteratorError) {\n throw _iteratorError;\n }\n }\n }\n\n Promise.all(reqs).then(function (responses) {\n window.location.reload();\n });\n }\n });\n}\n\nfunction bulkEditTeams(event) {\n var teamIDs = (0, _jquery.default)(\"input[data-team-id]:checked\").map(function () {\n return (0, _jquery.default)(this).data(\"team-id\");\n });\n (0, _ezq.ezAlert)({\n title: \"Edit Teams\",\n body: (0, _jquery.default)(\"\\n <form id=\\\"teams-bulk-edit\\\">\\n <div class=\\\"form-group\\\">\\n <label>Banned</label>\\n <select name=\\\"banned\\\" data-initial=\\\"\\\">\\n <option value=\\\"\\\">--</option>\\n <option value=\\\"true\\\">True</option>\\n <option value=\\\"false\\\">False</option>\\n </select>\\n </div>\\n <div class=\\\"form-group\\\">\\n <label>Hidden</label>\\n <select name=\\\"hidden\\\" data-initial=\\\"\\\">\\n <option value=\\\"\\\">--</option>\\n <option value=\\\"true\\\">True</option>\\n <option value=\\\"false\\\">False</option>\\n </select>\\n </div>\\n </form>\\n \"),\n button: \"Submit\",\n success: function success() {\n var data = (0, _jquery.default)(\"#teams-bulk-edit\").serializeJSON(true);\n var reqs = [];\n var _iteratorNormalCompletion2 = true;\n var _didIteratorError2 = false;\n var _iteratorError2 = undefined;\n\n try {\n for (var _iterator2 = teamIDs[Symbol.iterator](), _step2; !(_iteratorNormalCompletion2 = (_step2 = _iterator2.next()).done); _iteratorNormalCompletion2 = true) {\n var teamID = _step2.value;\n reqs.push(_CTFd.default.fetch(\"/api/v1/teams/\".concat(teamID), {\n method: \"PATCH\",\n body: JSON.stringify(data)\n }));\n }\n } catch (err) {\n _didIteratorError2 = true;\n _iteratorError2 = err;\n } finally {\n try {\n if (!_iteratorNormalCompletion2 && _iterator2.return != null) {\n _iterator2.return();\n }\n } finally {\n if (_didIteratorError2) {\n throw _iteratorError2;\n }\n }\n }\n\n Promise.all(reqs).then(function (responses) {\n window.location.reload();\n });\n }\n });\n}\n\n(0, _jquery.default)(function () {\n (0, _jquery.default)(\"#teams-delete-button\").click(deleteSelectedTeams);\n (0, _jquery.default)(\"#teams-edit-button\").click(bulkEditTeams);\n});\n\n//# sourceURL=webpack:///./CTFd/themes/admin/assets/js/pages/teams.js?");
/***/ }) /***/ })

File diff suppressed because one or more lines are too long

View File

@@ -147,7 +147,7 @@
/******/ /******/
/******/ /******/
/******/ // add entry module to deferred list /******/ // add entry module to deferred list
/******/ deferredModules.push(["./CTFd/themes/admin/assets/js/pages/user.js","helpers","graphs","plotly","vendor","default~pages/challenge~pages/configs~pages/editor~pages/main~pages/notifications~pages/pages~pages/~0fc9fcae"]); /******/ deferredModules.push(["./CTFd/themes/admin/assets/js/pages/user.js","helpers","graphs","plotly","vendor","default~pages/challenge~pages/challenges~pages/configs~pages/editor~pages/main~pages/notifications~p~d5a3cc0a"]);
/******/ // run deferred modules when ready /******/ // run deferred modules when ready
/******/ return checkDeferredModules(); /******/ return checkDeferredModules();
/******/ }) /******/ })

File diff suppressed because one or more lines are too long

View File

@@ -147,7 +147,7 @@
/******/ /******/
/******/ /******/
/******/ // add entry module to deferred list /******/ // add entry module to deferred list
/******/ deferredModules.push(["./CTFd/themes/admin/assets/js/pages/users.js","helpers","vendor","default~pages/challenge~pages/configs~pages/editor~pages/main~pages/notifications~pages/pages~pages/~0fc9fcae"]); /******/ deferredModules.push(["./CTFd/themes/admin/assets/js/pages/users.js","helpers","vendor","default~pages/challenge~pages/challenges~pages/configs~pages/editor~pages/main~pages/notifications~p~d5a3cc0a"]);
/******/ // run deferred modules when ready /******/ // run deferred modules when ready
/******/ return checkDeferredModules(); /******/ return checkDeferredModules();
/******/ }) /******/ })
@@ -162,7 +162,7 @@
/***/ (function(module, exports, __webpack_require__) { /***/ (function(module, exports, __webpack_require__) {
; ;
eval("\n\n__webpack_require__(/*! ./main */ \"./CTFd/themes/admin/assets/js/pages/main.js\");\n\n//# sourceURL=webpack:///./CTFd/themes/admin/assets/js/pages/users.js?"); eval("\n\n__webpack_require__(/*! ./main */ \"./CTFd/themes/admin/assets/js/pages/main.js\");\n\nvar _CTFd = _interopRequireDefault(__webpack_require__(/*! core/CTFd */ \"./CTFd/themes/core/assets/js/CTFd.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\nfunction _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }\n\nfunction deleteSelectedUsers(event) {\n var userIDs = (0, _jquery.default)(\"input[data-user-id]:checked\").map(function () {\n return (0, _jquery.default)(this).data(\"user-id\");\n });\n var target = userIDs.length === 1 ? \"user\" : \"users\";\n (0, _ezq.ezQuery)({\n title: \"Delete Users\",\n body: \"Are you sure you want to delete \".concat(userIDs.length, \" \").concat(target, \"?\"),\n success: function success() {\n var reqs = [];\n var _iteratorNormalCompletion = true;\n var _didIteratorError = false;\n var _iteratorError = undefined;\n\n try {\n for (var _iterator = userIDs[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {\n var userID = _step.value;\n reqs.push(_CTFd.default.fetch(\"/api/v1/users/\".concat(userID), {\n method: \"DELETE\"\n }));\n }\n } catch (err) {\n _didIteratorError = true;\n _iteratorError = err;\n } finally {\n try {\n if (!_iteratorNormalCompletion && _iterator.return != null) {\n _iterator.return();\n }\n } finally {\n if (_didIteratorError) {\n throw _iteratorError;\n }\n }\n }\n\n Promise.all(reqs).then(function (responses) {\n window.location.reload();\n });\n }\n });\n}\n\nfunction bulkEditUsers(event) {\n var userIDs = (0, _jquery.default)(\"input[data-user-id]:checked\").map(function () {\n return (0, _jquery.default)(this).data(\"user-id\");\n });\n (0, _ezq.ezAlert)({\n title: \"Edit Users\",\n body: (0, _jquery.default)(\"\\n <form id=\\\"users-bulk-edit\\\">\\n <div class=\\\"form-group\\\">\\n <label>Verified</label>\\n <select name=\\\"verified\\\" data-initial=\\\"\\\">\\n <option value=\\\"\\\">--</option>\\n <option value=\\\"true\\\">True</option>\\n <option value=\\\"false\\\">False</option>\\n </select>\\n </div>\\n <div class=\\\"form-group\\\">\\n <label>Banned</label>\\n <select name=\\\"banned\\\" data-initial=\\\"\\\">\\n <option value=\\\"\\\">--</option>\\n <option value=\\\"true\\\">True</option>\\n <option value=\\\"false\\\">False</option>\\n </select>\\n </div>\\n <div class=\\\"form-group\\\">\\n <label>Hidden</label>\\n <select name=\\\"hidden\\\" data-initial=\\\"\\\">\\n <option value=\\\"\\\">--</option>\\n <option value=\\\"true\\\">True</option>\\n <option value=\\\"false\\\">False</option>\\n </select>\\n </div>\\n </form>\\n \"),\n button: \"Submit\",\n success: function success() {\n var data = (0, _jquery.default)(\"#users-bulk-edit\").serializeJSON(true);\n var reqs = [];\n var _iteratorNormalCompletion2 = true;\n var _didIteratorError2 = false;\n var _iteratorError2 = undefined;\n\n try {\n for (var _iterator2 = userIDs[Symbol.iterator](), _step2; !(_iteratorNormalCompletion2 = (_step2 = _iterator2.next()).done); _iteratorNormalCompletion2 = true) {\n var userID = _step2.value;\n reqs.push(_CTFd.default.fetch(\"/api/v1/users/\".concat(userID), {\n method: \"PATCH\",\n body: JSON.stringify(data)\n }));\n }\n } catch (err) {\n _didIteratorError2 = true;\n _iteratorError2 = err;\n } finally {\n try {\n if (!_iteratorNormalCompletion2 && _iterator2.return != null) {\n _iterator2.return();\n }\n } finally {\n if (_didIteratorError2) {\n throw _iteratorError2;\n }\n }\n }\n\n Promise.all(reqs).then(function (responses) {\n window.location.reload();\n });\n }\n });\n}\n\n(0, _jquery.default)(function () {\n (0, _jquery.default)(\"#users-delete-button\").click(deleteSelectedUsers);\n (0, _jquery.default)(\"#users-edit-button\").click(bulkEditUsers);\n});\n\n//# sourceURL=webpack:///./CTFd/themes/admin/assets/js/pages/users.js?");
/***/ }) /***/ })

File diff suppressed because one or more lines are too long

View File

@@ -18,28 +18,53 @@
</div> </div>
</div> </div>
<div class="container"> <div class="container">
<div class="row">
<div class="col-md-12">
<div class="float-right pb-3">
<div class="btn-group" role="group">
<button type="button" class="btn btn-outline-secondary" data-toggle="tooltip" title="Edit Challenges" id="challenges-edit-button">
<i class="btn-fa fas fa-pencil-alt"></i>
</button>
<button type="button" class="btn btn-outline-danger" data-toggle="tooltip" title="Delete Challenges" id="challenges-delete-button">
<i class="btn-fa fas fa-trash-alt"></i>
</button>
</div>
</div>
</div>
</div>
<div class="row"> <div class="row">
<div class="col-md-12"> <div class="col-md-12">
<div> <div>
<table id="challenges" class="table table-striped"> <table id="challenges" class="table table-striped border">
<thead> <thead>
<tr> <tr>
<td><b>ID</b></td> <td class="d-block border-right border-bottom text-center" data-checkbox>
<td><b>Name</b></td> <div class="form-check">
<td class="d-none d-md-table-cell d-lg-table-cell"><b>Category</b></td> <input type="checkbox" class="form-check-input" data-checkbox-all>&nbsp;
<td class="d-none d-md-table-cell d-lg-table-cell"><b>Value</b></td> </div>
<td class="d-none d-md-table-cell d-lg-table-cell"><b>Type</b></td> </td>
<td class="d-none d-md-table-cell d-lg-table-cell text-center"><b>State</b></td> <th class="sort-col text-center"><b>ID</b></th>
<th class="sort-col"><b>Name</b></th>
<th class="d-none d-md-table-cell d-lg-table-cell sort-col"><b>Category</b></th>
<th class="d-none d-md-table-cell d-lg-table-cell sort-col text-center"><b>Value</b></th>
<th class="d-none d-md-table-cell d-lg-table-cell sort-col text-center"><b>Type</b></th>
<th class="d-none d-md-table-cell d-lg-table-cell sort-col text-center"><b>State</b></th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
{% for challenge in challenges %} {% for challenge in challenges %}
<tr data-href="{{ url_for('admin.challenges_detail', challenge_id=challenge.id) }}"> <tr data-href="{{ url_for('admin.challenges_detail', challenge_id=challenge.id) }}">
<td>{{ challenge.id }}</td> <td class="d-block border-right text-center" data-checkbox>
<div class="form-check">
<input type="checkbox" class="form-check-input" value="{{ challenge.id }}" data-challenge-id="{{ challenge.id }}">&nbsp;
</div>
</td>
<td class="text-center">{{ challenge.id }}</td>
<td><a href="{{ url_for('admin.challenges_detail', challenge_id=challenge.id) }}">{{ challenge.name }}</a></td> <td><a href="{{ url_for('admin.challenges_detail', challenge_id=challenge.id) }}">{{ challenge.name }}</a></td>
<td class="d-none d-md-table-cell d-lg-table-cell">{{ challenge.category }}</td> <td class="d-none d-md-table-cell d-lg-table-cell">{{ challenge.category }}</td>
<td class="d-none d-md-table-cell d-lg-table-cell">{{ challenge.value }}</td> <td class="d-none d-md-table-cell d-lg-table-cell text-center">{{ challenge.value }}</td>
<td class="d-none d-md-table-cell d-lg-table-cell">{{ challenge.type }}</td> <td class="d-none d-md-table-cell d-lg-table-cell text-center">{{ challenge.type }}</td>
<td class="d-none d-md-table-cell d-lg-table-cell text-center"> <td class="d-none d-md-table-cell d-lg-table-cell text-center">
{% set badge_state = 'badge-danger' if challenge.state == 'hidden' else 'badge-success' %} {% set badge_state = 'badge-danger' if challenge.state == 'hidden' else 'badge-success' %}
<span class="badge {{ badge_state }}">{{ challenge.state }}</span> <span class="badge {{ badge_state }}">{{ challenge.state }}</span>
@@ -56,3 +81,7 @@
{% block scripts %} {% block scripts %}
{% endblock %} {% endblock %}
{% block entrypoint %}
<script defer src="{{ url_for('views.themes', theme='admin', path='js/pages/challenges.js') }}"></script>
{% endblock %}

View File

@@ -19,20 +19,40 @@
<div class="container"> <div class="container">
<div class="row"> <div class="row">
<div class="col-md-12"> <div class="col-md-12">
<table id="pages" class="table table-striped"> <div class="float-right pb-3">
<div class="btn-group" role="group">
<button type="button" class="btn btn-outline-danger" data-toggle="tooltip" title="Delete Pages" id="pages-delete-button">
<i class="btn-fa fas fa-trash-alt"></i>
</button>
</div>
</div>
</div>
</div>
<div class="row">
<div class="col-md-12">
<table id="pages" class="table table-striped border">
<thead> <thead>
<tr> <tr>
<td><b>Title</b></td> <th class="border-right" data-checkbox>
<td><b>Route</b></td> <div class="form-check text-center">
<td class="text-center"><b>Authentication</b></td> <input type="checkbox" class="form-check-input" id="scoreboard-bulk-select" data-checkbox-all>&nbsp;
<td class="text-center"><b>Hidden</b></td> </div>
<td class="text-center"><b>Published</b></td> </th>
<td class="text-center" width="10px"><b>Settings</b></td> <th class="sort-col text-center"><b>Title</b></th>
<th class="sort-col text-center"><b>Route</b></th>
<th class="sort-col text-center"><b>Authentication</b></th>
<th class="sort-col text-center"><b>Hidden</b></th>
<th class="sort-col text-center"><b>Published</b></th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
{% for page in pages %} {% for page in pages %}
<tr> <tr data-href="{{ url_for('admin.pages_detail', page_id=page.id) }}">
<td class="border-right" data-checkbox>
<div class="form-check text-center">
<input type="checkbox" class="form-check-input" value="{{ page.id }}" data-page-id="{{ page.id }}">&nbsp;
</div>
</td>
<td class="page-title"> <td class="page-title">
{{ page.title }} {{ page.title }}
</td> </td>
@@ -60,17 +80,6 @@
Published Published
{% endif %} {% endif %}
</td> </td>
<td class="text-center">
<span class="edit-page" data-toggle="tooltip" data-placement="top"
title="Edit {{ page.route }}">
<a class="no-decoration" href="{{ url_for('admin.pages_detail', page_id=page.id) }}"><i class="btn-fa fas fa-edit"></i></a>
</span>
<span class="delete-page" page-id="{{ page.id }}" page-route="{{ page.route }}"
page-title="{{ page.title }}" data-toggle="tooltip" data-placement="top"
title="Delete {{ page.route }}">
<i class="btn-fa fas fa-times"></i>
</span>
</td>
</tr> </tr>
{% endfor %} {% endfor %}
</tbody> </tbody>

View File

@@ -9,19 +9,40 @@
<div class="container"> <div class="container">
<div class="row"> <div class="row">
<div class="col-md-12"> <div class="col-md-12">
<table id="scoreboard" class="table table-striped"> <div class="float-right pb-3">
<div class="btn-group" role="group">
<button type="button" class="btn btn-outline-secondary" id="scoreboard-edit-button" data-toggle="tooltip" title="Hide/Unhide Accounts">
<i class="btn-fa fas fa-eye"></i>
</button>
</div>
</div>
</div>
</div>
<div class="row">
<div class="col-md-12">
<table id="scoreboard" class="table table-striped border">
<thead> <thead>
<tr> <tr>
<td width="10px"><b>Place</b></td> <th class="border-right" data-checkbox>
<td><b>Team</b></td> <div class="form-check text-center">
<td><b>Score</b></td> <input type="checkbox" class="form-check-input" id="scoreboard-bulk-select" data-checkbox-all>&nbsp;
<td><b>Visibility</b></td> </div>
</th>
<th class="sort-col text-center"><b>Place</b></th>
<th class="sort-col"><b>Team</b></th>
<th class="sort-col"><b>Score</b></th>
<th class="sort-col"><b>Visibility</b></th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
{% for standing in standings %} {% for standing in standings %}
<tr data-href="{{ generate_account_url(standing.account_id, admin=True) }}"> <tr data-href="{{ generate_account_url(standing.account_id, admin=True) }}">
<td>{{ loop.index }}</td> <td class="border-right text-center" data-checkbox>
<div class="form-check">
<input type="checkbox" class="form-check-input" value="{{ standing.account_id }}" data-account-id="{{ standing.account_id }}">&nbsp;
</div>
</td>
<td class="text-center" width="10%">{{ loop.index }}</td>
<td> <td>
<a href="{{ generate_account_url(standing.account_id, admin=True) }}"> <a href="{{ generate_account_url(standing.account_id, admin=True) }}">
{{ standing.name }} {{ standing.name }}
@@ -40,15 +61,11 @@
</td> </td>
<td>{{ standing.score }}</td> <td>{{ standing.score }}</td>
<td> <td>
{% if standing.hidden %} {% if standing.hidden %}
<button class="btn-sm btn-danger cursor-pointer scoreboard-toggle" type="submit" <span class="badge badge-danger">hidden</span>
data-account-id="{{ standing.account_id }}" data-state="hidden">Hidden {% else %}
</button> <span class="badge badge-success">visible</span>
{% else %} {% endif %}
<button class="btn-sm btn-success cursor-pointer scoreboard-toggle" type="submit"
data-account-id="{{ standing.account_id }}" data-state="visible">Visible
</button>
{% endif %}
</td> </td>
</tr> </tr>
{% endfor %} {% endfor %}

View File

@@ -14,21 +14,41 @@
<div class="container"> <div class="container">
<div class="row"> <div class="row">
<div class="col-md-12"> <div class="col-md-12">
<table id="teamsboard" class=" table table-striped"> <div class="float-right pb-3">
<div class="btn-group" role="group">
<button type="button" class="btn btn-outline-danger" id="submission-delete-button">
<i class="btn-fa fas fa-trash-alt"></i>
</button>
</div>
</div>
</div>
</div>
<div class="row">
<div class="col-md-12">
<table id="teamsboard" class="table table-striped border">
<thead> <thead>
<tr> <tr>
<td class="text-center"><b>ID</b></td> <th class="border-right" data-checkbox>
<td><b>Team</b></td> <div class="form-check text-center">
<td><b>Challenge</b></td> <input type="checkbox" class="form-check-input" data-checkbox-all>&nbsp;
<td><b>Type</b></td> </div>
<td><b>Submission</b></td> </th>
<td class="text-center"><b>Date</b></td> <th class="text-center sort-col"><b>ID</b></th>
<td class="text-center"><b>Delete</b></td> <th class="sort-col"><b>Team</b></th>
<th class="sort-col"><b>Challenge</b></th>
<th class="sort-col"><b>Type</b></th>
<th class="sort-col"><b>Submission</b></th>
<th class="text-center sort-col"><b>Date</b></th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
{% for sub in submissions %} {% for sub in submissions %}
<tr> <tr>
<td class="border-right" data-checkbox>
<div class="form-check text-center">
<input type="checkbox" class="form-check-input" value="{{ sub.id }}" data-submission-id="{{ sub.id }}">&nbsp;
</div>
</td>
<td class="text-center" id="{{ sub.id }}"> <td class="text-center" id="{{ sub.id }}">
{{ sub.id }} {{ sub.id }}
</td> </td>
@@ -36,7 +56,9 @@
<a href="{{ generate_account_url(sub.account_id, admin=True) }}">{{ sub.team_name }}</a> <a href="{{ generate_account_url(sub.account_id, admin=True) }}">{{ sub.team_name }}</a>
</td> </td>
<td class="chal" id="{{ sub.challenge_id }}"> <td class="chal" id="{{ sub.challenge_id }}">
{{ sub.challenge_name }} <a href="{{ url_for('admin.challenges_detail', challenge_id=sub.challenge_id) }}">
{{ sub.challenge_name }}
</a>
</td> </td>
<td> <td>
{{ sub.type }} {{ sub.type }}
@@ -47,12 +69,6 @@
<td class="text-center solve-time"> <td class="text-center solve-time">
<span data-time="{{ sub.date | isoformat }}"></span> <span data-time="{{ sub.date | isoformat }}"></span>
</td> </td>
<td class="text-center">
<span class="delete-correct-submission" data-toggle="tooltip"
data-placement="top" title="Delete submission #{{ sub.id }}" data-submission-id="{{ sub.id }}">
<i class="fas fa-times"></i>
</span>
</td>
</tr> </tr>
{% endfor %} {% endfor %}
</tbody> </tbody>

View File

@@ -24,48 +24,71 @@
{% endif %} {% endif %}
<form method="GET" class="form-inline"> <form method="GET" class="form-inline">
<div class="form-group col-md-2"> <div class="form-group col-md-2">
<label for="sel1" class="sr-only" >Search Field</label> <label for="sel1" class="sr-only" >Search Field</label>
<select class="form-control custom-select w-100" id="sel1" name="field"> <select class="form-control custom-select w-100" id="sel1" name="field">
<option value="name" {% if field == 'name' %}selected{% endif %}>Name</option> <option value="name" {% if field == 'name' %}selected{% endif %}>Name</option>
<option value="id" {% if field == 'id' %}selected{% endif %}>ID</option> <option value="id" {% if field == 'id' %}selected{% endif %}>ID</option>
<option value="email" {% if field == 'email' %}selected{% endif %}>Email</option> <option value="email" {% if field == 'email' %}selected{% endif %}>Email</option>
<option value="affiliation" {% if field == 'affiliation' %}selected{% endif %}>Affiliation</option> <option value="affiliation" {% if field == 'affiliation' %}selected{% endif %}>Affiliation</option>
</select> </select>
</div> </div>
<div class="form-group col-md-8"> <div class="form-group col-md-8">
<label for="team-name-search" class="sr-only">Parameter</label> <label for="team-name-search" class="sr-only">Parameter</label>
<input type="text" class="form-control w-100" id="team-name-search" name="q" placeholder="Search for matching team names" {% if q %}value="{{q}}"{% endif %}> <input type="text" class="form-control w-100" id="team-name-search" name="q" placeholder="Search for matching team names" {% if q %}value="{{q}}"{% endif %}>
</div> </div>
<div class="form-group col-md-2"> <div class="form-group col-md-2">
<label for="team-name-search" class="sr-only">Search</label> <label for="team-name-search" class="sr-only">Search</label>
<button type="submit" class="btn btn-primary w-100"><i class="fas fa-search" aria-hidden="true"></i></button> <button type="submit" class="btn btn-primary w-100"><i class="fas fa-search" aria-hidden="true"></i></button>
</div> </div>
</form> </form>
</div>
</div>
<br> <hr>
<table id="teamsboard" class="table table-striped"> <div class="row">
<div class="col-md-12">
<div class="float-right pb-3">
<div class="btn-group" role="group">
<button type="button" class="btn btn-outline-secondary" data-toggle="tooltip" title="Edit Teams" id="teams-edit-button">
<i class="btn-fa fas fa-pencil-alt"></i>
</button>
<button type="button" class="btn btn-outline-danger" data-toggle="tooltip" title="Delete Teams" id="teams-delete-button">
<i class="btn-fa fas fa-trash-alt"></i>
</button>
</div>
</div>
</div>
</div>
<div class="row">
<div class="col-md-12">
<table id="teamsboard" class="table table-striped border">
<thead> <thead>
<tr> <tr>
<td width="10px" class="text-center"><b>ID</b> <th class="border-right" data-checkbox>
</td> <div class="form-check text-center">
<td class="text-left"><b>Team</b> <input type="checkbox" class="form-check-input" data-checkbox-all>&nbsp;
</td> </div>
<td class="text-center"><b>Website</b> </th>
</td> <th class="sort-col text-center"><b>ID</b></th>
<td class="text-center"><b>Country</b> <th class="sort-col text-left"><b>Team</b></th>
</td> <th class="sort-col text-center"><b>Website</b></th>
<td class="text-center"><b>Hidden</b> <th class="sort-col text-center"><b>Country</b></th>
</td> <th class="sort-col text-center"><b>Hidden</b></th>
<td class="text-center"><b>Banned</b> <th class="sort-col text-center"><b>Banned</b></th>
</td>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
{% for team in teams %} {% for team in teams %}
<tr name="{{ team.id }}" data-href="{{ url_for('admin.teams_detail', team_id=team.id) }}"> <tr name="{{ team.id }}" data-href="{{ url_for('admin.teams_detail', team_id=team.id) }}">
<td class="team-id" value="{{ team.id }}">{{ team.id }}</td> <td class="border-right" data-checkbox>
<div class="form-check text-center">
<input type="checkbox" class="form-check-input" value="{{ team.id }}" data-team-id="{{ team.id }}">&nbsp;
</div>
</td>
<td class="team-id text-center value="{{ team.id }}">{{ team.id }}</td>
<td class="team-name" value="{{ team.name }}"> <td class="team-name" value="{{ team.name }}">
<a href="{{ url_for('admin.teams_detail', team_id=team.id) }}"> <a href="{{ url_for('admin.teams_detail', team_id=team.id) }}">
{{ team.name | truncate(32) }} {{ team.name | truncate(32) }}
@@ -137,3 +160,7 @@
{% block scripts %} {% block scripts %}
{% endblock %} {% endblock %}
{% block entrypoint %}
<script defer src="{{ url_for('views.themes', theme='admin', path='js/pages/teams.js') }}"></script>
{% endblock %}

View File

@@ -17,6 +17,7 @@
</div> </div>
<div class="container"> <div class="container">
<div class="row"> <div class="row">
<div class="col-md-12"> <div class="col-md-12">
{% if q and field %} {% if q and field %}
@@ -24,55 +25,74 @@
{% endif %} {% endif %}
<form method="GET" class="form-inline"> <form method="GET" class="form-inline">
<div class="form-group col-md-2"> <div class="form-group col-md-2">
<label for="sel1" class="sr-only" >Search Field</label> <label for="sel1" class="sr-only" >Search Field</label>
<select class="form-control custom-select w-100" id="sel1" name="field"> <select class="form-control custom-select w-100" id="sel1" name="field">
<option value="name" {% if field == 'name' %}selected{% endif %}>Name</option> <option value="name" {% if field == 'name' %}selected{% endif %}>Name</option>
<option value="id" {% if field == 'id' %}selected{% endif %}>ID</option> <option value="id" {% if field == 'id' %}selected{% endif %}>ID</option>
<option value="email" {% if field == 'email' %}selected{% endif %}>Email</option> <option value="email" {% if field == 'email' %}selected{% endif %}>Email</option>
<option value="affiliation" {% if field == 'affiliation' %}selected{% endif %}>Affiliation</option> <option value="affiliation" {% if field == 'affiliation' %}selected{% endif %}>Affiliation</option>
<option value="ip" {% if field == 'ip' %}selected{% endif %}>IP Address</option> <option value="ip" {% if field == 'ip' %}selected{% endif %}>IP Address</option>
</select> </select>
</div> </div>
<div class="form-group col-md-8"> <div class="form-group col-md-8">
<label for="team-name-search" class="sr-only">Parameter</label> <label for="team-name-search" class="sr-only">Parameter</label>
<input type="text" class="form-control w-100" id="team-name-search" name="q" placeholder="Search for matching user" {% if q %}value="{{q}}"{% endif %}> <input type="text" class="form-control w-100" id="team-name-search" name="q" placeholder="Search for matching user" {% if q %}value="{{q}}"{% endif %}>
</div> </div>
<div class="form-group col-md-2"> <div class="form-group col-md-2">
<label for="team-name-search" class="sr-only">Search</label> <label for="team-name-search" class="sr-only">Search</label>
<button type="submit" class="btn btn-primary w-100"><i class="fas fa-search" aria-hidden="true"></i></button> <button type="submit" class="btn btn-primary w-100"><i class="fas fa-search" aria-hidden="true"></i></button>
</div> </div>
</form> </form>
</div>
</div>
<br> <hr>
<table id="teamsboard" class="table table-striped"> <div class="row">
<div class="col-md-12">
<div class="float-right pb-3">
<div class="btn-group" role="group">
<button type="button" class="btn btn-outline-secondary" data-toggle="tooltip" title="Edit Users" id="users-edit-button">
<i class="btn-fa fas fa-pencil-alt"></i>
</button>
<button type="button" class="btn btn-outline-danger" data-toggle="tooltip" title="Delete Users" id="users-delete-button">
<i class="btn-fa fas fa-trash-alt"></i>
</button>
</div>
</div>
</div>
</div>
<div class="row">
<div class="col-md-12">
<table id="teamsboard" class="table table-striped border">
<thead> <thead>
<tr> <tr>
<td width="10px" class="text-center"><b>ID</b> <th class="border-right" data-checkbox>
</td> <div class="form-check text-center">
<td class="text-center"><b>User</b> <input type="checkbox" class="form-check-input" data-checkbox-all>&nbsp;
</td> </div>
<td class="d-md-table-cell d-lg-table-cell text-center"><b>Email</b> </th>
</td> <th class="sort-col text-center"><b>ID</b></td>
<td class=""><b>Website</b> <th class="sort-col text-center"><b>User</b></th>
</td> <th class="d-md-table-cell d-lg-table-cell sort-col text-center"><b>Email</b></th>
<td class=""><b>Country</b> <th class="sort-col"><b>Website</b></th>
</td> <th class="sort-col"><b>Country</b></th>
<td class="text-center"><b>Admin</b> <th class="sort-col text-center"><b>Admin</b></th>
</td> <th class="sort-col text-center"><b>Verified</b></th>
<td class="text-center"><b>Verified</b> <th class="sort-col text-center"><b>Hidden</b></th>
</td> <th class="sort-col text-center"><b>Banned</b></th>
<td class="text-center"><b>Hidden</b>
</td>
<td class="text-center"><b>Banned</b>
</td>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
{% for user in users %} {% for user in users %}
<tr name="{{ user.id }}" data-href="{{ url_for('admin.users_detail', user_id=user.id) }}"> <tr name="{{ user.id }}" data-href="{{ url_for('admin.users_detail', user_id=user.id) }}">
<td class="team-id" value="{{ user.id }}">{{ user.id }}</td> <td class="border-right" data-checkbox>
<div class="form-check text-center">
<input type="checkbox" class="form-check-input" value="{{ user.id }}" data-user-id="{{ user.id }}">&nbsp;
</div>
</td>
<td class="team-id text-center" value="{{ user.id }}">{{ user.id }}</td>
<td class="team-name" value="{{ user.name }}"> <td class="team-name" value="{{ user.name }}">
<a href="{{ url_for('admin.users_detail', user_id=user.id) }}"> <a href="{{ url_for('admin.users_detail', user_id=user.id) }}">
{{ user.name | truncate(32) }} {{ user.name | truncate(32) }}

View File

@@ -256,3 +256,38 @@ export function copyToClipboard(event, selector) {
$(event.target).tooltip("hide"); $(event.target).tooltip("hide");
}, 1500); }, 1500);
} }
export function makeSortableTables() {
$("th.sort-col").append(` <i class="fas fa-sort"></i>`);
$("th.sort-col").click(function() {
var table = $(this)
.parents("table")
.eq(0);
var rows = table
.find("tr:gt(0)")
.toArray()
.sort(comparer($(this).index()));
this.asc = !this.asc;
if (!this.asc) {
rows = rows.reverse();
}
for (var i = 0; i < rows.length; i++) {
table.append(rows[i]);
}
});
function comparer(index) {
return function(a, b) {
var valA = getCellValue(a, index),
valB = getCellValue(b, index);
return $.isNumeric(valA) && $.isNumeric(valB)
? valA - valB
: valA.toString().localeCompare(valB);
};
}
function getCellValue(row, index) {
return $(row)
.children("td")
.eq(index)
.text();
}
}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -36,6 +36,7 @@ const roots = {
'js': { 'js': {
'pages/main': 'assets/js/pages/main.js', 'pages/main': 'assets/js/pages/main.js',
'pages/challenge': 'assets/js/pages/challenge.js', 'pages/challenge': 'assets/js/pages/challenge.js',
'pages/challenges': 'assets/js/pages/challenges.js',
'pages/configs': 'assets/js/pages/configs.js', 'pages/configs': 'assets/js/pages/configs.js',
'pages/notifications': 'assets/js/pages/notifications.js', 'pages/notifications': 'assets/js/pages/notifications.js',
'pages/editor': 'assets/js/pages/editor.js', 'pages/editor': 'assets/js/pages/editor.js',