Current File : /home/bqrcodec/contact.rohto.com.vn/wp-content/plugins/device-location-shortcode//backup.txt
<?php
/**
* Plugin Name: Device Location Shortcode (with Email Sender)
* Plugin URI: https://me.proid.vn/ (Optional)
* Description: Adds a shortcode [device_location] to display the user's current location (requires HTTPS and user permission) as a Google Maps link and iframe, with a button to email the location to the post author. Uses Vietnamese language strings.
* Version: 1.2.0
* Author: Your Name / ProID Developer
* Author URI: https://me.proid.vn/ (Optional)
* License: GPL v2 or later
* License URI: https://www.gnu.org/licenses/gpl-2.0.html
* Text Domain: device-location-shortcode
* Requires at least: 5.2
* Requires PHP: 7.2
*/
// If this file is called directly, abort.
defined( 'ABSPATH' ) or die( 'Cannot access this file directly!' );
if ( ! function_exists( 'proid_display_device_location_shortcode' ) ) {
/**
* Shortcode handler function to display current device location and send button.
* Outputs HTML, CSS, and JavaScript for geolocation, display, and AJAX email sending.
* Usage: [device_location]
*/
function proid_display_device_location_shortcode() {
// Get current post ID - crucial for fetching the email meta
$current_post_id = get_the_ID();
if ( ! $current_post_id || 'my-pro-id' !== get_post_type( $current_post_id ) ) {
// Only proceed if we are on a my-pro-id post type single view
// You might want to adjust this check depending on where the shortcode can be used
// return '<p style="color:orange;">Shortcode chỉ hoạt động trên trang chi tiết thẻ Pro ID.</p>';
// Allow it anywhere for now, but fetch email only if on my-pro-id CPT
$recipient_email = ''; // Set empty if not on the correct post type
} else {
// Fetch the recipient email from the post meta
$recipient_email = get_post_meta( $current_post_id, 'email', true );
}
// Validate the fetched email address
$is_valid_email = is_email( $recipient_email );
// Unique IDs for the HTML elements
$container_id = 'device-location-container-' . uniqid();
$spinner_id = 'location-spinner-' . uniqid();
$text_id = 'location-text-' . uniqid(); // For the link text/error message
$iframe_container_id = 'location-iframe-container-' . uniqid(); // For the map iframe
$send_button_id = 'send-location-button-' . uniqid(); // For the send button
$status_message_id = 'send-status-message-' . uniqid(); // For status messages
// Create a nonce for the AJAX request
$ajax_nonce = wp_create_nonce( 'proid_send_location_nonce' );
// Start output buffering
ob_start();
?>
<style>
/* Simple CSS Loader */
.proid-location-loader {
border: 4px solid #f3f3f3; /* Light grey */
border-top: 4px solid #3498db; /* Blue */
border-radius: 50%;
width: 16px;
height: 16px;
animation: proid-spin-<?php echo esc_attr($container_id); ?> 1s linear infinite; /* Unique animation name */
display: inline-block;
vertical-align: middle;
margin-right: 8px;
}
/* Unique keyframes per instance */
@keyframes proid-spin-<?php echo esc_attr($container_id); ?> {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
/* Style for the location link container */
#<?php echo esc_attr( $container_id ); ?> {
display: inline-flex;
align-items: center;
margin-bottom: 10px; /* Reduced margin */
}
/* Style for the location link */
#<?php echo esc_attr( $container_id ); ?> a {
text-decoration: none;
color: #0073aa; /* Standard WordPress link color */
}
#<?php echo esc_attr( $container_id ); ?> a:hover {
text-decoration: underline;
}
/* Style for the iframe container */
#<?php echo esc_attr( $iframe_container_id ); ?> {
margin-top: 10px; /* Reduced margin */
margin-bottom: 15px; /* Add margin below iframe */
width: 100%;
}
#<?php echo esc_attr( $iframe_container_id ); ?> iframe {
max-width: 100%;
border: 1px solid #ccc; /* Added a light border */
}
/* Style for the send button */
#<?php echo esc_attr( $send_button_id ); ?> {
padding: 8px 15px;
background-color: #0073aa;
color: white;
border: none;
border-radius: 4px;
cursor: pointer;
font-size: 14px;
display: none; /* Initially hidden */
margin-top: 5px; /* Space between iframe and button */
}
#<?php echo esc_attr( $send_button_id ); ?>:hover {
background-color: #005177;
}
#<?php echo esc_attr( $send_button_id ); ?>:disabled {
background-color: #a0a5aa;
cursor: not-allowed;
}
/* Style for status messages */
#<?php echo esc_attr( $status_message_id ); ?> {
margin-top: 10px;
font-size: 13px;
/* color: green; Initially hidden or neutral color */
}
#<?php echo esc_attr( $status_message_id ); ?>.proid-status-error {
color: red;
}
#<?php echo esc_attr( $status_message_id ); ?>.proid-status-success {
color: green;
}
#<?php echo esc_attr( $status_message_id ); ?>.proid-status-sending {
color: #666; /* Dark gray */
}
</style>
<div id="<?php echo esc_attr( $container_id ); ?>">
<div id="<?php echo esc_attr( $spinner_id ); ?>" class="proid-location-loader"></div>
<span id="<?php echo esc_attr( $text_id ); ?>">đang định vị thiết bị...</span>
</div>
<div id="<?php echo esc_attr( $iframe_container_id ); ?>"></div>
<?php // Only show the button if the recipient email is valid ?>
<?php if ( $is_valid_email ) : ?>
<button id="<?php echo esc_attr( $send_button_id ); ?>">Gửi định vị</button>
<div id="<?php echo esc_attr( $status_message_id ); ?>"></div>
<?php else: ?>
<?php if ( $current_post_id && 'my-pro-id' === get_post_type( $current_post_id ) ): ?>
<p style="color:orange; font-size:13px;">Không thể gửi định vị: Email người nhận không hợp lệ hoặc chưa được cấu hình.</p>
<?php endif; ?>
<?php endif; ?>
<script type="text/javascript">
(function() {
// Use unique IDs passed from PHP
const containerId = '<?php echo esc_js( $container_id ); ?>';
const spinnerId = '<?php echo esc_js( $spinner_id ); ?>';
const textId = '<?php echo esc_js( $text_id ); ?>';
const iframeContainerId = '<?php echo esc_js( $iframe_container_id ); ?>';
const sendButtonId = '<?php echo esc_js( $send_button_id ); ?>';
const statusMessageId = '<?php echo esc_js( $status_message_id ); ?>';
const postId = <?php echo $current_post_id ? intval( $current_post_id ) : 'null'; ?>; // Pass Post ID
const ajaxUrl = '<?php echo esc_js( admin_url( 'admin-ajax.php' ) ); ?>';
const nonce = '<?php echo esc_js( $ajax_nonce ); ?>';
const isValidEmail = <?php echo $is_valid_email ? 'true' : 'false'; ?>;
const spinnerElement = document.getElementById(spinnerId);
const textElement = document.getElementById(textId);
const iframeContainer = document.getElementById(iframeContainerId);
const sendButton = document.getElementById(sendButtonId); // Get the button element
const statusMessage = document.getElementById(statusMessageId); // Get the status message element
// Variables to store location data
let currentLatitude = null;
let currentLongitude = null;
// Check if required elements exist
if (!spinnerElement || !textElement || !iframeContainer ) {
console.error('Device Location Shortcode: Could not find required HTML elements for display.');
if (textElement) textElement.textContent = 'Lỗi: Không thể hiển thị định vị.';
if (spinnerElement) spinnerElement.style.display = 'none';
return;
}
// Check if button-related elements exist (only if email is valid)
if (isValidEmail && (!sendButton || !statusMessage)) {
console.error('Device Location Shortcode: Could not find required HTML elements for sending email.');
// Optionally display an error, though the button itself won't render if invalid.
return;
}
// Geolocation requires HTTPS
if (window.location.protocol !== 'https:') {
spinnerElement.style.display = 'none';
textElement.textContent = 'Lỗi: Định vị yêu cầu kết nối HTTPS an toàn.';
textElement.style.color = 'orange';
return;
}
if (!navigator.geolocation) {
spinnerElement.style.display = 'none';
textElement.textContent = 'Trình duyệt không hỗ trợ định vị.';
return;
}
function success(position) {
currentLatitude = position.coords.latitude; // Store globally in scope
currentLongitude = position.coords.longitude; // Store globally in scope
// Google Maps link (e.g., for sharing)
const mapsLinkUrl = `https://www.google.com/maps?q=${currentLatitude},${currentLongitude}`;
// URL for the iframe embed
const mapsEmbedUrl = `https://maps.google.com/maps?q=${currentLatitude},${currentLongitude}&output=embed&z=16&hl=vi`; // hl=vi for Vietnamese interface
// --- Create the Link ---
const linkElement = document.createElement('a');
linkElement.href = mapsLinkUrl;
linkElement.textContent = `Xem vị trí hiện tại (Lat: ${currentLatitude.toFixed(6)}, Long: ${currentLongitude.toFixed(6)})`;
linkElement.target = '_blank';
linkElement.rel = 'noopener noreferrer';
if (spinnerElement) spinnerElement.style.display = 'none';
if (textElement) textElement.replaceWith(linkElement);
// --- Create and Append the Iframe ---
const iframeElement = document.createElement('iframe');
iframeElement.src = mapsEmbedUrl;
iframeElement.width = '100%';
iframeElement.height = '250';
iframeElement.style.border = '0'; // Ensure no border if CSS fails
iframeElement.loading = 'lazy';
iframeElement.allowfullscreen = '';
iframeElement.referrerpolicy = 'no-referrer-when-downgrade';
if (iframeContainer) {
iframeContainer.innerHTML = ''; // Clear previous content if any
iframeContainer.appendChild(iframeElement);
}
// --- Show and Enable the Send Button ---
if (sendButton && isValidEmail) {
sendButton.style.display = 'block'; // Make button visible
}
}
function error(err) {
if (spinnerElement) spinnerElement.style.display = 'none';
let errorMessage = 'Không thể lấy vị trí. ';
switch(err.code) {
case err.PERMISSION_DENIED:
errorMessage += "Bạn đã từ chối yêu cầu Định vị.";
break;
case err.POSITION_UNAVAILABLE:
errorMessage += "Thông tin vị trí không có sẵn.";
break;
case err.TIMEOUT:
errorMessage += "Yêu cầu định vị đã hết thời gian chờ.";
break;
case err.UNKNOWN_ERROR:
default:
errorMessage += "Một lỗi không xác định đã xảy ra.";
break;
}
errorMessage += " Vui lòng kiểm tra cài đặt trình duyệt và cấp quyền truy cập vị trí.";
if (textElement) {
textElement.textContent = errorMessage;
textElement.style.color = 'red';
}
// Ensure button remains hidden on error
if (sendButton) {
sendButton.style.display = 'none';
}
}
// --- Event Listener for the Send Button ---
if (sendButton && isValidEmail) {
sendButton.addEventListener('click', function() {
if (currentLatitude === null || currentLongitude === null) {
statusMessage.textContent = 'Lỗi: Vị trí chưa được xác định.';
statusMessage.className = 'proid-status-error';
return;
}
if (!postId) {
statusMessage.textContent = 'Lỗi: Không xác định được ID thẻ.';
statusMessage.className = 'proid-status-error';
return;
}
// Disable button and show sending status
sendButton.disabled = true;
statusMessage.textContent = 'Đang gửi...';
statusMessage.className = 'proid-status-sending'; // Use class for styling
const formData = new FormData();
formData.append('action', 'proid_send_location');
formData.append('latitude', currentLatitude);
formData.append('longitude', currentLongitude);
formData.append('post_id', postId);
formData.append('security', nonce); // Send the nonce
fetch(ajaxUrl, {
method: 'POST',
body: formData
})
.then(response => response.json())
.then(data => {
if (data.success) {
statusMessage.textContent = 'Đã gửi định vị thành công!';
statusMessage.className = 'proid-status-success';
// Keep button disabled after success to prevent resending? Or re-enable?
// sendButton.disabled = false; // Re-enable if needed
} else {
statusMessage.textContent = 'Gửi thất bại: ' + (data.data.message || 'Lỗi không xác định.');
statusMessage.className = 'proid-status-error';
sendButton.disabled = false; // Re-enable on failure
}
})
.catch(fetchError => {
console.error('Error sending location:', fetchError);
statusMessage.textContent = 'Gửi thất bại: Lỗi kết nối.';
statusMessage.className = 'proid-status-error';
sendButton.disabled = false; // Re-enable on fetch failure
});
});
}
// Request location with options
const options = {
enableHighAccuracy: true,
timeout: 15000,
maximumAge: 0
};
navigator.geolocation.getCurrentPosition(success, error, options);
})(); // End IIFE
</script>
<?php
// Return the buffered content
return ob_get_clean();
}
} // End function_exists check
// Register the shortcode
if ( ! function_exists('proid_register_device_location_shortcode') ) {
function proid_register_device_location_shortcode() {
if ( ! shortcode_exists( 'device_location' ) ) {
add_shortcode( 'device_location', 'proid_display_device_location_shortcode' );
} else {
error_log('WARNING: Shortcode [device_location] already registered. Device Location Shortcode plugin will not overwrite it.');
}
}
add_action( 'init', 'proid_register_device_location_shortcode' );
}
// --- AJAX Handler for Sending Email ---
if ( ! function_exists( 'proid_send_location_email_ajax_handler' ) ) {
/**
* Handles the AJAX request to send the location email.
*/
function proid_send_location_email_ajax_handler() {
// 1. Verify Nonce
check_ajax_referer( 'proid_send_location_nonce', 'security' );
// 2. Get and Sanitize Data
$post_id = isset( $_POST['post_id'] ) ? intval( $_POST['post_id'] ) : 0;
$latitude = isset( $_POST['latitude'] ) ? sanitize_text_field( wp_unslash( $_POST['latitude'] ) ) : null;
$longitude = isset( $_POST['longitude'] ) ? sanitize_text_field( wp_unslash( $_POST['longitude'] ) ) : null;
// 3. Validate Data
if ( ! $post_id || $latitude === null || $longitude === null || ! is_numeric( $latitude ) || ! is_numeric( $longitude ) ) {
wp_send_json_error( array( 'message' => 'Dữ liệu không hợp lệ.' ), 400 ); // Bad Request
}
// 4. Get Recipient Email
$recipient_email = get_post_meta( $post_id, 'email', true );
if ( ! is_email( $recipient_email ) ) {
wp_send_json_error( array( 'message' => 'Email người nhận không hợp lệ hoặc không tìm thấy.' ), 400 );
}
// 5. Get Post Title (Optional, for email context)
$post_title = get_the_title($post_id); // e.g., "proid4de88399"
$post_link = get_permalink($post_id);
// 6. Prepare Email
$subject = "📍 Thông báo định vị từ khách truy cập thẻ: " . $post_title;
$google_maps_link = sprintf( 'https://www.google.com/maps?q=%s,%s', $latitude, $longitude );
// Get site name for branding
$site_name = get_bloginfo('name');
$site_url = home_url();
// Construct email body (HTML for better formatting)
$body = "<p>Xin chào,</p>";
$body .= "<p>Một khách truy cập đã gửi vị trí của họ từ thẻ Pro ID của bạn:</p>";
$body .= "<ul>";
$body .= "<li><strong>Thẻ Pro ID:</strong> " . esc_html($post_title) . " (<a href='" . esc_url($post_link) . "'>Xem thẻ</a>)</li>";
$body .= "<li><strong>Vĩ độ (Latitude):</strong> " . esc_html($latitude) . "</li>";
$body .= "<li><strong>Kinh độ (Longitude):</strong> " . esc_html($longitude) . "</li>";
$body .= "<li><strong>Xem trên Google Maps:</strong> <a href='" . esc_url($google_maps_link) . "' target='_blank'>" . esc_url($google_maps_link) . "</a></li>";
$body .= "</ul>";
$body .= "<p>Thời gian gửi: " . current_time( 'mysql' ) . " (UTC)</p>"; // Optional: Add timestamp
$body .= "<hr>";
$body .= "<p><em>Email này được gửi tự động từ hệ thống <a href='" . esc_url($site_url) . "'>" . esc_html($site_name) . "</a>.</em></p>";
// Set Headers
$headers = array(
'Content-Type: text/html; charset=UTF-8',
'From: ' . $site_name . ' <noreply@' . preg_replace( '#^www\.#', '', strtolower( $_SERVER['SERVER_NAME'] ) ) . '>' // Use a noreply address from your domain
// Consider setting Reply-To if needed, but noreply is often standard
);
// 7. Send Email
$sent = wp_mail( $recipient_email, $subject, $body, $headers );
// 8. Send JSON Response
if ( $sent ) {
wp_send_json_success( array( 'message' => 'Email đã được gửi thành công.' ) );
} else {
// Log the error for debugging
global $ts_mail_errors;
global $phpmailer;
if (!isset($ts_mail_errors)) $ts_mail_errors = array();
if (isset($phpmailer)) {
$ts_mail_errors[] = $phpmailer->ErrorInfo;
}
error_log('WP Mail Error: ' . print_r($ts_mail_errors, true));
wp_send_json_error( array( 'message' => 'Không thể gửi email. Vui lòng thử lại sau hoặc liên hệ quản trị viên.' ), 500 ); // Internal Server Error
}
}
// Hook the AJAX handler for both logged-in and non-logged-in users
add_action( 'wp_ajax_proid_send_location', 'proid_send_location_email_ajax_handler' );
add_action( 'wp_ajax_nopriv_proid_send_location', 'proid_send_location_email_ajax_handler' );
} // End function_exists check for AJAX handler
?>