diff --git a/app/app.py b/app/app.py index 2baf768..93fe0b2 100644 --- a/app/app.py +++ b/app/app.py @@ -118,10 +118,14 @@ def create_app(config=None): app.config['CORS_ALLOWED_ORIGINS'] = os.getenv('CORS_ALLOWED_ORIGINS', '') app.config['FORCE_HTTPS'] = os.getenv('FORCE_HTTPS', 'true').lower() == 'true' - # Still true: 76 inline event handlers remain across the templates, and - # no nonce can authorise those. Flip once tests/test_csp.py reports zero. + # Now false: every inline event handler has been replaced by a + # data-action attribute dispatched from main.js, so script-src no longer + # needs 'unsafe-inline'. Inline {% endblock %} \ No newline at end of file diff --git a/app/templates/pages/evaluate_player.html b/app/templates/pages/evaluate_player.html index b5bedfa..30faae2 100644 --- a/app/templates/pages/evaluate_player.html +++ b/app/templates/pages/evaluate_player.html @@ -32,21 +32,21 @@
- + {{ existing_eval.mecanics_score or 5 }}
- + {{ existing_eval.cohesion_score or 5 }}
- + {{ existing_eval.communication_score or 5 }}
@@ -56,21 +56,21 @@
- + {{ existing_eval.gamesense_score or 5 }}
- + {{ existing_eval.versatility_score or 5 }}
- + {{ existing_eval.discipline_score or 5 }}
@@ -80,21 +80,21 @@
- + {{ existing_eval.analysis_score or 5 }}
- + {{ existing_eval.sport_ethics_score or 5 }}
- + {{ existing_eval.mental_score or 5 }}
diff --git a/app/templates/pages/match_form.html b/app/templates/pages/match_form.html index 7b5074e..30ebb5d 100644 --- a/app/templates/pages/match_form.html +++ b/app/templates/pages/match_form.html @@ -32,7 +32,7 @@ {% if not match %}
- @@ -77,7 +77,7 @@

Select Match Time

Click time slots consecutively to set match duration. Players available in all selected time blocks are shown below.

-
@@ -154,7 +154,7 @@ {{ '✅ Confirmed' if pdata.attendance_confirmed else '⏳ Pending' }} @@ -180,7 +180,7 @@ {{ '✅ Confirmed' if pdata.attendance_confirmed else '⏳ Pending' }} @@ -204,10 +204,10 @@
- + vs 0 -
@@ -554,7 +554,7 @@ document.addEventListener('DOMContentLoaded', function() { var teamDiv = document.getElementById('team1-selection'); var playerName = playerDataById.player_data[pid]; if (playerName) { - var html = '
'; + var html = '
'; html += playerName; html += ''; html += '
'; @@ -567,7 +567,7 @@ document.addEventListener('DOMContentLoaded', function() { var teamDiv = document.getElementById('team2-selection'); var playerName = playerDataById.player_data[pid]; if (playerName) { - var html = '
'; + var html = '
'; html += playerName; html += ''; html += '
'; @@ -922,8 +922,8 @@ function updatePlayerPool() { html += '
'; html += '' + playerName + ''; html += '
'; - html += ''; - html += ''; + html += ''; + html += ''; html += '
'; html += '
'; }); @@ -942,7 +942,7 @@ function assignToTeam(playerId, teamSide) { var playerName = playerDataById.player_data[playerId]; if (!playerName) return; - var html = '
'; + var html = '
'; html += playerName; html += ''; html += '
'; @@ -952,8 +952,12 @@ function assignToTeam(playerId, teamSide) { updatePlayerPool(); } -function returnToPool(playerId, event) { +function returnToPool(element, event) { + // The clicked element already carries data-player-id: the generated + // markup sets it, so the value no longer has to be baked into an + // onclick attribute. if (event) event.stopPropagation(); + var playerId = element.getAttribute('data-player-id'); document.querySelectorAll('[data-player-id="' + playerId + '"]').forEach(function(el) { el.remove(); }); @@ -1057,7 +1061,7 @@ function randomizeTeams() { var teamDiv = document.getElementById('team1-selection'); var playerName = playerDataById.player_data[pid]; if (playerName) { - var html = '
'; + var html = '
'; html += playerName; html += ''; html += '
'; @@ -1069,7 +1073,7 @@ function randomizeTeams() { var teamDiv = document.getElementById('team2-selection'); var playerName = playerDataById.player_data[pid]; if (playerName) { - var html = '
'; + var html = '
'; html += playerName; html += ''; html += '
'; @@ -1110,5 +1114,27 @@ function togglePresence(matchId, participantId, badgeEl) { console.error('Error toggling presence:', error); }); } + +// Behaviours declared in the markup, dispatched by the delegated listener +// in main.js. Inline onclick attributes cannot be authorised by a CSP nonce. +registerActions({ + 'toggle-match-type': toggleMatchType, + 'clear-time-selection': clearTimeSelection, + 'update-randomize-preview': updateRandomizePreview, + 'randomize-teams': randomizeTeams, + 'return-to-pool': returnToPool, + 'assign-team': function (element) { + var item = element.closest('[data-player-id]'); + if (item) { + assignToTeam(item.getAttribute('data-player-id'), + element.getAttribute('data-team-side')); + } + }, + 'toggle-presence': function (element) { + togglePresence(element.getAttribute('data-match-id'), + element.getAttribute('data-participant-id'), + element); + }, +}); {% endblock %} diff --git a/app/templates/pages/one_on_one.html b/app/templates/pages/one_on_one.html index 5b58f3d..ba8b98e 100644 --- a/app/templates/pages/one_on_one.html +++ b/app/templates/pages/one_on_one.html @@ -170,7 +170,7 @@ {% if coach %} - {% endif %} diff --git a/app/templates/pages/teams.html b/app/templates/pages/teams.html index efdbe1a..682d2e6 100644 --- a/app/templates/pages/teams.html +++ b/app/templates/pages/teams.html @@ -5,7 +5,7 @@ {% block header_actions %} {% if can_manage %} - {% endif %} @@ -45,7 +45,7 @@
- +
@@ -63,7 +63,7 @@ -
+ @@ -111,7 +111,7 @@ {{ m.username }} {% if can_manage %} - + @@ -166,7 +166,7 @@ {% else %} @@ -180,7 +180,7 @@ {{ entry.player.phone or '-' }} {% if can_manage_team %} - + + {% else %}

There are no teams to display.

{% endif %} @@ -251,11 +251,11 @@