umgestellt auf DB4js_all

This commit is contained in:
rxf
2025-12-01 19:11:46 +01:00
parent 8fe9086205
commit ba026bda31
16 changed files with 454 additions and 151 deletions

View File

@@ -164,6 +164,7 @@ class DB
$st->execute($params);
return $st->fetchAll();
}
//"SELECT * FROM SoFue2 WHERE deleted=0 AND status=? AND wtermin >= NOW() ORDER BY wtermin DESC, id DESC LIMIT ? OFFSET ?"
public static function one(string $sql, array $params = []): ?array
{
@@ -527,22 +528,88 @@ class RepoSoFue
}
public static function getRecords(string $status = 'all', int $rows = 10, int $page = 1, ?string $termin = null): array
{
$offset = ($page - 1) * $rows;
// Lastdate: 9 Monate zurück
$lastdate = new DateTime();
$lastdate->sub(new DateInterval('P9M'));
$lastdateStr = $lastdate->format('Y-m-d');
$params = [];
$sql = "SELECT * FROM " . TBL_SOFUE . " WHERE deleted=0";
$countSql = "SELECT COUNT(*) as count FROM " . TBL_SOFUE . " WHERE deleted=0";
// WHERE Bedingungen aufbauen
if ($status !== 'all') {
$sql .= " AND status=?";
if ((int)$status === 4) {
$countSql .= " AND stattgefunden=1";
} else {
$countSql .= " AND status=?";
$params[] = (int)$status;
}
if ($termin) {
$sql .= " AND wtermin=?";
$params[] = $termin;
}
// Bei status=4 (stattgefunden) macht 'neu' keinen Sinn, ignoriere termin
// termin kann 'all', 'neu' sein - nur 'neu' wird behandelt
if ($termin === 'neu' && (int)$status !== 4) {
$countSql .= " AND wtermin >= NOW()";
}
// Lastdate-Filter auch beim Count hinzufügen
$countSql .= " AND DATE(wtermin) >= ?";
$params[] = $lastdateStr;
// Anzahl der Records holen
$countResult = DB::one($countSql, $params);
$count = (int)($countResult['count'] ?? 0);
// Anzahl der Seiten berechnen
$totalPages = $rows > 0 ? ceil($count / $rows) : 1;
// Falls angeforderte Seite > Anzahl der Seiten, letzte Seite verwenden
if ($page > $totalPages && $totalPages > 0) {
$page = $totalPages;
}
// Start-Record berechnen
$offset = $rows * ($page - 1);
if ($offset < 0) {
$offset = 0;
}
// Daten abrufen mit lastdate-Filter
$sql = "SELECT * FROM " . TBL_SOFUE . " WHERE deleted=0";
$dataParams = [];
if ($status !== 'all') {
if ((int)$status === 4) {
$sql .= " AND stattgefunden=1";
} else {
$sql .= " AND status=?";
$dataParams[] = (int)$status;
}
}
// Bei status=4 (stattgefunden) macht 'neu' keinen Sinn, ignoriere termin
// termin kann 'all', 'neu' sein - nur 'neu' wird behandelt
if ($termin === 'neu' && (int)$status !== 4) {
$sql .= " AND wtermin >= NOW()";
}
// Lastdate-Filter hinzufügen
$sql .= " AND DATE(wtermin) >= ?";
$dataParams[] = $lastdateStr;
$sql .= " ORDER BY wtermin DESC, id DESC LIMIT ? OFFSET ?";
$params[] = $rows;
$params[] = $offset;
return DB::all($sql, $params);
$dataParams[] = $rows;
$dataParams[] = $offset;
$records = DB::all($sql, $dataParams);
// Response mit Pagination-Info
return [
'page' => $page,
'total' => $totalPages,
'records' => $count,
'rows' => $records
];
}
// "SELECT * FROM SoFue2 WHERE deleted=0 AND stattgefunden=1 AND wtermin >= NOW() AND DATE(wtermin) >= ? ORDER BY wtermin DESC, id DESC LIMIT ? OFFSET ?"
public static function update(int $id, array $d): int
{
$sql = "UPDATE " . TBL_SOFUE . " SET mitarbeiter=?,status=?,bemerkung=?,wtermin=?,atermin=?,erledigt_datum=? WHERE id=?";
@@ -611,6 +678,183 @@ class RepoStatistik
}
}
// ---- Statistik Jahre Repository (StatistikJahre table) ----
class RepoStatistikJahre
{
const TBL = 'StatistikJahre';
public static function getByDate(string $datum): ?array
{
return DB::one("SELECT * FROM " . self::TBL . " WHERE datum=?", [$datum]);
}
public static function getByYear(int $year): array
{
$sql = "SELECT * FROM " . self::TBL . " WHERE YEAR(datum)=? ORDER BY datum";
$data = DB::all($sql, [$year]);
// Calculate sums
$sumB = 0;
$sumA = 0;
$sumBZ = 0;
$sumBT = 0;
foreach ($data as $row) {
$sumB += ($row['besucherNormal'] ?? 0) + ($row['besucherSonder'] ?? 0) + ($row['besucherToT'] ?? 0);
$sumA += ($row['fuehrungen'] ?? 0) + ($row['beobachtungen'] ?? 0) + ($row['techdienst'] ?? 0);
$sumBZ += ($row['beoZeit'] ?? 0);
$sumBT += ($row['beoTage'] ?? 0);
}
// Get Gesamt bemerkung
$gesamt = DB::one("SELECT bemerkung FROM StatistikGesamt WHERE jahr=?", [$year]);
$bemG = $gesamt['bemerkung'] ?? '';
return [
'data' => $data,
'sumB' => $sumB,
'sumA' => $sumA,
'sumBZ' => $sumBZ,
'sumBT' => $sumBT,
'bemG' => $bemG
];
}
public static function createOrUpdate(array $post): array
{
$existing = self::getByDate($post['datum']);
if (!$existing) {
// Insert
$sql = "INSERT INTO " . self::TBL . " (fuehrungen, beobachtungen, techdienst, besucherNormal, besucherSonder, besucherToT, bemerkung, datum, beoZeit, beoTage) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)";
DB::exec($sql, [
$post['fueh'] ?? 0,
$post['beob'] ?? 0,
$post['tech'] ?? 0,
$post['besN'] ?? 0,
$post['besS'] ?? 0,
$post['besT'] ?? 0,
$post['beme'] ?? '',
$post['datum'],
$post['beoZ'] ?? 0,
$post['beoT'] ?? 0
]);
} else {
// Update
$sql = "UPDATE " . self::TBL . " SET fuehrungen=?, beobachtungen=?, techdienst=?, besucherNormal=?, besucherSonder=?, besucherToT=?, bemerkung=?, beoZeit=?, beoTage=? WHERE datum=?";
DB::exec($sql, [
$post['fueh'] ?? 0,
$post['beob'] ?? 0,
$post['tech'] ?? 0,
$post['besN'] ?? 0,
$post['besS'] ?? 0,
$post['besT'] ?? 0,
$post['beme'] ?? '',
$post['beoZ'] ?? 0,
$post['beoT'] ?? 0,
$post['datum']
]);
}
return ['datum' => $post['datum']];
}
public static function getYearList(): array
{
$sql = "SELECT YEAR(datum) as jahr FROM " . self::TBL . " GROUP BY YEAR(datum) ORDER BY YEAR(datum) DESC";
$rows = DB::all($sql);
return array_map(fn($r) => (int)$r['jahr'], $rows);
}
}
// ---- Statistik Gesamt Repository (StatistikGesamt table) ----
class RepoStatistikGesamt
{
const TBL = 'StatistikGesamt';
public static function getAll(): array
{
$data = DB::all("SELECT * FROM " . self::TBL . " ORDER BY jahr DESC");
// Get last date info
$lastYearRow = DB::one("SELECT MAX(YEAR(datum)) as lastYear FROM StatistikJahre");
$lastYear = $lastYearRow['lastYear'] ?? date('Y');
$fullYear = DB::all("SELECT * FROM StatistikJahre WHERE YEAR(datum)=?", [$lastYear]);
// Calculate sums
$sumB = DB::one("SELECT SUM(besucher) as sum FROM " . self::TBL)['sum'] ?? 0;
$sumA = DB::one("SELECT SUM(aktivitaeten) as sum FROM " . self::TBL)['sum'] ?? 0;
return [
'data' => $data,
'lastDate' => [
'lastYear' => (int)$lastYear,
'fullYear' => $fullYear
],
'sumB' => (int)$sumB,
'sumA' => (int)$sumA
];
}
public static function getByYear(int $year): ?array
{
return DB::one("SELECT * FROM " . self::TBL . " WHERE jahr=?", [$year]);
}
public static function createOrUpdate(array $post): array
{
$existing = self::getByYear((int)$post['jahr']);
if (!$existing) {
// Insert
$sql = "INSERT INTO " . self::TBL . " (aktivitaeten, besucher, bemerkung, jahr) VALUES (?, ?, ?, ?)";
DB::exec($sql, [
$post['suma'] ?? 0,
$post['sumb'] ?? 0,
$post['bemG'] ?? '',
$post['jahr']
]);
} else {
// Update
$sql = "UPDATE " . self::TBL . " SET aktivitaeten=?, besucher=?, bemerkung=? WHERE jahr=?";
DB::exec($sql, [
$post['suma'] ?? 0,
$post['sumb'] ?? 0,
$post['bemG'] ?? '',
$post['jahr']
]);
}
return ['datum' => $post['jahr']];
}
}
// ---- Kalender Repository ----
class RepoKalender
{
const TBL = 'kalender';
public static function getEntries(string $start, string $end): array
{
$s = date('Ymd', strtotime($start));
$e = date('Ymd', strtotime($end));
return DB::all("SELECT * FROM " . self::TBL . " WHERE start >= ? AND start <= ?", [$s, $e]);
}
public static function insert(array $data): bool
{
$sql = "INSERT INTO " . self::TBL . " (start, end, title, description) VALUES (?, ?, ?, ?)";
DB::exec($sql, [$data['start'], $data['end'] ?? $data['start'], $data['title'], $data['description'] ?? '']);
return true;
}
public static function delete(int $id): bool
{
DB::exec("DELETE FROM " . self::TBL . " WHERE id=?", [$id]);
return true;
}
}
// ---- Email Service (einfach) ----
class Mailer
{
@@ -671,37 +915,25 @@ class Commands
{
public const MAP = [
'PING' => 'Health-Check',
'GET_ANMELD' => 'Liste Anmeldungen für fid',
'GET_ANMELDNEW' => 'Anmeldungen-Varianten (legacy: special/date)',
'GET_SONDERNEW' => 'Sonderführungen-Varianten (legacy: special/date)',
'GET_ANMELD' => 'Liste Anmeldungen für fid/Datum',
'GET_ANMELDNEW' => 'Anmeldungen-Varianten (special/date)',
'GET_TEILN_ID' => 'Teilnehmer nach id',
'GET_TEILN_NAME' => 'Teilnehmer nach Name/Vorname',
'GET_GROUP' => 'Gruppenbezeichnung für fdates dateTime',
'GET_ONEANMELD' => 'Eine Anmeldung nach id',
'GET_COUNTS' => 'Summe Anmeldungen für fid',
'GET_COUNTS_DATE' => 'Summe Anmeldungen für Datum (YYYY-MM-DD)',
'INSERT_TLN' => 'Anmeldung anlegen',
'GET_COUNTS' => 'Summe Anmeldungen für fid/Datum',
'GET_COUNTS_DATE' => 'Summe Anmeldungen für Datum',
'UPDATE_TLN' => 'Anmeldung ändern',
'DELETE_TLN' => 'Anmeldung löschen',
'UPDATE_TLN_BULK' => 'Mehrere Anmeldungen Feld-Update (legacy updateEntries)',
'UPDATE_TLN_BULK' => 'Mehrere Anmeldungen Feld-Update',
'DELETEONE' => 'Teilnehmer löschen (Alias)',
'GET_LASTANMELDUNG' => 'Letzte Anmeldung (Datum) ab Start',
'UPDATECOUNT' => 'Zähler in fdatum1 für Datum reduzieren',
'GET_SOFIANMELD' => 'Alle/sofue_id bezogene Sonderführungs-Anmeldungen',
'GET_ONESOFIANMELD' => 'Eine Sonderführungs-Anmeldung',
'GET_SOFIANMELD_COUNT' => 'Zähler Sonderführungs-Anmeldungen',
'INSERT_SOFIANMELD' => 'Neue Sonderführungs-Anmeldung',
'UPDATE_SOFIANMELD' => 'Sonderführungs-Anmeldung ändern',
'DELETE_SOFIANMELD' => 'Sonderführungs-Anmeldung löschen',
'GET_TERMINE' => 'Termine öffentliche Führungen',
'GET_DATES' => 'Nächste Führungstermine (limit + ab Datum)',
'GET_ONETERMIN' => 'Termin nach id',
'GET_FID' => 'Fid für Datum',
'GET_TIME' => 'Uhrzeit für Datum',
'GET_BEOS' => 'Liste BEOs optional nur Guides',
'GET_ONEBEO' => 'Ein BEO nach name',
'GET_ONE' => 'Sonderführung nach id',
'GET_ONETERMIN_SOFUE' => 'Sonderführung nach Termin',
'GET_MANY' => 'Gefilterte Sonderführungen',
'UPDATE' => 'Sonderführung Standard-Update',
'UPDATEAFTER' => 'Nachbearbeitung Sonderführung',
@@ -710,11 +942,28 @@ class Commands
'GET_STATISTIK_ANMELD' => 'Statistik öffentliche Führungen Jahr',
'GET_STATISTIK_BEO' => 'Statistik BEO Jahr',
'GET_STATISTIK_GESAMT' => 'Gesamtstatistik Jahr',
'SEND_CONFIRMATION' => 'Einfache Bestätigungs-Mail',
'SENDMYMAIL' => 'Mail mit BCC versenden',
'SENDMAILZUSAGE' => 'Zusage an Anfragenden',
'SENDMAIL2BEO' => 'Mail an Mitarbeiter',
'SENDMAIL2LISTE' => 'Mail an Verteiler',
'PUT2KALENDER' => 'Kalender-Eintrag (Platzhalter)',
'PUT2KALENDER' => 'Kalender-Eintrag',
'GET_FDATES' => 'Führungstermine für Kalenderansicht',
'GET_CALENTRIES' => 'Kalendereinträge abrufen',
'PUT_CALENTRY' => 'Kalendereintrag erstellen',
'DEL_CALENTRY' => 'Kalendereintrag löschen',
'GET_YEARS' => 'Liste verfügbare Jahre (Statistik)',
'GET_ONE_A' => 'Statistik-Eintrag für Datum',
'GET_ALL_A' => 'Alle Statistik-Einträge für Jahr',
'CRUP_A' => 'Statistik-Eintrag erstellen/aktualisieren (Jahre)',
'GET_ALL_G' => 'Gesamtstatistik alle Jahre',
'GET_ONE_G' => 'Gesamtstatistik für Jahr',
'CRUP_G' => 'Gesamtstatistik erstellen/aktualisieren',
'GET_TIME_BY_DATE' => 'Uhrzeit für Datum abrufen',
'DELETE_ENTRY' => 'Anmeldung löschen (Storno)',
'GET_FUEHRUNGEN' => 'Führungen in Zeitbereich',
'UPDATETLNFD' => 'Teilnehmer-Datum aktualisieren',
'SEND_MAIL_HTML' => 'Mail versenden (Text)',
'GET_ALLTEILN' => 'Alle Teilnehmer ab Datum',
'LIST_COMMANDS' => 'Liste aller Kommandos'
];
}
@@ -754,22 +1003,11 @@ try {
$special = $input['special'] ?? 'alllater';
$date = $input['date'] ?? date('Ymd');
respond(RepoAnmeld::getNew($special, $date));
case 'GET_SONDERNEW':
$special = $input['special'] ?? 'alllater';
$date = $input['date'] ?? date('Ymd');
if ($special !== 'alllater') respond([]);
respond(RepoSoFue::getAfterDate($date));
case 'GET_TEILN_ID':
$r = RepoAnmeld::getById((int)$input['id'], $typ);
respond($r ? [$r] : []);
case 'GET_TEILN_NAME':
respond(RepoAnmeld::getByName($input['name']));
case 'GET_GROUP':
$grp = RepoFdates::groupByDate($input['date']);
respond(['grp' => $grp]);
case 'GET_ONEANMELD':
$r = RepoAnmeld::getById((int)$input['id'], $typ);
respond($r ?: ['error' => 'Not found']);
case 'GET_COUNTS':
if (isset($input['fdate'])) {
respond(RepoAnmeld::countByDate($input['fdate'], $typ));
@@ -786,11 +1024,6 @@ try {
respondError('Missing identifier for GET_COUNTS');
case 'GET_COUNTS_DATE':
respond(['count' => RepoAnmeld::countByDate($input['date'], $typ)]);
case 'INSERT_TLN':
if (!isset($input['name'], $input['email'], $input['anzahl'], $input['fid'])) respondError('Missing fields');
if (!filter_var($input['email'], FILTER_VALIDATE_EMAIL)) respondError('Invalid email');
$id = RepoAnmeld::insert($input);
respond(['success' => true, 'id' => $id]);
case 'UPDATE_TLN':
if (!isset($input['id'])) respondError('id missing');
RepoAnmeld::update((int)$input['id'], $input);
@@ -809,28 +1042,6 @@ try {
$n = RepoAnmeld::bulkUpdateField($ids, (string)$input['field'], $val);
respond(['success' => $n > 0, 'updated' => $n]);
// Sonderführungs-Anmeldungen
case 'GET_SOFIANMELD':
if (isset($input['sofue_id'])) respond(RepoSoFiAnmeld::getBySoFue((int)$input['sofue_id']));
respond(RepoSoFiAnmeld::getAll());
case 'GET_ONESOFIANMELD':
$r = RepoSoFiAnmeld::getById((int)$input['id']);
respond($r ?: ['error' => 'Not found']);
case 'GET_SOFIANMELD_COUNT':
respond(['count' => RepoSoFiAnmeld::countBySoFue((int)$input['sofue_id'])]);
case 'INSERT_SOFIANMELD':
if (!isset($input['name'], $input['email'], $input['anzahl'], $input['sofue_id'])) respondError('Missing fields');
if (!filter_var($input['email'], FILTER_VALIDATE_EMAIL)) respondError('Invalid email');
$id = RepoSoFiAnmeld::insert($input);
respond(['success' => true, 'id' => $id]);
case 'UPDATE_SOFIANMELD':
if (!isset($input['id'])) respondError('id missing');
RepoSoFiAnmeld::update((int)$input['id'], $input);
respond(['success' => true]);
case 'DELETE_SOFIANMELD':
RepoSoFiAnmeld::delete((int)$input['id']);
respond(['success' => true]);
// Termine
case 'GET_TERMINE':
respond(RepoTermine::getAll(($input['includeOld'] ?? 'false') === 'true'));
@@ -838,9 +1049,6 @@ try {
$amount = isset($input['anzahl']) ? (int)$input['anzahl'] : 50;
$from = $input['date'] ?? date('Ymd');
respond(RepoTermine::getNextDates($amount, $from, $typ));
case 'GET_ONETERMIN':
$r = RepoTermine::getById((int)$input['id']);
respond($r ?: ['error' => 'Not found']);
case 'GET_FID':
respond(['fid' => RepoTermine::fidByDate($input['datum'], $typ)]);
case 'GET_TIME':
@@ -866,9 +1074,6 @@ try {
case 'GET_ONE':
$r = RepoSoFue::getById((int)$input['id']);
respond($r ?: ['error' => 'Not found']);
case 'GET_ONETERMIN_SOFUE':
$r = RepoSoFue::getByTermin($input['termin']);
respond($r ?: ['error' => 'Not found']);
case 'GET_MANY':
respond(RepoSoFue::getRecords($input['status'] ?? 'all', (int)($input['rows'] ?? 10), (int)($input['page'] ?? 1), $input['termin'] ?? null));
case 'UPDATE':
@@ -901,10 +1106,6 @@ try {
respond(RepoStatistik::gesamt((int)($input['year'] ?? date('Y'))));
// Mail
case 'SEND_CONFIRMATION':
if (!isset($input['to'], $input['subject'], $input['body'])) respondError('Missing mail fields');
$ok = Mailer::sendPlain($input['to'], $input['subject'], $input['body']);
respond(['success' => $ok]);
case 'SENDMYMAIL':
// Legacy-compatible mail send with BCC list
$subject = $input['subject'] ?? ($input['betreff'] ?? null);
@@ -949,6 +1150,107 @@ try {
if (!isset($input['id'], $input['termin'], $input['mitarbeiter'])) respondError('Missing fields');
error_log('Kalender-Eintrag: ' . $input['id'] . ' ' . $input['termin'] . ' ' . $input['mitarbeiter']);
respond(['success' => true]);
case 'GET_FDATES':
// Returns führungen for calendar display
$start = $input['start'] ?? date('Ymd');
$end = $input['end'] ?? date('Ymd', strtotime('+1 year'));
$s = date('Ymd', strtotime($start));
$e = date('Ymd', strtotime($end));
$sql = "SELECT * FROM " . TBL_FDATUM . " WHERE datum >= ? AND datum <= ? ORDER BY datum ASC";
$rows = DB::all($sql, [$s, $e]);
$result = [];
foreach ($rows as $r) {
$count = RepoAnmeld::countByDate($r['datum'], 'regular');
$result[] = [
'start' => $r['datum'],
'uhr' => substr($r['uhrzeit'] ?? '', 0, 2),
'title' => $r['gruppe'] ?? '',
'count' => $count
];
}
respond($result);
case 'GET_CALENTRIES':
respond(RepoKalender::getEntries($input['start'], $input['end']));
case 'PUT_CALENTRY':
RepoKalender::insert($input['data']);
respond(['success' => true]);
case 'DEL_CALENTRY':
RepoKalender::delete((int)$input['id']);
respond(['success' => true]);
// Statistik - Jahre
case 'GET_YEARS':
respond(RepoStatistikJahre::getYearList());
case 'GET_ONE_A':
$datum = $input['datum'] ?? respondError('datum missing');
$result = RepoStatistikJahre::getByDate($datum);
if ($result) {
// Add bemG from Gesamt
$year = substr($datum, 0, 4);
$gesamt = RepoStatistikGesamt::getByYear((int)$year);
$result['bemG'] = $gesamt['bemerkung'] ?? '';
}
respond($result ?: ['error' => 'Not found']);
case 'GET_ALL_A':
$year = (int)($input['jahr'] ?? date('Y'));
respond(RepoStatistikJahre::getByYear($year));
case 'CRUP_A':
if (!isset($input['toInsert'])) respondError('toInsert missing');
respond(RepoStatistikJahre::createOrUpdate($input['toInsert']));
// Statistik - Gesamt
case 'GET_ALL_G':
respond(RepoStatistikGesamt::getAll());
case 'GET_ONE_G':
$year = (int)($input['jahr'] ?? date('Y'));
$result = RepoStatistikGesamt::getByYear($year);
respond($result ?: ['error' => 'Not found']);
case 'CRUP_G':
if (!isset($input['toInsert'])) respondError('toInsert missing');
respond(RepoStatistikGesamt::createOrUpdate($input['toInsert']));
// Storno module commands
case 'GET_TIME_BY_DATE':
$dt = $input['dt'] ?? respondError('dt missing');
$typ = $input['typ'] ?? 'regular';
if ($typ === 'sonnen') {
respond(['time' => '11 Uhr']);
}
$time = DB::one("SELECT uhrzeit FROM " . TBL_FDATUM . " WHERE datum=?", [$dt]);
respond(['time' => $time['uhrzeit'] ?? '']);
case 'DELETE_ENTRY':
$id = (int)($input['id'] ?? respondError('id missing'));
DB::exec("DELETE FROM " . TBL_ANMELD . " WHERE id=?", [$id]);
respond(['success' => true]);
case 'GET_FUEHRUNGEN':
$start = $input['start'] ?? respondError('start missing');
$end = $input['end'] ?? respondError('end missing');
$typ = $input['typ'] ?? 'regular';
$table = ($typ === 'sonnen') ? 'sonnedatum' : TBL_FDATUM;
$sql = "SELECT * FROM $table WHERE datum >= ? AND datum <= ? ORDER BY datum ASC";
respond(DB::all($sql, [$start, $end]));
case 'UPDATETLNFD':
if (!isset($input['id'], $input['fdatum'], $input['fid'])) respondError('Missing fields');
$sql = "UPDATE " . TBL_ANMELD . " SET fdatum=?, fid=?, abgesagt=NULL WHERE id=?";
DB::exec($sql, [$input['fdatum'], $input['fid'], $input['id']]);
respond(['success' => true]);
case 'SEND_MAIL_HTML':
if (!isset($input['subject'], $input['to'], $input['body_txt'])) respondError('Missing mail fields');
require_once __DIR__ . '/phpmailer/dosendmail.php';
// Note: body_html is ignored because sendmail doesn't support it
$result = sendmail(
$input['subject'],
'noreply@sternwarte-welzheim.de',
$input['body_txt'],
[],
[],
is_array($input['to']) ? $input['to'] : [$input['to']]
);
respond(['success' => !($result['error'] ?? false)]);
case 'GET_ALLTEILN':
$fdatum = $input['fdatum'] ?? respondError('fdatum missing');
$sql = "SELECT * FROM " . TBL_ANMELD . " WHERE fdatum >= ? ORDER BY fid ASC";
respond(DB::all($sql, [$fdatum]));
case 'LIST_COMMANDS':
respond(['commands' => Commands::MAP, 'count' => count(Commands::MAP)]);

View File

@@ -292,7 +292,6 @@
"$stern_vorname $stern_name am " . preg_replace("/(\d+) Uhr/","um $0",$stern_datum) . " für $stern_teil $person " .
// $stern_teil == 1 ? "Person" : "Personen" .
".\r\n\r\n" .
"Bitte bringen Sie diese Bestätigung als Ausdruck oder digital zur Führung mit. \r\n\r\n".
"Die Führung findet NUR bei sternklarem Himmel statt. Falls der Himmel bedeckt ist \r\n" .
"und die Führung ausfällt, erhalten Sie bis spätestens eine Stunde vor Führungsbeginn \r\n" .
"eine Email. Sie können sich dann gerne zu einem neuen Termin anmelden.\r\n\r\n" .
@@ -379,8 +378,7 @@
</p>
<p>
Wenn Sie alle Felder ausgefüllt und abgeschickt haben (mit dem "Anmeldung senden"-Knopf),
erhalten Sie eine Anmeldebestätigung per e-mail. Diese bitte unbedingt zur Führung
ausgedruckt oder in digitaler Form mitbringen!
erhalten Sie eine Anmeldebestätigung per e-mail.
<!-- <div style="text-align:center;"><strong>Ohne die mitgebrachte Anmeldebestätigung erfolgt
k e i n Einlass.</strong></div>
@@ -484,7 +482,7 @@
<p>
Hinweis zum Datenschutz: <a href="" id="dschu">Datenschutzerklärung</a>
</p>
<p class="lastchange">Letzte Änderungen: 2025-10-22 rxf</p>
<p class="lastchange">Letzte Änderungen: 2025-12-01 rxf</p>
</form>
</div>

View File

@@ -290,8 +290,6 @@
"$stern_vorname $stern_name am " . preg_replace("/(\d+) Uhr/","um $0",$stern_datum) . " für $stern_teil $person " .
// $stern_teil == 1 ? "Person" : "Personen" .
".\r\n\r\n" .
"Bitte bringen Sie diese Bestätigung als Ausdruck oder digital zur Führung mit. \r\n".
"Ohne diese Bestätigung und 2G-Zertifikat erfolgt ausnahmslos k e i n Einlass.\r\n\r\n" .
"Die Führung findet NUR bei sternklarem Himmel statt. Falls der Himmel bedeckt ist \r\n" .
"und die Führung ausfällt, bitten wir Sie um eine neue Anmeldung.\r\n\r\n" .
"Die Hygienevorschriften sind zu beachten: die Teilnehmer müssen eine medizinische Maske,\r\n" .
@@ -367,8 +365,7 @@
</p>
<p>
Wenn Sie alle Felder ausgefüllt und abgeschickt haben (mit dem "Anmeldung senden"-Knopf),
erhalten Sie eine Anmeldebestätigung per e-mail. Diese bitte unbedingt zur Führung
ausgedruckt oder in digitaler Form mitbringen!
erhalten Sie eine Anmeldebestätigung per e-mail.
<div style="text-align:center;"><strong>Ohne die mitgebrachte Anmeldebestätigung und dem Impfzertifikat erfolgt ausnahmslos
k e i n Einlass.</strong></div>

View File

@@ -257,8 +257,6 @@
"$stern_vorname $stern_name am " . preg_replace("/(\d+) Uhr/","um $0",$stern_datum) . " für $stern_teil $person " .
// $stern_teil == 1 ? "Person" : "Personen" .
".\r\n\r\n" .
"Bitte bringen Sie diese Bestätigung als Ausdruck oder digital zur Führung mit. \r\n".
"Ohne diese Bestätigung erfolgt ausnahmslos k e i n Einlass.\r\n\r\n" .
"Die Führung findet NUR bei sternklarem Himmel statt. Falls der Himmel bedeckt ist \r\n" .
"und die Führung ausfällt, bitten wir Sie um eine neue Anmeldung.\r\n\r\n" .
"Die Hygienevorschriften sind zu beachten: Die Teilnehmer müssen Gesichtsmasken \r\n" .
@@ -340,8 +338,7 @@
</p>
<p>
Wenn Sie alle Felder ausgefüllt und abgeschickt haben (mit den "Anmeldung senden"-Knopf),
erhalten Sie eine Anmeldebestätigung per e-mail. Diese bitte unbedingt zur Führung
ausgedruckt oder in digitaler Form mitbringen!
erhalten Sie eine Anmeldebestätigung per e-mail.
<div style="text-align:center;"><strong>Ohne die mitgebrachte Anmeldebestätigung erfolgt ausnahmslos
k e i n Einlass.</strong></div>

View File

@@ -101,8 +101,7 @@
Für diese Führungen gelten folgende Regeln
<ul>
<li>Besucher/innen müssen sich zur Führung online über unsere Homepage
www.sternwarte-welzheim.de <strong>anmelden</strong> und erhalten eine Anmeldebestätigung,
die zur Sternführung mitzubringen ist.</li>
www.sternwarte-welzheim.de <strong>anmelden</strong>.</li>
<li>Pro Führung sind maximal <strong><?php echo $maxBesucher; ?></strong> Personen zugelassen.
</li>
</ul>
@@ -112,7 +111,7 @@
</p>
<p><a href="sonnenfuehrung.php">Anmeldung zu einer Sonnenführung</a>
</p>
<p class="lastchange">Letzte Änderungen: 2024-05-19 ae/rxf</p>
<p class="lastchange">Letzte Änderungen: 2025-12-01 ae/rxf</p>
</div> <!-- end #mainContent -->

View File

@@ -87,7 +87,7 @@ include 'maintenance.php';
Für diese Führungen gelten folgende Regeln
<ul>
<li>Besucher/innen müssen sich zur Führung online über unsere Homepage
www.sternwarte-welzheim.de <strong>anmelden</strong> und erhalten eine Anmeldebestätigung, die zur Sternführung mitzubringen ist.</li>
www.sternwarte-welzheim.de <strong>anmelden</strong>.</li>
<li>Pro Führung sind maximal <strong><?php echo $maxBesucher;?></strong> Personen zugelassen.</li>
</ul>
<p><a href="anmeldung.php">Anmeldung zu öffentlichen Führungen

View File

@@ -2,7 +2,7 @@
//globale Variable
var maint = false; // Wartung??
var ajaxURL="php/sofueDB.php";
var ajaxURL="../../DB4js_all.php"
var beos = [];
// 0 1 2 3 (4) (5)
@@ -54,7 +54,7 @@ function buildEntryScreen(id) {
// Übergenen Zeit testen, ob sie mit 1900 oder 0000 startet
function check1900(termin) {
if (termin.startsWith("1900") || termin.startsWith("0000")) {
if (termin === null || termin.startsWith("1900") || termin.startsWith("0000")) {
return true;
}
return false;

View File

@@ -7,7 +7,7 @@
$(document).ready(function() {
// Globals:
const url = "php/statistic.php";
const url = "../../DB4js_all.php";
const months = ['Januar', 'Februar', 'März', 'April', 'Mai', 'Juni', 'Juli', 'August', 'September', 'Oktober', 'November', 'Dezember' ];
const colors = { besucher: ['#FFB60A','#FF6702','#E54818'], aktivitaet: ['#D1D8D1','#ABC1C5','#81ACBC'], gesamt: ['#CBD26A','#88AD63','#5A8A40'] };
const barsPerPage = 23;

View File

@@ -47,3 +47,7 @@ body {
#beonamen {
font-size: 80%;
}
.fc {
--fc-today-bg-color: rgba(129, 222, 201, 0.75);
}

View File

@@ -10,10 +10,10 @@
<link href='lib/main.css' rel='stylesheet'/>
<link href='css/kalender.css' rel='stylesheet'/>
<script src='../javascript/jquery-1.11.0.min.js'></script>
<script src="https://code.jquery.com/jquery-3.5.1.min.js" integrity="sha384-ZvpUoO/+PpLXR1lu4jmpXWu80pZlYUAfxl5NsBMWOEPSjUn/6Z/hRTt8+pR6L4N2" crossorigin="anonymous"></script>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@4.5.3/dist/js/bootstrap.bundle.min.js" integrity="sha384-ho+j7jyWK8fNQe+A12Hb8AhRq26LrZ/JpcUGGOn+Y7RsweNrtN/tE3MoK7ZeZDyx" crossorigin="anonymous"></script>
<!-- <script src='/javascript/bootstrap.min.js'></script> -->
<script src='../javascript/moment.min.js'></script>
<script src="https://cdn.jsdelivr.net/npm/luxon@3.4.4/build/global/luxon.min.js"></script>
<script src='lib/main.js'></script>
<script src='js/version.js'></script>
<script src='js/kalender.js'></script>

View File

@@ -1,9 +1,10 @@
// calendar.js
document.addEventListener('DOMContentLoaded', async function () {
const DateTime = luxon.DateTime;
const months_short = [0, 'Jan', 'Feb', 'Mär', 'Apr', 'Mai', 'Jun', 'Jul', 'Aug', 'Sep', 'Okt', 'Nov', 'Dez'];
const weekdays = ['Montag', 'Dienstag', 'Mittwoch', 'Donnerstg', 'Freitag', 'Samstag', 'Sonntag'];
const url = '../DB4js.php';
const url = '../DB4js_all.php';
const default_dauer = 90; // Standardführung dauert 90min
let fuehrungszeiten;
let isedit;
@@ -230,7 +231,7 @@ document.addEventListener('DOMContentLoaded', async function () {
$('#txtTitle').val('');
$('#txtBeschr').val('');
let ret = await putToDbase({cmd: 'DEL_CALENTRY', id: id});
if (ret != true) {
if (ret.success != true) {
alert(`Fehler beim Löschen des Eintrages !`);
}
calendar.refetchEvents();
@@ -291,9 +292,12 @@ document.addEventListener('DOMContentLoaded', async function () {
// Return:
// entweder die Startzeit, falls gefunden oder aber 20 [Uhr]
function getFuehrungszeitZeit() {
if (fuehrungszeiten[3].start !== undefined) {
let st = moment(fuehrungszeiten[3].start);
return st.hour();
if (fuehrungszeiten && fuehrungszeiten.length > 3 && fuehrungszeiten[3].start) {
let st = DateTime.fromFormat(String(fuehrungszeiten[3].start), 'yyyyMMdd');
if (fuehrungszeiten[3].uhr) {
st = st.plus({ hours: fuehrungszeiten[3].uhr });
}
return st.hour;
} else {
return 20;
}
@@ -309,46 +313,43 @@ document.addEventListener('DOMContentLoaded', async function () {
function fillModal(st_seldate, end_seldate) {
let start, end;
if (isedit) {
start = moment(st_seldate);
end = moment(end_seldate);
start = DateTime.fromJSDate(st_seldate);
end = DateTime.fromJSDate(end_seldate);
} else {
start = moment(st_seldate);
start.hour(getFuehrungszeitZeit());
end = moment(st_seldate);
end.hour(getFuehrungszeitZeit());
end = end.add(default_dauer, 'm');
start = DateTime.fromJSDate(st_seldate).set({ hour: getFuehrungszeitZeit() });
end = DateTime.fromJSDate(st_seldate).set({ hour: getFuehrungszeitZeit() }).plus({ minutes: default_dauer });
$('#txtTitle').val('');
$('#txtBeschr').val('');
}
$('#st_date_day #end_date_day').html('');
$('#end_date_day').html('');
for (let i = 1; i <= 31; i++) {
$('#st_date_day').append(`<option ${start.date() == i ? 'selected' : ''}>${i}</option>`);
$('#end_date_day').append(`<option ${end.date() == i ? 'selected' : ''}>${i}</option>`);
$('#st_date_day').append(`<option ${start.day == i ? 'selected' : ''}>${i}</option>`);
$('#end_date_day').append(`<option ${end.day == i ? 'selected' : ''}>${i}</option>`);
}
$('#st_date_month #end_date_month').html('');
$('#end_date_month').html('');
for (let i = 1; i <= 12; i++) {
$('#st_date_month').append(`<option value=${i} ${(start.month() + 1) == i ? 'selected' : ''}>${months_short[i]}</option>`);
$('#end_date_month').append(`<option value=${i} ${(end.month() + 1) == i ? 'selected' : ''}>${months_short[i]}</option>`);
$('#st_date_month').append(`<option value=${i} ${start.month == i ? 'selected' : ''}>${months_short[i]}</option>`);
$('#end_date_month').append(`<option value=${i} ${end.month == i ? 'selected' : ''}>${months_short[i]}</option>`);
}
$('#st_date_year #end_date_year').html('');
$('#end_date_year').html('');
for (let i = 2020; i <= 2030; i++) {
$('#st_date_year').append(`<option ${start.year() == i ? 'selected' : ''}>${i}</option>`);
$('#end_date_year').append(`<option ${end.year() == i ? 'selected' : ''}>${i}</option>`);
$('#st_date_year').append(`<option ${start.year == i ? 'selected' : ''}>${i}</option>`);
$('#end_date_year').append(`<option ${end.year == i ? 'selected' : ''}>${i}</option>`);
}
$('#st_date_hours').html('');
$('#end_date_hours').html('');
for (let i = 0; i <= 23; i++) {
$('#st_date_hours').append(`<option ${start.hour() == i ? 'selected' : ''}>${("0" + i).slice(-2)}</option>`);
$('#end_date_hours').append(`<option ${end.hour() == i ? 'selected' : ''}>${("0" + i).slice(-2)}</option>`);
$('#st_date_hours').append(`<option ${start.hour == i ? 'selected' : ''}>${("0" + i).slice(-2)}</option>`);
$('#end_date_hours').append(`<option ${end.hour == i ? 'selected' : ''}>${("0" + i).slice(-2)}</option>`);
}
$('#st_date_minutes').html('');
$('#end_date_minutes').html('');
for (let i = 0; i <= 50; i += 10) {
$('#st_date_minutes').append(`<option ${start.minute() == i ? 'selected' : ''}>${("0" + i).slice(-2)}</option>`);
$('#end_date_minutes').append(`<option ${end.minute() == i ? 'selected' : ''}>${("0" + i).slice(-2)}</option>`);
$('#st_date_minutes').append(`<option ${start.minute == i ? 'selected' : ''}>${("0" + i).slice(-2)}</option>`);
$('#end_date_minutes').append(`<option ${end.minute == i ? 'selected' : ''}>${("0" + i).slice(-2)}</option>`);
}
}
@@ -387,9 +388,9 @@ document.addEventListener('DOMContentLoaded', async function () {
// Zuerst die regelmäßigen Führungen hole
fuehrungszeiten = await fetchFromDbase({cmd: 'GET_FDATES', start: start, end: end});
for (let i = 0; i < fuehrungszeiten.length; i++) {
let start = moment(fuehrungszeiten[i].start);
start.add(fuehrungszeiten[i].uhr,'h')
fuehrungszeiten[i].start = moment(start).format()
let start = DateTime.fromFormat(fuehrungszeiten[i].start, 'yyyyMMdd');
start = start.plus({ hours: fuehrungszeiten[i].uhr });
fuehrungszeiten[i].start = start.toISO();
let kurz = checkGroupinBeos(beos, fuehrungszeiten[i]);
if (kurz.k != "") {
fuehrungszeiten[i].title += " " + kurz.k;
@@ -433,16 +434,16 @@ document.addEventListener('DOMContentLoaded', async function () {
// Array mit den Feiertagen
async function getHolidays(start, end) {
let data = [];
let startyear = moment(start).year();
let endyear = moment(end).year();
let startyear = DateTime.fromJSDate(start).year;
let endyear = DateTime.fromJSDate(end).year;
let response = await fetch('https://feiertage-api.de/api/?jahr=' + startyear + '&nur_land=BW');
let holidays = await response.json();
for (const [k, v] of Object.entries(holidays)) {
if(k == "Ostermontag") {
data.push({start: moment(v.datum).subtract(1,'days').format("YYYY-MM-DD"), title: "Ostersonntag"});
data.push({start: DateTime.fromISO(v.datum).minus({ days: 1 }).toFormat('yyyy-MM-dd'), title: "Ostersonntag"});
}
if(k == "Pfingstmontag") {
data.push({start: moment(v.datum).subtract(1,'days').format("YYYY-MM-DD"), title: "Pfingstsonntag"});
data.push({start: DateTime.fromISO(v.datum).minus({ days: 1 }).toFormat('yyyy-MM-dd'), title: "Pfingstsonntag"});
}
data.push({start: v.datum, title: k});
}
@@ -462,20 +463,20 @@ document.addEventListener('DOMContentLoaded', async function () {
// Return:
// Array mit den Sonntagen
function getSundays(dt) {
let year = moment(dt).year();
const cm = moment(dt).month()
if (moment(dt).month() == 11) {
let year = DateTime.fromJSDate(dt).year;
const cm = DateTime.fromJSDate(dt).month;
if (DateTime.fromJSDate(dt).month == 12) {
year += 1;
}
let data = [];
for (let i = 1; i <= 12; i++) {
let d = moment(year + '-' + i + '-01');
let w = d.isoWeekday();
let dt = ''
let d = DateTime.fromObject({ year: year, month: i, day: 1 });
let w = d.weekday;
let dt = '';
if (w == 7) {
dt = d.format('YYYY-MM-DD');
dt = d.toFormat('yyyy-MM-dd');
} else {
dt = d.add(7 - w, 'd').format('YYYY-MM-DD')
dt = d.plus({ days: 7 - w }).toFormat('yyyy-MM-dd');
}
data.push({start: dt + 'T11:00:00', uhr: 11, title: 'Sonnenführung' })
}
@@ -510,17 +511,18 @@ document.addEventListener('DOMContentLoaded', async function () {
// Informationen einer Regelführung für das Popup zusammenbauen
function getRegularInfo(entryInfo) {
let curDate = moment(entryInfo.start).format('YYYYMMDD');
let dt = DateTime.fromJSDate(entryInfo.start);
let curDate = dt.toFormat('yyyyMMdd');
let besucher = entryInfo._def.extendedProps.count;
if (besucher == null) { besucher = "0"}
besucher = parseInt(besucher)
let wtg = moment(entryInfo.start).isoWeekday();
let wtg = dt.weekday;
let nbr = entryInfo.title.split(' ');
let str = `<h6>${weekdays[wtg - 1]}s-Team ${nbr[1]}</h6>`;
str += `<div id="beonamen">${entryInfo._def.extendedProps.namen}</div>`;
let btxt = besucher != 0 ? `<strong>${besucher}</strong> angemeldete Besucher` : 'Keine Anmeldungen';
str += btxt;
str += `<br />Von ${moment(entryInfo.start).format("HH:mm")} Uhr bis: ${moment(entryInfo.start).add(default_dauer, 'm').format("HH:mm")} Uhr`;
str += `<br />Von ${dt.toFormat('HH:mm')} Uhr bis: ${dt.plus({ minutes: default_dauer }).toFormat('HH:mm')} Uhr`;
return str;
}
@@ -529,7 +531,7 @@ document.addEventListener('DOMContentLoaded', async function () {
// Wird bei dem Popup und bei dem Ändern/löschen verwendent
function getFuehrungsInfo(entryInfo) {
let str = entryInfo._def.extendedProps.description;
str += `<br /><br />Start: ${moment(entryInfo.start).format("YYYY-MM-DD HH:mm")} Uhr <br />Ende: ${moment(entryInfo.end).format("YYYY-MM-DD HH:mm")} Uhr`;
str += `<br /><br />Start: ${DateTime.fromJSDate(entryInfo.start).toFormat('yyyy-MM-dd HH:mm')} Uhr <br />Ende: ${DateTime.fromJSDate(entryInfo.end).toFormat('yyyy-MM-dd HH:mm')} Uhr`;
return str;
}

View File

@@ -1,12 +1,17 @@
// VersiosNummern und -Geschichte
var VERSION="1.3.2";
var VDATE="2024-09-20";
var VERSION="1.3.3";
var VDATE="2025-12-01";
/* History
Rev. Datum Entwickler
1.3.3 2025-12-01 rxf
- 'heute' anders eingefärbt
- PHP-Interface nun DB4js_all.php
- anstelle 'moment' nun 'luxon' für die DateTime-Sachen verwendet
1.3.2 2024-09-20 rxf
- Sonnenführunge mit anzeigen

View File

@@ -67,9 +67,13 @@ function sendmail($subject, $from, $body, $cc=[], $bcc=[], $to=[]) {
// BCC
if (count($bcc) != 0) {
foreach ($bcc as $bc) {
if ($develop == 'true') {
$mail->addCC($bc);
} else {
$mail->addBCC($bc);
}
}
}
// Reply-To
if (!empty($from)) {

View File

@@ -252,8 +252,6 @@
"$stern_vorname $stern_name um $stern_zeit Uhr " .
"für $stern_teil $person." .
"\r\n\r\n" .
"Bitte bringen Sie diese Bestätigung als Ausdruck oder digital zur Führung mit. \r\n".
"Ohne diese Bestätigung erfolgt ausnahmslos k e i n Einlass.\r\n\r\n" .
"Die Führung findet NUR bei klarem Himmel statt. Falls der Himmel bedeckt ist " .
"und die Führung ausfällt, erhalten Sie bis spätestens eine Stunde vor Beginn der Finsternis " .
"eine Email.\r\n\r\n" .
@@ -269,8 +267,6 @@
"auf der Sternwarte Welzheim für</p>" .
"<p style='text-align: center;'>$stern_vorname $stern_name um $stern_zeit Uhr " .
"für $stern_teil $person.</p>" .
"<p>Bitte bringen Sie diese Bestätigung als Ausdruck oder digital zur Führung mit.<br />".
"Ohne diese Bestätigung erfolgt ausnahmslos <strong>kein</strong> Einlass.</p>" .
"<p>Die Führung findet <strong>nur</strong> bei klarem Himmel statt. Falls der Himmel bedeckt ist " .
"und die Führung ausfällt, erhalten Sie bis spätestens eine Stunde vor Beginn der Finsternis " .
"eine Email.<p>" .
@@ -357,8 +353,7 @@
</p>
<p>
Wenn Sie alle Felder ausgefüllt und abgeschickt haben (mit dem "Anmeldung senden"-Knopf),
erhalten Sie eine Anmeldebestätigung per e-mail. Diese bitte unbedingt zur Führung
ausgedruckt oder in digitaler Form mitbringen!
erhalten Sie eine Anmeldebestätigung per e-mail.
<div style="text-align:center;"><strong>Ohne die mitgebrachte Anmeldebestätigung erfolgt ausnahmslos
k e i n Einlass.</strong>
@@ -472,7 +467,7 @@
<p>
Hinweis zum Datenschutz: <a href="" id="dschu">Datenschutzerklärung</a>
</p>
<p class="lastchange">Letzte Änderungen: 2022-08-17 rxf</p>
<p class="lastchange">Letzte Änderungen: 2025-12-01 rxf</p>
</form>
</div>

View File

@@ -6,7 +6,7 @@ $(document).ready(() => {
// Globale Konstanten und Variable
// const ajaxURL="php/anmeldDB.php";
const ajaxURL = "../../DB4js.php";
const ajaxURL = "../../DB4js_all.php";
const maxVisitors = 25
const months2add = 3