PluginProbe
LearnPress – WordPress LMS Plugin for Create and Sell Online Courses / 4.4.10
LearnPress – WordPress LMS Plugin for Create and Sell Online Courses v4.4.10
4.4.10 4.4.9.1 4.4.9 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 All 142 releases
← All changes | inc/jwt/rest-api/version1/class-lp-rest-lessons-v1-controller.php +117 -24 4.1.7.2 → 4.4.10 View file →
@@ -1,5 +1,12 @@
1 1 <?php
2 +
3 +use LearnPress\Models\CourseModel;
4 +use LearnPress\Models\LessonPostModel;
5 +use LearnPress\Models\UserItems\UserCourseModel;
6 +use LearnPress\Models\UserItems\UserLessonModel;
7 +use LearnPress\Models\UserModel;
8 +
2 9 class LP_Jwt_Lessons_V1_Controller extends LP_REST_Jwt_Posts_Controller {
3 10 protected $namespace = 'learnpress/v1';
4 11
5 12 protected $rest_base = 'lessons';
@@ -28,9 +35,9 @@
28 35 '/' . $this->rest_base . '/(?P<id>[\d]+)',
29 36 array(
30 37 'args' => array(
31 38 'id' => array(
32 - 'description' => esc_html__( 'Unique identifier for the resource.', 'learnpress' ),
39 + 'description' => esc_html__( 'A unique identifier for the resource.', 'learnpress' ),
33 40 'type' => 'integer',
34 41 ),
35 42 ),
36 43 array(
@@ -54,9 +61,9 @@
54 61 '/' . $this->rest_base . '/finish',
55 62 array(
56 63 'args' => array(
57 64 'id' => array(
58 - 'description' => esc_html__( 'Unique identifier for the resource.', 'learnpress' ),
65 + 'description' => esc_html__( 'A unique identifier for the resource.', 'learnpress' ),
59 66 'type' => 'integer',
60 67 ),
61 68 ),
62 69 array(
@@ -75,8 +82,17 @@
75 82 );
76 83 }
77 84
78 85 /**
86 + * Allow guests/unauthenticated users to query the list endpoint (filtered by slug).
87 + * Per-item visibility is still enforced by check_read_permission, which only
88 + * allows preview items for guests.
89 + */
90 + public function get_items_permissions_check( $request ) {
91 + return true;
92 + }
93 +
94 + /**
79 95 * Checks if a course can be read.
80 96 *
81 97 * Correctly handles courses with the inherit status.
82 98 *
@@ -103,8 +119,20 @@
103 119 if ( ! $post_status_obj || ! $post_status_obj->public ) {
104 120 return false;
105 121 }
106 122
123 + // Allow guests and unenrolled users to read items marked as preview.
124 + $course_id = $this->get_course_by_item_id( $post_id );
125 + if ( $course_id ) {
126 + $course = learn_press_get_course( $course_id );
127 + if ( $course ) {
128 + $item = $course->get_item( $post_id );
129 + if ( $item && $item->is_preview() ) {
130 + return true;
131 + }
132 + }
133 + }
134 +
107 135 $user_id = get_current_user_id();
108 136
109 137 if ( ! $user_id ) {
110 138 return false;
@@ -111,11 +139,8 @@
111 139 }
112 140
113 141 $user = learn_press_get_user( $user_id );
114 142
115 - // Get course ID by lesson ID assigned.
116 - $course_id = $this->get_course_by_item_id( $post_id );
117 -
118 143 if ( empty( $course_id ) ) {
119 144 return false;
120 145 }
121 146
@@ -190,41 +215,94 @@
190 215 return false;
191 216 }
192 217
193 218 public function finish_lesson( $request ) {
194 - $response = new LP_REST_Response();
195 - $response->data = new stdClass();
219 + $response = new LP_REST_Response();
196 220
197 221 try {
198 222 $id = isset( $request['id'] ) ? absint( $request['id'] ) : '';
199 -
200 223 if ( empty( $id ) ) {
201 224 throw new Exception( esc_html__( 'Error: No lesson available!.', 'learnpress' ) );
202 225 }
203 226
227 + $lessonModel = LessonPostModel::find( $id, true );
228 + if ( ! $lessonModel ) {
229 + throw new Exception( esc_html__( 'Error: Lesson is invalid!', 'learnpress' ) );
230 + }
231 +
204 232 $course_id = $this->get_course_by_item_id( $id );
205 -
206 233 if ( empty( $course_id ) ) {
207 - throw new Exception( esc_html__( 'Error: This lesson is not assign in Course.', 'learnpress' ) );
234 + throw new Exception( esc_html__( 'Error: This lesson is not assigned in the Course.', 'learnpress' ) );
208 235 }
209 236
210 - $course = learn_press_get_course( $course_id );
211 - $user = learn_press_get_current_user();
237 + $courseModel = CourseModel::find( $course_id, true );
238 + $userModel = UserModel::find( get_current_user_id(), true );
212 239
213 - if ( empty( $course ) || empty( $user ) ) {
240 + if ( empty( $courseModel ) || empty( $userModel ) ) {
214 241 throw new Exception( esc_html__( 'Error: No Course or User available.', 'learnpress' ) );
215 242 }
216 243
217 - $result = $user->complete_lesson( $id, $course_id );
244 + $userLessonModel = UserLessonModel::find_user_item(
245 + get_current_user_id(),
246 + $id,
247 + $lessonModel->post_type,
248 + $course_id,
249 + LP_COURSE_CPT,
250 + true
251 + );
218 252
219 - if ( is_wp_error( $result ) ) {
220 - throw new Exception( $result->get_error_message() ?? esc_html__( 'Error: Cannot complete Lesson', 'learnpress' ) );
253 + // Auto-create user lesson item if user is enrolled but never opened the lesson.
254 + if ( ! $userLessonModel instanceof UserLessonModel ) {
255 + $userCourseModel = UserCourseModel::find( get_current_user_id(), $course_id, true );
256 + if ( ! $userCourseModel instanceof UserCourseModel || ! $userCourseModel->has_enrolled_or_finished() ) {
257 + throw new Exception( __( 'You must enroll the course first.', 'learnpress' ) );
258 + }
259 +
260 + $userLessonModel = new UserLessonModel();
261 + $userLessonModel->user_id = get_current_user_id();
262 + $userLessonModel->item_id = $id;
263 + $userLessonModel->item_type = $lessonModel->post_type;
264 + $userLessonModel->ref_id = $course_id;
265 + $userLessonModel->ref_type = LP_COURSE_CPT;
266 + $userLessonModel->parent_id = $userCourseModel->get_user_item_id();
267 + $userLessonModel->status = LP_ITEM_STARTED;
268 + $userLessonModel->save();
269 + } elseif ( empty( $userLessonModel->parent_id ) ) {
270 + // Repair legacy rows that were created without parent_id — without it, the lesson
271 + // won't be counted by UserCourseModel::count_items_completed().
272 + $userCourseModel = UserCourseModel::find( get_current_user_id(), $course_id, true );
273 + if ( $userCourseModel instanceof UserCourseModel ) {
274 + $userLessonModel->parent_id = $userCourseModel->get_user_item_id();
275 + $userLessonModel->save();
276 + }
221 277 }
222 278
279 + $userLessonModel->set_complete();
280 +
281 + // Recalculate course progress and persist to learnpress_user_item_results.
282 + $userCourseModel = $userLessonModel->get_user_course_model();
283 + if ( $userCourseModel instanceof UserCourseModel ) {
284 + $course_results = $userCourseModel->calculate_course_results( true );
285 + LP_User_Items_Result_DB::instance()->update(
286 + $userCourseModel->get_user_item_id(),
287 + wp_json_encode( $course_results )
288 + );
289 +
290 + // Auto-finish the course when evaluation criteria are met.
291 + $can_finish = $userCourseModel->can_finish();
292 + if ( $can_finish === true && ! is_wp_error( $can_finish ) ) {
293 + try {
294 + $userCourseModel->set_finish();
295 + } catch ( Throwable $finishError ) {
296 + // Non-fatal — user can still click Finish manually.
297 + error_log( __METHOD__ . ' auto-finish: ' . $finishError->getMessage() );
298 + }
299 + }
300 + }
301 +
223 302 $response->status = 'success';
224 - $response->message = esc_html__( 'Congrats! You complete lesson is successfully', 'learnpress' );
225 - } catch ( \Throwable $th ) {
226 - $response->status = 'error';
303 + $response->message = esc_html__( 'Congrats! You have completed the lesson successfully', 'learnpress' );
304 + } catch ( Throwable $th ) {
227 305 $response->message = $th->getMessage();
228 306 }
229 307
230 308 return rest_ensure_response( $response );
@@ -248,13 +326,28 @@
248 326 $data = array();
249 327
250 328 $assigned = $this->get_assigned( $id );
251 329
330 + $course_id = 0;
252 331 if ( ! empty( $assigned ) && method_exists( $object, 'set_course' ) ) {
253 332 $course_id = $assigned['course']['id'];
254 333 $object->set_course( $course_id );
255 334 }
256 335
336 + $userLessonModel = UserLessonModel::find_user_item(
337 + get_current_user_id(),
338 + $id,
339 + LP_LESSON_CPT,
340 + $course_id,
341 + LP_COURSE_CPT,
342 + true
343 + );
344 + $end_time = '';
345 +
346 + if ( $userLessonModel instanceof UserLessonModel ) {
347 + $end_time = $userLessonModel->get_end_time();
348 + }
349 +
257 350 foreach ( $fields as $field ) {
258 351 switch ( $field ) {
259 352 case 'id':
260 353 $data['id'] = $id;
@@ -306,8 +399,9 @@
306 399 break;
307 400 }
308 401 }
309 402
403 + $data['end_time'] = $end_time;
310 404 $data['meta_data'] = $this->get_course_meta( $id );
311 405
312 406 return $data;
313 407 }
@@ -329,9 +423,9 @@
329 423 public function check_can_finish_course( $id ) {
330 424 $user = learn_press_get_current_user();
331 425
332 426 if ( ! $user || ! $id ) {
333 - return falase;
427 + return false;
334 428 }
335 429
336 430 $course_id = $this->get_course_by_item_id( $id );
337 431
@@ -426,9 +520,9 @@
426 520 'title' => $this->post_type,
427 521 'type' => 'object',
428 522 'properties' => array(
429 523 'id' => array(
430 - 'description' => __( 'Unique identifier for the resource.', 'learnpress' ),
524 + 'description' => __( 'A unique identifier for the resource.', 'learnpress' ),
431 525 'type' => 'integer',
432 526 'context' => array( 'view', 'edit' ),
433 527 'readonly' => true,
434 528 ),
@@ -480,9 +574,9 @@
480 574 'enum' => array_merge( array_keys( get_post_statuses() ), array( 'future' ) ),
481 575 'context' => array( 'view', 'edit' ),
482 576 ),
483 577 'content' => array(
484 - 'description' => __( 'Content course.', 'learnpress' ),
578 + 'description' => __( 'Course content.', 'learnpress' ),
485 579 'type' => 'string',
486 580 'context' => array( 'view', 'edit' ),
487 581 ),
488 582 'excerpt' => array(
@@ -490,9 +584,9 @@
490 584 'type' => 'string',
491 585 'context' => array( 'view', 'edit' ),
492 586 ),
493 587 'can_finish_course' => array(
494 - 'description' => __( 'Can finish course', 'learnpress' ),
588 + 'description' => __( 'Can finish the course', 'learnpress' ),
495 589 'type' => 'boolean',
496 590 'context' => array( 'view' ),
497 591 'readonly' => true,
498 592 ),
@@ -601,6 +695,5 @@
601 695 }
602 696
603 697 return $response;
604 698 }
605 -
606 699 }