get_render_state() ) { return; } wp_enqueue_script( 'lp-ai-assistant' ); wp_enqueue_style( 'lp-ai-assistant' ); } /** * Gate checks — all must pass before rendering. * * Allows both lesson pages AND quiz item pages (quiz pages only when * the user has completed the quiz — checked later in render_widget). * * @return bool */ protected function should_render(): bool { $current_page = LP_Page_Controller::page_current(); if ( ! in_array( $current_page, array( LP_PAGE_SINGLE_COURSE_CURRICULUM, LP_PAGE_QUIZ ), true ) ) { return false; } if ( ! AIAssistantController::is_enabled() ) { return false; } if ( ! is_user_logged_in() ) { return false; } return true; } /** * Detect the rendering context. * * @return string 'quiz' | 'lesson' */ protected function detect_context(): string { return LP_Global::course_item_quiz() ? 'quiz' : 'lesson'; } /** * Resolve and cache the render state for the current request. * * @return array|false */ protected function get_render_state() { if ( $this->render_state_resolved ) { return $this->render_state; } $this->render_state_resolved = true; if ( ! $this->should_render() ) { return $this->render_state = false; } $context = $this->detect_context(); $item = LP_Global::course_item(); $item_id = $item ? absint( $item->get_id() ) : 0; $course_id = $item ? absint( $item->get_course_id() ) : 0; $user_id = get_current_user_id(); $enabled_actions = AIAssistantController::get_enabled_actions(); $free_chat_enabled = LP_Settings::get_option( 'ai_assistant_free_chat', 'no' ) === 'yes'; if ( $context === 'quiz' ) { if ( ! ( $enabled_actions['smart_review'] ?? true ) ) { return $this->render_state = false; } $quiz_result = $this->get_completed_quiz_result( $user_id, $item_id, $course_id ); if ( $quiz_result === false ) { return $this->render_state = false; } $enabled_actions = array( 'summarize' => false, 'explain' => false, 'quick_quiz' => false, 'smart_review' => true, ); $free_chat_enabled = false; } else { $enabled_actions['smart_review'] = false; if ( ! $free_chat_enabled && ! in_array( true, $enabled_actions, true ) ) { return $this->render_state = false; } $quiz_result = null; } return $this->render_state = array( 'context' => $context, 'item_id' => $item_id, 'course_id' => $course_id, 'enabled_actions' => $enabled_actions, 'free_chat_enabled' => $free_chat_enabled, 'quiz_result' => $quiz_result, ); } /** * Enqueue assets and localize runtime data for the frontend widget. * * @param array $render_state Computed render state. */ protected function localize_script_data( array $render_state ) { $js_data = wp_json_encode( array( 'ajaxUrl' => LP_Settings::url_handle_lp_ajax(), 'nonce' => wp_create_nonce( 'wp_rest' ), 'lessonId' => $render_state['item_id'], 'itemId' => $render_state['item_id'], 'courseId' => $render_state['course_id'], 'context' => $render_state['context'], 'quizCompleted' => $render_state['context'] === 'quiz', 'quizResult' => $render_state['quiz_result'], 'enabled' => true, 'freeChatEnabled' => $render_state['free_chat_enabled'], 'enabledActions' => $render_state['enabled_actions'], 'i18n' => array( 'you' => __( 'You', 'learnpress' ), 'assistant' => __( 'AI Assistant', 'learnpress' ), 'thinking' => __( 'Thinking...', 'learnpress' ), 'sendError' => __( 'An error occurred. Please try again.', 'learnpress' ), 'clearConfirm' => __( 'Clear chat history?', 'learnpress' ), 'quizPrompt' => __( 'Create a quick quiz from this lesson.', 'learnpress' ), 'explainPrompt' => __( 'Explain a concept from this lesson.', 'learnpress' ), 'summarizePrompt' => __( 'Summarize this lesson with key points.', 'learnpress' ), 'smartReviewPrompt' => __( 'Give me a smart review of my quiz results.', 'learnpress' ), 'quizCorrectTitle' => __( 'Correct', 'learnpress' ), 'quizWrongTitle' => __( 'Incorrect', 'learnpress' ), ), ) ); wp_add_inline_script( 'lp-ai-assistant', 'window.lpAIAssistant = ' . $js_data . ';', 'before' ); } /** * Backward-compatible entrypoint kept for external callers. */ public function render_widget() { $this->render_panel(); } /** * Render the shared footer wrapper for launcher buttons. */ public function render_launcher_wrapper() { ob_start(); do_action( self::FOOTER_LAUNCHERS_HOOK ); $launchers_html = trim( ob_get_clean() ); if ( '' === $launchers_html ) { return; } printf( '
', esc_attr__( 'Learning tools', 'learnpress' ), $launchers_html // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ); } /** * Render the AI Assistant launcher into the shared wrapper. */ public function render_launcher() { if ( ! $this->get_render_state() ) { return; } // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped echo $this->html_toggle(); } /** * Render the AI Assistant panel on wp_footer. */ public function render_panel() { try { $render_state = $this->get_render_state(); if ( ! $render_state ) { return; } $this->localize_script_data( $render_state ); $this->html_panel_widget( $render_state['free_chat_enabled'], $render_state['enabled_actions'] ); } catch ( Throwable $e ) { if ( defined( 'WP_DEBUG' ) && WP_DEBUG ) { // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped echo ''; } } } /** * Get quiz result if user has completed the specific quiz. * * Returns the result array from UserQuizModel::get_result() when the quiz * status is LP_ITEM_COMPLETED, or false if not completed yet. * * @param int $user_id * @param int $quiz_id * @param int $course_id * * @return array|false Result array on completion, false otherwise. */ private function get_completed_quiz_result( int $user_id, int $quiz_id, int $course_id ) { if ( $user_id <= 0 || $quiz_id <= 0 || $course_id <= 0 ) { return false; } $user_quiz = UserQuizModel::find_user_item( $user_id, $quiz_id, LP_QUIZ_CPT, $course_id, LP_COURSE_CPT, true ); if ( ! $user_quiz instanceof UserQuizModel ) { return false; } if ( ! method_exists( $user_quiz, 'get_status' ) || $user_quiz->get_status() !== LP_ITEM_COMPLETED ) { return false; } if ( ! method_exists( $user_quiz, 'get_result' ) ) { return false; } $result = $user_quiz->get_result(); return is_array( $result ) ? $result : false; } /** * Toggle button that opens/closes the chat panel. * * @return string */ public function html_toggle(): string { $icon = ''; $label = sprintf( '%s', esc_html__( 'AI Assistant', 'learnpress' ) ); $section = apply_filters( 'learn-press/ai-assistant/html-toggle', array( 'wrapper' => sprintf( '', ) ); return Template::combine_components( $section ); } /** * Panel header: title + clear and close action buttons. * * @return string */ public function html_header(): string { $title = sprintf( '