1 | <?php |
---|
2 | /** |
---|
3 | * PoC Bug: add_meta_box breaks if add_menu_page has dynamic title |
---|
4 | * |
---|
5 | * @package Dynamic add_menu_page Title |
---|
6 | * |
---|
7 | * @wordpress-plugin |
---|
8 | * Plugin Name: Dynamic add_menu_page Title |
---|
9 | * Description: PoC Bug: add_meta_box breaks if add_menu_page has dynamic title |
---|
10 | * Version: 0.1 |
---|
11 | * Author: Malcure |
---|
12 | * License: MIT |
---|
13 | */ |
---|
14 | if ( ! defined( 'ABSPATH' ) ) { |
---|
15 | exit; |
---|
16 | } |
---|
17 | |
---|
18 | |
---|
19 | class MyPlugin { |
---|
20 | private $cap = 'activate_plugins'; |
---|
21 | |
---|
22 | private function __construct() { |
---|
23 | } |
---|
24 | |
---|
25 | static function get_instance() { |
---|
26 | static $instance = null; |
---|
27 | if ( is_null( $instance ) ) { |
---|
28 | $instance = new self(); |
---|
29 | $instance->init(); |
---|
30 | } |
---|
31 | return $instance; |
---|
32 | } |
---|
33 | |
---|
34 | function init() { |
---|
35 | add_action( 'admin_menu', array( $this, 'menu' ) ); |
---|
36 | add_action( 'current_screen', array( $this, 'handle_screen' ) ); |
---|
37 | } |
---|
38 | |
---|
39 | function menu() { |
---|
40 | add_filter( 'sanitize_title', array( $this, 'screen_obj_fix' ), 9, 3 ); |
---|
41 | |
---|
42 | // Dynamic Menu Title |
---|
43 | $count = $this->count(); |
---|
44 | $top_menu_title = empty( $count ) ? 'My Plugin' : 'My Plugin <span class="menu-counter">' . $count . '</span>'; |
---|
45 | |
---|
46 | add_menu_page( |
---|
47 | 'My Plugin', // page_title |
---|
48 | $top_menu_title, // menu_title |
---|
49 | $this->cap, // capability |
---|
50 | 'myplugintop', // menu_slug |
---|
51 | array( $this, 'top_page' ), // function |
---|
52 | 'dashicons-admin-generic', // icon_url |
---|
53 | 6 // position |
---|
54 | ); |
---|
55 | $count = $this->count(); |
---|
56 | // Dynamic Sub-Menu Title |
---|
57 | $sub_menu_title = empty( $count ) ? 'Sub-Page' : 'Sub-Page <span class="menu-counter">' . $count . '</span>'; |
---|
58 | // add_submenu_page( string $parent_slug, string $page_title, string $menu_title, string $capability, string $menu_slug, callable $callback = ”, int|float $position = null ): string|false |
---|
59 | add_submenu_page( |
---|
60 | 'myplugintop', // parent_slug |
---|
61 | 'My Plugin Page', // page_title |
---|
62 | $top_menu_title, // menu_title |
---|
63 | $this->cap, // capability |
---|
64 | 'myplugintop', // menu_slug |
---|
65 | array( $this, 'top_page' )// function |
---|
66 | ); |
---|
67 | add_submenu_page( 'myplugintop', 'My Plugin Sub-Page', $sub_menu_title, $this->cap, 'mypluginsub', array( $this, 'sub_page' ) ); |
---|
68 | |
---|
69 | remove_filter( 'sanitize_title', array( $this, 'screen_obj_fix' ), 9 ); |
---|
70 | } |
---|
71 | |
---|
72 | function top_page() { |
---|
73 | $screen = get_current_screen(); |
---|
74 | ?> |
---|
75 | <div class="wrap"> |
---|
76 | <h1 id="page_title">Top Page Heading</h1> |
---|
77 | <div id="poststuff"> |
---|
78 | <div class="metabox-holder columns-2" id="post-body"> |
---|
79 | <div class="postbox-container" id="post-body-content"> |
---|
80 | <?php |
---|
81 | do_meta_boxes( $screen->id, 'main', null ); |
---|
82 | // This function is used to output meta boxes in a particular screen and context (like 'normal', 'side', 'advanced') in the WordPress admin. It's what actually renders the meta boxes on the post edit screen or custom screens. When you call this function, it looks for all the meta boxes that have been added for a specific screen/context and displays them. |
---|
83 | ?> |
---|
84 | </div> |
---|
85 | <!-- #postbox-container --> |
---|
86 | <div id="postbox-container-1" class="postbox-container"> |
---|
87 | <?php |
---|
88 | do_meta_boxes( $screen->id, 'side', null ); |
---|
89 | // This function is used to output meta boxes in a particular screen and context (like 'normal', 'side', 'advanced') in the WordPress admin. It's what actually renders the meta boxes on the post edit screen or custom screens. When you call this function, it looks for all the meta boxes that have been added for a specific screen/context and displays them. |
---|
90 | ?> |
---|
91 | </div> |
---|
92 | </div> |
---|
93 | </div> |
---|
94 | </div> |
---|
95 | <script type="text/javascript"> |
---|
96 | jQuery(document).ready(function($) { //wrapper |
---|
97 | // close postboxes that should be closed |
---|
98 | $('.if-js-closed').removeClass('if-js-closed').addClass('closed'); |
---|
99 | // postboxes setup |
---|
100 | postboxes.add_postbox_toggles('<?php echo $screen->id; ?>'); |
---|
101 | $('#main-sortables .postbox').not(':first').addClass('closed'); |
---|
102 | }); |
---|
103 | </script> |
---|
104 | <?php |
---|
105 | } |
---|
106 | |
---|
107 | function sub_page() { |
---|
108 | $screen = get_current_screen(); |
---|
109 | ?> |
---|
110 | <div class="wrap myplugin-test"> |
---|
111 | <h1>Sub-Page Heading</h1> |
---|
112 | <div id="poststuff"> |
---|
113 | <div class="metabox-holder columns-2" id="post-body"> |
---|
114 | <div class="postbox-container" id="post-body-content"> |
---|
115 | <?php |
---|
116 | do_meta_boxes( $screen->id, 'main', null ); |
---|
117 | // This function is used to output meta boxes in a particular screen and context (like 'normal', 'side', 'advanced') in the WordPress admin. It's what actually renders the meta boxes on the post edit screen or custom screens. When you call this function, it looks for all the meta boxes that have been added for a specific screen/context and displays them. |
---|
118 | ?> |
---|
119 | </div> |
---|
120 | <!-- #postbox-container --> |
---|
121 | <div id="postbox-container-1" class="postbox-container"> |
---|
122 | <?php |
---|
123 | do_meta_boxes( $screen->id, 'side', null ); |
---|
124 | // This function is used to output meta boxes in a particular screen and context (like 'normal', 'side', 'advanced') in the WordPress admin. It's what actually renders the meta boxes on the post edit screen or custom screens. When you call this function, it looks for all the meta boxes that have been added for a specific screen/context and displays them. |
---|
125 | ?> |
---|
126 | </div> |
---|
127 | </div> |
---|
128 | </div> |
---|
129 | </div> |
---|
130 | <script type="text/javascript"> |
---|
131 | jQuery(document).ready(function($) { //wrapper |
---|
132 | // close postboxes that should be closed |
---|
133 | $('.if-js-closed').removeClass('if-js-closed').addClass('closed'); |
---|
134 | // postboxes setup |
---|
135 | postboxes.add_postbox_toggles('<?php echo $screen->id; ?>'); |
---|
136 | $('#main-sortables .postbox').not(':first').addClass('closed'); |
---|
137 | }); |
---|
138 | </script> |
---|
139 | <?php |
---|
140 | } |
---|
141 | |
---|
142 | function handle_screen( $screen ) { |
---|
143 | flog( $screen->id ); |
---|
144 | if ( preg_match( '/myplugin/', $screen->id ) ) { |
---|
145 | $this->add_action_meta_boxes(); |
---|
146 | $this->do_action_meta_boxes(); |
---|
147 | $this->myplugin_enqueue_js_dependencies(); |
---|
148 | } |
---|
149 | } |
---|
150 | |
---|
151 | function myplugin_enqueue_js_dependencies() { |
---|
152 | wp_enqueue_script( 'jquery' ); |
---|
153 | wp_enqueue_script( 'common' ); |
---|
154 | wp_enqueue_script( 'wp-lists' ); |
---|
155 | wp_enqueue_script( 'postbox' ); |
---|
156 | } |
---|
157 | |
---|
158 | function count() { |
---|
159 | // return an random int |
---|
160 | return random_int( 0, 10 ); |
---|
161 | } |
---|
162 | |
---|
163 | function screen_obj_fix( $title, $raw_title, $context ) { |
---|
164 | // Comment Out The Next Line To Fix The Issus |
---|
165 | return $title; |
---|
166 | error_log( '$title' . $title ); |
---|
167 | $title = trim( preg_replace( '/<[^>]*?>.*?<\/[^>]*?>/si', '', $title ) ); |
---|
168 | error_log( '$title' . $title ); |
---|
169 | return $title; |
---|
170 | } |
---|
171 | |
---|
172 | function do_action_meta_boxes() { |
---|
173 | do_action( 'add_meta_boxes', 'toplevel_page_myplugin', '' ); // This is a way to trigger the 'add_meta_boxes' hook. WordPress core calls this action hook during the page load of admin screens that can have meta boxes. By using do_action(), you can manually trigger all functions attached to the 'add_meta_boxes' hook. This is useful for dynamically adding meta boxes based on certain conditions. However, it's more common to see WordPress itself triggering this action rather than being manually called in most themes and plugins. |
---|
174 | } |
---|
175 | |
---|
176 | function add_action_meta_boxes() { |
---|
177 | add_action( 'add_meta_boxes', array( $this, 'myplugin_add_metaboxes' ) ); // This function is used to register a callback function to the 'add_meta_boxes' hook. This is how you tell WordPress, "Hey, when you're at the point of adding meta boxes, also add this one that I'm specifying here." The callback function you register will typically use the add_meta_box() function to define the meta box (its ID, title, callback for rendering its content, the screen it should appear on, and its context). |
---|
178 | } |
---|
179 | |
---|
180 | function myplugin_add_metaboxes() { |
---|
181 | add_meta_box( 'myplugin_test_box', 'Test Meta Box', array( $this, 'meta_box_logs_test' ), 'toplevel_page_myplugintop', 'main', 'high' ); |
---|
182 | add_meta_box( 'myplugin_test_box', 'Test Meta Box', array( $this, 'meta_box_logs_test' ), 'my-plugin_page_mypluginsub', 'main', 'high' ); |
---|
183 | } |
---|
184 | |
---|
185 | function meta_box_logs_test() { |
---|
186 | ?> |
---|
187 | This is a test metabox. |
---|
188 | <?php |
---|
189 | } |
---|
190 | } |
---|
191 | |
---|
192 | function myplugin() { |
---|
193 | return MyPlugin::get_instance(); |
---|
194 | } |
---|
195 | |
---|
196 | myplugin(); |
---|