|
|
@@ -69,18 +69,18 @@ function escape($value)
|
|
|
*/
|
|
|
function generateCsrfToken()
|
|
|
{
|
|
|
- if (empty($_SESSION['csrf_token'])) {
|
|
|
- $_SESSION['csrf_token'] = bin2hex(random_bytes(32));
|
|
|
+ if (empty($_SESSION["csrf_token"])) {
|
|
|
+ $_SESSION["csrf_token"] = bin2hex(random_bytes(32));
|
|
|
}
|
|
|
- return $_SESSION['csrf_token'];
|
|
|
+ return $_SESSION["csrf_token"];
|
|
|
}
|
|
|
|
|
|
function validateCsrfToken($token)
|
|
|
{
|
|
|
- if (empty($_SESSION['csrf_token'])) {
|
|
|
+ if (empty($_SESSION["csrf_token"])) {
|
|
|
return false;
|
|
|
}
|
|
|
- return hash_equals($_SESSION['csrf_token'], $token);
|
|
|
+ return hash_equals($_SESSION["csrf_token"], $token);
|
|
|
}
|
|
|
|
|
|
function csrfField()
|
|
|
@@ -130,7 +130,7 @@ function setFlashMessage($key, $type, $message)
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
- $_SESSION['flash_messages'][$key] = [
|
|
|
+ $_SESSION["flash_messages"][$key] = [
|
|
|
"type" => $type,
|
|
|
"message" => $message,
|
|
|
];
|
|
|
@@ -143,7 +143,7 @@ function consumeFlashMessage($key)
|
|
|
return null;
|
|
|
}
|
|
|
|
|
|
- $messages = $_SESSION['flash_messages'] ?? [];
|
|
|
+ $messages = $_SESSION["flash_messages"] ?? [];
|
|
|
if (
|
|
|
!is_array($messages) ||
|
|
|
!isset($messages[$key]) ||
|
|
|
@@ -153,7 +153,7 @@ function consumeFlashMessage($key)
|
|
|
}
|
|
|
|
|
|
$message = $messages[$key];
|
|
|
- unset($_SESSION['flash_messages'][$key]);
|
|
|
+ unset($_SESSION["flash_messages"][$key]);
|
|
|
|
|
|
$type = trim((string) ($message["type"] ?? ""));
|
|
|
$text = trim((string) ($message["message"] ?? ""));
|
|
|
@@ -447,6 +447,39 @@ function getCategoryLabels($categoryIds)
|
|
|
return $labels;
|
|
|
}
|
|
|
|
|
|
+function getCategoryChipPalette($categoryId)
|
|
|
+{
|
|
|
+ $categoryId = normalizeCategoryId($categoryId);
|
|
|
+ if ($categoryId === "") {
|
|
|
+ return [
|
|
|
+ "background" => "#ebe8df",
|
|
|
+ "border" => "#d0c8b5",
|
|
|
+ "text" => "#4b4b4b",
|
|
|
+ ];
|
|
|
+ }
|
|
|
+
|
|
|
+ $hash = crc32($categoryId);
|
|
|
+ if ($hash < 0) {
|
|
|
+ $hash = $hash * -1;
|
|
|
+ }
|
|
|
+
|
|
|
+ // Spread hues across a distinct red -> blue range.
|
|
|
+ $hueSteps = [0, 16, 32, 48, 66, 84, 104, 128, 152, 176, 200, 224];
|
|
|
+ $hue = $hueSteps[$hash % count($hueSteps)];
|
|
|
+ $saturation = 44 + (($hash >> 8) % 10);
|
|
|
+ $lightness = 84 + (($hash >> 16) % 7);
|
|
|
+
|
|
|
+ $background = "hsl(" . $hue . ", " . $saturation . "%, " . $lightness . "%)";
|
|
|
+ $border = "hsl(" . $hue . ", " . ($saturation + 8) . "%, " . ($lightness - 16) . "%)";
|
|
|
+ $text = "hsl(" . $hue . ", " . ($saturation + 18) . "%, 24%)";
|
|
|
+
|
|
|
+ return [
|
|
|
+ "background" => $background,
|
|
|
+ "border" => $border,
|
|
|
+ "text" => $text,
|
|
|
+ ];
|
|
|
+}
|
|
|
+
|
|
|
function generateCategoryIdFromLabel($label, $existingCategories = [])
|
|
|
{
|
|
|
$baseId = normalizeCategoryId($label);
|
|
|
@@ -658,7 +691,9 @@ function saveProducts($products)
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- return writeJsonFile(PRODUCTS_FILE, ["products" => array_values($normalized)]);
|
|
|
+ return writeJsonFile(PRODUCTS_FILE, [
|
|
|
+ "products" => array_values($normalized),
|
|
|
+ ]);
|
|
|
}
|
|
|
|
|
|
function getFaqFilePath(): string
|
|
|
@@ -702,6 +737,24 @@ function saveFaqContent(string $markdown): bool
|
|
|
function renderFaqInlineMarkdown(string $text): string
|
|
|
{
|
|
|
$escaped = escape($text);
|
|
|
+ $escaped = preg_replace_callback(
|
|
|
+ '/\[([^\]]+)\]\(([^)\s]+)\)/',
|
|
|
+ function ($matches) {
|
|
|
+ $label = $matches[1];
|
|
|
+ $url = trim(html_entity_decode($matches[2], ENT_QUOTES, "UTF-8"));
|
|
|
+ if ($url === "") {
|
|
|
+ return $matches[0];
|
|
|
+ }
|
|
|
+
|
|
|
+ $scheme = strtolower((string) parse_url($url, PHP_URL_SCHEME));
|
|
|
+ if (!in_array($scheme, ["http", "https", "mailto"], true)) {
|
|
|
+ return $matches[0];
|
|
|
+ }
|
|
|
+
|
|
|
+ return '<a href="' . escape($url) . '">' . $label . "</a>";
|
|
|
+ },
|
|
|
+ $escaped,
|
|
|
+ );
|
|
|
$escaped = preg_replace(
|
|
|
"/\*\*(.+?)\*\*/s",
|
|
|
'<strong>$1</strong>',
|
|
|
@@ -944,6 +997,19 @@ function generateOrganizationIdFromLabel($label, $existingOrganizations = [])
|
|
|
|
|
|
function getDefaultSystemSettings()
|
|
|
{
|
|
|
+ $startpageIntroText = "";
|
|
|
+ if (defined("DISCLAIMER_LINES") && is_array(DISCLAIMER_LINES)) {
|
|
|
+ $lines = array_filter(
|
|
|
+ array_map(function ($line) {
|
|
|
+ return trim((string) $line);
|
|
|
+ }, DISCLAIMER_LINES),
|
|
|
+ function ($line) {
|
|
|
+ return $line !== "";
|
|
|
+ },
|
|
|
+ );
|
|
|
+ $startpageIntroText = implode("\n", $lines);
|
|
|
+ }
|
|
|
+
|
|
|
return [
|
|
|
"order_recipient_email" => defined("ORDER_RECIPIENT_EMAIL")
|
|
|
? ORDER_RECIPIENT_EMAIL
|
|
|
@@ -961,6 +1027,7 @@ function getDefaultSystemSettings()
|
|
|
)
|
|
|
? (bool) ATTACH_ORDER_PDF_TO_ADMIN_EMAIL
|
|
|
: true,
|
|
|
+ "startpage_intro_text" => $startpageIntroText,
|
|
|
];
|
|
|
}
|
|
|
|
|
|
@@ -986,6 +1053,10 @@ function normalizeSystemSettings($settings)
|
|
|
$expiryDays = 7;
|
|
|
}
|
|
|
|
|
|
+ $startpageIntroText = trim(
|
|
|
+ (string) ($settings["startpage_intro_text"] ?? $defaults["startpage_intro_text"]),
|
|
|
+ );
|
|
|
+
|
|
|
return [
|
|
|
"order_recipient_email" => $recipientEmail,
|
|
|
"order_confirmation_required" => !empty(
|
|
|
@@ -995,6 +1066,7 @@ function normalizeSystemSettings($settings)
|
|
|
"attach_order_pdf_to_admin_email" => !empty(
|
|
|
$settings["attach_order_pdf_to_admin_email"]
|
|
|
),
|
|
|
+ "startpage_intro_text" => $startpageIntroText,
|
|
|
];
|
|
|
}
|
|
|
|
|
|
@@ -1035,6 +1107,29 @@ function shouldAttachOrderPdfToAdminEmail()
|
|
|
return !empty($settings["attach_order_pdf_to_admin_email"]);
|
|
|
}
|
|
|
|
|
|
+function getStartpageIntroLines()
|
|
|
+{
|
|
|
+ $settings = getSystemSettings();
|
|
|
+ $text = trim((string) ($settings["startpage_intro_text"] ?? ""));
|
|
|
+ if ($text === "") {
|
|
|
+ return [];
|
|
|
+ }
|
|
|
+
|
|
|
+ $lines = preg_split('/\R+/', $text) ?: [];
|
|
|
+ $lines = array_values(
|
|
|
+ array_filter(
|
|
|
+ array_map(function ($line) {
|
|
|
+ return trim((string) $line);
|
|
|
+ }, $lines),
|
|
|
+ function ($line) {
|
|
|
+ return $line !== "";
|
|
|
+ },
|
|
|
+ ),
|
|
|
+ );
|
|
|
+
|
|
|
+ return $lines;
|
|
|
+}
|
|
|
+
|
|
|
function normalizeOrderItem($item)
|
|
|
{
|
|
|
if (!is_array($item)) {
|
|
|
@@ -1350,7 +1445,7 @@ function buildAbsoluteUrl($path)
|
|
|
}
|
|
|
|
|
|
$scheme = isHttpsRequest() ? "https" : "http";
|
|
|
- $host = $_SERVER['HTTP_HOST'] ?? "";
|
|
|
+ $host = $_SERVER["HTTP_HOST"] ?? "";
|
|
|
if ($host === "") {
|
|
|
return $path;
|
|
|
}
|
|
|
@@ -1361,20 +1456,20 @@ function buildAbsoluteUrl($path)
|
|
|
function isHttpsRequest(): bool
|
|
|
{
|
|
|
if (
|
|
|
- !empty($_SERVER['HTTPS']) &&
|
|
|
- strtolower((string) $_SERVER['HTTPS']) !== "off"
|
|
|
+ !empty($_SERVER["HTTPS"]) &&
|
|
|
+ strtolower((string) $_SERVER["HTTPS"]) !== "off"
|
|
|
) {
|
|
|
return true;
|
|
|
}
|
|
|
if (
|
|
|
- !empty($_SERVER['HTTP_X_FORWARDED_PROTO']) &&
|
|
|
- strtolower((string) $_SERVER['HTTP_X_FORWARDED_PROTO']) === "https"
|
|
|
+ !empty($_SERVER["HTTP_X_FORWARDED_PROTO"]) &&
|
|
|
+ strtolower((string) $_SERVER["HTTP_X_FORWARDED_PROTO"]) === "https"
|
|
|
) {
|
|
|
return true;
|
|
|
}
|
|
|
if (
|
|
|
- !empty($_SERVER['SERVER_PORT']) &&
|
|
|
- (int) $_SERVER['SERVER_PORT'] === 443
|
|
|
+ !empty($_SERVER["SERVER_PORT"]) &&
|
|
|
+ (int) $_SERVER["SERVER_PORT"] === 443
|
|
|
) {
|
|
|
return true;
|
|
|
}
|
|
|
@@ -1708,7 +1803,7 @@ function formatDate($dateString)
|
|
|
|
|
|
function getCart()
|
|
|
{
|
|
|
- $cart = $_SESSION['cart'] ?? [];
|
|
|
+ $cart = $_SESSION["cart"] ?? [];
|
|
|
if (!is_array($cart)) {
|
|
|
$cart = [];
|
|
|
}
|
|
|
@@ -1742,8 +1837,8 @@ function getCart()
|
|
|
];
|
|
|
}
|
|
|
|
|
|
- $_SESSION['cart'] = array_values($normalized);
|
|
|
- return $_SESSION['cart'];
|
|
|
+ $_SESSION["cart"] = array_values($normalized);
|
|
|
+ return $_SESSION["cart"];
|
|
|
}
|
|
|
|
|
|
function addCartItem($productId, $size = "")
|
|
|
@@ -1786,7 +1881,7 @@ function addCartItem($productId, $size = "")
|
|
|
}
|
|
|
|
|
|
$cart[$index]["size"] = $size;
|
|
|
- $_SESSION['cart'] = array_values($cart);
|
|
|
+ $_SESSION["cart"] = array_values($cart);
|
|
|
|
|
|
return [
|
|
|
"success" => true,
|
|
|
@@ -1801,7 +1896,7 @@ function addCartItem($productId, $size = "")
|
|
|
"size" => $size,
|
|
|
];
|
|
|
|
|
|
- $_SESSION['cart'] = array_values($cart);
|
|
|
+ $_SESSION["cart"] = array_values($cart);
|
|
|
return [
|
|
|
"success" => true,
|
|
|
"status" => "added",
|
|
|
@@ -1814,13 +1909,13 @@ function removeCartItemByIndex($index)
|
|
|
$cart = getCart();
|
|
|
if (isset($cart[$index])) {
|
|
|
unset($cart[$index]);
|
|
|
- $_SESSION['cart'] = array_values($cart);
|
|
|
+ $_SESSION["cart"] = array_values($cart);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
function clearCart()
|
|
|
{
|
|
|
- $_SESSION['cart'] = [];
|
|
|
+ $_SESSION["cart"] = [];
|
|
|
}
|
|
|
|
|
|
function getCartItemsDetailed()
|
|
|
@@ -2028,9 +2123,7 @@ function sendConfirmedOrderAdminNotification($order)
|
|
|
|
|
|
$subject = SITE_SERVICE_NAME . ": Neue Bestellung - " . $order["id"];
|
|
|
$intro =
|
|
|
- "<p>Eine neue Bestellung im " .
|
|
|
- escape(SITE_SERVICE_NAME) .
|
|
|
- " der Stadt Freising wurde freigegeben und muss bearbeitet werden.</p>";
|
|
|
+ "<p>Eine neue PSA-Bestellung wurde freigegeben und muss bearbeitet werden.</p>";
|
|
|
$message = buildOrderSummaryHtml($order, "Neue PSA-Bestellung", $intro);
|
|
|
|
|
|
$attachments = [];
|
|
|
@@ -2341,9 +2434,9 @@ function logError($message, $context = [], $level = "ERROR")
|
|
|
"level" => $level,
|
|
|
"message" => $message,
|
|
|
"context" => $context,
|
|
|
- "ip" => $_SERVER['REMOTE_ADDR'] ?? "unknown",
|
|
|
- "user_agent" => $_SERVER['HTTP_USER_AGENT'] ?? "unknown",
|
|
|
- "request_uri" => $_SERVER['REQUEST_URI'] ?? "unknown",
|
|
|
+ "ip" => $_SERVER["REMOTE_ADDR"] ?? "unknown",
|
|
|
+ "user_agent" => $_SERVER["HTTP_USER_AGENT"] ?? "unknown",
|
|
|
+ "request_uri" => $_SERVER["REQUEST_URI"] ?? "unknown",
|
|
|
"session_id" => session_id()
|
|
|
? substr(session_id(), 0, 8) . "..."
|
|
|
: "none",
|
|
|
@@ -2368,9 +2461,9 @@ function logAccess($message, $context = [])
|
|
|
"timestamp" => date("Y-m-d H:i:s.u"),
|
|
|
"message" => $message,
|
|
|
"context" => $context,
|
|
|
- "ip" => $_SERVER['REMOTE_ADDR'] ?? "unknown",
|
|
|
- "request_method" => $_SERVER['REQUEST_METHOD'] ?? "unknown",
|
|
|
- "request_uri" => $_SERVER['REQUEST_URI'] ?? "unknown",
|
|
|
+ "ip" => $_SERVER["REMOTE_ADDR"] ?? "unknown",
|
|
|
+ "request_method" => $_SERVER["REQUEST_METHOD"] ?? "unknown",
|
|
|
+ "request_uri" => $_SERVER["REQUEST_URI"] ?? "unknown",
|
|
|
];
|
|
|
|
|
|
$logLine = json_encode($entry, JSON_UNESCAPED_UNICODE) . PHP_EOL;
|