Getting awards feature ready

selenium-screenshot-testing
Kevin Chung 2016-04-22 18:19:49 -04:00
parent b46b0c560d
commit f4a2f165a2
14 changed files with 224 additions and 62 deletions

View File

@ -1,6 +1,6 @@
from flask import render_template, request, redirect, abort, jsonify, url_for, session, Blueprint 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.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 itsdangerous import TimedSerializer, BadTimeSignature
from sqlalchemy.sql import and_, or_, not_ from sqlalchemy.sql import and_, or_, not_
from werkzeug.utils import secure_filename from werkzeug.utils import secure_filename
@ -380,11 +380,12 @@ def admin_team(teamid):
solve_ids = [s.chalid for s in solves] solve_ids = [s.chalid for s in solves]
missing = Challenges.query.filter( not_(Challenges.id.in_(solve_ids) ) ).all() 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() 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() score = user.score()
place = user.place() place = user.place()
return render_template('admin/team.html', solves=solves, team=user, addrs=addrs, score=score, missing=missing, 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': elif request.method == 'POST':
admin_user = request.form.get('admin', None) admin_user = request.form.get('admin', None)
if admin_user: if admin_user:
@ -498,6 +499,57 @@ def admin_scoreboard():
return render_template('admin/scoreboard.html', teams=teams) 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') @admin.route('/admin/scores')
@admins_only @admins_only
def admin_scores(): def admin_scores():

View File

@ -12,6 +12,7 @@ with open('.ctfd_secret_key', 'a+') as secret:
##### SERVER SETTINGS ##### ##### SERVER SETTINGS #####
SECRET_KEY = key SECRET_KEY = key
SQLALCHEMY_DATABASE_URI = 'sqlite:///ctfd.db' SQLALCHEMY_DATABASE_URI = 'sqlite:///ctfd.db'
SQLALCHEMY_TRACK_MODIFICATIONS = False
SESSION_TYPE = "filesystem" SESSION_TYPE = "filesystem"
SESSION_FILE_DIR = "/tmp/flask_session" SESSION_FILE_DIR = "/tmp/flask_session"
SESSION_COOKIE_HTTPONLY = True SESSION_COOKIE_HTTPONLY = True

View File

@ -142,8 +142,10 @@ class Teams(db.Model):
def score(self): def score(self):
score = db.func.sum(Challenges.value).label('score') 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() 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: if team:
return team.score return int(team.score or 0) + int(award.award_score or 0)
else: else:
return 0 return 0

View File

@ -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; var challenges;
function loadchal(id) { function loadchal(id) {

View File

@ -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 () { function updatescores () {
$.get('/scores', function( data ) { $.get('/scores', function( data ) {
teams = $.parseJSON(JSON.stringify(data)); teams = $.parseJSON(JSON.stringify(data));

55
CTFd/static/js/utils.js Normal file
View File

@ -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();
}

View File

@ -27,7 +27,7 @@
<span class="icon-bar"></span> <span class="icon-bar"></span>
<span class="icon-bar"></span> <span class="icon-bar"></span>
</button> </button>
<a href="/" class="navbar-brand">CTF</a> <a href="/" class="navbar-brand">CTFd</a>
</div> </div>
<div class="navbar-collapse collapse" aria-expanded="false" style="height: 0px"> <div class="navbar-collapse collapse" aria-expanded="false" style="height: 0px">
<ul class="nav navbar-nav navbar-nav-right"> <ul class="nav navbar-nav navbar-nav-right">

View File

@ -297,6 +297,7 @@
{% endblock %} {% endblock %}
{% block scripts %} {% block scripts %}
<script src="/static/js/utils.js"></script>
<script src="/static/admin/js/multi-modal.js"></script> <script src="/static/admin/js/multi-modal.js"></script>
<script src="/static/admin/js/chalboard.js"></script> <script src="/static/admin/js/chalboard.js"></script>
{% endblock %} {% endblock %}

View File

@ -76,6 +76,7 @@
{% endblock %} {% endblock %}
{% block scripts %} {% block scripts %}
<script src="/static/js/utils.js"></script>
<script src="/static/admin/js/team.js"></script> <script src="/static/admin/js/team.js"></script>
<script> <script>
$('#delete-solve').click(function(e){ $('#delete-solve').click(function(e){

View File

@ -13,7 +13,48 @@
{% endblock %} {% endblock %}
{% block content %} {% 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">&times;</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 id="confirm" class="modal fade" tabindex="-1">
<div class="modal-dialog"> <div class="modal-dialog">
<div class="modal-content"> <div class="modal-content">
@ -70,6 +111,40 @@
</tbody> </tbody>
</table> </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"> <table class="table table-striped">
<h3>Solves</h3> <h3>Solves</h3>
<thead> <thead>
@ -96,7 +171,6 @@
</tbody> </tbody>
</table> </table>
<table class="table table-striped"> <table class="table table-striped">
<h3>Missing</h3> <h3>Missing</h3>
<thead> <thead>
@ -149,6 +223,7 @@
{% block scripts %} {% block scripts %}
<script src="/static/js/vendor/moment.min.js"></script> <script src="/static/js/vendor/moment.min.js"></script>
<script src="/static/js/vendor/plotly.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 src="/static/admin/js/team.js"></script>
<script> <script>
$('#delete-solve').click(function (e) { $('#delete-solve').click(function (e) {
@ -196,7 +271,7 @@
description = description.html() description = description.html()
var action = '/admin/solves/' + team + '/' + chal + '/delete'; var action = '/admin/solves/' + team + '/' + chal + '/delete';
} else { } else if (type == 'chal-wrong') {
var title = 'Delete Wrong Key'; var title = 'Delete Wrong Key';
var description = "<span>Are you sure you want to delete " + var description = "<span>Are you sure you want to delete " +
"<strong>incorrect</strong> " + "<strong>incorrect</strong> " +
@ -211,7 +286,12 @@
description = description.html() description = description.html()
var action = '/admin/wrong_keys/' + team + '/' + chal + '/delete'; 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 = { var msg = {
title : title, title : title,
@ -253,5 +333,24 @@
load_confirm_modal(msg); 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> </script>
{% endblock %} {% endblock %}

View File

@ -83,6 +83,7 @@
{% endblock %} {% endblock %}
{% block scripts %} {% block scripts %}
<script src="/static/js/utils.js"></script>
<script src="/static/admin/js/team.js"></script> <script src="/static/admin/js/team.js"></script>
<script> <script>
$('#delete-solve').click(function (e) { $('#delete-solve').click(function (e) {

View File

@ -154,6 +154,7 @@
{% endblock %} {% endblock %}
{% block scripts %} {% block scripts %}
<script src="/static/js/utils.js"></script>
<script src="/static/js/chalboard.js"></script> <script src="/static/js/chalboard.js"></script>
<script src="/static/js/style.js"></script> <script src="/static/js/style.js"></script>
{% endblock %} {% endblock %}

View File

@ -32,5 +32,6 @@
{% block scripts %} {% block scripts %}
<script src="/static/js/vendor/plotly.min.js"></script> <script src="/static/js/vendor/plotly.min.js"></script>
<script src="/static/js/utils.js"></script>
<script src="/static/js/scoreboard.js"></script> <script src="/static/js/scoreboard.js"></script>
{% endblock %} {% endblock %}

View File

@ -61,5 +61,6 @@
{% block scripts %} {% block scripts %}
<script src="/static/js/vendor/plotly.min.js"></script> <script src="/static/js/vendor/plotly.min.js"></script>
<script src="/static/js/utils.js"></script>
<script src="/static/js/team.js"></script> <script src="/static/js/team.js"></script>
{% endblock %} {% endblock %}