feat(i18n): traduire les messages flash et de validation
198 appels flash dans les sept modules de routes, plus les 23 messages de validation de app/validators.py. Le catalogue compte desormais 631 chaines, aucune non traduite. validators.py utilise lazy_gettext : les champs de schema sont construits a l'import, donc avant qu'une requete existe. Un gettext ordinaire s'y resoudrait une seule fois, dans la langue active au demarrage. Un bug introduit par la conversion, puis corrige Le convertisseur automatique ne voyait que le premier litteral d'un appel flash, ce qui a casse deux chaines concatenees sur plusieurs lignes dans users.py -- le resultat n'etait meme pas du Python valide. Ma premiere verification ne l'a pas vu : elle enchainait py_compile sur head, or head reussit toujours, donc le "OK" s'affichait quoi qu'il arrive. Les deux appels sont reecrits et la verification refaite correctement. Un bug plus interessant, revele par le test de fumee La langue choisie ne survivait pas a la connexion. login() et logout() appellent tous deux session.clear() -- l'un contre la fixation de session, l'autre pour terminer la session -- et le choix de langue partait avec le reste. Concretement : quelqu'un qui lisait la page de connexion en anglais se retrouvait en francais des qu'il se connectait. La langue est une preference d'affichage, pas un etat appartenant au compte. Les deux endroits la reportent maintenant explicitement, a cote du jeton CSRF. Quatre tests couvrent le cas, dont un qui verifie que corriger une cle preservee n'a pas fait tomber l'autre. Detail de nommage : le convertisseur avait genere %(value)s pour une expression conditionnelle, ce qui n'aide pas un traducteur. Renomme en %(player)s. Les 14 traductions ecrites avec une apostrophe droite sont normalisees en apostrophe typographique. Sans consequence en HTML, ou ' s'affiche correctement -- mais les blocs <script> ne decodent pas les entites, et autant que le catalogue soit homogene. 200 tests. Co-Authored-By: Claude Opus 5 <[email protected]>
This commit is contained in:
+20
-6
@@ -14,6 +14,7 @@ from flask_login import login_user, logout_user, login_required, current_user
|
||||
from app.extensions import db, hash_password, check_password, limiter
|
||||
from app.models import User, Player, ESPORT_GAMES
|
||||
from app.validators import RegisterSchema, LoginSchema
|
||||
from app.i18n import LOCALE_SESSION_KEY
|
||||
from app.logging_config import log_auth_event
|
||||
from flask_babel import gettext as _
|
||||
from marshmallow import ValidationError
|
||||
@@ -126,7 +127,7 @@ def login():
|
||||
except ValidationError as err:
|
||||
for field, messages in err.messages.items():
|
||||
for msg in messages:
|
||||
flash(f'{field}: {msg}', 'danger')
|
||||
flash(_('%(field)s: %(msg)s', field=field, msg=msg), 'danger')
|
||||
return render_template('pages/login.html')
|
||||
|
||||
username = validated['username']
|
||||
@@ -157,11 +158,19 @@ def login():
|
||||
db.session.commit()
|
||||
|
||||
# Clear old session data and preserve CSRF token to prevent
|
||||
# session fixation attacks (Flask-Login rotates the session ID)
|
||||
_csrf_token = session.get('csrf_token')
|
||||
# session fixation attacks (Flask-Login rotates the session ID).
|
||||
#
|
||||
# The language choice is carried across too. Someone who reads the
|
||||
# login page in English and signs in would otherwise be dropped
|
||||
# back into French — the preference lives in the session, and
|
||||
# clearing it discards a decision the user just made.
|
||||
_preserved = {
|
||||
key: session[key]
|
||||
for key in ('csrf_token', LOCALE_SESSION_KEY)
|
||||
if key in session
|
||||
}
|
||||
session.clear()
|
||||
if _csrf_token:
|
||||
session['csrf_token'] = _csrf_token
|
||||
session.update(_preserved)
|
||||
|
||||
# Mark the session permanent so PERMANENT_SESSION_LIFETIME applies.
|
||||
# Without this, Flask emits a browser-session cookie with no expiry
|
||||
@@ -255,7 +264,7 @@ def register():
|
||||
except ValidationError as err:
|
||||
for field, messages in err.messages.items():
|
||||
for msg in messages:
|
||||
flash(f'{field}: {msg}', 'danger')
|
||||
flash(_('%(field)s: %(msg)s', field=field, msg=msg), 'danger')
|
||||
captcha = generate_captcha()
|
||||
# Clear password fields on validation failure
|
||||
form_data.pop('password', None)
|
||||
@@ -517,6 +526,11 @@ def logout():
|
||||
"""
|
||||
log_auth_event('logout', username=current_user.username, user_id=current_user.id)
|
||||
logout_user()
|
||||
# Same reasoning as at login: the language is a display preference, not
|
||||
# session state belonging to the account being signed out.
|
||||
_locale = session.get(LOCALE_SESSION_KEY)
|
||||
session.clear()
|
||||
if _locale:
|
||||
session[LOCALE_SESSION_KEY] = _locale
|
||||
flash(_('You have been logged out.'), 'info')
|
||||
return redirect(url_for('auth.login'))
|
||||
@@ -5,6 +5,7 @@ Uses polymorphic isinstance checks instead of role-string comparisons.
|
||||
|
||||
from flask import Blueprint, render_template, redirect, url_for, flash, request
|
||||
from flask_login import login_required, current_user
|
||||
from flask_babel import gettext as _
|
||||
from app.extensions import db
|
||||
from app.models import (
|
||||
Admin, Player,
|
||||
@@ -37,7 +38,7 @@ def list_evaluations():
|
||||
user = current_user
|
||||
|
||||
if isinstance(user, Player):
|
||||
flash('You do not have permission to view evaluations.', 'danger')
|
||||
flash(_('You do not have permission to view evaluations.'), 'danger')
|
||||
return redirect(url_for('main.dashboard'))
|
||||
|
||||
sort_column = request.args.get('sort', 'created_at')
|
||||
@@ -118,24 +119,24 @@ def list_evaluations():
|
||||
def evaluate_player(tryout_id, player_id):
|
||||
"""Evaluate a specific player in a tryout."""
|
||||
if not current_user.can_evaluate():
|
||||
flash('You do not have permission to evaluate players.', 'danger')
|
||||
flash(_('You do not have permission to evaluate players.'), 'danger')
|
||||
return redirect(url_for('main.dashboard'))
|
||||
|
||||
tryout = Tryout.query.get_or_404(tryout_id)
|
||||
if not current_user.can_manage_this_tryout(tryout):
|
||||
flash('You do not have permission to evaluate players in this tryout.', 'danger')
|
||||
flash(_('You do not have permission to evaluate players in this tryout.'), 'danger')
|
||||
return redirect(url_for('tryouts.list_tryouts'))
|
||||
|
||||
is_registered = TryoutRegistration.query.filter_by(
|
||||
tryout_id=tryout_id, player_id=player_id,
|
||||
).first() is not None
|
||||
if not is_registered:
|
||||
flash('Player is not registered for this tryout.', 'danger')
|
||||
flash(_('Player is not registered for this tryout.'), 'danger')
|
||||
return redirect(url_for('tryouts.view_tryout', tryout_id=tryout_id))
|
||||
|
||||
player = User.query.get_or_404(player_id)
|
||||
if not isinstance(player, Player):
|
||||
flash('Can only evaluate players.', 'danger')
|
||||
flash(_('Can only evaluate players.'), 'danger')
|
||||
return redirect(url_for('tryouts.view_tryout', tryout_id=tryout_id))
|
||||
|
||||
existing_eval = Evaluation.query.filter_by(
|
||||
@@ -173,7 +174,7 @@ def evaluate_player(tryout_id, player_id):
|
||||
existing_eval.overall_score = overall
|
||||
existing_eval.comments = comments
|
||||
existing_eval.position_recommendation = position
|
||||
flash('Evaluation updated!', 'success')
|
||||
flash(_('Evaluation updated!'), 'success')
|
||||
else:
|
||||
evaluation = Evaluation(
|
||||
tryout_id=tryout_id, player_id=player_id,
|
||||
@@ -186,7 +187,7 @@ def evaluate_player(tryout_id, player_id):
|
||||
comments=comments, position_recommendation=position,
|
||||
)
|
||||
db.session.add(evaluation)
|
||||
flash('Evaluation submitted successfully!', 'success')
|
||||
flash(_('Evaluation submitted successfully!'), 'success')
|
||||
|
||||
db.session.commit()
|
||||
return redirect(url_for('tryouts.view_tryout', tryout_id=tryout_id))
|
||||
@@ -211,12 +212,12 @@ def evaluate_player(tryout_id, player_id):
|
||||
def players_to_evaluate(tryout_id):
|
||||
"""List players that need evaluation in a specific tryout."""
|
||||
if not current_user.can_evaluate():
|
||||
flash('Permission denied.', 'danger')
|
||||
flash(_('Permission denied.'), 'danger')
|
||||
return redirect(url_for('main.dashboard'))
|
||||
|
||||
tryout = Tryout.query.get_or_404(tryout_id)
|
||||
if not current_user.can_manage_this_tryout(tryout):
|
||||
flash('You do not have permission to evaluate players in this tryout.', 'danger')
|
||||
flash(_('You do not have permission to evaluate players in this tryout.'), 'danger')
|
||||
return redirect(url_for('tryouts.list_tryouts'))
|
||||
|
||||
registrations = TryoutRegistration.query.filter_by(tryout_id=tryout_id).all()
|
||||
|
||||
+2
-1
@@ -5,6 +5,7 @@ Uses polymorphic isinstance checks instead of role-string comparisons.
|
||||
|
||||
from flask import Blueprint, render_template, redirect, url_for, flash, request
|
||||
from flask_login import login_required, current_user
|
||||
from flask_babel import gettext as _
|
||||
from app.extensions import db
|
||||
from app.models import (
|
||||
Admin, Manager, Coach, Player, Scout,
|
||||
@@ -39,7 +40,7 @@ def set_language(locale):
|
||||
from app.routes.auth import is_safe_url
|
||||
|
||||
if not set_locale(locale):
|
||||
flash('That language is not available.', 'warning')
|
||||
flash(_('That language is not available.'), 'warning')
|
||||
|
||||
target = request.referrer
|
||||
if target and is_safe_url(target):
|
||||
|
||||
+15
-14
@@ -5,6 +5,7 @@ Uses polymorphic isinstance checks instead of role-string comparisons.
|
||||
|
||||
from flask import Blueprint, render_template, redirect, url_for, flash, request, jsonify
|
||||
from flask_login import login_required, current_user
|
||||
from flask_babel import gettext as _
|
||||
from app.extensions import db
|
||||
from app.models import (
|
||||
Admin, Manager, Coach, Player, Scout,
|
||||
@@ -197,11 +198,11 @@ def create_match(tryout_id):
|
||||
"""Create a new match / scrimmage within a tryout."""
|
||||
tryout = Tryout.query.get_or_404(tryout_id)
|
||||
if not current_user.can_manage_this_tryout(tryout):
|
||||
flash('You do not have permission to schedule matches for this tryout.', 'danger')
|
||||
flash(_('You do not have permission to schedule matches for this tryout.'), 'danger')
|
||||
return redirect(url_for('tryouts.view_tryout', tryout_id=tryout_id))
|
||||
|
||||
if tryout.is_ended:
|
||||
flash('This tryout has ended. Matches can no longer be created or modified.', 'danger')
|
||||
flash(_('This tryout has ended. Matches can no longer be created or modified.'), 'danger')
|
||||
return redirect(url_for('tryouts.view_tryout', tryout_id=tryout_id))
|
||||
|
||||
teams = Team.query.filter_by(tryout_id=tryout_id).all()
|
||||
@@ -220,14 +221,14 @@ def create_match(tryout_id):
|
||||
match_type = request.form.get('match_type')
|
||||
|
||||
if not start_time_str:
|
||||
flash('Start time is required. Please select a time slot.', 'danger')
|
||||
flash(_('Start time is required. Please select a time slot.'), 'danger')
|
||||
return render_template('pages/match_form.html', tryout=tryout, teams=teams,
|
||||
all_players=all_players, prefill_date=prefill_date)
|
||||
|
||||
try:
|
||||
date_obj = datetime.strptime(date_str, '%Y-%m-%d').date() if date_str else tryout.date
|
||||
except (ValueError, TypeError):
|
||||
flash('Invalid date format.', 'danger')
|
||||
flash(_('Invalid date format.'), 'danger')
|
||||
return render_template('pages/match_form.html', tryout=tryout, teams=teams,
|
||||
all_players=all_players, prefill_date=prefill_date)
|
||||
|
||||
@@ -242,7 +243,7 @@ def create_match(tryout_id):
|
||||
end_dt = start_dt + timedelta(minutes=30)
|
||||
end_time = end_dt.time()
|
||||
except ValueError:
|
||||
flash('Invalid time format.', 'danger')
|
||||
flash(_('Invalid time format.'), 'danger')
|
||||
return render_template('pages/match_form.html', tryout=tryout, teams=teams, all_players=all_players)
|
||||
|
||||
match = Match(
|
||||
@@ -313,7 +314,7 @@ def create_match(tryout_id):
|
||||
reference_id=reference_id,
|
||||
)
|
||||
|
||||
flash('Match scheduled successfully!', 'success')
|
||||
flash(_('Match scheduled successfully!'), 'success')
|
||||
return redirect(url_for('tryouts.view_tryout', tryout_id=tryout_id))
|
||||
|
||||
return render_template('pages/match_form.html', tryout=tryout, teams=teams,
|
||||
@@ -328,11 +329,11 @@ def edit_match(match_id):
|
||||
tryout = match.tryout
|
||||
|
||||
if not current_user.can_manage_this_tryout(tryout):
|
||||
flash('You do not have permission to edit this match.', 'danger')
|
||||
flash(_('You do not have permission to edit this match.'), 'danger')
|
||||
return redirect(url_for('matches.calendar'))
|
||||
|
||||
if tryout.is_ended:
|
||||
flash('This tryout has ended. Matches can no longer be created or modified.', 'danger')
|
||||
flash(_('This tryout has ended. Matches can no longer be created or modified.'), 'danger')
|
||||
return redirect(url_for('tryouts.view_tryout', tryout_id=tryout.id))
|
||||
|
||||
teams = Team.query.filter_by(tryout_id=tryout.id).all()
|
||||
@@ -355,13 +356,13 @@ def edit_match(match_id):
|
||||
try:
|
||||
match.date = datetime.strptime(date_str, '%Y-%m-%d').date()
|
||||
except (ValueError, TypeError):
|
||||
flash('Invalid date format.', 'danger')
|
||||
flash(_('Invalid date format.'), 'danger')
|
||||
return render_template('pages/match_form.html', match=match, tryout=tryout,
|
||||
teams=teams, all_players=all_players,
|
||||
current_player_ids=current_player_ids)
|
||||
|
||||
if not start_time_str:
|
||||
flash('Start time is required.', 'danger')
|
||||
flash(_('Start time is required.'), 'danger')
|
||||
return render_template('pages/match_form.html', match=match, tryout=tryout,
|
||||
teams=teams, all_players=all_players,
|
||||
current_player_ids=current_player_ids)
|
||||
@@ -457,7 +458,7 @@ def edit_match(match_id):
|
||||
reference_id=reference_id,
|
||||
)
|
||||
|
||||
flash('Match updated successfully!', 'success')
|
||||
flash(_('Match updated successfully!'), 'success')
|
||||
return redirect(url_for('tryouts.view_tryout', tryout_id=tryout.id))
|
||||
|
||||
participants_map = {}
|
||||
@@ -502,10 +503,10 @@ def delete_match(match_id):
|
||||
match = Match.query.get_or_404(match_id)
|
||||
tryout = match.tryout
|
||||
if not current_user.can_manage_this_tryout(tryout):
|
||||
flash('You do not have permission to delete this match.', 'danger')
|
||||
flash(_('You do not have permission to delete this match.'), 'danger')
|
||||
return redirect(url_for('matches.calendar'))
|
||||
if tryout.is_ended:
|
||||
flash('This tryout has ended. Matches can no longer be deleted.', 'danger')
|
||||
flash(_('This tryout has ended. Matches can no longer be deleted.'), 'danger')
|
||||
return redirect(url_for('tryouts.view_tryout', tryout_id=tryout.id))
|
||||
|
||||
# Notes outlive the match they were taken during: a coach's observation
|
||||
@@ -517,7 +518,7 @@ def delete_match(match_id):
|
||||
|
||||
db.session.delete(match)
|
||||
db.session.commit()
|
||||
flash('Match deleted successfully.', 'success')
|
||||
flash(_('Match deleted successfully.'), 'success')
|
||||
return redirect(url_for('tryouts.view_tryout', tryout_id=tryout.id))
|
||||
|
||||
|
||||
|
||||
+11
-10
@@ -5,6 +5,7 @@ Uses polymorphic isinstance checks instead of role-string comparisons.
|
||||
|
||||
from flask import Blueprint, render_template, redirect, url_for, flash, request, jsonify
|
||||
from flask_login import login_required, current_user
|
||||
from flask_babel import gettext as _
|
||||
from app.extensions import db
|
||||
from app.models import (
|
||||
Admin, Manager, Coach, Player,
|
||||
@@ -93,7 +94,7 @@ def create_match(team_id):
|
||||
"""Create a new regular-season team match."""
|
||||
team = OrgTeam.query.get_or_404(team_id)
|
||||
if not can_manage_team_match(team):
|
||||
flash('You do not have permission to schedule matches for this team.', 'danger')
|
||||
flash(_('You do not have permission to schedule matches for this team.'), 'danger')
|
||||
return redirect(url_for('team_matches.list_matches'))
|
||||
|
||||
team_players = [tp for tp in TeamPlayer.query.filter_by(org_team_id=team_id).all()]
|
||||
@@ -128,14 +129,14 @@ def create_match(team_id):
|
||||
location = request.form.get('location', '')
|
||||
|
||||
if not date_str:
|
||||
flash('Date is required.', 'danger')
|
||||
flash(_('Date is required.'), 'danger')
|
||||
return render_template('pages/team_match_form.html', team=team,
|
||||
team_players=team_players, prefill_date=prefill_date)
|
||||
|
||||
try:
|
||||
date_obj = datetime.strptime(date_str, '%Y-%m-%d').date()
|
||||
except (ValueError, TypeError):
|
||||
flash('Invalid date format.', 'danger')
|
||||
flash(_('Invalid date format.'), 'danger')
|
||||
return render_template('pages/team_match_form.html', team=team,
|
||||
team_players=team_players, prefill_date=prefill_date,
|
||||
is_practice=is_practice)
|
||||
@@ -152,7 +153,7 @@ def create_match(team_id):
|
||||
end_dt = start_dt + timedelta(minutes=30)
|
||||
end_time = end_dt.time()
|
||||
except ValueError:
|
||||
flash('Invalid time format.', 'danger')
|
||||
flash(_('Invalid time format.'), 'danger')
|
||||
return render_template('pages/team_match_form.html', team=team,
|
||||
team_players=team_players, prefill_date=prefill_date,
|
||||
is_practice=is_practice)
|
||||
@@ -191,7 +192,7 @@ def create_match(team_id):
|
||||
reference_id=reference_id,
|
||||
)
|
||||
|
||||
flash(f'Team match "{title}" scheduled successfully!', 'success')
|
||||
flash(_('Team match "%(title)s" scheduled successfully!', title=title), 'success')
|
||||
return redirect(url_for('team_matches.list_matches'))
|
||||
|
||||
return render_template('pages/team_match_form.html', team=team, team_players=team_players)
|
||||
@@ -205,7 +206,7 @@ def edit_match(match_id):
|
||||
team = team_match.org_team
|
||||
|
||||
if not can_manage_team_match(team):
|
||||
flash('You do not have permission to edit this match.', 'danger')
|
||||
flash(_('You do not have permission to edit this match.'), 'danger')
|
||||
return redirect(url_for('team_matches.list_matches'))
|
||||
|
||||
if request.method == 'POST':
|
||||
@@ -218,7 +219,7 @@ def edit_match(match_id):
|
||||
try:
|
||||
team_match.date = datetime.strptime(date_str, '%Y-%m-%d').date()
|
||||
except (ValueError, TypeError):
|
||||
flash('Invalid date format.', 'danger')
|
||||
flash(_('Invalid date format.'), 'danger')
|
||||
return redirect(url_for('team_matches.edit_match', match_id=match_id))
|
||||
|
||||
start_time_str = request.form.get('start_time')
|
||||
@@ -241,7 +242,7 @@ def edit_match(match_id):
|
||||
team_match.status = status
|
||||
|
||||
db.session.commit()
|
||||
flash('Match updated successfully!', 'success')
|
||||
flash(_('Match updated successfully!'), 'success')
|
||||
return redirect(url_for('team_matches.list_matches'))
|
||||
|
||||
return render_template('pages/team_match_form.html',
|
||||
@@ -255,11 +256,11 @@ def delete_match(match_id):
|
||||
team_match = TeamMatch.query.get_or_404(match_id)
|
||||
team = team_match.org_team
|
||||
if not can_manage_team_match(team):
|
||||
flash('You do not have permission to delete this match.', 'danger')
|
||||
flash(_('You do not have permission to delete this match.'), 'danger')
|
||||
return redirect(url_for('team_matches.list_matches'))
|
||||
db.session.delete(team_match)
|
||||
db.session.commit()
|
||||
flash('Match deleted successfully.', 'success')
|
||||
flash(_('Match deleted successfully.'), 'success')
|
||||
return redirect(url_for('team_matches.list_matches'))
|
||||
|
||||
|
||||
|
||||
+42
-41
@@ -5,6 +5,7 @@ Uses polymorphic isinstance checks instead of role-string comparisons.
|
||||
|
||||
from flask import Blueprint, render_template, redirect, url_for, flash, request, jsonify
|
||||
from flask_login import login_required, current_user
|
||||
from flask_babel import gettext as _
|
||||
from app.extensions import db
|
||||
from app.models import (
|
||||
Admin, Manager, Coach, Player,
|
||||
@@ -39,10 +40,10 @@ def list_teams():
|
||||
)
|
||||
).order_by(OrgTeam.name).all()
|
||||
elif isinstance(current_user, Player):
|
||||
flash('Use My Team(s) to view your teams.', 'info')
|
||||
flash(_('Use My Team(s) to view your teams.'), 'info')
|
||||
return redirect(url_for('teams.my_teams'))
|
||||
else:
|
||||
flash('You do not have permission to view teams.', 'danger')
|
||||
flash(_('You do not have permission to view teams.'), 'danger')
|
||||
return redirect(url_for('main.dashboard'))
|
||||
|
||||
coaches = User.query.filter_by(role='coach', is_active_account=True).order_by(User.username).all()
|
||||
@@ -57,7 +58,7 @@ def list_teams():
|
||||
def my_teams():
|
||||
"""View the player's own teams with upcoming matches."""
|
||||
if not isinstance(current_user, Player):
|
||||
flash('This page is for players.', 'info')
|
||||
flash(_('This page is for players.'), 'info')
|
||||
return redirect(url_for('teams.list_teams'))
|
||||
|
||||
from app.models import TeamMatch, TeamMatchParticipant
|
||||
@@ -99,7 +100,7 @@ def my_teams():
|
||||
def create_team():
|
||||
"""Create a new organization team."""
|
||||
if not current_user.can_manage_teams():
|
||||
flash('You do not have permission to create teams.', 'danger')
|
||||
flash(_('You do not have permission to create teams.'), 'danger')
|
||||
return redirect(url_for('teams.list_teams'))
|
||||
|
||||
name = request.form.get('name')
|
||||
@@ -107,12 +108,12 @@ def create_team():
|
||||
manager_id = request.form.get('manager_id')
|
||||
|
||||
if not name:
|
||||
flash('Team name is required.', 'danger')
|
||||
flash(_('Team name is required.'), 'danger')
|
||||
return redirect(url_for('teams.list_teams'))
|
||||
|
||||
existing = OrgTeam.query.filter_by(name=name).first()
|
||||
if existing:
|
||||
flash(f'Team "{name}" already exists.', 'danger')
|
||||
flash(_('Team "%(name)s" already exists.', name=name), 'danger')
|
||||
return redirect(url_for('teams.list_teams'))
|
||||
|
||||
team = OrgTeam(
|
||||
@@ -134,7 +135,7 @@ def create_team():
|
||||
team.managers.append(manager_user)
|
||||
|
||||
db.session.commit()
|
||||
flash(f'Team "{name}" created successfully!', 'success')
|
||||
flash(_('Team "%(name)s" created successfully!', name=name), 'success')
|
||||
return redirect(url_for('teams.list_teams'))
|
||||
|
||||
|
||||
@@ -144,7 +145,7 @@ def edit_team(team_id):
|
||||
"""Edit an existing organization team."""
|
||||
team = OrgTeam.query.get_or_404(team_id)
|
||||
if not current_user.can_manage_this_org_team(team):
|
||||
flash('You do not have permission to edit this team.', 'danger')
|
||||
flash(_('You do not have permission to edit this team.'), 'danger')
|
||||
return redirect(url_for('teams.list_teams'))
|
||||
|
||||
name = request.form.get('name')
|
||||
@@ -152,12 +153,12 @@ def edit_team(team_id):
|
||||
manager_id = request.form.get('manager_id')
|
||||
|
||||
if not name:
|
||||
flash('Team name is required.', 'danger')
|
||||
flash(_('Team name is required.'), 'danger')
|
||||
return redirect(url_for('teams.list_teams'))
|
||||
|
||||
existing = OrgTeam.query.filter(OrgTeam.name == name, OrgTeam.id != team_id).first()
|
||||
if existing:
|
||||
flash(f'Team "{name}" already exists.', 'danger')
|
||||
flash(_('Team "%(name)s" already exists.', name=name), 'danger')
|
||||
return redirect(url_for('teams.list_teams'))
|
||||
|
||||
if request.form.get('sync_staff') == '1':
|
||||
@@ -195,7 +196,7 @@ def edit_team(team_id):
|
||||
team.managers.append(manager_user)
|
||||
|
||||
db.session.commit()
|
||||
flash(f'Team "{name}" updated successfully!', 'success')
|
||||
flash(_('Team "%(name)s" updated successfully!', name=name), 'success')
|
||||
return redirect(url_for('teams.list_teams'))
|
||||
|
||||
|
||||
@@ -204,7 +205,7 @@ def edit_team(team_id):
|
||||
def delete_team(team_id):
|
||||
"""Delete an organization team."""
|
||||
if not current_user.can_manage_teams():
|
||||
flash('You do not have permission to delete teams.', 'danger')
|
||||
flash(_('You do not have permission to delete teams.'), 'danger')
|
||||
return redirect(url_for('teams.list_teams'))
|
||||
|
||||
team = OrgTeam.query.get_or_404(team_id)
|
||||
@@ -235,7 +236,7 @@ def delete_team(team_id):
|
||||
|
||||
db.session.delete(team)
|
||||
db.session.commit()
|
||||
flash(f'Team "{name}" deleted successfully.', 'success')
|
||||
flash(_('Team "%(name)s" deleted successfully.', name=name), 'success')
|
||||
return redirect(url_for('teams.list_teams'))
|
||||
|
||||
|
||||
@@ -245,28 +246,28 @@ def add_coach(team_id):
|
||||
"""Add a coach to an organization team."""
|
||||
team = OrgTeam.query.get_or_404(team_id)
|
||||
if not current_user.can_manage_this_org_team(team):
|
||||
flash('Permission denied.', 'danger')
|
||||
flash(_('Permission denied.'), 'danger')
|
||||
return redirect(url_for('teams.list_teams'))
|
||||
|
||||
coach_id = request.form.get('coach_id')
|
||||
if not coach_id:
|
||||
flash('Please select a coach.', 'danger')
|
||||
flash(_('Please select a coach.'), 'danger')
|
||||
return redirect(url_for('teams.list_teams'))
|
||||
|
||||
coach = User.query.get_or_404(int(coach_id))
|
||||
if not isinstance(coach, Coach):
|
||||
flash('Only coaches can be assigned as coach.', 'danger')
|
||||
flash(_('Only coaches can be assigned as coach.'), 'danger')
|
||||
return redirect(url_for('teams.list_teams'))
|
||||
|
||||
if team.coaches.filter_by(id=coach.id).first():
|
||||
flash(f'{coach.username} is already a coach of {team.name}.', 'info')
|
||||
flash(_('%(username)s is already a coach of %(name)s.', username=coach.username, name=team.name), 'info')
|
||||
return redirect(url_for('teams.list_teams'))
|
||||
|
||||
team.coaches.append(coach)
|
||||
if not team.coach_id:
|
||||
team.coach_id = coach.id
|
||||
db.session.commit()
|
||||
flash(f'{coach.username} added as coach of {team.name}.', 'success')
|
||||
flash(_('%(username)s added as coach of %(name)s.', username=coach.username, name=team.name), 'success')
|
||||
return redirect(url_for('teams.list_teams'))
|
||||
|
||||
|
||||
@@ -276,28 +277,28 @@ def add_manager(team_id):
|
||||
"""Add a manager to an organization team."""
|
||||
team = OrgTeam.query.get_or_404(team_id)
|
||||
if not current_user.can_manage_this_org_team(team):
|
||||
flash('Permission denied.', 'danger')
|
||||
flash(_('Permission denied.'), 'danger')
|
||||
return redirect(url_for('teams.list_teams'))
|
||||
|
||||
manager_id = request.form.get('manager_id')
|
||||
if not manager_id:
|
||||
flash('Please select a manager.', 'danger')
|
||||
flash(_('Please select a manager.'), 'danger')
|
||||
return redirect(url_for('teams.list_teams'))
|
||||
|
||||
manager = User.query.get_or_404(int(manager_id))
|
||||
if not isinstance(manager, Manager):
|
||||
flash('Only managers can be assigned as manager.', 'danger')
|
||||
flash(_('Only managers can be assigned as manager.'), 'danger')
|
||||
return redirect(url_for('teams.list_teams'))
|
||||
|
||||
if team.managers.filter_by(id=manager.id).first():
|
||||
flash(f'{manager.username} is already a manager of {team.name}.', 'info')
|
||||
flash(_('%(username)s is already a manager of %(name)s.', username=manager.username, name=team.name), 'info')
|
||||
return redirect(url_for('teams.list_teams'))
|
||||
|
||||
team.managers.append(manager)
|
||||
if not team.manager_id:
|
||||
team.manager_id = manager.id
|
||||
db.session.commit()
|
||||
flash(f'{manager.username} added as manager of {team.name}.', 'success')
|
||||
flash(_('%(username)s added as manager of %(name)s.', username=manager.username, name=team.name), 'success')
|
||||
return redirect(url_for('teams.list_teams'))
|
||||
|
||||
|
||||
@@ -307,7 +308,7 @@ def remove_coach(team_id):
|
||||
"""Remove a coach from an organization team."""
|
||||
team = OrgTeam.query.get_or_404(team_id)
|
||||
if not current_user.can_manage_this_org_team(team):
|
||||
flash('Permission denied.', 'danger')
|
||||
flash(_('Permission denied.'), 'danger')
|
||||
return redirect(url_for('teams.list_teams'))
|
||||
|
||||
coach_id = request.form.get('coach_id')
|
||||
@@ -322,7 +323,7 @@ def remove_coach(team_id):
|
||||
team.coach_id = None
|
||||
|
||||
db.session.commit()
|
||||
flash(f'Coach removed from {team.name}.', 'success')
|
||||
flash(_('Coach removed from %(name)s.', name=team.name), 'success')
|
||||
return redirect(url_for('teams.list_teams'))
|
||||
|
||||
|
||||
@@ -332,7 +333,7 @@ def remove_manager(team_id):
|
||||
"""Remove a manager from an organization team."""
|
||||
team = OrgTeam.query.get_or_404(team_id)
|
||||
if not current_user.can_manage_this_org_team(team):
|
||||
flash('Permission denied.', 'danger')
|
||||
flash(_('Permission denied.'), 'danger')
|
||||
return redirect(url_for('teams.list_teams'))
|
||||
|
||||
manager_id = request.form.get('manager_id')
|
||||
@@ -347,7 +348,7 @@ def remove_manager(team_id):
|
||||
team.manager_id = None
|
||||
|
||||
db.session.commit()
|
||||
flash(f'Manager removed from {team.name}.', 'success')
|
||||
flash(_('Manager removed from %(name)s.', name=team.name), 'success')
|
||||
return redirect(url_for('teams.list_teams'))
|
||||
|
||||
|
||||
@@ -357,29 +358,29 @@ def add_player(team_id):
|
||||
"""Add a player to an organization team."""
|
||||
team = OrgTeam.query.get_or_404(team_id)
|
||||
if not current_user.can_manage_this_org_team(team):
|
||||
flash('Permission denied.', 'danger')
|
||||
flash(_('Permission denied.'), 'danger')
|
||||
return redirect(url_for('teams.list_teams'))
|
||||
|
||||
player_id = request.form.get('player_id')
|
||||
status = request.form.get('status', 'starter')
|
||||
if not player_id:
|
||||
flash('Please select a player.', 'danger')
|
||||
flash(_('Please select a player.'), 'danger')
|
||||
return redirect(url_for('teams.list_teams'))
|
||||
|
||||
player = User.query.get_or_404(int(player_id))
|
||||
if not isinstance(player, Player):
|
||||
flash('Can only assign players to teams.', 'danger')
|
||||
flash(_('Can only assign players to teams.'), 'danger')
|
||||
return redirect(url_for('teams.list_teams'))
|
||||
|
||||
existing = TeamPlayer.query.filter_by(player_id=player.id, org_team_id=team.id).first()
|
||||
if existing:
|
||||
flash(f'{player.username} is already on {team.name}.', 'info')
|
||||
flash(_('%(username)s is already on %(name)s.', username=player.username, name=team.name), 'info')
|
||||
return redirect(url_for('teams.list_teams'))
|
||||
|
||||
tp = TeamPlayer(player_id=player.id, org_team_id=team.id, status=status)
|
||||
db.session.add(tp)
|
||||
db.session.commit()
|
||||
flash(f'{player.username} added to {team.name}!', 'success')
|
||||
flash(_('%(username)s added to %(name)s!', username=player.username, name=team.name), 'success')
|
||||
return redirect(url_for('teams.list_teams'))
|
||||
|
||||
|
||||
@@ -389,18 +390,18 @@ def remove_player(team_id, player_id):
|
||||
"""Remove a player from an organization team."""
|
||||
team = OrgTeam.query.get_or_404(team_id)
|
||||
if not current_user.can_manage_this_org_team(team):
|
||||
flash('Permission denied.', 'danger')
|
||||
flash(_('Permission denied.'), 'danger')
|
||||
return redirect(url_for('teams.list_teams'))
|
||||
|
||||
player = User.query.get_or_404(player_id)
|
||||
tp = TeamPlayer.query.filter_by(player_id=player_id, org_team_id=team_id).first()
|
||||
if not tp:
|
||||
flash(f'{player.username} is not on {team.name}.', 'danger')
|
||||
flash(_('%(username)s is not on %(name)s.', username=player.username, name=team.name), 'danger')
|
||||
return redirect(url_for('teams.list_teams'))
|
||||
|
||||
db.session.delete(tp)
|
||||
db.session.commit()
|
||||
flash(f'{player.username} removed from {team.name}.', 'success')
|
||||
flash(_('%(username)s removed from %(name)s.', username=player.username, name=team.name), 'success')
|
||||
return redirect(url_for('teams.list_teams'))
|
||||
|
||||
|
||||
@@ -430,7 +431,7 @@ def add_team_note(team_id):
|
||||
"""Add a team improvement note (coaches only)."""
|
||||
team = OrgTeam.query.get_or_404(team_id)
|
||||
if not current_user.can_manage_this_org_team(team):
|
||||
flash('You do not have permission to add notes to this team.', 'danger')
|
||||
flash(_('You do not have permission to add notes to this team.'), 'danger')
|
||||
return redirect(url_for('teams.list_teams'))
|
||||
|
||||
content = request.form.get('content', '').strip()
|
||||
@@ -438,7 +439,7 @@ def add_team_note(team_id):
|
||||
note = TeamNote(org_team_id=team_id, coach_id=current_user.id, content=content)
|
||||
db.session.add(note)
|
||||
db.session.commit()
|
||||
flash('Team notes added successfully!', 'success')
|
||||
flash(_('Team notes added successfully!'), 'success')
|
||||
return redirect(url_for('teams.list_teams'))
|
||||
|
||||
|
||||
@@ -448,17 +449,17 @@ def add_player_note(team_id, player_id):
|
||||
"""Add a personal note for a player (coaches only)."""
|
||||
team = OrgTeam.query.get_or_404(team_id)
|
||||
if not current_user.can_manage_this_org_team(team):
|
||||
flash('You do not have permission to add notes to this team.', 'danger')
|
||||
flash(_('You do not have permission to add notes to this team.'), 'danger')
|
||||
return redirect(url_for('teams.list_teams'))
|
||||
|
||||
player = User.query.get_or_404(player_id)
|
||||
if not isinstance(player, Player):
|
||||
flash('Can only add notes for players.', 'danger')
|
||||
flash(_('Can only add notes for players.'), 'danger')
|
||||
return redirect(url_for('teams.list_teams'))
|
||||
|
||||
tp = TeamPlayer.query.filter_by(player_id=player_id, org_team_id=team_id).first()
|
||||
if not tp:
|
||||
flash(f'{player.username} is not on {team.name}.', 'danger')
|
||||
flash(_('%(username)s is not on %(name)s.', username=player.username, name=team.name), 'danger')
|
||||
return redirect(url_for('teams.list_teams'))
|
||||
|
||||
content = request.form.get('content', '').strip()
|
||||
@@ -466,5 +467,5 @@ def add_player_note(team_id, player_id):
|
||||
note = PersonalNote(player_id=player_id, coach_id=current_user.id, content=content)
|
||||
db.session.add(note)
|
||||
db.session.commit()
|
||||
flash(f'Note added for {player.username}!', 'success')
|
||||
flash(_('Note added for %(username)s!', username=player.username), 'success')
|
||||
return redirect(url_for('teams.list_teams'))
|
||||
+39
-38
@@ -6,6 +6,7 @@ Uses polymorphic isinstance checks instead of role-string comparisons.
|
||||
|
||||
from flask import Blueprint, render_template, redirect, url_for, flash, request, abort
|
||||
from flask_login import login_required, current_user
|
||||
from flask_babel import gettext as _
|
||||
from app.extensions import db
|
||||
from app.models import (
|
||||
Admin, Manager, Coach, Player, Scout,
|
||||
@@ -39,7 +40,7 @@ def list_tryouts():
|
||||
def create_tryout():
|
||||
"""Create a new tryout event. Requires Admin or Manager."""
|
||||
if not can_manage():
|
||||
flash('You do not have permission to create tryouts.', 'danger')
|
||||
flash(_('You do not have permission to create tryouts.'), 'danger')
|
||||
return redirect(url_for('tryouts.list_tryouts'))
|
||||
|
||||
org_teams = OrgTeam.query.order_by(OrgTeam.name).all()
|
||||
@@ -61,7 +62,7 @@ def create_tryout():
|
||||
try:
|
||||
date_obj = datetime.strptime(date_str, '%Y-%m-%d').date()
|
||||
except (ValueError, TypeError):
|
||||
flash('Invalid start date format.', 'danger')
|
||||
flash(_('Invalid start date format.'), 'danger')
|
||||
return render_template('pages/tryout_form.html', tryout=None, org_teams=org_teams,
|
||||
managers=managers, coaches=coaches, esport_games=ESPORT_GAMES)
|
||||
|
||||
@@ -70,11 +71,11 @@ def create_tryout():
|
||||
try:
|
||||
end_date_obj = datetime.strptime(end_date_str, '%Y-%m-%d').date()
|
||||
if end_date_obj < date_obj:
|
||||
flash('End date cannot be before start date.', 'danger')
|
||||
flash(_('End date cannot be before start date.'), 'danger')
|
||||
return render_template('pages/tryout_form.html', tryout=None, org_teams=org_teams,
|
||||
managers=managers, coaches=coaches, esport_games=ESPORT_GAMES)
|
||||
except (ValueError, TypeError):
|
||||
flash('Invalid end date format.', 'danger')
|
||||
flash(_('Invalid end date format.'), 'danger')
|
||||
return render_template('pages/tryout_form.html', tryout=None, org_teams=org_teams,
|
||||
managers=managers, coaches=coaches, esport_games=ESPORT_GAMES)
|
||||
|
||||
@@ -96,7 +97,7 @@ def create_tryout():
|
||||
tryout.coaches = coach_users
|
||||
|
||||
db.session.commit()
|
||||
flash('Tryout created successfully!', 'success')
|
||||
flash(_('Tryout created successfully!'), 'success')
|
||||
return redirect(url_for('tryouts.view_tryout', tryout_id=tryout.id))
|
||||
|
||||
return render_template('pages/tryout_form.html', tryout=None, org_teams=org_teams,
|
||||
@@ -110,11 +111,11 @@ def edit_tryout(tryout_id):
|
||||
tryout = Tryout.query.get_or_404(tryout_id)
|
||||
|
||||
if not current_user.can_manage_this_tryout(tryout):
|
||||
flash('You do not have permission to edit this tryout.', 'danger')
|
||||
flash(_('You do not have permission to edit this tryout.'), 'danger')
|
||||
return redirect(url_for('tryouts.list_tryouts'))
|
||||
|
||||
if tryout.is_ended:
|
||||
flash('This tryout has ended and can no longer be modified.', 'danger')
|
||||
flash(_('This tryout has ended and can no longer be modified.'), 'danger')
|
||||
return redirect(url_for('tryouts.view_tryout', tryout_id=tryout.id))
|
||||
|
||||
org_teams = OrgTeam.query.order_by(OrgTeam.name).all()
|
||||
@@ -136,7 +137,7 @@ def edit_tryout(tryout_id):
|
||||
try:
|
||||
date_obj = datetime.strptime(date_str, '%Y-%m-%d').date()
|
||||
except (ValueError, TypeError):
|
||||
flash('Invalid start date format.', 'danger')
|
||||
flash(_('Invalid start date format.'), 'danger')
|
||||
return render_template('pages/tryout_form.html', tryout=tryout, org_teams=org_teams,
|
||||
managers=managers, coaches=coaches, esport_games=ESPORT_GAMES)
|
||||
|
||||
@@ -145,11 +146,11 @@ def edit_tryout(tryout_id):
|
||||
try:
|
||||
end_date_obj = datetime.strptime(end_date_str, '%Y-%m-%d').date()
|
||||
if end_date_obj < date_obj:
|
||||
flash('End date cannot be before start date.', 'danger')
|
||||
flash(_('End date cannot be before start date.'), 'danger')
|
||||
return render_template('pages/tryout_form.html', tryout=tryout, org_teams=org_teams,
|
||||
managers=managers, coaches=coaches, esport_games=ESPORT_GAMES)
|
||||
except (ValueError, TypeError):
|
||||
flash('Invalid end date format.', 'danger')
|
||||
flash(_('Invalid end date format.'), 'danger')
|
||||
return render_template('pages/tryout_form.html', tryout=tryout, org_teams=org_teams,
|
||||
managers=managers, coaches=coaches, esport_games=ESPORT_GAMES)
|
||||
|
||||
@@ -171,7 +172,7 @@ def edit_tryout(tryout_id):
|
||||
tryout.coaches = []
|
||||
|
||||
db.session.commit()
|
||||
flash('Tryout updated successfully!', 'success')
|
||||
flash(_('Tryout updated successfully!'), 'success')
|
||||
return redirect(url_for('tryouts.view_tryout', tryout_id=tryout.id))
|
||||
|
||||
return render_template('pages/tryout_form.html', tryout=tryout, org_teams=org_teams,
|
||||
@@ -203,7 +204,7 @@ def view_tryout(tryout_id):
|
||||
can_view = True
|
||||
|
||||
if not can_view:
|
||||
flash('You do not have permission to view this tryout.', 'danger')
|
||||
flash(_('You do not have permission to view this tryout.'), 'danger')
|
||||
return redirect(url_for('tryouts.list_tryouts'))
|
||||
|
||||
registrations = TryoutRegistration.query.filter_by(tryout_id=tryout_id).all()
|
||||
@@ -305,29 +306,29 @@ def register_for_tryout(tryout_id):
|
||||
"""Register a player for a tryout. Only Players can self-register."""
|
||||
tryout = Tryout.query.get_or_404(tryout_id)
|
||||
if not isinstance(current_user, Player):
|
||||
flash('Only players can register for tryouts.', 'danger')
|
||||
flash(_('Only players can register for tryouts.'), 'danger')
|
||||
return redirect(url_for('tryouts.view_tryout', tryout_id=tryout_id))
|
||||
|
||||
if tryout.status not in ['upcoming', 'in_progress']:
|
||||
flash('This tryout is not accepting registrations.', 'danger')
|
||||
flash(_('This tryout is not accepting registrations.'), 'danger')
|
||||
return redirect(url_for('tryouts.view_tryout', tryout_id=tryout_id))
|
||||
|
||||
existing = TryoutRegistration.query.filter_by(
|
||||
tryout_id=tryout_id, player_id=current_user.id).first()
|
||||
if existing:
|
||||
flash('You are already registered for this tryout.', 'info')
|
||||
flash(_('You are already registered for this tryout.'), 'info')
|
||||
return redirect(url_for('tryouts.view_tryout', tryout_id=tryout_id))
|
||||
|
||||
if tryout.max_players:
|
||||
count = TryoutRegistration.query.filter_by(tryout_id=tryout_id).count()
|
||||
if count >= tryout.max_players:
|
||||
flash('This tryout is full.', 'danger')
|
||||
flash(_('This tryout is full.'), 'danger')
|
||||
return redirect(url_for('tryouts.view_tryout', tryout_id=tryout_id))
|
||||
|
||||
registration = TryoutRegistration(tryout_id=tryout_id, player_id=current_user.id)
|
||||
db.session.add(registration)
|
||||
db.session.commit()
|
||||
flash('Successfully registered for tryout!', 'success')
|
||||
flash(_('Successfully registered for tryout!'), 'success')
|
||||
return redirect(url_for('tryouts.view_tryout', tryout_id=tryout_id))
|
||||
|
||||
|
||||
@@ -337,13 +338,13 @@ def update_status(tryout_id):
|
||||
"""Update the status of a tryout."""
|
||||
tryout = Tryout.query.get_or_404(tryout_id)
|
||||
if not current_user.can_manage_this_tryout(tryout):
|
||||
flash('Permission denied.', 'danger')
|
||||
flash(_('Permission denied.'), 'danger')
|
||||
return redirect(url_for('tryouts.list_tryouts'))
|
||||
new_status = request.form.get('status')
|
||||
if new_status in ['upcoming', 'in_progress', 'completed']:
|
||||
tryout.status = new_status
|
||||
db.session.commit()
|
||||
flash(f'Tryout status updated to {new_status}.', 'success')
|
||||
flash(_('Tryout status updated to %(new_status)s.', new_status=new_status), 'success')
|
||||
return redirect(url_for('tryouts.view_tryout', tryout_id=tryout_id))
|
||||
|
||||
|
||||
@@ -353,7 +354,7 @@ def update_registration_status(tryout_id, player_id):
|
||||
"""Update a registration's attendance status."""
|
||||
tryout = Tryout.query.get_or_404(tryout_id)
|
||||
if not current_user.can_manage_this_tryout(tryout):
|
||||
flash('Permission denied.', 'danger')
|
||||
flash(_('Permission denied.'), 'danger')
|
||||
return redirect(url_for('tryouts.list_tryouts'))
|
||||
|
||||
registration = TryoutRegistration.query.filter_by(
|
||||
@@ -362,7 +363,7 @@ def update_registration_status(tryout_id, player_id):
|
||||
if new_status in ['registered', 'attended', 'no_show']:
|
||||
registration.status = new_status
|
||||
db.session.commit()
|
||||
flash('Registration status updated.', 'success')
|
||||
flash(_('Registration status updated.'), 'success')
|
||||
return redirect(url_for('tryouts.view_tryout', tryout_id=tryout_id))
|
||||
|
||||
|
||||
@@ -372,34 +373,34 @@ def register_player(tryout_id):
|
||||
"""Manually register a player for a tryout (by managers/coaches)."""
|
||||
tryout = Tryout.query.get_or_404(tryout_id)
|
||||
if not current_user.can_manage_this_tryout(tryout):
|
||||
flash('Permission denied.', 'danger')
|
||||
flash(_('Permission denied.'), 'danger')
|
||||
return redirect(url_for('tryouts.view_tryout', tryout_id=tryout_id))
|
||||
player_id = request.form.get('player_id')
|
||||
if not player_id:
|
||||
flash('Please select a player.', 'danger')
|
||||
flash(_('Please select a player.'), 'danger')
|
||||
return redirect(url_for('tryouts.view_tryout', tryout_id=tryout_id))
|
||||
|
||||
player = User.query.get_or_404(int(player_id))
|
||||
if not isinstance(player, Player):
|
||||
flash('Can only register players.', 'danger')
|
||||
flash(_('Can only register players.'), 'danger')
|
||||
return redirect(url_for('tryouts.view_tryout', tryout_id=tryout_id))
|
||||
|
||||
existing = TryoutRegistration.query.filter_by(
|
||||
tryout_id=tryout_id, player_id=player.id).first()
|
||||
if existing:
|
||||
flash(f'{player.username} is already registered for this tryout.', 'info')
|
||||
flash(_('%(username)s is already registered for this tryout.', username=player.username), 'info')
|
||||
return redirect(url_for('tryouts.view_tryout', tryout_id=tryout_id))
|
||||
|
||||
if tryout.max_players:
|
||||
count = TryoutRegistration.query.filter_by(tryout_id=tryout_id).count()
|
||||
if count >= tryout.max_players:
|
||||
flash('This tryout is full.', 'danger')
|
||||
flash(_('This tryout is full.'), 'danger')
|
||||
return redirect(url_for('tryouts.view_tryout', tryout_id=tryout_id))
|
||||
|
||||
registration = TryoutRegistration(tryout_id=tryout_id, player_id=player.id)
|
||||
db.session.add(registration)
|
||||
db.session.commit()
|
||||
flash(f'{player.username} registered for tryout!', 'success')
|
||||
flash(_('%(username)s registered for tryout!', username=player.username), 'success')
|
||||
return redirect(url_for('tryouts.view_tryout', tryout_id=tryout_id))
|
||||
|
||||
|
||||
@@ -409,7 +410,7 @@ def remove_player(tryout_id, player_id):
|
||||
"""Remove a registered player from a tryout (cascades to teams/matches)."""
|
||||
tryout = Tryout.query.get_or_404(tryout_id)
|
||||
if not current_user.can_manage_this_tryout(tryout):
|
||||
flash('Permission denied.', 'danger')
|
||||
flash(_('Permission denied.'), 'danger')
|
||||
return redirect(url_for('tryouts.list_tryouts'))
|
||||
|
||||
player = User.query.get_or_404(player_id)
|
||||
@@ -434,7 +435,7 @@ def remove_player(tryout_id, player_id):
|
||||
).delete(synchronize_session=False)
|
||||
|
||||
db.session.commit()
|
||||
flash(f'{player.username} removed from tryout.', 'success')
|
||||
flash(_('%(username)s removed from tryout.', username=player.username), 'success')
|
||||
return redirect(url_for('tryouts.view_tryout', tryout_id=tryout_id))
|
||||
|
||||
|
||||
@@ -444,7 +445,7 @@ def create_team(tryout_id):
|
||||
"""Create a tryout-specific team."""
|
||||
tryout = Tryout.query.get_or_404(tryout_id)
|
||||
if not current_user.can_manage_this_tryout(tryout):
|
||||
flash('Permission denied.', 'danger')
|
||||
flash(_('Permission denied.'), 'danger')
|
||||
return redirect(url_for('tryouts.view_tryout', tryout_id=tryout_id))
|
||||
|
||||
team_name = request.form.get('team_name')
|
||||
@@ -452,7 +453,7 @@ def create_team(tryout_id):
|
||||
team = Team(tryout_id=tryout_id, name=team_name, created_by=current_user.id)
|
||||
db.session.add(team)
|
||||
db.session.commit()
|
||||
flash(f'Team "{team_name}" created!', 'success')
|
||||
flash(_('Team "%(team_name)s" created!', team_name=team_name), 'success')
|
||||
return redirect(url_for('tryouts.view_tryout', tryout_id=tryout_id))
|
||||
|
||||
|
||||
@@ -463,7 +464,7 @@ def add_to_team(tryout_id, team_id):
|
||||
team = Team.query.get_or_404(team_id)
|
||||
tryout = Tryout.query.get_or_404(tryout_id)
|
||||
if not current_user.can_manage_this_tryout(tryout):
|
||||
flash('Permission denied.', 'danger')
|
||||
flash(_('Permission denied.'), 'danger')
|
||||
return redirect(url_for('tryouts.view_tryout', tryout_id=tryout_id))
|
||||
|
||||
# The two ids arrive independently in the URL. Without this check, being
|
||||
@@ -474,25 +475,25 @@ def add_to_team(tryout_id, team_id):
|
||||
|
||||
player_id = request.form.get('player_id', type=int)
|
||||
if not player_id:
|
||||
flash('Please select a player.', 'danger')
|
||||
flash(_('Please select a player.'), 'danger')
|
||||
return redirect(url_for('tryouts.view_tryout', tryout_id=tryout_id))
|
||||
|
||||
# Only players registered for this tryout may be placed on its teams.
|
||||
is_registered = TryoutRegistration.query.filter_by(
|
||||
tryout_id=tryout_id, player_id=player_id).first() is not None
|
||||
if not is_registered:
|
||||
flash('That player is not registered for this tryout.', 'danger')
|
||||
flash(_('That player is not registered for this tryout.'), 'danger')
|
||||
return redirect(url_for('tryouts.view_tryout', tryout_id=tryout_id))
|
||||
|
||||
position = request.form.get('position', '')
|
||||
existing = TeamMember.query.filter_by(team_id=team_id, player_id=player_id).first()
|
||||
if existing:
|
||||
flash('Player is already on this team.', 'info')
|
||||
flash(_('Player is already on this team.'), 'info')
|
||||
else:
|
||||
member = TeamMember(team_id=team_id, player_id=player_id, position=position)
|
||||
db.session.add(member)
|
||||
db.session.commit()
|
||||
flash('Player added to team!', 'success')
|
||||
flash(_('Player added to team!'), 'success')
|
||||
return redirect(url_for('tryouts.view_tryout', tryout_id=tryout_id))
|
||||
|
||||
|
||||
@@ -502,7 +503,7 @@ def delete_tryout(tryout_id):
|
||||
"""Delete a tryout and all associated data (matches, teams, registrations, evaluations)."""
|
||||
tryout = Tryout.query.get_or_404(tryout_id)
|
||||
if not current_user.can_manage_this_tryout(tryout):
|
||||
flash('You do not have permission to delete this tryout.', 'danger')
|
||||
flash(_('You do not have permission to delete this tryout.'), 'danger')
|
||||
return redirect(url_for('tryouts.list_tryouts'))
|
||||
|
||||
match_ids = [m.id for m in Match.query.filter_by(tryout_id=tryout_id).all()]
|
||||
@@ -537,5 +538,5 @@ def delete_tryout(tryout_id):
|
||||
|
||||
db.session.delete(tryout)
|
||||
db.session.commit()
|
||||
flash('Tryout deleted successfully.', 'success')
|
||||
flash(_('Tryout deleted successfully.'), 'success')
|
||||
return redirect(url_for('tryouts.list_tryouts'))
|
||||
+72
-71
@@ -7,6 +7,7 @@ import os
|
||||
import uuid
|
||||
from flask import Blueprint, render_template, redirect, url_for, flash, request, jsonify, send_file
|
||||
from flask_login import login_required, current_user
|
||||
from flask_babel import gettext as _
|
||||
from app.extensions import db, hash_password
|
||||
from app.models import (
|
||||
Admin, Manager, Coach, Player, Scout,
|
||||
@@ -42,7 +43,7 @@ def _flash_validation_errors(err):
|
||||
"""Surface marshmallow errors the same way auth.py already does."""
|
||||
for field, messages in err.messages.items():
|
||||
for msg in messages:
|
||||
flash(f'{field}: {msg}', 'danger')
|
||||
flash(_('%(field)s: %(msg)s', field=field, msg=msg), 'danger')
|
||||
|
||||
|
||||
def _form_payload(*, checkboxes=(), list_fields=('games',), optional_blank=('password',)):
|
||||
@@ -112,7 +113,7 @@ _USER_CLASS_MAP = {
|
||||
def list_users():
|
||||
"""List all users for management (Admin only)."""
|
||||
if not isinstance(current_user, Admin):
|
||||
flash('Only the president can manage users.', 'danger')
|
||||
flash(_('Only the president can manage users.'), 'danger')
|
||||
return redirect(url_for('main.dashboard'))
|
||||
|
||||
users = User.query.order_by(User.role, User.username).all()
|
||||
@@ -124,7 +125,7 @@ def list_users():
|
||||
def edit_user(user_id):
|
||||
"""Edit an existing user (Admin only)."""
|
||||
if not isinstance(current_user, Admin):
|
||||
flash('Only the president can edit users.', 'danger')
|
||||
flash(_('Only the president can edit users.'), 'danger')
|
||||
return redirect(url_for('main.dashboard'))
|
||||
|
||||
user = User.query.get_or_404(user_id)
|
||||
@@ -164,15 +165,15 @@ def edit_user(user_id):
|
||||
# address surfaced as an IntegrityError, i.e. a 500.
|
||||
clash = User.query.filter(User.email == email, User.id != user.id).first()
|
||||
if clash:
|
||||
flash('Email already in use by another account.', 'danger')
|
||||
flash(_('Email already in use by another account.'), 'danger')
|
||||
return _rerender()
|
||||
|
||||
if user.role != role:
|
||||
# Two ways to lock everyone out of administration, neither of
|
||||
# which any interface can undo afterwards.
|
||||
if user.id == actor_id:
|
||||
flash('You cannot change your own role. Ask another president '
|
||||
'to do it.', 'danger')
|
||||
flash(_('You cannot change your own role. Ask another '
|
||||
'president to do it.'), 'danger')
|
||||
return _rerender()
|
||||
|
||||
if user.role == 'admin':
|
||||
@@ -182,8 +183,8 @@ def edit_user(user_id):
|
||||
User.id != user.id,
|
||||
).count()
|
||||
if remaining_admins == 0:
|
||||
flash('This is the last active president. Promote another '
|
||||
'account before changing this one.', 'danger')
|
||||
flash(_('This is the last active president. Promote '
|
||||
'another account before changing this one.'), 'danger')
|
||||
return _rerender()
|
||||
|
||||
# Change role via raw SQL to avoid polymorphic identity corruption.
|
||||
@@ -228,7 +229,7 @@ def edit_user(user_id):
|
||||
log_auth_event('account.updated',
|
||||
actor=actor_name, actor_id=actor_id,
|
||||
target=user.username, target_id=user.id, active=is_active)
|
||||
flash(f'User {user.username} updated successfully!', 'success')
|
||||
flash(_('User %(username)s updated successfully!', username=user.username), 'success')
|
||||
return redirect(url_for('users.list_users'))
|
||||
|
||||
user_gamertags = {gt.game: {'gamertag': gt.gamertag, 'platform': gt.platform}
|
||||
@@ -243,11 +244,11 @@ def edit_user(user_id):
|
||||
def delete_user(user_id):
|
||||
"""Delete a user (Admin only)."""
|
||||
if not isinstance(current_user, Admin):
|
||||
flash('Only the president can delete users.', 'danger')
|
||||
flash(_('Only the president can delete users.'), 'danger')
|
||||
return redirect(url_for('main.dashboard'))
|
||||
|
||||
if current_user.id == user_id:
|
||||
flash('You cannot delete your own account.', 'danger')
|
||||
flash(_('You cannot delete your own account.'), 'danger')
|
||||
return redirect(url_for('users.list_users'))
|
||||
|
||||
user = User.query.get_or_404(user_id)
|
||||
@@ -284,7 +285,7 @@ def delete_user(user_id):
|
||||
log_auth_event('account.deleted',
|
||||
actor=current_user.username, actor_id=current_user.id,
|
||||
target=deleted_username, target_id=user_id, role=deleted_role)
|
||||
flash(f'User {deleted_username} has been removed.', 'success')
|
||||
flash(_('User %(deleted_username)s has been removed.', deleted_username=deleted_username), 'success')
|
||||
return redirect(url_for('users.list_users'))
|
||||
|
||||
|
||||
@@ -293,7 +294,7 @@ def delete_user(user_id):
|
||||
def create_user():
|
||||
"""Create a new user (Admin only). Uses the correct polymorphic subclass."""
|
||||
if not isinstance(current_user, Admin):
|
||||
flash('Only the president can create users.', 'danger')
|
||||
flash(_('Only the president can create users.'), 'danger')
|
||||
return redirect(url_for('main.dashboard'))
|
||||
|
||||
if request.method == 'POST':
|
||||
@@ -313,11 +314,11 @@ def create_user():
|
||||
role = validated['role']
|
||||
|
||||
if User.query.filter_by(username=username).first():
|
||||
flash('Username already exists.', 'danger')
|
||||
flash(_('Username already exists.'), 'danger')
|
||||
return render_template('pages/create_user.html', roles=USER_TYPES)
|
||||
|
||||
if User.query.filter_by(email=email).first():
|
||||
flash('Email already registered.', 'danger')
|
||||
flash(_('Email already registered.'), 'danger')
|
||||
return render_template('pages/create_user.html', roles=USER_TYPES)
|
||||
|
||||
hashed_password = hash_password(password)
|
||||
@@ -332,7 +333,7 @@ def create_user():
|
||||
log_auth_event('account.created_by_admin',
|
||||
actor=current_user.username, actor_id=current_user.id,
|
||||
target=user.username, target_id=user.id, role=role)
|
||||
flash(f'User {full_name} created as {role}!', 'success')
|
||||
flash(_('User %(full_name)s created as %(role)s!', full_name=full_name, role=role), 'success')
|
||||
return redirect(url_for('users.list_users'))
|
||||
|
||||
return render_template('pages/create_user.html', roles=USER_TYPES)
|
||||
@@ -389,13 +390,13 @@ def edit_profile():
|
||||
league_os_profile = validated.get('league_os_profile')
|
||||
|
||||
if username != current_user.username and User.query.filter_by(username=username).first():
|
||||
flash('Username already taken.', 'danger')
|
||||
flash(_('Username already taken.'), 'danger')
|
||||
return render_template('pages/edit_profile.html', user=current_user,
|
||||
esport_games=ESPORT_GAMES, game_platforms=GAME_PLATFORMS,
|
||||
user_gamertags=current_user.get_gamertags())
|
||||
|
||||
if email != current_user.email and User.query.filter_by(email=email).first():
|
||||
flash('Email already in use.', 'danger')
|
||||
flash(_('Email already in use.'), 'danger')
|
||||
return render_template('pages/edit_profile.html', user=current_user,
|
||||
esport_games=ESPORT_GAMES, game_platforms=GAME_PLATFORMS,
|
||||
user_gamertags=current_user.get_gamertags())
|
||||
@@ -420,7 +421,7 @@ def edit_profile():
|
||||
username=current_user.username, user_id=current_user.id)
|
||||
|
||||
db.session.commit()
|
||||
flash('Profile updated successfully!', 'success')
|
||||
flash(_('Profile updated successfully!'), 'success')
|
||||
return redirect(url_for('users.profile'))
|
||||
|
||||
return render_template('pages/edit_profile.html', user=current_user,
|
||||
@@ -625,7 +626,7 @@ def list_contracts():
|
||||
def upload_contract():
|
||||
"""Upload a contract for a player."""
|
||||
if not isinstance(current_user, (Admin, Manager, Coach)):
|
||||
flash('Only presidents, managers, and coaches can upload contracts.', 'danger')
|
||||
flash(_('Only presidents, managers, and coaches can upload contracts.'), 'danger')
|
||||
return redirect(url_for('users.list_contracts'))
|
||||
|
||||
if isinstance(current_user, Coach):
|
||||
@@ -645,26 +646,26 @@ def upload_contract():
|
||||
except ValidationError as err:
|
||||
for field, messages in err.messages.items():
|
||||
for msg in messages:
|
||||
flash(f'{field}: {msg}', 'danger')
|
||||
flash(_('%(field)s: %(msg)s', field=field, msg=msg), 'danger')
|
||||
return render_template('pages/upload_contract.html', players=players)
|
||||
|
||||
player_id = validated['player_id']
|
||||
notes = validated.get('notes')
|
||||
|
||||
if not can_manage_player_contract(current_user, player_id):
|
||||
flash('You do not have permission to upload a contract for this player.', 'danger')
|
||||
flash(_('You do not have permission to upload a contract for this player.'), 'danger')
|
||||
return redirect(url_for('users.upload_contract'))
|
||||
|
||||
if 'contract_file' not in request.files:
|
||||
flash('No file selected.', 'danger')
|
||||
flash(_('No file selected.'), 'danger')
|
||||
return redirect(url_for('users.upload_contract'))
|
||||
|
||||
file = request.files['contract_file']
|
||||
if file.filename == '':
|
||||
flash('No file selected.', 'danger')
|
||||
flash(_('No file selected.'), 'danger')
|
||||
return redirect(url_for('users.upload_contract'))
|
||||
if not file.filename.lower().endswith('.pdf'):
|
||||
flash('Only PDF files are allowed for contracts.', 'danger')
|
||||
flash(_('Only PDF files are allowed for contracts.'), 'danger')
|
||||
return redirect(url_for('users.upload_contract'))
|
||||
|
||||
upload_dir = os.path.join(os.getcwd(), 'documents', 'contrats signés')
|
||||
@@ -697,7 +698,7 @@ def upload_contract():
|
||||
)
|
||||
db.session.add(contract)
|
||||
db.session.commit()
|
||||
flash(f'Contract uploaded successfully for {player.username}!', 'success')
|
||||
flash(_('Contract uploaded successfully for %(username)s!', username=player.username), 'success')
|
||||
return redirect(url_for('users.list_contracts'))
|
||||
|
||||
return render_template('pages/upload_contract.html', players=players)
|
||||
@@ -709,16 +710,16 @@ def upload_signed_contract(contract_id):
|
||||
"""Upload a signed contract (player only)."""
|
||||
contract = Contract.query.get_or_404(contract_id)
|
||||
if not contract.can_upload_signed(current_user):
|
||||
flash('Only the player can upload their signed contract.', 'danger')
|
||||
flash(_('Only the player can upload their signed contract.'), 'danger')
|
||||
return redirect(url_for('users.list_contracts'))
|
||||
|
||||
if 'signed_file' not in request.files:
|
||||
flash('No file selected.', 'danger')
|
||||
flash(_('No file selected.'), 'danger')
|
||||
return redirect(url_for('users.list_contracts'))
|
||||
|
||||
file = request.files['signed_file']
|
||||
if file.filename == '':
|
||||
flash('No file selected.', 'danger')
|
||||
flash(_('No file selected.'), 'danger')
|
||||
return redirect(url_for('users.list_contracts'))
|
||||
|
||||
signed_filename = f"signed_{contract.stored_filename}"
|
||||
@@ -729,7 +730,7 @@ def upload_signed_contract(contract_id):
|
||||
contract.status = 'signed'
|
||||
contract.signed_at = datetime.utcnow()
|
||||
db.session.commit()
|
||||
flash('Signed contract uploaded successfully!', 'success')
|
||||
flash(_('Signed contract uploaded successfully!'), 'success')
|
||||
return redirect(url_for('users.list_contracts'))
|
||||
|
||||
|
||||
@@ -739,7 +740,7 @@ def download_contract(contract_id):
|
||||
"""Download a contract file."""
|
||||
contract = Contract.query.get_or_404(contract_id)
|
||||
if not contract.can_view(current_user):
|
||||
flash('You do not have permission to download this contract.', 'danger')
|
||||
flash(_('You do not have permission to download this contract.'), 'danger')
|
||||
return redirect(url_for('users.list_contracts'))
|
||||
return send_file(contract.file_path, as_attachment=True, download_name=contract.original_filename)
|
||||
|
||||
@@ -750,10 +751,10 @@ def download_signed_contract(contract_id):
|
||||
"""Download a signed contract file."""
|
||||
contract = Contract.query.get_or_404(contract_id)
|
||||
if not contract.can_view(current_user):
|
||||
flash('You do not have permission to download this contract.', 'danger')
|
||||
flash(_('You do not have permission to download this contract.'), 'danger')
|
||||
return redirect(url_for('users.list_contracts'))
|
||||
if not contract.signed_file_path:
|
||||
flash('No signed contract available.', 'danger')
|
||||
flash(_('No signed contract available.'), 'danger')
|
||||
return redirect(url_for('users.list_contracts'))
|
||||
return send_file(contract.signed_file_path, as_attachment=True, download_name=contract.signed_filename)
|
||||
|
||||
@@ -820,7 +821,7 @@ def send_discord_notification(player_name, points, date_str, start_time_str, end
|
||||
def one_on_one():
|
||||
"""One on One request page for players."""
|
||||
if not isinstance(current_user, Player):
|
||||
flash('Only players can request One on One sessions.', 'danger')
|
||||
flash(_('Only players can request One on One sessions.'), 'danger')
|
||||
return redirect(url_for('main.dashboard'))
|
||||
|
||||
org_teams = current_user.get_org_teams()
|
||||
@@ -828,7 +829,7 @@ def one_on_one():
|
||||
coach = User.query.get(org_team.coach_id) if org_team and org_team.coach_id else None
|
||||
|
||||
if not coach:
|
||||
flash('You do not have a coach assigned to your team.', 'info')
|
||||
flash(_('You do not have a coach assigned to your team.'), 'info')
|
||||
|
||||
team_notes = []
|
||||
if org_team:
|
||||
@@ -855,7 +856,7 @@ def one_on_one():
|
||||
points = request.form.get('points', '').strip()
|
||||
|
||||
if not coach:
|
||||
flash('Cannot request One on One - no coach assigned.', 'danger')
|
||||
flash(_('Cannot request One on One - no coach assigned.'), 'danger')
|
||||
return redirect(url_for('users.one_on_one'))
|
||||
|
||||
try:
|
||||
@@ -863,7 +864,7 @@ def one_on_one():
|
||||
start_time = datetime.strptime(start_time_str, '%H:%M').time()
|
||||
end_time = datetime.strptime(end_time_str, '%H:%M').time()
|
||||
except (ValueError, TypeError):
|
||||
flash('Invalid date or time format.', 'danger')
|
||||
flash(_('Invalid date or time format.'), 'danger')
|
||||
return redirect(url_for('users.one_on_one'))
|
||||
|
||||
check_date = datetime.strptime(date_str, '%Y-%m-%d')
|
||||
@@ -877,7 +878,7 @@ def one_on_one():
|
||||
)
|
||||
|
||||
if not is_available:
|
||||
flash("The requested time is not within the coach's availability.", 'danger')
|
||||
flash(_("The requested time is not within the coach's availability."), 'danger')
|
||||
return redirect(url_for('users.one_on_one'))
|
||||
|
||||
request_obj = OneOnOneRequest(
|
||||
@@ -900,7 +901,7 @@ def one_on_one():
|
||||
request_id=request_obj.id,
|
||||
)
|
||||
|
||||
flash('Your One on One request has been submitted!', 'success')
|
||||
flash(_('Your One on One request has been submitted!'), 'success')
|
||||
return redirect(url_for('users.one_on_one'))
|
||||
|
||||
# Build list of upcoming dates that have coach availability
|
||||
@@ -935,17 +936,17 @@ def one_on_one():
|
||||
def accept_one_on_one(request_id):
|
||||
"""Coach accepts a One on One request."""
|
||||
if not isinstance(current_user, Coach):
|
||||
flash('Only coaches can accept One on One requests.', 'danger')
|
||||
flash(_('Only coaches can accept One on One requests.'), 'danger')
|
||||
return redirect(url_for('main.dashboard'))
|
||||
|
||||
request_obj = OneOnOneRequest.query.get_or_404(request_id)
|
||||
|
||||
if request_obj.coach_id != current_user.id:
|
||||
flash('This request is not for you.', 'danger')
|
||||
flash(_('This request is not for you.'), 'danger')
|
||||
return redirect(url_for('users.notes_dashboard'))
|
||||
|
||||
if request_obj.status != 'pending':
|
||||
flash('This request has already been processed.', 'info')
|
||||
flash(_('This request has already been processed.'), 'info')
|
||||
return redirect(url_for('users.notes_dashboard'))
|
||||
|
||||
player = request_obj.player
|
||||
@@ -967,7 +968,7 @@ def accept_one_on_one(request_id):
|
||||
approved=True,
|
||||
)
|
||||
|
||||
flash(f'One on One request from {player.username if player else "Unknown"} has been approved!', 'success')
|
||||
flash(_('One on One request from %(player)s has been approved!', player=player.username if player else 'Unknown'), 'success')
|
||||
return redirect(url_for('users.notes_dashboard'))
|
||||
|
||||
|
||||
@@ -976,17 +977,17 @@ def accept_one_on_one(request_id):
|
||||
def reject_one_on_one(request_id):
|
||||
"""Coach rejects a One on One request."""
|
||||
if not isinstance(current_user, Coach):
|
||||
flash('Only coaches can reject One on One requests.', 'danger')
|
||||
flash(_('Only coaches can reject One on One requests.'), 'danger')
|
||||
return redirect(url_for('main.dashboard'))
|
||||
|
||||
request_obj = OneOnOneRequest.query.get_or_404(request_id)
|
||||
|
||||
if request_obj.coach_id != current_user.id:
|
||||
flash('This request is not for you.', 'danger')
|
||||
flash(_('This request is not for you.'), 'danger')
|
||||
return redirect(url_for('users.notes_dashboard'))
|
||||
|
||||
if request_obj.status != 'pending':
|
||||
flash('This request has already been processed.', 'info')
|
||||
flash(_('This request has already been processed.'), 'info')
|
||||
return redirect(url_for('users.notes_dashboard'))
|
||||
|
||||
rejection_reason = request.form.get('rejection_reason', '').strip()
|
||||
@@ -1013,7 +1014,7 @@ def reject_one_on_one(request_id):
|
||||
refusal_note=rejection_reason or None,
|
||||
)
|
||||
|
||||
flash(f'One on One request from {player.username if player else "Unknown"} has been rejected.', 'info')
|
||||
flash(_('One on One request from %(player)s has been rejected.', player=player.username if player else 'Unknown'), 'info')
|
||||
return redirect(url_for('users.notes_dashboard'))
|
||||
|
||||
|
||||
@@ -1026,7 +1027,7 @@ def reject_one_on_one(request_id):
|
||||
def my_notes():
|
||||
"""View personal and team notes for the current player."""
|
||||
if not isinstance(current_user, Player):
|
||||
flash('This page is for players only.', 'info')
|
||||
flash(_('This page is for players only.'), 'info')
|
||||
return redirect(url_for('main.dashboard'))
|
||||
|
||||
org_teams = current_user.get_org_teams()
|
||||
@@ -1057,7 +1058,7 @@ def my_notes():
|
||||
def manage_coach_availability():
|
||||
"""Manage coach availability for One on One sessions."""
|
||||
if not isinstance(current_user, Coach):
|
||||
flash('Only coaches can manage availability.', 'danger')
|
||||
flash(_('Only coaches can manage availability.'), 'danger')
|
||||
return redirect(url_for('main.dashboard'))
|
||||
|
||||
if request.method == 'POST':
|
||||
@@ -1119,7 +1120,7 @@ def clear_coach_availability():
|
||||
def notes_dashboard():
|
||||
"""Notes and One on One dashboard for coaches."""
|
||||
if not isinstance(current_user, Coach):
|
||||
flash('Only coaches can access the notes dashboard.', 'danger')
|
||||
flash(_('Only coaches can access the notes dashboard.'), 'danger')
|
||||
return redirect(url_for('main.dashboard'))
|
||||
|
||||
org_team = OrgTeam.query.filter_by(coach_id=current_user.id).first()
|
||||
@@ -1180,12 +1181,12 @@ def notes_dashboard():
|
||||
def manage_team_notes():
|
||||
"""Create or update team notes for the coach's org team."""
|
||||
if not isinstance(current_user, Coach):
|
||||
flash('Only coaches can manage team notes.', 'danger')
|
||||
flash(_('Only coaches can manage team notes.'), 'danger')
|
||||
return redirect(url_for('main.dashboard'))
|
||||
|
||||
org_team = OrgTeam.query.filter_by(coach_id=current_user.id).first()
|
||||
if not org_team:
|
||||
flash('You are not assigned to a team.', 'danger')
|
||||
flash(_('You are not assigned to a team.'), 'danger')
|
||||
return redirect(url_for('users.notes_dashboard'))
|
||||
|
||||
content = request.form.get('content', '').strip()
|
||||
@@ -1197,7 +1198,7 @@ def manage_team_notes():
|
||||
)
|
||||
db.session.add(note)
|
||||
db.session.commit()
|
||||
flash('Team notes saved successfully!', 'success')
|
||||
flash(_('Team notes saved successfully!'), 'success')
|
||||
|
||||
return redirect(url_for('users.notes_dashboard'))
|
||||
|
||||
@@ -1211,23 +1212,23 @@ def manage_team_notes():
|
||||
def manage_personal_notes():
|
||||
"""Create a personal note for a player (coach only, simple form)."""
|
||||
if not isinstance(current_user, Coach):
|
||||
flash('Only coaches can manage personal notes.', 'danger')
|
||||
flash(_('Only coaches can manage personal notes.'), 'danger')
|
||||
return redirect(url_for('main.dashboard'))
|
||||
|
||||
player_id = request.form.get('player_id', type=int)
|
||||
content = request.form.get('content', '').strip()
|
||||
|
||||
if not player_id or not content:
|
||||
flash('Player and content are required.', 'danger')
|
||||
flash(_('Player and content are required.'), 'danger')
|
||||
return redirect(url_for('users.notes_dashboard'))
|
||||
|
||||
player = User.query.get_or_404(player_id)
|
||||
if not isinstance(player, Player):
|
||||
flash('Can only add notes for players.', 'danger')
|
||||
flash(_('Can only add notes for players.'), 'danger')
|
||||
return redirect(url_for('users.notes_dashboard'))
|
||||
|
||||
if not coach_can_access_player(current_user, player_id):
|
||||
flash('You can only write notes about players you work with.', 'danger')
|
||||
flash(_('You can only write notes about players you work with.'), 'danger')
|
||||
return redirect(url_for('users.notes_dashboard'))
|
||||
|
||||
note = PersonalNote(
|
||||
@@ -1237,7 +1238,7 @@ def manage_personal_notes():
|
||||
)
|
||||
db.session.add(note)
|
||||
db.session.commit()
|
||||
flash(f'Note added for {player.username}.', 'success')
|
||||
flash(_('Note added for %(username)s.', username=player.username), 'success')
|
||||
return redirect(url_for('users.notes_dashboard'))
|
||||
|
||||
|
||||
@@ -1250,7 +1251,7 @@ def manage_personal_notes():
|
||||
def add_personal_note():
|
||||
"""Create a personal note for a player with optional context (coach only)."""
|
||||
if not isinstance(current_user, Coach):
|
||||
flash('Only coaches can add personal notes.', 'danger')
|
||||
flash(_('Only coaches can add personal notes.'), 'danger')
|
||||
return redirect(url_for('main.dashboard'))
|
||||
|
||||
player_id = request.form.get('player_id', type=int)
|
||||
@@ -1260,16 +1261,16 @@ def add_personal_note():
|
||||
team_id_str = request.form.get('team_id')
|
||||
|
||||
if not player_id or not content:
|
||||
flash('Player and content are required.', 'danger')
|
||||
flash(_('Player and content are required.'), 'danger')
|
||||
return redirect(url_for('users.notes_dashboard'))
|
||||
|
||||
player = User.query.get_or_404(player_id)
|
||||
if not isinstance(player, Player):
|
||||
flash('Can only add notes for players.', 'danger')
|
||||
flash(_('Can only add notes for players.'), 'danger')
|
||||
return redirect(url_for('users.notes_dashboard'))
|
||||
|
||||
if not coach_can_access_player(current_user, player_id):
|
||||
flash('You can only write notes about players you work with.', 'danger')
|
||||
flash(_('You can only write notes about players you work with.'), 'danger')
|
||||
return redirect(url_for('users.notes_dashboard'))
|
||||
|
||||
note = PersonalNote(
|
||||
@@ -1282,7 +1283,7 @@ def add_personal_note():
|
||||
)
|
||||
db.session.add(note)
|
||||
db.session.commit()
|
||||
flash(f'Note added for {player.username}.', 'success')
|
||||
flash(_('Note added for %(username)s.', username=player.username), 'success')
|
||||
return redirect(url_for('users.notes_dashboard'))
|
||||
|
||||
|
||||
@@ -1295,7 +1296,7 @@ def add_personal_note():
|
||||
def add_note_from_tryout(tryout_id):
|
||||
"""Add a personal note for a player in the context of a tryout."""
|
||||
if not isinstance(current_user, Coach):
|
||||
flash('Only coaches can add personal notes.', 'danger')
|
||||
flash(_('Only coaches can add personal notes.'), 'danger')
|
||||
return redirect(url_for('main.dashboard'))
|
||||
|
||||
tryout = Tryout.query.get_or_404(tryout_id)
|
||||
@@ -1310,11 +1311,11 @@ def add_note_from_tryout(tryout_id):
|
||||
content = request.form.get('content', '').strip()
|
||||
|
||||
if not player_id or not content:
|
||||
flash('Player and content are required.', 'danger')
|
||||
flash(_('Player and content are required.'), 'danger')
|
||||
return redirect(url_for('users.add_note_from_tryout', tryout_id=tryout_id))
|
||||
|
||||
if not coach_can_access_player(current_user, player_id):
|
||||
flash('You can only write notes about players you work with.', 'danger')
|
||||
flash(_('You can only write notes about players you work with.'), 'danger')
|
||||
return redirect(url_for('users.add_note_from_tryout', tryout_id=tryout_id))
|
||||
|
||||
note = PersonalNote(
|
||||
@@ -1325,7 +1326,7 @@ def add_note_from_tryout(tryout_id):
|
||||
)
|
||||
db.session.add(note)
|
||||
db.session.commit()
|
||||
flash('Note added successfully.', 'success')
|
||||
flash(_('Note added successfully.'), 'success')
|
||||
return redirect(url_for('tryouts.view_tryout', tryout_id=tryout_id))
|
||||
|
||||
return render_template('pages/add_note.html',
|
||||
@@ -1345,7 +1346,7 @@ def add_note_from_tryout(tryout_id):
|
||||
def add_note_from_match(match_id):
|
||||
"""Add a personal note for a player in the context of a match."""
|
||||
if not isinstance(current_user, Coach):
|
||||
flash('Only coaches can add personal notes.', 'danger')
|
||||
flash(_('Only coaches can add personal notes.'), 'danger')
|
||||
return redirect(url_for('main.dashboard'))
|
||||
|
||||
match_obj = Match.query.get_or_404(match_id)
|
||||
@@ -1361,11 +1362,11 @@ def add_note_from_match(match_id):
|
||||
content = request.form.get('content', '').strip()
|
||||
|
||||
if not player_id or not content:
|
||||
flash('Player and content are required.', 'danger')
|
||||
flash(_('Player and content are required.'), 'danger')
|
||||
return redirect(url_for('users.add_note_from_match', match_id=match_id))
|
||||
|
||||
if not coach_can_access_player(current_user, player_id):
|
||||
flash('You can only write notes about players you work with.', 'danger')
|
||||
flash(_('You can only write notes about players you work with.'), 'danger')
|
||||
return redirect(url_for('users.add_note_from_match', match_id=match_id))
|
||||
|
||||
note = PersonalNote(
|
||||
@@ -1376,7 +1377,7 @@ def add_note_from_match(match_id):
|
||||
)
|
||||
db.session.add(note)
|
||||
db.session.commit()
|
||||
flash('Note added successfully.', 'success')
|
||||
flash(_('Note added successfully.'), 'success')
|
||||
return redirect(url_for('tryouts.view_tryout', tryout_id=match_obj.tryout_id))
|
||||
|
||||
return render_template('pages/add_note.html',
|
||||
|
||||
Reference in New Issue
Block a user