From 5c4a7cdeacfaa61ee7f6e7be4adc2f6c3664489d Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E6=96=87=E6=B4=BE=E5=A4=87=E6=A1=88?=
<130886204+modiqi@users.noreply.github.com>
Date: Sun, 1 Jun 2025 18:01:15 +0800
Subject: [PATCH] =?UTF-8?q?v1.2.0=20=E7=A8=B3=E5=AE=9A=E7=89=88?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
admin-page.php | 1008 ++++++++++++++++++++++++++++++++
admin.css | 920 +++++++++++++++++++++++++++++
admin.js | 494 ++++++++++++++++
assets/i-like-food.svg | 1 +
assets/leaf.svg | 1 +
class-admin.php | 131 +++++
class-public.php | 323 ++++++++++
class-wpnav-links.php | 494 ++++++++++++++++
external-indicator.css | 135 +++++
frontend.css | 837 ++++++++++++++++++++++++++
languages/wpnav-links-zh_CN.mo | Bin 0 -> 17388 bytes
languages/wpnav-links-zh_CN.po | 964 ++++++++++++++++++++++++++++++
languages/wpnav-links.pot | 389 ++++++++++++
readme.txt | 176 ++++++
redirect-template.php | 561 ++++++++++++++++++
redirect.js | 343 +++++++++++
wpnav-links.php | 533 +++++++++++++++++
17 files changed, 7310 insertions(+)
create mode 100644 admin-page.php
create mode 100644 admin.css
create mode 100644 admin.js
create mode 100644 assets/i-like-food.svg
create mode 100644 assets/leaf.svg
create mode 100644 class-admin.php
create mode 100644 class-public.php
create mode 100644 class-wpnav-links.php
create mode 100644 external-indicator.css
create mode 100644 frontend.css
create mode 100644 languages/wpnav-links-zh_CN.mo
create mode 100644 languages/wpnav-links-zh_CN.po
create mode 100644 languages/wpnav-links.pot
create mode 100644 readme.txt
create mode 100644 redirect-template.php
create mode 100644 redirect.js
create mode 100644 wpnav-links.php
diff --git a/admin-page.php b/admin-page.php
new file mode 100644
index 0000000..58994ce
--- /dev/null
+++ b/admin-page.php
@@ -0,0 +1,1008 @@
+ isset($_POST['auto_whitelist_same_root']) ? 1 : 0,
+ 'search_engines' => isset($_POST['auto_whitelist_search_engines']) ? 1 : 0
+ );
+
+ $old_options_json = json_encode($old_options);
+ $new_options_json = json_encode($options);
+ $data_changed = ($old_options_json !== $new_options_json);
+
+ if ($data_changed) {
+ $result = update_option('wpnav_links_options', $options);
+ if ($result !== false) {
+ $success_message = __('Whitelist settings saved successfully!', 'wpnav-links');
+ } else {
+ $error_message = __('Failed to save whitelist settings. Database error.', 'wpnav-links');
+ }
+ } else {
+ $success_message = __('Whitelist settings saved successfully!', 'wpnav-links');
+ }
+ $current_tab = 'whitelist';
+ }
+}
+
+if (isset($_POST['wpnav_redirect_nonce']) && wp_verify_nonce($_POST['wpnav_redirect_nonce'], 'wpnav_redirect_page_nonce')) {
+ if (!current_user_can('manage_options')) {
+ $error_message = __('You do not have sufficient permissions to access this page.', 'wpnav-links');
+ } else {
+ $old_options = get_option('wpnav_links_options', array());
+ $options = $old_options;
+
+ $options['template'] = isset($_POST['template']) ? sanitize_text_field($_POST['template']) : 'default';
+ $options['color_scheme'] = isset($_POST['color_scheme']) ? sanitize_text_field($_POST['color_scheme']) : 'blue';
+ $options['page_title'] = isset($_POST['page_title']) ? sanitize_text_field($_POST['page_title']) : 'External Link Warning';
+ $options['page_subtitle'] = isset($_POST['page_subtitle']) ? sanitize_text_field($_POST['page_subtitle']) : '';
+ $options['url_label'] = isset($_POST['url_label']) ? sanitize_text_field($_POST['url_label']) : 'You are about to visit:';
+ $options['warning_text'] = isset($_POST['warning_text']) ? sanitize_textarea_field($_POST['warning_text']) : '';
+ $options['show_warning_message'] = isset($_POST['show_warning_message']) ? 1 : 0;
+ $options['show_logo'] = isset($_POST['show_logo']) ? 1 : 0;
+ $options['show_url_full'] = isset($_POST['show_url_full']) ? 1 : 0;
+ $options['show_security_info'] = isset($_POST['show_security_info']) ? 1 : 0;
+ $options['show_security_tips'] = isset($_POST['show_security_tips']) ? 1 : 0;
+ $options['show_back_button'] = isset($_POST['show_back_button']) ? 1 : 0;
+ $options['custom_css'] = isset($_POST['custom_css']) ? wp_strip_all_tags($_POST['custom_css']) : '';
+ $options['button_text_continue'] = isset($_POST['button_text_continue']) ? sanitize_text_field($_POST['button_text_continue']) : 'Continue';
+ $options['button_text_back'] = isset($_POST['button_text_back']) ? sanitize_text_field($_POST['button_text_back']) : 'Back';
+ $options['button_style'] = isset($_POST['button_style']) ? sanitize_text_field($_POST['button_style']) : 'rounded';
+ $options['countdown_text'] = isset($_POST['countdown_text']) ? sanitize_text_field($_POST['countdown_text']) : 'Auto redirect in {seconds} seconds';
+ $options['show_progress_bar'] = isset($_POST['show_progress_bar']) ? 1 : 0;
+
+ $old_options_json = json_encode($old_options);
+ $new_options_json = json_encode($options);
+ $data_changed = ($old_options_json !== $new_options_json);
+
+ if ($data_changed) {
+ $result = update_option('wpnav_links_options', $options);
+ if ($result !== false) {
+ $success_message = __('Redirect page settings saved successfully!', 'wpnav-links');
+ } else {
+ $error_message = __('Failed to save redirect page settings. Database error.', 'wpnav-links');
+ }
+ } else {
+ $success_message = __('Redirect page settings saved successfully!', 'wpnav-links');
+ }
+ $current_tab = 'redirect_page';
+ }
+}
+
+if (isset($_POST['wpnav_import_nonce']) && wp_verify_nonce($_POST['wpnav_import_nonce'], 'wpnav_import_csv')) {
+ if (!current_user_can('manage_options')) {
+ $error_message = __('You do not have sufficient permissions to access this page.', 'wpnav-links');
+ } elseif (!$wp_china_yes_active) {
+ $error_message = __('Import functionality requires 文派叶子 🍃(WPCY.COM)to be active.', 'wpnav-links');
+ } else {
+ if (!empty($_FILES['whitelist_csv']['tmp_name'])) {
+ $csv_file = $_FILES['whitelist_csv']['tmp_name'];
+ $domains = array();
+
+ if (($handle = fopen($csv_file, "r")) !== FALSE) {
+ while (($data = fgetcsv($handle, 1000, ",")) !== FALSE) {
+ if (!empty($data[0])) {
+ $domains[] = sanitize_text_field($data[0]);
+ }
+ }
+ fclose($handle);
+ }
+
+ if (!empty($domains)) {
+ $options = get_option('wpnav_links_options', array());
+ $existing_domains = explode("\n", isset($options['whitelist_domains']) ? $options['whitelist_domains'] : '');
+ $existing_domains = array_map('trim', $existing_domains);
+ $combined_domains = array_merge($existing_domains, $domains);
+ $combined_domains = array_filter($combined_domains);
+ $combined_domains = array_unique($combined_domains);
+
+ $options['whitelist_domains'] = implode("\n", $combined_domains);
+
+ $result = update_option('wpnav_links_options', $options);
+ if ($result !== false) {
+ $imported_count = count($domains);
+ $success_message = sprintf(__('Successfully imported %d domains.', 'wpnav-links'), $imported_count);
+ } else {
+ $error_message = __('Failed to import CSV data. Database error.', 'wpnav-links');
+ }
+ } else {
+ $error_message = __('No valid domains found in the CSV file.', 'wpnav-links');
+ }
+ } else {
+ $error_message = __('Please select a CSV file to upload.', 'wpnav-links');
+ }
+ $current_tab = 'import_export';
+ }
+}
+
+if (isset($_GET['message'])) {
+ $message_type = sanitize_text_field($_GET['message']);
+ if ($message_type === 'saved') {
+ $success_message = __('Settings saved successfully!', 'wpnav-links');
+ } elseif ($message_type === 'imported') {
+ $count = isset($_GET['count']) ? intval($_GET['count']) : 0;
+ $success_message = sprintf(__('Successfully imported %d domains.', 'wpnav-links'), $count);
+ }
+}
+
+$options = get_option('wpnav_links_options', array());
+
+$current_time = current_time('timestamp');
+$end_date = isset($_GET['end_date']) ? sanitize_text_field($_GET['end_date']) : date('Y-m-d', $current_time);
+$start_date = isset($_GET['start_date']) ? sanitize_text_field($_GET['start_date']) : date('Y-m-d', strtotime('-30 days', $current_time));
+
+$limit = 20;
+$current_page = isset($_GET['paged']) ? max(1, intval($_GET['paged'])) : 1;
+$offset = ($current_page - 1) * $limit;
+
+$orderby = isset($_GET['orderby']) ? sanitize_text_field($_GET['orderby']) : 'click_time';
+$order = isset($_GET['order']) ? sanitize_text_field($_GET['order']) : 'DESC';
+
+$stats = $plugin->get_stats(array(
+ 'start_date' => $start_date,
+ 'end_date' => $end_date,
+ 'limit' => $limit,
+ 'offset' => $offset,
+ 'order_by' => $orderby,
+ 'order' => $order
+));
+
+$total_items = $plugin->get_total_count($start_date, $end_date);
+$total_pages = ceil($total_items / $limit);
+$top_urls = $plugin->get_top_urls(10);
+
+$custom_template_exists = file_exists(get_stylesheet_directory() . '/wpnav-redirect-template.php') ||
+ file_exists(get_template_directory() . '/wpnav-redirect-template.php');
+?>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
example.com
+
https://example.com/sample-page
+
+
+ HTTPS
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ %s',
+ esc_url($wpcy_link),
+ __('文派叶子 🍃(WPCY.COM)', 'wpnav-links')
+ )
+ );
+ ?>
+
+
+
+
+
>
+
+
+
+
+
+
+
+
+
>
+
+
+
+
+
+
+
+
+
+
+
+
+ |
+
+
+
+
+ google.com |
+
+
+ facebook.com |
+
+
+ youtube.com |
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ get_total_count(date('Y-m-d'), date('Y-m-d'));
+ $month_count = $plugin->get_total_count(date('Y-m-01'), date('Y-m-d'));
+ $https_stats = $plugin->get_https_stats();
+ $unique_domains = $plugin->get_unique_domains_count();
+ $avg_daily = 0;
+ if ($total_items > 0) {
+ $days_active = max(1, (strtotime($end_date) - strtotime($start_date)) / 86400);
+ $avg_daily = round($total_items / $days_active, 1);
+ }
+ ?>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 0): ?>
+
+ %
+
+
+
+ %
+
+
+
+
+ -
+
+
+
+ -
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 1) {
+ $current_url = remove_query_arg('paged');
+ echo '
';
+
+ if ($current_page > 1) {
+ echo '
« ';
+ }
+
+ echo '
' . sprintf(__('Page %1$s of %2$s', 'wpnav-links'), $current_page, $total_pages) . '';
+
+ if ($current_page < $total_pages) {
+ echo '
»';
+ }
+
+ echo '
';
+ }
+ ?>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ |
+ |
+
+
+ |
+ |
+
+
+ |
+ |
+
+
+ |
+ prefix . WPNAV_LINKS_TABLE; ?> |
+
+
+ |
+
|
+
+
+ |
+ |
+
+
+
+
+
diff --git a/admin.css b/admin.css
new file mode 100644
index 0000000..7067e27
--- /dev/null
+++ b/admin.css
@@ -0,0 +1,920 @@
+.card {
+ background: #fff;
+ border: 1px solid #c3c4c7;
+ border-radius: 4px;
+ max-width: unset;
+ margin-top: 20px;
+ padding: 20px;
+ box-shadow: 0 1px 1px rgba(0,0,0,0.04);
+}
+
+.card h2 {
+ margin-top: 0;
+ margin-bottom: 15px;
+ font-size: 18px;
+ font-weight: 600;
+ padding-bottom: 10px;
+ color: #23282d;
+}
+
+.card p {
+ color: #50575e;
+}
+
+.wpnav-tabs {
+ display: flex;
+ flex-wrap: wrap;
+ gap: 5px;
+ border-bottom: 1px solid #c3c4c7;
+ margin-bottom: 20px;
+}
+
+.wpnav-tab {
+ padding: 8px 16px;
+ border: none;
+ background: none;
+ cursor: pointer;
+ font-size: 14px;
+ border-bottom: 2px solid transparent;
+ color: #646970;
+ text-decoration: none;
+ white-space: nowrap;
+ transition: all 0.2s ease;
+}
+
+.wpnav-tab:hover:not(.active) {
+ background: #f0f0f1;
+ border-bottom-color: #dcdcde;
+ color: #1d2327;
+}
+
+.wpnav-tab.active {
+ border-bottom: 2px solid #0073aa;
+ font-weight: 600;
+ background: #f0f0f1;
+ color: #1d2327;
+}
+
+.wpnav-tab:focus {
+ outline: 2px solid #0073aa;
+ outline-offset: -2px;
+}
+
+.wpnav-tab-content {
+ flex: 1;
+}
+
+.wpnav-tab-section {
+ display: block;
+}
+
+.wpnav-section-header {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ margin-bottom: 15px;
+ padding-bottom: 10px;
+ border-bottom: 1px solid #ddd;
+}
+
+.wpnav-section-header h3 {
+ margin: 0;
+ font-size: 16px;
+ font-weight: 600;
+ color: #23282d;
+}
+
+.wpnav-section-actions {
+ display: flex;
+ gap: 8px;
+}
+
+.wpnav-info-grid {
+ display: grid;
+ grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
+ gap: 20px;
+ margin-top: 20px;
+}
+
+.wpnav-info-item {
+ background: #f9f9f9;
+ border: 1px solid #e5e5e5;
+ border-radius: 4px;
+ padding: 15px;
+}
+
+.wpnav-info-item h3,
+.wpnav-info-item h4 {
+ margin-top: 0;
+ margin-bottom: 10px;
+ font-size: 14px;
+ font-weight: 600;
+ color: #23282d;
+}
+
+.wpnav-export-grid {
+ display: grid;
+ grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
+ gap: 20px;
+ margin-bottom: 20px;
+}
+
+.wpnav-export-item {
+ background: #f6f7f7;
+ border: 1px solid #ddd;
+ border-radius: 4px;
+ padding: 20px;
+ margin-top: 20px;
+ box-shadow: 0 1px 1px rgba(0,0,0,0.04);
+}
+
+.wpnav-export-item h2 {
+ margin-top: 0;
+ margin-bottom: 15px;
+ font-size: 18px;
+ font-weight: 600;
+ border-bottom: 1px solid #ddd;
+ padding-bottom: 10px;
+ color: #23282d;
+}
+
+.wpnav-export-item p {
+ font-size: 13px;
+ color: #50575e;
+ margin-bottom: 15px;
+}
+
+.wpnav-notice {
+ padding: 8px 12px;
+ border-radius: 3px;
+ margin: 10px 0;
+ display: block;
+}
+
+.wpnav-notice-success {
+ background-color: #d1e7dd;
+ border: 1px solid #badbcc;
+ color: #0f5132;
+}
+
+.wpnav-notice-error {
+ background-color: #f8d7da;
+ border: 1px solid #f5c2c7;
+ color: #842029;
+}
+
+.wpnav-auto-rules {
+ margin-top: 20px;
+ padding-top: 20px;
+ border-top: 1px solid #ddd;
+}
+
+.wpnav-auto-rules h4 {
+ margin-top: 0;
+ margin-bottom: 15px;
+ font-size: 15px;
+ font-weight: 600;
+ color: #23282d;
+}
+
+.wpnav-auto-rules .wp-list-table td {
+ padding: 15px 12px;
+ border-radius: 4px;
+}
+
+.wpnav-auto-rules label {
+ font-weight: 600;
+ margin-bottom: 5px;
+ display: block;
+ color: #23282d;
+}
+
+.wpnav-auto-rules .description {
+ margin-top: 5px;
+ font-size: 13px;
+ color: #50575e;
+}
+
+.wpnav-quick-domains {
+ display: flex;
+ flex-wrap: wrap;
+ gap: 8px;
+ margin-top: 10px;
+}
+
+.wpnav-quick-domains .button-small {
+ font-size: 12px;
+ padding: 4px 10px;
+ height: auto;
+ line-height: 1.4;
+ border-radius: 3px;
+}
+
+.wpnav-stats-overview {
+ display: flex;
+ gap: 15px;
+ margin: 20px 0;
+ padding: 15px;
+ background: #f8f9fa;
+ border: 1px solid #dee2e6;
+ border-radius: 6px;
+}
+
+.wpnav-stat-item {
+ text-align: center;
+ flex: 1;
+ padding: 10px;
+}
+
+.wpnav-stat-number {
+ display: block;
+ font-size: 20px;
+ font-weight: 600;
+ color: #495057;
+ line-height: 1.2;
+ margin-bottom: 4px;
+}
+
+.wpnav-stat-label {
+ display: block;
+ font-size: 12px;
+ color: #6c757d;
+ font-weight: 500;
+}
+
+.wpnav-url-link {
+ color: #0073aa;
+ text-decoration: none;
+ transition: color 0.2s ease;
+}
+
+.wpnav-url-link:hover {
+ color: #005a87;
+ text-decoration: underline;
+}
+
+.wpnav-url-link .dashicons {
+ opacity: 0.6;
+ margin-left: 3px;
+ transition: opacity 0.2s ease;
+}
+
+.wpnav-url-link:hover .dashicons {
+ opacity: 1;
+}
+
+.wpnav-no-data,
+.wpnav-no-data-message {
+ color: #50575e;
+ font-style: italic;
+ text-align: center;
+ padding: 30px 20px;
+}
+
+.wpnav-no-data-message p {
+ margin: 8px 0;
+ font-size: 14px;
+}
+
+.wpnav-no-data-message .description {
+ font-size: 13px;
+ color: #8c8f94;
+}
+
+.wpnav-https-badge {
+ display: inline-block;
+ padding: 2px 8px;
+ border-radius: 12px;
+ color: white;
+ font-size: 11px;
+ font-weight: 600;
+ text-transform: uppercase;
+ letter-spacing: 0.5px;
+ min-width: 50px;
+ text-align: center;
+}
+
+.wpnav-https-badge.secure {
+ background-color: #00a32a;
+}
+
+.wpnav-https-badge.insecure {
+ background-color: #d63638;
+}
+
+.wpnav-top-links {
+ margin: 0;
+}
+
+.wpnav-top-link-item {
+ display: flex;
+ align-items: center;
+ padding: 5px 0;
+ border-bottom: 1px solid #f0f0f0;
+}
+
+.wpnav-top-link-item:last-child {
+ border-bottom: none;
+}
+
+.wpnav-rank {
+ font-weight: 700;
+ color: #0073aa;
+ margin-right: 12px;
+ min-width: 30px;
+ font-size: 14px;
+}
+
+.wpnav-link-info {
+ flex: 1;
+ min-width: 0;
+}
+
+.wpnav-link-title {
+ display: block;
+ color: #0073aa;
+ text-decoration: none;
+ font-size: 13px;
+ margin-bottom: 3px;
+ white-space: nowrap;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ transition: color 0.2s ease;
+}
+
+.wpnav-link-title:hover {
+ color: #005a87;
+ text-decoration: underline;
+}
+
+.wpnav-click-count {
+ font-size: 11px;
+ color: #50575e;
+ font-weight: 500;
+}
+
+.column-target-url { width: 30%; }
+.column-source-page { width: 25%; }
+.column-click-time { width: 15%; }
+.column-ip-address { width: 15%; }
+.column-https-status { width: 15%; }
+
+.wp-list-table tbody tr:hover,
+.wpnav-row-hover {
+ background-color: #f6f7f7;
+}
+
+.button-small {
+ font-size: 12px;
+ padding: 6px 10px;
+ line-height: 1.4;
+ height: auto;
+ border-radius: 3px;
+ transition: all 0.2s ease;
+}
+
+.button-small:hover {
+ transform: translateY(-1px);
+}
+
+.wp-list-table.fixed {
+ table-layout: fixed;
+}
+
+.wp-list-table th:first-child {
+ width: 200px;
+}
+
+.wp-list-table td code {
+ background: #f1f1f1;
+ padding: 3px 6px;
+ border-radius: 3px;
+ font-family: Consolas, Monaco, monospace;
+ font-size: 12px;
+}
+
+.form-table th {
+ font-weight: 600;
+ color: #23282d;
+}
+
+.form-table td .description {
+ font-size: 13px;
+ color: #50575e;
+ margin-top: 5px;
+}
+
+.form-table input[type="number"],
+.form-table select {
+ min-width: 100px;
+}
+
+.wpnav-redirect-preview {
+ border: 2px solid #ddd;
+ border-radius: 8px;
+ padding: 20px;
+ background: #f8f9fa;
+ margin-top: 20px;
+ position: relative;
+ overflow: hidden;
+}
+
+.wpnav-redirect-preview::before {
+ content: "Preview";
+ position: absolute;
+ top: 10px;
+ right: 15px;
+ background: #0073aa;
+ color: white;
+ padding: 4px 8px;
+ border-radius: 4px;
+ font-size: 11px;
+ font-weight: 600;
+ text-transform: uppercase;
+ letter-spacing: 0.5px;
+ z-index: 10;
+}
+
+.preview-container {
+ padding: 20px;
+ border-radius: 12px;
+ min-height: 400px;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ transform: scale(0.8);
+ transform-origin: center;
+ transition: all 0.3s ease;
+ overflow: visible;
+}
+
+.preview-container .wpnav-container {
+ max-width: 400px;
+ width: 100%;
+ background: rgba(255, 255, 255, 0.95);
+ backdrop-filter: blur(20px);
+ border-radius: 16px;
+ padding: 30px;
+ text-align: center;
+ box-shadow: 0 20px 40px rgba(0, 0, 0, 0.1);
+ transition: all 0.3s ease;
+ position: relative;
+ z-index: 1;
+}
+
+.preview-container .wpnav-container.wpnav-simple {
+ max-width: 320px;
+ padding: 25px;
+}
+
+.preview-container .wpnav-container.wpnav-minimal {
+ max-width: 360px;
+ padding: 28px;
+}
+
+.preview-container .wpnav-container.wpnav-full {
+ max-width: 480px;
+ padding: 35px;
+ text-align: left;
+}
+
+.preview-container .wpnav-container.wpnav-default {
+ max-width: 400px;
+ padding: 30px;
+ text-align: center;
+}
+
+.preview-container .wpnav-title {
+ font-size: 24px;
+ font-weight: 700;
+ margin: 0 0 15px;
+ color: #2c3e50;
+ transition: font-size 0.3s ease;
+}
+
+.preview-container .wpnav-simple .wpnav-title {
+ font-size: 20px;
+ margin-bottom: 12px;
+}
+
+.preview-container .wpnav-minimal .wpnav-title {
+ font-size: 22px;
+}
+
+.preview-container .wpnav-full .wpnav-title {
+ font-size: 28px;
+ text-align: center;
+}
+
+.preview-container .wpnav-default .wpnav-title {
+ font-size: 24px;
+}
+
+.preview-container .wpnav-subtitle {
+ font-size: 14px;
+ color: #7f8c8d;
+ margin: 5px 0 20px;
+ font-weight: 400;
+}
+
+.preview-container .wpnav-warning {
+ background: #fff3cd;
+ border: 1px solid #f39c12;
+ border-radius: 8px;
+ padding: 15px;
+ margin: 20px 0;
+ color: #8b4513;
+ font-size: 13px;
+}
+
+.preview-container .wpnav-url-container {
+ margin: 15px 0;
+}
+
+.preview-container .wpnav-url-label {
+ font-size: 12px;
+ color: #7f8c8d;
+ margin-bottom: 8px;
+ font-weight: 600;
+ text-transform: uppercase;
+ letter-spacing: 0.5px;
+}
+
+.preview-container .wpnav-url {
+ background: #f8f9fa;
+ border: 2px solid #e9ecef;
+ border-radius: 8px;
+ padding: 15px;
+ margin: 10px 0;
+ word-break: break-all;
+}
+
+.preview-container .wpnav-simple .wpnav-url {
+ padding: 12px;
+}
+
+.preview-container .wpnav-url-domain {
+ font-weight: 700;
+ color: #2c3e50;
+ font-size: 16px;
+}
+
+.preview-container .wpnav-simple .wpnav-url-domain {
+ font-size: 14px;
+}
+
+.preview-container .wpnav-url-full {
+ font-size: 11px;
+ color: #95a5a6;
+ margin-top: 8px;
+ font-family: 'Monaco', 'Consolas', monospace;
+ word-break: break-all;
+}
+
+.preview-container .wpnav-security-status {
+ margin-top: 10px;
+}
+
+.preview-container .wpnav-security-tips {
+ background: #f8f9fa;
+ border: 1px solid #e9ecef;
+ border-radius: 8px;
+ padding: 15px;
+ margin: 15px 0;
+ text-align: left;
+}
+
+.preview-container .wpnav-security-tips h4 {
+ margin: 0 0 10px;
+ font-size: 14px;
+ color: #2c3e50;
+ font-weight: 600;
+}
+
+.preview-container .wpnav-security-tips ul {
+ margin: 0;
+ padding-left: 15px;
+ font-size: 12px;
+ color: #5a6c7d;
+}
+
+.preview-container .wpnav-security-tips li {
+ margin-bottom: 5px;
+}
+
+.preview-container .wpnav-buttons {
+ display: flex;
+ gap: 10px;
+ margin-top: 20px;
+ justify-content: center;
+}
+
+.preview-container .wpnav-simple .wpnav-buttons {
+ gap: 8px;
+ flex-direction: column;
+}
+
+.preview-container .wpnav-btn {
+ flex: 1;
+ padding: 12px 16px;
+ border-radius: 8px;
+ border: none;
+ font-weight: 600;
+ font-size: 14px;
+ cursor: default;
+ transition: all 0.3s ease;
+ max-width: 140px;
+}
+
+.preview-container .wpnav-simple .wpnav-btn {
+ padding: 10px 14px;
+ font-size: 13px;
+ max-width: none;
+}
+
+.preview-container .wpnav-btn-primary {
+ background: #667eea;
+ color: white;
+}
+
+.preview-container .wpnav-btn-secondary {
+ background: rgba(248, 249, 250, 0.9);
+ color: #5a6c7d;
+ border: 2px solid #e9ecef;
+}
+
+.preview-container .wpnav-options {
+ margin: 20px 0 10px;
+}
+
+.preview-container .wpnav-checkbox {
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ gap: 8px;
+ font-size: 12px;
+ color: #5a6c7d;
+}
+
+.preview-container .wpnav-simple .wpnav-checkbox {
+ font-size: 11px;
+}
+
+.preview-container .checkmark {
+ width: 16px;
+ height: 16px;
+ border: 2px solid #bdc3c7;
+ border-radius: 3px;
+ position: relative;
+ flex-shrink: 0;
+}
+
+.preview-container .wpnav-countdown {
+ margin-top: 15px;
+ font-size: 12px;
+ color: #7f8c8d;
+ padding: 10px;
+ background: rgba(52, 152, 219, 0.1);
+ border-radius: 6px;
+ text-align: center;
+}
+
+.preview-container .wpnav-simple .wpnav-countdown {
+ margin-top: 10px;
+ padding: 8px;
+ font-size: 11px;
+}
+
+.preview-container .wpnav-progress-bar {
+ width: 100%;
+ height: 4px;
+ background: rgba(0, 0, 0, 0.1);
+ border-radius: 2px;
+ margin-top: 10px;
+ overflow: hidden;
+}
+
+.preview-container .wpnav-progress-fill {
+ height: 100%;
+ background: #667eea;
+ border-radius: 2px;
+ width: 60%;
+ animation: wpnav-progress 5s linear infinite;
+}
+
+@keyframes wpnav-progress {
+ 0% { width: 100%; }
+ 100% { width: 0%; }
+}
+
+.preview-container .wpnav-btn-rounded {
+ border-radius: 8px;
+}
+
+.preview-container .wpnav-btn-square {
+ border-radius: 0;
+}
+
+.preview-container .wpnav-btn-pill {
+ border-radius: 25px;
+}
+
+.preview-container.wpnav-color-blue .wpnav-btn-primary {
+ background: #3498db;
+}
+
+.preview-container.wpnav-color-green .wpnav-btn-primary {
+ background: #27ae60;
+}
+
+.preview-container.wpnav-color-red .wpnav-btn-primary {
+ background: #e74c3c;
+}
+
+.wpnav-loading {
+ opacity: 0.6;
+ pointer-events: none;
+}
+
+.wpnav-loading::after {
+ content: "";
+ position: absolute;
+ top: 50%;
+ left: 50%;
+ width: 20px;
+ height: 20px;
+ margin: -10px 0 0 -10px;
+ border: 2px solid #0073aa;
+ border-radius: 50%;
+ border-top-color: transparent;
+ animation: wpnav-spin 1s linear infinite;
+}
+
+@keyframes wpnav-spin {
+ to {
+ transform: rotate(360deg);
+ }
+}
+
+@media screen and (max-width: 1024px) {
+ .wpnav-info-grid,
+ .wpnav-export-grid {
+ grid-template-columns: 1fr;
+ }
+}
+
+@media screen and (max-width: 768px) {
+ .wrap {
+ margin: 10px;
+ }
+
+ .card {
+ padding: 15px;
+ margin-top: 15px;
+ }
+
+ .wpnav-stats-overview {
+ flex-direction: column;
+ gap: 10px;
+ }
+
+ .wpnav-section-header {
+ flex-direction: column;
+ align-items: flex-start;
+ gap: 10px;
+ }
+
+ .wpnav-section-actions {
+ width: 100%;
+ justify-content: flex-start;
+ }
+
+ .wpnav-quick-domains {
+ flex-direction: column;
+ align-items: stretch;
+ }
+
+ .wpnav-quick-domains .button-small {
+ text-align: center;
+ margin-bottom: 5px;
+ }
+
+ .preview-container {
+ transform: scale(0.7);
+ min-height: 320px;
+ }
+
+ .wpnav-redirect-preview {
+ padding: 15px;
+ }
+
+ .wpnav-activity-stats {
+ grid-template-columns: 1fr;
+ gap: 10px;
+ }
+
+ .wpnav-activity-item {
+ padding: 12px 8px;
+ }
+
+ .wpnav-activity-number {
+ font-size: 20px;
+ }
+}
+
+@media screen and (max-width: 600px) {
+ .wpnav-stat-number {
+ font-size: 18px;
+ }
+
+ .wp-list-table {
+ font-size: 12px;
+ }
+
+ .wp-list-table th,
+ .wp-list-table td {
+ padding: 8px 5px;
+ }
+
+ .column-target-url,
+ .column-source-page {
+ width: 35%;
+ }
+
+ .column-click-time,
+ .column-ip-address,
+ .column-https-status {
+ width: 10%;
+ }
+
+ .preview-container {
+ transform: scale(0.6);
+ min-height: 280px;
+ }
+
+ .wpnav-activity-stats {
+ grid-template-columns: 1fr;
+ gap: 8px;
+ }
+
+ .wpnav-activity-item {
+ padding: 10px 6px;
+ }
+
+ .wpnav-activity-number {
+ font-size: 18px;
+ }
+
+ .wpnav-activity-label {
+ font-size: 10px;
+ }
+}
+
+.wpnav-activity-stats {
+ display: grid;
+ grid-template-columns: 1fr 1fr;
+ gap: 15px;
+ margin-top: 10px;
+}
+
+.wpnav-activity-item {
+ text-align: center;
+ padding: 5px 5px;
+ background: #ffffff;
+ border: 1px solid #e2e4e7;
+ border-radius: 6px;
+ transition: all 0.2s ease;
+}
+
+.wpnav-activity-item:hover {
+ background: #ffffff;
+ border-color: #72aee6;
+ box-shadow: 0 2px 8px rgba(0,0,0,0.1);
+}
+
+.wpnav-activity-number {
+ display: block;
+ font-size: 20px;
+ font-weight: 600;
+ color: #2271b1;
+ line-height: 1.2;
+ margin-bottom: 5px;
+}
+
+.wpnav-activity-label {
+ display: block;
+ font-size: 11px;
+ color: #646970;
+ font-weight: 600;
+ text-transform: uppercase;
+ letter-spacing: 0.5px;
+}
+
+.wpnav-tabs .nav-tab:focus,
+.button:focus,
+input:focus,
+textarea:focus,
+select:focus {
+ outline: 2px solid #0073aa;
+ outline-offset: 2px;
+}
diff --git a/admin.js b/admin.js
new file mode 100644
index 0000000..2bc569b
--- /dev/null
+++ b/admin.js
@@ -0,0 +1,494 @@
+jQuery(document).ready(function($) {
+ $('.wpnav-tab').click(function(e) {
+ e.preventDefault();
+
+ $('.wpnav-tab').removeClass('active');
+ $(this).addClass('active');
+
+ var tab = $(this).data('tab');
+ $('.wpnav-tab-section').hide();
+ $('.wpnav-tab-section[data-section="' + tab + '"]').show();
+
+ if (history.pushState) {
+ var newUrl = updateUrlParameter(window.location.href, 'tab', tab);
+ history.pushState({path: newUrl}, '', newUrl);
+ }
+ });
+
+ $('.wpnav-add-domain').click(function(e) {
+ e.preventDefault();
+ var domain = $(this).data('domain');
+ var $textarea = $('textarea[name="whitelist_domains"]');
+ var domains = $textarea.val().split('\n').filter(function(d) { return d.trim(); });
+
+ if (domains.indexOf(domain) >= 0) {
+ showNotice('wpnav-whitelist-status', wpnav_admin.strings.domain_exists.replace('%s', domain), 'error');
+ return;
+ }
+
+ domains.push(domain);
+ $textarea.val(domains.join('\n'));
+ showNotice('wpnav-whitelist-status', wpnav_admin.strings.domain_added.replace('%s', domain), 'success');
+
+ $(this).addClass('wpnav-loading').prop('disabled', true);
+ setTimeout(function() {
+ $(this).removeClass('wpnav-loading').prop('disabled', false);
+ }.bind(this), 1000);
+ });
+
+ $('#wpnav-select-all-domains').click(function(e) {
+ e.preventDefault();
+ var commonDomains = [
+ 'google.com', 'facebook.com', 'youtube.com', 'twitter.com',
+ 'instagram.com', 'linkedin.com', 'baidu.com', 'bing.com'
+ ];
+ var $textarea = $('textarea[name="whitelist_domains"]');
+ var currentDomains = $textarea.val().split('\n').filter(function(d) { return d.trim(); });
+ var newDomains = [...new Set([...currentDomains, ...commonDomains])];
+
+ $textarea.val(newDomains.join('\n'));
+ showNotice('wpnav-whitelist-status', wpnav_admin.strings.common_domains_added, 'success');
+ });
+
+ $('#wpnav-clear-domains').click(function(e) {
+ e.preventDefault();
+ if (confirm(wpnav_admin.strings.confirm_clear)) {
+ $('textarea[name="whitelist_domains"]').val('');
+ showNotice('wpnav-whitelist-status', wpnav_admin.strings.whitelist_cleared, 'success');
+ }
+ });
+
+ $('#wpnav-export-whitelist').click(function(e) {
+ e.preventDefault();
+
+ var whitelist = $('textarea[name="whitelist_domains"]').val();
+ var domains = whitelist.split('\n').filter(function(d) { return d.trim(); });
+
+ if (domains.length === 0) {
+ showNotice('wpnav-whitelist-status', wpnav_admin.strings.whitelist_empty, 'error');
+ return;
+ }
+
+ var csv = domains.join('\n');
+ var blob = new Blob([csv], { type: 'text/csv;charset=utf-8;' });
+ var link = document.createElement('a');
+
+ if (link.download !== undefined) {
+ var url = URL.createObjectURL(blob);
+ link.setAttribute('href', url);
+ link.setAttribute('download', 'wpnav_whitelist_' + getFormattedDate() + '.csv');
+ link.style.visibility = 'hidden';
+ document.body.appendChild(link);
+ link.click();
+ document.body.removeChild(link);
+
+ showNotice('wpnav-whitelist-status', wpnav_admin.strings.export_success, 'success');
+ } else {
+ showNotice('wpnav-whitelist-status', wpnav_admin.strings.export_unsupported, 'error');
+ }
+ });
+
+ $('form').submit(function(e) {
+ var $form = $(this);
+ var $submitButton = $form.find('input[type="submit"], button[type="submit"]');
+
+ if ($form.attr('id') === 'sync-settings-form' || $form.attr('id') === 'sync-config-form') {
+ return true;
+ }
+
+ var formValid = validateForm($form);
+ if (!formValid) {
+ e.preventDefault();
+ return false;
+ }
+
+ if ($submitButton.length) {
+ var originalValue = $submitButton.val() || $submitButton.text();
+ $submitButton.prop('disabled', true).addClass('wpnav-loading');
+
+ if ($submitButton.is('input')) {
+ $submitButton.val(wpnav_admin.strings.saving);
+ } else {
+ $submitButton.text(wpnav_admin.strings.saving);
+ }
+
+ setTimeout(function() {
+ $submitButton.prop('disabled', false).removeClass('wpnav-loading');
+ if ($submitButton.is('input')) {
+ $submitButton.val(originalValue);
+ } else {
+ $submitButton.text(originalValue);
+ }
+ }, 10000);
+ }
+ });
+
+ if (window.location.search.indexOf('settings-updated=true') !== -1) {
+ var message = wpnav_admin.strings.settings_saved || 'Settings saved successfully!';
+ if (window.location.search.indexOf('imported=') !== -1) {
+ var match = window.location.search.match(/imported=(\d+)/);
+ if (match) {
+ message = wpnav_admin.strings.domains_imported ?
+ wpnav_admin.strings.domains_imported.replace('%d', match[1]) :
+ 'Successfully imported ' + match[1] + ' domains.';
+ }
+ }
+
+ $('.wrap h1').after('');
+
+ setTimeout(function() {
+ $('.notice-success').fadeIn();
+ }, 100);
+ }
+
+ if (window.location.search.indexOf('tab=') !== -1) {
+ setTimeout(function() {
+ $('.notice-success').fadeIn();
+ }, 100);
+ }
+
+ $('.wp-list-table tbody tr').hover(
+ function() {
+ $(this).addClass('wpnav-row-hover');
+ },
+ function() {
+ $(this).removeClass('wpnav-row-hover');
+ }
+ );
+
+ $('#auto_redirect_enabled').change(function() {
+ if ($(this).is(':checked')) {
+ $('#redirect_delay_row').show();
+ } else {
+ $('#redirect_delay_row').hide();
+ }
+ });
+
+ // Enhanced preview functionality
+ $('#page_title').on('input', function() {
+ $('#preview-page-title').text($(this).val());
+ });
+
+ $('#page_subtitle').on('input', function() {
+ var subtitle = $(this).val();
+ if (subtitle) {
+ $('#preview-page-subtitle').text(subtitle).show();
+ } else {
+ $('#preview-page-subtitle').hide();
+ }
+ });
+
+ $('#url_label').on('input', function() {
+ $('#preview-url-label').text($(this).val());
+ });
+
+ $('#warning_text').on('input', function() {
+ $('#preview-warning-text').text($(this).val());
+ });
+
+ $('#countdown_text').on('input', function() {
+ var text = $(this).val().replace('{seconds}', '5');
+ $('#preview-countdown-text').text(text);
+ });
+
+ $('#button_text_continue').on('input', function() {
+ $('#preview-continue-btn').text($(this).val());
+ });
+
+ $('#button_text_back').on('input', function() {
+ $('#preview-back-btn').text($(this).val());
+ });
+
+ // Show/hide warning message functionality
+ $('input[name="show_warning_message"], textarea[name="warning_text"]').on('change input', function() {
+ var showWarning = $('input[name="show_warning_message"]').is(':checked');
+ var warningText = $('textarea[name="warning_text"]').val().trim();
+
+ if (showWarning && warningText !== '') {
+ $('#preview-warning').show();
+ } else {
+ $('#preview-warning').hide();
+ }
+ });
+
+ function setupRedirectPagePreview() {
+ function updatePreview() {
+ var colorScheme = $('select[name="color_scheme"]').val();
+ var template = $('input[name="template"]:checked').val();
+ var warningText = $('textarea[name="warning_text"]').val();
+ var showWarningMessage = $('input[name="show_warning_message"]').is(':checked');
+ var continueText = $('input[name="button_text_continue"]').val();
+ var backText = $('input[name="button_text_back"]').val();
+ var showLogo = $('input[name="show_logo"]').is(':checked');
+ var showUrlFull = $('input[name="show_url_full"]').is(':checked');
+ var showSecurityTips = $('input[name="show_security_tips"]').is(':checked');
+ var showBackButton = $('input[name="show_back_button"]').is(':checked');
+
+ console.log('Updating preview - Template:', template, 'Color:', colorScheme);
+
+ var $preview = $('.preview-container');
+ $preview.removeClass('wpnav-color-blue wpnav-color-green wpnav-color-red');
+ $preview.addClass('wpnav-color-' + colorScheme);
+
+ var $container = $preview.find('.wpnav-container');
+ $container.removeClass('wpnav-simple wpnav-minimal wpnav-default wpnav-full');
+ $container.addClass('wpnav-' + template);
+
+ $('#preview-warning-text').text(warningText);
+ $('#preview-continue-btn').text(continueText);
+ $('#preview-back-btn').text(backText);
+
+ $('.wpnav-site-logo').toggle(showLogo);
+ $('.wpnav-url-full').toggle(showUrlFull);
+ $('.wpnav-security-tips').toggle(showSecurityTips);
+ $('#preview-back-btn').toggle(showBackButton);
+ $('#preview-warning').toggle(showWarningMessage && warningText.trim() !== '');
+
+ updateTemplateSpecificElements(template);
+ }
+
+ function updateTemplateSpecificElements(template) {
+ var $container = $('.wpnav-container');
+ var $header = $('.wpnav-header');
+
+ switch(template) {
+ case 'simple':
+ $container.css({
+ 'max-width': '480px',
+ 'padding': '30px 25px',
+ 'text-align': 'center'
+ });
+ $header.find('.wpnav-title').css('font-size', '22px');
+ break;
+
+ case 'minimal':
+ $container.css({
+ 'max-width': '680px',
+ 'padding': '30px',
+ 'text-align': 'center'
+ });
+ $header.find('.wpnav-title').css('font-size', '24px');
+ break;
+
+ case 'full':
+ $container.css({
+ 'max-width': '880px',
+ 'padding': '50px',
+ 'text-align': 'left'
+ });
+ $header.find('.wpnav-title').css('font-size', '36px');
+ break;
+
+ default: // default
+ $container.css({
+ 'max-width': '780px',
+ 'padding': '40px',
+ 'text-align': 'center'
+ });
+ $header.find('.wpnav-title').css('font-size', '28px');
+ break;
+ }
+ }
+
+ $('select[name="color_scheme"]').on('change', updatePreview);
+ $('input[name="template"]').on('change', updatePreview);
+ $('textarea[name="warning_text"]').on('input', updatePreview);
+ $('input[name="show_warning_message"]').on('change', updatePreview);
+ $('input[name="button_text_continue"]').on('input', updatePreview);
+ $('input[name="button_text_back"]').on('input', updatePreview);
+ $('input[name="show_logo"]').on('change', updatePreview);
+ $('input[name="show_url_full"]').on('change', updatePreview);
+ $('input[name="show_security_tips"]').on('change', updatePreview);
+ $('input[name="show_back_button"]').on('change', updatePreview);
+
+ updatePreview();
+ }
+
+ if ($('.wpnav-tab[data-tab="redirect_page"]').length) {
+ setupRedirectPagePreview();
+ }
+
+ $('#redirect_delay, #cookie_duration, #stats_retention').on('input', function() {
+ var $this = $(this);
+ var val = parseInt($this.val());
+ var min = parseInt($this.attr('min'));
+ var max = parseInt($this.attr('max'));
+
+ if (val < min || val > max) {
+ $this.addClass('error').css('border-color', '#dc3232');
+ } else {
+ $this.removeClass('error').css('border-color', '');
+ }
+ });
+
+ var draftTimeout;
+ $('textarea[name="whitelist_domains"], textarea[name="warning_text"], textarea[name="custom_css"]').on('input', function() {
+ var $this = $(this);
+ clearTimeout(draftTimeout);
+
+ draftTimeout = setTimeout(function() {
+ var draftKey = 'wpnav_draft_' + $this.attr('name');
+ try {
+ sessionStorage.setItem(draftKey, $this.val());
+
+ if ($this.siblings('.draft-saved').length === 0) {
+ $this.after('' + wpnav_admin.strings.draft_saved + '');
+ setTimeout(function() {
+ $('.draft-saved').fadeOut();
+ }, 2000);
+ }
+ } catch (e) {
+ console.log('Session storage not available');
+ }
+ }, 2000);
+ });
+
+ $('textarea[name="whitelist_domains"], textarea[name="warning_text"], textarea[name="custom_css"]').each(function() {
+ var $this = $(this);
+ var draftKey = 'wpnav_draft_' + $this.attr('name');
+
+ try {
+ var draft = sessionStorage.getItem(draftKey);
+ if (draft && draft !== $this.val() && draft.trim() !== '') {
+ var restore = confirm(wpnav_admin.strings.restore_draft);
+ if (restore) {
+ $this.val(draft);
+ if ($this.attr('name') === 'warning_text') {
+ $('#preview-warning-text').text(draft);
+ }
+ }
+ }
+ } catch (e) {
+ console.log('Session storage not available');
+ }
+ });
+
+ $('form').on('submit', function() {
+ setTimeout(function() {
+ if (window.location.search.indexOf('settings-updated=true') !== -1) {
+ try {
+ sessionStorage.removeItem('wpnav_draft_whitelist_domains');
+ sessionStorage.removeItem('wpnav_draft_warning_text');
+ sessionStorage.removeItem('wpnav_draft_custom_css');
+ } catch (e) {
+ console.log('Session storage not available');
+ }
+ }
+ }, 100);
+ });
+
+ function validateForm($form) {
+ var isValid = true;
+ var errors = [];
+
+ var redirectDelay = $('#redirect_delay').val();
+ if (redirectDelay && (redirectDelay < 1 || redirectDelay > 30)) {
+ errors.push('Redirect delay must be between 1 and 30 seconds.');
+ isValid = false;
+ }
+
+ var cookieDuration = $('#cookie_duration').val();
+ if (cookieDuration && (cookieDuration < 1 || cookieDuration > 365)) {
+ errors.push('Cookie duration must be between 1 and 365 days.');
+ isValid = false;
+ }
+
+ var statsRetention = $('#stats_retention').val();
+ if (statsRetention && (statsRetention < 1 || statsRetention > 365)) {
+ errors.push('Stats retention must be between 1 and 365 days.');
+ isValid = false;
+ }
+
+ if (!isValid) {
+ var tabName = $form.closest('.wpnav-tab-section').attr('data-section');
+ var statusId = 'wpnav-settings-status';
+
+ if (tabName === 'whitelist') {
+ statusId = 'wpnav-whitelist-status';
+ } else if (tabName === 'redirect_page') {
+ statusId = 'wpnav-redirect-status';
+ } else if (tabName === 'logs_statistics') {
+ statusId = 'wpnav-logs-status';
+ }
+
+ showNotice(statusId, errors.join(' '), 'error');
+ }
+
+ return isValid;
+ }
+
+ function updateUrlParameter(url, param, paramVal) {
+ var newAdditionalURL = "";
+ var tempArray = url.split("?");
+ var baseURL = tempArray[0];
+ var additionalURL = tempArray[1];
+ var temp = "";
+
+ if (additionalURL) {
+ tempArray = additionalURL.split("&");
+ for (var i = 0; i < tempArray.length; i++) {
+ if (tempArray[i].split('=')[0] != param) {
+ newAdditionalURL += temp + tempArray[i];
+ temp = "&";
+ }
+ }
+ }
+
+ var rows_txt = temp + "" + param + "=" + paramVal;
+ return baseURL + "?" + newAdditionalURL + rows_txt;
+ }
+
+ function showNotice(elementId, message, type) {
+ var $notice = $('#' + elementId);
+
+ if ($notice.length === 0) {
+ $notice = $('');
+ $('.card h2').first().after($notice);
+ }
+
+ $notice.removeClass('wpnav-notice-success wpnav-notice-error')
+ .addClass('wpnav-notice-' + type)
+ .text(message)
+ .show()
+ .delay(4000)
+ .fadeOut();
+ }
+
+ function getFormattedDate() {
+ var now = new Date();
+ var year = now.getFullYear();
+ var month = ('0' + (now.getMonth() + 1)).slice(-2);
+ var day = ('0' + now.getDate()).slice(-2);
+ return year + month + day;
+ }
+
+ function initColorSchemePreview() {
+ $('select[name="color_scheme"]').on('change', function() {
+ var scheme = $(this).val();
+ var $preview = $('.wpnav-redirect-preview');
+ $preview.removeClass('wpnav-color-blue wpnav-color-green wpnav-color-red wpnav-color-light');
+ $preview.addClass('wpnav-color-' + scheme);
+ });
+ }
+
+ if ($('.wpnav-redirect-preview').length) {
+ initColorSchemePreview();
+ }
+
+ console.log('WPNav Links Admin initialized');
+
+ var activeTab = $('.wpnav-tab.active').data('tab') || 'basic_settings';
+ if (activeTab) {
+ $('.wpnav-tab-section').hide();
+ $('.wpnav-tab-section[data-section="' + activeTab + '"]').show();
+ }
+
+ var urlParams = new URLSearchParams(window.location.search);
+ var tabFromUrl = urlParams.get('tab');
+ if (tabFromUrl) {
+ $('.wpnav-tab').removeClass('active');
+ $('.wpnav-tab[data-tab="' + tabFromUrl + '"]').addClass('active');
+ $('.wpnav-tab-section').hide();
+ $('.wpnav-tab-section[data-section="' + tabFromUrl + '"]').show();
+ }
+});
diff --git a/assets/i-like-food.svg b/assets/i-like-food.svg
new file mode 100644
index 0000000..212d82f
--- /dev/null
+++ b/assets/i-like-food.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/assets/leaf.svg b/assets/leaf.svg
new file mode 100644
index 0000000..a4565a6
--- /dev/null
+++ b/assets/leaf.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/class-admin.php b/class-admin.php
new file mode 100644
index 0000000..b59fd3f
--- /dev/null
+++ b/class-admin.php
@@ -0,0 +1,131 @@
+options = $options;
+ $this->plugin = new WPNAV_Links();
+
+ add_action('admin_menu', array($this, 'add_admin_menu'));
+ add_action('admin_enqueue_scripts', array($this, 'enqueue_admin_scripts'));
+ add_filter('plugin_action_links_wpnav-links/wpnav-links.php', array($this, 'add_settings_link'));
+ add_action('wp_ajax_wpnav_export_stats', array($this, 'ajax_export_stats'));
+ }
+
+ public function add_admin_menu() {
+ add_submenu_page(
+ 'tools.php',
+ __('External Links Redirect', 'wpnav-links'),
+ __('External Links', 'wpnav-links'),
+ 'manage_options',
+ 'wpnav-links',
+ array($this, 'display_admin_page')
+ );
+ }
+
+ public function enqueue_admin_scripts($hook) {
+ if ($hook !== 'tools_page_wpnav-links') {
+ return;
+ }
+
+ wp_enqueue_style(
+ 'wpnav-admin-style',
+ WPNAV_LINKS_PLUGIN_URL . 'admin.css',
+ array(),
+ WPNAV_LINKS_VERSION
+ );
+
+ wp_enqueue_script(
+ 'wpnav-admin-script',
+ WPNAV_LINKS_PLUGIN_URL . 'admin.js',
+ array('jquery'),
+ WPNAV_LINKS_VERSION,
+ true
+ );
+
+ wp_localize_script(
+ 'wpnav-admin-script',
+ 'wpnav_admin',
+ array(
+ 'ajax_url' => admin_url('admin-ajax.php'),
+ 'nonce' => wp_create_nonce('wpnav_admin_nonce'),
+ 'strings' => array(
+ 'domain_exists' => __('Domain %s is already in the whitelist.', 'wpnav-links'),
+ 'domain_added' => __('Added %s to whitelist.', 'wpnav-links'),
+ 'common_domains_added' => __('Added common domains to whitelist.', 'wpnav-links'),
+ 'whitelist_cleared' => __('Whitelist cleared.', 'wpnav-links'),
+ 'confirm_clear' => __('Are you sure you want to clear all domains from the whitelist?', 'wpnav-links'),
+ 'whitelist_empty' => __('Whitelist is empty, cannot export.', 'wpnav-links'),
+ 'export_success' => __('Whitelist exported successfully.', 'wpnav-links'),
+ 'export_unsupported' => __('Export not supported in this browser.', 'wpnav-links'),
+ 'saving' => __('Saving...', 'wpnav-links'),
+ 'draft_saved' => __('Draft saved', 'wpnav-links'),
+ 'restore_draft' => __('A draft was found for this field. Would you like to restore it?', 'wpnav-links'),
+ 'settings_saved' => __('Settings saved successfully!', 'wpnav-links'),
+ 'domains_imported' => __('Successfully imported %d domains.', 'wpnav-links')
+ )
+ )
+ );
+ }
+
+ public function add_settings_link($links) {
+ $settings_link = '' . __('Settings', 'wpnav-links') . '';
+ array_unshift($links, $settings_link);
+ return $links;
+ }
+
+ public function display_admin_page() {
+ include WPNAV_LINKS_PLUGIN_DIR . 'admin-page.php';
+ }
+
+ public function ajax_export_stats() {
+ if (!check_ajax_referer('wpnav_admin_nonce', 'nonce', false)) {
+ wp_send_json_error(array('message' => __('Security check failed', 'wpnav-links')));
+ }
+
+ if (!current_user_can('manage_options')) {
+ wp_send_json_error(array('message' => __('Insufficient permissions', 'wpnav-links')));
+ }
+
+ $start_date = isset($_POST['start_date']) ? sanitize_text_field($_POST['start_date']) : '';
+ $end_date = isset($_POST['end_date']) ? sanitize_text_field($_POST['end_date']) : '';
+
+ header('Content-Type: text/csv; charset=utf-8');
+ header('Content-Disposition: attachment; filename=wpnav_stats_' . date('Y-m-d') . '.csv');
+
+ $output = fopen('php://output', 'w');
+
+ fputcsv($output, array(
+ __('Target URL', 'wpnav-links'),
+ __('Source Page', 'wpnav-links'),
+ __('Click Time', 'wpnav-links'),
+ __('IP Address', 'wpnav-links'),
+ __('User Agent', 'wpnav-links')
+ ));
+
+ $stats = $this->plugin->get_stats(array(
+ 'start_date' => $start_date,
+ 'end_date' => $end_date,
+ 'limit' => 5000
+ ));
+
+ foreach ($stats as $row) {
+ fputcsv($output, array(
+ $row->target_url,
+ $row->source_page,
+ $row->click_time,
+ $row->user_ip,
+ isset($row->user_agent) ? $row->user_agent : ''
+ ));
+ }
+
+ fclose($output);
+ wp_die();
+ }
+}
diff --git a/class-public.php b/class-public.php
new file mode 100644
index 0000000..35e3a4a
--- /dev/null
+++ b/class-public.php
@@ -0,0 +1,323 @@
+options = $options;
+ $this->security = $security;
+
+ if (isset($options['enabled']) && $options['enabled']) {
+ add_action('wp_enqueue_scripts', array($this, 'enqueue_scripts'));
+ $this->setup_content_filters();
+ add_action('wp_footer', array($this, 'add_footer_script'));
+ }
+ }
+
+ public function enqueue_scripts() {
+ wp_enqueue_script(
+ 'wpnav-redirect-script',
+ WPNAV_LINKS_PLUGIN_URL . 'redirect.js',
+ array('jquery'),
+ WPNAV_LINKS_VERSION,
+ true
+ );
+
+ wp_localize_script(
+ 'wpnav-redirect-script',
+ 'wpnav_params',
+ array(
+ 'home_url' => home_url(),
+ 'site_domain' => parse_url(home_url(), PHP_URL_HOST),
+ 'exclude_class' => $this->options['exclude_css_class'],
+ 'open_new_tab' => $this->options['open_in_new_tab'],
+ 'redirect_method' => isset($this->options['url_format']) ? $this->options['url_format'] : 'query',
+ 'url_encoding' => 'base64',
+ 'permalink_structure' => get_option('permalink_structure') ? true : false,
+ 'whitelist_domains' => $this->get_whitelist_domains(),
+ 'strings' => array(
+ 'external_link' => __('External link', 'wpnav-links'),
+ 'opens_new_window' => __('(opens in a new window)', 'wpnav-links')
+ )
+ )
+ );
+ }
+
+ private function get_whitelist_domains() {
+ $whitelist = array();
+ if (!empty($this->options['whitelist_domains'])) {
+ $whitelist = explode("\n", $this->options['whitelist_domains']);
+ $whitelist = array_map('trim', $whitelist);
+ $whitelist = array_filter($whitelist);
+ }
+ return $whitelist;
+ }
+
+ private function setup_content_filters() {
+ if (isset($this->options['intercept_content']) && $this->options['intercept_content']) {
+ add_filter('the_content', array($this, 'process_content'));
+ }
+
+ if (isset($this->options['intercept_comments']) && $this->options['intercept_comments']) {
+ add_filter('comment_text', array($this, 'process_content'));
+ }
+
+ if (isset($this->options['intercept_widgets']) && $this->options['intercept_widgets']) {
+ add_filter('widget_text', array($this, 'process_content'));
+ add_filter('widget_custom_html_content', array($this, 'process_content'));
+ }
+ }
+
+ public function process_content($content) {
+ if (empty($content)) {
+ return $content;
+ }
+
+ $dom = new DOMDocument();
+ libxml_use_internal_errors(true);
+
+ $dom->loadHTML('' . $content . '
', LIBXML_HTML_NOIMPLIED | LIBXML_HTML_NODEFDTD);
+ libxml_clear_errors();
+
+ $links = $dom->getElementsByTagName('a');
+ $modified = false;
+
+ $links_to_process = array();
+ foreach ($links as $link) {
+ $links_to_process[] = $link;
+ }
+
+ foreach ($links_to_process as $link) {
+ $href = $link->getAttribute('href');
+ if (empty($href)) {
+ continue;
+ }
+
+ if (!empty($this->options['exclude_css_class'])) {
+ $class = $link->getAttribute('class');
+ if (strpos($class, $this->options['exclude_css_class']) !== false) {
+ continue;
+ }
+ }
+
+ if ($this->is_external_link($href)) {
+ if ($this->security->check_whitelist_domain($href)) {
+ continue;
+ }
+
+ $rel = $link->getAttribute('rel');
+ $rel_values = empty($rel) ? array() : explode(' ', $rel);
+ $rel_values = array_merge($rel_values, array('nofollow', 'noopener', 'noreferrer'));
+ $rel_values = array_unique($rel_values);
+ $link->setAttribute('rel', implode(' ', $rel_values));
+
+ if ($this->options['open_in_new_tab']) {
+ $link->setAttribute('target', '_blank');
+
+ // Add screen reader text for accessibility
+ $title = $link->getAttribute('title');
+ if (empty($title)) {
+ $link->setAttribute('title', __('External link (opens in a new window)', 'wpnav-links'));
+ }
+ }
+
+ $redirect_url = $this->security->get_redirect_url($href);
+ $link->setAttribute('href', $redirect_url);
+ $link->setAttribute('data-wpnav-external', '1');
+
+ $modified = true;
+ }
+ }
+
+ if (!$modified) {
+ return $content;
+ }
+
+ $new_html = $dom->saveHTML($dom->documentElement);
+ $new_html = preg_replace('/<\?xml encoding="UTF-8"\?>/', '', $new_html);
+ $new_html = preg_replace('/<\/?div>/', '', $new_html);
+
+ return $new_html;
+ }
+
+ private function is_external_link($url) {
+ if (strpos($url, '://') === false && strpos($url, '//') !== 0) {
+ return false;
+ }
+
+ $parsed_url = parse_url($url);
+
+ if (empty($parsed_url) || !isset($parsed_url['host'])) {
+ return false;
+ }
+
+ $site_domain = parse_url(home_url(), PHP_URL_HOST);
+ $is_external = ($parsed_url['host'] !== $site_domain);
+
+ if ($is_external) {
+ if (!empty($this->options['auto_whitelist'])) {
+ if (!empty($this->options['auto_whitelist']['same_root'])) {
+ $site_root = $this->get_root_domain($site_domain);
+ $url_root = $this->get_root_domain($parsed_url['host']);
+
+ if ($site_root === $url_root) {
+ $is_external = false;
+ }
+ }
+
+ if ($is_external && !empty($this->options['auto_whitelist']['search_engines'])) {
+ $search_engines = array('google.', 'bing.', 'yahoo.', 'baidu.', 'yandex.', 'duckduckgo.');
+ foreach ($search_engines as $engine) {
+ if (strpos($parsed_url['host'], $engine) !== false) {
+ $is_external = false;
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ return $is_external;
+ }
+
+ private function get_root_domain($domain) {
+ $domain_parts = explode('.', $domain);
+ if (count($domain_parts) > 2) {
+ $tld_part = array_slice($domain_parts, -2, 2);
+ if (in_array($tld_part[0], array('co', 'com', 'net', 'org', 'gov', 'edu'))) {
+ return implode('.', array_slice($domain_parts, -3, 3));
+ }
+ return implode('.', array_slice($domain_parts, -2, 2));
+ }
+ return $domain;
+ }
+
+ public function add_footer_script() {
+ ?>
+
+ table_name = $wpdb->prefix . WPNAV_LINKS_TABLE;
+ }
+
+ public function init() {
+ add_action('plugins_loaded', array($this, 'load_textdomain'));
+
+ $this->options = get_option('wpnav_links_options');
+
+ if (is_admin()) {
+ new WPNAV_Admin($this->options);
+ }
+
+ new WPNAV_Public($this->options, $this);
+
+ add_action('init', array($this, 'add_rewrite_rules'));
+ add_filter('query_vars', array($this, 'add_query_vars'));
+ add_action('template_redirect', array($this, 'handle_external_redirect'));
+ add_action('admin_bar_menu', array($this, 'add_admin_bar_menu'), 100);
+ }
+
+ public function load_textdomain() {
+ load_plugin_textdomain('wpnav-links', false, dirname(plugin_basename(__FILE__)) . '/languages/');
+ }
+
+ public function add_rewrite_rules() {
+ $url_format = isset($this->options['url_format']) ? $this->options['url_format'] : 'query';
+
+ if ($url_format === 'path') {
+ add_rewrite_rule('^go/([^/]+)/?$', 'index.php?wpnav_redirect=1&url_param=$matches[1]', 'top');
+ } else {
+ add_rewrite_rule('^go/?$', 'index.php?wpnav_redirect=1', 'top');
+ }
+ }
+
+ public function add_query_vars($vars) {
+ $vars[] = 'wpnav_redirect';
+ $vars[] = 'target';
+ $vars[] = 'url_param';
+ $vars[] = 'ref';
+ $vars[] = 'url';
+ return $vars;
+ }
+
+ public function handle_external_redirect() {
+ global $wp_query;
+
+ if (!isset($wp_query->query_vars['wpnav_redirect']) || $wp_query->query_vars['wpnav_redirect'] != 1) {
+ return;
+ }
+
+ $url = $this->get_redirect_url_from_request();
+
+ if (empty($url)) {
+ $this->handle_redirect_error(__('Invalid or missing URL parameter', 'wpnav-links'));
+ return;
+ }
+
+ if (!$this->validate_url($url)) {
+ $this->handle_redirect_error(__('Invalid URL format', 'wpnav-links'));
+ return;
+ }
+
+ $ref = isset($wp_query->query_vars['ref']) ? esc_url_raw($wp_query->query_vars['ref']) : wp_get_referer();
+
+ set_query_var('target_url', $url);
+ set_query_var('source_url', $ref);
+
+ if ($this->should_skip_redirect($url)) {
+ wp_redirect($url, 302);
+ exit;
+ }
+
+ $this->record_redirect($url, $ref);
+ $this->load_redirect_template($url, $ref);
+ exit;
+ }
+
+ private function get_redirect_url_from_request() {
+ global $wp_query;
+
+ $url_format = isset($this->options['url_format']) ? $this->options['url_format'] : 'query';
+ $url = '';
+
+ if ($url_format === 'path') {
+ if (isset($wp_query->query_vars['url_param'])) {
+ $url_param = $wp_query->query_vars['url_param'];
+ $decoded = base64_decode($url_param);
+ if ($decoded !== false) {
+ $url = $decoded;
+ }
+ }
+ } elseif ($url_format === 'target') {
+ if (isset($wp_query->query_vars['target'])) {
+ $url = urldecode($wp_query->query_vars['target']);
+ } elseif (isset($_GET['target'])) {
+ $url = urldecode(sanitize_text_field($_GET['target']));
+ }
+ } else {
+ if (isset($wp_query->query_vars['target'])) {
+ $url = urldecode($wp_query->query_vars['target']);
+ } elseif (isset($_GET['target'])) {
+ $url = urldecode(sanitize_text_field($_GET['target']));
+ } elseif (isset($wp_query->query_vars['url'])) {
+ $url_param = $wp_query->query_vars['url'];
+ $decoded = base64_decode($url_param);
+ if ($decoded !== false) {
+ $url = $decoded;
+ } else {
+ $url = urldecode($url_param);
+ }
+ } elseif (isset($_GET['url'])) {
+ $url_param = sanitize_text_field($_GET['url']);
+ $decoded = base64_decode($url_param);
+ if ($decoded !== false) {
+ $url = $decoded;
+ } else {
+ $url = urldecode($url_param);
+ }
+ }
+ }
+
+ if (!empty($url) && strpos($url, 'http') !== 0) {
+ $url = 'http://' . $url;
+ }
+
+ return $url;
+ }
+
+ private function handle_redirect_error($message) {
+ if (WP_DEBUG) {
+ error_log('WPNAV Redirect Error: ' . $message);
+ }
+
+ wp_die(
+ esc_html__('Redirect Error: ', 'wpnav-links') . esc_html($message),
+ esc_html__('External Link Redirect Error', 'wpnav-links'),
+ array(
+ 'response' => 400,
+ 'back_link' => true
+ )
+ );
+ }
+
+ private function should_skip_redirect($url) {
+ if (!empty($this->options['admin_exempt']) && current_user_can('manage_options')) {
+ return true;
+ }
+
+ if ($this->check_whitelist_domain($url)) {
+ return true;
+ }
+
+ if (isset($_COOKIE['wpnav_noredirect']) && $_COOKIE['wpnav_noredirect'] == '1') {
+ return true;
+ }
+
+ return false;
+ }
+
+ public function check_whitelist_domain($url) {
+ $domain = parse_url($url, PHP_URL_HOST);
+ if (empty($domain)) {
+ return false;
+ }
+
+ $whitelist = array();
+ if (!empty($this->options['whitelist_domains'])) {
+ $whitelist = explode("\n", $this->options['whitelist_domains']);
+ $whitelist = array_map('trim', $whitelist);
+ }
+
+ if (in_array($domain, $whitelist)) {
+ return true;
+ }
+
+ foreach ($whitelist as $pattern) {
+ if (strpos($pattern, '*') !== false) {
+ $pattern = str_replace('*', '(.*)', preg_quote($pattern, '/'));
+ if (preg_match('/^' . $pattern . '$/i', $domain)) {
+ return true;
+ }
+ }
+ }
+
+ if (!empty($this->options['auto_whitelist']['same_root'])) {
+ $site_domain = parse_url(home_url(), PHP_URL_HOST);
+ $site_root = $this->get_root_domain($site_domain);
+ $url_root = $this->get_root_domain($domain);
+
+ if ($site_root == $url_root) {
+ return true;
+ }
+ }
+
+ if (!empty($this->options['auto_whitelist']['search_engines'])) {
+ $search_engines = array('google.', 'bing.', 'yahoo.', 'baidu.', 'yandex.', 'duckduckgo.');
+ foreach ($search_engines as $engine) {
+ if (strpos($domain, $engine) !== false) {
+ return true;
+ }
+ }
+ }
+
+ return false;
+ }
+
+ private function get_root_domain($domain) {
+ $domain_parts = explode('.', $domain);
+ if (count($domain_parts) > 2) {
+ $tld_part = array_slice($domain_parts, -2, 2);
+ if (in_array($tld_part[0], array('co', 'com', 'net', 'org', 'gov', 'edu'))) {
+ return implode('.', array_slice($domain_parts, -3, 3));
+ }
+ return implode('.', array_slice($domain_parts, -2, 2));
+ }
+ return $domain;
+ }
+
+ private function load_redirect_template($url, $ref) {
+ wp_enqueue_style(
+ 'wpnav-redirect-style',
+ WPNAV_LINKS_PLUGIN_URL . 'frontend.css',
+ array(),
+ WPNAV_LINKS_VERSION
+ );
+
+ $template_locations = array(
+ get_stylesheet_directory() . '/wpnav-redirect-template.php',
+ get_template_directory() . '/wpnav-redirect-template.php',
+ WPNAV_LINKS_PLUGIN_DIR . 'redirect-template.php'
+ );
+
+ foreach ($template_locations as $template) {
+ if (file_exists($template)) {
+ include $template;
+ return;
+ }
+ }
+
+ include WPNAV_LINKS_PLUGIN_DIR . 'redirect-template.php';
+ }
+
+ public function get_redirect_url($url) {
+ $url_format = isset($this->options['url_format']) ? $this->options['url_format'] : 'query';
+
+ if ($url_format === 'path') {
+ $encoded_url = base64_encode($url);
+ if (get_option('permalink_structure')) {
+ return home_url('/go/' . $encoded_url);
+ } else {
+ return home_url('?wpnav_redirect=1&url_param=' . urlencode($encoded_url));
+ }
+ } elseif ($url_format === 'target') {
+ return home_url('?wpnav_redirect=1&target=' . urlencode($url));
+ } else {
+ return home_url('?wpnav_redirect=1&url=' . urlencode(base64_encode($url)));
+ }
+ }
+
+ public function add_admin_bar_menu($wp_admin_bar) {
+ if (!current_user_can('manage_options')) {
+ return;
+ }
+
+ $wp_admin_bar->add_node(array(
+ 'id' => 'wpnav-links',
+ 'title' => esc_html__('External Links', 'wpnav-links'),
+ 'href' => admin_url('tools.php?page=wpnav-links'),
+ ));
+ }
+
+ public function create_tables() {
+ global $wpdb;
+
+ $charset_collate = $wpdb->get_charset_collate();
+
+ $sql = "CREATE TABLE $this->table_name (
+ id BIGINT UNSIGNED AUTO_INCREMENT,
+ target_url VARCHAR(512) NOT NULL,
+ source_page VARCHAR(255) NOT NULL,
+ click_time DATETIME DEFAULT CURRENT_TIMESTAMP,
+ user_ip VARCHAR(45),
+ user_agent TEXT,
+ is_https TINYINT(1) DEFAULT 0,
+ PRIMARY KEY (id),
+ INDEX (target_url(191)),
+ INDEX (click_time)
+ ) $charset_collate;";
+
+ require_once(ABSPATH . 'wp-admin/includes/upgrade.php');
+ dbDelta($sql);
+
+ update_option('wpnav_links_db_version', WPNAV_LINKS_DB_VERSION);
+ }
+
+ public function record_redirect($target_url, $source_page) {
+ global $wpdb;
+
+ $is_https = strpos($target_url, 'https://') === 0 ? 1 : 0;
+ $ip = $this->get_client_ip();
+ $user_agent = isset($_SERVER['HTTP_USER_AGENT']) ? $_SERVER['HTTP_USER_AGENT'] : '';
+
+ $data = array(
+ 'target_url' => esc_url_raw($target_url),
+ 'source_page' => esc_url_raw($source_page),
+ 'click_time' => current_time('mysql'),
+ 'user_ip' => sanitize_text_field($ip),
+ 'user_agent' => sanitize_text_field($user_agent),
+ 'is_https' => $is_https
+ );
+
+ $result = $wpdb->insert($this->table_name, $data);
+ return $result ? $wpdb->insert_id : false;
+ }
+
+ private function get_client_ip() {
+ if (!empty($_SERVER['HTTP_CLIENT_IP'])) {
+ $ip = $_SERVER['HTTP_CLIENT_IP'];
+ } elseif (!empty($_SERVER['HTTP_X_FORWARDED_FOR'])) {
+ $ip = $_SERVER['HTTP_X_FORWARDED_FOR'];
+ } else {
+ $ip = $_SERVER['REMOTE_ADDR'];
+ }
+ return $ip;
+ }
+
+ public function get_stats($args = array()) {
+ global $wpdb;
+
+ $defaults = array(
+ 'start_date' => date('Y-m-d', strtotime('-30 days')),
+ 'end_date' => date('Y-m-d'),
+ 'limit' => 10,
+ 'offset' => 0,
+ 'order_by' => 'click_time',
+ 'order' => 'DESC'
+ );
+
+ $args = wp_parse_args($args, $defaults);
+
+ $start_date = sanitize_text_field($args['start_date']);
+ $end_date = sanitize_text_field($args['end_date']);
+ $limit = intval($args['limit']);
+ $offset = intval($args['offset']);
+ $order_by = sanitize_sql_orderby($args['order_by']) ?: 'click_time';
+ $order = strtoupper($args['order']) == 'ASC' ? 'ASC' : 'DESC';
+
+ $query = $wpdb->prepare(
+ "SELECT * FROM {$this->table_name}
+ WHERE click_time BETWEEN %s AND %s
+ ORDER BY {$order_by} {$order}
+ LIMIT %d OFFSET %d",
+ "{$start_date} 00:00:00",
+ "{$end_date} 23:59:59",
+ $limit,
+ $offset
+ );
+
+ return $wpdb->get_results($query);
+ }
+
+ public function get_total_count($start_date = '', $end_date = '') {
+ global $wpdb;
+
+ if (empty($start_date) || empty($end_date)) {
+ return $wpdb->get_var("SELECT COUNT(*) FROM {$this->table_name}");
+ }
+
+ $query = $wpdb->prepare(
+ "SELECT COUNT(*) FROM {$this->table_name}
+ WHERE click_time BETWEEN %s AND %s",
+ "{$start_date} 00:00:00",
+ "{$end_date} 23:59:59"
+ );
+
+ return $wpdb->get_var($query);
+ }
+
+ public function get_top_urls($limit = 10) {
+ global $wpdb;
+
+ $query = $wpdb->prepare(
+ "SELECT target_url, COUNT(*) as count
+ FROM {$this->table_name}
+ GROUP BY target_url
+ ORDER BY count DESC
+ LIMIT %d",
+ $limit
+ );
+
+ return $wpdb->get_results($query);
+ }
+
+ public function get_https_stats() {
+ global $wpdb;
+
+ $total = $wpdb->get_var("SELECT COUNT(*) FROM {$this->table_name}");
+
+ if ($total == 0) {
+ return false;
+ }
+
+ $https_count = $wpdb->get_var("SELECT COUNT(*) FROM {$this->table_name} WHERE is_https = 1");
+ $http_count = $total - $https_count;
+
+ return array(
+ 'total' => $total,
+ 'https_count' => $https_count,
+ 'http_count' => $http_count,
+ 'https_percentage' => ($https_count / $total) * 100,
+ 'http_percentage' => ($http_count / $total) * 100
+ );
+ }
+
+ public function get_unique_domains_count() {
+ global $wpdb;
+
+ $query = "SELECT COUNT(DISTINCT SUBSTRING_INDEX(SUBSTRING_INDEX(target_url, '/', 3), '://', -1)) as unique_domains FROM {$this->table_name}";
+
+ $result = $wpdb->get_var($query);
+
+ return $result ? intval($result) : 0;
+ }
+
+ public function cleanup_old_data($days = 90) {
+ global $wpdb;
+
+ $days = max(1, intval($days));
+
+ $query = $wpdb->prepare(
+ "DELETE FROM {$this->table_name}
+ WHERE click_time < DATE_SUB(NOW(), INTERVAL %d DAY)",
+ $days
+ );
+
+ return $wpdb->query($query);
+ }
+
+ public function validate_url($url) {
+ if (empty($url) || !wp_http_validate_url($url)) {
+ return false;
+ }
+
+ $parsed_url = parse_url($url);
+ if (empty($parsed_url['host'])) {
+ return false;
+ }
+
+ if ($this->contains_script_injection($url)) {
+ return false;
+ }
+
+ return true;
+ }
+
+ private function contains_script_injection($url) {
+ $patterns = array(
+ '/javascript:/i',
+ '/data:/i',
+ '/vbscript:/i',
+ '/
+
+
+