40061-vm/mailbox.php
Flatlogic Bot f9536ee19b 01
2026-05-24 07:14:49 +00:00

280 lines
14 KiB
PHP

<?php
declare(strict_types=1);
require_once __DIR__ . '/includes/app.php';
require_once __DIR__ . '/includes/pop3_client.php';
app_boot();
$accountId = (int) ($_GET['id'] ?? 0);
$account = $accountId > 0 ? find_mail_account($accountId) : null;
$flash = pull_flash();
$messages = [];
$selectedMessage = null;
$selectedNumber = max(0, (int) ($_GET['message'] ?? 0));
$syncError = null;
$totalRemoteMessages = 0;
$displayedMessages = 0;
$currentStatus = $account ? (string) ($account['last_status'] ?: 'Ready') : 'Ready';
$currentLastSync = $account['last_sync_at'] ?? null;
if ($account) {
$client = null;
try {
$password = decrypt_secret((string) $account['password_ciphertext']);
if ($password === '') {
throw new RuntimeException('Spremljena lozinka se ne može dešifrirati. Ponovno spremi mailbox račun.');
}
$client = new Pop3Client((string) $account['pop3_host'], (int) $account['pop3_port'], (string) $account['security_mode']);
$client->connect();
$client->login((string) $account['username'], $password);
$stats = $client->stat();
$totalRemoteMessages = (int) $stats['count'];
$messages = $client->fetchRecent((int) $account['sync_limit']);
$displayedMessages = count($messages);
if ($selectedNumber <= 0 && !empty($messages)) {
$selectedNumber = (int) $messages[0]['number'];
}
if ($selectedNumber > $totalRemoteMessages && !empty($messages)) {
$selectedNumber = (int) $messages[0]['number'];
}
if ($selectedNumber > 0 && $totalRemoteMessages > 0) {
$selectedMessage = $client->fetchMessage($selectedNumber);
}
$status = $totalRemoteMessages > 0 ? 'Connected' : 'Connected — empty mailbox';
update_mail_account_sync($accountId, $status, $totalRemoteMessages);
$currentStatus = $status;
$currentLastSync = gmdate('Y-m-d H:i:s');
} catch (Throwable $exception) {
$syncError = $exception->getMessage();
update_mail_account_sync($accountId, 'Sync failed', 0);
$currentStatus = 'Sync failed';
$currentLastSync = gmdate('Y-m-d H:i:s');
} finally {
if ($client instanceof Pop3Client) {
$client->quit();
}
}
}
$pageLabel = $account ? ($account['label'] . ' — Inbox') : 'Mailbox not found';
$pageTitle = project_name() . ' — ' . $pageLabel;
$projectBaseDescription = trim((string) ($_SERVER['PROJECT_DESCRIPTION'] ?? getenv('PROJECT_DESCRIPTION') ?: ''));
$pageDescription = $projectBaseDescription !== ''
? $projectBaseDescription . ' — Live POP3 inbox detail with message reading and fetched-list search.'
: 'Read the latest POP3 messages in a clean split-view inbox with server-side search helpers and mailbox status.';
$projectDescription = $projectBaseDescription !== '' ? $projectBaseDescription : $pageDescription;
$projectImageUrl = project_image_url();
?>
<!doctype html>
<html lang="hr">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title><?= h($pageTitle) ?></title>
<meta name="description" content="<?= h($pageDescription) ?>">
<?php if ($projectDescription): ?>
<meta property="og:description" content="<?= h($projectDescription) ?>">
<meta property="twitter:description" content="<?= h($projectDescription) ?>">
<?php endif; ?>
<?php if ($projectImageUrl): ?>
<meta property="og:image" content="<?= h($projectImageUrl) ?>">
<meta property="twitter:image" content="<?= h($projectImageUrl) ?>">
<?php endif; ?>
<meta property="og:title" content="<?= h($pageTitle) ?>">
<meta property="twitter:title" content="<?= h($pageTitle) ?>">
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap" rel="stylesheet">
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet">
<link rel="stylesheet" href="assets/css/custom.css?v=<?= h(asset_version('assets/css/custom.css')) ?>">
</head>
<body>
<?php if ($flash): ?>
<div class="toast-shell">
<div class="toast align-items-center border-0" role="status" aria-live="polite" aria-atomic="true">
<div class="d-flex">
<div class="toast-body"><?= h($flash['message']) ?></div>
<button type="button" class="btn-close me-2 m-auto" data-bs-dismiss="toast" aria-label="Close"></button>
</div>
</div>
</div>
<?php endif; ?>
<div class="container app-shell py-4 py-lg-5">
<nav class="navbar app-nav navbar-expand-lg">
<div class="container-fluid px-0">
<a class="navbar-brand d-flex align-items-center gap-3 m-0" href="index.php">
<span class="brand-mark">WM</span>
<span class="brand-copy">
<strong><?= h(project_name()) ?></strong>
<small>Mailbox detail</small>
</span>
</a>
<div class="d-flex flex-wrap align-items-center gap-2 ms-auto">
<a class="btn btn-sm btn-outline-secondary" href="index.php">Natrag na dashboard</a>
<?php if ($account): ?>
<a class="btn btn-sm btn-primary" href="mailbox.php?id=<?= h((string) $account['id']) ?>">Refresh inbox</a>
<?php endif; ?>
</div>
</div>
</nav>
<?php if (!$account): ?>
<div class="section-card empty-panel">
<h1 class="h4 mb-2">Mailbox nije pronađen.</h1>
<p class="empty-copy mb-3">Vrati se na dashboard i dodaj POP3 račun da bi otvorio inbox pregled.</p>
<a class="btn btn-primary" href="index.php">Idi na dashboard</a>
</div>
<?php else: ?>
<header class="hero-grid mb-4">
<section class="hero-card">
<div class="page-eyebrow mb-2">Detail</div>
<h1 class="hero-title mb-3"><?= h($account['label']) ?></h1>
<p class="section-subtitle"><?= h((string) ($account['email_address'] ?: $account['username'])) ?> · POP3 <?= h($account['pop3_host']) ?>:<?= h((string) $account['pop3_port']) ?> · <?= h(security_label((string) $account['security_mode'])) ?></p>
<div class="d-flex flex-wrap gap-2 mt-3">
<span class="status-badge <?= h(status_tone($currentStatus)) ?>"><?= h($currentStatus) ?></span>
<span class="soft-badge">Leave on server: <?= !empty($account['leave_on_server']) ? 'Yes' : 'No' ?></span>
<span class="soft-badge">Inbox only in this slice</span>
</div>
<ul class="nav nav-pills gap-2 mt-4">
<li class="nav-item"><span class="nav-link active">Inbox</span></li>
<li class="nav-item"><span class="nav-link disabled">Sent · next</span></li>
<li class="nav-item"><span class="nav-link disabled">Compose · next</span></li>
</ul>
</section>
<aside class="metrics-grid">
<div class="metric-card">
<div class="metric-label">Remote messages</div>
<div class="metric-value"><?= h((string) $totalRemoteMessages) ?></div>
<div class="metric-hint">Ukupan broj poruka koje je POP3 prijavio.</div>
</div>
<div class="metric-card">
<div class="metric-label">Displayed now</div>
<div class="metric-value"><?= h((string) $displayedMessages) ?></div>
<div class="metric-hint">Prikazujemo zadnjih <?= h((string) $account['sync_limit']) ?> poruka.</div>
</div>
<div class="metric-card">
<div class="metric-label">Last sync</div>
<div class="metric-value" style="font-size:1.1rem;"><?= h(format_datetime($currentLastSync)) ?></div>
<div class="metric-hint">Status se zapisuje u bazu pri svakom otvaranju inboxa.</div>
</div>
</aside>
</header>
<?php if ($syncError): ?>
<div class="alert alert-danger border-0 mb-4" role="alert">
<strong>Sinkronizacija nije uspjela.</strong> <?= h($syncError) ?>
<div class="small mt-2">Provjeri host, port, sigurnost veze i POP3 korisničke podatke. Za lokalni test često vrijedi 127.0.0.1:110 bez enkripcije.</div>
</div>
<?php endif; ?>
<main class="mailbox-shell">
<section class="section-card">
<div class="d-flex align-items-start justify-content-between gap-3 mb-3">
<div>
<div class="overline mb-1">List</div>
<h2 class="section-title">Inbox poruke</h2>
</div>
<span class="soft-badge">Search in fetched set</span>
</div>
<div class="message-search">
<input type="search" placeholder="Pretraži subject, from ili preview…" aria-label="Pretraži poruke" data-mail-search>
</div>
<?php if ($messages): ?>
<div class="mailbox-list">
<?php foreach ($messages as $message): ?>
<?php $isActive = (int) $message['number'] === $selectedNumber; ?>
<a
class="message-item<?= $isActive ? ' active' : '' ?>"
href="mailbox.php?id=<?= h((string) $account['id']) ?>&amp;message=<?= h((string) $message['number']) ?>"
data-mail-item
data-search="<?= h(strtolower($message['subject'] . ' ' . $message['from'] . ' ' . $message['preview'])) ?>"
>
<div class="d-flex justify-content-between gap-3 align-items-start">
<p class="message-subject"><?= h($message['subject']) ?></p>
<span class="message-date"><?= h($message['date'] !== '' ? $message['date'] : ('#' . $message['number'])) ?></span>
</div>
<div class="message-from"><?= h($message['from']) ?></div>
<div class="message-preview"><?= h($message['preview']) ?></div>
</a>
<?php endforeach; ?>
</div>
<div class="empty-panel d-none mt-3" data-mail-empty>
<p class="empty-copy mb-0">Nijedna poruka iz dohvaćenog seta ne odgovara trenutnoj pretrazi.</p>
</div>
<?php else: ?>
<div class="empty-panel">
<h3 class="h6 mb-2">Inbox je trenutno prazan.</h3>
<p class="empty-copy mb-0">Ako očekuješ poruke, klikni refresh ili provjeri da POP3 server zaista ima mail u sandučiću.</p>
</div>
<?php endif; ?>
</section>
<section class="section-card">
<div class="d-flex align-items-start justify-content-between gap-3 mb-3">
<div>
<div class="overline mb-1">Read / detail</div>
<h2 class="section-title">Detalj poruke</h2>
</div>
<span class="soft-badge">Server-rendered preview</span>
</div>
<?php if ($selectedMessage): ?>
<div class="message-header-grid">
<div class="detail-block">
<strong>Subject</strong>
<div><?= h($selectedMessage['subject']) ?></div>
</div>
<div class="detail-block">
<strong>From</strong>
<div><?= h($selectedMessage['from']) ?></div>
</div>
<div class="detail-block">
<strong>Date / POP3 #</strong>
<div><?= h($selectedMessage['date'] !== '' ? $selectedMessage['date'] : ('Message #' . $selectedMessage['number'])) ?></div>
</div>
</div>
<article class="mail-preview">
<div class="message-body"><?= nl2br(h($selectedMessage['body_text'])) ?></div>
</article>
<?php else: ?>
<div class="empty-panel">
<h3 class="h6 mb-2">Odaberi poruku s lijeve strane.</h3>
<p class="empty-copy mb-0">Kad odabereš mail, ovdje ćeš vidjeti subject, sender, datum i tekstualni sadržaj poruke.</p>
</div>
<?php endif; ?>
<div class="surface-muted p-3 rounded-4 border mt-3">
<div class="overline mb-2">Mailbox settings</div>
<div class="meta-list">
<div>
<strong>POP3 endpoint</strong>
<span><?= h($account['pop3_host']) ?>:<?= h((string) $account['pop3_port']) ?> · <?= h(security_label((string) $account['security_mode'])) ?></span>
</div>
<div>
<strong>Username</strong>
<span><?= h($account['username']) ?></span>
</div>
<div>
<strong>Saved in database</strong>
<span><?= h(format_datetime($account['created_at'])) ?></span>
</div>
</div>
</div>
</section>
</main>
<?php endif; ?>
</div>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js" defer></script>
<script src="assets/js/main.js?v=<?= h(asset_version('assets/js/main.js')) ?>" defer></script>
</body>
</html>