From e59ceaad999af02d6c149d89b3ef77ba1b5f9be3 Mon Sep 17 00:00:00 2001 From: Lars Gebhardt-Kusche Date: Wed, 10 Dec 2025 01:05:55 +0100 Subject: [PATCH] big update --- config/fileload.php | 26 -- partials/landingpage/admin/bridge.php | 123 +++------ partials/landingpage/admin/dashboard.php | 128 +++------ partials/landingpage/admin/profile.php | 93 ++----- partials/landingpage/admin/settings.php | 96 ++----- partials/structure/app_config.php | 40 +++ partials/structure/footer.php | 15 ++ partials/structure/header.php | 47 ++++ partials/structure/layout_end.php | 27 ++ partials/structure/layout_start.php | 57 ++++ partials/structure/matomo.php | 39 +++ src/functions.php | 320 +++++++++++++++++++++++ 12 files changed, 667 insertions(+), 344 deletions(-) create mode 100644 partials/structure/app_config.php create mode 100644 partials/structure/footer.php create mode 100644 partials/structure/header.php create mode 100644 partials/structure/layout_end.php create mode 100644 partials/structure/layout_start.php create mode 100644 partials/structure/matomo.php create mode 100644 src/functions.php diff --git a/config/fileload.php b/config/fileload.php index a3ade69..35854c4 100644 --- a/config/fileload.php +++ b/config/fileload.php @@ -68,32 +68,6 @@ if (!function_exists('get_version_badge_markup')) { } } -if (!defined('APP_VERSION_BADGE_REGISTERED')) { - register_shutdown_function(function () { - if (php_sapi_name() === 'cli') { - return; - } - $headers = headers_list(); - $isHtmlResponse = true; - if (!headers_sent() && !empty($headers)) { - $isHtmlResponse = false; - foreach ($headers as $header) { - $parts = explode(':', $header, 2); - $name = strtolower(trim($parts[0] ?? '')); - $value = strtolower(trim($parts[1] ?? '')); - if ($name === 'content-type' && strpos($value, 'text/html') !== false) { - $isHtmlResponse = true; - break; - } - } - } - if ($isHtmlResponse) { - echo get_version_badge_markup(); - } - }); - define('APP_VERSION_BADGE_REGISTERED', true); -} - // ----------------------------------------------------------- // set cookie / session parameters // ----------------------------------------------------------- diff --git a/partials/landingpage/admin/bridge.php b/partials/landingpage/admin/bridge.php index 5760499..c0bccba 100644 --- a/partials/landingpage/admin/bridge.php +++ b/partials/landingpage/admin/bridge.php @@ -1,66 +1,20 @@ + :root { color-scheme: light; } + .section-card{background:#fff;border:1px solid #e2e8f0;border-radius:1rem;padding:1.25rem;margin-bottom:1.5rem} + .section-card h4{margin:0 0 1rem;font-size:1rem;font-weight:600;color:#0f172a} + .input{width:100%;border:1px solid #cbd5f5;border-radius:.5rem;padding:.5rem .75rem} + .badge{display:inline-flex;align-items:center;padding:.1rem .5rem;border-radius:999px;font-size:.75rem;background:#e2e8f0;color:#0f172a} + .chip{display:inline-flex;align-items:center;padding:.15rem .55rem;border-radius:999px;background:#f1f5f9;color:#0f172a;border:1px solid #e2e8f0;font-size:.8rem} + +HTML; +require dirname(__DIR__) . '/../structure/layout_start.php'; ?> - - - - - - Email Template System – Bridge Setup - - - - - - - - - - - - -
-
- ← Administration -

Bridge Setup

-
-
- - -
-
-
-
- -
+

Bridge-Datei vorbereiten

@@ -154,13 +108,13 @@ $debugRedirect = isset($_GET['debug_redirect']);

-
+
- -
-

Beispiel: Mapping einer Config-Datei

-

Angenommen, deine ../config/database.php liefert folgendes Array:

-

+    
+      

Beispiel: Mapping einer Config-Datei

+

Angenommen, deine ../config/database.php liefert folgendes Array:

+
 [
@@ -177,20 +131,21 @@ return [
     ],
 ];
 PHP, ENT_QUOTES); ?>
-

Dann trägst du ein:

-
    -
  • Pfad zur Konfigurationsdatei: ../config/database.php
  • -
  • Basis-Pfad: database.connections.default
  • -
  • Host-/Port-/DB-/User-/Pass-/Charset-Key: jeweils host, port, database, username, password, charset
  • -
-

Die Bridge liest dann automatisch die Werte aus diesem Array und baut daraus den DSN.

-
- -
- -
- - - - - +

Dann trägst du ein:

+ +

Die Bridge liest dann automatisch die Werte aus diesem Array und baut daraus den DSN.

+
+ +
+ + + app_asset_url('/assets/js/toast.js')], + ['src' => app_asset_url('/assets/js/bridge-setup.js'), 'module' => true], +]; +require dirname(__DIR__) . '/../structure/layout_end.php'; diff --git a/partials/landingpage/admin/dashboard.php b/partials/landingpage/admin/dashboard.php index 27ae619..8cb92ee 100644 --- a/partials/landingpage/admin/dashboard.php +++ b/partials/landingpage/admin/dashboard.php @@ -1,72 +1,22 @@ + :root { color-scheme: light; } + .stat-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(180px,1fr));gap:1rem;margin-bottom:1.5rem} + .stat-card{background:#fff;border:1px solid #e2e8f0;border-radius:1rem;padding:1.25rem} + .stat-card h4{margin:0;font-size:.95rem;color:#475569} + .stat-card strong{display:block;font-size:1.75rem;color:#0f172a} + .section-card{background:#fff;border:1px solid #e2e8f0;border-radius:1rem;padding:1.25rem;margin-bottom:1.5rem} + .usage-table{width:100%;border-collapse:collapse;font-size:.9rem} + .usage-table th,.usage-table td{padding:.5rem;border-bottom:1px solid #e2e8f0;text-align:left} + +HTML; +require dirname(__DIR__) . '/../structure/layout_start.php'; ?> - - - - - - Email Template System – Dashboard - - - - - - - - - - - - -
-
- ← Übersicht -

Dashboard

-
-
- - -
-
-
-
- -
+

Templates

@@ -84,41 +34,41 @@ $debugRedirect = isset($_GET['debug_redirect']);

Snippets

-
-

Aufrufe gesamt

- -
-
+
-
-

Template-Nutzung

-

Wie oft wurden Templates über die API geladen? Setze einzelne Zähler bei Bedarf zurück.

-
- +

Aktivität

+ +
+
    +
    + +
    +
    +

    Nutzung & Speicher

    + Letzte 30 Tage
    - - - - + + + + - - - +
    TemplateAufrufeZuletzt verwendetAktionenDatumAktionenTemplates veröffentlichtVersandtests
    Lade Daten…
    - - - - - + app_asset_url('/assets/js/toast.js')], + ['src' => app_asset_url('/assets/js/dashboard.js'), 'module' => true], +]; +require dirname(__DIR__) . '/../structure/layout_end.php'; diff --git a/partials/landingpage/admin/profile.php b/partials/landingpage/admin/profile.php index 8d15096..7111dee 100644 --- a/partials/landingpage/admin/profile.php +++ b/partials/landingpage/admin/profile.php @@ -1,73 +1,19 @@ + :root { color-scheme: light; } + .section-card{background:#fff;border:1px solid #e2e8f0;border-radius:1rem;padding:1.25rem;margin-bottom:1.5rem} + .section-card h4{margin:0 0 1rem;font-size:1rem;font-weight:600;color:#0f172a} + .input{width:100%;border:1px solid #cbd5f5;border-radius:.5rem;padding:.5rem .75rem} + .user-tabs{display:flex;gap:.5rem;margin-bottom:1.25rem} + +HTML; +require dirname(__DIR__) . '/../structure/layout_start.php'; ?> - - - - - - Email Template System – Konto - - - - - - - - - - - - -
    -
    - ← Übersicht -

    Mein Konto

    -
    -
    - - -
    -
    -
    -
    - -
    +
    @@ -109,8 +55,9 @@ $debugRedirect = isset($_GET['debug_redirect']);
    - - - - - + app_asset_url('/assets/js/toast.js')], + ['src' => app_asset_url('/assets/js/account.js'), 'module' => true], +]; +require dirname(__DIR__) . '/../structure/layout_end.php'; diff --git a/partials/landingpage/admin/settings.php b/partials/landingpage/admin/settings.php index db09fd2..cefbdb8 100644 --- a/partials/landingpage/admin/settings.php +++ b/partials/landingpage/admin/settings.php @@ -1,71 +1,22 @@ + :root { color-scheme: light; } + .section-card{background:#fff;border:1px solid #e2e8f0;border-radius:1rem;padding:1.25rem;margin-bottom:1.5rem} + .section-card h4{margin:0 0 1rem;font-size:1rem;font-weight:600;color:#0f172a} + .input{width:100%;border:1px solid #cbd5f5;border-radius:.5rem;padding:.5rem .75rem} + .team-table{width:100%;border-collapse:collapse;font-size:.9rem} + .team-table th,.team-table td{padding:.35rem .5rem;border-bottom:1px solid #e2e8f0;text-align:left} + .badge{display:inline-flex;align-items:center;padding:.1rem .5rem;border-radius:999px;font-size:.75rem;background:#e2e8f0;color:#0f172a} + .chip{display:inline-flex;align-items:center;padding:.15rem .55rem;border-radius:999px;background:#f1f5f9;color:#0f172a;border:1px solid #e2e8f0;font-size:.8rem} + +HTML; +require dirname(__DIR__) . '/../structure/layout_start.php'; ?> - - - - - - Email Template System – Administration - - - - - - - - - - - - -
    -
    - ← Übersicht -

    Administration

    -
    -
    - - -
    -
    -
    -
    - -
    +

    Team

    @@ -171,7 +122,7 @@ $debugRedirect = isset($_GET['debug_redirect']);
    - Bridge-Setup & Tabellen öffnen + Bridge-Setup & Tabellen öffnen

    Dort kannst du Tabellen-Filter sowie DB-Quellen für die Bridge-Datei konfigurieren.

    @@ -186,8 +137,9 @@ $debugRedirect = isset($_GET['debug_redirect']);
    - - - - - + app_asset_url('/assets/js/toast.js')], + ['src' => app_asset_url('/assets/js/account.js'), 'module' => true], +]; +require dirname(__DIR__) . '/../structure/layout_end.php'; diff --git a/partials/structure/app_config.php b/partials/structure/app_config.php new file mode 100644 index 0000000..7131b89 --- /dev/null +++ b/partials/structure/app_config.php @@ -0,0 +1,40 @@ + rtrim($GLOBALS['app_base_url'] ?? ($scheme . '://' . $host), '/'), + 'app_api_base' => rtrim($GLOBALS['app_api_base'] ?? ($scheme . '://' . $host . '/api'), '/'), + 'asset_version' => defined('ASSET_VERSION') ? ASSET_VERSION : time(), + 'app_env' => $GLOBALS['app_env'] ?? (defined('APP_ENV') ? APP_ENV : 'prod'), + ]; + + $layoutContext['asset_base'] = $layoutContext['app_base_url'] ?: ''; + $layoutContext['debug_redirect'] = isset($_GET['debug_redirect']); + $GLOBALS['layoutContext'] = $layoutContext; +} + +$GLOBALS['layoutContext'] = $layoutContext; + +$appBaseUrl = $layoutContext['app_base_url']; +$appApiBase = $layoutContext['app_api_base']; +$assetBase = $layoutContext['asset_base']; +$assetVersion = $layoutContext['asset_version']; +$debugRedirect = $layoutContext['debug_redirect']; + +if (!function_exists('app_asset_url')) { + function app_asset_url(string $path, ?int $version = null): string + { + $base = $GLOBALS['layoutContext']['asset_base'] ?? ''; + $version = $version ?? ($GLOBALS['layoutContext']['asset_version'] ?? null); + $url = rtrim($base, '/') . $path; + if ($version !== null) { + $url .= (strpos($url, '?') === false ? '?' : '&') . 'v=' . rawurlencode((string)$version); + } + return $url; + } +} diff --git a/partials/structure/footer.php b/partials/structure/footer.php new file mode 100644 index 0000000..139d74e --- /dev/null +++ b/partials/structure/footer.php @@ -0,0 +1,15 @@ + +
    +
    + © Email Template System + + Version + + +
    +
    + +
    v
    + diff --git a/partials/structure/header.php b/partials/structure/header.php new file mode 100644 index 0000000..59eb980 --- /dev/null +++ b/partials/structure/header.php @@ -0,0 +1,47 @@ + 'dashboard', 'label' => 'Dashboard', 'href' => $appBaseUrl . '/admin/dashboard.php'], + ['id' => 'settings', 'label' => 'Administration','href' => $appBaseUrl . '/admin/settings.php'], + ['id' => 'bridge', 'label' => 'Bridge Setup', 'href' => $appBaseUrl . '/admin/bridge.php'], + ['id' => 'profile', 'label' => 'Mein Konto', 'href' => $appBaseUrl . '/admin/profile.php'], +]; + +$navActive = $navActive ?? null; +?> + diff --git a/partials/structure/layout_end.php b/partials/structure/layout_end.php new file mode 100644 index 0000000..d3daa87 --- /dev/null +++ b/partials/structure/layout_end.php @@ -0,0 +1,27 @@ + + + + + + + + + + + + diff --git a/partials/structure/layout_start.php b/partials/structure/layout_start.php new file mode 100644 index 0000000..b6fed2e --- /dev/null +++ b/partials/structure/layout_start.php @@ -0,0 +1,57 @@ + + + + + + + <?= htmlspecialchars($pageTitle) ?> + + + + + + + + + + + + + + + +> + diff --git a/partials/structure/matomo.php b/partials/structure/matomo.php new file mode 100644 index 0000000..b8fdb20 --- /dev/null +++ b/partials/structure/matomo.php @@ -0,0 +1,39 @@ + + + + + + + + + diff --git a/src/functions.php b/src/functions.php new file mode 100644 index 0000000..99faf2c --- /dev/null +++ b/src/functions.php @@ -0,0 +1,320 @@ + $src, + 'defer' => $defer, + 'async' => $async, + 'type' => $type, + 'version' => $version, + ]; + + if ($pos === 'header') { + $GLOBALS['page_header_scripts'][] = $data; + } else { + $GLOBALS['page_footer_scripts'][] = $data; + } +} + +/** + * CSS registrieren – MIT Cache-Buster und Priorität + * + * @param string $href Pfad zur CSS-Datei oder externe URL + * @param string $pos aktuell nur 'header' relevant (aber für Symmetrie drin) + * @param string $priority 'early' | 'normal' | 'late' + * @param string|null $version null = nutze ASSET_VERSION (falls definiert), + * '' = kein ?v=, + * 'xyz'= erzwinge diese Version + */ +function tpl_add_style( + string $href, + string $pos = 'header', + string $priority = 'normal', + ?string $version = null +): void { + if ($version === null && defined('ASSET_VERSION')) { + $version = ASSET_VERSION; + } + + $GLOBALS['page_styles'][] = [ + 'href' => $href, + 'pos' => $pos, + 'priority' => $priority, + 'version' => $version, + ]; +} + +/* ------------------------------------------------- + Template Loader + ------------------------------------------------- */ + +function tpl(string $file, string $type = 'structure', string $site = 'main'): void +{ + $base = __DIR__ . '/../partials/'; + + if (preg_match('/[^a-zA-Z0-9_\-]/', $file)) { + echo ""; + return; + } + if (preg_match('/[^a-zA-Z0-9_\-]/', $type)) { + echo ""; + return; + } + if (preg_match('/[^a-zA-Z0-9_\-]/', $site)) { + echo ""; + return; + } + + if ($type === 'landing') { + $path = $base . "landing/$site/$file.php"; + } else { + $path = $base . "structure/$file.php"; + } + + extract($GLOBALS, EXTR_SKIP); + + if (file_exists($path)) { + include $path; + } else { + echo ""; + } +} + +/* ------------------------------------------------- + Flash-Messages + ------------------------------------------------- */ + +function flash_set(string $type, string $message, ?string $context = null): void +{ + if (session_status() !== PHP_SESSION_ACTIVE) { + @session_start(); + } + + $_SESSION['flash'] = [ + 'type' => $type, + 'message' => $message, + 'context' => $context, + ]; +} + +function flash_get(): ?array +{ + if (session_status() !== PHP_SESSION_ACTIVE) { + @session_start(); + } + + if (empty($_SESSION['flash']) || !is_array($_SESSION['flash'])) { + return null; + } + + $flash = $_SESSION['flash']; + unset($_SESSION['flash']); + + $flash['type'] = $flash['type'] ?? 'info'; + $flash['message'] = $flash['message'] ?? ''; + $flash['context'] = $flash['context'] ?? null; + + return $flash; +} + +/* ------------------------------------------------- + i18n Helper + ------------------------------------------------- */ + +/** + * interner Helper für dot-notation keys + */ +function _i18n_traverse_array(array $data, string $key) +{ + $segments = explode('.', $key); + $node = $data; + + foreach ($segments as $seg) { + if (!is_array($node) || !array_key_exists($seg, $node)) { + return null; + } + $node = $node[$seg]; + } + + return $node; +} + +/** + * Hauptfunktion i18n_get + */ +function i18n_get(string $path, $default = null, array $replacements = []): string +{ + if (!isset($GLOBALS['i18n']) || !is_array($GLOBALS['i18n'])) { + return $default !== null ? (string)$default : ''; + } + + $current = $GLOBALS['i18n']['current'] ?? []; + $fallback = $GLOBALS['i18n']['fallback'] ?? []; + + $value = _i18n_traverse_array($current, $path); + + if ($value === null && !empty($fallback)) { + $value = _i18n_traverse_array($fallback, $path); + } + + if (!is_string($value)) { + return $default !== null ? (string)$default : ''; + } + + $text = $value; + + // built-in placeholder + $builtIn = [ + '{year}' => date('Y'), + '{{primary_domain}}' => function_exists('app_primary_domain') ? app_primary_domain() : '', + '{{primary_url}}' => function_exists('app_primary_url') ? app_primary_url() : '', + ]; + + foreach ($builtIn as $ph => $val) { + $text = str_replace($ph, $val, $text); + } + + foreach ($replacements as $key => $val) { + $val = (string)$val; + $text = str_replace('{' . $key . '}', $val, $text); + $text = str_replace('{{' . $key . '}}', $val, $text); + } + + return $text; +} + +/** + * Kurzform für i18n_get + */ +function i18n_get_fmt(string $path, $default = '', array $vars = []): string +{ + $def = $default ?? ''; + return i18n_get($path, $def, $vars); +}