From f4a2f165a223d5820d5d7b4819226b7199c65274 Mon Sep 17 00:00:00 2001 From: Kevin Chung Date: Fri, 22 Apr 2016 18:19:49 -0400 Subject: [PATCH] Getting awards feature ready --- CTFd/admin.py | 58 +++++++++++++- CTFd/config.py | 1 + CTFd/models.py | 4 +- CTFd/static/js/chalboard.js | 28 ------- CTFd/static/js/scoreboard.js | 25 ------ CTFd/static/js/utils.js | 55 +++++++++++++ CTFd/templates/admin/base.html | 2 +- CTFd/templates/admin/chals.html | 1 + CTFd/templates/admin/correct_keys.html | 1 + CTFd/templates/admin/team.html | 107 ++++++++++++++++++++++++- CTFd/templates/admin/wrong_keys.html | 1 + CTFd/templates/chals.html | 1 + CTFd/templates/scoreboard.html | 1 + CTFd/templates/team.html | 1 + 14 files changed, 224 insertions(+), 62 deletions(-) create mode 100644 CTFd/static/js/utils.js diff --git a/CTFd/admin.py b/CTFd/admin.py index 94309fc..2429bda 100644 --- a/CTFd/admin.py +++ b/CTFd/admin.py @@ -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//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//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(): diff --git a/CTFd/config.py b/CTFd/config.py index cf55c7f..f2e580e 100644 --- a/CTFd/config.py +++ b/CTFd/config.py @@ -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 diff --git a/CTFd/models.py b/CTFd/models.py index 91141d4..05b3ff0 100644 --- a/CTFd/models.py +++ b/CTFd/models.py @@ -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 diff --git a/CTFd/static/js/chalboard.js b/CTFd/static/js/chalboard.js index 5148716..f6ffb17 100644 --- a/CTFd/static/js/chalboard.js +++ b/CTFd/static/js/chalboard.js @@ -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 $('
').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) { diff --git a/CTFd/static/js/scoreboard.js b/CTFd/static/js/scoreboard.js index 1360696..d4f96a0 100644 --- a/CTFd/static/js/scoreboard.js +++ b/CTFd/static/js/scoreboard.js @@ -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 $('
').text(string).html(); -} - function updatescores () { $.get('/scores', function( data ) { teams = $.parseJSON(JSON.stringify(data)); diff --git a/CTFd/static/js/utils.js b/CTFd/static/js/utils.js new file mode 100644 index 0000000..784b3df --- /dev/null +++ b/CTFd/static/js/utils.js @@ -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 $('
').text(string).html(); +} \ No newline at end of file diff --git a/CTFd/templates/admin/base.html b/CTFd/templates/admin/base.html index c063f85..c64fcf6 100644 --- a/CTFd/templates/admin/base.html +++ b/CTFd/templates/admin/base.html @@ -27,7 +27,7 @@ - CTF + CTFd