mirror of
https://github.com/WenPai-org/wpnav-links.git
synced 2025-08-03 02:48:44 +08:00
v1.2.0 稳定版
This commit is contained in:
parent
e29cccc1a9
commit
5c4a7cdeac
17 changed files with 7310 additions and 0 deletions
1008
admin-page.php
Normal file
1008
admin-page.php
Normal file
File diff suppressed because it is too large
Load diff
920
admin.css
Normal file
920
admin.css
Normal file
|
@ -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;
|
||||
}
|
494
admin.js
Normal file
494
admin.js
Normal file
|
@ -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('<div class="notice notice-success is-dismissible"><p>' + message + '</p></div>');
|
||||
|
||||
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('<span class="draft-saved" style="font-size: 11px; color: #46b450; margin-left: 5px;">' + wpnav_admin.strings.draft_saved + '</span>');
|
||||
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 = $('<span id="' + elementId + '" class="wpnav-notice"></span>');
|
||||
$('.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();
|
||||
}
|
||||
});
|
1
assets/i-like-food.svg
Normal file
1
assets/i-like-food.svg
Normal file
File diff suppressed because one or more lines are too long
After Width: | Height: | Size: 9.6 KiB |
1
assets/leaf.svg
Normal file
1
assets/leaf.svg
Normal file
|
@ -0,0 +1 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 80 40"><path fill="#000" d="M0 40a19.96 19.96 0 0 1 5.9-14.11 20.17 20.17 0 0 1 19.44-5.2A20 20 0 0 1 20.2 40H0zM65.32.75A20.02 20.02 0 0 1 40.8 25.26 20.02 20.02 0 0 1 65.32.76zM.07 0h20.1l-.08.07A20.02 20.02 0 0 1 .75 5.25 20.08 20.08 0 0 1 .07 0zm1.94 40h2.53l4.26-4.24v-9.78A17.96 17.96 0 0 0 2 40zm5.38 0h9.8a17.98 17.98 0 0 0 6.67-16.42L7.4 40zm3.43-15.42v9.17l11.62-11.59c-3.97-.5-8.08.3-11.62 2.42zm32.86-.78A18 18 0 0 0 63.85 3.63L43.68 23.8zm7.2-19.17v9.15L62.43 2.22c-3.96-.5-8.05.3-11.57 2.4zm-3.49 2.72c-4.1 4.1-5.81 9.69-5.13 15.03l6.61-6.6V6.02c-.51.41-1 .85-1.48 1.33zM17.18 0H7.42L3.64 3.78A18 18 0 0 0 17.18 0zM2.08 0c-.01.8.04 1.58.14 2.37L4.59 0H2.07z"></path></svg>
|
After Width: | Height: | Size: 739 B |
131
class-admin.php
Normal file
131
class-admin.php
Normal file
|
@ -0,0 +1,131 @@
|
|||
<?php
|
||||
|
||||
if (!defined('ABSPATH')) {
|
||||
exit;
|
||||
}
|
||||
|
||||
class WPNAV_Admin {
|
||||
private $options;
|
||||
private $plugin;
|
||||
|
||||
public function __construct($options) {
|
||||
$this->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 = '<a href="' . admin_url('tools.php?page=wpnav-links') . '">' . __('Settings', 'wpnav-links') . '</a>';
|
||||
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();
|
||||
}
|
||||
}
|
323
class-public.php
Normal file
323
class-public.php
Normal file
|
@ -0,0 +1,323 @@
|
|||
<?php
|
||||
|
||||
if (!defined('ABSPATH')) {
|
||||
exit;
|
||||
}
|
||||
|
||||
class WPNAV_Public {
|
||||
private $options;
|
||||
private $security;
|
||||
|
||||
public function __construct($options, $security) {
|
||||
$this->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('<?xml encoding="UTF-8"><div>' . $content . '</div>', 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() {
|
||||
?>
|
||||
<script type="text/javascript">
|
||||
(function($) {
|
||||
function processAjaxContent() {
|
||||
var observer = new MutationObserver(function(mutations) {
|
||||
mutations.forEach(function(mutation) {
|
||||
if (mutation.addedNodes && mutation.addedNodes.length > 0) {
|
||||
var $links = $(mutation.addedNodes).find('a');
|
||||
if ($links.length) {
|
||||
processExternalLinks($links);
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
observer.observe(document.body, {
|
||||
childList: true,
|
||||
subtree: true
|
||||
});
|
||||
}
|
||||
|
||||
function processExternalLinks($elements) {
|
||||
$elements.each(function() {
|
||||
var $link = $(this);
|
||||
var href = $link.attr('href');
|
||||
|
||||
if (!href) return;
|
||||
|
||||
if ($link.attr('data-wpnav-external')) return;
|
||||
|
||||
if ($link.hasClass(wpnav_params.exclude_class)) return;
|
||||
|
||||
if (isExternalLink(href) && !isWhitelistedDomain(href)) {
|
||||
$link.attr('rel', 'nofollow noopener noreferrer');
|
||||
|
||||
if (wpnav_params.open_new_tab) {
|
||||
$link.attr('target', '_blank');
|
||||
|
||||
// Add accessibility attributes
|
||||
var currentTitle = $link.attr('title') || '';
|
||||
if (!currentTitle.includes('<?php echo esc_js(__('opens in a new window', 'wpnav-links')); ?>')) {
|
||||
var newTitle = currentTitle ?
|
||||
currentTitle + ' <?php echo esc_js(__('(opens in a new window)', 'wpnav-links')); ?>' :
|
||||
'<?php echo esc_js(__('External link (opens in a new window)', 'wpnav-links')); ?>';
|
||||
$link.attr('title', newTitle);
|
||||
}
|
||||
}
|
||||
|
||||
$link.attr('href', getRedirectUrl(href));
|
||||
$link.attr('data-wpnav-external', '1');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function isExternalLink(url) {
|
||||
if (url.indexOf('://') === -1 && url.indexOf('//') !== 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
var parser = document.createElement('a');
|
||||
parser.href = url;
|
||||
|
||||
return parser.hostname !== wpnav_params.site_domain;
|
||||
}
|
||||
|
||||
function isWhitelistedDomain(url) {
|
||||
if (!wpnav_params.whitelist_domains || wpnav_params.whitelist_domains.length === 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
var parser = document.createElement('a');
|
||||
parser.href = url;
|
||||
var hostname = parser.hostname;
|
||||
|
||||
return wpnav_params.whitelist_domains.indexOf(hostname) !== -1;
|
||||
}
|
||||
|
||||
function getRedirectUrl(url) {
|
||||
var encodedUrl = encodeUrl(url);
|
||||
var currentPage = encodeURIComponent(window.location.href);
|
||||
|
||||
switch(wpnav_params.redirect_method) {
|
||||
case 'path':
|
||||
if (wpnav_params.permalink_structure) {
|
||||
return wpnav_params.home_url + '/go/' + encodedUrl;
|
||||
} else {
|
||||
return wpnav_params.home_url + '?wpnav_redirect=1&url_param=' + encodeURIComponent(encodedUrl) + '&ref=' + currentPage;
|
||||
}
|
||||
break;
|
||||
|
||||
case 'target':
|
||||
return wpnav_params.home_url + '?wpnav_redirect=1&target=' + encodeURIComponent(url) + '&ref=' + currentPage;
|
||||
break;
|
||||
|
||||
default:
|
||||
return wpnav_params.home_url + '?wpnav_redirect=1&url=' + encodeURIComponent(encodedUrl) + '&ref=' + currentPage;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
function encodeUrl(url) {
|
||||
if (wpnav_params.redirect_method === 'target') {
|
||||
return url;
|
||||
}
|
||||
|
||||
try {
|
||||
return btoa(url).replace(/[+/=]/g, function(match) {
|
||||
return {'+': '-', '/': '_', '=': ''}[match];
|
||||
});
|
||||
} catch (e) {
|
||||
return encodeURIComponent(url);
|
||||
}
|
||||
}
|
||||
|
||||
$(document).ready(function() {
|
||||
processExternalLinks($('a:not([data-wpnav-external])'));
|
||||
processAjaxContent();
|
||||
});
|
||||
})(jQuery);
|
||||
</script>
|
||||
<?php
|
||||
}
|
||||
}
|
494
class-wpnav-links.php
Normal file
494
class-wpnav-links.php
Normal file
|
@ -0,0 +1,494 @@
|
|||
<?php
|
||||
|
||||
if (!defined('ABSPATH')) {
|
||||
exit;
|
||||
}
|
||||
|
||||
class WPNAV_Links {
|
||||
private $options;
|
||||
private $table_name;
|
||||
|
||||
public function __construct() {
|
||||
global $wpdb;
|
||||
$this->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',
|
||||
'/<script/i',
|
||||
'/onclick/i',
|
||||
'/onerror/i',
|
||||
'/onload/i',
|
||||
'/eval\(/i'
|
||||
);
|
||||
|
||||
foreach ($patterns as $pattern) {
|
||||
if (preg_match($pattern, $url)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public function sanitize_url($url) {
|
||||
$url = preg_replace('/[\x00-\x1F\x7F]/', '', $url);
|
||||
$url = esc_url_raw($url);
|
||||
return $url;
|
||||
}
|
||||
}
|
135
external-indicator.css
Normal file
135
external-indicator.css
Normal file
|
@ -0,0 +1,135 @@
|
|||
a[data-wpnav-external] {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
a[data-wpnav-external] .wpnav-external-icon {
|
||||
display: inline-block;
|
||||
font-size: 0.8em;
|
||||
margin-left: 3px;
|
||||
opacity: 0.6;
|
||||
transition: opacity 0.2s ease;
|
||||
vertical-align: super;
|
||||
line-height: 1;
|
||||
color: #646970;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
a[data-wpnav-external]:hover .wpnav-external-icon {
|
||||
opacity: 1;
|
||||
color: #1d2327;
|
||||
}
|
||||
|
||||
a[data-wpnav-external]:focus .wpnav-external-icon {
|
||||
opacity: 1;
|
||||
color: #2271b1;
|
||||
}
|
||||
|
||||
.wpnav-external-icon-box::after {
|
||||
content: "⧉";
|
||||
font-size: 0.7em;
|
||||
margin-left: 2px;
|
||||
opacity: 0.5;
|
||||
color: #646970;
|
||||
}
|
||||
|
||||
.wpnav-external-icon-arrow::after {
|
||||
content: "↗";
|
||||
font-size: 0.8em;
|
||||
margin-left: 2px;
|
||||
opacity: 0.6;
|
||||
color: #646970;
|
||||
}
|
||||
|
||||
.entry-content a[data-wpnav-external] .wpnav-external-icon,
|
||||
.comment-content a[data-wpnav-external] .wpnav-external-icon {
|
||||
color: #2271b1;
|
||||
opacity: 0.7;
|
||||
}
|
||||
|
||||
.entry-content a[data-wpnav-external]:hover .wpnav-external-icon,
|
||||
.comment-content a[data-wpnav-external]:hover .wpnav-external-icon {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.widget a[data-wpnav-external] .wpnav-external-icon {
|
||||
color: #50575e;
|
||||
opacity: 0.6;
|
||||
}
|
||||
|
||||
.widget a[data-wpnav-external]:hover .wpnav-external-icon {
|
||||
opacity: 1;
|
||||
color: #2271b1;
|
||||
}
|
||||
|
||||
.menu a[data-wpnav-external] .wpnav-external-icon,
|
||||
.nav-menu a[data-wpnav-external] .wpnav-external-icon {
|
||||
font-size: 0.7em;
|
||||
margin-left: 2px;
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
.site-footer a[data-wpnav-external] .wpnav-external-icon {
|
||||
color: #8c8f94;
|
||||
opacity: 0.6;
|
||||
}
|
||||
|
||||
.site-footer a[data-wpnav-external]:hover .wpnav-external-icon {
|
||||
opacity: 1;
|
||||
color: #50575e;
|
||||
}
|
||||
|
||||
.wp-block-button__link[data-wpnav-external] .wpnav-external-icon,
|
||||
.button[data-wpnav-external] .wpnav-external-icon {
|
||||
color: inherit;
|
||||
opacity: 0.8;
|
||||
}
|
||||
|
||||
@media (prefers-contrast: high) {
|
||||
a[data-wpnav-external] .wpnav-external-icon {
|
||||
opacity: 1;
|
||||
font-weight: bold;
|
||||
color: #000;
|
||||
}
|
||||
|
||||
a[data-wpnav-external]:hover .wpnav-external-icon {
|
||||
color: #2271b1;
|
||||
}
|
||||
}
|
||||
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
a[data-wpnav-external] .wpnav-external-icon {
|
||||
transition: none;
|
||||
}
|
||||
}
|
||||
|
||||
@media print {
|
||||
.wpnav-external-icon {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
.screen-reader-text.wpnav-external-text {
|
||||
position: absolute !important;
|
||||
clip: rect(1px, 1px, 1px, 1px);
|
||||
word-wrap: normal !important;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
a[data-wpnav-external]:focus-visible {
|
||||
outline: 2px solid #2271b1;
|
||||
outline-offset: 2px;
|
||||
}
|
||||
|
||||
@media screen and (max-width: 768px) {
|
||||
a[data-wpnav-external] .wpnav-external-icon {
|
||||
font-size: 0.75em;
|
||||
margin-left: 2px;
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (max-width: 480px) {
|
||||
a[data-wpnav-external] .wpnav-external-icon {
|
||||
font-size: 0.7em;
|
||||
margin-left: 1px;
|
||||
}
|
||||
}
|
837
frontend.css
Normal file
837
frontend.css
Normal file
|
@ -0,0 +1,837 @@
|
|||
.wpnav-redirect-page *,
|
||||
.wpnav-redirect-page *::before,
|
||||
.wpnav-redirect-page *::after {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.wpnav-redirect-page {
|
||||
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen-Sans, Ubuntu, Cantarell, "Helvetica Neue", sans-serif;
|
||||
line-height: 1.6;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
background: #f0f2f5;
|
||||
color: #2c3e50;
|
||||
min-height: 100vh;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
}
|
||||
|
||||
.wpnav-redirect-page.wpnav-color-blue {
|
||||
background: #e3f2fd;
|
||||
}
|
||||
|
||||
.wpnav-redirect-page.wpnav-color-green {
|
||||
background: #e8f5e8;
|
||||
}
|
||||
|
||||
.wpnav-redirect-page.wpnav-color-red {
|
||||
background: #ffebee;
|
||||
}
|
||||
|
||||
.wpnav-page-overlay {
|
||||
min-height: 100vh;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 20px;
|
||||
position: relative;
|
||||
overflow-x: hidden;
|
||||
}
|
||||
|
||||
.wpnav-container {
|
||||
max-width: 780px;
|
||||
width: 100%;
|
||||
margin: 0 auto;
|
||||
padding: 40px;
|
||||
background: #ffffff;
|
||||
border-radius: 12px;
|
||||
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.1);
|
||||
border: 1px solid #e1e5e9;
|
||||
text-align: center;
|
||||
position: relative;
|
||||
animation: wpnav-slideUp 0.6s ease-out;
|
||||
}
|
||||
|
||||
@keyframes wpnav-slideUp {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: translateY(30px) scale(0.95);
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: translateY(0) scale(1);
|
||||
}
|
||||
}
|
||||
|
||||
.wpnav-simple {
|
||||
max-width: 480px;
|
||||
padding: 30px 25px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.wpnav-simple .wpnav-title {
|
||||
font-size: 22px;
|
||||
margin-bottom: 20px;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.wpnav-simple .wpnav-url-container {
|
||||
margin: 20px 0;
|
||||
}
|
||||
|
||||
.wpnav-simple .wpnav-url {
|
||||
padding: 15px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.wpnav-simple .wpnav-url-domain {
|
||||
font-size: 16px;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.wpnav-simple .wpnav-buttons {
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.wpnav-simple .wpnav-btn {
|
||||
padding: 12px 20px;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.wpnav-simple .wpnav-countdown {
|
||||
margin-top: 15px;
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.wpnav-minimal {
|
||||
max-width: 680px;
|
||||
padding: 30px;
|
||||
}
|
||||
|
||||
.wpnav-full {
|
||||
max-width: 880px;
|
||||
padding: 50px;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.wpnav-logo img {
|
||||
max-height: 60px;
|
||||
margin-bottom: 20px;
|
||||
filter: drop-shadow(0 2px 4px rgba(0, 0, 0, 0.1));
|
||||
width: auto;
|
||||
height: 60px;
|
||||
}
|
||||
|
||||
@keyframes wpnav-pulse {
|
||||
0%, 100% {
|
||||
transform: scale(1);
|
||||
}
|
||||
50% {
|
||||
transform: scale(1.05);
|
||||
}
|
||||
}
|
||||
|
||||
.wpnav-title {
|
||||
font-size: 28px;
|
||||
font-weight: 600;
|
||||
margin: 0 0 10px;
|
||||
color: #2c3e50;
|
||||
letter-spacing: -0.5px;
|
||||
}
|
||||
|
||||
.wpnav-subtitle {
|
||||
font-size: 16px;
|
||||
color: #7f8c8d;
|
||||
margin: 0 0 30px;
|
||||
font-weight: 400;
|
||||
}
|
||||
|
||||
.wpnav-minimal .wpnav-title {
|
||||
font-size: 24px;
|
||||
}
|
||||
|
||||
.wpnav-full .wpnav-title {
|
||||
font-size: 36px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.wpnav-header {
|
||||
margin-bottom: 40px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.wpnav-warning {
|
||||
background: #fff3cd;
|
||||
border: 1px solid #f39c12;
|
||||
border-radius: 8px;
|
||||
font-size: 16px;
|
||||
padding: 20px;
|
||||
margin: 30px 0;
|
||||
color: #8b4513;
|
||||
font-weight: 400;
|
||||
box-shadow: 0 2px 4px rgba(243, 156, 18, 0.1);
|
||||
}
|
||||
|
||||
.wpnav-url-container {
|
||||
margin: 30px 0;
|
||||
}
|
||||
|
||||
.wpnav-url-label {
|
||||
font-size: 14px;
|
||||
color: #7f8c8d;
|
||||
margin-bottom: 10px;
|
||||
font-weight: 600;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.5px;
|
||||
}
|
||||
|
||||
.wpnav-url {
|
||||
background: #f8f9fa;
|
||||
border: 2px solid #e9ecef;
|
||||
border-radius: 8px;
|
||||
padding: 20px;
|
||||
margin: 15px 0;
|
||||
word-break: break-all;
|
||||
position: relative;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.wpnav-url:hover {
|
||||
border-color: #667eea;
|
||||
}
|
||||
|
||||
.wpnav-url-domain {
|
||||
font-weight: 700;
|
||||
font-size: 18px;
|
||||
color: #2c3e50;
|
||||
display: block;
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
|
||||
.wpnav-url-path {
|
||||
color: #7f8c8d;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.wpnav-url-full {
|
||||
font-size: 12px;
|
||||
color: #95a5a6;
|
||||
margin-top: 10px;
|
||||
font-family: 'Monaco', 'Consolas', monospace;
|
||||
}
|
||||
|
||||
.wpnav-security-status {
|
||||
margin-top: 15px;
|
||||
padding-top: 15px;
|
||||
border-top: 1px solid #e9ecef;
|
||||
}
|
||||
|
||||
.wpnav-security-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.wpnav-security-label {
|
||||
font-weight: 600;
|
||||
color: #2c3e50;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.wpnav-https-badge {
|
||||
display: inline-block;
|
||||
padding: 4px 8px;
|
||||
border-radius: 12px;
|
||||
color: white;
|
||||
font-size: 11px;
|
||||
font-weight: 600;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.5px;
|
||||
min-width: 60px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.wpnav-https-badge.secure {
|
||||
background-color: #00a32a;
|
||||
}
|
||||
|
||||
.wpnav-https-badge.insecure {
|
||||
background-color: #d63638;
|
||||
}
|
||||
|
||||
.wpnav-security-warning {
|
||||
background: rgba(231, 76, 60, 0.1);
|
||||
border: 1px solid rgba(231, 76, 60, 0.3);
|
||||
border-radius: 8px;
|
||||
padding: 10px;
|
||||
margin-top: 10px;
|
||||
font-size: 13px;
|
||||
color: #c0392b;
|
||||
}
|
||||
|
||||
.wpnav-security-tips {
|
||||
background: #f8f9fa;
|
||||
border: 1px solid #e9ecef;
|
||||
border-radius: 8px;
|
||||
padding: 20px;
|
||||
margin: 20px 0;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.wpnav-security-tips h4 {
|
||||
margin: 0 0 15px;
|
||||
color: #2c3e50;
|
||||
font-size: 16px;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.wpnav-security-tips ul {
|
||||
margin: 0;
|
||||
padding-left: 20px;
|
||||
color: #5a6c7d;
|
||||
}
|
||||
|
||||
.wpnav-security-tips li {
|
||||
margin-bottom: 8px;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.wpnav-copy-btn {
|
||||
position: absolute;
|
||||
top: 15px;
|
||||
right: 15px;
|
||||
background: #667eea;
|
||||
color: white;
|
||||
border: none;
|
||||
border-radius: 6px;
|
||||
padding: 8px 12px;
|
||||
font-size: 12px;
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 5px;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.wpnav-copy-btn:hover {
|
||||
background: #5a67d8;
|
||||
transform: scale(1.05);
|
||||
}
|
||||
|
||||
.wpnav-info {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr;
|
||||
gap: 30px;
|
||||
margin: 40px 0;
|
||||
}
|
||||
|
||||
.wpnav-info-item {
|
||||
background: #f8f9fa;
|
||||
border: 1px solid #e9ecef;
|
||||
border-radius: 8px;
|
||||
padding: 25px;
|
||||
}
|
||||
|
||||
.wpnav-info-title {
|
||||
font-weight: 700;
|
||||
font-size: 18px;
|
||||
margin: 0 0 15px;
|
||||
color: #2c3e50;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.wpnav-tips-list {
|
||||
margin: 0;
|
||||
padding-left: 0;
|
||||
list-style: none;
|
||||
color: #5a6c7d;
|
||||
}
|
||||
|
||||
.wpnav-tips-list li {
|
||||
font-weight: 400;
|
||||
font-size: 16px;
|
||||
margin-bottom: 10px;
|
||||
padding-left: 25px;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.wpnav-tips-list li:before {
|
||||
content: "✓";
|
||||
position: absolute;
|
||||
left: 0;
|
||||
color: #27ae60;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.wpnav-warning-tips {
|
||||
background: rgba(231, 76, 60, 0.1);
|
||||
border: 1px solid rgba(231, 76, 60, 0.3);
|
||||
border-radius: 8px;
|
||||
padding: 15px;
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
.wpnav-warning-tips h4 {
|
||||
color: #e74c3c;
|
||||
margin: 0 0 10px;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.wpnav-actions {
|
||||
margin-top: 40px;
|
||||
}
|
||||
|
||||
.wpnav-buttons {
|
||||
display: flex;
|
||||
gap: 15px;
|
||||
justify-content: center;
|
||||
margin-bottom: 30px;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.wpnav-btn {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 8px;
|
||||
padding: 10px 20px;
|
||||
border-radius: 8px;
|
||||
text-decoration: none;
|
||||
font-weight: 600;
|
||||
font-size: 16px;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s ease;
|
||||
border: 2px solid transparent;
|
||||
font-family: inherit;
|
||||
min-width: 140px;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.wpnav-btn:before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: -100%;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.2), transparent);
|
||||
}
|
||||
|
||||
.wpnav-btn:hover:before {
|
||||
left: 100%;
|
||||
}
|
||||
|
||||
.wpnav-btn-primary {
|
||||
background: #667eea;
|
||||
color: white;
|
||||
box-shadow: 0 4px 12px rgba(102, 126, 234, 0.3);
|
||||
}
|
||||
|
||||
.wpnav-btn-primary:hover {
|
||||
background: #5a67d8;
|
||||
}
|
||||
|
||||
.wpnav-btn-secondary {
|
||||
background: #f8f9fa;
|
||||
color: #5a6c7d;
|
||||
border: 2px solid #e9ecef;
|
||||
}
|
||||
|
||||
.wpnav-btn-secondary:hover {
|
||||
background: white;
|
||||
color: #2c3e50;
|
||||
border-color: #bdc3c7;
|
||||
}
|
||||
|
||||
.wpnav-options {
|
||||
margin: 30px 0;
|
||||
}
|
||||
|
||||
.wpnav-checkbox {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 12px;
|
||||
cursor: pointer;
|
||||
font-size: 14px;
|
||||
color: #5a6c7d;
|
||||
transition: color 0.3s ease;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.wpnav-checkbox:hover {
|
||||
color: #2c3e50;
|
||||
}
|
||||
|
||||
.wpnav-checkbox input[type="checkbox"] {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.checkmark {
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
border: 2px solid #bdc3c7;
|
||||
border-radius: 4px;
|
||||
position: relative;
|
||||
transition: all 0.3s ease;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.wpnav-checkbox input:checked + .checkmark {
|
||||
background: #667eea;
|
||||
border-color: #667eea;
|
||||
}
|
||||
|
||||
.wpnav-checkbox input:checked + .checkmark:after {
|
||||
content: '✓';
|
||||
position: absolute;
|
||||
color: white;
|
||||
font-size: 12px;
|
||||
font-weight: bold;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
}
|
||||
|
||||
.wpnav-countdown {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 15px;
|
||||
font-size: 14px;
|
||||
color: #7f8c8d;
|
||||
margin-top: 20px;
|
||||
padding: 15px;
|
||||
background: rgba(52, 152, 219, 0.1);
|
||||
border-radius: 8px;
|
||||
border: 1px solid rgba(52, 152, 219, 0.2);
|
||||
}
|
||||
|
||||
.countdown-circle {
|
||||
position: relative;
|
||||
animation: wpnav-rotate 1s linear infinite;
|
||||
}
|
||||
|
||||
@keyframes wpnav-rotate {
|
||||
from {
|
||||
transform: rotate(0deg);
|
||||
}
|
||||
to {
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
}
|
||||
|
||||
#countdown-progress {
|
||||
transition: stroke-dashoffset 1s linear;
|
||||
}
|
||||
|
||||
.wpnav-footer {
|
||||
margin-top: 40px;
|
||||
padding-top: 20px;
|
||||
border-top: 1px solid rgba(233, 236, 239, 0.8);
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.wpnav-footer-text {
|
||||
font-size: 12px;
|
||||
color: #95a5a6;
|
||||
}
|
||||
|
||||
.wpnav-footer-text a {
|
||||
color: #667eea;
|
||||
text-decoration: none;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.wpnav-toast {
|
||||
position: fixed;
|
||||
top: 20px;
|
||||
left: 50%;
|
||||
transform: translateX(-50%) translateY(-100px);
|
||||
background: rgba(39, 174, 96, 0.95);
|
||||
color: white;
|
||||
padding: 12px 24px;
|
||||
border-radius: 6px;
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
z-index: 10000;
|
||||
transition: transform 0.3s ease;
|
||||
}
|
||||
|
||||
.wpnav-toast.show {
|
||||
transform: translateX(-50%) translateY(0);
|
||||
}
|
||||
|
||||
.wpnav-color-blue .wpnav-container {
|
||||
border-color: #e3f2fd;
|
||||
}
|
||||
|
||||
.wpnav-color-blue .wpnav-btn-primary {
|
||||
background: #3498db;
|
||||
}
|
||||
|
||||
.wpnav-color-blue .wpnav-btn-primary:hover {
|
||||
background: #2980b9;
|
||||
}
|
||||
|
||||
.wpnav-color-blue .wpnav-copy-btn {
|
||||
background: #3498db;
|
||||
}
|
||||
|
||||
.wpnav-color-green .wpnav-container {
|
||||
border-color: #e8f5e8;
|
||||
}
|
||||
|
||||
.wpnav-color-green .wpnav-btn-primary {
|
||||
background: #27ae60;
|
||||
}
|
||||
|
||||
.wpnav-color-green .wpnav-btn-primary:hover {
|
||||
background: #229954;
|
||||
}
|
||||
|
||||
.wpnav-color-green .wpnav-copy-btn {
|
||||
background: #27ae60;
|
||||
}
|
||||
|
||||
.wpnav-color-red .wpnav-container {
|
||||
border-color: #ffebee;
|
||||
}
|
||||
|
||||
.wpnav-color-red .wpnav-btn-primary {
|
||||
background: #e74c3c;
|
||||
}
|
||||
|
||||
.wpnav-color-red .wpnav-btn-primary:hover {
|
||||
background: #c0392b;
|
||||
}
|
||||
|
||||
.wpnav-color-red .wpnav-copy-btn {
|
||||
background: #e74c3c;
|
||||
}
|
||||
|
||||
@media screen and (max-width: 768px) {
|
||||
.wpnav-page-overlay {
|
||||
padding: 15px;
|
||||
align-items: flex-start;
|
||||
padding-top: 40px;
|
||||
}
|
||||
|
||||
.wpnav-container {
|
||||
padding: 30px 25px;
|
||||
margin: 0;
|
||||
border-radius: 8px;
|
||||
max-width: none;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.wpnav-simple {
|
||||
padding: 25px 20px;
|
||||
}
|
||||
|
||||
.wpnav-simple .wpnav-title {
|
||||
font-size: 20px;
|
||||
}
|
||||
|
||||
.wpnav-simple .wpnav-url {
|
||||
padding: 12px;
|
||||
}
|
||||
|
||||
.wpnav-simple .wpnav-btn {
|
||||
padding: 10px 16px;
|
||||
font-size: 13px;
|
||||
min-width: auto;
|
||||
}
|
||||
|
||||
.wpnav-full {
|
||||
padding: 30px 25px;
|
||||
}
|
||||
|
||||
.wpnav-title {
|
||||
font-size: 24px;
|
||||
}
|
||||
|
||||
.wpnav-minimal .wpnav-title {
|
||||
font-size: 22px;
|
||||
}
|
||||
|
||||
.wpnav-full .wpnav-title {
|
||||
font-size: 26px;
|
||||
}
|
||||
|
||||
.wpnav-buttons {
|
||||
flex-direction: column;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.wpnav-btn {
|
||||
width: 100%;
|
||||
padding: 18px 20px;
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.wpnav-info {
|
||||
grid-template-columns: 1fr;
|
||||
gap: 20px;
|
||||
margin: 30px 0;
|
||||
}
|
||||
|
||||
.wpnav-info-item {
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.wpnav-url {
|
||||
padding: 15px;
|
||||
}
|
||||
|
||||
.wpnav-copy-btn {
|
||||
position: static;
|
||||
width: 100%;
|
||||
margin-top: 10px;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.wpnav-warning {
|
||||
padding: 15px;
|
||||
margin: 20px 0;
|
||||
}
|
||||
|
||||
.wpnav-countdown {
|
||||
flex-direction: column;
|
||||
gap: 10px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.wpnav-security-tips {
|
||||
padding: 15px;
|
||||
margin: 15px 0;
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (max-width: 480px) {
|
||||
.wpnav-container {
|
||||
padding: 25px 20px;
|
||||
border-radius: 6px;
|
||||
}
|
||||
|
||||
.wpnav-simple {
|
||||
padding: 20px 15px;
|
||||
max-width: none;
|
||||
}
|
||||
|
||||
.wpnav-simple .wpnav-title {
|
||||
font-size: 18px;
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
.wpnav-simple .wpnav-url-container {
|
||||
margin: 15px 0;
|
||||
}
|
||||
|
||||
.wpnav-simple .wpnav-url {
|
||||
padding: 10px;
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
.wpnav-simple .wpnav-url-domain {
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.wpnav-simple .wpnav-buttons {
|
||||
gap: 8px;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.wpnav-simple .wpnav-btn {
|
||||
width: 100%;
|
||||
padding: 12px 16px;
|
||||
font-size: 14px;
|
||||
min-width: auto;
|
||||
}
|
||||
|
||||
.wpnav-title {
|
||||
font-size: 20px;
|
||||
}
|
||||
|
||||
.wpnav-btn {
|
||||
padding: 16px 20px;
|
||||
font-size: 15px;
|
||||
min-width: auto;
|
||||
}
|
||||
|
||||
.wpnav-url-domain {
|
||||
font-size: 16px;
|
||||
}
|
||||
}
|
||||
|
||||
@media (hover: none) and (pointer: coarse) {
|
||||
.wpnav-btn:hover {
|
||||
transform: none;
|
||||
box-shadow: 0 4px 12px rgba(102, 126, 234, 0.3);
|
||||
}
|
||||
|
||||
.wpnav-btn:active {
|
||||
transform: scale(0.95);
|
||||
}
|
||||
|
||||
.wpnav-url:hover {
|
||||
transform: none;
|
||||
border-color: #e9ecef;
|
||||
box-shadow: none;
|
||||
}
|
||||
}
|
||||
|
||||
.wpnav-btn-rounded {
|
||||
border-radius: 8px;
|
||||
}
|
||||
|
||||
.wpnav-btn-square {
|
||||
border-radius: 0;
|
||||
}
|
||||
|
||||
.wpnav-btn-pill {
|
||||
border-radius: 25px;
|
||||
}
|
||||
|
||||
.wpnav-progress-bar {
|
||||
width: 100%;
|
||||
height: 4px;
|
||||
background: rgba(0, 0, 0, 0.1);
|
||||
border-radius: 2px;
|
||||
margin-top: 10px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.wpnav-progress-fill {
|
||||
height: 100%;
|
||||
background: #667eea;
|
||||
border-radius: 2px;
|
||||
width: 100%;
|
||||
transition: width 1s linear;
|
||||
}
|
||||
|
||||
.wpnav-color-blue .wpnav-progress-fill {
|
||||
background: #3498db;
|
||||
}
|
||||
|
||||
.wpnav-color-green .wpnav-progress-fill {
|
||||
background: #27ae60;
|
||||
}
|
||||
|
||||
.wpnav-color-red .wpnav-progress-fill {
|
||||
background: #e74c3c;
|
||||
}
|
||||
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
.wpnav-redirect-page *,
|
||||
.wpnav-redirect-page *::before,
|
||||
.wpnav-redirect-page *::after {
|
||||
animation-duration: 0.01ms !important;
|
||||
animation-iteration-count: 1 !important;
|
||||
transition-duration: 0.01ms !important;
|
||||
}
|
||||
}
|
BIN
languages/wpnav-links-zh_CN.mo
Normal file
BIN
languages/wpnav-links-zh_CN.mo
Normal file
Binary file not shown.
964
languages/wpnav-links-zh_CN.po
Normal file
964
languages/wpnav-links-zh_CN.po
Normal file
|
@ -0,0 +1,964 @@
|
|||
# Chinese (China) translation for WPNav Links
|
||||
# Copyright (C) 2024 WPNav Links
|
||||
# This file is distributed under the same license as the WPNav Links package.
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: WPNav Links 2.0.0\n"
|
||||
"Report-Msgid-Bugs-To: https://wordpress.org/support/plugin/wpnav-links\n"
|
||||
"POT-Creation-Date: 2024-01-01 12:00+0000\n"
|
||||
"PO-Revision-Date: 2025-06-01 06:26+0800\n"
|
||||
"Last-Translator: WPNav Links Team\n"
|
||||
"Language-Team: Chinese (China)\n"
|
||||
"Language: zh_CN\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Plural-Forms: nplurals=1; plural=0;\n"
|
||||
"X-Generator: Poedit 3.6\n"
|
||||
|
||||
#: wpnav-links.php:100
|
||||
msgid "External Links Monitor"
|
||||
msgstr "外链监控"
|
||||
|
||||
#: wpnav-links.php:150
|
||||
msgid "Active"
|
||||
msgstr "已启用"
|
||||
|
||||
#: wpnav-links.php:150
|
||||
msgid "Inactive"
|
||||
msgstr "未启用"
|
||||
|
||||
#: wpnav-links.php:153
|
||||
msgid "Manual Only"
|
||||
msgstr "仅手动"
|
||||
|
||||
#: wpnav-links.php:159
|
||||
msgid "Total Redirects"
|
||||
msgstr "总跳转数"
|
||||
|
||||
#: wpnav-links.php:164
|
||||
msgid "Today"
|
||||
msgstr "今天"
|
||||
|
||||
#: wpnav-links.php:172
|
||||
msgid "Last 7 Days"
|
||||
msgstr "最近7天"
|
||||
|
||||
#: wpnav-links.php:177
|
||||
msgid "Whitelisted"
|
||||
msgstr "白名单"
|
||||
|
||||
#: wpnav-links.php:184
|
||||
msgid "Top Domains This Week"
|
||||
msgstr "本周热门域名"
|
||||
|
||||
#: wpnav-links.php:199
|
||||
msgid "No external link activity yet."
|
||||
msgstr "暂无外链活动记录。"
|
||||
|
||||
#: wpnav-links.php:201
|
||||
msgid "Enable the plugin to start tracking external links."
|
||||
msgstr "启用插件开始跟踪外链。"
|
||||
|
||||
#: wpnav-links.php:208
|
||||
msgid "Enable Plugin"
|
||||
msgstr "启用插件"
|
||||
|
||||
#: wpnav-links.php:210
|
||||
msgid "View Details"
|
||||
msgstr "查看详情"
|
||||
|
||||
#: wpnav-links.php:213
|
||||
msgid "Manage Whitelist"
|
||||
msgstr "管理白名单"
|
||||
|
||||
#: wpnav-links.php:322
|
||||
msgid "Settings"
|
||||
msgstr "设置"
|
||||
|
||||
#: wpnav-links.php:323
|
||||
msgid "Support"
|
||||
msgstr "支持"
|
||||
|
||||
#: wpnav-links.php:324
|
||||
msgid "Documentation"
|
||||
msgstr "文档"
|
||||
|
||||
#: class-wpnav-links.php:50 class-admin.php:21
|
||||
msgid "External Links"
|
||||
msgstr "外部链接"
|
||||
|
||||
#: class-wpnav-links.php:80
|
||||
msgid "Invalid or missing URL parameter"
|
||||
msgstr "无效或缺失的URL参数"
|
||||
|
||||
#: class-wpnav-links.php:85
|
||||
msgid "Invalid URL format"
|
||||
msgstr "无效的URL格式"
|
||||
|
||||
#: class-wpnav-links.php:169
|
||||
msgid "Redirect Error: "
|
||||
msgstr "跳转错误:"
|
||||
|
||||
#: class-wpnav-links.php:170
|
||||
msgid "External Link Redirect Error"
|
||||
msgstr "外链跳转错误"
|
||||
|
||||
#: class-admin.php:20
|
||||
msgid "External Links Redirect"
|
||||
msgstr "外链跳转"
|
||||
|
||||
#: class-admin.php:49
|
||||
msgid "Security check failed"
|
||||
msgstr "安全检查失败"
|
||||
|
||||
#: class-admin.php:53
|
||||
msgid "Insufficient permissions"
|
||||
msgstr "权限不足"
|
||||
|
||||
#: class-admin.php:60
|
||||
msgid "Target URL"
|
||||
msgstr "目标URL"
|
||||
|
||||
#: class-admin.php:61 admin-page.php:776
|
||||
msgid "Source Page"
|
||||
msgstr "来源页面"
|
||||
|
||||
#: class-admin.php:62
|
||||
msgid "Click Time"
|
||||
msgstr "点击时间"
|
||||
|
||||
#: class-admin.php:63
|
||||
msgid "IP Address"
|
||||
msgstr "IP地址"
|
||||
|
||||
#: class-admin.php:64
|
||||
msgid "User Agent"
|
||||
msgstr "用户代理"
|
||||
|
||||
#: admin-page.php:25
|
||||
msgid "You do not have sufficient permissions to access this page."
|
||||
msgstr "您没有足够的权限访问此页面。"
|
||||
|
||||
#: admin-page.php:47
|
||||
msgid "Basic settings saved successfully!"
|
||||
msgstr "基本设置保存成功!"
|
||||
|
||||
#: admin-page.php:49
|
||||
msgid "Failed to save basic settings. Database error."
|
||||
msgstr "保存基本设置失败。数据库错误。"
|
||||
|
||||
#: admin-page.php:67
|
||||
msgid "Whitelist settings saved successfully!"
|
||||
msgstr "白名单设置保存成功!"
|
||||
|
||||
#: admin-page.php:69
|
||||
msgid "Failed to save whitelist settings. Database error."
|
||||
msgstr "保存白名单设置失败。数据库错误。"
|
||||
|
||||
#: admin-page.php:99
|
||||
msgid "Redirect page settings saved successfully!"
|
||||
msgstr "跳转页面设置保存成功!"
|
||||
|
||||
#: admin-page.php:101
|
||||
msgid "Failed to save redirect page settings. Database error."
|
||||
msgstr "保存跳转页面设置失败。数据库错误。"
|
||||
|
||||
#: admin-page.php:127
|
||||
msgid "Successfully imported %d domains."
|
||||
msgstr "成功导入 %d 个域名。"
|
||||
|
||||
#: admin-page.php:129
|
||||
msgid "Failed to import CSV data. Database error."
|
||||
msgstr "导入CSV数据失败。数据库错误。"
|
||||
|
||||
#: admin-page.php:132
|
||||
msgid "No valid domains found in the CSV file."
|
||||
msgstr "CSV文件中未找到有效域名。"
|
||||
|
||||
#: admin-page.php:135
|
||||
msgid "Please select a CSV file to upload."
|
||||
msgstr "请选择要上传的CSV文件。"
|
||||
|
||||
#: admin-page.php:145
|
||||
msgid "Settings saved successfully!"
|
||||
msgstr "设置保存成功!"
|
||||
|
||||
#: admin-page.php:183
|
||||
msgid "Custom Template Detected:"
|
||||
msgstr "检测到自定义模板:"
|
||||
|
||||
#: admin-page.php:183
|
||||
msgid ""
|
||||
"Your theme has a custom redirect template. Settings below may not apply if "
|
||||
"your custom template doesn't support them."
|
||||
msgstr ""
|
||||
"您的主题有自定义跳转模板。如果您的自定义模板不支持,以下设置可能不会生效。"
|
||||
|
||||
#: admin-page.php:187
|
||||
msgid "Basic Settings"
|
||||
msgstr "基本设置"
|
||||
|
||||
#: admin-page.php:190
|
||||
msgid "Redirect Page"
|
||||
msgstr "跳转页面"
|
||||
|
||||
#: admin-page.php:193
|
||||
msgid "Whitelist"
|
||||
msgstr "白名单"
|
||||
|
||||
#: admin-page.php:196
|
||||
msgid "Statistics"
|
||||
msgstr "统计"
|
||||
|
||||
#: admin-page.php:199
|
||||
msgid "Import/Export"
|
||||
msgstr "导入/导出"
|
||||
|
||||
#: admin-page.php:208
|
||||
msgid "Enable Redirect"
|
||||
msgstr "启用跳转"
|
||||
|
||||
#: admin-page.php:211
|
||||
msgid "Enable external link redirect functionality"
|
||||
msgstr "启用外链跳转功能"
|
||||
|
||||
#: admin-page.php:215
|
||||
msgid "Auto Redirect"
|
||||
msgstr "自动跳转"
|
||||
|
||||
#: admin-page.php:218
|
||||
msgid "Enable automatic redirect"
|
||||
msgstr "启用自动跳转"
|
||||
|
||||
#: admin-page.php:219
|
||||
msgid ""
|
||||
"When enabled, users will be automatically redirected after the specified "
|
||||
"delay."
|
||||
msgstr "启用后,用户将在指定延迟后自动跳转。"
|
||||
|
||||
#: admin-page.php:223
|
||||
msgid "Redirect Delay"
|
||||
msgstr "跳转延迟"
|
||||
|
||||
#: admin-page.php:226
|
||||
msgid "Seconds to wait before automatic redirect (1-30 seconds)."
|
||||
msgstr "自动跳转前等待的秒数(1-30秒)。"
|
||||
|
||||
#: admin-page.php:230
|
||||
msgid "Open in New Tab"
|
||||
msgstr "新标签页打开"
|
||||
|
||||
#: admin-page.php:233
|
||||
msgid "Open external links in new tab/window"
|
||||
msgstr "在新标签页/窗口中打开外部链接"
|
||||
|
||||
#: admin-page.php:237
|
||||
msgid "URL Format"
|
||||
msgstr "URL格式"
|
||||
|
||||
#: admin-page.php:241
|
||||
msgid "Query Format (?url=encoded_url)"
|
||||
msgstr "查询格式 (?url=encoded_url)"
|
||||
|
||||
#: admin-page.php:245
|
||||
msgid "Path Format (/go/encoded_url)"
|
||||
msgstr "路径格式 (/go/encoded_url)"
|
||||
|
||||
#: admin-page.php:249
|
||||
msgid "Target Format (?target=encoded_url)"
|
||||
msgstr "目标格式 (?target=encoded_url)"
|
||||
|
||||
#: admin-page.php:252
|
||||
msgid "Choose URL format style for external link redirects."
|
||||
msgstr "选择外链跳转的URL格式样式。"
|
||||
|
||||
#: admin-page.php:257
|
||||
msgid "Content Processing"
|
||||
msgstr "内容处理"
|
||||
|
||||
#: admin-page.php:260
|
||||
msgid "Process Post Content"
|
||||
msgstr "处理文章内容"
|
||||
|
||||
#: admin-page.php:263
|
||||
msgid "Process external links in post and page content"
|
||||
msgstr "处理文章和页面内容中的外部链接"
|
||||
|
||||
#: admin-page.php:267
|
||||
msgid "Process Comments"
|
||||
msgstr "处理评论"
|
||||
|
||||
#: admin-page.php:270
|
||||
msgid "Process external links in comments"
|
||||
msgstr "处理评论中的外部链接"
|
||||
|
||||
#: admin-page.php:274
|
||||
msgid "Process Widgets"
|
||||
msgstr "处理小工具"
|
||||
|
||||
#: admin-page.php:277
|
||||
msgid "Process external links in widgets"
|
||||
msgstr "处理小工具中的外部链接"
|
||||
|
||||
#: admin-page.php:281
|
||||
msgid "Exclude CSS Class"
|
||||
msgstr "排除CSS类"
|
||||
|
||||
#: admin-page.php:284
|
||||
msgid "Links with this CSS class will not be redirected."
|
||||
msgstr "具有此CSS类的链接将不会被跳转。"
|
||||
|
||||
#: admin-page.php:289
|
||||
msgid "User Experience"
|
||||
msgstr "用户体验"
|
||||
|
||||
#: admin-page.php:292
|
||||
msgid "Admin Exempt"
|
||||
msgstr "管理员免跳转"
|
||||
|
||||
#: admin-page.php:295
|
||||
msgid "Skip redirect page for admin users"
|
||||
msgstr "为管理员用户跳过跳转页面"
|
||||
|
||||
#: admin-page.php:299
|
||||
msgid "Cookie Duration"
|
||||
msgstr "Cookie持续时间"
|
||||
|
||||
#: admin-page.php:302
|
||||
msgid "Days to remember user's \"Do not show again\" preference."
|
||||
msgstr "记住用户\"不再显示\"偏好的天数。"
|
||||
|
||||
#: admin-page.php:306
|
||||
msgid "Stats Retention"
|
||||
msgstr "统计保留"
|
||||
|
||||
#: admin-page.php:309
|
||||
msgid "Days to retain statistics data before automatic cleanup."
|
||||
msgstr "自动清理前保留统计数据的天数。"
|
||||
|
||||
#: admin-page.php:314
|
||||
msgid "Save Settings"
|
||||
msgstr "保存设置"
|
||||
|
||||
#: admin-page.php:322
|
||||
msgid "Domain Whitelist Management"
|
||||
msgstr "域名白名单管理"
|
||||
|
||||
#: admin-page.php:324
|
||||
msgid ""
|
||||
"Manage domains that should not be redirected through the external link page."
|
||||
msgstr "管理不应通过外链页面跳转的域名。"
|
||||
|
||||
#: admin-page.php:331
|
||||
msgid "Domain List"
|
||||
msgstr "域名列表"
|
||||
|
||||
#: admin-page.php:334
|
||||
msgid "Add Common"
|
||||
msgstr "添加常用"
|
||||
|
||||
#: admin-page.php:335
|
||||
msgid "Clear All"
|
||||
msgstr "清空全部"
|
||||
|
||||
#: admin-page.php:339
|
||||
msgid ""
|
||||
"Enter domains that should not redirect, one per line. Example: google.com "
|
||||
"(do not include http:// or https://)"
|
||||
msgstr ""
|
||||
"输入不应跳转的域名,每行一个。示例:google.com(不要包含http://或https://)"
|
||||
|
||||
#: admin-page.php:343
|
||||
msgid "Click to add common domains to your whitelist:"
|
||||
msgstr "点击将常用域名添加到您的白名单:"
|
||||
|
||||
#: admin-page.php:345
|
||||
msgid "Google"
|
||||
msgstr "谷歌"
|
||||
|
||||
#: admin-page.php:346
|
||||
msgid "Baidu"
|
||||
msgstr "百度"
|
||||
|
||||
#: admin-page.php:347
|
||||
msgid "Bing"
|
||||
msgstr "必应"
|
||||
|
||||
#: admin-page.php:348
|
||||
msgid "YouTube"
|
||||
msgstr "优酷"
|
||||
|
||||
#: admin-page.php:349
|
||||
msgid "Facebook"
|
||||
msgstr "脸书"
|
||||
|
||||
#: admin-page.php:350
|
||||
msgid "Twitter"
|
||||
msgstr "推特"
|
||||
|
||||
#: admin-page.php:351
|
||||
msgid "LinkedIn"
|
||||
msgstr "领英"
|
||||
|
||||
#: admin-page.php:352
|
||||
msgid "Instagram"
|
||||
msgstr "Instagram"
|
||||
|
||||
#: admin-page.php:356
|
||||
msgid "Auto Whitelist Rules"
|
||||
msgstr "自动白名单规则"
|
||||
|
||||
#: admin-page.php:362
|
||||
msgid "Same root domain no redirect"
|
||||
msgstr "相同根域名不跳转"
|
||||
|
||||
#: admin-page.php:364
|
||||
msgid ""
|
||||
"Subdomains of your site won't redirect (e.g., blog.example.com and "
|
||||
"shop.example.com)"
|
||||
msgstr "您网站的子域名不会跳转(例如:blog.example.com 和 shop.example.com)"
|
||||
|
||||
#: admin-page.php:371
|
||||
msgid "Search engines no redirect"
|
||||
msgstr "搜索引擎不跳转"
|
||||
|
||||
#: admin-page.php:373
|
||||
msgid "Major search engines (Google, Baidu, Bing, etc.) won't redirect"
|
||||
msgstr "主要搜索引擎(谷歌、百度、必应等)不会跳转"
|
||||
|
||||
#: admin-page.php:380
|
||||
msgid "Save Whitelist"
|
||||
msgstr "保存白名单"
|
||||
|
||||
#: admin-page.php:388
|
||||
msgid "Redirect Page Design"
|
||||
msgstr "跳转页面设计"
|
||||
|
||||
#: admin-page.php:389
|
||||
msgid ""
|
||||
"Customize the appearance and content of the redirect warning page shown to "
|
||||
"users."
|
||||
msgstr "自定义显示给用户的跳转警告页面的外观和内容。"
|
||||
|
||||
#: admin-page.php:396
|
||||
msgid "Template Style"
|
||||
msgstr "模板样式"
|
||||
|
||||
#: admin-page.php:401
|
||||
msgid "Simple"
|
||||
msgstr "简洁"
|
||||
|
||||
#: admin-page.php:401
|
||||
msgid "Ultra-minimal design with just essentials"
|
||||
msgstr "极简设计,仅包含必要元素"
|
||||
|
||||
#: admin-page.php:405
|
||||
msgid "Minimal"
|
||||
msgstr "简约"
|
||||
|
||||
#: admin-page.php:405
|
||||
msgid "Clean and lightweight design"
|
||||
msgstr "简洁轻量的设计"
|
||||
|
||||
#: admin-page.php:409 admin-page.php:851
|
||||
msgid "Default"
|
||||
msgstr "默认"
|
||||
|
||||
#: admin-page.php:409
|
||||
msgid "Balanced information display"
|
||||
msgstr "平衡的信息显示"
|
||||
|
||||
#: admin-page.php:413
|
||||
msgid "Full"
|
||||
msgstr "完整"
|
||||
|
||||
#: admin-page.php:413
|
||||
msgid "Comprehensive with security info"
|
||||
msgstr "包含安全信息的综合版"
|
||||
|
||||
#: admin-page.php:418
|
||||
msgid "Color Scheme"
|
||||
msgstr "配色方案"
|
||||
|
||||
#: admin-page.php:421
|
||||
msgid "Blue - Professional"
|
||||
msgstr "蓝色 - 专业"
|
||||
|
||||
#: admin-page.php:422
|
||||
msgid "Green - Safe"
|
||||
msgstr "绿色 - 安全"
|
||||
|
||||
#: admin-page.php:423
|
||||
msgid "Red - Warning"
|
||||
msgstr "红色 - 警告"
|
||||
|
||||
#: admin-page.php:427
|
||||
msgid "Page Content"
|
||||
msgstr "页面内容"
|
||||
|
||||
#: admin-page.php:430
|
||||
msgid "Page Title:"
|
||||
msgstr "页面标题:"
|
||||
|
||||
#: admin-page.php:434
|
||||
msgid "Page Subtitle (optional):"
|
||||
msgstr "页面副标题(可选):"
|
||||
|
||||
#: admin-page.php:438
|
||||
msgid "URL Label:"
|
||||
msgstr "URL标签:"
|
||||
|
||||
#: admin-page.php:443
|
||||
msgid "Display Options"
|
||||
msgstr "显示选项"
|
||||
|
||||
#: admin-page.php:448
|
||||
msgid "Show warning message"
|
||||
msgstr "显示警告信息"
|
||||
|
||||
#: admin-page.php:452
|
||||
msgid "Show site logo"
|
||||
msgstr "显示站点Logo"
|
||||
|
||||
#: admin-page.php:456
|
||||
msgid "Show full URL below domain"
|
||||
msgstr "在域名下方显示完整URL"
|
||||
|
||||
#: admin-page.php:460
|
||||
msgid "Show HTTPS security status"
|
||||
msgstr "显示HTTPS安全状态"
|
||||
|
||||
#: admin-page.php:464
|
||||
msgid "Show security tips and warnings"
|
||||
msgstr "显示安全提示和警告"
|
||||
|
||||
#: admin-page.php:468
|
||||
msgid "Show back button"
|
||||
msgstr "显示返回按钮"
|
||||
|
||||
#: admin-page.php:473
|
||||
msgid "Warning Message"
|
||||
msgstr "警告信息"
|
||||
|
||||
#: admin-page.php:476
|
||||
msgid ""
|
||||
"Main warning message displayed to users. Leave empty to hide the warning box."
|
||||
msgstr "显示给用户的主要警告信息。留空则隐藏警告框。"
|
||||
|
||||
#: admin-page.php:480
|
||||
msgid "Button Settings"
|
||||
msgstr "按钮设置"
|
||||
|
||||
#: admin-page.php:483
|
||||
msgid "Continue Button Text:"
|
||||
msgstr "继续按钮文本:"
|
||||
|
||||
#: admin-page.php:487
|
||||
msgid "Back Button Text:"
|
||||
msgstr "返回按钮文本:"
|
||||
|
||||
#: admin-page.php:491
|
||||
msgid "Button Style:"
|
||||
msgstr "按钮样式:"
|
||||
|
||||
#: admin-page.php:494
|
||||
msgid "Rounded"
|
||||
msgstr "圆角"
|
||||
|
||||
#: admin-page.php:495
|
||||
msgid "Square"
|
||||
msgstr "方形"
|
||||
|
||||
#: admin-page.php:496
|
||||
msgid "Pill Shape"
|
||||
msgstr "药丸形"
|
||||
|
||||
#: admin-page.php:501
|
||||
msgid "Countdown Settings"
|
||||
msgstr "倒计时设置"
|
||||
|
||||
#: admin-page.php:504
|
||||
msgid "Countdown Text:"
|
||||
msgstr "倒计时文本:"
|
||||
|
||||
#: admin-page.php:506
|
||||
msgid "Use {seconds} as placeholder for countdown number"
|
||||
msgstr "使用 {seconds} 作为倒计时数字的占位符"
|
||||
|
||||
#: admin-page.php:511
|
||||
msgid "Show progress bar during countdown"
|
||||
msgstr "倒计时期间显示进度条"
|
||||
|
||||
#: admin-page.php:516
|
||||
msgid "Custom CSS"
|
||||
msgstr "自定义CSS"
|
||||
|
||||
#: admin-page.php:519
|
||||
msgid "Add custom CSS to further customize the redirect page appearance"
|
||||
msgstr "添加自定义CSS进一步自定义跳转页面外观"
|
||||
|
||||
#: admin-page.php:524
|
||||
msgid "Save Redirect Page Settings"
|
||||
msgstr "保存跳转页面设置"
|
||||
|
||||
#: admin-page.php:530
|
||||
msgid "Live Preview"
|
||||
msgstr "实时预览"
|
||||
|
||||
#: admin-page.php:580 redirect-template.php:252
|
||||
msgid "Security Tips"
|
||||
msgstr "安全提示"
|
||||
|
||||
#: admin-page.php:582 redirect-template.php:255
|
||||
msgid "Verify the link is from a trusted source"
|
||||
msgstr "验证链接来自可信来源"
|
||||
|
||||
#: admin-page.php:583 redirect-template.php:258
|
||||
msgid "Check for secure HTTPS connection"
|
||||
msgstr "检查安全的HTTPS连接"
|
||||
|
||||
#: admin-page.php:608
|
||||
msgid "Import Whitelist"
|
||||
msgstr "导入白名单"
|
||||
|
||||
#: admin-page.php:609
|
||||
msgid "Upload CSV file to bulk import domains to your whitelist."
|
||||
msgstr "上传CSV文件批量导入域名到您的白名单。"
|
||||
|
||||
#: admin-page.php:616
|
||||
msgid "CSV File"
|
||||
msgstr "CSV文件"
|
||||
|
||||
#: admin-page.php:619
|
||||
msgid "Select a CSV file containing domain names (one per line)"
|
||||
msgstr "选择包含域名的CSV文件(每行一个)"
|
||||
|
||||
#: admin-page.php:624
|
||||
msgid "Import CSV"
|
||||
msgstr "导入CSV"
|
||||
|
||||
#: admin-page.php:630
|
||||
msgid "Export Whitelist"
|
||||
msgstr "导出白名单"
|
||||
|
||||
#: admin-page.php:631
|
||||
msgid "Download current whitelist domains as CSV file."
|
||||
msgstr "将当前白名单域名下载为CSV文件。"
|
||||
|
||||
#: admin-page.php:635
|
||||
msgid "Downloads a CSV file with all whitelisted domains."
|
||||
msgstr "下载包含所有白名单域名的CSV文件。"
|
||||
|
||||
#: admin-page.php:640
|
||||
msgid "Export Statistics"
|
||||
msgstr "导出统计"
|
||||
|
||||
#: admin-page.php:641
|
||||
msgid "Download statistics data for the selected date range."
|
||||
msgstr "下载选定日期范围的统计数据。"
|
||||
|
||||
#: admin-page.php:647
|
||||
msgid "Start Date:"
|
||||
msgstr "开始日期:"
|
||||
|
||||
#: admin-page.php:648
|
||||
msgid "End Date:"
|
||||
msgstr "结束日期:"
|
||||
|
||||
#: admin-page.php:654
|
||||
msgid "Downloads a CSV file with redirect statistics data."
|
||||
msgstr "下载包含跳转统计数据的CSV文件。"
|
||||
|
||||
#: admin-page.php:662
|
||||
msgid "Access Statistics"
|
||||
msgstr "访问统计"
|
||||
|
||||
#: admin-page.php:663
|
||||
msgid "View external link access statistics and logs."
|
||||
msgstr "查看外链访问统计和日志。"
|
||||
|
||||
#: admin-page.php:668
|
||||
msgid "Top External Links"
|
||||
msgstr "热门外链"
|
||||
|
||||
#: admin-page.php:676
|
||||
msgid "%s clicks"
|
||||
msgstr "%s 次点击"
|
||||
|
||||
#: admin-page.php:684
|
||||
msgid ""
|
||||
"No data available yet. Top links will appear here after users start clicking "
|
||||
"external links."
|
||||
msgstr "暂无数据。用户开始点击外链后,热门链接将显示在这里。"
|
||||
|
||||
#: admin-page.php:689
|
||||
msgid "Activity Overview"
|
||||
msgstr "活动概览"
|
||||
|
||||
#: admin-page.php:705
|
||||
msgid "This Month"
|
||||
msgstr "本月"
|
||||
|
||||
#: admin-page.php:709
|
||||
msgid "Total Records"
|
||||
msgstr "总记录数"
|
||||
|
||||
#: admin-page.php:713
|
||||
msgid "Pages"
|
||||
msgstr "页面"
|
||||
|
||||
#: admin-page.php:717
|
||||
msgid "Daily Avg"
|
||||
msgstr "日均"
|
||||
|
||||
#: admin-page.php:721
|
||||
msgid "Unique Domains"
|
||||
msgstr "独立域名"
|
||||
|
||||
#: admin-page.php:726
|
||||
msgid "HTTPS Links"
|
||||
msgstr "HTTPS链接"
|
||||
|
||||
#: admin-page.php:730
|
||||
msgid "HTTP Links"
|
||||
msgstr "HTTP链接"
|
||||
|
||||
#: admin-page.php:744
|
||||
msgid "Usage Guide"
|
||||
msgstr "使用指南"
|
||||
|
||||
#: admin-page.php:746
|
||||
msgid "Enable redirect functionality in basic settings"
|
||||
msgstr "在基本设置中启用跳转功能"
|
||||
|
||||
#: admin-page.php:747
|
||||
msgid "Configure which content areas to process"
|
||||
msgstr "配置要处理的内容区域"
|
||||
|
||||
#: admin-page.php:748
|
||||
msgid "Customize redirect page appearance"
|
||||
msgstr "自定义跳转页面外观"
|
||||
|
||||
#: admin-page.php:749
|
||||
msgid "Add domains to whitelist that should not redirect"
|
||||
msgstr "将不应跳转的域名添加到白名单"
|
||||
|
||||
#: admin-page.php:750
|
||||
msgid "View access data in statistics tab"
|
||||
msgstr "在统计标签页中查看访问数据"
|
||||
|
||||
#: admin-page.php:761
|
||||
msgid "Date Range"
|
||||
msgstr "日期范围"
|
||||
|
||||
#: admin-page.php:765
|
||||
msgid "to"
|
||||
msgstr "至"
|
||||
|
||||
#: admin-page.php:767
|
||||
msgid "Apply Filter"
|
||||
msgstr "应用筛选"
|
||||
|
||||
#: admin-page.php:768
|
||||
msgid "Reset"
|
||||
msgstr "重置"
|
||||
|
||||
#: admin-page.php:777
|
||||
msgid "Time"
|
||||
msgstr "时间"
|
||||
|
||||
#: admin-page.php:779
|
||||
msgid "Security"
|
||||
msgstr "安全性"
|
||||
|
||||
#: admin-page.php:793
|
||||
msgid "N/A"
|
||||
msgstr "不适用"
|
||||
|
||||
#: admin-page.php:818
|
||||
msgid "Page %1$s of %2$s"
|
||||
msgstr "第 %1$s 页,共 %2$s 页"
|
||||
|
||||
#: admin-page.php:828
|
||||
msgid "No redirect records found for the selected date range."
|
||||
msgstr "在选定的日期范围内未找到跳转记录。"
|
||||
|
||||
#: admin-page.php:829
|
||||
msgid ""
|
||||
"Records will appear here after users click on external links processed by "
|
||||
"the plugin."
|
||||
msgstr "用户点击插件处理的外链后,记录将显示在这里。"
|
||||
|
||||
#: admin-page.php:836
|
||||
msgid "System Information"
|
||||
msgstr "系统信息"
|
||||
|
||||
#: admin-page.php:841
|
||||
msgid "WordPress Version"
|
||||
msgstr "WordPress版本"
|
||||
|
||||
#: admin-page.php:845
|
||||
msgid "PHP Version"
|
||||
msgstr "PHP版本"
|
||||
|
||||
#: admin-page.php:849
|
||||
msgid "Permalink Structure"
|
||||
msgstr "固定链接结构"
|
||||
|
||||
#: admin-page.php:855
|
||||
msgid "Database Table"
|
||||
msgstr "数据库表"
|
||||
|
||||
#: admin-page.php:859
|
||||
msgid "Plugin URL"
|
||||
msgstr "插件URL"
|
||||
|
||||
#: admin-page.php:863
|
||||
msgid "Custom Template"
|
||||
msgstr "自定义模板"
|
||||
|
||||
#: admin-page.php:865
|
||||
msgid "Yes - Custom template detected"
|
||||
msgstr "是 - 检测到自定义模板"
|
||||
|
||||
#: admin-page.php:865
|
||||
msgid "No - Using default template"
|
||||
msgstr "否 - 使用默认模板"
|
||||
|
||||
#: redirect-template.php:25
|
||||
msgid "External Link Warning"
|
||||
msgstr "外链警告"
|
||||
|
||||
#: redirect-template.php:27
|
||||
msgid "You are about to visit:"
|
||||
msgstr "您即将访问:"
|
||||
|
||||
#: redirect-template.php:28
|
||||
msgid ""
|
||||
"You are about to leave this site and visit an external website. We are not "
|
||||
"responsible for the content of external websites."
|
||||
msgstr "您即将离开本站并访问外部网站。我们不对外部网站的内容负责。"
|
||||
|
||||
#: redirect-template.php:32
|
||||
msgid "Continue"
|
||||
msgstr "继续"
|
||||
|
||||
#: redirect-template.php:33
|
||||
msgid "Back"
|
||||
msgstr "返回"
|
||||
|
||||
#: redirect-template.php:35
|
||||
msgid "Auto redirect in {seconds} seconds"
|
||||
msgstr "{seconds} 秒后自动跳转"
|
||||
|
||||
#: redirect-template.php:55
|
||||
msgid "External Link Redirect - %s"
|
||||
msgstr "外链跳转 - %s"
|
||||
|
||||
#: redirect-template.php:146
|
||||
msgid "Don't show this again"
|
||||
msgstr "不再显示"
|
||||
|
||||
#: redirect-template.php:213
|
||||
msgid "Destination Information"
|
||||
msgstr "目标信息"
|
||||
|
||||
#: redirect-template.php:230
|
||||
msgid "Copy"
|
||||
msgstr "复制"
|
||||
|
||||
#: redirect-template.php:238
|
||||
msgid "Security Status"
|
||||
msgstr "安全状态"
|
||||
|
||||
#: redirect-template.php:244
|
||||
msgid ""
|
||||
"This website does not use HTTPS encryption. Your data may not be secure."
|
||||
msgstr "此网站未使用HTTPS加密。您的数据可能不安全。"
|
||||
|
||||
#: redirect-template.php:256
|
||||
msgid "Avoid entering personal information on HTTP sites"
|
||||
msgstr "避免在HTTP网站上输入个人信息"
|
||||
|
||||
#: redirect-template.php:257
|
||||
msgid "Be cautious of download requests"
|
||||
msgstr "谨慎对待下载请求"
|
||||
|
||||
#: redirect-template.php:263
|
||||
msgid "Additional Caution Required"
|
||||
msgstr "需要额外注意"
|
||||
|
||||
#: redirect-template.php:265
|
||||
msgid "This link does not use secure HTTPS"
|
||||
msgstr "此链接未使用安全的HTTPS"
|
||||
|
||||
#: redirect-template.php:266
|
||||
msgid "Consider if this site is trustworthy before proceeding"
|
||||
msgstr "继续之前请考虑此网站是否值得信任"
|
||||
|
||||
#: redirect-template.php:295
|
||||
msgid "Don't show this warning for %d days"
|
||||
msgstr "%d 天内不显示此警告"
|
||||
|
||||
#: redirect-template.php:342
|
||||
msgid "Protected by %s"
|
||||
msgstr "受 %s 保护"
|
||||
|
||||
#: redirect-template.php:385
|
||||
msgid "Don't show this again (%d days)"
|
||||
msgstr "不再显示(%d 天)"
|
||||
|
||||
#: redirect-template.php:461
|
||||
msgid "Link copied to clipboard"
|
||||
msgstr "链接已复制到剪贴板"
|
||||
|
||||
#: redirect-template.php:536
|
||||
msgid "External link (opens in a new window)"
|
||||
msgstr "外部链接(在新窗口中打开)"
|
||||
|
||||
#: class-public.php:69
|
||||
msgid "External link"
|
||||
msgstr "外部链接"
|
||||
|
||||
#: class-public.php:70 class-public.php:137
|
||||
msgid "(opens in a new window)"
|
||||
msgstr "(在新窗口中打开)"
|
||||
|
||||
#: class-public.php:135
|
||||
msgid "opens in a new window"
|
||||
msgstr "在新窗口中打开"
|
||||
|
||||
# Admin JavaScript strings
|
||||
msgid "Domain %s is already in the whitelist."
|
||||
msgstr "域名 %s 已经在白名单中。"
|
||||
|
||||
msgid "Added %s to whitelist."
|
||||
msgstr "已将 %s 添加到白名单。"
|
||||
|
||||
msgid "Added common domains to whitelist."
|
||||
msgstr "已将常用域名添加到白名单。"
|
||||
|
||||
msgid "Whitelist cleared."
|
||||
msgstr "白名单已清空。"
|
||||
|
||||
msgid "Are you sure you want to clear all domains from the whitelist?"
|
||||
msgstr "您确定要清空白名单中的所有域名吗?"
|
||||
|
||||
msgid "Whitelist is empty, cannot export."
|
||||
msgstr "白名单为空,无法导出。"
|
||||
|
||||
msgid "Whitelist exported successfully."
|
||||
msgstr "白名单导出成功。"
|
||||
|
||||
msgid "Export not supported in this browser."
|
||||
msgstr "此浏览器不支持导出功能。"
|
||||
|
||||
msgid "Saving..."
|
||||
msgstr "保存中..."
|
||||
|
||||
msgid "Draft saved"
|
||||
msgstr "草稿已保存"
|
||||
|
||||
msgid "A draft was found for this field. Would you like to restore it?"
|
||||
msgstr "发现此字段的草稿。您要恢复它吗?"
|
389
languages/wpnav-links.pot
Normal file
389
languages/wpnav-links.pot
Normal file
|
@ -0,0 +1,389 @@
|
|||
# Copyright (C) 2024 WPNav Links
|
||||
# This file is distributed under the same license as the WPNav Links package.
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: WPNav Links 2.0.0\n"
|
||||
"Report-Msgid-Bugs-To: https://wordpress.org/support/plugin/wpnav-links\n"
|
||||
"POT-Creation-Date: 2024-01-01 12:00+0000\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"Language-Team: LANGUAGE <LL@li.org>\n"
|
||||
|
||||
#: wpnav-links.php:100
|
||||
msgid "External Links Monitor"
|
||||
msgstr ""
|
||||
|
||||
#: wpnav-links.php:150
|
||||
msgid "Active"
|
||||
msgstr ""
|
||||
|
||||
#: wpnav-links.php:150
|
||||
msgid "Inactive"
|
||||
msgstr ""
|
||||
|
||||
#: wpnav-links.php:153
|
||||
msgid "Manual Only"
|
||||
msgstr ""
|
||||
|
||||
#: wpnav-links.php:159
|
||||
msgid "Total Redirects"
|
||||
msgstr ""
|
||||
|
||||
#: wpnav-links.php:164
|
||||
msgid "Today"
|
||||
msgstr ""
|
||||
|
||||
#: wpnav-links.php:172
|
||||
msgid "Last 7 Days"
|
||||
msgstr ""
|
||||
|
||||
#: wpnav-links.php:177
|
||||
msgid "Whitelisted"
|
||||
msgstr ""
|
||||
|
||||
#: wpnav-links.php:184
|
||||
msgid "Top Domains This Week"
|
||||
msgstr ""
|
||||
|
||||
#: wpnav-links.php:199
|
||||
msgid "No external link activity yet."
|
||||
msgstr ""
|
||||
|
||||
#: wpnav-links.php:201
|
||||
msgid "Enable the plugin to start tracking external links."
|
||||
msgstr ""
|
||||
|
||||
#: wpnav-links.php:208
|
||||
msgid "Enable Plugin"
|
||||
msgstr ""
|
||||
|
||||
#: wpnav-links.php:210
|
||||
msgid "View Details"
|
||||
msgstr ""
|
||||
|
||||
#: wpnav-links.php:213
|
||||
msgid "Manage Whitelist"
|
||||
msgstr ""
|
||||
|
||||
#: wpnav-links.php:322
|
||||
msgid "Settings"
|
||||
msgstr ""
|
||||
|
||||
#: wpnav-links.php:323
|
||||
msgid "Support"
|
||||
msgstr ""
|
||||
|
||||
#: wpnav-links.php:324
|
||||
msgid "Documentation"
|
||||
msgstr ""
|
||||
|
||||
#: class-wpnav-links.php:50
|
||||
msgid "External Links"
|
||||
msgstr ""
|
||||
|
||||
#: class-wpnav-links.php:80
|
||||
msgid "Invalid or missing URL parameter"
|
||||
msgstr ""
|
||||
|
||||
#: class-wpnav-links.php:85
|
||||
msgid "Invalid URL format"
|
||||
msgstr ""
|
||||
|
||||
#: class-wpnav-links.php:169
|
||||
msgid "Redirect Error: "
|
||||
msgstr ""
|
||||
|
||||
#: class-wpnav-links.php:170
|
||||
msgid "External Link Redirect Error"
|
||||
msgstr ""
|
||||
|
||||
#: class-admin.php:20
|
||||
msgid "External Links Redirect"
|
||||
msgstr ""
|
||||
|
||||
#: class-admin.php:21
|
||||
msgid "External Links"
|
||||
msgstr ""
|
||||
|
||||
#: class-admin.php:49
|
||||
msgid "Security check failed"
|
||||
msgstr ""
|
||||
|
||||
#: class-admin.php:53
|
||||
msgid "Insufficient permissions"
|
||||
msgstr ""
|
||||
|
||||
#: class-admin.php:60
|
||||
msgid "Target URL"
|
||||
msgstr ""
|
||||
|
||||
#: class-admin.php:61
|
||||
msgid "Source Page"
|
||||
msgstr ""
|
||||
|
||||
#: class-admin.php:62
|
||||
msgid "Click Time"
|
||||
msgstr ""
|
||||
|
||||
#: class-admin.php:63
|
||||
msgid "IP Address"
|
||||
msgstr ""
|
||||
|
||||
#: class-admin.php:64
|
||||
msgid "User Agent"
|
||||
msgstr ""
|
||||
|
||||
#: admin-page.php:25
|
||||
msgid "You do not have sufficient permissions to access this page."
|
||||
msgstr ""
|
||||
|
||||
#: admin-page.php:47
|
||||
msgid "Basic settings saved successfully!"
|
||||
msgstr ""
|
||||
|
||||
#: admin-page.php:49
|
||||
msgid "Failed to save basic settings. Database error."
|
||||
msgstr ""
|
||||
|
||||
#: admin-page.php:67
|
||||
msgid "Whitelist settings saved successfully!"
|
||||
msgstr ""
|
||||
|
||||
#: admin-page.php:69
|
||||
msgid "Failed to save whitelist settings. Database error."
|
||||
msgstr ""
|
||||
|
||||
#: admin-page.php:99
|
||||
msgid "Redirect page settings saved successfully!"
|
||||
msgstr ""
|
||||
|
||||
#: admin-page.php:101
|
||||
msgid "Failed to save redirect page settings. Database error."
|
||||
msgstr ""
|
||||
|
||||
#: admin-page.php:127
|
||||
msgid "Successfully imported %d domains."
|
||||
msgstr ""
|
||||
|
||||
#: admin-page.php:129
|
||||
msgid "Failed to import CSV data. Database error."
|
||||
msgstr ""
|
||||
|
||||
#: admin-page.php:132
|
||||
msgid "No valid domains found in the CSV file."
|
||||
msgstr ""
|
||||
|
||||
#: admin-page.php:135
|
||||
msgid "Please select a CSV file to upload."
|
||||
msgstr ""
|
||||
|
||||
#: admin-page.php:145
|
||||
msgid "Settings saved successfully!"
|
||||
msgstr ""
|
||||
|
||||
#: admin-page.php:183
|
||||
msgid "Custom Template Detected:"
|
||||
msgstr ""
|
||||
|
||||
#: admin-page.php:183
|
||||
msgid "Your theme has a custom redirect template. Settings below may not apply if your custom template doesn't support them."
|
||||
msgstr ""
|
||||
|
||||
#: admin-page.php:187
|
||||
msgid "Basic Settings"
|
||||
msgstr ""
|
||||
|
||||
#: admin-page.php:190
|
||||
msgid "Redirect Page"
|
||||
msgstr ""
|
||||
|
||||
#: admin-page.php:193
|
||||
msgid "Whitelist"
|
||||
msgstr ""
|
||||
|
||||
#: admin-page.php:196
|
||||
msgid "Statistics"
|
||||
msgstr ""
|
||||
|
||||
#: admin-page.php:199
|
||||
msgid "Import/Export"
|
||||
msgstr ""
|
||||
|
||||
#: admin-page.php:208
|
||||
msgid "Enable Redirect"
|
||||
msgstr ""
|
||||
|
||||
#: admin-page.php:211
|
||||
msgid "Enable external link redirect functionality"
|
||||
msgstr ""
|
||||
|
||||
#: admin-page.php:215
|
||||
msgid "Auto Redirect"
|
||||
msgstr ""
|
||||
|
||||
#: admin-page.php:218
|
||||
msgid "Enable automatic redirect"
|
||||
msgstr ""
|
||||
|
||||
#: admin-page.php:219
|
||||
msgid "When enabled, users will be automatically redirected after the specified delay."
|
||||
msgstr ""
|
||||
|
||||
#: admin-page.php:223
|
||||
msgid "Redirect Delay"
|
||||
msgstr ""
|
||||
|
||||
#: admin-page.php:226
|
||||
msgid "Seconds to wait before automatic redirect (1-30 seconds)."
|
||||
msgstr ""
|
||||
|
||||
#: admin-page.php:230
|
||||
msgid "Open in New Tab"
|
||||
msgstr ""
|
||||
|
||||
#: admin-page.php:233
|
||||
msgid "Open external links in new tab/window"
|
||||
msgstr ""
|
||||
|
||||
#: admin-page.php:237
|
||||
msgid "URL Format"
|
||||
msgstr ""
|
||||
|
||||
#: admin-page.php:241
|
||||
msgid "Query Format (?url=encoded_url)"
|
||||
msgstr ""
|
||||
|
||||
#: admin-page.php:245
|
||||
msgid "Path Format (/go/encoded_url)"
|
||||
msgstr ""
|
||||
|
||||
#: admin-page.php:249
|
||||
msgid "Target Format (?target=encoded_url)"
|
||||
msgstr ""
|
||||
|
||||
#: admin-page.php:252
|
||||
msgid "Choose URL format style for external link redirects."
|
||||
msgstr ""
|
||||
|
||||
#: admin-page.php:257
|
||||
msgid "Content Processing"
|
||||
msgstr ""
|
||||
|
||||
#: admin-page.php:260
|
||||
msgid "Process Post Content"
|
||||
msgstr ""
|
||||
|
||||
#: admin-page.php:263
|
||||
msgid "Process external links in post and page content"
|
||||
msgstr ""
|
||||
|
||||
#: redirect-template.php:25
|
||||
msgid "External Link Warning"
|
||||
msgstr ""
|
||||
|
||||
#: redirect-template.php:27
|
||||
msgid "You are about to visit:"
|
||||
msgstr ""
|
||||
|
||||
#: redirect-template.php:28
|
||||
msgid "You are about to leave this site and visit an external website. We are not responsible for the content of external websites."
|
||||
msgstr ""
|
||||
|
||||
#: redirect-template.php:32
|
||||
msgid "Continue"
|
||||
msgstr ""
|
||||
|
||||
#: redirect-template.php:33
|
||||
msgid "Back"
|
||||
msgstr ""
|
||||
|
||||
#: redirect-template.php:35
|
||||
msgid "Auto redirect in {seconds} seconds"
|
||||
msgstr ""
|
||||
|
||||
#: redirect-template.php:55
|
||||
msgid "External Link Redirect - %s"
|
||||
msgstr ""
|
||||
|
||||
#: redirect-template.php:146
|
||||
msgid "Don't show this again"
|
||||
msgstr ""
|
||||
|
||||
#: redirect-template.php:213
|
||||
msgid "Destination Information"
|
||||
msgstr ""
|
||||
|
||||
#: redirect-template.php:230
|
||||
msgid "Copy"
|
||||
msgstr ""
|
||||
|
||||
#: redirect-template.php:238
|
||||
msgid "Security Status"
|
||||
msgstr ""
|
||||
|
||||
#: redirect-template.php:244
|
||||
msgid "This website does not use HTTPS encryption. Your data may not be secure."
|
||||
msgstr ""
|
||||
|
||||
#: redirect-template.php:252
|
||||
msgid "Security Tips"
|
||||
msgstr ""
|
||||
|
||||
#: redirect-template.php:255
|
||||
msgid "Verify the link is from a trusted source"
|
||||
msgstr ""
|
||||
|
||||
#: redirect-template.php:256
|
||||
msgid "Avoid entering personal information on HTTP sites"
|
||||
msgstr ""
|
||||
|
||||
#: redirect-template.php:257
|
||||
msgid "Be cautious of download requests"
|
||||
msgstr ""
|
||||
|
||||
#: redirect-template.php:258
|
||||
msgid "Check for secure HTTPS connection"
|
||||
msgstr ""
|
||||
|
||||
#: redirect-template.php:263
|
||||
msgid "Additional Caution Required"
|
||||
msgstr ""
|
||||
|
||||
#: redirect-template.php:265
|
||||
msgid "This link does not use secure HTTPS"
|
||||
msgstr ""
|
||||
|
||||
#: redirect-template.php:266
|
||||
msgid "Consider if this site is trustworthy before proceeding"
|
||||
msgstr ""
|
||||
|
||||
#: redirect-template.php:295
|
||||
msgid "Don't show this warning for %d days"
|
||||
msgstr ""
|
||||
|
||||
#: redirect-template.php:342
|
||||
msgid "Protected by %s"
|
||||
msgstr ""
|
||||
|
||||
#: redirect-template.php:385
|
||||
msgid "Don't show this again (%d days)"
|
||||
msgstr ""
|
||||
|
||||
#: redirect-template.php:461
|
||||
msgid "Link copied to clipboard"
|
||||
msgstr ""
|
||||
|
||||
#: redirect-template.php:536
|
||||
msgid "External link (opens in a new window)"
|
||||
msgstr ""
|
||||
|
||||
#: class-public.php:69
|
||||
msgid "External link"
|
||||
msgstr ""
|
||||
|
||||
#: class-public.php:70
|
||||
msgid "(opens in a new window)"
|
||||
msgstr ""
|
176
readme.txt
Normal file
176
readme.txt
Normal file
|
@ -0,0 +1,176 @@
|
|||
=== WPNav Links ===
|
||||
Contributors: wpnavlinks
|
||||
Tags: external links, redirect, security, links, SEO
|
||||
Requires at least: 5.0
|
||||
Tested up to: 6.4
|
||||
Requires PHP: 7.4
|
||||
Stable tag: 2.0.0
|
||||
License: GPLv2 or later
|
||||
License URI: https://www.gnu.org/licenses/gpl-2.0.html
|
||||
|
||||
A comprehensive WordPress external link redirect tool with customizable redirect pages, security features, and detailed analytics.
|
||||
|
||||
== Description ==
|
||||
|
||||
WPNav Links is a powerful WordPress plugin that helps you manage external links on your website by redirecting them through a customizable warning page. This provides better user experience, improved security, and detailed analytics about external link usage.
|
||||
|
||||
= Key Features =
|
||||
|
||||
* **Customizable Redirect Pages** - Choose from 4 different templates (Simple, Minimal, Default, Full) with 3 color schemes
|
||||
* **Smart Content Processing** - Automatically processes external links in posts, pages, comments, and widgets
|
||||
* **Domain Whitelist Management** - Easily manage domains that should not be redirected
|
||||
* **Detailed Analytics** - Track external link clicks with comprehensive statistics
|
||||
* **Security Features** - HTTPS detection, security warnings, and safety tips
|
||||
* **User Experience Options** - Auto-redirect timers, progress bars, and "don't show again" options
|
||||
* **Accessibility Ready** - Proper ARIA labels, keyboard navigation, and screen reader support
|
||||
* **Mobile Optimized** - Touch gestures and responsive design for mobile devices
|
||||
* **SEO Friendly** - Proper nofollow, noopener, and noreferrer attributes
|
||||
* **Import/Export** - Bulk import/export domain whitelists via CSV
|
||||
|
||||
= Template Options =
|
||||
|
||||
1. **Simple** - Ultra-minimal design with just the essentials
|
||||
2. **Minimal** - Clean and lightweight design
|
||||
3. **Default** - Balanced information display
|
||||
4. **Full** - Comprehensive with detailed security information
|
||||
|
||||
= Color Schemes =
|
||||
|
||||
* Blue - Professional appearance
|
||||
* Green - Safe and trustworthy feel
|
||||
* Red - Warning and caution emphasis
|
||||
|
||||
= Advanced Features =
|
||||
|
||||
* **Auto Whitelist Rules** - Automatically whitelist same-root domains and search engines
|
||||
* **Flexible URL Formats** - Query, Path, or Target parameter formats
|
||||
* **Content Filtering** - Choose which content areas to process (posts, comments, widgets)
|
||||
* **Admin Dashboard Widget** - Quick overview of link activity and statistics
|
||||
* **Custom CSS Support** - Add your own styling to redirect pages
|
||||
* **Cookie Management** - Remember user preferences for specified durations
|
||||
* **Data Cleanup** - Automatic old data cleanup with configurable retention periods
|
||||
|
||||
== Installation ==
|
||||
|
||||
1. Upload the plugin files to the `/wp-content/plugins/wpnav-links` directory, or install the plugin through the WordPress plugins screen directly.
|
||||
2. Activate the plugin through the 'Plugins' screen in WordPress.
|
||||
3. Use the Tools → External Links screen to configure the plugin.
|
||||
4. Customize your redirect page design and add domains to your whitelist.
|
||||
|
||||
== Frequently Asked Questions ==
|
||||
|
||||
= How does the plugin work? =
|
||||
|
||||
The plugin automatically detects external links in your content and redirects them through a customizable warning page. Users see information about the destination before being redirected.
|
||||
|
||||
= Can I customize the redirect page? =
|
||||
|
||||
Yes! You can choose from 4 different templates, 3 color schemes, customize text content, and even add custom CSS.
|
||||
|
||||
= Which domains should I whitelist? =
|
||||
|
||||
Common domains to whitelist include social media platforms, search engines, and trusted partner sites. The plugin can automatically whitelist same-root domains and major search engines.
|
||||
|
||||
= Does the plugin affect SEO? =
|
||||
|
||||
The plugin actually helps SEO by adding proper nofollow, noopener, and noreferrer attributes to external links, which is recommended by search engines.
|
||||
|
||||
= Can I see statistics about external link usage? =
|
||||
|
||||
Yes! The plugin provides detailed analytics including top clicked links, daily/monthly statistics, HTTPS vs HTTP breakdown, and more.
|
||||
|
||||
= Is the plugin accessible? =
|
||||
|
||||
Yes, the plugin is built with accessibility in mind, including proper ARIA labels, keyboard navigation, and screen reader support.
|
||||
|
||||
= Can I export my whitelist? =
|
||||
|
||||
Yes, you can export your domain whitelist as a CSV file and also import domains from CSV files for bulk management.
|
||||
|
||||
== Screenshots ==
|
||||
|
||||
1. **Admin Dashboard** - Main settings interface with tabbed navigation
|
||||
2. **Redirect Page Templates** - Four different template styles to choose from
|
||||
3. **Live Preview** - Real-time preview of your redirect page design
|
||||
4. **Whitelist Management** - Easy domain whitelist management with quick-add buttons
|
||||
5. **Statistics Dashboard** - Comprehensive analytics and reporting
|
||||
6. **Mobile Experience** - Responsive design works perfectly on mobile devices
|
||||
|
||||
== Changelog ==
|
||||
|
||||
= 2.0.0 =
|
||||
* Major user experience improvements
|
||||
* Added warning message show/hide control
|
||||
* Fixed back button functionality with multiple fallback strategies
|
||||
* Implemented complete internationalization support
|
||||
* Enhanced mobile touch gestures and keyboard navigation
|
||||
* Improved accessibility with better ARIA labels and screen reader support
|
||||
* Added draft saving for textarea fields
|
||||
* Enhanced preview functionality with real-time updates
|
||||
* Better error handling and user feedback
|
||||
* Optimized database queries and performance improvements
|
||||
|
||||
= 1.9.0 =
|
||||
* Added 4 redirect page templates (Simple, Minimal, Default, Full)
|
||||
* Introduced 3 color schemes (Blue, Green, Red)
|
||||
* Enhanced security features with HTTPS detection
|
||||
* Added progress bar option for countdown timer
|
||||
* Improved mobile responsiveness and touch support
|
||||
* Added copy-to-clipboard functionality
|
||||
* Enhanced statistics dashboard with more metrics
|
||||
|
||||
= 1.8.0 =
|
||||
* Added comprehensive analytics and statistics
|
||||
* Introduced admin dashboard widget
|
||||
* Added CSV import/export functionality
|
||||
* Enhanced whitelist management with quick-add buttons
|
||||
* Improved auto-redirect functionality with countdown timer
|
||||
* Added custom CSS support for redirect pages
|
||||
|
||||
= 1.7.0 =
|
||||
* Added domain whitelist functionality
|
||||
* Introduced auto-whitelist rules for same-root domains and search engines
|
||||
* Enhanced content processing with better DOM handling
|
||||
* Added cookie management for user preferences
|
||||
* Improved admin interface with tabbed navigation
|
||||
|
||||
= 1.6.0 =
|
||||
* Added multiple URL format options (Query, Path, Target)
|
||||
* Enhanced security with script injection detection
|
||||
* Improved content filtering options
|
||||
* Added admin exemption feature
|
||||
* Better error handling and validation
|
||||
|
||||
= 1.5.0 =
|
||||
* Initial stable release
|
||||
* Basic external link redirection
|
||||
* Simple redirect page
|
||||
* Admin configuration panel
|
||||
|
||||
== Upgrade Notice ==
|
||||
|
||||
= 2.0.0 =
|
||||
Major update with enhanced user experience, internationalization support, and improved functionality. Backup your settings before upgrading.
|
||||
|
||||
== Support ==
|
||||
|
||||
For support, please visit our [support forum](https://example.com/support) or [documentation](https://example.com/docs).
|
||||
|
||||
== Contributing ==
|
||||
|
||||
We welcome contributions! Please visit our [GitHub repository](https://github.com/example/wpnav-links) to report issues or submit pull requests.
|
||||
|
||||
== Privacy Policy ==
|
||||
|
||||
This plugin collects basic analytics data about external link clicks including:
|
||||
- Target URL
|
||||
- Source page
|
||||
- Timestamp
|
||||
- IP address (anonymized)
|
||||
- User agent string
|
||||
|
||||
This data is stored locally on your server and is used solely for analytics purposes. No data is transmitted to external services.
|
||||
|
||||
== Credits ==
|
||||
|
||||
Developed by the WPNav Links team with contributions from the WordPress community.
|
561
redirect-template.php
Normal file
561
redirect-template.php
Normal file
|
@ -0,0 +1,561 @@
|
|||
<?php
|
||||
|
||||
if (!defined('ABSPATH')) {
|
||||
exit;
|
||||
}
|
||||
|
||||
$target_url = get_query_var('target_url', '');
|
||||
if (empty($target_url) && isset($_GET['target'])) {
|
||||
$target_url = urldecode($_GET['target']);
|
||||
}
|
||||
|
||||
$source_url = get_query_var('source_url', '');
|
||||
if (empty($source_url) && isset($_GET['ref'])) {
|
||||
$source_url = urldecode($_GET['ref']);
|
||||
}
|
||||
|
||||
if (empty($target_url)) {
|
||||
wp_redirect(home_url());
|
||||
exit;
|
||||
}
|
||||
|
||||
$options = get_option('wpnav_links_options');
|
||||
|
||||
$delay = isset($options['redirect_delay']) ? intval($options['redirect_delay']) : 5;
|
||||
$auto_redirect_enabled = isset($options['auto_redirect_enabled']) ? $options['auto_redirect_enabled'] : 1;
|
||||
$color_scheme = isset($options['color_scheme']) ? $options['color_scheme'] : 'blue';
|
||||
$page_title = isset($options['page_title']) ? $options['page_title'] : __('External Link Warning', 'wpnav-links');
|
||||
$page_subtitle = isset($options['page_subtitle']) ? $options['page_subtitle'] : '';
|
||||
$url_label = isset($options['url_label']) ? $options['url_label'] : __('You are about to visit:', 'wpnav-links');
|
||||
$warning_text = isset($options['warning_text']) ? $options['warning_text'] : __('You are about to leave this site and visit an external website. We are not responsible for the content of external websites.', 'wpnav-links');
|
||||
$show_warning_message = isset($options['show_warning_message']) ? $options['show_warning_message'] : 1;
|
||||
$cookie_duration = isset($options['cookie_duration']) ? intval($options['cookie_duration']) : 30;
|
||||
$template = isset($options['template']) ? $options['template'] : 'default';
|
||||
$show_logo = isset($options['show_logo']) ? $options['show_logo'] : false;
|
||||
$show_url_full = isset($options['show_url_full']) ? $options['show_url_full'] : false;
|
||||
$show_security_info = isset($options['show_security_info']) ? $options['show_security_info'] : 1;
|
||||
$show_security_tips = isset($options['show_security_tips']) ? $options['show_security_tips'] : 0;
|
||||
$show_back_button = isset($options['show_back_button']) ? $options['show_back_button'] : 1;
|
||||
$custom_css = isset($options['custom_css']) ? $options['custom_css'] : '';
|
||||
$button_text_continue = isset($options['button_text_continue']) ? $options['button_text_continue'] : __('Continue', 'wpnav-links');
|
||||
$button_text_back = isset($options['button_text_back']) ? $options['button_text_back'] : __('Back', 'wpnav-links');
|
||||
$button_style = isset($options['button_style']) ? $options['button_style'] : 'rounded';
|
||||
$countdown_text = isset($options['countdown_text']) ? $options['countdown_text'] : __('Auto redirect in {seconds} seconds', 'wpnav-links');
|
||||
$show_progress_bar = isset($options['show_progress_bar']) ? $options['show_progress_bar'] : 0;
|
||||
|
||||
$domain = parse_url($target_url, PHP_URL_HOST);
|
||||
$is_https = strpos($target_url, 'https://') === 0;
|
||||
|
||||
$color_scheme_class = 'wpnav-color-' . $color_scheme;
|
||||
$no_redirect_checked = isset($_COOKIE['wpnav_noredirect']) && $_COOKIE['wpnav_noredirect'] == '1';
|
||||
|
||||
$is_mobile = wp_is_mobile();
|
||||
?>
|
||||
<!DOCTYPE html>
|
||||
<html <?php language_attributes(); ?>>
|
||||
<head>
|
||||
<meta charset="<?php bloginfo('charset'); ?>">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta name="robots" content="noindex,nofollow">
|
||||
<title><?php printf(__('External Link Redirect - %s', 'wpnav-links'), get_bloginfo('name')); ?></title>
|
||||
|
||||
<?php wp_head(); ?>
|
||||
|
||||
<?php if (!empty($custom_css)) : ?>
|
||||
<style type="text/css">
|
||||
<?php echo wp_kses($custom_css, array()); ?>
|
||||
</style>
|
||||
<?php endif; ?>
|
||||
</head>
|
||||
|
||||
<body class="wpnav-redirect-page <?php echo esc_attr($color_scheme_class); ?> <?php echo $is_mobile ? 'wpnav-mobile' : 'wpnav-desktop'; ?>" ontouchstart="">
|
||||
<div class="wpnav-page-overlay">
|
||||
<?php if ($template == 'simple') : ?>
|
||||
<div class="wpnav-container wpnav-simple">
|
||||
<h1 class="wpnav-title"><?php echo esc_html($page_title); ?></h1>
|
||||
|
||||
<div class="wpnav-url-container">
|
||||
<div class="wpnav-url" data-url="<?php echo esc_attr($target_url); ?>">
|
||||
<span class="wpnav-url-domain"><?php echo esc_html($domain); ?></span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="wpnav-buttons">
|
||||
<button type="button" class="wpnav-btn wpnav-btn-primary wpnav-btn-<?php echo esc_attr($button_style); ?>" id="wpnav-continue" data-target="<?php echo esc_attr($target_url); ?>">
|
||||
<?php echo esc_html($button_text_continue); ?>
|
||||
</button>
|
||||
<?php if ($show_back_button) : ?>
|
||||
<button type="button" class="wpnav-btn wpnav-btn-secondary wpnav-btn-<?php echo esc_attr($button_style); ?>" id="wpnav-back">
|
||||
<?php echo esc_html($button_text_back); ?>
|
||||
</button>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
|
||||
<?php if ($auto_redirect_enabled && $delay > 0) : ?>
|
||||
<div class="wpnav-countdown">
|
||||
<span id="wpnav-countdown-text"><?php echo str_replace('{seconds}', $delay, esc_html($countdown_text)); ?></span>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
|
||||
<?php elseif ($template == 'minimal') : ?>
|
||||
<div class="wpnav-container wpnav-minimal">
|
||||
<?php if ($show_logo && has_custom_logo()) : ?>
|
||||
<div class="wpnav-logo">
|
||||
<?php the_custom_logo(); ?>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
|
||||
<h1 class="wpnav-title"><?php echo esc_html($page_title); ?></h1>
|
||||
<?php if (!empty($page_subtitle)) : ?>
|
||||
<p class="wpnav-subtitle"><?php echo esc_html($page_subtitle); ?></p>
|
||||
<?php endif; ?>
|
||||
|
||||
<?php if ($show_warning_message && !empty(trim($warning_text))) : ?>
|
||||
<div class="wpnav-warning"><?php echo esc_html($warning_text); ?></div>
|
||||
<?php endif; ?>
|
||||
|
||||
<div class="wpnav-url-container">
|
||||
<div class="wpnav-url-label"><?php echo esc_html($url_label); ?></div>
|
||||
<div class="wpnav-url" data-url="<?php echo esc_attr($target_url); ?>">
|
||||
<span class="wpnav-url-domain"><?php echo esc_html($domain); ?></span>
|
||||
<?php if ($show_url_full) : ?>
|
||||
<div class="wpnav-url-full"><?php echo esc_html($target_url); ?></div>
|
||||
<?php endif; ?>
|
||||
<?php if ($show_security_info) : ?>
|
||||
<div class="wpnav-security-status">
|
||||
<span class="wpnav-https-badge <?php echo $is_https ? 'secure' : 'insecure'; ?>">
|
||||
<?php echo $is_https ? 'HTTPS' : 'HTTP'; ?>
|
||||
</span>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="wpnav-buttons">
|
||||
<button type="button" class="wpnav-btn wpnav-btn-primary wpnav-btn-<?php echo esc_attr($button_style); ?>" id="wpnav-continue" data-target="<?php echo esc_attr($target_url); ?>">
|
||||
<?php echo esc_html($button_text_continue); ?>
|
||||
</button>
|
||||
<?php if ($show_back_button) : ?>
|
||||
<button type="button" class="wpnav-btn wpnav-btn-secondary wpnav-btn-<?php echo esc_attr($button_style); ?>" id="wpnav-back">
|
||||
<?php echo esc_html($button_text_back); ?>
|
||||
</button>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
|
||||
<div class="wpnav-options">
|
||||
<label class="wpnav-checkbox">
|
||||
<input type="checkbox" id="wpnav-no-redirect" <?php checked($no_redirect_checked); ?>>
|
||||
<span class="checkmark"></span>
|
||||
<?php esc_html_e('Don\'t show this again', 'wpnav-links'); ?>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<?php if ($auto_redirect_enabled && $delay > 0) : ?>
|
||||
<div class="wpnav-countdown">
|
||||
<span id="wpnav-countdown-text"><?php echo str_replace('{seconds}', $delay, esc_html($countdown_text)); ?></span>
|
||||
<?php if ($show_progress_bar) : ?>
|
||||
<div class="wpnav-progress-bar">
|
||||
<div class="wpnav-progress-fill" id="progress-fill"></div>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
|
||||
<?php elseif ($template == 'full') : ?>
|
||||
<div class="wpnav-container wpnav-full">
|
||||
<div class="wpnav-header">
|
||||
<?php if ($show_logo && has_custom_logo()) : ?>
|
||||
<div class="wpnav-logo">
|
||||
<?php the_custom_logo(); ?>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
|
||||
<h1 class="wpnav-title"><?php echo esc_html($page_title); ?></h1>
|
||||
<?php if (!empty($page_subtitle)) : ?>
|
||||
<p class="wpnav-subtitle"><?php echo esc_html($page_subtitle); ?></p>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
|
||||
<?php if ($show_warning_message && !empty(trim($warning_text))) : ?>
|
||||
<div class="wpnav-warning">
|
||||
<?php echo esc_html($warning_text); ?>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
|
||||
<div class="wpnav-info">
|
||||
<div class="wpnav-info-item">
|
||||
<h3 class="wpnav-info-title"><?php esc_html_e('Destination Information', 'wpnav-links'); ?></h3>
|
||||
<div class="wpnav-url-container">
|
||||
<div class="wpnav-url-label"><?php echo esc_html($url_label); ?></div>
|
||||
<div class="wpnav-url" data-url="<?php echo esc_attr($target_url); ?>">
|
||||
<div class="wpnav-url-parts">
|
||||
<span class="wpnav-url-domain"><?php echo esc_html($domain); ?></span>
|
||||
<span class="wpnav-url-path"><?php echo esc_html(str_replace($domain, '', parse_url($target_url, PHP_URL_HOST) . parse_url($target_url, PHP_URL_PATH))); ?></span>
|
||||
</div>
|
||||
<?php if ($show_url_full) : ?>
|
||||
<div class="wpnav-url-full"><?php echo esc_html($target_url); ?></div>
|
||||
<?php endif; ?>
|
||||
<button type="button" class="wpnav-copy-btn" data-copy="<?php echo esc_attr($target_url); ?>">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none">
|
||||
<path d="M16 1H4C2.9 1 2 1.9 2 3V17H4V3H16V1ZM19 5H8C6.9 5 6 5.9 6 7V21C6 22.1 6.9 23 8 23H19C20.1 23 21 22.1 21 21V7C21 5.9 20.1 5 19 5ZM19 21H8V7H19V21Z" fill="currentColor"/>
|
||||
</svg>
|
||||
<?php esc_html_e('Copy', 'wpnav-links'); ?>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<?php if ($show_security_info) : ?>
|
||||
<div class="wpnav-security-status">
|
||||
<div class="wpnav-security-header">
|
||||
<span class="wpnav-security-label"><?php esc_html_e('Security Status', 'wpnav-links'); ?></span>
|
||||
<span class="wpnav-https-badge <?php echo $is_https ? 'secure' : 'insecure'; ?>">
|
||||
<?php echo $is_https ? 'HTTPS' : 'HTTP'; ?>
|
||||
</span>
|
||||
</div>
|
||||
<?php if (!$is_https) : ?>
|
||||
<div class="wpnav-security-warning">
|
||||
<p>⚠️ <?php esc_html_e('This website does not use HTTPS encryption. Your data may not be secure.', 'wpnav-links'); ?></p>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
|
||||
<?php if ($show_security_tips) : ?>
|
||||
<div class="wpnav-info-item">
|
||||
<h3 class="wpnav-info-title"><?php esc_html_e('Security Tips', 'wpnav-links'); ?></h3>
|
||||
<div class="wpnav-tips">
|
||||
<ul class="wpnav-tips-list">
|
||||
<li><?php esc_html_e('Verify the link is from a trusted source', 'wpnav-links'); ?></li>
|
||||
<li><?php esc_html_e('Avoid entering personal information on HTTP sites', 'wpnav-links'); ?></li>
|
||||
<li><?php esc_html_e('Be cautious of download requests', 'wpnav-links'); ?></li>
|
||||
<li><?php esc_html_e('Check for secure HTTPS connection', 'wpnav-links'); ?></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<?php if (!$is_https) : ?>
|
||||
<div class="wpnav-warning-tips">
|
||||
<h4>⚠️ <?php esc_html_e('Additional Caution Required', 'wpnav-links'); ?></h4>
|
||||
<ul>
|
||||
<li><?php esc_html_e('This link does not use secure HTTPS', 'wpnav-links'); ?></li>
|
||||
<li><?php esc_html_e('Consider if this site is trustworthy before proceeding', 'wpnav-links'); ?></li>
|
||||
</ul>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
|
||||
<div class="wpnav-actions">
|
||||
<div class="wpnav-buttons">
|
||||
<button type="button" class="wpnav-btn wpnav-btn-primary wpnav-btn-<?php echo esc_attr($button_style); ?>" id="wpnav-continue" data-target="<?php echo esc_attr($target_url); ?>">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none">
|
||||
<path d="M12 4l-1.41 1.41L16.17 11H4v2h12.17l-5.58 5.59L12 20l8-8z" fill="currentColor"/>
|
||||
</svg>
|
||||
<?php echo esc_html($button_text_continue); ?>
|
||||
</button>
|
||||
|
||||
<?php if ($show_back_button) : ?>
|
||||
<button type="button" class="wpnav-btn wpnav-btn-secondary wpnav-btn-<?php echo esc_attr($button_style); ?>" id="wpnav-back">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none">
|
||||
<path d="M12 20l1.41-1.41L7.83 13H20v-2H7.83l5.58-5.59L12 4l-8 8z" fill="currentColor"/>
|
||||
</svg>
|
||||
<?php echo esc_html($button_text_back); ?>
|
||||
</button>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
|
||||
<div class="wpnav-options">
|
||||
<label class="wpnav-checkbox">
|
||||
<input type="checkbox" id="wpnav-no-redirect" <?php checked($no_redirect_checked); ?>>
|
||||
<span class="checkmark"></span>
|
||||
<?php printf(__('Don\'t show this warning for %d days', 'wpnav-links'), $cookie_duration); ?>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<?php if ($auto_redirect_enabled && $delay > 0) : ?>
|
||||
<div class="wpnav-countdown">
|
||||
<div class="countdown-circle">
|
||||
<svg width="32" height="32" viewBox="0 0 32 32">
|
||||
<circle cx="16" cy="16" r="14" stroke="currentColor" stroke-width="2" fill="none" opacity="0.3"/>
|
||||
<circle id="countdown-progress" cx="16" cy="16" r="14" stroke="currentColor" stroke-width="2" fill="none"
|
||||
stroke-dasharray="87.96" stroke-dashoffset="0" transform="rotate(-90 16 16)"/>
|
||||
</svg>
|
||||
</div>
|
||||
<span id="wpnav-countdown-text"><?php echo str_replace('{seconds}', $delay, esc_html($countdown_text)); ?></span>
|
||||
<?php if ($show_progress_bar) : ?>
|
||||
<div class="wpnav-progress-bar">
|
||||
<div class="wpnav-progress-fill" id="progress-fill"></div>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
|
||||
<div class="wpnav-footer">
|
||||
<div class="wpnav-footer-text">
|
||||
<?php printf(__('Protected by %s', 'wpnav-links'), '<a href="' . esc_url(home_url()) . '">' . esc_html(get_bloginfo('name')) . '</a>'); ?>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<?php else : ?>
|
||||
<div class="wpnav-container wpnav-default">
|
||||
<?php if ($show_logo && has_custom_logo()) : ?>
|
||||
<div class="wpnav-logo">
|
||||
<?php the_custom_logo(); ?>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
|
||||
<h1 class="wpnav-title"><?php echo esc_html($page_title); ?></h1>
|
||||
<?php if (!empty($page_subtitle)) : ?>
|
||||
<p class="wpnav-subtitle"><?php echo esc_html($page_subtitle); ?></p>
|
||||
<?php endif; ?>
|
||||
|
||||
<?php if ($show_warning_message && !empty(trim($warning_text))) : ?>
|
||||
<div class="wpnav-warning">
|
||||
<?php echo esc_html($warning_text); ?>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
|
||||
<div class="wpnav-url-container">
|
||||
<div class="wpnav-url-label"><?php echo esc_html($url_label); ?></div>
|
||||
<div class="wpnav-url" data-url="<?php echo esc_attr($target_url); ?>">
|
||||
<span class="wpnav-url-domain"><?php echo esc_html($domain); ?></span>
|
||||
<?php if ($show_url_full) : ?>
|
||||
<div class="wpnav-url-full"><?php echo esc_html($target_url); ?></div>
|
||||
<?php endif; ?>
|
||||
<?php if ($show_security_info) : ?>
|
||||
<div class="wpnav-security-status">
|
||||
<span class="wpnav-https-badge <?php echo $is_https ? 'secure' : 'insecure'; ?>">
|
||||
<?php echo $is_https ? 'HTTPS' : 'HTTP'; ?>
|
||||
</span>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<?php if ($show_security_tips) : ?>
|
||||
<div class="wpnav-security-tips">
|
||||
<h4><?php esc_html_e('Security Tips', 'wpnav-links'); ?></h4>
|
||||
<ul>
|
||||
<li><?php esc_html_e('Verify the link is from a trusted source', 'wpnav-links'); ?></li>
|
||||
<li><?php esc_html_e('Check for secure HTTPS connection', 'wpnav-links'); ?></li>
|
||||
<li><?php esc_html_e('Be cautious of download requests', 'wpnav-links'); ?></li>
|
||||
</ul>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
|
||||
<div class="wpnav-buttons">
|
||||
<button type="button" class="wpnav-btn wpnav-btn-primary wpnav-btn-<?php echo esc_attr($button_style); ?>" id="wpnav-continue" data-target="<?php echo esc_attr($target_url); ?>">
|
||||
<?php echo esc_html($button_text_continue); ?>
|
||||
</button>
|
||||
<?php if ($show_back_button) : ?>
|
||||
<button type="button" class="wpnav-btn wpnav-btn-secondary wpnav-btn-<?php echo esc_attr($button_style); ?>" id="wpnav-back">
|
||||
<?php echo esc_html($button_text_back); ?>
|
||||
</button>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
|
||||
<div class="wpnav-options">
|
||||
<label class="wpnav-checkbox">
|
||||
<input type="checkbox" id="wpnav-no-redirect" <?php checked($no_redirect_checked); ?>>
|
||||
<span class="checkmark"></span>
|
||||
<?php printf(__('Don\'t show this again (%d days)', 'wpnav-links'), $cookie_duration); ?>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<?php if ($auto_redirect_enabled && $delay > 0) : ?>
|
||||
<div class="wpnav-countdown">
|
||||
<span id="wpnav-countdown-text"><?php echo str_replace('{seconds}', $delay, esc_html($countdown_text)); ?></span>
|
||||
<?php if ($show_progress_bar) : ?>
|
||||
<div class="wpnav-progress-bar">
|
||||
<div class="wpnav-progress-fill" id="progress-fill"></div>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
|
||||
<script type="text/javascript">
|
||||
(function() {
|
||||
var countdown = <?php echo $auto_redirect_enabled ? esc_js($delay) : 0; ?>;
|
||||
var countdownElement = document.getElementById('wpnav-countdown-text');
|
||||
var progressElement = document.getElementById('progress-fill');
|
||||
var continueButton = document.getElementById('wpnav-continue');
|
||||
var backButton = document.getElementById('wpnav-back');
|
||||
var targetUrl = continueButton ? continueButton.getAttribute('data-target') : '';
|
||||
var noRedirectCheckbox = document.getElementById('wpnav-no-redirect');
|
||||
var copyButtons = document.querySelectorAll('.wpnav-copy-btn');
|
||||
var countdownTemplate = <?php echo json_encode($countdown_text); ?>;
|
||||
|
||||
var initialCountdown = countdown;
|
||||
var autoRedirectEnabled = <?php echo $auto_redirect_enabled ? 'true' : 'false'; ?>;
|
||||
|
||||
// Enhanced back button functionality
|
||||
function goBack() {
|
||||
var referrer = document.referrer;
|
||||
var sourceUrl = <?php echo json_encode($source_url); ?>;
|
||||
|
||||
// Try multiple back strategies
|
||||
if (referrer && referrer !== window.location.href) {
|
||||
// If we have a valid referrer, go there
|
||||
window.location.href = referrer;
|
||||
} else if (sourceUrl && sourceUrl !== window.location.href) {
|
||||
// If we have source URL from plugin, use it
|
||||
window.location.href = sourceUrl;
|
||||
} else if (window.history.length > 1) {
|
||||
// Use browser history if available
|
||||
window.history.back();
|
||||
} else {
|
||||
// Fallback to homepage
|
||||
window.location.href = <?php echo json_encode(home_url()); ?>;
|
||||
}
|
||||
}
|
||||
|
||||
if (backButton) {
|
||||
backButton.addEventListener('click', function(e) {
|
||||
e.preventDefault();
|
||||
goBack();
|
||||
});
|
||||
}
|
||||
|
||||
if (noRedirectCheckbox) {
|
||||
noRedirectCheckbox.addEventListener('change', function() {
|
||||
var cookieExpiry = new Date();
|
||||
cookieExpiry.setDate(cookieExpiry.getDate() + <?php echo esc_js($cookie_duration); ?>);
|
||||
document.cookie = "wpnav_noredirect=" + (this.checked ? "1" : "0") +
|
||||
"; expires=" + cookieExpiry.toUTCString() +
|
||||
"; path=/; SameSite=Strict";
|
||||
});
|
||||
}
|
||||
|
||||
copyButtons.forEach(function(btn) {
|
||||
btn.addEventListener('click', function() {
|
||||
var url = this.getAttribute('data-copy');
|
||||
if (navigator.clipboard) {
|
||||
navigator.clipboard.writeText(url).then(function() {
|
||||
showToast(<?php echo json_encode(__('Link copied to clipboard', 'wpnav-links')); ?>);
|
||||
});
|
||||
} else {
|
||||
var input = document.createElement('input');
|
||||
input.value = url;
|
||||
document.body.appendChild(input);
|
||||
input.select();
|
||||
document.execCommand('copy');
|
||||
document.body.removeChild(input);
|
||||
showToast(<?php echo json_encode(__('Link copied to clipboard', 'wpnav-links')); ?>);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
function showToast(message) {
|
||||
var toast = document.createElement('div');
|
||||
toast.className = 'wpnav-toast';
|
||||
toast.textContent = message;
|
||||
document.body.appendChild(toast);
|
||||
|
||||
setTimeout(function() {
|
||||
toast.classList.add('show');
|
||||
}, 100);
|
||||
|
||||
setTimeout(function() {
|
||||
toast.classList.remove('show');
|
||||
setTimeout(function() {
|
||||
if (document.body.contains(toast)) {
|
||||
document.body.removeChild(toast);
|
||||
}
|
||||
}, 300);
|
||||
}, 2000);
|
||||
}
|
||||
|
||||
if (continueButton) {
|
||||
continueButton.addEventListener('click', function() {
|
||||
window.location.href = targetUrl;
|
||||
});
|
||||
|
||||
continueButton.addEventListener('touchstart', function() {
|
||||
this.classList.add('touched');
|
||||
});
|
||||
}
|
||||
|
||||
if (autoRedirectEnabled && countdown > 0 && countdownElement && targetUrl) {
|
||||
var countdownTimer = setInterval(function() {
|
||||
countdown--;
|
||||
|
||||
if (countdownElement) {
|
||||
var displayText = countdownTemplate.replace('{seconds}', countdown);
|
||||
countdownElement.textContent = displayText;
|
||||
}
|
||||
|
||||
if (progressElement) {
|
||||
var progress = (initialCountdown - countdown) / initialCountdown;
|
||||
progressElement.style.width = (100 - (progress * 100)) + '%';
|
||||
}
|
||||
|
||||
if (countdown <= 0) {
|
||||
clearInterval(countdownTimer);
|
||||
if (continueButton) {
|
||||
continueButton.click();
|
||||
}
|
||||
}
|
||||
}, 1000);
|
||||
}
|
||||
|
||||
// Enhanced keyboard navigation
|
||||
document.addEventListener('keydown', function(e) {
|
||||
if (e.key === 'Escape') {
|
||||
e.preventDefault();
|
||||
goBack();
|
||||
} else if (e.key === 'Enter' && continueButton) {
|
||||
e.preventDefault();
|
||||
continueButton.click();
|
||||
} else if (e.key === 'Backspace' && backButton) {
|
||||
e.preventDefault();
|
||||
goBack();
|
||||
}
|
||||
});
|
||||
|
||||
// Enhanced touch gestures
|
||||
var startY = 0;
|
||||
var currentY = 0;
|
||||
var isDragging = false;
|
||||
|
||||
document.addEventListener('touchstart', function(e) {
|
||||
startY = e.touches[0].clientY;
|
||||
isDragging = true;
|
||||
});
|
||||
|
||||
document.addEventListener('touchmove', function(e) {
|
||||
if (!isDragging) return;
|
||||
currentY = e.touches[0].clientY;
|
||||
var diff = currentY - startY;
|
||||
|
||||
if (Math.abs(diff) > 100) {
|
||||
if (diff > 0 && backButton) {
|
||||
goBack();
|
||||
} else if (diff < 0 && continueButton) {
|
||||
continueButton.click();
|
||||
}
|
||||
isDragging = false;
|
||||
}
|
||||
});
|
||||
|
||||
document.addEventListener('touchend', function() {
|
||||
isDragging = false;
|
||||
});
|
||||
|
||||
// Focus management for accessibility
|
||||
if (continueButton) {
|
||||
continueButton.focus();
|
||||
}
|
||||
})();
|
||||
</script>
|
||||
|
||||
<?php wp_footer(); ?>
|
||||
</body>
|
||||
</html>
|
343
redirect.js
Normal file
343
redirect.js
Normal file
|
@ -0,0 +1,343 @@
|
|||
(function($) {
|
||||
'use strict';
|
||||
|
||||
var wpnavRedirect = {
|
||||
init: function() {
|
||||
this.processExistingLinks();
|
||||
this.setupMutationObserver();
|
||||
this.setupEventHandlers();
|
||||
this.optimizeForMobile();
|
||||
},
|
||||
|
||||
processExistingLinks: function() {
|
||||
var self = this;
|
||||
$('a:not([data-wpnav-processed])').each(function() {
|
||||
self.processLink($(this));
|
||||
});
|
||||
},
|
||||
|
||||
processLink: function($link) {
|
||||
var href = $link.attr('href');
|
||||
|
||||
if (!href || this.shouldSkipLink(href, $link)) {
|
||||
$link.attr('data-wpnav-processed', '1');
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.isExternalLink(href) && !this.isWhitelistedDomain(href)) {
|
||||
this.processExternalLink($link, href);
|
||||
}
|
||||
|
||||
$link.attr('data-wpnav-processed', '1');
|
||||
},
|
||||
|
||||
shouldSkipLink: function(href, $link) {
|
||||
if (href.startsWith('#') || href.startsWith('javascript:') || href.startsWith('mailto:') || href.startsWith('tel:')) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (wpnav_params.exclude_class && $link.hasClass(wpnav_params.exclude_class)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if ($link.attr('data-wpnav-external') || $link.attr('data-wpnav-processed')) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
},
|
||||
|
||||
isExternalLink: function(url) {
|
||||
if (!url.match(/^(https?:)?\/\//i)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
var parser = document.createElement('a');
|
||||
parser.href = url;
|
||||
var linkDomain = parser.hostname.toLowerCase();
|
||||
var siteDomain = wpnav_params.site_domain.toLowerCase();
|
||||
|
||||
return linkDomain !== siteDomain;
|
||||
} catch (e) {
|
||||
return false;
|
||||
}
|
||||
},
|
||||
|
||||
isWhitelistedDomain: function(url) {
|
||||
if (!wpnav_params.whitelist_domains || wpnav_params.whitelist_domains.length === 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
var parser = document.createElement('a');
|
||||
parser.href = url;
|
||||
var hostname = parser.hostname.toLowerCase();
|
||||
|
||||
for (var i = 0; i < wpnav_params.whitelist_domains.length; i++) {
|
||||
var whitelistDomain = wpnav_params.whitelist_domains[i].toLowerCase();
|
||||
if (hostname === whitelistDomain) {
|
||||
return true;
|
||||
}
|
||||
if (whitelistDomain.indexOf('*') !== -1) {
|
||||
var pattern = whitelistDomain.replace(/\*/g, '.*');
|
||||
var regex = new RegExp('^' + pattern + '$', 'i');
|
||||
if (regex.test(hostname)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return false;
|
||||
},
|
||||
|
||||
processExternalLink: function($link, href) {
|
||||
var rel = $link.attr('rel') || '';
|
||||
var relValues = rel ? rel.split(' ') : [];
|
||||
|
||||
['nofollow', 'noopener', 'noreferrer'].forEach(function(value) {
|
||||
if (relValues.indexOf(value) === -1) {
|
||||
relValues.push(value);
|
||||
}
|
||||
});
|
||||
|
||||
$link.attr('rel', relValues.join(' '));
|
||||
|
||||
if (wpnav_params.open_new_tab) {
|
||||
$link.attr('target', '_blank');
|
||||
}
|
||||
|
||||
var redirectUrl = this.getRedirectUrl(href);
|
||||
$link.attr('href', redirectUrl);
|
||||
$link.attr('data-wpnav-external', '1');
|
||||
|
||||
this.addExternalIndicator($link);
|
||||
},
|
||||
|
||||
addExternalIndicator: function($link) {
|
||||
if (!$link.find('.wpnav-external-icon').length) {
|
||||
var iconHtml = '<span class="wpnav-external-icon" style="font-size: 0.8em; margin-left: 3px; opacity: 0.6; vertical-align: super; color: #646970; transition: opacity 0.2s ease;">↗</span>';
|
||||
$link.append(iconHtml);
|
||||
}
|
||||
},
|
||||
|
||||
getRedirectUrl: function(url) {
|
||||
var currentPage = encodeURIComponent(window.location.href);
|
||||
var baseParams = 'wpnav_redirect=1&ref=' + currentPage;
|
||||
|
||||
switch(wpnav_params.redirect_method) {
|
||||
case 'path':
|
||||
var encodedUrl = this.encodeUrl(url);
|
||||
if (wpnav_params.permalink_structure) {
|
||||
return wpnav_params.home_url + '/go/' + encodedUrl;
|
||||
} else {
|
||||
return wpnav_params.home_url + '?' + baseParams + '&url_param=' + encodeURIComponent(encodedUrl);
|
||||
}
|
||||
|
||||
case 'target':
|
||||
return wpnav_params.home_url + '?' + baseParams + '&target=' + encodeURIComponent(url);
|
||||
|
||||
default:
|
||||
var encodedUrl = this.encodeUrl(url);
|
||||
return wpnav_params.home_url + '?' + baseParams + '&url=' + encodeURIComponent(encodedUrl);
|
||||
}
|
||||
},
|
||||
|
||||
encodeUrl: function(url) {
|
||||
if (wpnav_params.redirect_method === 'target') {
|
||||
return url;
|
||||
}
|
||||
|
||||
switch(wpnav_params.url_encoding) {
|
||||
case 'base64':
|
||||
try {
|
||||
return btoa(encodeURIComponent(url)).replace(/[+/=]/g, function(match) {
|
||||
return {'+': '-', '/': '_', '=': ''}[match];
|
||||
});
|
||||
} catch (e) {
|
||||
return encodeURIComponent(url);
|
||||
}
|
||||
|
||||
case 'urlencode':
|
||||
return encodeURIComponent(url);
|
||||
|
||||
case 'none':
|
||||
return url;
|
||||
|
||||
default:
|
||||
try {
|
||||
return btoa(encodeURIComponent(url)).replace(/[+/=]/g, function(match) {
|
||||
return {'+': '-', '/': '_', '=': ''}[match];
|
||||
});
|
||||
} catch (e) {
|
||||
return encodeURIComponent(url);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
setupMutationObserver: function() {
|
||||
if (!window.MutationObserver) {
|
||||
return;
|
||||
}
|
||||
|
||||
var self = this;
|
||||
var observer = new MutationObserver(function(mutations) {
|
||||
mutations.forEach(function(mutation) {
|
||||
if (mutation.addedNodes && mutation.addedNodes.length > 0) {
|
||||
mutation.addedNodes.forEach(function(node) {
|
||||
if (node.nodeType === 1) {
|
||||
if (node.nodeName === 'A' && !node.hasAttribute('data-wpnav-processed')) {
|
||||
self.processLink($(node));
|
||||
}
|
||||
|
||||
var $links = $(node).find('a:not([data-wpnav-processed])');
|
||||
$links.each(function() {
|
||||
self.processLink($(this));
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
observer.observe(document.body, {
|
||||
childList: true,
|
||||
subtree: true
|
||||
});
|
||||
},
|
||||
|
||||
setupEventHandlers: function() {
|
||||
var self = this;
|
||||
|
||||
$(document).ajaxComplete(function() {
|
||||
setTimeout(function() {
|
||||
self.processExistingLinks();
|
||||
}, 100);
|
||||
});
|
||||
|
||||
$(document).on('click', 'a[data-wpnav-external]', function(e) {
|
||||
var $link = $(this);
|
||||
var href = $link.attr('href');
|
||||
|
||||
if (typeof gtag !== 'undefined') {
|
||||
gtag('event', 'click', {
|
||||
event_category: 'external_link',
|
||||
event_label: href,
|
||||
transport_type: 'beacon'
|
||||
});
|
||||
}
|
||||
|
||||
$(document).trigger('wpnav.external_link_click', {
|
||||
link: $link,
|
||||
url: href,
|
||||
target: $link.attr('target')
|
||||
});
|
||||
});
|
||||
|
||||
$(document).keydown(function(e) {
|
||||
if (e.keyCode === 27 && window.parent !== window) {
|
||||
if (typeof window.parent.postMessage === 'function') {
|
||||
window.parent.postMessage('wpnav_close', '*');
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
$(document).on('mouseenter', 'a[data-wpnav-external]', function() {
|
||||
$(this).find('.wpnav-external-icon').css('opacity', '1');
|
||||
});
|
||||
|
||||
$(document).on('mouseleave', 'a[data-wpnav-external]', function() {
|
||||
$(this).find('.wpnav-external-icon').css('opacity', '0.6');
|
||||
});
|
||||
},
|
||||
|
||||
optimizeForMobile: function() {
|
||||
if (!('ontouchstart' in window)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$(document).on('touchstart', 'a[data-wpnav-external]', function(e) {
|
||||
var $this = $(this);
|
||||
$this.addClass('wpnav-touch-active');
|
||||
|
||||
setTimeout(function() {
|
||||
$this.removeClass('wpnav-touch-active');
|
||||
}, 150);
|
||||
});
|
||||
|
||||
var style = document.createElement('style');
|
||||
style.textContent = `
|
||||
a[data-wpnav-external].wpnav-touch-active {
|
||||
opacity: 0.7;
|
||||
transform: scale(0.98);
|
||||
transition: all 0.15s ease;
|
||||
}
|
||||
|
||||
@media (hover: none) and (pointer: coarse) {
|
||||
a[data-wpnav-external]:hover {
|
||||
opacity: 1;
|
||||
transform: none;
|
||||
}
|
||||
|
||||
.wpnav-external-icon {
|
||||
opacity: 0.8 !important;
|
||||
}
|
||||
}
|
||||
`;
|
||||
document.head.appendChild(style);
|
||||
},
|
||||
|
||||
refresh: function() {
|
||||
this.processExistingLinks();
|
||||
}
|
||||
};
|
||||
|
||||
$(document).ready(function() {
|
||||
wpnavRedirect.init();
|
||||
});
|
||||
|
||||
window.wpnavRedirect = wpnavRedirect;
|
||||
|
||||
if (typeof addComment !== 'undefined') {
|
||||
var originalAddComment = addComment.moveForm;
|
||||
addComment.moveForm = function() {
|
||||
var result = originalAddComment.apply(this, arguments);
|
||||
setTimeout(function() {
|
||||
wpnavRedirect.processExistingLinks();
|
||||
}, 100);
|
||||
return result;
|
||||
};
|
||||
}
|
||||
|
||||
if (typeof wp !== 'undefined' && wp.data) {
|
||||
wp.data.subscribe(function() {
|
||||
setTimeout(function() {
|
||||
wpnavRedirect.processExistingLinks();
|
||||
}, 500);
|
||||
});
|
||||
}
|
||||
|
||||
$(document).on('wpcf7mailsent wpcf7invalid wpcf7spam wpcf7mailfailed', function() {
|
||||
setTimeout(function() {
|
||||
wpnavRedirect.processExistingLinks();
|
||||
}, 100);
|
||||
});
|
||||
|
||||
$(document.body).on('updated_wc_div updated_cart_totals', function() {
|
||||
setTimeout(function() {
|
||||
wpnavRedirect.processExistingLinks();
|
||||
}, 100);
|
||||
});
|
||||
|
||||
if ('serviceWorker' in navigator) {
|
||||
window.addEventListener('load', function() {
|
||||
setTimeout(function() {
|
||||
wpnavRedirect.processExistingLinks();
|
||||
}, 1000);
|
||||
});
|
||||
}
|
||||
|
||||
})(jQuery);
|
533
wpnav-links.php
Normal file
533
wpnav-links.php
Normal file
|
@ -0,0 +1,533 @@
|
|||
<?php
|
||||
/**
|
||||
* Plugin Name: WPNav Links
|
||||
* Plugin URI: https://wpnav.com/plugins/wpnav-links
|
||||
* Description: A simplified WordPress external link redirect tool with customizable redirect pages and basic security features.
|
||||
* Version: 1.2.0
|
||||
* Author: WPNav
|
||||
* Author URI: https://wpnaw.com
|
||||
* Text Domain: wpnav-links
|
||||
* Domain Path: /languages
|
||||
* License: GPL v2 or later
|
||||
* License URI: https://www.gnu.org/licenses/gpl-2.0.html
|
||||
* Requires at least: 5.0
|
||||
* Tested up to: 6.4
|
||||
* Requires PHP: 7.4
|
||||
*/
|
||||
|
||||
if (!defined('ABSPATH')) {
|
||||
exit;
|
||||
}
|
||||
|
||||
define('WPNAV_LINKS_VERSION', '1.2.0');
|
||||
define('WPNAV_LINKS_PLUGIN_DIR', plugin_dir_path(__FILE__));
|
||||
define('WPNAV_LINKS_PLUGIN_URL', plugin_dir_url(__FILE__));
|
||||
define('WPNAV_LINKS_DB_VERSION', '1.2.0');
|
||||
define('WPNAV_LINKS_TABLE', 'nav_link_redirects');
|
||||
|
||||
require_once WPNAV_LINKS_PLUGIN_DIR . 'class-wpnav-links.php';
|
||||
require_once WPNAV_LINKS_PLUGIN_DIR . 'class-admin.php';
|
||||
require_once WPNAV_LINKS_PLUGIN_DIR . 'class-public.php';
|
||||
|
||||
function wpnav_activate_plugin() {
|
||||
$plugin = new WPNAV_Links();
|
||||
$plugin->create_tables();
|
||||
|
||||
$default_options = array(
|
||||
'enabled' => true,
|
||||
'auto_redirect_enabled' => true,
|
||||
'redirect_delay' => 5,
|
||||
'open_in_new_tab' => true,
|
||||
'url_format' => 'query',
|
||||
'auto_whitelist' => array(
|
||||
'same_root' => true,
|
||||
'search_engines' => true
|
||||
),
|
||||
'whitelist_domains' => "google.com\nbaidu.com\nbing.com\nyoutube.com\nfacebook.com\ntwitter.com",
|
||||
'intercept_content' => true,
|
||||
'intercept_comments' => true,
|
||||
'intercept_widgets' => true,
|
||||
'exclude_css_class' => 'no-redirect',
|
||||
'template' => 'default',
|
||||
'color_scheme' => 'blue',
|
||||
'page_title' => 'External Link Warning',
|
||||
'page_subtitle' => '',
|
||||
'url_label' => 'You are about to visit:',
|
||||
'warning_text' => 'You are about to leave this site and visit an external website. We are not responsible for the content of external websites.',
|
||||
'show_warning_message' => true,
|
||||
'show_logo' => false,
|
||||
'show_url_full' => false,
|
||||
'show_security_info' => true,
|
||||
'show_security_tips' => false,
|
||||
'show_back_button' => true,
|
||||
'custom_css' => '',
|
||||
'button_text_continue' => 'Continue',
|
||||
'button_text_back' => 'Back',
|
||||
'button_style' => 'rounded',
|
||||
'countdown_text' => 'Auto redirect in {seconds} seconds',
|
||||
'show_progress_bar' => false,
|
||||
'admin_exempt' => true,
|
||||
'cookie_duration' => 30,
|
||||
'stats_retention' => 90
|
||||
);
|
||||
|
||||
$existing_options = get_option('wpnav_links_options', array());
|
||||
$merged_options = array_merge($default_options, $existing_options);
|
||||
update_option('wpnav_links_options', $merged_options);
|
||||
|
||||
if (!wp_next_scheduled('wpnav_cleanup_old_data')) {
|
||||
wp_schedule_event(time(), 'daily', 'wpnav_cleanup_old_data');
|
||||
}
|
||||
|
||||
flush_rewrite_rules();
|
||||
add_option('wpnav_links_activation_redirect', true);
|
||||
}
|
||||
register_activation_hook(__FILE__, 'wpnav_activate_plugin');
|
||||
|
||||
function wpnav_deactivate_plugin() {
|
||||
wp_clear_scheduled_hook('wpnav_cleanup_old_data');
|
||||
flush_rewrite_rules();
|
||||
}
|
||||
register_deactivation_hook(__FILE__, 'wpnav_deactivate_plugin');
|
||||
|
||||
function wpnav_uninstall_plugin() {
|
||||
delete_option('wpnav_links_options');
|
||||
delete_option('wpnav_links_db_version');
|
||||
delete_option('wpnav_links_activation_redirect');
|
||||
|
||||
global $wpdb;
|
||||
$table_name = $wpdb->prefix . WPNAV_LINKS_TABLE;
|
||||
$wpdb->query("DROP TABLE IF EXISTS $table_name");
|
||||
|
||||
wp_clear_scheduled_hook('wpnav_cleanup_old_data');
|
||||
}
|
||||
register_uninstall_hook(__FILE__, 'wpnav_uninstall_plugin');
|
||||
|
||||
add_action('wpnav_cleanup_old_data', 'wpnav_clean_old_statistics');
|
||||
function wpnav_clean_old_statistics() {
|
||||
$options = get_option('wpnav_links_options');
|
||||
$retention_days = isset($options['stats_retention']) ? intval($options['stats_retention']) : 90;
|
||||
|
||||
$plugin = new WPNAV_Links();
|
||||
$deleted_count = $plugin->cleanup_old_data($retention_days);
|
||||
|
||||
if (defined('WP_DEBUG') && WP_DEBUG) {
|
||||
error_log("WPNav Links: Cleaned up {$deleted_count} old records");
|
||||
}
|
||||
}
|
||||
|
||||
function wpnav_run_plugin() {
|
||||
$plugin = new WPNAV_Links();
|
||||
$plugin->init();
|
||||
}
|
||||
|
||||
add_action('plugins_loaded', 'wpnav_run_plugin');
|
||||
|
||||
add_action('admin_init', 'wpnav_activation_redirect');
|
||||
function wpnav_activation_redirect() {
|
||||
if (get_option('wpnav_links_activation_redirect', false)) {
|
||||
delete_option('wpnav_links_activation_redirect');
|
||||
if (!isset($_GET['activate-multi'])) {
|
||||
wp_redirect(admin_url('tools.php?page=wpnav-links&tab=basic_settings'));
|
||||
exit;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
add_action('wp_dashboard_setup', 'wpnav_add_dashboard_widget');
|
||||
function wpnav_add_dashboard_widget() {
|
||||
if (current_user_can('manage_options')) {
|
||||
wp_add_dashboard_widget(
|
||||
'wpnav_links_stats',
|
||||
__('External Links Monitor', 'wpnav-links'),
|
||||
'wpnav_dashboard_widget_content',
|
||||
null,
|
||||
null,
|
||||
'normal',
|
||||
'high'
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
function wpnav_dashboard_widget_content() {
|
||||
$plugin = new WPNAV_Links();
|
||||
$options = get_option('wpnav_links_options', array());
|
||||
|
||||
$total_redirects = $plugin->get_total_count();
|
||||
$today_redirects = $plugin->get_total_count(date('Y-m-d'), date('Y-m-d'));
|
||||
$recent_redirects = $plugin->get_total_count(date('Y-m-d', strtotime('-7 days')), date('Y-m-d'));
|
||||
$last_week_redirects = $plugin->get_total_count(date('Y-m-d', strtotime('-14 days')), date('Y-m-d', strtotime('-7 days')));
|
||||
$top_urls = $plugin->get_top_urls(3);
|
||||
|
||||
$trend = 0;
|
||||
if ($last_week_redirects > 0) {
|
||||
$trend = (($recent_redirects - $last_week_redirects) / $last_week_redirects) * 100;
|
||||
} elseif ($recent_redirects > 0) {
|
||||
$trend = 100;
|
||||
}
|
||||
|
||||
$plugin_enabled = !empty($options['enabled']);
|
||||
$auto_redirect = !empty($options['auto_redirect_enabled']);
|
||||
$whitelist_count = 0;
|
||||
if (!empty($options['whitelist_domains'])) {
|
||||
$domains = explode("\n", $options['whitelist_domains']);
|
||||
$whitelist_count = count(array_filter(array_map('trim', $domains)));
|
||||
}
|
||||
|
||||
echo '<div class="wpnav-dashboard-widget">';
|
||||
|
||||
echo '<div class="wpnav-status-bar">';
|
||||
echo '<span class="wpnav-status-item ' . ($plugin_enabled ? 'active' : 'inactive') . '">';
|
||||
echo '<span class="wpnav-status-dot"></span>';
|
||||
echo ($plugin_enabled ? esc_html__('Active', 'wpnav-links') : esc_html__('Inactive', 'wpnav-links'));
|
||||
echo '</span>';
|
||||
if ($plugin_enabled && !$auto_redirect) {
|
||||
echo '<span class="wpnav-warning-badge">' . esc_html__('Manual Only', 'wpnav-links') . '</span>';
|
||||
}
|
||||
echo '</div>';
|
||||
|
||||
echo '<div class="wpnav-stats-grid">';
|
||||
|
||||
echo '<div class="wpnav-stat-card primary">';
|
||||
echo '<div class="wpnav-stat-number">' . number_format($total_redirects) . '</div>';
|
||||
echo '<div class="wpnav-stat-label">' . esc_html__('Total Redirects', 'wpnav-links') . '</div>';
|
||||
echo '</div>';
|
||||
|
||||
echo '<div class="wpnav-stat-card">';
|
||||
echo '<div class="wpnav-stat-number">' . number_format($today_redirects) . '</div>';
|
||||
echo '<div class="wpnav-stat-label">' . esc_html__('Today', 'wpnav-links') . '</div>';
|
||||
echo '</div>';
|
||||
|
||||
echo '<div class="wpnav-stat-card">';
|
||||
echo '<div class="wpnav-stat-number">' . number_format($recent_redirects);
|
||||
if ($trend != 0) {
|
||||
$trend_class = $trend > 0 ? 'up' : 'down';
|
||||
$trend_icon = $trend > 0 ? '↗' : '↘';
|
||||
echo '<span class="wpnav-trend ' . $trend_class . '">' . $trend_icon . abs(round($trend)) . '%</span>';
|
||||
}
|
||||
echo '</div>';
|
||||
echo '<div class="wpnav-stat-label">' . esc_html__('Last 7 Days', 'wpnav-links') . '</div>';
|
||||
echo '</div>';
|
||||
|
||||
echo '<div class="wpnav-stat-card">';
|
||||
echo '<div class="wpnav-stat-number">' . number_format($whitelist_count) . '</div>';
|
||||
echo '<div class="wpnav-stat-label">' . esc_html__('Whitelisted', 'wpnav-links') . '</div>';
|
||||
echo '</div>';
|
||||
|
||||
echo '</div>';
|
||||
|
||||
if (!empty($top_urls)) {
|
||||
echo '<div class="wpnav-top-domains">';
|
||||
echo '<h4>' . esc_html__('Top Domains This Week', 'wpnav-links') . '</h4>';
|
||||
echo '<ul class="wpnav-domain-list">';
|
||||
foreach ($top_urls as $index => $url) {
|
||||
$domain = parse_url($url->target_url, PHP_URL_HOST);
|
||||
$is_https = strpos($url->target_url, 'https://') === 0;
|
||||
echo '<li class="wpnav-domain-item">';
|
||||
echo '<span class="wpnav-rank">' . ($index + 1) . '</span>';
|
||||
echo '<span class="wpnav-domain">' . esc_html($domain) . '</span>';
|
||||
echo '<span class="wpnav-https-indicator ' . ($is_https ? 'secure' : 'insecure') . '"></span>';
|
||||
echo '<span class="wpnav-count">' . number_format($url->count) . '</span>';
|
||||
echo '</li>';
|
||||
}
|
||||
echo '</ul>';
|
||||
echo '</div>';
|
||||
} else {
|
||||
echo '<div class="wpnav-no-data">';
|
||||
echo '<p>' . esc_html__('No external link activity yet.', 'wpnav-links') . '</p>';
|
||||
if (!$plugin_enabled) {
|
||||
echo '<p><small>' . esc_html__('Enable the plugin to start tracking external links.', 'wpnav-links') . '</small></p>';
|
||||
}
|
||||
echo '</div>';
|
||||
}
|
||||
|
||||
echo '<div class="wpnav-quick-actions">';
|
||||
echo '<div class="wpnav-action-buttons">';
|
||||
|
||||
if (!$plugin_enabled) {
|
||||
echo '<a href="' . admin_url('tools.php?page=wpnav-links&tab=basic_settings') . '" class="button button-primary button-small">' . esc_html__('Enable Plugin', 'wpnav-links') . '</a>';
|
||||
} else {
|
||||
echo '<a href="' . admin_url('tools.php?page=wpnav-links&tab=logs_statistics') . '" class="button button-small">' . esc_html__('View Details', 'wpnav-links') . '</a>';
|
||||
}
|
||||
|
||||
echo '<a href="' . admin_url('tools.php?page=wpnav-links&tab=whitelist') . '" class="button button-small">' . esc_html__('Manage Whitelist', 'wpnav-links') . '</a>';
|
||||
echo '</div>';
|
||||
echo '</div>';
|
||||
|
||||
echo '</div>';
|
||||
|
||||
echo '<style>
|
||||
.wpnav-dashboard-widget {
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.wpnav-status-bar {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
margin-bottom: 15px;
|
||||
padding-bottom: 10px;
|
||||
border-bottom: 1px solid #ddd;
|
||||
}
|
||||
|
||||
.wpnav-status-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
font-weight: 600;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.wpnav-status-item.active {
|
||||
color: #00a32a;
|
||||
}
|
||||
|
||||
.wpnav-status-item.inactive {
|
||||
color: #d63638;
|
||||
}
|
||||
|
||||
.wpnav-status-dot {
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
border-radius: 50%;
|
||||
background: currentColor;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.wpnav-warning-badge {
|
||||
background: #f0b849;
|
||||
color: white;
|
||||
padding: 2px 6px;
|
||||
border-radius: 10px;
|
||||
font-size: 10px;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.wpnav-stats-grid {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
gap: 10px;
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
.wpnav-stat-card {
|
||||
background: #f8f9fa;
|
||||
border: 1px solid #e2e4e7;
|
||||
border-radius: 4px;
|
||||
padding: 12px 10px;
|
||||
text-align: center;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.wpnav-stat-card.primary {
|
||||
background: #e7f3ff;
|
||||
border-color: #72aee6;
|
||||
}
|
||||
|
||||
.wpnav-stat-number {
|
||||
font-size: 18px;
|
||||
font-weight: 700;
|
||||
color: #2c3338;
|
||||
line-height: 1.2;
|
||||
margin-bottom: 4px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 4px;
|
||||
}
|
||||
|
||||
.wpnav-stat-label {
|
||||
font-size: 11px;
|
||||
color: #646970;
|
||||
font-weight: 500;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.5px;
|
||||
}
|
||||
|
||||
.wpnav-trend {
|
||||
font-size: 10px;
|
||||
padding: 1px 3px;
|
||||
border-radius: 2px;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.wpnav-trend.up {
|
||||
background: #d1e7dd;
|
||||
color: #0a3622;
|
||||
}
|
||||
|
||||
.wpnav-trend.down {
|
||||
background: #f8d7da;
|
||||
color: #58151c;
|
||||
}
|
||||
|
||||
.wpnav-top-domains h4 {
|
||||
margin: 0 0 10px;
|
||||
font-size: 13px;
|
||||
color: #2c3338;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.wpnav-domain-list {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
list-style: none;
|
||||
}
|
||||
|
||||
.wpnav-domain-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 6px 0;
|
||||
border-bottom: 1px solid #f0f0f1;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.wpnav-domain-item:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.wpnav-rank {
|
||||
background: #2271b1;
|
||||
color: white;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
border-radius: 50%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 10px;
|
||||
font-weight: 700;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.wpnav-domain {
|
||||
flex: 1;
|
||||
font-size: 12px;
|
||||
color: #2c3338;
|
||||
font-weight: 500;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.wpnav-https-indicator {
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
border-radius: 50%;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.wpnav-https-indicator.secure {
|
||||
background: #00a32a;
|
||||
}
|
||||
|
||||
.wpnav-https-indicator.insecure {
|
||||
background: #d63638;
|
||||
}
|
||||
|
||||
.wpnav-count {
|
||||
font-size: 11px;
|
||||
color: #646970;
|
||||
font-weight: 600;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.wpnav-no-data {
|
||||
text-align: center;
|
||||
padding: 20px 0;
|
||||
color: #646970;
|
||||
}
|
||||
|
||||
.wpnav-no-data p {
|
||||
margin: 5px 0;
|
||||
}
|
||||
|
||||
.wpnav-quick-actions {
|
||||
margin-top: 15px;
|
||||
padding-top: 10px;
|
||||
border-top: 1px solid #ddd;
|
||||
}
|
||||
|
||||
.wpnav-action-buttons {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.wpnav-action-buttons .button {
|
||||
font-size: 11px;
|
||||
padding: 4px 8px;
|
||||
height: auto;
|
||||
line-height: 1.4;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
@media screen and (max-width: 782px) {
|
||||
.wpnav-stats-grid {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
|
||||
.wpnav-action-buttons {
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.wpnav-action-buttons .button {
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
</style>';
|
||||
}
|
||||
|
||||
add_filter('plugin_row_meta', 'wpnav_plugin_row_meta', 10, 2);
|
||||
function wpnav_plugin_row_meta($links, $file) {
|
||||
if (plugin_basename(__FILE__) === $file) {
|
||||
$row_meta = array(
|
||||
'settings' => '<a href="' . admin_url('tools.php?page=wpnav-links') . '">' . esc_html__('Settings', 'wpnav-links') . '</a>',
|
||||
'support' => '<a href="https://example.com/support" target="_blank">' . esc_html__('Support', 'wpnav-links') . '</a>',
|
||||
'docs' => '<a href="https://example.com/docs" target="_blank">' . esc_html__('Documentation', 'wpnav-links') . '</a>',
|
||||
);
|
||||
return array_merge($links, $row_meta);
|
||||
}
|
||||
return $links;
|
||||
}
|
||||
|
||||
add_action('init', 'wpnav_check_database_version');
|
||||
function wpnav_check_database_version() {
|
||||
$installed_version = get_option('wpnav_links_db_version');
|
||||
|
||||
if ($installed_version !== WPNAV_LINKS_DB_VERSION) {
|
||||
$plugin = new WPNAV_Links();
|
||||
$plugin->create_tables();
|
||||
update_option('wpnav_links_db_version', WPNAV_LINKS_DB_VERSION);
|
||||
}
|
||||
}
|
||||
|
||||
add_action('wp_enqueue_scripts', 'wpnav_enqueue_frontend_styles');
|
||||
function wpnav_enqueue_frontend_styles() {
|
||||
$options = get_option('wpnav_links_options');
|
||||
if (!isset($options['enabled']) || !$options['enabled']) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!is_admin()) {
|
||||
wp_enqueue_style(
|
||||
'wpnav-external-indicator',
|
||||
WPNAV_LINKS_PLUGIN_URL . 'external-indicator.css',
|
||||
array(),
|
||||
WPNAV_LINKS_VERSION
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if (!wp_next_scheduled('wpnav_daily_maintenance')) {
|
||||
wp_schedule_event(time(), 'daily', 'wpnav_daily_maintenance');
|
||||
}
|
||||
|
||||
add_action('wpnav_daily_maintenance', 'wpnav_daily_maintenance_tasks');
|
||||
function wpnav_daily_maintenance_tasks() {
|
||||
wp_cache_flush();
|
||||
|
||||
global $wpdb;
|
||||
$table_name = $wpdb->prefix . WPNAV_LINKS_TABLE;
|
||||
$wpdb->query("OPTIMIZE TABLE $table_name");
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue