Plugin Directory

Changeset 3639046


Ignore:
Timestamp:
08/09/2026 03:54:35 AM (3 months ago)
Author:
eprimo
Message:

Version 1.1.0 - Test Email tool, column sorting, admin bar failed count badge, updated UI

Location:
eprimo-mail-radar/trunk
Files:
9 edited

Legend:

Unmodified
Added
Removed
  • eprimo-mail-radar/trunk/admin/css/admin-style.css

    r3631782 r3639046  
    44.eprimo-mail-radar-single .email-content-box {
    55    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;
    617}
    718
    … …  
    123134    color: inherit;
    124135}
     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}
    125193
    126194/* Highlight animation for newly resent emails */
  • eprimo-mail-radar/trunk/admin/js/admin-script.js

    r3631782 r3639046  
    178178            var status = currentUrl.searchParams.get('status') || '';
    179179           
    180             // Get date values
    181180            var dateFrom = $('#date-from').val() || '';
    182181            var dateTo = $('#date-to').val() || '';
     182            var orderby = $('#eprimo-current-orderby').val() || 'created_at';
     183            var order = $('#eprimo-current-order').val() || 'DESC';
    183184           
    184185            $.ajax({
    … …  
    192193                    paged: page,
    193194                    date_from: dateFrom,
    194                     date_to: dateTo
     195                    date_to: dateTo,
     196                    orderby: orderby,
     197                    order: order
    195198                },
    196199                success: function(response) {
    … …  
    211214                            $('.subsubsub li.failed .count').text('(' + response.data.counts.failed + ')');
    212215                        }
    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
    215237                        var newUrl = new URL(window.location.href);
    216238                        newUrl.searchParams.set('page', 'eprimo-mail-radar');
    … …  
    228250                        }
    229251                       
    230                         // ADDed date to URL
    231252                        if (dateFrom) {
    232253                            newUrl.searchParams.set('date_from', dateFrom);
    … …  
    240261                            newUrl.searchParams.delete('date_to');
    241262                        }
     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                        }
    242274                       
    243275                        if (page > 1) {
    … …  
    249281                        window.history.pushState({}, '', newUrl.toString());
    250282                       
    251                         // Show/hide clear button
    252283                        if (query || dateFrom || dateTo) {
    253284                            if (!$('.clear-search-btn').length) {
    … …  
    372403        });
    373404
     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
    374425        // ============================================
    375426        // Single Email View
    … …  
    685736
    686737        /**
     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        /**
    687807         * Toggle retention days input
    688808         */
  • eprimo-mail-radar/trunk/admin/views/email-list.php

    r3631782 r3639046  
    44 *
    55 * 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().
    88 *
    99 * @package Eprimo_Mail_Radar
    … …  
    1616 * @var string $date_from Date range start.
    1717 * @var string $date_to   Date range end.
     18 * @var string $orderby   Current sort column.
     19 * @var string $order     Current sort direction (ASC/DESC).
    1820 */
    1921
    … …  
    2628$current_status = $status;
    2729$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 */
     43function 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}
    2863?>
    2964
    … …  
    139174    </form>
    140175
     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
    141180    <!-- Email List Table -->
    142181    <form method="post" action="" id="email-list-form">
    … …  
    176215                        <input type="checkbox" id="select-all-top">
    177216                    </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                    ?>
    179221                    <th><?php esc_html_e( 'To', 'eprimo-mail-radar' ); ?></th>
    180222                    <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                    ?>
    183229                </tr>
    184230            </thead>
  • eprimo-mail-radar/trunk/admin/views/settings.php

    r3631782 r3639046  
    5656    <!-- Statistics Boxes -->
    5757    <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;">
    5959            <h3 style="margin: 0 0 10px 0; color: #1d2327;">
    6060                <?php echo esc_html( number_format( $stats->total ) ); ?>
    … …  
    6565        </div>
    6666
    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;">
    6868            <h3 style="margin: 0 0 10px 0; color: #1d2327;">
    6969                <?php echo esc_html( number_format( $stats->sent ) ); ?>
    … …  
    7474        </div>
    7575
    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;">
    7777            <h3 style="margin: 0 0 10px 0; color: #1d2327;">
    7878                <?php echo esc_html( number_format( $stats->failed ) ); ?>
    … …  
    8383        </div>
    8484
    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;">
    8686            <h3 style="margin: 0 0 10px 0; color: #1d2327; font-size: 16px;">
    8787                <?php echo esc_html( Eprimo_Mail_Radar_Formatting_Helper::format_date_only( $stats->oldest ) ); ?>
    … …  
    287287        </button>
    288288    </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>
    289318
    290319    <hr style="margin: 40px 0;">
  • eprimo-mail-radar/trunk/classes/class-admin.php

    r3631782 r3639046  
    4343        add_action( 'wp_ajax_eprimo_mail_radar_resend_ajax', [ $instance, 'ajax_resend_email' ] );
    4444        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' ] );
    4548    }
    4649
    … …  
    159162            'deleteText'          => __( 'Delete', 'eprimo-mail-radar' ),
    160163            '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' ),
    161168        ];
    162169    }
    … …  
    188195        $date_from = isset( $_GET['date_from'] ) ? sanitize_text_field( wp_unslash($_GET['date_from']) ) : '';
    189196        $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';
    190199
    191200        $args = [
    … …  
    196205            'date_from' => $date_from,
    197206            'date_to'   => $date_to,
     207            'order_by'  => $orderby,
     208            'order'     => $order,
    198209        ];
    199210
    … …  
    486497        $date_from = isset( $_POST['date_from'] ) ? sanitize_text_field( wp_unslash($_POST['date_from']) ) : '';
    487498        $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';
    488501
    489502        $args = [
    … …  
    494507            'date_from' => $date_from,
    495508            'date_to'   => $date_to,
     509            'order_by'  => $orderby,
     510            'order'     => $order,           
    496511        ];
    497512
    … …  
    721736        return ob_get_clean();
    722737    }
     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    }   
    723788}
  • eprimo-mail-radar/trunk/classes/class-database.php

    r3631782 r3639046  
    586586                }
    587587        }
     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        }       
    588605}
  • eprimo-mail-radar/trunk/classes/class-eprimo-mail-radar.php

    r3631782 r3639046  
    44 *
    55 * 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.
    77 *
    88 * @package Eprimo_Mail_Radar
    … …  
    1616
    1717    /**
     18     * Last inserted log ID for failed email tracking.
     19     *
     20     * @var int
     21     */
     22    private $last_log_id = 0;
     23
     24    /**
    1825     * Initialize hooks
    1926     *
    … …  
    3037        add_action( 'eprimo_mail_radar_cleanup', [ $instance, 'cleanup_old_logs' ] );
    3138
     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
    3242        // Admin
    3343        if ( is_admin() ) {
    … …  
    4656    public static function check_migrations() {
    4757        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        ] );
    48120    }
    49121
    … …  
    98170        }
    99171
    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( [
    102174            'to_email'    => $log_to,
    103175            'from_email'  => Eprimo_Mail_Radar_Formatting_Helper::extract_from_email( $headers ),
    … …  
    129201        }
    130202
     203        if ( empty( $this->last_log_id ) ) {
     204            return;
     205        }
     206
    131207        Eprimo_Mail_Radar_Database::mark_as_failed(
    132             $log_id,
     208            $this->last_log_id,
    133209            $wp_error->get_error_message()
    134210        );
     211
     212        // Reset after use
     213        $this->last_log_id = 0;
    135214    }
    136215
  • eprimo-mail-radar/trunk/eprimo-mail-radar.php

    r3631782 r3639046  
    33 * Plugin Name: Eprimo Mail Radar
    44 * Description: Log and debug all WordPress emails with easy resend functionality
    5  * Version: 1.0.0
     5 * Version: 1.1.0
    66 * Author: Eprimo Technologies
    77 * Author URI: https://eprimotech.com/
    … …  
    1616
    1717// Constants
    18 define( 'EPRIMO_MAIL_RADAR_VERSION', '1.0.0' );
     18define( 'EPRIMO_MAIL_RADAR_VERSION', '1.1.0' );
    1919define( 'EPRIMO_MAIL_RADAR_DB_VERSION', '1.0.0' );
    2020define( 'EPRIMO_MAIL_RADAR_PATH', plugin_dir_path( __FILE__ ) );
  • eprimo-mail-radar/trunk/readme.txt

    r3631782 r3639046  
    55Tested up to: 7.0
    66Requires PHP: 7.4
    7 Stable tag: 1.0.0
     7Stable tag: 1.1.0
    88License: GPLv2 or later
    99License URI: https://www.gnu.org/licenses/gpl-2.0.html
    … …  
    2828= Key Features =
    2929
    30 **📧 Automatic Email Logging**
     30**Automatic Email Logging**
    3131Every outgoing email is logged automatically — subject, recipient, content, headers, status, and which plugin sent it.
    3232
    33 **🔍 Source Detection**
     33**Source Detection**
    3434Automatically identifies which plugin or theme triggered the email — WooCommerce, Contact Form 7, Gravity Forms, WordPress core, or any other plugin.
    3535
    36 **📎 Attachment Tracking**
     36**Attachment Tracking**
    3737Logs attachment filenames, sizes, and file types. Download attachments directly from the email details page.
    3838
    39 **⚡ Live AJAX Search**
     39**Live AJAX Search**
    4040Search by recipient, subject, or source. Results update instantly as you type — no page reloads.
    4141
    42 **📅 Date Range Filter**
     42**Date Range Filter**
    4343Filter emails by date range to find exactly what you need.
    4444
    45 **🔄 Resend with Recipient Edit**
     45**Resend with Recipient Edit**
    4646Resend any logged email to the original or a different recipient. Edit the recipient address before sending.
    4747
    48 **🛡️ Privacy Controls**
     48**Privacy Controls**
    4949GDPR-friendly options to disable content logging, mask recipient addresses, or disable attachment logging.
    5050
    51 **🗑️ Bulk Actions**
     51**Bulk Actions**
    5252Delete selected emails, all failed emails, or all sent emails in one click.
    5353
    54 **⏰ Auto-Cleanup**
     54**Auto-Cleanup**
    5555Configure automatic deletion of old logs — 7 days, 30 days, 90 days, or keep forever.
    5656
    57 **🎨 Email Preview**
     57**Email Preview**
    5858View rendered HTML emails in a secure sandboxed iframe. Toggle between rendered view and source code.
    5959
    … …  
    150150
    151151== 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
    152159
    153160= 1.0.0 =
Note: See TracChangeset for help on using the changeset viewer.