PluginProbe
Code Snippets / 4.0.0-beta.2
Code Snippets v4.0.0-beta.2
4.0.0-beta.2 3.10.2 3.10.1 3.10.0 3.10.0-beta.2 3.10.0-beta.1 4.0.0-beta.1 3.9.6 trunk 2.10.0 2.10.1 2.12.0 2.12.1 2.13.0 2.13.1 2.13.2 2.13.3 2.14.0 2.14.1 2.14.2 2.14.3 2.14.4 2.14.5 2.14.6 3.0.0 All 65 releases
← All changes | php/Admin/Menus/Edit_Menu.php +96 -26 3.10.1 → 4.0.0-beta.2 View file →
@@ -3,9 +3,8 @@
3 3 namespace Code_Snippets\Admin\Menus;
4 4
5 5 use Code_Snippets\Admin\Contextual_Help;
6 6 use Code_Snippets\Model\Snippet;
7 -use WP_Screen;
8 7 use function Code_Snippets\code_snippets;
9 8 use function Code_Snippets\get_all_snippet_tags;
10 9 use function Code_Snippets\get_snippet;
11 10 use function Code_Snippets\Settings\get_setting;
@@ -48,10 +47,8 @@
48 47 _x( 'Edit Snippet', 'menu label', 'code-snippets' ),
49 48 __( 'Edit Snippet', 'code-snippets' )
50 49 );
51 50
52 - add_action( 'current_screen', array( $this, 'maybe_hide_menu_item' ) );
53 -
54 51 $this->remove_debug_bar_codemirror();
55 52 }
56 53
57 54 /**
@@ -59,10 +56,21 @@
59 56 *
60 57 * @return void
61 58 */
62 59 public function register() {
63 - parent::register();
60 + // The page itself is always registered outside the menu, so nothing that
61 + // reads the menu during `admin_menu` — menu editors and role managers
62 + // among them — ever sees it. Registering it in the menu and removing it
63 + // afterward is why a page that never renders in the menu could still end
64 + // up in someone's saved menu.
65 + $this->register_without_menu_item();
64 66
67 + $snippet_id = $this->get_requested_snippet_id();
68 +
69 + if ( $snippet_id ) {
70 + $this->add_current_snippet_menu_item( $snippet_id );
71 + }
72 +
65 73 // Create New Snippet menu.
66 74 $this->add_menu(
67 75 code_snippets()->get_menu_slug( 'add' ),
68 76 _x( 'Add New', 'menu label', 'code-snippets' ),
@@ -70,38 +78,101 @@
70 78 );
71 79 }
72 80
73 81 /**
74 - * Retrieve every hookname registered by this menu, including the separate
75 - * "Add New" page, so screen-based checks recognise both editor views.
82 + * The snippet this request is editing, if any.
76 83 *
77 - * @return string[]
84 + * Read from the request rather than the current screen, because this runs
85 + * during `admin_menu`, before the screen is known.
86 + *
87 + * @return int Snippet ID, or 0 when not editing a specific snippet.
78 88 */
79 - public function get_hooknames(): array {
80 - return [
81 - $this->get_hookname(),
82 - get_plugin_page_hookname( code_snippets()->get_menu_slug( 'add' ), $this->base_slug ),
83 - ];
89 + private function get_requested_snippet_id(): int {
90 + // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Read-only routing parameter.
91 + $page = isset( $_GET['page'] ) ? sanitize_key( wp_unslash( $_GET['page'] ) ) : '';
92 +
93 + if ( $page !== $this->slug ) {
94 + return 0;
95 + }
96 +
97 + // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Read-only routing parameter.
98 + return isset( $_GET['id'] ) ? absint( $_GET['id'] ) : 0;
84 99 }
85 100
86 101 /**
87 - * Hide the static Edit Snippet menu item unless a specific snippet is being edited.
102 + * Register the edit page without placing it in the admin menu.
88 103 *
89 - * @param WP_Screen $screen Current admin screen.
104 + * @return void
105 + */
106 + private function register_without_menu_item(): void {
107 + $hook = add_submenu_page(
108 + '',
109 + $this->title,
110 + $this->label,
111 + code_snippets()->get_cap(),
112 + $this->slug,
113 + array( $this, 'render' )
114 + );
115 +
116 + if ( $hook ) {
117 + add_action( 'load-' . $hook, array( $this, 'load' ) );
118 + }
119 + }
120 +
121 + /**
122 + * Mark the snippet being edited in the menu.
90 123 *
124 + * Added as a plain link rather than a second registration of the page: a
125 + * submenu slug doubles as the identifier WordPress checks permissions
126 + * against, so carrying the ID in the slug would make `page=edit-snippet`
127 + * match nothing and the screen would refuse to load.
128 + *
129 + * @param int $snippet_id Snippet being edited.
130 + *
91 131 * @return void
92 132 */
93 - public function maybe_hide_menu_item( WP_Screen $screen ) {
94 - // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Read-only admin menu context.
95 - $current_id = isset( $_GET['id'] ) ? absint( $_GET['id'] ) : 0;
96 - $edit_hook = get_plugin_page_hookname( $this->slug, $this->base_slug );
97 - $edit_hook .= $screen->in_admin( 'network' ) ? '-network' : '';
133 + private function add_current_snippet_menu_item( int $snippet_id ): void {
134 + add_submenu_page(
135 + $this->base_slug,
136 + $this->title,
137 + $this->label,
138 + code_snippets()->get_cap(),
139 + add_query_arg(
140 + [
141 + 'page' => $this->slug,
142 + 'id' => $snippet_id,
143 + ],
144 + 'admin.php'
145 + ),
146 + '',
147 + 1
148 + );
149 + }
98 150
99 - if ( ( $screen->id === $edit_hook || $screen->base === $edit_hook ) && 0 < $current_id ) {
100 - return;
101 - }
151 + /**
152 + * Retrieve the hookname of the edit page.
153 + *
154 + * The page is registered without a parent, so WordPress files it under
155 + * "admin_page_" rather than under the Snippets menu; deriving it from the
156 + * menu slug would name a screen that does not exist.
157 + *
158 + * @return string
159 + */
160 + public function get_hookname(): string {
161 + return get_plugin_page_hookname( $this->slug, '' );
162 + }
102 163
103 - remove_submenu_page( $this->base_slug, $this->slug );
164 + /**
165 + * Retrieve every hookname registered by this menu, including the separate
166 + * "Add New" page, so screen-based checks recognize both editor views.
167 + *
168 + * @return string[]
169 + */
170 + public function get_hooknames(): array {
171 + return [
172 + $this->get_hookname(),
173 + get_plugin_page_hookname( code_snippets()->get_menu_slug( 'add' ), $this->base_slug ),
174 + ];
104 175 }
105 176
106 177 /**
107 178 * Executed when the menu is loaded.
@@ -123,11 +194,10 @@
123 194 *
124 195 * @return void
125 196 */
126 197 protected function ensure_correct_page() {
127 - $screen = get_current_screen();
128 - $edit_hook = get_plugin_page_hookname( $this->slug, $this->base_slug );
129 - $edit_hook .= $screen->in_admin( 'network' ) ? '-network' : '';
198 + $screen = get_current_screen();
199 + $edit_hook = $this->get_hookname() . ( $screen->in_admin( 'network' ) ? '-network' : '' );
130 200
131 201 // Disallow visiting the edit snippet page without a valid ID.
132 202 if (
133 203 $screen->base === $edit_hook