PluginProbe
LearnPress – WordPress LMS Plugin for Create and Sell Online Courses / 4.4.8
LearnPress – WordPress LMS Plugin for Create and Sell Online Courses v4.4.8
4.4.8 4.4.7 4.4.6 4.4.5 4.4.4 4.4.3 4.4.2 4.4.1 4.4.0 4.3.9.1 4.3.9 4.3.8 4.3.7 4.1.6.9 4.1.6.9.1 4.1.6.9.2 4.1.6.9.3 4.1.6.9.4 4.1.7 4.1.7.1 4.1.7.2 4.1.7.3 4.1.7.3.1 4.1.7.3.2 4.2.0 All 139 releases
← All changes | inc/TemplateHooks/Course/CourseAIAssistantTemplate.php +20 -0 4.4.14.4.8 View file →
@@ -126,11 +126,27 @@
126 126
127 127 $context = $this->detect_context();
128 128 $item = LP_Global::course_item();
129 129 $item_id = $item ? absint( $item->get_id() ) : 0;
130 + $item_type = $item ? (string) $item->get_item_type() : '';
130 131 $course_id = $item ? absint( $item->get_course_id() ) : 0;
131 132 $user_id = get_current_user_id();
132 133
134 + /**
135 + * Defense in depth: run the same resolver the AJAX controller uses, so the widget
136 + * is never offered for an item the user cannot view. This is not the security
137 + * boundary — AIAssistantController::handle_chat() is, because the AJAX action is
138 + * reachable without this markup ever rendering.
139 + *
140 + * Catches Throwable because this runs on wp_enqueue_scripts, outside the
141 + * render_panel() try/catch. Any failure denies rather than fatals the page.
142 + */
143 + try {
144 + AIAssistantController::resolve_item_access( $user_id, $course_id, $item_type, $item_id );
145 + } catch ( Throwable $e ) {
146 + return $this->render_state = false;
147 + }
148 +
133 149 $enabled_actions = AIAssistantController::get_enabled_actions();
134 150 $free_chat_enabled = LP_Settings::get_option( 'ai_assistant_free_chat', 'no' ) === 'yes';
135 151
136 152 if ( $context === 'quiz' ) {
@@ -162,8 +178,9 @@
162 178
163 179 return $this->render_state = array(
164 180 'context' => $context,
165 181 'item_id' => $item_id,
182 + 'item_type' => $item_type,
166 183 'course_id' => $course_id,
167 184 'enabled_actions' => $enabled_actions,
168 185 'free_chat_enabled' => $free_chat_enabled,
169 186 'quiz_result' => $quiz_result,
@@ -181,8 +198,11 @@
181 198 'ajaxUrl' => LP_Settings::url_handle_lp_ajax(),
182 199 'nonce' => wp_create_nonce( 'wp_rest' ),
183 200 'lessonId' => $render_state['item_id'],
184 201 'itemId' => $render_state['item_id'],
202 + // Server-resolved curriculum type. The client echoes it back as item_type
203 + // and the server re-validates it; it is transport, not proof.
204 + 'itemType' => $render_state['item_type'],
185 205 'courseId' => $render_state['course_id'],
186 206 'context' => $render_state['context'],
187 207 'quizCompleted' => $render_state['context'] === 'quiz',
188 208 'quizResult' => $render_state['quiz_result'],