| @@ -1,11 +1,13 @@ | ||
| 1 | 1 | <?php |
| 2 | 2 | |
| 3 | 3 | namespace Code_Snippets\Admin\Menus; |
| 4 | 4 | |
| 5 | +use Code_Snippets\Admin\Contextual_Help; | |
| 5 | 6 | use Code_Snippets\Settings\Settings_Fields; |
| 6 | 7 | use function Code_Snippets\code_snippets; |
| 7 | 8 | use function Code_Snippets\Settings\are_settings_unified; |
| 9 | +use function Code_Snippets\Settings\do_settings_fields_with_headings; | |
| 8 | 10 | use function Code_Snippets\Utils\enqueue_code_editor; |
| 9 | 11 | use function Code_Snippets\Utils\get_editor_themes; |
| 10 | 12 | use const Code_Snippets\PLUGIN_FILE; |
| 11 | 13 | use const Code_Snippets\PLUGIN_VERSION; |
| @@ -36,8 +38,37 @@ | ||
| 36 | 38 | ); |
| 37 | 39 | } |
| 38 | 40 | |
| 39 | 41 | /** |
| 42 | + * Register the settings menu. | |
| 43 | + * | |
| 44 | + * Deliberately bypasses the snippet view-permission gate applied by the | |
| 45 | + * parent. The settings page is already restricted to the core capability | |
| 46 | + * (`manage_options` via `get_cap()`), so it stays admin-only regardless — | |
| 47 | + * but it must always remain reachable so an administrator who restricts | |
| 48 | + * their own role's snippet permissions can never lock themselves out of the | |
| 49 | + * Permissions tab needed to undo it. | |
| 50 | + */ | |
| 51 | + public function register() { | |
| 52 | + $this->add_menu( $this->slug, $this->label, $this->title ); | |
| 53 | + } | |
| 54 | + | |
| 55 | + /** | |
| 56 | + * The settings page is configuration territory (it includes the Permissions | |
| 57 | + * tab itself), so it must remain restricted to full administrators — never | |
| 58 | + * the broader snippet access capability that can be granted to other roles. | |
| 59 | + * This is also what keeps an administrator from ever losing access to the | |
| 60 | + * Permissions tab needed to undo a misconfiguration. | |
| 61 | + * | |
| 62 | + * @return string | |
| 63 | + */ | |
| 64 | + protected function menu_cap(): string { | |
| 65 | + return is_multisite() && ( is_network_admin() || ! code_snippets()->is_subsite_menu_enabled() ) | |
| 66 | + ? 'manage_network_options' | |
| 67 | + : 'manage_options'; | |
| 68 | + } | |
| 69 | + | |
| 70 | + /** | |
| 40 | 71 | * Executed when the admin page is loaded |
| 41 | 72 | */ |
| 42 | 73 | public function load() { |
| 43 | 74 | parent::load(); |
| @@ -49,8 +80,11 @@ | ||
| 49 | 80 | wp_safe_redirect( code_snippets()->get_menu_url( 'settings', 'admin' ) ); |
| 50 | 81 | exit; |
| 51 | 82 | } |
| 52 | 83 | } |
| 84 | + | |
| 85 | + $contextual_help = new Contextual_Help( 'settings' ); | |
| 86 | + $contextual_help->load(); | |
| 53 | 87 | } |
| 54 | 88 | |
| 55 | 89 | /** |
| 56 | 90 | * Enqueue the stylesheet for the settings menu |
| @@ -174,9 +208,9 @@ | ||
| 174 | 208 | * @param string $default_section Name of the default tab displayed. |
| 175 | 209 | * |
| 176 | 210 | * @return string |
| 177 | 211 | */ |
| 178 | - public function get_current_section( string $default_section = 'general' ): string { | |
| 212 | + public function get_current_section( string $default_section = '' ): string { | |
| 179 | 213 | $sections = $this->get_sections(); |
| 180 | 214 | |
| 181 | 215 | if ( ! $sections ) { |
| 182 | 216 | return $default_section; |
| @@ -181,11 +215,17 @@ | ||
| 181 | 215 | if ( ! $sections ) { |
| 182 | 216 | return $default_section; |
| 183 | 217 | } |
| 184 | 218 | |
| 219 | + // Fall back to the first registered tab. Naming a specific section here | |
| 220 | + // meant the page rendered empty whenever that section stopped existing. | |
| 221 | + $fallback = $default_section && isset( $sections[ $default_section ] ) | |
| 222 | + ? $default_section | |
| 223 | + : (string) array_key_first( $sections ); | |
| 224 | + | |
| 185 | 225 | // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Value is matched to registered sections. |
| 186 | - $active_tab = isset( $_REQUEST['section'] ) ? sanitize_text_field( wp_unslash( $_REQUEST['section'] ) ) : $default_section; | |
| 187 | - return isset( $sections[ $active_tab ] ) ? $active_tab : $default_section; | |
| 226 | + $active_tab = isset( $_REQUEST['section'] ) ? sanitize_text_field( wp_unslash( $_REQUEST['section'] ) ) : $fallback; | |
| 227 | + return isset( $sections[ $active_tab ] ) ? $active_tab : $fallback; | |
| 188 | 228 | } |
| 189 | 229 | |
| 190 | 230 | /** |
| 191 | 231 | * Render the admin screen |
| @@ -303,9 +343,9 @@ | ||
| 303 | 343 | } |
| 304 | 344 | |
| 305 | 345 | printf( '<div class="settings-section %s-settings"><table class="form-table">', esc_attr( $section['id'] ) ); |
| 306 | 346 | |
| 307 | - do_settings_fields( self::SETTINGS_PAGE, $section['id'] ); | |
| 347 | + do_settings_fields_with_headings( self::SETTINGS_PAGE, $section['id'] ); | |
| 308 | 348 | echo '</table></div>'; |
| 309 | 349 | } |
| 310 | 350 | } |
| 311 | 351 | |