mirror of https://github.com/JohnHammond/CTFd.git
Add bulk table actions for team page
parent
735faf0e23
commit
5a9d79f2bb
|
@ -80,6 +80,74 @@ function updateTeam(event) {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function deleteSelectedSubmissions(event, target) {
|
||||||
|
let submissions;
|
||||||
|
let type;
|
||||||
|
let title;
|
||||||
|
switch(target){
|
||||||
|
case "solves":
|
||||||
|
submissions = $("input[data-submission-type=correct]:checked");
|
||||||
|
type = "solve";
|
||||||
|
title = "Solves"
|
||||||
|
break;
|
||||||
|
case "fails":
|
||||||
|
submissions = $("input[data-submission-type=incorrect]:checked");
|
||||||
|
type = "fail";
|
||||||
|
title = "Fails"
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
let submissionIDs = submissions.map(function() {
|
||||||
|
return $(this).data("submission-id");
|
||||||
|
});
|
||||||
|
let target_string = submissionIDs.length === 1 ? type : (type + "s");
|
||||||
|
|
||||||
|
ezQuery({
|
||||||
|
title: `Delete ${title}`,
|
||||||
|
body: `Are you sure you want to delete ${submissionIDs.length} ${target_string}?`,
|
||||||
|
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();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function deleteSelectedAwards(event) {
|
||||||
|
let awardIDs = $("input[data-award-id]:checked").map(function() {
|
||||||
|
return $(this).data("award-id");
|
||||||
|
});
|
||||||
|
let target = awardIDs.length === 1 ? "award" : "awards";
|
||||||
|
|
||||||
|
ezQuery({
|
||||||
|
title: `Delete Awards`,
|
||||||
|
body: `Are you sure you want to delete ${awardIDs.length} ${target}?`,
|
||||||
|
success: function() {
|
||||||
|
const reqs = [];
|
||||||
|
for (var awardID of awardIDs) {
|
||||||
|
let req = CTFd.fetch("/api/v1/awards/" + awardID, {
|
||||||
|
method: "DELETE",
|
||||||
|
credentials: "same-origin",
|
||||||
|
headers: {
|
||||||
|
Accept: "application/json",
|
||||||
|
"Content-Type": "application/json"
|
||||||
|
}
|
||||||
|
});
|
||||||
|
reqs.push(req);
|
||||||
|
}
|
||||||
|
Promise.all(reqs).then(responses => {
|
||||||
|
window.location.reload();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
const api_funcs = {
|
const api_funcs = {
|
||||||
team: [
|
team: [
|
||||||
x => CTFd.api.get_team_solves({ teamId: x }),
|
x => CTFd.api.get_team_solves({ teamId: x }),
|
||||||
|
@ -335,82 +403,16 @@ $(() => {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
$(".delete-submission").click(function(e) {
|
$("#solves-delete-button").click(function(e){
|
||||||
e.preventDefault();
|
deleteSelectedSubmissions(e, "solves")
|
||||||
const submission_id = $(this).attr("submission-id");
|
|
||||||
const submission_type = $(this).attr("submission-type");
|
|
||||||
const submission_challenge = $(this).attr("submission-challenge");
|
|
||||||
|
|
||||||
const body = "<span>Are you sure you want to delete <strong>{0}</strong> submission from <strong>{1}</strong> for <strong>{2}</strong>?</span>".format(
|
|
||||||
htmlEntities(submission_type),
|
|
||||||
htmlEntities(TEAM_NAME),
|
|
||||||
htmlEntities(submission_challenge)
|
|
||||||
);
|
|
||||||
|
|
||||||
const row = $(this)
|
|
||||||
.parent()
|
|
||||||
.parent();
|
|
||||||
|
|
||||||
ezQuery({
|
|
||||||
title: "Delete Submission",
|
|
||||||
body: body,
|
|
||||||
success: function() {
|
|
||||||
CTFd.fetch("/api/v1/submissions/" + submission_id, {
|
|
||||||
method: "DELETE",
|
|
||||||
credentials: "same-origin",
|
|
||||||
headers: {
|
|
||||||
Accept: "application/json",
|
|
||||||
"Content-Type": "application/json"
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.then(function(response) {
|
|
||||||
return response.json();
|
|
||||||
})
|
|
||||||
.then(function(response) {
|
|
||||||
if (response.success) {
|
|
||||||
row.remove();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
$(".delete-award").click(function(e) {
|
$("#fails-delete-button").click(function(e){
|
||||||
e.preventDefault();
|
deleteSelectedSubmissions(e, "fails")
|
||||||
const award_id = $(this).attr("award-id");
|
});
|
||||||
const award_name = $(this).attr("award-name");
|
|
||||||
|
|
||||||
const body = "<span>Are you sure you want to delete the <strong>{0}</strong> award from <strong>{1}</strong>?".format(
|
$("#awards-delete-button").click(function(e){
|
||||||
htmlEntities(award_name),
|
deleteSelectedAwards(e);
|
||||||
htmlEntities(TEAM_NAME)
|
|
||||||
);
|
|
||||||
|
|
||||||
const row = $(this)
|
|
||||||
.parent()
|
|
||||||
.parent();
|
|
||||||
|
|
||||||
ezQuery({
|
|
||||||
title: "Delete Award",
|
|
||||||
body: body,
|
|
||||||
success: function() {
|
|
||||||
CTFd.fetch("/api/v1/awards/" + award_id, {
|
|
||||||
method: "DELETE",
|
|
||||||
credentials: "same-origin",
|
|
||||||
headers: {
|
|
||||||
Accept: "application/json",
|
|
||||||
"Content-Type": "application/json"
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.then(function(response) {
|
|
||||||
return response.json();
|
|
||||||
})
|
|
||||||
.then(function(response) {
|
|
||||||
if (response.success) {
|
|
||||||
row.remove();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
$("#team-info-create-form").submit(createTeam);
|
$("#team-info-create-form").submit(createTeam);
|
||||||
|
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
@ -237,125 +237,173 @@
|
||||||
aria-controls="nav-awards" aria-selected="false">Awards</a>
|
aria-controls="nav-awards" aria-selected="false">Awards</a>
|
||||||
</nav>
|
</nav>
|
||||||
|
|
||||||
<div class="tab-content min-vh-25" id="nav-tabContent">
|
<div class="tab-content min-vh-50" id="nav-tabContent">
|
||||||
<div class="tab-pane fade show active" id="nav-solves" role="tabpanel" aria-labelledby="nav-solves-tab">
|
<div class="tab-pane fade show active" id="nav-solves" role="tabpanel" aria-labelledby="nav-solves-tab">
|
||||||
|
<h3 class="text-center pt-5 d-block">Solves</h3>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-md-12">
|
<div class="col-md-12">
|
||||||
<table class="table table-striped">
|
<div class="float-right pb-3">
|
||||||
<h3 class="text-center py-3 d-block">Solves</h3>
|
<div class="btn-group" role="group">
|
||||||
<thead>
|
<button type="button" class="btn btn-outline-danger" id="solves-delete-button">
|
||||||
<tr>
|
<i class="btn-fa fas fa-trash-alt"></i>
|
||||||
<td class="text-center"><b>Challenge</b></td>
|
</button>
|
||||||
<td class="text-center"><b>User</b></td>
|
</div>
|
||||||
<td class="text-center"><b>Submitted</b></td>
|
</div>
|
||||||
<td class="text-center"><b>Category</b></td>
|
</div>
|
||||||
<td class="text-center"><b>Value</b></td>
|
</div>
|
||||||
<td class="text-center"><b>Time</b></td>
|
<div class="row">
|
||||||
<td class="text-center"><b>Delete</b></td>
|
<div class="col-md-12">
|
||||||
</tr>
|
<table class="table table-striped border">
|
||||||
</thead>
|
<thead>
|
||||||
<tbody>
|
<tr>
|
||||||
{% for solve in solves %}
|
<th class="border-right" data-checkbox>
|
||||||
<tr class="chal-solve" data-href="{{ url_for("admin.challenges_detail", challenge_id=solve.challenge_id) }}">
|
<div class="form-check text-center">
|
||||||
<td class="text-center chal" id="{{ solve.challenge_id }}">
|
<input type="checkbox" class="form-check-input" data-checkbox-all>
|
||||||
<a href="{{ url_for("admin.challenges_detail", challenge_id=solve.challenge_id) }}">
|
</div>
|
||||||
{{ solve.challenge.name }}
|
</th>
|
||||||
</a>
|
<th class="sort-col text-center"><b>Challenge</b></th>
|
||||||
</td>
|
<th class="sort-col text-center"><b>User</b></th>
|
||||||
<td class="text-center">
|
<th class="sort-col text-center"><b>Submitted</b></th>
|
||||||
<a href="{{ url_for("admin.users_detail", user_id=solve.user_id) }}">
|
<th class="sort-col text-center"><b>Category</b></th>
|
||||||
{{ solve.user.name }}
|
<th class="sort-col text-center"><b>Value</b></th>
|
||||||
</a>
|
<th class="sort-col text-center"><b>Time</b></th>
|
||||||
</td>
|
</tr>
|
||||||
<td class="flag" id="{{ solve.id }}"><pre>{{ solve.provided }}</pre></td>
|
</thead>
|
||||||
<td class="text-center">{{ solve.challenge.category }}</td>
|
<tbody>
|
||||||
<td class="text-center">{{ solve.challenge.value }}</td>
|
{% for solve in solves %}
|
||||||
<td class="text-center solve-time">
|
<tr class="chal-solve" data-href="{{ url_for("admin.challenges_detail", challenge_id=solve.challenge_id) }}">
|
||||||
<span data-time="{{ solve.date | isoformat }}"></span>
|
<td class="border-right" data-checkbox>
|
||||||
</td>
|
<div class="form-check text-center">
|
||||||
<td class="text-center">
|
<input type="checkbox" class="form-check-input" value="{{ solve.id }}" data-submission-id="{{ solve.id }}"
|
||||||
<span class="delete-submission" submission-id="{{ solve.id }}"
|
data-submission-type="{{ solve.type }}"
|
||||||
submission-type="{{ solve.type }}"
|
data-submission-challenge="{{ solve.challenge.name }}">
|
||||||
submission-challenge="{{ solve.challenge.name }}" data-toggle="tooltip"
|
</div>
|
||||||
data-placement="top" title="Delete solve #{{ solve.id }}">
|
</td>
|
||||||
<i class="fas fa-times"></i>
|
<td class="text-center chal" id="{{ solve.challenge_id }}">
|
||||||
</span>
|
<a href="{{ url_for("admin.challenges_detail", challenge_id=solve.challenge_id) }}">
|
||||||
</td>
|
{{ solve.challenge.name }}
|
||||||
</tr>
|
</a>
|
||||||
{% endfor %}
|
</td>
|
||||||
</tbody>
|
<td class="text-center">
|
||||||
</table>
|
<a href="{{ url_for("admin.users_detail", user_id=solve.user_id) }}">
|
||||||
|
{{ solve.user.name }}
|
||||||
|
</a>
|
||||||
|
</td>
|
||||||
|
<td class="flag" id="{{ solve.id }}"><pre>{{ solve.provided }}</pre></td>
|
||||||
|
<td class="text-center">{{ solve.challenge.category }}</td>
|
||||||
|
<td class="text-center">{{ solve.challenge.value }}</td>
|
||||||
|
<td class="text-center solve-time">
|
||||||
|
<span data-time="{{ solve.date | isoformat }}"></span>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
{% endfor %}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="tab-pane fade" id="nav-wrong" role="tabpanel" aria-labelledby="nav-wrong-tab">
|
<div class="tab-pane fade" id="nav-wrong" role="tabpanel" aria-labelledby="nav-wrong-tab">
|
||||||
|
<h3 class="text-center pt-5 d-block">Fails</h3>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-md-12">
|
<div class="col-md-12">
|
||||||
<table class="table table-striped">
|
<div class="float-right pb-3">
|
||||||
<h3 class="text-center py-3 d-block">Fails</h3>
|
<div class="btn-group" role="group">
|
||||||
<thead>
|
<button type="button" class="btn btn-outline-danger" id="fails-delete-button">
|
||||||
<tr>
|
<i class="btn-fa fas fa-trash-alt"></i>
|
||||||
<td class="text-center"><b>Challenge</b></td>
|
</button>
|
||||||
<td class="text-center"><b>User</b></td>
|
</div>
|
||||||
<td class="text-center"><b>Submitted</b></td>
|
</div>
|
||||||
<td class="text-center"><b>Time</b></td>
|
</div>
|
||||||
<td class="text-center"><b>Delete</b></td>
|
</div>
|
||||||
</tr>
|
<div class="row">
|
||||||
</thead>
|
<div class="col-md-12">
|
||||||
<tbody>
|
<table class="table table-striped border">
|
||||||
{% for fail in fails %}
|
<thead>
|
||||||
<tr class="chal-wrong" data-href="{{ url_for("admin.challenges_detail", challenge_id=fail.challenge_id) }}">
|
<tr>
|
||||||
<td class="text-center chal" id="{{ fail.challenge_id }}">
|
<th class="border-right" data-checkbox>
|
||||||
<a href="{{ url_for("admin.challenges_detail", challenge_id=fail.challenge_id) }}">
|
<div class="form-check text-center">
|
||||||
{{ fail.challenge.name }}
|
<input type="checkbox" class="form-check-input" data-checkbox-all>
|
||||||
</a>
|
</div>
|
||||||
</td>
|
</th>
|
||||||
<td class="text-center">
|
<th class="sort-col text-center"><b>Challenge</b></th>
|
||||||
<a href="{{ url_for("admin.users_detail", user_id=fail.user_id) }}">
|
<th class="sort-col text-center"><b>User</b></th>
|
||||||
{{ fail.user.name }}
|
<th class="sort-col text-center"><b>Submitted</b></th>
|
||||||
</a>
|
<th class="sort-col text-center"><b>Time</b></th>
|
||||||
</td>
|
</tr>
|
||||||
<td class="flag" id="{{ fail.id }}"><pre>{{ fail.provided }}</pre></td>
|
</thead>
|
||||||
<td class="text-center solve-time">
|
<tbody>
|
||||||
<span data-time="{{ fail.date | isoformat }}"></span>
|
{% for fail in fails %}
|
||||||
</td>
|
<tr class="chal-wrong" data-href="{{ url_for("admin.challenges_detail", challenge_id=fail.challenge_id) }}">
|
||||||
<td class="text-center">
|
<td class="border-right" data-checkbox>
|
||||||
<span class="delete-submission" submission-id="{{ fail.id }}"
|
<div class="form-check text-center">
|
||||||
submission-type="{{ fail.type }}"
|
<input type="checkbox" class="form-check-input" value="{{ fail.id }}" data-submission-id="{{ fail.id }}"
|
||||||
submission-challenge="{{ fail.challenge.name }}" data-toggle="tooltip"
|
data-submission-type="{{ fail.type }}"
|
||||||
data-placement="top" title="Delete fail #{{ fail.id }}">
|
data-submission-challenge="{{ fail.challenge.name }}">
|
||||||
<i class="fas fa-times"></i>
|
</div>
|
||||||
</span>
|
</td>
|
||||||
</td>
|
<td class="text-center chal" id="{{ fail.challenge_id }}">
|
||||||
</tr>
|
<a href="{{ url_for("admin.challenges_detail", challenge_id=fail.challenge_id) }}">
|
||||||
{% endfor %}
|
{{ fail.challenge.name }}
|
||||||
</tbody>
|
</a>
|
||||||
</table>
|
</td>
|
||||||
|
<td class="text-center">
|
||||||
|
<a href="{{ url_for("admin.users_detail", user_id=fail.user_id) }}">
|
||||||
|
{{ fail.user.name }}
|
||||||
|
</a>
|
||||||
|
</td>
|
||||||
|
<td class="flag" id="{{ fail.id }}"><pre>{{ fail.provided }}</pre></td>
|
||||||
|
<td class="text-center solve-time">
|
||||||
|
<span data-time="{{ fail.date | isoformat }}"></span>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
{% endfor %}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="tab-pane fade" id="nav-awards" role="tabpanel" aria-labelledby="nav-awards-tab">
|
<div class="tab-pane fade" id="nav-awards" role="tabpanel" aria-labelledby="nav-awards-tab">
|
||||||
|
<h3 class="text-center pt-5 d-block">Awards</h3>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-md-12">
|
<div class="col-md-12">
|
||||||
<table class="table table-striped">
|
<div class="float-right pb-3">
|
||||||
<h3 class="text-center py-3 d-block">Awards</h3>
|
<div class="btn-group" role="group">
|
||||||
|
<button type="button" class="btn btn-outline-danger" id="awards-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 class="table table-striped border">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<td class="text-center"><b>Name</b></td>
|
<th class="border-right" data-checkbox>
|
||||||
<td class="text-center"><b>User</b></td>
|
<div class="form-check text-center">
|
||||||
<td class="text-center"><b>Description</b></td>
|
<input type="checkbox" class="form-check-input" data-checkbox-all>
|
||||||
<td class="text-center"><b>Date</b></td>
|
</div>
|
||||||
<td class="text-center"><b>Value</b></td>
|
</th>
|
||||||
<td class="text-center"><b>Category</b></td>
|
<th class="sort-col text-center"><b>Name</b></th>
|
||||||
<td class="text-center"><b>Icon</b></td>
|
<th class="sort-col text-center"><b>User</b></th>
|
||||||
<td class="text-center"><b>Delete</b></td>
|
<th class="sort-col text-center"><b>Description</b></th>
|
||||||
|
<th class="sort-col text-center"><b>Date</b></th>
|
||||||
|
<th class="sort-col text-center"><b>Value</b></th>
|
||||||
|
<th class="sort-col text-center"><b>Category</b></th>
|
||||||
|
<th class="sort-col text-center"><b>Icon</b></th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody id="awards-body">
|
<tbody id="awards-body">
|
||||||
{% for award in awards %}
|
{% for award in awards %}
|
||||||
<tr class="award-row">
|
<tr class="award-row">
|
||||||
|
<td class="border-right" data-checkbox>
|
||||||
|
<div class="form-check text-center">
|
||||||
|
<input type="checkbox" class="form-check-input" value="{{ award.id }}" data-award-id="{{ award.id }}" data-award-name="{{ award.name }}">
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
<td class="text-center chal" id="{{ award.id }}">{{ award.name }}</td>
|
<td class="text-center chal" id="{{ award.id }}">{{ award.name }}</td>
|
||||||
<td class="text-center">
|
<td class="text-center">
|
||||||
<a href="{{ url_for("admin.users_detail", user_id=award.user_id) }}">
|
<a href="{{ url_for("admin.users_detail", user_id=award.user_id) }}">
|
||||||
|
@ -368,14 +416,7 @@
|
||||||
</td>
|
</td>
|
||||||
<td class="text-center">{{ award.value }}</td>
|
<td class="text-center">{{ award.value }}</td>
|
||||||
<td class="text-center">{{ award.category }}</td>
|
<td class="text-center">{{ award.category }}</td>
|
||||||
<td class="text-center">{{ award.icon }}</td>
|
<td class="text-center"><i class="award-icon award-{{ award.icon }}"></i> {{ award.icon }}</td>
|
||||||
|
|
||||||
<td class="text-center">
|
|
||||||
<span class="delete-award" award-id="{{ award.id }}" award-name="{{ award.name }}"
|
|
||||||
data-toggle="tooltip" data-placement="top" title="Delete award #{{ award.id }}">
|
|
||||||
<i class="fas fa-times"></i>
|
|
||||||
</span>
|
|
||||||
</td>
|
|
||||||
</tr>
|
</tr>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</tbody>
|
</tbody>
|
||||||
|
|
File diff suppressed because one or more lines are too long
Loading…
Reference in New Issue