mirror of https://github.com/JohnHammond/CTFd.git
Hide scores (#224)
* Starting work on hide_scores functionality * Hide teams in more views * Starting work on hide_scores functionality * Hide teams in more viewsselenium-screenshot-testing
parent
e9df6bf803
commit
d0cb92c644
|
@ -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,
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
});
|
|
@ -3,7 +3,10 @@
|
|||
<button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">×</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">
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
});
|
|
@ -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">
|
||||
|
|
|
@ -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">
|
||||
|
|
|
@ -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 %}
|
||||
|
||||
|
|
|
@ -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 %}
|
||||
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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':
|
||||
|
|
Loading…
Reference in New Issue