loreal-global-kickoff/EmailService.php
DJP c88cee98e6 Add comprehensive email sending logging
Added detailed logging for SMTP email process:
- Log recipient, subject, service type
- Log SMTP connection details (host, port, user)
- Log connection status
- Log each SMTP command step
- Log success/failure with clear markers
- Log full exception stack traces

Now easy to troubleshoot email issues by checking logs for:
'===== EMAIL SEND START ====='
and
'===== EMAIL SEND END ====='

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-18 11:51:39 -05:00

288 lines
9.2 KiB
PHP

<?php
/**
* Email Service
* Handles email notifications via Mailgun
*/
class EmailService {
private $config;
private $enabled;
public function __construct() {
$appConfig = require __DIR__ . '/config.php';
$this->config = $appConfig['email'];
$this->enabled = $this->config['enabled'] ?? false;
// Load email templates
require_once __DIR__ . '/EmailTemplates.php';
}
/**
* Send email via Mailgun API or SMTP
*/
public function send($to, $subject, $text, $html = null) {
if (!$this->enabled) {
error_log('Email not sent (service disabled): ' . $subject);
return ['success' => true, 'message' => 'Email disabled'];
}
$service = $this->config['service'] ?? 'mailgun';
if ($service === 'smtp') {
return $this->sendViaSMTP($to, $subject, $text, $html);
} else {
return $this->sendViaMailgunAPI($to, $subject, $text, $html);
}
}
/**
* Send templated email
*/
public function sendTemplate($to, $templateName, $data) {
$html = EmailTemplates::getTemplate($templateName, $data);
// Extract subject from HTML title
preg_match('/<h1[^>]*>(.*?)<\/h1>/', $html, $matches);
$subject = $matches[1] ?? 'L\'Oréal OMG Assistant Notification';
// Create plain text version from data
$text = strip_tags($subject) . "\n\n" . json_encode($data, JSON_PRETTY_PRINT);
return $this->send($to, $subject, $text, $html);
}
/**
* Send email via Mailgun API
*/
private function sendViaMailgunAPI($to, $subject, $text) {
try {
$url = 'https://api.mailgun.net/v3/' . $this->config['domain'] . '/messages';
$postData = [
'from' => $this->config['from'],
'to' => $to,
'subject' => $subject,
'text' => $text
];
$ch = curl_init($url);
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => $postData,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_USERPWD => 'api:' . $this->config['mailgun_api_key'],
CURLOPT_HTTPAUTH => CURLAUTH_BASIC,
CURLOPT_TIMEOUT => 10
]);
$response = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
$curlError = curl_error($ch);
curl_close($ch);
if ($curlError) {
error_log('Email send failed: ' . $curlError);
return [
'success' => false,
'error' => 'Email delivery failed',
'details' => $curlError
];
}
if ($httpCode !== 200) {
error_log('Email send failed (HTTP ' . $httpCode . '): ' . $response);
return [
'success' => false,
'error' => 'Email service error',
'details' => 'HTTP ' . $httpCode,
'httpCode' => $httpCode
];
}
error_log('Email sent successfully to ' . $to);
return [
'success' => true,
'httpCode' => $httpCode
];
} catch (Exception $e) {
error_log('Email exception: ' . $e->getMessage());
return [
'success' => false,
'error' => 'Email exception',
'details' => $e->getMessage()
];
}
}
/**
* Send email via SMTP
*/
private function sendViaSMTP($to, $subject, $text, $html = null) {
try {
error_log('===== EMAIL SEND START =====');
error_log('To: ' . $to);
error_log('Subject: ' . $subject);
error_log('Service: SMTP');
$from = $this->config['from'];
$host = $this->config['smtp_host'];
$port = $this->config['smtp_port'];
$username = $this->config['smtp_username'];
$password = $this->config['smtp_password'];
error_log('SMTP Host: ' . $host . ':' . $port);
error_log('SMTP User: ' . $username);
error_log('From: ' . $from);
// Build email message with multipart if HTML provided
$boundary = md5(time());
$headers = "From: {$from}\r\n";
$headers .= "Reply-To: {$from}\r\n";
$headers .= "X-Mailer: PHP/" . phpversion() . "\r\n";
$headers .= "MIME-Version: 1.0\r\n";
if ($html) {
$headers .= "Content-Type: multipart/alternative; boundary=\"{$boundary}\"\r\n";
} else {
$headers .= "Content-Type: text/plain; charset=UTF-8\r\n";
}
// Create SMTP connection
error_log('Connecting to SMTP server...');
$socket = fsockopen($host, $port, $errno, $errstr, 10);
if (!$socket) {
error_log('SMTP connection failed: ' . $errstr . ' (errno: ' . $errno . ')');
return [
'success' => false,
'error' => 'SMTP connection failed',
'details' => $errstr
];
}
error_log('SMTP connection established');
// SMTP conversation
$this->smtpCommand($socket, null, 220); // Wait for greeting
$this->smtpCommand($socket, "EHLO " . $host, 250);
$this->smtpCommand($socket, "AUTH LOGIN", 334);
$this->smtpCommand($socket, base64_encode($username), 334);
$this->smtpCommand($socket, base64_encode($password), 235);
$this->smtpCommand($socket, "MAIL FROM: <{$from}>", 250);
$this->smtpCommand($socket, "RCPT TO: <{$to}>", 250);
$this->smtpCommand($socket, "DATA", 354);
// Send email data
$message = "Subject: {$subject}\r\n";
$message .= $headers;
$message .= "\r\n";
if ($html) {
// Multipart message
$message .= "--{$boundary}\r\n";
$message .= "Content-Type: text/plain; charset=UTF-8\r\n\r\n";
$message .= $text . "\r\n\r\n";
$message .= "--{$boundary}\r\n";
$message .= "Content-Type: text/html; charset=UTF-8\r\n\r\n";
$message .= $html . "\r\n\r\n";
$message .= "--{$boundary}--\r\n";
} else {
// Plain text only
$message .= $text . "\r\n";
}
$message .= ".\r\n";
error_log('Sending email data...');
$this->smtpCommand($socket, $message, 250);
error_log('Sending QUIT command...');
$this->smtpCommand($socket, "QUIT", 221);
fclose($socket);
error_log('✅ Email sent successfully via SMTP to ' . $to);
error_log('===== EMAIL SEND END (SUCCESS) =====');
return [
'success' => true,
'method' => 'smtp'
];
} catch (Exception $e) {
error_log('❌ SMTP exception: ' . $e->getMessage());
error_log('Stack trace: ' . $e->getTraceAsString());
error_log('===== EMAIL SEND END (FAILED) =====');
return [
'success' => false,
'error' => 'SMTP exception',
'details' => $e->getMessage()
];
}
}
/**
* Send SMTP command and check response
*/
private function smtpCommand($socket, $command, $expectedCode) {
if ($command !== null) {
fwrite($socket, $command . "\r\n");
}
$response = '';
while ($line = fgets($socket, 515)) {
$response .= $line;
if (substr($line, 3, 1) === ' ') {
break;
}
}
$code = intval(substr($response, 0, 3));
if ($code !== $expectedCode) {
throw new Exception("SMTP Error: Expected {$expectedCode}, got {$code}. Response: {$response}");
}
return $response;
}
/**
* Send process started notification
*/
public function notifyStarted($userEmail, $data) {
return $this->sendTemplate($userEmail, 'global_to_local_started', $data);
}
/**
* Send process completed notification
*/
public function notifyCompleted($userEmail, $data) {
return $this->sendTemplate($userEmail, 'global_to_local_complete', $data);
}
/**
* Send error notification
*/
public function notifyError($userEmail, $data) {
return $this->sendTemplate($userEmail, 'global_to_local_failed', $data);
}
/**
* Send asset submission success notification
*/
public function notifyAssetSubmissionSuccess($userEmail, $data) {
return $this->sendTemplate($userEmail, 'asset_submission_success', $data);
}
/**
* Send asset submission failed notification
*/
public function notifyAssetSubmissionFailed($userEmail, $data) {
return $this->sendTemplate($userEmail, 'asset_submission_failed', $data);
}
}