90 lines
3.1 KiB
Python
90 lines
3.1 KiB
Python
"""Helpers used by more than one route module in this package.
|
|
|
|
Nothing here touches the blueprint: these are plain functions, so a test
|
|
can call them with a request context and nothing else.
|
|
"""
|
|
|
|
from flask_babel import gettext as _
|
|
|
|
from app.extensions import db
|
|
|
|
# Re-exported: these two moved to app/forms.py once the match and tryout
|
|
# routes needed them as well (ARCH-005). Importing them from here still
|
|
# works, so the thirty call sites in this package did not have to move.
|
|
from app.forms import flash_validation_errors, form_payload # noqa: F401
|
|
from app.models import Admin, Coach, Manager, Player, Scout, UserGamertag
|
|
|
|
ALLOWED_CONTRACT_EXTENSIONS = {'pdf'}
|
|
ALLOWED_SIGNED_EXTENSIONS = {'pdf'}
|
|
|
|
#: Every PDF starts with this. Checking the name alone accepted a file
|
|
#: called anything.pdf holding anything at all.
|
|
PDF_SIGNATURE = b'%PDF-'
|
|
|
|
#: USER_TYPE → model class, for create_user.
|
|
USER_CLASS_MAP = {
|
|
'admin': Admin,
|
|
'manager': Manager,
|
|
'coach': Coach,
|
|
'player': Player,
|
|
'scout': Scout,
|
|
}
|
|
|
|
|
|
def pdf_upload_error(file, allowed_extensions):
|
|
"""Why this upload is not an acceptable PDF, or None if it is.
|
|
|
|
upload_signed_contract checked nothing beyond a non-empty filename —
|
|
ALLOWED_SIGNED_EXTENSIONS was declared and never read — so a player
|
|
could put an arbitrary file on the server under a name the application
|
|
later hands back for download (SEC-021).
|
|
|
|
Args:
|
|
file: The uploaded FileStorage, or None.
|
|
allowed_extensions: Extensions to accept, lowercase and without dot.
|
|
|
|
Returns:
|
|
str | None: A message to flash, or None when the file is acceptable.
|
|
"""
|
|
if file is None or not file.filename:
|
|
return _('No file selected.')
|
|
|
|
stem, dot, extension = file.filename.rpartition('.')
|
|
if not (stem and dot) or extension.lower() not in allowed_extensions:
|
|
return _('Only PDF files are allowed for contracts.')
|
|
|
|
head = file.stream.read(len(PDF_SIGNATURE))
|
|
file.stream.seek(0)
|
|
if head != PDF_SIGNATURE:
|
|
return _('That file is not a PDF, whatever its name says.')
|
|
|
|
return None
|
|
|
|
|
|
def update_user_gamertags(user, selected_games):
|
|
"""Update gamertags for a user from validated dynamic form fields."""
|
|
from app.forms import form_gamertags
|
|
|
|
submitted = form_gamertags(selected_games)
|
|
existing_gamertags = {gt.game: gt for gt in user.gamertags}
|
|
for game in selected_games:
|
|
payload = submitted.get(game)
|
|
existing = existing_gamertags.get(game)
|
|
if payload:
|
|
if existing:
|
|
existing.gamertag = payload['gamertag']
|
|
existing.platform = payload['platform']
|
|
else:
|
|
gt = UserGamertag(
|
|
user_id=user.id,
|
|
game=game,
|
|
gamertag=payload['gamertag'],
|
|
platform=payload['platform'],
|
|
)
|
|
db.session.add(gt)
|
|
elif existing:
|
|
db.session.delete(existing)
|
|
for game in existing_gamertags:
|
|
if game not in selected_games:
|
|
db.session.delete(existing_gamertags[game])
|