Hide scores (#224)

* Starting work on hide_scores functionality

* Hide teams in more views

* Starting work on hide_scores functionality

* Hide teams in more views
selenium-screenshot-testing
Kevin Chung 2017-03-08 00:21:39 -05:00 committed by GitHub
parent e9df6bf803
commit d0cb92c644
14 changed files with 96 additions and 13 deletions

View File

@ -62,6 +62,7 @@ def admin_config():
try:
view_challenges_unregistered = bool(request.form.get('view_challenges_unregistered', None))
view_scoreboard_if_authed = bool(request.form.get('view_scoreboard_if_authed', None))
hide_scores = bool(request.form.get('hide_scores', None))
prevent_registration = bool(request.form.get('prevent_registration', None))
prevent_name_change = bool(request.form.get('prevent_name_change', None))
view_after_ctf = bool(request.form.get('view_after_ctf', None))
@ -71,6 +72,7 @@ def admin_config():
except (ValueError, TypeError):
view_challenges_unregistered = None
view_scoreboard_if_authed = None
hide_scores = None
prevent_registration = None
prevent_name_change = None
view_after_ctf = None
@ -80,6 +82,7 @@ def admin_config():
finally:
view_challenges_unregistered = set_config('view_challenges_unregistered', view_challenges_unregistered)
view_scoreboard_if_authed = set_config('view_scoreboard_if_authed', view_scoreboard_if_authed)
hide_scores = set_config('hide_scores', hide_scores)
prevent_registration = set_config('prevent_registration', prevent_registration)
prevent_name_change = set_config('prevent_name_change', prevent_name_change)
view_after_ctf = set_config('view_after_ctf', view_after_ctf)
@ -122,6 +125,7 @@ def admin_config():
ctf_name = get_config('ctf_name')
ctf_theme = get_config('ctf_theme')
max_tries = get_config('max_tries')
hide_scores = get_config('hide_scores')
mail_server = get_config('mail_server')
mail_port = get_config('mail_port')
@ -159,6 +163,7 @@ def admin_config():
ctf_theme_config=ctf_theme,
start=start,
end=end,
hide_scores=hide_scores,
max_tries=max_tries,
mail_server=mail_server,
mail_port=mail_port,

View File

@ -6,7 +6,7 @@ import time
from flask import render_template, request, redirect, jsonify, url_for, session, Blueprint
from sqlalchemy.sql import or_
from CTFd.utils import ctftime, view_after_ctf, authed, unix_time, get_kpm, user_can_view_challenges, is_admin, get_config, get_ip, is_verified, ctf_started, ctf_ended, ctf_name
from CTFd.utils import ctftime, view_after_ctf, authed, unix_time, get_kpm, user_can_view_challenges, is_admin, get_config, get_ip, is_verified, ctf_started, ctf_ended, ctf_name, hide_scores
from CTFd.models import db, Challenges, Files, Solves, WrongKeys, Keys, Tags, Teams, Awards
from CTFd.plugins.keys import get_key_class
from CTFd.plugins.challenges import get_chal_class
@ -79,12 +79,17 @@ def chals():
def solves_per_chal():
if not user_can_view_challenges():
return redirect(url_for('auth.login', next=request.path))
solves_sub = db.session.query(Solves.chalid, db.func.count(Solves.chalid).label('solves')).join(Teams, Solves.teamid == Teams.id).filter(Teams.banned == False).group_by(Solves.chalid).subquery()
solves = db.session.query(solves_sub.columns.chalid, solves_sub.columns.solves, Challenges.name) \
.join(Challenges, solves_sub.columns.chalid == Challenges.id).all()
json = {}
for chal, count, name in solves:
json[chal] = count
if hide_scores():
for chal, count, name in solves:
json[chal] = -1
else:
for chal, count, name in solves:
json[chal] = count
db.session.close()
return jsonify(json)
@ -158,8 +163,11 @@ def fails(teamid):
def who_solved(chalid):
if not user_can_view_challenges():
return redirect(url_for('auth.login', next=request.path))
solves = Solves.query.join(Teams, Solves.teamid == Teams.id).filter(Solves.chalid == chalid, Teams.banned == False).order_by(Solves.date.asc())
json = {'teams': []}
if hide_scores():
return jsonify(json)
solves = Solves.query.join(Teams, Solves.teamid == Teams.id).filter(Solves.chalid == chalid, Teams.banned == False).order_by(Solves.date.asc())
for solve in solves:
json['teams'].append({'id': solve.team.id, 'name': solve.team.name, 'date': solve.date})
return jsonify(json)

View File

View File

@ -1,7 +1,7 @@
from flask import render_template, jsonify, Blueprint, redirect, url_for, request
from sqlalchemy.sql.expression import union_all
from CTFd.utils import unix_time, authed, get_config
from CTFd.utils import unix_time, authed, get_config, hide_scores
from CTFd.models import db, Teams, Solves, Awards, Challenges
scoreboard = Blueprint('scoreboard', __name__)
@ -37,16 +37,22 @@ def get_standings(admin=False, count=None):
def scoreboard_view():
if get_config('view_scoreboard_if_authed') and not authed():
return redirect(url_for('auth.login', next=request.path))
if hide_scores():
return render_template('scoreboard.html', errors=['Scores are currently hidden'])
standings = get_standings()
return render_template('scoreboard.html', teams=standings)
@scoreboard.route('/scores')
def scores():
json = {'standings': []}
if get_config('view_scoreboard_if_authed') and not authed():
return redirect(url_for('auth.login', next=request.path))
if hide_scores():
return jsonify(json)
standings = get_standings()
json = {'standings': []}
for i, x in enumerate(standings):
json['standings'].append({'pos': i + 1, 'id': x.teamid, 'team': x.name, 'score': int(x.score)})
return jsonify(json)
@ -54,16 +60,15 @@ def scores():
@scoreboard.route('/top/<int:count>')
def topteams(count):
json = {'scores': {}}
if get_config('view_scoreboard_if_authed') and not authed():
return redirect(url_for('auth.login', next=request.path))
try:
count = int(count)
except ValueError:
count = 10
if hide_scores():
return jsonify(json)
if count > 20 or count < 0:
count = 10
json = {'scores': {}}
standings = get_standings(count=count)
for team in standings:

View File

@ -53,3 +53,11 @@ function colorhash (x) {
function htmlentities(string) {
return $('<div/>').text(string).html();
}
Handlebars.registerHelper('if_eq', function(a, b, opts) {
if (a == b) {
return opts.fn(this);
} else {
return opts.inverse(this);
}
});

View File

@ -3,7 +3,10 @@
<button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">&times;</span></button>
<ul class="nav nav-tabs" role="tablist">
<li role="presentation" class="active"><a href="#challenge" aria-controls="challenge" role="tab">Challenge</a></li>
{{#if_eq solves '-1 Solves'}}
{{else}}
<li role="presentation"><a href="#solves" aria-controls="solves" class="chal-solves" role="tab">{{solves}}</a></li>
{{/if_eq}}
</ul>
<div class="modal-body">
<div role="tabpanel">

View File

@ -53,3 +53,11 @@ function colorhash (x) {
function htmlentities(string) {
return $('<div/>').text(string).html();
}
Handlebars.registerHelper('if_eq', function(a, b, opts) {
if (a == b) {
return opts.fn(this);
} else {
return opts.inverse(this);
}
});

View File

@ -50,6 +50,13 @@
{% endfor %}
</select>
</div>
<div class="checkbox">
<label>
<input id="hide_scores" name="hide_scores" type="checkbox" {% if hide_scores %}checked{% endif %}>
Hide Scores from public view
</label>
</div>
</div>
<div role="tabpanel" class="tab-pane" id="accounts-section">
<div class="form-group">

View File

@ -37,7 +37,9 @@
<li><a href="{{ request.script_root }}/{{ page.route }}">{{ page.route|title }}</a></li>
{% endfor %}
<li><a href="{{ request.script_root }}/teams">Teams</a></li>
{% if not hide_scores() %}
<li><a href="{{ request.script_root }}/scoreboard">Scoreboard</a></li>
{% endif %}
<li><a href="{{ request.script_root }}/challenges">Challenges</a></li>
</ul>
<ul class="nav navbar-nav navbar-right">

View File

@ -7,6 +7,15 @@
</div>
</div>
<div class="container main-container">
{% if errors %}
<div class="container main-container">
<div id='errors' class="row">
{% for error in errors %}
<h1>{{ error }}</h1>
{% endfor %}
</div>
</div>
{% else %}
<div id="score-graph"></div>
<br>
@ -27,6 +36,7 @@
{% endfor %}
</tbody>
</table>
{% endif %}
</div>
{% endblock %}

View File

@ -10,6 +10,13 @@
</div>
</div>
<div class="container">
{% if errors %}
<div id='errors' class="row">
{% for error in errors %}
<h1>{{ error }}</h1>
{% endfor %}
</div>
{% else %}
<div class="team-info">
<h2 id="team-place" class="text-center">
{%if place %}
@ -76,6 +83,7 @@
</tbody>
</table>
</div>
{% endif %}
</div>
{% endblock %}

View File

@ -22,7 +22,13 @@
<tbody>
{% for team in teams %}
<tr>
<td><a href="{{ request.script_root }}/team/{{ team.id }}">{{ team.name }}</a></td>
<td>
{% if hide_scores() %}
<span>{{ team.name }}</span>
{% else %}
<a href="{{ request.script_root }}/team/{{ team.id }}">{{ team.name }}</a>
{% endif %}
</td>
<td>{% if team.website and (team.website.startswith('http://') or team.website.startswith('https://')) %}<a href="{{ team.website }}">{{ team.website }}</a>{% endif %}</td>
<td><span>{% if team.affiliation %}{{ team.affiliation }}{% endif %}</span></td>
<td class="hidden-xs"><span>{% if team.country %}{{ team.country }}{% endif %}</span></td>

View File

@ -102,6 +102,7 @@ def init_utils(app):
app.jinja_env.globals.update(can_create_container=can_create_container)
app.jinja_env.globals.update(get_configurable_plugins=get_configurable_plugins)
app.jinja_env.globals.update(get_config=get_config)
app.jinja_env.globals.update(hide_scores=hide_scores)
@app.context_processor
def inject_user():
@ -150,6 +151,11 @@ def ctf_theme():
return theme if theme else ''
@cache.memoize()
def hide_scores():
return get_config('hide_scores')
def pages():
pages = Pages.query.filter(Pages.route != "index").all()
return pages

View File

@ -6,7 +6,7 @@ from jinja2.exceptions import TemplateNotFound
from passlib.hash import bcrypt_sha256
from CTFd.utils import authed, is_setup, validate_url, get_config, set_config, sha512, cache, ctftime, view_after_ctf, ctf_started, \
is_admin
is_admin, hide_scores
from CTFd.models import db, Teams, Solves, Awards, Files, Pages
views = Blueprint('views', __name__)
@ -131,6 +131,7 @@ def teams(page):
def team(teamid):
if get_config('view_scoreboard_if_authed') and not authed():
return redirect(url_for('auth.login', next=request.path))
errors = []
user = Teams.query.filter_by(id=teamid).first_or_404()
solves = Solves.query.filter_by(teamid=teamid)
awards = Awards.query.filter_by(teamid=teamid).all()
@ -138,6 +139,12 @@ def team(teamid):
place = user.place()
db.session.close()
if hide_scores() and teamid != session.get('id'):
errors.append('Scores are currently hidden')
if errors:
return render_template('team.html', team=user, errors=errors)
if request.method == 'GET':
return render_template('team.html', solves=solves, awards=awards, team=user, score=score, place=place)
elif request.method == 'POST':