Search code examples
phpjqueryajaxwoocommerceadmin

Custom WooCommerce Admin notice with a dynamic message


I cannot display an admin message by sending an argument to the function. It was suggested I use a closure function but this does not appear to work. Here is the code

function custom_woocommerce_admin_notice( $message ) {
    
add_action( 'admin_notices', function() use ( $message ) {
    wc_get_logger()->info('Order Message: '.$message ) ;
    if ( function_exists( 'get_current_screen' ) ) {
        $screen = get_current_screen();
        if ( isset( $screen->id ) && $screen->id === 'edit-shop_order' ) {
            // Check if the notice has already been dismissed
            if ( ! get_option( 'custom_woocommerce_admin_notice_dismissed' ) ) {
                ?>
                <div class="notice notice-success is-dismissible woocommerce-notice">
                    <p><?php _e( 'Hello: '.$message , 'text-domain' ); ?></p>
                </div>
                <?php
            }
        }
    }
    });
}

add_action( 'admin_notices', 'custom_woocommerce_admin_notice' );


function custom_woocommerce_admin_notice_dismissed() {
    update_option( 'custom_woocommerce_admin_notice_dismissed', 1 );
}
add_action( 'wp_ajax_custom_woocommerce_admin_notice_dismissed', 'custom_woocommerce_admin_notice_dismissed' );

function enqueue_custom_woocommerce_admin_notice_script( $hook ) {
    // Check if we are on the WooCommerce orders page
    if ( function_exists( 'get_current_screen' ) ) {
        $screen = get_current_screen();
        if ( isset( $screen->id ) && $screen->id === 'edit-shop_order' ) {
            wp_enqueue_script( 'custom_woocommerce_admin_notice_script', get_template_directory_uri() . '/js/custom-woocommerce-admin-notice.js', array( 'jquery' ), null, true );
        }
    }
}
add_action( 'admin_enqueue_scripts', 'enqueue_custom_woocommerce_admin_notice_script' );

The function is called using

custom_woocommerce_admin_notice( $message );

Any ideas what is wrong? Without the closure function and not passing the $message variable and using a string it works.

For completeness here is the js script

jQuery(document).ready(function($) {
    $('.notice.is-dismissible').on('click', '.notice-dismiss', function() {
        $.ajax({
            url: ajaxurl,
            type: 'POST',
            data: {
                action: 'custom_woocommerce_admin_notice_dismissed'
            }
        });
    });
});

Solution

  • There are multiple mistakes in your code.

    To display a message dynamically inside this custom notice, can use the following approach.

    Note that the code is compatible with HPOS.

    For the dismissed option, instead of saving it as global option (settings), it is better to save it as user metadata.

    The PHP Code:

    class Custom_WC_Admin_Notice {
        /**
         * The variable to be displayed in the success notice.
         *
         * @var string
         */
        private string $message;
    
        /**
         * Initialize class.
         *
         * @param string $message Message to be displayed in a success notice.
         */
        public function __construct( string $message ) {
            $this->message = $message;
    
            add_action( 'admin_notices', array( $this, 'render' ) );
            add_action( 'admin_enqueue_scripts', array( $this, 'admin_enqueue_scripts') );
            add_action( 'wp_ajax_custom_wc_admin_orders_notice_dismissed', array( $this, 'notice_dismissed') );
        }
    
        /**
         * Displays success notice on WooCommerce admin orders screen.
         *
         * @return void
         */
        public function render() {
            if ( ! function_exists('get_current_screen') ) {
                return;
            }
            $screen = get_current_screen();
            
            if ( isset( $screen->id ) && in_array($screen->id, ['edit-shop_order', 'woocommerce_page_wc-orders'], true) ) {
                $dismissed = get_user_meta( get_current_user_id(), 'custom_wc_admin_notice_dismissed', true );
                
                if ( $dismissed ) {
                    return;
                }
                printf( '<div class="notice notice-success custom-notice is-dismissible"><p>Hello: %s</p></div>', esc_html( $this->message ) );
            }   
        }
    
        /**
         * Enqueue Scripts
         *
         * @return void
         */
        public function admin_enqueue_scripts() {
            if ( ! function_exists('get_current_screen') ) {
                return;
            }
            $screen = get_current_screen();
            
            if ( isset( $screen->id ) && in_array($screen->id, ['edit-shop_order', 'woocommerce_page_wc-orders'], true) ) {
                $dismissed = get_user_meta( get_current_user_id(), 'custom_wc_admin_notice_dismissed', true );
                
                if ( $dismissed ) {
                    return;
                }
                // With the main theme use: get_template_directory_uri() . /js/…
                // With a child theme use: get_stylesheet_directory_uri() . /js/…
                // With a plugin use: plugin_dir_url( __FILE__ ) . js/…
                wp_enqueue_script( 'custom-wc-admin-notice', get_template_directory_uri() . '/js/custom-wc-admin-notice.js', array( 'jquery' ), null, true );
            
                wp_localize_script('custom-wc-admin-notice', 'wc_custom_dan', array(
                    'ajax_url' => admin_url('admin-ajax.php'), 
                    'user_id'  => get_current_user_id(),
                    'nonce'    => wp_create_nonce('wc_custom_dan'),
                ));
            }   
        }
    
        /**
         * Ajax update user metadata when the notice is dismissed.
         *
         * @return void
         */
        public function notice_dismissed() {
            if ( isset($_POST['nonce']) && wp_verify_nonce($_POST['nonce'], 'wc_custom_dan') 
            && isset($_POST['user_id']) && $_POST['user_id'] > 0 ) {
                echo update_user_meta( intval($_POST['user_id']), 'custom_wc_admin_notice_dismissed', true );
            }
            wp_die();
        }
    }
    

    Code goes in functions.php file of your child theme (or in a plugin).

    The JavaScript code in a file named "custom-wc-admin-notice.js" (located in a "js" directory):

    jQuery(function($) {
        if ( typeof wc_custom_dan === 'undefined' ) {
            return false;
        }
    
        $('.notice.is-dismissible.custom-notice').on('click', '.notice-dismiss', function() {
            $.ajax({
                url: wc_custom_dan.ajax_url,
                type: 'POST',
                data: {
                    'action':  'custom_wc_admin_orders_notice_dismissed',
                    'user_id': wc_custom_dan.user_id,
                    'nonce':   wc_custom_dan.nonce
                },
                success: function(response) {
                    console.log(response);
                }
            });
        });
    });
    

    Now you can display the notice with a custom message using:

    $message = "My custom message";
    new Custom_WC_Admin_Notice( $message );
    

    Tested and works to display a custom dynamic notice on WooCommerce Admin orders List (and compatible with HPOS), dismisible once by each user.

    enter image description here