board->get_boards(); $current_boardid = isset( $_GET['boardid'] ) ? intval( $_GET['boardid'] ) : 0; // Validate boardid exists (use strict comparison for type safety) $valid_boardids = array_map( 'intval', array_column( $boards, 'boardid' ) ); if ( ! in_array( $current_boardid, $valid_boardids, true ) ) { $current_boardid = 0; // Default to main board } // Switch to the selected board context if ( $current_boardid > 0 ) { WPF()->change_board( $current_boardid ); } // Render board sub-tabs if multiple boards exist if ( count( $boards ) > 1 ) : // Find current board title (from settings, not label) $current_board_title = __( 'Board', 'wpforo' ); foreach ( $boards as $board ) { if ( isset( $board['boardid'] ) && (int) $board['boardid'] === $current_boardid ) { $current_board_title = isset( $board['settings']['title'] ) ? esc_html( $board['settings']['title'] ) : ( isset( $board['title'] ) ? esc_html( $board['title'] ) : $current_board_title ); break; } } ?>
'wpforo-ai', 'tab' => 'ai_tasks', 'boardid' => $board_id, ), admin_url( 'admin.php' ) ); $active_class = ( $current_boardid === $board_id ) ? 'active' : ''; ?>

' . number_format( $credits_remaining ) . '' ); ?>

()

0 ) : ?>

tables->ai_tasks; // Check if table exists $table_exists = $wpdb->get_var( $wpdb->prepare( "SHOW TABLES LIKE %s", $table ) ); if ( ! $table_exists ) { return []; } $sql = $wpdb->prepare( "SELECT * FROM `{$table}` WHERE board_id = %d ORDER BY created_at DESC", $board_id ); $tasks = $wpdb->get_results( $sql, ARRAY_A ); return $tasks ? $tasks : []; } /** * Render a single task row for the table * * @param array $task Task data */ function wpforo_ai_render_task_row( $task ) { $task_id = (int) $task['task_id']; $task_name = esc_html( $task['task_name'] ); $task_type = $task['task_type']; $status = $task['status']; // Decode config if it's a JSON string $config = $task['config'] ?? []; if ( is_string( $config ) ) { $config = json_decode( $config, true ) ?: []; } $frequency = $config['frequency'] ?? 'daily'; $last_run = $task['last_run_time']; $last_run_status = $task['last_run_status']; $total_runs = (int) ( $task['total_runs'] ?? 0 ); $items_created = (int) ( $task['items_created'] ?? 0 ); $credits_used = (int) ( $task['credits_used'] ?? 0 ); // Task type display $type_labels = [ 'topic_generator' => __( 'Topic Generator', 'wpforo' ), 'reply_generator' => __( 'Reply Generator', 'wpforo' ), 'tag_maintenance' => __( 'Tag Maintenance', 'wpforo' ), ]; $type_icons = [ 'topic_generator' => '', 'reply_generator' => '', 'tag_maintenance' => '', ]; $type_label = isset( $type_labels[ $task_type ] ) ? $type_labels[ $task_type ] : $task_type; $type_icon = isset( $type_icons[ $task_type ] ) ? $type_icons[ $task_type ] : ''; // Status display $status_classes = [ 'active' => 'status-active', 'paused' => 'status-paused', 'error' => 'status-error', ]; $status_labels = [ 'active' => ' ' . __( 'Active', 'wpforo' ), 'paused' => ' ' . __( 'Paused', 'wpforo' ), 'error' => ' ' . __( 'Error', 'wpforo' ), ]; $status_class = isset( $status_classes[ $status ] ) ? $status_classes[ $status ] : 'status-paused'; $status_label = isset( $status_labels[ $status ] ) ? $status_labels[ $status ] : $status; // Frequency display $frequency_labels = [ 'hourly' => __( 'Every Hour', 'wpforo' ), '3hours' => __( 'Every 3 Hours', 'wpforo' ), '6hours' => __( 'Every 6 Hours', 'wpforo' ), 'daily' => __( 'Daily', 'wpforo' ), '3days' => __( 'Every 3 Days', 'wpforo' ), 'weekly' => __( 'Weekly', 'wpforo' ), 'custom' => __( 'Custom', 'wpforo' ), ]; $frequency_label = isset( $frequency_labels[ $frequency ] ) ? $frequency_labels[ $frequency ] : $frequency; // Check if run_on_approval is enabled $run_on_approval = ! empty( $config['run_on_approval'] ); // Next run display (time until next run in "in X hours" style) $next_run = $task['next_run_time'] ?? null; $next_run_display = ''; $next_run_class = ''; if ( $run_on_approval && $status === 'active' ) { // run_on_approval tasks trigger on topic/post approval, not cron $next_run_display = __( 'New Topic', 'wpforo' ); $next_run_class = 'next-run-on-approval'; } elseif ( $next_run && $status === 'active' ) { $next_run_display = wpforo_ai_format_time_until( $next_run ); // Check if overdue if ( $next_run_display === __( 'overdue', 'wpforo' ) ) { $next_run_class = 'next-run-overdue'; } } elseif ( $status === 'paused' ) { $next_run_display = __( 'Paused', 'wpforo' ); } else { $next_run_display = '—'; } // Last run display $last_run_display = $last_run ? wpforo_ai_format_date( $last_run ) : __( 'Never', 'wpforo' ); $last_run_status_class = ''; if ( $last_run_status === 'success' ) { $last_run_status_class = 'last-run-success'; } elseif ( $last_run_status === 'failure' ) { $last_run_status_class = 'last-run-failure'; } ?>

forum->get_forums(); // Sort forums hierarchically: parents first, then children under each parent $forums_by_parent = []; foreach ( $forums as $forum ) { $parent = (int) ( $forum['parentid'] ?? 0 ); if ( ! isset( $forums_by_parent[ $parent ] ) ) { $forums_by_parent[ $parent ] = []; } $forums_by_parent[ $parent ][] = $forum; } foreach ( $forums_by_parent as &$children ) { usort( $children, function( $a, $b ) { return (int) ( $a['order'] ?? 0 ) - (int) ( $b['order'] ?? 0 ); } ); } unset( $children ); $sorted_forums = []; $add_forums_recursive = function( $parentid ) use ( &$add_forums_recursive, &$sorted_forums, &$forums_by_parent ) { if ( ! isset( $forums_by_parent[ $parentid ] ) ) { return; } foreach ( $forums_by_parent[ $parentid ] as $forum ) { $sorted_forums[] = $forum; $add_forums_recursive( (int) $forum['forumid'] ); } }; $add_forums_recursive( 0 ); $forums = $sorted_forums; ?>

0 ? 'wpforo-ai-forum-child' : 'wpforo-ai-forum-parent'; if ( $is_cat ) { $item_class .= ' wpforo-ai-forum-category'; } ?>

0/120

__( 'Mon', 'wpforo' ), 'tue' => __( 'Tue', 'wpforo' ), 'wed' => __( 'Wed', 'wpforo' ), 'thu' => __( 'Thu', 'wpforo' ), 'fri' => __( 'Fri', 'wpforo' ), 'sat' => __( 'Sat', 'wpforo' ), 'sun' => __( 'Sun', 'wpforo' ), ]; foreach ( $days as $day_key => $day_label ) : ?>

~60 credits
4 credits

75%

0 ? 'wpforo-ai-forum-child' : 'wpforo-ai-forum-parent'; if ( $is_cat ) { $item_class .= ' wpforo-ai-forum-category'; } ?>

0/120

 

__( 'Mon', 'wpforo' ), 'tue' => __( 'Tue', 'wpforo' ), 'wed' => __( 'Wed', 'wpforo' ), 'thu' => __( 'Thu', 'wpforo' ), 'fri' => __( 'Fri', 'wpforo' ), 'sat' => __( 'Sat', 'wpforo' ), 'sun' => __( 'Sun', 'wpforo' ), ]; foreach ( $days as $day_key => $day_label ) : ?>

~180 credits
4 credits

75%
()

  • Reply to first post - Generates a reply based only on the first post content', 'wpforo' ); ?>
  • Reply to whole topic - Considers the topic first post and all replies for context', 'wpforo' ); ?>
  • Reply to last post - Responds to the last post while understanding the sub-thread context', 'wpforo' ); ?>

0 ? 'wpforo-ai-forum-child' : 'wpforo-ai-forum-parent'; if ( $is_cat ) { $item_class .= ' wpforo-ai-forum-category'; } ?>

__( 'Mon', 'wpforo' ), 'tue' => __( 'Tue', 'wpforo' ), 'wed' => __( 'Wed', 'wpforo' ), 'thu' => __( 'Thu', 'wpforo' ), 'fri' => __( 'Fri', 'wpforo' ), 'sat' => __( 'Sat', 'wpforo' ), 'sun' => __( 'Sun', 'wpforo' ), ]; foreach ( $days as $day_key => $day_label ) : ?>

~120 credits
4 credits