diff --git a/includes/Core/Notifications/EmailManager.php b/includes/Core/Notifications/EmailManager.php new file mode 100644 index 0000000..e91ee82 --- /dev/null +++ b/includes/Core/Notifications/EmailManager.php @@ -0,0 +1,382 @@ +init_hooks(); + } + + /** + * Initialize hooks + */ + private function init_hooks() { + // Disable WooCommerce emails to prevent duplicates + add_action('woocommerce_email', [$this, 'disable_wc_emails'], 1); + + // Hook into WooCommerce order status changes + add_action('woocommerce_order_status_pending_to_processing', [$this, 'send_order_processing_email'], 10, 2); + add_action('woocommerce_order_status_pending_to_completed', [$this, 'send_order_completed_email'], 10, 2); + add_action('woocommerce_order_status_processing_to_completed', [$this, 'send_order_completed_email'], 10, 2); + add_action('woocommerce_order_status_completed', [$this, 'send_order_completed_email'], 10, 2); + add_action('woocommerce_order_status_pending_to_on-hold', [$this, 'send_order_on_hold_email'], 10, 2); + add_action('woocommerce_order_status_failed_to_processing', [$this, 'send_order_processing_email'], 10, 2); + add_action('woocommerce_order_status_cancelled', [$this, 'send_order_cancelled_email'], 10, 2); + add_action('woocommerce_order_status_refunded', [$this, 'send_order_refunded_email'], 10, 2); + add_action('woocommerce_order_fully_refunded', [$this, 'send_order_refunded_email'], 10, 2); + + // New order notification for admin + add_action('woocommerce_new_order', [$this, 'send_new_order_admin_email'], 10, 1); + + // Customer note + add_action('woocommerce_new_customer_note', [$this, 'send_customer_note_email'], 10, 1); + + // New customer account + add_action('woocommerce_created_customer', [$this, 'send_new_customer_email'], 10, 3); + + // Low stock / Out of stock + add_action('woocommerce_low_stock', [$this, 'send_low_stock_email'], 10, 1); + add_action('woocommerce_no_stock', [$this, 'send_out_of_stock_email'], 10, 1); + add_action('woocommerce_product_set_stock', [$this, 'check_stock_levels'], 10, 1); + } + + /** + * Disable WooCommerce default emails + * + * @param WC_Emails $email_class + */ + public function disable_wc_emails($email_class) { + // Get WooNooW notification settings + $settings = get_option('woonoow_notification_settings', []); + + // Check if custom emails are enabled (default: yes) + $use_custom_emails = $settings['use_custom_emails'] ?? true; + + if (!$use_custom_emails) { + return; // Keep WC emails if custom emails disabled + } + + // Disable all WooCommerce transactional emails + $emails_to_disable = [ + 'WC_Email_New_Order', // Admin: New order + 'WC_Email_Cancelled_Order', // Admin: Cancelled order + 'WC_Email_Failed_Order', // Admin: Failed order + 'WC_Email_Customer_On_Hold_Order', // Customer: Order on-hold + 'WC_Email_Customer_Processing_Order', // Customer: Processing order + 'WC_Email_Customer_Completed_Order', // Customer: Completed order + 'WC_Email_Customer_Refunded_Order', // Customer: Refunded order + 'WC_Email_Customer_Invoice', // Customer: Invoice + 'WC_Email_Customer_Note', // Customer: Note added + 'WC_Email_Customer_Reset_Password', // Customer: Reset password + 'WC_Email_Customer_New_Account', // Customer: New account + ]; + + foreach ($emails_to_disable as $email_id) { + add_filter('woocommerce_email_enabled_' . strtolower(str_replace('WC_Email_', '', $email_id)), '__return_false'); + } + } + + /** + * Send order processing email + * + * @param int $order_id + * @param WC_Order $order + */ + public function send_order_processing_email($order_id, $order = null) { + if (!$order) { + $order = wc_get_order($order_id); + } + + if (!$order) { + return; + } + + // Check if event is enabled + if (!$this->is_event_enabled('order_processing', 'email', 'customer')) { + return; + } + + // Send email + $this->send_email('order_processing', 'customer', $order); + } + + /** + * Send order completed email + * + * @param int $order_id + * @param WC_Order $order + */ + public function send_order_completed_email($order_id, $order = null) { + if (!$order) { + $order = wc_get_order($order_id); + } + + if (!$order) { + return; + } + + // Check if event is enabled + if (!$this->is_event_enabled('order_completed', 'email', 'customer')) { + return; + } + + // Send email + $this->send_email('order_completed', 'customer', $order); + } + + /** + * Send order on-hold email + * + * @param int $order_id + * @param WC_Order $order + */ + public function send_order_on_hold_email($order_id, $order = null) { + if (!$order) { + $order = wc_get_order($order_id); + } + + if (!$order) { + return; + } + + // Check if event is enabled + if (!$this->is_event_enabled('order_processing', 'email', 'customer')) { + return; + } + + // Send email (use processing template for on-hold) + $this->send_email('order_processing', 'customer', $order); + } + + /** + * Send order cancelled email + * + * @param int $order_id + * @param WC_Order $order + */ + public function send_order_cancelled_email($order_id, $order = null) { + if (!$order) { + $order = wc_get_order($order_id); + } + + if (!$order) { + return; + } + + // Send to admin + if ($this->is_event_enabled('order_cancelled', 'email', 'staff')) { + $this->send_email('order_cancelled', 'staff', $order); + } + } + + /** + * Send order refunded email + * + * @param int $order_id + * @param WC_Order $order + */ + public function send_order_refunded_email($order_id, $order = null) { + if (!$order) { + $order = wc_get_order($order_id); + } + + if (!$order) { + return; + } + + // Check if event is enabled + if (!$this->is_event_enabled('order_refunded', 'email', 'customer')) { + return; + } + + // Send email + $this->send_email('order_refunded', 'customer', $order); + } + + /** + * Send new order admin email + * + * @param int $order_id + */ + public function send_new_order_admin_email($order_id) { + $order = wc_get_order($order_id); + + if (!$order) { + return; + } + + // Check if event is enabled + if (!$this->is_event_enabled('order_placed', 'email', 'staff')) { + return; + } + + // Send email + $this->send_email('order_placed', 'staff', $order); + } + + /** + * Send customer note email + * + * @param array $args + */ + public function send_customer_note_email($args) { + $order = wc_get_order($args['order_id']); + + if (!$order) { + return; + } + + // Check if event is enabled + if (!$this->is_event_enabled('customer_note', 'email', 'customer')) { + return; + } + + // Send email with note data + $this->send_email('customer_note', 'customer', $order, ['note' => $args['customer_note']]); + } + + /** + * Send new customer email + * + * @param int $customer_id + * @param array $new_customer_data + * @param bool $password_generated + */ + public function send_new_customer_email($customer_id, $new_customer_data = [], $password_generated = false) { + // Check if event is enabled + if (!$this->is_event_enabled('new_customer', 'email', 'customer')) { + return; + } + + $customer = new \WC_Customer($customer_id); + + // Send email + $this->send_email('new_customer', 'customer', $customer, [ + 'password_generated' => $password_generated, + 'user_login' => $new_customer_data['user_login'] ?? '', + 'user_pass' => $new_customer_data['user_pass'] ?? '', + ]); + } + + /** + * Send low stock email + * + * @param WC_Product $product + */ + public function send_low_stock_email($product) { + // Check if event is enabled + if (!$this->is_event_enabled('low_stock', 'email', 'staff')) { + return; + } + + // Send email + $this->send_email('low_stock', 'staff', $product); + } + + /** + * Send out of stock email + * + * @param WC_Product $product + */ + public function send_out_of_stock_email($product) { + // Check if event is enabled + if (!$this->is_event_enabled('out_of_stock', 'email', 'staff')) { + return; + } + + // Send email + $this->send_email('out_of_stock', 'staff', $product); + } + + /** + * Check stock levels when product stock is updated + * + * @param WC_Product $product + */ + public function check_stock_levels($product) { + $stock = $product->get_stock_quantity(); + $low_stock_threshold = get_option('woocommerce_notify_low_stock_amount', 2); + + if ($stock <= 0) { + $this->send_out_of_stock_email($product); + } elseif ($stock <= $low_stock_threshold) { + $this->send_low_stock_email($product); + } + } + + /** + * Check if event is enabled + * + * @param string $event_id + * @param string $channel_id + * @param string $recipient_type + * @return bool + */ + private function is_event_enabled($event_id, $channel_id, $recipient_type) { + $settings = get_option('woonoow_notification_settings', []); + + // Check if event exists and channel is enabled + if (isset($settings['events'][$event_id]['channels'][$channel_id])) { + return $settings['events'][$event_id]['channels'][$channel_id]['enabled'] ?? false; + } + + return false; + } + + /** + * Send email + * + * @param string $event_id + * @param string $recipient_type + * @param mixed $data + * @param array $extra_data + */ + private function send_email($event_id, $recipient_type, $data, $extra_data = []) { + // Get email renderer + $renderer = EmailRenderer::instance(); + + // Render email + $email = $renderer->render($event_id, $recipient_type, $data, $extra_data); + + if (!$email) { + return; + } + + // Send email via wp_mail + $headers = [ + 'Content-Type: text/html; charset=UTF-8', + 'From: ' . get_bloginfo('name') . ' <' . get_option('admin_email') . '>', + ]; + + wp_mail($email['to'], $email['subject'], $email['body'], $headers); + + // Log email sent + do_action('woonoow_email_sent', $event_id, $recipient_type, $email); + } +} diff --git a/includes/Core/Notifications/EmailRenderer.php b/includes/Core/Notifications/EmailRenderer.php new file mode 100644 index 0000000..95c0423 --- /dev/null +++ b/includes/Core/Notifications/EmailRenderer.php @@ -0,0 +1,274 @@ +get_template_settings($event_id, $recipient_type); + + if (!$template_settings) { + return null; + } + + // Get recipient email + $to = $this->get_recipient_email($recipient_type, $data); + + if (!$to) { + return null; + } + + // Get variables + $variables = $this->get_variables($event_id, $data, $extra_data); + + // Replace variables in subject and content + $subject = $this->replace_variables($template_settings['subject'], $variables); + $content = $this->replace_variables($template_settings['body'], $variables); + + // Get HTML template design + $design_template = $this->get_design_template($template_settings['design'] ?? 'modern'); + + // Render final HTML + $html = $this->render_html($design_template, $content, $subject, $variables); + + return [ + 'to' => $to, + 'subject' => $subject, + 'body' => $html, + ]; + } + + /** + * Get template settings + * + * @param string $event_id + * @param string $recipient_type + * @return array|null + */ + private function get_template_settings($event_id, $recipient_type) { + // Get saved template + $template = TemplateProvider::get_template($event_id, 'email'); + + if (!$template) { + return null; + } + + // Get design template preference + $settings = get_option('woonoow_notification_settings', []); + $design = $settings['email_design_template'] ?? 'modern'; + + return [ + 'subject' => $template['subject'] ?? '', + 'body' => $template['body'] ?? '', + 'design' => $design, + ]; + } + + /** + * Get recipient email + * + * @param string $recipient_type + * @param mixed $data + * @return string|null + */ + private function get_recipient_email($recipient_type, $data) { + if ($recipient_type === 'staff') { + return get_option('admin_email'); + } + + // Customer + if ($data instanceof \WC_Order) { + return $data->get_billing_email(); + } + + if ($data instanceof \WC_Customer) { + return $data->get_email(); + } + + return null; + } + + /** + * Get variables for template + * + * @param string $event_id + * @param mixed $data + * @param array $extra_data + * @return array + */ + private function get_variables($event_id, $data, $extra_data = []) { + $variables = [ + 'store_name' => get_bloginfo('name'), + 'store_url' => home_url(), + 'site_title' => get_bloginfo('name'), + ]; + + // Order variables + if ($data instanceof \WC_Order) { + $variables = array_merge($variables, [ + 'order_number' => $data->get_order_number(), + 'order_id' => $data->get_id(), + 'order_date' => $data->get_date_created()->date('F j, Y'), + 'order_total' => $data->get_formatted_order_total(), + 'order_subtotal' => wc_price($data->get_subtotal()), + 'order_tax' => wc_price($data->get_total_tax()), + 'order_shipping' => wc_price($data->get_shipping_total()), + 'order_discount' => wc_price($data->get_discount_total()), + 'order_status' => wc_get_order_status_name($data->get_status()), + 'order_url' => $data->get_view_order_url(), + 'payment_method' => $data->get_payment_method_title(), + 'shipping_method' => $data->get_shipping_method(), + 'customer_name' => $data->get_formatted_billing_full_name(), + 'customer_first_name' => $data->get_billing_first_name(), + 'customer_last_name' => $data->get_billing_last_name(), + 'customer_email' => $data->get_billing_email(), + 'customer_phone' => $data->get_billing_phone(), + 'billing_address' => $data->get_formatted_billing_address(), + 'shipping_address' => $data->get_formatted_shipping_address(), + ]); + + // Order items + $items_html = ''; + foreach ($data->get_items() as $item) { + $product = $item->get_product(); + $items_html .= sprintf( + '%s × %d%s', + $item->get_name(), + $item->get_quantity(), + wc_price($item->get_total()) + ); + } + $variables['order_items'] = $items_html; + } + + // Product variables + if ($data instanceof \WC_Product) { + $variables = array_merge($variables, [ + 'product_id' => $data->get_id(), + 'product_name' => $data->get_name(), + 'product_sku' => $data->get_sku(), + 'product_price' => wc_price($data->get_price()), + 'product_url' => get_permalink($data->get_id()), + 'stock_quantity' => $data->get_stock_quantity(), + 'stock_status' => $data->get_stock_status(), + ]); + } + + // Customer variables + if ($data instanceof \WC_Customer) { + $variables = array_merge($variables, [ + 'customer_id' => $data->get_id(), + 'customer_name' => $data->get_display_name(), + 'customer_first_name' => $data->get_first_name(), + 'customer_last_name' => $data->get_last_name(), + 'customer_email' => $data->get_email(), + 'customer_username' => $data->get_username(), + ]); + } + + // Merge extra data + $variables = array_merge($variables, $extra_data); + + return apply_filters('woonoow_email_variables', $variables, $event_id, $data); + } + + /** + * Replace variables in text + * + * @param string $text + * @param array $variables + * @return string + */ + private function replace_variables($text, $variables) { + foreach ($variables as $key => $value) { + $text = str_replace('{' . $key . '}', $value, $text); + } + + return $text; + } + + /** + * Get design template path + * + * @param string $design Template name (modern, classic, minimal) + * @return string + */ + private function get_design_template($design) { + $template_path = WOONOOW_PATH . 'templates/emails/' . $design . '.html'; + + // Allow filtering template path + $template_path = apply_filters('woonoow_email_template', $template_path, $design); + + // Fallback to modern if template doesn't exist + if (!file_exists($template_path)) { + $template_path = WOONOOW_PATH . 'templates/emails/modern.html'; + } + + return $template_path; + } + + /** + * Render HTML email + * + * @param string $template_path Path to HTML template + * @param string $content Email content (HTML) + * @param string $subject Email subject + * @param array $variables All variables + * @return string + */ + private function render_html($template_path, $content, $subject, $variables) { + if (!file_exists($template_path)) { + // Fallback to plain HTML + return $content; + } + + // Load template + $html = file_get_contents($template_path); + + // Replace placeholders + $html = str_replace('{{email_heading}}', $subject, $html); + $html = str_replace('{{email_content}}', $content, $html); + $html = str_replace('{{store_name}}', $variables['store_name'], $html); + $html = str_replace('{{store_url}}', $variables['store_url'], $html); + $html = str_replace('{{current_year}}', date('Y'), $html); + + // Replace all other variables + foreach ($variables as $key => $value) { + $html = str_replace('{{' . $key . '}}', $value, $html); + } + + return $html; + } +} diff --git a/templates/emails/classic.html b/templates/emails/classic.html new file mode 100644 index 0000000..d9a7ca5 --- /dev/null +++ b/templates/emails/classic.html @@ -0,0 +1,238 @@ + + + + + + + {{email_heading}} + + + + + + + +
+ + + + + + + + + + + + + + + + +
+ + diff --git a/templates/emails/minimal.html b/templates/emails/minimal.html new file mode 100644 index 0000000..a5c1a50 --- /dev/null +++ b/templates/emails/minimal.html @@ -0,0 +1,280 @@ + + + + + + + {{email_heading}} + + + + + + + +
+ + + + + + + + + + + + + + + + +
+ + diff --git a/templates/emails/modern.html b/templates/emails/modern.html new file mode 100644 index 0000000..834d523 --- /dev/null +++ b/templates/emails/modern.html @@ -0,0 +1,275 @@ + + + + + + + {{email_heading}} + + + + + + + +
+ + + + + + + + + + + + + + + + +
+ +