Files
skyview.astronomiemuseum.de/public/my_favorites.php
T
2026-03-31 09:44:27 +02:00

669 lines
24 KiB
PHP

<?php
declare(strict_types=1);
ini_set('display_errors', '1');
error_reporting(E_ALL);
session_start();
if (!isset($_SESSION['user_id'])) {
header('Location: login.php');
exit;
}
$config = require __DIR__ . '/../config/database.php';
$dsn = sprintf(
'mysql:host=%s;dbname=%s;charset=%s',
$config['host'],
$config['dbname'],
$config['charset'] ?? 'utf8mb4'
);
$pdo = new PDO(
$dsn,
$config['user'],
$config['pass'],
[
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
]
);
$userId = (int) $_SESSION['user_id'];
$errors = [];
$successMessage = '';
$satelliteSearchResults = [];
$minorplanetSearchResults = [];
$satelliteSearchQuery = trim($_GET['sat_q'] ?? '');
$minorplanetSearchQuery = trim($_GET['mp_q'] ?? '');
$pageTitle = 'Favoriten - AstroTools';
$headerIntroPre = 'Merkliste';
$headerIntroTitle = 'Favoriten';
$headerIntroSub = 'Satelliten und Kleinplaneten zentral verwalten.';
function h(?string $value): string
{
return htmlspecialchars((string) $value, ENT_QUOTES, 'UTF-8');
}
function setExclusiveFavorite(PDO $pdo, string $tableName, string $idColumn, int $userId, int $entityId): bool
{
$pdo->beginTransaction();
try {
$stmtCurrent = $pdo->prepare("
SELECT `is_favorite`
FROM `{$tableName}`
WHERE `user_id` = :user_id
AND `{$idColumn}` = :entity_id
LIMIT 1
");
$stmtCurrent->execute([
':user_id' => $userId,
':entity_id' => $entityId,
]);
$current = $stmtCurrent->fetchColumn();
if ($current === false) {
$pdo->rollBack();
return false;
}
$newValue = (int) $current === 1 ? 0 : 1;
if ($newValue === 1) {
$stmtReset = $pdo->prepare("
UPDATE `{$tableName}`
SET `is_favorite` = 0
WHERE `user_id` = :user_id
");
$stmtReset->execute([
':user_id' => $userId,
]);
}
$stmtSet = $pdo->prepare("
UPDATE `{$tableName}`
SET `is_favorite` = :is_favorite
WHERE `user_id` = :user_id
AND `{$idColumn}` = :entity_id
");
$stmtSet->execute([
':is_favorite' => $newValue,
':user_id' => $userId,
':entity_id' => $entityId,
]);
$pdo->commit();
return true;
} catch (Throwable $e) {
if ($pdo->inTransaction()) {
$pdo->rollBack();
}
throw $e;
}
}
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$action = $_POST['action'] ?? '';
if ($action === 'add_satellite') {
$satelliteId = (int) ($_POST['satellite_id'] ?? 0);
if ($satelliteId <= 0) {
$errors[] = 'Ungueltiger Satellit.';
}
if (empty($errors)) {
$stmtCheck = $pdo->prepare("
SELECT `id`
FROM `sat_satellites`
WHERE `id` = :id
LIMIT 1
");
$stmtCheck->execute([
':id' => $satelliteId,
]);
if (!$stmtCheck->fetch()) {
$errors[] = 'Der Satellit wurde nicht gefunden.';
}
}
if (empty($errors)) {
$stmtInsert = $pdo->prepare("
INSERT INTO `app_user_satellites` (
`user_id`,
`satellite_id`,
`is_favorite`,
`created_at`
) VALUES (
:user_id,
:satellite_id,
0,
NOW()
)
ON DUPLICATE KEY UPDATE
`satellite_id` = VALUES(`satellite_id`)
");
$stmtInsert->execute([
':user_id' => $userId,
':satellite_id' => $satelliteId,
]);
$successMessage = 'Der Satellit wurde hinzugefuegt.';
}
}
if ($action === 'remove_satellite') {
$satelliteId = (int) ($_POST['satellite_id'] ?? 0);
if ($satelliteId <= 0) {
$errors[] = 'Ungueltiger Satellit.';
}
if (empty($errors)) {
$stmtDelete = $pdo->prepare("
DELETE FROM `app_user_satellites`
WHERE `user_id` = :user_id
AND `satellite_id` = :satellite_id
");
$stmtDelete->execute([
':user_id' => $userId,
':satellite_id' => $satelliteId,
]);
$successMessage = 'Der Satellit wurde entfernt.';
}
}
if ($action === 'toggle_satellite_favorite') {
$satelliteId = (int) ($_POST['satellite_id'] ?? 0);
if ($satelliteId <= 0) {
$errors[] = 'Ungueltiger Satellit.';
}
if (empty($errors)) {
if (!setExclusiveFavorite($pdo, 'app_user_satellites', 'satellite_id', $userId, $satelliteId)) {
$errors[] = 'Der Satellit ist nicht in deiner Liste vorhanden.';
} else {
$successMessage = 'Der Satelliten-Favorit wurde aktualisiert.';
}
}
}
if ($action === 'add_minorplanet') {
$minorplanetId = (int) ($_POST['minorplanet_id'] ?? 0);
if ($minorplanetId <= 0) {
$errors[] = 'Ungueltiger Kleinplanet.';
}
if (empty($errors)) {
$stmtCheck = $pdo->prepare("
SELECT `id`
FROM `minorplanets_mpc`
WHERE `id` = :id
LIMIT 1
");
$stmtCheck->execute([
':id' => $minorplanetId,
]);
if (!$stmtCheck->fetch()) {
$errors[] = 'Der Kleinplanet wurde nicht gefunden.';
}
}
if (empty($errors)) {
$stmtInsert = $pdo->prepare("
INSERT INTO `app_user_minorplanets` (
`user_id`,
`minorplanet_id`,
`is_favorite`,
`created_at`
) VALUES (
:user_id,
:minorplanet_id,
0,
NOW()
)
ON DUPLICATE KEY UPDATE
`minorplanet_id` = VALUES(`minorplanet_id`)
");
$stmtInsert->execute([
':user_id' => $userId,
':minorplanet_id' => $minorplanetId,
]);
$successMessage = 'Der Kleinplanet wurde hinzugefuegt.';
}
}
if ($action === 'remove_minorplanet') {
$minorplanetId = (int) ($_POST['minorplanet_id'] ?? 0);
if ($minorplanetId <= 0) {
$errors[] = 'Ungueltiger Kleinplanet.';
}
if (empty($errors)) {
$stmtDelete = $pdo->prepare("
DELETE FROM `app_user_minorplanets`
WHERE `user_id` = :user_id
AND `minorplanet_id` = :minorplanet_id
");
$stmtDelete->execute([
':user_id' => $userId,
':minorplanet_id' => $minorplanetId,
]);
$successMessage = 'Der Kleinplanet wurde entfernt.';
}
}
if ($action === 'toggle_minorplanet_favorite') {
$minorplanetId = (int) ($_POST['minorplanet_id'] ?? 0);
if ($minorplanetId <= 0) {
$errors[] = 'Ungueltiger Kleinplanet.';
}
if (empty($errors)) {
if (!setExclusiveFavorite($pdo, 'app_user_minorplanets', 'minorplanet_id', $userId, $minorplanetId)) {
$errors[] = 'Der Kleinplanet ist nicht in deiner Liste vorhanden.';
} else {
$successMessage = 'Der Kleinplaneten-Favorit wurde aktualisiert.';
}
}
}
}
if ($satelliteSearchQuery !== '') {
if (ctype_digit($satelliteSearchQuery)) {
$stmtSatelliteSearch = $pdo->prepare("
SELECT
s.`id`,
s.`norad_cat_id`,
s.`object_name`,
CASE
WHEN aus.`satellite_id` IS NULL THEN 0
ELSE 1
END AS `already_added`
FROM `sat_satellites` s
LEFT JOIN `app_user_satellites` aus
ON aus.`satellite_id` = s.`id`
AND aus.`user_id` = :user_id
WHERE s.`norad_cat_id` = :norad_cat_id
ORDER BY s.`object_name` ASC
LIMIT 50
");
$stmtSatelliteSearch->execute([
':user_id' => $userId,
':norad_cat_id' => (int) $satelliteSearchQuery,
]);
} else {
$stmtSatelliteSearch = $pdo->prepare("
SELECT
s.`id`,
s.`norad_cat_id`,
s.`object_name`,
CASE
WHEN aus.`satellite_id` IS NULL THEN 0
ELSE 1
END AS `already_added`
FROM `sat_satellites` s
LEFT JOIN `app_user_satellites` aus
ON aus.`satellite_id` = s.`id`
AND aus.`user_id` = :user_id
WHERE s.`object_name` LIKE :query
ORDER BY s.`object_name` ASC
LIMIT 50
");
$stmtSatelliteSearch->execute([
':user_id' => $userId,
':query' => '%' . $satelliteSearchQuery . '%',
]);
}
$satelliteSearchResults = $stmtSatelliteSearch->fetchAll();
}
if ($minorplanetSearchQuery !== '') {
if (ctype_digit($minorplanetSearchQuery)) {
$stmtMinorplanetSearch = $pdo->prepare("
SELECT
mp.`id`,
mp.`mp_number`,
mp.`designation_text`,
mp.`desig_packed`,
CASE
WHEN aum.`minorplanet_id` IS NULL THEN 0
ELSE 1
END AS `already_added`
FROM `minorplanets_mpc` mp
LEFT JOIN `app_user_minorplanets` aum
ON aum.`minorplanet_id` = mp.`id`
AND aum.`user_id` = :user_id
WHERE mp.`mp_number` = :mp_number
ORDER BY mp.`mp_number` ASC, mp.`designation_text` ASC
LIMIT 50
");
$stmtMinorplanetSearch->execute([
':user_id' => $userId,
':mp_number' => (int) $minorplanetSearchQuery,
]);
} else {
$stmtMinorplanetSearch = $pdo->prepare("
SELECT
mp.`id`,
mp.`mp_number`,
mp.`designation_text`,
mp.`desig_packed`,
CASE
WHEN aum.`minorplanet_id` IS NULL THEN 0
ELSE 1
END AS `already_added`
FROM `minorplanets_mpc` mp
LEFT JOIN `app_user_minorplanets` aum
ON aum.`minorplanet_id` = mp.`id`
AND aum.`user_id` = :user_id
WHERE mp.`designation_text` LIKE :query
OR mp.`desig_packed` LIKE :query
ORDER BY mp.`mp_number` IS NULL ASC, mp.`mp_number` ASC, mp.`designation_text` ASC
LIMIT 50
");
$stmtMinorplanetSearch->execute([
':user_id' => $userId,
':query' => '%' . $minorplanetSearchQuery . '%',
]);
}
$minorplanetSearchResults = $stmtMinorplanetSearch->fetchAll();
}
$stmtTrackedSatellites = $pdo->prepare("
SELECT
s.`id`,
s.`norad_cat_id`,
s.`object_name`,
aus.`is_favorite`,
aus.`created_at`
FROM `app_user_satellites` aus
INNER JOIN `sat_satellites` s
ON s.`id` = aus.`satellite_id`
WHERE aus.`user_id` = :user_id
ORDER BY aus.`is_favorite` DESC, s.`object_name` ASC
");
$stmtTrackedSatellites->execute([
':user_id' => $userId,
]);
$trackedSatellites = $stmtTrackedSatellites->fetchAll();
$stmtTrackedMinorplanets = $pdo->prepare("
SELECT
mp.`id`,
mp.`mp_number`,
mp.`designation_text`,
mp.`desig_packed`,
aum.`is_favorite`,
aum.`created_at`
FROM `app_user_minorplanets` aum
INNER JOIN `minorplanets_mpc` mp
ON mp.`id` = aum.`minorplanet_id`
WHERE aum.`user_id` = :user_id
ORDER BY aum.`is_favorite` DESC, mp.`mp_number` IS NULL ASC, mp.`mp_number` ASC, mp.`designation_text` ASC
");
$stmtTrackedMinorplanets->execute([
':user_id' => $userId,
]);
$trackedMinorplanets = $stmtTrackedMinorplanets->fetchAll();
$loggedIn = true;
?>
<?php require __DIR__ . '/header.php'; ?>
<?php if ($successMessage !== ''): ?>
<p class="msg-success"><?= h($successMessage) ?></p>
<?php endif; ?>
<?php if (!empty($errors)): ?>
<ul class="msg-error">
<?php foreach ($errors as $error): ?>
<li><?= h($error) ?></li>
<?php endforeach; ?>
</ul>
<?php endif; ?>
<div class="grid-2">
<div class="card">
<h2>Satelliten suchen</h2>
<form method="get" action="" class="favorites-loading-form" data-loading-message="Satelliten werden gesucht. Das kann kurz dauern...">
<div class="input-row">
<input
type="text"
id="sat_q"
name="sat_q"
value="<?= h($satelliteSearchQuery) ?>"
placeholder="Name oder NORAD-ID, z. B. ISS oder 25544"
>
<button type="submit" class="btn btn-primary">Suchen</button>
</div>
<p class="favorites-loading-hint" hidden>Satelliten werden gesucht. Das kann kurz dauern...</p>
</form>
<?php if ($satelliteSearchQuery !== ''): ?>
<div style="margin-top:1.5rem;">
<h2>Suchergebnisse</h2>
<?php if (empty($satelliteSearchResults)): ?>
<p class="hint">Keine passenden Satelliten gefunden.</p>
<?php else: ?>
<table class="data-table">
<thead>
<tr>
<th>Name</th>
<th>NORAD</th>
<th></th>
</tr>
</thead>
<tbody>
<?php foreach ($satelliteSearchResults as $satellite): ?>
<tr>
<td><?= h($satellite['object_name']) ?></td>
<td><?= h((string) $satellite['norad_cat_id']) ?></td>
<td>
<?php if ((int) $satellite['already_added'] === 1): ?>
<span class="hint">Bereits hinzugefuegt</span>
<?php else: ?>
<form method="post" action="" class="favorites-loading-form" data-loading-message="Satellit wird hinzugefuegt...">
<input type="hidden" name="action" value="add_satellite">
<input type="hidden" name="satellite_id" value="<?= (int) $satellite['id'] ?>">
<button type="submit" class="btn btn-sm btn-primary">Hinzufuegen</button>
</form>
<?php endif; ?>
</td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
<?php endif; ?>
</div>
<?php endif; ?>
</div>
<div class="card">
<h2>Kleinplaneten suchen</h2>
<form method="get" action="" class="favorites-loading-form" data-loading-message="Kleinplaneten werden gesucht. Das kann etwas dauern...">
<div class="input-row">
<input
type="text"
id="mp_q"
name="mp_q"
value="<?= h($minorplanetSearchQuery) ?>"
placeholder="Nummer oder Bezeichnung, z. B. 1 oder Ceres"
>
<button type="submit" class="btn btn-primary">Suchen</button>
</div>
<p class="favorites-loading-hint" hidden>Kleinplaneten werden gesucht. Das kann etwas dauern...</p>
</form>
<?php if ($minorplanetSearchQuery !== ''): ?>
<div style="margin-top:1.5rem;">
<h2>Suchergebnisse</h2>
<?php if (empty($minorplanetSearchResults)): ?>
<p class="hint">Keine passenden Kleinplaneten gefunden.</p>
<?php else: ?>
<table class="data-table">
<thead>
<tr>
<th>Bezeichnung</th>
<th>Nummer</th>
<th></th>
</tr>
</thead>
<tbody>
<?php foreach ($minorplanetSearchResults as $minorplanet): ?>
<tr>
<td><?= h($minorplanet['designation_text'] ?: $minorplanet['desig_packed']) ?></td>
<td><?= h($minorplanet['mp_number'] !== null ? (string) $minorplanet['mp_number'] : '—') ?></td>
<td>
<?php if ((int) $minorplanet['already_added'] === 1): ?>
<span class="hint">Bereits hinzugefuegt</span>
<?php else: ?>
<form method="post" action="" class="favorites-loading-form" data-loading-message="Kleinplanet wird hinzugefuegt...">
<input type="hidden" name="action" value="add_minorplanet">
<input type="hidden" name="minorplanet_id" value="<?= (int) $minorplanet['id'] ?>">
<button type="submit" class="btn btn-sm btn-primary">Hinzufuegen</button>
</form>
<?php endif; ?>
</td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
<?php endif; ?>
</div>
<?php endif; ?>
</div>
</div>
<div class="grid-2">
<div class="card">
<h2>Meine Satelliten</h2>
<?php if (empty($trackedSatellites)): ?>
<p class="hint">Du hast noch keine Satelliten ausgewaehlt.</p>
<?php else: ?>
<table class="data-table">
<thead>
<tr>
<th>Name</th>
<th>NORAD</th>
<th>Favorit</th>
<th>Hinzugefuegt</th>
<th></th>
</tr>
</thead>
<tbody>
<?php foreach ($trackedSatellites as $satellite): ?>
<tr>
<td><?= h($satellite['object_name']) ?></td>
<td><?= h((string) $satellite['norad_cat_id']) ?></td>
<td><?= (int) $satellite['is_favorite'] === 1 ? '*' : '-' ?></td>
<td><?= h($satellite['created_at']) ?></td>
<td class="action-cell">
<form method="post" action="" class="favorites-loading-form" data-loading-message="Satelliten-Favorit wird aktualisiert...">
<input type="hidden" name="action" value="toggle_satellite_favorite">
<input type="hidden" name="satellite_id" value="<?= (int) $satellite['id'] ?>">
<button type="submit" class="btn btn-sm btn-secondary">
<?= (int) $satellite['is_favorite'] === 1 ? 'Favorit entfernen' : 'Als Favorit' ?>
</button>
</form>
<form method="post" action="" class="favorites-loading-form" data-loading-message="Satellit wird entfernt..." onsubmit="return confirm('Satellit wirklich entfernen?');">
<input type="hidden" name="action" value="remove_satellite">
<input type="hidden" name="satellite_id" value="<?= (int) $satellite['id'] ?>">
<button type="submit" class="btn btn-sm btn-danger">Entfernen</button>
</form>
</td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
<?php endif; ?>
</div>
<div class="card">
<h2>Meine Kleinplaneten</h2>
<?php if (empty($trackedMinorplanets)): ?>
<p class="hint">Du hast noch keine Kleinplaneten ausgewaehlt.</p>
<?php else: ?>
<table class="data-table">
<thead>
<tr>
<th>Bezeichnung</th>
<th>Nummer</th>
<th>Favorit</th>
<th>Hinzugefuegt</th>
<th></th>
</tr>
</thead>
<tbody>
<?php foreach ($trackedMinorplanets as $minorplanet): ?>
<tr>
<td><?= h($minorplanet['designation_text'] ?: $minorplanet['desig_packed']) ?></td>
<td><?= h($minorplanet['mp_number'] !== null ? (string) $minorplanet['mp_number'] : '-') ?></td>
<td><?= (int) $minorplanet['is_favorite'] === 1 ? '*' : '-' ?></td>
<td><?= h($minorplanet['created_at']) ?></td>
<td class="action-cell">
<form method="post" action="" class="favorites-loading-form" data-loading-message="Kleinplaneten-Favorit wird aktualisiert...">
<input type="hidden" name="action" value="toggle_minorplanet_favorite">
<input type="hidden" name="minorplanet_id" value="<?= (int) $minorplanet['id'] ?>">
<button type="submit" class="btn btn-sm btn-secondary">
<?= (int) $minorplanet['is_favorite'] === 1 ? 'Favorit entfernen' : 'Als Favorit' ?>
</button>
</form>
<form method="post" action="" class="favorites-loading-form" data-loading-message="Kleinplanet wird entfernt..." onsubmit="return confirm('Kleinplanet wirklich entfernen?');">
<input type="hidden" name="action" value="remove_minorplanet">
<input type="hidden" name="minorplanet_id" value="<?= (int) $minorplanet['id'] ?>">
<button type="submit" class="btn btn-sm btn-danger">Entfernen</button>
</form>
</td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
<?php endif; ?>
</div>
</div>
<script>
document.querySelectorAll('.favorites-loading-form').forEach((form) => {
form.addEventListener('submit', () => {
const message = form.dataset.loadingMessage || 'Bitte warten...';
let hint = form.querySelector('.favorites-loading-hint');
if (!hint) {
hint = document.createElement('p');
hint.className = 'favorites-loading-hint';
form.appendChild(hint);
}
hint.textContent = message;
hint.hidden = false;
form.classList.add('is-loading');
form.querySelectorAll('button, input, select, textarea').forEach((field) => {
if (field.tagName === 'INPUT' && field.type === 'hidden') {
return;
}
field.setAttribute('aria-busy', 'true');
if (field.tagName === 'BUTTON') {
field.disabled = true;
}
});
});
});
</script>
<?php require __DIR__ . '/footer.php'; ?>