PluginProbe
wpForo Forum / 3.1.6
wpForo Forum v3.1.6
3.1.6 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 All 138 releases
← All changes | classes/AIChatbot.php +37 -6 3.0.83.1.6 View file →
@@ -204,9 +204,16 @@
204 204 */
205 205 public function delete_conversation( $conversation_id ) {
206 206 $userid = WPF()->current_userid;
207 207
208 - // Delete messages first
208 + // SECURITY: Verify ownership before deleting anything
209 + // get_conversation() returns null if conversation doesn't belong to current user
210 + $conversation = $this->get_conversation( $conversation_id );
211 + if ( ! $conversation ) {
212 + return false;
213 + }
214 +
215 + // Delete messages first (now safe - ownership verified above)
209 216 $messages_table = WPF()->tables->ai_chat_messages;
210 217 WPF()->db->delete(
211 218 $messages_table,
212 219 [ 'conversation_id' => $conversation_id ],
@@ -381,9 +388,9 @@
381 388 $updates = [
382 389 'message_count' => $conversation['message_count'] + 1,
383 390 'total_tokens' => $conversation['total_tokens'] + $tokens,
384 391 'total_credits' => $conversation['total_credits'] + $credits,
385 - 'last_message_at' => current_time( 'mysql' ),
392 + 'last_message_at' => current_time( 'mysql', true ), // UTC for timezone conversion
386 393 ];
387 394 $formats = [ '%d', '%d', '%d', '%s' ];
388 395
389 396 // Set title from first user message if empty (25 chars max)
@@ -455,8 +462,16 @@
455 462 if ( $min_score_setting > 0 ) {
456 463 $request_data['settings']['min_score'] = $min_score_setting / 100;
457 464 }
458 465
466 + // Add custom knowledge parameters (Business+ cloud mode only)
467 + if ( WPF()->ai_client->is_custom_knowledge_enabled() ) {
468 + $request_data['settings']['include_custom_knowledge'] = true;
469 + $request_data['settings']['knowledge_priority'] = [
470 + 'chat_priority' => WPF()->ai_client->get_knowledge_priorities( 'chat' ),
471 + ];
472 + }
473 +
459 474 // Add local context if enabled
460 475 if ( $use_local && ! empty( $local_context ) ) {
461 476 $request_data['local_context'] = $local_context;
462 477 }
@@ -513,10 +528,15 @@
513 528
514 529 // Convert post_id to url in sources (API sends post_id, PHP generates URL)
515 530 $sources = $has_no_content ? [] : ( $response['sources'] ?? [] );
516 531 foreach ( $sources as &$source ) {
517 - if ( ! empty( $source['post_id'] ) && empty( $source['url'] ) ) {
518 - $content_source = $source['content_source'] ?? 'wpforo';
532 + $content_source = $source['content_source'] ?? 'wpforo';
533 +
534 + if ( $content_source === 'custom_knowledge' ) {
535 + // Custom knowledge - no URL, just ensure proper labeling
536 + $source['url'] = '';
537 + $source['content_type_label'] = __( 'Knowledge Base', 'wpforo' );
538 + } elseif ( ! empty( $source['post_id'] ) && empty( $source['url'] ) ) {
519 539 if ( $content_source === 'wordpress' ) {
520 540 // WordPress content - use get_permalink() or stored permalink
521 541 if ( ! empty( $source['permalink'] ) ) {
522 542 $source['url'] = $source['permalink'];
@@ -600,12 +620,23 @@
600 620 // Only match if it looks like a post ID (not part of markdown link)
601 621 $text = preg_replace( '/(?<!\[)\[#?(\d{2,})\](?!\])(?!\()/', '[[#$1]]', $text );
602 622
603 623 // Early exit if no citations to process
604 - // Check for both [[# (standard) and [[wp_ (WordPress without hash - LLM sometimes forgets #)
605 - if ( strpos( $text, '[[#' ) === false && strpos( $text, '[[wp_' ) === false ) {
624 + // Check for [[# (standard), [[wp_ (WordPress), [[kb_ (knowledge base)
625 + if ( strpos( $text, '[[#' ) === false && strpos( $text, '[[wp_' ) === false && strpos( $text, '[[kb_' ) === false ) {
606 626 return $text;
607 627 }
628 +
629 + // Replace [[#kb_FILE_ID_CHUNK]] format - Knowledge Base content (no URL, show badge)
630 + // Matches patterns like [[#kb_59a02953-e6ca-40b6-9d0f-5f482f340b10_383]]
631 + $text = preg_replace_callback(
632 + '/\[\[#?kb_[a-f0-9\-]+_\d+(?::[^\]]+)?\]\]/',
633 + function ( $matches ) {
634 + // Show a simple knowledge base indicator (no link since KB has no URL)
635 + return '<sup class="wpf-ai-chat-reference wpf-ai-chat-kb-ref" title="' . esc_attr__( 'Source: Knowledge Base', 'wpforo' ) . '"><span>[<svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="vertical-align:-2px"><path d="M4 19.5A2.5 2.5 0 0 1 6.5 17H20"/><path d="M6.5 2H20v20H6.5A2.5 2.5 0 0 1 4 19.5v-15A2.5 2.5 0 0 1 6.5 2z"/></svg>KB]</span></sup>';
636 + },
637 + $text
638 + );
608 639
609 640 // Replace [[#wp_POST_ID:Title]] format - WordPress content with title (title ignored)
610 641 $text = preg_replace_callback(
611 642 '/\[\[#wp_(\d+):([^\]]+)\]\]/',