File: /home/holamediaagency/public_html/wp-content/plugins/wpseo-local/classes/class-storelocator.php
<?php
/**
* Yoast SEO: Local plugin file.
*
* @package WPSEO_Local\Frontend
*/
if ( ! class_exists( 'WPSEO_Local_Storelocator' ) ) {
/**
* Class WPSEO_Local_Storelocator
*
* Adds all functionality for the store locator
*/
class WPSEO_Local_Storelocator {
/**
* Stores the options for this plugin.
*
* @var array
*/
public $options = [];
/**
* Admin Asset Manager object.
*
* @var WPSEO_Local_Admin_Assets
*/
private $asset_manager;
/**
* Whether to load external stylesheet or not.
*
* @var bool
*/
public $load_styles = false;
/**
* Default attributes for the `wpseo_storelocator` shortcode.
*
* @var array
*/
protected $shortcode_defaults = [
'radius' => 10,
'max_number' => '',
'show_radius' => false,
'show_nearest_suggestion' => true,
'show_map' => true,
'show_filter' => false,
'map_width' => '100%',
'scrollable' => true,
'draggable' => true,
'marker_clustering' => false,
'show_country' => false,
'show_state' => false,
'show_phone' => false,
'show_phone_2' => false,
'show_fax' => false,
'show_email' => false,
'show_url' => false,
'map_style' => 'ROADMAP',
'show_route_label' => '',
'oneline' => false,
'show_opening_hours' => false,
'hide_closed' => false,
'show_category_filter' => false,
'from_widget' => false,
'widget_title' => '',
'before_title' => '',
'after_title' => '',
'echo' => false,
'width' => '100%',
'height' => 300,
'zoom' => -1,
];
/**
* Constructor.
*/
public function __construct() {
/**
* The functionality from this class never needed in the admin area.
* So we bail from executing the rest if we are there.
*/
if ( is_admin() ) {
return;
}
$this->options = get_option( 'wpseo_local' );
if ( isset( $this->options['map_view_style'] ) ) {
$this->shortcode_defaults['map_style'] = $this->options['map_view_style'];
}
if ( isset( $this->options['show_route_label'] ) && ! empty( $this->options['show_route_label'] ) ) {
$this->shortcode_defaults['show_route_label'] = $this->options['show_route_label'];
}
else {
$this->shortcode_defaults['show_route_label'] = __( 'Show route', 'yoast-local-seo' );
}
add_shortcode( 'wpseo_storelocator', [ $this, 'show_storelocator' ] );
add_action( 'wp_head', [ $this, 'load_scripts' ], 99 );
}
/**
* Enqueue the scripts necessary for the Store Locator to work.
*
* The wp-polyfill asset is needed for versions of WP before 5.0.
*/
public function enqueue_scripts() {
$this->asset_manager = new WPSEO_Local_Admin_Assets();
$this->asset_manager->enqueue_script( 'store-locator' );
$this->asset_manager->enqueue_script( 'google-maps' );
$default_country = isset( $this->options['default_country'] ) ? WPSEO_Local_Frontend::get_country( $this->options['default_country'] ) : '';
wp_localize_script( WPSEO_Local_Admin_Assets::PREFIX . 'store-locator', 'storeLocator', [ 'defaultCountry' => $default_country ] );
add_action( 'wp_footer', 'wpseo_enqueue_geocoder' );
add_action( 'admin_footer', 'wpseo_enqueue_geocoder' );
}
/**
* Outputs HTML for the store locator.
*
* @param array $atts Array of attributes for the store locator shortcode.
*
* @return string
*/
public function show_storelocator( $atts ) {
global $wpseo_enqueue_geocoder, $wpseo_sl_load_scripts;
$this->enqueue_scripts();
// Don't show any output when you don't have multiple locations enabled.
if ( wpseo_has_multiple_locations() === false ) {
return '';
}
$wpseo_sl_load_scripts = true;
$atts = wpseo_check_falses( shortcode_atts( $this->shortcode_defaults, $atts ) );
if ( $atts['show_map'] ) {
$wpseo_enqueue_geocoder = true;
}
ob_start();
?>
<!--local_seo_store_locator_start-->
<form action="#wpseo-storelocator-form" method="post" id="wpseo-storelocator-form" data-form="wpseo-local-store-locator">
<fieldset>
<?php
$search_string = isset( $_REQUEST['wpseo-sl-search'] ) ? esc_attr( $_REQUEST['wpseo-sl-search'] ) : '';
$sl_category_term = ! empty( $_REQUEST['wpseo-sl-category'] ) ? $_REQUEST['wpseo-sl-category'] : '';
?>
<p>
<label for="wpseo-sl-search"><?php echo apply_filters( 'yoast-local-seo-search-label', __( 'Enter your postal code, city and / or state', 'yoast-local-seo' ) ); ?></label>
<input type="text" name="wpseo-sl-search" id="wpseo-sl-form-search" value="<?php echo esc_attr( $search_string ); ?>">
<input type="hidden" name="wpseo-sl-lat" id="wpseo-sl-form-lat" value="">
<input type="hidden" name="wpseo-sl-lng" id="wpseo-sl-form-lng" value="">
<?php
// Show icon for retrieving current location.
if ( wpseo_may_use_current_location() === true ) {
echo ' <button type="button" class="wpseo_use_current_location" data-target="wpseo-sl-form-search"><img src="' . esc_url( plugins_url( 'images/location-icon.svg', WPSEO_LOCAL_FILE ) ) . '" class="wpseo_use_current_location_image" height="24" width="24" alt="' . esc_attr__( 'Use my current location', 'yoast-local-seo' ) . '" data-loading-text="' . esc_attr__( 'Determining current location', 'yoast-local-seo' ) . '"></button> ';
}
// Show the radius selectbox.
if ( $atts['show_radius'] ) {
esc_html_e( 'within', 'yoast-local-seo' );
?>
<select name="wpseo-sl-radius" id="wpseo-sl-radius">
<?php
$radius_array = [ 1, 5, 10, 25, 50, 100, 250, 500, 1000 ];
$selected_radius = ! empty( $_REQUEST['wpseo-sl-radius'] ) ? esc_attr( $_REQUEST['wpseo-sl-radius'] ) : $atts['radius'];
foreach ( $radius_array as $radius ) {
echo '<option value="' . (int) $radius . '" ' . selected( $selected_radius, $radius, false ) . '>' . (int) $radius . ( ( $this->options['unit_system'] === 'METRIC' ) ? 'km' : 'mi' ) . '</option>';
}
?>
</select>
<?php
}
else {
?>
<input type="hidden" name="wpseo-sl-radius" id="wpseo-sl-radius-text" value="<?php echo esc_attr( $atts['radius'] ); ?>">
<?php
}
?>
</p>
<?php if ( $atts['show_filter'] ) { ?>
<?php
$terms = get_terms( 'wpseo_locations_category' );
?>
<?php if ( count( $terms ) > 0 ) { ?>
<p class="sl-filter">
<label for="wpseo-sl-category"><?php esc_html_e( 'Filter by category', 'yoast-local-seo' ); ?></label>
<select name="wpseo-sl-category" id="wpseo-sl-category">
<option value=""></option>
<?php
foreach ( $terms as $term ) {
echo '<option value="' . esc_attr( $term->term_id ) . '" ' . selected( $sl_category_term, $term->term_id, false ) . '>' . esc_html( $term->name ) . '</option>';
}
?>
</select>
</p>
<?php } ?>
<?php } ?>
<p class="sl-submit">
<input type="submit" value="<?php esc_attr_e( 'Search', 'yoast-local-seo' ); ?>">
</p>
</fieldset>
</form>
<div id="wpseo-storelocator-results">
<?php
$results = false;
if ( empty( $_POST ) === false ) {
$results = $this->get_results( $atts );
}
if ( $atts['show_map'] ) {
$location_ids = [];
$ids = 'all';
if ( ! empty( $_POST ) && ! is_wp_error( $results ) ) {
foreach ( $results['locations'] as $location ) {
$location_ids[] = $location['ID'];
}
$ids = implode( ',', $location_ids );
}
$map_atts = [
'id' => $ids,
'max_number' => $atts['max_number'],
'width' => $atts['map_width'],
'from_sl' => true,
'show_route' => true,
'scrollable' => $atts['scrollable'],
'draggable' => $atts['draggable'],
'marker_clustering' => $atts['marker_clustering'],
'map_style' => $atts['map_style'],
'show_category_filter' => $atts['show_category_filter'],
'zoom' => $atts['zoom'],
'width' => $atts['width'],
'height' => $atts['height'],
'show_phone' => $atts['show_phone'],
'show_phone_2' => $atts['show_phone_2'],
'show_fax' => $atts['show_fax'],
'show_country' => $atts['show_country'],
'show_email' => $atts['show_email'],
'show_url' => $atts['show_url'],
];
echo wpseo_local_show_map( $map_atts );
}
if ( empty( $_POST ) === false ) {
if ( ! is_wp_error( $results ) ) {
$show_suggestion = $results['in_radius'] <= 0 && $atts['show_nearest_suggestion'] === true && ! empty( $results['locations'] );
if ( $results['in_radius'] > 0 ) {
$number = count( $results['locations'] );
/* translators: %d extends to the number of found locations in the radius */
echo '<h2>' . sprintf( esc_html( _n( '%d result has been found', '%d results have been found', $number, 'yoast-local-seo' ) ), (int) $number ) . '</h2>';
foreach ( $results['locations'] as $key => $location ) {
$this->get_location_details( $location['ID'], $atts );
}
}
else {
echo '<h2>' . esc_html__( 'No results found', 'yoast-local-seo' ) . '</h2>';
if ( $show_suggestion ) {
foreach ( $results['locations'] as $location ) {
/* translators: %s extends to the distance in miles */
$text_mi = sprintf( __( 'The nearest location is %s miles away', 'yoast-local-seo' ), $location['distance'] );
/* translators: %s extends to the distance in kilometers */
$text_km = sprintf( __( 'The nearest location is %s kilometers away', 'yoast-local-seo' ), $location['distance'] );
$text = ( ( $this->options['unit_system'] === 'METRIC' ) ? $text_km : $text_mi );
$text = apply_filters_deprecated( 'wpso_local_no_stores_in_radius', [ $text ], 'WPSEO Local 12.3', 'Yoast\WP\Local\no_stores_in_radius' );
// @codingStandardsIgnoreLine
$text = apply_filters( 'Yoast\WP\Local\no_stores_in_radius', $text );
echo '<p class="nearest_location">' . esc_html( $text ) . '</p>';
$this->get_location_details( $location['ID'], $atts );
}
}
}
}
else {
echo '<h2>' . esc_html__( 'No results found', 'yoast-local-seo' ) . '</h2>';
}
}
?>
</div><!--local_seo_store_locator_end-->
<?php
$output = ob_get_contents();
ob_end_clean();
return $output;
}
/**
* Retrieves the search results based on given search term (zipcode or city).
*
* @param array $atts Array of attributes for the store locator shortcode.
*
* @return array | WP_Error
*/
public function get_results( $atts ) {
if ( empty( $_POST['wpseo-sl-search'] ) ) {
return new WP_Error( 'wpseo-no-input', __( 'Please enter a zipcode or city', 'yoast-local-seo' ) );
}
$nr_results = $this->get_nr_results( $atts );
$locations = $this->get_locations( $nr_results, false );
$count_in_radius = $locations['count'];
if ( $count_in_radius === 0 ) {
$locations = $this->get_locations( $nr_results, true );
}
return [
'in_radius' => $count_in_radius,
'locations' => $locations['results'],
];
}
/**
* Fetch location results to work with before returning processed results
*
* @param int $nr_results The amount of location results to display.
* @param bool $check_outside_radius Determines whether we should search locations inside or outside the radius.
*
* @return array
*/
private function get_locations( $nr_results, $check_outside_radius ) {
global $wpdb;
$metric = ( $this->options['unit_system'] === 'METRIC' ) ? 'km' : 'mi';
$radius = ( ! empty( $_REQUEST['wpseo-sl-radius'] ) ) ? $_REQUEST['wpseo-sl-radius'] : 99999;
$sl_category_term = ( ! empty( $_REQUEST['wpseo-sl-category'] ) ) ? $_REQUEST['wpseo-sl-category'] : '';
$distances = [
'in_radius' => 0,
'locations' => [],
];
$search_string = isset( $_REQUEST['wpseo-sl-search'] ) ? esc_attr( $_REQUEST['wpseo-sl-search'] ) : '';
if ( $search_string === '' ) {
return $distances;
}
$coordinates = (object) [
'lat' => floatval( $_POST['wpseo-sl-lat'] ),
'lng' => floatval( $_POST['wpseo-sl-lng'] ),
];
if ( ! $coordinates ) {
return new WP_Error( 'wpseo-get-results-error', __( 'No valid coordinates. We cannot complete the search.', 'yoast-local-seo' ) );
}
$replacements = [ $coordinates->lat, $coordinates->lng, $coordinates->lat ];
// Extend SQL with category filter.
$inner_join = '';
if ( $sl_category_term !== '' ) {
$inner_join .= "
INNER JOIN $wpdb->term_relationships AS term_rel ON p.ID = term_rel.object_id
INNER JOIN $wpdb->term_taxonomy AS taxo ON term_rel.term_taxonomy_id = taxo.term_taxonomy_id
AND taxo.taxonomy = 'wpseo_locations_category'
AND taxo.term_id = %s
";
$replacements[] = $sl_category_term;
}
// Set post type.
$replacements[] = PostType::get_instance()->get_post_type();
/*
* Get all coordinates from posts.
*/
$post_status = [ 'publish' ];
// If the user is logged in and can edit posts, add more post statuses.
if ( is_user_logged_in() && current_user_can( 'edit_posts' ) ) {
array_push( $post_status, 'draft', 'future', 'pending', 'private' );
}
$replacements = array_merge( $replacements, $post_status );
// Distance query/parameter.
$distance_value = $this->get_distance_for_query( $radius, $metric );
$query_inside_radius_check = 'HAVING distance < %d';
$replacements[] = $distance_value;
// For outside checking we'll limit the number of results to 1.
if ( $check_outside_radius ) {
$nr_results = 1;
// Removing the distance query/parameter.
$query_inside_radius_check = '';
array_pop( $replacements );
}
$replacements[] = $nr_results;
// @codingStandardsIgnoreStart
$results = $wpdb->get_results(
$wpdb->prepare(
"SELECT p.ID, m1.meta_value as lat, m2.meta_value as lng,
ROUND( ( 6366.564864 * acos ( cos ( radians(%f) ) * cos( radians( m1.meta_value ) ) * cos( radians( m2.meta_value ) - radians(%f) ) + sin ( radians(%f) ) * sin( radians( m1.meta_value ) ) ) ), 2 ) AS distance
FROM {$wpdb->posts} p
INNER JOIN {$wpdb->postmeta} m1 ON p.ID = m1.post_id
INNER JOIN {$wpdb->postmeta} m2 ON p.ID = m2.post_id
$inner_join
WHERE
p.post_type = '%s' AND
p.post_status IN( " . implode( ',', array_fill( 0, count( $post_status ), '%s' ) ) . " ) AND
m1.meta_key = '_wpseo_coordinates_lat' AND
m2.meta_key = '_wpseo_coordinates_long'
GROUP BY p.ID, lat, lng
$query_inside_radius_check
ORDER BY distance ASC
LIMIT 0, %d",
$replacements
),
ARRAY_A
);
// @codingStandardsIgnoreEnd
$location_count = $wpdb->num_rows;
return [
'count' => $location_count,
'results' => $results,
];
}
/**
* Determines the number of results to display
*
* @param array $atts Array of attributes for the store locator shortcode.
*
* @return int
*/
private function get_nr_results( $atts ) {
$value = 10;
if ( ! empty( $this->options['sl_num_results'] ) ) {
$value = $this->options['sl_num_results'];
}
if ( ! empty( $atts['max_number'] ) && $atts['max_number'] <= $value ) {
$value = $atts['max_number'];
}
return $value;
}
/**
* Calculates distances from select value with defined metric for queries expecting KM metric.
*
* @param float $value Distance.
* @param string $metric Whether the distance is in 'km' or 'mi' (miles).
*
* @return float
*/
public function get_distance_for_query( $value, $metric ) {
$km = $value;
if ( $metric === 'mi' ) {
$km = ( $value * 1.60934 );
}
return $km;
}
/**
* Calculates distances from original KM to available metrics.
*
* @param float $km Distance in KM.
*
* @return array
*/
public function get_distance( $km ) {
$miles = ( $km * 0.621371 );
return [
'mi' => $miles,
'km' => $km,
];
}
/**
* Load jQuery script (if not already loaded before).
*/
public function load_scripts() {
if ( wp_script_is( 'jquery', 'done' ) === false && apply_filters( 'wpseo_local_load_jquery', true ) !== false ) {
wp_enqueue_script( 'jquery', '//ajax.googleapis.com/ajax/libs/jquery/1.10.2/jquery.min.js', [], '1.10.2', true );
}
}
/**
* Show all location information.
*
* @param int $location_id Post ID of the location.
* @param array $atts Array of attributes, used for displaying the address.
* These are matching attributes for the wpseo_local_show_address() method.
*/
public function get_location_details( $location_id, $atts ) {
$coords_lat = get_post_meta( $location_id, '_wpseo_coordinates_lat', true );
$coords_long = get_post_meta( $location_id, '_wpseo_coordinates_long', true );
?>
<div class="wpseo-result">
<?php
$address_atts = [
'id' => $location_id,
'show_state' => $atts['show_state'],
'show_country' => $atts['show_country'],
'show_phone' => $atts['show_phone'],
'show_phone_2' => $atts['show_phone_2'],
'show_fax' => $atts['show_fax'],
'show_email' => $atts['show_email'],
'show_url' => $atts['show_url'],
'show_opening_hours' => $atts['show_opening_hours'],
'hide_closed' => $atts['hide_closed'],
'oneline' => $atts['oneline'],
'from_sl' => true,
'echo' => false,
'hide_json_ld' => true,
];
$location = wpseo_local_show_address( $address_atts );
echo apply_filters( 'wpseo_local_sl_result', $location, $location_id );
?>
<div class="wpseo-sl-route">
<a href="javascript:;" onclick="wpseo_sl_show_route( this, '<?php echo $coords_lat; ?>', '<?php echo $coords_long; ?>' );"><?php echo $atts['show_route_label']; ?></a>
</div>
</div>
<?php
}
}
}
if ( ! function_exists( 'wpseo_local_storelocator' ) ) {
/**
* Initialize the store locator.
*
* @param array $atts Array of attributes for displaying the store locator.
*
* @return string
*/
function wpseo_local_storelocator( $atts ) {
global $wpseo_local_storelocator;
if ( $wpseo_local_storelocator === null ) {
$wpseo_local_storelocator = new WPSEO_Local_Storelocator();
}
return $wpseo_local_storelocator->show_storelocator( $atts );
}
}
$wpseo_sl_load_scripts = false;