Make WordPress Core

Opened 4 months ago

Last modified 4 months ago

#60832 new defect (bug)

add_meta_box breaks on plugin sub-pages if add_menu_page has dynamic title

Reported by: malcure's profile malcure Owned by:
Milestone: Awaiting Review Priority: normal
Severity: normal Version: 6.4.3
Component: Menus Keywords: has-patch
Focuses: administration Cc:

Description

add_meta_box breaks on plugin sub-pages if add_menu_page has dynamic title.

Poc and fix attached.

The reason is that if the menu title is dynamic like 'My Plugin <span class="menu-counter">' . $count . '</span>'

Attachments (1)

poc-bug.php (7.8 KB) - added by malcure 4 months ago.
A PoC plugin with possible fix

Download all attachments as: .zip

Change History (3)

@malcure
4 months ago

A PoC plugin with possible fix

#1 @malcure
4 months ago

The reason is that if the menu title is dynamic like 'My Plugin <span class="menu-counter">' . $count . '</span>', the Screen base / id changes dynamically thereby rendering the $screen passed to add_meta_box ineffective.

#2 @gorgebilly
4 months ago

The issue you're encountering seems to be related to the dynamic title you're using for the menu pages in your WordPress plugin. When you use a dynamic title for add_menu_page, it causes problems with add_meta_box on sub-pages. To fix this issue, you need to ensure that the menu titles remain consistent across different pages.

Here's how you can fix it:

class MyPlugin {
    private $cap = 'activate_plugins';
    private $count;

    private function __construct() {
        $this->count = $this->get_count();
    }

    static function get_instance() {
        static $instance = null;
        if ( is_null( $instance ) ) {
            $instance = new self();
            $instance->init();
        }
        return $instance;
    }

    function init() {
        add_action( 'admin_menu', array( $this, 'menu' ) );
        add_action( 'current_screen', array( $this, 'handle_screen' ) );
    }

    function menu() {
        add_filter( 'sanitize_title', array( $this, 'screen_obj_fix' ), 9, 3 );

        // Dynamic Menu Title
        $top_menu_title = empty( $this->count ) ? 'My Plugin' : 'My Plugin <span class="menu-counter">' . $this->count . '</span>';

        add_menu_page(
            'My Plugin', // page_title
            $top_menu_title, // menu_title
            $this->cap, // capability
            'myplugintop', // menu_slug
            array( $this, 'top_page' ), // function
            'dashicons-admin-generic', // icon_url
            6 // position
        );

        // Dynamic Sub-Menu Title
        $sub_menu_title = empty( $this->count ) ? 'Sub-Page' : 'Sub-Page <span class="menu-counter">' . $this->count . '</span>';

        add_submenu_page(
            'myplugintop', // parent_slug
            'My Plugin Page', // page_title
            $sub_menu_title, // menu_title
            $this->cap, // capability
            'mypluginsub', // menu_slug
            array( $this, 'sub_page' ) // function
        );

        remove_filter( 'sanitize_title', array( $this, 'screen_obj_fix' ), 9 );
    }

    function top_page() {
        // Your top page content
    }

    function sub_page() {
        // Your sub page content
    }

    function handle_screen( $screen ) {
        if ( preg_match( '/myplugin/', $screen->id ) ) {
            $this->add_action_meta_boxes();
            $this->do_action_meta_boxes();
            $this->myplugin_enqueue_js_dependencies();
        }
    }

    function myplugin_enqueue_js_dependencies() {
        wp_enqueue_script( 'jquery' );
        wp_enqueue_script( 'common' );
        wp_enqueue_script( 'wp-lists' );
        wp_enqueue_script( 'postbox' );
    }

    function get_count() {
        // Return a random integer count
        return random_int( 0, 10 );
    }

    function screen_obj_fix( $title, $raw_title, $context ) {
        // Remove any HTML tags from the title
        return strip_tags( $title );
    }

    function do_action_meta_boxes() {
        do_action( 'add_meta_boxes', 'toplevel_page_myplugin', '' );
    }

    function add_action_meta_boxes() {
        add_action( 'add_meta_boxes', array( $this, 'myplugin_add_metaboxes' ) );
    }

    function myplugin_add_metaboxes() {
        // Add meta boxes to top-level page and sub-page
        add_meta_box( 'myplugin_test_box', 'Test Meta Box', array( $this, 'meta_box_logs_test' ), 'toplevel_page_myplugintop', 'main', 'high' );
        add_meta_box( 'myplugin_test_box', 'Test Meta Box', array( $this, 'meta_box_logs_test' ), 'my-plugin_page_mypluginsub', 'main', 'high' );
    }

    function meta_box_logs_test() {
        // Content of your meta box
    }
}

function myplugin() {
    return MyPlugin::get_instance();
}

myplugin();

In this fix, I've made sure that the menu titles remain consistent across different pages by using the same count for both top-level and sub-level pages. Additionally, I've simplified the screen_obj_fix function to strip HTML tags from the title. This should resolve the issue you're facing with add_meta_box.

Note: See TracTickets for help on using tickets.