';
$html .= '
';
$html .= '
' . esc_html( $text ) . '
';
$html .= '
';
$html .= '
';
return $html;
}
}
if ( ! function_exists( 'airowp_render_map' ) ) {
/**
* Renders the Map block on the front end.
*
* @param array $attributes Block attributes.
* @param string $content Block content (unused).
* @param WP_Block $block Block instance.
*/
function airowp_render_map( $attributes, $content, $block ) {
$provider = isset( $attributes['dsgoProvider'] ) ? (string) $attributes['dsgoProvider'] : 'openstreetmap';
$latitude = isset( $attributes['dsgoLatitude'] ) ? (float) $attributes['dsgoLatitude'] : 40.7128;
$longitude = isset( $attributes['dsgoLongitude'] ) ? (float) $attributes['dsgoLongitude'] : -74.006;
$zoom = isset( $attributes['dsgoZoom'] ) ? (int) $attributes['dsgoZoom'] : 13;
$address = isset( $attributes['dsgoAddress'] ) ? (string) $attributes['dsgoAddress'] : '';
$marker_icon = isset( $attributes['dsgoMarkerIcon'] ) && '' !== $attributes['dsgoMarkerIcon'] ? (string) $attributes['dsgoMarkerIcon'] : '📍';
$height = isset( $attributes['dsgoHeight'] ) ? (string) $attributes['dsgoHeight'] : '400px';
$aspect_ratio = isset( $attributes['dsgoAspectRatio'] ) ? (string) $attributes['dsgoAspectRatio'] : 'custom';
$privacy_mode = ! empty( $attributes['dsgoPrivacyMode'] );
$privacy_note = isset( $attributes['dsgoPrivacyNotice'] ) ? (string) $attributes['dsgoPrivacyNotice'] : '';
$map_style = isset( $attributes['dsgoMapStyle'] ) ? (string) $attributes['dsgoMapStyle'] : 'standard';
// Marker colour: explicit attribute → kit theme.json custom → default, then filter.
$marker_color = '';
if ( isset( $attributes['dsgoMarkerColor'] ) && '' !== $attributes['dsgoMarkerColor'] ) {
$marker_color = (string) $attributes['dsgoMarkerColor'];
} elseif ( function_exists( 'wp_get_global_settings' ) ) {
$kit_color = wp_get_global_settings( array( 'custom', 'airo-wp', 'map', 'markerColor' ) );
if ( is_string( $kit_color ) && '' !== $kit_color ) {
$marker_color = $kit_color;
}
}
if ( '' === $marker_color ) {
$marker_color = '#e74c3c';
}
/**
* Filter the resolved map marker colour before it is written to the wrapper.
*
* @param string $marker_color Resolved colour (CSS colour string).
* @param array $attributes Block attributes.
* @param WP_Block $block Block instance.
*/
$marker_color = (string) apply_filters( 'airowp_map_marker_color', $marker_color, $attributes, $block );
// The color control stores theme palette selections as the preset shorthand
// "var:preset|color|{slug}" so the block tracks theme changes. Resolve it to a
// concrete value here: the marker is drawn by view.js, which cannot inherit the
// page's CSS custom properties. Fall back to the block default when the preset
// slug is missing from the active palette.
$marker_color = airowp_resolve_preset_color( $marker_color, '#e74c3c' );
// No location yet (a freshly inserted map starts at 0,0 with no
// address, see index.js): render nothing rather than a map of the
// ocean off West Africa. Mirrors hasNoLocation() in MapPlaceholder.js.
if ( 0.0 === $latitude && 0.0 === $longitude && '' === trim( $address ) ) {
return;
}
// Clamp coordinates / zoom to valid ranges (mirrors save.js).
$safe_lat = max( -90, min( 90, $latitude ) );
$safe_lng = max( -180, min( 180, $longitude ) );
$safe_zoom = max( 1, min( 20, $zoom ) );
// Wrapper classes — must mirror edit.js / the prior save().
$classes = 'airo-wp-map';
if ( $privacy_mode ) {
$classes .= ' airo-wp-map--privacy-mode';
}
if ( 'custom' !== $aspect_ratio ) {
$classes .= ' airo-wp-map--aspect-' . str_replace( ':', '-', $aspect_ratio );
}
// Height is only inline when the author picked a custom aspect ratio.
$wrapper_args = array( 'class' => $classes );
if ( 'custom' === $aspect_ratio ) {
$wrapper_args['style'] = 'height:' . $height;
}
$wrapper_attributes = get_block_wrapper_attributes( $wrapper_args );
// Config consumed by view.js (kept identical to the prior static output).
$data_attributes = array(
'data-airo-wp-provider' => $provider,
'data-airo-wp-lat' => (string) $safe_lat,
'data-airo-wp-lng' => (string) $safe_lng,
'data-airo-wp-zoom' => (string) $safe_zoom,
'data-airo-wp-address' => $address,
'data-airo-wp-marker-icon' => $marker_icon,
'data-airo-wp-marker-color' => $marker_color,
'data-airo-wp-privacy-mode' => $privacy_mode ? 'true' : 'false',
'data-airo-wp-map-style' => $map_style,
);
$aria_label = '' !== $address
/* translators: %s: The address being shown on the map */
? sprintf( __( 'Map showing %s', 'airo-wp' ), $address )
: __( 'Interactive map', 'airo-wp' );
// In privacy mode view.js builds the map container itself, after
// consent, so it needs the localized label handed over.
if ( $privacy_mode ) {
$data_attributes['data-airo-wp-map-label'] = $aria_label;
}
$data_attr_html = '';
foreach ( $data_attributes as $key => $value ) {
$data_attr_html .= ' ' . $key . '="' . esc_attr( $value ) . '"';
}
// The keyless Google provider is a plain iframe built server-side: no
// Maps JS API, no Leaflet, and no geocoding round-trip, so outside of
// privacy mode the block needs no JavaScript at all. The marker icon /
// colour and map style attributes have no effect here — Google owns the
// rendering — which is why the inspector hides those controls.
if ( 'googlemaps-embed' === $provider ) {
$embed_url = airowp_map_embed_url( $address, $safe_lat, $safe_lng, $safe_zoom );
// In privacy mode the URL is parked until the visitor asks for it,
// so no request reaches Google on page load.
$src_attribute = $privacy_mode
? 'data-airo-wp-src="' . esc_url( $embed_url ) . '"'
: 'src="' . esc_url( $embed_url ) . '"';
$inner = '' . $inner . '
'; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
return;
}
if ( $privacy_mode ) {
$inner = airowp_map_privacy_overlay( $privacy_note );
} else {
$inner = '' . $inner . '
'; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
}
}
airowp_render_map( $attributes, $content, $block );