mirror of
https://github.com/Deutscher-Tischfussballbund/com_sportsmanager.git
synced 2026-09-11 18:41:50 +00:00
Compare commits
6
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6b68d8a5ed | ||
|
|
5843fda2d6 | ||
|
|
511c17468c | ||
|
|
6f33599fd9 | ||
|
|
aac4c1458f | ||
|
|
f39ade0e9d |
@@ -1,84 +0,0 @@
|
||||
# Builds the codebase and publishes a rolling dev-preview release (no version bump).
|
||||
# The release uses a fixed tag "dev-preview" so each run replaces the previous one.
|
||||
|
||||
name: Sportsmanager Dev Preview
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
branch:
|
||||
description: 'Branch to build'
|
||||
required: true
|
||||
default: 'sportsmanager2-prod'
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
ref: ${{ inputs.branch }}
|
||||
|
||||
- name: Set up Node.js
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 20
|
||||
|
||||
- name: Install npm dependencies
|
||||
run: npm ci
|
||||
|
||||
- name: Run build script
|
||||
run: npm run release
|
||||
|
||||
- name: Delete existing dev-preview release (if any)
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
REPO: ${{ github.repository }}
|
||||
run: |
|
||||
release_id=$(curl -s -H "Authorization: token $GITHUB_TOKEN" \
|
||||
"https://api.github.com/repos/$REPO/releases/tags/dev-preview" \
|
||||
| jq -r '.id // empty')
|
||||
|
||||
if [ -n "$release_id" ]; then
|
||||
curl -s -X DELETE -H "Authorization: token $GITHUB_TOKEN" \
|
||||
"https://api.github.com/repos/$REPO/releases/$release_id"
|
||||
echo "Deleted existing dev-preview release (ID: $release_id)"
|
||||
else
|
||||
echo "No existing dev-preview release found"
|
||||
fi
|
||||
|
||||
- name: Delete existing dev-preview tag (if any)
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
REPO: ${{ github.repository }}
|
||||
run: |
|
||||
status=$(curl -s -o /dev/null -w "%{http_code}" \
|
||||
-H "Authorization: token $GITHUB_TOKEN" \
|
||||
"https://api.github.com/repos/$REPO/git/refs/tags/dev-preview")
|
||||
|
||||
if [ "$status" = "200" ]; then
|
||||
curl -s -X DELETE -H "Authorization: token $GITHUB_TOKEN" \
|
||||
"https://api.github.com/repos/$REPO/git/refs/tags/dev-preview"
|
||||
echo "Deleted existing dev-preview tag"
|
||||
else
|
||||
echo "No existing dev-preview tag found"
|
||||
fi
|
||||
|
||||
- name: Publish dev-preview release
|
||||
uses: softprops/action-gh-release@v2
|
||||
with:
|
||||
tag_name: dev-preview
|
||||
name: "Dev Preview (${{ github.run_number }})"
|
||||
body: |
|
||||
Automated dev preview build from branch `${{ inputs.branch }}`.
|
||||
Commit: ${{ github.sha }}
|
||||
Run: ${{ github.run_number }}
|
||||
|
||||
> This is a temporary preview release and will be replaced on the next run.
|
||||
files: package/packages/com_sportsmanager.zip
|
||||
draft: false
|
||||
prerelease: true
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
@@ -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"
|
||||
Generated
+5
-19
@@ -52,6 +52,7 @@
|
||||
"resolved": "https://registry.npmjs.org/@babel/core/-/core-7.12.0.tgz",
|
||||
"integrity": "sha512-iV7Gwg0DePKvdDZZWRTkj4MW+6/AbVWd4ZCg+zk8H1RVt5xBpUZS6vLQWwb3pyLg4BFTaGiQCPoJ4Ibmbne4fA==",
|
||||
"dev": true,
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@babel/code-frame": "^7.10.4",
|
||||
"@babel/generator": "^7.12.0",
|
||||
@@ -2694,25 +2695,10 @@
|
||||
}
|
||||
},
|
||||
"node_modules/caniuse-lite": {
|
||||
"version": "1.0.30001810",
|
||||
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001810.tgz",
|
||||
"integrity": "sha512-TITQPUkaz+aVk5GL6NhOdwk1aEaNTSDPsGFWrTuhKGtjTF70jL/Oht2W4c6rXUe5fu7Ie19VIahAXHIIiWWNeg==",
|
||||
"dev": true,
|
||||
"funding": [
|
||||
{
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/browserslist"
|
||||
},
|
||||
{
|
||||
"type": "tidelift",
|
||||
"url": "https://tidelift.com/funding/github/npm/caniuse-lite"
|
||||
},
|
||||
{
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/ai"
|
||||
}
|
||||
],
|
||||
"license": "CC-BY-4.0"
|
||||
"version": "1.0.30001148",
|
||||
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001148.tgz",
|
||||
"integrity": "sha512-E66qcd0KMKZHNJQt9hiLZGE3J4zuTqE1OnU53miEVtylFbwOEmeA5OsRu90noZful+XGSQOni1aT2tiqu/9yYw==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/chalk": {
|
||||
"version": "1.1.3",
|
||||
|
||||
@@ -30,8 +30,6 @@ COM_SPORTSMANAGER_LAYOUT_GENERAL_CONTENT_OPTION_DESCRIPTION="Beschreibung"
|
||||
COM_SPORTSMANAGER_LAYOUT_GENERAL_CONTENT_OPTION_DESCRIPTION_DESC="Beschreibung, die unterhalb des Titels angezeigt wird (WICHTIG: Werden HTML-Tags verwendet, müssen auch Umlaute in HTML-Code angeben werden)"
|
||||
COM_SPORTSMANAGER_LAYOUT_GENERAL_CONTENT_OPTION_CATEGORIES="Kategorien"
|
||||
COM_SPORTSMANAGER_LAYOUT_GENERAL_CONTENT_OPTION_CATEGORIES_DESC="Eine optionale Auswahl von Kategorienummern durch Kommata oder Spiegelstrich getrennt"
|
||||
COM_SPORTSMANAGER_LAYOUT_GENERAL_CONTENT_OPTION_EXTRA_PARAMS="Zusätzliche Parameter"
|
||||
COM_SPORTSMANAGER_LAYOUT_GENERAL_CONTENT_OPTION_EXTRA_PARAMS_DESC="Optionale zusätzliche Parameter für den Link in der Form Name1=Wert1&Name2=Wert2, die bei Aufruf dieses Menüpunkts wie GET-Parameter zur Verfügung stehen"
|
||||
COM_SPORTSMANAGER_LAYOUT_ELO_RANKING_TITLE="Layout: Elo-Rangliste"
|
||||
COM_SPORTSMANAGER_LAYOUT_ELO_RANKING_DESC="Auflistung der Spieler sortiert nach Elo-Wertung"
|
||||
COM_SPORTSMANAGER_LAYOUT_ELO_RANKING_OPTION_ELO_RANKING="Elo-Rangliste"
|
||||
|
||||
@@ -30,8 +30,6 @@ COM_SPORTSMANAGER_LAYOUT_GENERAL_CONTENT_OPTION_DESCRIPTION="Description"
|
||||
COM_SPORTSMANAGER_LAYOUT_GENERAL_CONTENT_OPTION_DESCRIPTION_DESC="Description that will be shows below the titel (IMPORTANT: if html tags are used, special characters must be maskeraded)"
|
||||
COM_SPORTSMANAGER_LAYOUT_GENERAL_CONTENT_OPTION_CATEGORIES="Categories"
|
||||
COM_SPORTSMANAGER_LAYOUT_GENERAL_CONTENT_OPTION_CATEGORIES_DESC="An optional selection of category numbers seperated by commas or bullet point"
|
||||
COM_SPORTSMANAGER_LAYOUT_GENERAL_CONTENT_OPTION_EXTRA_PARAMS="Additional parameters"
|
||||
COM_SPORTSMANAGER_LAYOUT_GENERAL_CONTENT_OPTION_EXTRA_PARAMS_DESC="Optional additional parameters for the link in the form name1=value1&name2=value2, made available as GET parameters whenever this menu item is called"
|
||||
COM_SPORTSMANAGER_LAYOUT_ELO_RANKING_TITLE="Layout: elo ranking"
|
||||
COM_SPORTSMANAGER_LAYOUT_ELO_RANKING_DESC="Listing of players sorted by elo rating"
|
||||
COM_SPORTSMANAGER_LAYOUT_ELO_RANKING_OPTION_ELO_RANKING="Elo ranking"
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -5709,7 +5709,6 @@ function updateDatabase(): void
|
||||
}
|
||||
|
||||
if ($datenbank_version < 120) {
|
||||
|
||||
$columns = $db->getTableColumns('#__sportsmanager_teamspiel_modus');
|
||||
if (!array_key_exists('spiele_in_spielerstatistik', $columns)){
|
||||
$query = "ALTER TABLE `#__sportsmanager_teamspiel_modus`"
|
||||
@@ -5742,40 +5741,29 @@ function updateDatabase(): void
|
||||
}
|
||||
|
||||
if ($datenbank_version < 121) {
|
||||
|
||||
$columns = $db->getTableColumns('#__sportsmanager_mitglied_von_halloffame');
|
||||
if (!array_key_exists('teamspieler', $columns)){
|
||||
$query = "ALTER TABLE `#__sportsmanager_mitglied_von_halloffame`"
|
||||
. "\n ADD `teamspieler` TEXT NULL DEFAULT NULL AFTER `teamname`;";
|
||||
$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));
|
||||
}
|
||||
|
||||
if (!array_key_exists('nicht_ausgespielt', $columns)){
|
||||
$query = "ALTER TABLE `#__sportsmanager_mitglied_von_halloffame`"
|
||||
. "\n ADD `nicht_ausgespielt` TINYINT(1) NOT NULL DEFAULT '0' AFTER `jahr`;";
|
||||
$db->setQuery($query);
|
||||
if (!$db->execute()) {
|
||||
die($db->stderr(true));
|
||||
}
|
||||
}
|
||||
|
||||
$columns = $db->getTableColumns('#__sportsmanager_spielort');
|
||||
if (!array_key_exists('zusatzinfo', $columns)){
|
||||
$query = "ALTER TABLE `#__sportsmanager_spielort`"
|
||||
. "\n ADD `zusatzinfo` TEXT NULL DEFAULT NULL AFTER `ruhetage`;";
|
||||
$db->setQuery($query);
|
||||
if (!$db->execute()) {
|
||||
die($db->stderr(true));
|
||||
}
|
||||
}
|
||||
|
||||
$zielpfad = JPATH_ROOT . DIRECTORY_SEPARATOR . 'images' . DIRECTORY_SEPARATOR . 'sportsmanager' . DIRECTORY_SEPARATOR . 'spieler' . DIRECTORY_SEPARATOR . 'n.png';
|
||||
if (!is_file($zielpfad)) {
|
||||
$quellpfad = JPATH_SITE . DIRECTORY_SEPARATOR . 'components' . DIRECTORY_SEPARATOR . 'com_sportsmanager' . DIRECTORY_SEPARATOR . 'images' . DIRECTORY_SEPARATOR . 'spieler-n.png';
|
||||
bildKopierenAngepasst($quellpfad, $zielpfad, 180, 240, 1);
|
||||
$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"
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 1.2 KiB |
@@ -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();
|
||||
@@ -61,27 +62,8 @@ global $params;
|
||||
|
||||
$app = Factory::getContainer()->get(SiteApplication::class);
|
||||
$jInput = $app->input;
|
||||
$params = $app->getParams('com_sportsmanager');
|
||||
|
||||
// zusätzliche, im Menüpunkt hinterlegte Parameter (z.B. "test=test&foo=bar") wie GET-Parameter verfügbar machen;
|
||||
// tatsächlich in der URL übergebene Werte haben weiterhin Vorrang (def() setzt nur, wenn noch nicht vorhanden);
|
||||
// muss vor dem Auslesen von task/content erfolgen, damit diese die Zusatzparameter berücksichtigen
|
||||
$zusatzparameter = trim((string) $params->get('zusatzparameter', ''));
|
||||
if ($zusatzparameter !== '') {
|
||||
// Namen, die den gesamten Seitenaufbau umstellen würden, nicht über einen Menüparameter
|
||||
$gesperrteNamen = ['option', 'Itemid', 'format', 'tmpl', 'lang'];
|
||||
parse_str($zusatzparameter, $zusatzparameterWerte);
|
||||
foreach ($zusatzparameterWerte as $zusatzparameterName => $zusatzparameterWert) {
|
||||
// Arrays (z.B. "task[]=x") würden weiter unten zu einem TypeError führen
|
||||
if (!is_scalar($zusatzparameterWert)
|
||||
|| in_array($zusatzparameterName, $gesperrteNamen, true)) {
|
||||
continue;
|
||||
}
|
||||
$jInput->def($zusatzparameterName, $zusatzparameterWert);
|
||||
}
|
||||
}
|
||||
|
||||
$task = $jInput->getCmd('task');
|
||||
$params = $app->getParams('com_sportsmanager');
|
||||
$content = isJson() && $jInput->getCmd('content', NULL) != NULL ? $jInput->getCmd('content') : $params->get('content');
|
||||
|
||||
if (berechnungen())
|
||||
@@ -93,10 +75,10 @@ if ($task == "spielerbild") {
|
||||
terminDokument();
|
||||
} else if ($task == "spieler_details") {
|
||||
spielerDetails();
|
||||
} else if ($task == "team_details") {
|
||||
mannschaftDetails($content == "teams_vereinigt", $content != "teams" && $content != "teams_vereinigt");
|
||||
} else if ($task == "verein_details") {
|
||||
vereinDetails();
|
||||
} 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) {
|
||||
@@ -157,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;
|
||||
@@ -870,6 +855,9 @@ if ($task == "spielerbild") {
|
||||
case 'begegnung_spielplan':
|
||||
begegnungSpielplan(true);
|
||||
break;
|
||||
case 'team_details':
|
||||
mannschaftDetails($content == "teams_vereinigt");
|
||||
break;
|
||||
case 'team_spielplan_xml':
|
||||
teamSpielplanXML();
|
||||
case 'team_begegnungen_ical':
|
||||
@@ -880,6 +868,9 @@ if ($task == "spielerbild") {
|
||||
}
|
||||
} else if ($content == "vereine") {
|
||||
switch ($task) {
|
||||
case 'verein_details':
|
||||
vereinDetails();
|
||||
break;
|
||||
case 'verein_begegnungen_ical':
|
||||
vereinBegegnungeniCal();
|
||||
break;
|
||||
@@ -1163,7 +1154,7 @@ if ($task == "spielerbild") {
|
||||
|
||||
return;
|
||||
|
||||
#[NoReturn] function redirectSportsManagerURL($weitereParameter = NULL, $nachricht = '', $nachrichtTyp = 'message'): void
|
||||
#[NoReturn] function redirectSportsManagerURL($weitereParameter = NULL, $nachricht = ''): void
|
||||
{
|
||||
global $redirect_session_id;
|
||||
if (!empty($redirect_session_id)) {
|
||||
@@ -1173,7 +1164,7 @@ return;
|
||||
}
|
||||
$app = Factory::getContainer()->get(SiteApplication::class);
|
||||
if ($nachricht != '') {
|
||||
$app->enqueueMessage($nachricht, $nachrichtTyp);
|
||||
$app->enqueueMessage($nachricht);
|
||||
}
|
||||
$app->redirect(SportsManagerURL($weitereParameter), 200);
|
||||
exit;
|
||||
@@ -4467,34 +4458,11 @@ function halloffame(): void
|
||||
{
|
||||
$db = getDatabase();
|
||||
global $params;
|
||||
$jInput = Factory::getContainer()->get(SiteApplication::class)->input;
|
||||
|
||||
$gruppierung = $jInput->get('gruppierung', '', 'STRING');
|
||||
if ($gruppierung !== 'jahr' && $gruppierung !== 'summe' && $gruppierung !== 'summedisziplin')
|
||||
$gruppierung = '';
|
||||
|
||||
if ($gruppierung === 'jahr') {
|
||||
halloffameNachJahrGruppiert();
|
||||
return;
|
||||
}
|
||||
|
||||
if ($gruppierung === 'summe') {
|
||||
halloffameNachSummeGruppiert();
|
||||
return;
|
||||
}
|
||||
|
||||
if ($gruppierung === 'summedisziplin') {
|
||||
halloffameNachSummeDisziplinGruppiert();
|
||||
return;
|
||||
}
|
||||
|
||||
$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 WHERE 1=1"
|
||||
. halloffameIdFilter("AND t1.halloffame_id IN")
|
||||
. halloffameJahrFilter("AND t2.jahr IN")
|
||||
. "\n GROUP BY t1.halloffame_id"
|
||||
. kategorieFilter("HAVING t1.kategorie IN")
|
||||
. "\n ORDER BY t1.reihenfolge;";
|
||||
@@ -4506,363 +4474,11 @@ function halloffame(): void
|
||||
if (isJson()) {
|
||||
echo json_encode($halloffame);
|
||||
} else {
|
||||
HTML_sportsmanager::halloffame(halloffameTitel(), $params->get('beschreibung'), $halloffame);
|
||||
HTML_sportsmanager::halloffame($params->get('titel'), $params->get('beschreibung'), $halloffame);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function halloffameNachJahrGruppiert(): void
|
||||
{
|
||||
$db = getDatabase();
|
||||
global $params;
|
||||
$jInput = Factory::getContainer()->get(SiteApplication::class)->input;
|
||||
|
||||
$platzierung = $jInput->get('platzierung', '', 'STRING');
|
||||
if ($platzierung !== 'inspalten' && $platzierung !== 'inzeilen')
|
||||
$platzierung = '';
|
||||
|
||||
$plaetzezeigen = $jInput->get('plaetzezeigen', 0, 'INT');
|
||||
|
||||
$sortierung = $jInput->get('sortierung', 'jahrabwaerts', 'STRING');
|
||||
if ($sortierung !== 'jahraufwaerts')
|
||||
$sortierung = 'jahrabwaerts';
|
||||
$jahrSortierung = $sortierung === 'jahraufwaerts' ? 'ASC' : 'DESC';
|
||||
|
||||
$query = "SELECT t2.*, t1.halloffame, t1.spielform, t1.reihenfolge"
|
||||
. "\n FROM #__sportsmanager_halloffame t1"
|
||||
. "\n LEFT JOIN #__sportsmanager_mitglied_von_halloffame t2 ON t2.halloffame_id = t1.halloffame_id"
|
||||
. "\n WHERE t2.jahr IS NOT NULL"
|
||||
. kategorieFilter("AND t1.kategorie IN")
|
||||
. halloffameIdFilter("AND t1.halloffame_id IN")
|
||||
. halloffameJahrFilter("AND t2.jahr IN")
|
||||
. "\n ORDER BY t2.jahr $jahrSortierung, t1.reihenfolge, t2.platz ASC;";
|
||||
$rows = loadObjectList($db, $query);
|
||||
$alleTeams = halloffameAlleTeams($db);
|
||||
$alleSpielerInfo = halloffameAlleSpielerInfo($db);
|
||||
$alleVereineInfo = halloffameAlleVereineInfo($db);
|
||||
|
||||
$disziplinen = [];
|
||||
$jahresDaten = [];
|
||||
|
||||
foreach ($rows as $row) {
|
||||
if (!isset($disziplinen[$row->halloffame_id])) {
|
||||
$disziplinen[$row->halloffame_id] = (object) [
|
||||
'halloffame_id' => $row->halloffame_id,
|
||||
'halloffame' => $row->halloffame,
|
||||
'spielform' => $row->spielform,
|
||||
'reihenfolge' => $row->reihenfolge,
|
||||
'platz2_zeigen' => 0,
|
||||
'platz3_zeigen' => 0,
|
||||
'teamspieler_zeigen' => 0,
|
||||
'anzahl_m' => 0,
|
||||
'anzahl_w' => 0,
|
||||
];
|
||||
}
|
||||
$disziplin = $disziplinen[$row->halloffame_id];
|
||||
|
||||
if (!empty($row->nicht_ausgespielt)) continue;
|
||||
|
||||
if (!isset($jahresDaten[$row->jahr]))
|
||||
$jahresDaten[$row->jahr] = [];
|
||||
if (!isset($jahresDaten[$row->jahr][$row->halloffame_id]))
|
||||
$jahresDaten[$row->jahr][$row->halloffame_id] = new stdClass();
|
||||
$ziel = $jahresDaten[$row->jahr][$row->halloffame_id];
|
||||
|
||||
if ($disziplin->spielform == 1) {
|
||||
$index_vereinid = "verein_id_" . $row->platz;
|
||||
$index_teamid = "team_id_" . $row->platz;
|
||||
$index_team = "teamname_" . $row->platz;
|
||||
$index_teamspieler = "teamspieler_" . $row->platz;
|
||||
|
||||
$ziel->$index_vereinid = $row->verein_id;
|
||||
$ziel->$index_team = $row->teamname;
|
||||
$ziel->$index_teamspieler = $row->teamspieler;
|
||||
if (empty($row->verein_id)) {
|
||||
$ziel->$index_teamid = halloffameTeamIdFuerTeamname($alleTeams, $row->teamname);
|
||||
} else {
|
||||
$ziel->$index_teamid = "";
|
||||
}
|
||||
if ($row->platz == 2 && !empty($row->teamname))
|
||||
$disziplin->platz2_zeigen = 1;
|
||||
if ($row->platz == 3 && !empty($row->teamname))
|
||||
$disziplin->platz3_zeigen = 1;
|
||||
if (mb_strlen(trim((string) $row->teamspieler)) >= 3)
|
||||
$disziplin->teamspieler_zeigen = 1;
|
||||
} else {
|
||||
$index_spieler1id = "spieler1_id_" . $row->platz;
|
||||
$index_spieler1 = "spieler1_" . $row->platz;
|
||||
$index_spieler2id = "spieler2_id_" . $row->platz;
|
||||
$index_spieler2 = "spieler2_" . $row->platz;
|
||||
|
||||
$ziel->$index_spieler1id = $row->spieler1_id;
|
||||
$ziel->$index_spieler1 = $row->spieler1;
|
||||
$ziel->$index_spieler2id = $row->spieler2_id;
|
||||
$ziel->$index_spieler2 = $row->spieler2;
|
||||
if ($row->platz == 2 && (!empty($row->spieler1) || !empty($row->spieler2)))
|
||||
$disziplin->platz2_zeigen = 1;
|
||||
if ($row->platz == 3 && (!empty($row->spieler1) || !empty($row->spieler2)))
|
||||
$disziplin->platz3_zeigen = 1;
|
||||
halloffameGeschlechtZaehlen($disziplin, $alleSpielerInfo, $row->spieler1_id, $disziplin->spielform == 2 ? $row->spieler2_id : null);
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($disziplinen as $disziplin)
|
||||
$disziplin->geschaetztes_geschlecht = halloffameGeschaetztesGeschlecht($disziplin->anzahl_m, $disziplin->anzahl_w);
|
||||
|
||||
if ($plaetzezeigen > 0) {
|
||||
foreach ($disziplinen as $disziplin) {
|
||||
if ($plaetzezeigen < 2)
|
||||
$disziplin->platz2_zeigen = 0;
|
||||
if ($plaetzezeigen < 3)
|
||||
$disziplin->platz3_zeigen = 0;
|
||||
}
|
||||
}
|
||||
|
||||
usort($disziplinen, fn($a, $b) => $a->reihenfolge <=> $b->reihenfolge);
|
||||
|
||||
if (isJson()) {
|
||||
echo json_encode(['disziplinen' => $disziplinen, 'jahre' => $jahresDaten]);
|
||||
} else {
|
||||
HTML_sportsmanager::halloffameNachJahrGruppiert(halloffameTitel(), $params->get('beschreibung'), $disziplinen, $jahresDaten, $platzierung, $alleSpielerInfo, $alleVereineInfo);
|
||||
}
|
||||
}
|
||||
|
||||
function halloffameSummeTitelHinzufuegen(object $traegerEintrag, object $row): void
|
||||
{
|
||||
if (!isset($traegerEintrag->titel[$row->halloffame_id])) {
|
||||
$traegerEintrag->titel[$row->halloffame_id] = (object) [
|
||||
'halloffame' => $row->halloffame,
|
||||
'jahre' => [],
|
||||
];
|
||||
}
|
||||
$traegerEintrag->titel[$row->halloffame_id]->jahre[] = $row->jahr;
|
||||
}
|
||||
|
||||
function halloffameNachSummeGruppiert(): void
|
||||
{
|
||||
$db = getDatabase();
|
||||
global $params;
|
||||
$jInput = Factory::getContainer()->get(SiteApplication::class)->input;
|
||||
|
||||
$anzahltitel = $jInput->get('anzahltitel', 0, 'INT');
|
||||
|
||||
$sortierung = $jInput->get('sortierung', 'anzahl', 'STRING');
|
||||
if ($sortierung !== 'name')
|
||||
$sortierung = 'anzahl';
|
||||
|
||||
$query = "SELECT t2.*, t1.halloffame, t1.spielform, t1.reihenfolge"
|
||||
. "\n FROM #__sportsmanager_halloffame t1"
|
||||
. "\n LEFT JOIN #__sportsmanager_mitglied_von_halloffame t2 ON t2.halloffame_id = t1.halloffame_id"
|
||||
. "\n WHERE t2.jahr IS NOT NULL AND t2.platz = 1"
|
||||
. kategorieFilter("AND t1.kategorie IN")
|
||||
. halloffameIdFilter("AND t1.halloffame_id IN")
|
||||
. halloffameJahrFilter("AND t2.jahr IN")
|
||||
. "\n ORDER BY t1.reihenfolge, t2.jahr ASC;";
|
||||
$rows = loadObjectList($db, $query);
|
||||
$alleTeams = halloffameAlleTeams($db);
|
||||
$alleSpielerInfo = halloffameAlleSpielerInfo($db);
|
||||
$alleVereineInfo = halloffameAlleVereineInfo($db);
|
||||
|
||||
$traeger = [];
|
||||
|
||||
foreach ($rows as $row) {
|
||||
if (!empty($row->nicht_ausgespielt)) continue;
|
||||
|
||||
if ($row->spielform == 1) {
|
||||
$name = trim($row->teamname);
|
||||
if ($name === '') continue;
|
||||
|
||||
$key = 'team:' . $name;
|
||||
if (!isset($traeger[$key])) {
|
||||
if (empty($row->verein_id)) {
|
||||
$bild_type = 'mannschaften';
|
||||
$bild_id = halloffameTeamIdFuerTeamname($alleTeams, $row->teamname);
|
||||
} else {
|
||||
$bild_type = 'vereine';
|
||||
$bild_id = $row->verein_id;
|
||||
}
|
||||
$traeger[$key] = (object) [
|
||||
'name' => $name,
|
||||
'bild_type' => $bild_type,
|
||||
'bild_id' => $bild_id,
|
||||
'aktiv' => $bild_type === 'vereine' ? halloffameVereinAktiv($alleVereineInfo, $bild_id) : true,
|
||||
'geschlecht' => null,
|
||||
'titel' => [],
|
||||
];
|
||||
}
|
||||
halloffameSummeTitelHinzufuegen($traeger[$key], $row);
|
||||
} else {
|
||||
if (!empty($row->spieler1_id)) {
|
||||
$key = 'spieler:' . $row->spieler1_id;
|
||||
if (!isset($traeger[$key])) {
|
||||
$traeger[$key] = (object) [
|
||||
'name' => $row->spieler1,
|
||||
'bild_type' => 'spieler',
|
||||
'bild_id' => $row->spieler1_id,
|
||||
'aktiv' => halloffameSpielerAktiv($alleSpielerInfo, $row->spieler1_id),
|
||||
'geschlecht' => $alleSpielerInfo[$row->spieler1_id]->geschlecht ?? null,
|
||||
'titel' => [],
|
||||
];
|
||||
}
|
||||
halloffameSummeTitelHinzufuegen($traeger[$key], $row);
|
||||
}
|
||||
if ($row->spielform == 2 && !empty($row->spieler2_id)) {
|
||||
$key = 'spieler:' . $row->spieler2_id;
|
||||
if (!isset($traeger[$key])) {
|
||||
$traeger[$key] = (object) [
|
||||
'name' => $row->spieler2,
|
||||
'bild_type' => 'spieler',
|
||||
'bild_id' => $row->spieler2_id,
|
||||
'aktiv' => halloffameSpielerAktiv($alleSpielerInfo, $row->spieler2_id),
|
||||
'geschlecht' => $alleSpielerInfo[$row->spieler2_id]->geschlecht ?? null,
|
||||
'titel' => [],
|
||||
];
|
||||
}
|
||||
halloffameSummeTitelHinzufuegen($traeger[$key], $row);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($traeger as $t) {
|
||||
$t->anzahl = 0;
|
||||
foreach ($t->titel as $titel)
|
||||
$t->anzahl += count($titel->jahre);
|
||||
}
|
||||
|
||||
if ($anzahltitel > 0)
|
||||
$traeger = array_filter($traeger, fn($t) => $t->anzahl >= $anzahltitel);
|
||||
|
||||
if ($sortierung === 'name') {
|
||||
usort($traeger, fn($a, $b) => strcasecmp($a->name, $b->name));
|
||||
} else {
|
||||
usort($traeger, fn($a, $b) => ($b->anzahl <=> $a->anzahl) ?: strcasecmp($a->name, $b->name));
|
||||
}
|
||||
|
||||
if (isJson()) {
|
||||
echo json_encode($traeger);
|
||||
} else {
|
||||
HTML_sportsmanager::halloffameNachSummeGruppiert(halloffameTitel(), $params->get('beschreibung'), $traeger);
|
||||
}
|
||||
}
|
||||
|
||||
function halloffameNachSummeDisziplinGruppiert(): void
|
||||
{
|
||||
$db = getDatabase();
|
||||
global $params;
|
||||
$jInput = Factory::getContainer()->get(SiteApplication::class)->input;
|
||||
|
||||
$anzahltitel = $jInput->get('anzahltitel', 0, 'INT');
|
||||
|
||||
$sortierung = $jInput->get('sortierung', 'anzahl', 'STRING');
|
||||
if ($sortierung !== 'name')
|
||||
$sortierung = 'anzahl';
|
||||
|
||||
$query = "SELECT t2.*, t1.halloffame, t1.spielform, t1.reihenfolge"
|
||||
. "\n FROM #__sportsmanager_halloffame t1"
|
||||
. "\n LEFT JOIN #__sportsmanager_mitglied_von_halloffame t2 ON t2.halloffame_id = t1.halloffame_id"
|
||||
. "\n WHERE t2.jahr IS NOT NULL AND t2.platz = 1"
|
||||
. kategorieFilter("AND t1.kategorie IN")
|
||||
. halloffameIdFilter("AND t1.halloffame_id IN")
|
||||
. halloffameJahrFilter("AND t2.jahr IN")
|
||||
. "\n ORDER BY t1.reihenfolge, t2.jahr ASC;";
|
||||
$rows = loadObjectList($db, $query);
|
||||
$alleTeams = halloffameAlleTeams($db);
|
||||
$alleSpielerInfo = halloffameAlleSpielerInfo($db);
|
||||
$alleVereineInfo = halloffameAlleVereineInfo($db);
|
||||
|
||||
$disziplinen = [];
|
||||
|
||||
foreach ($rows as $row) {
|
||||
if (!isset($disziplinen[$row->halloffame_id])) {
|
||||
$disziplinen[$row->halloffame_id] = (object) [
|
||||
'halloffame_id' => $row->halloffame_id,
|
||||
'halloffame' => $row->halloffame,
|
||||
'reihenfolge' => $row->reihenfolge,
|
||||
'traeger' => [],
|
||||
];
|
||||
}
|
||||
$disziplin = $disziplinen[$row->halloffame_id];
|
||||
|
||||
if (!empty($row->nicht_ausgespielt)) continue;
|
||||
|
||||
if ($row->spielform == 1) {
|
||||
$name = trim($row->teamname);
|
||||
if ($name === '') continue;
|
||||
|
||||
$key = 'team:' . $name;
|
||||
if (!isset($disziplin->traeger[$key])) {
|
||||
if (empty($row->verein_id)) {
|
||||
$bild_type = 'mannschaften';
|
||||
$bild_id = halloffameTeamIdFuerTeamname($alleTeams, $row->teamname);
|
||||
} else {
|
||||
$bild_type = 'vereine';
|
||||
$bild_id = $row->verein_id;
|
||||
}
|
||||
$disziplin->traeger[$key] = (object) [
|
||||
'name' => $name,
|
||||
'bild_type' => $bild_type,
|
||||
'bild_id' => $bild_id,
|
||||
'aktiv' => $bild_type === 'vereine' ? halloffameVereinAktiv($alleVereineInfo, $bild_id) : true,
|
||||
'geschlecht' => null,
|
||||
'jahre' => [],
|
||||
];
|
||||
}
|
||||
$disziplin->traeger[$key]->jahre[] = $row->jahr;
|
||||
} else {
|
||||
if (!empty($row->spieler1_id)) {
|
||||
$key = 'spieler:' . $row->spieler1_id;
|
||||
if (!isset($disziplin->traeger[$key])) {
|
||||
$disziplin->traeger[$key] = (object) [
|
||||
'name' => $row->spieler1,
|
||||
'bild_type' => 'spieler',
|
||||
'bild_id' => $row->spieler1_id,
|
||||
'aktiv' => halloffameSpielerAktiv($alleSpielerInfo, $row->spieler1_id),
|
||||
'geschlecht' => $alleSpielerInfo[$row->spieler1_id]->geschlecht ?? null,
|
||||
'jahre' => [],
|
||||
];
|
||||
}
|
||||
$disziplin->traeger[$key]->jahre[] = $row->jahr;
|
||||
}
|
||||
if ($row->spielform == 2 && !empty($row->spieler2_id)) {
|
||||
$key = 'spieler:' . $row->spieler2_id;
|
||||
if (!isset($disziplin->traeger[$key])) {
|
||||
$disziplin->traeger[$key] = (object) [
|
||||
'name' => $row->spieler2,
|
||||
'bild_type' => 'spieler',
|
||||
'bild_id' => $row->spieler2_id,
|
||||
'aktiv' => halloffameSpielerAktiv($alleSpielerInfo, $row->spieler2_id),
|
||||
'geschlecht' => $alleSpielerInfo[$row->spieler2_id]->geschlecht ?? null,
|
||||
'jahre' => [],
|
||||
];
|
||||
}
|
||||
$disziplin->traeger[$key]->jahre[] = $row->jahr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($disziplinen as $disziplin) {
|
||||
foreach ($disziplin->traeger as $t)
|
||||
$t->anzahl = count($t->jahre);
|
||||
|
||||
if ($anzahltitel > 0)
|
||||
$disziplin->traeger = array_filter($disziplin->traeger, fn($t) => $t->anzahl >= $anzahltitel);
|
||||
|
||||
if ($sortierung === 'name') {
|
||||
usort($disziplin->traeger, fn($a, $b) => strcasecmp($a->name, $b->name));
|
||||
} else {
|
||||
usort($disziplin->traeger, fn($a, $b) => ($b->anzahl <=> $a->anzahl) ?: strcasecmp($a->name, $b->name));
|
||||
}
|
||||
}
|
||||
|
||||
usort($disziplinen, fn($a, $b) => $a->reihenfolge <=> $b->reihenfolge);
|
||||
|
||||
if (isJson()) {
|
||||
echo json_encode($disziplinen);
|
||||
} else {
|
||||
HTML_sportsmanager::halloffameNachSummeDisziplinGruppiert(halloffameTitel(), $params->get('beschreibung'), $disziplinen);
|
||||
}
|
||||
}
|
||||
|
||||
function halloffameDetails($uebergabe_id = 0): void
|
||||
{
|
||||
$db = getDatabase();
|
||||
@@ -4876,21 +4492,9 @@ function halloffameDetails($uebergabe_id = 0): void
|
||||
else
|
||||
$id = $uebergabe_id;
|
||||
|
||||
$platzierung = $jInput->get('platzierung', '', 'STRING');
|
||||
if ($platzierung !== 'inspalten' && $platzierung !== 'inzeilen')
|
||||
$platzierung = '';
|
||||
|
||||
$plaetzezeigen = $jInput->get('plaetzezeigen', 0, 'INT');
|
||||
|
||||
$sortierung = $jInput->get('sortierung', 'jahrabwaerts', 'STRING');
|
||||
if ($sortierung !== 'jahraufwaerts')
|
||||
$sortierung = 'jahrabwaerts';
|
||||
$jahrSortierung = $sortierung === 'jahraufwaerts' ? 'ASC' : 'DESC';
|
||||
|
||||
$mitglieder = null;
|
||||
|
||||
$query = "SELECT * FROM #__sportsmanager_halloffame WHERE halloffame_id = $id"
|
||||
. halloffameIdFilter("AND halloffame_id IN");
|
||||
$query = "SELECT * FROM #__sportsmanager_halloffame WHERE halloffame_id = $id";
|
||||
$rows = loadObjectList($db, $query);
|
||||
if (count($rows) < 1) {
|
||||
abortWithError("Wrong id!");
|
||||
@@ -4898,46 +4502,36 @@ function halloffameDetails($uebergabe_id = 0): void
|
||||
$halloffame = $rows[0];
|
||||
$halloffame->platz2_zeigen = 0;
|
||||
$halloffame->platz3_zeigen = 0;
|
||||
$halloffame->teamspieler_zeigen = 0;
|
||||
$halloffame->anzahl_m = 0;
|
||||
$halloffame->anzahl_w = 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"
|
||||
. halloffameJahrFilter("AND t2.jahr IN")
|
||||
. "\n ORDER BY t2.jahr $jahrSortierung, platz ASC;";
|
||||
. "\n ORDER BY t2.jahr DESC, platz ASC;";
|
||||
$rows = loadObjectList($db, $query);
|
||||
$alleSpielerInfo = halloffameAlleSpielerInfo($db);
|
||||
$alleVereineInfo = halloffameAlleVereineInfo($db);
|
||||
|
||||
if (count($rows) > 0){
|
||||
$mitglieder = [];
|
||||
if ($halloffame->spielform == 1){
|
||||
$alleTeams = halloffameAlleTeams($db);
|
||||
foreach ($rows as $row) {
|
||||
$index_vereinid = "verein_id_" . $row->platz;
|
||||
$index_teamid = "team_id_" . $row->platz;
|
||||
$index_team = "teamname_" . $row->platz;
|
||||
$index_teamspieler = "teamspieler_" . $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 = !empty($row->nicht_ausgespielt) ? Text::_('COM_SPORTSMANAGER_HALL_OF_FAME_NOT_HELD') : $row->teamname;
|
||||
$mitglieder[$row->jahr]->$index_teamspieler = $row->teamspieler;
|
||||
$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;
|
||||
if (mb_strlen(trim((string) $row->teamspieler)) >= 3)
|
||||
$halloffame->teamspieler_zeigen = 1;
|
||||
|
||||
//Suche team_id wenn keine verein_id vorhanden
|
||||
if (empty($row->verein_id)){
|
||||
$mitglieder[$row->jahr]->$index_teamid = halloffameTeamIdFuerTeamname($alleTeams, $row->teamname);
|
||||
$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 = "";
|
||||
}
|
||||
@@ -4955,31 +4549,21 @@ function halloffameDetails($uebergabe_id = 0): void
|
||||
}
|
||||
$mitglieder[$row->jahr]->jahr = $row->jahr;
|
||||
$mitglieder[$row->jahr]->$index_spieler1id = $row->spieler1_id;
|
||||
$mitglieder[$row->jahr]->$index_spieler1 = !empty($row->nicht_ausgespielt) ? Text::_('COM_SPORTSMANAGER_HALL_OF_FAME_NOT_HELD') : $row->spieler1;
|
||||
$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;
|
||||
halloffameGeschlechtZaehlen($halloffame, $alleSpielerInfo, $row->spieler1_id, $halloffame->spielform == 2 ? $row->spieler2_id : null);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$halloffame->geschaetztes_geschlecht = halloffameGeschaetztesGeschlecht($halloffame->anzahl_m, $halloffame->anzahl_w);
|
||||
|
||||
if ($plaetzezeigen > 0) {
|
||||
if ($plaetzezeigen < 2)
|
||||
$halloffame->platz2_zeigen = 0;
|
||||
if ($plaetzezeigen < 3)
|
||||
$halloffame->platz3_zeigen = 0;
|
||||
}
|
||||
|
||||
if (isJson()) {
|
||||
echo json_encode($mitglieder);
|
||||
} else {
|
||||
HTML_sportsmanager::halloffameDetails(halloffameTitel(), $params->get('beschreibung'), $mitglieder, $halloffame, $platzierung, $alleSpielerInfo, $alleVereineInfo);
|
||||
HTML_sportsmanager::halloffameDetails($params->get('titel'), $params->get('beschreibung'), $mitglieder, $halloffame);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -19,17 +19,11 @@ require_once JPATH_SITE . '/components/com_sportsmanager/database/init.php';
|
||||
|
||||
/** @noinspection PhpUnused */
|
||||
function mathParserVerteilung($rohpunkte, $platz, $teilnehmer, $multiplikator) {
|
||||
if ($teilnehmer <= 1){
|
||||
return 0;
|
||||
}
|
||||
return max(round($multiplikator * round(((($rohpunkte - 1) * (-log($platz / $teilnehmer) * (1 - ($platz / $teilnehmer)))) / (-log(1 / $teilnehmer) * (1 - (1 / $teilnehmer)))) + 1)), 1);
|
||||
}
|
||||
|
||||
/** @noinspection PhpUnused */
|
||||
function mathParserVerteilungR($rohpunkte, $platz, $teilnehmer, $multiplikator) {
|
||||
if ($teilnehmer <= 1){
|
||||
return 0;
|
||||
}
|
||||
return max(round(((($multiplikator * $rohpunkte - 1) * (-log($platz / $teilnehmer) * (1 - ($platz / $teilnehmer)))) / (-log(1 / $teilnehmer) * (1 - (1 / $teilnehmer)))) + 1), 1);
|
||||
}
|
||||
|
||||
@@ -232,217 +226,6 @@ function kategorieFilter($prefix, $suffix = ""): string
|
||||
return " $prefix (" . implode(", ", $filter) . ") $suffix";
|
||||
}
|
||||
|
||||
function idListeParameterFilter(string $parameterName, string $prefix, string $suffix = ""): string
|
||||
{
|
||||
$jInput = Factory::getContainer()->get(SiteApplication::class)->input;
|
||||
$werte = $jInput->get($parameterName, '', 'STRING');
|
||||
|
||||
$result = [];
|
||||
foreach (explode(",", $werte) as $item) {
|
||||
$item = trim($item);
|
||||
if ($item === '') continue;
|
||||
|
||||
$num = intval($item);
|
||||
if ($num > 0) {
|
||||
$result[$num] = true; // Duplikate vermeiden
|
||||
}
|
||||
}
|
||||
|
||||
if (empty($result)) {
|
||||
return "";
|
||||
}
|
||||
|
||||
$filter = array_keys($result);
|
||||
sort($filter, SORT_NUMERIC);
|
||||
|
||||
return " $prefix (" . implode(", ", $filter) . ") $suffix";
|
||||
}
|
||||
|
||||
function halloffameIdFilter($prefix): string
|
||||
{
|
||||
// Keine Bereichssuche bei der Halloffame-Id, nur einzelne Werte
|
||||
return idListeParameterFilter('halloffame_id', $prefix);
|
||||
}
|
||||
|
||||
function halloffameJahrFilter($prefix): string
|
||||
{
|
||||
$minJahr = 1900;
|
||||
$maxJahr = (int) date('Y') + 5;
|
||||
|
||||
$jInput = Factory::getContainer()->get(SiteApplication::class)->input;
|
||||
$werte = $jInput->get('jahr', '', 'STRING');
|
||||
|
||||
$result = [];
|
||||
foreach (explode(",", $werte) as $item) {
|
||||
$item = trim($item);
|
||||
if ($item === '') continue;
|
||||
|
||||
// Prüfen, ob es ein Bereich ist
|
||||
if (strpos($item, '-') !== false) {
|
||||
$rangeParts = explode('-', $item);
|
||||
|
||||
// genau 2 Teile für einen gültigen Bereich
|
||||
if (count($rangeParts) !== 2) continue;
|
||||
|
||||
$start = intval(trim($rangeParts[0]));
|
||||
$end = intval(trim($rangeParts[1]));
|
||||
|
||||
if ($start <= 0 || $end <= 0) continue;
|
||||
|
||||
// Werte vertauschen, falls der 2. Wert kleiner als der 1. ist
|
||||
if ($end < $start) {
|
||||
[$start, $end] = [$end, $start];
|
||||
}
|
||||
|
||||
// Bereich auf 1900 .. aktuelles Jahr+5 begrenzen
|
||||
$start = max($start, $minJahr);
|
||||
$end = min($end, $maxJahr);
|
||||
|
||||
if ($start > $end) continue;
|
||||
|
||||
for ($i = $start; $i <= $end; $i++) {
|
||||
$result[$i] = true; // Duplikate vermeiden
|
||||
}
|
||||
} else {
|
||||
$num = intval($item);
|
||||
if ($num <= 0) continue;
|
||||
|
||||
// Einzelwert auf 1900 .. aktuelles Jahr+5 begrenzen
|
||||
$num = max($minJahr, min($maxJahr, $num));
|
||||
$result[$num] = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (empty($result)) {
|
||||
return "";
|
||||
}
|
||||
|
||||
$filter = array_keys($result);
|
||||
sort($filter, SORT_NUMERIC);
|
||||
|
||||
return " $prefix (" . implode(", ", $filter) . ")";
|
||||
}
|
||||
|
||||
function halloffameAlleTeams($db): array
|
||||
{
|
||||
return loadObjectList($db, "SELECT team_id, teamname FROM #__sportsmanager_team ORDER BY team_id DESC");
|
||||
}
|
||||
|
||||
function halloffameTeamIdFuerTeamname(array $alleTeams, ?string $teamname)
|
||||
{
|
||||
if (empty($teamname))
|
||||
return null;
|
||||
foreach ($alleTeams as $team) {
|
||||
if (str_starts_with($team->teamname, $teamname))
|
||||
return $team->team_id;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
function halloffameAlleSpielerInfo($db): array
|
||||
{
|
||||
// "Aktiv" = hat einen aktuellen Verein; ohne aktuellen Verein liefert spieler_details ohnehin keine Daten
|
||||
$rows = loadObjectList($db, "SELECT spieler_id, geschlecht, aktueller_verein_id FROM #__sportsmanager_spieler");
|
||||
$result = [];
|
||||
foreach ($rows as $row) {
|
||||
$result[$row->spieler_id] = (object) [
|
||||
'geschlecht' => $row->geschlecht,
|
||||
'aktiv' => !empty($row->aktueller_verein_id),
|
||||
];
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
function halloffameSpielerAktiv(array $alleSpielerInfo, $spieler_id): bool
|
||||
{
|
||||
return empty($spieler_id) || !isset($alleSpielerInfo[$spieler_id]) || $alleSpielerInfo[$spieler_id]->aktiv;
|
||||
}
|
||||
|
||||
function halloffameSpielerBildId(array $alleSpielerInfo, $spieler_id)
|
||||
{
|
||||
return halloffameSpielerAktiv($alleSpielerInfo, $spieler_id) ? $spieler_id : '';
|
||||
}
|
||||
|
||||
function halloffameSpielerBildAlternativ(array $alleSpielerInfo, $spieler_id, string $geschaetztesGeschlecht = ''): string
|
||||
{
|
||||
if (!empty($spieler_id) && isset($alleSpielerInfo[$spieler_id]))
|
||||
return $alleSpielerInfo[$spieler_id]->geschlecht == 'M' ? 'm' : 'w';
|
||||
if ($geschaetztesGeschlecht === 'M')
|
||||
return 'm';
|
||||
if ($geschaetztesGeschlecht === 'W')
|
||||
return 'w';
|
||||
return 'n';
|
||||
}
|
||||
|
||||
function halloffameGeschlechtZaehlen(object $ziel, array $alleSpielerInfo, $spieler1_id, $spieler2_id): void
|
||||
{
|
||||
foreach ([$spieler1_id, $spieler2_id] as $id) {
|
||||
if (empty($id) || !isset($alleSpielerInfo[$id]))
|
||||
continue;
|
||||
if ($alleSpielerInfo[$id]->geschlecht == 'M')
|
||||
$ziel->anzahl_m++;
|
||||
else
|
||||
$ziel->anzahl_w++;
|
||||
}
|
||||
}
|
||||
|
||||
function halloffameGeschaetztesGeschlecht($anzahl_m, $anzahl_w): string
|
||||
{
|
||||
if ($anzahl_m > $anzahl_w)
|
||||
return 'M';
|
||||
if ($anzahl_w > $anzahl_m)
|
||||
return 'W';
|
||||
return '';
|
||||
}
|
||||
|
||||
function halloffameAlleVereineInfo($db): array
|
||||
{
|
||||
$rows = loadObjectList($db, "SELECT verein_id, ausgetreten FROM #__sportsmanager_verein");
|
||||
$result = [];
|
||||
foreach ($rows as $row) {
|
||||
$result[$row->verein_id] = (object) [
|
||||
'aktiv' => empty($row->ausgetreten),
|
||||
];
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
function halloffameVereinAktiv(array $alleVereineInfo, $verein_id): bool
|
||||
{
|
||||
return empty($verein_id) || !isset($alleVereineInfo[$verein_id]) || $alleVereineInfo[$verein_id]->aktiv;
|
||||
}
|
||||
|
||||
function halloffameNameLink(string $bild_type, $id, array $alleSpielerInfo, array $alleVereineInfo, string $nameHtml): string
|
||||
{
|
||||
if (empty($id))
|
||||
return $nameHtml;
|
||||
if ($bild_type === 'spieler' && !halloffameSpielerAktiv($alleSpielerInfo, $id))
|
||||
return $nameHtml;
|
||||
if ($bild_type === 'vereine' && !halloffameVereinAktiv($alleVereineInfo, $id))
|
||||
return $nameHtml;
|
||||
|
||||
$tasks = ['spieler' => 'spieler_details', 'vereine' => 'verein_details', 'mannschaften' => 'team_details'];
|
||||
if (!isset($tasks[$bild_type]))
|
||||
return $nameHtml;
|
||||
|
||||
return '<a href="' . SportsManagerURL('&task=' . $tasks[$bild_type] . '&id=' . $id) . '">' . $nameHtml . '</a>';
|
||||
}
|
||||
|
||||
function halloffameTitel(): string
|
||||
{
|
||||
global $params;
|
||||
|
||||
$titel = trim((string) $params->get('titel', ''));
|
||||
if ($titel !== '')
|
||||
return $titel;
|
||||
|
||||
$menu = Factory::getContainer()->get(SiteApplication::class)->getMenu()->getActive();
|
||||
if ($menu !== null && trim((string) $menu->title) !== '')
|
||||
return $menu->title;
|
||||
|
||||
return '';
|
||||
}
|
||||
|
||||
function turnierFilter($prefix): string
|
||||
{
|
||||
$user_id = isExternalDatabase() ? 0 : Factory::getContainer()->get(SiteApplication::class)->getIdentity()->id;
|
||||
|
||||
@@ -45,11 +45,6 @@
|
||||
default=""
|
||||
label="COM_SPORTSMANAGER_LAYOUT_GENERAL_CONTENT_OPTION_CATEGORIES"
|
||||
description="COM_SPORTSMANAGER_LAYOUT_GENERAL_CONTENT_OPTION_CATEGORIES_DESC" />
|
||||
<field name="zusatzparameter"
|
||||
type="text"
|
||||
default=""
|
||||
label="COM_SPORTSMANAGER_LAYOUT_GENERAL_CONTENT_OPTION_EXTRA_PARAMS"
|
||||
description="COM_SPORTSMANAGER_LAYOUT_GENERAL_CONTENT_OPTION_EXTRA_PARAMS_DESC" />
|
||||
</fieldset>
|
||||
</fields>
|
||||
</metadata>
|
||||
File diff suppressed because it is too large
Load Diff
@@ -982,6 +982,21 @@ class HTML_sportsmanager_admin
|
||||
?>"/>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td nowrap colspan="2">
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="font-weight:bold"><label for="dtfb_sync_url">DTFB Sync Einstellungen</label>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="text-align: right"><label for="dtfb_sync_url">Sync-URL</label></td>
|
||||
<td>
|
||||
<input name="dtfb_sync_url" id="dtfb_sync_url" type="text" size="60"
|
||||
value="<?php echo htmlspecialchars($einstellungen["dtfb_sync_url"] ?? '') ?>"/>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
</table>
|
||||
</div>
|
||||
@@ -1255,6 +1270,18 @@ class HTML_sportsmanager_admin
|
||||
href="<?php echo SportsManagerURL('&task=admin_spieler_remove_inaktive_form'); ?>"><?php echo Text::_('COM_SPORTSMANAGER_CLEANUP_INACTIVE_PLAYERS'); ?></a>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td nowrap style="padding-top: 10px;">
|
||||
<a href="<?php echo SportsManagerURL('&task=admin_spieler_sync_dtfb'); ?>"
|
||||
onclick="return confirm('Spielerdaten jetzt mit DTFB synchronisieren?');"
|
||||
class="uk-button uk-button-primary uk-button-small button" style="padding: 3px 10px; font-weight: bold; background: #007bc3; color: white; border-radius: 4px; border: none; text-decoration: none; display: inline-block;">
|
||||
🔄 Sync zu DTFB
|
||||
</a>
|
||||
</td>
|
||||
<td nowrap colspan="4" style="padding-top: 10px; vertical-align: middle;">
|
||||
<small style="margin-left: 10px;">Letzter Sync: <?php echo syncGetLastStatus(); ?></small>
|
||||
</td>
|
||||
</tr>
|
||||
<?php
|
||||
}
|
||||
?>
|
||||
@@ -4436,17 +4463,6 @@ class HTML_sportsmanager_admin
|
||||
value="<?php if ($row != null) echo htmlentities_utf8($row->ruhetage); ?>"/>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td nowrap style="width: 20%; text-align: right; vertical-align: top">
|
||||
<label
|
||||
for="zusatzinfo"><?php echo Text::_('COM_SPORTSMANAGER_ADDITIONAL_INFO'); ?>
|
||||
:</label>
|
||||
</td>
|
||||
<td nowrap>
|
||||
<textarea name="zusatzinfo" id="zusatzinfo" cols="60"
|
||||
rows="8"><?php if ($row != null) echo htmlentities_utf8($row->zusatzinfo); ?></textarea>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td nowrap style="width: 20%; text-align: right; vertical-align: top">
|
||||
<label
|
||||
@@ -8178,7 +8194,6 @@ static function adminVerbandsorganMitglieder($rows,$verbandsorgan): void
|
||||
|
||||
<?php
|
||||
if ($rows != null) {
|
||||
$einspaltigesDoppel = ($halloffame->spielform == 2 && !$halloffame->platz2_zeigen && !$halloffame->platz3_zeigen);
|
||||
?>
|
||||
<div class="uk-overflow-auto">
|
||||
<table style='border-collapse: collapse;'
|
||||
@@ -8194,7 +8209,7 @@ static function adminVerbandsorganMitglieder($rows,$verbandsorgan): void
|
||||
if ($i == 2 && !$halloffame->platz2_zeigen) continue;
|
||||
if ($i == 3 && !$halloffame->platz3_zeigen) continue;
|
||||
?>
|
||||
<th style="text-align:center;" colspan="<?php echo $einspaltigesDoppel ? 4 : 2; ?>" nowrap>
|
||||
<th style="text-align:center;" colspan="2" nowrap>
|
||||
<strong><?php echo Text::_('COM_SPORTSMANAGER_PLACE') . " " . $i; ?></strong>
|
||||
</th>
|
||||
<?php } ?>
|
||||
@@ -8210,42 +8225,7 @@ static function adminVerbandsorganMitglieder($rows,$verbandsorgan): void
|
||||
$rowclass = "sectiontableentry" . ($k + 1) . $params->get('pageclass_sfx');
|
||||
$k = ($k + 1) % 2;
|
||||
|
||||
if ($halloffame->spielform == 2 && $einspaltigesDoppel) {
|
||||
?>
|
||||
|
||||
<tr class="<?php echo $rowclass; ?>">
|
||||
|
||||
<td nowrap style="text-align:center;">
|
||||
<a href="<?php echo SportsManagerURL('&task=admin_halloffame_mitglied_edit&halloffame_id=' . $halloffame->halloffame_id . '&jahr=' . $row->jahr); ?>">
|
||||
<?php echo $row->jahr; ?>
|
||||
</a>
|
||||
</td>
|
||||
|
||||
<td nowrap style="text-align:center; width:70px;">
|
||||
<?php echo bildHTML("spieler", $row->spieler1_id_1, 45, 60, 0, 0, 'border="0"'); ?>
|
||||
</td>
|
||||
<td nowrap style="text-align:left; width:300px;">
|
||||
<?php echo htmlentities_utf8($row->spieler1_1); ?>
|
||||
</td>
|
||||
<td nowrap style="text-align:center; width:70px;">
|
||||
<?php echo bildHTML("spieler", $row->spieler2_id_1, 45, 60, 0, 0, 'border="0"'); ?>
|
||||
</td>
|
||||
<td nowrap style="text-align:left; width:300px;">
|
||||
<?php echo htmlentities_utf8($row->spieler2_1); ?>
|
||||
</td>
|
||||
|
||||
<td nowrap>
|
||||
<small>
|
||||
<a href="<?php echo SportsManagerURL('&task=admin_halloffame_mitglied_remove&halloffame_id=' . $halloffame->halloffame_id . '&jahr=' . $row->jahr); ?>"
|
||||
onclick="return confirm('<?php echo Text::_('COM_SPORTSMANAGER_REALLY_REMOVE_HALL_OF_FAME_YEAR'); ?>');"
|
||||
title="<?php echo Text::_('COM_SPORTSMANAGER_REMOVE'); ?>">
|
||||
X
|
||||
</a>
|
||||
</small>
|
||||
</td>
|
||||
</tr>
|
||||
<?php
|
||||
} else if ($halloffame->spielform == 2) {
|
||||
if ($halloffame->spielform == 2) {
|
||||
?>
|
||||
|
||||
<!-- Erste Zeile -->
|
||||
@@ -8357,58 +8337,11 @@ static function adminVerbandsorganMitglieder($rows,$verbandsorgan): void
|
||||
static function adminEditHalloffameMitglied($row,$halloffame,$vereine,$spieler,$jahre): void
|
||||
{
|
||||
global $params;
|
||||
|
||||
// Options nur einmal aufbauen (statt bis zu 6x je Formular) - bei grossen Spieler-/Vereinslisten deutlich schneller
|
||||
$vereineOptionsHtml = '';
|
||||
$spielerOptionsHtml = '';
|
||||
if ($halloffame->spielform == 1) {
|
||||
$vereineAktiv = array();
|
||||
$vereineInaktiv = array();
|
||||
foreach ($vereine as $v) {
|
||||
if (empty($v->ausgetreten))
|
||||
$vereineAktiv[] = $v;
|
||||
else
|
||||
$vereineInaktiv[] = $v;
|
||||
}
|
||||
$ausgetretenSuffix = " (" . Text::_('COM_SPORTSMANAGER_BEATEN') . ")";
|
||||
|
||||
$vereineOptionsHtml = '<option value="0"></option>';
|
||||
$vereineOptionsHtml .= '<optgroup label="' . htmlentities_utf8(Text::_('COM_SPORTSMANAGER_ACTIVE_CLUBS')) . '">';
|
||||
foreach ($vereineAktiv as $v)
|
||||
$vereineOptionsHtml .= '<option value="' . $v->verein_id . '">' . htmlentities_utf8($v->verein) . '</option>';
|
||||
$vereineOptionsHtml .= '</optgroup>';
|
||||
$vereineOptionsHtml .= '<optgroup label="' . htmlentities_utf8(Text::_('COM_SPORTSMANAGER_INACTIVE_CLUBS')) . '">';
|
||||
foreach ($vereineInaktiv as $v)
|
||||
$vereineOptionsHtml .= '<option value="' . $v->verein_id . '">' . htmlentities_utf8($v->verein . $ausgetretenSuffix) . '</option>';
|
||||
$vereineOptionsHtml .= '</optgroup>';
|
||||
} else {
|
||||
$spielerOptionsHtml = '<option value="0"></option>';
|
||||
foreach ($spieler as $s)
|
||||
$spielerOptionsHtml .= '<option value="' . $s->spieler_id . '">' . htmlentities_utf8($s->spieler) . '</option>';
|
||||
}
|
||||
?>
|
||||
<div
|
||||
class="componentheading<?php echo $params->get('pageclass_sfx'); ?>"><?php echo htmlentities_utf8($halloffame->halloffame); ?>
|
||||
: <?php echo($row != null ? Text::_('COM_SPORTSMANAGER_CHANGING') : Text::_('COM_SPORTSMANAGER_ADD')); ?></div>
|
||||
|
||||
<template id="halloffame-vereine-options"><?php echo $vereineOptionsHtml; ?></template>
|
||||
<template id="halloffame-spieler-options"><?php echo $spielerOptionsHtml; ?></template>
|
||||
|
||||
<script type="text/javascript">
|
||||
function halloffameNichtAusgespieltUmschalten(deaktiviert) {
|
||||
document.querySelectorAll('.halloffame-platz-zeile input, .halloffame-platz-zeile select, .halloffame-platz-zeile textarea').forEach(function (feld) {
|
||||
feld.disabled = deaktiviert;
|
||||
});
|
||||
}
|
||||
|
||||
function halloffameOptionenFuellen(selectId, templateId, wert) {
|
||||
var select = document.getElementById(selectId);
|
||||
var template = document.getElementById(templateId);
|
||||
select.innerHTML = template.innerHTML;
|
||||
if (wert) select.value = wert;
|
||||
}
|
||||
</script>
|
||||
|
||||
<form action="<?php echo SportsManagerURL(); ?>" method="post" name="adminForm" id="adminForm"
|
||||
enctype="multipart/form-data">
|
||||
<div class="uk-overflow-auto">
|
||||
@@ -8427,13 +8360,6 @@ static function adminVerbandsorganMitglieder($rows,$verbandsorgan): void
|
||||
}
|
||||
?>
|
||||
</select>
|
||||
|
||||
<label for="nicht_ausgespielt">
|
||||
<input type="checkbox" name="nicht_ausgespielt" id="nicht_ausgespielt" value="1"
|
||||
onclick="halloffameNichtAusgespieltUmschalten(this.checked);"
|
||||
<?php echo ($row != null && !empty($row->nicht_ausgespielt)) ? " checked" : ""; ?> />
|
||||
<?php echo Text::_('COM_SPORTSMANAGER_HALL_OF_FAME_NOT_HELD'); ?>
|
||||
</label>
|
||||
</td>
|
||||
</tr>
|
||||
<?php
|
||||
@@ -8441,35 +8367,26 @@ static function adminVerbandsorganMitglieder($rows,$verbandsorgan): void
|
||||
if ($halloffame->spielform == 1){
|
||||
$index_vereinid = "verein_id_" . $p;
|
||||
$index_team = "teamname_" . $p;
|
||||
$index_teamspieler = "teamspieler_" . $p;
|
||||
?>
|
||||
<tr class="halloffame-platz-zeile">
|
||||
<tr>
|
||||
<td nowrap style="width: 20%; text-align: right">
|
||||
<label for="verein1_<?php echo $p; ?>"><?php echo Text::_('COM_SPORTSMANAGER_PLACE') . " " . $p; ?>
|
||||
:</label>
|
||||
</td>
|
||||
<td nowrap>
|
||||
<select class="uk-select uk-form-width-large" name="verein_id_<?php echo $p; ?>"
|
||||
id="verein_id_<?php echo $p; ?>" size="1" style="width: 370px;"></select>
|
||||
<script type="text/javascript">
|
||||
halloffameOptionenFuellen('verein_id_<?php echo $p; ?>', 'halloffame-vereine-options', '<?php echo $row != null ? (int) $row->$index_vereinid : 0; ?>');
|
||||
</script>
|
||||
id="verein_id_<?php echo $p; ?>" size="1" style="width: 370px;">
|
||||
<?php
|
||||
echo "<option value=\"0\"></option>";
|
||||
foreach ($vereine as $v)
|
||||
echo "<option value=\"" . $v->verein_id . "\"" . ($row != null ? ($row->$index_vereinid == $v->verein_id ? " selected" : "") : "") . ">" . htmlentities_utf8($v->verein) . "</option>";
|
||||
?>
|
||||
</select>
|
||||
<input class="inputbox uk-select" type="text" style="width: 370px; height: 35px;"
|
||||
name="teamname_<?php echo $p; ?>" id="verein_<?php echo $p; ?>" size="50" maxlength="64"
|
||||
value="<?php echo $row != null ? htmlentities_utf8($row->$index_team) : ''; ?>"/>
|
||||
</td>
|
||||
</tr>
|
||||
<tr class="halloffame-platz-zeile">
|
||||
<td nowrap style="width: 20%; text-align: right">
|
||||
<label for="teamspieler_<?php echo $p; ?>"><?php echo Text::_('COM_SPORTSMANAGER_HALL_OF_FAME_TEAM_PLAYERS'); ?>
|
||||
:</label>
|
||||
</td>
|
||||
<td nowrap>
|
||||
<textarea class="uk-textarea" style="width: 740px; height: 70px;"
|
||||
name="teamspieler_<?php echo $p; ?>" id="teamspieler_<?php echo $p; ?>"
|
||||
><?php echo $row != null ? htmlentities_utf8($row->$index_teamspieler) : ''; ?></textarea>
|
||||
</td>
|
||||
</tr>
|
||||
<?php
|
||||
}
|
||||
|
||||
@@ -8479,27 +8396,33 @@ static function adminVerbandsorganMitglieder($rows,$verbandsorgan): void
|
||||
$index_spieler2id = "spieler2_id_" . $p;
|
||||
$index_spieler2 = "spieler2_" . $p;
|
||||
?>
|
||||
<tr class="halloffame-platz-zeile">
|
||||
<tr>
|
||||
<td nowrap style="width: 20%; text-align: right">
|
||||
<label for="player1_<?php echo $p; ?>"><?php echo Text::_('COM_SPORTSMANAGER_PLACE') . " " . $p; ?>
|
||||
:</label>
|
||||
</td>
|
||||
<td nowrap>
|
||||
<select class="uk-select uk-form-width-large" name="spieler1_id_<?php echo $p; ?>"
|
||||
id="player1_id_<?php echo $p; ?>" size="1" style="width: 370px;"></select>
|
||||
<script type="text/javascript">
|
||||
halloffameOptionenFuellen('player1_id_<?php echo $p; ?>', 'halloffame-spieler-options', '<?php echo $row != null ? (int) $row->$index_spieler1id : 0; ?>');
|
||||
</script>
|
||||
id="player1_id_<?php echo $p; ?>" size="1" style="width: 370px;">
|
||||
<?php
|
||||
echo "<option value=\"0\"></option>";
|
||||
foreach ($spieler as $s)
|
||||
echo "<option value=\"" . $s->spieler_id . "\"" . ($row != null ? ($row->$index_spieler1id == $s->spieler_id ? " selected" : "") : "") . ">" . htmlentities_utf8($s->spieler) . "</option>";
|
||||
?>
|
||||
</select>
|
||||
<input class="inputbox uk-select" type="text" style="width: 370px; height: 35px;"
|
||||
name="spieler1_<?php echo $p; ?>" id="player1_<?php echo $p; ?>" size="50" maxlength="64"
|
||||
value="<?php echo $row != null ? htmlentities_utf8($row->$index_spieler1) : ''; ?>"/>
|
||||
<?PHP if ($halloffame->spielform == 2){ ?>
|
||||
<br>
|
||||
<select class="uk-select uk-form-width-large" name="spieler2_id_<?php echo $p; ?>"
|
||||
id="player2_id_<?php echo $p; ?>" size="1" style="width: 370px;"></select>
|
||||
<script type="text/javascript">
|
||||
halloffameOptionenFuellen('player2_id_<?php echo $p; ?>', 'halloffame-spieler-options', '<?php echo $row != null ? (int) $row->$index_spieler2id : 0; ?>');
|
||||
</script>
|
||||
id="player2_id_<?php echo $p; ?>" size="1" style="width: 370px;">
|
||||
<?php
|
||||
echo "<option value=\"0\"></option>";
|
||||
foreach ($spieler as $s)
|
||||
echo "<option value=\"" . $s->spieler_id . "\"" . ($row != null ? ($row->$index_spieler2id == $s->spieler_id ? " selected" : "") : "") . ">" . htmlentities_utf8($s->spieler) . "</option>";
|
||||
?>
|
||||
</select>
|
||||
<input class="inputbox uk-select" type="text" style="width: 370px; height: 35px;"
|
||||
name="spieler2_<?php echo $p; ?>" id="player2_<?php echo $p; ?>" size="50" maxlength="64"
|
||||
value="<?php echo $row != null ? htmlentities_utf8($row->$index_spieler2) : ''; ?>"/>
|
||||
@@ -8522,9 +8445,6 @@ static function adminVerbandsorganMitglieder($rows,$verbandsorgan): void
|
||||
<input type="hidden" name="halloffame_id" value="<?php echo $halloffame->halloffame_id; ?>"/>
|
||||
<input type="hidden" name="spielform" value="<?php echo $halloffame->spielform; ?>"/>
|
||||
</form>
|
||||
<script type="text/javascript">
|
||||
halloffameNichtAusgespieltUmschalten(document.getElementById('nicht_ausgespielt').checked);
|
||||
</script>
|
||||
<?php
|
||||
}
|
||||
|
||||
@@ -11948,9 +11868,8 @@ static function adminVerbandsorganMitglieder($rows,$verbandsorgan): void
|
||||
$spiel_einzel = $spieltypen[$i - 1][0] != "D";
|
||||
if (($spiel_einzel && $spielpunkte_wertung_einzel == 2) || (!$spiel_einzel && $spielpunkte_wertung_doppel == 2))
|
||||
continue;
|
||||
// Number(): .value ist eine Zeichenkette, "10" > "9" wäre sonst unwahr
|
||||
echo " punkte_heim = document.adminForm.spiel_" . $i . "_heim_punkte.value != '' ? Number(document.adminForm.spiel_" . $i . "_heim_punkte.value) : 0;\n"
|
||||
. " punkte_gast = document.adminForm.spiel_" . $i . "_gast_punkte.value != '' ? Number(document.adminForm.spiel_" . $i . "_gast_punkte.value) : 0;\n"
|
||||
echo " punkte_heim = document.adminForm.spiel_" . $i . "_heim_punkte.value != '' ? document.adminForm.spiel_" . $i . "_heim_punkte.value : 0;\n"
|
||||
. " punkte_gast = document.adminForm.spiel_" . $i . "_gast_punkte.value != '' ? document.adminForm.spiel_" . $i . "_gast_punkte.value : 0;\n"
|
||||
. " if (punkte_heim > punkte_gast)\n"
|
||||
. " spielpunkte_heim += " . ($spiel_einzel ? "spielpunkte_sieg_einzel" : "spielpunkte_sieg_doppel") . ";\n"
|
||||
. " else if (punkte_heim < punkte_gast)\n"
|
||||
@@ -12950,12 +12869,6 @@ static function adminVerbandsorganMitglieder($rows,$verbandsorgan): void
|
||||
{
|
||||
global $params;
|
||||
?>
|
||||
<script type="text/javascript">
|
||||
function anzeige_aktualisieren() {
|
||||
document.getElementById("export_row").style.display = document.adminForm.exportformat.value !== "jsondatei" ? '' : 'none';
|
||||
}
|
||||
window.addEventListener("pageshow", anzeige_aktualisieren);
|
||||
</script>
|
||||
<form action="<?php echo SportsManagerURL(); ?>" method="post" name="adminForm" id="adminForm">
|
||||
<div class="uk-overflow-auto">
|
||||
<table style="width: 500px">
|
||||
@@ -12975,7 +12888,7 @@ static function adminVerbandsorganMitglieder($rows,$verbandsorgan): void
|
||||
</select>
|
||||
</td>
|
||||
</tr>
|
||||
<tr id="export_row">
|
||||
<tr>
|
||||
<td nowrap style="text-align: right">
|
||||
<label for="export">Exportieren</label>
|
||||
</td>
|
||||
@@ -12993,9 +12906,8 @@ static function adminVerbandsorganMitglieder($rows,$verbandsorgan): void
|
||||
<label for="exportformat">Exportformat</label>
|
||||
</td>
|
||||
<td nowrap>
|
||||
<select class="uk-select uk-form-width-large" style='width: 320px;' name="exportformat" id="exportformat" size="1" onchange="anzeige_aktualisieren();">
|
||||
<select class="uk-select uk-form-width-large" style='width: 320px;' name="exportformat" id="exportformat" size="1">
|
||||
<option value='csvdatei'>CSV-Datei</option>
|
||||
<option value='jsondatei'>JSON-Datei</option>
|
||||
</select>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
@@ -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">💻</span>
|
||||
<span id="theme-light" class="theme-btn" onclick="setTheme('light')" title="Light Theme">☀️</span>
|
||||
<span id="theme-dark" class="theme-btn" onclick="setTheme('dark')" title="Dark Theme">🌙</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
|
||||
}
|
||||
|
||||
|
||||
@@ -395,8 +395,6 @@ COM_SPORTSMANAGER_BEATEN="Ausgetreten"
|
||||
COM_SPORTSMANAGER_HIDE="Verstecken"
|
||||
COM_SPORTSMANAGER_PASSIVE="Passiv"
|
||||
COM_SPORTSMANAGER_BEATEN_CLUB="Verein ausgetreten"
|
||||
COM_SPORTSMANAGER_ACTIVE_CLUBS="Aktive Vereine"
|
||||
COM_SPORTSMANAGER_INACTIVE_CLUBS="Inaktive Vereine"
|
||||
COM_SPORTSMANAGER_SINGLE_SEED="Elo-Startwert Einzel"
|
||||
COM_SPORTSMANAGER_DOUBLE_SEED="Elo-Startwert Doppel"
|
||||
COM_SPORTSMANAGER_PLAYER_EXPORT="Spieler: Exportieren"
|
||||
@@ -1089,7 +1087,6 @@ COM_SPORTSMANAGER_MATCH_SWAPPING_HELP="Bei Heimrechttausch gleichen Termin eintr
|
||||
COM_SPORTSMANAGER_NOT_VALID_TIME="Ungültige Uhrzeit"
|
||||
COM_SPORTSMANAGER_REALLY_MATCH_RESCHEDULING="Willst Du diesen Spielverlegung wirklich entfernen?"
|
||||
COM_SPORTSMANAGER_REST_DAYS="Ruhetage"
|
||||
COM_SPORTSMANAGER_ADDITIONAL_INFO="Zusatzinfo"
|
||||
COM_SPORTSMANAGER_TRAINING_DAYS="Trainingstage"
|
||||
COM_SPORTSMANAGER_NOT_ACTUALIZED_DATA="Nicht aktualisierte Daten"
|
||||
COM_SPORTSMANAGER_ASSOCIATION_BODIES="Verbandsorgane"
|
||||
@@ -1110,10 +1107,6 @@ COM_SPORTSMANAGER_MATCH_TYPE="Spielform"
|
||||
COM_SPORTSMANAGER_REALLY_REMOVE_HALL_OF_FAME_YEAR="Willst Du wirklich dieses Hall of Fame Jahr löschen?"
|
||||
COM_SPORTSMANAGER_YEARS="Jahre"
|
||||
COM_SPORTSMANAGER_ADD_HALL_OF_FAME_YEAR="Hall of Fame Jahr hinzufügen"
|
||||
COM_SPORTSMANAGER_HALL_OF_FAME_TITLE_COUNT="Anzahl"
|
||||
COM_SPORTSMANAGER_HALL_OF_FAME_TITLES="Titel"
|
||||
COM_SPORTSMANAGER_HALL_OF_FAME_TEAM_PLAYERS="Mannschaftsspieler"
|
||||
COM_SPORTSMANAGER_HALL_OF_FAME_NOT_HELD="Nicht ausgespielt"
|
||||
COM_SPORTSMANAGER_NO_ENTRY="kein Eintrag"
|
||||
COM_SPORTSMANAGER_REALLY_SWAP_MATCH="Willst Du wirklich das Heimrecht tauschen?"
|
||||
COM_SPORTSMANAGER_SWAP_MATCH="Heimrechttausch"
|
||||
@@ -1122,6 +1115,4 @@ COM_SPORTSMANAGER_MATCH_REPORT_DELETED="Spielbericht gelöscht"
|
||||
COM_SPORTSMANAGER_MATCH_REPORT_WAS_DELETED="Der Spielbericht wurde erfolgreich gelöscht!"
|
||||
COM_SPORTSMANAGER_MATCH_REPORT_CORRECTED="Spielberichtskorrektur"
|
||||
COM_SPORTSMANAGER_MIN_MATCHES="Mindestzahl Spiele"
|
||||
COM_SPORTSMANAGER_SELECT_ALL="Alle"
|
||||
COM_SPORTSMANAGER_MAIL_SEND_ERROR="Die Benachrichtigungs-E-Mail zum Ergebnis konnte nicht versendet werden."
|
||||
COM_SPORTSMANAGER_TERMIN_MAIL_SEND_ERROR="Mindestens eine Benachrichtigungs-E-Mail zum Termin konnte nicht versendet werden."
|
||||
COM_SPORTSMANAGER_SELECT_ALL="Alle"
|
||||
@@ -395,8 +395,6 @@ COM_SPORTSMANAGER_BEATEN="Excreted"
|
||||
COM_SPORTSMANAGER_HIDE="Hide"
|
||||
COM_SPORTSMANAGER_PASSIVE="Passive"
|
||||
COM_SPORTSMANAGER_BEATEN_CLUB="Club excreted"
|
||||
COM_SPORTSMANAGER_ACTIVE_CLUBS="Active clubs"
|
||||
COM_SPORTSMANAGER_INACTIVE_CLUBS="Inactive clubs"
|
||||
COM_SPORTSMANAGER_SINGLE_SEED="Elo starting value singles"
|
||||
COM_SPORTSMANAGER_DOUBLE_SEED="Elo starting value doubles"
|
||||
COM_SPORTSMANAGER_PLAYER_EXPORT="Players: Export"
|
||||
@@ -1089,7 +1087,6 @@ COM_SPORTSMANAGER_MATCH_SWAPPING_HELP="If home advantage is swapped, enter the s
|
||||
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_ADDITIONAL_INFO="Additional info"
|
||||
COM_SPORTSMANAGER_TRAINING_DAYS="Training days"
|
||||
COM_SPORTSMANAGER_NOT_ACTUALIZED_DATA="Data not updated"
|
||||
COM_SPORTSMANAGER_ASSOCIATION_BODIES="Association bodies"
|
||||
@@ -1110,10 +1107,6 @@ 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_HALL_OF_FAME_TITLE_COUNT="Number"
|
||||
COM_SPORTSMANAGER_HALL_OF_FAME_TITLES="Titles"
|
||||
COM_SPORTSMANAGER_HALL_OF_FAME_TEAM_PLAYERS="Team Players"
|
||||
COM_SPORTSMANAGER_HALL_OF_FAME_NOT_HELD="Not held"
|
||||
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"
|
||||
@@ -1122,6 +1115,4 @@ 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"
|
||||
COM_SPORTSMANAGER_MAIL_SEND_ERROR="The notification e-mail for the result could not be sent."
|
||||
COM_SPORTSMANAGER_TERMIN_MAIL_SEND_ERROR="At least one notification e-mail for the appointment could not be sent."
|
||||
COM_SPORTSMANAGER_SELECT_ALL="All"
|
||||
@@ -94,12 +94,6 @@ return new class () implements InstallerScriptInterface
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!bildKopierenAngepasst(JPATH_ROOT.DIRECTORY_SEPARATOR.'components'.DIRECTORY_SEPARATOR.'com_sportsmanager'.DIRECTORY_SEPARATOR.'images'.DIRECTORY_SEPARATOR.'spieler-n.png', JPATH_ROOT.DIRECTORY_SEPARATOR.'images'.DIRECTORY_SEPARATOR.'sportsmanager'.DIRECTORY_SEPARATOR.'spieler'.DIRECTORY_SEPARATOR.'n.png', 180, 240, 1)) {
|
||||
Log::add('Image /components/com_sportsmanager/images/spieler-n.png could not be copied to /images/sportsmanager/spieler/n.png', Log::ERROR);
|
||||
echo '<p>Fehler: Bild spieler-n.png konnte nicht nach /images/sportsmanager/spieler/n.png verschoben werden</p>';
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
$adapter->getParent()->setRedirectURL('index.php?option=com_sportsmanager');
|
||||
return true;
|
||||
@@ -680,7 +674,6 @@ return new class () implements InstallerScriptInterface
|
||||
. "\n `telefon` varchar(64) DEFAULT NULL,"
|
||||
. "\n `email` varchar(64) DEFAULT NULL,"
|
||||
. "\n `ruhetage` varchar(64) DEFAULT NULL,"
|
||||
. "\n `zusatzinfo` text DEFAULT NULL,"
|
||||
. "\n `beschreibung` varchar(500) DEFAULT NULL,"
|
||||
. "\n `status` tinyint(1) NOT NULL DEFAULT '0',"
|
||||
. "\n PRIMARY KEY (`spielort_id`)"
|
||||
@@ -1258,11 +1251,9 @@ return new class () implements InstallerScriptInterface
|
||||
. "\n `mitglied_halloffame_id` int(11) NOT NULL AUTO_INCREMENT,"
|
||||
. "\n `halloffame_id` int(11) NOT NULL,"
|
||||
. "\n `jahr` int(4) DEFAULT NULL,"
|
||||
. "\n `nicht_ausgespielt` tinyint(1) NOT NULL DEFAULT '0',"
|
||||
. "\n `platz` int(11) DEFAULT NULL,"
|
||||
. "\n `verein_id` int(11) DEFAULT NULL,"
|
||||
. "\n `teamname` varchar(64) DEFAULT NULL,"
|
||||
. "\n `teamspieler` text DEFAULT NULL,"
|
||||
. "\n `spieler1_id` int(11) DEFAULT NULL,"
|
||||
. "\n `spieler1` varchar(64) DEFAULT NULL,"
|
||||
. "\n `spieler2_id` int(11) DEFAULT NULL,"
|
||||
@@ -1287,6 +1278,27 @@ return new class () implements InstallerScriptInterface
|
||||
$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)); }
|
||||
|
||||
$query = "INSERT IGNORE #__sportsmanager_einstellungen SET name = 'basis_spielernr', wert = '';";
|
||||
$db->setQuery( $query );
|
||||
if (!$db->execute()) { die($db->stderr(true)); }
|
||||
|
||||
@@ -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.
|
||||
Reference in New Issue
Block a user