1234 error components (#1465)

Start cleaning up a lot of the core theme. 
Extract pieces into components that can be included into overarching templates. 
Work on #1234
This commit is contained in:
Kevin Chung
2020-06-03 00:17:18 -04:00
committed by GitHub
parent 97f0beb9ca
commit 8313ccb443
34 changed files with 586 additions and 659 deletions

View File

@@ -66,7 +66,7 @@ def confirm(data=None):
return redirect(url_for("auth.login"))
# User is trying to start or restart the confirmation flow
if not current_user.authed():
if current_user.authed() is False:
return redirect(url_for("auth.login"))
user = Users.query.filter_by(id=session["id"]).first_or_404()
@@ -82,13 +82,11 @@ def confirm(data=None):
format="[{date}] {ip} - {name} initiated a confirmation email resend",
)
return render_template(
"confirm.html",
user=user,
infos=["Your confirmation email has been resent!"],
"confirm.html", infos=[f"Confirmation email sent to {user.email}!"]
)
elif request.method == "GET":
# User has been directed to the confirm page
return render_template("confirm.html", user=user)
return render_template("confirm.html")
@auth.route("/reset_password", methods=["POST", "GET"])
@@ -115,7 +113,7 @@ def reset_password(data=None):
if user.oauth_id:
return render_template(
"reset_password.html",
errors=[
infos=[
"Your account was registered via an authentication provider and does not have an associated password. Please login via your authentication provider."
],
)
@@ -153,7 +151,7 @@ def reset_password(data=None):
if not user:
return render_template(
"reset_password.html",
errors=[
infos=[
"If that account exists you will receive an email, please check your inbox"
],
)
@@ -161,7 +159,7 @@ def reset_password(data=None):
if user.oauth_id:
return render_template(
"reset_password.html",
errors=[
infos=[
"The email address associated with this account was registered via an authentication provider and does not have an associated password. Please login via your authentication provider."
],
)
@@ -170,7 +168,7 @@ def reset_password(data=None):
return render_template(
"reset_password.html",
errors=[
infos=[
"If that account exists you will receive an email, please check your inbox"
],
)

View File

@@ -1,7 +1,7 @@
from flask import Blueprint, render_template
from CTFd.utils import config, get_config
from CTFd.utils.dates import ctf_ended, ctf_paused, view_after_ctf
from CTFd.utils import config
from CTFd.utils.dates import ctf_ended, ctf_paused, ctf_started
from CTFd.utils.decorators import (
during_ctf_time_only,
require_team,
@@ -21,16 +21,14 @@ challenges = Blueprint("challenges", __name__)
def listing():
infos = get_infos()
errors = get_errors()
start = get_config("start") or 0
end = get_config("end") or 0
if ctf_paused():
infos.append("{} is paused".format(config.ctf_name()))
if ctf_started() is False:
errors.append(f"{config.ctf_name()} has not started yet")
# CTF has ended but we want to allow view_after_ctf. Show error but let JS load challenges.
if ctf_ended() and view_after_ctf():
infos.append("{} has ended".format(config.ctf_name()))
if ctf_paused() is True:
infos.append(f"{config.ctf_name()} is paused")
return render_template(
"challenges.html", infos=infos, errors=errors, start=int(start), end=int(end)
)
if ctf_ended() is True:
infos.append(f"{config.ctf_name()} has ended")
return render_template("challenges.html", infos=infos, errors=errors)

19
CTFd/constants/config.py Normal file
View File

@@ -0,0 +1,19 @@
from markupsafe import Markup
from CTFd.utils import get_config
class _ConfigsWrapper:
def __getattr__(self, attr):
return get_config(attr)
@property
def theme_header(self):
return Markup(get_config("theme_header", default=""))
@property
def theme_footer(self):
return Markup(get_config("theme_footer", default=""))
Configs = _ConfigsWrapper()

54
CTFd/constants/plugins.py Normal file
View File

@@ -0,0 +1,54 @@
from flask import current_app
from markupsafe import Markup
from CTFd.plugins import get_admin_plugin_menu_bar, get_user_page_menu_bar
from CTFd.utils.plugins import get_registered_scripts, get_registered_stylesheets
class _PluginWrapper:
@property
def scripts(self):
application_root = current_app.config.get("APPLICATION_ROOT")
subdir = application_root != "/"
scripts = []
for script in get_registered_scripts():
if script.startswith("http"):
scripts.append(f'<script defer src="{script}"></script>')
elif subdir:
scripts.append(
f'<script defer src="{application_root}/{script}"></script>'
)
else:
scripts.append(f'<script defer src="{script}"></script>')
return Markup("\n".join(scripts))
@property
def styles(self):
application_root = current_app.config.get("APPLICATION_ROOT")
subdir = application_root != "/"
_styles = []
for stylesheet in get_registered_stylesheets():
if stylesheet.startswith("http"):
_styles.append(
f'<link rel="stylesheet" type="text/css" href="{stylesheet}">'
)
elif subdir:
_styles.append(
f'<link rel="stylesheet" type="text/css" href="{application_root}/{stylesheet}">'
)
else:
_styles.append(
f'<link rel="stylesheet" type="text/css" href="{stylesheet}">'
)
return Markup("\n".join(_styles))
@property
def user_menu_pages(self):
return get_user_page_menu_bar()
@property
def admin_menu_pages(self):
return get_admin_plugin_menu_bar()
Plugins = _PluginWrapper()

View File

@@ -266,6 +266,16 @@ class Users(db.Model):
elif user_mode == "users":
return self.id
@hybrid_property
def account(self):
from CTFd.utils import get_config
user_mode = get_config("user_mode")
if user_mode == "teams":
return self.team
elif user_mode == "users":
return self
@property
def solves(self):
return self.get_solves(admin=False)

View File

@@ -4,7 +4,7 @@ import os
from collections import namedtuple
from flask import current_app as app
from flask import send_file, send_from_directory
from flask import send_file, send_from_directory, url_for
from CTFd.utils.config.pages import get_pages
from CTFd.utils.decorators import admins_only as admins_only_wrapper
@@ -114,6 +114,9 @@ def register_admin_plugin_menu_bar(title, route):
:param route: A string that is the href used by the link
:return:
"""
if (route.startswith("http://") or route.startswith("https://")) is False:
route = url_for("views.static_html", route=route)
am = Menu(title=title, route=route)
app.admin_plugin_menu_bar.append(am)
@@ -135,6 +138,9 @@ def register_user_page_menu_bar(title, route):
:param route: A string that is the href used by the link
:return:
"""
if (route.startswith("http://") or route.startswith("https://")) is False:
route = url_for("views.static_html", route=route)
p = Menu(title=title, route=route)
app.plugin_menu_bar.append(p)

View File

@@ -3,6 +3,7 @@ from flask import Blueprint, render_template
from CTFd.cache import cache, make_cache_key
from CTFd.utils import config
from CTFd.utils.decorators.visibility import check_score_visibility
from CTFd.utils.helpers import get_infos
from CTFd.utils.scores import get_standings
scoreboard = Blueprint("scoreboard", __name__)
@@ -12,9 +13,10 @@ scoreboard = Blueprint("scoreboard", __name__)
@check_score_visibility
@cache.cached(timeout=60, key_prefix=make_cache_key)
def listing():
infos = get_infos()
if config.is_scoreboard_frozen():
infos.append("Scoreboard has been frozen")
standings = get_standings()
return render_template(
"scoreboard.html",
standings=standings,
score_frozen=config.is_scoreboard_frozen(),
)
return render_template("scoreboard.html", standings=standings, infos=infos)

View File

@@ -1,4 +1,4 @@
/* Move down content because we have a fixed navbar that is 3.5rem tall */
body {
main {
padding-top: 3.5rem;
}

View File

@@ -5,9 +5,9 @@ html {
min-height: 100%;
}
body {
main {
/* Margin bottom by footer height */
margin-bottom: 60px;
margin-bottom: 100px;
}
.footer {

File diff suppressed because one or more lines are too long

View File

@@ -8,16 +8,10 @@
<link rel="stylesheet" href="{{ url_for('views.themes', path='css/fonts.css') }}">
<link rel="stylesheet" href="{{ url_for('views.themes', path='css/main.css') }}">
<link rel="stylesheet" href="{{ url_for('views.themes', path='css/core.css') }}">
{% block stylesheets %}{% endblock %}
{% for stylesheet in get_registered_stylesheets() %}
{% if stylesheet.startswith('http') %}
<link rel="stylesheet" type="text/css" href="{{ stylesheet }}">
{% elif request.script_root %}
<link rel="stylesheet" type="text/css" href="{{ request.script_root }}/{{ stylesheet }}">
{% else %}
<link rel="stylesheet" type="text/css" href="{{ stylesheet }}">
{% endif %}
{% endfor %}
{% block stylesheets %}
{% endblock %}
{{ Plugins.styles }}
<script type="text/javascript">
var init = {
'urlRoot': "{{ request.script_root }}",
@@ -28,154 +22,10 @@
'end': {{ get_config("end") | tojson }},
}
</script>
{{ get_config('theme_header', '') | safe }}
{{ Configs.theme_header }}
</head>
<body>
<nav class="navbar navbar-expand-md navbar-dark bg-dark fixed-top">
<div class="container">
<a href="{{ url_for('views.static_html', route='/') }}" class="navbar-brand">
{% if get_ctf_logo() %}
<img class="img-responsive ctf_logo" src="{{ url_for('views.files', path=get_ctf_logo()) }}" height="25" alt="{{ get_ctf_name() }}">
{% else %}
{{ get_ctf_name() }}
{% endif %}
</a>
<button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#base-navbars"
aria-controls="base-navbars" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="base-navbars">
<ul class="navbar-nav mr-auto">
{% for page in get_user_page_menu_bar() %}
{% if page.route.startswith('http://') or page.route.startswith('https://') %}
<li class="nav-item">
<a class="nav-link" href="{{ page.route }}">{{ page.title }}</a>
</li>
{% else %}
<li class="nav-item">
<a class="nav-link" href="{{ url_for('views.static_html', route=page.route) }}">{{ page.title }}</a>
</li>
{% endif %}
{% endfor %}
{% if get_config('account_visibility') != 'admins' %}
<li class="nav-item">
<a class="nav-link" href="{{ url_for('users.listing') }}">Users</a>
</li>
{% if get_config('user_mode') == 'teams' %}
<li class="nav-item">
<a class="nav-link" href="{{ url_for('teams.listing') }}">Teams</a>
</li>
{% endif %}
{% endif %}
{% if get_config('score_visibility') != 'admins' %}
<li class="nav-item">
<a class="nav-link" href="{{ url_for('scoreboard.listing') }}">Scoreboard</a>
</li>
{% endif %}
<li class="nav-item">
<a class="nav-link" href="{{ url_for('challenges.listing') }}">Challenges</a>
</li>
</ul>
<hr class="d-sm-flex d-md-flex d-lg-none">
<ul class="navbar-nav ml-md-auto d-block d-sm-flex d-md-flex">
{% if authed() %}
{% if is_admin() %}
<li class="nav-item">
<a class="nav-link" href="{{ url_for('admin.view') }}">
<span class="d-block" data-toggle="tooltip" data-placement="bottom" title="Admin Panel">
<i class="fas fa-wrench d-none d-md-block d-lg-none"></i>
</span>
<span class="d-sm-block d-md-none d-lg-block">
<i class="fas fa-wrench pr-1"></i>Admin Panel
</span>
</a>
</li>
{% endif %}
<li class="nav-item">
<a class="nav-link" href="{{ url_for('views.notifications') }}">
<span class="d-block" data-toggle="tooltip" data-placement="bottom" title="Notifications">
<i class="fas fa-bell d-none d-md-block d-lg-none"></i>
</span>
<span class="d-sm-block d-md-none d-lg-block">
<i class="fas fa-bell pr-1"></i>
<span class="badge badge-pill badge-danger badge-notification"></span>
Notifications
</span>
</a>
</li>
{% if config.user_mode() == "teams" %}
<li class="nav-item">
<a class="nav-link" href="{{ url_for('teams.private') }}">
<span class="d-block" data-toggle="tooltip" data-placement="bottom" title="Team">
<i class="fas fa-users d-none d-md-block d-lg-none"></i>
</span>
<span class="d-sm-block d-md-none d-lg-block">
<i class="fas fa-users pr-1"></i>Team
</span>
</a>
</li>
{% endif %}
<li class="nav-item">
<a class="nav-link" href="{{ url_for('users.private') }}">
<span class="d-block" data-toggle="tooltip" data-placement="bottom" title="Profile">
<i class="fas fa-user-circle d-none d-md-block d-lg-none"></i>
</span>
<span class="d-sm-block d-md-none d-lg-block">
<i class="fas fa-user-circle pr-1"></i>Profile
</span>
</a>
</li>
<li class="nav-item">
<a class="nav-link" href="{{ url_for('views.settings') }}">
<span class="d-block" data-toggle="tooltip" data-placement="bottom" title="Settings">
<i class="fas fa-cogs d-none d-md-block d-lg-none"></i>
</span>
<span class="d-sm-block d-md-none d-lg-block">
<i class="fas fa-cogs pr-1"></i>Settings
</span>
</a>
</li>
<li class="nav-item">
<a class="nav-link" href="{{ url_for('auth.logout') }}">
<span class="d-block" data-toggle="tooltip" data-placement="bottom" title="Logout">
<i class="fas fa-sign-out-alt d-none d-md-block d-lg-none"></i>
</span>
<span class="d-sm-block d-md-none d-lg-block">
<i class="fas fa-sign-out-alt pr-1"></i><span class="d-lg-none">Logout</span>
</span>
</a>
</li>
{% else %}
{% if registration_visible() %}
<li class="nav-item">
<a class="nav-link" href="{{ url_for('auth.register') }}">
<span class="d-block" data-toggle="tooltip" data-placement="bottom" title="Register">
<i class="fas fa-user-plus d-none d-md-block d-lg-none"></i>
</span>
<span class="d-sm-block d-md-none d-lg-block">
<i class="fas fa-user-plus pr-1"></i>Register
</span>
</a>
</li>
{% endif %}
<li class="nav-item">
<a class="nav-link" href="{{ url_for('auth.login') }}">
<span class="d-block" data-toggle="tooltip" data-placement="bottom" title="Login">
<i class="fas fa-sign-in-alt d-none d-md-block d-lg-none"></i>
</span>
<span class="d-sm-block d-md-none d-lg-block">
<i class="fas fa-sign-in-alt pr-1"></i>Login
</span>
</a>
</li>
{% endif %}
</ul>
</div>
</div>
</nav>
{% include "components/navbar.html" %}
<main role="main">
{% block content %}
@@ -201,16 +51,8 @@
{% block scripts %}
{% endblock %}
{% for script in get_registered_scripts() %}
{% if script.startswith('http') %}
<script defer src="{{ script }}"></script>
{% elif request.script_root %}
<script defer src="{{ request.script_root }}/{{ script }}"></script>
{% else %}
<script defer src="{{ script }}"></script>
{% endif %}
{% endfor %}
{{ Plugins.scripts }}
{{ get_config('theme_footer', '') | safe }}
{{ Configs.theme_footer }}
</body>
</html>

View File

@@ -5,58 +5,29 @@
{% endblock %}
{% block content %}
<div class="jumbotron">
<div class="container">
<h1>Challenges</h1>
</div>
</div>
{% if infos %}
<div class="container">
<div id='errors' class="row">
<div class="col-md-12">
{% for info in infos %}
<h1 class="text-center">{{ info }}</h1>
{% endfor %}
</div>
</div>
</div>
{% endif %}
{% if errors %}
<div class="container">
<div id='errors' class="row">
<div class="col-md-12">
{% for error in errors %}
<h1 class="text-center">{{ error }}</h1>
{% endfor %}
</div>
</div>
<div class="modal fade" id="challenge-window" tabindex="-1" role="dialog">
</div>
{% endif %}
{% if admin or not errors %}
<div class="container">
{% include "components/errors.html" %}
<div id='challenges-board'>
<div class="text-center">
<i class="fas fa-circle-notch fa-spin fa-3x fa-fw spinner"></i>
</div>
</div>
</div>
<input id="nonce" type="hidden" name="nonce" value="{{ nonce }}">
<div class="modal fade" id="challenge-window" tabindex="-1" role="dialog">
</div>
{% endif %}
{% endblock %}
{% block scripts %}
{% endblock %}
{% block entrypoint %}
{% if admin or not errors %}
<script defer src="{{ url_for('views.themes', path='js/pages/challenges.js') }}"></script>
{% endif %}
<script defer src="{{ url_for('views.themes', path='js/pages/challenges.js') }}"></script>
{% endblock %}

View File

@@ -0,0 +1,18 @@
<div>
{% for info in infos %}
<div class="alert alert-info alert-dismissable text-center" role="alert">
<span>{{ info }}</span>
<button type="button" class="close" data-dismiss="alert" aria-label="Close">
<span aria-hidden="true">×</span>
</button>
</div>
{% endfor %}
{% for error in errors %}
<div class="alert alert-danger alert-dismissable text-center" role="alert">
<span>{{ error }}</span>
<button type="button" class="close" data-dismiss="alert" aria-label="Close">
<span aria-hidden="true">×</span>
</button>
</div>
{% endfor %}
</div>

View File

@@ -0,0 +1,141 @@
<nav class="navbar navbar-expand-md navbar-dark bg-dark fixed-top">
<div class="container">
<a href="{{ url_for('views.static_html', route='/') }}" class="navbar-brand">
{% if get_ctf_logo() %}
<img class="img-responsive ctf_logo" src="{{ url_for('views.files', path=get_ctf_logo()) }}" height="25" alt="{{ get_ctf_name() }}">
{% else %}
{{ get_ctf_name() }}
{% endif %}
</a>
<button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#base-navbars"
aria-controls="base-navbars" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="base-navbars">
<ul class="navbar-nav mr-auto">
{% for page in Plugins.user_menu_pages %}
<li class="nav-item">
<a class="nav-link" href="{{ page.route }}">{{ page.title }}</a>
</li>
{% endfor %}
{% if Configs.account_visibility != 'admins' %}
<li class="nav-item">
<a class="nav-link" href="{{ url_for('users.listing') }}">Users</a>
</li>
{% if Configs.user_mode == 'teams' %}
<li class="nav-item">
<a class="nav-link" href="{{ url_for('teams.listing') }}">Teams</a>
</li>
{% endif %}
{% endif %}
{% if Configs.score_visibility != 'admins' %}
<li class="nav-item">
<a class="nav-link" href="{{ url_for('scoreboard.listing') }}">Scoreboard</a>
</li>
{% endif %}
<li class="nav-item">
<a class="nav-link" href="{{ url_for('challenges.listing') }}">Challenges</a>
</li>
</ul>
<hr class="d-sm-flex d-md-flex d-lg-none">
<ul class="navbar-nav ml-md-auto d-block d-sm-flex d-md-flex">
{% if authed() %}
{% if is_admin() %}
<li class="nav-item">
<a class="nav-link" href="{{ url_for('admin.view') }}">
<span class="d-block" data-toggle="tooltip" data-placement="bottom" title="Admin Panel">
<i class="fas fa-wrench d-none d-md-block d-lg-none"></i>
</span>
<span class="d-sm-block d-md-none d-lg-block">
<i class="fas fa-wrench pr-1"></i>Admin Panel
</span>
</a>
</li>
{% endif %}
<li class="nav-item">
<a class="nav-link" href="{{ url_for('views.notifications') }}">
<span class="d-block" data-toggle="tooltip" data-placement="bottom" title="Notifications">
<i class="fas fa-bell d-none d-md-block d-lg-none"></i>
</span>
<span class="d-sm-block d-md-none d-lg-block">
<i class="fas fa-bell pr-1"></i>
<span class="badge badge-pill badge-danger badge-notification"></span>
Notifications
</span>
</a>
</li>
{% if Configs.user_mode == "teams" %}
<li class="nav-item">
<a class="nav-link" href="{{ url_for('teams.private') }}">
<span class="d-block" data-toggle="tooltip" data-placement="bottom" title="Team">
<i class="fas fa-users d-none d-md-block d-lg-none"></i>
</span>
<span class="d-sm-block d-md-none d-lg-block">
<i class="fas fa-users pr-1"></i>Team
</span>
</a>
</li>
{% endif %}
<li class="nav-item">
<a class="nav-link" href="{{ url_for('users.private') }}">
<span class="d-block" data-toggle="tooltip" data-placement="bottom" title="Profile">
<i class="fas fa-user-circle d-none d-md-block d-lg-none"></i>
</span>
<span class="d-sm-block d-md-none d-lg-block">
<i class="fas fa-user-circle pr-1"></i>Profile
</span>
</a>
</li>
<li class="nav-item">
<a class="nav-link" href="{{ url_for('views.settings') }}">
<span class="d-block" data-toggle="tooltip" data-placement="bottom" title="Settings">
<i class="fas fa-cogs d-none d-md-block d-lg-none"></i>
</span>
<span class="d-sm-block d-md-none d-lg-block">
<i class="fas fa-cogs pr-1"></i>Settings
</span>
</a>
</li>
<li class="nav-item">
<a class="nav-link" href="{{ url_for('auth.logout') }}">
<span class="d-block" data-toggle="tooltip" data-placement="bottom" title="Logout">
<i class="fas fa-sign-out-alt d-none d-md-block d-lg-none"></i>
</span>
<span class="d-sm-block d-md-none d-lg-block">
<i class="fas fa-sign-out-alt pr-1"></i><span class="d-lg-none">Logout</span>
</span>
</a>
</li>
{% else %}
{% if registration_visible() %}
<li class="nav-item">
<a class="nav-link" href="{{ url_for('auth.register') }}">
<span class="d-block" data-toggle="tooltip" data-placement="bottom" title="Register">
<i class="fas fa-user-plus d-none d-md-block d-lg-none"></i>
</span>
<span class="d-sm-block d-md-none d-lg-block">
<i class="fas fa-user-plus pr-1"></i>Register
</span>
</a>
</li>
{% endif %}
<li class="nav-item">
<a class="nav-link" href="{{ url_for('auth.login') }}">
<span class="d-block" data-toggle="tooltip" data-placement="bottom" title="Login">
<i class="fas fa-sign-in-alt d-none d-md-block d-lg-none"></i>
</span>
<span class="d-sm-block d-md-none d-lg-block">
<i class="fas fa-sign-in-alt pr-1"></i>Login
</span>
</a>
</li>
{% endif %}
</ul>
</div>
</div>
</nav>

View File

@@ -12,25 +12,10 @@
<div class="container">
<div class="row">
<div class="col-md-6 offset-md-3">
{% for info in infos %}
<div class="alert alert-info alert-dismissable" role="alert">
<span class="sr-only"></span>
{{ info }}
<button type="button" class="close" data-dismiss="alert" aria-label="Close"><span
aria-hidden="true">×</span></button>
</div>
{% endfor %}
{% for error in errors %}
<div class="alert alert-danger alert-dismissable" role="alert">
<span class="sr-only">Error:</span>
{{ error }}
<button type="button" class="close" data-dismiss="alert" aria-label="Close"><span
aria-hidden="true">×</span></button>
</div>
{% endfor %}
{% if user %}
{% include "components/errors.html" %}
<h3 class="text-center">
We've sent a confirmation email to {{ user.email }}
We've sent a confirmation email to your email address.
</h3>
<br>
@@ -38,11 +23,9 @@
<h4 class="text-center">
Please click the link in that email to confirm your account.
</h4>
{% endif %}
<hr>
{% if name %}
<form method="POST" action="{{ url_for('auth.confirm') }}">
<h4 class="text-center">
Need to resend the confirmation email?
@@ -52,7 +35,6 @@
</div>
<input type="hidden" name="nonce" value="{{ nonce }}">
</form>
{% endif %}
</div>
</div>
</div>

View File

@@ -5,8 +5,11 @@
<div class="container">
<div class="row">
<div class="col-md-12">
<h1 class="text-center">Forbidden</h1>
<h2 class="text-center">{{ error }}</h2>
<div class="pt-5 mt-5 text-center">
<h1>Forbidden</h1>
<hr class="w-50">
<h2>{{ error }}</h2>
</div>
</div>
</div>
</div>

View File

@@ -5,9 +5,12 @@
<div class="container">
<div class="row">
<div class="col-md-12">
<h1 class="text-center">404</h1>
<h2 class="text-center">Whoops, looks like we can't find that.</h2>
<h2 class="text-center">Sorry about that</h2>
<div class="pt-5 mt-5 text-center">
<h1>404</h1>
<hr class="w-50">
<h2>File not found</h2>
<h2>Sorry about that</h2>
</div>
</div>
</div>
</div>

View File

@@ -5,9 +5,12 @@
<div class="container">
<div class="row">
<div class="col-md-12">
<h1 class="text-center">429</h1>
<h2 class="text-center">Too many requests</h2>
<h2 class="text-center">Please slow down!</h2>
<div class="pt-5 mt-5 text-center">
<h1>429</h1>
<hr class="w-50">
<h2>Too many requests</h2>
<h2>Please slow down!</h2>
</div>
</div>
</div>
</div>

View File

@@ -5,8 +5,11 @@
<div class="container">
<div class="row">
<div class="col-md-12">
<h1 class="text-center">500</h1>
<h2 class="text-center">An Internal Server Error has occurred</h2>
<div class="pt-5 mt-5 text-center">
<h1>500</h1>
<hr class="w-50">
<h2>An Internal Server Error has occurred</h2>
</div>
</div>
</div>
</div>

View File

@@ -5,8 +5,11 @@
<div class="container">
<div class="row">
<div class="col-md-12">
<h1 class="text-center">502</h1>
<h2 class="text-center">Bad Gateway</h2>
<div class="pt-5 mt-5 text-center">
<h1>502</h1>
<hr class="w-50">
<h2>Bad Gateway</h2>
</div>
</div>
</div>
</div>

View File

@@ -12,13 +12,7 @@
<div class="container">
<div class="row">
<div class="col-md-6 offset-md-3">
{% for error in errors %}
<div class="alert alert-danger alert-dismissable" role="alert">
<span class="sr-only">Error:</span>
{{ error }}
<button type="button" class="close" data-dismiss="alert" aria-label="Close"><span aria-hidden="true">×</span></button>
</div>
{% endfor %}
{% include "components/errors.html" %}
{% if integrations.mlc() %}
<a class="btn btn-secondary btn-lg btn-block" href="{{ url_for('auth.oauth_login') }}">

View File

@@ -12,13 +12,7 @@
<div class="container">
<div class="row">
<div class="col-md-6 offset-md-3">
{% for error in errors %}
<div class="alert alert-danger alert-dismissable" role="alert">
<span class="sr-only">Error:</span>
{{ error }}
<button type="button" class="close" data-dismiss="alert" aria-label="Close"><span aria-hidden="true">×</span></button>
</div>
{% endfor %}
{% include "components/errors.html" %}
{% if integrations.mlc() %}
<a class="btn btn-secondary btn-lg btn-block" href="{{ url_for('auth.oauth_login') }}">

View File

@@ -12,13 +12,8 @@
<div class="container">
<div class="row">
<div class="col-md-6 offset-md-3">
{% for error in errors %}
<div class="alert alert-info alert-dismissable" role="alert">
<span class="sr-only">Error:</span>
{{ error }}
<button type="button" class="close" data-dismiss="alert" aria-label="Close"><span aria-hidden="true">×</span></button>
</div>
{% endfor %}
{% include "components/errors.html" %}
{% if can_send_mail() %}
<form method="post" accept-charset="utf-8" autocomplete="off" role="form" class="form-horizontal">
<input name='nonce' type='hidden' value="{{ nonce }}">

View File

@@ -7,70 +7,52 @@
</div>
</div>
<div class="container">
{% if errors %}
<div id='errors' class="row">
<div class="col-md-12">
{% for error in errors %}
<h1 class="text-center">{{ error }}</h1>
{% endfor %}
{% include "components/errors.html" %}
<div id="score-graph" class="row">
<div class="col-md-12 text-center">
<i class="fas fa-circle-notch fa-spin fa-3x fa-fw spinner"></i>
</div>
</div>
{% else %}
{% if score_frozen %}
<div class="row">
<div class="col-md-12">
<h1 class="text-center">Scoreboard has been frozen.</h1>
</div>
</div>
{% endif %}
<div id="score-graph" class="row">
<div class="col-md-12 text-center">
<i class="fas fa-circle-notch fa-spin fa-3x fa-fw spinner"></i>
</div>
</div>
<div id="scoreboard" class="row">
<div class="col-md-12">
<table class="table table-striped">
<thead>
<tr>
<td scope="col" width="10px"><b>Place</b></td>
<td scope="col"><b>Team</b></td>
<td scope="col"><b>Score</b></td>
</tr>
</thead>
<tbody>
{% for standing in standings %}
<tr>
<th scope="row" class="text-center">{{ loop.index }}</th>
<td>
<a href="{{ generate_account_url(standing.account_id) }}">
{{ standing.name | truncate(50) }}
{% if standings %}
<div id="scoreboard" class="row">
<div class="col-md-12">
<table class="table table-striped">
<thead>
<tr>
<td scope="col" width="10px"><b>Place</b></td>
<td scope="col"><b>Team</b></td>
<td scope="col"><b>Score</b></td>
</tr>
</thead>
<tbody>
{% for standing in standings %}
<tr>
<th scope="row" class="text-center">{{ loop.index }}</th>
<td>
<a href="{{ generate_account_url(standing.account_id) }}">
{{ standing.name | truncate(50) }}
{% if standing.oauth_id %}
{% if get_config('user_mode') == 'teams' %}
<a href="https://majorleaguecyber.org/t/{{ standing.name }}">
<span class="badge badge-primary">Official</span>
</a>
{% elif get_config('user_mode') == 'users' %}
<a href="https://majorleaguecyber.org/u/{{ standing.name }}">
<span class="badge badge-primary">Official</span>
</a>
{% endif %}
{% if standing.oauth_id %}
{% if get_config('user_mode') == 'teams' %}
<a href="https://majorleaguecyber.org/t/{{ standing.name }}">
<span class="badge badge-primary">Official</span>
</a>
{% elif get_config('user_mode') == 'users' %}
<a href="https://majorleaguecyber.org/u/{{ standing.name }}">
<span class="badge badge-primary">Official</span>
</a>
{% endif %}
</a>
</td>
<td>{{ standing.score }}</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
{% endif %}
</a>
</td>
<td>{{ standing.score }}</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
{% endif %}
{% endif %}
</div>
</div>
{% endblock %}

View File

@@ -20,18 +20,8 @@
<div class="col-md-8">
<div class="tab-content" id="v-pills-tabContent">
<div class="tab-pane fade show active" id="profile" role="tabpanel">
{% if confirm_email %}
<div class="alert alert-info alert-dismissable submit-row" role="alert">
Your email address isn't confirmed!
Please check your email to confirm your email address.
<br>
<br>
To have the confirmation email resent please <a href="{{ url_for('auth.confirm') }}">click
here.</a>
<button type="button" class="close" data-dismiss="alert" aria-label="Close"><span
aria-hidden="true">×</span></button>
</div>
{% endif %}
{% include "components/errors.html" %}
<form id="user-profile-form" method="post" accept-charset="utf-8" autocomplete="off" role="form"
class="form-horizontal">
<div class="form-group">

View File

@@ -11,16 +11,8 @@
</div>
<div class="container">
<div class="col-md-8 offset-md-2">
{% for error in errors %}
<div class="submit-row">
<div class="alert alert-danger alert-dismissable" role="alert">
<span class="sr-only">Error:</span>
{{ error }}
<button type="button" class="close" data-dismiss="alert" aria-label="Close"><span
aria-hidden="true">×</span></button>
</div>
</div>
{% endfor %}
{% include "components/errors.html" %}
<form method="post" accept-charset="utf-8" autocomplete="off" role="form" class="form-horizontal" id="setup-form">
<ul class="nav nav-pills nav-fill mb-4">
<li class="nav-item">

View File

@@ -12,22 +12,8 @@
<div class="container">
<div class="row">
<div class="col-md-6 offset-md-3">
{% for info in infos %}
<div class="alert alert-info alert-dismissable" role="alert">
<span class="sr-only"></span>
{{ info }}
<button type="button" class="close" data-dismiss="alert" aria-label="Close"><span
aria-hidden="true">×</span></button>
</div>
{% endfor %}
{% for error in errors %}
<div class="alert alert-danger alert-dismissable" role="alert">
<span class="sr-only">Error:</span>
{{ error }}
<button type="button" class="close" data-dismiss="alert" aria-label="Close"><span
aria-hidden="true">×</span></button>
</div>
{% endfor %}
{% include "components/errors.html" %}
<form method="POST">
<div class="form-group">
<label>Team Name:</label>

View File

@@ -12,22 +12,8 @@
<div class="container">
<div class="row">
<div class="col-md-6 offset-md-3">
{% for info in infos %}
<div class="alert alert-info alert-dismissable" role="alert">
<span class="sr-only"></span>
{{ info }}
<button type="button" class="close" data-dismiss="alert" aria-label="Close"><span
aria-hidden="true">×</span></button>
</div>
{% endfor %}
{% for error in errors %}
<div class="alert alert-danger alert-dismissable" role="alert">
<span class="sr-only">Error:</span>
{{ error }}
<button type="button" class="close" data-dismiss="alert" aria-label="Close"><span
aria-hidden="true">×</span></button>
</div>
{% endfor %}
{% include "components/errors.html" %}
<form method="POST">
<div class="form-group">
<label>Team Name:</label>

View File

@@ -6,10 +6,10 @@
{% block content %}
<div class="jumbotron">
<div class="container">
<h1 id="user-id" user-id="{{ user.id }}">{{ user.name }}</h1>
<h1>{{ user.name }}</h1>
{% if user.team_id %}
<h2 id="user-team-id" user-team-id="{{ user.team_id }}">
<h2>
<a href="{{ url_for('teams.private') }}">
<span class="badge badge-secondary">
{{ user.team.name }}
@@ -39,39 +39,21 @@
</h3>
{% endif %}
{% if user.team_id %}
<div class="user-team-info">
<h2 id="user-team-place" class="text-center">
{# This intentionally hides the user's place because this can be their internal profile. #}
{# Public page hiding is done at the route level #}
{% if scores_visible() and user.team.place %}
{{ user.team.place }} <small>place</small>
{% endif %}
</h2>
<h2 id="user-team-score" class="text-center">
{{ user.team.score }} <small>points</small>
</h2>
</div>
{% else %}
<div class="user-info">
<h2 id="user-place" class="text-center">
{# This intentionally hides the user's place because this can be their internal profile. #}
{# Public page hiding is done at the route level #}
{% if scores_visible() and user.place %}
{{ user.place }} <small>place</small>
{% endif %}
</h2>
<h2 id="user-score" class="text-center">
{% if score %}
{{ user.score }}
<small>points</small>
{% endif %}
</h2>
</div>
{% endif %}
<div>
<h2 class="text-center">
{% if account.place %}
{{ account.place }} <small>place</small>
{% endif %}
</h2>
<h2 class="text-center">
{% if account.place %}
{{ account.score }} <small>points</small>
{% endif %}
</h2>
</div>
<div class="pt-3">
{% if user.website and (user.website.startswith('http://') or user.website.startswith('https://')) %}
{% if user.website %}
<a href="{{ user.website }}" target="_blank" style="color: inherit;">
<i class="fas fa-external-link-alt fa-2x px-2" data-toggle="tooltip" data-placement="top"
title="{{ user.website }}"></i>
@@ -81,103 +63,83 @@
</div>
</div>
<div class="container">
{% if errors %}
<div id='errors' class="row">
{% for error in errors %}
<h1>{{ error }}</h1>
{% include "components/errors.html" %}
{% if user.solves %}
<div id="keys-pie-graph" class="w-50 mr-0 pr-0 float-left d-none d-md-block d-lg-block">
<div class="text-center">
<i class="fas fa-circle-notch fa-spin fa-3x fa-fw spinner"></i>
</div>
</div>
<div id="categories-pie-graph" class="w-50 mr-0 pr-0 float-left d-none d-md-block d-lg-block">
<div class="text-center">
<i class="fas fa-circle-notch fa-spin fa-3x fa-fw spinner"></i>
</div>
</div>
<br class="clearfix">
<div id="score-graph" class="w-100 float-right d-none d-md-block d-lg-block">
<div class="text-center">
<i class="fas fa-circle-notch fa-spin fa-3x fa-fw spinner"></i>
</div>
</div>
<div class="clearfix"></div>
<div class="row">
<div class="col-md-12">
<h3>Awards</h3>
</div>
{% for award in user.awards %}
<div class="col-md-3 col-sm-6">
<p class="text-center">
<i class="award-icon award-{{ award.icon }} fa-2x"></i>
<br>
<strong>{{ award.name }}</strong>
</p>
<p class="text-center">{{ award.category or ""}}</p>
<p class="text-center">{{ award.description or ""}}</p>
<p class="text-center">{{ award.value }}</p>
</div>
{% endfor %}
</div>
{% else %}
{% if score_frozen %}
<div class="row">
<h1 class="text-center">Scoreboard has been frozen.</h1>
</div>
{% endif %}
<br>
{% set solves = user.solves %}
{% set awards = user.awards %}
{% if solves %}
<div id="keys-pie-graph" class="w-50 mr-0 pr-0 float-left d-none d-md-block d-lg-block">
<div class="text-center">
<i class="fas fa-circle-notch fa-spin fa-3x fa-fw spinner"></i>
</div>
</div>
<div id="categories-pie-graph" class="w-50 mr-0 pr-0 float-left d-none d-md-block d-lg-block">
<div class="text-center">
<i class="fas fa-circle-notch fa-spin fa-3x fa-fw spinner"></i>
</div>
</div>
<br class="clearfix">
<div id="score-graph" class="w-100 float-right d-none d-md-block d-lg-block">
<div class="text-center">
<i class="fas fa-circle-notch fa-spin fa-3x fa-fw spinner"></i>
</div>
</div>
<div class="clearfix"></div>
{% if awards %}
<div class="row">
<div class="col-md-12">
<h3>Awards</h3>
</div>
{% for award in awards %}
<div class="col-md-3 col-sm-6">
<p class="text-center">
<i class="award-icon award-{{ award.icon }} fa-2x"></i>
<br>
<strong>{{ award.name }}</strong>
</p>
{% if award.category %}<p class="text-center">{{ award.category }}</p>{% endif %}
{% if award.description %}<p class="text-center">{{ award.description }}</p>{% endif %}
<p class="text-center">{{ award.value }}</p>
</div>
{% endfor %}
</div>
<br>
{% endif %}
<div class="row">
<div class="col-md-12">
<h3>Solves</h3>
<table class="table table-striped">
<thead>
<div class="row">
<div class="col-md-12">
<h3>Solves</h3>
<table class="table table-striped">
<thead>
<tr>
<td><b>Challenge</b></td>
<td class="d-none d-md-block d-lg-block"><b>Category</b></td>
<td><b>Value</b></td>
<td><b>Time</b></td>
</tr>
</thead>
<tbody>
{% for solve in user.solves %}
<tr>
<td><b>Challenge</b></td>
<td class="d-none d-md-block d-lg-block"><b>Category</b></td>
<td><b>Value</b></td>
<td><b>Time</b></td>
<td>
<a href="{{ url_for('challenges.listing') }}#{{ solve.challenge.name }}">{{ solve.challenge.name }}</a>
</td>
<td class="d-none d-md-block d-lg-block">{{ solve.challenge.category }}</td>
<td>{{ solve.challenge.value }}</td>
<td class="solve-time">
<span data-time="{{ solve.date | isoformat }}"></span>
</td>
</tr>
</thead>
<tbody>
{% for solve in solves %}
<tr>
<td>
<a href="{{ url_for('challenges.listing') }}#{{ solve.challenge.name }}">{{ solve.challenge.name }}</a>
</td>
<td class="d-none d-md-block d-lg-block">{{ solve.challenge.category }}</td>
<td>{{ solve.challenge.value }}</td>
<td class="solve-time">
<span data-time="{{ solve.date | isoformat }}"></span>
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
{% endfor %}
</tbody>
</table>
</div>
{% else %}
<div class="row min-vh-25">
<h3 class="opacity-50 text-center w-100 justify-content-center align-self-center">
No solves yet
</h3>
</div>
{% endif %}
</div>
{% else %}
<div class="row min-vh-25">
<h3 class="opacity-50 text-center w-100 justify-content-center align-self-center">
No solves yet
</h3>
</div>
{% endif %}
</div>
{% endblock %}

View File

@@ -6,10 +6,10 @@
{% block content %}
<div class="jumbotron">
<div class="container">
<h1 id="user-id" user-id="{{ user.id }}">{{ user.name }}</h1>
<h1>{{ user.name }}</h1>
{% if user.team_id %}
<h2 id="user-team-id" user-team-id="{{ user.team_id }}">
<h2>
<a href="{{ url_for('teams.public', team_id=user.team_id) }}">
<span class="badge badge-secondary">
{{ user.team.name }}
@@ -39,42 +39,21 @@
</h3>
{% endif %}
{% if user.team_id %}
<div class="user-team-info">
<h2 id="user-team-place" class="text-center">
{# This intentionally hides the user's place because this can be their internal profile. #}
{# Public page hiding is done at the route level #}
{% if scores_visible() and user.team.place %}
{{ user.team.place }}
<small>place</small>
{% endif %}
</h2>
<h2 id="user-team-score" class="text-center">
{{ user.team.score }}
<small>points</small>
</h2>
</div>
{% else %}
<div class="user-info">
<h2 id="user-place" class="text-center">
{# This intentionally hides the user's place because this can be their internal profile. #}
{# Public page hiding is done at the route level #}
{% if scores_visible() and user.place %}
{{ user.place }}
<small>place</small>
{% endif %}
</h2>
<h2 id="user-score" class="text-center">
{% if user.score %}
{{ user.score }}
<small>points</small>
{% endif %}
</h2>
</div>
{% endif %}
<div>
<h2 class="text-center">
{% if account.place %}
{{ account.place }} <small>place</small>
{% endif %}
</h2>
<h2 class="text-center">
{% if account.place %}
{{ account.score }} <small>points</small>
{% endif %}
</h2>
</div>
<div class="pt-3">
{% if user.website and (user.website.startswith('http://') or user.website.startswith('https://')) %}
{% if user.website %}
<a href="{{ user.website }}" target="_blank" style="color: inherit;">
<i class="fas fa-external-link-alt fa-2x px-2" data-toggle="tooltip" data-placement="top"
title="{{ user.website }}"></i>
@@ -84,103 +63,83 @@
</div>
</div>
<div class="container">
{% if errors %}
<div id='errors' class="row">
{% for error in errors %}
<h1>{{ error }}</h1>
{% include "components/errors.html" %}
{% if user.solves %}
<div id="keys-pie-graph" class="w-50 mr-0 pr-0 float-left d-none d-md-block d-lg-block">
<div class="text-center">
<i class="fas fa-circle-notch fa-spin fa-3x fa-fw spinner"></i>
</div>
</div>
<div id="categories-pie-graph" class="w-50 mr-0 pr-0 float-left d-none d-md-block d-lg-block">
<div class="text-center">
<i class="fas fa-circle-notch fa-spin fa-3x fa-fw spinner"></i>
</div>
</div>
<br class="clearfix">
<div id="score-graph" class="w-100 float-right d-none d-md-block d-lg-block">
<div class="text-center">
<i class="fas fa-circle-notch fa-spin fa-3x fa-fw spinner"></i>
</div>
</div>
<div class="clearfix"></div>
<div class="row">
<div class="col-md-12">
<h3>Awards</h3>
</div>
{% for award in user.awards %}
<div class="col-md-3 col-sm-6">
<p class="text-center">
<i class="award-icon award-{{ award.icon }} fa-2x"></i>
<br>
<strong>{{ award.name }}</strong>
</p>
<p class="text-center">{{ award.category or "" }}</p>
<p class="text-center">{{ award.description or "" }}</p>
<p class="text-center">{{ award.value }}</p>
</div>
{% endfor %}
</div>
{% else %}
{% if score_frozen %}
<div class="row">
<h1 class="text-center">Scoreboard has been frozen.</h1>
</div>
{% endif %}
<br>
{% set solves = user.solves %}
{% set awards = user.awards %}
{% if solves %}
<div id="keys-pie-graph" class="w-50 mr-0 pr-0 float-left d-none d-md-block d-lg-block">
<div class="text-center">
<i class="fas fa-circle-notch fa-spin fa-3x fa-fw spinner"></i>
</div>
</div>
<div id="categories-pie-graph" class="w-50 mr-0 pr-0 float-left d-none d-md-block d-lg-block">
<div class="text-center">
<i class="fas fa-circle-notch fa-spin fa-3x fa-fw spinner"></i>
</div>
</div>
<br class="clearfix">
<div id="score-graph" class="w-100 float-right d-none d-md-block d-lg-block">
<div class="text-center">
<i class="fas fa-circle-notch fa-spin fa-3x fa-fw spinner"></i>
</div>
</div>
<div class="clearfix"></div>
{% if awards %}
<div class="row">
<div class="col-md-12">
<h3>Awards</h3>
</div>
{% for award in awards %}
<div class="col-md-3 col-sm-6">
<p class="text-center">
<i class="award-icon award-{{ award.icon }} fa-2x"></i>
<br>
<strong>{{ award.name }}</strong>
</p>
{% if award.category %}<p class="text-center">{{ award.category }}</p>{% endif %}
{% if award.description %}<p class="text-center">{{ award.description }}</p>{% endif %}
<p class="text-center">{{ award.value }}</p>
</div>
{% endfor %}
</div>
<br>
{% endif %}
<div class="row">
<div class="col-md-12">
<h3>Solves</h3>
<table class="table table-striped">
<thead>
<div class="row">
<div class="col-md-12">
<h3>Solves</h3>
<table class="table table-striped">
<thead>
<tr>
<td><b>Challenge</b></td>
<td class="d-none d-md-block d-lg-block"><b>Category</b></td>
<td><b>Value</b></td>
<td><b>Time</b></td>
</tr>
</thead>
<tbody>
{% for solve in user.solves %}
<tr>
<td><b>Challenge</b></td>
<td class="d-none d-md-block d-lg-block"><b>Category</b></td>
<td><b>Value</b></td>
<td><b>Time</b></td>
<td>
<a href="{{ url_for('challenges.listing') }}#{{ solve.challenge.name }}">{{ solve.challenge.name }}</a>
</td>
<td class="d-none d-md-block d-lg-block">{{ solve.challenge.category }}</td>
<td>{{ solve.challenge.value }}</td>
<td class="solve-time">
<span data-time="{{ solve.date | isoformat }}"></span>
</td>
</tr>
</thead>
<tbody>
{% for solve in solves %}
<tr>
<td>
<a href="{{ url_for('challenges.listing') }}#{{ solve.challenge.name }}">{{ solve.challenge.name }}</a>
</td>
<td class="d-none d-md-block d-lg-block">{{ solve.challenge.category }}</td>
<td>{{ solve.challenge.value }}</td>
<td class="solve-time">
<span data-time="{{ solve.date | isoformat }}"></span>
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
{% endfor %}
</tbody>
</table>
</div>
{% else %}
<div class="row min-vh-25">
<h3 class="opacity-50 text-center w-100 justify-content-center align-self-center">
No solves yet
</h3>
</div>
{% endif %}
</div>
{% else %}
<div class="row min-vh-25">
<h3 class="opacity-50 text-center w-100 justify-content-center align-self-center">
No solves yet
</h3>
</div>
{% endif %}
</div>
{% endblock %}

View File

@@ -7,6 +7,7 @@ from CTFd.utils.decorators.visibility import (
check_account_visibility,
check_score_visibility,
)
from CTFd.utils.helpers import get_errors, get_infos
from CTFd.utils.user import get_current_user
users = Blueprint("users", __name__)
@@ -20,7 +21,7 @@ def listing():
users = (
Users.query.filter_by(banned=False, hidden=False)
.order_by(Users.id.asc())
.paginate(page=page, per_page=10)
.paginate(page=page, per_page=50)
)
return render_template("users/users.html", users=users)
@@ -30,22 +31,20 @@ def listing():
@users.route("/user")
@authed_only
def private():
infos = get_infos()
errors = get_errors()
user = get_current_user()
solves = user.get_solves()
awards = user.get_awards()
place = user.place
score = user.score
if config.is_scoreboard_frozen():
infos.append("Scoreboard has been frozen")
return render_template(
"users/private.html",
solves=solves,
awards=awards,
user=user,
score=score,
place=place,
score_frozen=config.is_scoreboard_frozen(),
account=user.account,
infos=infos,
errors=errors,
)
@@ -53,5 +52,13 @@ def private():
@check_account_visibility
@check_score_visibility
def public(user_id):
infos = get_infos()
errors = get_errors()
user = Users.query.filter_by(id=user_id, banned=False, hidden=False).first_or_404()
return render_template("users/public.html", user=user)
if config.is_scoreboard_frozen():
infos.append("Scoreboard has been frozen")
return render_template(
"users/public.html", user=user, account=user.account, infos=infos, errors=errors
)

View File

@@ -1,6 +1,14 @@
import os
from flask import current_app, flash, get_flashed_messages, request
from markupsafe import Markup
def markup(text):
"""
Mark text as safe to inject as HTML into templates
"""
return Markup(text)
def info_for(endpoint, message):

View File

@@ -8,6 +8,8 @@ from sqlalchemy.exc import IntegrityError, InvalidRequestError
from werkzeug.middleware.dispatcher import DispatcherMiddleware
from CTFd.cache import clear_user_recent_ips
from CTFd.constants.config import Configs
from CTFd.constants.plugins import Plugins
from CTFd.exceptions import UserNotFoundException, UserTokenExpiredException
from CTFd.models import Tracking, db
from CTFd.utils import config, get_config, markdown
@@ -87,6 +89,8 @@ def init_template_globals(app):
app.jinja_env.globals.update(get_current_user_attrs=get_current_user_attrs)
app.jinja_env.globals.update(get_current_team_attrs=get_current_team_attrs)
app.jinja_env.globals.update(get_ip=get_ip)
app.jinja_env.globals.update(Configs=Configs)
app.jinja_env.globals.update(Plugins=Plugins)
def init_logs(app):

View File

@@ -35,7 +35,7 @@ from CTFd.utils.email import (
DEFAULT_VERIFICATION_EMAIL_BODY,
DEFAULT_VERIFICATION_EMAIL_SUBJECT,
)
from CTFd.utils.helpers import get_errors
from CTFd.utils.helpers import get_errors, get_infos, markup
from CTFd.utils.modes import USERS_MODE
from CTFd.utils.security.auth import login_user
from CTFd.utils.security.csrf import generate_nonce
@@ -272,6 +272,8 @@ def notifications():
@views.route("/settings", methods=["GET"])
@authed_only
def settings():
infos = get_infos()
user = get_current_user()
name = user.name
email = user.email
@@ -282,7 +284,17 @@ def settings():
tokens = UserTokens.query.filter_by(user_id=user.id).all()
prevent_name_change = get_config("prevent_name_change")
confirm_email = get_config("verify_emails") and not user.verified
if get_config("verify_emails") and not user.verified:
confirm_url = markup(url_for("auth.confirm"))
infos.append(
markup(
"Your email address isn't confirmed!<br>"
"Please check your email to confirm your email address.<br><br>"
f'To have the confirmation email resent please <a href="{confirm_url}">click here</a>.'
)
)
return render_template(
"settings.html",
name=name,
@@ -292,7 +304,7 @@ def settings():
country=country,
tokens=tokens,
prevent_name_change=prevent_name_change,
confirm_email=confirm_email,
infos=infos,
)