Merge immortal/main into audit/securite-maintenabilite-standards
This commit is contained in:
Binary file not shown.
|
After Width: | Height: | Size: 148 KiB |
@@ -96,6 +96,7 @@ a:hover { color: var(--primary-dark); }
|
||||
}
|
||||
|
||||
.logo i { color: var(--primary); font-size: 1.6rem; }
|
||||
.logo-img { height: 40px; width: auto; }
|
||||
|
||||
.user-badge {
|
||||
display: flex;
|
||||
@@ -621,6 +622,12 @@ a:hover { color: var(--primary-dark); }
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
.auth-logo-img {
|
||||
height: 80px;
|
||||
width: auto;
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
.auth-header h2 {
|
||||
font-size: 1.6rem;
|
||||
font-weight: 700;
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 148 KiB |
@@ -1,143 +0,0 @@
|
||||
"""Development HTTPS server for testing production settings locally.
|
||||
|
||||
Generates a self-signed certificate (if not present) and runs the
|
||||
application via Waitress wrapped in a TLS socket. This simulates the
|
||||
production environment where Nginx handles TLS termination.
|
||||
|
||||
Usage:
|
||||
python run_https.py
|
||||
|
||||
The server will listen on https://localhost:8443
|
||||
Accept the self-signed certificate warning in your browser to proceed.
|
||||
"""
|
||||
|
||||
import os
|
||||
import socket
|
||||
import ssl
|
||||
import subprocess
|
||||
import sys
|
||||
|
||||
from waitress.server import create_server
|
||||
|
||||
from app.app import create_app
|
||||
|
||||
CERT_FILE = 'certs/localhost.pem'
|
||||
KEY_FILE = 'certs/localhost-key.pem'
|
||||
|
||||
|
||||
def generate_self_signed_cert():
|
||||
"""Generate a self-signed certificate for local HTTPS testing.
|
||||
|
||||
Uses OpenSSL to create a key and certificate valid for 365 days.
|
||||
Skips generation if certificate files already exist.
|
||||
"""
|
||||
if os.path.exists(CERT_FILE) and os.path.exists(KEY_FILE):
|
||||
print('[OK] Self-signed certificate already exists.')
|
||||
return
|
||||
|
||||
os.makedirs('certs', exist_ok=True)
|
||||
|
||||
print('[INFO] Generating self-signed certificate for localhost...')
|
||||
try:
|
||||
subprocess.run(
|
||||
[
|
||||
'openssl',
|
||||
'req',
|
||||
'-x509',
|
||||
'-newkey',
|
||||
'rsa:2048',
|
||||
'-keyout',
|
||||
KEY_FILE,
|
||||
'-out',
|
||||
CERT_FILE,
|
||||
'-days',
|
||||
'365',
|
||||
'-nodes',
|
||||
'-subj',
|
||||
'/CN=localhost',
|
||||
],
|
||||
check=True,
|
||||
capture_output=True,
|
||||
)
|
||||
print('[OK] Certificate generated: certs/localhost.pem')
|
||||
except FileNotFoundError:
|
||||
print('[ERROR] OpenSSL not found. Install OpenSSL or use:')
|
||||
print(' winget install OpenSSL.OpenSSL')
|
||||
print(' OR download from https://slproweb.com/products/Win32OpenSSL.html')
|
||||
sys.exit(1)
|
||||
except subprocess.CalledProcessError as e:
|
||||
print(f'[ERROR] Certificate generation failed: {e}')
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
def main():
|
||||
"""Start the HTTPS development server."""
|
||||
port = int(os.getenv('HTTPS_PORT', 8443))
|
||||
host = os.getenv('HOST', '127.0.0.1')
|
||||
|
||||
# Ensure certificate exists
|
||||
generate_self_signed_cert()
|
||||
|
||||
# Create the Flask application
|
||||
app = create_app()
|
||||
|
||||
# Create SSL context
|
||||
context = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER)
|
||||
context.load_cert_chain(CERT_FILE, KEY_FILE)
|
||||
|
||||
# Create a TCP socket, wrap it with TLS, then pass to Waitress
|
||||
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM, 0)
|
||||
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
|
||||
sock.bind((host, port))
|
||||
sock.listen(5)
|
||||
|
||||
# Wrap the socket with TLS
|
||||
ssl_sock = context.wrap_socket(sock, server_side=True)
|
||||
|
||||
# =====================================================================
|
||||
# WSGI Middleware: Tell Flask the connection is HTTPS
|
||||
#
|
||||
# Waitress doesn't know the underlying socket is TLS, so Flask sees
|
||||
# wsgi.url_scheme = "http". Without this middleware, force_https()
|
||||
# would cause an infinite redirect loop (ERR_TOO_MANY_REDIRECTS).
|
||||
# =====================================================================
|
||||
class ForceHTTPSMiddleware:
|
||||
"""WSGI middleware that sets url_scheme to 'https'.
|
||||
|
||||
Since we're wrapping the TCP socket with SSL before passing it
|
||||
to Waitress, Flask's request.is_secure returns False because
|
||||
Waitress reports wsgi.url_scheme='http'. This middleware fixes
|
||||
that so Flask correctly identifies the connection as HTTPS.
|
||||
"""
|
||||
|
||||
def __init__(self, wsgi_app):
|
||||
self.wsgi_app = wsgi_app
|
||||
|
||||
def __call__(self, environ, start_response):
|
||||
environ['wsgi.url_scheme'] = 'https'
|
||||
environ['HTTPS'] = 'on'
|
||||
return self.wsgi_app(environ, start_response)
|
||||
|
||||
# Wrap the Flask app with the HTTPS middleware
|
||||
app.wsgi_app = ForceHTTPSMiddleware(app.wsgi_app)
|
||||
|
||||
print('\n╔══════════════════════════════════════════════════════╗')
|
||||
print('║ TEAM TRYOUTS - Development HTTPS Server ║')
|
||||
print('╠══════════════════════════════════════════════════════╣')
|
||||
print(f'║ URL: https://{host}:{port} ║')
|
||||
print('║ Cert: self-signed (accept browser warning) ║')
|
||||
print('║ Press Ctrl+C to stop ║')
|
||||
print('╚══════════════════════════════════════════════════════╝\n')
|
||||
|
||||
# Create Waitress server with the SSL-wrapped socket
|
||||
server = create_server(
|
||||
app,
|
||||
sockets=[ssl_sock],
|
||||
threads=4,
|
||||
channel_timeout=30,
|
||||
)
|
||||
server.run()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
@@ -1,5 +1,5 @@
|
||||
{% extends "layouts/base.html" %}
|
||||
{% block title %}{{ _('400 Bad Request') }} - TryoutPro{% endblock %}
|
||||
{% block title %}{{ _('400 Bad Request') }} - UdeS team manager{% endblock %}
|
||||
{% block page_title %}{{ _('Bad Request') }}{% endblock %}
|
||||
{% block content %}
|
||||
<div class="error-container">
|
||||
@@ -12,4 +12,4 @@
|
||||
<i class="fas fa-arrow-left"></i> {{ _('Go Back') }}
|
||||
</a>
|
||||
</div>
|
||||
{% endblock %}
|
||||
{% endblock %}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{% extends "layouts/base.html" %}
|
||||
{% block title %}{{ _('403 Forbidden') }} - TryoutPro{% endblock %}
|
||||
{% block title %}{{ _('403 Forbidden') }} - UdeS team manager{% endblock %}
|
||||
{% block page_title %}{{ _('Access Denied') }}{% endblock %}
|
||||
{% block content %}
|
||||
<div class="error-container">
|
||||
@@ -12,4 +12,4 @@
|
||||
<i class="fas fa-arrow-left"></i> {{ _('Go Back') }}
|
||||
</a>
|
||||
</div>
|
||||
{% endblock %}
|
||||
{% endblock %}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{% extends "layouts/base.html" %}
|
||||
{% block title %}{{ _('404 Not Found') }} - TryoutPro{% endblock %}
|
||||
{% block title %}{{ _('404 Not Found') }} - UdeS team manager{% endblock %}
|
||||
{% block page_title %}{{ _('Page Not Found') }}{% endblock %}
|
||||
{% block content %}
|
||||
<div class="error-container">
|
||||
@@ -12,4 +12,4 @@
|
||||
<i class="fas fa-home"></i> {{ _('Return Home') }}
|
||||
</a>
|
||||
</div>
|
||||
{% endblock %}
|
||||
{% endblock %}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{% extends "layouts/base.html" %}
|
||||
{% block title %}{{ _('429 Too Many Requests') }} - TryoutPro{% endblock %}
|
||||
{% block title %}{{ _('429 Too Many Requests') }} - UdeS team manager{% endblock %}
|
||||
{% block page_title %}{{ _('Rate Limit Exceeded') }}{% endblock %}
|
||||
{% block content %}
|
||||
<div class="error-container">
|
||||
@@ -12,4 +12,4 @@
|
||||
<i class="fas fa-arrow-left"></i> {{ _('Go Back') }}
|
||||
</a>
|
||||
</div>
|
||||
{% endblock %}
|
||||
{% endblock %}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{% extends "layouts/base.html" %}
|
||||
{% block title %}{{ _('500 Server Error') }} - TryoutPro{% endblock %}
|
||||
{% block title %}{{ _('500 Server Error') }} - UdeS team manager{% endblock %}
|
||||
{% block page_title %}{{ _('Internal Server Error') }}{% endblock %}
|
||||
{% block content %}
|
||||
<div class="error-container">
|
||||
@@ -18,4 +18,4 @@
|
||||
<i class="fas fa-redo-alt"></i> {{ _('Try Again') }}
|
||||
</a>
|
||||
</div>
|
||||
{% endblock %}
|
||||
{% endblock %}
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>{% block title %}Team Tryout Management{% endblock %}</title>
|
||||
<title>{% block title %}UdeS team manager{% endblock %}</title>
|
||||
{# Subresource integrity (QUA-004). Without it, whoever controls the CDN
|
||||
controls what runs on every page of this site — and the CSP names these
|
||||
hosts as allowed, so it would not object.
|
||||
@@ -28,8 +28,8 @@
|
||||
<nav class="sidebar" id="sidebar">
|
||||
<div class="sidebar-header">
|
||||
<div class="logo">
|
||||
<i class="fas fa-trophy"></i>
|
||||
<span>TryoutPro</span>
|
||||
<img src="{{ url_for('static', filename='images/UdeS_logo.png') }}" alt="UdeS Logo" class="logo-img">
|
||||
<span>UdeS team manager</span>
|
||||
</div>
|
||||
<div class="user-badge">
|
||||
<div class="user-avatar">
|
||||
@@ -100,12 +100,6 @@
|
||||
</li>
|
||||
{% endif %}
|
||||
{% if current_user.role == 'coach' %}
|
||||
<li>
|
||||
<a href="{{ url_for('users.manage_coach_availability') }}" class="{% if request.endpoint == 'users.manage_coach_availability' %}active{% endif %}">
|
||||
<i class="fas fa-clock"></i>
|
||||
<span>{{ _('Availability') }}</span>
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="{{ url_for('users.notes_dashboard') }}" class="{% if request.endpoint == 'users.notes_dashboard' or request.endpoint == 'users.manage_team_notes' or request.endpoint == 'users.manage_personal_notes' %}active{% endif %}">
|
||||
<i class="fas fa-sticky-note"></i>
|
||||
@@ -194,9 +188,9 @@
|
||||
</div>
|
||||
<div class="auth-container">
|
||||
<div class="auth-header">
|
||||
<i class="fas fa-trophy"></i>
|
||||
<h2>TryoutPro</h2>
|
||||
<p>{{ _('Team Tryout Management System') }}</p>
|
||||
<img src="{{ url_for('static', filename='images/UdeS_logo.png') }}" alt="UdeS Logo" class="auth-logo-img">
|
||||
<h2>UdeS team manager</h2>
|
||||
<p>{{ _('UdeS team manager') }}</p>
|
||||
</div>
|
||||
<div class="auth-language">
|
||||
{% include "layouts/_language_switcher.html" %}
|
||||
@@ -223,4 +217,4 @@
|
||||
<script src="{{ url_for('static', filename='js/main.js') }}"></script>
|
||||
{% block scripts %}{% endblock %}
|
||||
</body>
|
||||
</html>
|
||||
</html>
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
|
||||
{# Page Header Macro - renders title and breadcrumb #}
|
||||
{% macro page_header(title, breadcrumb) %}
|
||||
{% block title %}{{ title }} - TryoutPro{% endblock %}
|
||||
{% block title %}{{ title }} - UdeS team manager{% endblock %}
|
||||
{% block page_title %}{{ title }}{% endblock %}
|
||||
{% block breadcrumb %}<span class="breadcrumb">{{ breadcrumb }}</span>{% endblock %}
|
||||
{% endmacro %}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{% extends "layouts/base.html" %}
|
||||
{% block title %}{{ _('Add Personal Note') }} - TryoutPro{% endblock %}
|
||||
{% block title %}{{ _('Add Personal Note') }} - UdeS team manager{% endblock %}
|
||||
{% block page_title %}{{ _('Add Personal Note') }}{% endblock %}
|
||||
{% block breadcrumb %}
|
||||
<span class="breadcrumb">
|
||||
@@ -149,4 +149,4 @@
|
||||
font-style: italic;
|
||||
}
|
||||
</style>
|
||||
{% endblock %}
|
||||
{% endblock %}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{% extends "layouts/base.html" %}
|
||||
{% block title %}{{ _('Calendar') }} - TryoutPro{% endblock %}
|
||||
{% block title %}{{ _('Calendar') }} - UdeS team manager{% endblock %}
|
||||
{% block page_title %}{{ _('Calendar') }}{% endblock %}
|
||||
{% block breadcrumb %}<span class="breadcrumb">Home / Calendar</span>{% endblock %}
|
||||
|
||||
@@ -459,4 +459,4 @@ registerActions({
|
||||
'hide-event-modal': hideEventModal,
|
||||
});
|
||||
</script>
|
||||
{% endblock %}
|
||||
{% endblock %}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{% extends "layouts/base.html" %}
|
||||
{% block title %}{{ _('Manage Availability') }} - TryoutPro{% endblock %}
|
||||
{% block title %}{{ _('Manage Availability') }} - UdeS team manager{% endblock %}
|
||||
{% block page_title %}{{ _('Manage Availability') }}{% endblock %}
|
||||
{% block breadcrumb %}<span class="breadcrumb">Home / Coach Availability</span>{% endblock %}
|
||||
|
||||
@@ -15,6 +15,9 @@
|
||||
</div>
|
||||
|
||||
<div class="form-actions mt-4">
|
||||
<button type="button" class="btn btn-primary" data-action="save-availability">
|
||||
<i class="fas fa-save"></i> {{ _('Save Availability') }}
|
||||
</button>
|
||||
<button type="button" class="btn btn-secondary" data-action="clear-availability">
|
||||
<i class="fas fa-trash"></i> {{ _('Clear All') }}
|
||||
</button>
|
||||
@@ -170,13 +173,18 @@ function saveAvailability() {
|
||||
|
||||
fetch('{{ url_for("users.manage_coach_availability") }}', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'X-CSRFToken': '{{ csrf_token() }}'
|
||||
},
|
||||
body: JSON.stringify({ slots: slots })
|
||||
})
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
if (data.success) {
|
||||
flash('Availability saved!', 'success');
|
||||
} else {
|
||||
flash(data.error || 'Error saving availability.', 'danger');
|
||||
}
|
||||
})
|
||||
.catch(function(error) {
|
||||
@@ -190,7 +198,10 @@ function clearAllAvailability() {
|
||||
return;
|
||||
}
|
||||
|
||||
fetch('{{ url_for("users.clear_coach_availability") }}', { method: 'POST' })
|
||||
fetch('{{ url_for("users.clear_coach_availability") }}', {
|
||||
method: 'POST',
|
||||
headers: { 'X-CSRFToken': '{{ csrf_token() }}' }
|
||||
})
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
if (data.success) {
|
||||
@@ -222,8 +233,9 @@ document.addEventListener('click', function(e) {
|
||||
// dispatched by the delegated listener in main.js. This replaces inline
|
||||
// onclick attributes, which no CSP nonce is able to authorise.
|
||||
registerActions({
|
||||
'save-availability': saveAvailability,
|
||||
'clear-availability': clearAllAvailability,
|
||||
'toggle-slot': toggleSlot,
|
||||
});
|
||||
</script>
|
||||
{% endblock %}
|
||||
{% endblock %}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{% extends "layouts/base.html" %}
|
||||
{% block title %}{{ _('Contracts') }} - TryoutPro{% endblock %}
|
||||
{% block title %}{{ _('Contracts') }} - UdeS team manager{% endblock %}
|
||||
{% block page_title %}{{ _('Contracts') }}{% endblock %}
|
||||
{% block breadcrumb %}<span class="breadcrumb">Home / <a href="{{ url_for('users.profile') }}">Profile</a> / Contracts</span>{% endblock %}
|
||||
|
||||
@@ -127,4 +127,4 @@ registerActions({
|
||||
'hide-upload-signed': hideUploadSignedForm,
|
||||
});
|
||||
</script>
|
||||
{% endblock %}
|
||||
{% endblock %}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{% extends "layouts/base.html" %}
|
||||
{% block title %}{{ _('Create User') }} - TryoutPro{% endblock %}
|
||||
{% block title %}{{ _('Create User') }} - UdeS team manager{% endblock %}
|
||||
{% block page_title %}{{ _('Create User') }}{% endblock %}
|
||||
{% block breadcrumb %}<span class="breadcrumb">Home / <a href="{{ url_for('users.list_users') }}">Users</a> / Create</span>{% endblock %}
|
||||
|
||||
@@ -49,4 +49,4 @@
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
{% endblock %}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{% extends "layouts/base.html" %}
|
||||
{% block title %}{{ _('Dashboard') }} - TryoutPro{% endblock %}
|
||||
{% block title %}{{ _('Dashboard') }} - UdeS team manager{% endblock %}
|
||||
{% block page_title %}{{ _('Dashboard') }}{% endblock %}
|
||||
{% block breadcrumb %}<span class="breadcrumb">Home / Dashboard</span>{% endblock %}
|
||||
|
||||
@@ -378,4 +378,4 @@
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endblock %}
|
||||
{% endblock %}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{% extends "layouts/base.html" %}
|
||||
{% block title %}{{ _('Edit Profile') }} - TryoutPro{% endblock %}
|
||||
{% block title %}{{ _('Edit Profile') }} - UdeS team manager{% endblock %}
|
||||
{% block page_title %}{{ _('Edit Profile') }}{% endblock %}
|
||||
{% block breadcrumb %}<span class="breadcrumb">Home / <a href="{{ url_for('users.profile') }}">Profile</a> / Edit</span>{% endblock %}
|
||||
|
||||
@@ -148,4 +148,4 @@ document.addEventListener('DOMContentLoaded', function() {
|
||||
toggleGamertagInputs();
|
||||
});
|
||||
</script>
|
||||
{% endblock %}
|
||||
{% endblock %}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{% extends "layouts/base.html" %}
|
||||
{% block title %}Edit {{ user.username }} - TryoutPro{% endblock %}
|
||||
{% block title %}Edit {{ user.username }} - UdeS team manager{% endblock %}
|
||||
{% block page_title %}{{ _('Edit User') }}{% endblock %}
|
||||
{% block breadcrumb %}<span class="breadcrumb">Home / <a href="{{ url_for('users.list_users') }}">Users</a> / Edit</span>{% endblock %}
|
||||
|
||||
@@ -148,4 +148,4 @@ document.addEventListener('DOMContentLoaded', function() {
|
||||
toggleGamertagInputs();
|
||||
});
|
||||
</script>
|
||||
{% endblock %}
|
||||
{% endblock %}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{% extends "layouts/base.html" %}
|
||||
{% block title %}Evaluate {{ player.username }} - TryoutPro{% endblock %}
|
||||
{% block title %}Evaluate {{ player.username }} - UdeS team manager{% endblock %}
|
||||
{% block page_title %}Evaluate {{ player.username }}{% endblock %}
|
||||
{% block breadcrumb %}<span class="breadcrumb">Home / <a href="{{ url_for('tryouts.view_tryout', tryout_id=tryout.id) }}">{{ tryout.title }}</a> / Evaluate</span>{% endblock %}
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{% extends "layouts/base.html" %}
|
||||
{% import 'layouts/_pagination.html' as pager %}
|
||||
{% block title %}{{ _('Evaluations') }} - TryoutPro{% endblock %}
|
||||
{% block title %}{{ _('Evaluations') }} - UdeS team manager{% endblock %}
|
||||
{% block page_title %}{{ _('Evaluations') }}{% endblock %}
|
||||
{% block breadcrumb %}<span class="breadcrumb">Home / Evaluations</span>{% endblock %}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{% extends "layouts/base.html" %}
|
||||
{% block title %}{{ _('Login') }} - TryoutPro{% endblock %}
|
||||
{% block title %}{{ _('Login') }} - UdeS team manager{% endblock %}
|
||||
{% block auth_content %}
|
||||
<form method="POST" action="{{ url_for('auth.login') }}" class="auth-form">
|
||||
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}"/>
|
||||
@@ -14,4 +14,4 @@
|
||||
<button type="submit" class="btn btn-primary btn-block">{{ _('Sign In') }}</button>
|
||||
<p class="auth-link">{{ _("Don't have an account?") }} <a href="{{ url_for('auth.register') }}">{{ _('Register here') }}</a></p>
|
||||
</form>
|
||||
{% endblock %}
|
||||
{% endblock %}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{% extends "layouts/base.html" %}
|
||||
{% block title %}{% if match %}Edit Match{% else %}Schedule Match{% endif %} - {{ tryout.title }} - TryoutPro{% endblock %}
|
||||
{% block title %}{% if match %}Edit Match{% else %}Schedule Match{% endif %} - {{ tryout.title }} - UdeS team manager{% endblock %}
|
||||
{% block page_title %}{% if match %}Edit Match{% else %}Schedule Match{% endif %}{% endblock %}
|
||||
{% block breadcrumb %}
|
||||
<span class="breadcrumb">
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{% extends "layouts/base.html" %}
|
||||
{% block title %}{{ _('My Team(s)') }} - TryoutPro{% endblock %}
|
||||
{% block title %}{{ _('My Team(s)') }} - UdeS team manager{% endblock %}
|
||||
{% block page_title %}{{ _('My Team(s)') }}{% endblock %}
|
||||
{% block breadcrumb %}<span class="breadcrumb">Home / My Team(s)</span>{% endblock %}
|
||||
|
||||
@@ -282,4 +282,4 @@ registerActions({
|
||||
color: #fcd34d;
|
||||
}
|
||||
</style>
|
||||
{% endblock %}
|
||||
{% endblock %}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{% extends "layouts/base.html" %}
|
||||
{% block title %}{{ _('Notes') }} - TryoutPro{% endblock %}
|
||||
{% block title %}{{ _('Notes') }} - UdeS team manager{% endblock %}
|
||||
{% block page_title %}{{ _('Notes') }}{% endblock %}
|
||||
{% block breadcrumb %}<span class="breadcrumb">Home / Notes</span>{% endblock %}
|
||||
|
||||
@@ -283,4 +283,4 @@ registerActions({
|
||||
'hide-reject-modal': hideRejectModal,
|
||||
});
|
||||
</script>
|
||||
{% endblock %}
|
||||
{% endblock %}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{% extends "layouts/base.html" %}
|
||||
{% block title %}{{ _('One on One') }} - TryoutPro{% endblock %}
|
||||
{% block title %}{{ _('One on One') }} - UdeS team manager{% endblock %}
|
||||
{% block page_title %}{{ _('One on One') }}{% endblock %}
|
||||
{% block breadcrumb %}<span class="breadcrumb">Home / One on One</span>{% endblock %}
|
||||
|
||||
@@ -314,4 +314,4 @@ registerActions({
|
||||
'update-end-times': updateEndTimeOptions,
|
||||
});
|
||||
</script>
|
||||
{% endblock %}
|
||||
{% endblock %}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{% extends "layouts/base.html" %}
|
||||
{% block title %}{{ _('Personal Notes') }} - TryoutPro{% endblock %}
|
||||
{% block title %}{{ _('Personal Notes') }} - UdeS team manager{% endblock %}
|
||||
{% block page_title %}{{ _('Personal Notes') }}{% endblock %}
|
||||
{% block breadcrumb %}<span class="breadcrumb">Home / Personal Notes</span>{% endblock %}
|
||||
|
||||
@@ -61,4 +61,4 @@
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
{% endblock %}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{% extends "layouts/base.html" %}
|
||||
{% block title %}{{ _('My Notes') }} - TryoutPro{% endblock %}
|
||||
{% block title %}{{ _('My Notes') }} - UdeS team manager{% endblock %}
|
||||
{% block page_title %}{{ _('My Notes') }}{% endblock %}
|
||||
{% block breadcrumb %}<span class="breadcrumb">Home / One on One / My Notes</span>{% endblock %}
|
||||
|
||||
@@ -82,4 +82,4 @@
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
{% endblock %}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{% extends "layouts/base.html" %}
|
||||
{% block title %}{{ _('Players to Evaluate') }} - TryoutPro{% endblock %}
|
||||
{% block title %}{{ _('Players to Evaluate') }} - UdeS team manager{% endblock %}
|
||||
{% block page_title %}{{ _('Players to Evaluate') }}{% endblock %}
|
||||
{% block breadcrumb %}<span class="breadcrumb">Home / <a href="{{ url_for('tryouts.view_tryout', tryout_id=tryout.id) }}">{{ tryout.title }}</a> / Evaluate</span>{% endblock %}
|
||||
|
||||
@@ -56,4 +56,4 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
{% endblock %}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{% extends "layouts/base.html" %}
|
||||
{% block title %}{{ _('My Profile') }} - TryoutPro{% endblock %}
|
||||
{% block title %}{{ _('My Profile') }} - UdeS team manager{% endblock %}
|
||||
{% block page_title %}{{ _('My Profile') }}{% endblock %}
|
||||
{% block breadcrumb %}<span class="breadcrumb">Home / Profile</span>{% endblock %}
|
||||
|
||||
@@ -357,7 +357,10 @@ function saveCoachAvailability() {
|
||||
}
|
||||
fetch('{{ url_for("users.manage_coach_availability") }}', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'X-CSRFToken': '{{ csrf_token() }}'
|
||||
},
|
||||
body: JSON.stringify({ slots: slots })
|
||||
})
|
||||
.then(r => r.json())
|
||||
@@ -376,7 +379,10 @@ function saveCoachAvailability() {
|
||||
|
||||
function clearAllAvailability() {
|
||||
if (!confirm('Are you sure you want to clear all your availability slots?')) return;
|
||||
fetch('{{ url_for("users.clear_coach_availability") }}', { method: 'POST' })
|
||||
fetch('{{ url_for("users.clear_coach_availability") }}', {
|
||||
method: 'POST',
|
||||
headers: { 'X-CSRFToken': '{{ csrf_token() }}' }
|
||||
})
|
||||
.then(r => r.json())
|
||||
.then(data => {
|
||||
if (data.success) {
|
||||
@@ -610,4 +616,4 @@ registerActions({
|
||||
});
|
||||
</script>
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
{% endblock %}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{% extends "layouts/base.html" %}
|
||||
{% block title %}{{ _('Register') }} - TryoutPro{% endblock %}
|
||||
{% block title %}{{ _('Register') }} - UdeS team manager{% endblock %}
|
||||
{% block auth_content %}
|
||||
<form method="POST" action="{{ url_for('auth.register') }}" class="auth-form">
|
||||
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}"/>
|
||||
@@ -53,7 +53,7 @@
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<a href="{{ url_for('auth.discord_login') }}" class="btn btn-sm btn-outline discord-reconnect">
|
||||
<a href="{{ url_for('auth.discord_login') }}" class="btn btn-sm btn-outline discord-reconnect" data-action="save-form-draft">
|
||||
<i class="fas fa-sync-alt"></i> {{ _('Reconnect') }}
|
||||
</a>
|
||||
</div>
|
||||
@@ -65,7 +65,7 @@
|
||||
</div>
|
||||
{% else %}
|
||||
<div class="form-group discord-connect-section">
|
||||
<a href="{{ url_for('auth.discord_login') }}" class="btn btn-discord btn-block">
|
||||
<a href="{{ url_for('auth.discord_login') }}" class="btn btn-discord btn-block" data-action="save-form-draft">
|
||||
<i class="fab fa-discord"></i> {{ _('Connect Discord Account') }}
|
||||
</a>
|
||||
<small class="form-text text-muted">{{ _('Connect to pre-fill your gamertags from Steam, Battle.net, Xbox, etc.') }}</small>
|
||||
@@ -170,8 +170,62 @@ function toggleGamertagInput(checkbox) {
|
||||
}
|
||||
}
|
||||
|
||||
// On page load, ensure gamertag groups match checkbox state
|
||||
// Save form draft to sessionStorage before navigating to Discord OAuth
|
||||
function saveFormDraft() {
|
||||
var draft = {};
|
||||
var form = document.querySelector('.auth-form');
|
||||
if (!form) return;
|
||||
var inputs = form.querySelectorAll('input, select, textarea');
|
||||
inputs.forEach(function(input) {
|
||||
if (!input.name) return;
|
||||
if (input.type === 'checkbox') {
|
||||
if (!draft[input.name]) draft[input.name] = [];
|
||||
if (input.checked) draft[input.name].push(input.value);
|
||||
} else if (input.type === 'password') {
|
||||
// Never save passwords
|
||||
} else {
|
||||
draft[input.name] = input.value;
|
||||
}
|
||||
});
|
||||
sessionStorage.setItem('register_form_draft', JSON.stringify(draft));
|
||||
}
|
||||
|
||||
// Restore form draft from sessionStorage on page load
|
||||
function restoreFormDraft() {
|
||||
var saved = sessionStorage.getItem('register_form_draft');
|
||||
if (!saved) return;
|
||||
try {
|
||||
var draft = JSON.parse(saved);
|
||||
var hasServerData = document.querySelector('.auth-form input[name="full_name"]').value !== '';
|
||||
if (hasServerData) return;
|
||||
for (var key in draft) {
|
||||
if (key === 'games') {
|
||||
var values = draft[key];
|
||||
var checkboxes = document.querySelectorAll('input[name="games"]');
|
||||
checkboxes.forEach(function(cb) {
|
||||
cb.checked = values.indexOf(cb.value) !== -1;
|
||||
toggleGamertagInput(cb);
|
||||
});
|
||||
} else if (key === 'csrf_token' || key.indexOf('password') !== -1) {
|
||||
// Skip CSRF token and passwords
|
||||
} else {
|
||||
var input = document.querySelector('input[name="' + key + '"], textarea[name="' + key + '"]');
|
||||
if (input) input.value = draft[key];
|
||||
}
|
||||
}
|
||||
} catch(e) {
|
||||
// Invalid JSON, ignore
|
||||
}
|
||||
}
|
||||
|
||||
// Clear draft on successful form submission
|
||||
document.querySelector('.auth-form').addEventListener('submit', function() {
|
||||
sessionStorage.removeItem('register_form_draft');
|
||||
});
|
||||
|
||||
// On page load, ensure gamertag groups match checkbox state and restore draft
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
restoreFormDraft();
|
||||
var checkboxes = document.querySelectorAll('input[name="games"]');
|
||||
checkboxes.forEach(function(checkbox) {
|
||||
toggleGamertagInput(checkbox);
|
||||
@@ -182,6 +236,7 @@ document.addEventListener('DOMContentLoaded', function() {
|
||||
// dispatched by the delegated listener in main.js. This replaces inline
|
||||
// onclick attributes, which no CSP nonce is able to authorise.
|
||||
registerActions({
|
||||
'save-form-draft': saveFormDraft,
|
||||
'toggle-gamertag': toggleGamertagInput,
|
||||
});
|
||||
</script>
|
||||
@@ -301,4 +356,4 @@ registerActions({
|
||||
color: #57F287 !important;
|
||||
}
|
||||
</style>
|
||||
{% endblock %}
|
||||
{% endblock %}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{% extends "layouts/base.html" %}
|
||||
{% block title %}{% if match %}Edit{% else %}Schedule{% endif %} Team Match - TryoutPro{% endblock %}
|
||||
{% block title %}{% if match %}Edit{% else %}Schedule{% endif %} Team Match - UdeS team manager{% endblock %}
|
||||
{% block page_title %}{% if match %}Edit{% else %}Schedule{% endif %} Team Match{% endblock %}
|
||||
{% block breadcrumb %}<span class="breadcrumb">Home / <a href="{{ url_for('team_matches.list_matches') }}">Team Matches</a> / {% if match %}Edit{% else %}New{% endif %}</span>{% endblock %}
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{% extends "layouts/base.html" %}
|
||||
{% import 'layouts/_pagination.html' as pager %}
|
||||
{% block title %}{{ _('Team Matches') }} - TryoutPro{% endblock %}
|
||||
{% block title %}{{ _('Team Matches') }} - UdeS team manager{% endblock %}
|
||||
{% block page_title %}{{ _('Team Matches') }}{% endblock %}
|
||||
{% block breadcrumb %}<span class="breadcrumb">Home / Team Matches</span>{% endblock %}
|
||||
|
||||
@@ -236,4 +236,4 @@ registerActions({
|
||||
margin-left: 4px;
|
||||
}
|
||||
</style>
|
||||
{% endblock %}
|
||||
{% endblock %}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{% extends "layouts/base.html" %}
|
||||
{% block title %}{{ _('Team Notes') }} - TryoutPro{% endblock %}
|
||||
{% block title %}{{ _('Team Notes') }} - UdeS team manager{% endblock %}
|
||||
{% block page_title %}{{ _('Team Notes') }}{% endblock %}
|
||||
{% block breadcrumb %}<span class="breadcrumb">Home / Team Notes</span>{% endblock %}
|
||||
|
||||
@@ -55,4 +55,4 @@
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
{% endblock %}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{% extends "layouts/base.html" %}
|
||||
{% block title %}{{ _('Teams') }} - TryoutPro{% endblock %}
|
||||
{% block title %}{{ _('Teams') }} - UdeS team manager{% endblock %}
|
||||
{% block page_title %}{{ _('Teams') }}{% endblock %}
|
||||
{% block breadcrumb %}<span class="breadcrumb">Home / Teams</span>{% endblock %}
|
||||
|
||||
@@ -548,4 +548,4 @@ registerActions({
|
||||
color: var(--text-primary);
|
||||
}
|
||||
</style>
|
||||
{% endblock %}
|
||||
{% endblock %}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{% extends "layouts/base.html" %}
|
||||
{% block title %}{% if tryout %}Edit Tryout{% else %}Create Tryout{% endif %} - TryoutPro{% endblock %}
|
||||
{% block title %}{% if tryout %}Edit Tryout{% else %}Create Tryout{% endif %} - UdeS team manager{% endblock %}
|
||||
{% block page_title %}{% if tryout %}Edit Tryout{% else %}Create Tryout{% endif %}{% endblock %}
|
||||
{% block breadcrumb %}
|
||||
<span class="breadcrumb">
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{% extends "layouts/base.html" %}
|
||||
{% block title %}{{ _('Tryouts') }} - TryoutPro{% endblock %}
|
||||
{% block title %}{{ _('Tryouts') }} - UdeS team manager{% endblock %}
|
||||
{% block page_title %}{{ _('Tryouts') }}{% endblock %}
|
||||
{% block breadcrumb %}<span class="breadcrumb">Home / Tryouts</span>{% endblock %}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{% extends "layouts/base.html" %}
|
||||
{% block title %}{{ _('Upload Contract') }} - TryoutPro{% endblock %}
|
||||
{% block title %}{{ _('Upload Contract') }} - UdeS team manager{% endblock %}
|
||||
{% block page_title %}{{ _('Upload Contract') }}{% endblock %}
|
||||
{% block breadcrumb %}<span class="breadcrumb">Home / <a href="{{ url_for('users.list_contracts') }}">Contracts</a> / Upload</span>{% endblock %}
|
||||
|
||||
@@ -42,4 +42,4 @@
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
{% endblock %}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{% extends "layouts/base.html" %}
|
||||
{% import 'layouts/_pagination.html' as pager %}
|
||||
{% block title %}{{ _('Manage Users') }} - TryoutPro{% endblock %}
|
||||
{% block title %}{{ _('Manage Users') }} - UdeS team manager{% endblock %}
|
||||
{% block page_title %}{{ _('Manage Users') }}{% endblock %}
|
||||
{% block breadcrumb %}<span class="breadcrumb">Home / Users</span>{% endblock %}
|
||||
|
||||
@@ -67,4 +67,4 @@
|
||||
{{ pager.controls(pagination) }}
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
{% endblock %}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{% extends "layouts/base.html" %}
|
||||
{% block title %}{{ tryout.title }} - TryoutPro{% endblock %}
|
||||
{% block title %}{{ tryout.title }} - UdeS team manager{% endblock %}
|
||||
{% block page_title %}{{ tryout.title }}{% endblock %}
|
||||
{% block breadcrumb %}<span class="breadcrumb">Home / <a href="{{ url_for('tryouts.list_tryouts') }}">Tryouts</a> / {{ tryout.title }}</span>{% endblock %}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{% extends "layouts/base.html" %}
|
||||
{% block title %}{{ profile_user.username }} - TryoutPro{% endblock %}
|
||||
{% block title %}{{ profile_user.username }} - UdeS team manager{% endblock %}
|
||||
{% block page_title %}{{ profile_user.username }}{% endblock %}
|
||||
{% block breadcrumb %}<span class="breadcrumb">Home / <a href="#" data-action="history-back">Back</a> / {{ profile_user.username }}</span>{% endblock %}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user