Changeset 3765041
- Timestamp:
- 10/25/2026 06:01:43 PM (29 hours ago)
- Location:
- nexoy-order-tracker-for-woocommerce
- Files:
-
- 14 edited
- 1 copied
-
tags/1.1.0 (copied) (copied from nexoy-order-tracker-for-woocommerce/trunk )
-
tags/1.1.0/assets/js/script.js (modified) (6 diffs)
-
tags/1.1.0/includes/admin-settings.php (modified) (6 diffs)
-
tags/1.1.0/includes/ajax-handler.php (modified) (3 diffs)
-
tags/1.1.0/includes/frontend-shortcode.php (modified) (3 diffs)
-
tags/1.1.0/includes/order-meta.php (modified) (3 diffs)
-
tags/1.1.0/nexoy-order-tracker.php (modified) (4 diffs)
-
tags/1.1.0/readme.txt (modified) (2 diffs)
-
trunk/assets/js/script.js (modified) (6 diffs)
-
trunk/includes/admin-settings.php (modified) (6 diffs)
-
trunk/includes/ajax-handler.php (modified) (3 diffs)
-
trunk/includes/frontend-shortcode.php (modified) (3 diffs)
-
trunk/includes/order-meta.php (modified) (3 diffs)
-
trunk/nexoy-order-tracker.php (modified) (4 diffs)
-
trunk/readme.txt (modified) (2 diffs)
Legend:
- Unmodified
- Added
- Removed
-
nexoy-order-tracker-for-woocommerce/tags/1.1.0/assets/js/script.js
r3467879 r3765041 1 1 jQuery(document).ready(function($) { 2 const i18n = nexoy_ot_params.i18n || {}; 3 4 // Escape any text before it is placed into HTML 5 function esc(value) { 6 return $('<div>').text(value == null ? '' : String(value)).html(); 7 } 8 2 9 // Tab Switching Logic 3 10 const $tabs = $('input[name="tracker_type"]'); … … 25 32 $form.on('submit', function(e) { 26 33 e.preventDefault(); 27 28 $resultContainer.html('<p style="text-align:center; padding:20px; font-weight:500;"> Fetching order details...</p>').fadeIn();34 35 $resultContainer.html('<p style="text-align:center; padding:20px; font-weight:500;">' + esc(i18n.fetching) + '</p>').fadeIn(); 29 36 30 37 const trackerType = $('input[name="tracker_type"]:checked').val(); … … 43 50 renderNexoyTimeline(response.data); 44 51 } else { 45 $resultContainer.html(`<p style="color:#ff4d4d; text-align:center; padding:20px; font-weight:500;">${response.data.message}</p>`); 52 const message = response.data && response.data.message ? response.data.message : i18n.conn_error; 53 $resultContainer.html('<p style="color:#ff4d4d; text-align:center; padding:20px; font-weight:500;">' + esc(message) + '</p>'); 46 54 } 47 55 }).fail(function() { 48 $resultContainer.html('<p style="color:#ff4d4d; text-align:center; padding:20px;"> Connection error. Please try again.</p>');56 $resultContainer.html('<p style="color:#ff4d4d; text-align:center; padding:20px;">' + esc(i18n.conn_error) + '</p>'); 49 57 }); 50 58 }); … … 53 61 function renderNexoyTimeline(data) { 54 62 let html = '<div class="nexoy-ot-timeline-container">'; 55 63 56 64 if (data.courier && data.courier.name) { 65 const url = data.courier.url && /^https?:\/\//i.test(data.courier.url) ? data.courier.url : ''; 57 66 html += ` 58 67 <div class="nexoy-ot-courier-card"> 59 68 <div class="nexoy-ot-courier-info"> 60 <span class="nexoy-ot-label"> Carrier:</span>61 <span class="nexoy-ot-val">${ data.courier.name}</span>69 <span class="nexoy-ot-label">${esc(i18n.carrier)}</span> 70 <span class="nexoy-ot-val">${esc(data.courier.name)}</span> 62 71 </div> 63 ${ data.courier.url ? `<a href="${data.courier.url}" target="_blank" class="nexoy-ot-track-btn">Track Order</a>` : ''}72 ${url ? `<a href="${esc(url)}" target="_blank" rel="noopener noreferrer" class="nexoy-ot-track-btn">${esc(i18n.track_order)}</a>` : ''} 64 73 </div> 65 74 `; … … 68 77 if (data.history && data.history.length > 0) { 69 78 html += '<div class="nexoy-ot-v-timeline">'; 70 const history = data.history.reverse(); 71 history.forEach((item, index) => { 72 const date = new Date(item.timestamp * 1000); 73 const timeStr = date.toLocaleDateString('en-US', { month: 'short', day: 'numeric' }) + ' ' + 74 date.toLocaleTimeString('en-US', { hour: '2-digit', minute: '2-digit' }); 75 const statusTitle = item.new_status.replace(/-/g, ' ').toUpperCase(); 79 data.history.forEach((item, index) => { 80 const message = (i18n.updated_to || '%s').replace('%s', item.status); 76 81 html += ` 77 82 <div class="nexoy-ot-t-item" style="animation-delay: ${index * 0.1}s"> 78 <div class="nexoy-ot-t-meta">${ timeStr}</div>83 <div class="nexoy-ot-t-meta">${esc(item.date)}</div> 79 84 <div class="nexoy-ot-t-content"> 80 85 <div class="nexoy-ot-t-marker"></div> 81 86 <div class="nexoy-ot-t-text"> 82 <h5>${ statusTitle}</h5>83 <p> Your order has been updated to ${item.new_status}.</p>87 <h5>${esc(item.status)}</h5> 88 <p>${esc(message)}</p> 84 89 </div> 85 90 </div> … … 88 93 }); 89 94 html += '</div>'; 95 } else if (data.order_status) { 96 html += `<p class="nexoy-ot-current-status">${esc(i18n.current)} <strong>${esc(data.order_status)}</strong></p>`; 90 97 } 91 98 html += '</div>'; -
nexoy-order-tracker-for-woocommerce/tags/1.1.0/includes/admin-settings.php
r3467879 r3765041 8 8 add_submenu_page( 9 9 'woocommerce', 10 'Nexoy Order Tracker',11 'Nexoy Order Tracker',10 __( 'Nexoy Order Tracker', 'nexoy-order-tracker-for-woocommerce' ), 11 __( 'Nexoy Order Tracker', 'nexoy-order-tracker-for-woocommerce' ), 12 12 'manage_woocommerce', 13 13 'nexoy-order-tracker', … … 38 38 <div class="nexoy-header"> 39 39 <div class="nexoy-brand"><h1>Nexoy <span>Order Tracker</span></h1></div> 40 <div class="nexoy-version"><span style="background: #e2e8f0; padding: 5px 12px; border-radius: 20px; font-size: 12px; font-weight: 700;">v 1.0.0</span></div>40 <div class="nexoy-version"><span style="background: #e2e8f0; padding: 5px 12px; border-radius: 20px; font-size: 12px; font-weight: 700;">v <?php echo esc_html( NEXOY_OT_VERSION ); ?></span></div> 41 41 </div> 42 42 <div class="nexoy-grid"> … … 45 45 <?php settings_fields( 'nexoy_ot_settings_group' ); ?> 46 46 <div class="nexoy-card"> 47 <div class="nexoy-card-header"><h3> 🎨 Visual Customization</h3></div>47 <div class="nexoy-card-header"><h3><span class="dashicons dashicons-admin-appearance"></span> <?php esc_html_e( 'Visual Customization', 'nexoy-order-tracker-for-woocommerce' ); ?></h3></div> 48 48 <div class="nexoy-card-body"> 49 <p style="margin-top:0; color: #64748b;"> Choose the base theme for your tracking interface.</p>49 <p style="margin-top:0; color: #64748b;"><?php esc_html_e( 'Choose the base theme for your tracking interface.', 'nexoy-order-tracker-for-woocommerce' ); ?></p> 50 50 <?php do_settings_sections( 'nexoy-order-tracker' ); ?> 51 51 </div> 52 52 </div> 53 53 <div class="nexoy-card"> 54 <div class="nexoy-card-header"><h3> 🚀 Ready to Launch?</h3></div>54 <div class="nexoy-card-header"><h3><span class="dashicons dashicons-shortcode"></span> <?php esc_html_e( 'Ready to Launch?', 'nexoy-order-tracker-for-woocommerce' ); ?></h3></div> 55 55 <div class="nexoy-card-body"> 56 56 <div class="nexoy-guide-box"> 57 <strong style="color: #0c4a6e;"> Integration Shortcode</strong>58 <p style="font-size: 13px; color: #334155; margin: 5px 0 15px;"> Copy and paste this shortcode to any page where you want the tracker to appear.</p>57 <strong style="color: #0c4a6e;"><?php esc_html_e( 'Integration Shortcode', 'nexoy-order-tracker-for-woocommerce' ); ?></strong> 58 <p style="font-size: 13px; color: #334155; margin: 5px 0 15px;"><?php esc_html_e( 'Copy and paste this shortcode to any page where you want the tracker to appear.', 'nexoy-order-tracker-for-woocommerce' ); ?></p> 59 59 <div class="nexoy-shortcode-display"><span>[nexoy_order_tracker]</span></div> 60 60 </div> 61 <?php submit_button( 'Save All Changes', 'nexoy-btn-save' ); ?>61 <?php submit_button( __( 'Save All Changes', 'nexoy-order-tracker-for-woocommerce' ), 'nexoy-btn-save' ); ?> 62 62 </div> 63 63 </div> … … 66 66 <div class="nexoy-sidebar"> 67 67 <div class="nexoy-card"> 68 <div class="nexoy-card-header"><h3> ℹ️ Plugin Info</h3></div>68 <div class="nexoy-card-header"><h3><span class="dashicons dashicons-info-outline"></span> <?php esc_html_e( 'Plugin Info', 'nexoy-order-tracker-for-woocommerce' ); ?></h3></div> 69 69 <div class="nexoy-card-body"> 70 <p style="font-size: 13px; line-height: 1.6; color: #64748b;">< strong>Nexoy Order Tracker</strong> is built for high-performance WooCommerce stores. If you need help, contact our support team at <strong>thenexoy.com</strong>.</p>70 <p style="font-size: 13px; line-height: 1.6; color: #64748b;"><?php echo wp_kses( __( '<strong>Nexoy Order Tracker</strong> is built for high-performance WooCommerce stores. If you need help, contact our support team at <strong>thenexoy.com</strong>.', 'nexoy-order-tracker-for-woocommerce' ), array( 'strong' => array() ) ); ?></p> 71 71 </div> 72 72 </div> … … 80 80 add_action( 'admin_init', 'nexoy_ot_register_settings' ); 81 81 function nexoy_ot_register_settings() { 82 register_setting( 'nexoy_ot_settings_group', 'nexoy_ot_theme_preference', 'nexoy_ot_sanitize_theme_choice');83 add_settings_section( 'nexoy_ot_main_section', 'Display Settings', '__return_empty_string', 'nexoy-order-tracker' );84 add_settings_field( 'nexoy_ot_theme_preference', 'Tracker Theme', 'nexoy_ot_theme_radio_callback', 'nexoy-order-tracker', 'nexoy_ot_main_section' );82 register_setting( 'nexoy_ot_settings_group', 'nexoy_ot_theme_preference', array( 'type' => 'string', 'sanitize_callback' => 'nexoy_ot_sanitize_theme_choice', 'default' => 'light' ) ); 83 add_settings_section( 'nexoy_ot_main_section', __( 'Display Settings', 'nexoy-order-tracker-for-woocommerce' ), '__return_empty_string', 'nexoy-order-tracker' ); 84 add_settings_field( 'nexoy_ot_theme_preference', __( 'Tracker Theme', 'nexoy-order-tracker-for-woocommerce' ), 'nexoy_ot_theme_radio_callback', 'nexoy-order-tracker', 'nexoy_ot_main_section' ); 85 85 } 86 86 … … 92 92 $theme = get_option( 'nexoy_ot_theme_preference', 'light' ); 93 93 ?> 94 <label><input type="radio" name="nexoy_ot_theme_preference" value="light" <?php checked( $theme, 'light' ); ?> /> Light Theme (Default)</label><br><br>95 <label><input type="radio" name="nexoy_ot_theme_preference" value="dark" <?php checked( $theme, 'dark' ); ?> /> Dark Theme (Premium Look)</label>94 <label><input type="radio" name="nexoy_ot_theme_preference" value="light" <?php checked( $theme, 'light' ); ?> /> <?php esc_html_e( 'Light Theme (Default)', 'nexoy-order-tracker-for-woocommerce' ); ?></label><br><br> 95 <label><input type="radio" name="nexoy_ot_theme_preference" value="dark" <?php checked( $theme, 'dark' ); ?> /> <?php esc_html_e( 'Dark Theme (Premium Look)', 'nexoy-order-tracker-for-woocommerce' ); ?></label> 96 96 <?php 97 97 } -
nexoy-order-tracker-for-woocommerce/tags/1.1.0/includes/ajax-handler.php
r3467879 r3765041 6 6 add_action( 'wp_ajax_nopriv_nexoy_ot_search_order', 'nexoy_ot_search_order_handler' ); 7 7 8 /** 9 * Rate limit: max failed lookups per visitor IP within the window. 10 */ 11 define( 'NEXOY_OT_MAX_ATTEMPTS', 10 ); 12 define( 'NEXOY_OT_ATTEMPT_WINDOW', 15 * MINUTE_IN_SECONDS ); 13 14 function nexoy_ot_attempts_key() { 15 $ip = isset( $_SERVER['REMOTE_ADDR'] ) ? sanitize_text_field( wp_unslash( $_SERVER['REMOTE_ADDR'] ) ) : 'unknown'; 16 return 'nexoy_ot_fail_' . md5( $ip ); 17 } 18 19 function nexoy_ot_register_failed_attempt() { 20 $key = nexoy_ot_attempts_key(); 21 $attempts = (int) get_transient( $key ); 22 set_transient( $key, $attempts + 1, NEXOY_OT_ATTEMPT_WINDOW ); 23 } 24 8 25 function nexoy_ot_search_order_handler() { 9 26 check_ajax_referer( 'nexoy_ot_search_nonce', 'security' ); 27 28 if ( (int) get_transient( nexoy_ot_attempts_key() ) >= NEXOY_OT_MAX_ATTEMPTS ) { 29 wp_send_json_error( array( 'message' => __( 'Too many attempts. Please try again in a few minutes.', 'nexoy-order-tracker-for-woocommerce' ) ) ); 30 } 10 31 11 32 $order_id = isset( $_POST['order_id'] ) ? absint( wp_unslash( $_POST['order_id'] ) ) : 0; … … 14 35 $billing_phone = isset( $_POST['billing_phone'] ) ? sanitize_text_field( wp_unslash( $_POST['billing_phone'] ) ) : ''; 15 36 16 if ( ! $order_id ) wp_send_json_error( array( 'message' => 'Please provide a valid Order ID.' ) ); 37 if ( ! $order_id ) wp_send_json_error( array( 'message' => __( 'Please provide a valid Order ID.', 'nexoy-order-tracker-for-woocommerce' ) ) ); 38 39 // Same message for "not found" and "no match", so order IDs cannot be probed 40 $no_match = __( 'Order ID and credentials do not match.', 'nexoy-order-tracker-for-woocommerce' ); 17 41 18 42 $order = wc_get_order( $order_id ); 19 if ( ! $order || $order->get_id() !== $order_id ) wp_send_json_error( array( 'message' => 'Order not found.' ) ); 43 if ( ! $order || $order->get_id() !== $order_id ) { 44 nexoy_ot_register_failed_attempt(); 45 wp_send_json_error( array( 'message' => $no_match ) ); 46 } 20 47 21 48 $is_valid_user = false; 22 49 if ( 'email' === $tracker_type ) { 23 if ( strtolower( $order->get_billing_email() ) === strtolower( $billing_email ) ) $is_valid_user = true;50 if ( ! empty( $billing_email ) && strtolower( $order->get_billing_email() ) === strtolower( $billing_email ) ) $is_valid_user = true; 24 51 } else { 25 $saved_phone = preg_replace( '/[^0-9]/', '', $order->get_billing_phone() );52 $saved_phone = preg_replace( '/[^0-9]/', '', $order->get_billing_phone() ); 26 53 $provided_phone = preg_replace( '/[^0-9]/', '', $billing_phone ); 27 54 if ( ! empty( $provided_phone ) && $saved_phone === $provided_phone ) $is_valid_user = true; 28 55 } 29 56 30 if ( ! $is_valid_user ) wp_send_json_error( array( 'message' => 'Order ID and credentials do not match.' ) ); 57 if ( ! $is_valid_user ) { 58 nexoy_ot_register_failed_attempt(); 59 wp_send_json_error( array( 'message' => $no_match ) ); 60 } 31 61 32 62 $history = $order->get_meta( '_nexoy_ot_activity_log', true ); … … 34 64 $tracking_url = $order->get_meta( '_nexoy_ot_tracking_url', true ); 35 65 66 // Send ready-to-display labels and dates (site timezone, site date format) 67 $timeline = array(); 68 if ( is_array( $history ) ) { 69 foreach ( $history as $log ) { 70 if ( ! isset( $log['event_type'], $log['new_status'], $log['timestamp'] ) || 'status_change' !== $log['event_type'] ) { 71 continue; 72 } 73 $timeline[] = array( 74 'status' => wc_get_order_status_name( $log['new_status'] ), 75 'date' => date_i18n( get_option( 'date_format' ) . ' ' . get_option( 'time_format' ), (int) $log['timestamp'] ), 76 ); 77 } 78 } 79 36 80 $response_data = array( 37 81 'order_status' => wc_get_order_status_name( $order->get_status() ), 38 'history' => $history, 39 'courier' => array( 'name' => $courier_name, 'url' => $tracking_url ) 82 'history' => array_reverse( $timeline ), 83 'courier' => array( 84 'name' => $courier_name, 85 'url' => $tracking_url ? esc_url( $tracking_url, array( 'http', 'https' ) ) : '', 86 ), 40 87 ); 41 88 -
nexoy-order-tracker-for-woocommerce/tags/1.1.0/includes/frontend-shortcode.php
r3467879 r3765041 6 6 7 7 function nexoy_ot_render_search_form() { 8 nexoy_ot_enqueue_assets(); 9 8 10 ob_start(); 9 11 10 12 $current_user = wp_get_current_user(); 11 13 $user_email = $current_user->exists() ? $current_user->user_email : ''; … … 15 17 <div class="nexoy-ot-wrapper <?php echo esc_attr( $theme_class ); ?>"> 16 18 <div class="nexoy-ot-toggle-area"> 17 <p class="nexoy-ot-label-main"> How would you like to track your order?</p>19 <p class="nexoy-ot-label-main"><?php esc_html_e( 'How would you like to track your order?', 'nexoy-order-tracker-for-woocommerce' ); ?></p> 18 20 <div class="nexoy-ot-tabs"> 19 21 <label class="nexoy-ot-tab"> 20 22 <input type="radio" name="tracker_type" value="email" checked> 21 <span> Track with Email</span>23 <span><?php esc_html_e( 'Track with Email', 'nexoy-order-tracker-for-woocommerce' ); ?></span> 22 24 </label> 23 25 <label class="nexoy-ot-tab"> 24 26 <input type="radio" name="tracker_type" value="phone"> 25 <span> Track with Phone</span>27 <span><?php esc_html_e( 'Track with Phone', 'nexoy-order-tracker-for-woocommerce' ); ?></span> 26 28 </label> 27 29 </div> … … 30 32 <form id="nexoy-ot-form" class="nexoy-ot-form"> 31 33 <div class="nexoy-ot-input-group"> 32 <label> Order ID</label>33 <input type="text" name="order_id" placeholder=" e.g. 12345" required>34 <label><?php esc_html_e( 'Order ID', 'nexoy-order-tracker-for-woocommerce' ); ?></label> 35 <input type="text" name="order_id" placeholder="<?php esc_attr_e( 'e.g. 12345', 'nexoy-order-tracker-for-woocommerce' ); ?>" required> 34 36 </div> 35 37 <div id="nexoy-ot-email-field" class="nexoy-ot-input-group"> 36 <label> Billing Email</label>38 <label><?php esc_html_e( 'Billing Email', 'nexoy-order-tracker-for-woocommerce' ); ?></label> 37 39 <input type="email" name="billing_email" value="<?php echo esc_attr( $user_email ); ?>" placeholder="you@example.com"> 38 40 </div> 39 41 <div id="nexoy-ot-phone-field" class="nexoy-ot-input-group" style="display:none;"> 40 <label> Billing Phone</label>41 <input type="text" name="billing_phone" value="<?php echo esc_attr( $user_phone ); ?>" placeholder=" e.g. +880123456789">42 <label><?php esc_html_e( 'Billing Phone', 'nexoy-order-tracker-for-woocommerce' ); ?></label> 43 <input type="text" name="billing_phone" value="<?php echo esc_attr( $user_phone ); ?>" placeholder="<?php esc_attr_e( 'e.g. +880123456789', 'nexoy-order-tracker-for-woocommerce' ); ?>"> 42 44 </div> 43 <button type="submit" class="nexoy-ot-submit"> Track Order Now</button>45 <button type="submit" class="nexoy-ot-submit"><?php esc_html_e( 'Track Order Now', 'nexoy-order-tracker-for-woocommerce' ); ?></button> 44 46 </form> 45 47 -
nexoy-order-tracker-for-woocommerce/tags/1.1.0/includes/order-meta.php
r3467879 r3765041 27 27 function nexoy_ot_add_order_meta_box() { 28 28 $screen = class_exists( '\Automattic\WooCommerce\Internal\DataStores\Orders\CustomOrdersTableController' ) && wc_get_container()->get( \Automattic\WooCommerce\Internal\DataStores\Orders\CustomOrdersTableController::class )->custom_orders_table_usage_is_enabled() ? wc_get_page_screen_id( 'shop-order' ) : 'shop_order'; 29 add_meta_box( 'nexoy_ot_tracker_meta_box', 'Nexoy Order Tracker', 'nexoy_ot_tracker_meta_box_callback', $screen, 'side', 'default' );29 add_meta_box( 'nexoy_ot_tracker_meta_box', __( 'Nexoy Order Tracker', 'nexoy-order-tracker-for-woocommerce' ), 'nexoy_ot_tracker_meta_box_callback', $screen, 'side', 'default' ); 30 30 } 31 31 … … 40 40 wp_nonce_field( 'nexoy_ot_save_meta_box', 'nexoy_ot_meta_box_nonce' ); 41 41 42 echo '<p><strong> Courier Name:</strong><br><input type="text" name="nexoy_ot_courier_name" value="' . esc_attr( $courier_name ) . '" style="width:100%;" placeholder="e.g. FedEx, DHL" /></p>';43 echo '<p><strong> Tracking URL:</strong><br><input type="url" name="nexoy_ot_tracking_url" value="' . esc_attr( $tracking_url ) . '" style="width:100%;" placeholder="https://..." /></p>';42 echo '<p><strong>' . esc_html__( 'Courier Name:', 'nexoy-order-tracker-for-woocommerce' ) . '</strong><br><input type="text" name="nexoy_ot_courier_name" value="' . esc_attr( $courier_name ) . '" style="width:100%;" placeholder="' . esc_attr__( 'e.g. FedEx, DHL', 'nexoy-order-tracker-for-woocommerce' ) . '" /></p>'; 43 echo '<p><strong>' . esc_html__( 'Tracking URL:', 'nexoy-order-tracker-for-woocommerce' ) . '</strong><br><input type="url" name="nexoy_ot_tracking_url" value="' . esc_attr( $tracking_url ) . '" style="width:100%;" placeholder="https://..." /></p>'; 44 44 45 echo '<hr><p><strong> Activity Log:</strong></p>';45 echo '<hr><p><strong>' . esc_html__( 'Activity Log:', 'nexoy-order-tracker-for-woocommerce' ) . '</strong></p>'; 46 46 if ( is_array( $history ) && ! empty( $history ) ) { 47 47 echo '<ul style="margin:0; padding-left:15px; font-size:12px;">'; 48 48 foreach ( $history as $log ) { 49 49 if ( isset( $log['event_type'] ) && 'status_change' === $log['event_type'] ) { 50 $time = date_i18n( 'M d, Y h:i A',$log['timestamp'] );50 $time = date_i18n( get_option( 'date_format' ) . ' ' . get_option( 'time_format' ), (int) $log['timestamp'] ); 51 51 $status_name = wc_get_order_status_name( $log['new_status'] ); 52 52 echo '<li>' . esc_html( $status_name ) . ' - <em>' . esc_html( $time ) . '</em></li>'; … … 55 55 echo '</ul>'; 56 56 } else { 57 echo '<p style="color:#777;"> No activity recorded yet.</p>';57 echo '<p style="color:#777;">' . esc_html__( 'No activity recorded yet.', 'nexoy-order-tracker-for-woocommerce' ) . '</p>'; 58 58 } 59 59 } -
nexoy-order-tracker-for-woocommerce/tags/1.1.0/nexoy-order-tracker.php
r3467879 r3765041 4 4 * Plugin URI: https://thenexoy.com/ 5 5 * Description: A premium, high-performance order tracking system for WooCommerce. Allow customers to track their orders via a luxury timeline UI using their Order ID and Email/Phone. 6 * Version: 1. 0.06 * Version: 1.1.0 7 7 * Requires at least: 5.8 8 8 * Requires PHP: 7.4 9 * Author: Rayhan Chowdhury 10 * Author URI: https://prorayhan.com 9 * Requires Plugins: woocommerce 10 * WC requires at least: 6.0 11 * WC tested up to: 11.0 12 * Author: The Nexoy 13 * Author URI: https://thenexoy.com/ 11 14 * License: GPL v2 or later 12 15 * License URI: https://www.gnu.org/licenses/gpl-2.0.html … … 20 23 21 24 // Define Plugin Constants 22 define( 'NEXOY_OT_VERSION', '1. 0.1' );25 define( 'NEXOY_OT_VERSION', '1.1.0' ); 23 26 define( 'NEXOY_OT_PLUGIN_DIR', plugin_dir_path( __FILE__ ) ); 24 27 define( 'NEXOY_OT_PLUGIN_URL', plugin_dir_url( __FILE__ ) ); 28 29 /** 30 * Declare compatibility with WooCommerce HPOS and Cart/Checkout blocks. 31 */ 32 add_action( 'before_woocommerce_init', 'nexoy_ot_declare_wc_compatibility' ); 33 34 function nexoy_ot_declare_wc_compatibility() { 35 if ( class_exists( '\Automattic\WooCommerce\Utilities\FeaturesUtil' ) ) { 36 \Automattic\WooCommerce\Utilities\FeaturesUtil::declare_compatibility( 'custom_order_tables', __FILE__, true ); 37 \Automattic\WooCommerce\Utilities\FeaturesUtil::declare_compatibility( 'cart_checkout_blocks', __FILE__, true ); 38 } 39 } 25 40 26 41 /** … … 38 53 require_once NEXOY_OT_PLUGIN_DIR . 'includes/frontend-shortcode.php'; 39 54 } 40 // FIXED ISSUE 1:Priority 99 ensures WooCommerce loads before our plugin55 // Priority 99 ensures WooCommerce loads before our plugin 41 56 add_action( 'plugins_loaded', 'nexoy_ot_init_plugin', 99 ); 42 57 43 58 /** 44 * Enqueue Frontend Scripts and Styles unconditionally for Page Builders59 * Register frontend assets. They are enqueued only where the tracker is shown. 45 60 */ 46 function nexoy_ot_enqueue_scripts() { 47 // FIXED ISSUE 2: Removed strict shortcode check to ensure CSS loads in Elementor/Gutenberg perfectly 48 wp_enqueue_style( 'nexoy-ot-style', NEXOY_OT_PLUGIN_URL . 'assets/css/style.css', array(), NEXOY_OT_VERSION ); 49 wp_enqueue_script( 'nexoy-ot-script', NEXOY_OT_PLUGIN_URL . 'assets/js/script.js', array( 'jquery' ), NEXOY_OT_VERSION, true ); 50 61 function nexoy_ot_register_scripts() { 62 wp_register_style( 'nexoy-ot-style', NEXOY_OT_PLUGIN_URL . 'assets/css/style.css', array(), NEXOY_OT_VERSION ); 63 wp_register_script( 'nexoy-ot-script', NEXOY_OT_PLUGIN_URL . 'assets/js/script.js', array( 'jquery' ), NEXOY_OT_VERSION, true ); 64 51 65 wp_localize_script( 'nexoy-ot-script', 'nexoy_ot_params', array( 52 66 'ajax_url' => admin_url( 'admin-ajax.php' ), 53 'nonce' => wp_create_nonce( 'nexoy_ot_search_nonce' ) 67 'nonce' => wp_create_nonce( 'nexoy_ot_search_nonce' ), 68 'i18n' => array( 69 'fetching' => __( 'Fetching order details...', 'nexoy-order-tracker-for-woocommerce' ), 70 'conn_error' => __( 'Connection error. Please try again.', 'nexoy-order-tracker-for-woocommerce' ), 71 'carrier' => __( 'Carrier:', 'nexoy-order-tracker-for-woocommerce' ), 72 'track_order' => __( 'Track Order', 'nexoy-order-tracker-for-woocommerce' ), 73 'current' => __( 'Current status:', 'nexoy-order-tracker-for-woocommerce' ), 74 /* translators: %s: order status name */ 75 'updated_to' => __( 'Your order has been updated to %s.', 'nexoy-order-tracker-for-woocommerce' ), 76 ), 54 77 ) ); 78 79 // Load early on regular pages that contain the shortcode (avoids a flash of unstyled content) 80 if ( is_singular() ) { 81 $post = get_post(); 82 if ( $post && has_shortcode( $post->post_content, 'nexoy_order_tracker' ) ) { 83 nexoy_ot_enqueue_assets(); 84 } 85 } 55 86 } 56 add_action( 'wp_enqueue_scripts', 'nexoy_ot_enqueue_scripts' ); 87 add_action( 'wp_enqueue_scripts', 'nexoy_ot_register_scripts' ); 88 89 /** 90 * Enqueue frontend assets (also called from the shortcode, for page builders). 91 */ 92 function nexoy_ot_enqueue_assets() { 93 wp_enqueue_style( 'nexoy-ot-style' ); 94 wp_enqueue_script( 'nexoy-ot-script' ); 95 } 57 96 58 97 … … 63 102 64 103 function nexoy_ot_add_settings_link( $links ) { 65 // Create the settings link 66 $settings_url = admin_url( 'admin.php?page=nexoy-order-tracker' ); 67 $settings_link = '<a href="' . esc_url( $settings_url ) . '" style="font-weight: bold; color: #2271b1;">Settings</a>'; 68 69 // Add it to the beginning of the links array 104 $settings_url = admin_url( 'admin.php?page=nexoy-order-tracker' ); 105 $settings_link = '<a href="' . esc_url( $settings_url ) . '" style="font-weight: bold; color: #2271b1;">' . esc_html__( 'Settings', 'nexoy-order-tracker-for-woocommerce' ) . '</a>'; 106 70 107 array_unshift( $links, $settings_link ); 71 108 72 109 return $links; 73 110 } -
nexoy-order-tracker-for-woocommerce/tags/1.1.0/readme.txt
r3467879 r3765041 4 4 Tags: woocommerce, order tracker, order tracking, shipment tracking, tracking 5 5 Requires at least: 5.8 6 Tested up to: 6.97 Stable tag: 1. 0.06 Tested up to: 7.1 7 Stable tag: 1.1.0 8 8 Requires PHP: 7.4 9 9 License: GPLv2 or later … … 59 59 == Changelog == 60 60 61 = 1.1.0 = 62 * Security: Courier details are now escaped before display. 63 * Security: Failed lookups are rate limited per visitor to stop order probing. 64 * Improved: Timeline shows proper status names and dates in your site's timezone and date format. 65 * Improved: CSS/JS load only on pages that show the tracker. 66 * Improved: Declared compatibility with WooCommerce HPOS and Cart/Checkout blocks. 67 * Improved: All texts are now translatable; admin icons use Dashicons. 68 * Fixed: Version number mismatch on the settings page. 69 * Tested with WordPress 7.1. 70 61 71 = 1.0.0 = 62 72 * Initial Release: The grand launch of Nexoy Order Tracker for WooCommerce. 73 74 == Upgrade Notice == 75 76 = 1.1.0 = 77 Security and display improvements. Recommended update for all users. -
nexoy-order-tracker-for-woocommerce/trunk/assets/js/script.js
r3467879 r3765041 1 1 jQuery(document).ready(function($) { 2 const i18n = nexoy_ot_params.i18n || {}; 3 4 // Escape any text before it is placed into HTML 5 function esc(value) { 6 return $('<div>').text(value == null ? '' : String(value)).html(); 7 } 8 2 9 // Tab Switching Logic 3 10 const $tabs = $('input[name="tracker_type"]'); … … 25 32 $form.on('submit', function(e) { 26 33 e.preventDefault(); 27 28 $resultContainer.html('<p style="text-align:center; padding:20px; font-weight:500;"> Fetching order details...</p>').fadeIn();34 35 $resultContainer.html('<p style="text-align:center; padding:20px; font-weight:500;">' + esc(i18n.fetching) + '</p>').fadeIn(); 29 36 30 37 const trackerType = $('input[name="tracker_type"]:checked').val(); … … 43 50 renderNexoyTimeline(response.data); 44 51 } else { 45 $resultContainer.html(`<p style="color:#ff4d4d; text-align:center; padding:20px; font-weight:500;">${response.data.message}</p>`); 52 const message = response.data && response.data.message ? response.data.message : i18n.conn_error; 53 $resultContainer.html('<p style="color:#ff4d4d; text-align:center; padding:20px; font-weight:500;">' + esc(message) + '</p>'); 46 54 } 47 55 }).fail(function() { 48 $resultContainer.html('<p style="color:#ff4d4d; text-align:center; padding:20px;"> Connection error. Please try again.</p>');56 $resultContainer.html('<p style="color:#ff4d4d; text-align:center; padding:20px;">' + esc(i18n.conn_error) + '</p>'); 49 57 }); 50 58 }); … … 53 61 function renderNexoyTimeline(data) { 54 62 let html = '<div class="nexoy-ot-timeline-container">'; 55 63 56 64 if (data.courier && data.courier.name) { 65 const url = data.courier.url && /^https?:\/\//i.test(data.courier.url) ? data.courier.url : ''; 57 66 html += ` 58 67 <div class="nexoy-ot-courier-card"> 59 68 <div class="nexoy-ot-courier-info"> 60 <span class="nexoy-ot-label"> Carrier:</span>61 <span class="nexoy-ot-val">${ data.courier.name}</span>69 <span class="nexoy-ot-label">${esc(i18n.carrier)}</span> 70 <span class="nexoy-ot-val">${esc(data.courier.name)}</span> 62 71 </div> 63 ${ data.courier.url ? `<a href="${data.courier.url}" target="_blank" class="nexoy-ot-track-btn">Track Order</a>` : ''}72 ${url ? `<a href="${esc(url)}" target="_blank" rel="noopener noreferrer" class="nexoy-ot-track-btn">${esc(i18n.track_order)}</a>` : ''} 64 73 </div> 65 74 `; … … 68 77 if (data.history && data.history.length > 0) { 69 78 html += '<div class="nexoy-ot-v-timeline">'; 70 const history = data.history.reverse(); 71 history.forEach((item, index) => { 72 const date = new Date(item.timestamp * 1000); 73 const timeStr = date.toLocaleDateString('en-US', { month: 'short', day: 'numeric' }) + ' ' + 74 date.toLocaleTimeString('en-US', { hour: '2-digit', minute: '2-digit' }); 75 const statusTitle = item.new_status.replace(/-/g, ' ').toUpperCase(); 79 data.history.forEach((item, index) => { 80 const message = (i18n.updated_to || '%s').replace('%s', item.status); 76 81 html += ` 77 82 <div class="nexoy-ot-t-item" style="animation-delay: ${index * 0.1}s"> 78 <div class="nexoy-ot-t-meta">${ timeStr}</div>83 <div class="nexoy-ot-t-meta">${esc(item.date)}</div> 79 84 <div class="nexoy-ot-t-content"> 80 85 <div class="nexoy-ot-t-marker"></div> 81 86 <div class="nexoy-ot-t-text"> 82 <h5>${ statusTitle}</h5>83 <p> Your order has been updated to ${item.new_status}.</p>87 <h5>${esc(item.status)}</h5> 88 <p>${esc(message)}</p> 84 89 </div> 85 90 </div> … … 88 93 }); 89 94 html += '</div>'; 95 } else if (data.order_status) { 96 html += `<p class="nexoy-ot-current-status">${esc(i18n.current)} <strong>${esc(data.order_status)}</strong></p>`; 90 97 } 91 98 html += '</div>'; -
nexoy-order-tracker-for-woocommerce/trunk/includes/admin-settings.php
r3467879 r3765041 8 8 add_submenu_page( 9 9 'woocommerce', 10 'Nexoy Order Tracker',11 'Nexoy Order Tracker',10 __( 'Nexoy Order Tracker', 'nexoy-order-tracker-for-woocommerce' ), 11 __( 'Nexoy Order Tracker', 'nexoy-order-tracker-for-woocommerce' ), 12 12 'manage_woocommerce', 13 13 'nexoy-order-tracker', … … 38 38 <div class="nexoy-header"> 39 39 <div class="nexoy-brand"><h1>Nexoy <span>Order Tracker</span></h1></div> 40 <div class="nexoy-version"><span style="background: #e2e8f0; padding: 5px 12px; border-radius: 20px; font-size: 12px; font-weight: 700;">v 1.0.0</span></div>40 <div class="nexoy-version"><span style="background: #e2e8f0; padding: 5px 12px; border-radius: 20px; font-size: 12px; font-weight: 700;">v <?php echo esc_html( NEXOY_OT_VERSION ); ?></span></div> 41 41 </div> 42 42 <div class="nexoy-grid"> … … 45 45 <?php settings_fields( 'nexoy_ot_settings_group' ); ?> 46 46 <div class="nexoy-card"> 47 <div class="nexoy-card-header"><h3> 🎨 Visual Customization</h3></div>47 <div class="nexoy-card-header"><h3><span class="dashicons dashicons-admin-appearance"></span> <?php esc_html_e( 'Visual Customization', 'nexoy-order-tracker-for-woocommerce' ); ?></h3></div> 48 48 <div class="nexoy-card-body"> 49 <p style="margin-top:0; color: #64748b;"> Choose the base theme for your tracking interface.</p>49 <p style="margin-top:0; color: #64748b;"><?php esc_html_e( 'Choose the base theme for your tracking interface.', 'nexoy-order-tracker-for-woocommerce' ); ?></p> 50 50 <?php do_settings_sections( 'nexoy-order-tracker' ); ?> 51 51 </div> 52 52 </div> 53 53 <div class="nexoy-card"> 54 <div class="nexoy-card-header"><h3> 🚀 Ready to Launch?</h3></div>54 <div class="nexoy-card-header"><h3><span class="dashicons dashicons-shortcode"></span> <?php esc_html_e( 'Ready to Launch?', 'nexoy-order-tracker-for-woocommerce' ); ?></h3></div> 55 55 <div class="nexoy-card-body"> 56 56 <div class="nexoy-guide-box"> 57 <strong style="color: #0c4a6e;"> Integration Shortcode</strong>58 <p style="font-size: 13px; color: #334155; margin: 5px 0 15px;"> Copy and paste this shortcode to any page where you want the tracker to appear.</p>57 <strong style="color: #0c4a6e;"><?php esc_html_e( 'Integration Shortcode', 'nexoy-order-tracker-for-woocommerce' ); ?></strong> 58 <p style="font-size: 13px; color: #334155; margin: 5px 0 15px;"><?php esc_html_e( 'Copy and paste this shortcode to any page where you want the tracker to appear.', 'nexoy-order-tracker-for-woocommerce' ); ?></p> 59 59 <div class="nexoy-shortcode-display"><span>[nexoy_order_tracker]</span></div> 60 60 </div> 61 <?php submit_button( 'Save All Changes', 'nexoy-btn-save' ); ?>61 <?php submit_button( __( 'Save All Changes', 'nexoy-order-tracker-for-woocommerce' ), 'nexoy-btn-save' ); ?> 62 62 </div> 63 63 </div> … … 66 66 <div class="nexoy-sidebar"> 67 67 <div class="nexoy-card"> 68 <div class="nexoy-card-header"><h3> ℹ️ Plugin Info</h3></div>68 <div class="nexoy-card-header"><h3><span class="dashicons dashicons-info-outline"></span> <?php esc_html_e( 'Plugin Info', 'nexoy-order-tracker-for-woocommerce' ); ?></h3></div> 69 69 <div class="nexoy-card-body"> 70 <p style="font-size: 13px; line-height: 1.6; color: #64748b;">< strong>Nexoy Order Tracker</strong> is built for high-performance WooCommerce stores. If you need help, contact our support team at <strong>thenexoy.com</strong>.</p>70 <p style="font-size: 13px; line-height: 1.6; color: #64748b;"><?php echo wp_kses( __( '<strong>Nexoy Order Tracker</strong> is built for high-performance WooCommerce stores. If you need help, contact our support team at <strong>thenexoy.com</strong>.', 'nexoy-order-tracker-for-woocommerce' ), array( 'strong' => array() ) ); ?></p> 71 71 </div> 72 72 </div> … … 80 80 add_action( 'admin_init', 'nexoy_ot_register_settings' ); 81 81 function nexoy_ot_register_settings() { 82 register_setting( 'nexoy_ot_settings_group', 'nexoy_ot_theme_preference', 'nexoy_ot_sanitize_theme_choice');83 add_settings_section( 'nexoy_ot_main_section', 'Display Settings', '__return_empty_string', 'nexoy-order-tracker' );84 add_settings_field( 'nexoy_ot_theme_preference', 'Tracker Theme', 'nexoy_ot_theme_radio_callback', 'nexoy-order-tracker', 'nexoy_ot_main_section' );82 register_setting( 'nexoy_ot_settings_group', 'nexoy_ot_theme_preference', array( 'type' => 'string', 'sanitize_callback' => 'nexoy_ot_sanitize_theme_choice', 'default' => 'light' ) ); 83 add_settings_section( 'nexoy_ot_main_section', __( 'Display Settings', 'nexoy-order-tracker-for-woocommerce' ), '__return_empty_string', 'nexoy-order-tracker' ); 84 add_settings_field( 'nexoy_ot_theme_preference', __( 'Tracker Theme', 'nexoy-order-tracker-for-woocommerce' ), 'nexoy_ot_theme_radio_callback', 'nexoy-order-tracker', 'nexoy_ot_main_section' ); 85 85 } 86 86 … … 92 92 $theme = get_option( 'nexoy_ot_theme_preference', 'light' ); 93 93 ?> 94 <label><input type="radio" name="nexoy_ot_theme_preference" value="light" <?php checked( $theme, 'light' ); ?> /> Light Theme (Default)</label><br><br>95 <label><input type="radio" name="nexoy_ot_theme_preference" value="dark" <?php checked( $theme, 'dark' ); ?> /> Dark Theme (Premium Look)</label>94 <label><input type="radio" name="nexoy_ot_theme_preference" value="light" <?php checked( $theme, 'light' ); ?> /> <?php esc_html_e( 'Light Theme (Default)', 'nexoy-order-tracker-for-woocommerce' ); ?></label><br><br> 95 <label><input type="radio" name="nexoy_ot_theme_preference" value="dark" <?php checked( $theme, 'dark' ); ?> /> <?php esc_html_e( 'Dark Theme (Premium Look)', 'nexoy-order-tracker-for-woocommerce' ); ?></label> 96 96 <?php 97 97 } -
nexoy-order-tracker-for-woocommerce/trunk/includes/ajax-handler.php
r3467879 r3765041 6 6 add_action( 'wp_ajax_nopriv_nexoy_ot_search_order', 'nexoy_ot_search_order_handler' ); 7 7 8 /** 9 * Rate limit: max failed lookups per visitor IP within the window. 10 */ 11 define( 'NEXOY_OT_MAX_ATTEMPTS', 10 ); 12 define( 'NEXOY_OT_ATTEMPT_WINDOW', 15 * MINUTE_IN_SECONDS ); 13 14 function nexoy_ot_attempts_key() { 15 $ip = isset( $_SERVER['REMOTE_ADDR'] ) ? sanitize_text_field( wp_unslash( $_SERVER['REMOTE_ADDR'] ) ) : 'unknown'; 16 return 'nexoy_ot_fail_' . md5( $ip ); 17 } 18 19 function nexoy_ot_register_failed_attempt() { 20 $key = nexoy_ot_attempts_key(); 21 $attempts = (int) get_transient( $key ); 22 set_transient( $key, $attempts + 1, NEXOY_OT_ATTEMPT_WINDOW ); 23 } 24 8 25 function nexoy_ot_search_order_handler() { 9 26 check_ajax_referer( 'nexoy_ot_search_nonce', 'security' ); 27 28 if ( (int) get_transient( nexoy_ot_attempts_key() ) >= NEXOY_OT_MAX_ATTEMPTS ) { 29 wp_send_json_error( array( 'message' => __( 'Too many attempts. Please try again in a few minutes.', 'nexoy-order-tracker-for-woocommerce' ) ) ); 30 } 10 31 11 32 $order_id = isset( $_POST['order_id'] ) ? absint( wp_unslash( $_POST['order_id'] ) ) : 0; … … 14 35 $billing_phone = isset( $_POST['billing_phone'] ) ? sanitize_text_field( wp_unslash( $_POST['billing_phone'] ) ) : ''; 15 36 16 if ( ! $order_id ) wp_send_json_error( array( 'message' => 'Please provide a valid Order ID.' ) ); 37 if ( ! $order_id ) wp_send_json_error( array( 'message' => __( 'Please provide a valid Order ID.', 'nexoy-order-tracker-for-woocommerce' ) ) ); 38 39 // Same message for "not found" and "no match", so order IDs cannot be probed 40 $no_match = __( 'Order ID and credentials do not match.', 'nexoy-order-tracker-for-woocommerce' ); 17 41 18 42 $order = wc_get_order( $order_id ); 19 if ( ! $order || $order->get_id() !== $order_id ) wp_send_json_error( array( 'message' => 'Order not found.' ) ); 43 if ( ! $order || $order->get_id() !== $order_id ) { 44 nexoy_ot_register_failed_attempt(); 45 wp_send_json_error( array( 'message' => $no_match ) ); 46 } 20 47 21 48 $is_valid_user = false; 22 49 if ( 'email' === $tracker_type ) { 23 if ( strtolower( $order->get_billing_email() ) === strtolower( $billing_email ) ) $is_valid_user = true;50 if ( ! empty( $billing_email ) && strtolower( $order->get_billing_email() ) === strtolower( $billing_email ) ) $is_valid_user = true; 24 51 } else { 25 $saved_phone = preg_replace( '/[^0-9]/', '', $order->get_billing_phone() );52 $saved_phone = preg_replace( '/[^0-9]/', '', $order->get_billing_phone() ); 26 53 $provided_phone = preg_replace( '/[^0-9]/', '', $billing_phone ); 27 54 if ( ! empty( $provided_phone ) && $saved_phone === $provided_phone ) $is_valid_user = true; 28 55 } 29 56 30 if ( ! $is_valid_user ) wp_send_json_error( array( 'message' => 'Order ID and credentials do not match.' ) ); 57 if ( ! $is_valid_user ) { 58 nexoy_ot_register_failed_attempt(); 59 wp_send_json_error( array( 'message' => $no_match ) ); 60 } 31 61 32 62 $history = $order->get_meta( '_nexoy_ot_activity_log', true ); … … 34 64 $tracking_url = $order->get_meta( '_nexoy_ot_tracking_url', true ); 35 65 66 // Send ready-to-display labels and dates (site timezone, site date format) 67 $timeline = array(); 68 if ( is_array( $history ) ) { 69 foreach ( $history as $log ) { 70 if ( ! isset( $log['event_type'], $log['new_status'], $log['timestamp'] ) || 'status_change' !== $log['event_type'] ) { 71 continue; 72 } 73 $timeline[] = array( 74 'status' => wc_get_order_status_name( $log['new_status'] ), 75 'date' => date_i18n( get_option( 'date_format' ) . ' ' . get_option( 'time_format' ), (int) $log['timestamp'] ), 76 ); 77 } 78 } 79 36 80 $response_data = array( 37 81 'order_status' => wc_get_order_status_name( $order->get_status() ), 38 'history' => $history, 39 'courier' => array( 'name' => $courier_name, 'url' => $tracking_url ) 82 'history' => array_reverse( $timeline ), 83 'courier' => array( 84 'name' => $courier_name, 85 'url' => $tracking_url ? esc_url( $tracking_url, array( 'http', 'https' ) ) : '', 86 ), 40 87 ); 41 88 -
nexoy-order-tracker-for-woocommerce/trunk/includes/frontend-shortcode.php
r3467879 r3765041 6 6 7 7 function nexoy_ot_render_search_form() { 8 nexoy_ot_enqueue_assets(); 9 8 10 ob_start(); 9 11 10 12 $current_user = wp_get_current_user(); 11 13 $user_email = $current_user->exists() ? $current_user->user_email : ''; … … 15 17 <div class="nexoy-ot-wrapper <?php echo esc_attr( $theme_class ); ?>"> 16 18 <div class="nexoy-ot-toggle-area"> 17 <p class="nexoy-ot-label-main"> How would you like to track your order?</p>19 <p class="nexoy-ot-label-main"><?php esc_html_e( 'How would you like to track your order?', 'nexoy-order-tracker-for-woocommerce' ); ?></p> 18 20 <div class="nexoy-ot-tabs"> 19 21 <label class="nexoy-ot-tab"> 20 22 <input type="radio" name="tracker_type" value="email" checked> 21 <span> Track with Email</span>23 <span><?php esc_html_e( 'Track with Email', 'nexoy-order-tracker-for-woocommerce' ); ?></span> 22 24 </label> 23 25 <label class="nexoy-ot-tab"> 24 26 <input type="radio" name="tracker_type" value="phone"> 25 <span> Track with Phone</span>27 <span><?php esc_html_e( 'Track with Phone', 'nexoy-order-tracker-for-woocommerce' ); ?></span> 26 28 </label> 27 29 </div> … … 30 32 <form id="nexoy-ot-form" class="nexoy-ot-form"> 31 33 <div class="nexoy-ot-input-group"> 32 <label> Order ID</label>33 <input type="text" name="order_id" placeholder=" e.g. 12345" required>34 <label><?php esc_html_e( 'Order ID', 'nexoy-order-tracker-for-woocommerce' ); ?></label> 35 <input type="text" name="order_id" placeholder="<?php esc_attr_e( 'e.g. 12345', 'nexoy-order-tracker-for-woocommerce' ); ?>" required> 34 36 </div> 35 37 <div id="nexoy-ot-email-field" class="nexoy-ot-input-group"> 36 <label> Billing Email</label>38 <label><?php esc_html_e( 'Billing Email', 'nexoy-order-tracker-for-woocommerce' ); ?></label> 37 39 <input type="email" name="billing_email" value="<?php echo esc_attr( $user_email ); ?>" placeholder="you@example.com"> 38 40 </div> 39 41 <div id="nexoy-ot-phone-field" class="nexoy-ot-input-group" style="display:none;"> 40 <label> Billing Phone</label>41 <input type="text" name="billing_phone" value="<?php echo esc_attr( $user_phone ); ?>" placeholder=" e.g. +880123456789">42 <label><?php esc_html_e( 'Billing Phone', 'nexoy-order-tracker-for-woocommerce' ); ?></label> 43 <input type="text" name="billing_phone" value="<?php echo esc_attr( $user_phone ); ?>" placeholder="<?php esc_attr_e( 'e.g. +880123456789', 'nexoy-order-tracker-for-woocommerce' ); ?>"> 42 44 </div> 43 <button type="submit" class="nexoy-ot-submit"> Track Order Now</button>45 <button type="submit" class="nexoy-ot-submit"><?php esc_html_e( 'Track Order Now', 'nexoy-order-tracker-for-woocommerce' ); ?></button> 44 46 </form> 45 47 -
nexoy-order-tracker-for-woocommerce/trunk/includes/order-meta.php
r3467879 r3765041 27 27 function nexoy_ot_add_order_meta_box() { 28 28 $screen = class_exists( '\Automattic\WooCommerce\Internal\DataStores\Orders\CustomOrdersTableController' ) && wc_get_container()->get( \Automattic\WooCommerce\Internal\DataStores\Orders\CustomOrdersTableController::class )->custom_orders_table_usage_is_enabled() ? wc_get_page_screen_id( 'shop-order' ) : 'shop_order'; 29 add_meta_box( 'nexoy_ot_tracker_meta_box', 'Nexoy Order Tracker', 'nexoy_ot_tracker_meta_box_callback', $screen, 'side', 'default' );29 add_meta_box( 'nexoy_ot_tracker_meta_box', __( 'Nexoy Order Tracker', 'nexoy-order-tracker-for-woocommerce' ), 'nexoy_ot_tracker_meta_box_callback', $screen, 'side', 'default' ); 30 30 } 31 31 … … 40 40 wp_nonce_field( 'nexoy_ot_save_meta_box', 'nexoy_ot_meta_box_nonce' ); 41 41 42 echo '<p><strong> Courier Name:</strong><br><input type="text" name="nexoy_ot_courier_name" value="' . esc_attr( $courier_name ) . '" style="width:100%;" placeholder="e.g. FedEx, DHL" /></p>';43 echo '<p><strong> Tracking URL:</strong><br><input type="url" name="nexoy_ot_tracking_url" value="' . esc_attr( $tracking_url ) . '" style="width:100%;" placeholder="https://..." /></p>';42 echo '<p><strong>' . esc_html__( 'Courier Name:', 'nexoy-order-tracker-for-woocommerce' ) . '</strong><br><input type="text" name="nexoy_ot_courier_name" value="' . esc_attr( $courier_name ) . '" style="width:100%;" placeholder="' . esc_attr__( 'e.g. FedEx, DHL', 'nexoy-order-tracker-for-woocommerce' ) . '" /></p>'; 43 echo '<p><strong>' . esc_html__( 'Tracking URL:', 'nexoy-order-tracker-for-woocommerce' ) . '</strong><br><input type="url" name="nexoy_ot_tracking_url" value="' . esc_attr( $tracking_url ) . '" style="width:100%;" placeholder="https://..." /></p>'; 44 44 45 echo '<hr><p><strong> Activity Log:</strong></p>';45 echo '<hr><p><strong>' . esc_html__( 'Activity Log:', 'nexoy-order-tracker-for-woocommerce' ) . '</strong></p>'; 46 46 if ( is_array( $history ) && ! empty( $history ) ) { 47 47 echo '<ul style="margin:0; padding-left:15px; font-size:12px;">'; 48 48 foreach ( $history as $log ) { 49 49 if ( isset( $log['event_type'] ) && 'status_change' === $log['event_type'] ) { 50 $time = date_i18n( 'M d, Y h:i A',$log['timestamp'] );50 $time = date_i18n( get_option( 'date_format' ) . ' ' . get_option( 'time_format' ), (int) $log['timestamp'] ); 51 51 $status_name = wc_get_order_status_name( $log['new_status'] ); 52 52 echo '<li>' . esc_html( $status_name ) . ' - <em>' . esc_html( $time ) . '</em></li>'; … … 55 55 echo '</ul>'; 56 56 } else { 57 echo '<p style="color:#777;"> No activity recorded yet.</p>';57 echo '<p style="color:#777;">' . esc_html__( 'No activity recorded yet.', 'nexoy-order-tracker-for-woocommerce' ) . '</p>'; 58 58 } 59 59 } -
nexoy-order-tracker-for-woocommerce/trunk/nexoy-order-tracker.php
r3467879 r3765041 4 4 * Plugin URI: https://thenexoy.com/ 5 5 * Description: A premium, high-performance order tracking system for WooCommerce. Allow customers to track their orders via a luxury timeline UI using their Order ID and Email/Phone. 6 * Version: 1. 0.06 * Version: 1.1.0 7 7 * Requires at least: 5.8 8 8 * Requires PHP: 7.4 9 * Author: Rayhan Chowdhury 10 * Author URI: https://prorayhan.com 9 * Requires Plugins: woocommerce 10 * WC requires at least: 6.0 11 * WC tested up to: 11.0 12 * Author: The Nexoy 13 * Author URI: https://thenexoy.com/ 11 14 * License: GPL v2 or later 12 15 * License URI: https://www.gnu.org/licenses/gpl-2.0.html … … 20 23 21 24 // Define Plugin Constants 22 define( 'NEXOY_OT_VERSION', '1. 0.1' );25 define( 'NEXOY_OT_VERSION', '1.1.0' ); 23 26 define( 'NEXOY_OT_PLUGIN_DIR', plugin_dir_path( __FILE__ ) ); 24 27 define( 'NEXOY_OT_PLUGIN_URL', plugin_dir_url( __FILE__ ) ); 28 29 /** 30 * Declare compatibility with WooCommerce HPOS and Cart/Checkout blocks. 31 */ 32 add_action( 'before_woocommerce_init', 'nexoy_ot_declare_wc_compatibility' ); 33 34 function nexoy_ot_declare_wc_compatibility() { 35 if ( class_exists( '\Automattic\WooCommerce\Utilities\FeaturesUtil' ) ) { 36 \Automattic\WooCommerce\Utilities\FeaturesUtil::declare_compatibility( 'custom_order_tables', __FILE__, true ); 37 \Automattic\WooCommerce\Utilities\FeaturesUtil::declare_compatibility( 'cart_checkout_blocks', __FILE__, true ); 38 } 39 } 25 40 26 41 /** … … 38 53 require_once NEXOY_OT_PLUGIN_DIR . 'includes/frontend-shortcode.php'; 39 54 } 40 // FIXED ISSUE 1:Priority 99 ensures WooCommerce loads before our plugin55 // Priority 99 ensures WooCommerce loads before our plugin 41 56 add_action( 'plugins_loaded', 'nexoy_ot_init_plugin', 99 ); 42 57 43 58 /** 44 * Enqueue Frontend Scripts and Styles unconditionally for Page Builders59 * Register frontend assets. They are enqueued only where the tracker is shown. 45 60 */ 46 function nexoy_ot_enqueue_scripts() { 47 // FIXED ISSUE 2: Removed strict shortcode check to ensure CSS loads in Elementor/Gutenberg perfectly 48 wp_enqueue_style( 'nexoy-ot-style', NEXOY_OT_PLUGIN_URL . 'assets/css/style.css', array(), NEXOY_OT_VERSION ); 49 wp_enqueue_script( 'nexoy-ot-script', NEXOY_OT_PLUGIN_URL . 'assets/js/script.js', array( 'jquery' ), NEXOY_OT_VERSION, true ); 50 61 function nexoy_ot_register_scripts() { 62 wp_register_style( 'nexoy-ot-style', NEXOY_OT_PLUGIN_URL . 'assets/css/style.css', array(), NEXOY_OT_VERSION ); 63 wp_register_script( 'nexoy-ot-script', NEXOY_OT_PLUGIN_URL . 'assets/js/script.js', array( 'jquery' ), NEXOY_OT_VERSION, true ); 64 51 65 wp_localize_script( 'nexoy-ot-script', 'nexoy_ot_params', array( 52 66 'ajax_url' => admin_url( 'admin-ajax.php' ), 53 'nonce' => wp_create_nonce( 'nexoy_ot_search_nonce' ) 67 'nonce' => wp_create_nonce( 'nexoy_ot_search_nonce' ), 68 'i18n' => array( 69 'fetching' => __( 'Fetching order details...', 'nexoy-order-tracker-for-woocommerce' ), 70 'conn_error' => __( 'Connection error. Please try again.', 'nexoy-order-tracker-for-woocommerce' ), 71 'carrier' => __( 'Carrier:', 'nexoy-order-tracker-for-woocommerce' ), 72 'track_order' => __( 'Track Order', 'nexoy-order-tracker-for-woocommerce' ), 73 'current' => __( 'Current status:', 'nexoy-order-tracker-for-woocommerce' ), 74 /* translators: %s: order status name */ 75 'updated_to' => __( 'Your order has been updated to %s.', 'nexoy-order-tracker-for-woocommerce' ), 76 ), 54 77 ) ); 78 79 // Load early on regular pages that contain the shortcode (avoids a flash of unstyled content) 80 if ( is_singular() ) { 81 $post = get_post(); 82 if ( $post && has_shortcode( $post->post_content, 'nexoy_order_tracker' ) ) { 83 nexoy_ot_enqueue_assets(); 84 } 85 } 55 86 } 56 add_action( 'wp_enqueue_scripts', 'nexoy_ot_enqueue_scripts' ); 87 add_action( 'wp_enqueue_scripts', 'nexoy_ot_register_scripts' ); 88 89 /** 90 * Enqueue frontend assets (also called from the shortcode, for page builders). 91 */ 92 function nexoy_ot_enqueue_assets() { 93 wp_enqueue_style( 'nexoy-ot-style' ); 94 wp_enqueue_script( 'nexoy-ot-script' ); 95 } 57 96 58 97 … … 63 102 64 103 function nexoy_ot_add_settings_link( $links ) { 65 // Create the settings link 66 $settings_url = admin_url( 'admin.php?page=nexoy-order-tracker' ); 67 $settings_link = '<a href="' . esc_url( $settings_url ) . '" style="font-weight: bold; color: #2271b1;">Settings</a>'; 68 69 // Add it to the beginning of the links array 104 $settings_url = admin_url( 'admin.php?page=nexoy-order-tracker' ); 105 $settings_link = '<a href="' . esc_url( $settings_url ) . '" style="font-weight: bold; color: #2271b1;">' . esc_html__( 'Settings', 'nexoy-order-tracker-for-woocommerce' ) . '</a>'; 106 70 107 array_unshift( $links, $settings_link ); 71 108 72 109 return $links; 73 110 } -
nexoy-order-tracker-for-woocommerce/trunk/readme.txt
r3467879 r3765041 4 4 Tags: woocommerce, order tracker, order tracking, shipment tracking, tracking 5 5 Requires at least: 5.8 6 Tested up to: 6.97 Stable tag: 1. 0.06 Tested up to: 7.1 7 Stable tag: 1.1.0 8 8 Requires PHP: 7.4 9 9 License: GPLv2 or later … … 59 59 == Changelog == 60 60 61 = 1.1.0 = 62 * Security: Courier details are now escaped before display. 63 * Security: Failed lookups are rate limited per visitor to stop order probing. 64 * Improved: Timeline shows proper status names and dates in your site's timezone and date format. 65 * Improved: CSS/JS load only on pages that show the tracker. 66 * Improved: Declared compatibility with WooCommerce HPOS and Cart/Checkout blocks. 67 * Improved: All texts are now translatable; admin icons use Dashicons. 68 * Fixed: Version number mismatch on the settings page. 69 * Tested with WordPress 7.1. 70 61 71 = 1.0.0 = 62 72 * Initial Release: The grand launch of Nexoy Order Tracker for WooCommerce. 73 74 == Upgrade Notice == 75 76 = 1.1.0 = 77 Security and display improvements. Recommended update for all users.
Note:
See TracChangeset
for help on using the changeset viewer.
![(please configure the [header_logo] section in trac.ini)](/chrome/site/your_project_logo.png)