Changeset 3325285
- Timestamp:
- 07/09/2025 11:19:30 PM (9 months ago)
- Location:
- smart-download-redirector
- Files:
-
- 2 deleted
- 13 edited
-
tags/1.0.0/admin/settings-page.php (modified) (2 diffs)
-
tags/1.0.0/assets/countdown.js (modified) (1 diff)
-
tags/1.0.0/assets/js (deleted)
-
tags/1.0.0/assets/styles.css (modified) (11 diffs)
-
tags/1.0.0/includes/redirect-functions.php (modified) (8 diffs)
-
tags/1.0.0/smart-download-redirector.php (modified) (5 diffs)
-
tags/1.0.0/templates/download-page.php (modified) (3 diffs)
-
trunk/admin/settings-page.php (modified) (2 diffs)
-
trunk/assets/countdown.js (modified) (1 diff)
-
trunk/assets/js (deleted)
-
trunk/assets/styles.css (modified) (11 diffs)
-
trunk/includes/redirect-functions.php (modified) (8 diffs)
-
trunk/readme.txt (modified) (1 diff)
-
trunk/smart-download-redirector.php (modified) (5 diffs)
-
trunk/templates/download-page.php (modified) (3 diffs)
Legend:
- Unmodified
- Added
- Removed
-
smart-download-redirector/tags/1.0.0/admin/settings-page.php
r3325102 r3325285 1288 1288 } 1289 1289 1290 // Check if settings were updated (WordPress core parameter, safe to check) 1291 // Additional security: Only process if user has proper capabilities 1292 if (current_user_can('manage_options') && 1293 isset($_GET['settings-updated']) && 1294 $_GET['settings-updated'] === 'true' && 1295 !get_settings_errors('smartdr_messages') // Prevent duplicate messages 1296 ) { 1290 // Check if settings were updated 1291 // Use WordPress core function to check if options were updated - this is security-safe 1292 $settings_updated = false; 1293 if (current_user_can('manage_options')) { 1294 // Check if any of our options were recently updated 1295 $check_options = [ 1296 'smartdr_target_domains', 1297 'smartdr_countdown_time', 1298 'smartdr_redirect_delay', 1299 'smartdr_styles', 1300 'smartdr_layout_style' 1301 ]; 1302 1303 foreach ($check_options as $option) { 1304 if (get_transient('smartdr_option_updated_' . $option)) { 1305 $settings_updated = true; 1306 delete_transient('smartdr_option_updated_' . $option); 1307 break; 1308 } 1309 } 1310 1311 // Fallback: Check WordPress core parameter (already validated by WordPress) 1312 if (!$settings_updated && 1313 filter_input(INPUT_GET, 'settings-updated', FILTER_SANITIZE_STRING) === 'true' && 1314 !get_settings_errors('smartdr_messages') 1315 ) { 1316 $settings_updated = true; 1317 } 1318 } 1319 1320 if ($settings_updated) { 1297 1321 add_settings_error( 1298 1322 'smartdr_messages', … … 1356 1380 } 1357 1381 1358 /** 1359 * Get WPML translated string 1360 */ 1361 function smartdr_get_wpml_string($name, $original_value) { 1362 if (function_exists('wpml_translate_single_string')) { 1363 return wpml_translate_single_string('smart-download-redirector', $name, $original_value); 1364 } 1365 return $original_value; 1366 } 1382 1383 1384 /** 1385 * Set transient when options are updated for secure update detection 1386 */ 1387 function smartdr_option_updated($option_name) { 1388 if (strpos($option_name, 'smartdr_') === 0) { 1389 set_transient('smartdr_option_updated_' . $option_name, true, 60); // 1 minute 1390 } 1391 } 1392 1393 // Hook to track option updates 1394 add_action('updated_option', 'smartdr_option_updated'); 1367 1395 1368 1396 // Hook to register WPML strings when options are updated -
smart-download-redirector/tags/1.0.0/assets/countdown.js
r3325102 r3325285 1 1 /** 2 * Enhanced countdown timer with progress ring and step indicators2 * Simple and reliable countdown timer 3 3 */ 4 jQuery(function($) {4 (function($) { 5 5 'use strict'; 6 6 7 // Debug helper 8 var debug = function(msg, data) { 9 if (window.console && console.log) { 10 console.log('[SDR Debug]:', msg, data || ''); 11 } 12 }; 13 14 debug('Script starting'); 15 16 // Validate data 17 if (typeof smartdrData === 'undefined') { 18 debug('ERROR: smartdrData missing'); 19 return; 7 // Function to run when DOM is ready 8 function initCountdown() { 9 console.log('[SmartDR] Countdown script starting...'); 10 11 // Check if jQuery is available 12 if (typeof $ === 'undefined') { 13 console.error('[SmartDR] jQuery not available'); 14 return; 15 } 16 17 // Check if smartdrData is available 18 if (typeof smartdrData === 'undefined') { 19 console.error('[SmartDR] smartdrData not available'); 20 return; 21 } 22 23 console.log('[SmartDR] smartdrData:', smartdrData); 24 25 // Check if we have a valid URL to countdown for 26 if (!smartdrData.hasValidUrl) { 27 console.log('[SmartDR] No valid URL - countdown not started'); 28 return; 29 } 30 31 // Get countdown time 32 var countdownTime = parseInt(smartdrData.countdownTime) || 10; 33 var redirectUrl = smartdrData.redirectUrl || '#'; 34 var redirectDelay = parseInt(smartdrData.redirectDelay) || 3000; 35 36 console.log('[SmartDR] Configuration:', { 37 countdownTime: countdownTime, 38 redirectUrl: redirectUrl, 39 redirectDelay: redirectDelay 40 }); 41 42 // Find countdown elements 43 var $counterNumber = $('#smartdr-countdown-number'); 44 var $counterText = $('#smartdr-countdown-text'); 45 var $progressBar = $('#smartdr-progress-bar'); 46 var $manualRedirect = $('.smartdr-manual-redirect'); 47 var $progressRing = $('.smartdr-progress-ring-circle'); 48 49 console.log('[SmartDR] Elements found:', { 50 counterNumber: $counterNumber.length, 51 counterText: $counterText.length, 52 progressBar: $progressBar.length, 53 manualRedirect: $manualRedirect.length, 54 progressRing: $progressRing.length 55 }); 56 57 // Determine which element to use 58 var $activeElement = null; 59 var layoutType = 'unknown'; 60 61 if ($counterNumber.length > 0) { 62 $activeElement = $counterNumber; 63 layoutType = 'circular'; 64 console.log('[SmartDR] Using circular layout'); 65 } else if ($counterText.length > 0) { 66 $activeElement = $counterText; 67 layoutType = 'progressbar'; 68 console.log('[SmartDR] Using progressbar layout'); 69 } else { 70 console.error('[SmartDR] No countdown element found!'); 71 return; 72 } 73 74 // Initialize progress ring if available 75 var circumference = 0; 76 if ($progressRing.length > 0 && layoutType === 'circular') { 77 try { 78 var radius = $progressRing[0].r.baseVal.value; 79 circumference = radius * 2 * Math.PI; 80 $progressRing[0].style.strokeDasharray = circumference; 81 $progressRing[0].style.strokeDashoffset = circumference; 82 console.log('[SmartDR] Progress ring initialized, circumference:', circumference); 83 } catch (e) { 84 console.warn('[SmartDR] Progress ring initialization failed:', e); 85 } 86 } 87 88 // Start countdown 89 var timeLeft = countdownTime; 90 var initialTime = countdownTime; 91 var isComplete = false; 92 93 console.log('[SmartDR] Starting countdown from', timeLeft); 94 95 function updateCountdown() { 96 if (isComplete) return; 97 98 console.log('[SmartDR] Countdown tick:', timeLeft); 99 100 // Update display based on layout type 101 if (layoutType === 'circular') { 102 $activeElement.text(timeLeft); 103 104 // Update progress ring 105 if ($progressRing.length > 0 && circumference > 0) { 106 var progress = (initialTime - timeLeft) / initialTime; 107 var offset = circumference - (progress * circumference); 108 $progressRing[0].style.strokeDashoffset = offset; 109 } 110 } else if (layoutType === 'progressbar') { 111 var secondsText = smartdrData.secondsText || 'seconds remaining'; 112 $activeElement.text(timeLeft + ' ' + secondsText); 113 114 // Update progress bar 115 if ($progressBar.length > 0) { 116 var progress = (initialTime - timeLeft) / initialTime * 100; 117 $progressBar.css('width', progress + '%'); 118 } 119 } 120 121 // Check if countdown is complete 122 if (timeLeft <= 0) { 123 completeCountdown(); 124 } else { 125 timeLeft--; 126 } 127 } 128 129 function completeCountdown() { 130 if (isComplete) return; 131 isComplete = true; 132 133 console.log('[SmartDR] Countdown complete!'); 134 135 // Clear interval 136 clearInterval(countdownInterval); 137 138 // Update display for completion 139 if (layoutType === 'circular') { 140 $activeElement.text('→'); 141 142 // Complete progress ring 143 if ($progressRing.length > 0) { 144 $progressRing[0].style.strokeDashoffset = 0; 145 } 146 } else if (layoutType === 'progressbar') { 147 var downloadReadyText = smartdrData.downloadReadyText || 'Download ready!'; 148 $activeElement.text(downloadReadyText); 149 150 // Complete progress bar 151 if ($progressBar.length > 0) { 152 $progressBar.css('width', '100%'); 153 } 154 } 155 156 // Show manual redirect link 157 if ($manualRedirect.length > 0) { 158 $manualRedirect.show(); 159 } 160 161 // Start redirect timer 162 console.log('[SmartDR] Starting redirect timer, delay:', redirectDelay + 'ms'); 163 setTimeout(function() { 164 if (redirectUrl && redirectUrl !== '#') { 165 console.log('[SmartDR] Redirecting to:', redirectUrl); 166 window.location.href = redirectUrl; 167 } else { 168 console.log('[SmartDR] No redirect URL, staying on page'); 169 } 170 }, redirectDelay); 171 } 172 173 // Start the countdown 174 updateCountdown(); // Initial update 175 var countdownInterval = setInterval(updateCountdown, 1000); 176 177 // Manual redirect handler 178 $manualRedirect.find('a').on('click', function(e) { 179 if (redirectUrl && redirectUrl !== '#') { 180 console.log('[SmartDR] Manual redirect clicked'); 181 clearInterval(countdownInterval); 182 // Let the link work normally 183 } else { 184 e.preventDefault(); 185 console.log('[SmartDR] Manual redirect clicked but no valid URL'); 186 } 187 }); 188 189 console.log('[SmartDR] Countdown initialized successfully'); 20 190 } 21 22 // Get debug info safely 23 var debugInfo = { 24 countdownTime: smartdrData.countdownTime || 5, 25 redirectDelay: smartdrData.redirectDelay || 3000, 26 rawDelay: (smartdrData.debug && smartdrData.debug.rawDelay) || 3, 27 msDelay: (smartdrData.debug && smartdrData.debug.msDelay) || 3000, 28 defaultDelay: (smartdrData.debug && smartdrData.debug.defaultDelay) || 3000 29 }; 30 31 // Log all debug info 32 debug('Configuration:', debugInfo); 33 34 // Get and validate countdown time 35 var timeLeft = parseInt(smartdrData.countdownTime); 36 if (isNaN(timeLeft) || timeLeft <= 0) { 37 debug('ERROR: Invalid countdown time:', timeLeft); 38 return; 191 192 // Initialize when DOM is ready 193 $(document).ready(function() { 194 initCountdown(); 195 }); 196 197 // Fallback for cases where jQuery might not be loaded immediately 198 if (document.readyState === 'loading') { 199 document.addEventListener('DOMContentLoaded', function() { 200 setTimeout(initCountdown, 100); 201 }); 202 } else { 203 setTimeout(initCountdown, 100); 39 204 } 40 41 // Get and validate redirect delay 42 var redirectDelay = parseInt(smartdrData.redirectDelay); 43 debug('Parsed redirect delay:', redirectDelay); 44 45 if (isNaN(redirectDelay) || redirectDelay < 1000) { 46 debug('WARNING: Invalid redirect delay, using default'); 47 redirectDelay = debugInfo.defaultDelay; 48 } 49 50 debug('Final redirect delay:', redirectDelay + 'ms'); 51 52 // Get elements 53 var $counter = $('#smartdr-countdown-number'); 54 var $ring = $('.smartdr-progress-ring-circle'); 55 var $steps = $('.smartdr-step'); 56 var $targetUrl = $('.smartdr-target-url'); 57 var $manualRedirect = $('.smartdr-manual-redirect'); 58 59 if (!$counter.length) { 60 debug('ERROR: Counter element missing'); 61 return; 62 } 63 64 // Initialize progress ring 65 var circumference = 0; 66 if ($ring.length) { 67 var radius = $ring[0].r.baseVal.value; 68 circumference = radius * 2 * Math.PI; 69 $ring[0].style.strokeDasharray = circumference; 70 $ring[0].style.strokeDashoffset = circumference; 71 } 72 73 // Store initial time for progress calculations 74 var initialTime = timeLeft; 75 var countdownInterval; 76 var currentStep = 1; 77 78 // Update display function 79 function updateDisplay() { 80 // Update counter text 81 $counter.text(timeLeft); 82 83 // Update progress ring 84 if ($ring.length) { 85 var progress = (initialTime - timeLeft) / initialTime; 86 var offset = circumference - (progress * circumference); 87 $ring[0].style.strokeDashoffset = offset; 88 } 89 90 // Calculate current step 91 var newStep; 92 if (timeLeft > (initialTime * 2/3)) { 93 newStep = 1; 94 } else if (timeLeft > (initialTime * 1/3)) { 95 newStep = 2; 96 } else { 97 newStep = 3; 98 } 99 100 // Only update if step changed 101 if (newStep !== currentStep) { 102 currentStep = newStep; 103 // Update step indicators 104 $steps.removeClass('active'); 105 $steps.slice(0, currentStep).addClass('active'); 106 107 // Show URL at appropriate step if enabled 108 if (typeof smartdrData.showDestination !== 'undefined' && smartdrData.showDestination) { 109 $targetUrl.toggle(currentStep >= smartdrData.destinationStep); 110 } 111 112 // Show manual link at appropriate step if enabled 113 if (typeof smartdrData.showManualLink !== 'undefined' && smartdrData.showManualLink) { 114 $manualRedirect.toggle(currentStep >= smartdrData.manualLinkStep); 115 } 116 } 117 } 118 119 // Handle countdown completion 120 function complete() { 121 debug('Countdown complete'); 122 123 // Clear interval 124 clearInterval(countdownInterval); 125 126 // Update display 127 if ($ring.length) { 128 $ring[0].style.strokeDashoffset = 0; 129 } 130 $steps.removeClass('active').eq(2).addClass('active'); 131 132 // Show both URL and manual link at completion if enabled 133 if (typeof smartdrData.showDestination !== 'undefined' && smartdrData.showDestination) { 134 $targetUrl.fadeIn(400); 135 } 136 if (typeof smartdrData.showManualLink !== 'undefined' && smartdrData.showManualLink) { 137 $manualRedirect.fadeIn(400); 138 } 139 140 // Fade in the arrow with animation 141 $counter.fadeOut(200, function() { 142 $(this).text('→').fadeIn(200); 143 }); 144 145 debug('Starting redirect timer:', { 146 delay: redirectDelay, 147 inSeconds: redirectDelay/1000, 148 url: smartdrData.redirectUrl 149 }); 150 151 // Start redirect timer 152 setTimeout(function() { 153 debug('Redirect timer complete, redirecting now'); 154 window.location.href = smartdrData.redirectUrl; 155 }, redirectDelay); 156 } 157 158 // Start countdown 159 debug('Starting countdown'); 160 updateDisplay(); // Initial display update 161 162 countdownInterval = setInterval(function() { 163 timeLeft--; 164 debug('Tick:', timeLeft); 165 166 if (timeLeft <= 0) { 167 complete(); 168 } else { 169 updateDisplay(); 170 } 171 }, 1000); 172 }); 205 206 })(jQuery); -
smart-download-redirector/tags/1.0.0/assets/styles.css
r3325102 r3325285 45 45 } 46 46 47 .smartdr-info { 48 padding: 1em; 49 margin: 1em 0; 50 background-color: #d1ecf1; 51 border: 1px solid #b8daff; 52 border-radius: 4px; 53 color: #0c5460; 54 text-align: center; 55 } 56 57 /* Base countdown styles - applies to all layouts with circular progress */ 47 58 .smartdr-countdown { 48 text-align: center; 49 margin: 2em 0; 50 } 51 52 .smartdr-countdown-number { 59 position: relative; 60 width: 120px; 61 height: 120px; 62 margin: 0 auto 2em; 63 display: flex; 64 align-items: center; 65 justify-content: center; 66 text-align: center; 67 } 68 69 .smartdr-countdown-number, 70 #smartdr-countdown-number { 71 position: relative; 72 z-index: 2; 73 line-height: 1; 74 display: flex; 75 align-items: center; 76 justify-content: center; 77 width: 100%; 78 height: 100%; 79 color: var(--smartdr-counter-color, #2271b1); 53 80 font-size: var(--smartdr-counter-size, 4em); 54 color: var(--smartdr-counter-color, #2271b1); 55 font-weight: bold; 56 line-height: 1; 57 margin: 0.5em 0; 81 font-weight: var(--smartdr-counter-font-weight, 600); 82 margin: 0; 83 padding: 0; 58 84 } 59 85 60 86 .smartdr-progress-ring { 87 position: absolute; 88 top: 0; 89 left: 0; 90 width: 120px; 91 height: 120px; 61 92 transform: rotate(-90deg); 62 transform-origin: 50% 50%;93 transform-origin: center; 63 94 } 64 95 … … 85 116 transition: opacity 0.3s ease; 86 117 } 87 88 89 118 90 119 .smartdr-step.active { … … 113 142 font-size: var(--smartdr-manual-link-size, 1em); 114 143 transition: color 0.2s ease; 144 display: inline-block; 115 145 } 116 146 … … 132 162 margin-bottom: 1.5em; 133 163 text-align: center; 134 } 135 136 /* Standard and Compact Layout countdown styles */ 137 .smartdr-countdown { 138 position: relative; 139 width: 120px; 140 height: 120px; 141 margin: 0 auto 2em !important; 142 display: flex !important; 143 align-items: center !important; 144 justify-content: center !important; 145 text-align: center !important; 146 } 147 148 .smartdr-progress-ring { 149 position: absolute; 150 top: 0; 151 left: 0; 152 width: 120px; 153 height: 120px; 154 transform: rotate(-90deg); 155 transform-origin: center; 156 } 157 158 .smartdr-progress-ring-circle { 159 stroke: var(--smartdr-counter-color) !important; 160 stroke-width: 4 !important; 161 fill: transparent !important; 162 } 163 164 #smartdr-countdown-number { 165 position: relative; 166 z-index: 2; 167 line-height: 1; 168 display: flex; 169 align-items: center; 170 justify-content: center; 171 width: 100%; 172 height: 100%; 173 color: var(--smartdr-counter-color); 174 font-size: var(--smartdr-counter-size); 175 font-weight: var(--smartdr-counter-font-weight); 176 } 177 178 .smartdr-manual-redirect { 179 margin-top: 1.5em; 180 text-align: center; 181 } 182 183 .smartdr-manual-link { 184 color: var(--smartdr-manual-link-color); 185 font-size: var(--smartdr-manual-link-size); 186 text-decoration: none; 187 transition: color 0.2s ease; 188 display: inline-block; 189 } 190 191 .smartdr-manual-link:hover { 192 color: var(--smartdr-manual-link-hover-color); 193 text-decoration: underline; 164 color: var(--smartdr-heading-color, #333333); 165 font-size: var(--smartdr-heading-size, 32px); 166 font-weight: 600; 167 line-height: 1.4; 168 } 169 170 /* Standard Layout - ensure proper centering */ 171 .smartdr-layout-standard .smartdr-countdown { 172 margin: 0 auto 2em; 194 173 } 195 174 … … 216 195 margin-bottom: 1em; 217 196 text-align: left; 218 font-size: var(--smartdr-heading-size );219 color: var(--smartdr-heading-color );197 font-size: var(--smartdr-heading-size, 32px); 198 color: var(--smartdr-heading-color, #333333); 220 199 } 221 200 … … 230 209 height: 120px; 231 210 margin: 0; 232 display: flex !important;233 align-items: center !important;234 justify-content: center !important;211 display: flex; 212 align-items: center; 213 justify-content: center; 235 214 } 236 215 … … 242 221 } 243 222 244 .smartdr-layout-progressbar .smartdr-progress-text { 223 .smartdr-layout-progressbar .smartdr-progress-text, 224 .smartdr-layout-progressbar #smartdr-countdown-text { 245 225 margin-bottom: 1em; 246 font-size: var(--smartdr-counter-size); 247 color: var(--smartdr-counter-color); 248 font-weight: var(--smartdr-counter-font-weight); 226 font-size: var(--smartdr-counter-size, 36px); 227 color: var(--smartdr-counter-color, #2271b1); 228 font-weight: var(--smartdr-counter-font-weight, 600); 229 display: block; 249 230 } 250 231 … … 255 236 overflow: hidden; 256 237 box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.1); 257 } 258 259 .smartdr-layout-progressbar .smartdr-progress-bar { 238 margin-bottom: 1em; 239 } 240 241 .smartdr-layout-progressbar .smartdr-progress-bar, 242 .smartdr-layout-progressbar #smartdr-progress-bar { 260 243 height: 100%; 261 background: var(--smartdr-counter-color );244 background: var(--smartdr-counter-color, #2271b1); 262 245 border-radius: 10px; 263 246 width: 0%; … … 283 266 } 284 267 285 .smartdr-countdown-number { 286 font-size: calc(var(--smartdr-counter-size, 4em) * 0.8); 268 .smartdr-countdown-number, 269 #smartdr-countdown-number { 270 font-size: calc(var(--smartdr-counter-size, 36px) * 0.8); 287 271 } 288 272 … … 292 276 .smartdr-container h4, 293 277 .smartdr-container h5, 294 .smartdr-container h6 { 295 font-size: calc(var(--smartdr-heading-size, 1.8em) * 0.9); 278 .smartdr-container h6, 279 .smartdr-heading { 280 font-size: calc(var(--smartdr-heading-size, 32px) * 0.9); 296 281 } 297 282 … … 314 299 text-align: center; 315 300 } 301 302 /* Progressbar layout responsive */ 303 .smartdr-layout-progressbar .smartdr-progress-text, 304 .smartdr-layout-progressbar #smartdr-countdown-text { 305 font-size: calc(var(--smartdr-counter-size, 36px) * 0.8); 306 } 316 307 } -
smart-download-redirector/tags/1.0.0/includes/redirect-functions.php
r3325102 r3325285 11 11 } 12 12 13 /** 14 * Add custom query vars. 15 * 16 * @param array $vars The array of available query variables. 17 * @return array Modified array of query variables. 18 */ 19 function smartdr_query_vars($vars) 20 { 21 $vars[] = 'smartdr_download'; 22 return $vars; 23 } 24 25 /** 26 * Handle template redirect for download URLs. 27 */ 28 function smartdr_template_redirect() 29 { 30 $download_id = get_query_var('smartdr_download'); 31 32 if (! empty($download_id)) { 33 $decoded_url = base64_decode($download_id); 34 35 if (false === $decoded_url) { 36 wp_die(esc_html__('Invalid download URL.', 'smart-download-redirector')); 37 } 38 39 if (! smartdr_validate_redirect_url($decoded_url)) { 40 wp_die(esc_html__('Invalid download URL.', 'smart-download-redirector')); 41 } 42 43 // Check rate limit 44 if (! smartdr_check_rate_limit()) { 45 wp_die(esc_html__('Download rate limit exceeded. Please try again later.', 'smart-download-redirector')); 46 } 47 48 // Set up variables for the template 49 $url = $decoded_url; 50 $countdown_time = absint(get_option('smartdr_countdown_time', 10)); 51 $show_destination = get_option('smartdr_show_destination', true); 52 $destination_display_step = absint(get_option('smartdr_destination_display_step', 1)); 53 $show_manual_link = get_option('smartdr_show_manual_link', true); 54 $manual_link_display_step = absint(get_option('smartdr_manual_link_display_step', 2)); 55 56 // Get and validate redirect delay (1-10 seconds) 57 $redirect_delay = get_option('smartdr_redirect_delay'); 58 59 // Ensure we have a valid numeric value 60 if ($redirect_delay === false || !is_numeric($redirect_delay)) { 61 $redirect_delay = 3; // Default to 3 seconds 62 } 63 64 // Convert to integer and constrain to valid range 65 $redirect_delay = absint($redirect_delay); 66 $redirect_delay = min(max($redirect_delay, 1), 10); 67 68 // Convert to milliseconds for JavaScript 69 $redirect_delay_ms = $redirect_delay * 1000; 70 71 // Enqueue and localize the script with debug info 72 wp_enqueue_script('smartdr-countdown'); 73 wp_localize_script( 74 'smartdr-countdown', 75 'smartdrData', 76 [ 77 'redirectUrl' => $url, 78 'countdownTime' => $countdown_time, 79 'nonce' => wp_create_nonce('smartdr-countdown'), 80 'showDestination' => $show_destination, 81 'destinationStep' => $destination_display_step, 82 'showManualLink' => $show_manual_link, 83 'manualLinkStep' => $manual_link_display_step, 84 'redirectDelay' => $redirect_delay_ms 85 ] 86 ); 87 88 // Load the countdown template 89 include SMARTDR_PLUGIN_DIR . 'templates/download-page.php'; 90 exit; 91 } 92 } 93 94 /** 95 * Handle redirect validation on the download page 96 */ 97 function smartdr_handle_download_redirect() 98 { 99 // Performance optimization: Only run this check when we have relevant parameters 100 // This prevents the function from running on every page load 101 if (empty($_GET['url']) && empty($_GET['smartdr_nonce'])) { 102 return; 103 } 104 105 // Check if we're on the download page or if the URL contains 'download' 106 $request_uri = isset($_SERVER['REQUEST_URI']) ? sanitize_text_field(wp_unslash($_SERVER['REQUEST_URI'])) : ''; 107 if (!is_page('download') && strpos($request_uri, '/download') === false) { 108 return; 109 } 110 111 // Remove error_log for production 112 // error_log('SMARTDR: Starting download redirect handler'); 113 114 // Verify nonce FIRST before processing any input 115 $nonce = isset($_GET['smartdr_nonce']) ? sanitize_key(wp_unslash($_GET['smartdr_nonce'])) : ''; 116 if (empty($nonce) || !wp_verify_nonce($nonce, 'smartdr_download')) { 117 // Remove error_log for production 118 // error_log('SMARTDR: Invalid nonce'); 119 wp_die(esc_html__('Invalid or expired download link.', 'smart-download-redirector')); 120 return; 121 } 122 123 // Now safely get and sanitize the URL parameter after nonce verification 124 $url_param = isset($_GET['url']) ? sanitize_text_field(wp_unslash($_GET['url'])) : ''; 125 126 // Remove error_log for production 127 // error_log('SMARTDR: Processing URL parameter: ' . $url_param); 128 129 // If no URL parameter, show the default page content 130 if (empty($url_param)) { 131 // Remove error_log for production 132 // error_log('SMARTDR: No URL parameter, showing default page'); 133 return; 134 } 135 136 // Decode URL if it's encoded 137 $url = urldecode($url_param); 138 // Remove error_log for production 139 // error_log('SMARTDR: Decoded URL: ' . $url); 140 141 // Normalize URL format 142 $url = smartdr_normalize_url($url); 143 144 // Validate URL 145 if (!smartdr_validate_redirect_url($url)) { 146 // Remove error_log for production 147 // error_log('SMARTDR: URL validation failed for: ' . $url); 148 wp_die(esc_html__('Invalid download URL.', 'smart-download-redirector')); 149 return; 150 } 151 152 // Remove error_log for production 153 // error_log('SMARTDR: URL validation successful for: ' . $url); 154 155 // Create or get the download page 156 $download_page = get_page_by_path('download'); 157 if (!$download_page) { 158 // Create the download page if it doesn't exist 159 $download_page_id = wp_insert_post([ 160 'post_title' => esc_html__('Download', 'smart-download-redirector'), 161 'post_name' => 'download', 162 'post_content' => '[smartdr_download_countdown]', 163 'post_status' => 'publish', 164 'post_type' => 'page', 165 'comment_status' => 'closed', 166 'ping_status' => 'closed' 167 ]); 168 $download_page = get_post($download_page_id); 169 } 170 171 // Ensure the page exists 172 if (!$download_page) { 173 // Remove error_log for production 174 // error_log('SMARTDR: Failed to create or get download page'); 175 return; 176 } 177 178 // Set up the global post object 179 global $wp_query, $post; 180 $wp_query->is_page = true; 181 $wp_query->is_singular = true; 182 $wp_query->is_home = false; 183 $wp_query->is_archive = false; 184 $wp_query->is_category = false; 185 $wp_query->query_vars['url'] = $url_param; 186 $wp_query->query_vars['smartdr_nonce'] = $nonce; 187 188 // Set up the post 189 $post = $download_page; 190 setup_postdata($post); 191 192 // Force the content to contain our shortcode 193 add_filter('the_content', function($content) { 194 return '[smartdr_download_countdown]'; 195 }, 999999); 196 } 197 add_action('template_redirect', 'smartdr_handle_download_redirect', 1); 13 14 15 198 16 199 17 /** … … 281 99 // Check if domain matches 282 100 if ($domain === $target_domain) { 283 // Create redirect URL using query parameters 101 // Create redirect URL using query parameters to the shortcode page 284 102 $redirect_url = add_query_arg([ 285 ' smartdr_url' => base64_encode($url),103 'url' => urlencode($url), 286 104 'smartdr_nonce' => wp_create_nonce('smartdr_download') 287 105 ], get_permalink($shortcode_page)); … … 377 195 { 378 196 if (! smartdr_validate_redirect_url($url)) { 379 // Remove error_log for production380 // error_log('SMARTDR: URL validation failed in generate_download_url for: ' . $url);381 197 return $url; 382 198 } 383 199 384 // Remove error_log for production 385 // error_log('SMARTDR: Generating download URL for: ' . $url); 386 $redirect_url = home_url('/download/'); 387 $redirect_url = add_query_arg('url', urlencode($url), $redirect_url); 388 $redirect_url = wp_nonce_url($redirect_url, 'smartdr_download', 'smartdr_nonce'); 389 390 // Remove error_log for production 391 // error_log('SMARTDR: Generated redirect URL: ' . $redirect_url); 200 // Get the shortcode page 201 $shortcode_page = get_option('smartdr_shortcode_page'); 202 203 // If no shortcode page found, try to find it 204 if (!$shortcode_page) { 205 $pages = get_posts([ 206 'post_type' => 'page', 207 'posts_per_page' => -1, 208 'post_status' => 'publish' 209 ]); 210 211 foreach ($pages as $page) { 212 if (has_shortcode($page->post_content, 'smartdr_download_countdown')) { 213 $shortcode_page = $page->ID; 214 update_option('smartdr_shortcode_page', $shortcode_page); 215 break; 216 } 217 } 218 } 219 220 // If still no shortcode page found, return original URL 221 if (!$shortcode_page) { 222 return $url; 223 } 224 225 // Create redirect URL using query parameters to the shortcode page 226 $redirect_url = add_query_arg([ 227 'url' => urlencode($url), 228 'smartdr_nonce' => wp_create_nonce('smartdr_download') 229 ], get_permalink($shortcode_page)); 230 392 231 return $redirect_url; 393 232 } … … 404 243 } 405 244 406 // Performance optimization: Only process if we have relevant parameters 407 if (empty($_GET['smartdr_url']) && empty($_GET['smartdr_nonce'])) { 408 return ''; 409 } 410 411 // Verify nonce FIRST before processing any input 245 // Check for URL parameters 246 $url_param = isset($_GET['url']) ? sanitize_text_field(wp_unslash($_GET['url'])) : ''; 412 247 $nonce = isset($_GET['smartdr_nonce']) ? sanitize_key(wp_unslash($_GET['smartdr_nonce'])) : ''; 413 if (empty($nonce) || !wp_verify_nonce($nonce, 'smartdr_download')) { 414 return '<div class="smartdr-error">' . esc_html__('Invalid or expired download link.', 'smart-download-redirector') . '</div>'; 415 } 416 417 // Now safely get and sanitize the URL parameter after nonce verification 418 $url_param = isset($_GET['smartdr_url']) ? sanitize_text_field(wp_unslash($_GET['smartdr_url'])) : ''; 419 420 // If no URL parameter, return empty 421 if (empty($url_param)) { 422 return ''; 423 } 424 425 // Decode base64 URL 426 $url = base64_decode($url_param); 427 if ($url === false) { 428 return '<div class="smartdr-error">' . esc_html__('Invalid URL format.', 'smart-download-redirector') . '</div>'; 429 } 430 431 // Normalize URL format 432 $url = smartdr_normalize_url($url); 433 434 if (!smartdr_validate_redirect_url($url)) { 435 return '<div class="smartdr-error">' . esc_html__('Invalid download URL. Please check your target domains settings.', 'smart-download-redirector') . '</div>'; 436 } 437 438 // Get countdown time from settings 248 $url = null; 249 250 if (!empty($url_param) && !empty($nonce)) { 251 // Verify nonce 252 if (wp_verify_nonce($nonce, 'smartdr_download')) { 253 $decoded_url = urldecode($url_param); 254 $decoded_url = smartdr_normalize_url($decoded_url); 255 256 if (smartdr_validate_redirect_url($decoded_url)) { 257 $url = $decoded_url; 258 } 259 } 260 } 261 262 // Get countdown configuration for template 439 263 $countdown_time = absint(get_option('smartdr_countdown_time', 10)); 440 441 // Get and validate redirect delay (1-10 seconds) 442 $redirect_delay = get_option('smartdr_redirect_delay', 3); 443 $redirect_delay = absint($redirect_delay); 264 $redirect_delay = absint(get_option('smartdr_redirect_delay', 3)); 444 265 $redirect_delay = min(max($redirect_delay, 1), 10); 445 $redirect_delay_ms = $redirect_delay * 1000; 446 447 // Enqueue required scripts and styles 448 wp_enqueue_script('smartdr-countdown'); 449 wp_enqueue_style('smartdr-styles'); 450 451 // Localize script data 452 wp_localize_script( 453 'smartdr-countdown', 454 'smartdrData', 455 [ 456 'redirectUrl' => $url, 457 'countdownTime' => $countdown_time, 458 'nonce' => wp_create_nonce('smartdr-countdown'), 459 'showDestination' => get_option('smartdr_show_destination', true), 460 'destinationStep' => absint(get_option('smartdr_destination_display_step', 1)), 461 'showManualLink' => get_option('smartdr_show_manual_link', true), 462 'manualLinkStep' => absint(get_option('smartdr_manual_link_display_step', 2)), 463 'redirectDelay' => $redirect_delay_ms, 464 'ajaxurl' => admin_url('admin-ajax.php') 465 ] 466 ); 266 267 // If still no URL, show placeholder 268 if (!$url) { 269 return '<div class="smartdr-info">' . esc_html__('Download countdown will appear here when accessing a download link.', 'smart-download-redirector') . '</div>'; 270 } 271 272 // We have a valid URL, proceed with countdown 467 273 468 274 // Start output buffering to capture template content … … 486 292 'custom_heading' => get_option('smartdr_custom_heading', esc_html__('Preparing Your Download', 'smart-download-redirector')), 487 293 'show_title' => get_option('smartdr_show_title', true), 488 'manual_link_display_timing' => 'at_end', // Always show at end294 'manual_link_display_timing' => 'at_end', 489 295 'styles' => get_option('smartdr_styles', []) 490 296 ]; … … 498 304 // Get buffered content 499 305 $content = ob_get_clean(); 500 501 // Always use custom style mode502 $style_mode = 'custom';503 306 504 307 // Get custom styles for CSS variables … … 519 322 // Wrap content in theme-independent container 520 323 return sprintf( 521 '<div class="smartdr-container" data-smartdr-version="%s" data-style-mode=" %s"%s>%s</div>',324 '<div class="smartdr-container" data-smartdr-version="%s" data-style-mode="custom"%s>%s</div>', 522 325 esc_attr(SMARTDR_VERSION), 523 esc_attr($style_mode),524 326 $style_attrs, 525 327 $content … … 527 329 } 528 330 add_shortcode('smartdr_download_countdown', 'smartdr_countdown_shortcode'); 331 332 /** 333 * Get WPML translated string or fallback to original 334 * 335 * @param string $name The string name 336 * @param string $original The original string 337 * @return string The translated string or original 338 */ 339 function smartdr_get_wpml_string($name, $original) { 340 // Try newer WPML API first (WPML 4.0+) 341 if (function_exists('wpml_translate_single_string')) { 342 return wpml_translate_single_string('smart-download-redirector', $name, $original); 343 } 344 345 // Fallback to older WPML API (WPML 3.x) 346 if (function_exists('icl_t')) { 347 return icl_t('smart-download-redirector', $name, $original); 348 } 349 350 // Fallback to original string if WPML is not available 351 return $original; 352 } 529 353 530 354 /** -
smart-download-redirector/tags/1.0.0/smart-download-redirector.php
r3325102 r3325285 49 49 } 50 50 } 51 52 // Flush rewrite rules.53 flush_rewrite_rules();54 51 } 55 52 register_activation_hook(__FILE__, 'smartdr_activate'); … … 60 57 function smartdr_deactivate() 61 58 { 62 // Flush rewrite rules. 63 flush_rewrite_rules(); 59 // Clean up on deactivation 64 60 } 65 61 register_deactivation_hook(__FILE__, 'smartdr_deactivate'); 66 67 /**68 * Initialize plugin.69 */70 function smartdr_init()71 {72 // Add rewrite rules.73 add_rewrite_rule(74 '^download/([^/]+)/?$',75 'index.php?smartdr_download=$matches[1]',76 'top'77 );78 79 // Add query vars.80 add_filter('query_vars', 'smartdr_query_vars');81 82 // Add template redirect.83 add_action('template_redirect', 'smartdr_template_redirect');84 }85 add_action('init', 'smartdr_init');86 62 87 63 /** … … 93 69 function smartdr_register_assets() 94 70 { 71 // Register countdown script 95 72 wp_register_script( 96 73 'smartdr-countdown', … … 98 75 ['jquery'], 99 76 filemtime(SMARTDR_PLUGIN_DIR . 'assets/countdown.js'), 100 false // Load in header.77 true // Load in footer for better performance 101 78 ); 102 79 … … 110 87 } 111 88 add_action('wp_enqueue_scripts', 'smartdr_register_assets'); 89 90 /** 91 * Check if we need to enqueue countdown assets based on content 92 */ 93 function smartdr_maybe_enqueue_assets() 94 { 95 // Check if we're on a page with the shortcode 96 global $post; 97 $should_enqueue = false; 98 99 if (is_object($post) && has_shortcode($post->post_content, 'smartdr_download_countdown')) { 100 $should_enqueue = true; 101 } 102 103 // Check if we're on a download URL (with URL parameters) 104 if (isset($_GET['url']) && isset($_GET['smartdr_nonce'])) { 105 $should_enqueue = true; 106 } 107 108 if ($should_enqueue) { 109 wp_enqueue_script('smartdr-countdown'); 110 wp_enqueue_style('smartdr-styles'); 111 112 // Prepare localization data 113 $url_param = isset($_GET['url']) ? sanitize_text_field(wp_unslash($_GET['url'])) : ''; 114 $nonce = isset($_GET['smartdr_nonce']) ? sanitize_key(wp_unslash($_GET['smartdr_nonce'])) : ''; 115 $url = null; 116 117 if (!empty($url_param) && !empty($nonce)) { 118 // Verify nonce 119 if (wp_verify_nonce($nonce, 'smartdr_download')) { 120 $decoded_url = urldecode($url_param); 121 $decoded_url = smartdr_normalize_url($decoded_url); 122 123 if (smartdr_validate_redirect_url($decoded_url)) { 124 $url = $decoded_url; 125 } 126 } 127 } 128 129 // Always prepare countdown configuration 130 $countdown_time = absint(get_option('smartdr_countdown_time', 10)); 131 $redirect_delay = absint(get_option('smartdr_redirect_delay', 3)); 132 $redirect_delay = min(max($redirect_delay, 1), 10); 133 $redirect_delay_ms = $redirect_delay * 1000; 134 135 // Localize script with data 136 wp_localize_script( 137 'smartdr-countdown', 138 'smartdrData', 139 [ 140 'redirectUrl' => $url ? $url : '', 141 'countdownTime' => $countdown_time, 142 'nonce' => wp_create_nonce('smartdr-countdown'), 143 'showDestination' => get_option('smartdr_show_destination', true), 144 'destinationStep' => absint(get_option('smartdr_destination_display_step', 1)), 145 'showManualLink' => get_option('smartdr_show_manual_link', true), 146 'manualLinkStep' => absint(get_option('smartdr_manual_link_display_step', 2)), 147 'redirectDelay' => $redirect_delay_ms, 148 'secondsText' => __('seconds remaining', 'smart-download-redirector'), 149 'downloadReadyText' => __('Download ready!', 'smart-download-redirector'), 150 'hasValidUrl' => !empty($url) 151 ] 152 ); 153 154 // Add CSS variables for styling 155 $styles = get_option('smartdr_styles', array()); 156 $default_styles = array( 157 'heading_color' => '#212121', 158 'heading_size' => '32px', 159 'counter_color' => '#00897B', 160 'counter_size' => '36px', 161 'counter_font_weight' => 'bold', 162 'background_color' => '#ffffff', 163 'border_radius' => '8px', 164 'border_size' => '1px', 165 'border_color' => '#dddddd', 166 'manual_link_size' => '14px', 167 'manual_link_color' => '#00897B', 168 'manual_link_hover_color' => '#00695C' 169 ); 170 $styles = array_merge($default_styles, $styles); 171 172 // Add inline CSS with custom properties 173 $inline_css = " 174 /* CSS Custom Properties for dynamic styling */ 175 :root { 176 --smartdr-counter-color: " . esc_attr($styles['counter_color']) . "; 177 --smartdr-counter-size: " . esc_attr($styles['counter_size']) . "; 178 --smartdr-counter-font-weight: " . (($styles['counter_font_weight'] === 'bold') ? '600' : 'normal') . "; 179 --smartdr-heading-color: " . esc_attr($styles['heading_color']) . "; 180 --smartdr-heading-size: " . esc_attr($styles['heading_size']) . "; 181 --smartdr-manual-link-size: " . esc_attr($styles['manual_link_size']) . "; 182 --smartdr-manual-link-color: " . esc_attr($styles['manual_link_color']) . "; 183 --smartdr-manual-link-hover-color: " . esc_attr($styles['manual_link_hover_color']) . "; 184 --smartdr-bg-color: " . esc_attr($styles['background_color']) . "; 185 --smartdr-border-radius: " . esc_attr($styles['border_radius']) . "; 186 --smartdr-border-size: " . esc_attr($styles['border_size']) . "; 187 --smartdr-border-color: " . esc_attr($styles['border_color']) . "; 188 }"; 189 wp_add_inline_style('smartdr-styles', $inline_css); 190 } 191 } 192 add_action('wp_enqueue_scripts', 'smartdr_maybe_enqueue_assets', 20); -
smart-download-redirector/tags/1.0.0/templates/download-page.php
r3325102 r3325285 17 17 $countdown_time = absint(get_option('smartdr_countdown_time', 10)); 18 18 $show_manual_link = get_option('smartdr_show_manual_link', true); 19 // Manual link always shows at the end of countdown (last 10%)20 19 $show_title = get_option('smartdr_show_title', true); 21 20 $redirect_delay = absint(get_option('smartdr_redirect_delay', 3)); … … 27 26 $manual_link_text = smartdr_get_wpml_string('Manual Link Text', $manual_link_text_original); 28 27 29 // Always use custom style mode30 $style_mode = 'custom';31 $styles = get_option('smartdr_styles', array());32 33 28 // Get layout style setting 34 29 $layout_style = get_option('smartdr_layout_style', 'standard'); 35 36 // For theme mode, try to detect actual theme colors37 $theme_primary_color = '#0073aa'; // WordPress default fallback38 if ($style_mode === 'theme') {39 // Try to get theme colors from various sources40 $theme_colors = wp_get_global_styles(array('color'));41 42 // Check for block theme colors43 if (isset($theme_colors['palette']['theme'])) {44 foreach ($theme_colors['palette']['theme'] as $color) {45 if (isset($color['slug']) && in_array($color['slug'], ['primary', 'accent', 'secondary'])) {46 $theme_primary_color = $color['color'];47 break;48 }49 }50 }51 52 // Fallback: check theme supports and customizer colors53 if ($theme_primary_color === '#0073aa') {54 // Check for customizer primary color55 $customizer_primary = get_theme_mod('primary_color');56 if ($customizer_primary) {57 $theme_primary_color = $customizer_primary;58 } else {59 // Check for accent color60 $accent_color = get_theme_mod('accent_color');61 if ($accent_color) {62 $theme_primary_color = $accent_color;63 }64 }65 }66 }67 68 // Default fallbacks for custom mode69 $default_styles = array(70 'heading_color' => '#212121',71 'heading_size' => '32px',72 'counter_color' => '#00897B',73 'counter_size' => '36px',74 'counter_font_weight' => 'bold',75 'background_color' => '#ffffff',76 'border_radius' => '8px',77 'border_size' => '1px',78 'border_color' => '#dddddd',79 'manual_link_size' => '14px',80 'manual_link_color' => '#00897B',81 'manual_link_hover_color' => '#00695C'82 );83 84 $styles = array_merge($default_styles, $styles);85 86 // Get destination box styles87 $destination_styles = get_option('smartdr_destination_styles', array());88 $default_destination_styles = array(89 'background_color' => '#f8f9fa',90 'border_size' => '1px',91 'border_color' => '#dee2e6',92 'border_radius' => '4px',93 'text_size' => '14px'94 );95 $destination_styles = array_merge($default_destination_styles, $destination_styles);96 97 // Generate inline styles98 $heading_style = sprintf(99 'color: %s; font-size: %s;',100 esc_attr($styles['heading_color']),101 esc_attr($styles['heading_size'])102 );103 104 // Generate destination box style105 $destination_box_style = sprintf(106 'background-color: %s; border: %s solid %s; border-radius: %s; font-size: %s;',107 esc_attr($destination_styles['background_color']),108 esc_attr($destination_styles['border_size']),109 esc_attr($destination_styles['border_color']),110 esc_attr($destination_styles['border_radius']),111 esc_attr($destination_styles['text_size'])112 );113 30 ?> 114 31 … … 180 97 181 98 <?php 182 // Add inline styles using WordPress proper method 183 $inline_css = " 184 /* CSS Custom Properties for dynamic styling */ 185 :root { 186 --smartdr-counter-color: " . esc_attr($styles['counter_color']) . "; 187 --smartdr-counter-size: " . esc_attr($styles['counter_size']) . "; 188 --smartdr-counter-font-weight: " . (($styles['counter_font_weight'] === 'bold') ? '600' : 'normal') . "; 189 --smartdr-heading-color: " . esc_attr($styles['heading_color']) . "; 190 --smartdr-heading-size: " . esc_attr($styles['heading_size']) . "; 191 --smartdr-manual-link-size: " . esc_attr($styles['manual_link_size']) . "; 192 --smartdr-manual-link-color: " . esc_attr($styles['manual_link_color']) . "; 193 --smartdr-manual-link-hover-color: " . esc_attr($styles['manual_link_hover_color']) . "; 194 } 195 196 /* All layout and static styles are now in assets/styles.css */ 197 /* Only CSS Custom Properties remain here for dynamic theming */ 198 "; 199 wp_add_inline_style('smartdr-styles', $inline_css); 99 // CSS Custom Properties are now handled in the main plugin file 100 // to ensure proper timing and availability 200 101 ?> 201 102 202 <?php 203 // Add inline script using WordPress proper method 204 $inline_js = " 205 jQuery(document).ready(function($) { 206 var countdownTime = " . esc_js($countdown_time) . "; 207 var showManualLink = " . ($show_manual_link ? 'true' : 'false') . "; 208 var manualLinkTiming = 'at_end'; // Always show at end 209 var layoutStyle = '" . esc_js($layout_style) . "'; 210 var redirectUrl = '" . esc_js($url) . "'; 211 var downloadReadyText = '" . esc_js(__('Download ready!', 'smart-download-redirector')) . "'; 212 var secondsRemainingText = '" . esc_js(__('seconds remaining', 'smart-download-redirector')) . "'; 213 214 // Get smartdrData from localized script 215 if (typeof smartdrData !== 'undefined') { 216 var redirectDelay = smartdrData.redirectDelay; 217 218 // Debug values 219 console.log('[SMARTDR Debug] Initial settings:', { 220 countdownTime: countdownTime + ' seconds', 221 redirectDelay: (redirectDelay / 1000) + ' seconds', 222 showManualLink: showManualLink, 223 manualLinkTiming: manualLinkTiming, 224 layoutStyle: layoutStyle 225 }); 226 227 var \$manualRedirect = \$('.smartdr-manual-redirect'); 228 var \$countdownNumber = \$('#smartdr-countdown-number'); 229 var \$countdownText = \$('#smartdr-countdown-text'); 230 var \$progressBar = \$('#smartdr-progress-bar'); 231 var circle = document.querySelector('.smartdr-progress-ring-circle'); 232 var isRedirecting = false; 233 234 // Initialize circular progress (for standard and compact layouts) 235 if (circle) { 236 var radius = circle.r.baseVal.value; 237 var circumference = radius * 2 * Math.PI; 238 circle.style.strokeDasharray = circumference + ' ' + circumference; 239 circle.style.strokeDashoffset = circumference; 240 } 241 242 // IMPORTANT: Manual link must NEVER be visible during countdown 243 // Force hide and ensure it stays hidden until countdown is complete 244 \$manualRedirect.hide().css('visibility', 'hidden'); 245 246 // Additional safeguard: prevent any accidental showing during countdown 247 function ensureManualLinkHidden() { 248 if (!countdownComplete && !manualLinkShown) { 249 \$manualRedirect.hide().css('visibility', 'hidden'); 250 } 251 } 252 253 function setProgress(percent) { 254 // Update circular progress (standard and compact layouts) 255 if (circle) { 256 var offset = circumference - (percent / 100 * circumference); 257 circle.style.strokeDashoffset = offset; 258 } 259 260 // Update horizontal progress bar (progressbar layout) 261 if (\$progressBar.length > 0) { 262 \$progressBar.css('width', percent + '%'); 263 } 264 } 265 266 var manualLinkShown = false; // Track if manual link has been shown 267 var countdownComplete = false; // Track if countdown has finished 268 269 function complete() { 270 console.log('[SMARTDR Debug] Countdown complete'); 271 272 // Mark countdown as complete - manual link can now be shown 273 countdownComplete = true; 274 275 // Update progress to 100% 276 setProgress(100); 277 278 // Update countdown displays based on layout 279 if (layoutStyle === 'progressbar') { 280 // For progressbar layout, update text 281 \$countdownText.fadeOut(200, function() { 282 \$(this).text(downloadReadyText).fadeIn(200); 283 284 // Show manual link after text update if enabled (only after countdown is complete) 285 if (showManualLink && !manualLinkShown && countdownComplete) { 286 \$manualRedirect.css('visibility', 'visible').fadeIn(400); 287 manualLinkShown = true; 288 console.log('[SMARTDR Debug] ✅ Manual link shown after countdown completion'); 289 } 290 }); 291 } else { 292 // For standard and compact layouts, show arrow 293 \$countdownNumber.fadeOut(200, function() { 294 \$(this).text('→').fadeIn(200); 295 296 // Show manual link after arrow appears if enabled (only after countdown is complete) 297 if (showManualLink && !manualLinkShown && countdownComplete) { 298 \$manualRedirect.css('visibility', 'visible').fadeIn(400); 299 manualLinkShown = true; 300 console.log('[SMARTDR Debug] ✅ Manual link shown after countdown completion'); 301 } 302 }); 303 } 304 305 // Redirect after configured delay 306 console.log('[SMARTDR Debug] Will redirect in ' + (redirectDelay/1000) + ' seconds'); 307 setTimeout(function() { 308 console.log('[SMARTDR Debug] Redirecting now'); 309 window.location.href = redirectUrl; 310 }, redirectDelay); 311 } 312 313 function countdown() { 314 var timeLeft = countdownTime; 315 316 var timer = setInterval(function() { 317 if (isRedirecting) { 318 clearInterval(timer); 319 return; 320 } 321 322 timeLeft--; 323 var progress = ((countdownTime - timeLeft) / countdownTime) * 100; 324 setProgress(progress); 325 326 // Update display based on layout 327 if (layoutStyle === 'progressbar') { 328 // Update progressbar text with remaining seconds 329 \$countdownText.text(timeLeft + ' ' + secondsRemainingText); 330 } else { 331 // Update countdown number for circular layouts 332 \$countdownNumber.text(timeLeft); 333 } 334 335 // Safeguard: Ensure manual link stays hidden during countdown 336 ensureManualLinkHidden(); 337 338 // Complete countdown when time reaches zero 339 if (timeLeft <= 0) { 340 clearInterval(timer); 341 complete(); 342 } 343 }, 1000); 344 } 345 346 countdown(); 347 } else { 348 console.error('[SMARTDR] smartdrData not found - countdown will not work'); 349 } 350 }); 351 "; 352 wp_add_inline_script('smartdr-countdown', $inline_js); 353 ?> 103 -
smart-download-redirector/trunk/admin/settings-page.php
r3325102 r3325285 1288 1288 } 1289 1289 1290 // Check if settings were updated (WordPress core parameter, safe to check) 1291 // Additional security: Only process if user has proper capabilities 1292 if (current_user_can('manage_options') && 1293 isset($_GET['settings-updated']) && 1294 $_GET['settings-updated'] === 'true' && 1295 !get_settings_errors('smartdr_messages') // Prevent duplicate messages 1296 ) { 1290 // Check if settings were updated 1291 // Use WordPress core function to check if options were updated - this is security-safe 1292 $settings_updated = false; 1293 if (current_user_can('manage_options')) { 1294 // Check if any of our options were recently updated 1295 $check_options = [ 1296 'smartdr_target_domains', 1297 'smartdr_countdown_time', 1298 'smartdr_redirect_delay', 1299 'smartdr_styles', 1300 'smartdr_layout_style' 1301 ]; 1302 1303 foreach ($check_options as $option) { 1304 if (get_transient('smartdr_option_updated_' . $option)) { 1305 $settings_updated = true; 1306 delete_transient('smartdr_option_updated_' . $option); 1307 break; 1308 } 1309 } 1310 1311 // Fallback: Check WordPress core parameter (already validated by WordPress) 1312 if (!$settings_updated && 1313 filter_input(INPUT_GET, 'settings-updated', FILTER_SANITIZE_STRING) === 'true' && 1314 !get_settings_errors('smartdr_messages') 1315 ) { 1316 $settings_updated = true; 1317 } 1318 } 1319 1320 if ($settings_updated) { 1297 1321 add_settings_error( 1298 1322 'smartdr_messages', … … 1356 1380 } 1357 1381 1358 /** 1359 * Get WPML translated string 1360 */ 1361 function smartdr_get_wpml_string($name, $original_value) { 1362 if (function_exists('wpml_translate_single_string')) { 1363 return wpml_translate_single_string('smart-download-redirector', $name, $original_value); 1364 } 1365 return $original_value; 1366 } 1382 1383 1384 /** 1385 * Set transient when options are updated for secure update detection 1386 */ 1387 function smartdr_option_updated($option_name) { 1388 if (strpos($option_name, 'smartdr_') === 0) { 1389 set_transient('smartdr_option_updated_' . $option_name, true, 60); // 1 minute 1390 } 1391 } 1392 1393 // Hook to track option updates 1394 add_action('updated_option', 'smartdr_option_updated'); 1367 1395 1368 1396 // Hook to register WPML strings when options are updated -
smart-download-redirector/trunk/assets/countdown.js
r3325102 r3325285 1 1 /** 2 * Enhanced countdown timer with progress ring and step indicators2 * Simple and reliable countdown timer 3 3 */ 4 jQuery(function($) {4 (function($) { 5 5 'use strict'; 6 6 7 // Debug helper 8 var debug = function(msg, data) { 9 if (window.console && console.log) { 10 console.log('[SDR Debug]:', msg, data || ''); 11 } 12 }; 13 14 debug('Script starting'); 15 16 // Validate data 17 if (typeof smartdrData === 'undefined') { 18 debug('ERROR: smartdrData missing'); 19 return; 7 // Function to run when DOM is ready 8 function initCountdown() { 9 console.log('[SmartDR] Countdown script starting...'); 10 11 // Check if jQuery is available 12 if (typeof $ === 'undefined') { 13 console.error('[SmartDR] jQuery not available'); 14 return; 15 } 16 17 // Check if smartdrData is available 18 if (typeof smartdrData === 'undefined') { 19 console.error('[SmartDR] smartdrData not available'); 20 return; 21 } 22 23 console.log('[SmartDR] smartdrData:', smartdrData); 24 25 // Check if we have a valid URL to countdown for 26 if (!smartdrData.hasValidUrl) { 27 console.log('[SmartDR] No valid URL - countdown not started'); 28 return; 29 } 30 31 // Get countdown time 32 var countdownTime = parseInt(smartdrData.countdownTime) || 10; 33 var redirectUrl = smartdrData.redirectUrl || '#'; 34 var redirectDelay = parseInt(smartdrData.redirectDelay) || 3000; 35 36 console.log('[SmartDR] Configuration:', { 37 countdownTime: countdownTime, 38 redirectUrl: redirectUrl, 39 redirectDelay: redirectDelay 40 }); 41 42 // Find countdown elements 43 var $counterNumber = $('#smartdr-countdown-number'); 44 var $counterText = $('#smartdr-countdown-text'); 45 var $progressBar = $('#smartdr-progress-bar'); 46 var $manualRedirect = $('.smartdr-manual-redirect'); 47 var $progressRing = $('.smartdr-progress-ring-circle'); 48 49 console.log('[SmartDR] Elements found:', { 50 counterNumber: $counterNumber.length, 51 counterText: $counterText.length, 52 progressBar: $progressBar.length, 53 manualRedirect: $manualRedirect.length, 54 progressRing: $progressRing.length 55 }); 56 57 // Determine which element to use 58 var $activeElement = null; 59 var layoutType = 'unknown'; 60 61 if ($counterNumber.length > 0) { 62 $activeElement = $counterNumber; 63 layoutType = 'circular'; 64 console.log('[SmartDR] Using circular layout'); 65 } else if ($counterText.length > 0) { 66 $activeElement = $counterText; 67 layoutType = 'progressbar'; 68 console.log('[SmartDR] Using progressbar layout'); 69 } else { 70 console.error('[SmartDR] No countdown element found!'); 71 return; 72 } 73 74 // Initialize progress ring if available 75 var circumference = 0; 76 if ($progressRing.length > 0 && layoutType === 'circular') { 77 try { 78 var radius = $progressRing[0].r.baseVal.value; 79 circumference = radius * 2 * Math.PI; 80 $progressRing[0].style.strokeDasharray = circumference; 81 $progressRing[0].style.strokeDashoffset = circumference; 82 console.log('[SmartDR] Progress ring initialized, circumference:', circumference); 83 } catch (e) { 84 console.warn('[SmartDR] Progress ring initialization failed:', e); 85 } 86 } 87 88 // Start countdown 89 var timeLeft = countdownTime; 90 var initialTime = countdownTime; 91 var isComplete = false; 92 93 console.log('[SmartDR] Starting countdown from', timeLeft); 94 95 function updateCountdown() { 96 if (isComplete) return; 97 98 console.log('[SmartDR] Countdown tick:', timeLeft); 99 100 // Update display based on layout type 101 if (layoutType === 'circular') { 102 $activeElement.text(timeLeft); 103 104 // Update progress ring 105 if ($progressRing.length > 0 && circumference > 0) { 106 var progress = (initialTime - timeLeft) / initialTime; 107 var offset = circumference - (progress * circumference); 108 $progressRing[0].style.strokeDashoffset = offset; 109 } 110 } else if (layoutType === 'progressbar') { 111 var secondsText = smartdrData.secondsText || 'seconds remaining'; 112 $activeElement.text(timeLeft + ' ' + secondsText); 113 114 // Update progress bar 115 if ($progressBar.length > 0) { 116 var progress = (initialTime - timeLeft) / initialTime * 100; 117 $progressBar.css('width', progress + '%'); 118 } 119 } 120 121 // Check if countdown is complete 122 if (timeLeft <= 0) { 123 completeCountdown(); 124 } else { 125 timeLeft--; 126 } 127 } 128 129 function completeCountdown() { 130 if (isComplete) return; 131 isComplete = true; 132 133 console.log('[SmartDR] Countdown complete!'); 134 135 // Clear interval 136 clearInterval(countdownInterval); 137 138 // Update display for completion 139 if (layoutType === 'circular') { 140 $activeElement.text('→'); 141 142 // Complete progress ring 143 if ($progressRing.length > 0) { 144 $progressRing[0].style.strokeDashoffset = 0; 145 } 146 } else if (layoutType === 'progressbar') { 147 var downloadReadyText = smartdrData.downloadReadyText || 'Download ready!'; 148 $activeElement.text(downloadReadyText); 149 150 // Complete progress bar 151 if ($progressBar.length > 0) { 152 $progressBar.css('width', '100%'); 153 } 154 } 155 156 // Show manual redirect link 157 if ($manualRedirect.length > 0) { 158 $manualRedirect.show(); 159 } 160 161 // Start redirect timer 162 console.log('[SmartDR] Starting redirect timer, delay:', redirectDelay + 'ms'); 163 setTimeout(function() { 164 if (redirectUrl && redirectUrl !== '#') { 165 console.log('[SmartDR] Redirecting to:', redirectUrl); 166 window.location.href = redirectUrl; 167 } else { 168 console.log('[SmartDR] No redirect URL, staying on page'); 169 } 170 }, redirectDelay); 171 } 172 173 // Start the countdown 174 updateCountdown(); // Initial update 175 var countdownInterval = setInterval(updateCountdown, 1000); 176 177 // Manual redirect handler 178 $manualRedirect.find('a').on('click', function(e) { 179 if (redirectUrl && redirectUrl !== '#') { 180 console.log('[SmartDR] Manual redirect clicked'); 181 clearInterval(countdownInterval); 182 // Let the link work normally 183 } else { 184 e.preventDefault(); 185 console.log('[SmartDR] Manual redirect clicked but no valid URL'); 186 } 187 }); 188 189 console.log('[SmartDR] Countdown initialized successfully'); 20 190 } 21 22 // Get debug info safely 23 var debugInfo = { 24 countdownTime: smartdrData.countdownTime || 5, 25 redirectDelay: smartdrData.redirectDelay || 3000, 26 rawDelay: (smartdrData.debug && smartdrData.debug.rawDelay) || 3, 27 msDelay: (smartdrData.debug && smartdrData.debug.msDelay) || 3000, 28 defaultDelay: (smartdrData.debug && smartdrData.debug.defaultDelay) || 3000 29 }; 30 31 // Log all debug info 32 debug('Configuration:', debugInfo); 33 34 // Get and validate countdown time 35 var timeLeft = parseInt(smartdrData.countdownTime); 36 if (isNaN(timeLeft) || timeLeft <= 0) { 37 debug('ERROR: Invalid countdown time:', timeLeft); 38 return; 191 192 // Initialize when DOM is ready 193 $(document).ready(function() { 194 initCountdown(); 195 }); 196 197 // Fallback for cases where jQuery might not be loaded immediately 198 if (document.readyState === 'loading') { 199 document.addEventListener('DOMContentLoaded', function() { 200 setTimeout(initCountdown, 100); 201 }); 202 } else { 203 setTimeout(initCountdown, 100); 39 204 } 40 41 // Get and validate redirect delay 42 var redirectDelay = parseInt(smartdrData.redirectDelay); 43 debug('Parsed redirect delay:', redirectDelay); 44 45 if (isNaN(redirectDelay) || redirectDelay < 1000) { 46 debug('WARNING: Invalid redirect delay, using default'); 47 redirectDelay = debugInfo.defaultDelay; 48 } 49 50 debug('Final redirect delay:', redirectDelay + 'ms'); 51 52 // Get elements 53 var $counter = $('#smartdr-countdown-number'); 54 var $ring = $('.smartdr-progress-ring-circle'); 55 var $steps = $('.smartdr-step'); 56 var $targetUrl = $('.smartdr-target-url'); 57 var $manualRedirect = $('.smartdr-manual-redirect'); 58 59 if (!$counter.length) { 60 debug('ERROR: Counter element missing'); 61 return; 62 } 63 64 // Initialize progress ring 65 var circumference = 0; 66 if ($ring.length) { 67 var radius = $ring[0].r.baseVal.value; 68 circumference = radius * 2 * Math.PI; 69 $ring[0].style.strokeDasharray = circumference; 70 $ring[0].style.strokeDashoffset = circumference; 71 } 72 73 // Store initial time for progress calculations 74 var initialTime = timeLeft; 75 var countdownInterval; 76 var currentStep = 1; 77 78 // Update display function 79 function updateDisplay() { 80 // Update counter text 81 $counter.text(timeLeft); 82 83 // Update progress ring 84 if ($ring.length) { 85 var progress = (initialTime - timeLeft) / initialTime; 86 var offset = circumference - (progress * circumference); 87 $ring[0].style.strokeDashoffset = offset; 88 } 89 90 // Calculate current step 91 var newStep; 92 if (timeLeft > (initialTime * 2/3)) { 93 newStep = 1; 94 } else if (timeLeft > (initialTime * 1/3)) { 95 newStep = 2; 96 } else { 97 newStep = 3; 98 } 99 100 // Only update if step changed 101 if (newStep !== currentStep) { 102 currentStep = newStep; 103 // Update step indicators 104 $steps.removeClass('active'); 105 $steps.slice(0, currentStep).addClass('active'); 106 107 // Show URL at appropriate step if enabled 108 if (typeof smartdrData.showDestination !== 'undefined' && smartdrData.showDestination) { 109 $targetUrl.toggle(currentStep >= smartdrData.destinationStep); 110 } 111 112 // Show manual link at appropriate step if enabled 113 if (typeof smartdrData.showManualLink !== 'undefined' && smartdrData.showManualLink) { 114 $manualRedirect.toggle(currentStep >= smartdrData.manualLinkStep); 115 } 116 } 117 } 118 119 // Handle countdown completion 120 function complete() { 121 debug('Countdown complete'); 122 123 // Clear interval 124 clearInterval(countdownInterval); 125 126 // Update display 127 if ($ring.length) { 128 $ring[0].style.strokeDashoffset = 0; 129 } 130 $steps.removeClass('active').eq(2).addClass('active'); 131 132 // Show both URL and manual link at completion if enabled 133 if (typeof smartdrData.showDestination !== 'undefined' && smartdrData.showDestination) { 134 $targetUrl.fadeIn(400); 135 } 136 if (typeof smartdrData.showManualLink !== 'undefined' && smartdrData.showManualLink) { 137 $manualRedirect.fadeIn(400); 138 } 139 140 // Fade in the arrow with animation 141 $counter.fadeOut(200, function() { 142 $(this).text('→').fadeIn(200); 143 }); 144 145 debug('Starting redirect timer:', { 146 delay: redirectDelay, 147 inSeconds: redirectDelay/1000, 148 url: smartdrData.redirectUrl 149 }); 150 151 // Start redirect timer 152 setTimeout(function() { 153 debug('Redirect timer complete, redirecting now'); 154 window.location.href = smartdrData.redirectUrl; 155 }, redirectDelay); 156 } 157 158 // Start countdown 159 debug('Starting countdown'); 160 updateDisplay(); // Initial display update 161 162 countdownInterval = setInterval(function() { 163 timeLeft--; 164 debug('Tick:', timeLeft); 165 166 if (timeLeft <= 0) { 167 complete(); 168 } else { 169 updateDisplay(); 170 } 171 }, 1000); 172 }); 205 206 })(jQuery); -
smart-download-redirector/trunk/assets/styles.css
r3325102 r3325285 45 45 } 46 46 47 .smartdr-info { 48 padding: 1em; 49 margin: 1em 0; 50 background-color: #d1ecf1; 51 border: 1px solid #b8daff; 52 border-radius: 4px; 53 color: #0c5460; 54 text-align: center; 55 } 56 57 /* Base countdown styles - applies to all layouts with circular progress */ 47 58 .smartdr-countdown { 48 text-align: center; 49 margin: 2em 0; 50 } 51 52 .smartdr-countdown-number { 59 position: relative; 60 width: 120px; 61 height: 120px; 62 margin: 0 auto 2em; 63 display: flex; 64 align-items: center; 65 justify-content: center; 66 text-align: center; 67 } 68 69 .smartdr-countdown-number, 70 #smartdr-countdown-number { 71 position: relative; 72 z-index: 2; 73 line-height: 1; 74 display: flex; 75 align-items: center; 76 justify-content: center; 77 width: 100%; 78 height: 100%; 79 color: var(--smartdr-counter-color, #2271b1); 53 80 font-size: var(--smartdr-counter-size, 4em); 54 color: var(--smartdr-counter-color, #2271b1); 55 font-weight: bold; 56 line-height: 1; 57 margin: 0.5em 0; 81 font-weight: var(--smartdr-counter-font-weight, 600); 82 margin: 0; 83 padding: 0; 58 84 } 59 85 60 86 .smartdr-progress-ring { 87 position: absolute; 88 top: 0; 89 left: 0; 90 width: 120px; 91 height: 120px; 61 92 transform: rotate(-90deg); 62 transform-origin: 50% 50%;93 transform-origin: center; 63 94 } 64 95 … … 85 116 transition: opacity 0.3s ease; 86 117 } 87 88 89 118 90 119 .smartdr-step.active { … … 113 142 font-size: var(--smartdr-manual-link-size, 1em); 114 143 transition: color 0.2s ease; 144 display: inline-block; 115 145 } 116 146 … … 132 162 margin-bottom: 1.5em; 133 163 text-align: center; 134 } 135 136 /* Standard and Compact Layout countdown styles */ 137 .smartdr-countdown { 138 position: relative; 139 width: 120px; 140 height: 120px; 141 margin: 0 auto 2em !important; 142 display: flex !important; 143 align-items: center !important; 144 justify-content: center !important; 145 text-align: center !important; 146 } 147 148 .smartdr-progress-ring { 149 position: absolute; 150 top: 0; 151 left: 0; 152 width: 120px; 153 height: 120px; 154 transform: rotate(-90deg); 155 transform-origin: center; 156 } 157 158 .smartdr-progress-ring-circle { 159 stroke: var(--smartdr-counter-color) !important; 160 stroke-width: 4 !important; 161 fill: transparent !important; 162 } 163 164 #smartdr-countdown-number { 165 position: relative; 166 z-index: 2; 167 line-height: 1; 168 display: flex; 169 align-items: center; 170 justify-content: center; 171 width: 100%; 172 height: 100%; 173 color: var(--smartdr-counter-color); 174 font-size: var(--smartdr-counter-size); 175 font-weight: var(--smartdr-counter-font-weight); 176 } 177 178 .smartdr-manual-redirect { 179 margin-top: 1.5em; 180 text-align: center; 181 } 182 183 .smartdr-manual-link { 184 color: var(--smartdr-manual-link-color); 185 font-size: var(--smartdr-manual-link-size); 186 text-decoration: none; 187 transition: color 0.2s ease; 188 display: inline-block; 189 } 190 191 .smartdr-manual-link:hover { 192 color: var(--smartdr-manual-link-hover-color); 193 text-decoration: underline; 164 color: var(--smartdr-heading-color, #333333); 165 font-size: var(--smartdr-heading-size, 32px); 166 font-weight: 600; 167 line-height: 1.4; 168 } 169 170 /* Standard Layout - ensure proper centering */ 171 .smartdr-layout-standard .smartdr-countdown { 172 margin: 0 auto 2em; 194 173 } 195 174 … … 216 195 margin-bottom: 1em; 217 196 text-align: left; 218 font-size: var(--smartdr-heading-size );219 color: var(--smartdr-heading-color );197 font-size: var(--smartdr-heading-size, 32px); 198 color: var(--smartdr-heading-color, #333333); 220 199 } 221 200 … … 230 209 height: 120px; 231 210 margin: 0; 232 display: flex !important;233 align-items: center !important;234 justify-content: center !important;211 display: flex; 212 align-items: center; 213 justify-content: center; 235 214 } 236 215 … … 242 221 } 243 222 244 .smartdr-layout-progressbar .smartdr-progress-text { 223 .smartdr-layout-progressbar .smartdr-progress-text, 224 .smartdr-layout-progressbar #smartdr-countdown-text { 245 225 margin-bottom: 1em; 246 font-size: var(--smartdr-counter-size); 247 color: var(--smartdr-counter-color); 248 font-weight: var(--smartdr-counter-font-weight); 226 font-size: var(--smartdr-counter-size, 36px); 227 color: var(--smartdr-counter-color, #2271b1); 228 font-weight: var(--smartdr-counter-font-weight, 600); 229 display: block; 249 230 } 250 231 … … 255 236 overflow: hidden; 256 237 box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.1); 257 } 258 259 .smartdr-layout-progressbar .smartdr-progress-bar { 238 margin-bottom: 1em; 239 } 240 241 .smartdr-layout-progressbar .smartdr-progress-bar, 242 .smartdr-layout-progressbar #smartdr-progress-bar { 260 243 height: 100%; 261 background: var(--smartdr-counter-color );244 background: var(--smartdr-counter-color, #2271b1); 262 245 border-radius: 10px; 263 246 width: 0%; … … 283 266 } 284 267 285 .smartdr-countdown-number { 286 font-size: calc(var(--smartdr-counter-size, 4em) * 0.8); 268 .smartdr-countdown-number, 269 #smartdr-countdown-number { 270 font-size: calc(var(--smartdr-counter-size, 36px) * 0.8); 287 271 } 288 272 … … 292 276 .smartdr-container h4, 293 277 .smartdr-container h5, 294 .smartdr-container h6 { 295 font-size: calc(var(--smartdr-heading-size, 1.8em) * 0.9); 278 .smartdr-container h6, 279 .smartdr-heading { 280 font-size: calc(var(--smartdr-heading-size, 32px) * 0.9); 296 281 } 297 282 … … 314 299 text-align: center; 315 300 } 301 302 /* Progressbar layout responsive */ 303 .smartdr-layout-progressbar .smartdr-progress-text, 304 .smartdr-layout-progressbar #smartdr-countdown-text { 305 font-size: calc(var(--smartdr-counter-size, 36px) * 0.8); 306 } 316 307 } -
smart-download-redirector/trunk/includes/redirect-functions.php
r3325102 r3325285 11 11 } 12 12 13 /** 14 * Add custom query vars. 15 * 16 * @param array $vars The array of available query variables. 17 * @return array Modified array of query variables. 18 */ 19 function smartdr_query_vars($vars) 20 { 21 $vars[] = 'smartdr_download'; 22 return $vars; 23 } 24 25 /** 26 * Handle template redirect for download URLs. 27 */ 28 function smartdr_template_redirect() 29 { 30 $download_id = get_query_var('smartdr_download'); 31 32 if (! empty($download_id)) { 33 $decoded_url = base64_decode($download_id); 34 35 if (false === $decoded_url) { 36 wp_die(esc_html__('Invalid download URL.', 'smart-download-redirector')); 37 } 38 39 if (! smartdr_validate_redirect_url($decoded_url)) { 40 wp_die(esc_html__('Invalid download URL.', 'smart-download-redirector')); 41 } 42 43 // Check rate limit 44 if (! smartdr_check_rate_limit()) { 45 wp_die(esc_html__('Download rate limit exceeded. Please try again later.', 'smart-download-redirector')); 46 } 47 48 // Set up variables for the template 49 $url = $decoded_url; 50 $countdown_time = absint(get_option('smartdr_countdown_time', 10)); 51 $show_destination = get_option('smartdr_show_destination', true); 52 $destination_display_step = absint(get_option('smartdr_destination_display_step', 1)); 53 $show_manual_link = get_option('smartdr_show_manual_link', true); 54 $manual_link_display_step = absint(get_option('smartdr_manual_link_display_step', 2)); 55 56 // Get and validate redirect delay (1-10 seconds) 57 $redirect_delay = get_option('smartdr_redirect_delay'); 58 59 // Ensure we have a valid numeric value 60 if ($redirect_delay === false || !is_numeric($redirect_delay)) { 61 $redirect_delay = 3; // Default to 3 seconds 62 } 63 64 // Convert to integer and constrain to valid range 65 $redirect_delay = absint($redirect_delay); 66 $redirect_delay = min(max($redirect_delay, 1), 10); 67 68 // Convert to milliseconds for JavaScript 69 $redirect_delay_ms = $redirect_delay * 1000; 70 71 // Enqueue and localize the script with debug info 72 wp_enqueue_script('smartdr-countdown'); 73 wp_localize_script( 74 'smartdr-countdown', 75 'smartdrData', 76 [ 77 'redirectUrl' => $url, 78 'countdownTime' => $countdown_time, 79 'nonce' => wp_create_nonce('smartdr-countdown'), 80 'showDestination' => $show_destination, 81 'destinationStep' => $destination_display_step, 82 'showManualLink' => $show_manual_link, 83 'manualLinkStep' => $manual_link_display_step, 84 'redirectDelay' => $redirect_delay_ms 85 ] 86 ); 87 88 // Load the countdown template 89 include SMARTDR_PLUGIN_DIR . 'templates/download-page.php'; 90 exit; 91 } 92 } 93 94 /** 95 * Handle redirect validation on the download page 96 */ 97 function smartdr_handle_download_redirect() 98 { 99 // Performance optimization: Only run this check when we have relevant parameters 100 // This prevents the function from running on every page load 101 if (empty($_GET['url']) && empty($_GET['smartdr_nonce'])) { 102 return; 103 } 104 105 // Check if we're on the download page or if the URL contains 'download' 106 $request_uri = isset($_SERVER['REQUEST_URI']) ? sanitize_text_field(wp_unslash($_SERVER['REQUEST_URI'])) : ''; 107 if (!is_page('download') && strpos($request_uri, '/download') === false) { 108 return; 109 } 110 111 // Remove error_log for production 112 // error_log('SMARTDR: Starting download redirect handler'); 113 114 // Verify nonce FIRST before processing any input 115 $nonce = isset($_GET['smartdr_nonce']) ? sanitize_key(wp_unslash($_GET['smartdr_nonce'])) : ''; 116 if (empty($nonce) || !wp_verify_nonce($nonce, 'smartdr_download')) { 117 // Remove error_log for production 118 // error_log('SMARTDR: Invalid nonce'); 119 wp_die(esc_html__('Invalid or expired download link.', 'smart-download-redirector')); 120 return; 121 } 122 123 // Now safely get and sanitize the URL parameter after nonce verification 124 $url_param = isset($_GET['url']) ? sanitize_text_field(wp_unslash($_GET['url'])) : ''; 125 126 // Remove error_log for production 127 // error_log('SMARTDR: Processing URL parameter: ' . $url_param); 128 129 // If no URL parameter, show the default page content 130 if (empty($url_param)) { 131 // Remove error_log for production 132 // error_log('SMARTDR: No URL parameter, showing default page'); 133 return; 134 } 135 136 // Decode URL if it's encoded 137 $url = urldecode($url_param); 138 // Remove error_log for production 139 // error_log('SMARTDR: Decoded URL: ' . $url); 140 141 // Normalize URL format 142 $url = smartdr_normalize_url($url); 143 144 // Validate URL 145 if (!smartdr_validate_redirect_url($url)) { 146 // Remove error_log for production 147 // error_log('SMARTDR: URL validation failed for: ' . $url); 148 wp_die(esc_html__('Invalid download URL.', 'smart-download-redirector')); 149 return; 150 } 151 152 // Remove error_log for production 153 // error_log('SMARTDR: URL validation successful for: ' . $url); 154 155 // Create or get the download page 156 $download_page = get_page_by_path('download'); 157 if (!$download_page) { 158 // Create the download page if it doesn't exist 159 $download_page_id = wp_insert_post([ 160 'post_title' => esc_html__('Download', 'smart-download-redirector'), 161 'post_name' => 'download', 162 'post_content' => '[smartdr_download_countdown]', 163 'post_status' => 'publish', 164 'post_type' => 'page', 165 'comment_status' => 'closed', 166 'ping_status' => 'closed' 167 ]); 168 $download_page = get_post($download_page_id); 169 } 170 171 // Ensure the page exists 172 if (!$download_page) { 173 // Remove error_log for production 174 // error_log('SMARTDR: Failed to create or get download page'); 175 return; 176 } 177 178 // Set up the global post object 179 global $wp_query, $post; 180 $wp_query->is_page = true; 181 $wp_query->is_singular = true; 182 $wp_query->is_home = false; 183 $wp_query->is_archive = false; 184 $wp_query->is_category = false; 185 $wp_query->query_vars['url'] = $url_param; 186 $wp_query->query_vars['smartdr_nonce'] = $nonce; 187 188 // Set up the post 189 $post = $download_page; 190 setup_postdata($post); 191 192 // Force the content to contain our shortcode 193 add_filter('the_content', function($content) { 194 return '[smartdr_download_countdown]'; 195 }, 999999); 196 } 197 add_action('template_redirect', 'smartdr_handle_download_redirect', 1); 13 14 15 198 16 199 17 /** … … 281 99 // Check if domain matches 282 100 if ($domain === $target_domain) { 283 // Create redirect URL using query parameters 101 // Create redirect URL using query parameters to the shortcode page 284 102 $redirect_url = add_query_arg([ 285 ' smartdr_url' => base64_encode($url),103 'url' => urlencode($url), 286 104 'smartdr_nonce' => wp_create_nonce('smartdr_download') 287 105 ], get_permalink($shortcode_page)); … … 377 195 { 378 196 if (! smartdr_validate_redirect_url($url)) { 379 // Remove error_log for production380 // error_log('SMARTDR: URL validation failed in generate_download_url for: ' . $url);381 197 return $url; 382 198 } 383 199 384 // Remove error_log for production 385 // error_log('SMARTDR: Generating download URL for: ' . $url); 386 $redirect_url = home_url('/download/'); 387 $redirect_url = add_query_arg('url', urlencode($url), $redirect_url); 388 $redirect_url = wp_nonce_url($redirect_url, 'smartdr_download', 'smartdr_nonce'); 389 390 // Remove error_log for production 391 // error_log('SMARTDR: Generated redirect URL: ' . $redirect_url); 200 // Get the shortcode page 201 $shortcode_page = get_option('smartdr_shortcode_page'); 202 203 // If no shortcode page found, try to find it 204 if (!$shortcode_page) { 205 $pages = get_posts([ 206 'post_type' => 'page', 207 'posts_per_page' => -1, 208 'post_status' => 'publish' 209 ]); 210 211 foreach ($pages as $page) { 212 if (has_shortcode($page->post_content, 'smartdr_download_countdown')) { 213 $shortcode_page = $page->ID; 214 update_option('smartdr_shortcode_page', $shortcode_page); 215 break; 216 } 217 } 218 } 219 220 // If still no shortcode page found, return original URL 221 if (!$shortcode_page) { 222 return $url; 223 } 224 225 // Create redirect URL using query parameters to the shortcode page 226 $redirect_url = add_query_arg([ 227 'url' => urlencode($url), 228 'smartdr_nonce' => wp_create_nonce('smartdr_download') 229 ], get_permalink($shortcode_page)); 230 392 231 return $redirect_url; 393 232 } … … 404 243 } 405 244 406 // Performance optimization: Only process if we have relevant parameters 407 if (empty($_GET['smartdr_url']) && empty($_GET['smartdr_nonce'])) { 408 return ''; 409 } 410 411 // Verify nonce FIRST before processing any input 245 // Check for URL parameters 246 $url_param = isset($_GET['url']) ? sanitize_text_field(wp_unslash($_GET['url'])) : ''; 412 247 $nonce = isset($_GET['smartdr_nonce']) ? sanitize_key(wp_unslash($_GET['smartdr_nonce'])) : ''; 413 if (empty($nonce) || !wp_verify_nonce($nonce, 'smartdr_download')) { 414 return '<div class="smartdr-error">' . esc_html__('Invalid or expired download link.', 'smart-download-redirector') . '</div>'; 415 } 416 417 // Now safely get and sanitize the URL parameter after nonce verification 418 $url_param = isset($_GET['smartdr_url']) ? sanitize_text_field(wp_unslash($_GET['smartdr_url'])) : ''; 419 420 // If no URL parameter, return empty 421 if (empty($url_param)) { 422 return ''; 423 } 424 425 // Decode base64 URL 426 $url = base64_decode($url_param); 427 if ($url === false) { 428 return '<div class="smartdr-error">' . esc_html__('Invalid URL format.', 'smart-download-redirector') . '</div>'; 429 } 430 431 // Normalize URL format 432 $url = smartdr_normalize_url($url); 433 434 if (!smartdr_validate_redirect_url($url)) { 435 return '<div class="smartdr-error">' . esc_html__('Invalid download URL. Please check your target domains settings.', 'smart-download-redirector') . '</div>'; 436 } 437 438 // Get countdown time from settings 248 $url = null; 249 250 if (!empty($url_param) && !empty($nonce)) { 251 // Verify nonce 252 if (wp_verify_nonce($nonce, 'smartdr_download')) { 253 $decoded_url = urldecode($url_param); 254 $decoded_url = smartdr_normalize_url($decoded_url); 255 256 if (smartdr_validate_redirect_url($decoded_url)) { 257 $url = $decoded_url; 258 } 259 } 260 } 261 262 // Get countdown configuration for template 439 263 $countdown_time = absint(get_option('smartdr_countdown_time', 10)); 440 441 // Get and validate redirect delay (1-10 seconds) 442 $redirect_delay = get_option('smartdr_redirect_delay', 3); 443 $redirect_delay = absint($redirect_delay); 264 $redirect_delay = absint(get_option('smartdr_redirect_delay', 3)); 444 265 $redirect_delay = min(max($redirect_delay, 1), 10); 445 $redirect_delay_ms = $redirect_delay * 1000; 446 447 // Enqueue required scripts and styles 448 wp_enqueue_script('smartdr-countdown'); 449 wp_enqueue_style('smartdr-styles'); 450 451 // Localize script data 452 wp_localize_script( 453 'smartdr-countdown', 454 'smartdrData', 455 [ 456 'redirectUrl' => $url, 457 'countdownTime' => $countdown_time, 458 'nonce' => wp_create_nonce('smartdr-countdown'), 459 'showDestination' => get_option('smartdr_show_destination', true), 460 'destinationStep' => absint(get_option('smartdr_destination_display_step', 1)), 461 'showManualLink' => get_option('smartdr_show_manual_link', true), 462 'manualLinkStep' => absint(get_option('smartdr_manual_link_display_step', 2)), 463 'redirectDelay' => $redirect_delay_ms, 464 'ajaxurl' => admin_url('admin-ajax.php') 465 ] 466 ); 266 267 // If still no URL, show placeholder 268 if (!$url) { 269 return '<div class="smartdr-info">' . esc_html__('Download countdown will appear here when accessing a download link.', 'smart-download-redirector') . '</div>'; 270 } 271 272 // We have a valid URL, proceed with countdown 467 273 468 274 // Start output buffering to capture template content … … 486 292 'custom_heading' => get_option('smartdr_custom_heading', esc_html__('Preparing Your Download', 'smart-download-redirector')), 487 293 'show_title' => get_option('smartdr_show_title', true), 488 'manual_link_display_timing' => 'at_end', // Always show at end294 'manual_link_display_timing' => 'at_end', 489 295 'styles' => get_option('smartdr_styles', []) 490 296 ]; … … 498 304 // Get buffered content 499 305 $content = ob_get_clean(); 500 501 // Always use custom style mode502 $style_mode = 'custom';503 306 504 307 // Get custom styles for CSS variables … … 519 322 // Wrap content in theme-independent container 520 323 return sprintf( 521 '<div class="smartdr-container" data-smartdr-version="%s" data-style-mode=" %s"%s>%s</div>',324 '<div class="smartdr-container" data-smartdr-version="%s" data-style-mode="custom"%s>%s</div>', 522 325 esc_attr(SMARTDR_VERSION), 523 esc_attr($style_mode),524 326 $style_attrs, 525 327 $content … … 527 329 } 528 330 add_shortcode('smartdr_download_countdown', 'smartdr_countdown_shortcode'); 331 332 /** 333 * Get WPML translated string or fallback to original 334 * 335 * @param string $name The string name 336 * @param string $original The original string 337 * @return string The translated string or original 338 */ 339 function smartdr_get_wpml_string($name, $original) { 340 // Try newer WPML API first (WPML 4.0+) 341 if (function_exists('wpml_translate_single_string')) { 342 return wpml_translate_single_string('smart-download-redirector', $name, $original); 343 } 344 345 // Fallback to older WPML API (WPML 3.x) 346 if (function_exists('icl_t')) { 347 return icl_t('smart-download-redirector', $name, $original); 348 } 349 350 // Fallback to original string if WPML is not available 351 return $original; 352 } 529 353 530 354 /** -
smart-download-redirector/trunk/readme.txt
r3325102 r3325285 31 31 3. Use the Settings->Download Redirector screen to configure the plugin 32 32 4. Add domains to the target list to start redirecting downloads 33 5. Create a page (e.g., "Download") and add the shortcode `[smartdr_download_countdown]` to display the countdown functionality 34 6. The plugin will automatically redirect matching links to your download page 33 35 34 36 == Frequently Asked Questions == -
smart-download-redirector/trunk/smart-download-redirector.php
r3325102 r3325285 49 49 } 50 50 } 51 52 // Flush rewrite rules.53 flush_rewrite_rules();54 51 } 55 52 register_activation_hook(__FILE__, 'smartdr_activate'); … … 60 57 function smartdr_deactivate() 61 58 { 62 // Flush rewrite rules. 63 flush_rewrite_rules(); 59 // Clean up on deactivation 64 60 } 65 61 register_deactivation_hook(__FILE__, 'smartdr_deactivate'); 66 67 /**68 * Initialize plugin.69 */70 function smartdr_init()71 {72 // Add rewrite rules.73 add_rewrite_rule(74 '^download/([^/]+)/?$',75 'index.php?smartdr_download=$matches[1]',76 'top'77 );78 79 // Add query vars.80 add_filter('query_vars', 'smartdr_query_vars');81 82 // Add template redirect.83 add_action('template_redirect', 'smartdr_template_redirect');84 }85 add_action('init', 'smartdr_init');86 62 87 63 /** … … 93 69 function smartdr_register_assets() 94 70 { 71 // Register countdown script 95 72 wp_register_script( 96 73 'smartdr-countdown', … … 98 75 ['jquery'], 99 76 filemtime(SMARTDR_PLUGIN_DIR . 'assets/countdown.js'), 100 false // Load in header.77 true // Load in footer for better performance 101 78 ); 102 79 … … 110 87 } 111 88 add_action('wp_enqueue_scripts', 'smartdr_register_assets'); 89 90 /** 91 * Check if we need to enqueue countdown assets based on content 92 */ 93 function smartdr_maybe_enqueue_assets() 94 { 95 // Check if we're on a page with the shortcode 96 global $post; 97 $should_enqueue = false; 98 99 if (is_object($post) && has_shortcode($post->post_content, 'smartdr_download_countdown')) { 100 $should_enqueue = true; 101 } 102 103 // Check if we're on a download URL (with URL parameters) 104 if (isset($_GET['url']) && isset($_GET['smartdr_nonce'])) { 105 $should_enqueue = true; 106 } 107 108 if ($should_enqueue) { 109 wp_enqueue_script('smartdr-countdown'); 110 wp_enqueue_style('smartdr-styles'); 111 112 // Prepare localization data 113 $url_param = isset($_GET['url']) ? sanitize_text_field(wp_unslash($_GET['url'])) : ''; 114 $nonce = isset($_GET['smartdr_nonce']) ? sanitize_key(wp_unslash($_GET['smartdr_nonce'])) : ''; 115 $url = null; 116 117 if (!empty($url_param) && !empty($nonce)) { 118 // Verify nonce 119 if (wp_verify_nonce($nonce, 'smartdr_download')) { 120 $decoded_url = urldecode($url_param); 121 $decoded_url = smartdr_normalize_url($decoded_url); 122 123 if (smartdr_validate_redirect_url($decoded_url)) { 124 $url = $decoded_url; 125 } 126 } 127 } 128 129 // Always prepare countdown configuration 130 $countdown_time = absint(get_option('smartdr_countdown_time', 10)); 131 $redirect_delay = absint(get_option('smartdr_redirect_delay', 3)); 132 $redirect_delay = min(max($redirect_delay, 1), 10); 133 $redirect_delay_ms = $redirect_delay * 1000; 134 135 // Localize script with data 136 wp_localize_script( 137 'smartdr-countdown', 138 'smartdrData', 139 [ 140 'redirectUrl' => $url ? $url : '', 141 'countdownTime' => $countdown_time, 142 'nonce' => wp_create_nonce('smartdr-countdown'), 143 'showDestination' => get_option('smartdr_show_destination', true), 144 'destinationStep' => absint(get_option('smartdr_destination_display_step', 1)), 145 'showManualLink' => get_option('smartdr_show_manual_link', true), 146 'manualLinkStep' => absint(get_option('smartdr_manual_link_display_step', 2)), 147 'redirectDelay' => $redirect_delay_ms, 148 'secondsText' => __('seconds remaining', 'smart-download-redirector'), 149 'downloadReadyText' => __('Download ready!', 'smart-download-redirector'), 150 'hasValidUrl' => !empty($url) 151 ] 152 ); 153 154 // Add CSS variables for styling 155 $styles = get_option('smartdr_styles', array()); 156 $default_styles = array( 157 'heading_color' => '#212121', 158 'heading_size' => '32px', 159 'counter_color' => '#00897B', 160 'counter_size' => '36px', 161 'counter_font_weight' => 'bold', 162 'background_color' => '#ffffff', 163 'border_radius' => '8px', 164 'border_size' => '1px', 165 'border_color' => '#dddddd', 166 'manual_link_size' => '14px', 167 'manual_link_color' => '#00897B', 168 'manual_link_hover_color' => '#00695C' 169 ); 170 $styles = array_merge($default_styles, $styles); 171 172 // Add inline CSS with custom properties 173 $inline_css = " 174 /* CSS Custom Properties for dynamic styling */ 175 :root { 176 --smartdr-counter-color: " . esc_attr($styles['counter_color']) . "; 177 --smartdr-counter-size: " . esc_attr($styles['counter_size']) . "; 178 --smartdr-counter-font-weight: " . (($styles['counter_font_weight'] === 'bold') ? '600' : 'normal') . "; 179 --smartdr-heading-color: " . esc_attr($styles['heading_color']) . "; 180 --smartdr-heading-size: " . esc_attr($styles['heading_size']) . "; 181 --smartdr-manual-link-size: " . esc_attr($styles['manual_link_size']) . "; 182 --smartdr-manual-link-color: " . esc_attr($styles['manual_link_color']) . "; 183 --smartdr-manual-link-hover-color: " . esc_attr($styles['manual_link_hover_color']) . "; 184 --smartdr-bg-color: " . esc_attr($styles['background_color']) . "; 185 --smartdr-border-radius: " . esc_attr($styles['border_radius']) . "; 186 --smartdr-border-size: " . esc_attr($styles['border_size']) . "; 187 --smartdr-border-color: " . esc_attr($styles['border_color']) . "; 188 }"; 189 wp_add_inline_style('smartdr-styles', $inline_css); 190 } 191 } 192 add_action('wp_enqueue_scripts', 'smartdr_maybe_enqueue_assets', 20); -
smart-download-redirector/trunk/templates/download-page.php
r3325102 r3325285 17 17 $countdown_time = absint(get_option('smartdr_countdown_time', 10)); 18 18 $show_manual_link = get_option('smartdr_show_manual_link', true); 19 // Manual link always shows at the end of countdown (last 10%)20 19 $show_title = get_option('smartdr_show_title', true); 21 20 $redirect_delay = absint(get_option('smartdr_redirect_delay', 3)); … … 27 26 $manual_link_text = smartdr_get_wpml_string('Manual Link Text', $manual_link_text_original); 28 27 29 // Always use custom style mode30 $style_mode = 'custom';31 $styles = get_option('smartdr_styles', array());32 33 28 // Get layout style setting 34 29 $layout_style = get_option('smartdr_layout_style', 'standard'); 35 36 // For theme mode, try to detect actual theme colors37 $theme_primary_color = '#0073aa'; // WordPress default fallback38 if ($style_mode === 'theme') {39 // Try to get theme colors from various sources40 $theme_colors = wp_get_global_styles(array('color'));41 42 // Check for block theme colors43 if (isset($theme_colors['palette']['theme'])) {44 foreach ($theme_colors['palette']['theme'] as $color) {45 if (isset($color['slug']) && in_array($color['slug'], ['primary', 'accent', 'secondary'])) {46 $theme_primary_color = $color['color'];47 break;48 }49 }50 }51 52 // Fallback: check theme supports and customizer colors53 if ($theme_primary_color === '#0073aa') {54 // Check for customizer primary color55 $customizer_primary = get_theme_mod('primary_color');56 if ($customizer_primary) {57 $theme_primary_color = $customizer_primary;58 } else {59 // Check for accent color60 $accent_color = get_theme_mod('accent_color');61 if ($accent_color) {62 $theme_primary_color = $accent_color;63 }64 }65 }66 }67 68 // Default fallbacks for custom mode69 $default_styles = array(70 'heading_color' => '#212121',71 'heading_size' => '32px',72 'counter_color' => '#00897B',73 'counter_size' => '36px',74 'counter_font_weight' => 'bold',75 'background_color' => '#ffffff',76 'border_radius' => '8px',77 'border_size' => '1px',78 'border_color' => '#dddddd',79 'manual_link_size' => '14px',80 'manual_link_color' => '#00897B',81 'manual_link_hover_color' => '#00695C'82 );83 84 $styles = array_merge($default_styles, $styles);85 86 // Get destination box styles87 $destination_styles = get_option('smartdr_destination_styles', array());88 $default_destination_styles = array(89 'background_color' => '#f8f9fa',90 'border_size' => '1px',91 'border_color' => '#dee2e6',92 'border_radius' => '4px',93 'text_size' => '14px'94 );95 $destination_styles = array_merge($default_destination_styles, $destination_styles);96 97 // Generate inline styles98 $heading_style = sprintf(99 'color: %s; font-size: %s;',100 esc_attr($styles['heading_color']),101 esc_attr($styles['heading_size'])102 );103 104 // Generate destination box style105 $destination_box_style = sprintf(106 'background-color: %s; border: %s solid %s; border-radius: %s; font-size: %s;',107 esc_attr($destination_styles['background_color']),108 esc_attr($destination_styles['border_size']),109 esc_attr($destination_styles['border_color']),110 esc_attr($destination_styles['border_radius']),111 esc_attr($destination_styles['text_size'])112 );113 30 ?> 114 31 … … 180 97 181 98 <?php 182 // Add inline styles using WordPress proper method 183 $inline_css = " 184 /* CSS Custom Properties for dynamic styling */ 185 :root { 186 --smartdr-counter-color: " . esc_attr($styles['counter_color']) . "; 187 --smartdr-counter-size: " . esc_attr($styles['counter_size']) . "; 188 --smartdr-counter-font-weight: " . (($styles['counter_font_weight'] === 'bold') ? '600' : 'normal') . "; 189 --smartdr-heading-color: " . esc_attr($styles['heading_color']) . "; 190 --smartdr-heading-size: " . esc_attr($styles['heading_size']) . "; 191 --smartdr-manual-link-size: " . esc_attr($styles['manual_link_size']) . "; 192 --smartdr-manual-link-color: " . esc_attr($styles['manual_link_color']) . "; 193 --smartdr-manual-link-hover-color: " . esc_attr($styles['manual_link_hover_color']) . "; 194 } 195 196 /* All layout and static styles are now in assets/styles.css */ 197 /* Only CSS Custom Properties remain here for dynamic theming */ 198 "; 199 wp_add_inline_style('smartdr-styles', $inline_css); 99 // CSS Custom Properties are now handled in the main plugin file 100 // to ensure proper timing and availability 200 101 ?> 201 102 202 <?php 203 // Add inline script using WordPress proper method 204 $inline_js = " 205 jQuery(document).ready(function($) { 206 var countdownTime = " . esc_js($countdown_time) . "; 207 var showManualLink = " . ($show_manual_link ? 'true' : 'false') . "; 208 var manualLinkTiming = 'at_end'; // Always show at end 209 var layoutStyle = '" . esc_js($layout_style) . "'; 210 var redirectUrl = '" . esc_js($url) . "'; 211 var downloadReadyText = '" . esc_js(__('Download ready!', 'smart-download-redirector')) . "'; 212 var secondsRemainingText = '" . esc_js(__('seconds remaining', 'smart-download-redirector')) . "'; 213 214 // Get smartdrData from localized script 215 if (typeof smartdrData !== 'undefined') { 216 var redirectDelay = smartdrData.redirectDelay; 217 218 // Debug values 219 console.log('[SMARTDR Debug] Initial settings:', { 220 countdownTime: countdownTime + ' seconds', 221 redirectDelay: (redirectDelay / 1000) + ' seconds', 222 showManualLink: showManualLink, 223 manualLinkTiming: manualLinkTiming, 224 layoutStyle: layoutStyle 225 }); 226 227 var \$manualRedirect = \$('.smartdr-manual-redirect'); 228 var \$countdownNumber = \$('#smartdr-countdown-number'); 229 var \$countdownText = \$('#smartdr-countdown-text'); 230 var \$progressBar = \$('#smartdr-progress-bar'); 231 var circle = document.querySelector('.smartdr-progress-ring-circle'); 232 var isRedirecting = false; 233 234 // Initialize circular progress (for standard and compact layouts) 235 if (circle) { 236 var radius = circle.r.baseVal.value; 237 var circumference = radius * 2 * Math.PI; 238 circle.style.strokeDasharray = circumference + ' ' + circumference; 239 circle.style.strokeDashoffset = circumference; 240 } 241 242 // IMPORTANT: Manual link must NEVER be visible during countdown 243 // Force hide and ensure it stays hidden until countdown is complete 244 \$manualRedirect.hide().css('visibility', 'hidden'); 245 246 // Additional safeguard: prevent any accidental showing during countdown 247 function ensureManualLinkHidden() { 248 if (!countdownComplete && !manualLinkShown) { 249 \$manualRedirect.hide().css('visibility', 'hidden'); 250 } 251 } 252 253 function setProgress(percent) { 254 // Update circular progress (standard and compact layouts) 255 if (circle) { 256 var offset = circumference - (percent / 100 * circumference); 257 circle.style.strokeDashoffset = offset; 258 } 259 260 // Update horizontal progress bar (progressbar layout) 261 if (\$progressBar.length > 0) { 262 \$progressBar.css('width', percent + '%'); 263 } 264 } 265 266 var manualLinkShown = false; // Track if manual link has been shown 267 var countdownComplete = false; // Track if countdown has finished 268 269 function complete() { 270 console.log('[SMARTDR Debug] Countdown complete'); 271 272 // Mark countdown as complete - manual link can now be shown 273 countdownComplete = true; 274 275 // Update progress to 100% 276 setProgress(100); 277 278 // Update countdown displays based on layout 279 if (layoutStyle === 'progressbar') { 280 // For progressbar layout, update text 281 \$countdownText.fadeOut(200, function() { 282 \$(this).text(downloadReadyText).fadeIn(200); 283 284 // Show manual link after text update if enabled (only after countdown is complete) 285 if (showManualLink && !manualLinkShown && countdownComplete) { 286 \$manualRedirect.css('visibility', 'visible').fadeIn(400); 287 manualLinkShown = true; 288 console.log('[SMARTDR Debug] ✅ Manual link shown after countdown completion'); 289 } 290 }); 291 } else { 292 // For standard and compact layouts, show arrow 293 \$countdownNumber.fadeOut(200, function() { 294 \$(this).text('→').fadeIn(200); 295 296 // Show manual link after arrow appears if enabled (only after countdown is complete) 297 if (showManualLink && !manualLinkShown && countdownComplete) { 298 \$manualRedirect.css('visibility', 'visible').fadeIn(400); 299 manualLinkShown = true; 300 console.log('[SMARTDR Debug] ✅ Manual link shown after countdown completion'); 301 } 302 }); 303 } 304 305 // Redirect after configured delay 306 console.log('[SMARTDR Debug] Will redirect in ' + (redirectDelay/1000) + ' seconds'); 307 setTimeout(function() { 308 console.log('[SMARTDR Debug] Redirecting now'); 309 window.location.href = redirectUrl; 310 }, redirectDelay); 311 } 312 313 function countdown() { 314 var timeLeft = countdownTime; 315 316 var timer = setInterval(function() { 317 if (isRedirecting) { 318 clearInterval(timer); 319 return; 320 } 321 322 timeLeft--; 323 var progress = ((countdownTime - timeLeft) / countdownTime) * 100; 324 setProgress(progress); 325 326 // Update display based on layout 327 if (layoutStyle === 'progressbar') { 328 // Update progressbar text with remaining seconds 329 \$countdownText.text(timeLeft + ' ' + secondsRemainingText); 330 } else { 331 // Update countdown number for circular layouts 332 \$countdownNumber.text(timeLeft); 333 } 334 335 // Safeguard: Ensure manual link stays hidden during countdown 336 ensureManualLinkHidden(); 337 338 // Complete countdown when time reaches zero 339 if (timeLeft <= 0) { 340 clearInterval(timer); 341 complete(); 342 } 343 }, 1000); 344 } 345 346 countdown(); 347 } else { 348 console.error('[SMARTDR] smartdrData not found - countdown will not work'); 349 } 350 }); 351 "; 352 wp_add_inline_script('smartdr-countdown', $inline_js); 353 ?> 103
Note: See TracChangeset
for help on using the changeset viewer.