Files
desktop/partials/desktop/shell.php
Lars Gebhardt-Kusche 8c1ae513ea
All checks were successful
Deploy / deploy-staging (push) Successful in 26s
Deploy / deploy-production (push) Has been skipped
asdasd
2026-06-26 01:31:48 +02:00

267 lines
16 KiB
PHP

<?php
declare(strict_types=1);
$payloadJson = json_encode($desktopPayload, JSON_THROW_ON_ERROR | JSON_PRETTY_PRINT);
$activeSkin = $desktopPayload['meta']['active_skin'];
$baseSkinProfile = $desktopPayload['meta']['base_skin_profile'];
$skinProfile = $desktopPayload['meta']['skin_profile'];
$wallpaper = $desktopPayload['desktop']['wallpaper'];
$debugShellApp = is_array($desktopPayload['shell']['debug_app'] ?? null) ? $desktopPayload['shell']['debug_app'] : null;
$debugShellLabel = trim((string) ($debugShellApp['label'] ?? 'Debug')) ?: 'Debug';
$sessionDisplayName = (string) ($desktopPayload['session']['display_name'] ?? 'Gast');
$publicDesktopMode = (string) ($desktopPayload['desktop']['login']['mode'] ?? '') === 'public-desktop';
$loginUrl = (string) ($desktopPayload['desktop']['login']['login_url'] ?? '');
$loginAvailable = !empty($desktopPayload['desktop']['login']['login_available']) && $loginUrl !== '';
$assetVersion = static function (string $publicPath): string {
$filesystemPath = dirname(__DIR__, 2) . '/public' . $publicPath;
if (!is_file($filesystemPath)) {
return $publicPath;
}
$mtime = filemtime($filesystemPath);
return $mtime === false ? $publicPath : $publicPath . '?v=' . $mtime;
};
$nativeModuleStyles = [];
$nativeModuleScripts = [];
foreach ($desktopPayload['apps'] as $desktopApp) {
$assets = is_array($desktopApp['native_module']['assets'] ?? null) ? $desktopApp['native_module']['assets'] : [];
foreach ((array) ($assets['styles'] ?? []) as $style) {
if (!is_array($style) || trim((string) ($style['href'] ?? '')) === '') {
continue;
}
$href = (string) $style['href'];
$nativeModuleStyles[$href] = $style;
}
foreach ((array) ($assets['scripts'] ?? []) as $script) {
if (!is_array($script) || trim((string) ($script['src'] ?? '')) === '') {
continue;
}
$src = (string) $script['src'];
$nativeModuleScripts[$src] = $script;
}
}
$renderStartIcon = static function (array $profile): string {
$style = (string) ($profile['taskbar_icon_style'] ?? 'label');
return match ($style) {
'windows' => '<span class="start-glyph start-glyph-windows" aria-hidden="true"><span></span><span></span><span></span><span></span></span>',
'apple' => '<span class="start-glyph start-glyph-apple" aria-hidden="true"></span>',
'linux' => '<span class="start-glyph start-glyph-linux" aria-hidden="true"><span></span><span></span><span></span></span>',
default => '<span class="start-glyph start-glyph-label" aria-hidden="true">' . htmlspecialchars((string) ($profile['taskbar_icon'] ?? 'OS'), ENT_QUOTES) . '</span>',
};
};
$buildInitials = static function (string $value): string {
$normalized = trim($value);
if ($normalized === '') {
return 'GU';
}
$parts = preg_split('/\s+/', $normalized) ?: [];
$parts = array_values(array_filter($parts, static fn ($part) => trim((string) $part) !== ''));
if (count($parts) <= 1) {
return strtoupper(substr((string) ($parts[0] ?? $normalized), 0, 2));
}
return strtoupper(substr((string) $parts[0], 0, 1) . substr((string) $parts[1], 0, 1));
};
$renderTrayVisual = static function (array $trayItem, string $displayName = '') use ($buildInitials): string {
$trayId = (string) ($trayItem['id'] ?? '');
$appId = (string) ($trayItem['app_id'] ?? '');
if ($trayId === 'account') {
$initials = htmlspecialchars($buildInitials($displayName), ENT_QUOTES);
return '<span class="tray-avatar" aria-hidden="true">' . $initials . '</span><span class="sr-only">Profil</span>';
}
if ($appId === 'fx-rates') {
return '<span class="tray-icon tray-icon--banknote" aria-hidden="true"><svg viewBox="0 0 24 24" focusable="false"><rect x="3" y="6" width="18" height="12" rx="3"></rect><circle cx="12" cy="12" r="2.5"></circle><path d="M7 9h.01M17 15h.01"></path></svg></span><span class="sr-only">FX</span>';
}
if ($trayId === 'gitea-deploy-status') {
return '<span class="tray-status-dot" aria-hidden="true"></span><span class="sr-only">Gitea Deploy Status</span>';
}
return htmlspecialchars((string) ($trayItem['label'] ?? ''), ENT_QUOTES);
};
?><!DOCTYPE html>
<html lang="de">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title><?= htmlspecialchars($desktopPayload['meta']['title'], ENT_QUOTES) ?></title>
<link rel="stylesheet" href="<?= htmlspecialchars($assetVersion('/assets/desktop/desktop.css'), ENT_QUOTES) ?>">
<?php if (!empty($baseSkinProfile['css_path'])): ?>
<link rel="stylesheet" href="<?= htmlspecialchars($assetVersion((string) $baseSkinProfile['css_path']), ENT_QUOTES) ?>">
<?php endif; ?>
<?php if (!empty($skinProfile['css_path']) && $skinProfile['css_path'] !== $baseSkinProfile['css_path']): ?>
<link rel="stylesheet" href="<?= htmlspecialchars($assetVersion((string) $skinProfile['css_path']), ENT_QUOTES) ?>">
<?php endif; ?>
</head>
<body
data-skin="<?= htmlspecialchars($activeSkin, ENT_QUOTES) ?>"
data-skin-family="<?= htmlspecialchars((string) ($skinProfile['family'] ?? $activeSkin), ENT_QUOTES) ?>"
data-taskbar-mode="<?= htmlspecialchars((string) ($skinProfile['taskbar_mode'] ?? 'taskbar'), ENT_QUOTES) ?>"
data-window-controls="<?= htmlspecialchars((string) ($skinProfile['window_controls'] ?? 'windows'), ENT_QUOTES) ?>"
style="--wallpaper: <?= htmlspecialchars($wallpaper, ENT_QUOTES) ?>;"
>
<div class="desktop-shell">
<?php if (($skinProfile['top_bar'] ?? false) === true): ?>
<header class="system-bar">
<div class="system-bar-left">
<span class="system-brand"><?= htmlspecialchars((string) ($skinProfile['label'] ?? ''), ENT_QUOTES) ?></span>
<span class="system-divider"></span>
<span>Finder</span>
<span>Ablage</span>
<span>Bearbeiten</span>
<span>Fenster</span>
</div>
<div class="system-bar-right">
<?php if (($desktopPayload['session']['is_admin'] ?? false) === true): ?>
<button class="tray-pill tray-pill-button tray-pill-debug" id="debug-toggle-top" type="button" aria-expanded="false" title="<?= htmlspecialchars($debugShellLabel, ENT_QUOTES) ?>"><span class="tray-icon tray-icon--bug" aria-hidden="true"><svg viewBox="0 0 24 24" focusable="false"><path d="M9 7.5V6a3 3 0 0 1 6 0v1.5"></path><path d="M8 10.5h8a2 2 0 0 1 2 2V15a6 6 0 0 1-12 0v-2.5a2 2 0 0 1 2-2Z"></path><path d="M4 13h3M17 13h3M5.5 8.5l2 1.5M18.5 8.5l-2 1.5M7 18l-2 1.5M17 18l2 1.5"></path></svg></span><span class="sr-only"><?= htmlspecialchars($debugShellLabel, ENT_QUOTES) ?></span></button>
<?php endif; ?>
<time id="clock-top"></time>
</div>
</header>
<?php endif; ?>
<main class="desktop-stage">
<div class="desktop-icons" id="desktop-icons"></div>
<div class="widget-zone" id="widget-zone">
<?php foreach ($desktopPayload['widgets']['active'] as $widget): ?>
<article class="widget-card">
<div class="widget-card-header">
<div>
<h3><?= htmlspecialchars($widget['title'], ENT_QUOTES) ?></h3>
<p><?= htmlspecialchars($widget['summary'] ?? '', ENT_QUOTES) ?></p>
</div>
<span class="widget-badge"><?= htmlspecialchars($widget['icon'] ?? 'WG', ENT_QUOTES) ?></span>
</div>
<p><?= htmlspecialchars($widget['content'] ?? '', ENT_QUOTES) ?></p>
</article>
<?php endforeach; ?>
</div>
<div class="window-layer" id="window-layer"></div>
<?php if (!$publicDesktopMode): ?>
<section class="start-menu" id="start-menu" hidden>
<div class="start-menu-sidebar" id="start-menu-user-settings">
<button class="start-menu-user-card" id="start-menu-user-card" type="button">
<span class="start-menu-user-avatar" id="start-menu-user-avatar">GU</span>
<span class="start-menu-user-copy">
<strong id="start-menu-user-name"><?= htmlspecialchars((string) ($desktopPayload['session']['display_name'] ?? 'Gast'), ENT_QUOTES) ?></strong>
<small>Benutzer</small>
</span>
</button>
<div class="start-menu-function-list" id="start-menu-functions">
<button class="start-menu-function is-active" type="button" data-function-id="programs">
<span class="start-menu-function-icon" aria-hidden="true">📁</span>
<span class="start-menu-function-copy">
<strong>Programme</strong>
<small>Installierte Apps</small>
</span>
</button>
</div>
<?php if (($desktopPayload['session']['authenticated'] ?? false) === true && !empty($desktopPayload['session']['logout_url'])): ?>
<a class="start-menu-session-action is-logout" id="start-menu-session-action" href="<?= htmlspecialchars((string) $desktopPayload['session']['logout_url'], ENT_QUOTES) ?>">
<span class="start-menu-session-icon" aria-hidden="true">⏻</span>
<span>Abmelden</span>
</a>
<?php else: ?>
<button class="start-menu-session-action" id="start-menu-session-action" type="button" data-session-action="login">
<span class="start-menu-session-icon" aria-hidden="true">→</span>
<span>Anmelden</span>
</button>
<?php endif; ?>
</div>
<div class="start-menu-selection-area start-menu-selection-area--programs">
<div class="start-menu-selection-header" id="start-menu-selection-header"></div>
<div class="start-menu-selection-list" id="start-menu-apps"></div>
</div>
</section>
<?php endif; ?>
<footer class="taskbar<?= $publicDesktopMode ? ' taskbar--public' : '' ?>">
<?php if ($publicDesktopMode): ?>
<div class="taskbar-public-spacer" aria-hidden="true"></div>
<button
class="public-login-tab"
id="public-login-tab"
type="button"
<?= $loginAvailable ? 'data-login-url="' . htmlspecialchars($loginUrl, ENT_QUOTES) . '"' : 'disabled' ?>
>
<span class="public-login-tab-kicker">Desktop Login</span>
<strong><?= $loginAvailable ? 'Anmelden' : 'Login nicht verfuegbar' ?></strong>
</button>
<?php else: ?>
<button class="start-button" id="start-button" type="button">
<span class="start-button-icon"><?= $renderStartIcon($skinProfile) ?></span>
<span class="start-button-label"><?= htmlspecialchars((string) ($skinProfile['taskbar_label'] ?? 'Menu'), ENT_QUOTES) ?></span>
</button>
<div class="taskbar-apps" id="taskbar-apps"></div>
<div class="tray">
<?php foreach ($desktopPayload['tray'] as $trayItem): ?>
<button
class="tray-pill tray-pill-button<?= (($trayItem['id'] ?? '') === 'gitea-deploy-status') ? ' tray-pill-status tray-pill-status--idle' : '' ?><?= (($trayItem['id'] ?? '') === 'account') ? ' tray-pill-account' : '' ?>"
type="button"
data-tray-id="<?= htmlspecialchars((string) $trayItem['id'], ENT_QUOTES) ?>"
<?php if (!empty($trayItem['app_id'])): ?>data-tray-app-id="<?= htmlspecialchars((string) $trayItem['app_id'], ENT_QUOTES) ?>" title="<?= htmlspecialchars((string) ($trayItem['title'] ?? $trayItem['label'] ?? ''), ENT_QUOTES) ?>"<?php endif; ?>
<?php if (($trayItem['id'] ?? '') === 'account'): ?>title="<?= htmlspecialchars($sessionDisplayName, ENT_QUOTES) ?>"<?php elseif (empty($trayItem['app_id']) && !empty($trayItem['title'])): ?>title="<?= htmlspecialchars((string) $trayItem['title'], ENT_QUOTES) ?>"<?php endif; ?>
><?= $renderTrayVisual($trayItem, $sessionDisplayName) ?></button>
<?php endforeach; ?>
<?php if (($desktopPayload['session']['is_admin'] ?? false) === true): ?>
<button class="tray-pill tray-pill-button tray-pill-debug" id="debug-toggle" type="button" aria-expanded="false" title="<?= htmlspecialchars($debugShellLabel, ENT_QUOTES) ?>"><span class="tray-icon tray-icon--bug" aria-hidden="true"><svg viewBox="0 0 24 24" focusable="false"><path d="M9 7.5V6a3 3 0 0 1 6 0v1.5"></path><path d="M8 10.5h8a2 2 0 0 1 2 2V15a6 6 0 0 1-12 0v-2.5a2 2 0 0 1 2-2Z"></path><path d="M4 13h3M17 13h3M5.5 8.5l2 1.5M18.5 8.5l-2 1.5M7 18l-2 1.5M17 18l2 1.5"></path></svg></span><span class="sr-only"><?= htmlspecialchars($debugShellLabel, ENT_QUOTES) ?></span></button>
<?php endif; ?>
<time id="clock"></time>
</div>
<div class="tray-menu" id="tray-account-menu" hidden>
<button class="tray-menu-entry" type="button" data-account-action="settings">Einstellungen</button>
<?php if (($desktopPayload['session']['authenticated'] ?? false) === true && !empty($desktopPayload['session']['logout_url'])): ?>
<a class="tray-menu-entry" data-account-action="logout" href="<?= htmlspecialchars((string) $desktopPayload['session']['logout_url'], ENT_QUOTES) ?>">Logout</a>
<?php else: ?>
<button class="tray-menu-entry" type="button" data-account-action="login">Login</button>
<?php endif; ?>
</div>
<div class="tray-menu" id="tray-fx-menu" hidden>
<p class="tray-menu-copy" id="tray-fx-status">Kursstatus wird geladen ...</p>
<button class="tray-menu-entry" type="button" data-fx-action="refresh">Refresh</button>
<button class="tray-menu-entry" type="button" data-fx-action="force-refresh">Force-Refresh</button>
</div>
<?php endif; ?>
</footer>
</main>
</div>
<script id="desktop-payload" type="application/json"><?= $payloadJson ?></script>
<script src="<?= htmlspecialchars($assetVersion('/assets/desktop/desktop.js'), ENT_QUOTES) ?>"></script>
<?php if (!empty($baseSkinProfile['js_path'])): ?>
<script src="<?= htmlspecialchars($assetVersion((string) $baseSkinProfile['js_path']), ENT_QUOTES) ?>"></script>
<?php endif; ?>
<?php if (!empty($skinProfile['js_path']) && $skinProfile['js_path'] !== $baseSkinProfile['js_path']): ?>
<script src="<?= htmlspecialchars($assetVersion((string) $skinProfile['js_path']), ENT_QUOTES) ?>"></script>
<?php endif; ?>
<?php foreach ($nativeModuleStyles as $style): ?>
<link rel="stylesheet" href="<?= htmlspecialchars((string) $style['href'], ENT_QUOTES) ?>">
<?php endforeach; ?>
<?php foreach ($nativeModuleScripts as $script): ?>
<script
<?php if (!empty($script['crossorigin'])): ?>crossorigin<?php endif; ?>
src="<?= htmlspecialchars((string) $script['src'], ENT_QUOTES) ?>"
></script>
<?php endforeach; ?>
</body>
</html>