# easy-invoice/2.3.8/includes/Services/ReviewNoticeService.php

Easy Invoice – Invoice Generator, PDF Quotes &amp; Payments, version 2.3.8. 558 lines.

- Page: https://pluginprobe.com/plugins/easy-invoice/2.3.8/code/includes/Services/ReviewNoticeService.php
- Raw: https://pluginprobe.com/plugins/easy-invoice/2.3.8/raw/includes/Services/ReviewNoticeService.php
- Modified: 2025-11-03T03:42:48+00:00

Line numbers below start at 1. Link to a line or a range by appending a fragment to the
page URL, for example `https://pluginprobe.com/plugins/easy-invoice/2.3.8/code/includes/Services/ReviewNoticeService.php#L10-L20`.

```php
<?php
/**
 * Review Notice Service
 *
 * @package EasyInvoice
 * @subpackage Services
 */

namespace EasyInvoice\Services;

/**
 * ReviewNoticeService Class
 *
 * Handles review notice display logic for free users
 */
class ReviewNoticeService {
    
    /**
     * Notice option key for dismissal
     */
    const DISMISSED_OPTION = 'easy_invoice_review_notice_dismissed';
    
    /**
     * Notice option key for last shown timestamp
     */
    const LAST_SHOWN_OPTION = 'easy_invoice_review_notice_last_shown';
    
    /**
     * Notice option key to track if user actually interacted (clicked a button)
     * This helps us distinguish between old auto-set values and user interactions
     */
    const USER_INTERACTED_OPTION = 'easy_invoice_review_notice_user_interacted';
    
    /**
     * Minimum documents required to show notice
     */
    const MIN_DOCUMENTS = 2;
    
    /**
     * Days to wait before showing again if skipped
     */
    const RESHOW_AFTER_DAYS = 7;
    
    /**
     * WordPress.org review URL (5-star reviews page)
     */
    const REVIEW_URL = 'https://wordpress.org/support/plugin/easy-invoice/reviews/?filter=5';
    
    /**
     * Initialize the review notice service
     */
    public static function init() {
        $instance = new self();
        
        // Add admin notice with high priority (before notices are removed)
        // Note: EasyInvoice removes admin notices, so we add it early
        add_action('admin_notices', [$instance, 'displayAdminNotice'], 5);
        
        // Add custom UI notice (via filter to inject into template)
        // Use priority 10 to ensure it runs
        add_action('easy_invoice_admin_before_main_content', [$instance, 'displayCustomNotice'], 10);
        
        // Handle AJAX actions
        add_action('wp_ajax_easy_invoice_dismiss_review_notice', [$instance, 'handleDismissal']);
        add_action('wp_ajax_easy_invoice_skip_review_notice', [$instance, 'handleSkip']);
    }
    
    /**
     * Check if notice should be displayed
     *
     * @return bool
     */
    public function shouldDisplayNotice(): bool {
        // Only show for free users
        if (easy_invoice_has_pro()) {
            return false;
        }
        
        $user_id = get_current_user_id();
        
        // Don't show if dismissed (user clicked "Close")
        if (get_user_meta($user_id, self::DISMISSED_OPTION, true)) {
            return false;
        }
        
        // Check if user has created enough documents
        $total_documents = $this->getTotalDocumentsCount();
        if ($total_documents <= self::MIN_DOCUMENTS) {
            return false;
        }
        
        // Check if we should reshow (if user clicked "Maybe Later" and 7 days passed)
        // IMPORTANT: Only respect LAST_SHOWN_OPTION if user actually interacted (clicked a button)
        // This prevents old auto-set values from blocking the notice
        $user_interacted = get_user_meta($user_id, self::USER_INTERACTED_OPTION, true);
        $last_shown = get_user_meta($user_id, self::LAST_SHOWN_OPTION, true);
        
        // Only check LAST_SHOWN_OPTION if user has actually interacted with a button
        // If LAST_SHOWN_OPTION exists but user_interacted is false, it means the old code set it
        // In that case, we ignore it and show the notice (since user never clicked a button)
        if ($user_interacted && $last_shown && (int)$last_shown > 0) {
            $reshow_date = strtotime('+' . self::RESHOW_AFTER_DAYS . ' days', (int)$last_shown);
            
            // Only block if it's been less than 7 days since user clicked "Maybe Later"
            if (time() < $reshow_date) {
                return false;
            }
        } elseif ($last_shown && !$user_interacted) {
            // LAST_SHOWN_OPTION exists but user never interacted - this was set by old code
            // Clear it silently and show the notice
            delete_user_meta($user_id, self::LAST_SHOWN_OPTION);
        }
        
        // All checks passed - show the notice
        return true;
    }
    
    /**
     * Get total count of invoices and quotes
     *
     * @return int
     */
    private function getTotalDocumentsCount(): int {
        global $wpdb;
        
        // Count ALL invoices including drafts, published, pending, etc.
        // Query directly to get accurate count of all post statuses
        $invoice_count = (int) $wpdb->get_var($wpdb->prepare(
            "SELECT COUNT(*) FROM {$wpdb->posts} 
            WHERE post_type = %s 
            AND post_status != 'trash'",
            \EasyInvoice\Constants\PostTypes::EASY_INVOICE_POST_TYPE
        ));
        
        // Count ALL quotes including drafts, published, pending, etc.
        $quote_count = (int) $wpdb->get_var($wpdb->prepare(
            "SELECT COUNT(*) FROM {$wpdb->posts} 
            WHERE post_type = %s 
            AND post_status != 'trash'",
            \EasyInvoice\Constants\PostTypes::EASY_INVOICE_QUOTE_POST_TYPE
        ));
        
        return $invoice_count + $quote_count;
    }
    
    /**
     * Display admin notice (WordPress admin UI)
     * Shows on ALL WordPress admin pages (Dashboard, Posts, Pages, etc.)
     */
    public function displayAdminNotice() {
        if (!$this->shouldDisplayNotice()) {
            return;
        }
        
        // Show on all WordPress admin pages
        $this->renderNotice();
        
        // DO NOT update LAST_SHOWN_OPTION here - only update when user clicks a button
    }
    
    /**
     * Display custom notice (plugin's custom UI)
     *
     * @param string $page Current page slug
     */
    public function displayCustomNotice($page = '') {
        if (!$this->shouldDisplayNotice()) {
            return;
        }
        
        // Add wrapper div for proper spacing
        echo '<div style="padding: 0 20px; padding-top: 20px;">';
        $this->renderCustomNotice();
        echo '</div>';
        
        // DO NOT update LAST_SHOWN_OPTION here - only update when user clicks a button
    }
    
    /**
     * Render WordPress admin notice
     */
    private function renderNotice() {
        ?>
        <div class="notice notice-info easy-invoice-review-notice" id="easy-invoice-review-notice-admin" role="status" aria-live="polite">
            <div class="easy-invoice-review-notice-content" style="display: flex; align-items: center; gap: 15px; padding: 10px 0;">
                <div style="flex: 1;">
                    <p style="margin: 0; font-size: 14px;">
                        <strong><?php esc_html_e('Enjoying Easy Invoice?', 'easy-invoice'); ?></strong>
                        <?php esc_html_e('If you find Easy Invoice helpful, would you mind taking a moment to leave us a 5-star review? It really helps us grow and improve!', 'easy-invoice'); ?>
                    </p>
                </div>
                <div style="display: flex; gap: 10px; align-items: center; flex-wrap: wrap;">
                    <a href="<?php echo esc_url(self::REVIEW_URL); ?>" 
                       target="_blank" 
                       rel="noopener noreferrer"
                       class="button button-primary easy-invoice-leave-review" 
                       aria-label="<?php esc_attr_e('Leave a 5-star review on WordPress.org', 'easy-invoice'); ?>"
                       style="margin-right: 5px;">
                        <?php esc_html_e('Leave a Review', 'easy-invoice'); ?>
                    </a>
                    <button type="button" 
                            class="button easy-invoice-skip-review-notice"
                            aria-label="<?php esc_attr_e('Remind me later about leaving a review', 'easy-invoice'); ?>"
                            style="margin-right: 5px;">
                        <?php esc_html_e('Maybe Later', 'easy-invoice'); ?>
                    </button>
                    <button type="button" 
                            class="button-link easy-invoice-close-review-notice"
                            aria-label="<?php esc_attr_e('Dismiss this notice permanently', 'easy-invoice'); ?>"
                            style="text-decoration: none; border: none; background: none; color: #2271b1; cursor: pointer;">
                        <?php esc_html_e('Close', 'easy-invoice'); ?>
                    </button>
                </div>
            </div>
        </div>
        <script>
        (function($) {
            'use strict';
            
            $(document).ready(function() {
                var $adminNotice = $('#easy-invoice-review-notice-admin');
                
                // Handle "Leave a Review" - open review page and mark as shown (skip)
                $(document).on('click', '.easy-invoice-leave-review', function(e) {
                    e.preventDefault();
                    var $link = $(this);
                    var originalHtml = $link.html();
                    var isDisabled = $link.prop('disabled');
                    
                    if (isDisabled) {
                        return;
                    }
                    
                    $link.prop('disabled', true).css('opacity', '0.7').css('pointer-events', 'none');
                    
                    // Mark as shown so we don't show again for 7 days
                    $.ajax({
                        url: ajaxurl || '<?php echo esc_js(admin_url('admin-ajax.php')); ?>',
                        type: 'POST',
                        data: {
                            action: 'easy_invoice_skip_review_notice',
                            nonce: '<?php echo wp_create_nonce('easy_invoice_skip_review_notice'); ?>'
                        },
                        error: function() {
                            $link.prop('disabled', false).css('opacity', '1').css('pointer-events', 'auto');
                        }
                    });
                    
                    // Open review page in new tab
                    var reviewUrl = '<?php echo esc_js(self::REVIEW_URL); ?>';
                    if (reviewUrl) {
                        window.open(reviewUrl, '_blank', 'noopener,noreferrer');
                    }
                });
                
                // Handle "Maybe Later" button
                $(document).on('click', '.easy-invoice-skip-review-notice', function(e) {
                    e.preventDefault();
                    var $button = $(this);
                    var originalHtml = $button.html();
                    var isDisabled = $button.prop('disabled');
                    
                    if (isDisabled) {
                        return;
                    }
                    
                    $button.prop('disabled', true).css('opacity', '0.7');
                    
                    $.ajax({
                        url: ajaxurl || '<?php echo esc_js(admin_url('admin-ajax.php')); ?>',
                        type: 'POST',
                        data: {
                            action: 'easy_invoice_skip_review_notice',
                            nonce: '<?php echo wp_create_nonce('easy_invoice_skip_review_notice'); ?>'
                        },
                        success: function() {
                            $adminNotice.fadeOut(300, function() {
                                $(this).remove();
                            });
                        },
                        error: function() {
                            $button.prop('disabled', false).css('opacity', '1').html(originalHtml);
                        }
                    });
                });
                
                // Handle "Close" button - dismiss permanently
                $(document).on('click', '.easy-invoice-close-review-notice', function(e) {
                    e.preventDefault();
                    var $button = $(this);
                    var originalHtml = $button.html();
                    var isDisabled = $button.prop('disabled');
                    
                    if (isDisabled) {
                        return;
                    }
                    
                    $button.prop('disabled', true).css('opacity', '0.7');
                    
                    $.ajax({
                        url: ajaxurl || '<?php echo esc_js(admin_url('admin-ajax.php')); ?>',
                        type: 'POST',
                        data: {
                            action: 'easy_invoice_dismiss_review_notice',
                            nonce: '<?php echo wp_create_nonce('easy_invoice_dismiss_review_notice'); ?>'
                        },
                        success: function() {
                            $adminNotice.fadeOut(300, function() {
                                $(this).remove();
                            });
                        },
                        error: function() {
                            $button.prop('disabled', false).css('opacity', '1').html(originalHtml);
                        }
                    });
                });
            });
        })(jQuery);
        </script>
        <?php
    }
    
    /**
     * Render custom UI notice
     */
    private function renderCustomNotice() {
        ?>
        <div class="easy-invoice-review-notice-custom" id="easy-invoice-review-notice-custom" role="status" aria-live="polite" style="margin: 20px 0; padding: 16px; background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); border-radius: 8px; color: white; box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);">
            <div style="display: flex; align-items: center; justify-content: space-between; gap: 20px; flex-wrap: wrap;">
                <div style="flex: 1; min-width: 250px;">
                    <div style="display: flex; align-items: center; gap: 12px; margin-bottom: 8px;">
                        <svg style="width: 24px; height: 24px; fill: currentColor;" viewBox="0 0 24 24" aria-hidden="true">
                            <path d="M12 2l3.09 6.26L22 9l-5.91 3.74L18.18 21 12 17.27 5.82 21l1.73-8.26L2 9l6.91-.74L12 2z"/>
                        </svg>
                        <h3 style="margin: 0; font-size: 18px; font-weight: 600;">
                            <?php esc_html_e('Enjoying Easy Invoice?', 'easy-invoice'); ?>
                        </h3>
                    </div>
                    <p style="margin: 0; font-size: 14px; opacity: 0.95;">
                        <?php esc_html_e('If you find Easy Invoice helpful, would you mind taking a moment to leave us a 5-star review? It really helps us grow and improve!', 'easy-invoice'); ?>
                    </p>
                </div>
                <div style="display: flex; gap: 12px; align-items: center; flex-shrink: 0; flex-wrap: wrap;">
                    <a href="<?php echo esc_url(self::REVIEW_URL); ?>" 
                       target="_blank" 
                       rel="noopener noreferrer"
                       class="easy-invoice-leave-review-custom easy-invoice-review-btn" 
                       aria-label="<?php esc_attr_e('Leave a 5-star review on WordPress.org', 'easy-invoice'); ?>"
                       style="background: white; color: #667eea; border: none; padding: 10px 20px; border-radius: 6px; text-decoration: none; font-weight: 600; transition: transform 0.2s ease, box-shadow 0.2s ease; display: inline-block;">
                        <?php esc_html_e('Leave a Review', 'easy-invoice'); ?>
                        <span style="margin-left: 6px;" aria-hidden="true">→</span>
                    </a>
                    <button type="button" 
                            class="easy-invoice-skip-review-notice-custom easy-invoice-review-btn"
                            aria-label="<?php esc_attr_e('Remind me later about leaving a review', 'easy-invoice'); ?>"
                            style="background: rgba(255, 255, 255, 0.2); color: white; border: 1px solid rgba(255, 255, 255, 0.3); padding: 10px 16px; border-radius: 6px; cursor: pointer; font-weight: 500; transition: background 0.2s ease;">
                        <?php esc_html_e('Maybe Later', 'easy-invoice'); ?>
                    </button>
                    <button type="button" 
                            class="easy-invoice-close-review-notice-custom easy-invoice-review-btn"
                            aria-label="<?php esc_attr_e('Dismiss this notice permanently', 'easy-invoice'); ?>"
                            style="background: rgba(255, 255, 255, 0.1); color: white; border: 1px solid rgba(255, 255, 255, 0.2); padding: 10px 16px; border-radius: 6px; cursor: pointer; font-weight: 500; transition: background 0.2s ease;">
                        <?php esc_html_e('Close', 'easy-invoice'); ?>
                    </button>
                </div>
            </div>
        </div>
        <style>
        .easy-invoice-review-btn:hover {
            transform: translateY(-1px);
            box-shadow: 0 2px 4px rgba(0, 0, 0, 0.15);
        }
        .easy-invoice-leave-review-custom:hover {
            transform: scale(1.02) translateY(-1px);
            box-shadow: 0 2px 8px rgba(102, 126, 234, 0.3);
        }
        .easy-invoice-skip-review-notice-custom:hover {
            background: rgba(255, 255, 255, 0.3) !important;
        }
        .easy-invoice-close-review-notice-custom:hover {
            background: rgba(255, 255, 255, 0.2) !important;
        }
        @media (max-width: 768px) {
            .easy-invoice-review-notice-custom > div {
                flex-direction: column;
                align-items: flex-start !important;
            }
            .easy-invoice-review-notice-custom > div > div:last-child {
                width: 100%;
                justify-content: flex-start;
            }
        }
        </style>
        <script>
        (function($) {
            'use strict';
            
            $(document).ready(function() {
                var $customNotice = $('#easy-invoice-review-notice-custom');
                
                // Handle "Leave a Review" - open review page and mark as shown (skip)
                $(document).on('click', '.easy-invoice-leave-review-custom', function(e) {
                    e.preventDefault();
                    var $link = $(this);
                    var originalHtml = $link.html();
                    var isDisabled = $link.prop('disabled') || $link.css('pointer-events') === 'none';
                    
                    if (isDisabled) {
                        return;
                    }
                    
                    $link.prop('disabled', true).css('opacity', '0.7').css('pointer-events', 'none');
                    
                    // Mark as shown so we don't show again for 7 days
                    $.ajax({
                        url: ajaxurl || '<?php echo esc_js(admin_url('admin-ajax.php')); ?>',
                        type: 'POST',
                        data: {
                            action: 'easy_invoice_skip_review_notice',
                            nonce: '<?php echo wp_create_nonce('easy_invoice_skip_review_notice'); ?>'
                        },
                        error: function() {
                            $link.prop('disabled', false).css('opacity', '1').css('pointer-events', 'auto');
                        }
                    });
                    
                    // Open review page in new tab
                    var reviewUrl = '<?php echo esc_js(self::REVIEW_URL); ?>';
                    if (reviewUrl) {
                        window.open(reviewUrl, '_blank', 'noopener,noreferrer');
                    }
                });
                
                // Handle "Maybe Later" button
                $(document).on('click', '.easy-invoice-skip-review-notice-custom', function(e) {
                    e.preventDefault();
                    var $button = $(this);
                    var originalHtml = $button.html();
                    var isDisabled = $button.prop('disabled');
                    
                    if (isDisabled) {
                        return;
                    }
                    
                    $button.prop('disabled', true).css('opacity', '0.7');
                    
                    $.ajax({
                        url: ajaxurl || '<?php echo esc_js(admin_url('admin-ajax.php')); ?>',
                        type: 'POST',
                        data: {
                            action: 'easy_invoice_skip_review_notice',
                            nonce: '<?php echo wp_create_nonce('easy_invoice_skip_review_notice'); ?>'
                        },
                        success: function() {
                            $customNotice.fadeOut(300, function() {
                                $(this).remove();
                            });
                        },
                        error: function() {
                            $button.prop('disabled', false).css('opacity', '1').html(originalHtml);
                        }
                    });
                });
                
                // Handle "Close" button - dismiss permanently
                $(document).on('click', '.easy-invoice-close-review-notice-custom', function(e) {
                    e.preventDefault();
                    var $button = $(this);
                    var originalHtml = $button.html();
                    var isDisabled = $button.prop('disabled');
                    
                    if (isDisabled) {
                        return;
                    }
                    
                    $button.prop('disabled', true).css('opacity', '0.7');
                    
                    $.ajax({
                        url: ajaxurl || '<?php echo esc_js(admin_url('admin-ajax.php')); ?>',
                        type: 'POST',
                        data: {
                            action: 'easy_invoice_dismiss_review_notice',
                            nonce: '<?php echo wp_create_nonce('easy_invoice_dismiss_review_notice'); ?>'
                        },
                        success: function() {
                            $customNotice.fadeOut(300, function() {
                                $(this).remove();
                            });
                        },
                        error: function() {
                            $button.prop('disabled', false).css('opacity', '1').html(originalHtml);
                        }
                    });
                });
            });
        })(jQuery);
        </script>
        <?php
    }
    
    /**
     * Handle AJAX dismissal (Close button - never show again)
     */
    public function handleDismissal() {
        // Verify nonce and user capability
        check_ajax_referer('easy_invoice_dismiss_review_notice', 'nonce');
        
        if (!current_user_can('read')) {
            wp_send_json_error(['message' => __('You do not have permission to perform this action.', 'easy-invoice')]);
            return;
        }
        
        $user_id = get_current_user_id();
        
        if (!$user_id) {
            wp_send_json_error(['message' => __('User not found.', 'easy-invoice')]);
            return;
        }
        
        // Mark that user has interacted
        update_user_meta($user_id, self::USER_INTERACTED_OPTION, true);
        
        // Save dismissal state per user (permanent)
        update_user_meta($user_id, self::DISMISSED_OPTION, true);
        
        wp_send_json_success(['message' => __('Review notice dismissed.', 'easy-invoice')]);
    }
    
    /**
     * Handle AJAX skip (Maybe Later button - show again after 7 days)
     */
    public function handleSkip() {
        // Verify nonce and user capability
        check_ajax_referer('easy_invoice_skip_review_notice', 'nonce');
        
        if (!current_user_can('read')) {
            wp_send_json_error(['message' => __('You do not have permission to perform this action.', 'easy-invoice')]);
            return;
        }
        
        $user_id = get_current_user_id();
        
        if (!$user_id) {
            wp_send_json_error(['message' => __('User not found.', 'easy-invoice')]);
            return;
        }
        
        // Mark that user has interacted
        update_user_meta($user_id, self::USER_INTERACTED_OPTION, true);
        
        // Update last shown timestamp - will reshow after 7 days
        update_user_meta($user_id, self::LAST_SHOWN_OPTION, time());
        
        wp_send_json_success(['message' => __('Review notice skipped. We\'ll remind you again later!', 'easy-invoice')]);
    }
}


```
