Headline
CVE-2023-5467: class-gmw-single-location.php in geo-my-wp/tags/4.0.1/plugins/single-location/includes – WordPress Plugin Repository
The GEO my WordPress plugin for WordPress is vulnerable to Stored Cross-Site Scripting via shortcodes in versions up to, and including, 4.0 due to insufficient input sanitization and output escaping on user supplied attributes. This makes it possible for authenticated attackers with contributor-level and above permissions to inject arbitrary web scripts in pages that will execute whenever a user accesses an injected page.
1<?php2/**3 * GEO my WP Single Location class.4 *5 * @package geo-my-wp6 */78if ( ! defined( ‘ABSPATH’ ) ) {9 exit; // Exit if accessed directly.10}1112/**13 * GMW_Single_Location Class14 *15 * Core class for displaying location information of an object ( post, member, group… ).16 *17 * You can extend this class to be used with a custom objects.18 *19 * @author Eyal Fitoussi20 *21 * @since 2.6.122 */23class GMW_Single_Location {2425 /**26 * Add on being used.27 *28 * @var string29 */30 protected $addon = '’;3132 /**33 * Array of Incoming arguments34 *35 * @var array36 *37 * @since 2.6.138 */39 protected $defaults = array(40 ‘element_id’ => 0,41 ‘object’ => 'post’, // replaced item_type.42 ‘object_type’ => '’,43 ‘object_id’ => 0, // replaced item_id.44 ‘elements’ => 0,45 ‘element_width’ => '100%’,46 ‘address_fields’ => 'address’,47 ‘location_meta’ => '’,48 ‘units’ => 'imperial’,49 ‘directions_form_units’ => 'default’,50 ‘map_height’ => '300px’,51 ‘map_width’ => '100%’,52 ‘map_type’ => 'ROADMAP’,53 ‘zoom_level’ => 13,54 ‘scrollwheel_map_zoom’ => 1,55 ‘expand_map_on_load’ => 0,56 ‘map_icon_url’ => '’,57 ‘map_icon_size’ => '’,58 ‘info_window’ => 'title,address,distance’,59 ‘user_map_icon_url’ => '’,60 ‘user_map_icon_size’ => '’,61 ‘user_info_window’ => 'Your Location’,62 ‘no_location_message’ => '’,63 ‘disable_linked_address’ => 0,64 ‘css_class’ => '’,65 ‘css_id’ => '’,66 ‘use_generic_location’ => 0,67 /* ‘is_widget’ => 0,68 // ‘widget_title’ => 0, */69 );7071 /**72 * Array for child class to extends the main array above73 *74 * @since 2.6.1.75 *76 * Public $args77 *78 * @var array79 */80 protected $args = array();8182 /**83 * Object contains the item location information84 *85 * @var object86 *87 * @since 2.6.188 *89 * Public $location_data90 */91 public $location_data;9293 /**94 * Hold the object data ( post, user, group … ).95 *96 * @var object97 */98 public $object_data;99100 /**101 * Holds the location meta data.102 *103 * @var array104 */105 public $location_meta = false;106107 /**108 * Array contains the current user position if exists109 *110 * @var array111 *112 * @since 2.6.1.113 *114 * Public $user_position115 */116 public $user_position = array(117 ‘exists’ => false,118 ‘lat’ => false,119 ‘lng’ => false,120 ‘address’ => false,121 );122123 /**124 * Map locations holder.125 *126 * @var array127 */128 public $map_locations = array();129130 /**131 * Array contains the elements to be output132 *133 * @var array134 *135 * @since 2.6.1136 *137 * Public $this->elements138 */139 public $elements = array();140141 /**142 * Try to get object ID when missing.143 *144 * @return [type] [description]145 */146 public function get_object_id() {147 return 0;148 }149150 /**151 * Generate generic location when needed if object location was not found.152 *153 * Mainly to be used in the block editor.154 *155 * @since 4.0156 *157 * @return [type] [description]158 */159 public function get_generic_location() {160161 return (Object) array(162 ‘ID’ => 99999999,163 ‘object_type’ => 'post’,164 ‘object_id’ => 99999999,165 ‘blog_id’ => 1,166 ‘user_id’ => 1,167 ‘parent’ => 1,168 ‘status’ => 1,169 ‘featured’ => 0,170 ‘location_type’ => 0,171 ‘title’ => '’,172 ‘latitude’ => '40.77984321782061’,173 ‘longitude’ => '-73.96318071528319’,174 ‘street_number’ => '1000’,175 ‘street_name’ => 'Museum Mile’,176 ‘street’ => '1000 Museum Mile’,177 ‘premise’ => '’,178 ‘neighborhood’ => '’,179 ‘city’ => 'New York’,180 ‘county’ => 'New York County’,181 ‘region_name’ => 'New York’,182 ‘region_code’ => 'NY’,183 ‘postcode’ => '10028’,184 ‘country_name’ => 'United States’,185 ‘country_code’ => 'US’,186 ‘address’ => '1000 Museum Mile, New York, NY 10028, USA’,187 ‘formatted_address’ => '1000 Museum Mile, New York, NY 10028, USA’,188 ‘place_id’ => '0’,189 ‘map_icon’ => '_default.png’,190 ‘radius’ => '0.0’,191 ‘created’ => '2023-04-22 04:59:57’,192 ‘updated’ => '2023-04-22 04:59:57’,193 ‘lat’ => ‘40.77984321782061’,194 ‘lng’ => '-73.96318071528319’,195 ‘location_name’ => ‘Generic Location’,196 ‘featured_location’ => 0,197 );198 }199200 /**201 * Get location data.202 *203 * @return [type] [description]204 */205 public function location_data() {206207 // check if provided object ID.208 if ( empty( $this->args[‘object_type’] ) || empty( $this->args[‘object_id’] ) ) {209 return;210 }211212 return gmw_get_location_by_object( $this->args[‘object_type’], $this->args[‘object_id’] );213 }214215 /**216 * Get the object data ( post, member, user… ).217 *218 * @return [type] [description]219 */220 public function get_object_data() {221 return false;222 }223224 /**225 * Display the title of an item226 *227 * @since 2.6.1228 *229 * @param object $location object location.230 *231 * @access public232 */233 public function title( $location ) {}234235236 /**237 * Verify that the object exists before getting the location238 * Object might be deleted or in trash while location data still239 * exists in databased240 *241 * @return [type] [description]242 */243 public function object_exists() {244 return true;245 }246247 /**248 * [__construct description]249 *250 * @param array $atts [description].251 */252 public function __construct( $atts = array() ) {253254 // item_type replaced by object - remove in the future.255 if ( empty( $atts[‘object’] ) && ! empty( $atts[‘item_type’] ) ) {256257 $atts[‘object’] = $atts[‘item_type’];258259 gmw_trigger_error( '[gmw_single_location] attribute item_type is deprecated since version 3.0. Use “object” instead.’ );260261 unset( $atts[‘item_type’] );262 }263264 // item_id replaced by object_id - remove in the future.265 if ( empty( $atts[‘object_id’] ) && ! empty( $atts[‘item_id’] ) ) {266267 $atts[‘object_id’] = $atts[‘item_id’];268269 gmw_trigger_error( '[gmw_single_location] shortcode attribute item_id is deprecated since version 3.0. Use object_id instead.’ );270271 unset( $atts[‘item_id’] );272 }273274 if ( isset( $atts[‘item_map_icon’] ) ) {275276 $atts[‘map_icon_url’] = $atts[‘item_map_icon’];277278 unset( $atts[‘item_map_icon’] );279 }280281 if ( isset( $atts[‘object_map_icon’] ) ) {282283 $atts[‘map_icon_url’] = $atts[‘object_map_icon’];284285 unset( $atts[‘object_map_icon’] );286 }287288 if ( isset( $atts[‘item_info_window’] ) ) {289290 $atts[‘info_window’] = $atts[‘item_info_window’];291292 unset( $atts[‘item_info_window’] );293 }294295 if ( isset( $atts[‘object_info_window’] ) ) {296297 $atts[‘info_window’] = $atts[‘object_info_window’];298299 unset( $atts[‘object_info_window’] );300 }301302 if ( isset( $atts[‘user_map_icon’] ) ) {303304 $atts[‘user_map_icon_url’] = $atts[‘user_map_icon’];305306 unset( $atts[‘user_map_icon’] );307 }308309 // extend the default args.310 $this->args = array_merge( $this->defaults, $this->args );311312 // get the shortcode atts.313 $this->args = shortcode_atts( $this->args, $atts, ‘gmw_single_location’ );314315 // set random element id if not exists.316 $this->args[‘element_id’] = ! empty( $this->args[‘element_id’] ) ? $this->args[‘element_id’] : wp_rand( 100, 549 );317318 // extra protection from XSS attacks.319 foreach ( $this->args as $attr_key => $attr_value ) {320321 if ( ! empty( $attr_value ) ) {322323 if ( ! in_array( $attr_key, array( 'no_location_message’, ‘css_class’ ), true ) ) {324 $this->args[ $attr_key ] = str_replace( array( ' ', '"’, “’” ), '’, $attr_value );325 } else {326 $this->args[ $attr_key ] = str_replace( array( '"’, “’” ), '’, $attr_value );327 }328 }329 }330331 // in case object_type is missing.332 if ( empty( $this->args[‘object_type’] ) ) {333 $this->args[‘object_type’] = $this->args[‘object’];334 }335336 // If icon size provided, make it an array.337 if ( ! empty( $this->args[‘map_icon_size’] ) ) {338 $this->args[‘map_icon_size’] = explode( ',’, $this->args[‘map_icon_size’] );339 }340341 // Default icon URL and size.342 if ( ‘’ === $this->args[‘map_icon_url’] ) {343344 $this->args[‘map_icon_url’] = GMW()->default_icons[‘location_icon_url’];345346 // use default icon size if no size provided.347 if ( ‘’ === $this->args[‘map_icon_size’] ) {348 $this->args[‘map_icon_size’] = GMW()->default_icons[‘location_icon_size’];349 }350 }351352 // If icon size provided, make it an array.353 if ( ! empty( $this->args[‘user_map_icon_size’] ) ) {354 $this->args[‘user_map_icon_size’] = explode( ',’, $this->args[‘user_map_icon_size’] );355 }356357 // Default icon URL and size.358 if ( ‘’ === $this->args[‘user_map_icon_url’] ) {359360 $this->args[‘user_map_icon_url’] = GMW()->default_icons[‘user_location_icon_url’];361362 // use default icon size if no size provided.363 if ( ‘’ === $this->args[‘user_map_icon_size’] ) {364 $this->args[‘user_map_icon_size’] = GMW()->default_icons[‘user_location_icon_size’];365 }366 }367368 // get elements to display.369 $this->elements_value = explode( ',’, str_replace( ' ', '’, $this->args[‘elements’] ) );370371 $object_exists = $this->object_exists();372373 // check that object exists before anything else.374 if ( empty( $object_exists ) ) {375 return;376 }377378 // check that we have at least one element to display.379 if ( empty( $this->elements_value ) ) {380 return;381 }382383 if ( empty( $this->args[‘object_id’] ) ) {384 $this->args[‘object_id’] = $this->get_object_id();385 }386387 // get the location data.388 $this->location_data = $this->location_data();389390 if ( empty( $this->location_data ) && $this->args[‘use_generic_location’] ) {391 $this->location_data = $this->get_generic_location();392 }393394 // abort if no location found and no need to show message.395 if ( empty( $this->location_data ) && empty( $this->args[‘no_location_message’] ) ) {396 return;397 }398399 // get the object data.400 $this->object_data = $this->get_object_data();401402 if ( strpos( $this->args[‘elements’], ‘map’ ) !== false && ! empty( $this->args[‘map_width’] ) && ‘100%’ === $this->args[‘map_width’] ) {403 $display = 'style="display:block"’;404 } else {405 $display = 'style="display:inline-block"’;406 }407408 $display = '’;409 $css_class = 'gmw-element-wrapper gmw-single-location-wrapper gmw-sl-wrapper gmw-sl-single-' . $this->args[‘object’] . '-wrapper ' . $this->args[‘object’] . ' ' . $this->args[‘css_class’];410 $css_id = ! empty( $this->args[‘css_id’] ) ? $this->args[‘css_id’] : 'gmw-single-location-wrapper-' . $this->args[‘element_id’];411412 // generate the elements array.413 $this->elements[‘element_wrap_start’] = ‘<div id="’ . esc_attr( $css_id ) . ‘" class="’ . esc_attr( $css_class ) . ‘" object_type="’ . esc_attr( $this->args[‘object’] ) . ‘" object_id="’ . esc_attr( $this->args[‘object_id’] ) . ‘" ' . $display . ' style="width:’ . esc_attr( $this->args[‘element_width’] ) . '">’;414415 /** Check if this is widget and we use widget title */416 /** If ( $this->args[‘is_widget’] && ! empty( $this->args[‘widget_title’] ) ) {417 $this->elements[‘widget_title’] = true;418 } */419420 // if no location found.421 if ( empty( $this->location_data ) ) {422423 // generate element for the title ( if title exists in elements ).424 if ( in_array( 'title’, $this->elements_value, true ) ) {425 $this->elements[‘title’] = false;426 }427428 // generate element for the no location message.429 $this->elements[‘no_location_message’] = false;430431 // otherwise, generate additional data.432 } else {433434 // get labels.435 $this->labels = $this->labels();436 $ulc_prefix = gmw_get_ulc_prefix();437438 // check for last location in URL.439 if ( ! empty( $_GET[‘lat’] ) && ! empty( $_GET[‘lng’] ) ) { // WPCS: CSRF ok.440441 $this->user_position[‘exists’] = true;442 $this->user_position[‘lat’] = sanitize_text_field( wp_unslash( $_GET[‘lat’] ) ); // WPCS: CSRF ok.443 $this->user_position[‘lng’] = sanitize_text_field( wp_unslash( $_GET[‘lng’] ) ); // WPCS: CSRF ok.444445 $address = '’;446447 if ( ! empty( $_GET[‘address’] ) ) {448449 if ( is_array( $_GET[‘address’] ) ) {450451 $address = implode( ' ', $_GET[‘address’] ); // WPCS: XSS ok, sanitization ok, CSRF ok.452453 } else {454 $address = $_GET[‘address’]; // WPCS: XSS ok, sanitization ok, CSRF ok.455 }456 }457458 $this->user_position[‘address’] = sanitize_text_field( wp_unslash( $address ) );459460 // Otherwise check for user location in cookies.461 } elseif ( ! empty( $_COOKIE[ $ulc_prefix . ‘lat’ ] ) && ! empty( $_COOKIE[ $ulc_prefix . ‘lng’ ] ) ) {462463 $this->user_position[‘exists’] = true;464 $this->user_position[‘lat’] = urldecode( wp_unslash( $_COOKIE[ $ulc_prefix . ‘lat’ ] ) ); // WPCS: sanitization ok.465 $this->user_position[‘lng’] = urldecode( wp_unslash( $_COOKIE[ $ulc_prefix . ‘lng’ ] ) ); // WPCS: sanitization ok.466467 if ( ! empty( $_COOKIE[ $ulc_prefix . ‘address’ ] ) ) {468469 $this->user_position[‘address’] = urldecode( wp_unslash( $_COOKIE[ $ulc_prefix . ‘address’ ] ) ); // WPCS: sanitization ok.470471 } elseif ( ! empty( $_COOKIE[ $ulc_prefix . ‘formatted_address’ ] ) ) { // WPCS: sanitization ok, CSRF ok.472473 $this->user_position[‘address’] = urldecode( wp_unslash( $_COOKIE[ $ulc_prefix . ‘formatted_address’ ] ) ); // WPCS: sanitization ok.474 } else {475476 $this->user_position[‘address’] = '’;477 }478479 // $this->user_position[‘address’] = ! empty( $_COOKIE[ $ulc_prefix . ‘address’ ] ) ? urldecode( wp_unslash( $_COOKIE[ $ulc_prefix . ‘address’ ] ) ) : '’; // WPCS: sanitization ok.480 }481482 // generate elements.483 foreach ( $this->elements_value as $value ) {484 $this->elements[ $value ] = false;485 }486 }487488 $this->elements[‘element_wrap_end’] = '</div>’;489 }490491 /**492 * Create labels for the elements493 *494 * @since 2.6.1495 *496 * Public $labes497 */498 public function labels() {499500 return apply_filters(501 'gmw_sl_labels’,502 array(503 ‘distance’ => __( 'Distance: ', ‘geo-my-wp’ ),504 ‘directions’ => __( 'Get directions’, ‘geo-my-wp’ ),505 ‘from’ => __( 'From:’, ‘geo-my-wp’ ),506 ‘show_directions’ => __( 'Show directions’, ‘geo-my-wp’ ),507 ),508 $this->args,509 $this->location_data,510 $this511 );512 }513514 /**515 * Get the location’s name516 *517 * @since 3.4.1518 *519 * @param object $location locaiton obbject.520 *521 * @return [type] [description]522 */523 public function location_name( $location ) {524525 $name = esc_html( $location->title );526527 return apply_filters( ‘gmw_sl_location_name’, “<div class=’gmw-sl-title-wrapper’><h3 class=\"gmw-sl-title post-title gmw-sl-element\">{$name}</h3></div>", $location, $this->args, $this->user_position, $this );528 }529530 /**531 *532 * Get address533 *534 * @since 2.6.1535 *536 * @param object $location object location.537 *538 * @access public539 *540 * The address of the displayed item541 */542 public function address( $location ) {543544 // if item has no location, abort!545 if ( empty( $location ) ) {546 return ! empty( $this->args[‘no_location_message’] ) ? $this->no_location_message() : false;547 }548549 // get the full address.550 /*551 if ( empty( $this->args[‘address_fields’] ) || ‘address’ === $this->args[‘address_fields’] ) {552553 $address = ! empty( $this->location_data->formatted_address ) ? $this->location_data->formatted_address : $this->location_data->address;554555 // Otherwise, get specific address fields.556 } else {557558 $this->args[‘address_fields’] = ! is_array( $this->args[‘address_fields’] ) ? explode( ',’, $this->args[‘address_fields’] ) : $this->args[‘address_fields’];559560 $address_array = array();561562 foreach ( $this->args[‘address_fields’] as $field ) {563564 if ( empty( $this->location_data->$field ) ) {565 continue;566 }567568 $address_array[] = $this->location_data->$field;569 }570571 $address = implode( ' ', $address_array );572 }*/573574 $address = gmw_get_location_address( $location, $this->args[‘address_fields’], $this->args );575 $address = esc_attr( stripslashes( $address ) );576577 if ( ! empty( $this->args[‘disable_linked_address’] ) ) {578 $address_value = $address;579 } else {580 $address_value = ‘<a href="https://maps.google.com/?q=’ . $address . '” target="_blank">’ . $address . '</a>’;581 }582583 $output = ‘<div class="gmw-sl-address gmw-sl-element"><i class="gmw-location-icon gmw-icon-location"></i><span class="address">’ . $address_value . '</span></div>’;584585 return apply_filters( 'gmw_sl_address’, $output, $address, $this->args, $location, $this->user_position, $this );586 }587588 /**589 * Show Distance590 *591 * @since 2.6.1592 *593 * @param object $location object location.594 *595 * @access public596 *597 * Get the distance betwwen the user’s position to the item being displayed598 */599 public function distance( $location ) {600601 // if item has no location, abort!602 if ( empty( $location ) ) {603 return ! empty( $this->args[‘no_location_message’] ) ? $this->no_location_message() : false;604 }605606 // check for user position.607 if ( ! $this->user_position[‘exists’] ) {608 return;609 }610611 if ( ‘k’ === $this->args[‘units’] || ‘metric’ === $this->args[‘units’] ) {612 $units = 'km’;613 } else {614 $units = 'mi’;615 }616617 $distance = gmw_calculate_distance( $this->user_position[‘lat’], $this->user_position[‘lng’], $location->lat, $location->lng, $this->args[‘units’] );618619 $output = '<div class="gmw-sl-distance gmw-sl-element">’;620 $output .= '<i class="gmw-distance-icon gmw-icon-compass"></i>’;621 $output .= ‘<span class="label">’ . esc_attr( $this->labels[‘distance’] ) . '</span> ';622 $output .= ‘<span>’ . $distance . ' ' . $units . '</span></div>’;623624 return apply_filters( 'gmw_sl_distance’, $output, $distance, $units, $this->args, $location, $this->user_position, $this );625 }626627 /**628 * Map element629 *630 * @param object $location object location.631 *632 * @since 2.6.1633 *634 * @access public635 */636 public function map( $location ) {637638 // if item has no location, abort!639 if ( empty( $location ) ) {640 return ! empty( $this->args[‘no_location_message’] ) ? $this->no_location_message() : false;641 }642643 // map args.644 $map_args = array(645 ‘map_id’ => $this->args[‘element_id’],646 ‘map_type’ => 'single_location’,647 ‘prefix’ => 'sl’,648 /** ‘map_element’ => 'gmw-map-'.$this->args[‘element_id’], */649 ‘zoom_position’ => array(650 ‘lat’ => isset( $location->latitude ) ? $location->latitude : $location->lat,651 ‘lng’ => isset( $location->longitude ) ? $location->longitude : $location->lng,652 ),653 ‘map_width’ => $this->args[‘map_width’],654 ‘map_height’ => $this->args[‘map_height’],655 ‘expand_on_load’ => $this->args[‘expand_map_on_load’],656 ‘init_visible’ => true,657 );658659 $locations = array(660 0 => array(661 ‘ID’ => $location->object_id,662 ‘location_id’ => $location->ID,663 ‘object_id’ => $location->object_id,664 ‘object_type’ => $location->object_type,665 ‘lat’ => isset( $location->latitude ) ? $location->latitude : $location->lat,666 ‘lng’ => isset( $location->longitude ) ? $location->longitude : $location->lng,667 ‘map_icon’ => apply_filters( 'gmw_sl_post_map_icon’, $this->args[‘map_icon_url’], $this->args, $location, $this->user_position, $this ),668 ‘icon_size’ => $this->args[‘map_icon_size’],669 ‘info_window_content’ => $this->info_window_content( $location ),670 ),671 );672673 $map_options = array(674 ‘mapTypeId’ => $this->args[‘map_type’],675 ‘zoom’ => $this->args[‘zoom_level’],676 ‘mapTypeControl’ => true,677 ‘streetViewControl’ => false,678 ‘scrollwheel’ => ! empty( $this->args[‘scrollwheel_map_zoom’] ) ? true : false,679 ‘panControl’ => false,680 );681682 $user_position = array(683 ‘lat’ => $this->user_position[‘lat’],684 ‘lng’ => $this->user_position[‘lng’],685 ‘address’ => $this->user_position[‘address’],686 ‘map_icon’ => $this->args[‘user_map_icon_url’],687 ‘icon_size’ => $this->args[‘user_map_icon_size’],688 ‘iw_content’ => ! empty( $this->args[‘user_info_window’] ) ? $this->args[‘user_info_window’] : null,689 );690691 return ‘<div class="gmw-sl-map-wrapper gmw-sl-element">’ . gmw_get_map( $map_args, $map_options, $locations, $user_position ) . '</div>’;692 }693694 /**695 * Directions function696 *697 * @param object $location object location.698 *699 * @since 2.6.1700 *701 * @access public702 */703 public function directions_link( $location ) {704705 // if item has no location, abort!706 if ( empty( $location ) ) {707 return ! empty( $this->args[‘no_location_message’] ) ? $this->no_location_message() : false;708 }709710 $element_id = esc_attr( $this->args[‘element_id’] );711 $object = esc_attr( $this->args[‘object’] );712713 $output = '’;714 $output .= “<div id=\"gmw-sl-directions-link-wrapper-{$element_id}\” class=\"gmw-sl-directions-link-wrapper gmw-sl-element gmw-sl-{$object}-direction-link-wrapper\">";715 $output .= '<div class="trigger-wrapper">’;716 $output .= '<i class="gmw-icon-directions-solid"></i>’;717 $output .= “<a href=\"#\” id=\"form-trigger-{$element_id}\" class=\"form-trigger\" onclick=\"event.preventDefault();jQuery(this).closest( ‘.gmw-sl-element’ ).find( ‘.directions-link-form-wrapper’ ).slideToggle();\">" . esc_attr( $this->labels[‘directions’] ) . '</a>’;718 $output .= '</div>’;719 $output .= “<div id=\"directions-link-form-wrapper-{$element_id}\” class=\"directions-link-form-wrapper\" style=\"display:none;\">";720 $output .= '<form action="https://maps.google.com/maps" method="get" target="_blank">’;721 $output .= '<div class="address-field-wrapper">’;722 $output .= '<label for="origin-' . $element_id . ‘">’ . esc_attr( $this->labels[‘from’] ) . ' </label>’;723 $output .= '<input type="text" size="35" id="origin-' . $element_id . ‘" class="origin-field" name="saddr" value="’ . esc_attr( $this->user_position[‘address’] ) . '" placeholder="Your location" />’;724 $output .= “<a href=\"#\” class=\"get-directions-link-submit gmw-icon-search\" onclick=\"jQuery( this ).closest( ‘form’ ).submit();\"></a>";725 $output .= '</div>’;726 $output .= ‘<input type="hidden" name="daddr" value="’ . esc_attr( $location->address ) . '" />’;727 $output .= '</form>’;728 $output .= '</div>’;729 $output .= '</div>’;730731 return apply_filters( 'gmw_sl_directions’, $output, $this->args, $location, $this->user_position, $this );732 }733734 /**735 * Live directions function736 *737 * @param object $location object location.738 *739 * @since 2.6.1740 *741 * @access public742 */743 public function directions_form( $location ) {744745 // if item has no location, abort!746 if ( empty( $location ) ) {747 return ! empty( $this->args[‘no_location_message’] ) ? $this->no_location_message() : false;748 }749750 $element_id = esc_attr( $this->args[‘element_id’] );751752 $args = array(753 ‘element_id’ => $this->args[‘element_id’],754 ‘origin’ => $this->user_position[‘address’],755 ‘destination’ => $location->address,756 ‘units’ => $this->args[‘directions_form_units’],757 );758759 $output = '<div class="gmw-sl-directions-trigger-wrapper">’;760 $output .= '<i class="gmw-directions-icon gmw-icon-location-thin"></i>’;761 $output .= “<a href=\"#\” id=\"gmw-sl-directions-trigger-{$element_id}\" class=\"gmw-sl-directions-trigger\" onclick=\"event.preventDefault();jQuery(‘#gmw-directions-form-wrapper-{$element_id}, #gmw-directions-panel-wrapper-{$element_id}’).slideToggle();\">" . esc_attr( $this->labels[‘show_directions’] ) . '</a>’;762 $output .= '</div>’;763764 $output .= gmw_get_directions_form( $args );765766 return apply_filters( 'gmw_sl_directions_form’, $output, $this->args, $location, $this->user_position, $this );767 }768769 /**770 * Live directions panel771 *772 * Holder for the results of the live directions.773 *774 * @param object $location object location.775 *776 * @since 2.6.1777 */778 public function directions_panel( $location ) {779780 $output = gmw_get_directions_panel( $this->args[‘element_id’] );781782 return apply_filters( 'gmw_sl_directions_panel’, $output, $this->args, $location, $this->user_position, $this );783 }784785 /**786 * Display location meta787 *788 * @param object $location object location.789 *790 * @since 3.0791 *792 * @access public793 */794 public function location_meta( $location ) {795796 if ( empty( $this->args[‘location_meta’] ) ) {797 return false;798 }799800 $contact_info = explode( ',’, $this->args[‘location_meta’] );801 $location_meta = gmw_get_location_meta_list( $location->ID, $contact_info );802803 if ( empty( $location_meta ) ) {804 return false;805 }806807 $output = '<div class="gmw-sl-location-metas gmw-sl-element gmw-sl-additional-info-wrapper">’;808 $output .= $location_meta;809 $output .= '</div>’;810811 // for older version - to be removed.812 $output = apply_filters( 'gmw_sl_additional_info’, $output, $this->args, $location, $this->user_position, $this );813814 return apply_filters( 'gmw_sl_location_meta’, $output, $this->args, $location, $this->user_position, $this );815 }816817 /**818 * Create the content of the info window819 *820 * @param object $location object location.821 *822 * @since 2.5823 */824 public function info_window_content( $location ) {825826 if ( empty( $this->args[‘info_window’] ) ) {827 return false;828 }829830 // get info window elements.831 $iw_elements_array = explode( ',’, $this->args[‘info_window’] );832833 $iw_elements = array();834835 $iw_elements[‘iw_start’] = '<div class="gmw-iw-wrapper gmw-info-window-inner gmw-sl-iw-wrapper ' . esc_attr( $this->args[‘object’] ) . '">’;836837 foreach ( $iw_elements_array as $value ) {838 $iw_elements[ $value ] = false;839 }840841 $iw_elements[‘iw_end’] = '</div>’;842843 if ( isset( $iw_elements[‘distance’] ) ) {844 $iw_elements[‘distance’] = $this->distance( $location );845 }846847 if ( isset( $iw_elements[‘title’] ) ) {848 $iw_elements[‘title’] = $this->title( $location );849 }850851 if ( isset( $iw_elements[‘location_name’] ) ) {852 $iw_elements[‘location_name’] = $this->location_name( $location );853 }854855 if ( isset( $iw_elements[‘address’] ) ) {856 $iw_elements[‘address’] = $this->address( $location );857 }858859 if ( isset( $iw_elements[‘location_meta’] ) ) {860 $iw_elements[‘location_meta’] = ! empty( $this->location_meta ) ? $this->location_meta : $this->location_meta( $location );861 }862863 $output = apply_filters( 'gmw_sl_object_info_window’, $iw_elements, $this->args, $location, $this->user_position, $this );864865 return implode( ' ', $output );866 }867868 /**869 * Display no location message870 *871 * @param object $location object location.872 *873 * @since 2.6.1874 *875 * @access public876 */877 public function no_location_message( $location ) {878879 return apply_filters( 'gmw_sl_no_location_message’, ‘<h3 class="no-location">’ . esc_attr( $this->args[‘no_location_message’] ) . '</h3>’, $location, $this->args, $this->user_position, $this );880 }881882 /**883 * Display elements based on arguments884 *885 * @since 2.6.1886 *887 * @access public888 */889 public function output() {890891 // check that we have at least one element to display.892 if ( empty( $this->elements_value ) ) {893 return;894 }895896 // loop through and generate the elements.897 foreach ( $this->elements as $element => $value ) {898899 if ( method_exists( $this, $element ) ) {900 $this->elements[ $element ] = $this->$element( $this->location_data );901 }902 }903904 do_action( 'gmw_sl_before_output_elements’, $this->elements, $this->args, $this->location_data, $this->user_position );905906 $output = implode( '’, $this->elements );907908 do_action( 'gmw_element_loaded’, 'single_location’, $this );909910 return apply_filters( 'gmw_sl_display_output’, $output, $this->elements, $this->args, $this->location_data, $this->user_position, $this );911 }912}