Compare commits

...

100 Commits

Author SHA1 Message Date
Tim 6b68d8a5ed Fix DB migration for sync_log: bump version to 121 and mirror into script.php
Addresses review feedback (jmeyer26):
- update.php: move the sync_log table + dtfb_sync_url setting out of the
  already-released <120 migration block into a new <121 block, so instances
  already at version 120 actually receive them.
- script.php: create the sync_log table, seed dtfb_sync_url, and set the
  fresh-install datenbank_version to 121 (parity with update.php).
2026-06-04 11:53:10 +02:00
Tim 5843fda2d6 QA: harden DTFB player sync receiver
Applies 6 fixes to sync.php found during QA of the player-sync feature:
1. Normalise non-UTF-8 (latin1/Win-1252) payloads -> fixes silent 0-row imports
2. Fail loudly (success=false) when N rows parse but nothing is added/updated
3. Remove dead \ block (undefined-variable notice)
4. Gate mass-deactivation: skip the sweep when a payload carries < 50% of an
   org's currently-active members (configurable via sync_deactivation_min_ratio,
   default 0.5); adds/updates still proceed, skipped sweeps return warnings
5. Use a single DB clock (NOW()) for staging session id/cleanup
6. Enforce Passnummer format ^[0-9]{2}-[0-9]{4,6}\$ (parity with manual import)

Adds tests/dtfb-player-sync/FINDINGS.md documenting the findings and fixes.
End-to-end validation is to be done on the staging environments.
2026-06-04 11:41:50 +02:00
Tim 511c17468c QA Edge Case Fixes: enforce spielernr and preserve DTFB fields
- Fix 1: Discard players missing spielernr instead of auto-generating them

- Fix 2: Preserve lizenznr, geburtsjahr, and pseudonym fields during player updates

- Fix 3: Resolve PHP 8 array offset warning by using !empty() for geschlecht parsing

- Fix 4: Leave clubless players handling as-is (skip them) per user request
2026-06-04 00:33:05 +02:00
Tim 6f33599fd9 Remove lizenz import handling - field should never be overwritten at DTFB 2026-06-04 00:09:31 +02:00
Tim aac4c1458f Merge sportsmanager2-dev and fix critical sync bugs
Merge resolution:
- Combined migration 120 (sync_log table + dev branch schema changes)

Bug fixes from intensive code review:
- C1: Fix session_id type mismatch - use datetime format matching
  the staging table schema instead of varchar string (was breaking
  the entire sync receive import)
- C2: Fix staging table cleanup query - use datetime comparison
  matching the original admin import pattern
- W1: Add set_time_limit(300) to prevent timeout during large imports
- W2: Add REDIRECT_HTTP_AUTHORIZATION header support for Apache
  mod_rewrite compatibility
- W4: Add lizenz column parsing and update during sync import
- M1: Tighten export WHERE clause to require both aktueller_verein_id
  and spielernr (consistent with original export behavior)
- M2: Wrap syncGetLastStatus() in try/catch for graceful handling
  when sync_log table doesn't exist yet
2026-06-04 00:02:56 +02:00
Tim f39ade0e9d Implement Player Sync to DTFB (#286) 2026-06-03 18:36:39 +02:00
MarvinF 2a307b0987 Merge branch 'sportsmanager2-stage' into sportsmanager2-dev 2026-05-13 00:03:28 +02:00
MarvinF e8e6f7046d Merge pull request #283 from Deutscher-Tischfussballbund/sportsmanager2-issue282
add club mailing functionality to admin area
2026-05-13 00:02:35 +02:00
Marvin Flock 20ab5a44a9 fix: add table headers 2026-05-13 00:00:34 +02:00
Jürgen Meyer a5357e4a51 mailto Funktion bei Mannschaften in admin-Bereich Veranstaltung 2026-04-28 11:46:22 +02:00
Jürgen Meyer 68e16a3adb mailto Funktion bei Vereine in admin-Bereich 2026-04-28 09:45:29 +02:00
MarvinF cfc821f8ff Merge branch 'sportsmanager2-prod' into sportsmanager2-stage 2026-04-14 19:09:46 +02:00
MarvinF 582829331c Merge pull request #280 from Deutscher-Tischfussballbund/sportsmanager2-dev
dev to stage
2026-04-14 19:08:39 +02:00
MarvinF d8ccd08843 Merge branch 'sportsmanager2-stage' into sportsmanager2-dev 2026-04-14 19:08:15 +02:00
MarvinF 57e92da771 Merge pull request #279 from Deutscher-Tischfussballbund/sportsmanager2-issue274-neu
Enhancing Playerstatistics (Performance Index, Club Membership and more)
2026-04-14 19:06:45 +02:00
Marvin Flock 7f85888a26 fix: add table fix 2026-04-14 19:02:38 +02:00
Marvin Flock 13ad52f221 fix: add small fixes 2026-04-14 18:50:05 +02:00
Jürgen Meyer a44564a40e Anzeige naegative Satzpunkte in Ligatabelle 2026-04-09 11:11:37 +02:00
Jürgen Meyer ee4e817ad4 Aktualisierung Spielerstatistiken 2026-04-06 08:10:55 +02:00
jmeyer26 8a7ff6c234 Merge branch 'sportsmanager2-dev' into sportsmanager2-issue274-neu 2026-04-06 07:41:01 +02:00
Jürgen Meyer 8fb4ed1cdd Filter Mannschaften in Spielerstatistik 2026-04-06 07:33:33 +02:00
Jürgen Meyer a2243d9ccf Teams und Filter Spiele in Wertung hinzugefügt. 2026-04-06 05:53:13 +02:00
MarvinF 308fbae4e5 Merge pull request #277 from Deutscher-Tischfussballbund/sportsmanager2-issue275
Display Teamlogo in Hall of Fame
2026-04-05 19:10:55 +02:00
MarvinF d6d2a04ceb Merge branch 'sportsmanager2-dev' into sportsmanager2-issue275 2026-04-05 19:10:32 +02:00
MarvinF d7d6751b70 Merge pull request #278 from Deutscher-Tischfussballbund/sportsmanager2-issue276
Optimization of direct comparison
2026-04-05 19:07:34 +02:00
Jürgen Meyer b112b4dc31 Effizienzindex und Punkteschnitt zu Spielerstatistoken hinzugefügt. 2026-04-04 12:42:07 +02:00
jmeyer26 ac416c1822 Fehler Vergleich Satzpunkte korrigiert 2026-04-03 07:37:32 +02:00
Jürgen Meyer c92774b27d Spielerstatistik Race Permormance Index hinzugefügt 2026-04-02 18:48:43 +02:00
Jürgen Meyer b33a7e6a25 Unnötige Variablen gelöscht 2026-04-02 11:59:39 +02:00
Jürgen Meyer f86815dae5 Auswahl Direkter Vergleich nach Punkte, Satzpunkte, Tore 2026-04-02 11:56:51 +02:00
Jürgen Meyer 43a03bbb09 Hall of Fame - Zuordnung Mannschaftswappen über Teamname 2026-04-01 15:51:23 +02:00
MarvinF f430c7d35d Merge pull request #273 from Deutscher-Tischfussballbund/sportsmanager2-stage
stage to prod
2026-03-24 17:17:57 +01:00
MarvinF 73c352f6cf Merge branch 'sportsmanager2-prod' into sportsmanager2-stage 2026-03-24 17:16:00 +01:00
MarvinF c8c19ef8e0 Merge pull request #272 from Deutscher-Tischfussballbund/sportsmanager2-dev
dev to stage
2026-03-24 17:13:22 +01:00
MarvinF cb0db3833c Merge branch 'sportsmanager2-stage' into sportsmanager2-dev 2026-03-24 17:12:24 +01:00
MarvinF 29e7ea7ffa Merge pull request #271 from Deutscher-Tischfussballbund/fix/common-update
fix: refactor the update php to allow different databases
2026-03-24 17:10:46 +01:00
MarvinF 426a582844 Merge pull request #270 from Deutscher-Tischfussballbund/sportsmanager2-issue269
Fixes for Direct Compare, Player Statistics, Home field right
2026-03-24 02:12:16 +01:00
Marvin Flock c1763536c9 fix: refactor the update php to allow different databases 2026-03-24 01:38:18 +01:00
Jürgen Meyer 507861fd56 Neuberechung Quote in Spielerstatistik 2026-03-23 13:33:45 +01:00
Jürgen Meyer 168ad7dab4 Terminänderung funktionierte nicht mehr bei Tausch Heimrecht. 2026-03-23 12:08:11 +01:00
Jürgen Meyer c8483b077e Weiter Optimierung 2026-03-17 13:08:02 +01:00
Jürgen Meyer c23ed24962 Kleine Optimierung 2026-03-17 13:05:59 +01:00
Jürgen Meyer f70048f90f Fehlerkorrektur Direkter Vergleich 2026-03-17 12:47:43 +01:00
MarvinF 572ccee6fd Merge pull request #267 from Deutscher-Tischfussballbund/sportsmanager2-stage
stage to prod
2026-03-10 00:13:41 +01:00
MarvinF fa03965b17 Merge branch 'sportsmanager2-prod' into sportsmanager2-stage 2026-03-10 00:13:14 +01:00
MarvinF 0376b46470 Merge pull request #266 from Deutscher-Tischfussballbund/sportsmanager2-dev
dev to stage
2026-03-10 00:11:00 +01:00
MarvinF c8f419e00a Merge branch 'sportsmanager2-stage' into sportsmanager2-dev 2026-03-10 00:10:35 +01:00
MarvinF 116e4929b2 Merge pull request #265 from Deutscher-Tischfussballbund/sportsmanager2-issue261-issue262
Optimizations for Hall of fame, gameplans, associations, game postponement and more
2026-03-10 00:09:13 +01:00
MarvinF aed4d19c47 Merge branch 'sportsmanager2-dev' into sportsmanager2-issue261-issue262 2026-03-10 00:08:54 +01:00
Jürgen Meyer 2faa38e2b8 Hilfsausgabe gelöscht 2026-03-09 07:44:59 +01:00
Jürgen Meyer dc0fcea945 Beanstandung Copilot abgearbeitet 5 2026-03-09 07:43:20 +01:00
Jürgen Meyer e139ac4fc2 Beanstandung Copilot abgearbeitet 4 2026-03-08 23:59:38 +01:00
jmeyer26 5919994a3e Update src/structure/components/com_sportsmanager/admin.php
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2026-03-08 23:02:02 +01:00
jmeyer26 eccecedf95 Update src/structure/language/de-DE/de-DE.com_sportsmanager.ini
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2026-03-08 22:58:31 +01:00
Jürgen Meyer 9ed58f2916 Beanstandung Copilot abgearbeitet 2 2026-03-08 22:43:05 +01:00
Jürgen Meyer 1a9188c07b Beanstandung Copilot abgearbeitet 1 2026-03-08 21:46:09 +01:00
Jürgen Meyer 9435515088 Beim Löschen von Spieler bzw. Vereine werden die entsprechenden ids in Hall of Fame und Verbandsorgane gelöscht 2026-03-08 20:43:10 +01:00
Jürgen Meyer 4ada3a3b77 Bemerkungen angepasst 2026-03-08 12:23:39 +01:00
Jürgen Meyer 8391bf9df1 Automatische Spielberichtskorrektur bei doppelter Spielnummer 2026-03-08 12:19:10 +01:00
Jürgen Meyer bac4c0ada4 Funktion Spielbericht löschen hinzugefügt. 2026-03-08 09:23:27 +01:00
MarvinF ba50b0d6a4 Merge pull request #264 from Deutscher-Tischfussballbund/sportsmanager2-stage
stage to prod
2026-03-07 01:12:47 +01:00
MarvinF e4c4694dcb Merge branch 'sportsmanager2-prod' into sportsmanager2-stage 2026-03-07 01:12:18 +01:00
MarvinF e5de0030f2 Merge pull request #263 from Deutscher-Tischfussballbund/sportsmanager2-dev
dev to stage
2026-03-07 01:10:38 +01:00
MarvinF 8d364e0bfd Merge branch 'sportsmanager2-stage' into sportsmanager2-dev 2026-03-07 01:10:17 +01:00
Jürgen Meyer eed02e396d Evtl. vorhandene Ordnungsstrafen werden beim Löschen eines Spieles mitgelöscht. 2026-03-06 20:16:54 +01:00
Jürgen Meyer 40be14ad75 Heimrechttausch: Sicherheitsabfrage und speichern in Historie 2026-03-06 15:10:01 +01:00
Jürgen Meyer 54965f9ef2 Scrollbalken bei Ordnungsstrafen, Verbandsorgane und Hall of Fame 2026-03-06 10:43:46 +01:00
jmeyer26 8780229435 Merge pull request #260 from Deutscher-Tischfussballbund/sportsmanager2-250
Hall of Fame hinzugefügt
2026-03-02 19:51:09 +01:00
Jürgen Meyer a856a979aa Hall of Fame hinzugefügt 2026-03-02 19:49:22 +01:00
Jürgen Meyer 578fb76ead Klammer in update.php fehlte nach resolve conflicts 2026-02-25 11:00:02 +01:00
jmeyer26 b199b73d6f Merge pull request #258 from Deutscher-Tischfussballbund/sportsmanager2-issue070
Sportsmanager2 issue070
2026-02-25 09:42:22 +01:00
jmeyer26 0d61e9a823 Merge branch 'sportsmanager2-dev' into sportsmanager2-issue070 2026-02-25 09:40:39 +01:00
jmeyer26 299b0bef4e Merge pull request #257 from Deutscher-Tischfussballbund/sportsmanager2-explicit-penalties
Explicit penalties for league teams
2026-02-25 09:30:49 +01:00
Jürgen Meyer 57c158a770 Kleine Korrekturen 2026-02-24 18:49:29 +01:00
Wieland Hagen 84d07aecb1 Explicit penalties for league teams (3): add moderator_user_id 2026-02-23 16:18:35 +01:00
Wieland Hagen 5bd0735708 Explicit penalties for league teams (2): resolved issues in PR 2026-02-23 15:43:29 +01:00
Jürgen Meyer 69e2032916 Letzte Änderung 2026-02-13 11:00:11 +01:00
jmeyer26 e5aede7cac Update src/structure/components/com_sportsmanager/admin.php
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2026-02-13 10:35:07 +01:00
jmeyer26 7f8f842796 Update src/structure/components/com_sportsmanager/admin.php
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2026-02-13 08:53:21 +01:00
jmeyer26 112511aa12 Update src/structure/components/com_sportsmanager/sportsmanager.php
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2026-02-13 08:49:51 +01:00
jmeyer26 eacb0dc165 Update src/structure/components/com_sportsmanager/views/sportsmanager/view.html.php
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2026-02-13 08:47:22 +01:00
Jürgen Meyer 67f5f38d1a Vorschläge des Copiloten umgesetzt 2026-02-13 08:10:35 +01:00
jmeyer26 f65ec6be88 Update src/structure/components/com_sportsmanager/database/update.php
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2026-02-13 06:54:41 +01:00
jmeyer26 427bd545af Update src/structure/script.php
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2026-02-13 06:54:20 +01:00
jmeyer26 986cd6d99e Update src/structure/components/com_sportsmanager/admin.php
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2026-02-13 06:42:58 +01:00
Jürgen Meyer 66baeb0f77 update.php und script.php erweitert 2026-02-12 17:04:26 +01:00
Jürgen Meyer 46474c6c40 Verteilerliste erweitert 2026-02-12 15:58:23 +01:00
Jürgen Meyer a583131162 Exportfunktion und ein paar Korrekturen 2026-02-12 15:06:38 +01:00
Jürgen Meyer cb8b6ebe06 Einige Optimierungen 2026-02-12 11:33:29 +01:00
Jürgen Meyer d41376494f Bilder hinzugefügt und ein paar Optimierungen 2026-02-11 17:03:02 +01:00
Jürgen Meyer d462137424 Frontend für Verbandsorgane erstellt. 2026-02-11 13:58:12 +01:00
Jürgen Meyer 3cb3fa2b28 Formulare für Mitglieder Verbandsorgane erstellt. 2026-02-10 16:23:57 +01:00
Jürgen Meyer 10807c7ea8 Organisation hinzugefügt und kleine Korrekturen. 2026-02-10 10:00:46 +01:00
Jürgen Meyer e469e70b97 Formulare für Tabelle Verbandsorgane erstellt 2026-02-09 17:20:45 +01:00
Wieland Hagen 522369bb14 Explicit penalties for league teams 2026-02-09 11:09:02 +01:00
MarvinF 91caf76a71 Merge pull request #256 from Deutscher-Tischfussballbund/sportsmanager2-stage
stage to prod
2026-02-03 19:27:41 +01:00
MarvinF 12037f9bac Merge branch 'sportsmanager2-prod' into sportsmanager2-stage 2026-02-03 19:27:10 +01:00
MarvinF b1af98df54 Merge pull request #255 from Deutscher-Tischfussballbund/sportsmanager2-dev
dev to stage
2026-02-03 19:25:43 +01:00
MarvinF fe885160d9 Merge branch 'sportsmanager2-stage' into sportsmanager2-dev 2026-02-03 19:25:22 +01:00
MarvinF 308bbd4d76 Update build_release.yml
remove teams notification
2026-02-03 19:23:19 +01:00
16 changed files with 5148 additions and 607 deletions
-21
View File
@@ -98,24 +98,3 @@ jobs:
else
echo "No draft releases found"
fi
- name: Send notification to Teams
run: |
curl -H 'Content-Type: application/json' \
-d "{
\"@type\": \"MessageCard\",
\"@context\": \"http://schema.org/extensions\",
\"summary\": \"New Release\",
\"themeColor\": \"0076D7\",
\"title\": \"🚀 New Release: ${{ github.ref_name }}\",
\"sections\": [{
\"activityTitle\": \"Repository: ${{ github.repository }}\",
\"text\": \"${{ steps.release_notes_github.outputs.body }}\"
}],
\"potentialAction\": [{
\"@type\": \"OpenUri\",
\"name\": \"View Release\",
\"targets\": [{ \"os\": \"default\", \"uri\": \"https://github.com/${{ github.repository }}/releases/tag/${{ github.ref_name }}\" }]
}]
}" \
${{ secrets.TEAMS_WEBHOOK_URL }}
+22
View File
@@ -0,0 +1,22 @@
name: Nightly DTFB Player Sync
on:
schedule:
- cron: '0 2 * * *' # Every night at 2:00 AM UTC
workflow_dispatch: # Allow manual trigger from GitHub
jobs:
sync:
runs-on: ubuntu-latest
steps:
- name: Trigger DTFB Sync
run: |
response=$(curl -s -o /dev/null -w "%{http_code}" \
-X POST \
-H "Authorization: Bearer ${{ secrets.DTFB_SYNC_KEY }}" \
-H "Content-Type: application/json" \
"${{ secrets.DTFB_SYNC_TRIGGER_URL }}")
if [ "$response" != "200" ]; then
echo "Sync failed with HTTP $response"
exit 1
fi
echo "Sync triggered successfully"
@@ -22,6 +22,8 @@ COM_SPORTSMANAGER_LAYOUT_GENERAL_CONTENT_OPTION_CLUBS="Vereine"
COM_SPORTSMANAGER_LAYOUT_GENERAL_CONTENT_OPTION_VENUES="Spielorte"
COM_SPORTSMANAGER_LAYOUT_GENERAL_CONTENT_OPTION_APPOINTMENTS="Termine"
COM_SPORTSMANAGER_LAYOUT_GENERAL_CONTENT_OPTION_MATCH_RESCHEDULING="Spielverlegungen"
COM_SPORTSMANAGER_LAYOUT_GENERAL_CONTENT_OPTION_ASSOCIATION_BODIES="Verbandsorgane"
COM_SPORTSMANAGER_LAYOUT_GENERAL_CONTENT_OPTION_HALL_OF_FAME="Hall Of Fame"
COM_SPORTSMANAGER_LAYOUT_GENERAL_CONTENT_OPTION_TITLE="Titel"
COM_SPORTSMANAGER_LAYOUT_GENERAL_CONTENT_OPTION_TITLE_DESC="Titel, der im Fenster oben angezeigt wird"
COM_SPORTSMANAGER_LAYOUT_GENERAL_CONTENT_OPTION_DESCRIPTION="Beschreibung"
@@ -22,6 +22,8 @@ COM_SPORTSMANAGER_LAYOUT_GENERAL_CONTENT_OPTION_CLUBS="Clubs"
COM_SPORTSMANAGER_LAYOUT_GENERAL_CONTENT_OPTION_VENUES="Venues"
COM_SPORTSMANAGER_LAYOUT_GENERAL_CONTENT_OPTION_APPOINTMENTS="Appointments"
COM_SPORTSMANAGER_LAYOUT_GENERAL_CONTENT_OPTION_MATCH_RESCHEDULING="Match reschedulings"
COM_SPORTSMANAGER_LAYOUT_GENERAL_CONTENT_OPTION_ASSOCIATION_BODIES="Association bodies"
COM_SPORTSMANAGER_LAYOUT_GENERAL_CONTENT_OPTION_HALL_OF_FAME="Hall of fame"
COM_SPORTSMANAGER_LAYOUT_GENERAL_CONTENT_OPTION_TITLE="Title"
COM_SPORTSMANAGER_LAYOUT_GENERAL_CONTENT_OPTION_TITLE_DESC="Title which will be shows on top"
COM_SPORTSMANAGER_LAYOUT_GENERAL_CONTENT_OPTION_DESCRIPTION="Description"
File diff suppressed because it is too large Load Diff
@@ -5503,10 +5503,12 @@ function updateDatabase(): void
$db->setQuery($query);
if (!$db->execute()) { die($db->stderr(true)); }
$query = "ALTER TABLE `#__sportsmanager_veranstaltung` ADD COLUMN IF NOT EXISTS `regelwerke_id` INT(11) NOT NULL DEFAULT '0' AFTER `bezeichnung`;";
$db->setQuery($query);
if (!$db->execute()) { die($db->stderr(true)); }
$columns = $db->getTableColumns('#__sportsmanager_veranstaltung');
if (!array_key_exists('regelwerke_id', $columns)) {
$query = "ALTER TABLE `#__sportsmanager_veranstaltung` ADD COLUMN `regelwerke_id` INT(11) NOT NULL DEFAULT '0' AFTER `bezeichnung`;";
$db->setQuery($query);
if (!$db->execute()) { die($db->stderr(true)); }
}
$query = "UPDATE #__sportsmanager_einstellungen"
. "\n SET wert = '114'"
. "\n WHERE name = 'datenbank_version'";
@@ -5519,19 +5521,24 @@ function updateDatabase(): void
if ($datenbank_version < 115) {
// Erweiterung Tabelle #__sportsmanager_verschieberegel
$query = "ALTER TABLE `#__sportsmanager_verschieberegel`"
."\n ADD COLUMN IF NOT EXISTS `begruendung_erforderlich` INT(1) NOT NULL DEFAULT 0 AFTER `ablehnen`,"
."\n ADD COLUMN IF NOT EXISTS `vereine_berechtigt` INT(1) NOT NULL DEFAULT 1 AFTER `begruendung_erforderlich`,"
."\n ADD COLUMN IF NOT EXISTS `verband_berechtigt` INT(1) NOT NULL DEFAULT 0 AFTER `vereine_berechtigt`;";
$db->setQuery($query);
if (!$db->execute()) {
die($db->stderr(true));
$columns = $db->getTableColumns('#__sportsmanager_verschieberegel');
if (!array_key_exists('begruendung_erforderlich', $columns)
|| !array_key_exists('vereine_berechtigt', $columns)
|| !array_key_exists('verband_berechtigt', $columns)) {
$query = "ALTER TABLE `#__sportsmanager_verschieberegel`"
. "\n ADD COLUMN `begruendung_erforderlich` INT(1) NOT NULL DEFAULT 0 AFTER `ablehnen`,"
. "\n ADD COLUMN `vereine_berechtigt` INT(1) NOT NULL DEFAULT 1 AFTER `begruendung_erforderlich`,"
. "\n ADD COLUMN `verband_berechtigt` INT(1) NOT NULL DEFAULT 0 AFTER `vereine_berechtigt`;";
$db->setQuery($query);
if (!$db->execute()) {
die($db->stderr(true));
}
}
$query = "INSERT IGNORE INTO `#__sportsmanager_email_vorlagen` (`email_vorlagen_id`, `vorlage`) VALUES (NULL, 'Spielverlegung');";
$db->setQuery($query);
if (!$db->execute()) { die($db->stderr(true)); }
$query = "UPDATE #__sportsmanager_einstellungen"
. "\n SET wert = '115'"
. "\n WHERE name = 'datenbank_version'";
@@ -5540,25 +5547,33 @@ function updateDatabase(): void
die($db->stderr(true));
}
}
if ($datenbank_version < 116) {
$query = "ALTER TABLE `#__sportsmanager_spielort`"
. "\n ADD IF NOT EXISTS `telefon` VARCHAR(64) NULL DEFAULT NULL AFTER `url`,"
. "\n ADD IF NOT EXISTS `email` VARCHAR(64) NULL DEFAULT NULL AFTER `telefon`,"
. "\n ADD IF NOT EXISTS `ruhetage` VARCHAR(64) NULL DEFAULT NULL AFTER `email`;";
$db->setQuery($query);
if (!$db->execute()) {
die($db->stderr(true));
$columns = $db->getTableColumns('#__sportsmanager_spielort');
if (!array_key_exists('telefon', $columns)
|| !array_key_exists('email', $columns)
|| !array_key_exists('ruhetage', $columns)) {
$query = "ALTER TABLE `#__sportsmanager_spielort`"
. "\n ADD `telefon` VARCHAR(64) NULL DEFAULT NULL AFTER `url`,"
. "\n ADD `email` VARCHAR(64) NULL DEFAULT NULL AFTER `telefon`,"
. "\n ADD `ruhetage` VARCHAR(64) NULL DEFAULT NULL AFTER `email`;";
$db->setQuery($query);
if (!$db->execute()) {
die($db->stderr(true));
}
}
$query = "ALTER TABLE `#__sportsmanager_team`"
. "\n ADD IF NOT EXISTS `trainingstage` VARCHAR(64) NULL DEFAULT NULL AFTER `heimspielort_id`;";
$db->setQuery($query);
if (!$db->execute()) {
die($db->stderr(true));
$columns = $db->getTableColumns('#__sportsmanager_team');
if (!array_key_exists('trainingstage', $columns)){
$query = "ALTER TABLE `#__sportsmanager_team`"
. "\n ADD `trainingstage` VARCHAR(64) NULL DEFAULT NULL AFTER `heimspielort_id`;";
$db->setQuery($query);
if (!$db->execute()) {
die($db->stderr(true));
}
}
$query = "UPDATE #__sportsmanager_einstellungen"
. "\n SET wert = '116'"
. "\n WHERE name = 'datenbank_version'";
@@ -5567,7 +5582,199 @@ function updateDatabase(): void
die($db->stderr(true));
}
}
if ($datenbank_version < 117) {
$query = "CREATE TABLE IF NOT EXISTS `#__sportsmanager_team_strafen` ("
. "\n `team_strafen_id` int(11) NOT NULL AUTO_INCREMENT,"
. "\n `zeitpunkt` datetime NOT NULL,"
. "\n `moderator_user_id` int(11) DEFAULT NULL,"
. "\n `team_id` int(11) NOT NULL DEFAULT '0',"
. "\n `strafe` float(6,2) NOT NULL DEFAULT '0',"
. "\n `beschreibung` text NOT NULL,"
. "\n PRIMARY KEY (`team_strafen_id`)"
. "\n) ENGINE=MyISAM DEFAULT CHARACTER SET utf8 COLLATE utf8_general_ci;";
$db->setQuery( $query );
if (!$db->execute()) { die($db->stderr(true)); }
$columns = $db->getTableColumns('#__sportsmanager_veranstaltung');
if (!array_key_exists('explizite_strafen', $columns)) {
$query = "ALTER TABLE #__sportsmanager_veranstaltung"
. "\n ADD explizite_strafen tinyint(4) DEFAULT '0' AFTER elo_wertung";
$db->setQuery($query);
if (!$db->execute()) {
die($db->stderr(true));
}
}
$query = "UPDATE #__sportsmanager_einstellungen SET wert = '117' WHERE name = 'datenbank_version'";
$db->setQuery($query);
if (!$db->execute()) { die($db->stderr(true)); }
}
if ($datenbank_version < 118) {
$query = "CREATE TABLE IF NOT EXISTS `#__sportsmanager_verbandsorgane` ("
. "\n `verbandsorgane_id` int(11) NOT NULL AUTO_INCREMENT,"
. "\n `veranstalter_id` int(11) DEFAULT NULL,"
. "\n `verbandsorgan` varchar(32) DEFAULT NULL,"
. "\n `kategorie` int(4) DEFAULT NULL,"
. "\n `reihenfolge` int(4) DEFAULT NULL,"
. "\n `email` varchar(64) DEFAULT NULL,"
. "\n `beschreibung` text NOT NULL,"
. "\n PRIMARY KEY (`verbandsorgane_id`),"
. "\n KEY (`veranstalter_id`)"
. "\n ) ENGINE=MyISAM DEFAULT CHARACTER SET utf8 COLLATE utf8_general_ci;";
$db->setQuery($query);
if (!$db->execute()) {
die($db->stderr(true));
}
$query = "CREATE TABLE IF NOT EXISTS `#__sportsmanager_mitglied_von_verbandsorgan` ("
. "\n `mitglied_von_verbandsorgan_id` int(11) NOT NULL AUTO_INCREMENT,"
. "\n `verbandsorgane_id` int(11) DEFAULT NULL,"
. "\n `funktion` varchar(32) DEFAULT NULL,"
. "\n `zusatzinfo` varchar(64) DEFAULT NULL,"
. "\n `spieler_id` int(11) DEFAULT NULL,"
. "\n `nachname` varchar(32) DEFAULT NULL,"
. "\n `vorname` varchar(32) DEFAULT NULL,"
. "\n `email` varchar(64) DEFAULT NULL,"
. "\n `telefon` varchar(32) DEFAULT NULL,"
. "\n `mobil` varchar(32) DEFAULT NULL,"
. "\n `reihenfolge` int(11) DEFAULT NULL,"
. "\n PRIMARY KEY (`mitglied_von_verbandsorgan_id`),"
. "\n KEY (`verbandsorgane_id`),"
. "\n KEY (`spieler_id`)"
. "\n ) ENGINE=MyISAM DEFAULT CHARACTER SET utf8 COLLATE utf8_general_ci;";
$db->setQuery($query);
if (!$db->execute()) {
die($db->stderr(true));
}
$query = "UPDATE #__sportsmanager_einstellungen"
. "\n SET wert = '118'"
. "\n WHERE name = 'datenbank_version'";
$db->setQuery($query);
if (!$db->execute()) {
die($db->stderr(true));
}
}
if ($datenbank_version < 119) {
$query = "CREATE TABLE IF NOT EXISTS `#__sportsmanager_halloffame` ("
. "\n `halloffame_id` int(11) NOT NULL AUTO_INCREMENT,"
. "\n `veranstalter_id` int(11) DEFAULT NULL,"
. "\n `halloffame` varchar(64) DEFAULT NULL,"
. "\n `kategorie` int(4) DEFAULT NULL,"
. "\n `spielform` int(11) DEFAULT NULL,"
. "\n `reihenfolge` int(4) DEFAULT NULL,"
. "\n PRIMARY KEY (`halloffame_id`),"
. "\n KEY `veranstalter_id` (`veranstalter_id`)"
. "\n ) ENGINE=MyISAM DEFAULT CHARACTER SET utf8 COLLATE utf8_general_ci;";
$db->setQuery($query);
if (!$db->execute()) {
die($db->stderr(true));
}
$query = "CREATE TABLE IF NOT EXISTS `#__sportsmanager_mitglied_von_halloffame` ("
. "\n `mitglied_halloffame_id` int(11) NOT NULL AUTO_INCREMENT,"
. "\n `halloffame_id` int(11) NOT NULL,"
. "\n `jahr` int(4) DEFAULT NULL,"
. "\n `platz` int(11) DEFAULT NULL,"
. "\n `verein_id` int(11) DEFAULT NULL,"
. "\n `teamname` varchar(64) DEFAULT NULL,"
. "\n `spieler1_id` int(11) DEFAULT NULL,"
. "\n `spieler1` varchar(64) DEFAULT NULL,"
. "\n `spieler2_id` int(11) DEFAULT NULL,"
. "\n `spieler2` varchar(64) DEFAULT NULL,"
. "\n PRIMARY KEY (`mitglied_halloffame_id`),"
. "\n UNIQUE KEY `halloffame_jahr_platz` (`halloffame_id`,`jahr`,`platz`),"
. "\n KEY `halloffame_id` (`halloffame_id`),"
. "\n KEY `verein_id` (`verein_id`),"
. "\n KEY `spieler1_id` (`spieler1_id`),"
. "\n KEY `spieler2_id` (`spieler2_id`)"
. "\n ) ENGINE=MyISAM DEFAULT CHARACTER SET utf8 COLLATE utf8_general_ci;";
$db->setQuery($query);
if (!$db->execute()) {
die($db->stderr(true));
}
$query = "UPDATE #__sportsmanager_einstellungen"
. "\n SET wert = '119'"
. "\n WHERE name = 'datenbank_version'";
$db->setQuery($query);
if (!$db->execute()) {
die($db->stderr(true));
}
}
if ($datenbank_version < 120) {
$columns = $db->getTableColumns('#__sportsmanager_teamspiel_modus');
if (!array_key_exists('spiele_in_spielerstatistik', $columns)){
$query = "ALTER TABLE `#__sportsmanager_teamspiel_modus`"
. "\n ADD `spiele_in_spielerstatistik` INT(4) NOT NULL DEFAULT '0' AFTER `heimtausch`;";
$db->setQuery($query);
if (!$db->execute()) {
die($db->stderr(true));
}
}
$columns = $db->getTableColumns('#__sportsmanager_bestenliste_punkte');
if (!array_key_exists('team_id', $columns)){
$query = "ALTER TABLE `#__sportsmanager_bestenliste_punkte`"
. "\n ADD `team_id` INT(11) NULL DEFAULT NULL AFTER `spieler_2_id`;";
$db->setQuery($query);
if (!$db->execute()) {
die($db->stderr(true));
}
}
$spielerstatistik_aktualisieren = true;
$query = "UPDATE #__sportsmanager_einstellungen"
. "\n SET wert = '120'"
. "\n WHERE name = 'datenbank_version'";
$db->setQuery($query);
if (!$db->execute()) {
die($db->stderr(true));
}
}
if ($datenbank_version < 121) {
$query = "CREATE TABLE IF NOT EXISTS `#__sportsmanager_sync_log` ("
. "\n `sync_id` INT(11) NOT NULL AUTO_INCREMENT,"
. "\n `sync_timestamp` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,"
. "\n `sync_direction` ENUM('push', 'receive') NOT NULL,"
. "\n `sync_trigger` ENUM('manual', 'cron', 'api') NOT NULL,"
. "\n `sync_status` ENUM('success', 'error') NOT NULL,"
. "\n `spieler_count` INT(11) DEFAULT 0,"
. "\n `spieler_updated` INT(11) DEFAULT 0,"
. "\n `spieler_added` INT(11) DEFAULT 0,"
. "\n `message` TEXT,"
. "\n `details` TEXT,"
. "\n PRIMARY KEY (`sync_id`),"
. "\n INDEX `idx_timestamp` (`sync_timestamp`)"
. "\n) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;";
$db->setQuery($query);
if (!$db->execute()) {
die($db->stderr(true));
}
$query = "INSERT IGNORE #__sportsmanager_einstellungen SET name = 'dtfb_sync_url', wert = '';";
$db->setQuery($query);
if (!$db->execute()) {
die($db->stderr(true));
}
$query = "UPDATE #__sportsmanager_einstellungen"
. "\n SET wert = '121'"
. "\n WHERE name = 'datenbank_version'";
$db->setQuery($query);
if (!$db->execute()) {
die($db->stderr(true));
}
}
if ($termin_aktionen_email_setzen) {
$query = "SELECT aktion_user_id, termin_aktion_id"
. "\n FROM #__sportsmanager_termin_aktion";
@@ -44,6 +44,7 @@ require_once JPATH_SITE . '/components/com_sportsmanager/views/sportsmanager/vie
require_once JPATH_SITE . '/components/com_sportsmanager/util/image.php';
require_once JPATH_SITE . '/components/com_sportsmanager/util/email.php';
require_once JPATH_SITE . '/components/com_sportsmanager/database/update.php'; // will also include init.php and util.php
require_once JPATH_SITE . '/components/com_sportsmanager/sync.php';
initDatabase();
updateDatabase();
@@ -74,6 +75,10 @@ if ($task == "spielerbild") {
terminDokument();
} else if ($task == "spieler_details") {
spielerDetails();
} else if ($task === 'api_sync_spieler_receive') {
apiSyncSpielerReceive();
} else if ($task === 'api_sync_spieler_trigger') {
apiSyncSpielerTrigger();
} else if ($task !== null && str_starts_with($task, "admin_")) {
// in some cases there are no breaks needed due to no return from method
switch ($task) {
@@ -134,6 +139,9 @@ if ($task == "spielerbild") {
case 'admin_spieler_export_sport':
adminExportSpielerSport();
break;
case 'admin_spieler_sync_dtfb':
adminSyncSpielerToDtfb();
break;
case 'admin_spieler_remove_inaktive_form':
adminRemoveInaktiveSpielerForm();
break;
@@ -371,6 +379,57 @@ if ($task == "spielerbild") {
case 'admin_joomla_mail_senden':
sendJoomlaMail();
break;
case 'admin_verbandsorgane':
adminVerbandsorgane();
break;
case 'admin_verbandsorgan_edit':
adminEditVerbandsorgan();
break;
case 'admin_verbandsorgan_save':
adminSaveVerbandsorgan();
break;
case 'admin_verbandsorgan_remove':
adminRemoveVerbandsorgan();
break;
case 'admin_verbandsorgane_export':
adminExportVerbandsorgane();
break;
case 'admin_verbandsorgan_mitglieder':
adminVerbandsorganMitglieder();
break;
case 'admin_verbandsorgan_mitglied_edit':
adminEditVerbandsorganMitglied();
break;
case 'admin_verbandsorgan_mitglied_save':
adminSaveVerbandsorganMitglied();
break;
case 'admin_verbandsorgan_mitglied_remove':
adminRemoveVerbandsorganMitglied();
break;
case 'admin_halloffame':
adminHalloffame();
break;
case 'admin_halloffame_edit':
adminEditHalloffame();
break;
case 'admin_halloffame_save':
adminSaveHalloffame();
break;
case 'admin_halloffame_remove':
adminRemoveHalloffame();
break;
case 'admin_halloffame_mitglieder':
adminHalloffameMitglieder();
break;
case 'admin_halloffame_mitglied_edit':
adminEditHalloffameMitglied();
break;
case 'admin_halloffame_mitglied_save':
adminSaveHalloffameMitglied();
break;
case 'admin_halloffame_mitglied_remove':
adminRemoveHalloffameMitglied();
break;
case 'admin_regelwerke':
adminRegelwerke();
break;
@@ -461,6 +520,12 @@ if ($task == "spielerbild") {
case 'admin_team_edit':
adminEditMannschaft();
break;
case 'admin_team_strafen':
adminMannschaftStrafen();
break;
case 'admin_team_add_strafe':
adminMannschaftAddStrafe();
break;
case 'admin_team_save':
adminSaveMannschaft();
break;
@@ -1061,6 +1126,24 @@ if ($task == "spielerbild") {
}
} else if ($content == 'spielverlegungen') {
spielverlegungen();
} else if ($content == 'verbandsorgane') {
switch ($task) {
case 'verbandsorgane_details':
verbandsorganeDetails();
break;
default:
verbandsorgane();
break;
}
} else if ($content == 'hall_of_fame') {
switch ($task) {
case 'hall_of_fame_details':
halloffameDetails();
break;
default:
halloffame();
break;
}
} else if ($content == 'login') {
userAuth();
} else if ($content == 'login_refresh') {
@@ -1730,7 +1813,7 @@ function tabelle($veranstaltung, $spieltag, $alleine_angezeigt, $praesentation =
$teams = getTabelleAktuellerSpieltag($veranstaltung);
} else {
$teams = getTabelleSpieltag($veranstaltung,$spieltag);
if ($veranstaltung->direktervergleich && $spieltag >= $anzahl_spieltage_komplett/2 && $anzahl_spieltage_komplett >= 3){
if ($veranstaltung->direktervergleich > 0 && $spieltag >= $anzahl_spieltage_komplett/2 && $anzahl_spieltage_komplett >= 3){
$teams = getTabelleDirekterVergleich($teams,$veranstaltung,$spieltag);
}
}
@@ -1757,17 +1840,41 @@ function tabelle($veranstaltung, $spieltag, $alleine_angezeigt, $praesentation =
function getTabelleDirekterVergleich($teams,$veranstaltung,$spieltag)
{
$gesamtpunkte = 1000;
$anzahlSpiele = 1000;
$anzahl_spiele = 1000;
$punkte_differenz = 1000;
$punkte_quotient = 1000.0000;
$spielpunkte_differenz = 1000;
$spielpunkte_quotient = 1000.0000;
$platz = 0;
$punktgleicheMannschaften = [];
$korrekturen = [];
$bufferplatz = [];
foreach($teams AS $team){
if (($gesamtpunkte != $team->gesamtpunkte) OR ($anzahlSpiele != $team->anzahl_spiele)){
$bufferplatz[$team->team_id]['platz'] = $team->platz;
$unterschied = false;
if (($gesamtpunkte != $team->gesamtpunkte) || ($anzahl_spiele != $team->anzahl_spiele))
$unterschied = true;
if ($veranstaltung->direktervergleich >= 2 && in_array($veranstaltung->tabellenwertung, [1,4,7,10]) && $spielpunkte_differenz != $team->spielpunkte_differenz)
$unterschied = true;
if ($veranstaltung->direktervergleich >= 2 && in_array($veranstaltung->tabellenwertung, [2,5,8,11]) && $spielpunkte_quotient != $team->spielpunkte_quotient)
$unterschied = true;
if ($veranstaltung->direktervergleich == 3 && in_array($veranstaltung->tabellenwertung, [1,4,7,10]) && $punkte_differenz != $team->punkte_differenz)
$unterschied = true;
if ($veranstaltung->direktervergleich == 3 && in_array($veranstaltung->tabellenwertung, [2,5,8,11]) && $punkte_quotient != $team->punkte_quotient)
$unterschied = true;
if ($unterschied)
{
$gesamtpunkte = $team->gesamtpunkte;
$anzahlSpiele = $team->anzahl_spiele;
$anzahl_spiele = $team->anzahl_spiele;
$punkte_differenz = $team->punkte_differenz;
$punkte_quotient = $team->punkte_quotient;
$spielpunkte_differenz = $team->spielpunkte_differenz;
$spielpunkte_quotient = $team->spielpunkte_quotient;
$platz = $team->platz;
$punktgleicheMannschaften[$platz] = $team->team_id;
}
@@ -1776,10 +1883,54 @@ function getTabelleDirekterVergleich($teams,$veranstaltung,$spieltag)
$korrekturen[$platz] = $punktgleicheMannschaften[$platz];
}
}
if (!empty($korrekturen)){
foreach($korrekturen AS $key => $korrektur){
$teamsKorrektur = getTabelleSpieltag($veranstaltung,$spieltag,$korrektur,$key-1);
foreach($teamsKorrektur AS $teamKorrektur){
$teamKorrektur->platz_alt = $bufferplatz[$teamKorrektur->team_id]['platz'];
}
if ($veranstaltung->tabellenwertung == 1 || $veranstaltung->tabellenwertung == 4 || $veranstaltung->tabellenwertung == 7){
usort($teamsKorrektur, fn($a, $b) =>
[(float)$b->begegnungspunkte, (int)$a->anzahl_spiele, (float)$b->spielpunkte_differenz, (float)$b->punkte_differenz, (int)$a->platz_alt]
<=>
[(float)$a->begegnungspunkte, (int)$b->anzahl_spiele, (float)$a->spielpunkte_differenz, (float)$a->punkte_differenz, (int)$b->platz_alt]
);
}
else if ($veranstaltung->tabellenwertung == 2 || $veranstaltung->tabellenwertung == 5 || $veranstaltung->tabellenwertung == 8){
usort($teamsKorrektur, fn($a, $b) =>
[(float)$b->begegnungspunkte, (int)$a->anzahl_spiele, (float)$b->spielpunkte_quotient, (float)$b->punkte_quotient, (int)$a->platz_alt]
<=>
[(float)$a->begegnungspunkte, (int)$b->anzahl_spiele, (float)$a->spielpunkte_quotient, (float)$a->punkte_quotient, (int)$b->platz_alt]
);
}
else if ($veranstaltung->tabellenwertung == 10){
usort($teamsKorrektur, fn($a, $b) =>
[(float)$b->spielpunkte_differenz, (float)$b->punkte_differenz, (int)$a->platz_alt]
<=>
[(float)$a->spielpunkte_differenz, (float)$a->punkte_differenz, (int)$b->platz_alt]
);
}
else if ($veranstaltung->tabellenwertung == 11){
usort($teamsKorrektur, fn($a, $b) =>
[(float)$b->spielpunkte_quotient, (float)$b->punkte_quotient, (int)$a->platz_alt]
<=>
[(float)$a->spielpunkte_quotient, (float)$a->punkte_quotient, (int)$b->platz_alt]
);
}
else {
usort($teamsKorrektur, fn($a, $b) =>
[(float)$b->begegnungspunkte, (int)$a->anzahl_spiele, (int)$a->platz_alt]
<=>
[(float)$a->begegnungspunkte, (int)$b->anzahl_spiele, (int)$b->platz_alt]
);
}
$neuerPlatz = $key;
foreach ($teamsKorrektur as $teamKorrektur) {
$teamKorrektur->platz = $neuerPlatz++;
}
foreach($teamsKorrektur AS $teamKorrektur){
foreach($teams AS $team){
if ($team->team_id == $teamKorrektur->team_id){
@@ -2453,7 +2604,7 @@ function turnierbaumAnzeigen($veranstaltung): void
else
$begegnungen[$begegnung['Spieltag_Nr'] + 1][round($begegnung['Spiel_Nr'] / 2)]['gast'] = htmlentities($begegnung['Heim']);
}
} else
} else
if ($begegnung['Heim_Satzpunkte'] < $begegnung['Gast_Satzpunkte']) {
$begegnungen[$begegnung['Spieltag_Nr']][$begegnung['Spiel_Nr']]['sieger'] = 2;
if ($begegnung['Spieltag_Nr'] < $anzahlRunden){
@@ -2462,7 +2613,7 @@ function turnierbaumAnzeigen($veranstaltung): void
else
$begegnungen[$begegnung['Spieltag_Nr'] + 1][round($begegnung['Spiel_Nr'] / 2)]['gast'] = htmlentities($begegnung['Gast']);
}
} else
} else
if ($begegnung['Heim_Satzpunkte'] == $begegnung['Gast_Satzpunkte'] && $begegnung['Heim_Tore'] > $begegnung['Gast_Tore']) {
$begegnungen[$begegnung['Spieltag_Nr']][$begegnung['Spiel_Nr']]['sieger'] = 1;
if ($begegnung['Spieltag_Nr'] < $anzahlRunden){
@@ -2471,7 +2622,7 @@ function turnierbaumAnzeigen($veranstaltung): void
else
$begegnungen[$begegnung['Spieltag_Nr'] + 1][round($begegnung['Spiel_Nr'] / 2)]['gast'] = htmlentities($begegnung['Heim']);
}
} else
} else
if ($begegnung['Heim_Satzpunkte'] == $begegnung['Gast_Satzpunkte'] && $begegnung['Heim_Tore'] < $begegnung['Gast_Tore']) {
$begegnungen[$begegnung['Spieltag_Nr']][$begegnung['Spiel_Nr']]['sieger'] = 2;
if ($begegnung['Spieltag_Nr'] < $anzahlRunden){
@@ -4125,16 +4276,16 @@ function spielverlegungen(): void
global $params;
$jInput = Factory::getContainer()->get(SiteApplication::class)->input;
$filter_status = $jInput->get('filter_status', '', 'RAW');
if (empty($filter_status))
$filter_status = "offen,genehmigt";
$query = "SELECT saison_id, saisonbezeichnung"
. "\n FROM #__sportsmanager_saison"
. "\n ORDER BY saisonbezeichnung DESC LIMIT 1";
$saisons = loadObjectList($db, $query);
$filter_saison_id = $saisons[0]->saison_id;
$query = "SELECT t1.begegnung_historie_id AS beantragt_id, t1.begegnung_id AS begegnung_id,"
. "\n t1.eingetragen AS beantragt, t1.kommentar AS Begruendung_beantragt, t1.zeitpunkt AS Termin_alt,"
. "\n t1.team_id as beantragt_verein, t5.bezeichnung AS Liga,"
@@ -4150,23 +4301,23 @@ function spielverlegungen(): void
. kategorieFilter("AND t5.kategorie IN")
. "\n ORDER BY t1.begegnung_id, t1.zeitpunkt ASC;";
$spielverlegungen = loadObjectList($db, $query);
$last_id = "";
foreach ($spielverlegungen as $key => $spielverlegung) {
if ($spielverlegung->begegnung_id == $last_id) {
unset($spielverlegungen[$key]);
} else {
$last_id = $spielverlegung->begegnung_id;
$query = "SELECT t1.begegnung_historie_id AS genehmigt_id, t1.eingetragen AS genehmigt,"
. "\n t1.user_id AS genehmigt_von, t1.team_id as genehmigt_verein, t1.zeitpunkt AS Termin_neu"
. "\n FROM #__sportsmanager_begegnung_historie AS t1"
. "\n LEFT JOIN #__sportsmanager_begegnung AS t2 ON t2.begegnung_id = t1.begegnung_id"
. "\n WHERE t1.aktion = 1 AND t1.begegnung_id = $last_id"
. "\n ORDER BY t1.zeitpunkt DESC LIMIT 1;";
$genehmigt = loadObjectList($db, $query);
if (!empty($genehmigt)) {
$spielverlegung->genehmigt_id = $genehmigt[0]->genehmigt_id;
$spielverlegung->genehmigt = $genehmigt[0]->genehmigt;
@@ -4189,9 +4340,9 @@ function spielverlegungen(): void
. "\n LEFT JOIN #__sportsmanager_begegnung AS t2 ON t2.begegnung_id = t1.begegnung_id"
. "\n WHERE t1.aktion = 10 AND t1.begegnung_id = $last_id"
. "\n ORDER BY t1.zeitpunkt DESC LIMIT 1;";
$abgelehnt = loadObjectList($db, $query);
if (!empty($abgelehnt)) {
$spielverlegung->abgelehnt_id = $abgelehnt[0]->abgelehnt_id;
$spielverlegung->abgelehnt = $abgelehnt[0]->abgelehnt;
@@ -4218,6 +4369,204 @@ function spielverlegungen(): void
}
}
function verbandsorgane(): void
{
$db = getDatabase();
global $params;
$jInput = Factory::getContainer()->get(SiteApplication::class)->input;
$query = "SELECT t1.*, COUNT(t2.verbandsorgane_id) AS anzahl, t3.veranstalterbezeichnung AS organisation"
. "\n FROM #__sportsmanager_verbandsorgane t1"
. "\n LEFT JOIN #__sportsmanager_mitglied_von_verbandsorgan t2 ON t2.verbandsorgane_id = t1.verbandsorgane_id"
. "\n LEFT JOIN #__sportsmanager_veranstalter t3 ON t3.veranstalter_id = t1.veranstalter_id"
. "\n GROUP BY t1.verbandsorgane_id"
. kategorieFilter("HAVING t1.kategorie IN")
. "\n ORDER BY t1.reihenfolge;";
$verbandsorgane = loadObjectList($db, $query);
$query = "SELECT COUNT(veranstalter_id) AS anzahl"
. "\n FROM #__sportsmanager_verbandsorgane"
. "\n GROUP BY veranstalter_id"
. kategorieFilter("WHERE kategorie IN");
$organisationen = loadObjectList($db, $query);
if (count($organisationen) > 1)
$organisation_zeigen = true;
else
$organisation_zeigen = false;
if (count($verbandsorgane) == 1){
verbandsorganeDetails($verbandsorgane[0]->verbandsorgane_id);
} else {
if (isJson()) {
echo json_encode($verbandsorgane);
} else {
HTML_sportsmanager::verbandsorgane($params->get('titel'), $params->get('beschreibung'), $verbandsorgane, $organisation_zeigen);
}
}
}
function verbandsorganeDetails($uebergabe_id = 0): void
{
$db = getDatabase();
$app = Factory::getContainer()->get(SiteApplication::class);
$jInput = $app->input;
global $params;
$user_id = isExternalDatabase() ? 0 : (isJson() ? getUserID() : $app->getIdentity()->id);
if ($user_id > 0)
$erweiterte_anzeige = true;
else
$erweiterte_anzeige = false;
if ($uebergabe_id == 0)
$id = $jInput->get('id', 0, 'INT');
else
$id = $uebergabe_id;
$query = "SELECT * FROM #__sportsmanager_verbandsorgane WHERE verbandsorgane_id = $id";
$rows = loadObjectList($db, $query);
if (count($rows) < 1) {
abortWithError("Wrong id!");
}
$verbandsorgan = $rows[0];
$query = "SELECT * FROM #__sportsmanager_mitglied_von_verbandsorgan"
. "\n WHERE verbandsorgane_id = $id"
. "\n ORDER BY reihenfolge, nachname, vorname";
$mitglieder = loadObjectList($db, $query);
$query = "SELECT zusatzinfo FROM #__sportsmanager_mitglied_von_verbandsorgan"
. "\n WHERE verbandsorgane_id = $id"
. "\n AND zusatzinfo IS NOT NULL AND TRIM(zusatzinfo) <> ''"
. "\n ORDER BY reihenfolge, nachname, vorname";
$zusatzinfos = loadObjectList($db, $query);
if (count($zusatzinfos) > 0)
$zusatzinfos_vorhanden = true;
else
$zusatzinfos_vorhanden = false;
if (isJson()) {
echo json_encode($mitglieder);
} else {
HTML_sportsmanager::verbandsorganeDetails($params->get('titel'), $params->get('beschreibung'), $verbandsorgan, $mitglieder, $erweiterte_anzeige, $zusatzinfos_vorhanden);
}
}
function halloffame(): void
{
$db = getDatabase();
global $params;
$query = "SELECT t1.*, COUNT(DISTINCT t2.jahr) AS anzahl,"
. "\n IF (COUNT(DISTINCT t2.jahr) > 0, CONCAT(min(t2.jahr), ' - ', max(t2.jahr)), '" . Text::_('COM_SPORTSMANAGER_NO_ENTRY') . "') AS zeitspanne"
. "\n FROM #__sportsmanager_halloffame t1"
. "\n LEFT JOIN #__sportsmanager_mitglied_von_halloffame t2 ON t2.halloffame_id = t1.halloffame_id"
. "\n GROUP BY t1.halloffame_id"
. kategorieFilter("HAVING t1.kategorie IN")
. "\n ORDER BY t1.reihenfolge;";
$halloffame = loadObjectList($db, $query);
if (count($halloffame) == 1){
halloffameDetails($halloffame[0]->halloffame_id);
} else {
if (isJson()) {
echo json_encode($halloffame);
} else {
HTML_sportsmanager::halloffame($params->get('titel'), $params->get('beschreibung'), $halloffame);
}
}
}
function halloffameDetails($uebergabe_id = 0): void
{
$db = getDatabase();
$app = Factory::getContainer()->get(SiteApplication::class);
$jInput = $app->input;
global $params;
if ($uebergabe_id == 0)
$id = $jInput->get('id', 0, 'INT');
else
$id = $uebergabe_id;
$mitglieder = null;
$query = "SELECT * FROM #__sportsmanager_halloffame WHERE halloffame_id = $id";
$rows = loadObjectList($db, $query);
if (count($rows) < 1) {
abortWithError("Wrong id!");
}
$halloffame = $rows[0];
$halloffame->platz2_zeigen = 0;
$halloffame->platz3_zeigen = 0;
$query = "SELECT t2.*, t1.halloffame"
. "\n FROM #__sportsmanager_halloffame t1"
. "\n LEFT JOIN #__sportsmanager_mitglied_von_halloffame t2 ON t2.halloffame_id = t1.halloffame_id"
. "\n WHERE t2.halloffame_id = $id"
. "\n ORDER BY t2.jahr DESC, platz ASC;";
$rows = loadObjectList($db, $query);
if (count($rows) > 0){
$mitglieder = [];
if ($halloffame->spielform == 1){
foreach ($rows as $row) {
$index_vereinid = "verein_id_" . $row->platz;
$index_teamid = "team_id_" . $row->platz;
$index_team = "teamname_" . $row->platz;
if (!isset($mitglieder[$row->jahr])) {
$mitglieder[$row->jahr] = new stdClass();
}
$mitglieder[$row->jahr]->jahr = $row->jahr;
$mitglieder[$row->jahr]->$index_vereinid = $row->verein_id;
$mitglieder[$row->jahr]->$index_team = $row->teamname;
if ($row->platz == 2 && !empty($row->teamname))
$halloffame->platz2_zeigen = 1;
if ($row->platz == 3 && !empty($row->teamname))
$halloffame->platz3_zeigen = 1;
//Suche team_id wenn keine verein_id vorhanden
if (empty($row->verein_id)){
$query = "SELECT team_id FROM #__sportsmanager_team WHERE teamname LIKE '$row->teamname%' ORDER BY team_id DESC LIMIT 1;";
$mitglieder[$row->jahr]->$index_teamid = loadResult($db, $query);
} else {
$mitglieder[$row->jahr]->$index_teamid = "";
}
}
}
if ($halloffame->spielform == 2 || $halloffame->spielform == 3){
foreach ($rows as $row) {
$index_spieler1id = "spieler1_id_" . $row->platz;
$index_spieler1 = "spieler1_" . $row->platz;
$index_spieler2id = "spieler2_id_" . $row->platz;
$index_spieler2 = "spieler2_" . $row->platz;
if (!isset($mitglieder[$row->jahr])) {
$mitglieder[$row->jahr] = new stdClass();
}
$mitglieder[$row->jahr]->jahr = $row->jahr;
$mitglieder[$row->jahr]->$index_spieler1id = $row->spieler1_id;
$mitglieder[$row->jahr]->$index_spieler1 = $row->spieler1;
$mitglieder[$row->jahr]->$index_spieler2id = $row->spieler2_id;
$mitglieder[$row->jahr]->$index_spieler2 = $row->spieler2;
if ($row->platz == 2 && (!empty($row->spieler1) || !empty($row->spieler2)))
$halloffame->platz2_zeigen = 1;
if ($row->platz == 3 && (!empty($row->spieler1) || !empty($row->spieler2)))
$halloffame->platz3_zeigen = 1;
}
}
}
if (isJson()) {
echo json_encode($mitglieder);
} else {
HTML_sportsmanager::halloffameDetails($params->get('titel'), $params->get('beschreibung'), $mitglieder, $halloffame);
}
}
function begegnungSpielplan($teamansicht = false): void
{
$db = getDatabase();
@@ -4405,10 +4754,10 @@ function begegnungVerlegenForm(): void
$aus_uebersicht = $jInput->get('aus_uebersicht', 0, 'INT');
$query = "SELECT #__sportsmanager_veranstaltung.*, initial_ohne_termin, keine_gegenvorschlaege, vorlaufzeit_tage,"
. "\n termine_minimal, termine_maximal, ablehnen, begruendung_erforderlich"
. "\n termine_minimal, termine_maximal, ablehnen, begruendung_erforderlich"
. "\n FROM #__sportsmanager_veranstaltung LEFT JOIN #__sportsmanager_verschieberegel USING (verschieberegel_id)"
. "\n WHERE veranstaltung_id = $veranstaltungid";
$rows = loadObjectList($db, $query);
if (count($rows) < 1)
die("Wrong id!");
@@ -4776,6 +5125,54 @@ function begegnungVerlegen(): void
}
}
function neueTeamStrafe($teamid, $veranstaltungid, $strafe, $moderator_user_id, $beschreibung) {
$app = Factory::getContainer()->get(SiteApplication::class);
$db = getDatabase();
$zeitpunkt = date('Y-m-d H:i:s');
$query = "INSERT INTO #__sportsmanager_team_strafen"
. "\n (team_id, zeitpunkt, moderator_user_id, strafe, beschreibung)"
. "\n VALUES ($teamid, '$zeitpunkt', $moderator_user_id, $strafe, '$beschreibung')";
$db->setQuery($query);
if (!$result = $db->execute()) { die($db->stderr(true)); }
$query = "UPDATE #__sportsmanager_team"
. "\n SET zusatzpunkte = ("
. "\n SELECT SUM(#__sportsmanager_team_strafen.strafe)"
. "\n FROM #__sportsmanager_team_strafen"
. "\n WHERE #__sportsmanager_team_strafen.team_id = $teamid"
. "\n ) WHERE #__sportsmanager_team.team_id = $teamid";
$db->setQuery($query);
if (!$result = $db->execute()) { die($db->stderr(true)); }
teamstatistikAktualisieren($veranstaltungid);
$query = "SELECT teamname FROM #__sportsmanager_team WHERE team_id = $teamid";
$teamname = loadResult($db, $query);
$mailfrom = $app->getCfg('mailfrom');
$fromname = $app->getCfg('fromname');
$penaltycc = $app->getCfg('penaltycc');
$subject = JText::sprintf('COM_SPORTSMANAGER_EXPLICIT_PENALTIES_EMAIL_SUBJECT', $teamname);
$body = JText::sprintf('COM_SPORTSMANAGER_EXPLICIT_PENALTIES_EMAIL_BODY', $teamname, $strafe, $beschreibung);
$query = "SELECT t1.email FROM #__users AS t1, #__sportsmanager_berechtigt_fuer_team AS t2 WHERE t1.id = t2.berechtigt_user_id AND t2.berechtigt_team_id = $teamid";
$rows = loadObjectList($db, $query);
if(!empty($rows)) {
$mailer = Factory::getContainer()->get(MailerFactoryInterface::class)->createMailer();
$mailer->setSender($mailfrom, $fromname);
if (!empty($penaltycc)) {
$mailer->addCc($penaltycc);
}
$mailer->setSubject($subject);
$mailer->setBody($body);
foreach ($rows as $row) {
$mailer->addBcc($row->email);
}
$mailer->send();
}
}
function teamstatistikAktualisieren($geaenderte_veranstaltung_id = 0, $geaenderter_teamspiel_modus_id = 0): void
{
$db = getDatabase();
@@ -4913,7 +5310,7 @@ function teamstatistikAktualisieren($geaenderte_veranstaltung_id = 0, $geaendert
}
// Direkter Vergleich, wenn mindestens die Hälfte der Spieltage gespielt sind.
if ($veranstaltung->direktervergleich && $anzahl_spieltage_gespielt >= $anzahl_spieltage_komplett/2 && $anzahl_spieltage_komplett >= 3){
if ($veranstaltung->direktervergleich > 0 && $anzahl_spieltage_gespielt >= $anzahl_spieltage_komplett/2 && $anzahl_spieltage_komplett >= 3){
$teams = getTabelleDirekterVergleich($teams,$veranstaltung,$aktueller_spieltag);
}
@@ -4927,7 +5324,10 @@ function teamstatistikAktualisieren($geaenderte_veranstaltung_id = 0, $geaendert
$buchholz1_wert = 0;
$buchholz2_wert = 0;
foreach ($teams as $team) {
if ($platz == 1 || (($veranstaltung->tabellenwertung <= 9 || $veranstaltung->tabellenwertung >= 21) && $gesamtpunkte != $team->gesamtpunkte) || (($veranstaltung->tabellenwertung == 1 || $veranstaltung->tabellenwertung == 4 || $veranstaltung->tabellenwertung == 7 || $veranstaltung->tabellenwertung == 10 || $veranstaltung->tabellenwertung == 21 || $veranstaltung->tabellenwertung == 24 || $veranstaltung->tabellenwertung == 27) && ($spielpunkte_differenz != $team->spielpunkte_differenz || $punkte_differenz != $team->punkte_differenz)) || (($veranstaltung->tabellenwertung == 2 || $veranstaltung->tabellenwertung == 5 || $veranstaltung->tabellenwertung == 8 || $veranstaltung->tabellenwertung == 11 || $veranstaltung->tabellenwertung == 22 || $veranstaltung->tabellenwertung == 25 || $veranstaltung->tabellenwertung == 28) && ($spielpunkte_quotient != $team->spielpunkte_quotient || $punkte_quotient != $team->punkte_quotient)) || (($veranstaltung->tabellenwertung >= 21) && ($team->buchholz1 != $buchholz1_wert || $team->buchholz2 != $buchholz2_wert)))
if ($platz == 1 || (($veranstaltung->tabellenwertung <= 9 || $veranstaltung->tabellenwertung >= 21) && $gesamtpunkte != $team->gesamtpunkte)
|| (($veranstaltung->tabellenwertung == 1 || $veranstaltung->tabellenwertung == 4 || $veranstaltung->tabellenwertung == 7 || $veranstaltung->tabellenwertung == 10 || $veranstaltung->tabellenwertung == 21 || $veranstaltung->tabellenwertung == 24 || $veranstaltung->tabellenwertung == 27) && ($spielpunkte_differenz != $team->spielpunkte_differenz || $punkte_differenz != $team->punkte_differenz))
|| (($veranstaltung->tabellenwertung == 2 || $veranstaltung->tabellenwertung == 5 || $veranstaltung->tabellenwertung == 8 || $veranstaltung->tabellenwertung == 11 || $veranstaltung->tabellenwertung == 22 || $veranstaltung->tabellenwertung == 25 || $veranstaltung->tabellenwertung == 28) && ($spielpunkte_quotient != $team->spielpunkte_quotient || $punkte_quotient != $team->punkte_quotient))
|| (($veranstaltung->tabellenwertung >= 21) && ($team->buchholz1 != $buchholz1_wert || $team->buchholz2 != $buchholz2_wert)))
$tatsaechlicher_platz = $platz;
$query = "UPDATE #__sportsmanager_team"
. "\n SET platz = $tatsaechlicher_platz";
@@ -5468,17 +5868,23 @@ function spielerstatistikAktualisieren($geaenderte_spielerstatistik_id = 0, $gea
$punkte = array();
foreach ($veranstaltungen as $veranstaltung) {
$query = "SELECT heim_spieler_1_id, heim_spieler_2_id, gast_spieler_1_id, gast_spieler_2_id, teamspiel_heim_punkte, teamspiel_gast_punkte, teamspiel_heim_spielpunkte, teamspiel_gast_spielpunkte"
$query = "SELECT heim_spieler_1_id, heim_spieler_2_id, gast_spieler_1_id, gast_spieler_2_id, heim_team_id, gast_team_id,"
. "\n teamspiel_heim_punkte, teamspiel_gast_punkte, teamspiel_heim_spielpunkte, teamspiel_gast_spielpunkte"
. "\n FROM #__sportsmanager_begegnung"
. "\n LEFT JOIN #__sportsmanager_team ON heim_team_id = #__sportsmanager_team.team_id"
. "\n LEFT JOIN #__sportsmanager_teamspiel ON #__sportsmanager_begegnung.begegnung_id = #__sportsmanager_teamspiel.begegnung_id"
. "\n LEFT JOIN #__sportsmanager_unbestaetigtes_ergebnis ON #__sportsmanager_begegnung.begegnung_id = #__sportsmanager_unbestaetigtes_ergebnis.begegnung_id"
. "\n WHERE ISNULL(unbestaetigtes_ergebnis_id) AND veranstaltung_id = $veranstaltung->veranstaltung_id";
if ($spielerstatistik->typ == 1)
. "\n LEFT JOIN #__sportsmanager_veranstaltung ON #__sportsmanager_team.veranstaltung_id = #__sportsmanager_veranstaltung.veranstaltung_id"
. "\n LEFT JOIN #__sportsmanager_teamspiel_modus ON #__sportsmanager_veranstaltung.modus_id = #__sportsmanager_teamspiel_modus.teamspiel_modus_id"
. "\n WHERE ISNULL(unbestaetigtes_ergebnis_id) AND #__sportsmanager_veranstaltung.veranstaltung_id = $veranstaltung->veranstaltung_id"
. "\n AND (#__sportsmanager_teamspiel_modus.spiele_in_spielerstatistik <= 0"
. "\n OR teamspiel_nummer <= #__sportsmanager_teamspiel_modus.spiele_in_spielerstatistik)";
if ($spielerstatistik->typ == 1)
$query .= "\n AND ISNULL(heim_spieler_2_id) AND ISNULL(gast_spieler_2_id)";
else if ($spielerstatistik->typ == 2 || $spielerstatistik->typ == 3)
$query .= "\n AND NOT (ISNULL(heim_spieler_2_id) AND ISNULL(gast_spieler_2_id))";
$begegnungen = loadObjectList($db, $query);
$query .= "\n ORDER BY #__sportsmanager_begegnung.zeitpunkt ASC;";
$begegnungen = loadObjectList($db, $query);
foreach ($begegnungen as $begegnung) {
$beteiligte = array();
@@ -5511,6 +5917,8 @@ function spielerstatistikAktualisieren($geaenderte_spielerstatistik_id = 0, $gea
$gast_punkte = $begegnung->teamspiel_gast_punkte;
$heim_spielpunkte = $begegnung->teamspiel_heim_spielpunkte;
$gast_spielpunkte = $begegnung->teamspiel_gast_spielpunkte;
$heim_team_id = $begegnung->heim_team_id;
$gast_team_id = $begegnung->gast_team_id;
$ergebnis = $heim_punkte > $gast_punkte ? 1 : ($heim_punkte < $gast_punkte ? 2 : 0);
$heim_saetze = $ergebnis == 1 ? 1 : 0;
$unentschieden_saetze = $ergebnis == 0 ? 1 : 0;
@@ -5531,6 +5939,10 @@ function spielerstatistikAktualisieren($geaenderte_spielerstatistik_id = 0, $gea
$punkte[$spieler_id]["sv"] = 0;
$punkte[$spieler_id]["pg"] = 0;
$punkte[$spieler_id]["pv"] = 0;
if ($typ == "H")
$punkte[$spieler_id]["team_id"] = $heim_team_id;
else
$punkte[$spieler_id]["team_id"] = $gast_team_id;
}
if (($ergebnis == 1 && $typ == "H") || ($ergebnis == 2 && $typ != "H"))
$punkte[$spieler_id]["s"] += 1;
@@ -5657,6 +6069,11 @@ function spielerstatistikAktualisieren($geaenderte_spielerstatistik_id = 0, $gea
$spieler_punkte_saetze_verloren = $spieler_punkte["sv"];
$spieler_punkte_punkte_gewonnen = $spieler_punkte["pg"];
$spieler_punkte_punkte_verloren = $spieler_punkte["pv"];
if (!empty($spieler_punkte["team_id"]))
$spieler_team_id = $spieler_punkte["team_id"];
else
$spieler_team_id = "NULL";
$query = "UPDATE #__sportsmanager_bestenliste_punkte"
. "\n SET siege = $spieler_punkte_siege,"
. "\n unentschieden = $spieler_punkte_unentschieden,"
@@ -5667,7 +6084,8 @@ function spielerstatistikAktualisieren($geaenderte_spielerstatistik_id = 0, $gea
. "\n saetze_unentschieden = $spieler_punkte_saetze_unentschieden,"
. "\n saetze_verloren = $spieler_punkte_saetze_verloren,"
. "\n punkte_gewonnen = $spieler_punkte_punkte_gewonnen,"
. "\n punkte_verloren = $spieler_punkte_punkte_verloren"
. "\n punkte_verloren = $spieler_punkte_punkte_verloren,"
. "\n team_id = $spieler_team_id"
. "\n WHERE bestenliste_punkte_id = $row->bestenliste_punkte_id;";
$db->setQuery($query);
if (!$db->execute()) {
@@ -5694,6 +6112,10 @@ function spielerstatistikAktualisieren($geaenderte_spielerstatistik_id = 0, $gea
$spieler_punkte_saetze_verloren = $spieler_punkte["sv"];
$spieler_punkte_punkte_gewonnen = $spieler_punkte["pg"];
$spieler_punkte_punkte_verloren = $spieler_punkte["pv"];
if (!empty($spieler_punkte["team_id"]))
$spieler_team_id = $spieler_punkte["team_id"];
else
$spieler_team_id = "NULL";
$query = "INSERT #__sportsmanager_bestenliste_punkte"
. "\n SET siege = $spieler_punkte_siege,"
. "\n unentschieden = $spieler_punkte_unentschieden,"
@@ -5707,6 +6129,7 @@ function spielerstatistikAktualisieren($geaenderte_spielerstatistik_id = 0, $gea
. "\n punkte_verloren = $spieler_punkte_punkte_verloren,"
. "\n spieler_id = $spieler_1_id,"
. "\n spieler_2_id = " . ($spieler_2_id != NULL ? "$spieler_2_id" : "NULL") . ","
. "\n team_id = $spieler_team_id,"
. "\n bestenliste_id = $spielerstatistik->bestenliste_id;";
$db->setQuery($query);
if (!$db->execute()) {
@@ -5716,6 +6139,56 @@ function spielerstatistikAktualisieren($geaenderte_spielerstatistik_id = 0, $gea
}
}
function halloffameAktualisieren(): void
{
//Beim Löschen von Spieler bzw. Vereine werden die entsprechenden ids in Hall of Fame und Verbandsorgane gelöscht
$db = getDatabase();
// spieler1_id prüfen
$query = "UPDATE #__sportsmanager_mitglied_von_halloffame h"
. "\n LEFT JOIN #__sportsmanager_spieler s ON s.spieler_id = h.spieler1_id"
. "\n SET h.spieler1_id = NULL"
. "\n WHERE h.spieler1_id IS NOT NULL"
. "\n AND s.spieler_id IS NULL;";
$db->setQuery($query);
if (!$db->execute()) {
die($db->stderr(true));
}
// spieler2_id prüfen
$query = "UPDATE #__sportsmanager_mitglied_von_halloffame h"
. "\n LEFT JOIN #__sportsmanager_spieler s ON s.spieler_id = h.spieler2_id"
. "\n SET h.spieler2_id = NULL"
. "\n WHERE h.spieler2_id IS NOT NULL"
. "\n AND s.spieler_id IS NULL;";
$db->setQuery($query);
if (!$db->execute()) {
die($db->stderr(true));
}
// verein_id prüfen
$query = "UPDATE #__sportsmanager_mitglied_von_halloffame h"
. "\n LEFT JOIN #__sportsmanager_verein v ON v.verein_id = h.verein_id"
. "\n SET h.verein_id = NULL"
. "\n WHERE h.verein_id IS NOT NULL"
. "\n AND v.verein_id IS NULL;";
$db->setQuery($query);
if (!$db->execute()) {
die($db->stderr(true));
}
// Verbandsorgan-Spieler prüfen
$query = "UPDATE #__sportsmanager_mitglied_von_verbandsorgan h"
. "\n LEFT JOIN #__sportsmanager_spieler s ON s.spieler_id = h.spieler_id"
. "\n SET h.spieler_id = NULL"
. "\n WHERE h.spieler_id IS NOT NULL"
. "\n AND s.spieler_id IS NULL;";
$db->setQuery($query);
if (!$db->execute()) {
die($db->stderr(true));
}
}
function wannGespieltAktualisieren(): void
{
$db = getDatabase();
@@ -6905,6 +7378,7 @@ function spielerstatistiken(): void
$details_anzeigen = currentUserHasAccessToDetails();
$filter_saison_id = $jInput->get('filter_saison_id', 0, 'INT');
if ($filter_saison_id == 0)
$filter_saison_id = $jInput->cookie->get('sportsmanager_filter_saison_id', 0, 'INT');
@@ -6934,25 +7408,53 @@ function spielerstatistiken(): void
. "\n ORDER BY reihenfolge, bezeichnung, kategorie";
$spielerstatistiken = loadObjectList($db, $query);
HTML_sportsmanager::spielerstatistikenHeader($params->get('titel'), $params->get('beschreibung'), $saisons, $filter_saison_id);
HTML_sportsmanager::spielerstatistikenHeader($params->get('titel'), $params->get('beschreibung'), $saisons, $filter_saison_id, "", null, null, null, null);
foreach ($spielerstatistiken as $spielerstatistik) {
$query = "SELECT #__sportsmanager_bestenliste_punkte.*, #__sportsmanager_spieler.nachname, #__sportsmanager_spieler.vorname, #__sportsmanager_spieler.geschlecht, #__sportsmanager_spieler.aktueller_verein_id, #__sportsmanager_spieler.bild_ausblenden, spieler_2.nachname AS nachname_2, spieler_2.vorname AS vorname_2, spieler_2.geschlecht AS geschlecht_2, spieler_2.aktueller_verein_id AS aktueller_verein_id_2, spieler_2.bild_ausblenden AS bild_ausblenden_2";
if ($spielerstatistik->tabellenwertung == 1)
//Mindestzahl an Spielen
$spielerstatistik->min_spiele = 0;
if (in_array($spielerstatistik->tabellenwertung, [4, 5])) {
$query = "SELECT siege + unentschieden + niederlagen AS spiele"
. "\n FROM #__sportsmanager_bestenliste_punkte"
. "\n WHERE bestenliste_id = $spielerstatistik->bestenliste_id"
. "\n ORDER BY spiele DESC;";
$max_spiele = loadResult($db, $query);
$spielerstatistik->min_spiele = round($max_spiele/2);
}
$query = "SELECT #__sportsmanager_bestenliste_punkte.*, #__sportsmanager_spieler.nachname, team.teamname,"
. "\n siege + unentschieden + niederlagen AS spiele,"
. "\n #__sportsmanager_spieler.vorname, #__sportsmanager_spieler.geschlecht,"
. "\n #__sportsmanager_spieler.aktueller_verein_id, #__sportsmanager_spieler.bild_ausblenden,"
. "\n spieler_2.nachname AS nachname_2, spieler_2.vorname AS vorname_2,"
. "\n spieler_2.geschlecht AS geschlecht_2, spieler_2.aktueller_verein_id AS aktueller_verein_id_2,"
. "\n spieler_2.bild_ausblenden AS bild_ausblenden_2,"
. "\n IF(siege > 0 OR unentschieden > 0, ROUND(((siege * 2 + unentschieden)*100) / ((siege + unentschieden + niederlagen)*2)), 0) AS quotient";
if ($spielerstatistik->tabellenwertung == 0){
$query .= "\n , 0 AS leistungsindex";
} else if ($spielerstatistik->tabellenwertung == 1){
$query .= ", IF(spielpunkte_gewonnen > 0 OR spielpunkte_verloren > 0, spielpunkte_gewonnen * spielpunkte_gewonnen * 100 / (spielpunkte_gewonnen + spielpunkte_verloren), 0) AS leistungsindex";
else if ($spielerstatistik->tabellenwertung == 2)
$query .= ", IF(punkte_gewonnen > 0 OR punkte_verloren > 0, siege * punkte_gewonnen * 10 / (punkte_gewonnen + punkte_verloren), 0) AS leistungsindex";
$query .= "\n FROM #__sportsmanager_bestenliste_punkte"
} else if ($spielerstatistik->tabellenwertung == 2){
$query .= ", IF(punkte_gewonnen > 0 OR punkte_verloren > 0, ROUND(siege * punkte_gewonnen * 10 / (punkte_gewonnen + punkte_verloren)), 0) AS leistungsindex";
} else if ($spielerstatistik->tabellenwertung == 3){
$query .= "\n , IF(punkte_gewonnen > 0 OR punkte_verloren > 0, siege * 2 + unentschieden + punkte_gewonnen - punkte_verloren, 0) AS leistungsindex";
} else if ($spielerstatistik->tabellenwertung == 4){
$query .= "\n , IF(punkte_gewonnen > 0 OR punkte_verloren > 0, ROUND(punkte_gewonnen*100 / (punkte_gewonnen + punkte_verloren),2), 0) AS leistungsindex";
} else if ($spielerstatistik->tabellenwertung == 5){
$query .= "\n , IF(punkte_gewonnen > 0 OR punkte_verloren > 0, ROUND(spielpunkte_gewonnen / (siege + unentschieden + niederlagen),2), 0) AS leistungsindex";
}
$query .= "\n FROM #__sportsmanager_bestenliste_punkte"
. "\n LEFT JOIN #__sportsmanager_spieler USING (spieler_id)"
. "\n LEFT JOIN (SELECT team_id, teamname FROM #__sportsmanager_team) AS team ON #__sportsmanager_bestenliste_punkte.team_id = team.team_id"
. "\n LEFT JOIN #__sportsmanager_spieler AS spieler_2 ON spieler_2.spieler_id = spieler_2_id"
. "\n WHERE bestenliste_id = $spielerstatistik->bestenliste_id";
. "\n WHERE bestenliste_id = $spielerstatistik->bestenliste_id"
. "\n AND siege + unentschieden + niederlagen >= $spielerstatistik->min_spiele";
if ($spielerstatistik->tabellenwertung == 0)
$query .= "\n ORDER BY spielpunkte_gewonnen DESC, spielpunkte_verloren, punkte_gewonnen - punkte_verloren DESC, nachname, vorname";
else
$query .= "\n ORDER BY leistungsindex DESC, spielpunkte_gewonnen DESC, spielpunkte_verloren, punkte_gewonnen - punkte_verloren DESC, nachname, vorname";
$spielerstatistik_punkte = loadObjectList($db, $query);
$query .= "\n ORDER BY leistungsindex DESC, quotient DESC, punkte_gewonnen - punkte_verloren DESC, nachname, vorname";
$spielerstatistik_punkte = loadObjectList($db, $query);
HTML_sportsmanager::spielerstatistik($spielerstatistik, $spielerstatistik_punkte, FALSE, 0, 0, $details_anzeigen);
HTML_sportsmanager::spielerstatistik($spielerstatistik, $spielerstatistik_punkte, FALSE, 0, 0, 0, $details_anzeigen);
}
administrationFooter();
@@ -6961,6 +7463,7 @@ function spielerstatistiken(): void
function spielerstatistik(): void
{
$db = getDatabase();
global $params;
$jInput = Factory::getContainer()->get(SiteApplication::class)->input;
$id = $jInput->get('id', 0, 'INT');
@@ -6976,7 +7479,10 @@ function spielerstatistik(): void
}
}
$details_anzeigen = currentUserHasAccessToDetails();
$filter_min_spiele = $jInput->get('filter_min_spiele', 999, 'INT');
$filter_saison_id = $jInput->get('filter_saison_id', 0, 'INT');
$filter_teams = $jInput->get('filter_teams', 0, 'INT');
if ($filter_saison_id == 0) {
$filter_saison_id = $jInput->cookie->get('sportsmanager_filter_saison_id', 0, 'INT');
}
@@ -7012,29 +7518,80 @@ function spielerstatistik(): void
}
$spielerstatistik = $rows[0];
//Mindestzahl an Spielen
$spiele = null;
$spielerstatistik->min_spiele = 0;
if (in_array($spielerstatistik->tabellenwertung, [4, 5])) {
$query = "SELECT siege + unentschieden + niederlagen AS spiele"
. "\n FROM #__sportsmanager_bestenliste_punkte"
. "\n WHERE bestenliste_id = $id"
. "\n ORDER BY spiele DESC;";
$max_spiele = loadResult($db, $query);
if ($max_spiele > 0){
$offset = $max_spiele/10;
$spiele[0] = "Alle zeigen";
for($i = 1; $i <= 10; $i++){
$spiele[round($offset*$i)] = $i*10 . "%";
}
}
$spielerstatistik->max_spiele = $max_spiele;
if ($filter_min_spiele == 999)
$spielerstatistik->min_spiele = round($max_spiele/2);
else
$spielerstatistik->min_spiele = $filter_min_spiele;
}
// Spielerstatistikpunkte ermitteln
$query = "SELECT #__sportsmanager_bestenliste_punkte.*, #__sportsmanager_spieler.nachname, #__sportsmanager_spieler.vorname, #__sportsmanager_spieler.geschlecht, #__sportsmanager_spieler.aktueller_verein_id, #__sportsmanager_spieler.bild_ausblenden, spieler_2.nachname AS nachname_2, spieler_2.vorname AS vorname_2, spieler_2.geschlecht AS geschlecht_2, spieler_2.aktueller_verein_id AS aktueller_verein_id_2, spieler_2.bild_ausblenden AS bild_ausblenden_2";
if ($spielerstatistik->tabellenwertung == 1) {
$query .= ", IF(spielpunkte_gewonnen > 0 OR spielpunkte_verloren > 0, spielpunkte_gewonnen * spielpunkte_gewonnen * 100 / (spielpunkte_gewonnen + spielpunkte_verloren), 0) AS leistungsindex";
} else if ($spielerstatistik->tabellenwertung == 2) {
$query .= ", IF(punkte_gewonnen > 0 OR punkte_verloren > 0, siege * punkte_gewonnen * 10 / (punkte_gewonnen + punkte_verloren), 0) AS leistungsindex";
}
$query = "SELECT #__sportsmanager_bestenliste_punkte.*, #__sportsmanager_spieler.nachname, #__sportsmanager_spieler.vorname, team.teamname,"
. "\n siege + unentschieden + niederlagen AS spiele,"
. "\n #__sportsmanager_spieler.geschlecht, #__sportsmanager_spieler.aktueller_verein_id, #__sportsmanager_spieler.bild_ausblenden,"
. "\n spieler_2.nachname AS nachname_2, spieler_2.vorname AS vorname_2, spieler_2.geschlecht AS geschlecht_2,"
. "\n spieler_2.aktueller_verein_id AS aktueller_verein_id_2, spieler_2.bild_ausblenden AS bild_ausblenden_2,"
. "\n IF(siege > 0 OR unentschieden > 0, ROUND(((siege * 2 + unentschieden)*100) / ((siege + unentschieden + niederlagen)*2)), 0) AS quotient";
if ($spielerstatistik->tabellenwertung == 0){
$query .= "\n , spielpunkte_gewonnen AS leistungsindex";
} else if ($spielerstatistik->tabellenwertung == 1){
$query .= "\n , IF(spielpunkte_gewonnen > 0 OR spielpunkte_verloren > 0, spielpunkte_gewonnen * spielpunkte_gewonnen * 100 / (spielpunkte_gewonnen + spielpunkte_verloren), 0) AS leistungsindex";
} else if ($spielerstatistik->tabellenwertung == 2){
$query .= "\n , IF(punkte_gewonnen > 0 OR punkte_verloren > 0, ROUND(siege * punkte_gewonnen * 10 / (punkte_gewonnen + punkte_verloren)), 0) AS leistungsindex";
} else if ($spielerstatistik->tabellenwertung == 3){
$query .= "\n , IF(punkte_gewonnen > 0 OR punkte_verloren > 0, siege * 2 + unentschieden + punkte_gewonnen - punkte_verloren, 0) AS leistungsindex";
} else if ($spielerstatistik->tabellenwertung == 4){
$query .= "\n , IF(punkte_gewonnen > 0 OR punkte_verloren > 0, ROUND(punkte_gewonnen*100 / (punkte_gewonnen + punkte_verloren),2), 0) AS leistungsindex";
} else if ($spielerstatistik->tabellenwertung == 5){
$query .= "\n , IF(punkte_gewonnen > 0 OR punkte_verloren > 0, ROUND(spielpunkte_gewonnen / (siege + unentschieden + niederlagen),2), 0) AS leistungsindex";
}
$query .= "\n FROM #__sportsmanager_bestenliste_punkte"
. "\n LEFT JOIN #__sportsmanager_spieler USING (spieler_id)"
. "\n LEFT JOIN (SELECT team_id, teamname FROM #__sportsmanager_team) AS team ON #__sportsmanager_bestenliste_punkte.team_id = team.team_id"
. "\n LEFT JOIN #__sportsmanager_spieler AS spieler_2 ON spieler_2.spieler_id = spieler_2_id"
. "\n WHERE bestenliste_id = $spielerstatistik->bestenliste_id";
. "\n WHERE bestenliste_id = $spielerstatistik->bestenliste_id"
. "\n AND siege + unentschieden + niederlagen >= $spielerstatistik->min_spiele";
if ($spielerstatistik->tabellenwertung == 0) {
$query .= "\n ORDER BY spielpunkte_gewonnen DESC, spielpunkte_verloren, punkte_gewonnen - punkte_verloren DESC, nachname, vorname";
} else {
$query .= "\n ORDER BY leistungsindex DESC, spielpunkte_gewonnen DESC, spielpunkte_verloren, punkte_gewonnen - punkte_verloren DESC, nachname, vorname";
$query .= "\n ORDER BY leistungsindex DESC, quotient DESC, spielpunkte_gewonnen - spielpunkte_verloren DESC, punkte_gewonnen - punkte_verloren DESC, nachname, vorname";
}
$spielerstatistik_punkte = loadObjectList($db, $query);
//Dropdown teams
$query = "SELECT 0 AS team_id, '" . Text::_('COM_SPORTSMANAGER_SELECT_ALL') . "' AS teamname, 0 AS sort_order"
. "\n UNION"
. "\n SELECT team.team_id, team.teamname, 1 AS sort_order"
. "\n FROM #__sportsmanager_bestenliste_punkte"
. "\n LEFT JOIN (SELECT team_id, teamname FROM #__sportsmanager_team) AS team"
. "\n ON #__sportsmanager_bestenliste_punkte.team_id = team.team_id"
. "\n WHERE bestenliste_id = $spielerstatistik->bestenliste_id"
. "\n GROUP BY team.team_id"
. "\n ORDER BY sort_order, teamname;";
$db->setQuery($query);
$teams = $db->loadObjectList('team_id');
// Vorherige und nächste Spielerstatistik ermitteln
$query = "SELECT *"
. "\n FROM #__sportsmanager_bestenliste"
. "\n WHERE status > 0" . kategorieFilter("AND kategorie IN") . ($filter_saison_id != 0 ? " AND saison_id = $filter_saison_id" : "")
. "\n ORDER BY reihenfolge, bezeichnung, kategorie";
. "\n ORDER BY reihenfolge, bezeichnung, kategorie";
$rows = loadObjectList($db, $query);
$n = count($rows);
@@ -7056,7 +7613,8 @@ function spielerstatistik(): void
if (isJson()) {
JSON_sportsmanager::spielerstatistik($spielerstatistik_punkte);
} else {
HTML_sportsmanager::spielerstatistik($spielerstatistik, $spielerstatistik_punkte, TRUE, $vorherige_spielerstatistik_id, $naechste_spielerstatistik_id, $details_anzeigen);
HTML_sportsmanager::spielerstatistikenHeader($params->get('titel'), $params->get('beschreibung'), $saisons, $filter_saison_id, $id, $spiele, $spielerstatistik->min_spiele, $teams, $filter_teams);
HTML_sportsmanager::spielerstatistik($spielerstatistik, $spielerstatistik_punkte, TRUE, $filter_teams, $vorherige_spielerstatistik_id, $naechste_spielerstatistik_id, $details_anzeigen);
}
}
@@ -0,0 +1,899 @@
<?php
/**
* Sports Manager Sync Extension
*/
use Joomla\CMS\Application\SiteApplication;
use Joomla\CMS\Factory;
use Joomla\CMS\Language\Text;
defined("_JEXEC") or die();
/**
* Gets the Bearer token from the Authorization header.
*
* @return string|null
*/
function syncGetBearerToken(): ?string
{
$headers = null;
if (isset($_SERVER['Authorization'])) {
$headers = trim($_SERVER["Authorization"]);
} elseif (isset($_SERVER['HTTP_AUTHORIZATION'])) {
$headers = trim($_SERVER["HTTP_AUTHORIZATION"]);
} elseif (isset($_SERVER['REDIRECT_HTTP_AUTHORIZATION'])) {
$headers = trim($_SERVER["REDIRECT_HTTP_AUTHORIZATION"]);
} elseif (function_exists('apache_request_headers')) {
$requestHeaders = apache_request_headers();
$requestHeaders = array_combine(array_map('ucwords', array_keys($requestHeaders)), array_values($requestHeaders));
if (isset($requestHeaders['Authorization'])) {
$headers = trim($requestHeaders['Authorization']);
}
}
if (!empty($headers)) {
if (preg_match('/Bearer\s(\S+)/i', $headers, $matches)) {
return $matches[1];
}
}
return null;
}
/**
* Logs a sync event in the database.
*
* @param string $direction ('push' or 'receive')
* @param string $trigger ('manual', 'cron', or 'api')
* @param string $status ('success' or 'error')
* @param int $spieler_count
* @param int $spieler_updated
* @param int $spieler_added
* @param string $message
* @param string $details
*/
function syncLogEntry(string $direction, string $trigger, string $status, int $spieler_count, int $spieler_updated, int $spieler_added, string $message, string $details = ''): void
{
try {
$db = getDatabase();
$query = "INSERT INTO #__sportsmanager_sync_log"
. "\n SET sync_timestamp = NOW(),"
. "\n sync_direction = '" . $db->escape($direction) . "',"
. "\n sync_trigger = '" . $db->escape($trigger) . "',"
. "\n sync_status = '" . $db->escape($status) . "',"
. "\n spieler_count = " . intval($spieler_count) . ","
. "\n spieler_updated = " . intval($spieler_updated) . ","
. "\n spieler_added = " . intval($spieler_added) . ","
. "\n message = '" . $db->escape($message) . "',"
. "\n details = '" . $db->escape($details) . "'";
$db->setQuery($query);
$db->execute();
} catch (Exception $e) {
error_log("Failed to write sync log: " . $e->getMessage());
}
}
/**
* Returns HTML displaying the status of the last sync operation.
*
* @return string
*/
function syncGetLastStatus(): string
{
try {
$db = getDatabase();
$query = "SELECT * FROM #__sportsmanager_sync_log ORDER BY sync_id DESC LIMIT 1";
$rows = loadObjectList($db, $query);
if (count($rows) === 0) {
return "Noch nie synchronisiert";
}
$row = $rows[0];
$statusClass = $row->sync_status === 'success' ? 'uk-text-success' : 'uk-text-danger';
$statusText = $row->sync_status === 'success' ? 'Erfolgreich' : 'Fehlgeschlagen';
$directionText = $row->sync_direction === 'push' ? 'Export (Push)' : 'Import (Receive)';
$triggerText = $row->sync_trigger === 'manual' ? 'Manuell' : ($row->sync_trigger === 'cron' ? 'Cron' : 'API');
$stats = "";
if ($row->sync_status === 'success') {
$stats = sprintf(
" (Spieler gesamt: %d, Aktualisiert: %d, Hinzugefügt: %d)",
$row->spieler_count,
$row->spieler_updated,
$row->spieler_added
);
} else {
$stats = " (Fehler: " . htmlspecialchars($row->message) . ")";
}
return sprintf(
"<span class='%s'><strong>%s</strong></span> am %s via %s / %s%s",
$statusClass,
$statusText,
date('d.m.Y H:i:s', strtotime($row->sync_timestamp)),
$directionText,
$triggerText,
$stats
);
} catch (Exception $e) {
return "Noch nie synchronisiert";
}
}
/**
* Exports player data to a tab-separated CSV string.
* Excludes personal contact details and images.
*
* @return string
*/
function syncExportSpielerCSV(): string
{
$db = getDatabase();
$jahr = date("Y");
$query = "SELECT nachname, vorname, spielernr, lizenznr, lizenz, geschlecht";
$query .= ",\n IF(ISNULL(geburtsjahr), IF(geschlecht = 'M', 'H', 'D'), IF(" . ($jahr - 18) . " <= geburtsjahr, 'J', IF(" . ($jahr - 50) . " > geburtsjahr, 'S', IF(geschlecht = 'M', 'H', 'D')))) AS kategorie";
$query .= ",\n vereinsname as verein, vereinssitz, veranstalterbezeichnung as organisation, IF(mitgliedsstatus = 1, 'Aktiv', IF(mitgliedsstatus = 0, 'Ausgetreten', IF(mitgliedsstatus = 2, 'Eingeschränkt', 'Passiv'))) AS mitgliedsstatus";
$query .= ",\n geburtsjahr";
$query .= "\n FROM #__sportsmanager_spieler";
$query .= "\n LEFT JOIN #__sportsmanager_mitglied_von_verein ON #__sportsmanager_spieler.spieler_id = #__sportsmanager_mitglied_von_verein.spieler_id AND #__sportsmanager_mitglied_von_verein.verein_id = #__sportsmanager_spieler.aktueller_verein_id"
. "\n LEFT JOIN #__sportsmanager_verein ON #__sportsmanager_verein.verein_id = #__sportsmanager_spieler.aktueller_verein_id"
. "\n LEFT JOIN #__sportsmanager_veranstalter ON #__sportsmanager_veranstalter.veranstalter_id = #__sportsmanager_verein.veranstalter_id";
$query .= "\n WHERE NOT ISNULL(aktueller_verein_id) AND NOT ISNULL(spielernr) AND spielernr != ''";
$query .= "\n ORDER BY nachname, vorname";
$rows = loadObjectList($db, $query);
if (count($rows) === 0) {
return "";
}
$trennzeichen = "\t";
$header = "";
foreach ($rows[0] as $field => $value) {
$header .= $field . $trennzeichen;
}
$header = rtrim($header, $trennzeichen);
$data = "";
foreach ($rows as $row) {
$line = '';
foreach ($row as $value) {
if ((!isset($value)) or ($value === "")) {
$value = $trennzeichen;
} else {
$value = str_replace('"', '""', $value);
$value = str_replace("\t", ' ', $value);
$value = str_replace("\r", '', $value);
$value = str_replace("\n", ' ', $value);
$value = '="' . $value . '"' . $trennzeichen;
}
$line .= $value;
}
$data .= rtrim($line, $trennzeichen) . "\n";
}
$data = str_replace("\r", "", $data);
return "sep=" . $trennzeichen . "\n" . $header . "\n" . $data;
}
/**
* Pushes the exported CSV data to DTFB (dtfb_sync_url) via cURL.
*
* @param string $csvData
* @return array
*/
function syncPushToDtfb(string $csvData): array
{
$push_key = einstellungswert("api_push_key");
$sync_url = einstellungswert("dtfb_sync_url");
if (empty($sync_url)) {
return [
'success' => false,
'message' => 'Sync-URL nicht konfiguriert.'
];
}
if (empty($push_key)) {
return [
'success' => false,
'message' => 'API Push Key nicht konfiguriert.'
];
}
$ch = curl_init($sync_url);
if (!$ch) {
return [
'success' => false,
'message' => 'Initialisierung von cURL fehlgeschlagen.'
];
}
curl_setopt_array($ch, array(
CURLOPT_POST => TRUE,
CURLOPT_RETURNTRANSFER => TRUE,
CURLOPT_HTTPHEADER => array(
'Authorization: Bearer ' . $push_key,
'Content-Type: text/csv; charset=utf-8',
),
CURLOPT_TIMEOUT => 60,
CURLOPT_POSTFIELDS => $csvData,
));
$resp = curl_exec($ch);
$http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
if (curl_errno($ch)) {
$error_msg = curl_error($ch);
curl_close($ch);
return [
'success' => false,
'message' => 'cURL-Fehler: ' . $error_msg
];
}
curl_close($ch);
if ($http_code !== 200) {
return [
'success' => false,
'message' => 'HTTP-Status ' . $http_code . ': ' . $resp
];
}
$result = json_decode($resp, true);
if (json_last_error() === JSON_ERROR_NONE) {
if (isset($result['success']) && $result['success']) {
return [
'success' => true,
'message' => $result['message'] ?? 'Erfolgreich synchronisiert.',
'spieler_count' => $result['spieler_count'] ?? 0,
'spieler_updated' => $result['spieler_updated'] ?? 0,
'spieler_added' => $result['spieler_added'] ?? 0
];
} else {
return [
'success' => false,
'message' => $result['error'] ?? $result['message'] ?? 'Import auf Empfängerseite fehlgeschlagen.'
];
}
}
if (str_contains(strtolower($resp), 'success')) {
return [
'success' => true,
'message' => 'Erfolgreich synchronisiert (Klartext-Antwort).'
];
}
return [
'success' => false,
'message' => 'Unerwartete Antwort vom Server: ' . substr($resp, 0, 200)
];
}
/**
* Processes incoming CSV data and imports it into the local database.
* Aborts and returns an error if any organization name in the CSV cannot
* be matched with an existing local organization.
*
* @param string $csvData
* @return array
*/
function syncReceiveSpielerImport(string $csvData): array
{
if (!ini_get('safe_mode'))
set_time_limit(300);
$db = getDatabase();
// Normalise the payload to UTF-8. The automatic/semi-automatic sync path is
// UTF-8 end to end, but a legacy/manual export (e.g. from TFVHH) can be
// latin1/Windows-1252 encoded. Without this, non-ASCII bytes (e.g. "ß" in an
// organisation name) get truncated when staged into the utf8mb4 import table,
// causing the organisation match to fail and every row to be skipped silently.
if (!mb_check_encoding($csvData, 'UTF-8')) {
$csvData = mb_convert_encoding($csvData, 'UTF-8', 'Windows-1252');
}
$lines = explode("\n", str_replace("\r", "", $csvData));
if (count($lines) < 2) {
return [
'success' => false,
'message' => 'Keine Daten in der CSV-Datei gefunden.'
];
}
$lineIdx = 0;
$titelzeile = trim($lines[$lineIdx]);
if (str_starts_with($titelzeile, "sep=")) {
$trennzeichen = substr($titelzeile, 4);
if ($trennzeichen === "") {
$trennzeichen = "\t";
}
$lineIdx++;
if (isset($lines[$lineIdx])) {
$titelzeile = trim($lines[$lineIdx]);
} else {
return [
'success' => false,
'message' => 'CSV-Datei enthält nach sep= keine Titelzeile.'
];
}
} else {
$trennzeichen = "\t";
}
$titel = explode($trennzeichen, strtolower($titelzeile));
$spalte = array();
foreach ($titel as $index => $bezeichnung) {
$bezeichnung = trim($bezeichnung);
$len = strlen($bezeichnung);
if ($len >= 2 && $bezeichnung[0] === '"' && $bezeichnung[$len - 1] === '"') {
$bezeichnung = trim(str_replace('""', '"', substr($bezeichnung, 1, $len - 2)));
}
if ($bezeichnung === "name" || $bezeichnung === "nachname") {
$spalte["nachname"] = $index;
} else if ($bezeichnung === "vorname") {
$spalte["vorname"] = $index;
} else if ($bezeichnung === "name, vorname" || $bezeichnung === "name,vorname") {
$spalte["name,vorname"] = $index;
} else if ($bezeichnung === "pseudonym") {
$spalte["pseudonym"] = $index;
} else if ($bezeichnung === "geschlecht" || $bezeichnung === "anrede") {
$spalte["geschlecht"] = $index;
} else if ($bezeichnung === "spielernr" || $bezeichnung === "spielernr." || $bezeichnung === "spielerpass") {
$spalte["spielernr"] = $index;
} else if ($bezeichnung === "spielernr alt" || $bezeichnung === "spielernr. alt" || $bezeichnung === "spielernr_alt") {
$spalte["spielernr_alt"] = $index;
} else if ($bezeichnung === "lizenznr" || $bezeichnung === "lizenznr.") {
$spalte["lizenznr"] = $index;
} else if ($bezeichnung === "organisation") {
$spalte["organisation"] = $index;
} else if ($bezeichnung === "vereinssitz") {
$spalte["vereinssitz"] = $index;
} else if ($bezeichnung === "vereinsname" || $bezeichnung === "verein") {
$spalte["vereinsname"] = $index;
} else if ($bezeichnung === "geburtsdatum") {
$spalte["geburtsdatum"] = $index;
} else if ($bezeichnung === "geburtsjahr") {
$spalte["geburtsjahr"] = $index;
} else if ($bezeichnung === "email" || $bezeichnung === "e-mail") {
$spalte["email"] = $index;
} else if (str_starts_with($bezeichnung, "stra")) {
$spalte["strasse"] = $index;
} else if ($bezeichnung === "plz/ort") {
$spalte["plz/ort"] = $index;
} else if ($bezeichnung === "plz") {
$spalte["plz"] = $index;
} else if ($bezeichnung === "ort") {
$spalte["ort"] = $index;
} else if ($bezeichnung === "landeskennung") {
$spalte["landeskennung"] = $index;
} else if ($bezeichnung === "telefon") {
$spalte["telefon"] = $index;
} else if ($bezeichnung === "mobil") {
$spalte["mobil"] = $index;
} else if ($bezeichnung === "austritt" || $bezeichnung === "ausgetreten") {
$spalte["ausgetreten"] = $index;
} else if ($bezeichnung === "mitgliedsstatus") {
$spalte["mitgliedsstatus"] = $index;
}
}
if (((!isset($spalte["nachname"]) || !isset($spalte["vorname"])) && !isset($spalte["name,vorname"])) || !isset($spalte["spielernr"])) {
return [
'success' => false,
'message' => 'Die übergebene Datei ist keine gültige Spielerdatei (erforderliche Spalten fehlen).'
];
}
$lineIdx++;
// Source the staging session id from the database clock, not PHP's. The
// stale-row cleanup below compares session_id against the database NOW(); if
// PHP and the database run in different timezones, a PHP-generated timestamp
// can fall outside the window and the just-inserted rows get deleted mid-import.
$session_id = loadResult($db, "SELECT DATE_FORMAT(NOW(), '%Y-%m-%d %H:%i:%s')");
if (empty($session_id)) {
$session_id = date('Y-m-d H:i:s');
}
$organisations = [];
$rows_to_insert = [];
for ($i = $lineIdx; $i < count($lines); $i++) {
$buffer = trim($lines[$i]);
if ($buffer === "") {
continue;
}
$daten = explode($trennzeichen, $buffer);
foreach ($daten as $index => $wert) {
$wert = trim($wert);
$len = strlen($wert);
if ($len < 2 || $wert[$len - 1] !== '"' || !($wert[0] === '"' || ($wert[0] === '=' && $wert[1] === '"'))) {
$daten[$index] = $wert;
} else if ($wert[0] === '"') {
$daten[$index] = trim(str_replace('""', '"', substr($wert, 1, $len - 2)));
} else {
$daten[$index] = trim(str_replace('""', '"', substr($wert, 2, $len - 3)));
}
}
if (isset($spalte["vorname"]) && isset($spalte["nachname"]) && isset($daten[$spalte["vorname"]]) && isset($daten[$spalte["nachname"]])) {
$nachname = $daten[$spalte["nachname"]];
$vorname = $daten[$spalte["vorname"]];
} else if (isset($spalte["name,vorname"]) && isset($daten[$spalte["name,vorname"]])) {
$pos = strpos($daten[$spalte["name,vorname"]], ",");
if ($pos === false) {
continue;
}
$nachname = trim(substr($daten[$spalte["name,vorname"]], 0, $pos));
$vorname = trim(substr($daten[$spalte["name,vorname"]], $pos + 1));
} else {
continue;
}
if ($vorname === "" || $nachname === "") {
continue;
}
$mitgliedsstatus = 1;
if (isset($spalte["mitgliedsstatus"]) && !empty($daten[$spalte["mitgliedsstatus"]])) {
$s = strtolower($daten[$spalte["mitgliedsstatus"]]);
if ($s === "ausgetreten") {
$mitgliedsstatus = 0;
} else if ($s === "passiv") {
$mitgliedsstatus = 3;
} else if (str_starts_with($s, "eingeschr")) {
$mitgliedsstatus = 2;
}
} else if (isset($spalte["ausgetreten"]) && !empty($daten[$spalte["ausgetreten"]])) {
if (strtolower($daten[$spalte["ausgetreten"]]) === "ja") {
$mitgliedsstatus = 0;
}
}
if ($mitgliedsstatus == 0) {
continue;
}
$geschlecht = isset($spalte["geschlecht"]) && !empty($daten[$spalte["geschlecht"]]) ? (($daten[$spalte["geschlecht"]][0] === "M" || $daten[$spalte["geschlecht"]][0] === "m" || $daten[$spalte["geschlecht"]][0] === "H" || $daten[$spalte["geschlecht"]][0] === "h") ? "M" : "W") : "M";
$spielernr = isset($daten[$spalte["spielernr"]]) ? trim($daten[$spalte["spielernr"]]) : "";
// Validate the Passnummer with the same format the manual import enforces
// (NN-NNNN[NN]). Invalid values are dropped rather than aborting the whole
// automated feed, keeping the player but treating them as having no pass.
if (!empty($spielernr) && !preg_match('/^[0-9]{2}-[0-9]{4,6}$/', $spielernr)) {
$spielernr = "";
}
$spielernr_alt = isset($spalte["spielernr_alt"]) && isset($daten[$spalte["spielernr_alt"]]) ? trim($daten[$spalte["spielernr_alt"]]) : "";
if (!empty($spielernr_alt) && !preg_match('/^[0-9]{2}-[0-9]{4,6}$/', $spielernr_alt)) {
$spielernr_alt = "";
}
$lizenznr = isset($spalte["lizenznr"]) && isset($daten[$spalte["lizenznr"]]) ? $daten[$spalte["lizenznr"]] : "";
if (!empty($lizenznr) && !ctype_digit(substr($lizenznr, strlen($lizenznr) - 1, 1))) {
$lizenznr = "";
}
$pseudonym = isset($spalte["pseudonym"]) && isset($daten[$spalte["pseudonym"]]) ? $daten[$spalte["pseudonym"]] : "";
$organisation = isset($spalte["organisation"]) && isset($daten[$spalte["organisation"]]) ? $daten[$spalte["organisation"]] : "";
$vereinssitz = isset($spalte["vereinssitz"]) && isset($daten[$spalte["vereinssitz"]]) ? $daten[$spalte["vereinssitz"]] : "";
$vereinsname = isset($spalte["vereinsname"]) && isset($daten[$spalte["vereinsname"]]) ? $daten[$spalte["vereinsname"]] : "";
$geburtsjahr = isset($spalte["geburtsjahr"]) && isset($daten[$spalte["geburtsjahr"]]) ? $daten[$spalte["geburtsjahr"]] : null;
if (empty($geburtsjahr) || !ctype_digit($geburtsjahr) || $geburtsjahr < 1800) {
$geburtsjahr = null;
}
if (!empty($organisation)) {
$organisations[trim($organisation)] = true;
}
$rows_to_insert[] = [
'vorname' => $vorname,
'nachname' => $nachname,
'spielernr' => $spielernr,
'spielernr_alt' => $spielernr_alt,
'lizenznr' => $lizenznr,
'pseudonym' => $pseudonym,
'organisation' => $organisation,
'vereinssitz' => $vereinssitz,
'vereinsname' => $vereinsname,
'geburtsjahr' => $geburtsjahr,
'mitgliedsstatus' => $mitgliedsstatus,
'geschlecht' => $geschlecht
];
}
if (empty($rows_to_insert)) {
return [
'success' => false,
'message' => 'Keine gültigen Spielerzeilen zum Importieren gefunden.'
];
}
// Auto-match Veranstalter by name. If it does not match, abort.
$org_map = [];
foreach (array_keys($organisations) as $orgName) {
$query = "SELECT veranstalter_id FROM #__sportsmanager_veranstalter WHERE veranstalterbezeichnung = '" . $db->escape($orgName) . "'";
$res = loadObjectList($db, $query);
if (count($res) === 0) {
return [
'success' => false,
'message' => 'Veranstalter "' . $orgName . '" existiert nicht auf diesem Empfänger-System. Import abgebrochen.'
];
}
$org_map[$orgName] = intval($res[0]->veranstalter_id);
}
// Insert into staging table
foreach ($rows_to_insert as $row) {
$query = "INSERT INTO #__sportsmanager_spieler_import"
. "\n SET session_id = '" . $db->escape($session_id) . "',"
. "\n vorname = '" . $db->escape($row['vorname']) . "',"
. "\n nachname = '" . $db->escape($row['nachname']) . "',"
. "\n spielernr = '" . $db->escape($row['spielernr']) . "',"
. "\n spielernr_alt = '" . $db->escape($row['spielernr_alt']) . "',"
. "\n lizenznr = '" . $db->escape($row['lizenznr']) . "',"
. "\n pseudonym = '" . $db->escape($row['pseudonym']) . "',"
. "\n geschlecht = '" . $db->escape($row['geschlecht']) . "',"
. "\n geburtsjahr = " . ($row['geburtsjahr'] === null ? "NULL" : "'" . $db->escape($row['geburtsjahr']) . "'") . ","
. "\n vereinsname = '" . $db->escape($row['vereinsname']) . "',"
. "\n vereinssitz = '" . $db->escape($row['vereinssitz']) . "',"
. "\n veranstalterbezeichnung = '" . $db->escape($row['organisation']) . "',"
. "\n mitgliedsstatus = '" . $row['mitgliedsstatus'] . "'";
$db->setQuery($query);
if (!$db->execute()) {
return [
'success' => false,
'message' => 'Fehler beim Schreiben in die Staging-Tabelle: ' . $db->stderr()
];
}
}
// Clean up older staging data (older than 5 minutes)
$query = "SELECT DISTINCT session_id"
. "\n FROM #__sportsmanager_spieler_import"
. "\n WHERE session_id < SUBTIME(NOW(), '00:05:00')";
$old_sessions = loadObjectList($db, $query);
foreach ($old_sessions as $old_session) {
$query = "DELETE FROM #__sportsmanager_spieler_import WHERE session_id = '" . $db->escape($old_session->session_id) . "'";
$db->setQuery($query);
$db->execute();
}
// Fetch staging players with matching ID
$query = "SELECT #__sportsmanager_spieler_import.*, #__sportsmanager_spieler.spieler_id"
. "\n FROM #__sportsmanager_spieler_import"
. "\n LEFT JOIN #__sportsmanager_spieler ON #__sportsmanager_spieler_import.spielernr != '' AND #__sportsmanager_spieler_import.spielernr = #__sportsmanager_spieler.spielernr"
. "\n WHERE session_id = '" . $db->escape($session_id) . "'";
$spieler_import = loadObjectList($db, $query);
// Count how many active players the incoming payload provides per organisation.
// The mass-deactivation below is only safe when the payload is a *full* roster;
// a partial CSV would otherwise silently deactivate every member not listed.
$incoming_per_org = [];
foreach ($rows_to_insert as $row) {
$o = trim($row['organisation']);
if ($o !== "") {
$incoming_per_org[$o] = ($incoming_per_org[$o] ?? 0) + 1;
}
}
// Minimum fraction of the currently-active roster the payload must contain
// before the sweep is allowed to run. Configurable; defaults to 0.5.
$deactivation_min_ratio = (float) (einstellungswert("sync_deactivation_min_ratio") ?? 0.5);
if ($deactivation_min_ratio <= 0 || $deactivation_min_ratio > 1) {
$deactivation_min_ratio = 0.5;
}
$warnings = [];
$deactivated_total = 0;
// Deactivate all memberships for involved organisations temporarily. The
// players present in the payload are reactivated further below; anyone not
// listed stays deactivated (i.e. is treated as having left the organisation).
foreach ($org_map as $orgName => $veranstalterId) {
$aktiv_vorher = (int) loadResult(
$db,
"SELECT COUNT(*) FROM #__sportsmanager_mitglied_von_verein"
. " INNER JOIN #__sportsmanager_verein USING (verein_id)"
. " WHERE veranstalter_id = " . $veranstalterId
. " AND NOT #__sportsmanager_mitglied_von_verein.ausgetreten"
);
$eingehend = $incoming_per_org[$orgName] ?? 0;
// Guard: skip the sweep when the payload looks like a partial roster
// (far fewer players than are currently active). This prevents a partial
// export from wiping an entire organisation's memberships.
if ($aktiv_vorher > 0 && $eingehend < $aktiv_vorher * $deactivation_min_ratio) {
$warnings[] = sprintf(
'Massen-Deaktivierung für "%s" übersprungen: nur %d von %d aktiven Mitgliedern in den Daten (mögliche Teil-Liste).',
$orgName,
$eingehend,
$aktiv_vorher
);
continue;
}
$query = "UPDATE #__sportsmanager_mitglied_von_verein INNER JOIN #__sportsmanager_verein USING (verein_id)"
. "\n SET mitgliedsstatus = 0,"
. "\n #__sportsmanager_mitglied_von_verein.ausgetreten = TRUE"
. "\n WHERE veranstalter_id = " . $veranstalterId;
$db->setQuery($query);
if (!$db->execute()) {
return [
'success' => false,
'message' => 'Fehler beim Deaktivieren der alten Vereinsmitgliedschaften.'
];
}
$deactivated_total += $aktiv_vorher;
}
$spieler_updated = 0;
$spieler_added = 0;
$spielerIdsHinzugefuegt = array();
foreach ($spieler_import as $t) {
$orgName = $t->veranstalterbezeichnung;
$veranstalterId = $org_map[$orgName] ?? -1;
if ($veranstalterId === -1 && !empty($orgName)) {
continue;
}
$spieler_id = $t->spieler_id;
$nachname = $t->nachname;
$vorname = $t->vorname;
$geschlecht = $t->geschlecht;
$lizenznr = $t->lizenznr;
$pseudonym = $t->pseudonym;
$vereinsname = $t->vereinsname;
$vereinssitz = $t->vereinssitz;
$geburtsjahr = $t->geburtsjahr;
$spielernr = $t->spielernr;
$mitgliedsstatus = $t->mitgliedsstatus;
if ($spieler_id === null && !empty($spielernr) && isset($spielerIdsHinzugefuegt[$spielernr])) {
$spieler_id = $spielerIdsHinzugefuegt[$spielernr];
}
if ($spieler_id === null && empty($spielernr)) {
continue;
}
if ($spieler_id === null && empty($vereinsname)) {
continue;
}
if ($spieler_id !== null) {
$query = "UPDATE #__sportsmanager_spieler"
. "\n SET vorname = '" . $db->escape($vorname) . "',"
. "\n nachname = '" . $db->escape($nachname) . "',"
. "\n geschlecht = '" . $db->escape($geschlecht) . "'"
. "\n WHERE spieler_id = " . intval($spieler_id);
$db->setQuery($query);
if (!$db->execute()) {
return [
'success' => false,
'message' => 'Fehler beim Aktualisieren des Spielers ID ' . $spieler_id
];
}
$spieler_updated++;
} else {
$query = "INSERT INTO #__sportsmanager_spieler"
. "\n SET vorname = '" . $db->escape($vorname) . "',"
. "\n nachname = '" . $db->escape($nachname) . "',"
. "\n spielernr = '" . $db->escape($spielernr) . "',"
. "\n lizenznr = '" . $db->escape($lizenznr) . "',"
. "\n geschlecht = '" . $db->escape($geschlecht) . "',"
. "\n geburtsjahr = " . ($geburtsjahr === null ? "NULL" : "'" . $db->escape($geburtsjahr) . "'");
if (!empty($pseudonym)) {
$query .= ",\n pseudonym = '" . $db->escape($pseudonym) . "'";
}
$db->setQuery($query);
if (!$db->execute()) {
return [
'success' => false,
'message' => 'Fehler beim Anlegen des neuen Spielers ' . $vorname . ' ' . $nachname
];
}
$spieler_id = $db->insertid();
$spielerIdsHinzugefuegt[$spielernr] = $spieler_id;
$spieler_added++;
}
if (!empty($vereinsname) && $veranstalterId !== -1) {
$query = "SELECT spieler_id FROM #__sportsmanager_mitglied_von_verein"
. "\n WHERE spieler_id = $spieler_id AND verein_id = "
. " (SELECT verein_id FROM #__sportsmanager_verein WHERE vereinsname = '" . $db->escape($vereinsname) . "' AND veranstalter_id = $veranstalterId LIMIT 1)";
$memb_check = loadObjectList($db, $query);
if (count($memb_check) > 0) {
$query = "UPDATE #__sportsmanager_mitglied_von_verein, #__sportsmanager_verein"
. "\n SET mitgliedsstatus = '$mitgliedsstatus', #__sportsmanager_mitglied_von_verein.ausgetreten = FALSE"
. "\n WHERE spieler_id = $spieler_id AND vereinsname = '" . $db->escape($vereinsname) . "' AND #__sportsmanager_verein.verein_id = #__sportsmanager_mitglied_von_verein.verein_id"
. " AND veranstalter_id = $veranstalterId";
$db->setQuery($query);
$db->execute();
} else {
$query = "SELECT verein_id FROM #__sportsmanager_verein"
. "\n WHERE vereinsname = '" . $db->escape($vereinsname) . "' AND veranstalter_id = $veranstalterId";
$club_rows = loadObjectList($db, $query);
if (count($club_rows) > 0) {
$verein_id = intval($club_rows[0]->verein_id);
} else {
$query = "INSERT INTO #__sportsmanager_verein"
. "\n SET vereinsname = '" . $db->escape($vereinsname) . "',"
. "\n veranstalter_id = $veranstalterId";
if (!empty($vereinssitz)) {
$query .= ",\n vereinssitz = '" . $db->escape($vereinssitz) . "'";
}
$db->setQuery($query);
$db->execute();
$verein_id = $db->insertid();
}
$query = "INSERT INTO #__sportsmanager_mitglied_von_verein"
. "\n SET spieler_id = $spieler_id, verein_id = $verein_id, mitgliedsstatus = '$mitgliedsstatus', ausgetreten = FALSE";
$db->setQuery($query);
$db->execute();
}
}
}
foreach ($org_map as $orgName => $veranstalterId) {
$query = "UPDATE #__sportsmanager_verein"
. "\n SET ausgetreten = TRUE"
. "\n WHERE NOT EXISTS(SELECT * FROM #__sportsmanager_mitglied_von_verein WHERE #__sportsmanager_verein.verein_id = #__sportsmanager_mitglied_von_verein.verein_id AND NOT #__sportsmanager_mitglied_von_verein.ausgetreten) AND NOT ausgetreten AND veranstalter_id = " . $veranstalterId;
$db->setQuery($query);
$db->execute();
$query = "UPDATE #__sportsmanager_verein"
. "\n SET ausgetreten = FALSE"
. "\n WHERE EXISTS(SELECT * FROM #__sportsmanager_mitglied_von_verein WHERE #__sportsmanager_verein.verein_id = #__sportsmanager_mitglied_von_verein.verein_id AND NOT #__sportsmanager_mitglied_von_verein.ausgetreten) AND ausgetreten AND veranstalter_id = " . $veranstalterId;
$db->setQuery($query);
$db->execute();
$query = "SELECT DISTINCT verein_id, #__sportsmanager_spieler_import.vereinsname, #__sportsmanager_spieler_import.vereinssitz"
. "\n FROM #__sportsmanager_spieler_import"
. "\n INNER JOIN #__sportsmanager_verein ON #__sportsmanager_verein.vereinsname = #__sportsmanager_spieler_import.vereinsname"
. "\n WHERE session_id = '" . $db->escape($session_id) . "' AND #__sportsmanager_spieler_import.veranstalterbezeichnung = '" . $db->escape($orgName) . "' AND #__sportsmanager_spieler_import.vereinsname != '' AND #__sportsmanager_spieler_import.vereinssitz != '' AND (ISNULL(#__sportsmanager_verein.vereinssitz) OR #__sportsmanager_verein.vereinssitz != #__sportsmanager_spieler_import.vereinssitz) AND NOT #__sportsmanager_verein.ausgetreten AND veranstalter_id = " . $veranstalterId;
$rows_headquarters = loadObjectList($db, $query);
foreach ($rows_headquarters as $row) {
$query = "UPDATE #__sportsmanager_verein"
. "\n SET vereinssitz = '" . $db->escape($row->vereinssitz) . "'"
. "\n WHERE verein_id = $row->verein_id";
$db->setQuery($query);
$db->execute();
}
}
$query = "DELETE FROM #__sportsmanager_spieler_import WHERE session_id = '" . $db->escape($session_id) . "'";
$db->setQuery($query);
$db->execute();
// Fail loudly on a zero-effect import: if valid rows were parsed but nothing
// was added or updated, the data almost certainly failed to map (e.g. an
// encoding mismatch corrupting organisation names). Reporting success here
// would silently hide data loss.
if (count($rows_to_insert) > 0 && $spieler_added === 0 && $spieler_updated === 0) {
return [
'success' => false,
'message' => 'Import ergab keine Änderungen trotz ' . count($rows_to_insert)
. ' gültiger Zeilen mögliche Encoding- oder Zuordnungsfehler.',
'spieler_count' => count($rows_to_insert),
'spieler_updated' => 0,
'spieler_added' => 0,
'warnings' => $warnings
];
}
aktuellerVereinAktualisieren();
ranglisteAktualisieren();
einstufungAktualisieren();
return [
'success' => true,
'spieler_count' => count($rows_to_insert),
'spieler_updated' => $spieler_updated,
'spieler_added' => $spieler_added,
'deactivated' => $deactivated_total,
'warnings' => $warnings
];
}
/**
* Endpoint task: triggered by GitHub Actions or other schedule.
* Authenticates with local api_push_key, exports player data, pushes to DTFB, and returns JSON.
*/
function apiSyncSpielerTrigger(): void
{
$token = syncGetBearerToken();
$expected_key = einstellungswert("api_push_key");
if (empty($expected_key) || $token !== $expected_key) {
header('HTTP/1.1 401 Unauthorized');
header('Content-Type: application/json; charset=utf-8');
echo json_encode(['success' => false, 'error' => 'Ungültiges Authentifizierungs-Token.']);
exit;
}
$csvData = syncExportSpielerCSV();
if (empty($csvData)) {
header('Content-Type: application/json; charset=utf-8');
echo json_encode(['success' => false, 'error' => 'Keine Spieler zum Synchronisieren gefunden.']);
exit;
}
$res = syncPushToDtfb($csvData);
// Log sync status
syncLogEntry(
'push',
'api',
$res['success'] ? 'success' : 'error',
$res['spieler_count'] ?? 0,
$res['spieler_updated'] ?? 0,
$res['spieler_added'] ?? 0,
$res['message'] ?? '',
''
);
header('Content-Type: application/json; charset=utf-8');
if ($res['success']) {
echo json_encode($res);
} else {
header('HTTP/1.1 500 Internal Server Error');
echo json_encode($res);
}
exit;
}
/**
* Endpoint task: receives CSV data from another Sportsmanager instance, imports it.
* Authenticates with local api_push_key.
*/
function apiSyncSpielerReceive(): void
{
$token = syncGetBearerToken();
$expected_key = einstellungswert("api_push_key");
if (empty($expected_key) || $token !== $expected_key) {
header('HTTP/1.1 401 Unauthorized');
header('Content-Type: application/json; charset=utf-8');
echo json_encode(['success' => false, 'error' => 'Ungültiges Authentifizierungs-Token.']);
exit;
}
$csvData = file_get_contents('php://input');
if (empty($csvData)) {
header('HTTP/1.1 400 Bad Request');
header('Content-Type: application/json; charset=utf-8');
echo json_encode(['success' => false, 'error' => 'Keine Formulardaten empfangen.']);
exit;
}
$res = syncReceiveSpielerImport($csvData);
// Log sync status
syncLogEntry(
'receive',
'api',
$res['success'] ? 'success' : 'error',
$res['spieler_count'] ?? 0,
$res['spieler_updated'] ?? 0,
$res['spieler_added'] ?? 0,
$res['message'] ?? '',
''
);
header('Content-Type: application/json; charset=utf-8');
if ($res['success']) {
echo json_encode($res);
} else {
header('HTTP/1.1 500 Internal Server Error');
echo json_encode($res);
}
exit;
}
@@ -25,6 +25,8 @@
<option value="spielorte"><![CDATA[COM_SPORTSMANAGER_LAYOUT_GENERAL_CONTENT_OPTION_VENUES]]></option>
<option value="termine"><![CDATA[COM_SPORTSMANAGER_LAYOUT_GENERAL_CONTENT_OPTION_APPOINTMENTS]]></option>
<option value="spielverlegungen"><![CDATA[COM_SPORTSMANAGER_LAYOUT_GENERAL_CONTENT_OPTION_MATCH_RESCHEDULING]]></option>
<option value="verbandsorgane"><![CDATA[COM_SPORTSMANAGER_LAYOUT_GENERAL_CONTENT_OPTION_ASSOCIATION_BODIES]]></option>
<option value="hall_of_fame"><![CDATA[COM_SPORTSMANAGER_LAYOUT_GENERAL_CONTENT_OPTION_HALL_OF_FAME]]></option>
</field>
<field name="titel"
type="text"
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
@@ -411,6 +411,51 @@ class HTML_sportsmanager_ticker
moreResults(matches, groups, day, page, 0);
});
</script>
<style>
#theme-toggle {
float: right;
margin-top: 15px;
margin-right: 20px;
}
.theme-btn {
cursor: pointer;
margin-left: 10px;
font-size: 18px;
opacity: 0.5;
transition: opacity 0.2s;
}
.theme-btn:hover, .theme-btn.active {
opacity: 1;
}
</style>
<script>
function applyTheme(theme) {
if (theme === 'dark' || (theme === 'auto' && window.matchMedia('(prefers-color-scheme: dark)').matches)) {
document.body.classList.add('dark-mode');
} else {
document.body.classList.remove('dark-mode');
}
if (document.getElementById('theme-' + theme)) {
document.querySelectorAll('.theme-btn').forEach(function(btn) { btn.classList.remove('active'); });
document.getElementById('theme-' + theme).classList.add('active');
}
}
var savedTheme = localStorage.getItem('livescore-theme') || 'auto';
function setTheme(theme) {
localStorage.setItem('livescore-theme', theme);
applyTheme(theme);
}
window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', function(e) {
if (localStorage.getItem('livescore-theme') === 'auto' || !localStorage.getItem('livescore-theme')) {
applyTheme('auto');
}
});
document.addEventListener('DOMContentLoaded', function() {
applyTheme(localStorage.getItem('livescore-theme') || 'auto');
});
</script>
</head>
<body onResize="resizee()">
@@ -433,6 +478,11 @@ class HTML_sportsmanager_ticker
<div id="left_page_header">
<h1 id="pagetitle_text">LIVE-TICKER</h1>
</div>
<div id="theme-toggle">
<span id="theme-auto" class="theme-btn" onclick="setTheme('auto')" title="Auto Theme">&#x1F4BB;</span>
<span id="theme-light" class="theme-btn" onclick="setTheme('light')" title="Light Theme">&#x2600;&#xFE0F;</span>
<span id="theme-dark" class="theme-btn" onclick="setTheme('dark')" title="Dark Theme">&#x1F319;</span>
</div>
<a href="<?php echo SportsManagerURL(); ?>" id="homeicon">Home</a>
<div style="clear:both;"></div>
<div id="left_menu">
@@ -1838,6 +1888,99 @@ class HTML_sportsmanager_ticker
height: 39px;
}
}
body.dark-mode {
background: #121212;
}
body.dark-mode #right_page {
background: #1e1e1e;
}
body.dark-mode h1#pagetitle_text {
color: #ffffff;
}
body.dark-mode a#homeicon {
color: #cccccc;
}
body.dark-mode #left_menu ul li a {
color: #ffffff;
}
body.dark-mode #tbl th {
color: #eeeeee;
background-color: #333333;
}
body.dark-mode #tbl tr td {
color: #dddddd;
}
body.dark-mode #detailedresults #tbl tr.odd td {
background-color: #242424;
}
body.dark-mode #detailedresults #tbl tr.even td {
background-color: #2a2a2a;
}
body.dark-mode #tbl tr.tablehead td {
background-color: #333333;
}
body.dark-mode tr.finished.odd {
background-color: #2a2a2a;
}
body.dark-mode tr.finished.even {
background-color: #2e2e2e;
}
body.dark-mode tr.updated {
background-color: #2d2616;
}
body.dark-mode tr.livenow {
background-color: #173824;
}
body.dark-mode tr.upcoming.odd {
background-color: #1a222f;
}
body.dark-mode tr.upcoming.even {
background-color: #1e2a3b;
}
body.dark-mode #tbl tr td.finished_winner {
color: #ffffff;
}
body.dark-mode tr.last_row {
background-color: #333333;
}
body.dark-mode #tbl tr td#last_row {
background-color: #333333;
}
body.dark-mode tr.upcoming.odd #resultat_holder,
body.dark-mode tr.upcoming.even #resultat_holder {
color: #eeeeee;
}
body.dark-mode #sponsorz {
background: #333333;
}
body.dark-mode .grey_button {
background: #333333;
}
body.dark-mode .grey_button a {
color: #eeeeee;
}
body.dark-mode .place_final {
color: #ffffff;
}
body.dark-mode .field_score {
color: #ffffff;
}
body.dark-mode .field_team {
color: #dddddd;
}
body.dark-mode .field_team.winner_bold {
color: #ffffff;
}
body.dark-mode #winner_area_positions span {
color: #dddddd;
}
body.dark-mode #winner_area_positions span#winner {
color: #ffffff;
}
body.dark-mode #winner_area_positions span#winner_name {
color: #ffffff;
}
<?php
}
@@ -39,6 +39,7 @@ COM_SPORTSMANAGER_ORGANISATION="Organisation"
COM_SPORTSMANAGER_TEAM_MEMBERS="Mannschaftsmitglieder"
COM_SPORTSMANAGER_TEAM_MEMBERS2=Vereinsmitglieder
COM_SPORTSMANAGER_MEMBERS="Mitglieder"
COM_SPORTSMANAGER_MEMBER="Mitglied"
COM_SPORTSMANAGER_ACTIVE_MEMBERS="Aktive Mitglieder"
COM_SPORTSMANAGER_TEAMS="Mannschaften"
COM_SPORTSMANAGER_CURRENT_TEAMS="Aktuelle Mannschaften"
@@ -72,7 +73,6 @@ COM_SPORTSMANAGER_CONFIRM_REMOVE_RANKING="Willst du die Rangliste wirklich entfe
COM_SPORTSMANAGER_ADD_INDIVIDUAL_COMPETITION="Individualwettbewerb hinzuf&uuml;gen"
COM_SPORTSMANAGER_INDIVIDUAL_COMPETITION="Individualwettbewerb"
COM_SPORTSMANAGER_INDIVIDUAL_COMPETITIONS="Individualwettbewerbe"
COM_SPORTSMANAGER_ASSOCIATION_BODIES="Verbandsorgane"
COM_SPORTSMANAGER_GAME="Spiel"
COM_SPORTSMANAGER_GAMES="Spiele"
COM_SPORTSMANAGER_GAMES_SHORTCUT="S"
@@ -157,6 +157,7 @@ COM_SPORTSMANAGER_DEFEAT="Niederlage"
COM_SPORTSMANAGER_DEFEATS="Niederlagen"
COM_SPORTSMANAGER_DEFEATS_SHORTCUT="N"
COM_SPORTSMANAGER_GOALS="Tore"
COM_SPORTSMANAGER_GOALS_SHORTCUT="T"
COM_SPORTSMANAGER_SETS="S&auml;tze"
COM_SPORTSMANAGER_POINT="Punkt"
COM_SPORTSMANAGER_POINTS="Punkte"
@@ -169,9 +170,9 @@ COM_SPORTSMANAGER_GAME_POINTS="Spielpunkte"
COM_SPORTSMANAGER_GAME_POINTS_SHORTCUT="SP"
COM_SPORTSMANAGER_SUFFIX_ONE_TEAM=" (eine Mannschaft)"
COM_SPORTSMANAGER_SUFFIX_TEAMS_TOGETHER=" (Mannschaften zusammen)"
COM_SPORTSMANAGER_DIFFERENCE="Differenz"
COM_SPORTSMANAGER_DIFFERENCE_IN_POINTS="Punktedifferenz"
COM_SPORTSMANAGER_POINTS_RATIO="Punkteverh&auml;ltnis"
COM_SPORTSMANAGER_DIFFERENCE="Differenz Tore"
COM_SPORTSMANAGER_DIFFERENCE_IN_POINTS="Differenz Spielpunkte"
COM_SPORTSMANAGER_POINTS_RATIO="Verh&auml;ltnis Spielpunkte"
COM_SPORTSMANAGER_SCHEDULE_DATE="Zeitpunkt"
COM_SPORTSMANAGER_TEAM_HOME="Heim"
COM_SPORTSMANAGER_TEAM_VISITOR="Gast"
@@ -296,10 +297,9 @@ COM_SPORTSMANAGER_REQUEST_MESSAGE_PLURAL="Es m&uuml;ssen mindestens %d Termine v
COM_SPORTSMANAGER_REJECT_SHIFT="Verschiebung ablehnen"
COM_SPORTSMANAGER_TO="bis"
COM_SPORTSMANAGER_PLAYER_STATISTICS="Spielerstatistiken"
COM_SPORTSMANAGER_PERFORMANCE_INDEX_SHORTCUT="LI"
COM_SPORTSMANAGER_WON="gewonnen"
COM_SPORTSMANAGER_LOST="verloren"
COM_SPORTSMANAGER_RATE="Quote"
COM_SPORTSMANAGER_RATE="Siegquote"
COM_SPORTSMANAGER_RATE_SHORTCUT="Q"
COM_SPORTSMANAGER_NO_CLUB="Kein Verein"
COM_SPORTSMANAGER_RATING="Wertung"
@@ -500,6 +500,8 @@ COM_SPORTSMANAGER_WIN_1_POINT="Sieg: 1 Punkt"
COM_SPORTSMANAGER_WIN_2_POINTS="Sieg: 2 Punkte, Unentschieden: 1 Punkt"
COM_SPORTSMANAGER_WIN_3_POINTS="Sieg: 3 Punkte, Unentschieden: 1 Punkt"
COM_SPORTSMANAGER_MEETING_CONCLUDED_AT="Begegnung abgeschlossen bei"
COM_SPORTSMANAGER_GAMES_IN_STATISTIK="Spiele in Spielerstatistik"
COM_SPORTSMANAGER_GAMES_IN_STATISTIK_ALL="Alle Spiele"
COM_SPORTSMANAGER_GENERALLY="Allgemein"
COM_SPORTSMANAGER_TYPE="Typ"
COM_SPORTSMANAGER_ELO_MIN="Elo min."
@@ -550,9 +552,22 @@ COM_SPORTSMANAGER_NO_RATING="Keine Wertung"
COM_SPORTSMANAGER_TEAM_COMPETITIONS="Mannschaftswettbewerbe"
COM_SPORTSMANAGER_TABLE_SUMMARY="Tabellenwertung"
COM_SPORTSMANAGER_HEAD_TO_HEAD_RECORD="Direkter Vergleich"
COM_SPORTSMANAGER_HEAD_TO_HEAD_OPT_NOT="deaktiviert"
COM_SPORTSMANAGER_HEAD_TO_HEAD_OPT_POINTS="bei gleicher Punktzahl"
COM_SPORTSMANAGER_HEAD_TO_HEAD_OPT_SETS="bei gleicher Punktzahl, Satzpunkte"
COM_SPORTSMANAGER_HEAD_TO_HEAD_OPT_GOALS="bei gleicher Punktzahl, Satzpunkte, Tore"
COM_SPORTSMANAGER_POINTS_WON_LOST_DIFFERENCE="Spielpunkte gewonnen, Spielpunkte verloren, Punktedifferenz"
COM_SPORTSMANAGER_PERFORMANCE_INDEX="Leistungsindex (SP+ * SP+ * 100) / (SP+ + SP-), Spielpunkte gewonnen, ..."
COM_SPORTSMANAGER_PERFORMANCE_INDEX0="Spielpunkte gewonnen, Spielpunkte verloren, Punktedifferenz"
COM_SPORTSMANAGER_PERFORMANCE_INDEX1="Leistungsindex (SP+ * SP+ * 100) / (SP+ + SP-), Spielpunkte gewonnen, ..."
COM_SPORTSMANAGER_PERFORMANCE_INDEX2="Leistungsindex (S * P+ * 10) / (P+ + P-), Spielpunkte gewonnen, ..."
COM_SPORTSMANAGER_PERFORMANCE_INDEX3="Race Performance Index (Siege*2 + Unentschieden + Tordifferenz)"
COM_SPORTSMANAGER_PERFORMANCE_INDEX4="Effizienzindex (T+ / (T+ + T-))"
COM_SPORTSMANAGER_PERFORMANCE_INDEX5="Punkteschnitt (P+ / Anzahl Sätze)"
COM_SPORTSMANAGER_PERFORMANCE_SHORT1="LI"
COM_SPORTSMANAGER_PERFORMANCE_SHORT2="LI"
COM_SPORTSMANAGER_PERFORMANCE_SHORT3="RPI"
COM_SPORTSMANAGER_PERFORMANCE_SHORT4="EI"
COM_SPORTSMANAGER_PERFORMANCE_SHORT5="AVG"
COM_SPORTSMANAGER_INDIVIDUAL_STATISTICS="Einzelstatistik aus allen Spielen"
COM_SPORTSMANAGER_INDIVIDUAL_STATISTICS_SINGLES="Einzelstatistik aus Einzel-Spielen"
COM_SPORTSMANAGER_INDIVIDUAL_STATISTICS_DOUBLES="Einzelstatistik aus Doppel-Spielen"
@@ -900,6 +915,9 @@ COM_SPORTSMANAGER_RESULT_ONLY="Nur Ergebnis"
COM_SPORTSMANAGER_GAME_NUMBER="Spiel %d"
COM_SPORTSMANAGER_LABEL_GAME_NUMBER="Spiel Nr"
COM_SPORTSMANAGER_LABEL_GAME_TITLE="Spieltag Titel"
COM_SPORTSMANAGER_EXPLICIT_PENALTIES="Explizite Strafen"
COM_SPORTSMANAGER_EXPLICIT_PENALTIES_EMAIL_SUBJECT="%s: Strafpunkte erhalten"
COM_SPORTSMANAGER_EXPLICIT_PENALTIES_EMAIL_BODY="%s wurden %f Strafpunkte zugeteilt mit der Begründung: %s"
COM_SPORTSMANAGER_EMAIL_SHIFT_GAME_APPOINTMENT_SUBJECT="%s vs %s: Spieltermin verlegen"
COM_SPORTSMANAGER_EMAIL_SHIFT_GAME_APPOINTMENT_BODY="Zur Begegnung %s gegen %s am %s in %s wird von %s der Spieltermin verschoben.\n\nAlternative Termine:\n\n"
COM_SPORTSMANAGER_EMAIL_SHIFT_GAME_APPOINTMENT_REQUESTED_BODY="Zur Begegnung %s gegen %s am %s in %s wird von %s der Spieltermin verschoben.\n\nBitte alternative Termine vorschlagen unter %s"
@@ -1070,4 +1088,31 @@ COM_SPORTSMANAGER_NOT_VALID_TIME="Ung&uuml;ltige Uhrzeit"
COM_SPORTSMANAGER_REALLY_MATCH_RESCHEDULING="Willst Du diesen Spielverlegung wirklich entfernen?"
COM_SPORTSMANAGER_REST_DAYS="Ruhetage"
COM_SPORTSMANAGER_TRAINING_DAYS="Trainingstage"
COM_SPORTSMANAGER_NOT_ACTUALIZED_DATA="Nicht aktualisierte Daten"
COM_SPORTSMANAGER_NOT_ACTUALIZED_DATA="Nicht aktualisierte Daten"
COM_SPORTSMANAGER_ASSOCIATION_BODIES="Verbandsorgane"
COM_SPORTSMANAGER_ASSOCIATION_BODY="Verbandsorgan"
COM_SPORTSMANAGER_ADD_ASSOCIATION_BODY="Verbandsorgan hinzuf&uuml;gen"
COM_SPORTSMANAGER_REALLY_REMOVE_ASSOCIATION_BODY="Willst Du dieses Verbandsorgan wirklich entfernen?"
COM_SPORTSMANAGER_INVALID_ASSOCIATION_BODY_NAME="Ung&uuml;ltiger Name f&uuml;r Verbandsorgan!"
COM_SPORTSMANAGER_NAME_NOT_COMPLETE="Der Name ist nicht komplett ausgef&uuml;llt"
COM_SPORTSMANAGER_ADDITIONAL_INFO="Zusatzinfo"
COM_SPORTSMANAGER_USE_HTML="Hier sollte HTML-formatierter Text verwendet werden"
COM_SPORTSMANAGER_REALLY_REMOVE_ASSOCIATION_BODY_MEMBER="M&ouml;chtest du dieses Mitglied des Verbandsorgans wirklich entfernen?"
COM_SPORTSMANAGER_HELP_EDIT_ASSOCIATION_BODY_MEMBER="Wird ein Name aus der Spielerliste ausgew&auml;hlt, werden Nachname und Vorname &uuml;bernommen.<br>Telefon, Mobil, E-Mail werden aus der Spielerliste &uuml;bernommen, wenn sie hier nicht ausgef&uuml;llt sind."
COM_SPORTSMANAGER_HALL_OF_FAME="Hall of Fame"
COM_SPORTSMANAGER_ADD_HALL_OF_FAME="Hall of Fame hinzuf&uuml;gen"
COM_SPORTSMANAGER_INVALID_HALL_OF_FAME_NAME="Invalider Name f&uuml;r Hall of Fame"
COM_SPORTSMANAGER_REALLY_REMOVE_HALL_OF_FAME="Willst Du wirklich diese Hall of Fame mit allen Mitgliedern l&ouml;schen?"
COM_SPORTSMANAGER_MATCH_TYPE="Spielform"
COM_SPORTSMANAGER_REALLY_REMOVE_HALL_OF_FAME_YEAR="Willst Du wirklich dieses Hall of Fame Jahr l&ouml;schen?"
COM_SPORTSMANAGER_YEARS="Jahre"
COM_SPORTSMANAGER_ADD_HALL_OF_FAME_YEAR="Hall of Fame Jahr hinzuf&uuml;gen"
COM_SPORTSMANAGER_NO_ENTRY="kein Eintrag"
COM_SPORTSMANAGER_REALLY_SWAP_MATCH="Willst Du wirklich das Heimrecht tauschen?"
COM_SPORTSMANAGER_SWAP_MATCH="Heimrechttausch"
COM_SPORTSMANAGER_REALLY_DELETE_MATCH_REPORT="Der Spielbericht wird zusammen mit allen historischen Eintr&auml;gen gel&ouml;scht. Willst du den Spielbericht wirklich l&ouml;schen?"
COM_SPORTSMANAGER_MATCH_REPORT_DELETED="Spielbericht gel&ouml;scht"
COM_SPORTSMANAGER_MATCH_REPORT_WAS_DELETED="Der Spielbericht wurde erfolgreich gel&ouml;scht!"
COM_SPORTSMANAGER_MATCH_REPORT_CORRECTED="Spielberichtskorrektur"
COM_SPORTSMANAGER_MIN_MATCHES="Mindestzahl Spiele"
COM_SPORTSMANAGER_SELECT_ALL="Alle"
@@ -39,6 +39,7 @@ COM_SPORTSMANAGER_ORGANISATION="Organisation"
COM_SPORTSMANAGER_TEAM_MEMBERS="Team members"
COM_SPORTSMANAGER_TEAM_MEMBERS2="Club members"
COM_SPORTSMANAGER_MEMBERS="Members"
COM_SPORTSMANAGER_MEMBER="Member"
COM_SPORTSMANAGER_ACTIVE_MEMBERS="Active members"
COM_SPORTSMANAGER_TEAMS="Teams"
COM_SPORTSMANAGER_CURRENT_TEAMS="Current teams"
@@ -72,7 +73,6 @@ COM_SPORTSMANAGER_CONFIRM_REMOVE_RANKING="Do you really want to remove the ranki
COM_SPORTSMANAGER_ADD_INDIVIDUAL_COMPETITION="Add individual competition"
COM_SPORTSMANAGER_INDIVIDUAL_COMPETITION="Individual competition"
COM_SPORTSMANAGER_INDIVIDUAL_COMPETITIONS="Individual competitions"
COM_SPORTSMANAGER_ASSOCIATION_BODIES="Association bodies"
COM_SPORTSMANAGER_GAME="Game"
COM_SPORTSMANAGER_GAMES="Games"
COM_SPORTSMANAGER_GAMES_SHORTCUT="G"
@@ -157,6 +157,7 @@ COM_SPORTSMANAGER_DEFEAT="Defeat"
COM_SPORTSMANAGER_DEFEATS="Defeats"
COM_SPORTSMANAGER_DEFEATS_SHORTCUT="F"
COM_SPORTSMANAGER_GOALS="Goals"
COM_SPORTSMANAGER_GOALS_SHORTCUT="G"
COM_SPORTSMANAGER_SETS="Sets"
COM_SPORTSMANAGER_POINT="Point"
COM_SPORTSMANAGER_POINTS="Points"
@@ -169,9 +170,9 @@ COM_SPORTSMANAGER_GAME_POINTS="Game points"
COM_SPORTSMANAGER_GAME_POINTS_SHORTCUT="GP"
COM_SPORTSMANAGER_SUFFIX_ONE_TEAM=" (one team)"
COM_SPORTSMANAGER_SUFFIX_TEAMS_TOGETHER=" (teams together)"
COM_SPORTSMANAGER_DIFFERENCE="Difference"
COM_SPORTSMANAGER_DIFFERENCE_IN_POINTS="Difference in points"
COM_SPORTSMANAGER_POINTS_RATIO="Points ratio"
COM_SPORTSMANAGER_DIFFERENCE="Goal difference"
COM_SPORTSMANAGER_DIFFERENCE_IN_POINTS="Difference in game points"
COM_SPORTSMANAGER_POINTS_RATIO="Game points ratio"
COM_SPORTSMANAGER_SCHEDULE_DATE="Appointment date"
COM_SPORTSMANAGER_TEAM_HOME="Home"
COM_SPORTSMANAGER_TEAM_VISITOR="Visitor"
@@ -296,10 +297,9 @@ COM_SPORTSMANAGER_REQUEST_MESSAGE_PLURAL="At least %d dates must be given comple
COM_SPORTSMANAGER_REJECT_SHIFT="Reject shift"
COM_SPORTSMANAGER_TO="until"
COM_SPORTSMANAGER_PLAYER_STATISTICS="Player statistics"
COM_SPORTSMANAGER_PERFORMANCE_INDEX_SHORTCUT="PI"
COM_SPORTSMANAGER_WON="won"
COM_SPORTSMANAGER_LOST="lost"
COM_SPORTSMANAGER_RATE="Rate"
COM_SPORTSMANAGER_RATE="Winning rate"
COM_SPORTSMANAGER_RATE_SHORTCUT="R"
COM_SPORTSMANAGER_NO_CLUB="No club"
COM_SPORTSMANAGER_RATING="Rating"
@@ -500,6 +500,8 @@ COM_SPORTSMANAGER_WIN_1_POINT="Win: 1 point"
COM_SPORTSMANAGER_WIN_2_POINTS="Win: 2 points, draw: 1 point"
COM_SPORTSMANAGER_WIN_3_POINTS="Win: 3 points, draw: 1 point"
COM_SPORTSMANAGER_MEETING_CONCLUDED_AT="Match won at"
COM_SPORTSMANAGER_GAMES_IN_STATISTIK="Games in player statistics"
COM_SPORTSMANAGER_GAMES_IN_STATISTIK_ALL="All games"
COM_SPORTSMANAGER_GENERALLY="Generally"
COM_SPORTSMANAGER_TYPE="Type"
COM_SPORTSMANAGER_ELO_MIN="Elo min."
@@ -550,9 +552,22 @@ COM_SPORTSMANAGER_NO_RATING="No rating"
COM_SPORTSMANAGER_TEAM_COMPETITIONS="Team competitions"
COM_SPORTSMANAGER_TABLE_SUMMARY="Table rating"
COM_SPORTSMANAGER_HEAD_TO_HEAD_RECORD="Head-to-head record"
COM_SPORTSMANAGER_HEAD_TO_HEAD_OPT_NOT="Off"
COM_SPORTSMANAGER_HEAD_TO_HEAD_OPT_POINTS="Tie: pts"
COM_SPORTSMANAGER_HEAD_TO_HEAD_OPT_SETS="Tie: pts, set points"
COM_SPORTSMANAGER_HEAD_TO_HEAD_OPT_GOALS="Tie: pts, set points, goals"
COM_SPORTSMANAGER_POINTS_WON_LOST_DIFFERENCE="Game points won, game points lost, point difference"
COM_SPORTSMANAGER_PERFORMANCE_INDEX="Performance index (GP+ * GP+ * 100) / (GP+ + GP-), game points won, ..."
COM_SPORTSMANAGER_PERFORMANCE_INDEX0="Game points won, game points lost, point difference"
COM_SPORTSMANAGER_PERFORMANCE_INDEX1="Performance index (GP+ * GP+ * 100) / (GP+ + GP-), game points won, ..."
COM_SPORTSMANAGER_PERFORMANCE_INDEX2="Performance index (games * P+ * 10) / (P+ + P-), game points won, ..."
COM_SPORTSMANAGER_PERFORMANCE_INDEX3="Race Performance Index (victories*2 + draws + goal difference)"
COM_SPORTSMANAGER_PERFORMANCE_INDEX4="Efficency index (G+ / (G+ + G-))"
COM_SPORTSMANAGER_PERFORMANCE_INDEX5="Set point average (P+ / count sets)"
COM_SPORTSMANAGER_PERFORMANCE_SHORT1="LI"
COM_SPORTSMANAGER_PERFORMANCE_SHORT2="LI"
COM_SPORTSMANAGER_PERFORMANCE_SHORT3="RPI"
COM_SPORTSMANAGER_PERFORMANCE_SHORT4="EI"
COM_SPORTSMANAGER_PERFORMANCE_SHORT5="AVG"
COM_SPORTSMANAGER_INDIVIDUAL_STATISTICS="Individual statistics of all games"
COM_SPORTSMANAGER_INDIVIDUAL_STATISTICS_SINGLES="Individual statistics of singles games"
COM_SPORTSMANAGER_INDIVIDUAL_STATISTICS_DOUBLES="Individual statistics of doubles games"
@@ -900,6 +915,9 @@ COM_SPORTSMANAGER_RESULT_ONLY="Result only"
COM_SPORTSMANAGER_GAME_NUMBER="Game %d"
COM_SPORTSMANAGER_LABEL_GAME_NUMBER="Game nr"
COM_SPORTSMANAGER_LABEL_GAME_TITLE="Gameday title"
COM_SPORTSMANAGER_EXPLICIT_PENALTIES="Explicit Penalties"
COM_SPORTSMANAGER_EXPLICIT_PENALTIES_EMAIL_SUBJECT="%s: received penalty"
COM_SPORTSMANAGER_EXPLICIT_PENALTIES_EMAIL_BODY="%s received a penalty of %f points based on the following justification: %s"
COM_SPORTSMANAGER_EMAIL_SHIFT_GAME_APPOINTMENT_SUBJECT="%s vs %s: Shift game appointment"
COM_SPORTSMANAGER_EMAIL_SHIFT_GAME_APPOINTMENT_BODY="For match %s versus %s on %s in %s the game appointment is shifted by %s.\n\nAlternative appointments:\n\n"
COM_SPORTSMANAGER_EMAIL_SHIFT_GAME_APPOINTMENT_REQUESTED_BODY="For match %s on %s in %s the game appointment is shifted by %s.\n\nPlease propose alternative appointments under %s"
@@ -1070,4 +1088,31 @@ COM_SPORTSMANAGER_NOT_VALID_TIME="Not valid time"
COM_SPORTSMANAGER_REALLY_MATCH_RESCHEDULING="Do you really want to remove this match rescheduling?"
COM_SPORTSMANAGER_REST_DAYS="Rest days"
COM_SPORTSMANAGER_TRAINING_DAYS="Training days"
COM_SPORTSMANAGER_NOT_ACTUALIZED_DATA="Data not updated"
COM_SPORTSMANAGER_NOT_ACTUALIZED_DATA="Data not updated"
COM_SPORTSMANAGER_ASSOCIATION_BODIES="Association bodies"
COM_SPORTSMANAGER_ASSOCIATION_BODY="Association body"
COM_SPORTSMANAGER_ADD_ASSOCIATION_BODY="Add association body"
COM_SPORTSMANAGER_REALLY_REMOVE_ASSOCIATION_BODY="Do you really want to remove this association body?"
COM_SPORTSMANAGER_INVALID_ASSOCIATION_BODY_NAME="Invalid association body name"
COM_SPORTSMANAGER_NAME_NOT_COMPLETE="The name is not completely filled in"
COM_SPORTSMANAGER_ADDITIONAL_INFO="Additional information"
COM_SPORTSMANAGER_USE_HTML="HTML-formatted text should be used here."
COM_SPORTSMANAGER_REALLY_REMOVE_ASSOCIATION_BODY_MEMBER="Do you really want to remove this association body member?"
COM_SPORTSMANAGER_HELP_EDIT_ASSOCIATION_BODY_MEMBER="Selecting a name from the player list will fill in the first and last name.<br>Phone, mobile, and email are filled from the player list if left blank here."
COM_SPORTSMANAGER_HALL_OF_FAME="Hall of Fame"
COM_SPORTSMANAGER_ADD_HALL_OF_FAME="Add Hall of Fame"
COM_SPORTSMANAGER_INVALID_HALL_OF_FAME_NAME="Invalid Hall of Fame name"
COM_SPORTSMANAGER_REALLY_REMOVE_HALL_OF_FAME="Are you sure you want to delete this Hall of Fame including all its members?"
COM_SPORTSMANAGER_MATCH_TYPE="Game Type"
COM_SPORTSMANAGER_REALLY_REMOVE_HALL_OF_FAME_YEAR="Are you sure you want to delete this Hall of Fame year?"
COM_SPORTSMANAGER_YEARS="Years"
COM_SPORTSMANAGER_ADD_HALL_OF_FAME_YEAR="Add Hall of Fame Year"
COM_SPORTSMANAGER_NO_ENTRY="no entry"
COM_SPORTSMANAGER_REALLY_SWAP_MATCH="Do you really want to swap the home advantage?"
COM_SPORTSMANAGER_SWAP_MATCH="Swap home advantage"
COM_SPORTSMANAGER_REALLY_DELETE_MATCH_REPORT="The match report and all its history will be deleted. Are you sure you want to delete the match report?"
COM_SPORTSMANAGER_MATCH_REPORT_DELETED="Match report deleted"
COM_SPORTSMANAGER_MATCH_REPORT_WAS_DELETED="The match report has been successfully deleted!"
COM_SPORTSMANAGER_MATCH_REPORT_CORRECTED="Match report corrected"
COM_SPORTSMANAGER_MIN_MATCHES="Min count matches"
COM_SPORTSMANAGER_SELECT_ALL="All"
+140 -13
View File
@@ -526,6 +526,7 @@ return new class () implements InstallerScriptInterface
. "\n `bestenliste_id` int(11) NOT NULL DEFAULT '0',"
. "\n `spieler_id` int(11) NOT NULL DEFAULT '0',"
. "\n `spieler_2_id` int(11) DEFAULT NULL,"
. "\n `team_id` INT(11) DEFAULT NULL,"
. "\n `siege` smallint(6) DEFAULT NULL,"
. "\n `unentschieden` smallint(6) DEFAULT NULL,"
. "\n `niederlagen` smallint(6) DEFAULT NULL,"
@@ -722,6 +723,18 @@ return new class () implements InstallerScriptInterface
$db->setQuery( $query );
if (!$db->execute()) { die($db->stderr(true)); }
$query = "CREATE TABLE IF NOT EXISTS `#__sportsmanager_team_strafen` ("
. "\n `team_strafen_id` int(11) NOT NULL AUTO_INCREMENT,"
. "\n `zeitpunkt` datetime NOT NULL,"
. "\n `moderator_user_id` int(11) DEFAULT NULL,"
. "\n `team_id` int(11) NOT NULL DEFAULT '0',"
. "\n `strafe` float(6,2) NOT NULL DEFAULT '0',"
. "\n `beschreibung` text NOT NULL,"
. "\n PRIMARY KEY (`team_strafen_id`)"
. "\n) ENGINE=MyISAM DEFAULT CHARACTER SET utf8 COLLATE utf8_general_ci;";
$db->setQuery( $query );
if (!$db->execute()) { die($db->stderr(true)); }
$query = "CREATE TABLE IF NOT EXISTS `#__sportsmanager_teamansprechpartner` ("
. "\n `kontaktperson_id` int(11) NOT NULL AUTO_INCREMENT,"
. "\n `team_id` int(11) NOT NULL DEFAULT '0',"
@@ -777,6 +790,7 @@ return new class () implements InstallerScriptInterface
. "\n `spielpunkte_bedingung` tinyint(4) NOT NULL DEFAULT '0',"
. "\n `spielernamen` tinyint(4) NOT NULL DEFAULT '0',"
. "\n `heimtausch` tinyint(4) NOT NULL DEFAULT '0',"
. "\n `spiele_in_spielerstatistik` tinyint(4) NOT NULL DEFAULT '0',"
. "\n `modus` varchar(200) NOT NULL DEFAULT '',"
. "\n `status` tinyint(4) NOT NULL DEFAULT '0',"
. "\n PRIMARY KEY (`teamspiel_modus_id`)"
@@ -1049,6 +1063,7 @@ return new class () implements InstallerScriptInterface
. "\n `erster_tag` date NOT NULL DEFAULT '0000-00-00',"
. "\n `letzter_tag` date DEFAULT NULL,"
. "\n `elo_wertung` tinyint(4) DEFAULT NULL,"
. "\n `explizite_strafen` tinyint(4) DEFAULT '0',"
. "\n `logo_url` TINYTEXT NOT NULL,"
. "\n `ticker_logo_url` TINYTEXT NOT NULL,"
. "\n `status` tinyint(4) NOT NULL DEFAULT '0',"
@@ -1134,7 +1149,7 @@ return new class () implements InstallerScriptInterface
. "\n ) ENGINE=MyISAM DEFAULT CHARACTER SET utf8 COLLATE utf8_general_ci;";
$db->setQuery( $query );
if (!$db->execute()) { die($db->stderr(true)); }
$query = "CREATE TABLE IF NOT EXISTS `#__sportsmanager_verstoesse` ("
. "\n `verstoesse_id` int(11) NOT NULL AUTO_INCREMENT,"
. "\n `regelwerke_id` int(11) NOT NULL DEFAULT 0,"
@@ -1150,7 +1165,7 @@ return new class () implements InstallerScriptInterface
. "\n ) ENGINE=MyISAM DEFAULT CHARACTER SET utf8 COLLATE utf8_general_ci;";
$db->setQuery( $query );
if (!$db->execute()) { die($db->stderr(true)); }
$query = "CREATE TABLE IF NOT EXISTS `#__sportsmanager_ordnungsstrafen` ("
. "\n `ordnungsstrafen_id` int(11) NOT NULL AUTO_INCREMENT,"
. "\n `verstoesse_id` int(11) NOT NULL DEFAULT 0,"
@@ -1168,7 +1183,7 @@ return new class () implements InstallerScriptInterface
. "\n ) ENGINE=MyISAM DEFAULT CHARACTER SET utf8 COLLATE utf8_general_ci;";
$db->setQuery( $query );
if (!$db->execute()) { die($db->stderr(true)); }
$query = "CREATE TABLE IF NOT EXISTS `#__sportsmanager_email_vorlagen` ("
. "\n `email_vorlagen_id` int(11) NOT NULL AUTO_INCREMENT,"
. "\n `vorlage` varchar(64) NOT NULL,"
@@ -1183,12 +1198,104 @@ return new class () implements InstallerScriptInterface
. "\n ) ENGINE=MyISAM DEFAULT CHARACTER SET utf8 COLLATE utf8_general_ci;";
$db->setQuery( $query );
if (!$db->execute()) { die($db->stderr(true)); }
$query = "CREATE TABLE IF NOT EXISTS `#__sportsmanager_verbandsorgane` ("
. "\n `verbandsorgane_id` int(11) NOT NULL AUTO_INCREMENT,"
. "\n `veranstalter_id` int(11) DEFAULT NULL,"
. "\n `verbandsorgan` varchar(32) DEFAULT NULL,"
. "\n `kategorie` int(4) DEFAULT NULL,"
. "\n `reihenfolge` int(4) DEFAULT NULL,"
. "\n `email` varchar(64) DEFAULT NULL,"
. "\n `beschreibung` text NOT NULL,"
. "\n PRIMARY KEY (`verbandsorgane_id`),"
. "\n KEY (`veranstalter_id`)"
. "\n ) ENGINE=MyISAM DEFAULT CHARACTER SET utf8 COLLATE utf8_general_ci;";
$db->setQuery( $query );
if (!$db->execute()) { die($db->stderr(true)); }
$query = "CREATE TABLE IF NOT EXISTS `#__sportsmanager_mitglied_von_verbandsorgan` ("
. "\n `mitglied_von_verbandsorgan_id` int(11) NOT NULL AUTO_INCREMENT,"
. "\n `verbandsorgane_id` int(11) DEFAULT NULL,"
. "\n `funktion` varchar(32) DEFAULT NULL,"
. "\n `zusatzinfo` varchar(64) DEFAULT NULL,"
. "\n `spieler_id` int(11) DEFAULT NULL,"
. "\n `nachname` varchar(32) DEFAULT NULL,"
. "\n `vorname` varchar(32) DEFAULT NULL,"
. "\n `email` varchar(64) DEFAULT NULL,"
. "\n `telefon` varchar(32) DEFAULT NULL,"
. "\n `mobil` varchar(32) DEFAULT NULL,"
. "\n `reihenfolge` int(11) DEFAULT NULL,"
. "\n PRIMARY KEY (`mitglied_von_verbandsorgan_id`),"
. "\n KEY (`verbandsorgane_id`),"
. "\n KEY (`spieler_id`)"
. "\n ) ENGINE=MyISAM DEFAULT CHARACTER SET utf8 COLLATE utf8_general_ci;";
$db->setQuery( $query );
if (!$db->execute()) { die($db->stderr(true)); }
$query = "CREATE TABLE IF NOT EXISTS `#__sportsmanager_halloffame` ("
. "\n `halloffame_id` int(11) NOT NULL AUTO_INCREMENT,"
. "\n `veranstalter_id` int(11) DEFAULT NULL,"
. "\n `halloffame` varchar(64) DEFAULT NULL,"
. "\n `kategorie` int(4) DEFAULT NULL,"
. "\n `spielform` int(11) DEFAULT NULL,"
. "\n `reihenfolge` int(4) DEFAULT NULL,"
. "\n PRIMARY KEY (`halloffame_id`),"
. "\n KEY `veranstalter_id` (`veranstalter_id`)"
. "\n ) ENGINE=MyISAM DEFAULT CHARACTER SET utf8 COLLATE utf8_general_ci;";
$db->setQuery($query);
if (!$db->execute()) {
die($db->stderr(true));
}
$query = "CREATE TABLE IF NOT EXISTS `#__sportsmanager_mitglied_von_halloffame` ("
. "\n `mitglied_halloffame_id` int(11) NOT NULL AUTO_INCREMENT,"
. "\n `halloffame_id` int(11) NOT NULL,"
. "\n `jahr` int(4) DEFAULT NULL,"
. "\n `platz` int(11) DEFAULT NULL,"
. "\n `verein_id` int(11) DEFAULT NULL,"
. "\n `teamname` varchar(64) DEFAULT NULL,"
. "\n `spieler1_id` int(11) DEFAULT NULL,"
. "\n `spieler1` varchar(64) DEFAULT NULL,"
. "\n `spieler2_id` int(11) DEFAULT NULL,"
. "\n `spieler2` varchar(64) DEFAULT NULL,"
. "\n PRIMARY KEY (`mitglied_halloffame_id`),"
. "\n UNIQUE KEY `halloffame_jahr_platz` (`halloffame_id`,`jahr`,`platz`),"
. "\n KEY `halloffame_id` (`halloffame_id`),"
. "\n KEY `verein_id` (`verein_id`),"
. "\n KEY `spieler1_id` (`spieler1_id`),"
. "\n KEY `spieler2_id` (`spieler2_id`)"
. "\n ) ENGINE=MyISAM DEFAULT CHARACTER SET utf8 COLLATE utf8_general_ci;";
$db->setQuery($query);
if (!$db->execute()) {
die($db->stderr(true));
}
$query = "INSERT IGNORE #__sportsmanager_einstellungen SET name = 'verbands_kuerzel', wert = 'tbe.';";
$db->setQuery( $query );
if (!$db->execute()) { die($db->stderr(true)); }
$query = "INSERT IGNORE #__sportsmanager_einstellungen SET name = 'datenbank_version', wert = '116';";
$query = "INSERT IGNORE #__sportsmanager_einstellungen SET name = 'datenbank_version', wert = '121';";
$db->setQuery( $query );
if (!$db->execute()) { die($db->stderr(true)); }
$query = "CREATE TABLE IF NOT EXISTS `#__sportsmanager_sync_log` ("
. "\n `sync_id` INT(11) NOT NULL AUTO_INCREMENT,"
. "\n `sync_timestamp` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,"
. "\n `sync_direction` ENUM('push', 'receive') NOT NULL,"
. "\n `sync_trigger` ENUM('manual', 'cron', 'api') NOT NULL,"
. "\n `sync_status` ENUM('success', 'error') NOT NULL,"
. "\n `spieler_count` INT(11) DEFAULT 0,"
. "\n `spieler_updated` INT(11) DEFAULT 0,"
. "\n `spieler_added` INT(11) DEFAULT 0,"
. "\n `message` TEXT,"
. "\n `details` TEXT,"
. "\n PRIMARY KEY (`sync_id`),"
. "\n INDEX `idx_timestamp` (`sync_timestamp`)"
. "\n) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;";
$db->setQuery( $query );
if (!$db->execute()) { die($db->stderr(true)); }
$query = "INSERT IGNORE #__sportsmanager_einstellungen SET name = 'dtfb_sync_url', wert = '';";
$db->setQuery( $query );
if (!$db->execute()) { die($db->stderr(true)); }
@@ -1219,11 +1326,11 @@ return new class () implements InstallerScriptInterface
$query = "INSERT IGNORE #__sportsmanager_einstellungen SET name = 'turnierbaum_zeigen', wert = '0';";
$db->setQuery( $query );
if (!$db->execute()) { die($db->stderr(true)); }
$query = "INSERT IGNORE #__sportsmanager_einstellungen SET name = 'ordnungsstrafen_verwenden', wert = '0';";
$db->setQuery( $query );
if (!$db->execute()) { die($db->stderr(true)); }
$query = "INSERT IGNORE #__sportsmanager_einstellungen SET name = 'spalte_mitglieder_zeigen', wert = '1';";
$db->setQuery( $query );
if (!$db->execute()) { die($db->stderr(true)); }
@@ -1236,7 +1343,7 @@ return new class () implements InstallerScriptInterface
. "\n VALUES ('Beliebig', '0', '0', '0', '1', '3', '0');";
$db->setQuery($query);
if (!$db->execute()) { die($db->stderr(true)); }
$query = "INSERT IGNORE INTO `#__sportsmanager_email_vorlagen` (`email_vorlagen_id`, `vorlage`) VALUES (NULL, 'Ordnungsstrafe');";
$db->setQuery($query);
if (!$db->execute()) { die($db->stderr(true)); }
@@ -1244,7 +1351,7 @@ return new class () implements InstallerScriptInterface
$query = "INSERT IGNORE INTO `#__sportsmanager_email_vorlagen` (`email_vorlagen_id`, `vorlage`) VALUES (NULL, 'Spielverlegung');";
$db->setQuery($query);
if (!$db->execute()) { die($db->stderr(true)); }
$query = "INSERT #__sportsmanager_rangliste_system"
. "\n SET systembezeichnung = 'Klostermann/Wahle (1 Punkt für letzten Platz)',"
. "\n status = '1',"
@@ -1677,6 +1784,10 @@ return new class () implements InstallerScriptInterface
$db->setQuery( $query );
if (!$db->execute()) { die($db->stderr(true)); }
$query = "DROP TABLE IF EXISTS `#__sportsmanager_team_strafen`;";
$db->setQuery( $query );
if (!$db->execute()) { die($db->stderr(true)); }
$query = "DROP TABLE IF EXISTS `#__sportsmanager_turnier`;";
$db->setQuery( $query );
if (!$db->execute()) { die($db->stderr(true)); }
@@ -1732,21 +1843,37 @@ return new class () implements InstallerScriptInterface
$query = "DROP TABLE IF EXISTS `#__sportsmanager_verteiler`;";
$db->setQuery( $query );
if (!$db->execute()) { die($db->stderr(true)); }
$query = "DROP TABLE IF EXISTS `#__sportsmanager_regelwerke`;";
$db->setQuery( $query );
if (!$db->execute()) { die($db->stderr(true)); }
$query = "DROP TABLE IF EXISTS `#__sportsmanager_verstoesse`;";
$db->setQuery( $query );
if (!$db->execute()) { die($db->stderr(true)); }
$query = "DROP TABLE IF EXISTS `#__sportsmanager_ordnungsstrafen`;";
$db->setQuery( $query );
if (!$db->execute()) { die($db->stderr(true)); }
$query = "DROP TABLE IF EXISTS `#__sportsmanager_email_vorlagen`;";
$db->setQuery( $query );
if (!$db->execute()) { die($db->stderr(true)); }
$query = "DROP TABLE IF EXISTS `#__sportsmanager_verbandsorgane`;";
$db->setQuery( $query );
if (!$db->execute()) { die($db->stderr(true)); }
$query = "DROP TABLE IF EXISTS `#__sportsmanager_mitglied_von_verbandsorgan`;";
$db->setQuery( $query );
if (!$db->execute()) { die($db->stderr(true)); }
$query = "DROP TABLE IF EXISTS `#__sportsmanager_halloffame`;";
$db->setQuery( $query );
if (!$db->execute()) { die($db->stderr(true)); }
$query = "DROP TABLE IF EXISTS `#__sportsmanager_mitglied_von_halloffame`;";
$db->setQuery( $query );
if (!$db->execute()) { die($db->stderr(true)); }
}
};
+23
View File
@@ -0,0 +1,23 @@
# DTFB Player Sync — QA findings & applied fixes
Concise record of the defects found while reviewing the player-sync receiver
(`syncReceiveSpielerImport()` in `sync.php`) and the six fixes applied in this PR.
The exploratory test harness used to find these has been removed — the
authoritative next test is the **staging end-to-end sync** (see the PR description).
## The six issues fixed
| # | Issue | Impact | Fix in `sync.php` |
|---|-------|--------|-------------------|
| 1 | **Receiver did not normalise input encoding.** A latin1 / Windows-1252 CSV (the legacy manual export) has `ß` as the single byte `0xDF`. Staging the org name into the utf8mb4 table truncated it at that byte, so the org lookup missed and **every row was skipped**. | Critical — silent data loss (e.g. 2964 rows parsed, 0 imported, `success=true`). | Transcode the payload to UTF-8 when it is not already valid UTF-8, before staging. |
| 2 | **A zero-effect import reported success.** When N rows parsed but nothing was added or updated, the function returned `success=true`. | Critical — masks encoding/mapping failures. | Return `success=false` with a diagnostic message when `rows>0` but `added==0 && updated==0`. |
| 3 | **Dead `$naechste_spielernr` block** referenced an undefined variable in the insert branch. | Runtime notice; incoming rows already carry their Passnummer. | Removed the block. |
| 4 | **Unconditional mass-deactivation.** A partial CSV deactivated every member not listed. | High — a broken/partial export could wipe an org's roster. | Per organisation, skip the deactivation sweep when the incoming count is below `sync_deactivation_min_ratio` (default 0.5) of the org's currently-active members; adds/updates still proceed and a warning is returned. |
| 5 | **Split clock for staging.** `session_id` came from PHP `date()` while stale-row cleanup used MySQL `NOW()`; a PHP/MySQL timezone gap could delete in-flight staging rows. | Medium — another silent 0-row path. | Derive `session_id` from the DB clock (`NOW()`), with `date()` fallback. |
| 6 | **No Passnummer format check.** The manual import UI enforces `^[0-9]{2}-[0-9]{4,6}$`; the sync receiver did not. | Medium — inconsistent data quality vs the manual flow. | Apply the same regex to `spielernr` / `spielernr_alt` (blank/reject on mismatch). |
## Confirmed by design (not bugs)
- **No contact / personal data** (email, phone, address) is ever exported or imported.
- Existing players keep their `lizenznr` and `geburtsjahr` on update; only name / sex / Passnummer-driven fields change.
- An unknown organisation aborts the whole import with no mutation.
- The sync path itself (export → cURL push → receive) is UTF-8 end-to-end; the encoding defect (#1) only affected ingesting a legacy latin1 *manual* file.