Sindbad~EG File Manager

Current Path : /home/copmadinaarea/thecopmadinaarea.org/portal/classes/
Upload File :
Current File : /home/copmadinaarea/thecopmadinaarea.org/portal/classes/EmailService.php

<?php
class EmailService {
    private $db;
    private $settings;
    
    public function __construct() {
        $this->db = Database::getInstance()->getConnection();
        $this->loadSettings();
    }
    
    private function loadSettings() {
        $stmt = $this->db->query("SELECT * FROM email_settings ORDER BY id DESC LIMIT 1");
        $this->settings = $stmt->fetch();
        
        // Default settings if none exist
        if (!$this->settings) {
            $this->settings = [
                'is_enabled' => 0,
                'smtp_host' => 'localhost',
                'smtp_port' => 587,
                'smtp_username' => '',
                'smtp_password' => '',
                'smtp_encryption' => 'tls',
                'from_email' => 'noreply@localhost',
                'from_name' => 'Church Management System',
                'send_welcome_user' => 1,
                'send_welcome_member' => 1,
                'user_welcome_subject' => 'Welcome to our Church Management System',
                'user_welcome_template' => '<h2>Welcome!</h2><p>Dear {name}, your account has been created.</p>',
                'member_welcome_subject' => 'Welcome to our Church Community',
                'member_welcome_template' => '<h2>Welcome!</h2><p>Dear {name}, welcome to our church community!</p>'
            ];
        }
    }
    
    public function isEnabled() {
        return $this->settings['is_enabled'] == 1;
    }
    
    public function queueEmail($recipientEmail, $recipientName, $subject, $body, $emailType = 'general') {
        if (!$this->isEnabled()) {
            return false;
        }
        
        try {
            $stmt = $this->db->prepare("
                INSERT INTO email_queue (recipient_email, recipient_name, subject, body, email_type) 
                VALUES (:email, :name, :subject, :body, :type)
            ");
            
            return $stmt->execute([
                'email' => $recipientEmail,
                'name' => $recipientName,
                'subject' => $subject,
                'body' => $body,
                'type' => $emailType
            ]);
        } catch (Exception $e) {
            error_log("Queue email error: " . $e->getMessage());
            return false;
        }
    }
    
    /**
     * Send email instantly without queueing (for time-sensitive emails like 2FA)
     */
    public function sendInstantEmail($recipientEmail, $recipientName, $subject, $body) {
        if (!$this->isEnabled()) {
            error_log("Email service is not enabled");
            return false;
        }
        
        try {
            // Create email data array
            $emailData = [
                'recipient_email' => $recipientEmail,
                'recipient_name' => $recipientName,
                'subject' => $subject,
                'body' => $body,
                'id' => null // No queue ID for instant send
            ];
            
            // Load Symfony Mailer if not already loaded
            if (!class_exists('Symfony\Component\Mailer\Mailer')) {
                if (file_exists(__DIR__ . '/../vendor/autoload.php')) {
                    require_once __DIR__ . '/../vendor/autoload.php';
                }
            }
            
            // Use Symfony Mailer if available
            if (class_exists('Symfony\Component\Mailer\Mailer')) {
                return $this->sendWithSymfonyMailer($emailData);
            } else {
                // Fallback to simulation mode
                error_log("Symfony Mailer not available, simulating send");
                return $this->simulateEmailSend($emailData);
            }
            
        } catch (Exception $e) {
            error_log("Instant email send error: " . $e->getMessage());
            return false;
        }
    }
    
    public function sendWelcomeUserEmail($userData) {
        if (!$this->isEnabled() || !$this->settings['send_welcome_user']) {
            return false;
        }
        
        $subject = $this->settings['user_welcome_subject'];
        $template = $this->settings['user_welcome_template'];
        
        // Replace template variables
        $variables = [
            '{name}' => $userData['full_name'] ?? $userData['first_name'] . ' ' . $userData['last_name'],
            '{email}' => $userData['email'],
            '{username}' => $userData['username'] ?? $userData['email'],
            '{login_url}' => BASE_URL . 'login.php'
        ];
        
        $body = str_replace(array_keys($variables), array_values($variables), $template);
        
        // Send instantly - welcome emails should arrive immediately
        return $this->sendInstantEmail(
            $userData['email'],
            $variables['{name}'],
            $subject,
            $body
        );
    }
    
    public function sendWelcomeMemberEmail($memberData) {
        if (!$this->isEnabled() || !$this->settings['send_welcome_member']) {
            return false;
        }
        
        $subject = $this->settings['member_welcome_subject'];
        $template = $this->settings['member_welcome_template'];
        
        // Replace template variables
        $variables = [
            '{name}' => $memberData['first_name'] . ' ' . $memberData['last_name'],
            '{email}' => $memberData['email'],
            '{member_id}' => $memberData['membershipcard_id'] ?? $memberData['id'],
            '{phone}' => $memberData['phone'] ?? 'N/A'
        ];
        
        $body = str_replace(array_keys($variables), array_values($variables), $template);
        
        // Send instantly - welcome emails should arrive immediately
        return $this->sendInstantEmail(
            $memberData['email'],
            $variables['{name}'],
            $subject,
            $body
        );
    }
    
    /**
     * Send password reset email instantly
     */
    public function sendPasswordResetEmail($email, $name, $resetToken, $userType = 'admin') {
        if (!$this->isEnabled()) {
            error_log("Password Reset Email Failed: Email service is not enabled");
            return false;
        }
        
        $resetUrl = BASE_URL . 'reset-password.php?token=' . $resetToken . '&type=' . $userType;
        
        $subject = "Password Reset Request";
        $message = "
            <h2>Password Reset Request</h2>
            <p>Dear {$name},</p>
            <p>We received a request to reset your password. Click the link below to reset your password:</p>
            <p style='margin: 20px 0;'>
                <a href='{$resetUrl}' style='display: inline-block; padding: 12px 24px; background: linear-gradient(135deg, #1E40AF 0%, #9333EA 100%); color: white; text-decoration: none; border-radius: 8px; font-weight: bold;'>
                    Reset Password
                </a>
            </p>
            <p>Or copy and paste this link in your browser:</p>
            <p style='word-break: break-all; color: #1E40AF;'>{$resetUrl}</p>
            <p>This link will expire in 1 hour.</p>
            <p>If you didn't request a password reset, please ignore this email or contact support if you have concerns.</p>
            <hr style='margin: 20px 0; border: none; border-top: 1px solid #e5e7eb;'>
            <p style='font-size: 12px; color: #6b7280;'>This is an automated email. Please do not reply.</p>
        ";
        
        error_log("Attempting to send password reset email to: " . $email);
        
        // Send instantly - password reset is time-sensitive
        $result = $this->sendInstantEmail($email, $name, $subject, $message);
        
        if ($result) {
            error_log("Password reset email sent successfully to: " . $email);
        } else {
            error_log("Password reset email failed to send to: " . $email);
        }
        
        return $result;
    }
    
    public function processPendingEmails($limit = 10) {
        if (!$this->isEnabled()) {
            return 0;
        }
        
        $stmt = $this->db->prepare("
            SELECT * FROM email_queue 
            WHERE status = 'pending' AND attempts < 3 
            ORDER BY created_at ASC 
            LIMIT :limit
        ");
        $stmt->bindValue(':limit', $limit, PDO::PARAM_INT);
        $stmt->execute();
        $pendingEmails = $stmt->fetchAll();
        
        $processed = 0;
        
        foreach ($pendingEmails as $email) {
            if ($this->sendEmail($email)) {
                $this->updateEmailStatus($email['id'], 'sent');
                $processed++;
            } else {
                $this->updateEmailStatus($email['id'], 'failed', 'SMTP send failed');
            }
        }
        
        return $processed;
    }
    
    private function sendEmail($emailData) {
        try {
            // Load composer autoload if available
            if (!class_exists('Symfony\Component\Mailer\Mailer')) {
                if (file_exists(__DIR__ . '/../vendor/autoload.php')) {
                    require_once __DIR__ . '/../vendor/autoload.php';
                }
            }
            
            // Use Symfony Mailer if available, otherwise simulate
            if (class_exists('Symfony\Component\Mailer\Mailer')) {
                return $this->sendWithSymfonyMailer($emailData);
            } else {
                // Fallback to simulation mode
                return $this->simulateEmailSend($emailData);
            }
            
        } catch (Exception $e) {
            error_log("Email send error: " . $e->getMessage());
            return false;
        }
    }
    
    private function sendWithSymfonyMailer($emailData) {
        try {
            // Build DSN (Data Source Name) for SMTP
            // Format: smtp://username:password@host:port or smtps:// for SSL
            $protocol = ($this->settings['smtp_encryption'] === 'ssl') ? 'smtps' : 'smtp';
            
            // Trim and clean all SMTP settings
            $smtpHost = trim($this->settings['smtp_host']);
            $smtpPort = (int)trim($this->settings['smtp_port']);
            $smtpUsername = trim($this->settings['smtp_username'] ?? '');
            $smtpPassword = trim($this->settings['smtp_password'] ?? '');
            
            // Build authentication part
            $auth = '';
            if (!empty($smtpUsername)) {
                $auth = urlencode($smtpUsername) . ':' . urlencode($smtpPassword) . '@';
            }
            
            // Build base DSN
            $dsn = sprintf(
                '%s://%s%s:%d',
                $protocol,
                $auth,
                $smtpHost,
                $smtpPort
            );
            
            // Add query parameters for TLS if needed
            if ($this->settings['smtp_encryption'] === 'tls' && $protocol === 'smtp') {
                $dsn .= '?encryption=tls';
            }
            
            // Log DSN (without password) for debugging
            $debugDsn = preg_replace('/:[^:@]+@/', ':****@', $dsn);
            error_log("Attempting to send email with DSN: " . $debugDsn);
            
            // Create transport
            $transport = \Symfony\Component\Mailer\Transport::fromDsn($dsn);
            
            // Create mailer
            $mailer = new \Symfony\Component\Mailer\Mailer($transport);
            
            // Create email message with trimmed addresses
            $fromEmail = trim($this->settings['from_email']);
            $fromName = trim($this->settings['from_name']);
            $toEmail = trim($emailData['recipient_email']);
            $toName = trim($emailData['recipient_name']);
            
            $email = (new \Symfony\Component\Mime\Email())
                ->from(new \Symfony\Component\Mime\Address($fromEmail, $fromName))
                ->to(new \Symfony\Component\Mime\Address($toEmail, $toName))
                ->subject($emailData['subject'])
                ->html($emailData['body'])
                ->text(strip_tags($emailData['body']));
            
            // Send email
            $mailer->send($email);
            
            // Update sent timestamp only if this is a queued email
            if (isset($emailData['id']) && $emailData['id'] !== null) {
                $stmt = $this->db->prepare("UPDATE email_queue SET sent_at = NOW() WHERE id = :id");
                $stmt->execute(['id' => $emailData['id']]);
            }
            
            error_log("Email sent successfully via Symfony Mailer to: " . $emailData['recipient_email']);
            return true;
            
        } catch (\Symfony\Component\Mailer\Exception\TransportExceptionInterface $e) {
            error_log("Symfony Mailer Transport Error: " . $e->getMessage());
            throw new Exception("SMTP Error: " . $e->getMessage());
        } catch (Exception $e) {
            error_log("Symfony Mailer Error: " . $e->getMessage());
            throw new Exception("Failed to send email: " . $e->getMessage());
        }
    }
    
    private function simulateEmailSend($emailData) {
        // Simulation mode (for testing without SMTP)
        usleep(100000); // 0.1 second
        
        // Log the email
        error_log("Email simulated to: " . $emailData['recipient_email'] . " - Subject: " . $emailData['subject']);
        
        // Update sent timestamp
        $stmt = $this->db->prepare("UPDATE email_queue SET sent_at = NOW() WHERE id = :id");
        $stmt->execute(['id' => $emailData['id']]);
        
        return true;
    }
    
    private function updateEmailStatus($emailId, $status, $errorMessage = null) {
        $stmt = $this->db->prepare("
            UPDATE email_queue 
            SET status = :status, 
                attempts = attempts + 1,
                error_message = :error,
                updated_at = NOW()
            WHERE id = :id
        ");
        
        return $stmt->execute([
            'id' => $emailId,
            'status' => $status,
            'error' => $errorMessage
        ]);
    }
    
    public function getEmailStats() {
        $stmt = $this->db->query("
            SELECT 
                COUNT(*) as total,
                SUM(CASE WHEN status = 'pending' THEN 1 ELSE 0 END) as pending,
                SUM(CASE WHEN status = 'sent' THEN 1 ELSE 0 END) as sent,
                SUM(CASE WHEN status = 'failed' THEN 1 ELSE 0 END) as failed
            FROM email_queue
        ");
        
        return $stmt->fetch();
    }
    
    public function getRecentEmails($limit = 20) {
        $stmt = $this->db->prepare("
            SELECT * FROM email_queue 
            ORDER BY created_at DESC 
            LIMIT :limit
        ");
        $stmt->bindValue(':limit', $limit, PDO::PARAM_INT);
        $stmt->execute();
        
        return $stmt->fetchAll();
    }
    
    public function getFailedEmails($limit = 50) {
        $stmt = $this->db->prepare("
            SELECT * FROM email_queue 
            WHERE status = 'failed' 
            ORDER BY updated_at DESC 
            LIMIT :limit
        ");
        $stmt->bindValue(':limit', $limit, PDO::PARAM_INT);
        $stmt->execute();
        
        return $stmt->fetchAll();
    }
    
    public function retryEmail($emailId) {
        // Reset email status to pending and reset attempts
        $stmt = $this->db->prepare("
            UPDATE email_queue 
            SET status = 'pending', 
                attempts = 0,
                error_message = NULL,
                updated_at = NOW()
            WHERE id = :id
        ");
        
        return $stmt->execute(['id' => $emailId]);
    }
    
    public function retryAllFailed() {
        // Reset all failed emails to pending
        $stmt = $this->db->prepare("
            UPDATE email_queue 
            SET status = 'pending', 
                attempts = 0,
                error_message = NULL,
                updated_at = NOW()
            WHERE status = 'failed'
        ");
        
        $result = $stmt->execute();
        
        // Return count of reset emails
        if ($result) {
            return $stmt->rowCount();
        }
        
        return 0;
    }
    
    public function deleteEmail($emailId) {
        $stmt = $this->db->prepare("DELETE FROM email_queue WHERE id = :id");
        return $stmt->execute(['id' => $emailId]);
    }
    
    public function deleteAllFailed() {
        $stmt = $this->db->prepare("DELETE FROM email_queue WHERE status = 'failed'");
        $result = $stmt->execute();
        
        if ($result) {
            return $stmt->rowCount();
        }
        
        return 0;
    }
}
?>

Sindbad File Manager Version 1.0, Coded By Sindbad EG ~ The Terrorists