PluginProbe
wpForo Forum / 3.0.2
wpForo Forum v3.0.2
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.0.2, at admin/settings/ai.php

600 lines 34.2 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_enhance' );
65 WPF()->settings->form_field( 'ai', 'search_enhance_quality' );
66 WPF()->settings->form_field( 'ai', 'search_language' );
67 WPF()->settings->form_field( 'ai', 'search_max_results' );
68 ?>
69 </div>
70 </div>
71
72 <!-- AI Translation Section -->
73 <div class="wpf-ai-collapsible-section">
74 <div class="wpf-subtitle wpf-ai-section-header">
75 <span class="wpf-ai-toggle-icon dashicons dashicons-arrow-down-alt2"></span>
76 <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' ) ?>
77 </div>
78 <div class="wpf-ai-section-content">
79 <?php
80 WPF()->settings->form_field( 'ai', 'translation' );
81 WPF()->settings->form_field( 'ai', 'translation_quality' );
82 ?>
83 </div>
84 </div>
85
86 <!-- AI Topic Summarization Section -->
87 <div class="wpf-ai-collapsible-section">
88 <div class="wpf-subtitle wpf-ai-section-header">
89 <span class="wpf-ai-toggle-icon dashicons dashicons-arrow-down-alt2"></span>
90 <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' ) ?>
91 </div>
92 <div class="wpf-ai-section-content">
93 <?php
94 WPF()->settings->form_field( 'ai', 'topic_summary' );
95 WPF()->settings->form_field( 'ai', 'topic_summary_quality' );
96 WPF()->settings->form_field( 'ai', 'topic_summary_style' );
97 WPF()->settings->form_field( 'ai', 'topic_summary_min_replies' );
98 WPF()->settings->form_field( 'ai', 'topic_summary_language' );
99 ?>
100 </div>
101 </div>
102
103 <!-- AI Topic Suggestions Section -->
104 <div class="wpf-ai-collapsible-section">
105 <div class="wpf-subtitle wpf-ai-section-header">
106 <span class="wpf-ai-toggle-icon dashicons dashicons-arrow-down-alt2"></span>
107 <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' ) ?>
108 </div>
109 <div class="wpf-ai-section-content">
110 <?php
111 WPF()->settings->form_field( 'ai', 'topic_suggestions' );
112 WPF()->settings->form_field( 'ai', 'topic_suggestions_quality' );
113 WPF()->settings->form_field( 'ai', 'topic_suggestions_min_words' );
114 WPF()->settings->form_field( 'ai', 'topic_suggestions_max_calls' );
115 WPF()->settings->form_field( 'ai', 'topic_suggestions_show_related' );
116 WPF()->settings->form_field( 'ai', 'topic_suggestions_show_answer' );
117 WPF()->settings->form_field( 'ai', 'topic_suggestions_max_similar' );
118 WPF()->settings->form_field( 'ai', 'topic_suggestions_max_related' );
119 WPF()->settings->form_field( 'ai', 'topic_suggestions_similarity' );
120 WPF()->settings->form_field( 'ai', 'topic_suggestions_language' );
121 ?>
122 </div>
123 </div>
124
125 <!-- AI Content Moderation - Spam Detection Section -->
126 <div class="wpf-ai-collapsible-section">
127 <div class="wpf-subtitle wpf-ai-section-header">
128 <span class="wpf-ai-toggle-icon dashicons dashicons-arrow-down-alt2"></span>
129 <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' ) ?>
130 </div>
131 <div class="wpf-ai-section-content">
132 <?php
133 WPF()->settings->form_field( 'ai', 'moderation_spam' );
134 WPF()->settings->form_field( 'ai', 'moderation_spam_quality' );
135 WPF()->settings->form_field( 'ai', 'moderation_spam_action_detected' );
136 WPF()->settings->form_field( 'ai', 'moderation_spam_action_suspected' );
137 WPF()->settings->form_field( 'ai', 'moderation_spam_action_uncertain' );
138 WPF()->settings->form_field( 'ai', 'moderation_spam_action_clean' );
139 // Only show forum context settings if storage mode is 'cloud' (Lambda can access S3 Vectors)
140 $storage_mode = WPF()->vector_storage ? WPF()->vector_storage->get_storage_mode() : 'local';
141 if ( $storage_mode === 'cloud' ) {
142 WPF()->settings->form_field( 'ai', 'moderation_spam_use_context' );
143 WPF()->settings->form_field( 'ai', 'moderation_spam_min_indexed' );
144 }
145
146 // Custom display for usergroups with "Dashboard - Moderate Topics & Posts" permission
147 $aup_groupids = array_map( 'intval', WPF()->usergroup->get_groupids_by_can( 'aum' ) );
148 $usergroups = WPF()->usergroup->get_usergroups( 'full' );
149 $aup_groups = [];
150 foreach( $usergroups as $group ) {
151 if( in_array( (int) $group['groupid'], $aup_groupids, true ) ) {
152 $aup_groups[] = $group;
153 }
154 }
155 ?>
156 <div class="wpf-opt-row" data-wpf-opt="moderation_spam_exempt_usergroups">
157 <div class="wpf-opt-name">
158 <label><?php _e( 'Usergroups Not Scanned by AI Antispam', 'wpforo' ); ?></label>
159 <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>
160 </div>
161 <div class="wpf-opt-input">
162 <?php if( ! empty( $aup_groups ) ) : ?>
163 <ul style="margin: 0; padding-left: 0; list-style-type: none; font-size: 14px;">
164 <?php foreach( $aup_groups as $group ) :
165 $edit_url = admin_url( 'admin.php?page=' . wpforo_prefix_slug( 'usergroups' ) . '&groupid=' . intval( $group['groupid'] ) . '&wpfaction=wpforo_usergroup_save_form' );
166 ?>
167 <li style="margin-bottom: 4px;">» <a href="<?php echo esc_url( $edit_url ); ?>"><?php echo esc_html( $group['name'] ); ?></a></li>
168 <?php endforeach; ?>
169 </ul>
170 <?php else : ?>
171 <em style="color: #999;"><?php _e( 'No usergroups have "Dashboard - Moderate Topics & Posts" enabled.', 'wpforo' ); ?></em>
172 <?php endif; ?>
173 </div>
174 <div class="wpf-opt-doc">&nbsp;</div>
175 </div>
176 <?php
177 WPF()->settings->form_field( 'ai', 'moderation_spam_exempt_minposts' );
178 WPF()->settings->form_field( 'ai', 'moderation_spam_autoban_unapproved' );
179 ?>
180 </div>
181 </div>
182
183 <!-- AI Content Moderation - Toxicity Detection Section -->
184 <div class="wpf-ai-collapsible-section">
185 <div class="wpf-subtitle wpf-ai-section-header">
186 <span class="wpf-ai-toggle-icon dashicons dashicons-arrow-down-alt2"></span>
187 <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' ) ?>
188 </div>
189 <div class="wpf-ai-section-content">
190 <?php
191 WPF()->settings->form_field( 'ai', 'moderation_toxicity' );
192 WPF()->settings->form_field( 'ai', 'moderation_toxicity_sensitivity' );
193 WPF()->settings->form_field( 'ai', 'moderation_toxicity_action' );
194 ?>
195 </div>
196 </div>
197
198 <!-- AI Content Moderation - Rule Compliance Section -->
199 <div class="wpf-ai-collapsible-section">
200 <div class="wpf-subtitle wpf-ai-section-header">
201 <span class="wpf-ai-toggle-icon dashicons dashicons-arrow-down-alt2"></span>
202 <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' ) ?>
203 </div>
204 <div class="wpf-ai-section-content">
205 <?php
206 WPF()->settings->form_field( 'ai', 'moderation_compliance' );
207
208 // Get built-in policy and rules status
209 $legal_settings = WPF()->settings->legal;
210 $policy_enabled = ! empty( $legal_settings['checkbox_forum_privacy'] );
211 $policy_text = trim( $legal_settings['forum_privacy_text'] ?? '' );
212 $policy_word_count = $policy_enabled && $policy_text ? str_word_count( strip_tags( $policy_text ) ) : 0;
213
214 $rules_enabled = ! empty( $legal_settings['rules_checkbox'] );
215 $rules_text = trim( $legal_settings['rules_text'] ?? '' );
216 $rules_word_count = $rules_enabled && $rules_text ? str_word_count( strip_tags( $rules_text ) ) : 0;
217
218 // Get all pages for custom policy/rules dropdowns
219 $pages = get_pages( [ 'post_status' => 'publish', 'sort_column' => 'post_title' ] );
220 $current_custom_policy = intval( wpforo_setting( 'ai', 'moderation_compliance_custom_policy' ) );
221 $current_custom_rules = intval( wpforo_setting( 'ai', 'moderation_compliance_custom_rules' ) );
222 ?>
223
224 <!-- Built-in Forum Policy & Rules Status -->
225 <div class="wpf-opt-row" data-wpf-opt="moderation_compliance_builtin_status">
226 <div class="wpf-opt-name">
227 <label><?php wpforo_phrase( 'Built-in Forum Policy & Rules' ); ?></label>
228 <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>
229 </div>
230 <div class="wpf-opt-input">
231 <table style="border-collapse: collapse; font-size: 14px;">
232 <tr>
233 <td style="padding: 4px 15px 4px 0; color: #666;"><?php wpforo_phrase( 'Forum Privacy Policy' ); ?>:</td>
234 <td style="padding: 4px 0;">
235 <?php if ( $policy_enabled && $policy_word_count > 0 ) : ?>
236 <span style="color: #46b450;">✓ <?php wpforo_phrase( 'Enabled' ); ?></span>
237 <span style="color: #999; margin-left: 8px;">(<?php echo esc_html( sprintf( wpforo_phrase( '%d words', false ), $policy_word_count ) ); ?>)</span>
238 <?php else : ?>
239 <span style="color: #999;">✗ <?php wpforo_phrase( 'Disabled' ); ?></span>
240 <?php endif; ?>
241 </td>
242 </tr>
243 <tr>
244 <td style="padding: 4px 15px 4px 0; color: #666;"><?php wpforo_phrase( 'Forum Rules' ); ?>:</td>
245 <td style="padding: 4px 0;">
246 <?php if ( $rules_enabled && $rules_word_count > 0 ) : ?>
247 <span style="color: #46b450;">✓ <?php wpforo_phrase( 'Enabled' ); ?></span>
248 <span style="color: #999; margin-left: 8px;">(<?php echo esc_html( sprintf( wpforo_phrase( '%d words', false ), $rules_word_count ) ); ?>)</span>
249 <?php else : ?>
250 <span style="color: #999;">✗ <?php wpforo_phrase( 'Disabled' ); ?></span>
251 <?php endif; ?>
252 </td>
253 </tr>
254 </table>
255 <?php if ( ! $policy_enabled && ! $rules_enabled ) : ?>
256 <p style="margin-top: 10px; color: #d63638; font-size: 13px;">
257 <span class="dashicons dashicons-warning" style="font-size: 16px; vertical-align: text-bottom;"></span>
258 <?php wpforo_phrase( 'No built-in policy or rules configured. Enable them in Settings > Legal & Privacy, or select custom pages below.' ); ?>
259 </p>
260 <?php endif; ?>
261 </div>
262 <div class="wpf-opt-doc">&nbsp;</div>
263 </div>
264
265 <!-- Custom Policy Page Dropdown -->
266 <div class="wpf-opt-row wpf-opt-row-select" data-wpf-opt="moderation_compliance_custom_policy">
267 <div class="wpf-opt-name">
268 <label for="ai-moderation_compliance_custom_policy"><?php wpforo_phrase( 'Custom Policy Page' ); ?></label>
269 <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>
270 </div>
271 <div class="wpf-opt-input">
272 <div class="wpf-switch-field">
273 <select id="ai-moderation_compliance_custom_policy" name="ai[moderation_compliance_custom_policy]">
274 <option value="0"><?php wpforo_phrase( '— Not Selected —' ); ?></option>
275 <?php foreach ( $pages as $page ) : ?>
276 <option value="<?php echo esc_attr( $page->ID ); ?>" <?php selected( $current_custom_policy, $page->ID ); ?>>
277 <?php echo esc_html( $page->post_title ); ?>
278 </option>
279 <?php endforeach; ?>
280 </select>
281 </div>
282 </div>
283 <div class="wpf-opt-doc">&nbsp;</div>
284 </div>
285
286 <!-- Custom Rules Page Dropdown -->
287 <div class="wpf-opt-row wpf-opt-row-select" data-wpf-opt="moderation_compliance_custom_rules">
288 <div class="wpf-opt-name">
289 <label for="ai-moderation_compliance_custom_rules"><?php wpforo_phrase( 'Custom Rules Page' ); ?></label>
290 <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>
291 </div>
292 <div class="wpf-opt-input">
293 <div class="wpf-switch-field">
294 <select id="ai-moderation_compliance_custom_rules" name="ai[moderation_compliance_custom_rules]">
295 <option value="0"><?php wpforo_phrase( '— Not Selected —' ); ?></option>
296 <?php foreach ( $pages as $page ) : ?>
297 <option value="<?php echo esc_attr( $page->ID ); ?>" <?php selected( $current_custom_rules, $page->ID ); ?>>
298 <?php echo esc_html( $page->post_title ); ?>
299 </option>
300 <?php endforeach; ?>
301 </select>
302 </div>
303 </div>
304 <div class="wpf-opt-doc">&nbsp;</div>
305 </div>
306
307 <?php WPF()->settings->form_field( 'ai', 'moderation_compliance_action' ); ?>
308 </div>
309 </div>
310
311 <?php
312 // AI Bot Reply requires Professional plan or higher
313 $wpf_ai_bot_reply_available = isset( WPF()->ai_client ) && WPF()->ai_client->is_feature_available( 'ai_bot_reply' );
314 ?>
315
316 <!-- AI Bot Reply Section -->
317 <div class="wpf-ai-collapsible-section">
318 <div class="wpf-subtitle wpf-ai-section-header">
319 <span class="wpf-ai-toggle-icon dashicons dashicons-arrow-down-alt2"></span>
320 <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' ) ?>
321 <?php if ( ! $wpf_ai_bot_reply_available ) : ?>
322 <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>
323 <?php endif; ?>
324 </div>
325 <div class="wpf-ai-section-content">
326 <?php if ( $wpf_ai_bot_reply_available ) : ?>
327 <?php
328 WPF()->settings->form_field( 'ai', 'bot_reply' );
329 WPF()->settings->form_field( 'ai', 'bot_reply_quality' );
330 WPF()->settings->form_field( 'ai', 'bot_reply_style' );
331 WPF()->settings->form_field( 'ai', 'bot_reply_tone' );
332 WPF()->settings->form_field( 'ai', 'bot_reply_length' );
333 WPF()->settings->form_field( 'ai', 'bot_reply_knowledge_source' );
334 WPF()->settings->form_field( 'ai', 'bot_reply_includes' );
335 WPF()->settings->form_field( 'ai', 'bot_reply_language' );
336 ?>
337
338 <div class="wpf-opt-row wpf-opt-subheader">
339 <div class="wpf-opt-name">
340 <label style="font-weight: 600; color: #1d2327;"><?php _e( 'Bot Settings', 'wpforo' ); ?></label>
341 </div>
342 </div>
343
344 <?php WPF()->settings->form_field( 'ai', 'bot_reply_unapproved' ); ?>
345
346 <!-- Bot Reply User - Custom autocomplete search field -->
347 <div class="wpf-opt-row">
348 <div class="wpf-opt-name">
349 <label for="wpforo-ai-bot-user-search"><?php _e( 'Bot Reply User', 'wpforo' ); ?></label>
350 </div>
351 <div class="wpf-opt-input">
352 <div class="wpforo-ai-user-search-wrapper" style="max-width: 400px;">
353 <?php
354 $bot_user_id = WPF()->settings->ai['bot_reply_user_id'];
355 $bot_user_label = '';
356 if ( $bot_user_id ) {
357 $user = get_userdata( $bot_user_id );
358 if ( $user ) {
359 $role = ! empty( $user->roles ) ? ucfirst( $user->roles[0] ) : '';
360 $bot_user_label = sprintf(
361 '%s (%s)%s',
362 $user->display_name,
363 $user->user_login,
364 $role ? ' - ' . $role : ''
365 );
366 }
367 }
368 ?>
369 <input type="text"
370 id="wpforo-ai-bot-user-search"
371 class="wpforo-ai-user-search"
372 placeholder="<?php esc_attr_e( 'Type to search users...', 'wpforo' ); ?>"
373 value="<?php echo esc_attr( $bot_user_label ); ?>"
374 autocomplete="off" />
375 <input type="hidden"
376 name="ai[bot_reply_user_id]"
377 class="wpforo-ai-user-id-input"
378 value="<?php echo esc_attr( $bot_user_id ); ?>" />
379 <div class="wpforo-ai-user-search-results"></div>
380 </div>
381 <?php wp_nonce_field( 'wpforo_ai_bot_user_search', 'wpforo_ai_bot_user_nonce' ); ?>
382 <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>
383 </div>
384 <div class="wpf-opt-doc">&nbsp;</div>
385 </div>
386
387 <?php
388 WPF()->settings->form_field( 'ai', 'bot_reply_max_per_topic' );
389 WPF()->settings->form_field( 'ai', 'bot_reply_max_per_day' );
390 ?>
391 <?php else : ?>
392 <div class="wpf-opt-row">
393 <div class="wpf-opt-name">
394 <label><?php _e( 'Upgrade Required', 'wpforo' ); ?></label>
395 </div>
396 <div class="wpf-opt-input">
397 <p style="color: #666; margin: 0;">
398 <?php _e( 'AI Bot Reply and Reply Suggestion is available on Professional plan and higher.', 'wpforo' ); ?>
399 <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>
400 </p>
401 </div>
402 <div class="wpf-opt-doc">&nbsp;</div>
403 </div>
404 <?php endif; ?>
405 </div>
406 </div>
407
408 <?php
409 // AI Chat Assistant requires Business plan or higher
410 $wpf_ai_chatbot_available = isset( WPF()->ai_client ) && WPF()->ai_client->is_feature_available( 'ai_assistant_chatbot' );
411 ?>
412
413 <!-- AI Chat Assistant Section -->
414 <div class="wpf-ai-collapsible-section">
415 <div class="wpf-subtitle wpf-ai-section-header">
416 <span class="wpf-ai-toggle-icon dashicons dashicons-arrow-down-alt2"></span>
417 <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' ) ?>
418 <?php if ( ! $wpf_ai_chatbot_available ) : ?>
419 <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>
420 <?php endif; ?>
421 </div>
422 <div class="wpf-ai-section-content">
423 <?php if ( $wpf_ai_chatbot_available ) : ?>
424 <?php
425 WPF()->settings->form_field( 'ai', 'chatbot' );
426 WPF()->settings->form_field( 'ai', 'chatbot_quality' );
427 WPF()->settings->form_field( 'ai', 'chatbot_welcome_message' );
428 WPF()->settings->form_field( 'ai', 'chatbot_no_content_message' );
429 WPF()->settings->form_field( 'ai', 'chatbot_max_conversations' );
430 WPF()->settings->form_field( 'ai', 'chatbot_context_update_threshold' );
431 WPF()->settings->form_field( 'ai', 'chatbot_use_rag' );
432 WPF()->settings->form_field( 'ai', 'chatbot_use_local_context' );
433 WPF()->settings->form_field( 'ai', 'chatbot_allowed_groups' );
434 WPF()->settings->form_field( 'ai', 'chatbot_language' );
435 ?>
436 <?php else : ?>
437 <div class="wpf-opt-row">
438 <div class="wpf-opt-name">
439 <label><?php _e( 'Upgrade Required', 'wpforo' ); ?></label>
440 </div>
441 <div class="wpf-opt-input">
442 <p style="color: #666; margin: 0;">
443 <?php _e( 'AI Chat Assistant is available on Professional plan and higher.', 'wpforo' ); ?>
444 <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>
445 </p>
446 </div>
447 <div class="wpf-opt-doc">&nbsp;</div>
448 </div>
449 <?php endif; ?>
450 </div>
451 </div>
452
453 <!-- Rate Limiting Section -->
454 <div class="wpf-ai-collapsible-section">
455 <div class="wpf-subtitle wpf-ai-section-header">
456 <span class="wpf-ai-toggle-icon dashicons dashicons-arrow-down-alt2"></span>
457 <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' ) ?>
458 </div>
459 <div class="wpf-ai-section-content">
460 <p class="wpf-info" style="margin: 10px 0 20px 0;">
461 <?php _e( 'Limit how many AI requests guests and logged-in users can make per day. Moderators are exempt from these limits.', 'wpforo' ); ?>
462 </p>
463
464 <?php
465 $rate_limits = wpforo_setting( 'ai', 'rate_limits' );
466 $features = [
467 'search' => __( 'AI Semantic Search', 'wpforo' ),
468 'translation' => __( 'AI Translation', 'wpforo' ),
469 'summarization' => __( 'AI Topic Summarization', 'wpforo' ),
470 'suggestions' => __( 'AI Topic Suggestions', 'wpforo' ),
471 'chatbot' => __( 'AI Chat Assistant', 'wpforo' ),
472 ];
473 ?>
474
475 <table class="wpf-rate-limits-table" style="width: 100%; border-collapse: collapse; margin-bottom: 15px;">
476 <thead>
477 <tr style="background: #f8f9fa;">
478 <th style="text-align: left; padding: 10px; font-weight: 600; border-bottom: 2px solid #e0e0e0;"><?php _e( 'Feature', 'wpforo' ); ?></th>
479 <th style="text-align: center; padding: 10px; font-weight: 600; border-bottom: 2px solid #e0e0e0; width: 150px;"><?php _e( 'Guest Limit/Day', 'wpforo' ); ?></th>
480 <th style="text-align: center; padding: 10px; font-weight: 600; border-bottom: 2px solid #e0e0e0; width: 150px;"><?php _e( 'User Limit/Day', 'wpforo' ); ?></th>
481 </tr>
482 </thead>
483 <tbody>
484 <?php foreach ( $features as $feature_key => $feature_label ) :
485 // Skip chatbot if not available (requires Business+ plan)
486 if ( $feature_key === 'chatbot' && ! $wpf_ai_chatbot_available ) {
487 continue;
488 }
489 $guest_limit = isset( $rate_limits[$feature_key]['guest'] ) ? (int) $rate_limits[$feature_key]['guest'] : 0;
490 $user_limit = isset( $rate_limits[$feature_key]['user'] ) ? (int) $rate_limits[$feature_key]['user'] : 0;
491 $guest_disabled = ( $feature_key === 'chatbot' ); // Chatbot requires login
492 ?>
493 <tr style="border-bottom: 1px solid #e8e8e8;">
494 <td style="padding: 12px 10px;">
495 <strong><?php echo esc_html( $feature_label ); ?></strong>
496 <?php if ( $feature_key === 'chatbot' ) : ?>
497 <span style="color: #999; font-size: 12px; margin-left: 5px;"><?php _e( '(login required)', 'wpforo' ); ?></span>
498 <?php endif; ?>
499 </td>
500 <td style="text-align: center; padding: 10px;">
501 <?php if ( $guest_disabled ) : ?>
502 <span style="color: #999;">—</span>
503 <input type="hidden" name="ai[rate_limits][<?php echo esc_attr( $feature_key ); ?>][guest]" value="0">
504 <?php else : ?>
505 <input type="number" name="ai[rate_limits][<?php echo esc_attr( $feature_key ); ?>][guest]"
506 value="<?php echo esc_attr( $guest_limit ); ?>"
507 min="0" max="1000" step="1"
508 style="width: 80px; text-align: center;">
509 <?php endif; ?>
510 </td>
511 <td style="text-align: center; padding: 10px;">
512 <input type="number" name="ai[rate_limits][<?php echo esc_attr( $feature_key ); ?>][user]"
513 value="<?php echo esc_attr( $user_limit ); ?>"
514 min="0" max="1000" step="1"
515 style="width: 80px; text-align: center;">
516 </td>
517 </tr>
518 <?php endforeach; ?>
519 </tbody>
520 </table>
521
522 <p class="wpf-desc" style="color: #666; margin: 0;">
523 <strong><?php _e( 'Note:', 'wpforo' ); ?></strong>
524 <?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' ); ?>
525 </p>
526 </div>
527 </div>
528
529 <!-- Collapsible Sections CSS & JS -->
530 <style>
531 .wpf-ai-collapsible-section {
532 margin-bottom: 0;
533 }
534 .wpf-ai-section-header {
535 cursor: pointer;
536 user-select: none;
537 position: relative;
538 padding-left: 30px !important;
539 transition: background-color 0.2s ease;
540 }
541 .wpf-ai-section-header:hover {
542 background-color: #f8f9fa;
543 }
544 .wpf-ai-toggle-icon {
545 position: absolute;
546 left: 4px;
547 top: 50%;
548 transform: translateY(-50%);
549 color: #43A6DF;
550 font-size: 18px;
551 transition: transform 0.2s ease;
552 }
553 .wpf-ai-collapsible-section.collapsed .wpf-ai-toggle-icon {
554 transform: translateY(-50%) rotate(-90deg);
555 }
556 .wpf-ai-section-content {
557 overflow: hidden;
558 transition: max-height 0.3s ease-out, opacity 0.2s ease-out;
559 max-height: 5000px;
560 opacity: 1;
561 }
562 .wpf-ai-collapsible-section.collapsed .wpf-ai-section-content {
563 max-height: 0;
564 opacity: 0;
565 padding: 0;
566 }
567 .wpf-ai-section-controls .button {
568 display: inline-flex;
569 align-items: center;
570 }
571 .wpf-ai-section-controls .dashicons {
572 font-size: 16px;
573 width: 16px;
574 height: 16px;
575 }
576 </style>
577
578 <script>
579 jQuery(document).ready(function($) {
580 // Toggle individual section
581 $('.wpf-ai-section-header').on('click', function(e) {
582 // Don't toggle if clicking on a link or badge inside the header
583 if ($(e.target).is('a, .wpf-plan-badge')) {
584 return;
585 }
586 $(this).closest('.wpf-ai-collapsible-section').toggleClass('collapsed');
587 });
588
589 // Collapse All button
590 $('.wpf-ai-collapse-all').on('click', function() {
591 $('.wpf-ai-collapsible-section').addClass('collapsed');
592 });
593
594 // Expand All button
595 $('.wpf-ai-expand-all').on('click', function() {
596 $('.wpf-ai-collapsible-section').removeClass('collapsed');
597 });
598 });
599 </script>
600