This commit is contained in:
2026-02-04 23:31:53 +01:00
commit cd59ccd99b
2401 changed files with 56808 additions and 0 deletions

View File

@@ -0,0 +1,57 @@
<?php
use MintyPHP\Router;
use MintyPHP\Support\Guard;
use MintyPHP\Service\MailLogService;
use MintyPHP\Service\PermissionService;
Guard::requireLogin();
Guard::requirePermissionOrForbidden(PermissionService::MAIL_LOG_VIEW);
$limit = (int) ($_GET['limit'] ?? 10);
$offset = (int) ($_GET['offset'] ?? 0);
$search = trim((string) ($_GET['search'] ?? ''));
$order = (string) ($_GET['order'] ?? 'created_at');
$dir = (string) ($_GET['dir'] ?? 'desc');
$status = trim((string) ($_GET['status'] ?? ''));
$createdFrom = trim((string) ($_GET['created_from'] ?? ''));
$createdTo = trim((string) ($_GET['created_to'] ?? ''));
$result = MailLogService::listPaged([
'limit' => $limit,
'offset' => $offset,
'search' => $search,
'order' => $order,
'dir' => $dir,
'status' => $status,
'created_from' => $createdFrom,
'created_to' => $createdTo,
]);
$rows = [];
foreach ($result['rows'] as $row) {
$status = (string) ($row['status'] ?? '');
$statusBadge = 'neutral';
if ($status === 'sent') {
$statusBadge = 'success';
} elseif ($status === 'failed') {
$statusBadge = 'danger';
}
$rows[] = [
'id' => $row['id'] ?? null,
'to_email' => $row['to_email'] ?? '',
'subject' => $row['subject'] ?? '',
'template' => $row['template'] ?? '',
'status' => $status,
'status_badge' => $statusBadge,
'status_label' => t(ucfirst($status)),
'created_at' => dt($row['created_at'] ?? ''),
'sent_at' => dt($row['sent_at'] ?? ''),
];
}
Router::json([
'data' => $rows,
'total' => $result['total'] ?? 0,
]);

View File

@@ -0,0 +1,23 @@
<?php
use MintyPHP\Buffer;
use MintyPHP\Support\Guard;
use MintyPHP\Session;
use MintyPHP\Service\PermissionService;
Guard::requireLogin();
Guard::requirePermission(PermissionService::MAIL_LOG_VIEW);
$currentUserId = (int) ($_SESSION['user']['id'] ?? 0);
if ($currentUserId > 0) {
PermissionService::getUserPermissions($currentUserId);
}
Buffer::set('title', t('Mail logs'));
Buffer::set('grid_lang', json_encode(gridLang(), JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES));
$csrfKey = Session::$csrfSessionKey;
$csrfToken = $_SESSION[$csrfKey] ?? '';
Buffer::set(
'grid_csrf',
json_encode(['key' => $csrfKey, 'token' => $csrfToken], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES)
);

View File

@@ -0,0 +1,174 @@
<?php
use MintyPHP\Router;
?>
<div class="app-breadcrumb">
<a href="/admin">Startseite</a><i class="bi bi-chevron-right"></i><?php e(t('Mail logs')); ?>
</div>
<div class="app-list-titlebar">
<h1><?php e(t('Mail logs')); ?></h1>
<div class="app-list-titlebar-actions">
<button
class="outline secondary"
type="button"
data-toolbar-toggle
data-toolbar-target="#mail-log-toolbar"
data-toolbar-text-show="<?php e(t('Show filters')); ?>"
data-toolbar-text-hide="<?php e(t('Hide filters')); ?>"
>
<i class="bi bi-funnel-fill"></i> <span data-toolbar-label><?php e(t('Hide filters')); ?></span>
</button>
</div>
</div>
<div class="app-list-toolbar" id="mail-log-toolbar">
<label class="app-field">
<span><?php e(t('Search')); ?></span>
<input type="search" id="mail-log-search" placeholder="<?php e(t('Search...')); ?>">
</label>
<label class="app-field">
<span><?php e(t('Status')); ?></span>
<select id="mail-log-status-filter">
<option value=""><?php e(t('All statuses')); ?></option>
<option value="sent"><?php e(t('Sent')); ?></option>
<option value="queued"><?php e(t('Queued')); ?></option>
<option value="failed"><?php e(t('Failed')); ?></option>
</select>
</label>
<label class="app-field">
<span><?php e(t('Created from')); ?></span>
<input type="date" id="mail-log-created-from">
</label>
<label class="app-field">
<span><?php e(t('Created to')); ?></span>
<input type="date" id="mail-log-created-to">
</label>
</div>
<div class="app-list-table">
<div id="mail-log-grid"></div>
</div>
<?php $gridFactoryVersion = @filemtime('web/js/core/app-grid-factory.js') ?: time(); ?>
<script src="<?php e(asset('vendor/gridjs/gridjs.umd.js')); ?>"></script>
<script type="module">
import { initToolbarToggles } from "<?php e(asset('js/components/app-toggle-toolbar.js')); ?>";
import { createServerGrid } from "<?php e(asset('js/core/app-grid-factory.js?v=' . $gridFactoryVersion)); ?>";
import { buildUrl, badgeHtml } from "<?php e(asset('js/pages/app-list-utils.js')); ?>";
initToolbarToggles();
const appBase = "<?php e(localeBase()); ?>";
const initMailLogGrid = () => {
const formatBadge = (value) => badgeHtml(gridjs, value);
const gridConfig = createServerGrid({
gridjs: window.gridjs,
container: '#mail-log-grid',
dataUrl: 'admin/mail-log/data',
appBase,
columns: [
{
name: "<?php e(t('Created')); ?>",
sort: true,
formatter: (cell) => formatBadge(cell)
},
{
name: "<?php e(t('Recipient')); ?>",
sort: true,
formatter: (cell) => {
if (!cell || typeof cell !== 'object') {
return gridjs.html(String(cell ?? ''));
}
return gridjs.html(String(cell ?? ''));
}
},
{
name: "<?php e(t('Subject')); ?>",
sort: true
},
{
name: "<?php e(t('Template')); ?>",
sort: false
},
{
name: "<?php e(t('Status')); ?>",
sort: true,
formatter: (cell) => {
if (!cell || typeof cell !== 'object') {
return gridjs.html('');
}
const variant = cell.variant || 'neutral';
const label = cell.label || '';
return gridjs.html(`<span class="badge" data-variant="${variant}">${label}</span>`);
}
},
{
name: "ID",
hidden: true
}
],
sortColumns: ['created_at', 'to_email', 'subject', null, 'status', null],
paginationLimit: 10,
language: <?php MintyPHP\Buffer::get('grid_lang'); ?>,
mapData: (data) => data.data.map((row) => [
row.created_at,
row.to_email,
row.subject,
row.template || '-',
{
variant: row.status_badge,
label: row.status_label
},
row.id
]),
actions: {
enabled: true,
label: "<?php e(t('Actions')); ?>",
getRowId: (row) => row?.cells?.[5]?.data,
items: [
{
key: 'view',
type: 'link',
label: "<i class=\"bi bi-eye-fill\"></i>",
href: ({ id }) => buildUrl(appBase, `admin/mail-log/view/${id}`)
}
]
},
search: {
input: '#mail-log-search',
param: 'search',
debounce: 250
},
filters: [
{
input: '#mail-log-status-filter',
param: 'status'
},
{
input: '#mail-log-created-from',
param: 'created_from',
normalize: (value) => (value ? value : '')
},
{
input: '#mail-log-created-to',
param: 'created_to',
normalize: (value) => (value ? value : '')
}
],
urlSync: true,
rowDblClick: {
getUrl: (rowData) => {
const id = rowData?.cells?.[5]?.data;
return id ? new URL(`admin/mail-log/view/${id}`, appBase).toString() : '';
}
}
});
if (!gridConfig || !gridConfig.grid) {
console.warn('Mail log grid init failed');
return null;
}
return gridConfig;
};
initMailLogGrid();
</script>

View File

@@ -0,0 +1,25 @@
<?php
use MintyPHP\Buffer;
use MintyPHP\Support\Flash;
use MintyPHP\Support\Guard;
use MintyPHP\Router;
use MintyPHP\Service\MailLogService;
use MintyPHP\Service\PermissionService;
Guard::requireLogin();
Guard::requirePermission(PermissionService::MAIL_LOG_VIEW);
$currentUserId = (int) ($_SESSION['user']['id'] ?? 0);
if ($currentUserId > 0) {
PermissionService::getUserPermissions($currentUserId);
}
$mailLogId = (int) ($id ?? 0);
$mailLog = $mailLogId > 0 ? MailLogService::find($mailLogId) : null;
if (!$mailLog) {
Flash::error('Mail log not found', 'admin/mail-log', 'mail_log_not_found');
Router::redirect('admin/mail-log');
}
Buffer::set('title', t('View mail log'));

View File

@@ -0,0 +1,137 @@
<?php
/**
* @var array $mailLog
*/
$mailLog = $mailLog ?? [];
$status = (string) ($mailLog['status'] ?? '');
$statusBadge = 'neutral';
if ($status === 'sent') {
$statusBadge = 'success';
} elseif ($status === 'failed') {
$statusBadge = 'danger';
}
?>
<div class="app-details-container">
<section>
<div class="app-breadcrumb">
<a href="/admin">Startseite</a><i class="bi bi-chevron-right"></i><a
href="/admin/mail-log"><?php e(t('Mail logs')); ?></a><i class="bi bi-chevron-right"></i><?php e(t('View')); ?>
</div>
<div class="app-details-titlebar">
<h1>
<a href="admin/mail-log" title="<?php e(t('Back')); ?>"><i class="bi bi-arrow-left"></i></a>
<?php e(t('View mail log')); ?>
</h1>
</div>
<div class="app-details-content">
<details open>
<summary><?php e(t('Email details')); ?></summary>
<hr>
<div class="app-details-section">
<div class="grid">
<div class="app-form-group">
<label><?php e(t('Recipient')); ?></label>
<input type="text" value="<?php e($mailLog['to_email'] ?? ''); ?>" readonly>
</div>
<div class="app-form-group">
<label><?php e(t('Subject')); ?></label>
<input type="text" value="<?php e($mailLog['subject'] ?? ''); ?>" readonly>
</div>
</div>
<div class="app-form-group">
<label><?php e(t('Template')); ?></label>
<input type="text" value="<?php e($mailLog['template'] ?? '-'); ?>" readonly>
</div>
</div>
</details>
<?php if ($status === 'sent'): ?>
<hr>
<details open>
<summary><?php e(t('Delivery information')); ?></summary>
<hr>
<div class="app-details-section">
<div class="app-form-group">
<label><?php e(t('Sent at')); ?></label>
<input type="text" value="<?php e(dt($mailLog['sent_at'] ?? '')); ?>" readonly>
</div>
<?php if (!empty($mailLog['provider_message_id'])): ?>
<div class="app-form-group">
<label><?php e(t('Provider message ID')); ?></label>
<input type="text" value="<?php e($mailLog['provider_message_id'] ?? ''); ?>" readonly>
</div>
<?php endif; ?>
</div>
</details>
<?php endif; ?>
<?php if ($status === 'failed' && !empty($mailLog['error_message'])): ?>
<hr>
<details open>
<summary><?php e(t('Error information')); ?></summary>
<hr>
<div class="app-details-section">
<div class="app-form-group">
<label><?php e(t('Error message')); ?></label>
<textarea readonly rows="6"><?php e($mailLog['error_message'] ?? ''); ?></textarea>
</div>
</div>
</details>
<?php endif; ?>
</div>
</section>
<aside id="app-details-aside-section">
<div class="app-details-aside-section">
<hgroup>
<strong><?php e($mailLog['to_email'] ?? ''); ?></strong>
<p><small><?php e(t('Mail log')); ?></small></p>
</hgroup>
<hr>
<div class="grid">
<div>
<small><?php e(t('ID')); ?></small>
<p>
<span class="badge" data-variant="neutral">
<?php e($mailLog['id'] ?? '-'); ?>
</span>
</p>
</div>
<div>
<small>
<?php e(t('Status')); ?>
</small>
<div>
<span class="badge" data-variant="<?php e($statusBadge); ?>">
<?php e(t(ucfirst($status))); ?>
</span>
</div>
</div>
</div>
<div>
<small><?php e(t('Created')); ?></small>
<p><?php e(dt($mailLog['created_at'] ?? '') ?: '-'); ?></p>
</div>
<?php if ($status === 'sent' && !empty($mailLog['sent_at'])): ?>
<div>
<small><?php e(t('Sent')); ?></small>
<p><?php e(dt($mailLog['sent_at'] ?? '') ?: '-'); ?></p>
</div>
<?php endif; ?>
<?php if (!empty($mailLog['provider_message_id'])): ?>
<div class="grid">
<div>
<small><?php e(t('Provider message ID')); ?></small>
<span class="badge" data-copy="true" data-copy-value="<?php e($mailLog['provider_message_id']); ?>"
data-variant="neutral"><?php e(substr($mailLog['provider_message_id'], 0, 20)); ?></span>
</div>
</div>
<?php endif; ?>
</div>
</aside>
</div>