Changeset 3465325
- Timestamp:
- 02/19/2026 06:53:25 PM (9 hours ago)
- Location:
- faecursor
- Files:
-
- 4 added
- 6 deleted
- 10 edited
-
assets/screenshot-3.png (added)
-
assets/screenshot-4.png (added)
-
trunk/README.txt (modified) (4 diffs)
-
trunk/assets/css/fae-cursor-admin.css (modified) (3 diffs)
-
trunk/assets/effects/drop-effect (deleted)
-
trunk/assets/effects/duo-circle (deleted)
-
trunk/assets/effects/duo-circle-2 (deleted)
-
trunk/assets/effects/line-effect (deleted)
-
trunk/assets/effects/rise-effect (deleted)
-
trunk/assets/js/fae-cursor-review.js (added)
-
trunk/assets/seasonal/valentines (deleted)
-
trunk/faecursor.php (modified) (2 diffs)
-
trunk/includes/class-fae-cursor-admin.php (modified) (11 diffs)
-
trunk/includes/class-fae-cursor-device.php (modified) (1 diff)
-
trunk/includes/class-fae-cursor-enqueue.php (modified) (3 diffs)
-
trunk/includes/class-fae-cursor-pro.php (modified) (1 diff)
-
trunk/includes/class-fae-cursor-review.php (added)
-
trunk/includes/views/admin-page.php (modified) (19 diffs)
-
trunk/includes/views/preview-embed.php (modified) (3 diffs)
-
trunk/includes/views/preview-page.php (modified) (4 diffs)
Legend:
- Unmodified
- Added
- Removed
-
faecursor/trunk/README.txt
r3455856 r3465325 1 === FaeCursor | WordPress Custom Cursor, Keyboard & Screen Effects===1 === FaeCursor – Interaction Effects Toolkit === 2 2 Contributors: psakhilsoman, faecursor 3 3 Author: FaeCursor Plugin Team 4 Version: 1.2 4 Version: 1.2.1 5 5 Plugin URI: https://faecursor.com/ 6 Tags: wordpress custom cursor, custom cursor wordpress plugin, mouse cursor effects, keyboard effects, screen effects, particle effects, interaction effects, animation effects, UIanimation6 Tags: custom cursor, cursor effects, keyboard effects, particle effects, ui animation 7 7 Requires at least: 5.6 8 Tested up to: 6.9 .19 Stable tag: 1.2 8 Tested up to: 6.9 9 Stable tag: 1.2.1 10 10 Requires PHP: 7.4 11 11 License: GPLv2 or later 12 12 License URI: https://www.gnu.org/licenses/gpl-2.0.html 13 13 14 Bring your WordPress site to life with lightweight **custom cursor, keyboard, and screen effects** — star trails, sparkles, particle animations, and smooth interactive UI effects, fully optimized for performance.14 Bring your WordPress site to life with interactive cursor, keyboard, and screen effects — built for smooth performance and full control. 15 15 16 16 == Description == 17 17 18 FaeCursor is a lightweight **WordPress custom cursor plugin** that adds interactive cursor, keyboard, and screen effects to your website. Create engaging experiences with smooth, responsive animations such as **cursor trails, sparkles, stars, and subtle screen particles**. Perfect for Elementor, Divi, and any WordPress theme, FaeCursor enhances user engagement without slowing your pages.18 FaeCursor adds interactive cursor, keyboard, and screen effects to your WordPress website, designed for engaging visual feedback without compromising performance. 19 19 20 **Features:** 21 - Multiple **cursor effects**, including star trails, sparkles, dual circles, and magic aura. 22 - **Keyboard interaction effects** and shortcut animations for dynamic UI engagement. 23 - **Screen effects and particle animations** triggered by user actions. 24 - Easy-to-use admin panel with **real-time preview** of all effects. 25 - Fully responsive, lightweight, and optimized for **performance**. 26 - Works seamlessly with any WordPress theme, Elementor, and Divi. 20 Create smooth trails, sparkles, particles, and subtle motion effects that enhance user interaction while keeping your site lightweight and responsive. 27 21 28 **Why Choose FaeCursor?** 29 - Boost user engagement with interactive **cursor, keyboard, and screen effects**. 30 - Customize animations easily through an intuitive plugin settings screen. 31 - Lightweight and performance-optimized — won’t slow your site. 22 Unlike effect packs that load everything globally, FaeCursor uses a structured module system so you can enable only what you need. 32 23 33 **Upgrade to FaeCursor Pro** 34 Love the free version? Take it further — get the full experience with [FaeCursor Pro](https://faecursor.com/). 24 == Modular Architecture == 25 26 FaeCursor is built as a controlled interaction system, not a collection of random animations. 27 28 = Cursor Effects = 29 Add refined visual feedback that follows pointer movement. Includes multiple presets with adjustable size, speed, and color controls. 30 31 = Keyboard Effects = 32 Display subtle interaction feedback during typing. Ideal for forms, comment sections, and interactive experiences. 33 34 = Screen Effects = 35 Optional screen-based visual layers such as particle systems and motion backgrounds that enhance atmosphere without interfering with usability. 36 37 Each module can be enabled or disabled independently. 38 39 == Key Features == 40 41 • Independent interaction layers 42 • Conditional asset loading (disabled modules do not load scripts) 43 • Real-time live preview in the admin dashboard 44 • Adjustable performance and animation controls 45 • Mobile and touch device filtering 46 • Compatible with Elementor, Divi, Bricks, Gutenberg, and any WordPress themes 47 • Lightweight and optimized for modern browsers 48 49 == Why FaeCursor? == 50 51 FaeCursor focuses on performance, control, and clean integration: 52 53 • Enable only the effects you need 54 • Avoid unnecessary frontend bloat 55 • Maintain compatibility with modern themes and builders 56 • Keep full control over visual behavior 57 58 == FaeCursor Pro == 59 60 FaeCursor Pro extends the toolkit with advanced control options: 61 62 • Page-level and post-level targeting 63 • Role-based visibility rules 64 • Extended customization controls (size, speed, opacity, triggers) 65 66 Want even more control over your interaction effects? 67 Explore everything FaeCursor Pro has to offer: [FaeCursor Pro](https://faecursor.com/) 35 68 36 69 == Installation == … … 42 75 == Frequently Asked Questions == 43 76 44 = Does FaeCursor work with all themes? = 45 Yes! FaeCursor is fully compatible with any WordPress theme, including Elementor and Divi, without affecting page layout. 77 = Will FaeCursor slow down my site? = 46 78 47 = Will FaeCursor slow down my site? = 48 No, the plugin is lightweight and optimized for performance. Smooth animations will not impact your site speed. 79 No. Only enabled modules load their assets. Disabled effects do not enqueue scripts or styles. 80 81 = Can I disable effects on mobile devices? = 82 83 Yes. You can disable specific modules on touch devices while keeping others active. 84 85 = Does it work with page builders? = 86 87 Yes. FaeCursor works with Elementor, Divi, Bricks, Gutenberg, and most any WordPress themes. 49 88 50 89 == Screenshots == … … 56 95 57 96 == Changelog == 97 98 = 20 Feb 2026 - ver 1.2.1 = 99 * Improved WordPress Coding Standards compliance for better code quality. 100 * Enhanced security with proper input sanitization and nonce verification. 101 * Fixed compatibility issues for WordPress.org submission. 102 * Code optimization and performance improvements. 58 103 59 104 = 05 Feb 2026 - ver 1.2.0 = … … 73 118 == Upgrade Notice == 74 119 120 = 1.2.1 = 121 Security and code quality improvements. Adds smart review request system. Recommended update for WordPress.org compliance. 122 75 123 = 1.2.0 = 76 124 This update adds keyboard and screen interaction effects with a fully improved admin UI for live previews. -
faecursor/trunk/assets/css/fae-cursor-admin.css
r3454888 r3465325 141 141 } 142 142 143 /* Review Request Card */ 144 .fae-review-request-card { 145 background: #fff; 146 border-radius: 12px; 147 padding: 20px 25px; 148 margin-bottom: 30px; 149 box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08); 150 border: 1px solid #e5e7eb; 151 transition: all 0.3s ease; 152 } 153 154 .fae-review-request-card:hover { 155 box-shadow: 0 4px 12px rgba(0, 0, 0, 0.12); 156 border-color: #667eea; 157 } 158 159 .fae-review-content { 160 display: flex; 161 align-items: center; 162 justify-content: space-between; 163 gap: 20px; 164 } 165 166 .fae-review-left { 167 display: flex; 168 align-items: center; 169 gap: 20px; 170 flex: 1; 171 min-width: 0; 172 } 173 174 .fae-review-stars { 175 display: flex; 176 gap: 4px; 177 flex-shrink: 0; 178 } 179 180 .fae-review-stars svg { 181 width: 20px; 182 height: 20px; 183 color: #fbbf24; 184 fill: #fbbf24; 185 filter: drop-shadow(0 1px 2px rgba(251, 191, 36, 0.3)); 186 } 187 188 .fae-review-text { 189 flex: 1; 190 min-width: 0; 191 } 192 193 .fae-review-text h3 { 194 margin: 0 0 4px 0; 195 color: #1f2937; 196 font-size: 1.05em; 197 font-weight: 600; 198 line-height: 1.3; 199 } 200 201 .fae-review-text p { 202 margin: 0; 203 color: #6b7280; 204 font-size: 0.9em; 205 line-height: 1.4; 206 } 207 208 .fae-review-actions { 209 flex-shrink: 0; 210 display: flex; 211 gap: 10px; 212 align-items: center; 213 } 214 215 .fae-review-btn { 216 padding: 10px 20px; 217 border-radius: 6px; 218 font-size: 14px; 219 font-weight: 500; 220 text-decoration: none; 221 transition: all 0.2s ease; 222 cursor: pointer; 223 border: none; 224 display: inline-flex; 225 align-items: center; 226 justify-content: center; 227 gap: 6px; 228 white-space: nowrap; 229 line-height: 1; 230 } 231 232 .fae-review-btn-primary { 233 background: #667eea; 234 color: #fff; 235 padding: 10px 24px; 236 } 237 238 .fae-review-btn-primary:hover { 239 background: #5568d3; 240 transform: translateY(-1px); 241 box-shadow: 0 4px 12px rgba(102, 126, 234, 0.3); 242 } 243 244 .fae-review-btn-dismiss { 245 background: transparent; 246 color: #9ca3af; 247 padding: 8px; 248 border: 1px solid transparent; 249 } 250 251 .fae-review-btn-dismiss svg { 252 width: 18px; 253 height: 18px; 254 } 255 256 .fae-review-btn-dismiss:hover { 257 background: #f3f4f6; 258 color: #6b7280; 259 border-color: #e5e7eb; 260 } 261 262 .fae-review-request-card.fae-review-hidden { 263 display: none; 264 } 265 266 /* Responsive adjustments for review card */ 267 @media (max-width: 900px) { 268 .fae-review-content { 269 flex-wrap: wrap; 270 } 271 272 .fae-review-actions { 273 width: 100%; 274 justify-content: flex-end; 275 } 276 } 277 278 @media (max-width: 600px) { 279 .fae-review-request-card { 280 padding: 18px 20px; 281 } 282 283 .fae-review-left { 284 flex-direction: column; 285 align-items: flex-start; 286 gap: 12px; 287 } 288 289 .fae-review-content { 290 flex-direction: column; 291 align-items: stretch; 292 } 293 294 .fae-review-actions { 295 width: 100%; 296 justify-content: space-between; 297 margin-top: 8px; 298 } 299 300 .fae-review-btn-primary { 301 flex: 1; 302 } 303 } 304 143 305 .fae-stat-card { 144 306 background: white; … … 150 312 } 151 313 314 .fae-stat-card-content { 315 position: relative; 316 z-index: 1; 317 } 318 152 319 .fae-stat-card:hover { 153 320 transform: translateY(-5px); … … 197 364 font-weight: 500; 198 365 line-height: 1.4; 366 } 367 368 /* Review Notification - Slide in from Right */ 369 .fae-review-notification { 370 position: fixed; 371 right: 20px; 372 bottom: 20px; 373 width: 360px; 374 max-width: calc(100vw - 40px); 375 background: #fff; 376 border-radius: 12px; 377 box-shadow: 0 10px 40px rgba(0, 0, 0, 0.15), 0 0 0 1px rgba(0, 0, 0, 0.05); 378 transform: translateX(120%); 379 opacity: 0; 380 transition: transform 0.5s cubic-bezier(0.68, -0.55, 0.265, 1.55), opacity 0.5s ease; 381 z-index: 99999; 382 overflow: hidden; 383 } 384 385 .fae-review-notification.fae-show { 386 transform: translateX(0); 387 opacity: 1; 388 } 389 390 .fae-review-notification-bar { 391 height: 4px; 392 background: linear-gradient(90deg, #667eea 0%, #764ba2 50%, #f093fb 100%); 393 background-size: 200% 100%; 394 animation: fae-gradient-shift 3s ease infinite; 395 } 396 397 @keyframes fae-gradient-shift { 398 0%, 100% { background-position: 0% 50%; } 399 50% { background-position: 100% 50%; } 400 } 401 402 .fae-review-notification-content { 403 padding: 20px; 404 display: flex; 405 gap: 16px; 406 } 407 408 .fae-review-notification-icon { 409 flex-shrink: 0; 410 width: 48px; 411 height: 48px; 412 background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); 413 border-radius: 50%; 414 display: flex; 415 align-items: center; 416 justify-content: center; 417 box-shadow: 0 4px 12px rgba(102, 126, 234, 0.3); 418 } 419 420 .fae-review-notification-icon svg { 421 width: 24px; 422 height: 24px; 423 color: #fbbf24; 424 fill: #fbbf24; 425 filter: drop-shadow(0 2px 4px rgba(0, 0, 0, 0.2)); 426 animation: fae-icon-bounce 2s ease-in-out infinite; 427 } 428 429 @keyframes fae-icon-bounce { 430 0%, 100% { transform: translateY(0); } 431 50% { transform: translateY(-4px); } 432 } 433 434 .fae-review-notification-body { 435 flex: 1; 436 min-width: 0; 437 } 438 439 .fae-review-notification-header { 440 display: flex; 441 align-items: flex-start; 442 justify-content: space-between; 443 gap: 12px; 444 margin-bottom: 6px; 445 } 446 447 .fae-review-notification-header h4 { 448 margin: 0; 449 font-size: 1.05em; 450 font-weight: 600; 451 color: #1f2937; 452 line-height: 1.3; 453 } 454 455 .fae-review-notification-dismiss { 456 flex-shrink: 0; 457 background: transparent; 458 border: none; 459 color: #9ca3af; 460 padding: 2px; 461 cursor: pointer; 462 transition: all 0.2s ease; 463 border-radius: 4px; 464 display: flex; 465 align-items: center; 466 justify-content: center; 467 } 468 469 .fae-review-notification-dismiss svg { 470 width: 18px; 471 height: 18px; 472 } 473 474 .fae-review-notification-dismiss:hover { 475 color: #ef4444; 476 background: #fee2e2; 477 transform: rotate(90deg); 478 } 479 480 .fae-review-notification-text { 481 margin: 0 0 10px 0; 482 font-size: 0.9em; 483 color: #6b7280; 484 line-height: 1.4; 485 } 486 487 .fae-review-notification-stars { 488 display: flex; 489 gap: 4px; 490 margin-bottom: 12px; 491 } 492 493 .fae-review-notification-stars svg { 494 width: 18px; 495 height: 18px; 496 color: #fbbf24; 497 fill: #fbbf24; 498 transition: transform 0.2s ease; 499 cursor: pointer; 500 } 501 502 .fae-review-notification-stars svg:hover { 503 transform: scale(1.2) rotate(-10deg); 504 } 505 506 .fae-review-notification-actions { 507 display: flex; 508 gap: 8px; 509 align-items: stretch; 510 flex-wrap: nowrap; 511 } 512 513 .fae-review-notification-btn { 514 display: inline-flex; 515 align-items: center; 516 justify-content: center; 517 gap: 6px; 518 background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); 519 color: #fff; 520 padding: 10px 14px; 521 border-radius: 8px; 522 font-size: 0.85em; 523 font-weight: 600; 524 text-decoration: none; 525 transition: all 0.3s ease; 526 box-shadow: 0 4px 12px rgba(102, 126, 234, 0.3); 527 white-space: nowrap; 528 flex-shrink: 0; 529 } 530 531 .fae-review-notification-btn:visited { 532 color: #fff; 533 } 534 535 .fae-review-notification-btn:focus { 536 color: #fff; 537 outline: 2px solid #667eea; 538 outline-offset: 2px; 539 } 540 541 .fae-review-notification-btn:active { 542 color: #fff; 543 } 544 545 .fae-review-notification-btn svg { 546 width: 14px; 547 height: 14px; 548 transition: transform 0.3s ease; 549 flex-shrink: 0; 550 } 551 552 .fae-review-notification-btn:hover { 553 background: linear-gradient(135deg, #5568d3 0%, #6941a5 100%); 554 color: #fff; 555 transform: translateY(-2px); 556 box-shadow: 0 6px 20px rgba(102, 126, 234, 0.4); 557 } 558 559 .fae-review-notification-btn:hover svg { 560 transform: translateX(4px); 561 } 562 563 .fae-review-notification-later { 564 display: inline-flex; 565 align-items: center; 566 justify-content: center; 567 background: transparent; 568 color: #6b7280; 569 border: 1px solid #e5e7eb; 570 padding: 10px 14px; 571 border-radius: 8px; 572 font-size: 0.85em; 573 font-weight: 600; 574 cursor: pointer; 575 transition: all 0.2s ease; 576 white-space: nowrap; 577 flex-shrink: 0; 578 } 579 580 .fae-review-notification-later:hover { 581 background: #f3f4f6; 582 color: #374151; 583 border-color: #d1d5db; 584 } 585 586 /* Responsive for mobile */ 587 @media (max-width: 480px) { 588 .fae-review-notification { 589 left: 10px; 590 right: 10px; 591 bottom: 10px; 592 width: auto; 593 max-width: none; 594 } 595 596 .fae-review-notification-content { 597 padding: 16px; 598 } 599 600 .fae-review-notification-icon { 601 width: 40px; 602 height: 40px; 603 } 604 605 .fae-review-notification-icon svg { 606 width: 20px; 607 height: 20px; 608 } 199 609 } 200 610 -
faecursor/trunk/faecursor.php
r3454888 r3465325 4 4 */ 5 5 /* 6 Plugin Name: FaeCursor 7 Description: Lightweight WordPress custom cursor plugin — add cursor, keyboard, and screen effects like trails and sparkles.8 Version: 1.2 6 Plugin Name: FaeCursor – Interaction Effects Toolkit 7 Description: Bring your WordPress site to life with interactive cursor, keyboard, and screen effects — built for smooth performance and full control. 8 Version: 1.2.1 9 9 Author: FaeCursor Plugin Team 10 10 Author URI: https://faecursor.com … … 105 105 // Constants. 106 106 define( 'FAE_CURSOR_PLUGIN_NAME', 'faecursor' ); // Updated to match text domain 107 define( 'FAE_CURSOR_VERSION', '1.2 ' ); // Update this version as necessary107 define( 'FAE_CURSOR_VERSION', '1.2.1' ); // Update this version as necessary 108 108 109 109 // Load plugin classes 110 110 require_once FAE_CURSOR_DIR . '/includes/class-fae-cursor-loader.php'; 111 require_once FAE_CURSOR_DIR . '/includes/class-fae-cursor-review.php'; 111 112 112 113 // Initialize the plugin 113 114 Fae_Cursor_Loader::init(); 115 116 // Initialize review system 117 Fae_Cursor_Review::init(); 114 118 115 119 // Backward compatibility functions -
faecursor/trunk/includes/class-fae-cursor-admin.php
r3454888 r3465325 86 86 87 87 // Get preview background from cookie (set by JavaScript), default to 'dark' 88 $preview_bg = isset($_COOKIE['fae_preview_bg']) ? sanitize_text_field( $_COOKIE['fae_preview_bg']) : 'dark';88 $preview_bg = isset($_COOKIE['fae_preview_bg']) ? sanitize_text_field(wp_unslash($_COOKIE['fae_preview_bg'])) : 'dark'; 89 89 if (!in_array($preview_bg, array('dark', 'light'))) { 90 90 $preview_bg = 'dark'; … … 245 245 public static function handle_preview_page() { 246 246 // Embed preview (for iframe in dashboard) 247 // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Preview page for admin users only 247 248 if (isset($_GET['fae_embed_preview']) && $_GET['fae_embed_preview'] === '1' && current_user_can('manage_options')) { 248 249 include FAE_CURSOR_DIR . '/includes/views/preview-embed.php'; … … 250 251 } 251 252 // Full preview page (legacy, can still be used) 253 // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Preview page for admin users only 252 254 if (isset($_GET['fae_preview']) && $_GET['fae_preview'] === '1' && current_user_can('manage_options')) { 253 255 include FAE_CURSOR_DIR . '/includes/views/preview-page.php'; … … 261 263 public static function ajax_save_preview_settings() { 262 264 // Verify nonce 263 if (!isset($_POST['nonce']) || !wp_verify_nonce(sanitize_text_field( $_POST['nonce']), 'fae_save_preview_settings')) {265 if (!isset($_POST['nonce']) || !wp_verify_nonce(sanitize_text_field(wp_unslash($_POST['nonce'])), 'fae_save_preview_settings')) { 264 266 wp_send_json_error('Invalid nonce'); 265 267 } … … 270 272 } 271 273 272 $type = isset($_POST['type']) ? sanitize_text_field( $_POST['type']) : 'cursor';273 $color = isset($_POST['color']) ? sanitize_hex_color( $_POST['color']) : '#667eea';274 $speed = isset($_POST['speed']) ? sanitize_text_field( $_POST['speed']) : 'normal';275 $size = isset($_POST['size']) ? sanitize_text_field( $_POST['size']) : '1.5rem';276 $icon = isset($_POST['icon']) ? sanitize_file_name( $_POST['icon']) : 'star.svg';274 $type = isset($_POST['type']) ? sanitize_text_field(wp_unslash($_POST['type'])) : 'cursor'; 275 $color = isset($_POST['color']) ? sanitize_hex_color(wp_unslash($_POST['color'])) : '#667eea'; 276 $speed = isset($_POST['speed']) ? sanitize_text_field(wp_unslash($_POST['speed'])) : 'normal'; 277 $size = isset($_POST['size']) ? sanitize_text_field(wp_unslash($_POST['size'])) : '1.5rem'; 278 $icon = isset($_POST['icon']) ? sanitize_file_name(wp_unslash($_POST['icon'])) : 'star.svg'; 277 279 278 280 // Get current options and update only appearance settings … … 303 305 public static function ajax_save_cursor_settings() { 304 306 // Verify nonce 305 if (!isset($_POST['nonce']) || !wp_verify_nonce(sanitize_text_field( $_POST['nonce']), 'fae_cursor')) {307 if (!isset($_POST['nonce']) || !wp_verify_nonce(sanitize_text_field(wp_unslash($_POST['nonce'])), 'fae_cursor')) { 306 308 wp_send_json_error(array('message' => 'Invalid security token. Please refresh the page and try again.')); 307 309 } … … 313 315 314 316 // Get and sanitize all form data 315 $input = isset($_POST['fae_cursor_options']) ? $_POST['fae_cursor_options'] : array(); 317 // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized, WordPress.Security.ValidatedSanitizedInput.MissingUnslash -- Array data sanitized via sanitize_options method 318 $input = isset($_POST['fae_cursor_options']) ? wp_unslash($_POST['fae_cursor_options']) : array(); 316 319 $sanitized = Fae_Cursor_Settings::sanitize_options($input); 317 320 … … 392 395 public static function ajax_save_keyboard_settings() { 393 396 // Verify nonce 394 if (!isset($_POST['nonce']) || !wp_verify_nonce(sanitize_text_field( $_POST['nonce']), 'fae_keyboard')) {397 if (!isset($_POST['nonce']) || !wp_verify_nonce(sanitize_text_field(wp_unslash($_POST['nonce'])), 'fae_keyboard')) { 395 398 wp_send_json_error(array('message' => 'Invalid security token. Please refresh the page and try again.')); 396 399 } … … 402 405 403 406 // Get and sanitize all form data 404 $input = isset($_POST['fae_keyboard_options']) ? $_POST['fae_keyboard_options'] : array(); 407 // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized, WordPress.Security.ValidatedSanitizedInput.MissingUnslash -- Array data sanitized via sanitize_options method 408 $input = isset($_POST['fae_keyboard_options']) ? wp_unslash($_POST['fae_keyboard_options']) : array(); 405 409 $sanitized = Fae_Keyboard_Settings::sanitize_options($input); 406 410 … … 481 485 public static function ajax_save_particle_settings() { 482 486 // Verify nonce 483 if (!isset($_POST['nonce']) || !wp_verify_nonce(sanitize_text_field( $_POST['nonce']), 'fae_particle')) {487 if (!isset($_POST['nonce']) || !wp_verify_nonce(sanitize_text_field(wp_unslash($_POST['nonce'])), 'fae_particle')) { 484 488 wp_send_json_error(array('message' => 'Invalid security token. Please refresh the page and try again.')); 485 489 } … … 491 495 492 496 // Get and sanitize all form data 493 $input = isset($_POST['fae_particle_options']) ? $_POST['fae_particle_options'] : array(); 497 // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized, WordPress.Security.ValidatedSanitizedInput.MissingUnslash -- Array data sanitized via sanitize_options method 498 $input = isset($_POST['fae_particle_options']) ? wp_unslash($_POST['fae_particle_options']) : array(); 494 499 $sanitized = Fae_Particle_Settings::sanitize_options($input); 495 500 -
faecursor/trunk/includes/class-fae-cursor-device.php
r3454888 r3465325 16 16 */ 17 17 public static function detect() { 18 // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- User agent is used for device detection only, not output 18 19 $ua = isset($_SERVER['HTTP_USER_AGENT']) ? strtolower(wp_unslash($_SERVER['HTTP_USER_AGENT'])) : ''; 19 20 -
faecursor/trunk/includes/class-fae-cursor-enqueue.php
r3454888 r3465325 419 419 ); 420 420 421 // Load seasonal themes (Valentine's Day, Christmas, etc.)422 self::enqueue_seasonal_assets();423 424 421 // Enqueue admin scripts 425 422 wp_enqueue_script( … … 443 440 FAE_CURSOR_URL . 'assets/js/fae-cursor-admin.js', 444 441 array('jquery', 'fae-cursor-effects-config', 'fae-particle-effects-config'), 442 FAE_CURSOR_VERSION, 443 true 444 ); 445 446 // Enqueue review notification script 447 wp_enqueue_script( 448 'fae-cursor-review-script', 449 FAE_CURSOR_URL . 'assets/js/fae-cursor-review.js', 450 array('jquery'), 445 451 FAE_CURSOR_VERSION, 446 452 true … … 476 482 ); 477 483 } 478 479 /**480 * Enqueue seasonal theme assets (Valentine's Day, Christmas, etc.)481 * Only loads CSS when the seasonal event is active482 */483 private static function enqueue_seasonal_assets() {484 // Valentine's Day (Feb 7-14)485 $valentines_php = FAE_CURSOR_DIR . '/assets/seasonal/valentines/valentines.php';486 if (file_exists($valentines_php)) {487 require_once $valentines_php;488 489 if (function_exists('fae_is_valentines_week') && fae_is_valentines_week()) {490 wp_enqueue_style(491 'fae-seasonal-valentines',492 FAE_CURSOR_URL . 'assets/seasonal/valentines/valentines.css',493 array('fae-cursor-admin-styles'),494 FAE_CURSOR_VERSION495 );496 }497 }498 499 // Add more seasonal themes here:500 // - Christmas (Dec 20-26)501 // - Halloween (Oct 28-31)502 // - New Year (Dec 31 - Jan 2)503 // etc.504 }505 484 } 506 -
faecursor/trunk/includes/class-fae-cursor-pro.php
r3454888 r3465325 113 113 public static function get_upgrade_notice($feature_name = '') { 114 114 $message = !empty($feature_name) 115 /* translators: %s: Feature name (e.g., "Custom Cursors", "Particle Effects") */ 115 116 ? sprintf(__('Get %s with FaeCursor Pro.', 'faecursor'), $feature_name) 116 117 : __('Get advanced features with FaeCursor Pro.', 'faecursor'); -
faecursor/trunk/includes/views/admin-page.php
r3454888 r3465325 12 12 <div class="wrap fae-cursor-dashboard"> 13 13 <!-- Header Section --> 14 <div class="fae-dashboard-header<?php echo function_exists('fae_get_valentines_header_classes') ? fae_get_valentines_header_classes() : ''; ?>"> 15 <?php 16 // Render seasonal elements (Valentine's, Christmas, etc.) 17 if (function_exists('fae_render_valentines_elements')) { 18 fae_render_valentines_elements(); 19 } 20 ?> 14 <div class="fae-dashboard-header"> 21 15 <div class="fae-header-content"> 22 16 <div class="fae-header-main"> … … 24 18 <span class="fae-title-group"> 25 19 <span class="fae-title-name"> 26 FaeCursor <span class="fae-version-badge">v1.2</span> 27 <?php 28 // Render seasonal badge (Valentine's, Christmas, etc.) 29 if (function_exists('fae_render_valentines_badge')) { 30 fae_render_valentines_badge(); 31 } 32 ?> 20 FaeCursor <span class="fae-version-badge">v1.2.1</span> 33 21 </span> 34 22 <span class="fae-tagline">Move with Magic</span> … … 114 102 echo 'None'; 115 103 } elseif ($active_count === 1) { 116 echo $active_effects[0];104 echo esc_html($active_effects[0]); 117 105 } else { 118 106 // Multiple effects - show count 119 echo $active_count. ' Active';107 echo esc_html($active_count) . ' Active'; 120 108 } 121 109 ?> 122 110 </p> 123 111 <?php if ($active_count > 1) : ?> 124 <p class="fae-stat-detail"><?php echo implode(', ', $active_effects); ?></p>112 <p class="fae-stat-detail"><?php echo esc_html(implode(', ', $active_effects)); ?></p> 125 113 <?php endif; ?> 126 114 </div> 127 115 <div class="fae-stat-card"> 128 116 <h3>Effects Available</h3> 129 <p class="fae-stat-value"><?php echo $total_effects; ?></p>117 <p class="fae-stat-value"><?php echo esc_html($total_effects); ?></p> 130 118 <?php if ($pro_count > 0) : ?> 131 <p class="fae-stat-detail"><?php echo $free_count; ?> Free<?php echo $pro_count > 0 ? ' + ' . $pro_count. ' Pro' : ''; ?></p>119 <p class="fae-stat-detail"><?php echo esc_html($free_count); ?> Free<?php echo $pro_count > 0 ? ' + ' . esc_html($pro_count) . ' Pro' : ''; ?></p> 132 120 <?php endif; ?> 133 121 </div> 134 122 </div> 123 124 <!-- Review Request Notification --> 125 <?php Fae_Cursor_Review::render_notification(); ?> 135 126 136 127 <!-- Main Content --> … … 222 213 <span class="fae-effect-name"><?php echo esc_html($config['display_name']); ?></span> 223 214 <?php if ($is_pro_effect) : ?> 224 <?php echo Fae_Cursor_Pro::get_pro_badge(); ?>215 <?php echo Fae_Cursor_Pro::get_pro_badge(); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?> 225 216 <?php endif; ?> 226 217 </div> … … 308 299 <input type="hidden" name="fae_cursor_options[scope_pages]" value=""> 309 300 <input type="hidden" name="fae_cursor_options[scope_css_selector]" value=""> 310 <?php echo Fae_Cursor_Pro::get_upgrade_notice('Advanced scoping options require Pro.'); ?>301 <?php echo Fae_Cursor_Pro::get_upgrade_notice('Advanced scoping options require Pro.'); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?> 311 302 <?php endif; ?> 312 303 </div> … … 383 374 <input type="hidden" name="fae_cursor_options[user_roles]" value=""> 384 375 <input type="hidden" name="fae_cursor_options[include_logged_out]" value="0"> 385 <?php echo Fae_Cursor_Pro::get_upgrade_notice('User role restrictions require Pro.'); ?>376 <?php echo Fae_Cursor_Pro::get_upgrade_notice('User role restrictions require Pro.'); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?> 386 377 <?php endif; ?> 387 378 </div> … … 503 494 $is_selected = ($selected_flag === $flag_file) ? 'selected' : ''; 504 495 $flag_url = FAE_CURSOR_URL . 'assets/flags/' . $flag_file; 505 echo '<div class="fae-flag-dropdown-item ' . $is_selected. '" data-flag="' . esc_attr($flag_file) . '" data-name="' . esc_attr($flag_name) . '" data-country-name="' . esc_attr(strtolower($country_name)) . '">';496 echo '<div class="fae-flag-dropdown-item ' . esc_attr($is_selected) . '" data-flag="' . esc_attr($flag_file) . '" data-name="' . esc_attr($flag_name) . '" data-country-name="' . esc_attr(strtolower($country_name)) . '">'; 506 497 echo '<img src="' . esc_url($flag_url) . '" alt="' . esc_attr($country_name) . '" class="fae-flag-preview-img">'; 507 498 echo '</div>'; … … 561 552 <label> 562 553 <input type="checkbox" name="fae_cursor_options[multi_color]" value="1" disabled id="fae-cursor-multi-color"> 563 Multi-Color <?php echo Fae_Cursor_Pro::get_pro_badge(); ?>554 Multi-Color <?php echo Fae_Cursor_Pro::get_pro_badge(); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?> 564 555 </label> 565 556 <!-- Hidden input to always force multi_color to 0 (Pro feature protection) --> … … 594 585 $selected_icon_path = FAE_CURSOR_DIR . '/assets/ionicons/' . $selected_icon; 595 586 if (file_exists($selected_icon_path)) { 596 echo file_get_contents($selected_icon_path); 587 echo file_get_contents($selected_icon_path); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- Local SVG file content 597 588 } 598 589 ?> … … 613 604 $is_selected = ($icon_file === $selected_icon) ? 'selected' : ''; 614 605 ?> 615 <div class="fae-icon-dropdown-item <?php echo $is_selected; ?>" data-icon="<?php echo esc_attr($icon_file); ?>">616 <?php echo file_get_contents($icon_path); ?>606 <div class="fae-icon-dropdown-item <?php echo esc_attr($is_selected); ?>" data-icon="<?php echo esc_attr($icon_file); ?>"> 607 <?php echo file_get_contents($icon_path); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped, WordPress.WP.AlternativeFunctions.file_get_contents_file_get_contents ?> 617 608 </div> 618 609 <?php endforeach; ?> … … 625 616 $is_selected = ($icon_file === $selected_icon) ? 'selected' : ''; 626 617 ?> 627 <div class="fae-icon-dropdown-item <?php echo $is_selected; ?>" data-icon="<?php echo esc_attr($icon_file); ?>">628 <?php echo file_get_contents($icon_path); ?>618 <div class="fae-icon-dropdown-item <?php echo esc_attr($is_selected); ?>" data-icon="<?php echo esc_attr($icon_file); ?>"> 619 <?php echo file_get_contents($icon_path); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped, WordPress.WP.AlternativeFunctions.file_get_contents_file_get_contents ?> 629 620 </div> 630 621 <?php endforeach; ?> … … 675 666 <span class="fae-effect-name"><?php echo esc_html($config['display_name']); ?></span> 676 667 <?php if ($is_pro_effect) : ?> 677 <?php echo Fae_Cursor_Pro::get_pro_badge(); ?>668 <?php echo Fae_Cursor_Pro::get_pro_badge(); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?> 678 669 <?php endif; ?> 679 670 </div> … … 726 717 <input type="hidden" name="fae_keyboard_options[scope_pages]" value=""> 727 718 <input type="hidden" name="fae_keyboard_options[scope_css_selector]" value=""> 728 <?php echo Fae_Cursor_Pro::get_upgrade_notice('Advanced scoping options require Pro.'); ?>719 <?php echo Fae_Cursor_Pro::get_upgrade_notice('Advanced scoping options require Pro.'); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?> 729 720 <?php endif; ?> 730 721 </div> … … 801 792 <input type="hidden" name="fae_keyboard_options[user_roles]" value=""> 802 793 <input type="hidden" name="fae_keyboard_options[include_logged_out]" value="0"> 803 <?php echo Fae_Cursor_Pro::get_upgrade_notice('User role restrictions require Pro.'); ?>794 <?php echo Fae_Cursor_Pro::get_upgrade_notice('User role restrictions require Pro.'); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?> 804 795 <?php endif; ?> 805 796 </div> … … 882 873 <label> 883 874 <input type="checkbox" name="fae_keyboard_options[multi_color]" value="1" disabled id="fae-keyboard-multi-color"> 884 Multi-Color <?php echo Fae_Cursor_Pro::get_pro_badge(); ?>875 Multi-Color <?php echo Fae_Cursor_Pro::get_pro_badge(); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?> 885 876 </label> 886 877 <!-- Hidden input to always force multi_color to 0 (Pro feature protection) --> … … 937 928 <span class="fae-effect-name"><?php echo esc_html($config['display_name']); ?></span> 938 929 <?php if ($is_pro_effect) : ?> 939 <?php echo Fae_Cursor_Pro::get_pro_badge(); ?>930 <?php echo Fae_Cursor_Pro::get_pro_badge(); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?> 940 931 <?php endif; ?> 941 932 </div> … … 985 976 <input type="hidden" name="fae_particle_options[scope_type]" value="entire_website"> 986 977 <input type="hidden" name="fae_particle_options[scope_pages]" value=""> 987 <?php echo Fae_Cursor_Pro::get_upgrade_notice('Advanced scoping options require Pro.'); ?>978 <?php echo Fae_Cursor_Pro::get_upgrade_notice('Advanced scoping options require Pro.'); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?> 988 979 <?php endif; ?> 989 980 </div> … … 1060 1051 <input type="hidden" name="fae_particle_options[user_roles]" value=""> 1061 1052 <input type="hidden" name="fae_particle_options[include_logged_out]" value="0"> 1062 <?php echo Fae_Cursor_Pro::get_upgrade_notice('User role restrictions require Pro.'); ?>1053 <?php echo Fae_Cursor_Pro::get_upgrade_notice('User role restrictions require Pro.'); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?> 1063 1054 <?php endif; ?> 1064 1055 </div> … … 1172 1163 </div> 1173 1164 </div> 1174 <p class="fae-dashboard-footer-version"><?php echo esc_html(__('FaeCursor v1.2 ', 'faecursor')); ?></p>1165 <p class="fae-dashboard-footer-version"><?php echo esc_html(__('FaeCursor v1.2.1', 'faecursor')); ?></p> 1175 1166 </div> 1176 1167 -
faecursor/trunk/includes/views/preview-embed.php
r3454888 r3465325 10 10 11 11 // Get parameters 12 $effect_type = isset($_GET['type']) ? sanitize_text_field($_GET['type']) : 'cursor'; 13 $effect_name = isset($_GET['effect']) ? sanitize_text_field($_GET['effect']) : 'none'; 14 $color = isset($_GET['color']) ? sanitize_hex_color($_GET['color']) : '#667eea'; 15 $speed = isset($_GET['speed']) ? sanitize_text_field($_GET['speed']) : 'normal'; 16 $size = isset($_GET['size']) ? sanitize_text_field($_GET['size']) : '1.5rem'; 17 $icon = isset($_GET['icon']) ? sanitize_file_name($_GET['icon']) : 'star.svg'; 18 $flag = isset($_GET['flag']) ? sanitize_file_name($_GET['flag']) : ''; 19 $flag_position = isset($_GET['flag_position']) ? sanitize_text_field($_GET['flag_position']) : 'center'; 20 $multi_color = isset($_GET['multi_color']) ? sanitize_text_field($_GET['multi_color']) : '0'; 21 $bg = isset($_GET['bg']) ? sanitize_text_field($_GET['bg']) : 'dark'; 12 // phpcs:disable WordPress.Security.NonceVerification.Recommended -- Preview embed for admin users only, checked in handle_preview_page 13 // phpcs:disable WordPress.Security.ValidatedSanitizedInput.MissingUnslash -- Simple GET parameters unslashed below 14 $effect_type = isset($_GET['type']) ? sanitize_text_field(wp_unslash($_GET['type'])) : 'cursor'; 15 $effect_name = isset($_GET['effect']) ? sanitize_text_field(wp_unslash($_GET['effect'])) : 'none'; 16 $color = isset($_GET['color']) ? sanitize_hex_color(wp_unslash($_GET['color'])) : '#667eea'; 17 $speed = isset($_GET['speed']) ? sanitize_text_field(wp_unslash($_GET['speed'])) : 'normal'; 18 $size = isset($_GET['size']) ? sanitize_text_field(wp_unslash($_GET['size'])) : '1.5rem'; 19 $icon = isset($_GET['icon']) ? sanitize_file_name(wp_unslash($_GET['icon'])) : 'star.svg'; 20 $flag = isset($_GET['flag']) ? sanitize_file_name(wp_unslash($_GET['flag'])) : ''; 21 $flag_position = isset($_GET['flag_position']) ? sanitize_text_field(wp_unslash($_GET['flag_position'])) : 'center'; 22 $multi_color = isset($_GET['multi_color']) ? sanitize_text_field(wp_unslash($_GET['multi_color'])) : '0'; 23 $bg = isset($_GET['bg']) ? sanitize_text_field(wp_unslash($_GET['bg'])) : 'dark'; 24 // phpcs:enable WordPress.Security.NonceVerification.Recommended 25 // phpcs:enable WordPress.Security.ValidatedSanitizedInput.MissingUnslash 22 26 23 27 // Effect paths … … 158 162 <?php if ($effect_name !== 'none') : ?> 159 163 <?php if (file_exists($css_path)) : ?> 164 <?php // phpcs:ignore WordPress.WP.EnqueuedResources.NonEnqueuedStylesheet -- Preview embed page with inline styles ?> 160 165 <link rel="stylesheet" href="<?php echo esc_url($css_url); ?>"> 161 166 <?php elseif (file_exists($css_path_alt)) : ?> 167 <?php // phpcs:ignore WordPress.WP.EnqueuedResources.NonEnqueuedStylesheet -- Preview embed page with inline styles ?> 162 168 <link rel="stylesheet" href="<?php echo esc_url($css_url_alt); ?>"> 163 169 <?php endif; ?> … … 223 229 </script> 224 230 <?php if (file_exists($js_path)) : ?> 231 <?php // phpcs:ignore WordPress.WP.EnqueuedResources.NonEnqueuedScript -- Preview embed page with inline scripts ?> 225 232 <script src="<?php echo esc_url($js_url); ?>"></script> 226 233 <?php endif; ?> -
faecursor/trunk/includes/views/preview-page.php
r3454888 r3465325 9 9 10 10 // Get parameters 11 $effect_type = isset($_GET['type']) ? sanitize_text_field($_GET['type']) : 'cursor'; 12 $effect_name = isset($_GET['effect']) ? sanitize_text_field($_GET['effect']) : 'none'; 11 // phpcs:disable WordPress.Security.NonceVerification.Recommended -- Preview page for admin users only, checked in handle_preview_page 12 // phpcs:disable WordPress.Security.ValidatedSanitizedInput.MissingUnslash -- Simple GET parameters unslashed below 13 $effect_type = isset($_GET['type']) ? sanitize_text_field(wp_unslash($_GET['type'])) : 'cursor'; 14 $effect_name = isset($_GET['effect']) ? sanitize_text_field(wp_unslash($_GET['effect'])) : 'none'; 13 15 $display_name = ucwords(str_replace('-', ' ', $effect_name)); 14 16 15 17 // Check for URL overrides (for live preview) 16 $url_color = isset($_GET['color']) ? sanitize_hex_color($_GET['color']) : null; 17 $url_speed = isset($_GET['speed']) ? sanitize_text_field($_GET['speed']) : null; 18 $url_size = isset($_GET['size']) ? sanitize_text_field($_GET['size']) : null; 19 $url_icon = isset($_GET['icon']) ? sanitize_file_name($_GET['icon']) : null; 18 $url_color = isset($_GET['color']) ? sanitize_hex_color(wp_unslash($_GET['color'])) : null; 19 $url_speed = isset($_GET['speed']) ? sanitize_text_field(wp_unslash($_GET['speed'])) : null; 20 $url_size = isset($_GET['size']) ? sanitize_text_field(wp_unslash($_GET['size'])) : null; 21 $url_icon = isset($_GET['icon']) ? sanitize_file_name(wp_unslash($_GET['icon'])) : null; 22 // phpcs:enable WordPress.Security.NonceVerification.Recommended 23 // phpcs:enable WordPress.Security.ValidatedSanitizedInput.MissingUnslash 20 24 21 25 // Effect paths … … 332 336 </style> 333 337 <?php if (file_exists($css_path)) : ?> 338 <?php // phpcs:ignore WordPress.WP.EnqueuedResources.NonEnqueuedStylesheet -- Preview page with inline styles ?> 334 339 <link rel="stylesheet" href="<?php echo esc_url($css_url); ?>"> 335 340 <?php elseif (file_exists($css_path_alt)) : ?> 341 <?php // phpcs:ignore WordPress.WP.EnqueuedResources.NonEnqueuedStylesheet -- Preview page with inline styles ?> 336 342 <link rel="stylesheet" href="<?php echo esc_url($css_url_alt); ?>"> 337 343 <?php endif; ?> … … 393 399 $is_selected = ($icon_file === $icon) ? 'selected' : ''; 394 400 ?> 395 <div class="fae-icon-item <?php echo $is_selected; ?>" data-icon="<?php echo esc_attr($icon_file); ?>">396 <?php echo file_get_contents($icon_path); ?>401 <div class="fae-icon-item <?php echo esc_attr($is_selected); ?>" data-icon="<?php echo esc_attr($icon_file); ?>"> 402 <?php echo file_get_contents($icon_path); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- Local SVG file content ?> 397 403 </div> 398 404 <?php endforeach; ?> … … 565 571 </script> 566 572 <?php if (file_exists($js_path)) : ?> 573 <?php // phpcs:ignore WordPress.WP.EnqueuedResources.NonEnqueuedScript -- Preview page with inline scripts ?> 567 574 <script src="<?php echo esc_url($js_url); ?>"></script> 568 575 <?php endif; ?>
Note: See TracChangeset
for help on using the changeset viewer.