Changeset 3639046
- Timestamp:
- 08/09/2026 03:54:35 AM (3 months ago)
- Location:
- eprimo-mail-radar/trunk
- Files:
-
- 9 edited
-
admin/css/admin-style.css (modified) (2 diffs)
-
admin/js/admin-script.js (modified) (8 diffs)
-
admin/views/email-list.php (modified) (5 diffs)
-
admin/views/settings.php (modified) (5 diffs)
-
classes/class-admin.php (modified) (7 diffs)
-
classes/class-database.php (modified) (1 diff)
-
classes/class-eprimo-mail-radar.php (modified) (6 diffs)
-
eprimo-mail-radar.php (modified) (2 diffs)
-
readme.txt (modified) (3 diffs)
Legend:
- Unmodified
- Added
- Removed
-
eprimo-mail-radar/trunk/admin/css/admin-style.css
r3631782 r3639046 4 4 .eprimo-mail-radar-single .email-content-box { 5 5 margin-top: 20px; 6 border-radius: 10px; 7 } 8 .eprimo-mail-radar-single .email-meta-box.postbox, 9 .eprimo-mail-radar-single .email-headers-box.postbox, 10 .eprimo-mail-radar-single .email-content-box.postbox { 11 border: 1px solid #e9e9e9; 12 } 13 .eprimo-mail-radar-single .email-meta-box .postbox-header, 14 .eprimo-mail-radar-single .email-headers-box .postbox-header, 15 .eprimo-mail-radar-single .email-content-box .postbox-header { 16 border-bottom: 1px solid #e9e9e9; 6 17 } 7 18 … … 123 134 color: inherit; 124 135 } 136 #email-list-form #bulk-action-selector-top, 137 #email-list-form #bulk-action-selector-bottom { 138 padding: 0px 10px; 139 border: 1px solid #e9e9e9; 140 font-size: 12px; 141 height: 33px; 142 } 143 #email-search-form .search-box input[type=search] { 144 padding: 10px; 145 border: 1px solid #e9e9e9; 146 font-size: 12px; 147 width: 250px; 148 } 149 #email-search-form .search-box #date-from, 150 #email-search-form .search-box #date-to { 151 padding: 0px 10px; 152 border: 1px solid #e9e9e9; 153 font-size: 12px; 154 } 155 #email-list-form table.widefat { 156 border: 1px solid #f9f9f9; 157 border-radius: 10px; 158 } 159 #email-list-form .widefat thead td, 160 #email-list-form .widefat thead th { 161 border-bottom: 1px solid #e9e9e9; 162 } 163 #email-list-form .striped>tbody>:nth-child(odd) { 164 background-color: #fafafa; 165 } 166 167 #email-list-form .tablenav-pages .page-numbers.current { 168 background: #3858e9; 169 border-color: #3858e9; 170 } 171 #email-list-form .tablenav-pages .page-numbers { 172 color: #3858e9; 173 } 174 #email-list-form .tablenav-pages .page-numbers:hover { 175 color: #3858e9; 176 } 177 #email-list-form .tablenav-pages .page-numbers.current { 178 color: #fff; 179 } 180 #email-list-form .source-badge { 181 background: #bbbbf9; 182 color: #ffffff; 183 } 184 .eprimo-mail-radar-settings #exclude_emails, 185 .eprimo-mail-radar-settings .widefat { 186 border: 1px solid #e9e9e9; 187 border-radius: 10px; 188 } 189 .eprimo-mail-radar-settings #clear_days, 190 .eprimo-mail-radar-settings #retention_days { 191 border: 1px solid #e9e9e9; 192 } 125 193 126 194 /* Highlight animation for newly resent emails */ -
eprimo-mail-radar/trunk/admin/js/admin-script.js
r3631782 r3639046 178 178 var status = currentUrl.searchParams.get('status') || ''; 179 179 180 // Get date values181 180 var dateFrom = $('#date-from').val() || ''; 182 181 var dateTo = $('#date-to').val() || ''; 182 var orderby = $('#eprimo-current-orderby').val() || 'created_at'; 183 var order = $('#eprimo-current-order').val() || 'DESC'; 183 184 184 185 $.ajax({ … … 192 193 paged: page, 193 194 date_from: dateFrom, 194 date_to: dateTo 195 date_to: dateTo, 196 orderby: orderby, 197 order: order 195 198 }, 196 199 success: function(response) { … … 211 214 $('.subsubsub li.failed .count').text('(' + response.data.counts.failed + ')'); 212 215 } 213 214 // Update URL added date params 216 217 // Update sort arrows 218 $('.eprimo-sort-column').each(function() { 219 var $col = $(this); 220 var colKey = $col.data('orderby'); 221 var isActive = (colKey === orderby); 222 var curOrder = order; 223 224 $col.find('.dashicons').remove(); 225 $col.css('font-weight', isActive ? '700' : 'normal'); 226 227 if (isActive) { 228 var arrowClass = (curOrder === 'ASC') ? 'dashicons-arrow-up' : 'dashicons-arrow-down'; 229 $col.append(' <span class="dashicons ' + arrowClass + '" style="font-size:14px;width:14px;height:14px;vertical-align:middle;"></span>'); 230 $col.data('order', curOrder === 'ASC' ? 'DESC' : 'ASC'); 231 } else { 232 $col.data('order', 'ASC'); 233 } 234 }); 235 236 // Update URL 215 237 var newUrl = new URL(window.location.href); 216 238 newUrl.searchParams.set('page', 'eprimo-mail-radar'); … … 228 250 } 229 251 230 // ADDed date to URL231 252 if (dateFrom) { 232 253 newUrl.searchParams.set('date_from', dateFrom); … … 240 261 newUrl.searchParams.delete('date_to'); 241 262 } 263 264 if (orderby && orderby !== 'created_at') { 265 newUrl.searchParams.set('orderby', orderby); 266 newUrl.searchParams.set('order', order); 267 } else if (orderby === 'created_at' && order === 'ASC') { 268 newUrl.searchParams.set('orderby', orderby); 269 newUrl.searchParams.set('order', order); 270 } else { 271 newUrl.searchParams.delete('orderby'); 272 newUrl.searchParams.delete('order'); 273 } 242 274 243 275 if (page > 1) { … … 249 281 window.history.pushState({}, '', newUrl.toString()); 250 282 251 // Show/hide clear button252 283 if (query || dateFrom || dateTo) { 253 284 if (!$('.clear-search-btn').length) { … … 372 403 }); 373 404 405 /** 406 * Column Sorting — click handler 407 */ 408 $(document).on('click', '.eprimo-sort-column', function(e) { 409 e.preventDefault(); 410 411 var newOrderby = $(this).data('orderby'); 412 var newOrder = $(this).data('order'); 413 414 // Update hidden state 415 $('#eprimo-current-orderby').val(newOrderby); 416 $('#eprimo-current-order').val(newOrder); 417 418 // Show loading 419 $('#search-loading').show(); 420 421 // Re-fetch with new sort 422 performAjaxSearch($('#email-search-input').val().trim(), 1); 423 }); 424 374 425 // ============================================ 375 426 // Single Email View … … 685 736 686 737 /** 738 * Send Test Email 739 */ 740 $(document).on('click', '#eprimo-send-test-email', function(e) { 741 e.preventDefault(); 742 743 var $button = $(this); 744 var $result = $('#eprimo-test-email-result'); 745 var to = $('#eprimo-test-email-to').val().trim(); 746 747 // Validate 748 if (!to || !/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(to)) { 749 alert(eprimoMailRadar.invalidEmail); 750 $('#eprimo-test-email-to').focus(); 751 return; 752 } 753 754 // Show loading 755 var originalText = $button.html(); 756 $button.prop('disabled', true).html( 757 '<span class="spinner is-active" style="float: none; margin: 0 5px 0 0;"></span>' + 758 eprimoMailRadar.testEmailSending 759 ); 760 $result.hide(); 761 762 $.ajax({ 763 url: eprimoMailRadar.ajaxurl, 764 type: 'POST', 765 data: { 766 action: 'eprimo_mail_radar_send_test', 767 nonce: eprimoMailRadar.ajaxNonce, 768 to: to 769 }, 770 success: function(response) { 771 if (response.success) { 772 $result 773 .html('<div class="notice notice-success inline" style="margin: 0;"><p>' + 774 response.data.message + '</p></div>') 775 .fadeIn(200); 776 } else { 777 $result 778 .html('<div class="notice notice-error inline" style="margin: 0;"><p>' + 779 (response.data.message || eprimoMailRadar.testEmailFailed) + '</p></div>') 780 .fadeIn(200); 781 } 782 783 $button.prop('disabled', false).html(originalText); 784 }, 785 error: function() { 786 $result 787 .html('<div class="notice notice-error inline" style="margin: 0;"><p>' + 788 eprimoMailRadar.testEmailFailed + '</p></div>') 789 .fadeIn(200); 790 791 $button.prop('disabled', false).html(originalText); 792 } 793 }); 794 }); 795 796 /** 797 * Send test email on Enter key 798 */ 799 $('#eprimo-test-email-to').on('keydown', function(e) { 800 if (e.key === 'Enter') { 801 e.preventDefault(); 802 $('#eprimo-send-test-email').click(); 803 } 804 }); 805 806 /** 687 807 * Toggle retention days input 688 808 */ -
eprimo-mail-radar/trunk/admin/views/email-list.php
r3631782 r3639046 4 4 * 5 5 * Pure display — uses template partials for rows, Database class for counts. 6 * Variables $emails, $total, $search, $status, $paged, $date_from, $date_to 7 * are set by Eprimo_Mail_Radar_Admin::render_list_page() before this file is included.6 * Variables $emails, $total, $search, $status, $paged, $date_from, $date_to, 7 * $orderby, $order are set by Eprimo_Mail_Radar_Admin::render_list_page(). 8 8 * 9 9 * @package Eprimo_Mail_Radar … … 16 16 * @var string $date_from Date range start. 17 17 * @var string $date_to Date range end. 18 * @var string $orderby Current sort column. 19 * @var string $order Current sort direction (ASC/DESC). 18 20 */ 19 21 … … 26 28 $current_status = $status; 27 29 $total_pages = ceil( $total / 20 ); 30 31 // Sort helper — generates the opposite direction for toggle 32 $next_order = ( 'ASC' === $order ) ? 'DESC' : 'ASC'; 33 34 /** 35 * Build a sortable column header. 36 * 37 * @param string $column Column key matching database column. 38 * @param string $label Display label. 39 * @param string $orderby Current active sort column. 40 * @param string $order Current sort direction. 41 * @return string HTML for the sortable <th>. 42 */ 43 function eprimo_mail_radar_sortable_header( $column, $label, $orderby, $order ) { 44 $is_active = ( $orderby === $column ); 45 $next = $is_active && 'ASC' === $order ? 'DESC' : 'ASC'; 46 $arrow = ''; 47 48 if ( $is_active ) { 49 $arrow = ( 'ASC' === $order ) 50 ? ' <span class="dashicons dashicons-arrow-up" style="font-size: 14px; width: 14px; height: 14px; vertical-align: middle;"></span>' 51 : ' <span class="dashicons dashicons-arrow-down" style="font-size: 14px; width: 14px; height: 14px; vertical-align: middle;"></span>'; 52 } 53 54 return sprintf( 55 '<th><a href="#" class="eprimo-sort-column" data-orderby="%s" data-order="%s" style="text-decoration: none; color: inherit; font-weight: %s;">%s%s</a></th>', 56 esc_attr( $column ), 57 esc_attr( $next ), 58 $is_active ? '700' : 'normal', 59 esc_html( $label ), 60 $arrow 61 ); 62 } 28 63 ?> 29 64 … … 139 174 </form> 140 175 176 <!-- Sort state (hidden, used by JS) --> 177 <input type="hidden" id="eprimo-current-orderby" value="<?php echo esc_attr( $orderby ); ?>"> 178 <input type="hidden" id="eprimo-current-order" value="<?php echo esc_attr( $order ); ?>"> 179 141 180 <!-- Email List Table --> 142 181 <form method="post" action="" id="email-list-form"> … … 176 215 <input type="checkbox" id="select-all-top"> 177 216 </td> 178 <th><?php esc_html_e( 'Subject', 'eprimo-mail-radar' ); ?></th> 217 <?php 218 // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- Output is escaped inside the function. 219 echo eprimo_mail_radar_sortable_header( 'subject', __( 'Subject', 'eprimo-mail-radar' ), $orderby, $order ); 220 ?> 179 221 <th><?php esc_html_e( 'To', 'eprimo-mail-radar' ); ?></th> 180 222 <th><?php esc_html_e( 'Source', 'eprimo-mail-radar' ); ?></th> 181 <th><?php esc_html_e( 'Status', 'eprimo-mail-radar' ); ?></th> 182 <th><?php esc_html_e( 'Date', 'eprimo-mail-radar' ); ?></th> 223 <?php 224 // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped 225 echo eprimo_mail_radar_sortable_header( 'status', __( 'Status', 'eprimo-mail-radar' ), $orderby, $order ); 226 // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped 227 echo eprimo_mail_radar_sortable_header( 'created_at', __( 'Date', 'eprimo-mail-radar' ), $orderby, $order ); 228 ?> 183 229 </tr> 184 230 </thead> -
eprimo-mail-radar/trunk/admin/views/settings.php
r3631782 r3639046 56 56 <!-- Statistics Boxes --> 57 57 <div class="stats-boxes" style="display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 20px; margin: 20px 0;"> 58 <div class="stats-box" style="background: white; padding: 20px; border-left: 4px solid #2271b1; box-shadow: 0 1px 3px rgba(0,0,0,0.1); ">58 <div class="stats-box" style="background: white; padding: 20px; border-left: 4px solid #2271b1; box-shadow: 0 1px 3px rgba(0,0,0,0.1); border-radius: 0px 10px 10px 0px;"> 59 59 <h3 style="margin: 0 0 10px 0; color: #1d2327;"> 60 60 <?php echo esc_html( number_format( $stats->total ) ); ?> … … 65 65 </div> 66 66 67 <div class="stats-box" style="background: white; padding: 20px; border-left: 4px solid #00a32a; box-shadow: 0 1px 3px rgba(0,0,0,0.1); ">67 <div class="stats-box" style="background: white; padding: 20px; border-left: 4px solid #00a32a; box-shadow: 0 1px 3px rgba(0,0,0,0.1); border-radius: 0px 10px 10px 0px;"> 68 68 <h3 style="margin: 0 0 10px 0; color: #1d2327;"> 69 69 <?php echo esc_html( number_format( $stats->sent ) ); ?> … … 74 74 </div> 75 75 76 <div class="stats-box" style="background: white; padding: 20px; border-left: 4px solid #d63638; box-shadow: 0 1px 3px rgba(0,0,0,0.1); ">76 <div class="stats-box" style="background: white; padding: 20px; border-left: 4px solid #d63638; box-shadow: 0 1px 3px rgba(0,0,0,0.1); border-radius: 0px 10px 10px 0px;"> 77 77 <h3 style="margin: 0 0 10px 0; color: #1d2327;"> 78 78 <?php echo esc_html( number_format( $stats->failed ) ); ?> … … 83 83 </div> 84 84 85 <div class="stats-box" style="background: white; padding: 20px; border-left: 4px solid #646970; box-shadow: 0 1px 3px rgba(0,0,0,0.1); ">85 <div class="stats-box" style="background: white; padding: 20px; border-left: 4px solid #646970; box-shadow: 0 1px 3px rgba(0,0,0,0.1); border-radius: 0px 10px 10px 0px;"> 86 86 <h3 style="margin: 0 0 10px 0; color: #1d2327; font-size: 16px;"> 87 87 <?php echo esc_html( Eprimo_Mail_Radar_Formatting_Helper::format_date_only( $stats->oldest ) ); ?> … … 287 287 </button> 288 288 </form> 289 290 <hr style="margin: 40px 0;"> 291 292 <!-- Send Test Email --> 293 <h2><?php esc_html_e( 'Send Test Email', 'eprimo-mail-radar' ); ?></h2> 294 295 <div style="max-width: 600px; background: white; padding: 20px 25px; border: 1px solid #e5e5e5; border-radius: 10px; box-shadow: 0 1px 1px rgba(0,0,0,.04);"> 296 <p class="description" style="margin-top: 0;"> 297 <?php esc_html_e( 'Send a test email to verify that your WordPress email delivery is working. The result will appear in the email log.', 'eprimo-mail-radar' ); ?> 298 </p> 299 300 <div style="display: flex; gap: 10px; align-items: flex-start; flex-wrap: wrap;"> 301 <input type="email" 302 id="eprimo-test-email-to" 303 class="regular-text" 304 value="<?php echo esc_attr( get_option( 'admin_email' ) ); ?>" 305 placeholder="<?php esc_attr_e( 'Recipient email address', 'eprimo-mail-radar' ); ?>" 306 style="flex: 1; min-width: 250px; border: 1px solid #e5e5e5;"> 307 308 <button type="button" 309 id="eprimo-send-test-email" 310 class="button button-primary"> 311 <span class="dashicons dashicons-email" style="margin-top: 3px; font-size: 16px; width: 16px; height: 16px;"></span> 312 <?php esc_html_e( 'Send Test Email', 'eprimo-mail-radar' ); ?> 313 </button> 314 </div> 315 316 <div id="eprimo-test-email-result" style="margin-top: 15px; display: none;"></div> 317 </div> 289 318 290 319 <hr style="margin: 40px 0;"> -
eprimo-mail-radar/trunk/classes/class-admin.php
r3631782 r3639046 43 43 add_action( 'wp_ajax_eprimo_mail_radar_resend_ajax', [ $instance, 'ajax_resend_email' ] ); 44 44 add_action( 'wp_ajax_eprimo_mail_radar_delete_ajax', [ $instance, 'ajax_delete_email' ] ); 45 46 // Resend Email 47 add_action( 'wp_ajax_eprimo_mail_radar_send_test', [ $instance, 'ajax_send_test_email' ] ); 45 48 } 46 49 … … 159 162 'deleteText' => __( 'Delete', 'eprimo-mail-radar' ), 160 163 'scriptWarning' => __( 'Enabling scripts can be dangerous. Only enable this if you trust the email source. Continue?', 'eprimo-mail-radar' ), 164 'testEmailSuccess' => __( 'Test email sent successfully! Check your inbox and the email log.', 'eprimo-mail-radar' ), 165 'testEmailFailed' => __( 'Test email failed to send. Check the email log for details.', 'eprimo-mail-radar' ), 166 'testEmailSending' => __( 'Sending...', 'eprimo-mail-radar' ), 167 'sendTestEmail' => __( 'Send Test Email', 'eprimo-mail-radar' ), 161 168 ]; 162 169 } … … 188 195 $date_from = isset( $_GET['date_from'] ) ? sanitize_text_field( wp_unslash($_GET['date_from']) ) : ''; 189 196 $date_to = isset( $_GET['date_to'] ) ? sanitize_text_field( wp_unslash($_GET['date_to']) ) : ''; 197 $orderby = isset( $_GET['orderby'] ) ? sanitize_text_field( wp_unslash($_GET['orderby']) ) : 'created_at'; 198 $order = isset( $_GET['order'] ) ? strtoupper( sanitize_text_field( wp_unslash($_GET['order']) ) ) : 'DESC'; 190 199 191 200 $args = [ … … 196 205 'date_from' => $date_from, 197 206 'date_to' => $date_to, 207 'order_by' => $orderby, 208 'order' => $order, 198 209 ]; 199 210 … … 486 497 $date_from = isset( $_POST['date_from'] ) ? sanitize_text_field( wp_unslash($_POST['date_from']) ) : ''; 487 498 $date_to = isset( $_POST['date_to'] ) ? sanitize_text_field( wp_unslash($_POST['date_to']) ) : ''; 499 $orderby = isset( $_POST['orderby'] ) ? sanitize_text_field( wp_unslash($_POST['orderby']) ) : 'created_at'; 500 $order = isset( $_POST['order'] ) ? strtoupper( sanitize_text_field( wp_unslash($_POST['order']) ) ) : 'DESC'; 488 501 489 502 $args = [ … … 494 507 'date_from' => $date_from, 495 508 'date_to' => $date_to, 509 'order_by' => $orderby, 510 'order' => $order, 496 511 ]; 497 512 … … 721 736 return ob_get_clean(); 722 737 } 738 739 /** 740 * AJAX: Send a test email 741 * 742 * @return void 743 */ 744 public function ajax_send_test_email() { 745 if ( ! isset( $_POST['nonce'] ) || ! wp_verify_nonce( sanitize_text_field( wp_unslash( $_POST['nonce'] ) ), 'eprimo_mail_radar_ajax_nonce' ) ) { 746 wp_send_json_error( [ 'message' => __( 'Security check failed', 'eprimo-mail-radar' ) ] ); 747 } 748 749 if ( ! current_user_can( 'manage_options' ) ) { 750 wp_send_json_error( [ 'message' => __( 'Unauthorized', 'eprimo-mail-radar' ) ] ); 751 } 752 753 $to = isset( $_POST['to'] ) ? sanitize_email( wp_unslash( $_POST['to'] ) ) : ''; 754 755 if ( ! is_email( $to ) ) { 756 wp_send_json_error( [ 'message' => __( 'Please enter a valid email address.', 'eprimo-mail-radar' ) ] ); 757 } 758 759 $subject = sprintf( 760 /* translators: %s: site name */ 761 __( '[Test] Email Radar — %s', 'eprimo-mail-radar' ), 762 get_bloginfo( 'name' ) 763 ); 764 765 $body = sprintf( 766 /* translators: 1: site name, 2: site URL, 3: date/time */ 767 __( 768 "This is a test email sent by Eprimo Mail Radar.\n\nSite: %1\$s\nURL: %2\$s\nSent at: %3\$s\n\nIf you received this email, your WordPress email delivery is working correctly.\n\n— Eprimo Mail Radar", 769 'eprimo-mail-radar' 770 ), 771 get_bloginfo( 'name' ), 772 home_url(), 773 current_time( 'mysql' ) 774 ); 775 776 $sent = wp_mail( $to, $subject, $body ); 777 778 if ( $sent ) { 779 wp_send_json_success( [ 780 'message' => __( 'Test email sent successfully! Check your inbox and the email log.', 'eprimo-mail-radar' ), 781 ] ); 782 } else { 783 wp_send_json_error( [ 784 'message' => __( 'Test email failed to send. Check the email log for error details.', 'eprimo-mail-radar' ), 785 ] ); 786 } 787 } 723 788 } -
eprimo-mail-radar/trunk/classes/class-database.php
r3631782 r3639046 586 586 } 587 587 } 588 589 590 /** 591 * Get count of failed emails. 592 * 593 * Lightweight single query used by admin bar badge. 594 * 595 * @return int 596 */ 597 public static function get_failed_count() { 598 global $wpdb; 599 600 $table_name = self::get_table_name(); 601 602 // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared 603 return (int) $wpdb->get_var( "SELECT COUNT(*) FROM {$table_name} WHERE status = 'failed'" ); 604 } 588 605 } -
eprimo-mail-radar/trunk/classes/class-eprimo-mail-radar.php
r3631782 r3639046 4 4 * 5 5 * Hooks into wp_mail to log emails, detects source, 6 * handles activation/deactivation and cron cleanup.6 * handles activation/deactivation, cron cleanup, and admin bar badge. 7 7 * 8 8 * @package Eprimo_Mail_Radar … … 16 16 17 17 /** 18 * Last inserted log ID for failed email tracking. 19 * 20 * @var int 21 */ 22 private $last_log_id = 0; 23 24 /** 18 25 * Initialize hooks 19 26 * … … 30 37 add_action( 'eprimo_mail_radar_cleanup', [ $instance, 'cleanup_old_logs' ] ); 31 38 39 // Admin bar failed count (shows on both frontend + backend for admins) 40 add_action( 'admin_bar_menu', [ $instance, 'add_admin_bar_badge' ], 999 ); 41 32 42 // Admin 33 43 if ( is_admin() ) { … … 46 56 public static function check_migrations() { 47 57 Eprimo_Mail_Radar_Migrator::maybe_migrate(); 58 } 59 60 // ========================================================================= 61 // Admin Bar Badge 62 // ========================================================================= 63 64 /** 65 * Add failed email count badge to admin bar 66 * 67 * @param WP_Admin_Bar $admin_bar The admin bar instance. 68 * @return void 69 */ 70 public function add_admin_bar_badge( $admin_bar ) { 71 // Only show to users who can manage options 72 if ( ! current_user_can( 'manage_options' ) ) { 73 return; 74 } 75 76 // Only show if logging is enabled 77 if ( ! Eprimo_Mail_Radar_Settings_Helper::is_enabled() ) { 78 return; 79 } 80 81 $failed_count = Eprimo_Mail_Radar_Database::get_failed_count(); 82 83 // Only show badge if there are failed emails 84 if ( $failed_count < 1 ) { 85 return; 86 } 87 88 $title = sprintf( 89 '<span class="ab-icon dashicons dashicons-email" style="margin-top: 2px;"></span>' 90 . '<span class="eprimo-mail-radar-failed-badge" style="' 91 . 'background: #d63638;' 92 . 'color: #fff;' 93 . 'font-size: 9px;' 94 . 'font-weight: 600;' 95 . 'line-height: 17px;' 96 . 'min-width: 18px;' 97 . 'padding: 0 5px;' 98 . 'border-radius: 4px;' 99 . 'display: inline-block;' 100 . 'text-align: center;' 101 . 'margin-left: 2px;' 102 . 'vertical-align: top;' 103 . 'margin-top: 7px;' 104 . '">%d</span>', 105 $failed_count 106 ); 107 108 $admin_bar->add_node( [ 109 'id' => 'eprimo-mail-radar-failed', 110 'title' => $title, 111 'href' => admin_url( 'admin.php?page=eprimo-mail-radar&status=failed' ), 112 'meta' => [ 113 'title' => sprintf( 114 /* translators: %d: number of failed emails */ 115 __( '%d failed email(s)', 'eprimo-mail-radar' ), 116 $failed_count 117 ), 118 ], 119 ] ); 48 120 } 49 121 … … 98 170 } 99 171 100 // Insert log 101 Eprimo_Mail_Radar_Database::insert( [172 // Insert log and store ID for failed email tracking 173 $this->last_log_id = Eprimo_Mail_Radar_Database::insert( [ 102 174 'to_email' => $log_to, 103 175 'from_email' => Eprimo_Mail_Radar_Formatting_Helper::extract_from_email( $headers ), … … 129 201 } 130 202 203 if ( empty( $this->last_log_id ) ) { 204 return; 205 } 206 131 207 Eprimo_Mail_Radar_Database::mark_as_failed( 132 $ log_id,208 $this->last_log_id, 133 209 $wp_error->get_error_message() 134 210 ); 211 212 // Reset after use 213 $this->last_log_id = 0; 135 214 } 136 215 -
eprimo-mail-radar/trunk/eprimo-mail-radar.php
r3631782 r3639046 3 3 * Plugin Name: Eprimo Mail Radar 4 4 * Description: Log and debug all WordPress emails with easy resend functionality 5 * Version: 1. 0.05 * Version: 1.1.0 6 6 * Author: Eprimo Technologies 7 7 * Author URI: https://eprimotech.com/ … … 16 16 17 17 // Constants 18 define( 'EPRIMO_MAIL_RADAR_VERSION', '1. 0.0' );18 define( 'EPRIMO_MAIL_RADAR_VERSION', '1.1.0' ); 19 19 define( 'EPRIMO_MAIL_RADAR_DB_VERSION', '1.0.0' ); 20 20 define( 'EPRIMO_MAIL_RADAR_PATH', plugin_dir_path( __FILE__ ) ); -
eprimo-mail-radar/trunk/readme.txt
r3631782 r3639046 5 5 Tested up to: 7.0 6 6 Requires PHP: 7.4 7 Stable tag: 1. 0.07 Stable tag: 1.1.0 8 8 License: GPLv2 or later 9 9 License URI: https://www.gnu.org/licenses/gpl-2.0.html … … 28 28 = Key Features = 29 29 30 ** 📧Automatic Email Logging**30 **Automatic Email Logging** 31 31 Every outgoing email is logged automatically — subject, recipient, content, headers, status, and which plugin sent it. 32 32 33 ** 🔍Source Detection**33 **Source Detection** 34 34 Automatically identifies which plugin or theme triggered the email — WooCommerce, Contact Form 7, Gravity Forms, WordPress core, or any other plugin. 35 35 36 ** 📎Attachment Tracking**36 **Attachment Tracking** 37 37 Logs attachment filenames, sizes, and file types. Download attachments directly from the email details page. 38 38 39 ** ⚡Live AJAX Search**39 **Live AJAX Search** 40 40 Search by recipient, subject, or source. Results update instantly as you type — no page reloads. 41 41 42 ** 📅Date Range Filter**42 **Date Range Filter** 43 43 Filter emails by date range to find exactly what you need. 44 44 45 ** 🔄Resend with Recipient Edit**45 **Resend with Recipient Edit** 46 46 Resend any logged email to the original or a different recipient. Edit the recipient address before sending. 47 47 48 ** 🛡️Privacy Controls**48 **Privacy Controls** 49 49 GDPR-friendly options to disable content logging, mask recipient addresses, or disable attachment logging. 50 50 51 ** 🗑️Bulk Actions**51 **Bulk Actions** 52 52 Delete selected emails, all failed emails, or all sent emails in one click. 53 53 54 ** ⏰Auto-Cleanup**54 **Auto-Cleanup** 55 55 Configure automatic deletion of old logs — 7 days, 30 days, 90 days, or keep forever. 56 56 57 ** 🎨Email Preview**57 **Email Preview** 58 58 View rendered HTML emails in a secure sandboxed iframe. Toggle between rendered view and source code. 59 59 … … 150 150 151 151 == Changelog == 152 153 = 1.1.0 = 154 * Added: Send Test Email tool in Settings 155 * Added: Column sorting (Subject, Status, Date) 156 * Added: Admin bar failed email count badge 157 * Fixed: Failed email tracking with explicit log ID 158 * Update: Layout style updated 152 159 153 160 = 1.0.0 =
Note:
See TracChangeset
for help on using the changeset viewer.
![(please configure the [header_logo] section in trac.ini)](/chrome/site/your_project_logo.png)