File manager - Edit - /home/proidvn/me.proid.vn/wp-content/plugins/device-location-shortcode/backup.txt
Back
<?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 ?>
| ver. 1.4 |
Github
|
.
| PHP 8.0.30 | Generation time: 0.14 |
proxy
|
phpinfo
|
Settings