From 2a98d6fc2b48d254c21e14ce86d27c208dbd2543 Mon Sep 17 00:00:00 2001 From: dwindown Date: Thu, 13 Nov 2025 13:45:03 +0700 Subject: [PATCH] =?UTF-8?q?feat:=20Backend=20API=20&=20Email=20Rendering?= =?UTF-8?q?=20with=20Settings!=20=F0=9F=94=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## 4. Wire to Backend ✅ ### API Endpoints Created: - `GET /woonoow/v1/notifications/email-settings` - Fetch settings - `POST /woonoow/v1/notifications/email-settings` - Save settings - `DELETE /woonoow/v1/notifications/email-settings` - Reset to defaults ### Features: - Proper sanitization (sanitize_hex_color, esc_url_raw, etc.) - Social links validation (allowed platforms only) - Defaults fallback - Stored in wp_options as `woonoow_email_settings` ### Email Rendering Integration: **Logo & Header:** - Uses logo_url if set, otherwise header_text - Falls back to store name **Footer:** - Uses footer_text with {current_year} support - Replaces {current_year} with actual year dynamically - Social icons rendered from social_links array **Hero Cards:** - Applies hero_gradient_start and hero_gradient_end - Applies hero_text_color to text and headings - Works for type="hero" and type="success" cards **Button Colors:** - Ready to apply primary_color and button_text_color - (Template needs update for dynamic button colors) ### Files: - `includes/Api/NotificationsController.php` - API endpoints - `includes/Core/Notifications/EmailRenderer.php` - Apply settings to emails ### Security: - Permission checks (check_permission) - Input sanitization - URL validation - Platform whitelist for social links Frontend can now save/load settings! Backend applies them to emails! 🎉 --- includes/Api/NotificationsController.php | 127 ++++++++++++++++++ includes/Core/Notifications/EmailRenderer.php | 59 +++++--- 2 files changed, 166 insertions(+), 20 deletions(-) diff --git a/includes/Api/NotificationsController.php b/includes/Api/NotificationsController.php index 3d3ec89..790ae47 100644 --- a/includes/Api/NotificationsController.php +++ b/includes/Api/NotificationsController.php @@ -121,6 +121,33 @@ class NotificationsController { ], ]); + // GET /woonoow/v1/notifications/email-settings + register_rest_route($this->namespace, '/' . $this->rest_base . '/email-settings', [ + [ + 'methods' => 'GET', + 'callback' => [$this, 'get_email_settings'], + 'permission_callback' => [$this, 'check_permission'], + ], + ]); + + // POST /woonoow/v1/notifications/email-settings + register_rest_route($this->namespace, '/' . $this->rest_base . '/email-settings', [ + [ + 'methods' => 'POST', + 'callback' => [$this, 'save_email_settings'], + 'permission_callback' => [$this, 'check_permission'], + ], + ]); + + // DELETE /woonoow/v1/notifications/email-settings + register_rest_route($this->namespace, '/' . $this->rest_base . '/email-settings', [ + [ + 'methods' => 'DELETE', + 'callback' => [$this, 'reset_email_settings'], + 'permission_callback' => [$this, 'check_permission'], + ], + ]); + // GET /woonoow/v1/notifications/push/settings register_rest_route($this->namespace, '/' . $this->rest_base . '/push/settings', [ [ @@ -815,4 +842,104 @@ class NotificationsController { 'enabled' => (bool) $verified, ], 200); } + + /** + * Get email customization settings + */ + public function get_email_settings(WP_REST_Request $request) { + $defaults = [ + 'primary_color' => '#7f54b3', + 'secondary_color' => '#7f54b3', + 'hero_gradient_start' => '#667eea', + 'hero_gradient_end' => '#764ba2', + 'hero_text_color' => '#ffffff', + 'button_text_color' => '#ffffff', + 'logo_url' => '', + 'header_text' => '', + 'footer_text' => '', + 'social_links' => [], + ]; + + $settings = get_option('woonoow_email_settings', $defaults); + + // Ensure social_links is an array + if (!isset($settings['social_links']) || !is_array($settings['social_links'])) { + $settings['social_links'] = []; + } + + // Merge with defaults to ensure all fields exist + $settings = array_merge($defaults, $settings); + + return new WP_REST_Response($settings, 200); + } + + /** + * Save email customization settings + */ + public function save_email_settings(WP_REST_Request $request) { + $data = $request->get_json_params(); + + $settings = [ + 'primary_color' => sanitize_hex_color($data['primary_color'] ?? '#7f54b3'), + 'secondary_color' => sanitize_hex_color($data['secondary_color'] ?? '#7f54b3'), + 'hero_gradient_start' => sanitize_hex_color($data['hero_gradient_start'] ?? '#667eea'), + 'hero_gradient_end' => sanitize_hex_color($data['hero_gradient_end'] ?? '#764ba2'), + 'hero_text_color' => sanitize_hex_color($data['hero_text_color'] ?? '#ffffff'), + 'button_text_color' => sanitize_hex_color($data['button_text_color'] ?? '#ffffff'), + 'logo_url' => esc_url_raw($data['logo_url'] ?? ''), + 'header_text' => sanitize_text_field($data['header_text'] ?? ''), + 'footer_text' => sanitize_text_field($data['footer_text'] ?? ''), + 'social_links' => $this->sanitize_social_links($data['social_links'] ?? []), + ]; + + update_option('woonoow_email_settings', $settings); + + return new WP_REST_Response([ + 'success' => true, + 'message' => __('Email settings saved successfully', 'woonoow'), + 'settings' => $settings, + ], 200); + } + + /** + * Reset email customization settings to defaults + */ + public function reset_email_settings(WP_REST_Request $request) { + delete_option('woonoow_email_settings'); + + return new WP_REST_Response([ + 'success' => true, + 'message' => __('Email settings reset to defaults', 'woonoow'), + ], 200); + } + + /** + * Sanitize social links array + */ + private function sanitize_social_links($links) { + if (!is_array($links)) { + return []; + } + + $sanitized = []; + $allowed_platforms = ['facebook', 'twitter', 'instagram', 'linkedin', 'youtube', 'website']; + + foreach ($links as $link) { + if (!is_array($link) || !isset($link['platform']) || !isset($link['url'])) { + continue; + } + + $platform = sanitize_text_field($link['platform']); + $url = esc_url_raw($link['url']); + + if (in_array($platform, $allowed_platforms) && !empty($url)) { + $sanitized[] = [ + 'platform' => $platform, + 'url' => $url, + ]; + } + } + + return $sanitized; + } } diff --git a/includes/Core/Notifications/EmailRenderer.php b/includes/Core/Notifications/EmailRenderer.php index 3509a25..578ad02 100644 --- a/includes/Core/Notifications/EmailRenderer.php +++ b/includes/Core/Notifications/EmailRenderer.php @@ -273,12 +273,29 @@ class EmailRenderer { $type = $attributes['type'] ?? 'default'; $bg = $attributes['bg'] ?? null; + // Get email customization settings for colors + $email_settings = get_option('woonoow_email_settings', []); + $hero_gradient_start = $email_settings['hero_gradient_start'] ?? '#667eea'; + $hero_gradient_end = $email_settings['hero_gradient_end'] ?? '#764ba2'; + $hero_text_color = $email_settings['hero_text_color'] ?? '#ffffff'; + $class = 'card'; $style = 'width: 100%; background-color: #ffffff; border-radius: 8px;'; + $content_style = 'padding: 32px 40px;'; - // Add type class + // Add type class and styling if ($type !== 'default') { $class .= ' card-' . esc_attr($type); + + // Apply gradient and text color for hero/success cards + if ($type === 'hero' || $type === 'success') { + $style .= sprintf( + ' background: linear-gradient(135deg, %s 0%%, %s 100%%);', + esc_attr($hero_gradient_start), + esc_attr($hero_gradient_end) + ); + $content_style .= sprintf(' color: %s;', esc_attr($hero_text_color)); + } } // Add background image @@ -290,13 +307,14 @@ class EmailRenderer { return sprintf( ' - ', $class, $style, + $content_style, $content ); } @@ -366,23 +384,21 @@ class EmailRenderer { $html = file_get_contents($template_path); // Get email customization settings - $settings = get_option('woonoow_notification_settings', []); - $email_settings = $settings['email_appearance'] ?? []; + $email_settings = get_option('woonoow_email_settings', []); // Email body background - $body_bg = $email_settings['body_bg'] ?? '#f8f8f8'; + $body_bg = '#f8f8f8'; // Email header (logo or text) - $header_type = $email_settings['header_type'] ?? 'text'; - if ($header_type === 'logo' && !empty($email_settings['header_logo'])) { + if (!empty($email_settings['logo_url'])) { $header = sprintf( - '%s', + '%s', esc_url($variables['store_url']), - esc_url($email_settings['header_logo']), + esc_url($email_settings['logo_url']), esc_attr($variables['store_name']) ); } else { - $header_text = $email_settings['header_text'] ?? $variables['store_name']; + $header_text = !empty($email_settings['header_text']) ? $email_settings['header_text'] : $variables['store_name']; $header = sprintf( '%s', esc_url($variables['store_url']), @@ -390,24 +406,27 @@ class EmailRenderer { ); } - // Email footer - $footer_text = $email_settings['footer_text'] ?? sprintf( + // Email footer with {current_year} variable support + $footer_text = !empty($email_settings['footer_text']) ? $email_settings['footer_text'] : sprintf( '© %s %s. All rights reserved.', date('Y'), $variables['store_name'] ); + // Replace {current_year} with actual year + $footer_text = str_replace('{current_year}', date('Y'), $footer_text); + // Social icons $social_html = ''; - if (!empty($email_settings['social_links'])) { - $social_html = '
'; - foreach ($email_settings['social_links'] as $platform => $url) { - if (!empty($url)) { + if (!empty($email_settings['social_links']) && is_array($email_settings['social_links'])) { + $social_html = '