schreiben jetzt per Ajax
This commit is contained in:
@@ -0,0 +1,140 @@
|
|||||||
|
<?php
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
session_start();
|
||||||
|
|
||||||
|
header('Content-Type: application/json; charset=UTF-8');
|
||||||
|
|
||||||
|
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
|
||||||
|
http_response_code(405);
|
||||||
|
echo json_encode([
|
||||||
|
'success' => false,
|
||||||
|
'message' => 'Nur POST ist erlaubt.',
|
||||||
|
], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
$sessionToken = isset($_SESSION['observation_tip_insert_token']) && is_string($_SESSION['observation_tip_insert_token'])
|
||||||
|
? $_SESSION['observation_tip_insert_token']
|
||||||
|
: '';
|
||||||
|
$submittedToken = isset($_POST['observation_tip_insert_token']) ? (string) $_POST['observation_tip_insert_token'] : '';
|
||||||
|
|
||||||
|
if ($sessionToken === '' || $submittedToken === '' || !hash_equals($sessionToken, $submittedToken)) {
|
||||||
|
http_response_code(403);
|
||||||
|
echo json_encode([
|
||||||
|
'success' => false,
|
||||||
|
'message' => 'Sicherheitspruefung fehlgeschlagen. Bitte die Seite neu laden und erneut versuchen.',
|
||||||
|
], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
$payloadJson = isset($_POST['observation_tip_payload']) ? (string) $_POST['observation_tip_payload'] : '';
|
||||||
|
$rows = json_decode($payloadJson, true);
|
||||||
|
if (!is_array($rows)) {
|
||||||
|
http_response_code(400);
|
||||||
|
echo json_encode([
|
||||||
|
'success' => false,
|
||||||
|
'message' => 'Ungueltige Ereignisdaten empfangen.',
|
||||||
|
], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
$normalized = [];
|
||||||
|
foreach ($rows as $row) {
|
||||||
|
if (!is_array($row)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$moment = trim((string) ($row['moment'] ?? ''));
|
||||||
|
$tip = trim((string) ($row['tip'] ?? ''));
|
||||||
|
if ($moment === '' || $tip === '') {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$normalized[] = [
|
||||||
|
'moment' => $moment,
|
||||||
|
'tip' => $tip,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($normalized === []) {
|
||||||
|
echo json_encode([
|
||||||
|
'success' => false,
|
||||||
|
'message' => 'Keine gueltigen Ereignisse zum Eintragen vorhanden.',
|
||||||
|
'inserted' => 0,
|
||||||
|
'skipped' => 0,
|
||||||
|
], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
$dbConfig = require __DIR__ . '/../../config/observationtips_database.php';
|
||||||
|
$dsn = sprintf(
|
||||||
|
'mysql:host=%s;dbname=%s;charset=%s',
|
||||||
|
$dbConfig['host'],
|
||||||
|
$dbConfig['dbname'],
|
||||||
|
$dbConfig['charset'] ?? 'utf8mb4'
|
||||||
|
);
|
||||||
|
$pdo = new PDO(
|
||||||
|
$dsn,
|
||||||
|
$dbConfig['user'],
|
||||||
|
$dbConfig['pass'],
|
||||||
|
[
|
||||||
|
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
|
||||||
|
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
|
||||||
|
]
|
||||||
|
);
|
||||||
|
|
||||||
|
$checkStmt = $pdo->prepare(
|
||||||
|
'SELECT 1 FROM `astro_sonobs_observationtips` WHERE `moment` = :moment AND `tip` = :tip LIMIT 1'
|
||||||
|
);
|
||||||
|
$insertStmt = $pdo->prepare(
|
||||||
|
'INSERT INTO `astro_sonobs_observationtips` (`moment`, `tip`) VALUES (:moment, :tip)'
|
||||||
|
);
|
||||||
|
|
||||||
|
$inserted = 0;
|
||||||
|
$skipped = 0;
|
||||||
|
|
||||||
|
$pdo->beginTransaction();
|
||||||
|
try {
|
||||||
|
foreach ($normalized as $row) {
|
||||||
|
$checkStmt->execute([
|
||||||
|
':moment' => $row['moment'],
|
||||||
|
':tip' => $row['tip'],
|
||||||
|
]);
|
||||||
|
|
||||||
|
if ($checkStmt->fetchColumn()) {
|
||||||
|
$skipped++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$insertStmt->execute([
|
||||||
|
':moment' => $row['moment'],
|
||||||
|
':tip' => $row['tip'],
|
||||||
|
]);
|
||||||
|
$inserted++;
|
||||||
|
}
|
||||||
|
|
||||||
|
$pdo->commit();
|
||||||
|
} catch (Throwable $e) {
|
||||||
|
if ($pdo->inTransaction()) {
|
||||||
|
$pdo->rollBack();
|
||||||
|
}
|
||||||
|
throw $e;
|
||||||
|
}
|
||||||
|
|
||||||
|
echo json_encode([
|
||||||
|
'success' => true,
|
||||||
|
'message' => sprintf('%d Ereignisse eingetragen, %d bereits vorhanden.', $inserted, $skipped),
|
||||||
|
'inserted' => $inserted,
|
||||||
|
'skipped' => $skipped,
|
||||||
|
], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
|
||||||
|
} catch (Throwable $e) {
|
||||||
|
http_response_code(500);
|
||||||
|
echo json_encode([
|
||||||
|
'success' => false,
|
||||||
|
'message' => 'Eintragen in `astro_sonobs_observationtips` fehlgeschlagen: ' . $e->getMessage(),
|
||||||
|
'inserted' => 0,
|
||||||
|
'skipped' => 0,
|
||||||
|
], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
|
||||||
|
}
|
||||||
+90
-114
@@ -257,11 +257,12 @@ $favoriteCometEvents = [];
|
|||||||
$seasonChanges = [];
|
$seasonChanges = [];
|
||||||
$timeChanges = [];
|
$timeChanges = [];
|
||||||
$monthEventList = [];
|
$monthEventList = [];
|
||||||
$observationTipInsertResult = null;
|
|
||||||
$starDataError = null;
|
$starDataError = null;
|
||||||
$monthNarrativeText = '';
|
$monthNarrativeText = '';
|
||||||
$observationTipInsertSql = '';
|
$observationTipInsertSql = '';
|
||||||
$observationTipCalendarIcs = '';
|
$observationTipCalendarIcs = '';
|
||||||
|
$observationTipInsertPayloadJson = '[]';
|
||||||
|
$observationTipInsertEndpoint = $publicBasePath . 'ajax/insert_observationtips.php';
|
||||||
$chartConfig = null;
|
$chartConfig = null;
|
||||||
$chartConfigJson = 'null';
|
$chartConfigJson = 'null';
|
||||||
$starDataJson = '[]';
|
$starDataJson = '[]';
|
||||||
@@ -635,62 +636,6 @@ if (!function_exists('normalizeObservationTipEvents')) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!function_exists('insertObservationTipEvents')) {
|
|
||||||
function insertObservationTipEvents(PDO $pdo, array $events, DateTimeZone $timezone): array
|
|
||||||
{
|
|
||||||
$normalized = normalizeObservationTipEvents($events, $timezone);
|
|
||||||
if ($normalized === []) {
|
|
||||||
return [
|
|
||||||
'inserted' => 0,
|
|
||||||
'skipped' => 0,
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
$checkStmt = $pdo->prepare(
|
|
||||||
'SELECT 1 FROM `astro_sonobs_observationtips` WHERE `moment` = :moment AND `tip` = :tip LIMIT 1'
|
|
||||||
);
|
|
||||||
$insertStmt = $pdo->prepare(
|
|
||||||
'INSERT INTO `astro_sonobs_observationtips` (`moment`, `tip`) VALUES (:moment, :tip)'
|
|
||||||
);
|
|
||||||
|
|
||||||
$inserted = 0;
|
|
||||||
$skipped = 0;
|
|
||||||
|
|
||||||
$pdo->beginTransaction();
|
|
||||||
try {
|
|
||||||
foreach ($normalized as $row) {
|
|
||||||
$checkStmt->execute([
|
|
||||||
':moment' => $row['moment'],
|
|
||||||
':tip' => $row['tip'],
|
|
||||||
]);
|
|
||||||
|
|
||||||
if ($checkStmt->fetchColumn()) {
|
|
||||||
$skipped++;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
$insertStmt->execute([
|
|
||||||
':moment' => $row['moment'],
|
|
||||||
':tip' => $row['tip'],
|
|
||||||
]);
|
|
||||||
$inserted++;
|
|
||||||
}
|
|
||||||
|
|
||||||
$pdo->commit();
|
|
||||||
} catch (Throwable $e) {
|
|
||||||
if ($pdo->inTransaction()) {
|
|
||||||
$pdo->rollBack();
|
|
||||||
}
|
|
||||||
throw $e;
|
|
||||||
}
|
|
||||||
|
|
||||||
return [
|
|
||||||
'inserted' => $inserted,
|
|
||||||
'skipped' => $skipped,
|
|
||||||
];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!function_exists('escapeIcalText')) {
|
if (!function_exists('escapeIcalText')) {
|
||||||
function escapeIcalText(string $value): string
|
function escapeIcalText(string $value): string
|
||||||
{
|
{
|
||||||
@@ -2490,56 +2435,10 @@ $monthNarrativeText = monthForecastBuildNarrativeText($monthEventList, $selected
|
|||||||
|
|
||||||
$observationTipInsertSql = buildObservationTipInsertSql($monthEventList, new DateTimeZone($chartTimezone));
|
$observationTipInsertSql = buildObservationTipInsertSql($monthEventList, new DateTimeZone($chartTimezone));
|
||||||
$observationTipCalendarIcs = buildObservationTipCalendarIcs($monthEventList, new DateTimeZone($chartTimezone));
|
$observationTipCalendarIcs = buildObservationTipCalendarIcs($monthEventList, new DateTimeZone($chartTimezone));
|
||||||
|
$observationTipInsertPayloadJson = json_encode(
|
||||||
if (
|
normalizeObservationTipEvents($monthEventList, new DateTimeZone($chartTimezone)),
|
||||||
$_SERVER['REQUEST_METHOD'] === 'POST'
|
JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES
|
||||||
&& isset($_POST['insert_observation_tips'])
|
) ?: '[]';
|
||||||
&& $_POST['insert_observation_tips'] === '1'
|
|
||||||
) {
|
|
||||||
$submittedToken = isset($_POST['observation_tip_insert_token']) ? (string) $_POST['observation_tip_insert_token'] : '';
|
|
||||||
if (!hash_equals($observationTipInsertToken, $submittedToken)) {
|
|
||||||
$observationTipInsertResult = [
|
|
||||||
'success' => false,
|
|
||||||
'message' => 'Sicherheitspruefung fehlgeschlagen. Bitte die Seite neu laden und erneut versuchen.',
|
|
||||||
];
|
|
||||||
} else {
|
|
||||||
try {
|
|
||||||
$observationTipDbConfig = require __DIR__ . '/../config/observationtips_database.php';
|
|
||||||
$observationTipDsn = sprintf(
|
|
||||||
'mysql:host=%s;dbname=%s;charset=%s',
|
|
||||||
$observationTipDbConfig['host'],
|
|
||||||
$observationTipDbConfig['dbname'],
|
|
||||||
$observationTipDbConfig['charset'] ?? 'utf8mb4'
|
|
||||||
);
|
|
||||||
$observationTipPdo = new PDO(
|
|
||||||
$observationTipDsn,
|
|
||||||
$observationTipDbConfig['user'],
|
|
||||||
$observationTipDbConfig['pass'],
|
|
||||||
[
|
|
||||||
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
|
|
||||||
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
|
|
||||||
]
|
|
||||||
);
|
|
||||||
|
|
||||||
$observationTipInsertResult = insertObservationTipEvents(
|
|
||||||
$observationTipPdo,
|
|
||||||
$monthEventList,
|
|
||||||
new DateTimeZone($chartTimezone)
|
|
||||||
);
|
|
||||||
$observationTipInsertResult['success'] = true;
|
|
||||||
$observationTipInsertResult['message'] = sprintf(
|
|
||||||
'%d Ereignisse eingetragen, %d bereits vorhanden.',
|
|
||||||
(int) $observationTipInsertResult['inserted'],
|
|
||||||
(int) $observationTipInsertResult['skipped']
|
|
||||||
);
|
|
||||||
} catch (Throwable $e) {
|
|
||||||
$observationTipInsertResult = [
|
|
||||||
'success' => false,
|
|
||||||
'message' => 'Eintragen in `astro_sonobs_observationtips` fehlgeschlagen: ' . $e->getMessage(),
|
|
||||||
];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$chartConfig = [
|
$chartConfig = [
|
||||||
'monthName' => $months[$selectedMonth],
|
'monthName' => $months[$selectedMonth],
|
||||||
@@ -2768,10 +2667,11 @@ $observationTipCalendarFilenameJson = json_encode(sprintf('astrotools-monatserei
|
|||||||
</div>
|
</div>
|
||||||
<div class="monatskarte-export">
|
<div class="monatskarte-export">
|
||||||
<?php if ($monthEventList): ?>
|
<?php if ($monthEventList): ?>
|
||||||
<form method="post" class="monatskarte-inline-form">
|
<form method="post" action="<?= htmlspecialchars($observationTipInsertEndpoint, ENT_QUOTES, 'UTF-8') ?>" class="monatskarte-inline-form" id="observationTipInsertForm">
|
||||||
<input type="hidden" name="insert_observation_tips" value="1">
|
<input type="hidden" name="insert_observation_tips" value="1">
|
||||||
<input type="hidden" name="observation_tip_insert_token" value="<?= htmlspecialchars($observationTipInsertToken, ENT_QUOTES, 'UTF-8') ?>">
|
<input type="hidden" name="observation_tip_insert_token" value="<?= htmlspecialchars($observationTipInsertToken, ENT_QUOTES, 'UTF-8') ?>">
|
||||||
<button type="submit" class="monatskarte-export-button">
|
<input type="hidden" name="observation_tip_payload" value="<?= htmlspecialchars($observationTipInsertPayloadJson, ENT_QUOTES, 'UTF-8') ?>">
|
||||||
|
<button type="submit" class="monatskarte-export-button" id="observationTipInsertButton">
|
||||||
Direkt in Datenbank eintragen
|
Direkt in Datenbank eintragen
|
||||||
</button>
|
</button>
|
||||||
</form>
|
</form>
|
||||||
@@ -2790,11 +2690,7 @@ $observationTipCalendarFilenameJson = json_encode(sprintf('astrotools-monatserei
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="monatskarte-footer">
|
<div class="monatskarte-footer">
|
||||||
<?php if (is_array($observationTipInsertResult)): ?>
|
<p class="monatskarte-insert-status" id="observationTipInsertStatus" role="status" aria-live="polite" hidden></p>
|
||||||
<p class="monatskarte-insert-status <?= !empty($observationTipInsertResult['success']) ? 'is-success' : 'is-error' ?>">
|
|
||||||
<?= htmlspecialchars((string) ($observationTipInsertResult['message'] ?? ''), ENT_QUOTES, 'UTF-8') ?>
|
|
||||||
</p>
|
|
||||||
<?php endif; ?>
|
|
||||||
<?php if ($observationTipInsertSql !== ''): ?>
|
<?php if ($observationTipInsertSql !== ''): ?>
|
||||||
<div class="monatskarte-sql-export" id="observationTipSqlPanel" hidden>
|
<div class="monatskarte-sql-export" id="observationTipSqlPanel" hidden>
|
||||||
<label class="monatskarte-sql-label" for="observationTipSqlOutput">SQL für `astro_sonobs_observationtips`</label>
|
<label class="monatskarte-sql-label" for="observationTipSqlOutput">SQL für `astro_sonobs_observationtips`</label>
|
||||||
@@ -2869,6 +2765,11 @@ $observationTipCalendarFilenameJson = json_encode(sprintf('astrotools-monatserei
|
|||||||
border: 1px solid #efc0c0;
|
border: 1px solid #efc0c0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.monatskarte-export-button.is-success {
|
||||||
|
background: rgba(36, 104, 62, 0.92);
|
||||||
|
border-color: rgba(141, 214, 167, 0.82);
|
||||||
|
}
|
||||||
|
|
||||||
.monatsvorhersage-shell {
|
.monatsvorhersage-shell {
|
||||||
display: grid;
|
display: grid;
|
||||||
gap: 18px;
|
gap: 18px;
|
||||||
@@ -3475,12 +3376,87 @@ function monthChartCometRaDec(comet, date) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
(function () {
|
(function () {
|
||||||
|
const insertForm = document.getElementById('observationTipInsertForm');
|
||||||
|
const insertButton = document.getElementById('observationTipInsertButton');
|
||||||
|
let insertStatus = document.getElementById('observationTipInsertStatus');
|
||||||
const toggleButton = document.getElementById('toggleObservationTipSql');
|
const toggleButton = document.getElementById('toggleObservationTipSql');
|
||||||
const copyButton = document.getElementById('copyObservationTipSql');
|
const copyButton = document.getElementById('copyObservationTipSql');
|
||||||
const icalButton = document.getElementById('downloadObservationTipIcal');
|
const icalButton = document.getElementById('downloadObservationTipIcal');
|
||||||
const panel = document.getElementById('observationTipSqlPanel');
|
const panel = document.getElementById('observationTipSqlPanel');
|
||||||
const output = document.getElementById('observationTipSqlOutput');
|
const output = document.getElementById('observationTipSqlOutput');
|
||||||
|
|
||||||
|
if (insertForm && insertButton) {
|
||||||
|
insertForm.addEventListener('submit', async function (event) {
|
||||||
|
event.preventDefault();
|
||||||
|
const originalLabel = insertButton.textContent;
|
||||||
|
insertButton.classList.remove('is-success');
|
||||||
|
insertButton.disabled = true;
|
||||||
|
insertButton.textContent = 'Trage ein...';
|
||||||
|
|
||||||
|
try {
|
||||||
|
const response = await fetch(insertForm.action, {
|
||||||
|
method: 'POST',
|
||||||
|
body: new FormData(insertForm),
|
||||||
|
headers: {
|
||||||
|
'X-Requested-With': 'fetch'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const payload = await response.json();
|
||||||
|
if (!insertStatus) {
|
||||||
|
insertStatus = document.createElement('p');
|
||||||
|
insertStatus.id = 'observationTipInsertStatus';
|
||||||
|
insertStatus.setAttribute('role', 'status');
|
||||||
|
insertStatus.setAttribute('aria-live', 'polite');
|
||||||
|
insertStatus.className = 'monatskarte-insert-status';
|
||||||
|
const footer = insertForm.closest('.monatskarte-card')?.querySelector('.monatskarte-footer');
|
||||||
|
if (footer) {
|
||||||
|
footer.insertBefore(insertStatus, footer.firstChild);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (insertStatus) {
|
||||||
|
insertStatus.hidden = false;
|
||||||
|
insertStatus.className = 'monatskarte-insert-status ' + (payload && payload.success ? 'is-success' : 'is-error');
|
||||||
|
insertStatus.textContent = payload && payload.message ? payload.message : 'Unbekannte Rueckmeldung vom Server.';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (payload && payload.success) {
|
||||||
|
insertButton.classList.add('is-success');
|
||||||
|
insertButton.textContent = 'Eingetragen';
|
||||||
|
window.setTimeout(function () {
|
||||||
|
insertButton.classList.remove('is-success');
|
||||||
|
insertButton.textContent = originalLabel;
|
||||||
|
}, 1800);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
if (!insertStatus) {
|
||||||
|
insertStatus = document.createElement('p');
|
||||||
|
insertStatus.id = 'observationTipInsertStatus';
|
||||||
|
insertStatus.setAttribute('role', 'status');
|
||||||
|
insertStatus.setAttribute('aria-live', 'polite');
|
||||||
|
insertStatus.className = 'monatskarte-insert-status is-error';
|
||||||
|
const footer = insertForm.closest('.monatskarte-card')?.querySelector('.monatskarte-footer');
|
||||||
|
if (footer) {
|
||||||
|
footer.insertBefore(insertStatus, footer.firstChild);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (insertStatus) {
|
||||||
|
insertStatus.hidden = false;
|
||||||
|
insertStatus.className = 'monatskarte-insert-status is-error';
|
||||||
|
insertStatus.textContent = 'Eintragen fehlgeschlagen. Bitte erneut versuchen.';
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
insertButton.disabled = false;
|
||||||
|
if (!insertButton.classList.contains('is-success')) {
|
||||||
|
insertButton.textContent = originalLabel;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
if (toggleButton && panel) {
|
if (toggleButton && panel) {
|
||||||
toggleButton.addEventListener('click', function () {
|
toggleButton.addEventListener('click', function () {
|
||||||
const isHidden = panel.hasAttribute('hidden');
|
const isHidden = panel.hasAttribute('hidden');
|
||||||
|
|||||||
Reference in New Issue
Block a user