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

' . __( 'open a support ticket', 'wpforo' ) . '' ); ?>

0 ? round( ( $credits_remaining / $credits_total ) * 100 ) : 0; wpforo_ai_render_connected_state( $status, 'free_trial', $is_post_purchase ); } /** * Render "Paid Plan" state * * @param array $status Tenant status data * @param bool $is_post_purchase Whether this is a post-purchase page load */ function wpforo_ai_render_paid_plan_state( $status, $is_post_purchase = false ) { wpforo_ai_render_connected_state( $status, 'paid', $is_post_purchase ); } /** * Render connected state (shared between free trial and paid) * * @param array $status Tenant status data * @param string $mode 'free_trial' or 'paid' * @param bool $is_post_purchase Whether this is a post-purchase page load */ function wpforo_ai_render_connected_state( $status, $mode = 'free_trial', $is_post_purchase = false ) { $subscription = isset($status['subscription']) && is_array($status['subscription']) ? $status['subscription'] : []; $tenant_id = isset($status['tenant_id']) ? $status['tenant_id'] : ''; $features = isset($status['features_enabled']) && is_array($status['features_enabled']) ? $status['features_enabled'] : []; $usage = isset($status['usage_this_month']) && is_array($status['usage_this_month']) ? $status['usage_this_month'] : []; $usage_lifetime = isset($status['usage_lifetime']) && is_array($status['usage_lifetime']) ? $status['usage_lifetime'] : []; $plan = isset($subscription['plan']) ? $subscription['plan'] : 'free_trial'; $credits_total = isset($subscription['credits_total']) ? (int) $subscription['credits_total'] : 0; $credits_baseline = isset($subscription['credits_baseline']) ? (int) $subscription['credits_baseline'] : $credits_total; $credits_remaining = isset($subscription['credits_remaining']) ? (int) $subscription['credits_remaining'] : 0; $credits_used = isset($subscription['credits_used']) ? (int) $subscription['credits_used'] : 0; $max_credits = isset($subscription['max_credits']) ? (int) $subscription['max_credits'] : 0; $expires_at = isset($subscription['expires_at']) ? $subscription['expires_at'] : ''; $renews_at = isset($subscription['renews_at']) ? $subscription['renews_at'] : ''; // Payment providers: detect all providers used by this tenant // API returns payment_providers array (e.g., ["freemius", "paddle"]) and payment_provider string (most recent) $payment_providers = isset($subscription['payment_providers']) && is_array($subscription['payment_providers']) ? $subscription['payment_providers'] : []; $payment_provider = isset($subscription['payment_provider']) ? $subscription['payment_provider'] : ''; // Backwards compatibility: if API doesn't return payment_providers yet, build from single value if ( empty( $payment_providers ) && $payment_provider ) { $payment_providers = [ $payment_provider ]; } // Legacy fallback: existing Freemius subscribers may not have payment_provider set if ( empty( $payment_providers ) && $mode === 'paid' ) { $payment_providers = [ 'freemius' ]; $payment_provider = 'freemius'; } $credits_percent = $credits_baseline > 0 ? round( ( $credits_remaining / $credits_baseline ) * 100 ) : 0; $is_free_trial = ( $mode === 'free_trial' ); // Get stored API key (global option, shared across all boards) $api_key = WPF()->ai_client->get_api_key(); $api_key_masked = WPF()->ai_client->mask_api_key( WPF()->ai_client->decrypt_api_key( $api_key ) ); // Check bonus credits status $bonus_credits_status = WPF()->ai_client->get_bonus_credits_status(); $bonus_credits_claimed = ! empty( $bonus_credits_status['claimed'] ); $bonus_credits_amount = $bonus_credits_claimed ? (int) $bonus_credits_status['amount'] : 0; // Check eligibility for bonus credits (only if not claimed) $bonus_credits_eligible = false; if ( ! $bonus_credits_claimed ) { $eligibility = WPF()->ai_client->check_bonus_credits_eligibility(); $bonus_credits_eligible = $eligibility['eligible']; } ?>

/
' . esc_html( wpforo_ai_format_date( $expires_at ) ) . '' ); } elseif ( ! empty( $renews_at ) ) { printf( __( 'Next billing: %s', 'wpforo' ), '' . esc_html( wpforo_ai_format_date( $renews_at ) ) . '' ); } ?>
0 && ! $is_free_trial ) : ?>
' . number_format( $max_credits ) . '' ); ?>

' . esc_html( ucfirst( $p ) ) . ''; }, $payment_providers ); printf( __( 'Payment: %s', 'wpforo' ), implode( ' + ', $provider_labels ) ); ?>

ai_client->get_tenant_id(); $subscription = wpfval( $status, 'subscription' ); $expires_at = wpfval( $subscription, 'expires_at' ); $plan = wpfval( $subscription, 'plan' ); $is_trial = ( $plan === 'free_trial' ); ?>

' . esc_html( wpforo_ai_format_date( $expires_at ) ) . '' ); ?>

' . __( 'Open Support Ticket', 'wpforo' ) . '' ); ?>

ai_client->get_tenant_id(); $provider = wpfval( $subscription, 'payment_provider' ); if ( empty( $provider ) ) { $provider = get_option( 'wpforo_ai_payment_provider', 'freemius' ); } ?>

' . __( 'Open Support Ticket', 'wpforo' ) . '' ); ?>

get_error_message() : __( 'Unable to connect to AI service.', 'wpforo' ); ?>

' . __( 'Open Support Ticket', 'wpforo' ) . '' ); ?>

0, 'starter' => 0, // Starter and free_trial are same level 'professional' => 1, 'business' => 2, 'enterprise' => 3, ); $current_level = isset( $plan_hierarchy[ $current_plan ] ) ? $plan_hierarchy[ $current_plan ] : 0; $required_level = isset( $plan_hierarchy[ $feature_plan ] ) ? $plan_hierarchy[ $feature_plan ] : 0; // Feature is enabled if current plan level >= required plan level return $current_level >= $required_level; } /** * Render features list * * @param array $enabled_features List of enabled feature IDs * @param array $usage Usage statistics per feature * @param bool $is_free_trial Whether on free trial * @param array $status Tenant status data from API (required for plan detection) */ function wpforo_ai_render_features_list( $enabled_features, $usage, $is_free_trial, $status = null ) { $all_features = wpforo_ai_get_all_features(); // Ensure $enabled_features is an array if ( ! is_array( $enabled_features ) ) { $enabled_features = []; } // Ensure $usage is an array if ( ! is_array( $usage ) ) { $usage = []; } // Get current plan from status (passed from parent function) $current_plan = 'free_trial'; if ( isset( $status['subscription']['plan'] ) ) { $current_plan = $status['subscription']['plan']; } elseif ( ! $is_free_trial ) { // If not free trial but no plan set, assume starter $current_plan = 'starter'; } // Group features by plan $features_by_plan = [ 'starter' => [], 'professional' => [], 'business' => [], 'enterprise' => [], ]; foreach ( $all_features as $feature_id => $feature ) { $feature_plan = isset( $feature['plan'] ) ? $feature['plan'] : 'professional'; if ( isset( $features_by_plan[ $feature_plan ] ) ) { $features_by_plan[ $feature_plan ][ $feature_id ] = $feature; } } // Plan display names $plan_names = [ 'starter' => __( 'Starter Features', 'wpforo' ), 'professional' => __( 'Professional Features', 'wpforo' ), 'business' => __( 'Business Features', 'wpforo' ), 'enterprise' => __( 'Enterprise Features', 'wpforo' ), ]; // Render accordion for each plan echo '
'; foreach ( $features_by_plan as $plan_level => $features ) { if ( empty( $features ) ) { continue; } $plan_id = 'plan-' . $plan_level; $is_plan_unlocked = wpforo_ai_is_feature_enabled_by_plan( $current_plan, $plan_level ); $is_current_plan = ( $current_plan === $plan_level ); $plan_status_icon = $is_plan_unlocked ? 'dashicons-yes-alt' : 'dashicons-lock'; $plan_status_class = $is_plan_unlocked ? 'plan-unlocked' : 'plan-locked'; echo '
'; echo ''; echo ''; // .accordion-content echo '
'; // .accordion-item } echo '
'; // .wpforo-ai-features-accordion } /** * Render features preview list (for "What You'll Get" section) * Shows all features except enterprise plan when no API key is registered */ function wpforo_ai_render_features_preview() { $features = wpforo_ai_get_all_features(); // Only WooCommerce Products indexing is coming soon $coming_soon_features = [ 'woocommerce_products_indexing' ]; // Plan display names and colors $plan_labels = [ 'starter' => [ 'label' => __( 'Free / Starter', 'wpforo' ), 'class' => 'plan-starter' ], 'professional' => [ 'label' => __( 'Professional', 'wpforo' ), 'class' => 'plan-professional' ], 'business' => [ 'label' => __( 'Business', 'wpforo' ), 'class' => 'plan-business' ], 'enterprise' => [ 'label' => __( 'Enterprise', 'wpforo' ), 'class' => 'plan-enterprise' ], ]; echo ''; } /** * Render Getting Started Steps Box * * Displays a visual progress indicator showing the steps to use wpForo AI. * First step (Connect) is marked as completed when connected, shows "1" when not. * * @param bool $is_connected Whether the user is connected to AI services */ function wpforo_ai_render_getting_started_steps( $is_connected = true ) { // Build URLs for each step $indexing_url = admin_url( 'admin.php?page=wpforo-ai&tab=rag_indexing' ); $settings_url = admin_url( 'admin.php?page=wpforo-settings&wpf_tab=ai' ); $spam_url = admin_url( 'admin.php?page=wpforo-settings&wpf_tab=ai#moderation_spam' ); $forum_url = wpforo_home_url(); $logs_url = admin_url( 'admin.php?page=wpforo-ai&tab=ai_logs' ); ?>

'https://www.youtube.com/watch?v=ZmNEAYSWg8M', 'title' => __( 'wpForo AI Search', 'wpforo' ), ], [ 'url' => 'https://www.youtube.com/watch?v=Pq5BvlrkXwE', 'title' => __( 'wpForo AI Topic Summary', 'wpforo' ), ], [ 'url' => 'https://www.youtube.com/watch?v=2NmKtJO6miQ', 'title' => __( 'wpForo AI Translation', 'wpforo' ), ], ]; ?>

[ 'name' => __( 'AI Knowledge Generation - Forum Content Indexing & Vectorization', 'wpforo' ), 'description' => __( 'Index forum content to build a searchable vector database optimized for Retrieval Augmented Generation (RAG). This feature transforms unstructured forum discussions, threads, and posts into semantic vectors, enabling AI that truly understands your domain, products, and audience.', 'wpforo' ), 'plan' => 'starter', 'preview' => false, ], 'vector_db_local_storage' => [ 'name' => __( 'AI Indexed Vector Database - Local Storage', 'wpforo' ), 'description' => __( 'Store AI-generated vector embeddings locally on your WordPress server. Perfect for smaller forums or sites with storage capacity. Vectors are stored in your database, giving you full control over your data while enabling semantic search and AI features without external dependencies.', 'wpforo' ), 'plan' => 'starter', 'preview' => false, ], 'semantic_search' => [ 'name' => __( "AI Powered Semantic Search", 'wpforo' ), 'description' => __( "Traditional search fails when users don't know exact keywords. Stop losing members to \"no results found\". AI-powered semantic search understands what users mean, not just what they type. Find relevant discussions even with different wording, typos, or synonyms—delivering answers that actually help.", 'wpforo' ), 'plan' => 'starter', 'preview' => true, ], 'search_enhance' => [ 'name' => __( 'AI Search Enhancement', 'wpforo' ), 'description' => __( 'Elevate search results with AI-powered summaries and recommendations. When users search, AI analyzes the results and generates a contextual summary explaining how each result relates to the query. Get intelligent suggestions for related topics and better answers instantly.', 'wpforo' ), 'plan' => 'starter', 'preview' => true, ], 'topic_summary' => [ 'name' => __( 'AI Topic Summarization', 'wpforo' ), 'description' => __( 'Get instant AI-generated summaries of long forum discussions. Help members quickly understand the key points, main conclusions, and important details of any topic without reading through hundreds of replies. Perfect for catching up on active discussions or reviewing complex threads at a glance.', 'wpforo' ), 'plan' => 'starter', 'preview' => true, ], 'smart_topic_suggestions' => [ 'name' => __( 'AI Topic Suggestions', 'wpforo' ), 'description' => __( 'Enhance topic creation with intelligent suggestions. AI analyzes what members are typing and instantly recommends similar existing discussions, related topics, and potential answers. Reduce duplicate posts, improve search visibility, and help users discover solutions before they even ask—all in real-time.', 'wpforo' ), 'plan' => 'starter', 'preview' => true, ], 'multi_language_translation' => [ 'name' => __( 'AI Multi-Language Translation', 'wpforo' ), 'description' => __( 'Break language barriers and grow your global community. No more limiting your community to one language. Multi-language AI translation lets members from anywhere participate naturally. Each user sees content in their language, posts in their language, and AI bridges the gap—no manual translation needed.', 'wpforo' ), 'plan' => 'starter', 'preview' => true, ], 'ai_spam_detection' => [ 'name' => __( 'AI Moderation - Spam Detection and User Banning', 'wpforo' ), 'description' => __( 'Keep your forum clean with intelligent spam detection. AI automatically identifies spam content after post submission, blocking malicious posts before they appear while learning from your moderation decisions to improve accuracy.', 'wpforo' ), 'plan' => 'starter', 'preview' => true, ], 'ai_toxicity_detection' => [ 'name' => __( 'AI Moderation - Content Safety & Toxicity Detection', 'wpforo' ), 'description' => __( 'Protect your community from harmful content with advanced toxicity detection. AI analyzes posts for hate speech, harassment, personal attacks, threats, and other toxic behavior. Configurable severity thresholds let you set how aggressively to filter content, while automated actions like unapproval or user banning help maintain a respectful environment without constant manual moderation.', 'wpforo' ), 'plan' => 'starter', 'preview' => false, ], 'ai_rule_compliance' => [ 'name' => __( 'AI Moderation - Rule Compliance & Policy Enforcement', 'wpforo' ), 'description' => __( 'Automatically enforce your forum rules and community guidelines. AI checks every post against configurable policies including topic relevance, promotional content restrictions, and illegal content detection. Smart keyword pre-filtering catches obvious violations instantly without API calls, while nuanced cases receive full AI analysis. Define custom rules for your community and let AI ensure members stay on topic and follow your guidelines.', 'wpforo' ), 'plan' => 'starter', 'preview' => false, ], 'forum_image_indexing' => [ 'name' => __( 'AI Knowledge Generation - Forum Image Indexing & Vectorization', 'wpforo' ), 'description' => __( 'Unlock the power of visual content in your forum. AI automatically analyzes images embedded in posts—screenshots, diagrams, infographics, product photos—and generates rich semantic embeddings. Visual information is indexed alongside text, enabling AI search and chat to understand and retrieve content based on what images show, not just surrounding text. Perfect for technical forums, support communities, and visual-heavy discussions.', 'wpforo' ), 'plan' => 'professional', 'preview' => false, ], 'forum_document_indexing' => [ 'name' => __( 'AI Knowledge Generation - Forum Document Indexing & Vectorization', 'wpforo' ), 'description' => __( 'Transform document attachments into searchable knowledge. AI automatically extracts text from PDF, Word (DOCX), PowerPoint (PPTX), RTF, and plain text files attached to forum posts. Extracted content is chunked, embedded, and indexed alongside regular post text, making document contents fully searchable through AI semantic search and accessible to the AI chatbot. Ideal for forums with manuals, guides, reports, and technical documentation shared as attachments.', 'wpforo' ), 'plan' => 'professional', 'preview' => false, ], 'ai_bot_reply' => [ 'name' => __( 'AI Bot Reply and Reply Suggestion', 'wpforo' ), 'description' => __( 'Generate AI-powered replies manually with a single click or use the Suggest Reply button to get AI-generated content directly in the editor. Moderators can trigger bot replies from any post, or use the suggest feature to draft responses based on forum context, topic content, and AI knowledge base.', 'wpforo' ), 'plan' => 'professional', 'preview' => true, ], 'ai_topic_generator' => [ 'name' => __( 'AI Tasks - Automated Topic Generation', 'wpforo' ), 'description' => __( 'Keep your forum active with AI-generated topics! The AI analyzes your existing discussions and automatically creates engaging threads based on your instructions every day or in any time period you want. Topics appear naturally from AI created users or pre-defined users, sparking fresh conversations. The AI can search for trending news and current issues related to your community, then create timely, relevant topics that drive engagement.', 'wpforo' ), 'plan' => 'professional', 'preview' => true, ], 'ai_reply_generator' => [ 'name' => __( 'AI Tasks - Automated Reply Generation', 'wpforo' ), 'description' => __( 'Boost engagement with intelligent reply generation. AI monitors your forum for unanswered topics, analyzes context from existing discussions and knowledge base, and automatically posts helpful, relevant responses through bot users. Configure reply frequency, tone, and trigger conditions to maintain authentic community interaction.', 'wpforo' ), 'plan' => 'professional', 'preview' => true, ], 'auto_tag_generation' => [ 'name' => __( 'AI Tasks - Automated Topic Tag Moderation', 'wpforo' ), 'description' => __( 'Improve content discovery with smart tag management. Complete tag management powered by AI. Automatically generates relevant tags for new and existing topics, removes outdated or irrelevant tags, maintains consistent tag vocabulary, identifies duplicate tags, and suggests related keywords. Self-cleaning system ensures your tag structure stays organized as your forum evolves while leaving existing topics untouched to preserve forum integrity.', 'wpforo' ), 'plan' => 'professional', 'preview' => true, ], 'analytics_insights' => [ 'name' => __( 'AI Analytics & Insights', 'wpforo' ), 'description' => __( 'Stop guessing what your community wants. AI Analytics reveals hidden patterns in member behavior. Comprehensive AI-driven analytics for smarter community management. Monitor real-time engagement metrics, track topic performance, identify trending discussions, discover underserved content areas, and receive personalized growth recommendations. Advanced sentiment analysis, member retention insights, and predictive trends help you stay ahead.', 'wpforo' ), 'plan' => 'professional', 'preview' => true, ], 'ai_assistant_chatbot' => [ 'name' => __( 'AI Chat Assistant', 'wpforo' ), 'description' => __( '24/7 intelligent assistance for your forum members. Intelligent chatbot powered by your forum\'s content. Provides instant answers based on indexed discussions, suggests relevant topics, guides new members through common tasks, and escalates complex questions to moderators when needed. Learns from interactions, supports multiple languages, and maintains your community\'s tone and style.', 'wpforo' ), 'plan' => 'business', 'preview' => false, ], 'vector_db_cloud_storage' => [ 'name' => __( 'AI Indexed Vector Database - Cloud Storage', 'wpforo' ), 'description' => __( 'Scale your AI capabilities with cloud-based vector storage powered by AWS S3 Vectors. Ideal for large forums with thousands of posts and growing communities. Cloud storage provides unlimited scalability, faster query performance, automatic backups, and enterprise-grade reliability for your AI knowledge base.', 'wpforo' ), 'plan' => 'business', 'preview' => false, ], 'extended_knowledge_base' => [ 'name' => __( 'Extended Knowledge Base', 'wpforo' ), 'description' => __( 'Expand AI knowledge beyond forum content to include WordPress posts, pages, and products.', 'wpforo' ), 'plan' => 'business', 'preview' => false, ], 'wordpress_content_indexing' => [ 'name' => __( 'WordPress Content Indexing', 'wpforo' ), 'description' => __( 'Index WordPress posts and pages for comprehensive site-wide AI search.', 'wpforo' ), 'plan' => 'business', 'preview' => false, ], 'custom_post_types_indexing' => [ 'name' => __( 'Custom Post Types Indexing', 'wpforo' ), 'description' => __( 'Include custom post types in AI knowledge base for complete content coverage.', 'wpforo' ), 'plan' => 'business', 'preview' => false, ], 'woocommerce_products_indexing' => [ 'name' => __( 'WooCommerce Products Indexing', 'wpforo' ), 'description' => __( 'Index WooCommerce products to provide AI-powered product search and support.', 'wpforo' ), 'plan' => 'business', 'preview' => false, ], 'custom_knowledge_indexing' => [ 'name' => __( 'Custom Knowledge Indexing', 'wpforo' ), 'description' => __( 'Upload and index custom knowledge files (JSON, Markdown, Text) to enhance AI responses with your expert documentation, FAQs, and specialized content. Perfect for product manuals, internal guides, and domain-specific knowledge bases.', 'wpforo' ), 'plan' => 'business', 'preview' => false, ], 'file_indexing' => [ 'name' => __( 'File Indexing (TXT, MD, JSON, PDF)', 'wpforo' ), 'description' => __( 'Index plain text, Markdown, JSON, and PDF files into the AI knowledge base for richer answers from custom documentation and reference material. Scanned PDFs are auto-OCR\'d when the text layer is too sparse.', 'wpforo' ), 'plan' => 'business', 'preview' => false, ], 'developer_features' => [ 'name' => __( 'Developer Features', 'wpforo' ), 'description' => __( 'Advanced developer tools and API access for custom integrations.', 'wpforo' ), 'plan' => 'enterprise', 'preview' => false, ], 'rest_api_access' => [ 'name' => __( 'REST API Access', 'wpforo' ), 'description' => __( 'Full REST API access for custom applications and third-party integrations.', 'wpforo' ), 'plan' => 'enterprise', 'preview' => false, ], 'custom_ai_models' => [ 'name' => __( 'Custom AI Models', 'wpforo' ), 'description' => __( 'Use custom-trained AI models tailored to your specific community needs.', 'wpforo' ), 'plan' => 'enterprise', 'preview' => false, ], 'custom_feature_development' => [ 'name' => __( 'Custom feature development', 'wpforo' ), 'description' => __( 'Dedicated development resources for custom AI features unique to your forum.', 'wpforo' ), 'plan' => 'enterprise', 'preview' => false, ], 'premium_support' => [ 'name' => __( 'Premium Support', 'wpforo' ), 'description' => __( 'Priority support with dedicated account management and faster response times.', 'wpforo' ), 'plan' => 'enterprise', 'preview' => false, ], 'dedicated_account_manager' => [ 'name' => __( 'Dedicated account manager', 'wpforo' ), 'description' => __( 'Personal account manager for strategic guidance and priority assistance.', 'wpforo' ), 'plan' => 'enterprise', 'preview' => false, ], 'enterprise_capabilities' => [ 'name' => __( 'Enterprise Capabilities', 'wpforo' ), 'description' => __( 'Enterprise-grade features including SLA, compliance support, and advanced security.', 'wpforo' ), 'plan' => 'enterprise', 'preview' => false, ], ] ); } /** * Get SVG icon for a feature * * @param string $feature_id Feature identifier * @return string SVG icon markup */ function wpforo_ai_get_feature_icon( $feature_id ) { $icons = [ // Free Features - Vector Database Storage 'vector_db_local_storage' => '', 'vector_db_cloud_storage' => '', // Starter Features 'semantic_search' => '', 'content_indexing' => '', 'image_indexing' => '', 'document_indexing' => '', 'multi_language_translation' => '', 'topic_summary' => '', 'smart_topic_suggestions' => '', 'topic_suggestions' => '', 'ai_topic_generator' => '', 'ai_reply_generator' => '', 'ai_bot_reply' => '', 'forum_image_indexing' => '', 'forum_document_indexing' => '', 'automatic_faq_generation' => '', 'auto_tag_generation' => '', 'analytics_insights' => '', // Professional Features - Content Moderation 'ai_spam_detection' => '', 'ai_toxicity_detection' => '', 'ai_rule_compliance' => '', 'unified_moderation' => '', 'ai_assistant_chatbot' => '', // Business Features 'extended_knowledge_base' => '', 'wordpress_content_indexing' => '', 'custom_post_types_indexing' => '', 'woocommerce_products_indexing' => '', 'custom_knowledge_indexing' => '', 'file_indexing' => '', // Enterprise Features 'developer_features' => '', 'rest_api_access' => '', 'custom_ai_models' => '', 'custom_feature_development' => '', 'premium_support' => '', 'dedicated_account_manager' => '', 'enterprise_capabilities' => '', ]; // Return the icon or a default one if ( isset( $icons[ $feature_id ] ) ) { return $icons[ $feature_id ]; } // Default icon (star) return ''; } /** * Render usage statistics * * @param array $usage Usage data (since last credit purchase) * @param int $total_credits Total credits used * @param array $usage_lifetime Lifetime usage data (optional) * @param string $current_plan Current subscription plan (optional) */ function wpforo_ai_render_usage_stats( $usage, $total_credits, $usage_lifetime = [], $current_plan = 'free_trial' ) { // Ensure $total_credits is an integer $total_credits = (int) $total_credits; $features = wpforo_ai_get_all_features(); $has_lifetime = ! empty( $usage_lifetime ); echo '
'; echo '
'; echo '
'; echo ''; echo '
'; echo '' . number_format( $total_credits ) . ''; echo '' . __( 'Lifetime Credits Used', 'wpforo' ) . ''; echo '
'; echo '
'; echo '
'; echo ''; echo ''; echo ''; echo ''; echo ''; if ( $has_lifetime ) { echo ''; } echo ''; echo ''; echo ''; // Get all features available for the current plan $plan_features = []; // Skip storage features and sub-indexing features (their credits are summed into content_indexing) $skip_features = [ 'vector_db_local_storage', 'vector_db_cloud_storage', 'forum_image_indexing', 'forum_document_indexing' ]; foreach ( $features as $feature_id => $feature_data ) { // Skip storage features from usage statistics if ( in_array( $feature_id, $skip_features, true ) ) { continue; } $feature_plan = isset( $feature_data['plan'] ) ? $feature_data['plan'] : 'starter'; $feature_status = isset( $feature_data['status'] ) ? $feature_data['status'] : 'available'; // Only include features that are enabled for this plan and not "coming soon" if ( wpforo_ai_is_feature_enabled_by_plan( $current_plan, $feature_plan ) && $feature_status !== 'coming_soon' ) { $plan_features[] = $feature_id; } } // If no plan features found, fall back to usage keys if ( empty( $plan_features ) ) { $plan_features = $has_lifetime ? array_keys( $usage_lifetime ) : array_keys( $usage ); } foreach ( $plan_features as $feature_id ) { $feature_name = ''; if ( isset( $features[ $feature_id ]['name'] ) ) { $feature_name = $features[ $feature_id ]['name']; } if ( ! $feature_name ) { $feature_name = ucwords( str_replace( '_', ' ', $feature_id ) ); } // Get counts (since last purchase and lifetime) $count_since_purchase = isset( $usage[ $feature_id ] ) ? (int) $usage[ $feature_id ] : 0; $count_lifetime = $has_lifetime && isset( $usage_lifetime[ $feature_id ] ) ? (int) $usage_lifetime[ $feature_id ] : 0; // For content_indexing, include image and document indexing credits if ( 'content_indexing' === $feature_id ) { $count_since_purchase += ( isset( $usage['multimodal_image_indexing'] ) ? (int) $usage['multimodal_image_indexing'] : 0 ); $count_since_purchase += ( isset( $usage['document_indexing'] ) ? (int) $usage['document_indexing'] : 0 ); if ( $has_lifetime ) { $count_lifetime += ( isset( $usage_lifetime['multimodal_image_indexing'] ) ? (int) $usage_lifetime['multimodal_image_indexing'] : 0 ); $count_lifetime += ( isset( $usage_lifetime['document_indexing'] ) ? (int) $usage_lifetime['document_indexing'] : 0 ); } } echo ''; echo ''; echo ''; if ( $has_lifetime ) { echo ''; } echo ''; } echo ''; echo '
' . __( 'Feature', 'wpforo' ) . '' . __( 'Since Last Purchase', 'wpforo' ) . '' . __( 'Lifetime Total', 'wpforo' ) . '
' . esc_html( $feature_name ) . '' . number_format( $count_since_purchase ) . '' . number_format( $count_lifetime ) . '
'; echo '
'; } /** * Render pricing table * * @param string $tenant_id Tenant ID for upgrade URLs * @param array $subscription Current subscription data (optional, for showing active plan) */ function wpforo_ai_render_pricing_table( $tenant_id, $subscription = [] ) { // Get current plan and expiration date $current_plan = isset( $subscription['plan'] ) ? $subscription['plan'] : 'free_trial'; $renews_at = isset( $subscription['renews_at'] ) ? $subscription['renews_at'] : ''; $expires_at = isset( $subscription['expires_at'] ) ? $subscription['expires_at'] : ''; // Plan hierarchy for disabling lower plan buttons $plan_levels = [ 'free_trial' => 0, 'free' => 0, 'starter' => 1, 'professional' => 2, 'business' => 3, 'enterprise' => 4, ]; $current_level = isset( $plan_levels[ $current_plan ] ) ? $plan_levels[ $current_plan ] : 0; // Fetch dynamic pricing from Freemius API (with fallback to defaults) $pricing = wpforo_ai_get_freemius_pricing(); $plans = $pricing['plans']; ?>
$/month
+1,000
$/month
+3,000
$/month
+9,000
✓ ✓ ✓ ✓
✓ ✓ ✓ ✓
✓ ✓ ✓ ✓
✓ ✓ ✓ ✓
✓ ✓ ✓ ✓
✓ ✓ ✓ ✓
✓ ✓ ✓ ✓
✓ ✓ ✓ ✓
✓ ✓ ✓ ✓
✓ ✓ ✓ ✓
✓ ✓ ✓ ✓
✗ ✓ ✓ ✓
✗ ✓ ✓ ✓
✗ ✓ ✓ ✓
✗ ✓ ✓ ✓
✗ ✓ ✓ ✓
✗ ✓ ✓ ✓
✗ ✓ ✓ ✓
✗ ✓ ✓ ✓
✗ ✓ ✓ ✓
✗ ✓ ✓ ✓
✗ ✓ ✓ ✓
✗ ✓ ✓ ✓
✗ ✓ ✓ ✓
✗ ✓ ✓ ✓
✗ ✗ ✓ ✓
✗ ✗ ✓ ✓
✗ ✗ ✓ ✓
✗ ✗ ✓ ✓
✗ ✗ ✓ ✓
' . esc_html__( 'Coming Soon', 'wpforo' ) . ''; ?> ✗ ✗ ✓ ✓
✗ ✗ ✓ ✓
✗ ✗ ✓ ✓
✗ ✗ ✗ ✓
✗ ✗ ✗ ✓
✗ ✗ ✗ ✓
✗ ✗ ✗ ✓
✗ ✗ ✗ ✓
✗ ✗ ✗ ✓
✗ ✗ ✗ ✓
1; ?>

' . __('as long as you have an active subscription', 'wpforo' ) . '' ); ?>

$

$

$

$

user_email ) ? $current_user->user_email : get_option( 'admin_email' ); // Get first name and last name from user meta only - no fallbacks // Empty is better than wrong data (like website title) $first_name = ! empty( $current_user->first_name ) ? $current_user->first_name : ''; $last_name = ! empty( $current_user->last_name ) ? $current_user->last_name : ''; return [ 'email' => $email, 'first' => $first_name, 'last' => $last_name, ]; } /** * Get Freemius upgrade URL * * @param string $tenant_id Tenant ID * @param string $plan Plan name ('starter', 'professional', 'business', 'enterprise') * @return string Checkout URL */ function wpforo_ai_get_upgrade_url( $tenant_id, $plan = 'professional' ) { // Freemius plugin configuration // wpForo AI Addon ID: 21923 $freemius_plugin_id = wpforo_get_option( 'ai_freemius_plugin_id', '21923' ); // Ensure plugin_id is never empty if ( empty( $freemius_plugin_id ) ) { $freemius_plugin_id = '21923'; } // Fetch dynamic pricing from Freemius API (with fallback to defaults) $pricing = wpforo_ai_get_freemius_pricing(); $plans = $pricing['plans']; $config = [ 'starter' => [ 'plugin_id' => $freemius_plugin_id, 'plan_id' => $plans['starter']['plan_id'], 'pricing_id' => $plans['starter']['pricing_id'], ], 'professional' => [ 'plugin_id' => $freemius_plugin_id, 'plan_id' => $plans['professional']['plan_id'], 'pricing_id' => $plans['professional']['pricing_id'], ], 'business' => [ 'plugin_id' => $freemius_plugin_id, 'plan_id' => $plans['business']['plan_id'], 'pricing_id' => $plans['business']['pricing_id'], ], 'enterprise' => [ 'plugin_id' => $freemius_plugin_id, 'plan_id' => $plans['enterprise']['plan_id'], 'pricing_id' => $plans['enterprise']['pricing_id'], ], ]; $plan_config = isset( $config[ $plan ] ) ? $config[ $plan ] : $config['professional']; // Ensure plan_config is valid if ( ! is_array( $plan_config ) ) { $plan_config = $config['professional']; } // Get API Gateway webhook URL $webhook_url = WPF()->ai_client->get_api_base_url() . '/v1/freemius/webhook'; // Build config object for JavaScript (not a URL) $plugin_id = isset( $plan_config['plugin_id'] ) && ! empty( $plan_config['plugin_id'] ) ? $plan_config['plugin_id'] : '21923'; $plan_id = isset( $plan_config['plan_id'] ) && ! empty( $plan_config['plan_id'] ) ? $plan_config['plan_id'] : $plans['professional']['plan_id']; $pricing_id = isset( $plan_config['pricing_id'] ) && ! empty( $plan_config['pricing_id'] ) ? $plan_config['pricing_id'] : $plans['professional']['pricing_id']; return [ 'plugin_id' => $plugin_id, 'plan_id' => $plan_id, 'pricing_id' => $pricing_id, 'public_key' => 'pk_bbb26ca8c136ce743c4661e2dd23f', // Freemius Public Key 'billing_cycle' => 'monthly', 'currency' => 'usd', 'success' => admin_url( 'admin.php?page=wpforo-ai&upgraded=1&plan=' . $plan ), 'cancel' => admin_url( 'admin.php?page=wpforo-ai' ), 'webhook_url' => $webhook_url, 'user' => wpforo_ai_get_freemius_user_data(), 'metadata' => [ 'tenant_id' => $tenant_id, // CRITICAL: Pass tenant_id so webhook can identify tenant ], ]; } /** * Get Freemius credit pack configuration * * @param string $tenant_id Tenant ID * @param string $pack Pack size ('500', '1500', '4500', '15000') * @return array Checkout configuration */ function wpforo_ai_get_credit_pack_config( $tenant_id, $pack = '1500' ) { // Freemius plugin configuration // AI Credits Addon ID: 21955 (different from main wpForo AI plugin 21923) $freemius_addon_id = wpforo_get_option( 'ai_freemius_addon_id', '21955' ); // Ensure addon_id is never empty if ( empty( $freemius_addon_id ) ) { $freemius_addon_id = '21955'; } // Fetch dynamic pricing from Freemius API (with fallback to defaults) $pricing = wpforo_ai_get_freemius_pricing(); $credit_packs = $pricing['credit_packs']; // Credit pack add-on plan IDs from Freemius $config = [ '500' => [ 'plugin_id' => $freemius_addon_id, 'plan_id' => $credit_packs['500']['plan_id'], 'pricing_id' => $credit_packs['500']['pricing_id'], ], '1500' => [ 'plugin_id' => $freemius_addon_id, 'plan_id' => $credit_packs['1500']['plan_id'], 'pricing_id' => $credit_packs['1500']['pricing_id'], ], '4500' => [ 'plugin_id' => $freemius_addon_id, 'plan_id' => $credit_packs['4500']['plan_id'], 'pricing_id' => $credit_packs['4500']['pricing_id'], ], '15000' => [ 'plugin_id' => $freemius_addon_id, 'plan_id' => $credit_packs['15000']['plan_id'], 'pricing_id' => $credit_packs['15000']['pricing_id'], ], ]; $pack_config = isset( $config[ $pack ] ) ? $config[ $pack ] : $config['1500']; // Ensure pack_config is valid if ( ! is_array( $pack_config ) ) { $pack_config = $config['1500']; } // Get API Gateway webhook URL $webhook_url = WPF()->ai_client->get_api_base_url() . '/v1/freemius/webhook'; // Build config object for JavaScript (not a URL) $plugin_id = isset( $pack_config['plugin_id'] ) && ! empty( $pack_config['plugin_id'] ) ? $pack_config['plugin_id'] : '21955'; $plan_id = isset( $pack_config['plan_id'] ) && ! empty( $pack_config['plan_id'] ) ? $pack_config['plan_id'] : $credit_packs['1500']['plan_id']; $pricing_id = isset( $pack_config['pricing_id'] ) && ! empty( $pack_config['pricing_id'] ) ? $pack_config['pricing_id'] : $credit_packs['1500']['pricing_id']; return [ 'plugin_id' => $plugin_id, 'plan_id' => $plan_id, 'pricing_id' => $pricing_id, 'public_key' => 'pk_d241fb18a0097c75e4de7c12d8b67', // AI Credits Addon Public Key 'billing_cycle' => 'one-time', 'currency' => 'usd', 'success' => admin_url( 'admin.php?page=wpforo-ai&credits_purchased=1&pack=' . $pack ), 'cancel' => admin_url( 'admin.php?page=wpforo-ai' ), 'webhook_url' => $webhook_url, 'user' => wpforo_ai_get_freemius_user_data(), 'metadata' => [ 'tenant_id' => $tenant_id, // CRITICAL: Pass tenant_id so webhook can identify tenant 'is_addon' => true, // Flag to distinguish add-on from subscription ], ]; } /** * Get manage subscription URL based on payment provider * * For Paddle: customer-portal.paddle.com/cpl_{ID} (seller-specific portal with email login) * For Freemius: customers.gvectors.com (Freemius customer portal) * * @return string Manage subscription URL */ function wpforo_ai_get_manage_subscription_url( $provider = '' ) { // If no provider specified, use the stored default if ( ! $provider ) { $provider = get_option( 'wpforo_ai_payment_provider', '' ); } if ( $provider === 'paddle' ) { // Paddle customer portal with seller-specific CPL ID // Shows email login page where customers get a magic link $url = 'https://customer-portal.paddle.com/cpl_01f49qd36rqh2vswk36847drxt'; } else { $url = 'https://customers.gvectors.com'; } return apply_filters( 'wpforo_ai_manage_subscription_url', $url, $provider ); } /** * Get CSS class for credit status based on percentage remaining * * @param int $percent Percentage remaining * @return string CSS class */ function wpforo_ai_get_credit_status_class( $percent ) { if ( $percent >= 50 ) { return 'status-good'; } elseif ( $percent >= 20 ) { return 'status-warning'; } else { return 'status-critical'; } } /** * Format date for display * * @param string $date ISO 8601 date string * @return string Formatted date */