PluginProbe
wpForo Forum / 3.1.4
wpForo Forum v3.1.4
3.1.5 3.1.4 3.1.2 3.1.1 3.1.0 3.0.9 3.0.8 3.0.7 trunk 1.0.0 1.0.1 1.0.2 1.1.0 1.1.1 1.1.2 1.2.0 1.3.0 1.3.1 1.4.0 1.4.1 1.4.10 1.4.11 1.4.12 1.4.13 1.4.2 All 137 releases
wpforo / admin / settings / ai.php

ai.php in wpForo Forum 3.1.4, at admin/settings/ai.php

602 lines 34.3 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php if( ! defined( "ABSPATH" ) ) exit() ?>
2
3 <?php
4 // Enqueue AI features script for user search and other AI settings functionality
5 // (On the dedicated AI Features page this is already done in ai-features.php,
6 // but on the Settings page we need to enqueue it here)
7 if ( ! wp_script_is( 'wpforo-ai-features', 'enqueued' ) ) {
8 wp_enqueue_script( 'wpforo-ai-features', WPFORO_URL . '/admin/assets/js/ai-features.js', [ 'jquery', 'suggest' ], WPFORO_VERSION, true );
9 wp_localize_script( 'wpforo-ai-features', 'wpforoAIAdmin', [
10 'ajaxUrl' => admin_url( 'admin-ajax.php' ),
11 'nonce' => wp_create_nonce( 'wpforo_ai_features_nonce' ),
12 'adminNonce' => wp_create_nonce( 'wpforo_admin_ajax' ),
13 'debugMode' => (bool) wpforo_setting( 'general', 'debug_mode' ),
14 ] );
15 }
16 ?>
17
18 <input type="hidden" name="wpfaction[]" value="ai_settings_save">
19
20 <?php
21 WPF()->settings->header( 'ai' );
22 ?>
23
24 <!-- Collapse/Expand All Buttons -->
25 <div class="wpf-ai-section-controls" style="margin: 5px 0 5px 0; padding: 5px 0 0 0; text-align: right">
26 <button type="button" class="button wpf-ai-collapse-all" style="margin-right: 8px; border: none;">
27 <span class="dashicons dashicons-arrow-up-alt2" style="vertical-align: middle; margin-right: 3px;"></span>
28 <?php _e( 'Collapse All', 'wpforo' ); ?>
29 </button>
30 <button type="button" class="button wpf-ai-expand-all" style="border: none;">
31 <span class="dashicons dashicons-arrow-down-alt2" style="vertical-align: middle; margin-right: 3px;"></span>
32 <?php _e( 'Expand All', 'wpforo' ); ?>
33 </button>
34 </div>
35
36 <!-- AI Assistant Section -->
37 <div class="wpf-ai-collapsible-section">
38 <div class="wpf-subtitle wpf-ai-section-header">
39 <span class="wpf-ai-toggle-icon dashicons dashicons-arrow-down-alt2"></span>
40 <svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="vertical-align: middle; margin-right: 5px;"><path d="M12 3l1.912 5.813a2 2 0 0 0 1.275 1.275L21 12l-5.813 1.912a2 2 0 0 0-1.275 1.275L12 21l-1.912-5.813a2 2 0 0 0-1.275-1.275L3 12l5.813-1.912a2 2 0 0 0 1.275-1.275L12 3z"/></svg> <?php _e( 'AI Assistant', 'wpforo' ) ?>
41 </div>
42 <div class="wpf-ai-section-content">
43 <?php
44 WPF()->settings->form_field( 'ai', 'assistant' );
45 WPF()->settings->form_field( 'ai', 'assistant_open' );
46 WPF()->settings->form_field( 'ai', 'assistant_classic_search' );
47 WPF()->settings->form_field( 'ai', 'assistant_preferences' );
48 WPF()->settings->form_field( 'ai', 'assistant_highlight' );
49 WPF()->settings->form_field( 'ai', 'assistant_icon' );
50 ?>
51 </div>
52 </div>
53
54 <!-- AI Semantic Search Section -->
55 <div class="wpf-ai-collapsible-section">
56 <div class="wpf-subtitle wpf-ai-section-header">
57 <span class="wpf-ai-toggle-icon dashicons dashicons-arrow-down-alt2"></span>
58 <svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="vertical-align: middle; margin-right: 5px;"><circle cx="11" cy="11" r="8"/><path d="m21 21-4.3-4.3"/><path d="M11 8a2 2 0 0 0-2 2"/><path d="M11 8V6"/><path d="M11 8h2"/></svg> <?php _e( 'AI Semantic Search', 'wpforo' ) ?>
59 </div>
60 <div class="wpf-ai-section-content">
61 <?php
62 WPF()->settings->form_field( 'ai', 'search' );
63 WPF()->settings->form_field( 'ai', 'search_quality' );
64 WPF()->settings->form_field( 'ai', 'search_min_score' );
65 WPF()->settings->form_field( 'ai', 'search_enhance' );
66 WPF()->settings->form_field( 'ai', 'search_enhance_quality' );
67 WPF()->settings->form_field( 'ai', 'search_language' );
68 WPF()->settings->form_field( 'ai', 'search_max_results' );
69 ?>
70 </div>
71 </div>
72
73 <!-- AI Translation Section -->
74 <div class="wpf-ai-collapsible-section">
75 <div class="wpf-subtitle wpf-ai-section-header">
76 <span class="wpf-ai-toggle-icon dashicons dashicons-arrow-down-alt2"></span>
77 <svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="vertical-align: middle; margin-right: 5px;"><path d="m5 8 6 6"/><path d="m4 14 6-6 2-3"/><path d="M2 5h12"/><path d="M7 2h1"/><path d="m22 22-5-10-5 10"/><path d="M14 18h6"/></svg> <?php _e( 'AI Translation', 'wpforo' ) ?>
78 </div>
79 <div class="wpf-ai-section-content">
80 <?php
81 WPF()->settings->form_field( 'ai', 'translation' );
82 WPF()->settings->form_field( 'ai', 'translation_quality' );
83 ?>
84 </div>
85 </div>
86
87 <!-- AI Topic Summarization Section -->
88 <div class="wpf-ai-collapsible-section">
89 <div class="wpf-subtitle wpf-ai-section-header">
90 <span class="wpf-ai-toggle-icon dashicons dashicons-arrow-down-alt2"></span>
91 <svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="vertical-align: middle; margin-right: 5px;"><path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"/><polyline points="14 2 14 8 20 8"/><line x1="16" y1="13" x2="8" y2="13"/><line x1="16" y1="17" x2="8" y2="17"/><polyline points="10 9 9 9 8 9"/></svg> <?php _e( 'AI Topic Summarization', 'wpforo' ) ?>
92 </div>
93 <div class="wpf-ai-section-content">
94 <?php
95 WPF()->settings->form_field( 'ai', 'topic_summary' );
96 WPF()->settings->form_field( 'ai', 'topic_summary_quality' );
97 WPF()->settings->form_field( 'ai', 'topic_summary_style' );
98 WPF()->settings->form_field( 'ai', 'topic_summary_min_replies' );
99 WPF()->settings->form_field( 'ai', 'topic_summary_language' );
100 ?>
101 </div>
102 </div>
103
104 <!-- AI Topic Suggestions Section -->
105 <div class="wpf-ai-collapsible-section">
106 <div class="wpf-subtitle wpf-ai-section-header">
107 <span class="wpf-ai-toggle-icon dashicons dashicons-arrow-down-alt2"></span>
108 <svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="vertical-align: middle; margin-right: 5px;"><path d="M12 3l1.912 5.813a2 2 0 0 0 1.275 1.275L21 12l-5.813 1.912a2 2 0 0 0-1.275 1.275L12 21l-1.912-5.813a2 2 0 0 0-1.275-1.275L3 12l5.813-1.912a2 2 0 0 0 1.275-1.275L12 3z"/><path d="M5 3v4"/><path d="M19 17v4"/><path d="M3 5h4"/><path d="M17 19h4"/></svg> <?php _e( 'AI Topic Suggestions', 'wpforo' ) ?>
109 </div>
110 <div class="wpf-ai-section-content">
111 <?php
112 WPF()->settings->form_field( 'ai', 'topic_suggestions' );
113 WPF()->settings->form_field( 'ai', 'topic_suggestions_quality' );
114 WPF()->settings->form_field( 'ai', 'topic_suggestions_min_words' );
115 WPF()->settings->form_field( 'ai', 'topic_suggestions_max_calls' );
116 WPF()->settings->form_field( 'ai', 'topic_suggestions_show_related' );
117 WPF()->settings->form_field( 'ai', 'topic_suggestions_show_answer' );
118 WPF()->settings->form_field( 'ai', 'topic_suggestions_max_similar' );
119 WPF()->settings->form_field( 'ai', 'topic_suggestions_max_related' );
120 WPF()->settings->form_field( 'ai', 'topic_suggestions_similarity' );
121 WPF()->settings->form_field( 'ai', 'topic_suggestions_language' );
122 ?>
123 </div>
124 </div>
125
126 <!-- AI Content Moderation - Spam Detection Section -->
127 <div class="wpf-ai-collapsible-section">
128 <div class="wpf-subtitle wpf-ai-section-header">
129 <span class="wpf-ai-toggle-icon dashicons dashicons-arrow-down-alt2"></span>
130 <svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="vertical-align: middle; margin-right: 5px;"><path d="M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10"/><path d="m9 12 2 2 4-4"/></svg> <?php _e( 'AI Content Moderation - Spam Detection', 'wpforo' ) ?>
131 </div>
132 <div class="wpf-ai-section-content">
133 <?php
134 WPF()->settings->form_field( 'ai', 'moderation_spam' );
135 WPF()->settings->form_field( 'ai', 'moderation_spam_quality' );
136 WPF()->settings->form_field( 'ai', 'moderation_spam_action_detected' );
137 WPF()->settings->form_field( 'ai', 'moderation_spam_action_suspected' );
138 WPF()->settings->form_field( 'ai', 'moderation_spam_action_uncertain' );
139 WPF()->settings->form_field( 'ai', 'moderation_spam_action_clean' );
140 // Only show forum context settings if storage mode is 'cloud' (Lambda can access S3 Vectors)
141 $storage_mode = WPF()->vector_storage ? WPF()->vector_storage->get_storage_mode() : 'local';
142 if ( $storage_mode === 'cloud' ) {
143 WPF()->settings->form_field( 'ai', 'moderation_spam_use_context' );
144 WPF()->settings->form_field( 'ai', 'moderation_spam_min_indexed' );
145 }
146
147 // Custom display for usergroups with "Dashboard - Moderate Topics & Posts" permission
148 $aup_groupids = array_map( 'intval', WPF()->usergroup->get_groupids_by_can( 'aum' ) );
149 $usergroups = WPF()->usergroup->get_usergroups( 'full' );
150 $aup_groups = [];
151 foreach( $usergroups as $group ) {
152 if( in_array( (int) $group['groupid'], $aup_groupids, true ) ) {
153 $aup_groups[] = $group;
154 }
155 }
156 ?>
157 <div class="wpf-opt-row" data-wpf-opt="moderation_spam_exempt_usergroups">
158 <div class="wpf-opt-name">
159 <label><?php _e( 'Usergroups Not Scanned by AI Antispam', 'wpforo' ); ?></label>
160 <p class="wpf-desc"><?php _e( 'Usergroups with the "Dashboard - Moderate Topics & Posts" permission enabled will skip AI spam detection. Edit usergroup permissions to enable or disable this for each group. The "Front - Can pass moderation" permission is only bypass the default "unapproved" status and the post is being scanned by spam filters.', 'wpforo' ); ?></p>
161 </div>
162 <div class="wpf-opt-input">
163 <?php if( ! empty( $aup_groups ) ) : ?>
164 <ul style="margin: 0; padding-left: 0; list-style-type: none; font-size: 14px;">
165 <?php foreach( $aup_groups as $group ) :
166 $edit_url = admin_url( 'admin.php?page=' . wpforo_prefix_slug( 'usergroups' ) . '&groupid=' . intval( $group['groupid'] ) . '&wpfaction=wpforo_usergroup_save_form' );
167 ?>
168 <li style="margin-bottom: 4px;">» <a href="<?php echo esc_url( $edit_url ); ?>"><?php echo esc_html( $group['name'] ); ?></a></li>
169 <?php endforeach; ?>
170 </ul>
171 <?php else : ?>
172 <em style="color: #999;"><?php _e( 'No usergroups have "Dashboard - Moderate Topics & Posts" enabled.', 'wpforo' ); ?></em>
173 <?php endif; ?>
174 </div>
175 <div class="wpf-opt-doc">&nbsp;</div>
176 </div>
177 <?php
178 WPF()->settings->form_field( 'ai', 'moderation_spam_exempt_minposts' );
179 WPF()->settings->form_field( 'ai', 'moderation_spam_autoban_unapproved' );
180 ?>
181 </div>
182 </div>
183
184 <!-- AI Content Moderation - Toxicity Detection Section -->
185 <div class="wpf-ai-collapsible-section">
186 <div class="wpf-subtitle wpf-ai-section-header">
187 <span class="wpf-ai-toggle-icon dashicons dashicons-arrow-down-alt2"></span>
188 <svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="vertical-align: middle; margin-right: 5px;"><path d="M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10"/><path d="M12 8v4"/><path d="M12 16h.01"/></svg> <?php _e( 'AI Content Moderation - Content Safety & Toxicity Detection', 'wpforo' ) ?>
189 </div>
190 <div class="wpf-ai-section-content">
191 <?php
192 WPF()->settings->form_field( 'ai', 'moderation_toxicity' );
193 WPF()->settings->form_field( 'ai', 'moderation_toxicity_sensitivity' );
194 WPF()->settings->form_field( 'ai', 'moderation_toxicity_action' );
195 ?>
196 </div>
197 </div>
198
199 <!-- AI Content Moderation - Rule Compliance Section -->
200 <div class="wpf-ai-collapsible-section">
201 <div class="wpf-subtitle wpf-ai-section-header">
202 <span class="wpf-ai-toggle-icon dashicons dashicons-arrow-down-alt2"></span>
203 <svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="vertical-align: middle; margin-right: 5px;"><path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"/><path d="M14 2v6h6"/><path d="m9 15 2 2 4-4"/></svg> <?php wpforo_phrase( 'AI Content Moderation - Rule Compliance & Policy Enforcement' ) ?>
204 </div>
205 <div class="wpf-ai-section-content">
206 <?php
207 WPF()->settings->form_field( 'ai', 'moderation_compliance' );
208
209 // Get built-in policy and rules status
210 $legal_settings = WPF()->settings->legal;
211 $policy_enabled = ! empty( $legal_settings['checkbox_forum_privacy'] );
212 $policy_text = trim( $legal_settings['forum_privacy_text'] ?? '' );
213 $policy_word_count = $policy_enabled && $policy_text ? str_word_count( strip_tags( $policy_text ) ) : 0;
214
215 $rules_enabled = ! empty( $legal_settings['rules_checkbox'] );
216 $rules_text = trim( $legal_settings['rules_text'] ?? '' );
217 $rules_word_count = $rules_enabled && $rules_text ? str_word_count( strip_tags( $rules_text ) ) : 0;
218
219 // Get all pages for custom policy/rules dropdowns
220 $pages = get_pages( [ 'post_status' => 'publish', 'sort_column' => 'post_title' ] );
221 $current_custom_policy = intval( wpforo_setting( 'ai', 'moderation_compliance_custom_policy' ) );
222 $current_custom_rules = intval( wpforo_setting( 'ai', 'moderation_compliance_custom_rules' ) );
223 ?>
224
225 <!-- Built-in Forum Policy & Rules Status -->
226 <div class="wpf-opt-row" data-wpf-opt="moderation_compliance_builtin_status">
227 <div class="wpf-opt-name">
228 <label><?php wpforo_phrase( 'Built-in Forum Policy & Rules' ); ?></label>
229 <p class="wpf-desc"><?php wpforo_phrase( 'Status of the built-in wpForo forum policy and rules. These are configured in Settings > Legal & Privacy.' ); ?></p>
230 </div>
231 <div class="wpf-opt-input">
232 <table style="border-collapse: collapse; font-size: 14px;">
233 <tr>
234 <td style="padding: 4px 15px 4px 0; color: #666;"><?php wpforo_phrase( 'Forum Privacy Policy' ); ?>:</td>
235 <td style="padding: 4px 0;">
236 <?php if ( $policy_enabled && $policy_word_count > 0 ) : ?>
237 <span style="color: #46b450;">✓ <?php wpforo_phrase( 'Enabled' ); ?></span>
238 <span style="color: #999; margin-left: 8px;">(<?php echo esc_html( sprintf( wpforo_phrase( '%d words', false ), $policy_word_count ) ); ?>)</span>
239 <?php else : ?>
240 <span style="color: #999;">✗ <?php wpforo_phrase( 'Disabled' ); ?></span>
241 <?php endif; ?>
242 </td>
243 </tr>
244 <tr>
245 <td style="padding: 4px 15px 4px 0; color: #666;"><?php wpforo_phrase( 'Forum Rules' ); ?>:</td>
246 <td style="padding: 4px 0;">
247 <?php if ( $rules_enabled && $rules_word_count > 0 ) : ?>
248 <span style="color: #46b450;">✓ <?php wpforo_phrase( 'Enabled' ); ?></span>
249 <span style="color: #999; margin-left: 8px;">(<?php echo esc_html( sprintf( wpforo_phrase( '%d words', false ), $rules_word_count ) ); ?>)</span>
250 <?php else : ?>
251 <span style="color: #999;">✗ <?php wpforo_phrase( 'Disabled' ); ?></span>
252 <?php endif; ?>
253 </td>
254 </tr>
255 </table>
256 <?php if ( ! $policy_enabled && ! $rules_enabled ) : ?>
257 <p style="margin-top: 10px; color: #d63638; font-size: 13px;">
258 <span class="dashicons dashicons-warning" style="font-size: 16px; vertical-align: text-bottom;"></span>
259 <?php wpforo_phrase( 'No built-in policy or rules configured. Enable them in Settings > Legal & Privacy, or select custom pages below.' ); ?>
260 </p>
261 <?php endif; ?>
262 </div>
263 <div class="wpf-opt-doc">&nbsp;</div>
264 </div>
265
266 <!-- Custom Policy Page Dropdown -->
267 <div class="wpf-opt-row wpf-opt-row-select" data-wpf-opt="moderation_compliance_custom_policy">
268 <div class="wpf-opt-name">
269 <label for="ai-moderation_compliance_custom_policy"><?php wpforo_phrase( 'Custom Policy Page' ); ?></label>
270 <p class="wpf-desc"><?php wpforo_phrase( 'Optional: Select a WordPress page containing additional terms or policies. This will be used alongside the built-in forum policy.' ); ?></p>
271 </div>
272 <div class="wpf-opt-input">
273 <div class="wpf-switch-field">
274 <select id="ai-moderation_compliance_custom_policy" name="ai[moderation_compliance_custom_policy]">
275 <option value="0"><?php wpforo_phrase( '— Not Selected —' ); ?></option>
276 <?php foreach ( $pages as $page ) : ?>
277 <option value="<?php echo esc_attr( $page->ID ); ?>" <?php selected( $current_custom_policy, $page->ID ); ?>>
278 <?php echo esc_html( $page->post_title ); ?>
279 </option>
280 <?php endforeach; ?>
281 </select>
282 </div>
283 </div>
284 <div class="wpf-opt-doc">&nbsp;</div>
285 </div>
286
287 <!-- Custom Rules Page Dropdown -->
288 <div class="wpf-opt-row wpf-opt-row-select" data-wpf-opt="moderation_compliance_custom_rules">
289 <div class="wpf-opt-name">
290 <label for="ai-moderation_compliance_custom_rules"><?php wpforo_phrase( 'Custom Rules Page' ); ?></label>
291 <p class="wpf-desc"><?php wpforo_phrase( 'Optional: Select a WordPress page containing additional forum rules. This will be used alongside the built-in forum rules.' ); ?></p>
292 </div>
293 <div class="wpf-opt-input">
294 <div class="wpf-switch-field">
295 <select id="ai-moderation_compliance_custom_rules" name="ai[moderation_compliance_custom_rules]">
296 <option value="0"><?php wpforo_phrase( '— Not Selected —' ); ?></option>
297 <?php foreach ( $pages as $page ) : ?>
298 <option value="<?php echo esc_attr( $page->ID ); ?>" <?php selected( $current_custom_rules, $page->ID ); ?>>
299 <?php echo esc_html( $page->post_title ); ?>
300 </option>
301 <?php endforeach; ?>
302 </select>
303 </div>
304 </div>
305 <div class="wpf-opt-doc">&nbsp;</div>
306 </div>
307
308 <?php WPF()->settings->form_field( 'ai', 'moderation_compliance_action' ); ?>
309 </div>
310 </div>
311
312 <?php
313 // AI Bot Reply requires Professional plan or higher
314 $wpf_ai_bot_reply_available = isset( WPF()->ai_client ) && WPF()->ai_client->is_feature_available( 'ai_bot_reply' );
315 ?>
316
317 <!-- AI Bot Reply Section -->
318 <div class="wpf-ai-collapsible-section">
319 <div class="wpf-subtitle wpf-ai-section-header">
320 <span class="wpf-ai-toggle-icon dashicons dashicons-arrow-down-alt2"></span>
321 <svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" style="vertical-align: middle; margin-right: 5px;"><rect width="18" height="10" x="3" y="11" rx="2"/><circle cx="12" cy="5" r="2"/><path d="M12 7v4"/><path d="M8 16h0"/><path d="M16 16h0"/><path d="m2 2 2 2"/><path d="m22 2-2 2"/></svg> <?php _e( 'AI Bot Reply', 'wpforo' ) ?>
322 <?php if ( ! $wpf_ai_bot_reply_available ) : ?>
323 <span class="wpf-plan-badge wpf-plan-professional" style="margin-left: 8px; font-size: 11px; padding: 2px 8px; background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); color: #fff; border-radius: 10px; font-weight: 500;"><?php _e( 'Professional', 'wpforo' ); ?></span>
324 <?php endif; ?>
325 </div>
326 <div class="wpf-ai-section-content">
327 <?php if ( $wpf_ai_bot_reply_available ) : ?>
328 <?php
329 WPF()->settings->form_field( 'ai', 'bot_reply' );
330 WPF()->settings->form_field( 'ai', 'bot_reply_quality' );
331 WPF()->settings->form_field( 'ai', 'bot_reply_style' );
332 WPF()->settings->form_field( 'ai', 'bot_reply_tone' );
333 WPF()->settings->form_field( 'ai', 'bot_reply_length' );
334 WPF()->settings->form_field( 'ai', 'bot_reply_knowledge_source' );
335 WPF()->settings->form_field( 'ai', 'bot_reply_includes' );
336 WPF()->settings->form_field( 'ai', 'bot_reply_language' );
337 ?>
338
339 <div class="wpf-opt-row wpf-opt-subheader">
340 <div class="wpf-opt-name">
341 <label style="font-weight: 600; color: #1d2327;"><?php _e( 'Bot Settings', 'wpforo' ); ?></label>
342 </div>
343 </div>
344
345 <?php WPF()->settings->form_field( 'ai', 'bot_reply_unapproved' ); ?>
346
347 <!-- Bot Reply User - Custom autocomplete search field -->
348 <div class="wpf-opt-row">
349 <div class="wpf-opt-name">
350 <label for="wpforo-ai-bot-user-search"><?php _e( 'Bot Reply User', 'wpforo' ); ?></label>
351 </div>
352 <div class="wpf-opt-input">
353 <div class="wpforo-ai-user-search-wrapper" style="max-width: 400px;">
354 <?php
355 $bot_user_id = WPF()->settings->ai['bot_reply_user_id'];
356 $bot_user_label = '';
357 if ( $bot_user_id ) {
358 $user = get_userdata( $bot_user_id );
359 if ( $user ) {
360 $role = ! empty( $user->roles ) ? ucfirst( $user->roles[0] ) : '';
361 $bot_user_label = sprintf(
362 '%s (%s)%s',
363 $user->display_name,
364 $user->user_login,
365 $role ? ' - ' . $role : ''
366 );
367 }
368 }
369 ?>
370 <input type="text"
371 id="wpforo-ai-bot-user-search"
372 class="wpforo-ai-user-search"
373 placeholder="<?php esc_attr_e( 'Type to search users...', 'wpforo' ); ?>"
374 value="<?php echo esc_attr( $bot_user_label ); ?>"
375 autocomplete="off" />
376 <input type="hidden"
377 name="ai[bot_reply_user_id]"
378 class="wpforo-ai-user-id-input"
379 value="<?php echo esc_attr( $bot_user_id ); ?>" />
380 <div class="wpforo-ai-user-search-results"></div>
381 </div>
382 <?php wp_nonce_field( 'wpforo_ai_bot_user_search', 'wpforo_ai_bot_user_nonce' ); ?>
383 <p class="wpf-info"><?php _e( 'Select the WordPress user account that will be used for bot-generated replies. Create a dedicated user account for the bot.', 'wpforo' ); ?></p>
384 </div>
385 <div class="wpf-opt-doc">&nbsp;</div>
386 </div>
387
388 <?php
389 WPF()->settings->form_field( 'ai', 'bot_reply_max_per_topic' );
390 WPF()->settings->form_field( 'ai', 'bot_reply_max_per_day' );
391 ?>
392 <?php else : ?>
393 <div class="wpf-opt-row">
394 <div class="wpf-opt-name">
395 <label><?php _e( 'Upgrade Required', 'wpforo' ); ?></label>
396 </div>
397 <div class="wpf-opt-input">
398 <p style="color: #666; margin: 0;">
399 <?php _e( 'AI Bot Reply and Reply Suggestion is available on Professional plan and higher.', 'wpforo' ); ?>
400 <a href="<?php echo esc_url( admin_url( 'admin.php?page=' . wpforo_prefix_slug( 'ai#wpforo-ai-plans' ) ) ); ?>" style="margin-left: 5px;"><?php _e( 'View Plans', 'wpforo' ); ?> →</a>
401 </p>
402 </div>
403 <div class="wpf-opt-doc">&nbsp;</div>
404 </div>
405 <?php endif; ?>
406 </div>
407 </div>
408
409 <?php
410 // AI Chat Assistant requires Business plan or higher
411 $wpf_ai_chatbot_available = isset( WPF()->ai_client ) && WPF()->ai_client->is_feature_available( 'ai_assistant_chatbot' );
412 ?>
413
414 <!-- AI Chat Assistant Section -->
415 <div class="wpf-ai-collapsible-section">
416 <div class="wpf-subtitle wpf-ai-section-header">
417 <span class="wpf-ai-toggle-icon dashicons dashicons-arrow-down-alt2"></span>
418 <svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="vertical-align: middle; margin-right: 5px;"><path d="M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z"/><path d="M8 10h.01"/><path d="M12 10h.01"/><path d="M16 10h.01"/></svg> <?php _e( 'AI Chat Assistant', 'wpforo' ) ?>
419 <?php if ( ! $wpf_ai_chatbot_available ) : ?>
420 <span class="wpf-plan-badge wpf-plan-professional" style="margin-left: 8px; font-size: 11px; padding: 2px 8px; background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); color: #fff; border-radius: 10px; font-weight: 500;"><?php _e( 'Business', 'wpforo' ); ?></span>
421 <?php endif; ?>
422 </div>
423 <div class="wpf-ai-section-content">
424 <?php if ( $wpf_ai_chatbot_available ) : ?>
425 <?php
426 WPF()->settings->form_field( 'ai', 'chatbot' );
427 WPF()->settings->form_field( 'ai', 'chatbot_quality' );
428 WPF()->settings->form_field( 'ai', 'chatbot_welcome_message' );
429 WPF()->settings->form_field( 'ai', 'chatbot_no_content_message' );
430 WPF()->settings->form_field( 'ai', 'chatbot_max_conversations' );
431 WPF()->settings->form_field( 'ai', 'chatbot_context_update_threshold' );
432 WPF()->settings->form_field( 'ai', 'chatbot_use_rag' );
433 WPF()->settings->form_field( 'ai', 'chatbot_use_local_context' );
434 WPF()->settings->form_field( 'ai', 'chatbot_allowed_groups' );
435 WPF()->settings->form_field( 'ai', 'chatbot_language' );
436 WPF()->settings->form_field( 'ai', 'chatbot_min_score' );
437 ?>
438 <?php else : ?>
439 <div class="wpf-opt-row">
440 <div class="wpf-opt-name">
441 <label><?php _e( 'Upgrade Required', 'wpforo' ); ?></label>
442 </div>
443 <div class="wpf-opt-input">
444 <p style="color: #666; margin: 0;">
445 <?php _e( 'AI Chat Assistant is available on Professional plan and higher.', 'wpforo' ); ?>
446 <a href="<?php echo esc_url( admin_url( 'admin.php?page=' . wpforo_prefix_slug( 'ai#wpforo-ai-plans' ) ) ); ?>" style="margin-left: 5px;"><?php _e( 'View Plans', 'wpforo' ); ?> →</a>
447 </p>
448 </div>
449 <div class="wpf-opt-doc">&nbsp;</div>
450 </div>
451 <?php endif; ?>
452 </div>
453 </div>
454
455 <!-- Rate Limiting Section -->
456 <div class="wpf-ai-collapsible-section">
457 <div class="wpf-subtitle wpf-ai-section-header">
458 <span class="wpf-ai-toggle-icon dashicons dashicons-arrow-down-alt2"></span>
459 <svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="vertical-align: middle; margin-right: 5px;"><circle cx="12" cy="12" r="10"/><polyline points="12 6 12 12 16 14"/></svg> <?php _e( 'Rate Limiting', 'wpforo' ) ?>
460 </div>
461 <div class="wpf-ai-section-content">
462 <p class="wpf-info" style="margin: 10px 0 20px 0;">
463 <?php _e( 'Limit how many AI requests guests and logged-in users can make per day. Moderators are exempt from these limits.', 'wpforo' ); ?>
464 </p>
465
466 <?php
467 $rate_limits = wpforo_setting( 'ai', 'rate_limits' );
468 $features = [
469 'search' => __( 'AI Semantic Search', 'wpforo' ),
470 'translation' => __( 'AI Translation', 'wpforo' ),
471 'summarization' => __( 'AI Topic Summarization', 'wpforo' ),
472 'suggestions' => __( 'AI Topic Suggestions', 'wpforo' ),
473 'chatbot' => __( 'AI Chat Assistant', 'wpforo' ),
474 ];
475 ?>
476
477 <table class="wpf-rate-limits-table" style="width: 100%; border-collapse: collapse; margin-bottom: 15px;">
478 <thead>
479 <tr style="background: #f8f9fa;">
480 <th style="text-align: left; padding: 10px; font-weight: 600; border-bottom: 2px solid #e0e0e0;"><?php _e( 'Feature', 'wpforo' ); ?></th>
481 <th style="text-align: center; padding: 10px; font-weight: 600; border-bottom: 2px solid #e0e0e0; width: 150px;"><?php _e( 'Guest Limit/Day', 'wpforo' ); ?></th>
482 <th style="text-align: center; padding: 10px; font-weight: 600; border-bottom: 2px solid #e0e0e0; width: 150px;"><?php _e( 'User Limit/Day', 'wpforo' ); ?></th>
483 </tr>
484 </thead>
485 <tbody>
486 <?php foreach ( $features as $feature_key => $feature_label ) :
487 // Skip chatbot if not available (requires Business+ plan)
488 if ( $feature_key === 'chatbot' && ! $wpf_ai_chatbot_available ) {
489 continue;
490 }
491 $guest_limit = isset( $rate_limits[$feature_key]['guest'] ) ? (int) $rate_limits[$feature_key]['guest'] : 0;
492 $user_limit = isset( $rate_limits[$feature_key]['user'] ) ? (int) $rate_limits[$feature_key]['user'] : 0;
493 $guest_disabled = ( $feature_key === 'chatbot' ); // Chatbot requires login
494 ?>
495 <tr style="border-bottom: 1px solid #e8e8e8;">
496 <td style="padding: 12px 10px;">
497 <strong><?php echo esc_html( $feature_label ); ?></strong>
498 <?php if ( $feature_key === 'chatbot' ) : ?>
499 <span style="color: #999; font-size: 12px; margin-left: 5px;"><?php _e( '(login required)', 'wpforo' ); ?></span>
500 <?php endif; ?>
501 </td>
502 <td style="text-align: center; padding: 10px;">
503 <?php if ( $guest_disabled ) : ?>
504 <span style="color: #999;">—</span>
505 <input type="hidden" name="ai[rate_limits][<?php echo esc_attr( $feature_key ); ?>][guest]" value="0">
506 <?php else : ?>
507 <input type="number" name="ai[rate_limits][<?php echo esc_attr( $feature_key ); ?>][guest]"
508 value="<?php echo esc_attr( $guest_limit ); ?>"
509 min="0" max="1000" step="1"
510 style="width: 80px; text-align: center;">
511 <?php endif; ?>
512 </td>
513 <td style="text-align: center; padding: 10px;">
514 <input type="number" name="ai[rate_limits][<?php echo esc_attr( $feature_key ); ?>][user]"
515 value="<?php echo esc_attr( $user_limit ); ?>"
516 min="0" max="1000" step="1"
517 style="width: 80px; text-align: center;">
518 </td>
519 </tr>
520 <?php endforeach; ?>
521 </tbody>
522 </table>
523
524 <p class="wpf-desc" style="color: #666; margin: 0;">
525 <strong><?php _e( 'Note:', 'wpforo' ); ?></strong>
526 <?php _e( 'Set to 0 to disable access for that user type. Moderators (users with "ms" permission) are always unlimited. Limits reset daily at midnight.', 'wpforo' ); ?>
527 </p>
528 </div>
529 </div>
530
531 <!-- Collapsible Sections CSS & JS -->
532 <style>
533 .wpf-ai-collapsible-section {
534 margin-bottom: 0;
535 }
536 .wpf-ai-section-header {
537 cursor: pointer;
538 user-select: none;
539 position: relative;
540 padding-left: 30px !important;
541 transition: background-color 0.2s ease;
542 }
543 .wpf-ai-section-header:hover {
544 background-color: #f8f9fa;
545 }
546 .wpf-ai-toggle-icon {
547 position: absolute;
548 left: 4px;
549 top: 50%;
550 transform: translateY(-50%);
551 color: #43A6DF;
552 font-size: 18px;
553 transition: transform 0.2s ease;
554 }
555 .wpf-ai-collapsible-section.collapsed .wpf-ai-toggle-icon {
556 transform: translateY(-50%) rotate(-90deg);
557 }
558 .wpf-ai-section-content {
559 overflow: hidden;
560 transition: max-height 0.3s ease-out, opacity 0.2s ease-out;
561 max-height: 5000px;
562 opacity: 1;
563 }
564 .wpf-ai-collapsible-section.collapsed .wpf-ai-section-content {
565 max-height: 0;
566 opacity: 0;
567 padding: 0;
568 }
569 .wpf-ai-section-controls .button {
570 display: inline-flex;
571 align-items: center;
572 }
573 .wpf-ai-section-controls .dashicons {
574 font-size: 16px;
575 width: 16px;
576 height: 16px;
577 }
578 </style>
579
580 <script>
581 jQuery(document).ready(function($) {
582 // Toggle individual section
583 $('.wpf-ai-section-header').on('click', function(e) {
584 // Don't toggle if clicking on a link or badge inside the header
585 if ($(e.target).is('a, .wpf-plan-badge')) {
586 return;
587 }
588 $(this).closest('.wpf-ai-collapsible-section').toggleClass('collapsed');
589 });
590
591 // Collapse All button
592 $('.wpf-ai-collapse-all').on('click', function() {
593 $('.wpf-ai-collapsible-section').addClass('collapsed');
594 });
595
596 // Expand All button
597 $('.wpf-ai-expand-all').on('click', function() {
598 $('.wpf-ai-collapsible-section').removeClass('collapsed');
599 });
600 });
601 </script>
602