<?php
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
class Rusnetim_Shortcodes {
private static $maps_count = 0;
private static $marker_count = 0;
private static $current_map_index = 0;
public function __construct() {
add_shortcode( 'rusnetim_map', [ $this, 'render_map' ] );
add_shortcode( 'rusnetim_marker', [ $this, 'render_marker' ] );
}
public function render_map( $atts, $content = '' ) {
$current_map_index = self::$maps_count;
self::set_current_map_index( $current_map_index );
$defaults = Rusnetim_Options::get_all();
$atts = shortcode_atts(
[
'center' => $defaults['center_map_option'],
'zoom' => $defaults['zoom_map_option'],
'type' => 'map',
'height' => $defaults['height_map_option'],
'controls' => $defaults['controls_map_option'],
'scrollzoom' => ( $defaults['wheelzoom_map_option'] === 'on' ) ? '1' : '0',
'mobiledrag' => ( $defaults['mobiledrag_map_option'] === 'on' ) ? '1' : '0',
'container' => '',
'cluster' => ( $defaults['cluster_map_option'] === 'on' ) ? '1' : '0',
'clustergrid' => $defaults['cluster_grid_option'],
'clustercolor' => $defaults['cluster_color_option'],
'markers' => '',
'category' => '',
'marker_id' => '',
'infowidth' => $defaults['info_panel_width'],
'infoside' => $defaults['info_panel_side'],
'titleposition' => $defaults['info_panel_title_position'],
'title_tag' => $defaults['info_title_tag'],
'title_font_size' => $defaults['info_title_font_size'],
'title_color' => $defaults['info_title_color'],
'infobgcolor' => $defaults['info_panel_bg_color'],
],
$atts
);
// ADDED FILTER: allow filtering of shortcode attributes
$atts = apply_filters( 'rusnetim_shortcode_atts', $atts, $content );
if ( $atts['height'] === '' ) {
$atts['height'] = $defaults['height_map_option'];
}
if ( ! preg_match( '/^\d+(\.\d+)?(rem|em|px|vh)$/', $atts['height'] ) ) {
$atts['height'] = '22rem';
}
$category_filter = '';
$markers_data = [];
if ( ! empty( $atts['marker_id'] ) && is_numeric( $atts['marker_id'] ) ) {
$single_id = intval( $atts['marker_id'] );
$marker_data = $this->prepare_marker_data_from_post( $single_id );
if ( $marker_data ) {
$markers_data[] = $marker_data;
}
} else {
if ( ! empty( $atts['markers'] ) && 'all' === $atts['markers'] ) {
$args = [
'post_type' => 'rusnetim_marker',
'posts_per_page' => -1,
'post_status' => 'publish',
'orderby' => 'title',
'order' => 'ASC',
'meta_query' => [
'relation' => 'OR',
[
'key' => '_marker_visible',
'compare' => 'NOT EXISTS',
],
[
'key' => '_marker_visible',
'value' => '0',
'compare' => '!=',
],
],
];
$db_markers = get_posts( $args );
foreach ( $db_markers as $marker ) {
$marker_data = $this->prepare_marker_data_from_post( $marker->ID );
if ( $marker_data ) {
$markers_data[] = $marker_data;
}
}
} elseif ( ! empty( $atts['category'] ) ) {
$args = [
'post_type' => 'rusnetim_marker',
'posts_per_page' => -1,
'post_status' => 'publish',
'tax_query' => [
[
'taxonomy' => 'rusnetim_category',
'field' => 'slug',
'terms' => explode( ',', $atts['category'] ),
],
],
'meta_query' => [
'relation' => 'OR',
[
'key' => '_marker_visible',
'compare' => 'NOT EXISTS',
],
[
'key' => '_marker_visible',
'value' => '0',
'compare' => '!=',
],
],
];
$db_markers = get_posts( $args );
foreach ( $db_markers as $marker ) {
$marker_data = $this->prepare_marker_data_from_post( $marker->ID );
if ( $marker_data ) {
$markers_data[] = $marker_data;
}
}
}
if ( ! empty( $content ) ) {
$pattern = get_shortcode_regex( [ 'rusnetim_marker' ] );
if ( preg_match_all( '/' . $pattern . '/s', $content, $matches ) ) {
foreach ( $matches[0] as $shortcode_text ) {
$raw_atts = shortcode_parse_atts( $shortcode_text );
if ( isset( $raw_atts[0] ) ) unset( $raw_atts[0] );
$marker_data = $this->prepare_marker_data_from_shortcode_atts( $raw_atts );
if ( $marker_data ) {
$markers_data[] = $marker_data;
}
}
}
}
}
if ( $atts['center'] === $defaults['center_map_option'] && ! empty( $markers_data ) ) {
$first_marker = reset( $markers_data );
if ( isset( $first_marker['coord'] ) && ! empty( $first_marker['coord'] ) ) {
$atts['center'] = $first_marker['coord'];
}
}
$safe_center = Rusnetim_Options::sanitize_coords( $atts['center'] );
$safe_zoom = Rusnetim_Options::sanitize_zoom( $atts['zoom'] );
$safe_type = Rusnetim_Options::sanitize_map_type( $atts['type'] );
$controls_array = Rusnetim_Options::sanitize_controls( $atts['controls'] );
$infoside = in_array( $atts['infoside'], [ 'right', 'left', 'modal' ] ) ? $atts['infoside'] : 'right';
$infowidth = $atts['infowidth'];
if ( ! preg_match( '/^\d+(\.\d+)?(px|%|em|rem|vw|vh)$/', $infowidth ) ) {
$infowidth = '300px';
}
$atts['container'] = trim( $atts['container'] );
if ( '' !== $atts['container'] ) {
$mapcontainer = preg_replace( '/[^a-zA-Z0-9_\-]/', '', $atts['container'] );
if ( preg_match( '/^[0-9]/', $mapcontainer ) ) {
$mapcontainer = 'rusnetim-map-' . $mapcontainer;
}
} else {
$mapcontainer = 'rusnetim-map' . $current_map_index;
}
$info_panel_id = 'rusnetim-info-' . $current_map_index;
$map_data = [
'map_index' => $current_map_index,
'container' => $mapcontainer,
'info_panel_id' => $info_panel_id,
'center' => array_map( 'floatval', explode( ',', $safe_center ) ),
'zoom' => (int) $safe_zoom,
'type' => $safe_type,
'controls' => $controls_array,
'scrollzoom' => $atts['scrollzoom'],
'mobiledrag' => $atts['mobiledrag'],
'cluster' => $atts['cluster'],
'clustergrid' => (int) $atts['clustergrid'],
'clustercolor' => $atts['clustercolor'],
'titleposition' => $atts['titleposition'],
'title_tag' => $atts['title_tag'],
'title_font_size' => $atts['title_font_size'],
'title_color' => $atts['title_color'],
'cluster_list_title' => __( 'Organizations in this building', 'rusnet-interactive-map' ),
'unnamed' => __( 'Unnamed', 'rusnet-interactive-map' ),
'markers' => $markers_data,
'infoside' => $infoside,
'infowidth' => $infowidth,
];
$api_key = $defaults['apikey_map_option'];
$locale = Rusnetim_Options::sanitize_locale( get_locale() );
wp_enqueue_script( 'yandex-maps-api', Rusnetim_API::get_api_url( $locale, $api_key ), [], null, true );
if ( ! wp_script_is( 'rusnetim-front', 'registered' ) ) {
wp_register_script(
'rusnetim-front',
RUSNETIM_PLUGIN_URL . 'assets/js/frontend/map-init.js',
[],
RUSNETIM_VERSION,
true
);
}
wp_localize_script( 'rusnetim-front', 'rusnetim_map_' . $current_map_index, $map_data );
wp_enqueue_script( 'rusnetim-front' );
$show_filter = empty( $atts['marker_id'] ) && empty( $atts['category'] );
$categories = array();
if ( $show_filter ) {
$categories = $this->extract_categories_from_markers( $markers_data );
$show_filter = count( $categories ) > 1;
}
if ( $show_filter ) {
$cat_filter_css = '
.rusnetim-cat-btn[data-map="' . $current_map_index . '"] {
display: inline-block;
cursor: pointer;
' . ( ! empty( $defaults['cat_filter_button_font_size'] ) ? 'font-size: ' . esc_attr( $defaults['cat_filter_button_font_size'] ) . ';' : '' ) . '
' . ( ! empty( $defaults['cat_filter_button_color'] ) ? 'color: ' . esc_attr( $defaults['cat_filter_button_color'] ) . ';' : '' ) . '
' . ( ! empty( $defaults['cat_filter_button_bg_color'] ) ? 'background-color: ' . esc_attr( $defaults['cat_filter_button_bg_color'] ) . ';' : '' ) . '
' . ( ! empty( $defaults['cat_filter_button_border'] ) ? 'border: ' . esc_attr( $defaults['cat_filter_button_border'] ) . ';' : 'border: none;' ) . '
' . ( ! empty( $defaults['cat_filter_button_box_shadow'] ) ? 'box-shadow: ' . esc_attr( $defaults['cat_filter_button_box_shadow'] ) . ';' : '' ) . '
' . ( ! empty( $defaults['cat_filter_button_padding'] ) ? 'padding: ' . esc_attr( $defaults['cat_filter_button_padding'] ) . ';' : 'padding: 6px 12px;' ) . '
' . ( ! empty( $defaults['cat_filter_button_border_radius'] ) ? 'border-radius: ' . esc_attr( $defaults['cat_filter_button_border_radius'] ) . ';' : 'border-radius: 4px;' ) . '
}
.rusnetim-cat-btn.active[data-map="' . $current_map_index . '"] {
' . ( ! empty( $defaults['cat_filter_button_active_color'] ) ? 'color: ' . esc_attr( $defaults['cat_filter_button_active_color'] ) . ';' : '' ) . '
' . ( ! empty( $defaults['cat_filter_button_active_bg_color'] ) ? 'background-color: ' . esc_attr( $defaults['cat_filter_button_active_bg_color'] ) . ';' : '' ) . '
' . ( ! empty( $defaults['cat_filter_button_active_border'] ) ? 'border: ' . esc_attr( $defaults['cat_filter_button_active_border'] ) . ';' : '' ) . '
}';
wp_register_style( 'rusnetim-cat-filter', false );
wp_enqueue_style( 'rusnetim-cat-filter' );
wp_add_inline_style( 'rusnetim-cat-filter', $cat_filter_css );
$tag = esc_html( $defaults['cat_filter_button_tag'] );
$container_justify = esc_attr( $defaults['cat_filter_container_justify'] );
$category_filter = '<div class="rusnetim-category-filter" style="display: flex; flex-wrap: wrap; gap: 8px; margin-bottom: 10px; justify-content: ' . $container_justify . ';">';
$category_filter .= '<' . $tag . ' class="rusnetim-cat-btn active" data-cat="all" data-map="' . $current_map_index . '">' . esc_html__( 'All', 'rusnet-interactive-map' ) . '</' . $tag . '>';
foreach ( $categories as $slug => $name ) {
$category_filter .= '<' . $tag . ' class="rusnetim-cat-btn" data-cat="' . esc_attr( $slug ) . '" data-map="' . $current_map_index . '">' . esc_html( $name ) . '</' . $tag . '>';
}
$category_filter .= '</div>';
}
$authorLinkTitle = __( 'Interactive Map plugin by ANO "Rusnet"', 'rusnet-interactive-map' );
if ( isset( $defaults['authorlink_map_option'] ) && 'on' !== $defaults['authorlink_map_option'] ) {
$authorlink = '<div style="position: relative; height: 0; margin-bottom: 0rem !important; margin-top:0 !important; overflow: visible; width: 100%; text-align: center; top: -32px;">'
. '<a href="https://rusnet.su" title="' . esc_attr( $authorLinkTitle ) . '" target="_blank" style="display: inline-block; -webkit-box-align: center; padding: 3.5px 5px; text-decoration: none !important; border-bottom: 0; border-radius: 3px; background-color: #fff; cursor: pointer; white-space: nowrap; box-shadow: 0 1px 2px 1px rgba(0,0,0,.15),0 2px 5px -3px rgba(0,0,0,.15);">'
. '<img src="' . RUSNETIM_PLUGIN_URL . 'assets/js/img/placeholder.svg" alt="" style="width: 17px; height: 17px; margin: 0; display: block;" /></a></div>';
} else {
$authorlink = '';
}
$output = '';
if ( ! empty( $category_filter ) ) {
$output .= $category_filter;
}
$info_panel_html = apply_filters( 'rusnetim_info_panel_html', '', $mapcontainer, $info_panel_id, $infoside, $infowidth, $atts );
if ( '' === $atts['container'] ) {
$output .= '<div id="' . esc_attr( $mapcontainer ) . '" style="position: relative; height: ' . esc_attr( $atts['height'] ) . '; margin-bottom: 0 !important;">';
if ( ! empty( $info_panel_html ) ) {
$output .= $info_panel_html;
} else {
$panel_style = 'display:none; position:absolute; top:0; ' . $infoside . ':0; width:' . esc_attr( $infowidth ) . '; height:100%; background:' . esc_attr( $atts['infobgcolor'] ) . '; border-' . ( $infoside === 'right' ? 'left' : 'right' ) . ':1px solid #ccc; box-shadow:' . ( $infoside === 'right' ? '-2px 0 5px rgba(0,0,0,0.1)' : '2px 0 5px rgba(0,0,0,0.1)' ) . '; z-index:1000; overflow-y:auto; padding:15px; box-sizing:border-box;';
$output .= '<div id="' . esc_attr( $info_panel_id ) . '" class="rusnetim-info-panel" style="' . $panel_style . '"></div>';
}
$output .= '</div>' . $authorlink;
} else {
$output .= $authorlink;
$inline_js = '
document.addEventListener("DOMContentLoaded", function() {
var container = document.getElementById("' . esc_js( $mapcontainer ) . '");
if ( container && ! document.getElementById("' . esc_js( $info_panel_id ) . '") ) {
var panel = document.createElement("div");
panel.id = "' . esc_js( $info_panel_id ) . '";
panel.className = "rusnetim-info-panel";
panel.style.display = "none";
panel.style.position = "absolute";
panel.style.top = "0";
panel.style.' . $infoside . ' = "0";
panel.style.width = "' . esc_attr( $infowidth ) . '";
panel.style.height = "100%";
panel.style.background = "' . esc_attr( $atts['infobgcolor'] ) . '";
panel.style.border' . ( $infoside === 'right' ? 'Left' : 'Right' ) . ' = "1px solid #ccc";
panel.style.boxShadow = "' . ( $infoside === 'right' ? '-2px 0 5px rgba(0,0,0,0.1)' : '2px 0 5px rgba(0,0,0,0.1)' ) . '";
panel.style.overflowY = "auto";
panel.style.padding = "15px";
panel.style.zIndex = "1000";
panel.style.boxSizing = "border-box";
container.style.position = "relative";
container.appendChild(panel);
}
});';
wp_add_inline_script( 'rusnetim-front', $inline_js );
}
self::$maps_count++;
return $output;
}
public function render_marker( $atts ) {
return '';
}
/**
* Получить все мета-поля маркера из базы по ID (с проверкой статуса publish и видимости)
*/
private function get_marker_post_data( $post_id ) {
$post = get_post( $post_id );
if ( ! $post || $post->post_type !== 'rusnetim_marker' || $post->post_status !== 'publish' ) {
return null;
}
$visible = get_post_meta( $post_id, '_marker_visible', true );
if ( '0' === $visible ) {
return null;
}
$coord = get_post_meta( $post_id, '_marker_coord', true );
$name = $post->post_title;
$icon = get_post_meta( $post_id, '_marker_icon', true );
$color = get_post_meta( $post_id, '_marker_color', true );
$url = get_post_meta( $post_id, '_marker_url', true );
$image = get_post_meta( $post_id, '_marker_image', true );
$description = $post->post_content;
$icon_width = get_post_meta( $post_id, '_marker_icon_width', true );
$icon_height = get_post_meta( $post_id, '_marker_icon_height', true );
$custom_icon = get_post_meta( $post_id, '_marker_custom_icon', true );
if ( ! empty( $custom_icon ) ) {
$icon = $custom_icon;
}
$gallery_ids = get_post_meta( $post_id, '_marker_gallery', true );
$gallery_urls = array();
if ( ! empty( $gallery_ids ) && is_array( $gallery_ids ) ) {
foreach ( $gallery_ids as $id ) {
$url_img = wp_get_attachment_image_url( $id, 'full' );
if ( $url_img ) {
$gallery_urls[] = $url_img;
}
}
}
$terms = wp_get_post_terms( $post_id, 'rusnetim_category', [ 'fields' => 'slugs' ] );
$category = ! empty( $terms ) ? $terms[0] : '';
return [
'coord' => $coord,
'name' => $name,
'icon' => $icon,
'color' => $color,
'url' => $url,
'category' => $category,
'image' => $image,
'description' => $description,
'icon_width' => $icon_width,
'icon_height' => $icon_height,
'gallery' => $gallery_urls,
];
}
private function prepare_marker_data_from_shortcode_atts( $raw_atts ) {
if ( ! empty( $raw_atts['id'] ) && is_numeric( $raw_atts['id'] ) ) {
$post_id = intval( $raw_atts['id'] );
return $this->prepare_marker_data_from_post( $post_id );
}
$default_atts = [
'id' => 0,
'coord' => '',
'name' => '',
'color' => '',
'url' => '',
'icon' => '',
'category' => '',
'image' => '',
'description' => '',
'icon_width' => '',
'icon_height' => '',
'gallery' => '',
];
$all_atts = shortcode_atts( $default_atts, $raw_atts );
if ( empty( $all_atts['coord'] ) ) {
$defaults = Rusnetim_Options::get_all();
$all_atts['coord'] = $defaults['center_map_option'];
}
if ( empty( $all_atts['coord'] ) ) {
return null;
}
if ( ! empty( $all_atts['gallery'] ) && is_string( $all_atts['gallery'] ) ) {
$decoded_json = base64_decode( $all_atts['gallery'] );
if ( $decoded_json !== false ) {
$decoded = json_decode( $decoded_json, true );
$all_atts['gallery'] = is_array( $decoded ) ? $decoded : [];
} else {
$all_atts['gallery'] = [];
}
} elseif ( ! is_array( $all_atts['gallery'] ) ) {
$all_atts['gallery'] = [];
}
return $this->normalize_marker_data( $all_atts );
}
private function prepare_marker_data_from_post( $post_id ) {
$post_data = $this->get_marker_post_data( $post_id );
if ( ! $post_data ) {
return null;
}
if ( empty( $post_data['coord'] ) ) {
$defaults = Rusnetim_Options::get_all();
$post_data['coord'] = $defaults['center_map_option'];
}
return $this->normalize_marker_data( $post_data );
}
private function normalize_marker_data( $atts ) {
$defaults = Rusnetim_Options::get_all();
if ( ! empty( $atts['category'] ) ) {
$term = get_term_by( 'slug', $atts['category'], 'rusnetim_category' );
if ( $term && ! is_wp_error( $term ) ) {
if ( empty( $atts['icon'] ) ) {
$cat_icon = get_term_meta( $term->term_id, 'category_icon', true );
if ( ! empty( $cat_icon ) ) $atts['icon'] = $cat_icon;
}
if ( empty( $atts['color'] ) ) {
$cat_color = get_term_meta( $term->term_id, 'category_color', true );
if ( ! empty( $cat_color ) ) $atts['color'] = $cat_color;
}
if ( empty( $atts['icon_width'] ) ) {
$cat_width = get_term_meta( $term->term_id, 'category_icon_width', true );
if ( ! empty( $cat_width ) ) $atts['icon_width'] = $cat_width;
}
if ( empty( $atts['icon_height'] ) ) {
$cat_height = get_term_meta( $term->term_id, 'category_icon_height', true );
if ( ! empty( $cat_height ) ) $atts['icon_height'] = $cat_height;
}
}
}
if ( empty( $atts['icon'] ) ) {
if ( ! empty( $defaults['custom_icon_option'] ) ) {
$atts['icon'] = $defaults['custom_icon_option'];
if ( empty( $atts['icon_width'] ) ) $atts['icon_width'] = $defaults['custom_icon_width_option'] ?? '';
if ( empty( $atts['icon_height'] ) ) $atts['icon_height'] = $defaults['custom_icon_height_option'] ?? '';
} else {
$atts['icon'] = $defaults['type_icon_option'];
}
}
if ( empty( $atts['color'] ) ) {
$atts['color'] = $defaults['color_icon_option'];
}
$icon = $atts['icon'];
$icon_url = '';
$icon_preset = '';
$icon_size = [];
if ( strpos( $icon, 'myset:' ) === 0 ) {
$slug = substr( $icon, 6 );
$my_iconset = $defaults['my_iconset'];
if ( isset( $my_iconset[ $slug ] ) ) {
$data = $my_iconset[ $slug ];
$icon_url = $data['url'];
if ( empty( $atts['icon_width'] ) && ! empty( $data['width'] ) ) {
$atts['icon_width'] = $data['width'];
}
if ( empty( $atts['icon_height'] ) && ! empty( $data['height'] ) ) {
$atts['icon_height'] = $data['height'];
}
} else {
$icon_preset = $defaults['type_icon_option'];
}
} elseif ( is_numeric( $icon ) ) {
$image_url = wp_get_attachment_image_url( intval( $icon ), 'full' );
if ( $image_url ) {
$icon_url = $image_url;
$img_data = wp_get_attachment_image_src( intval( $icon ), 'full' );
if ( $img_data ) {
if ( empty( $atts['icon_width'] ) ) $atts['icon_width'] = $img_data[1];
if ( empty( $atts['icon_height'] ) ) $atts['icon_height'] = $img_data[2];
}
} else {
$icon_preset = $defaults['type_icon_option'];
}
} elseif ( filter_var( $icon, FILTER_VALIDATE_URL ) ) {
$icon_url = $icon;
} else {
$icon_preset = $icon;
}
if ( $icon_url && $atts['icon_width'] && $atts['icon_height'] ) {
$icon_size = [ (int) $atts['icon_width'], (int) $atts['icon_height'] ];
}
$hint = '';
$content = '';
if ( strpos( $icon_preset, 'Stretchy' ) !== false ) {
$content = $atts['name'];
} else {
if ( in_array( $icon_preset, [ 'islands#blueIcon', 'islands#blueCircleIcon' ], true ) ) {
$hint = $atts['name'];
$content = $atts['name'] ? mb_substr( $atts['name'], 0, 1 ) : '';
} else {
$hint = $atts['name'];
}
}
$link = $atts['url'];
if ( is_numeric( $link ) ) {
$link = get_permalink( intval( $link ) );
}
return [
'coord' => Rusnetim_Options::sanitize_coords( $atts['coord'] ),
'name' => sanitize_text_field( $atts['name'] ),
'hint' => $hint,
'content' => $content,
'iconPreset' => $icon_preset,
'iconUrl' => esc_url_raw( $icon_url ),
'iconColor' => sanitize_hex_color( $atts['color'] ) ?: '#1e98ff',
'iconSize' => $icon_size,
'url' => esc_url_raw( $link ),
'category' => sanitize_text_field( $atts['category'] ),
'image' => esc_url_raw( $atts['image'] ),
'description' => wp_kses_post( $atts['description'] ),
'gallery' => array_map( 'esc_url_raw', $atts['gallery'] ),
];
}
private function extract_categories_from_markers( $markers ) {
$slugs = [];
foreach ( $markers as $m ) {
if ( ! empty( $m['category'] ) ) {
$slugs[] = $m['category'];
}
}
$slugs = array_unique( $slugs );
$result = [];
foreach ( $slugs as $slug ) {
$term = get_term_by( 'slug', $slug, 'rusnetim_category' );
if ( $term && ! is_wp_error( $term ) ) {
$result[ $slug ] = $term->name;
} else {
$result[ $slug ] = $slug;
}
}
return $result;
}
public static function set_current_map_index( $index ) {
self::$current_map_index = $index;
}
}