Plugin Directory

Changeset 3465325


Ignore:
Timestamp:
02/19/2026 06:53:25 PM (9 hours ago)
Author:
psakhilsoman
Message:

Release 1.2.1 - cleanup old effects, added review system, improvements

Location:
faecursor
Files:
4 added
6 deleted
10 edited

Legend:

Unmodified
Added
Removed
  • faecursor/trunk/README.txt

    r3455856 r3465325  
    1 === FaeCursor | WordPress Custom Cursor, Keyboard & Screen Effects ===
     1=== FaeCursor – Interaction Effects Toolkit ===
    22Contributors: psakhilsoman, faecursor
    33Author: FaeCursor Plugin Team
    4 Version: 1.2
     4Version: 1.2.1
    55Plugin 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, UI animation
     6Tags: custom cursor, cursor effects, keyboard effects, particle effects, ui animation
    77Requires at least: 5.6
    8 Tested up to: 6.9.1
    9 Stable tag: 1.2
     8Tested up to: 6.9
     9Stable tag: 1.2.1
    1010Requires PHP: 7.4
    1111License: GPLv2 or later
    1212License URI: https://www.gnu.org/licenses/gpl-2.0.html
    1313
    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.
     14Bring your WordPress site to life with interactive cursor, keyboard, and screen effects — built for smooth performance and full control.
    1515
    1616== Description ==
    1717
    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.
     18FaeCursor adds interactive cursor, keyboard, and screen effects to your WordPress website, designed for engaging visual feedback without compromising performance.
    1919
    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.
     20Create smooth trails, sparkles, particles, and subtle motion effects that enhance user interaction while keeping your site lightweight and responsive.
    2721
    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.
     22Unlike effect packs that load everything globally, FaeCursor uses a structured module system so you can enable only what you need.
    3223
    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
     26FaeCursor is built as a controlled interaction system, not a collection of random animations.
     27
     28= Cursor Effects =
     29Add refined visual feedback that follows pointer movement. Includes multiple presets with adjustable size, speed, and color controls.
     30
     31= Keyboard Effects =
     32Display subtle interaction feedback during typing. Ideal for forms, comment sections, and interactive experiences.
     33
     34= Screen Effects =
     35Optional screen-based visual layers such as particle systems and motion backgrounds that enhance atmosphere without interfering with usability.
     36
     37Each 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
     51FaeCursor 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
     60FaeCursor 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
     66Want even more control over your interaction effects? 
     67Explore everything FaeCursor Pro has to offer: [FaeCursor Pro](https://faecursor.com/)
    3568
    3669== Installation ==
     
    4275== Frequently Asked Questions ==
    4376
    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? =
    4678
    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.
     79No. Only enabled modules load their assets. Disabled effects do not enqueue scripts or styles.
     80
     81= Can I disable effects on mobile devices? =
     82
     83Yes. You can disable specific modules on touch devices while keeping others active.
     84
     85= Does it work with page builders? =
     86
     87Yes. FaeCursor works with Elementor, Divi, Bricks, Gutenberg, and most any WordPress themes.
    4988
    5089== Screenshots ==
     
    5695
    5796== 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.
    58103
    59104= 05 Feb 2026 - ver 1.2.0 =
     
    73118== Upgrade Notice ==
    74119
     120= 1.2.1 =
     121Security and code quality improvements. Adds smart review request system. Recommended update for WordPress.org compliance.
     122
    75123= 1.2.0 =
    76124This 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  
    141141}
    142142
     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
    143305.fae-stat-card {
    144306  background: white;
     
    150312}
    151313
     314.fae-stat-card-content {
     315  position: relative;
     316  z-index: 1;
     317}
     318
    152319.fae-stat-card:hover {
    153320  transform: translateY(-5px);
     
    197364  font-weight: 500;
    198365  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  }
    199609}
    200610
  • faecursor/trunk/faecursor.php

    r3454888 r3465325  
    44 */
    55/*
    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
     6Plugin Name: FaeCursor – Interaction Effects Toolkit
     7Description: Bring your WordPress site to life with interactive cursor, keyboard, and screen effects — built for smooth performance and full control.
     8Version: 1.2.1
    99Author: FaeCursor Plugin Team
    1010Author URI: https://faecursor.com
     
    105105// Constants.
    106106define( 'FAE_CURSOR_PLUGIN_NAME', 'faecursor' ); // Updated to match text domain
    107 define( 'FAE_CURSOR_VERSION', '1.2' ); // Update this version as necessary
     107define( 'FAE_CURSOR_VERSION', '1.2.1' ); // Update this version as necessary
    108108
    109109// Load plugin classes
    110110require_once FAE_CURSOR_DIR . '/includes/class-fae-cursor-loader.php';
     111require_once FAE_CURSOR_DIR . '/includes/class-fae-cursor-review.php';
    111112
    112113// Initialize the plugin
    113114Fae_Cursor_Loader::init();
     115
     116// Initialize review system
     117Fae_Cursor_Review::init();
    114118
    115119// Backward compatibility functions
  • faecursor/trunk/includes/class-fae-cursor-admin.php

    r3454888 r3465325  
    8686       
    8787        // 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';
    8989        if (!in_array($preview_bg, array('dark', 'light'))) {
    9090            $preview_bg = 'dark';
     
    245245    public static function handle_preview_page() {
    246246        // Embed preview (for iframe in dashboard)
     247        // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Preview page for admin users only
    247248        if (isset($_GET['fae_embed_preview']) && $_GET['fae_embed_preview'] === '1' && current_user_can('manage_options')) {
    248249            include FAE_CURSOR_DIR . '/includes/views/preview-embed.php';
     
    250251        }
    251252        // Full preview page (legacy, can still be used)
     253        // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Preview page for admin users only
    252254        if (isset($_GET['fae_preview']) && $_GET['fae_preview'] === '1' && current_user_can('manage_options')) {
    253255            include FAE_CURSOR_DIR . '/includes/views/preview-page.php';
     
    261263    public static function ajax_save_preview_settings() {
    262264        // 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')) {
    264266            wp_send_json_error('Invalid nonce');
    265267        }
     
    270272        }
    271273       
    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';
    277279       
    278280        // Get current options and update only appearance settings
     
    303305    public static function ajax_save_cursor_settings() {
    304306        // 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')) {
    306308            wp_send_json_error(array('message' => 'Invalid security token. Please refresh the page and try again.'));
    307309        }
     
    313315       
    314316        // 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();
    316319        $sanitized = Fae_Cursor_Settings::sanitize_options($input);
    317320       
     
    392395    public static function ajax_save_keyboard_settings() {
    393396        // 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')) {
    395398            wp_send_json_error(array('message' => 'Invalid security token. Please refresh the page and try again.'));
    396399        }
     
    402405       
    403406        // 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();
    405409        $sanitized = Fae_Keyboard_Settings::sanitize_options($input);
    406410       
     
    481485    public static function ajax_save_particle_settings() {
    482486        // 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')) {
    484488            wp_send_json_error(array('message' => 'Invalid security token. Please refresh the page and try again.'));
    485489        }
     
    491495       
    492496        // 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();
    494499        $sanitized = Fae_Particle_Settings::sanitize_options($input);
    495500       
  • faecursor/trunk/includes/class-fae-cursor-device.php

    r3454888 r3465325  
    1616     */
    1717    public static function detect() {
     18        // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- User agent is used for device detection only, not output
    1819        $ua = isset($_SERVER['HTTP_USER_AGENT']) ? strtolower(wp_unslash($_SERVER['HTTP_USER_AGENT'])) : '';
    1920
  • faecursor/trunk/includes/class-fae-cursor-enqueue.php

    r3454888 r3465325  
    419419        );
    420420       
    421         // Load seasonal themes (Valentine's Day, Christmas, etc.)
    422         self::enqueue_seasonal_assets();
    423        
    424421        // Enqueue admin scripts
    425422        wp_enqueue_script(
     
    443440            FAE_CURSOR_URL . 'assets/js/fae-cursor-admin.js',
    444441            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'),
    445451            FAE_CURSOR_VERSION,
    446452            true
     
    476482        );
    477483    }
    478    
    479     /**
    480      * Enqueue seasonal theme assets (Valentine's Day, Christmas, etc.)
    481      * Only loads CSS when the seasonal event is active
    482      */
    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_VERSION
    495                 );
    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     }
    505484}
    506 
  • faecursor/trunk/includes/class-fae-cursor-pro.php

    r3454888 r3465325  
    113113    public static function get_upgrade_notice($feature_name = '') {
    114114        $message = !empty($feature_name)
     115            /* translators: %s: Feature name (e.g., "Custom Cursors", "Particle Effects") */
    115116            ? sprintf(__('Get %s with FaeCursor Pro.', 'faecursor'), $feature_name)
    116117            : __('Get advanced features with FaeCursor Pro.', 'faecursor');
  • faecursor/trunk/includes/views/admin-page.php

    r3454888 r3465325  
    1212<div class="wrap fae-cursor-dashboard">
    1313    <!-- 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">
    2115        <div class="fae-header-content">
    2216            <div class="fae-header-main">
     
    2418                    <span class="fae-title-group">
    2519                        <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>
    3321                        </span>
    3422                        <span class="fae-tagline">Move with Magic</span>
     
    114102                    echo 'None';
    115103                } elseif ($active_count === 1) {
    116                     echo $active_effects[0];
     104                    echo esc_html($active_effects[0]);
    117105                } else {
    118106                    // Multiple effects - show count
    119                     echo $active_count . ' Active';
     107                    echo esc_html($active_count) . ' Active';
    120108                }
    121109                ?>
    122110            </p>
    123111            <?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>
    125113            <?php endif; ?>
    126114        </div>
    127115        <div class="fae-stat-card">
    128116            <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>
    130118            <?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>
    132120            <?php endif; ?>
    133121        </div>
    134122    </div>
     123
     124    <!-- Review Request Notification -->
     125    <?php Fae_Cursor_Review::render_notification(); ?>
    135126
    136127    <!-- Main Content -->
     
    222213                                            <span class="fae-effect-name"><?php echo esc_html($config['display_name']); ?></span>
    223214                                            <?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 ?>
    225216                                            <?php endif; ?>
    226217                                        </div>
     
    308299                                <input type="hidden" name="fae_cursor_options[scope_pages]" value="">
    309300                                <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 ?>
    311302                            <?php endif; ?>
    312303                                    </div>
     
    383374                                <input type="hidden" name="fae_cursor_options[user_roles]" value="">
    384375                                <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 ?>
    386377                            <?php endif; ?>
    387378                                </div>
     
    503494                                                            $is_selected = ($selected_flag === $flag_file) ? 'selected' : '';
    504495                                                            $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)) . '">';
    506497                                                            echo '<img src="' . esc_url($flag_url) . '" alt="' . esc_attr($country_name) . '" class="fae-flag-preview-img">';
    507498                                                            echo '</div>';
     
    561552                                        <label>
    562553                                            <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 ?>
    564555                                        </label>
    565556                                        <!-- Hidden input to always force multi_color to 0 (Pro feature protection) -->
     
    594585                                                    $selected_icon_path = FAE_CURSOR_DIR . '/assets/ionicons/' . $selected_icon;
    595586                                                    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
    597588                                                    }
    598589                                                    ?>
     
    613604                                                        $is_selected = ($icon_file === $selected_icon) ? 'selected' : '';
    614605                                                    ?>
    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 ?>
    617608                                                    </div>
    618609                                                    <?php endforeach; ?>
     
    625616                                                        $is_selected = ($icon_file === $selected_icon) ? 'selected' : '';
    626617                                                    ?>
    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 ?>
    629620                                                    </div>
    630621                                                    <?php endforeach; ?>
     
    675666                                            <span class="fae-effect-name"><?php echo esc_html($config['display_name']); ?></span>
    676667                                            <?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 ?>
    678669                                            <?php endif; ?>
    679670                                        </div>
     
    726717                                <input type="hidden" name="fae_keyboard_options[scope_pages]" value="">
    727718                                <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 ?>
    729720                            <?php endif; ?>
    730721                                    </div>
     
    801792                                <input type="hidden" name="fae_keyboard_options[user_roles]" value="">
    802793                                <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 ?>
    804795                            <?php endif; ?>
    805796                                </div>
     
    882873                                        <label>
    883874                                            <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 ?>
    885876                                        </label>
    886877                                        <!-- Hidden input to always force multi_color to 0 (Pro feature protection) -->
     
    937928                                            <span class="fae-effect-name"><?php echo esc_html($config['display_name']); ?></span>
    938929                                            <?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 ?>
    940931                                            <?php endif; ?>
    941932                                        </div>
     
    985976                                <input type="hidden" name="fae_particle_options[scope_type]" value="entire_website">
    986977                                <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 ?>
    988979                            <?php endif; ?>
    989980                                    </div>
     
    10601051                                <input type="hidden" name="fae_particle_options[user_roles]" value="">
    10611052                                <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 ?>
    10631054                            <?php endif; ?>
    10641055                                </div>
     
    11721163        </div>
    11731164    </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>
    11751166</div>
    11761167
  • faecursor/trunk/includes/views/preview-embed.php

    r3454888 r3465325  
    1010
    1111// 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
    2226
    2327// Effect paths
     
    158162    <?php if ($effect_name !== 'none') : ?>
    159163        <?php if (file_exists($css_path)) : ?>
     164        <?php // phpcs:ignore WordPress.WP.EnqueuedResources.NonEnqueuedStylesheet -- Preview embed page with inline styles ?>
    160165        <link rel="stylesheet" href="<?php echo esc_url($css_url); ?>">
    161166        <?php elseif (file_exists($css_path_alt)) : ?>
     167        <?php // phpcs:ignore WordPress.WP.EnqueuedResources.NonEnqueuedStylesheet -- Preview embed page with inline styles ?>
    162168        <link rel="stylesheet" href="<?php echo esc_url($css_url_alt); ?>">
    163169        <?php endif; ?>
     
    223229    </script>
    224230    <?php if (file_exists($js_path)) : ?>
     231    <?php // phpcs:ignore WordPress.WP.EnqueuedResources.NonEnqueuedScript -- Preview embed page with inline scripts ?>
    225232    <script src="<?php echo esc_url($js_url); ?>"></script>
    226233    <?php endif; ?>
  • faecursor/trunk/includes/views/preview-page.php

    r3454888 r3465325  
    99
    1010// 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';
    1315$display_name = ucwords(str_replace('-', ' ', $effect_name));
    1416
    1517// 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
    2024
    2125// Effect paths
     
    332336    </style>
    333337    <?php if (file_exists($css_path)) : ?>
     338    <?php // phpcs:ignore WordPress.WP.EnqueuedResources.NonEnqueuedStylesheet -- Preview page with inline styles ?>
    334339    <link rel="stylesheet" href="<?php echo esc_url($css_url); ?>">
    335340    <?php elseif (file_exists($css_path_alt)) : ?>
     341    <?php // phpcs:ignore WordPress.WP.EnqueuedResources.NonEnqueuedStylesheet -- Preview page with inline styles ?>
    336342    <link rel="stylesheet" href="<?php echo esc_url($css_url_alt); ?>">
    337343    <?php endif; ?>
     
    393399                        $is_selected = ($icon_file === $icon) ? 'selected' : '';
    394400                    ?>
    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 ?>
    397403                    </div>
    398404                    <?php endforeach; ?>
     
    565571    </script>
    566572    <?php if (file_exists($js_path)) : ?>
     573    <?php // phpcs:ignore WordPress.WP.EnqueuedResources.NonEnqueuedScript -- Preview page with inline scripts ?>
    567574    <script src="<?php echo esc_url($js_url); ?>"></script>
    568575    <?php endif; ?>
Note: See TracChangeset for help on using the changeset viewer.