$config ) { $types[] = CPT::post_type_slug( $slug ); } return $types; } // ── REST/meta registration ────────────────────────────────────────── public function register_meta(): void { foreach ( $this->managed_post_types() as $post_type ) { register_post_meta( $post_type, '_mapfield_spots_lat', array( 'type' => 'number', 'single' => true, 'show_in_rest' => true, 'sanitize_callback' => function ( $v ) { return is_numeric( $v ) ? (float) $v : ''; }, 'auth_callback' => function () { return current_user_can( 'edit_posts' ); }, ) ); register_post_meta( $post_type, '_mapfield_spots_lng', array( 'type' => 'number', 'single' => true, 'show_in_rest' => true, 'sanitize_callback' => function ( $v ) { return is_numeric( $v ) ? (float) $v : ''; }, 'auth_callback' => function () { return current_user_can( 'edit_posts' ); }, ) ); register_post_meta( $post_type, '_mapfield_spots_address', array( 'type' => 'string', 'single' => true, 'show_in_rest' => true, 'sanitize_callback' => 'sanitize_text_field', 'auth_callback' => function () { return current_user_can( 'edit_posts' ); }, ) ); register_post_meta( $post_type, '_mapfield_spots_placement_mode', array( 'type' => 'string', 'single' => true, 'show_in_rest' => true, 'sanitize_callback' => function ( $v ) { return 'click' === $v ? 'click' : 'manual'; }, 'auth_callback' => function () { return current_user_can( 'edit_posts' ); }, ) ); } } // ── Meta box ───────────────────────────────────────────────────────── public function add_meta_box(): void { foreach ( $this->managed_post_types() as $post_type ) { add_meta_box( 'mfs-location', __( 'Location', 'mapfield-spots' ), array( $this, 'render' ), $post_type, 'normal', 'high' ); } } public function render( \WP_Post $post ): void { wp_nonce_field( self::NONCE_ACTION, self::NONCE_NAME ); $lat = get_post_meta( $post->ID, '_mapfield_spots_lat', true ); $lng = get_post_meta( $post->ID, '_mapfield_spots_lng', true ); $address = get_post_meta( $post->ID, '_mapfield_spots_address', true ); $mode = get_post_meta( $post->ID, '_mapfield_spots_placement_mode', true ) ?: 'manual'; ?>

managed_post_types(), true ) ) { return; } if ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE ) { return; } if ( ! current_user_can( 'edit_post', $post_id ) ) { return; } // Blank stays '' rather than (float) '' === 0.0 — used to silently // save an untouched Spot as a real 0,0 (Atlantic), indistinguishable // from an intentional value. render_map() already skips falsy lat/lng. $lat_raw = isset( $_POST['mapfield_spots_lat'] ) ? sanitize_text_field( wp_unslash( $_POST['mapfield_spots_lat'] ) ) : ''; $lng_raw = isset( $_POST['mapfield_spots_lng'] ) ? sanitize_text_field( wp_unslash( $_POST['mapfield_spots_lng'] ) ) : ''; $lat = '' !== $lat_raw ? (float) str_replace( ',', '.', $lat_raw ) : ''; $lng = '' !== $lng_raw ? (float) str_replace( ',', '.', $lng_raw ) : ''; update_post_meta( $post_id, '_mapfield_spots_lat', $lat ); update_post_meta( $post_id, '_mapfield_spots_lng', $lng ); update_post_meta( $post_id, '_mapfield_spots_address', sanitize_text_field( wp_unslash( $_POST['mapfield_spots_address'] ?? '' ) ) ); $placement_mode = isset( $_POST['mapfield_spots-placement-mode'] ) ? sanitize_text_field( wp_unslash( $_POST['mapfield_spots-placement-mode'] ) ) : ''; update_post_meta( $post_id, '_mapfield_spots_placement_mode', 'click' === $placement_mode ? 'click' : 'manual' ); // save_post fires before the redirect, too early to print a notice — // flag it via a query arg instead, same mechanism core uses itself. if ( ! $lat || ! $lng ) { add_filter( 'redirect_post_location', array( $this, 'flag_no_location_in_redirect' ) ); } } public function flag_no_location_in_redirect( string $location ): string { return add_query_arg( 'mapfield_spots_no_location', '1', $location ); } // Post-save counterpart to render()'s inline warning — repeats it as a // dismissible notice right after Publish/Update, easy to miss otherwise. public function render_no_location_notice(): void { $screen = get_current_screen(); if ( ! $screen || ! in_array( $screen->post_type, $this->managed_post_types(), true ) ) { return; } if ( '1' !== sanitize_text_field( wp_unslash( $_GET['mapfield_spots_no_location'] ?? '' ) ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- read-only display flag off core's own redirect query arg, nothing acted on. return; } ?>

post_type, $this->managed_post_types(), true ) ) { return; } // MapLibre GL JS, bundled locally — no CDN. Address search/reverse-geocoding // call Mapbox Search Box/Geocoding directly via fetch() in location-picker.js. 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_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-mapbox-style-loader', MAPFIELD_SPOTS_URL . 'assets/mapbox-style-loader.js', array(), MAPFIELD_SPOTS_VERSION, true ); wp_enqueue_script( 'mapfield-spots-location', MAPFIELD_SPOTS_URL . 'assets/location-picker.js', array( 'maplibre-gl', 'mapfield-spots-mapbox-style-loader' ), 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 ); $map_type = Map_Types::get_by_post_type( $screen->post_type ) ?? Map_Types::default_config(); $settings = Settings::get_mapbox_settings(); wp_localize_script( 'mapfield-spots-location', 'MapfieldSpots', array( 'token' => $settings['token'], 'style' => $map_type['map']['style_url'] ?: $settings['style_url'], 'center' => array( $map_type['map']['center'][1], $map_type['map']['center'][0] ), // [lng, lat] 'proximity' => array( $map_type['map']['center'][1], $map_type['map']['center'][0] ), 'zoom' => $map_type['map']['zoom'], 'markerSvg' => Marker_Library::resolve( $map_type['map'] )['markup'], 'i18n' => array( 'noToken' => __( 'No Mapbox token configured. Set one under Mapfield Spots → Settings.', 'mapfield-spots' ), 'searchPlaceholder' => __( 'Search for an address…', 'mapfield-spots' ), 'mapError' => __( 'Could not display the map:', 'mapfield-spots' ), 'unsavedWarning' => __( 'This location has an unsaved position change.', 'mapfield-spots' ), ), ) ); } }