Termine auf Prepared Statements umgebaut

Kalender, Terminliste, Terminformular und Terminzettel laufen ueber
db_abfrage() mit Platzhaltern; das Anlegen einer mehrtaegigen Terminreihe
liegt in einer Transaktion. Die Logik steckt jetzt in incs/termin.php,
sites/termin*.php rufen sie nur noch auf.

Dabei behoben:
- SQL-Injektion ueber die Suchbegriffe der Terminliste, ueber die
  Mehrfachauswahl (Ankreuzfelder del<N>/erl<N> gingen roh in DELETE bzw.
  UPDATE), ueber die Wiedervorlage-Tage (INTERVAL $folgedays DAY) und ueber
  ?action=terminshow&id=... in maketerminarray(), maketerminadresse(),
  maketelnrn() und makegooglestring()
- gespeichertes XSS: Beschreibung, Notiz, Termintyp, Ort, Kundenname und
  Mitarbeiterfarbe im Kalender, in der Terminliste und im Formular
- "or $err=error(...)" stand innerhalb des Abfrage-Strings, dadurch wurden
  die Nebenzeilen einer Terminreihe beim Loeschen nie entfernt
- die Abfrage zum Folgetermin las eine Spalte typ_id, die es in termin nicht
  gibt; Typ und Wiedervorlage fehlten deshalb beim Bearbeiten
- ein Folgetermin wurde mit termtime='' und termdauer='' angelegt, ein
  Termin ohne Folgetermin mit followed_by='' - im strikten Modus Fehler
- ein Termin ohne Datum landete auf dem 01.01.1970 und gab dabei
  "NICHTTERMINIERT!!" mitten in die Seite aus
- die Ueberschneidungspruefung verglich ab dem zweiten Tag gegen das Ende
  des ersten Tages
- eine unbekannte Registerkarte der Terminliste ergab ein leeres WHERE und
  damit einen SQL-Fehler; jetzt gilt die Standardansicht
- terminshow.php pruefte den Typ mit = statt ==
- der Kalender haengt nicht mehr an der Locale de_DE (strftime)

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
2026-09-22 00:04:38 +02:00
co-authored by Claude Opus 5
parent 3dfbf1c9c7
commit 490c4f12a4
12 changed files with 986 additions and 950 deletions
+554
View File
@@ -0,0 +1,554 @@
<?php
/*
* Termine: Kalenderansicht, Terminliste, Anlegen, Ändern und Löschen.
*
* Ein Termin über mehrere Tage wird als Reihe einzelner Zeilen gespeichert,
* die alle dasselbe `main_id` tragen - die id der ersten Zeile. Der Kalender
* zeigt die Zeilen einzeln, die Liste fasst sie über `main_id` zusammen.
*
* Urlaub und Krankheit sind Termine ohne Kunden. Ihre Typ-ids stehen seit
* jeher fest im Code (auch in `incs/users.php` bei der Urlaubsberechnung und
* in `templates/addtermin.tpl`).
*/
require_once(__DIR__."/db.php");
require_once(__DIR__."/saveNchange.php");
define("TERMTYP_URLAUB", 0);
define("TERMTYP_KRANKHEIT", 5);
/* Ganztägige Termine bekommen diese Dauer; die Terminliste filtert darauf */
define("TERMIN_GANZTAG", "24:00:00");
/* Leerwerte der NOT-NULL-Spalten - der strikte Modus lehnt '' dort ab */
define("TERMIN_KEIN_DATUM", "0000-00-00 00:00:00");
define("TERMIN_KEINE_DAUER", "00:00:00");
/* Urlaub und Krankheit laufen immer über ganze Tage */
function terminIstGanztaegig($typ)
{
return $typ == TERMTYP_URLAUB || $typ == TERMTYP_KRANKHEIT;
}
/* ---------------------------------------------------------------------
* Formularwerte
* ------------------------------------------------------------------ */
/*
* Die Felder aus addtermin.tpl. `datetime_start` und `datetime_end` kommen
* in deutscher Schreibweise ("24.12.2026 um 09:30") und werden hier schon
* in das Datenbankformat gebracht.
*/
function terminFormularwerte()
{
return array(
"head" => formularText("head"),
"text" => formularText("text"),
"typ" => formularGanzzahl("typ"),
"done" => formularGanzzahl("done") ? 1 : 0,
"user" => formularGanzzahl("user"),
"user2" => formularGanzzahl("user2"),
"user3" => formularGanzzahl("user3"),
"user4" => formularGanzzahl("user4"),
"folge" => formularGanzzahl("folge") ? 1 : 0,
"folgetyp" => formularGanzzahl("folgetyp"),
"folgedays" => formularGanzzahl("folgedays"),
"start" => dispdate_to_mysql_date(formularText("datetime_start"), 0),
"ende" => dispdate_to_mysql_date(formularText("datetime_end"), 1),
);
}
/*
* Verteilt einen Termin auf Tage. Länger als zwölf Stunden oder Urlaub und
* Krankheit werden zu ganzen Tagen: Rückgabe ist
* array(erster Tag, Dauer je Tag, Anzahl Tage).
*/
function terminTageUndDauer($werte)
{
$start = $werte["start"];
$dauer = mysql_dauer($start, $werte["ende"]);
$stunden = (int)strtok($dauer, ":");
if($stunden <= 12 && !terminIstGanztaegig($werte["typ"]))
return array($start, $dauer, 1);
// ceil() rechnet mit der Stundenzahl, nicht mit dem ganzen Zeitstring
$tage = max(1, (int)ceil($stunden / 24));
if($start != TERMIN_KEIN_DATUM)
$start = substr($start, 0, 11)."00:00:00";
return array($start, TERMIN_GANZTAG, $tage);
}
/* Der $i-te Tag einer Terminreihe, ohne Datum bleibt es ohne Datum */
function terminTag($start, $i)
{
if($start == TERMIN_KEIN_DATUM)
return TERMIN_KEIN_DATUM;
return date("Y-m-d H:i:s", strtotime("+".$i." day", strtotime($start)));
}
/* ---------------------------------------------------------------------
* Überschneidungen
* ------------------------------------------------------------------ */
/*
* Sucht einen Termin, bei dem $benutzer schon zwischen $start und $ende
* eingetragen ist. $ausserMain schließt die gerade bearbeitete Reihe aus.
* Gibt die id des kollidierenden Termins zurück, sonst null (false bei
* Fehlern).
*/
function terminUeberschneidung($dbconn, $benutzer, $start, $ende, $ausserMain)
{
if(!$benutzer || $start == TERMIN_KEIN_DATUM)
return null;
$params = array(TERMIN_KEIN_DATUM, $benutzer, $benutzer, $benutzer, $benutzer);
// eine main_id von 0 gibt es nur bei Altdaten, dann darf nichts ausgenommen werden
$eigene = "";
if($ausserMain > 0)
{
$eigene = " AND main_id != ?";
$params[] = (int)$ausserMain;
}
array_push($params, $start, $start, $ende, $ende, $start, $ende);
$sql = "SELECT id FROM
(SELECT id, main_id, user, user2, user3, user4, termtime,
DATE_ADD(termtime, INTERVAL CONCAT(HOUR(termdauer),' ',MINUTE(termdauer)) HOUR_MINUTE) AS termend
FROM termin WHERE termtime != ? AND done = 0) AS belegt
WHERE (user = ? OR user2 = ? OR user3 = ? OR user4 = ?)$eigene
AND ((? >= termtime AND ? < termend)
OR (? > termtime AND ? <= termend)
OR (? <= termtime AND ? >= termend))
LIMIT 1";
return db_wert($dbconn, $sql, $params);
}
/*
* Prüft alle vier Mitarbeiter eines Termins für jeden Tag der Reihe.
* Rückgabe 0, wenn nichts kollidiert, sonst die Fehlermeldung.
*/
function pruefeUeberschneidungen($dbconn, $werte, $start, $dauer, $tage, $ausserMain)
{
foreach(range(0, $tage - 1) as $i)
{
$tagStart = terminTag($start, $i);
$tagEnde = dauer_to_date($tagStart, $dauer);
foreach(array("user", "user2", "user3", "user4") as $feld)
{
$treffer = terminUeberschneidung($dbconn, $werte[$feld], $tagStart, $tagEnde, $ausserMain);
if($treffer === false)
return error("Fehler beim überprüfen von Überlappungen:<br />".mysqli_error($dbconn));
if($treffer !== null)
return error("Dieser Mitarbeiter hat zu der Zeit bereits einen Termin, er kann nicht an zwei Orten gleichzeitig sein.");
}
}
return 0;
}
/* ---------------------------------------------------------------------
* Speichern
* ------------------------------------------------------------------ */
/*
* Legt einen Folgetermin an: gleicher Kopf und dieselben Mitarbeiter, aber
* ohne Datum und mit einem Wiedervorlagedatum in $tage Tagen. Gibt dessen
* id zurück oder false.
*/
function legeFolgeterminAn($dbconn, $werte, $adressid)
{
$sql = "INSERT INTO termin SET head = ?, text = '', adresse_id = ?, typ = ?,
changetime = NOW(), datetime = NOW(), done = 0,
termtime = ?, termdauer = ?, followed_by = 0,
user = ?, user2 = ?, user3 = ?, user4 = ?, changeuser = ?,
act_date = DATE_ADD(CURDATE(), INTERVAL ? DAY)";
$params = array($werte["head"], $adressid, $werte["folgetyp"],
TERMIN_KEIN_DATUM, TERMIN_KEINE_DAUER,
$werte["user"], $werte["user2"], $werte["user3"], $werte["user4"],
$_SESSION["user"], $werte["folgedays"]);
if(!db_abfrage($dbconn, $sql, $params))
{
error("Fehler beim anlegen des Folgetermins:<br />".mysqli_error($dbconn));
return false;
}
$id = mysqli_insert_id($dbconn);
// Ein Termin ohne eigene Reihe ist sein eigener Hauptsatz
db_abfrage($dbconn, "UPDATE termin SET main_id = ? WHERE id = ?", array($id, $id));
if(saveLog($dbconn, db_sql_text($dbconn, $sql, $params)." [folgetermin, hat ID $id]",
"DELETE FROM termin WHERE id = $id", "anlegen"))
return false;
return $id;
}
/*
* Legt einen Termin an oder ändert ihn. $terminid ist die id der Reihe
* (0 = neu), $adressid die Kundenadresse (0 bei Urlaub und Krankheit).
*
* Wird aus einem eintägigen Termin ein mehrtägiger, wird die alte Reihe
* verworfen und neu angelegt - anders lässt sich die Reihe nicht umbauen.
*/
function speichereTermin($dbconn, $terminid, $adressid)
{
$werte = terminFormularwerte();
list($start, $dauer, $tage) = terminTageUndDauer($werte);
beginneSpeichern($dbconn);
$neuAnlegen = !$terminid;
if($terminid && $tage > 1)
{
// aus einem Einzeltermin wird eine Reihe: alte Zeilen verwerfen
$err = loescheTerminreihe($dbconn, $terminid);
if($err)
return beendeSpeichern($dbconn, $err);
$neuAnlegen = true;
$terminid = 0;
}
elseif($terminid)
{
// Zeilen der bisherigen Reihe bis auf die erste entfernen
$sql = "DELETE FROM termin WHERE main_id = ? AND id != ?";
db_abfrage($dbconn, $sql, array($terminid, $terminid))
or $err = error("Konnte Nebentermine nicht löschen:<br />".mysqli_error($dbconn));
if(!empty($err))
return beendeSpeichern($dbconn, $err);
}
if(!$werte["done"])
{
$err = pruefeUeberschneidungen($dbconn, $werte, $start, $dauer, $tage, $terminid);
if($err)
return beendeSpeichern($dbconn, $err);
}
$alt = null;
if(!$neuAnlegen)
{
$alt = db_zeile($dbconn, "SELECT * FROM termin WHERE id = ?", array($terminid));
if($alt === false)
return beendeSpeichern($dbconn, error("Fehler beim ermitteln des Termins:<br />".mysqli_error($dbconn)));
if(!$alt)
return beendeSpeichern($dbconn, error("Dieser Termin existiert nicht!"));
}
// Folgetermin nur einmal und nur, wenn der Termin gerade erledigt wird
$folgeId = $alt ? (int)$alt["followed_by"] : 0;
if($werte["done"] && $werte["folge"] && !$folgeId)
{
$folgeId = legeFolgeterminAn($dbconn, $werte, $adressid);
if($folgeId === false)
return beendeSpeichern($dbconn, 1);
}
$felder = array(
"head" => $werte["head"],
"text" => $werte["text"],
"adresse_id" => $adressid,
"typ" => $werte["typ"],
"done" => $werte["done"],
"termdauer" => $dauer,
"changeuser" => $_SESSION["user"],
"user" => $werte["user"],
"user2" => $werte["user2"],
"user3" => $werte["user3"],
"user4" => $werte["user4"],
"followed_by" => $folgeId,
);
$zuweisung = array();
foreach(array_keys($felder) as $spalte)
$zuweisung[] = "`$spalte` = ?";
$set = implode(", ", $zuweisung);
$err = 0;
if(!$neuAnlegen)
{
$sql = "UPDATE termin SET $set, termtime = ?, changetime = NOW() WHERE id = ?";
$params = array_merge(array_values($felder), array(terminTag($start, 0), $terminid));
if(!db_abfrage($dbconn, $sql, $params))
return beendeSpeichern($dbconn, error("Fehler beim ändern des Termins:<br />".mysqli_error($dbconn)));
$altWerte = array();
foreach(array_keys($felder) as $spalte)
$altWerte[] = $alt[$spalte];
$err = saveLog($dbconn, db_sql_text($dbconn, $sql, $params)." [termin change]",
db_sql_text($dbconn, $sql, array_merge($altWerte, array($alt["termtime"], $terminid))), "ändern");
}
else
{
$mainId = 0;
foreach(range(0, $tage - 1) as $i)
{
if($err)
break;
$sql = "INSERT INTO termin SET $set, termtime = ?, main_id = ?, changetime = NOW(), datetime = NOW()";
$params = array_merge(array_values($felder), array(terminTag($start, $i), $mainId));
if(!db_abfrage($dbconn, $sql, $params))
{
$err = error("Fehler beim anlegen des Termins:<br />".mysqli_error($dbconn));
break;
}
$id = mysqli_insert_id($dbconn);
if(!$mainId)
{
// der erste Tag ist der Hauptsatz der Reihe
$mainId = $id;
db_abfrage($dbconn, "UPDATE termin SET main_id = ? WHERE id = ?", array($id, $id))
or $err = error("Fehler beim verknüpfen der Terminreihe:<br />".mysqli_error($dbconn));
}
if(!$err)
$err = saveLog($dbconn, db_sql_text($dbconn, $sql, $params)." [termin add, hat ID $id]",
"DELETE FROM termin WHERE id = $id", "anlegen");
}
}
return beendeSpeichern($dbconn, $err);
}
/* Löscht eine Terminreihe samt Nebenzeilen */
function loescheTerminreihe($dbconn, $id)
{
beginneSpeichern($dbconn);
$err = 0;
$beschreibung = db_wert($dbconn, "SELECT head FROM termin WHERE id = ?", array($id));
if($beschreibung === false)
return beendeSpeichern($dbconn, error("Fehler beim ermitteln des Termins:<br />".mysqli_error($dbconn)));
if($beschreibung === null)
return beendeSpeichern($dbconn, error("Dieser Termin existiert nicht!"));
// erst die Nebenzeilen, dann der Hauptsatz - sonst verliert main_id den Bezug
$sql = "DELETE FROM termin WHERE main_id = ? AND id != ?";
db_abfrage($dbconn, $sql, array($id, $id))
or $err = error("Konnte Nebentermine nicht löschen:<br />".mysqli_error($dbconn));
if(!$err)
{
$sql = "DELETE FROM termin WHERE id = ?";
db_abfrage($dbconn, $sql, array($id))
or $err = error("Konnte Haupttermin nicht löschen:<br />".mysqli_error($dbconn));
}
if(!$err)
$err = saveLog($dbconn, db_sql_text($dbconn, "DELETE FROM termin WHERE main_id = ?", array($id))." [$beschreibung]", "", "löschen");
return beendeSpeichern($dbconn, $err);
}
/* Markiert eine Terminreihe als erledigt ($erledigt = 1) oder offen (0) */
function setzeTerminErledigt($dbconn, $id, $erledigt)
{
$erledigt = $erledigt ? 1 : 0;
$vorhanden = db_wert($dbconn, "SELECT id FROM termin WHERE id = ?", array($id));
if($vorhanden === false)
return error("Fehler beim ermitteln des Termins:<br />".mysqli_error($dbconn));
if($vorhanden === null)
return error("Dieser Termin existiert nicht!");
beginneSpeichern($dbconn);
$err = 0;
$sql = "UPDATE termin SET done = ?, changetime = NOW(), changeuser = ? WHERE main_id = ? OR id = ?";
$params = array($erledigt, $_SESSION["user"], $id, $id);
db_abfrage($dbconn, $sql, $params)
or $err = error("Konnte den Termin nicht umstellen:<br />".mysqli_error($dbconn));
if(!$err)
$err = saveLog($dbconn, db_sql_text($dbconn, $sql, $params)." [termin erledigt]", "", "ändern");
return beendeSpeichern($dbconn, $err);
}
/* ---------------------------------------------------------------------
* Lesen
* ------------------------------------------------------------------ */
/*
* Der Termin für das Formular: die Reihe zu einer Hauptsatz-id wird zu
* einem Eintrag zusammengefasst, Anfang und Gesamtdauer eingeschlossen.
*/
function terminZumBearbeiten($dbconn, $id)
{
$res = db_zeile($dbconn, "SELECT MIN(termtime) AS termtime,
SEC_TO_TIME(SUM(TIME_TO_SEC(termdauer))) AS termdauer,
head, followed_by, MIN(id) AS id, main_id, typ, adresse_id,
done, user, user2, user3, user4, changeuser, act_date, text
FROM termin WHERE main_id = ? GROUP BY main_id", array($id));
if($res === false)
{
error("Fehler beim ermitteln des Termins:<br />".mysqli_error($dbconn));
return null;
}
if(!$res)
return null;
if($res["followed_by"] > 0)
{
$folge = db_zeile($dbconn, "SELECT DATE_FORMAT(act_date,'%Y-%m-%d') AS act_date, typ FROM termin WHERE id = ?",
array($res["followed_by"]));
if($folge)
{
$res["folgetyp"] = $folge["typ"];
$tage = (strtotime($folge["act_date"]) - strtotime(date("Y-m-d"))) / 86400;
if($tage > 0)
{
$res["folge"] = 1;
$res["folgedays"] = (int)$tage;
}
}
}
$res["termend"] = dauer_to_date($res["termtime"], $res["termdauer"]);
if(str_contains($res["termend"], "00:00:00"))
$res["termend"] = date("Y-m-d H:i:s", strtotime("-1 minutes", strtotime($res["termend"])));
return $res;
}
/* Die Auswahllisten des Terminformulars */
function terminAuswahllisten($dbconn, $smarty)
{
list($typids, $typs) = auswahlliste($dbconn, "SELECT id, typ FROM termtyp WHERE id > 0");
$smarty->assign("typids", $typids);
$smarty->assign("typs", $typs);
$benutzer = db_zeilen($dbconn, "SELECT id, vname, nname FROM user WHERE blocked = 0 AND id > 0 ORDER BY nname, vname");
$ids = array();
$namen = array();
foreach((array)$benutzer as $b)
{
$ids[] = $b["id"];
$namen[] = trim($b["vname"]." ".$b["nname"]);
}
$smarty->assign("userids", $ids);
$smarty->assign("users", $namen);
$ids[] = 0;
$namen[] = "Keiner";
$smarty->assign("optuserids", $ids);
$smarty->assign("optusers", $namen);
$smarty->assign("days", range(0, 40));
list($urlaubIds, $urlaubNamen) = auswahlliste($dbconn, "SELECT id, typ FROM termtyp WHERE typ = 'Urlaub' OR typ = 'Krankheit'");
$smarty->assign("vac_types_id", $urlaubIds);
$smarty->assign("vac_types_name", $urlaubNamen);
}
/* Die Adresse, an der ein Termin stattfindet - leer, wenn es keine gibt */
function terminAdresse($dbconn, $adressid)
{
$res = db_zeile($dbconn, "SELECT plz, ortsteil, ort, str, hnr, vname, partner_id, nname, vname2, nname2,
firma, adresse.id, land, anrede_id, titel_id, anrede2_id, titel2_id
FROM adresse
LEFT JOIN strasse ON str_id = strasse.id
LEFT JOIN ort ON ort_id = ort.id
LEFT JOIN land ON land_id = land.id
WHERE adresse.id = ?", array($adressid));
if($res === false)
{
error("Fehler beim abrufen der Daten:<br />".mysqli_error($dbconn));
return array();
}
return $res ? $res : array();
}
/* ---------------------------------------------------------------------
* Kalender
* ------------------------------------------------------------------ */
/* Deutsche Wochentagsnamen - unabhängig davon, ob die Locale vorhanden ist */
function wochentag($zeitpunkt)
{
$namen = array("Sonntag", "Montag", "Dienstag", "Mittwoch", "Donnerstag", "Freitag", "Samstag");
return $namen[(int)date("w", $zeitpunkt)];
}
/*
* Die Termine eines Tages. $ganztags trennt die ganztägigen (Dauer über
* zwölf Stunden) von den übrigen.
*/
function kalenderTermine($dbconn, $tag, $ganztags)
{
$vergleich = $ganztags ? ">" : "<";
$sql = "SELECT termin.id, termtyp.typ, termin.head, termtime, termdauer,
user, user2, user3, user4, done, partner_id, main_id,
str, ort, ortsteil, plz, hnr, adresse.vname AS vname, adresse.nname AS nname,
user.vname AS uservname, user.nname AS usernname,
user2.vname AS uservname2, user2.nname AS usernname2,
user3.vname AS uservname3, user3.nname AS usernname3,
user4.vname AS uservname4, user4.nname AS usernname4,
firma, vname2, nname2
FROM termin
LEFT JOIN termtyp ON termin.typ = termtyp.id
LEFT JOIN adresse ON termin.adresse_id = adresse.id
LEFT JOIN partner ON adresse.partner_id = partner.id
LEFT JOIN strasse ON adresse.str_id = strasse.id
LEFT JOIN ort ON strasse.ort_id = ort.id
LEFT JOIN user ON termin.user = user.id
LEFT JOIN user AS user2 ON termin.user2 = user2.id
LEFT JOIN user AS user3 ON termin.user3 = user3.id
LEFT JOIN user AS user4 ON termin.user4 = user4.id
WHERE DATE(termtime) = ? AND termdauer $vergleich '12:00:00'
ORDER BY termtime ASC";
return db_zeilen($dbconn, $sql, array($tag)) ?: array();
}
/* Die an einem Tag gebuchten Arbeiten aus der Projektverwaltung */
function kalenderArbeiten($dbconn, $tag)
{
$sql = "SELECT work.id, project.id AS projectid, work.bezeichn AS head, work.rechn AS done,
project.name AS projectname, work.datetime AS termtime, work.std AS termdauer,
project.changeuser AS user, project.partner_id,
adresse.vname AS vname, adresse.nname AS nname, adresse.firma AS firma
FROM work
LEFT JOIN project ON work.project_id = project.id
LEFT JOIN partner ON project.partner_id = partner.id
LEFT JOIN adresse ON adresse.partner_id = partner.id
LEFT JOIN user ON work.changeuser = user.id
WHERE adresse.typ_id = 1 AND DATE(work.datetime) = ?
ORDER BY work.datetime ASC";
return db_zeilen($dbconn, $sql, array($tag)) ?: array();
}
/* Wer an einem Tag gestempelt hat und ob der Tag abgeschlossen ist */
function kalenderAnwesenheit($dbconn, $tag)
{
$sql = "SELECT user.vname, user.nname, user.color,
CASE MAX(zeiten.gehen) WHEN MAX(zeiten.kommen) THEN 0 ELSE 1 END AS complete
FROM user
LEFT JOIN zeiten ON user.id = zeiten.ma_id
WHERE DATE(kommen) = ?
GROUP BY user.id ORDER BY user.nname ASC";
return db_zeilen($dbconn, $sql, array($tag)) ?: array();
}
/*
* Die acht Tage einer Kalenderseite, beginnend einen Tag vor dem
* Seitenanfang (so ist der Vortag immer sichtbar).
*/
function kalenderTage($dbconn, $seite)
{
$heute = strtotime("today");
$tage = array();
for($i = $seite * 8 - 1; $i < $seite * 8 + 7; $i++)
{
$zeitpunkt = strtotime("+".$i." day", $heute);
$datum = date("Y-m-d", $zeitpunkt);
$feiertag = feiertag_bayern((int)date("d", $zeitpunkt), (int)date("m", $zeitpunkt), (int)date("Y", $zeitpunkt));
$termine = array();
if(($_SESSION["firma"] ?? "") == "Ingenieurbüro Wagner")
$termine = kalenderArbeiten($dbconn, $datum);
$termine = array_merge($termine, kalenderTermine($dbconn, $datum, false));
$tage[] = array(
// am Feiertag steht der Name des Feiertags statt des Wochentags
"weekday" => ($feiertag ? date("d.m.Y", $zeitpunkt)."<br/>" : wochentag($zeitpunkt)."<br/>".date("d.m.Y", $zeitpunkt)),
"date" => date("Ymd", $zeitpunkt),
"termine" => $termine,
"fulldaytermine" => kalenderTermine($dbconn, $datum, true),
"numfulldaytermine" => 0,
"ma_check" => kalenderAnwesenheit($dbconn, $datum),
"feiertag" => $feiertag ? $feiertag : "",
);
}
return $tage;
}
/* Die Mitarbeiter der Kalenderspalte samt Resturlaub */
function kalenderBenutzer($dbconn)
{
$users = array();
$zeilen = db_zeilen($dbconn, "SELECT id, vname, nname, color FROM user WHERE blocked = 0 AND nick != 'ALL'");
foreach((array)$zeilen as $res)
{
$res["openvacation"] = urlaub_remaining($dbconn, $res["id"]);
$users[$res["id"]] = $res;
}
return $users;
}
?>