mirror of https://github.com/JohnHammond/CTFd.git
Getting awards feature ready
parent
b46b0c560d
commit
f4a2f165a2
|
@ -1,6 +1,6 @@
|
|||
from flask import render_template, request, redirect, abort, jsonify, url_for, session, Blueprint
|
||||
from CTFd.utils import sha512, is_safe_url, authed, admins_only, is_admin, unix_time, unix_time_millis, get_config, set_config, sendmail, rmdir
|
||||
from CTFd.models import db, Teams, Solves, Challenges, WrongKeys, Keys, Tags, Files, Tracking, Pages, Config, DatabaseError
|
||||
from CTFd.models import db, Teams, Solves, Awards, Challenges, WrongKeys, Keys, Tags, Files, Tracking, Pages, Config, DatabaseError
|
||||
from itsdangerous import TimedSerializer, BadTimeSignature
|
||||
from sqlalchemy.sql import and_, or_, not_
|
||||
from werkzeug.utils import secure_filename
|
||||
|
@ -380,11 +380,12 @@ def admin_team(teamid):
|
|||
solve_ids = [s.chalid for s in solves]
|
||||
missing = Challenges.query.filter( not_(Challenges.id.in_(solve_ids) ) ).all()
|
||||
addrs = Tracking.query.filter_by(team=teamid).order_by(Tracking.date.desc()).group_by(Tracking.ip).all()
|
||||
wrong_keys = WrongKeys.query.filter_by(teamid=teamid).order_by(WrongKeys.date.desc()).all()
|
||||
wrong_keys = WrongKeys.query.filter_by(teamid=teamid).order_by(WrongKeys.date.asc()).all()
|
||||
awards = Awards.query.filter_by(teamid=teamid).order_by(Awards.date.asc()).all()
|
||||
score = user.score()
|
||||
place = user.place()
|
||||
return render_template('admin/team.html', solves=solves, team=user, addrs=addrs, score=score, missing=missing,
|
||||
place=place, wrong_keys=wrong_keys)
|
||||
place=place, wrong_keys=wrong_keys, awards=awards)
|
||||
elif request.method == 'POST':
|
||||
admin_user = request.form.get('admin', None)
|
||||
if admin_user:
|
||||
|
@ -498,6 +499,57 @@ def admin_scoreboard():
|
|||
return render_template('admin/scoreboard.html', teams=teams)
|
||||
|
||||
|
||||
@admin.route('/admin/teams/<teamid>/awards', methods=['GET'])
|
||||
@admins_only
|
||||
def admin_awards(teamid):
|
||||
awards = Awards.query.filter_by(teamid=teamid).all()
|
||||
|
||||
awards_list = []
|
||||
for award in awards:
|
||||
awards_list.append({
|
||||
'id':award.id,
|
||||
'name':award.name,
|
||||
'description':award.description,
|
||||
'date':award.date,
|
||||
'value':award.value,
|
||||
'category':award.category,
|
||||
'icon':award.icon
|
||||
})
|
||||
json_data = {'awards':awards_list}
|
||||
return jsonify(json_data)
|
||||
|
||||
|
||||
@admin.route('/admin/awards/add', methods=['POST'])
|
||||
@admins_only
|
||||
def create_award():
|
||||
try:
|
||||
teamid = request.form['teamid']
|
||||
name = request.form.get('name', 'Award')
|
||||
value = request.form.get('value', 0)
|
||||
award = Awards(teamid, name, value)
|
||||
award.description = request.form.get('description')
|
||||
award.category = request.form.get('category')
|
||||
db.session.add(award)
|
||||
db.session.commit()
|
||||
return "1"
|
||||
except Exception as e:
|
||||
print e
|
||||
return "0"
|
||||
|
||||
|
||||
@admin.route('/admin/awards/<award_id>/delete', methods=['POST'])
|
||||
@admins_only
|
||||
def delete_award(award_id):
|
||||
try:
|
||||
award = Awards.query.filter_by(id=award_id).first()
|
||||
db.session.delete(award)
|
||||
db.session.commit()
|
||||
return '1'
|
||||
except Exception as e:
|
||||
print e
|
||||
return "0"
|
||||
|
||||
|
||||
@admin.route('/admin/scores')
|
||||
@admins_only
|
||||
def admin_scores():
|
||||
|
|
|
@ -12,6 +12,7 @@ with open('.ctfd_secret_key', 'a+') as secret:
|
|||
##### SERVER SETTINGS #####
|
||||
SECRET_KEY = key
|
||||
SQLALCHEMY_DATABASE_URI = 'sqlite:///ctfd.db'
|
||||
SQLALCHEMY_TRACK_MODIFICATIONS = False
|
||||
SESSION_TYPE = "filesystem"
|
||||
SESSION_FILE_DIR = "/tmp/flask_session"
|
||||
SESSION_COOKIE_HTTPONLY = True
|
||||
|
|
|
@ -142,8 +142,10 @@ class Teams(db.Model):
|
|||
def score(self):
|
||||
score = db.func.sum(Challenges.value).label('score')
|
||||
team = db.session.query(Solves.teamid, score).join(Teams).join(Challenges).filter(Teams.banned == None, Teams.id==self.id).group_by(Solves.teamid).first()
|
||||
award_score = db.func.sum(Awards.value).label('award_score')
|
||||
award = db.session.query(award_score).filter_by(teamid=self.id).first()
|
||||
if team:
|
||||
return team.score
|
||||
return int(team.score or 0) + int(award.award_score or 0)
|
||||
else:
|
||||
return 0
|
||||
|
||||
|
|
|
@ -1,31 +1,3 @@
|
|||
|
||||
//http://stackoverflow.com/a/2648463 - wizardry!
|
||||
String.prototype.format = String.prototype.f = function() {
|
||||
var s = this,
|
||||
i = arguments.length;
|
||||
|
||||
while (i--) {
|
||||
s = s.replace(new RegExp('\\{' + i + '\\}', 'gm'), arguments[i]);
|
||||
}
|
||||
return s;
|
||||
};
|
||||
|
||||
function htmlentities(string) {
|
||||
return $('<div/>').text(string).html();
|
||||
}
|
||||
|
||||
//http://stackoverflow.com/a/7616484
|
||||
String.prototype.hashCode = function() {
|
||||
var hash = 0, i, chr, len;
|
||||
if (this.length == 0) return hash;
|
||||
for (i = 0, len = this.length; i < len; i++) {
|
||||
chr = this.charCodeAt(i);
|
||||
hash = ((hash << 5) - hash) + chr;
|
||||
hash |= 0; // Convert to 32bit integer
|
||||
}
|
||||
return hash;
|
||||
};
|
||||
|
||||
var challenges;
|
||||
|
||||
function loadchal(id) {
|
||||
|
|
|
@ -1,28 +1,3 @@
|
|||
//http://stackoverflow.com/a/2648463 - wizardry!
|
||||
String.prototype.format = String.prototype.f = function() {
|
||||
var s = this,
|
||||
i = arguments.length;
|
||||
|
||||
while (i--) {
|
||||
s = s.replace(new RegExp('\\{' + i + '\\}', 'gm'), arguments[i]);
|
||||
}
|
||||
return s;
|
||||
};
|
||||
|
||||
function colorhash (x) {
|
||||
color = ""
|
||||
for (var i = 20; i <= 60; i+=20){
|
||||
x += i
|
||||
x *= i
|
||||
color += x.toString(16)
|
||||
};
|
||||
return "#" + color.substring(0, 6)
|
||||
}
|
||||
|
||||
function htmlentities(string) {
|
||||
return $('<div/>').text(string).html();
|
||||
}
|
||||
|
||||
function updatescores () {
|
||||
$.get('/scores', function( data ) {
|
||||
teams = $.parseJSON(JSON.stringify(data));
|
||||
|
|
|
@ -0,0 +1,55 @@
|
|||
//http://stackoverflow.com/a/1186309
|
||||
$.fn.serializeObject = function()
|
||||
{
|
||||
var o = {};
|
||||
var a = this.serializeArray();
|
||||
$.each(a, function() {
|
||||
if (o[this.name] !== undefined) {
|
||||
if (!o[this.name].push) {
|
||||
o[this.name] = [o[this.name]];
|
||||
}
|
||||
o[this.name].push(this.value || '');
|
||||
} else {
|
||||
o[this.name] = this.value || '';
|
||||
}
|
||||
});
|
||||
return o;
|
||||
};
|
||||
|
||||
|
||||
//http://stackoverflow.com/a/2648463 - wizardry!
|
||||
String.prototype.format = String.prototype.f = function() {
|
||||
var s = this,
|
||||
i = arguments.length;
|
||||
|
||||
while (i--) {
|
||||
s = s.replace(new RegExp('\\{' + i + '\\}', 'gm'), arguments[i]);
|
||||
}
|
||||
return s;
|
||||
};
|
||||
|
||||
//http://stackoverflow.com/a/7616484
|
||||
String.prototype.hashCode = function() {
|
||||
var hash = 0, i, chr, len;
|
||||
if (this.length == 0) return hash;
|
||||
for (i = 0, len = this.length; i < len; i++) {
|
||||
chr = this.charCodeAt(i);
|
||||
hash = ((hash << 5) - hash) + chr;
|
||||
hash |= 0; // Convert to 32bit integer
|
||||
}
|
||||
return hash;
|
||||
};
|
||||
|
||||
function colorhash (x) {
|
||||
color = ""
|
||||
for (var i = 20; i <= 60; i+=20){
|
||||
x += i
|
||||
x *= i
|
||||
color += x.toString(16)
|
||||
};
|
||||
return "#" + color.substring(0, 6)
|
||||
}
|
||||
|
||||
function htmlentities(string) {
|
||||
return $('<div/>').text(string).html();
|
||||
}
|
|
@ -27,7 +27,7 @@
|
|||
<span class="icon-bar"></span>
|
||||
<span class="icon-bar"></span>
|
||||
</button>
|
||||
<a href="/" class="navbar-brand">CTF</a>
|
||||
<a href="/" class="navbar-brand">CTFd</a>
|
||||
</div>
|
||||
<div class="navbar-collapse collapse" aria-expanded="false" style="height: 0px">
|
||||
<ul class="nav navbar-nav navbar-nav-right">
|
||||
|
|
|
@ -297,6 +297,7 @@
|
|||
{% endblock %}
|
||||
|
||||
{% block scripts %}
|
||||
<script src="/static/js/utils.js"></script>
|
||||
<script src="/static/admin/js/multi-modal.js"></script>
|
||||
<script src="/static/admin/js/chalboard.js"></script>
|
||||
{% endblock %}
|
||||
|
|
|
@ -76,6 +76,7 @@
|
|||
{% endblock %}
|
||||
|
||||
{% block scripts %}
|
||||
<script src="/static/js/utils.js"></script>
|
||||
<script src="/static/admin/js/team.js"></script>
|
||||
<script>
|
||||
$('#delete-solve').click(function(e){
|
||||
|
|
|
@ -13,7 +13,48 @@
|
|||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="row">
|
||||
|
||||
<div class="modal fade" id="create-award-modal" tabindex="-1" role="dialog" aria-labelledby="create-award-label">
|
||||
<div class="modal-dialog" role="document">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">×</span></button>
|
||||
<h4 class="modal-title" id="create-award-label">Create Award</h4>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<form id="award-create-form" method="POST" action="/admin/awards/add">
|
||||
<div class="form-group">
|
||||
<label for="award-name-input">Name</label>
|
||||
<input type="text" class="form-control" id="award-name-input" name="name" placeholder="Enter award name">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="award-value-input">Value</label>
|
||||
<input type="number" class="form-control" id="award-value-input" name="value" placeholder="Enter award value">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="award-category-input">Category</label>
|
||||
<input type="text" class="form-control" id="award-category-input" name="category" placeholder="Enter award category">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="award-description-input" class="control-label">Description</label>
|
||||
<textarea id="award-description-input" class="form-control" name="description" rows="10" placeholder="Description of what the award is for"></textarea>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="award-icon-input">Award Icon</label>
|
||||
<input type="file" id="award-icon-input" name="icon">
|
||||
</div>
|
||||
<input type="hidden" value="{{ team.id }}" name="teamid" id="teamid">
|
||||
<input type="hidden" value="{{ nonce }}" name="nonce" id="nonce">
|
||||
<div class="modal-footer">
|
||||
<button type="submit" id="award-create-button" class="btn btn-primary">Create</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row container-fluid">
|
||||
<div id="confirm" class="modal fade" tabindex="-1">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content">
|
||||
|
@ -70,6 +111,40 @@
|
|||
</tbody>
|
||||
</table>
|
||||
|
||||
|
||||
<table class="table table-striped">
|
||||
<h3>Awards</h3>
|
||||
<thead>
|
||||
<tr>
|
||||
<td class="text-center"><b>Name</b></td>
|
||||
<td class="text-center"><b>Description</b></td>
|
||||
<td class="text-center"><b>Date</b></td>
|
||||
<td class="text-center"><b>Value</b></td>
|
||||
<td class="text-center"><b>Category</b></td>
|
||||
<td class="text-center"><b>Icon</b></td>
|
||||
<td class="text-center"><b>Delete</b></td>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="awards-body">
|
||||
{% for award in awards %}
|
||||
<tr class="award-row">
|
||||
<td class="text-center chal" id="{{ award.id }}">{{ award.name }}</td>
|
||||
<td class="text-center">{{ award.description }}</td>
|
||||
<td class="text-center solve-time"><script>document.write( moment({{ award.date|unix_time_millis }}).local().format('MMMM Do, h:mm:ss A'))</script></td>
|
||||
<td class="text-center">{{ award.value }}</td>
|
||||
<td class="text-center">{{ award.category }}</td>
|
||||
<td class="text-center">{{ award.icon }}</td>
|
||||
|
||||
<td class="text-center"><i class="fa fa-times"></i></td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<div class="row container-fluid">
|
||||
<button type="button" data-toggle="modal" data-target="#create-award-modal" class="btn btn-primary pull-right">Create Award</button>
|
||||
</div>
|
||||
|
||||
<table class="table table-striped">
|
||||
<h3>Solves</h3>
|
||||
<thead>
|
||||
|
@ -96,7 +171,6 @@
|
|||
</tbody>
|
||||
</table>
|
||||
|
||||
|
||||
<table class="table table-striped">
|
||||
<h3>Missing</h3>
|
||||
<thead>
|
||||
|
@ -149,6 +223,7 @@
|
|||
{% block scripts %}
|
||||
<script src="/static/js/vendor/moment.min.js"></script>
|
||||
<script src="/static/js/vendor/plotly.min.js"></script>
|
||||
<script src="/static/js/utils.js"></script>
|
||||
<script src="/static/admin/js/team.js"></script>
|
||||
<script>
|
||||
$('#delete-solve').click(function (e) {
|
||||
|
@ -196,7 +271,7 @@
|
|||
description = description.html()
|
||||
|
||||
var action = '/admin/solves/' + team + '/' + chal + '/delete';
|
||||
} else {
|
||||
} else if (type == 'chal-wrong') {
|
||||
var title = 'Delete Wrong Key';
|
||||
var description = "<span>Are you sure you want to delete " +
|
||||
"<strong>incorrect</strong> " +
|
||||
|
@ -211,6 +286,11 @@
|
|||
description = description.html()
|
||||
|
||||
var action = '/admin/wrong_keys/' + team + '/' + chal + '/delete';
|
||||
} else if (type == 'award-row') {
|
||||
var title = 'Delete Award';
|
||||
var description = "<span>Are you sure you want to delete the " +
|
||||
"<strong>{0}</strong> award?</span>".format(chal_name);
|
||||
var action = '/admin/awards/{0}/delete'.format(chal);
|
||||
}
|
||||
|
||||
var msg = {
|
||||
|
@ -253,5 +333,24 @@
|
|||
|
||||
load_confirm_modal(msg);
|
||||
});
|
||||
|
||||
$('#award-create-form').submit(function(e){
|
||||
$.post($(this).attr('action'), $(this).serialize(), function(res){
|
||||
if (res == '1'){
|
||||
var award = $('#award-create-form').serializeObject();
|
||||
var award_text = '<td class="text-center">{0}</td>'.format(award.name) +
|
||||
'<td class="text-center">{0}</td>'.format(award.description) +
|
||||
'<td class="text-center solve-time">{0}</td>'.format(moment().local().format('MMMM Do, h:mm:ss A')) +
|
||||
'<td class="text-center">{0}</td>'.format(award.value) +
|
||||
'<td class="text-center">{0}</td>'.format(award.category) +
|
||||
'<td class="text-center">{0}</td>'.format('None') +
|
||||
'<td class="text-center"><i class="fa fa-times"></i></td>'
|
||||
$('#awards-body').append(award_text);
|
||||
$('#create-award-modal').modal('hide');
|
||||
}
|
||||
})
|
||||
e.preventDefault();
|
||||
});
|
||||
|
||||
</script>
|
||||
{% endblock %}
|
||||
|
|
|
@ -83,6 +83,7 @@
|
|||
{% endblock %}
|
||||
|
||||
{% block scripts %}
|
||||
<script src="/static/js/utils.js"></script>
|
||||
<script src="/static/admin/js/team.js"></script>
|
||||
<script>
|
||||
$('#delete-solve').click(function (e) {
|
||||
|
|
|
@ -154,6 +154,7 @@
|
|||
{% endblock %}
|
||||
|
||||
{% block scripts %}
|
||||
<script src="/static/js/utils.js"></script>
|
||||
<script src="/static/js/chalboard.js"></script>
|
||||
<script src="/static/js/style.js"></script>
|
||||
{% endblock %}
|
||||
|
|
|
@ -32,5 +32,6 @@
|
|||
|
||||
{% block scripts %}
|
||||
<script src="/static/js/vendor/plotly.min.js"></script>
|
||||
<script src="/static/js/utils.js"></script>
|
||||
<script src="/static/js/scoreboard.js"></script>
|
||||
{% endblock %}
|
||||
|
|
|
@ -61,5 +61,6 @@
|
|||
|
||||
{% block scripts %}
|
||||
<script src="/static/js/vendor/plotly.min.js"></script>
|
||||
<script src="/static/js/utils.js"></script>
|
||||
<script src="/static/js/team.js"></script>
|
||||
{% endblock %}
|
||||
|
|
Loading…
Reference in New Issue