From 76bf967bd04cd9197c6e667bf9968aab1a24b9e5 Mon Sep 17 00:00:00 2001 From: Myron Blair Date: Fri, 29 May 2026 18:49:15 +0000 Subject: [PATCH] Switch email to CyberMail API; integrations page manages API key via DB --- admin/email-log.php | 234 +++++++++++++++++++++ admin/integrations.php | 88 ++++---- api/test-notification.php | 2 +- includes/email.php | 425 +++++++++++++++++--------------------- includes/functions.php | 34 ++- 5 files changed, 479 insertions(+), 304 deletions(-) create mode 100644 admin/email-log.php diff --git a/admin/email-log.php b/admin/email-log.php new file mode 100644 index 0000000..3968b86 --- /dev/null +++ b/admin/email-log.php @@ -0,0 +1,234 @@ +checkDeliveryStatus($messageId); + if (!empty($data)) { + $statusMap = ['queued'=>'sent','sent'=>'sent','delivered'=>'delivered','bounced'=>'bounced','failed'=>'failed']; + db()->update('email_log', [ + 'status' => $statusMap[$data['status']] ?? 'unknown', + 'opened' => $data['opened'] ?? 0, + 'opened_at' => !empty($data['opened_at']) ? date('Y-m-d H:i:s', strtotime($data['opened_at'])) : null, + 'open_count' => $data['open_count'] ?? 0, + 'clicked' => $data['clicked'] ?? 0, + 'click_count' => $data['click_count'] ?? 0, + 'status_checked_at'=> date('Y-m-d H:i:s'), + ], 'id = :id', ['id' => $logId]); + } + } + header('Location: /admin/email-log.php' . (!empty($_POST['customer_filter']) ? '?customer=' . urlencode($_POST['customer_filter']) : '')); + exit; +} + +// Filters +$customerFilter = trim($_GET['customer'] ?? ''); +$statusFilter = trim($_GET['status'] ?? ''); +$search = trim($_GET['search'] ?? ''); +$page = max(1, (int)($_GET['page'] ?? 1)); +$perPage = 25; +$offset = ($page - 1) * $perPage; + +$where = []; +$params = []; + +if ($customerFilter) { + $where[] = 'l.customer_id = :customer_id'; + $params['customer_id'] = $customerFilter; +} +if ($statusFilter) { + $where[] = 'l.status = :status'; + $params['status'] = $statusFilter; +} +if ($search) { + $where[] = '(l.recipient_email LIKE :search OR l.subject LIKE :search)'; + $params['search'] = '%' . $search . '%'; +} + +$whereClause = $where ? 'WHERE ' . implode(' AND ', $where) : ''; + +$total = db()->fetch( + "SELECT COUNT(*) as cnt FROM email_log l $whereClause", + $params +)['cnt'] ?? 0; + +$logs = db()->fetchAll( + "SELECT l.*, c.name as customer_name + FROM email_log l + LEFT JOIN customers c ON l.customer_id = c.customer_id + $whereClause + ORDER BY l.sent_at DESC + LIMIT $perPage OFFSET $offset", + $params +); + +$totalPages = max(1, ceil($total / $perPage)); + +// Status badge helper +$statusBadge = [ + 'sent' => ['bg'=>'#3B82F6','label'=>'Sent'], + 'delivered' => ['bg'=>'#10B981','label'=>'Delivered'], + 'bounced' => ['bg'=>'#EF4444','label'=>'Bounced'], + 'failed' => ['bg'=>'#EF4444','label'=>'Failed'], + 'unknown' => ['bg'=>'#9CA3AF','label'=>'Unknown'], +]; +?> + +
+
+

Email Log

+
+ emails total +
+
+ + +
+
+ + +
+
+ + +
+ + +
+ + Filtered by customer   + × + +
+ +
+ + Reset +
+
+ + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
RecipientSubjectPreviewStatusOpenedClickedSent AtActions
No emails found.
+
+ + + + + + + +
+ + +
+ +
+ + + + + + +
+ +
+ + + ✓ 1 ? '('.$log['open_count'].')' : '' ?> + + + + + + + 1 ? '('.$log['click_count'].')' : '' ?> + + + + +
+ + +
checked + +
+ +
+ + + + + + + +
+ +
+
+ + + 1): ?> +
+ + + + + +
+ +
+ + diff --git a/admin/integrations.php b/admin/integrations.php index bf3664c..1d7ade8 100644 --- a/admin/integrations.php +++ b/admin/integrations.php @@ -3,33 +3,31 @@ * Tom's Java Jive - Admin Integrations Settings */ -// Auth only — no HTML yet require_once __DIR__ . '/../includes/auth.php'; AdminAuth::require(); -// Handle POST before any output if ($_SERVER['REQUEST_METHOD'] === 'POST') { $section = $_POST['section'] ?? ''; - if ($section === 'cybermail') { - setSetting('cybermail_api_key', trim($_POST['cybermail_api_key'] ?? '')); - setSetting('cybermail_from_email', trim($_POST['cybermail_from_email'] ?? '')); - setSetting('cybermail_from_name', trim($_POST['cybermail_from_name'] ?? '')); - setSetting('email_notifications_enabled', isset($_POST['email_notifications_enabled']) ? '1' : '0'); - setFlash('success', 'CyberMail settings saved.'); + if ($section === 'email') { + setSetting('cybermail_api_key', trim($_POST['cybermail_api_key'] ?? '')); + setSetting('cybermail_from_email', trim($_POST['cybermail_from_email'] ?? '')); + setSetting('cybermail_from_name', trim($_POST['cybermail_from_name'] ?? '')); + setSetting('email_notifications_enabled', isset($_POST['email_notifications_enabled']) ? '1' : '0'); + setFlash('success', 'Email settings saved.'); } if ($section === 'twilio') { - setSetting('twilio_account_sid', trim($_POST['twilio_account_sid'] ?? '')); - setSetting('twilio_auth_token', trim($_POST['twilio_auth_token'] ?? '')); - setSetting('twilio_phone_number', trim($_POST['twilio_phone_number'] ?? '')); + setSetting('twilio_account_sid', trim($_POST['twilio_account_sid'] ?? '')); + setSetting('twilio_auth_token', trim($_POST['twilio_auth_token'] ?? '')); + setSetting('twilio_phone_number', trim($_POST['twilio_phone_number'] ?? '')); setSetting('sms_notifications_enabled', isset($_POST['sms_notifications_enabled']) ? '1' : '0'); setFlash('success', 'Twilio settings saved.'); } if ($section === 'push') { - setSetting('vapid_public_key', trim($_POST['vapid_public_key'] ?? '')); - setSetting('vapid_private_key', trim($_POST['vapid_private_key'] ?? '')); + setSetting('vapid_public_key', trim($_POST['vapid_public_key'] ?? '')); + setSetting('vapid_private_key', trim($_POST['vapid_private_key'] ?? '')); setSetting('push_notifications_enabled', isset($_POST['push_notifications_enabled']) ? '1' : '0'); setFlash('success', 'Push notification settings saved.'); } @@ -43,23 +41,21 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') { exit; } -// --- GET: render page --- ob_start(); $pageTitle = 'Integrations'; $currentPage = 'integrations'; require_once __DIR__ . '/includes/header.php'; -// Load current settings -$cm = [ - 'api_key' => getSetting('cybermail_api_key', ''), - 'from_email' => getSetting('cybermail_from_email', 'noreply@tomsjavajive.com'), - 'from_name' => getSetting('cybermail_from_name', "Tom's Java Jive"), - 'enabled' => getSetting('email_notifications_enabled', '0'), +$email = [ + 'api_key' => getSetting('cybermail_api_key', ''), + 'from' => getSetting('cybermail_from_email', 'noreply@tomsjavajive.com'), + 'from_name' => getSetting('cybermail_from_name', "Tom's Java Jive"), + 'enabled' => getSetting('email_notifications_enabled', '0'), ]; $tw = [ - 'sid' => getSetting('twilio_account_sid', ''), - 'token' => getSetting('twilio_auth_token', ''), - 'phone' => getSetting('twilio_phone_number', ''), + 'sid' => getSetting('twilio_account_sid', ''), + 'token' => getSetting('twilio_auth_token', ''), + 'phone' => getSetting('twilio_phone_number', ''), 'enabled' => getSetting('sms_notifications_enabled', '0'), ]; $push = [ @@ -75,10 +71,10 @@ $loyaltyEnabled = getSetting('loyalty_enabled', '1') === '1'; .integration-header { display: flex; justify-content: space-between; align-items: center; padding: 1.25rem 1.5rem; border-bottom: 1px solid var(--admin-border); } .integration-title { display: flex; align-items: center; gap: 1rem; } .integration-icon { width: 48px; height: 48px; border-radius: 10px; display: flex; align-items: center; justify-content: center; font-size: 1.5rem; } -.integration-icon.cybermail { background: #0ea5e9; color: white; } -.integration-icon.twilio { background: #F22F46; color: white; } -.integration-icon.push { background: #8B5CF6; color: white; } -.integration-icon.loyalty { background: #F59E0B; color: white; } +.integration-icon.email { background: #0ea5e9; color: white; } +.integration-icon.twilio { background: #F22F46; color: white; } +.integration-icon.push { background: #8B5CF6; color: white; } +.integration-icon.loyalty { background: #F59E0B; color: white; } .integration-body { padding: 1.5rem; } .status-badge { padding: .375rem .75rem; border-radius: 20px; font-size: .75rem; font-weight: 600; } .status-badge.configured { background: rgba(16,185,129,.1); color: var(--admin-success); } @@ -99,49 +95,51 @@ $loyaltyEnabled = getSetting('loyalty_enabled', '1') === '1';
- +
-
+
-

CyberMail

-

Transactional email — order confirmations, shipping updates

+

Email — CyberMail

+

Transactional email — order confirmations, shipping updates, password resets

- - - + + +
- +
+ value="" placeholder="sk_live_...">

Manage at CyberMail Dashboard

- +
- +
- +
@@ -296,7 +294,7 @@ $loyaltyEnabled = getSetting('loyalty_enabled', '1') === '1';