Fix remaining code review findings: email transport errors, metadata payload, from-name, pagination

- Email::send(): add curl_error() check so transport failures (timeout,
  DNS, TLS) return a diagnosable error string instead of Unknown error
- Email::send(): strip metadata key from options before array_merge so
  non-API fields are never sent to CyberMail endpoint
- Email::send() + sendEmail(): include from-name in From field using
  RFC 5322 "Name <email>" format so fromName DB setting takes effect
- email-log.php: replace unbounded page-link loop with a windowed
  paginator (first/last 2 pages + ±2 around current) with ellipsis
  gaps — prevents hundreds of anchors rendering at scale
This commit is contained in:
2026-06-03 06:01:44 +00:00
parent a5b91ca0ea
commit 7eddf9e85d
3 changed files with 32 additions and 8 deletions
+19 -5
View File
@@ -217,16 +217,30 @@ $statusBadge = [
</div>
<!-- Pagination -->
<?php if ($totalPages > 1): ?>
<div style="display:flex;justify-content:center;gap:8px;margin-top:20px;">
<?php for ($i = 1; $i <= $totalPages; $i++): ?>
<a href="?page=<?= $i ?>&search=<?= urlencode($search) ?>&status=<?= urlencode($statusFilter) ?>&customer=<?= urlencode($customerFilter) ?>"
<?php if ($totalPages > 1):
$window = 2;
$pages = [];
for ($i = 1; $i <= $totalPages; $i++) {
if ($i <= $window || $i > $totalPages - $window || abs($i - $page) <= $window) {
$pages[] = $i;
}
}
$pages = array_unique($pages);
sort($pages);
$qs = '&search=' . urlencode($search) . '&status=' . urlencode($statusFilter) . '&customer=' . urlencode($customerFilter);
?>
<div style="display:flex;justify-content:center;gap:8px;margin-top:20px;flex-wrap:wrap;">
<?php $prev = null; foreach ($pages as $i):
if ($prev !== null && $i - $prev > 1): ?>
<span style="padding:6px 4px;color:#9CA3AF;">…</span>
<?php endif; ?>
<a href="?page=<?= $i ?><?= $qs ?>"
style="padding:6px 12px;border-radius:4px;text-decoration:none;
background:<?= $i === $page ? '#FF5E1A' : '#f3f4f6' ?>;
color:<?= $i === $page ? 'white' : '#374151' ?>;">
<?= $i ?>
</a>
<?php endfor; ?>
<?php $prev = $i; endforeach; ?>
</div>
<?php endif; ?>
</div>
+12 -2
View File
@@ -57,12 +57,14 @@ class Email {
}
public function send(string $to, string $subject, string $html, ?string $text = null, array $options = []): array {
// Strip internal-only keys that are not CyberMail API fields
$apiOptions = array_diff_key($options, array_flip(['metadata']));
$payload = array_merge([
'from' => $this->fromEmail,
'from' => $this->fromName . ' <' . $this->fromEmail . '>',
'to' => $to,
'subject' => $subject,
'html' => $html,
], $options);
], $apiOptions);
if ($text) $payload['text'] = $text;
$ch = curl_init('https://platform.cyberpersons.com/email/v1/send');
@@ -76,8 +78,16 @@ class Email {
]);
$response = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
$curlErr = curl_error($ch);
curl_close($ch);
if ($curlErr) {
error_log('[TJJ Email] cURL error: ' . $curlErr);
$result = ['success' => false, 'error' => $curlErr, 'code' => 0];
$this->logEmail($to, $subject, $html, $result, $options);
return $result;
}
$body = json_decode($response, true);
if ($httpCode === 202) {
$result = ['success' => true, 'message_id' => $body['data']['message_id'] ?? null];
+1 -1
View File
@@ -336,7 +336,7 @@ function sendEmail($to, $subject, $htmlContent, $textContent = '') {
error_log('[TJJ sendEmail] CYBERMAIL_API_KEY not configured');
return false;
}
$payload = ['from' => $from, 'to' => $to, 'subject' => $subject, 'html' => $htmlContent];
$payload = ['from' => $fromName . ' <' . $from . '>', 'to' => $to, 'subject' => $subject, 'html' => $htmlContent];
if ($textContent) $payload['text'] = $textContent;
$ch = curl_init('https://platform.cyberpersons.com/email/v1/send');
curl_setopt_array($ch, [