280 lines
14 KiB
PHP
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']) ?>&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>
|