WordPress 地图定位插件

专业WordPress插件,用于管理和显示具有高级功能的Google地图上多个商店位置

<?php
/**
* Plugin Name: BDFG Map Multi Locations
* Plugin URI: https://beiduofengou.net/2024/04/15/bdfg-map-multi-locations/
* Description: Professional WordPress plugin for managing and displaying multiple store locations on Google Maps with advanced features
* Version: 2.3.1
* Author: beiduofengou
* Author URI: https://beiduofengou.net
* License: GPL v2 or later
* Text Domain: bdfg-map-locations
* Domain Path: /languages
*
* @package BDFG_Map_Locations
* @author beiduofengou <[email protected]>
* @copyright 2024 beiduofengou.net
*/

// Prevent direct access to this file
if (!defined('ABSPATH')) {
exit('Direct access denied.');
}

// Define plugin constants
define('BDFG_MAP_VERSION', '2.3.1');
define('BDFG_MAP_PLUGIN_DIR', plugin_dir_path(__FILE__));
define('BDFG_MAP_PLUGIN_URL', plugin_dir_url(__FILE__));
define('BDFG_MAP_CACHE_TIME', 3600); // Cache lifetime in seconds

/**
* Main plugin class
*/
class BDFG_Map_Locations {
/**
* Singleton instance
*
* @var BDFG_Map_Locations
*/
private static $instance = null;

/**
* Store locations array
*
* @var array
*/
private $locations = [];

/**
* Plugin settings
*
* @var array
*/
private $settings = [];

/**
* Get singleton instance
*
* @return BDFG_Map_Locations
*/
public static function get_instance() {
if (null === self::$instance) {
self::$instance = new self();
}
return self::$instance;
}

/**
* Constructor
*/
private function __construct() {
// Core initialization
add_action('init', [$this, 'init']);
add_action('plugins_loaded', [$this, 'load_language_files']);

// Admin hooks
add_action('admin_menu', [$this, 'add_admin_menu']);
add_action('admin_enqueue_scripts', [$this, 'admin_enqueue_scripts']);
add_action('admin_init', [$this, 'register_settings']);

// Frontend hooks
add_action('wp_enqueue_scripts', [$this, 'frontend_enqueue_scripts']);

// Shortcodes
add_shortcode('bdfg_map', [$this, 'map_shortcode']);
add_shortcode('bdfg_location_list', [$this, 'location_list_shortcode']);

// Ajax handlers
add_action('wp_ajax_bdfg_save_location', [$this, 'ajax_save_location']);
add_action('wp_ajax_bdfg_delete_location', [$this, 'ajax_delete_location']);
add_action('wp_ajax_nopriv_bdfg_get_locations', [$this, 'ajax_get_locations']);
add_action('wp_ajax_bdfg_get_locations', [$this, 'ajax_get_locations']);

// REST API
add_action('rest_api_init', [$this, 'register_rest_routes']);
}

/**
* Initialize plugin
*/
public function init() {
// Load locations from cache or database
$this->locations = $this->get_cached_locations();

// Register location taxonomy
$this->register_location_taxonomy();

// Load plugin settings
$this->settings = get_option('bdfg_map_settings', [
'api_key' => '',
'default_zoom' => 12,
'default_lat' => 40.7128,
'default_lng' => -74.0060,
'enable_clustering' => true,
'analytics_enabled' => true
]);
}

/**
* Register location taxonomy
*/
public function register_location_taxonomy() {
$labels = [
'name' => __('Location Categories', 'bdfg-map-locations'),
'singular_name' => __('Location Category', 'bdfg-map-locations'),
'search_items' => __('Search Categories', 'bdfg-map-locations'),
'all_items' => __('All Categories', 'bdfg-map-locations'),
'parent_item' => __('Parent Category', 'bdfg-map-locations'),
'parent_item_colon' => __('Parent Category:', 'bdfg-map-locations'),
'edit_item' => __('Edit Category', 'bdfg-map-locations'),
'update_item' => __('Update Category', 'bdfg-map-locations'),
'add_new_item' => __('Add New Category', 'bdfg-map-locations'),
'new_item_name' => __('New Category Name', 'bdfg-map-locations'),
'menu_name' => __('Categories', 'bdfg-map-locations'),
];

$args = [
'hierarchical' => true,
'labels' => $labels,
'show_ui' => true,
'show_admin_column' => true,
'query_var' => true,
'rewrite' => ['slug' => 'location-category'],
];

register_taxonomy('bdfg_location_category', null, $args);
}

/**
* Add admin menu items
*/
public function add_admin_menu() {
add_menu_page(
__('BDFG Map Locations', 'bdfg-map-locations'),
__('BDFG Maps', 'bdfg-map-locations'),
'manage_options',
'bdfg-map-locations',
[$this, 'admin_page'],
'dashicons-location',
30
);

add_submenu_page(
'bdfg-map-locations',
__('Settings', 'bdfg-map-locations'),
__('Settings', 'bdfg-map-locations'),
'manage_options',
'bdfg-map-settings',
[$this, 'settings_page']
);

add_submenu_page(
'bdfg-map-locations',
__('Import/Export', 'bdfg-map-locations'),
__('Import/Export', 'bdfg-map-locations'),
'manage_options',
'bdfg-map-import-export',
[$this, 'import_export_page']
);
}

/**
* Enqueue admin scripts and styles
*/
public function admin_enqueue_scripts($hook) {
if (!strpos($hook, 'bdfg-map')) {
return;
}

// Admin styles
wp_enqueue_style(
'bdfg-admin-style',
BDFG_MAP_PLUGIN_URL . 'assets/css/admin.css',
[],
BDFG_MAP_VERSION
);

// Google Maps with places library
wp_enqueue_script(
'google-maps',
'https://maps.googleapis.com/maps/api/js?key=' . $this->get_api_key() . '&libraries=places',
[],
BDFG_MAP_VERSION,
true
);

// Admin scripts
wp_enqueue_script(
'bdfg-admin-script',
BDFG_MAP_PLUGIN_URL . 'assets/js/admin.js',
['jquery', 'google-maps'],
BDFG_MAP_VERSION,
true
);

wp_localize_script('bdfg-admin-script', 'bdfgMapParams', [
'ajaxUrl' => admin_url('admin-ajax.php'),
'nonce' => wp_create_nonce('bdfg_map_nonce'),
'strings' => [
'confirmDelete' => __('Are you sure you want to delete this location?', 'bdfg-map-locations'),
'locationSaved' => __('Location saved successfully!', 'bdfg-map-locations'),
'locationDeleted' => __('Location deleted successfully!', 'bdfg-map-locations'),
'error' => __('An error occurred. Please try again.', 'bdfg-map-locations')
]
]);
}

/**
* Get API key from settings
*/
private function get_api_key() {
return $this->settings['api_key'] ?? '';
}

/**
* Get cached locations
*/
private function get_cached_locations() {
$cached = wp_cache_get('bdfg_map_locations');
if (false === $cached) {
$cached = get_option('bdfg_map_locations', []);
wp_cache_set('bdfg_map_locations', $cached, '', BDFG_MAP_CACHE_TIME);
}
return $cached;
}

/**
* Map shortcode handler
*/
public function map_shortcode($atts) {
$atts = shortcode_atts([
'height' => '400px',
'zoom' => $this->settings['default_zoom'],
'category' => '',
'clustering' => $this->settings['enable_clustering']
], $atts);

// Filter locations by category if specified
$locations = $this->locations;
if (!empty($atts['category'])) {
$locations = array_filter($locations, function($location) use ($atts) {
return isset($location['category']) && $location['category'] === $atts['category'];
});
}

// Add schema markup for SEO
$schema_markup = '';
foreach ($locations as $location) {
$schema_markup .= $this->add_location_schema($location);
}

ob_start();
include BDFG_MAP_PLUGIN_DIR . 'templates/map.php';
return ob_get_clean();
}

// Add remaining methods here (ajax_save_location, ajax_delete_location, etc.)
// ...
}

// Initialize the plugin
function bdfg_map_locations_init() {
BDFG_Map_Locations::get_instance();
}
add_action('plugins_loaded', 'bdfg_map_locations_init');

// Activation hook
register_activation_hook(__FILE__, 'bdfg_map_locations_activate');
function bdfg_map_locations_activate() {
// Create necessary database tables and options
if (!get_option('bdfg_map_locations')) {
add_option('bdfg_map_locations', []);
}

if (!get_option('bdfg_map_settings')) {
add_option('bdfg_map_settings', [
'api_key' => '',
'default_zoom' => 12,
'default_lat' => 40.7128,
'default_lng' => -74.0060,
'enable_clustering' => true,
'analytics_enabled' => true
]);
}

// Clear any existing caches
wp_cache_delete('bdfg_map_locations');

// Create custom capabilities
$role = get_role('administrator');
$role->add_cap('manage_bdfg_locations');
}

// Deactivation hook
register_deactivation_hook(__FILE__, 'bdfg_map_locations_deactivate');
function bdfg_map_locations_deactivate() {
// Clean up caches
wp_cache_delete('bdfg_map_locations');

// Optional: Remove capabilities
$role = get_role('administrator');
$role->remove_cap('manage_bdfg_locations');
}

includes/class-bdfg-locations-api.php

相关文章: WooCommerce 自定义”加入购物车”按钮

<?php
/**
* BDFG Map Locations API Handler
*
* @package BDFG_Map_Locations
* @author beiduofengou <[email protected]>
* @copyright 2025 beiduofengou.net
*/

if (!defined('ABSPATH')) {
exit('Direct access denied.');
}

class BDFG_Locations_API {
/**
* Register REST API routes
*/
public function register_routes() {
register_rest_route('bdfg-map/v1', '/locations', [
[
'methods' => WP_REST_Server::READABLE,
'callback' => [$this, 'get_locations'],
'permission_callback' => '__return_true',
],
[
'methods' => WP_REST_Server::CREATABLE,
'callback' => [$this, 'create_location'],
'permission_callback' => [$this, 'check_admin_permission'],
]
]);
}

/**
* Check if user has admin permissions
*/
public function check_admin_permission() {
return current_user_can('manage_options');
}

/**
* Get all locations
*/
public function get_locations($request) {
$locations = get_option('bdfg_map_locations', []);

if (empty($locations)) {
return new WP_REST_Response([], 200);
}

return new WP_REST_Response($locations, 200);
}

/**
* Create new location
*/
public function create_location($request) {
$params = $request->get_params();

$location = [
'id' => uniqid('bdfg_loc_'),
'name' => sanitize_text_field($params['name']),
'address' => sanitize_text_field($params['address']),
'lat' => (float) $params['lat'],
'lng' => (float) $params['lng'],
'description' => wp_kses_post($params['description']),
'category' => sanitize_text_field($params['category'] ?? ''),
'created_at' => current_time('mysql'),
'created_by' => get_current_user_id()
];

$locations = get_option('bdfg_map_locations', []);
$locations[] = $location;

update_option('bdfg_map_locations', $locations);
wp_cache_delete('bdfg_map_locations');

return new WP_REST_Response($location, 201);
}
}

includes/class-bdfg-locations-importer.php

<?php
/**
* BDFG Map Locations Importer
*
* @package BDFG_Map_Locations
* @author beiduofengou <[email protected]>
* @copyright 2025 beiduofengou.net
*/

if (!defined('ABSPATH')) {
exit('Direct access denied.');
}

class BDFG_Locations_Importer {
/**
* Import locations from CSV file
*/
public function import_from_csv($file) {
if (!current_user_can('manage_options')) {
return new WP_Error('permission_denied', __('Permission denied', 'bdfg-map-locations'));
}

if (!file_exists($file)) {
return new WP_Error('file_not_found', __('Import file not found', 'bdfg-map-locations'));
}

$csv = array_map('str_getcsv', file($file));
array_shift($csv); // Remove headers

$locations = get_option('bdfg_map_locations', []);
$imported = 0;

foreach ($csv as $row) {
if (count($row) < 4) continue;

$locations[] = [
'id' => uniqid('bdfg_loc_'),
'name' => sanitize_text_field($row[0]),
'address' => sanitize_text_field($row[1]),
'lat' => (float)$row[2],
'lng' => (float)$row[3],
'description' => wp_kses_post($row[4] ?? ''),
'category' => sanitize_text_field($row[5] ?? ''),
'created_at' => current_time('mysql'),
'created_by' => get_current_user_id()
];

$imported++;
}

update_option('bdfg_map_locations', $locations);
wp_cache_delete('bdfg_map_locations');

return $imported;
}
}

assets/js/admin.js

相关文章: Woocommerce强大的购物车管理扩展

/**
* BDFG Map Locations Admin JavaScript
*
* @package BDFG_Map_Locations
* @author beiduofengou <[email protected]>
*/

(function($) {
'use strict';

const BDFGMapAdmin = {
map: null,
marker: null,
geocoder: null,

init: function() {
this.initMap();
this.bindEvents();
this.initAutocomplete();
},

initMap: function() {
const mapElement = document.getElementById('bdfg-admin-map');
if (!mapElement) return;

this.map = new google.maps.Map(mapElement, {
zoom: 12,
center: { lat: 40.7128, lng: -74.0060 },
styles: this.getMapStyles()
});

this.geocoder = new google.maps.Geocoder();
},

bindEvents: function() {
$('#bdfg-location-form').on('submit', this.handleFormSubmit.bind(this));
$('.bdfg-delete-location').on('click', this.handleDelete.bind(this));
$('#bdfg-search-address').on('change', this.handleAddressSearch.bind(this));
},

initAutocomplete: function() {
const input = document.getElementById('bdfg-search-address');
if (!input) return;

const autocomplete = new google.maps.places.Autocomplete(input);
autocomplete.addListener('place_changed', () => {
const place = autocomplete.getPlace();
if (!place.geometry) return;

this.updateMarker(place.geometry.location);
this.map.setCenter(place.geometry.location);

$('#bdfg-lat').val(place.geometry.location.lat());
$('#bdfg-lng').val(place.geometry.location.lng());
});
},

updateMarker: function(location) {
if (this.marker) {
this.marker.setMap(null);
}

this.marker = new google.maps.Marker({
map: this.map,
position: location,
draggable: true,
animation: google.maps.Animation.DROP
});

google.maps.event.addListener(this.marker, 'dragend', (event) => {
$('#bdfg-lat').val(event.latLng.lat());
$('#bdfg-lng').val(event.latLng.lng());
});
},

handleFormSubmit: function(e) {
e.preventDefault();
const form = $(e.target);

$.ajax({
url: bdfgMapParams.ajaxUrl,
type: 'POST',
data: {
action: 'bdfg_save_location',
nonce: bdfgMapParams.nonce,
...form.serializeArray().reduce((obj, item) => {
obj[item.name] = item.value;
return obj;
}, {})
},
success: (response) => {
if (response.success) {
alert(bdfgMapParams.strings.locationSaved);
location.reload();
} else {
alert(bdfgMapParams.strings.error);
}
},
error: () => alert(bdfgMapParams.strings.error)
});
},

handleDelete: function(e) {
e.preventDefault();
if (!confirm(bdfgMapParams.strings.confirmDelete)) return;

const btn = $(e.target);

$.ajax({
url: bdfgMapParams.ajaxUrl,
type: 'POST',
data: {
action: 'bdfg_delete_location',
nonce: bdfgMapParams.nonce,
location_id: btn.data('id')
},
success: (response) => {
if (response.success) {
btn.closest('tr').fadeOut();
} else {
alert(bdfgMapParams.strings.error);
}
},
error: () => alert(bdfgMapParams.strings.error)
});
},

getMapStyles: function() {
return [
{
"featureType": "administrative",
"elementType": "geometry",
"stylers": [{"visibility": "simplified"}]
},
{
"featureType": "poi",
"stylers": [{"visibility": "simplified"}]
}
// Add more custom styles as needed
];
}
};

$(document).ready(() => BDFGMapAdmin.init());

})(jQuery);

assets/js/frontend.js

/**
* BDFG Map Locations Frontend JavaScript
*
* @package BDFG_Map_Locations
* @author beiduofengou <[email protected]>
*/

(function($) {
'use strict';

const BDFGMapFrontend = {
map: null,
markers: [],
infoWindow: null,
markerCluster: null,

init: function() {
$('.bdfg-map-container').each((i, element) => {
this.initMap(element);
});
},

initMap: function(element) {
const mapOptions = {
zoom: parseInt($(element).data('zoom')) || 12,
styles: this.getMapStyles(),
mapTypeControl: true,
streetViewControl: true,
fullscreenControl: true
};

this.map = new google.maps.Map(element, mapOptions);
this.infoWindow = new google.maps.InfoWindow();

this.loadLocations();
},

loadLocations: function() {
$.ajax({
url: bdfgMapParams.ajaxUrl,
data: {
action: 'bdfg_get_locations',
nonce: bdfgMapParams.nonce
},
success: (response) => {
if (response.success && response.data) {
this.addMarkers(response.data);
}
}
});
},

addMarkers: function(locations) {
const bounds = new google.maps.LatLngBounds();

locations.forEach(location => {
const position = new google.maps.LatLng(location.lat, location.lng);
const marker = new google.maps.Marker({
position: position,
map: this.map,
title: location.name,
animation: google.maps.Animation.DROP
});

bounds.extend(position);
this.markers.push(marker);

marker.addListener('click', () => {
this.showInfoWindow(marker, location);
});
});

if (locations.length > 1) {
this.map.fitBounds(bounds);
} else if (locations.length === 1) {
this.map.setCenter(bounds.getCenter());
this.map.setZoom(15);
}

if (bdfgMapParams.enableClustering) {
this.initMarkerClusterer();
}
},

showInfoWindow: function(marker, location) {
const content = `
<div class="bdfg-info-window">
<h3>${location.name}</h3>
<p>${location.address}</p>
${location.description ? `<p>${location.description}</p>` : ''}
<p>
<a href="https://www.google.com/maps/dir/?api=1&destination=${location.lat},${location.lng}"
target="_blank" rel="noopener noreferrer">
${bdfgMapParams.strings.getDirections}
</a>
</p>
</div>
`;

this.infoWindow.setContent(content);
this.infoWindow.open(this.map, marker);
},

initMarkerClusterer: function() {
if (typeof MarkerClusterer !== 'undefined') {
this.markerCluster = new MarkerClusterer(this.map, this.markers, {
imagePath: bdfgMapParams.clusterImagePath
});
}
},

getMapStyles: function() {
return [
{
"featureType": "administrative",
"elementType": "geometry",
"stylers": [{"visibility": "simplified"}]
},
{
"featureType": "poi",
"stylers": [{"visibility": "simplified"}]
}
// Add more custom styles as needed
];
}
};

$(document).ready(() => BDFGMapFrontend.init());

})(jQuery);

assets/css/admin.css

相关文章: WooCommerce 快速下单插件

/**
* BDFG Map Locations Admin Styles
*
* @package BDFG_Map_Locations
* @author beiduofengou <[email protected]>
*/

.bdfg-admin-wrap {
margin: 20px;
max-width: 1200px;
}

.bdfg-admin-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 20px;
padding-bottom: 10px;
border-bottom: 1px solid #ccd0d4;
}

.bdfg-admin-title {
margin: 0;
padding: 0;
font-size: 23px;
font-weight: 400;
color: #1d2327;
}

#bdfg-admin-map {
width: 100%;
height: 400px;
margin-bottom: 20px;
border: 1px solid #ccd0d4;
border-radius: 4px;
}

.bdfg-form-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
gap: 20px;
margin-bottom: 20px;
}

.bdfg-form-group {
margin-bottom: 15px;
}

.bdfg-form-group label {
display: block;
margin-bottom: 5px;
font-weight: 600;
}

.bdfg-form-group input[type="text"],
.bdfg-form-group textarea {
width: 100%;
padding: 8px;
border: 1px solid #8c8f94;
border-radius: 4px;
}

.bdfg-locations-table {
width: 100%;
border-collapse: collapse;
margin-top: 20px;
background: #fff;
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
}

.bdfg-locations-table th {
text-align: left;
padding: 12px;
border-bottom: 2px solid #0073aa;
color: #1d2327;
font-weight: 600;
}

.bdfg-locations-table td {
padding: 12px;
border-bottom: 1px solid #f0f0f1;
vertical-align: middle;
}

.bdfg-locations-table tr:hover {
background-color: #f6f7f7;
}

.bdfg-action-buttons {
display: flex;
gap: 10px;
}

.bdfg-button {
display: inline-block;
padding: 8px 12px;
border-radius: 4px;
cursor: pointer;
text-decoration: none;
border: 1px solid transparent;
transition: all 0.2s ease;
}

.bdfg-button-primary {
background: #0073aa;
color: #fff;
border-color: #006291;
}

.bdfg-button-primary:hover {
background: #006291;
}

.bdfg-button-danger {
background: #dc3232;
color: #fff;
border-color: #cc2929;
}

.bdfg-button-danger:hover {
background: #cc2929;
}

.bdfg-settings-form {
max-width: 600px;
background: #fff;
padding: 20px;
border-radius: 4px;
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
}

.bdfg-api-key-field {
position: relative;
}

.bdfg-api-key-toggle {
position: absolute;
right: 10px;
top: 50%;
transform: translateY(-50%);
cursor: pointer;
color: #0073aa;
}

.bdfg-import-export {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 30px;
margin-top: 20px;
}

assets/css/frontend.css

/**
* BDFG Map Locations Frontend Styles
*
* @package BDFG_Map_Locations
* @author beiduofengou <[email protected]>
*/

.bdfg-map-container {
width: 100%;
height: 500px;
margin-bottom: 30px;
border-radius: 8px;
overflow: hidden;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
}

.bdfg-info-window {
padding: 5px;
max-width: 300px;
}

.bdfg-info-window h3 {
margin: 0 0 10px;
font-size: 16px;
color: #333;
}

.bdfg-info-window p {
margin: 0 0 8px;
font-size: 14px;
color: #666;
}

.bdfg-info-window a {
color: #0073aa;
text-decoration: none;
font-weight: 500;
}

.bdfg-info-window a:hover {
text-decoration: underline;
}

.bdfg-location-list {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
gap: 20px;
margin: 20px 0;
}

.bdfg-location-card {
background: #fff;
border-radius: 8px;
padding: 15px;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
transition: transform 0.2s ease;
}

.bdfg-location-card:hover {
transform: translateY(-2px);
}

.bdfg-location-card h3 {
margin: 0 0 10px;
color: #333;
}

.bdfg-location-card p {
margin: 0 0 8px;
color: #666;
font-size: 14px;
}

.bdfg-directions-button {
display: inline-block;
padding: 8px 16px;
background: #0073aa;
color: #fff;
text-decoration: none;
border-radius: 4px;
font-size: 14px;
transition: background 0.2s ease;
}

.bdfg-directions-button:hover {
background: #006291;
text-decoration: none;
color: #fff;
}

templates/admin.php

<?php
/**
* Admin page template for BDFG Map Locations
*
* @package BDFG_Map_Locations
* @author beiduofengou <[email protected]>
*/

if (!defined('ABSPATH')) {
exit('Direct access denied.');
}
?>

<div class="wrap bdfg-admin-wrap">
<div class="bdfg-admin-header">
<h1 class="bdfg-admin-title">
<?php _e('BDFG Map Locations', 'bdfg-map-locations'); ?>
</h1>
<a href="<?php echo admin_url('admin.php?page=bdfg-map-settings'); ?>" class="bdfg-button bdfg-button-primary">
<?php _e('Settings', 'bdfg-map-locations'); ?>
</a>
</div>

<div class="bdfg-admin-content">
<div id="bdfg-admin-map"></div>

<form id="bdfg-location-form" method="post">
<?php wp_nonce_field('bdfg_map_nonce'); ?>

<div class="bdfg-form-grid">
<div class="bdfg-form-group">
<label for="bdfg-name"><?php _e('Location Name', 'bdfg-map-locations'); ?></label>
<input type="text" id="bdfg-name" name="name" required>
</div>

<div class="bdfg-form-group">
<label for="bdfg-search-address"><?php _e('Search Address', 'bdfg-map-locations'); ?></label>
<input type="text" id="bdfg-search-address" name="address" required>
</div>

<div class="bdfg-form-group">
<label for="bdfg-category"><?php _e('Category', 'bdfg-map-locations'); ?></label>
<select id="bdfg-category" name="category">
<option value=""><?php _e('Select Category', 'bdfg-map-locations'); ?></option>
<?php
$categories = get_terms([
'taxonomy' => 'bdfg_location_category',
'hide_empty' => false,
]);
foreach ($categories as $category) {
echo sprintf(
'<option value="%s">%s</option>',
esc_attr($category->slug),
esc_html($category->name)
);
}
?>
</select>
</div>
</div>

<div class="bdfg-form-group">
<label for="bdfg-description"><?php _e('Description', 'bdfg-map-locations'); ?></label>
<textarea id="bdfg-description" name="description" rows="4"></textarea>
</div>

<input type="hidden" id="bdfg-lat" name="lat" required>
<input type="hidden" id="bdfg-lng" name="lng" required>

<button type="submit" class="bdfg-button bdfg-button-primary">
<?php _e('Add Location', 'bdfg-map-locations'); ?>
</button>
</form>

<table class="bdfg-locations-table">
<thead>
<tr>
<th><?php _e('Name', 'bdfg-map-locations'); ?></th>
<th><?php _e('Address', 'bdfg-map-locations'); ?></th>
<th><?php _e('Category', 'bdfg-map-locations'); ?></th>
<th><?php _e('Actions', 'bdfg-map-locations'); ?></th>
</tr>
</thead>
<tbody>
<?php foreach ($this->locations as $location): ?>
<tr>
<td><?php echo esc_html($location['name']); ?></td>
<td><?php echo esc_html($location['address']); ?></td>
<td><?php echo esc_html($location['category'] ?? ''); ?></td>
<td class="bdfg-action-buttons">
<button class="bdfg-button bdfg-button-danger bdfg-delete-location"
data-id="<?php echo esc_attr($location['id']); ?>">
<?php _e('Delete', 'bdfg-map-locations'); ?>
</button>
</td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
</div>
</div>

templates/settings.php

<?php
/**
* Settings page template for BDFG Map Locations
*
* @package BDFG_Map_Locations
* @author beiduofengou <[email protected]>
*/

if (!defined('ABSPATH')) {
exit('Direct access denied.');
}

$settings = get_option('bdfg_map_settings', []);
?>

<div class="wrap bdfg-admin-wrap">
<h1 class="bdfg-admin-title"><?php _e('BDFG Map Settings', 'bdfg-map-locations'); ?></h1>

<form method="post" action="options.php" class="bdfg-settings-form">
<?php settings_fields('bdfg_map_settings'); ?>

<div class="bdfg-form-group">
<label for="bdfg_map_api_key">
<?php _e('Google Maps API Key', 'bdfg-map-locations'); ?>
</label>
<div class="bdfg-api-key-field">
<input type="password"
id="bdfg_map_api_key"
name="bdfg_map_settings[api_key]"
value="<?php echo esc_attr($settings['api_key'] ?? ''); ?>"
class="regular-text">
<span class="bdfg-api-key-toggle dashicons dashicons-visibility"></span>
</div>
<p class="description">
<?php _e('Enter your Google Maps API key. Get one from the Google Cloud Console.', 'bdfg-map-locations'); ?>
</p>
</div>

<div class="bdfg-form-group">
<label for="bdfg_map_default_zoom">
<?php _e('Default Zoom Level', 'bdfg-map-locations'); ?>
</label>
<input type="number"
id="bdfg_map_default_zoom"
name="bdfg_map_settings[default_zoom]"
value="<?php echo intval($settings['default_zoom'] ?? 12); ?>"
min="1" max="20" class="small-text">
</div>

<div class="bdfg-form-group">
<label>
<input type="checkbox"
name="bdfg_map_settings[enable_clustering]"
value="1"
<?php checked(($settings['enable_clustering'] ?? false), true); ?>>
<?php _e('Enable Marker Clustering', 'bdfg-map-locations'); ?>
</label>
</div>

<div class="bdfg-form-group">
<label>
<input type="checkbox"
name="bdfg_map_settings[analytics_enabled]"
value="1"
<?php checked(($settings['analytics_enabled'] ?? false), true); ?>>
<?php _e('Enable Analytics', 'bdfg-map-locations'); ?>
</label>
</div>

<?php submit_button(); ?>
</form>
</div>

templates/map.php

<?php
/**
* Frontend map template for BDFG Map Locations
*
* @package BDFG_Map_Locations
* @author beiduofengou <[email protected]>
*/

if (!defined('ABSPATH')) {
exit('Direct access denied.');
}
?>

<div class="bdfg-map-container"
id="bdfg-map-<?php echo esc_attr(uniqid()); ?>"
data-zoom="<?php echo esc_attr($atts['zoom']); ?>"
style="height: <?php echo esc_attr($atts['height']); ?>;">
</div>

<?php echo $schema_markup; // Schema markup for SEO ?>

<?php if (!empty($atts['show_list'])): ?>
<div class="bdfg-location-list">
<?php foreach ($locations as $location): ?>
<div class="bdfg-location-card">
<h3><?php echo esc_html($location['name']); ?></h3>
<p><?php echo esc_html($location['address']); ?></p>
<?php if (!empty($location['description'])): ?>
<p><?php echo wp_kses_post($location['description']); ?></p>
<?php endif; ?>
<a href="https://www.google.com/maps/dir/?api=1&destination=<?php
echo esc_attr($location['lat']);
?>,<?php
echo esc_attr($location['lng']);
?>"
class="bdfg-directions-button"
target="_blank"
rel="noopener noreferrer">
<?php _e('Get Directions', 'bdfg-map-locations'); ?>
</a>
</div>
<?php endforeach; ?>
</div>
<?php endif; ?>

Leave a Comment