id, 'mapfield-spots' ) !== false; $is_own_cpt = $screen->post_type && strpos( $screen->post_type, CPT::PREFIX ) === 0; if ( ! $is_own_page && ! $is_own_cpt ) { return; } // Skip on the Settings page itself — render_page() already highlights the field there. if ( 'mapfield-spots-settings' === sanitize_key( $_GET['page'] ?? '' ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- read-only view routing, not a state-changing action. return; } ?>

' . esc_html__( 'add yours in Settings', 'mapfield-spots' ) . '' ); ?>

id, 'mapfield-spots-settings' ) === false ) { return; } global $wp_settings_errors; get_settings_errors(); // merges the transient into $wp_settings_errors and deletes it. $wp_settings_errors = array(); } /** * Styled toast (.mapfield_spots-toast, admin.css) replacing WP's own * "Settings saved." banner — this page renders its own template, so * nothing shows a confirmation without this. Covers both the settings * save and the "Restore default settings" redirect. */ public function maybe_render_saved_toast(): void { $screen = get_current_screen(); if ( ! $screen || strpos( $screen->id, 'mapfield-spots-settings' ) === false ) { return; } // Read-only UI flags from an already-nonce-verified redirect, not form data. // Checked independently (not elseif) so both can show at once. // phpcs:ignore WordPress.Security.NonceVerification.Recommended if ( isset( $_GET['mapfield_spots_reset'] ) ) { ?>

get_option( self::OPTION_TOKEN, '' ), // streets-v9 and other classic styles are retired; v12 is the current equivalent. 'style_url' => get_option( self::OPTION_STYLE, 'mapbox://styles/mapbox/streets-v12' ), ); } /** Mapbox's core styles, all same pricing. Shared by Settings and each Map Type's picker. */ public static function style_presets(): array { return array( 'mapbox://styles/mapbox/streets-v12' => __( 'Streets', 'mapfield-spots' ), 'mapbox://styles/mapbox/outdoors-v12' => __( 'Outdoors', 'mapfield-spots' ), 'mapbox://styles/mapbox/light-v11' => __( 'Light', 'mapfield-spots' ), 'mapbox://styles/mapbox/dark-v11' => __( 'Dark', 'mapfield-spots' ), 'mapbox://styles/mapbox/satellite-streets-v12' => __( 'Satellite Streets', 'mapfield-spots' ), 'mapbox://styles/mapbox/satellite-v9' => __( 'Satellite', 'mapfield-spots' ), 'mapbox://styles/mapbox/navigation-day-v1' => __( 'Navigation Day', 'mapfield-spots' ), 'mapbox://styles/mapbox/navigation-night-v1' => __( 'Navigation Night', 'mapfield-spots' ), ); } /** * Grid of clickable style cards (live MapLibre previews, style-picker.js) * bound to a text input. Only used for a Map Type's own style field now — * a former site-wide default picker was removed as confusing (two * overlapping controls). * * @param string $placeholder In practice never shown; every Map Type resolves to a real style URL. */ public static function render_style_picker( string $input_id, string $current_value, string $placeholder = '' ): void { $presets = self::style_presets(); $is_custom = '' === $current_value || ! isset( $presets[ $current_value ] ); ?>
$label ) : ?>
/>
true, 'id' => true, 'fill' => true, 'stroke' => true, 'stroke-width' => true, 'stroke-linecap' => true, 'stroke-linejoin' => true, ); return array( 'svg' => $common + array( 'xmlns' => true, 'viewbox' => true, 'width' => true, 'height' => true, 'aria-hidden' => true, 'focusable' => true ), 'path' => $common + array( 'd' => true, 'fill-rule' => true, 'clip-rule' => true ), 'g' => $common, 'circle' => $common + array( 'cx' => true, 'cy' => true, 'r' => true ), 'rect' => $common + array( 'x' => true, 'y' => true, 'width' => true, 'height' => true, 'rx' => true, 'ry' => true ), 'line' => $common + array( 'x1' => true, 'y1' => true, 'x2' => true, 'y2' => true ), 'polygon' => $common + array( 'points' => true ), 'polyline' => $common + array( 'points' => true ), 'defs' => $common, 'clippath' => $common, 'use' => $common + array( 'href' => true, 'xlink:href' => true ), ); } public function register_menu(): void { add_options_page( // Plain "Settings" would be meaningless among other plugins' entries here. __( 'Mapfield Spots', 'mapfield-spots' ), __( 'Mapfield Spots', 'mapfield-spots' ), 'manage_options', 'mapfield-spots-settings', array( $this, 'render_page' ) ); } public function register_settings(): void { register_setting( 'mapfield_spots_settings_group', self::OPTION_TOKEN, array( 'type' => 'string', 'sanitize_callback' => 'sanitize_text_field', 'default' => '', ) ); register_setting( 'mapfield_spots_settings_group', self::OPTION_STYLE, array( 'type' => 'string', 'sanitize_callback' => array( self::class, 'sanitize_style_url' ), 'default' => 'mapbox://styles/mapbox/streets-v12', ) ); // PRO registers its own options on the same group/admin_init hook // (Mapfield_Spots_Pro\Dark_Mode, \Backup_Scheduler) — one form, one save. } public function enqueue_assets( string $hook ): void { if ( strpos( $hook, self::MENU_SLUG ) === false ) { return; } wp_enqueue_style( 'mapfield-spots-admin', MAPFIELD_SPOTS_URL . 'assets/admin.css', array( 'mapfield-spots-palette' ), MAPFIELD_SPOTS_VERSION ); wp_enqueue_script( 'mapfield-spots-admin', MAPFIELD_SPOTS_URL . 'assets/admin.js', array(), MAPFIELD_SPOTS_VERSION, true ); wp_enqueue_script( 'mapfield-spots-tabs', MAPFIELD_SPOTS_URL . 'assets/tabs.js', array(), MAPFIELD_SPOTS_VERSION, true ); wp_enqueue_style( 'pimp-scrollbar', MAPFIELD_SPOTS_URL . 'assets/pimp-scrollbar.css', array(), MAPFIELD_SPOTS_VERSION ); wp_enqueue_script( 'pimp-scrollbar', MAPFIELD_SPOTS_URL . 'assets/pimp-scrollbar.js', array(), MAPFIELD_SPOTS_VERSION, true ); self::enqueue_style_picker(); } /** MapLibre GL JS + style picker assets, shared by Settings and Map_Types_Admin. */ public static function enqueue_style_picker(): void { wp_enqueue_style( 'maplibre-gl', MAPFIELD_SPOTS_URL . 'assets/vendor/maplibre-gl/maplibre-gl.css', array(), '4.7.1' ); wp_enqueue_script( 'maplibre-gl', MAPFIELD_SPOTS_URL . 'assets/vendor/maplibre-gl/maplibre-gl.js', array(), '4.7.1', true ); wp_enqueue_script( 'mapfield-spots-mapbox-style-loader', MAPFIELD_SPOTS_URL . 'assets/mapbox-style-loader.js', array(), MAPFIELD_SPOTS_VERSION, true ); wp_enqueue_script( 'mapfield-spots-style-picker', MAPFIELD_SPOTS_URL . 'assets/style-picker.js', array( 'maplibre-gl', 'mapfield-spots-mapbox-style-loader' ), MAPFIELD_SPOTS_VERSION, true ); wp_localize_script( 'mapfield-spots-style-picker', 'MapfieldSpotsStylePicker', array( 'token' => self::get_mapbox_settings()['token'], ) ); } private function render_header( string $page_title ): void { ?>

render_header( __( 'General settings', 'mapfield-spots' ) ); ?>
" on switch, so a save reopens the right tab. Don't add // a second one — it would just shadow this one. settings_fields( 'mapfield_spots_settings_group' ); ?> __( 'Mapbox account', 'mapfield-spots' ), ) ); ?>
$tab_label ) : ?>

account.mapbox.com' ); ?> ' . esc_html__( 'Terms of Service', 'mapfield-spots' ) . '' ); ?>


/

wrapper — see the tabs array above, // no upsell fallback needed since the tab button only exists when PRO added it. do_action( 'mapfield_spots_settings_tab_sections' ); ?>