PluginProbe
LearnPress – WordPress LMS Plugin for Create and Sell Online Courses / 4.4.9
LearnPress – WordPress LMS Plugin for Create and Sell Online Courses v4.4.9
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 4.1.7.3.1 4.1.7.3.2 All 140 releases
← All changes | inc/rest-api/v1/frontend/class-lp-rest-users-controller.php +140 -96 4.1.6.9.4 → 4.4.9 View file →
@@ -1,6 +1,11 @@
1 1 <?php
2 2
3 +use LearnPress\Models\CourseModel;
4 +use LearnPress\Models\Question\QuestionPostModel;
5 +use LearnPress\Models\Quiz\QuizQuestionModel;
6 +use LearnPress\Models\UserItems\UserQuizModel;
7 +
3 8 /**
4 9 * Class LP_REST_Users_Controller
5 10 *
6 11 * @since 3.3.0
@@ -53,24 +58,13 @@
53 58 ),
54 59 'submit-quiz' => array(
55 60 array(
56 61 'methods' => WP_REST_Server::EDITABLE,
57 - // 'callback' => array( $this, 'submit_quiz' ),
58 - 'callback' => array( $this, 'submit_quiz_new' ),
59 - // 'permission_callback' => array( $this, 'check_admin_permission' ),
62 + 'callback' => array( $this, 'submit_quiz' ),
60 63 'permission_callback' => '__return_true',
61 64 'args' => $this->get_item_endpoint_args(),
62 65 ),
63 66 ),
64 - 'hint-answer' => array(
65 - array(
66 - 'methods' => WP_REST_Server::EDITABLE,
67 - 'callback' => array( $this, 'hint_answer' ),
68 - // 'permission_callback' => array( $this, 'check_admin_permission' ),
69 - 'permission_callback' => '__return_true',
70 - 'args' => $this->get_item_endpoint_args(),
71 - ),
72 - ),
73 67 'check-answer' => array(
74 68 array(
75 69 'methods' => WP_REST_Server::EDITABLE,
76 70 'callback' => array( $this, 'check_answer' ),
@@ -91,15 +85,15 @@
91 85 */
92 86 public function get_item_endpoint_args() {
93 87 return array(
94 88 'item_id' => array(
95 - 'description' => __( 'The ID of course item object.', 'learnpress' ),
89 + 'description' => __( 'The ID of the course item object.', 'learnpress' ),
96 90 'type' => 'int',
97 91 'validate_callback' => array( $this, 'validate_arg' ),
98 92 'required' => true,
99 93 ),
100 94 'course_id' => array(
101 - 'description' => __( 'The ID of course object.', 'learnpress' ),
95 + 'description' => __( 'The ID of the course object.', 'learnpress' ),
102 96 'type' => 'int',
103 97 'validate_callback' => array( $this, 'validate_arg' ),
104 98 'required' => true,
105 99 ),
@@ -134,9 +128,9 @@
134 128 *
135 129 * @param WP_REST_Request $request
136 130 *
137 131 * @editor tungnx
138 - * @version 1.0.1
132 + * @version 1.0.4
139 133 * @sicne 4.0.0
140 134 * @return WP_REST_Response
141 135 */
142 136 public function start_quiz( WP_REST_Request $request ): WP_REST_Response {
@@ -157,44 +151,62 @@
157 151 $user = learn_press_get_user( $user_id );
158 152 $course = learn_press_get_course( $course_id );
159 153 $quiz = learn_press_get_quiz( $item_id );
160 154
161 - if ( ! $course ) {
162 - throw new Exception( __( 'Course is invalid!', 'learnpress' ) );
155 + $courseModel = CourseModel::find( $course_id, true );
156 + if ( ! $courseModel ) {
157 + throw new Exception( esc_html__( 'The course is invalid', 'learnpress' ) );
163 158 }
164 159
165 - if ( ! $quiz ) {
166 - throw new Exception( __( 'Quiz is invalid!', 'learnpress' ) );
160 + $quizPostModel = $courseModel->get_item_model( $item_id, LP_QUIZ_CPT );
161 + if ( ! $quizPostModel ) {
162 + throw new Exception( esc_html__( 'The quiz is invalid', 'learnpress' ) );
167 163 }
168 164
169 - // For no required enroll course
165 + $quiz->set_course( $course );
166 +
167 + do_action( 'learn-press/user/before/start-quiz', $item_id, $course_id, $user_id );
168 +
169 + $link = LP_Helper::get_link_no_cache( $courseModel->get_item_link( $item_id, LP_QUIZ_CPT ) );
170 +
171 + // For no required enroll course (no need login)
170 172 if ( $user->is_guest() && $course->is_no_required_enroll() ) {
171 - if ( $quiz->get_retake_count() >= 0 ) {
172 - //learn_press_remove_cookie( 'quiz_submit_status_' . $course_id . '_' . $item_id . '' );
173 - }
174 - $no_required_enroll = new LP_Course_No_Required_Enroll( $course );
175 - $response = $no_required_enroll->guest_start_quiz( $quiz );
173 + $no_required_enroll = new LP_Course_No_Required_Enroll( $course );
174 + $response = $no_required_enroll->guest_start_quiz( $quiz );
175 + $response['redirect_url'] = $link;
176 176
177 177 return rest_ensure_response( $response );
178 178 }
179 179
180 - // Require enroll course
181 - if ( $user->has_started_quiz( $item_id, $course_id ) ) {
182 - /**
183 - * @var LP_User_Item_Quiz $user_quiz
184 - */
185 - $user_quiz = $user->retake_quiz( $item_id, $course_id, true );
186 - $results['answered'] = []; // Reset answered before on js
187 - } else {
188 - /**
189 - * @var LP_User_Item_Quiz $user_quiz
190 - */
191 - $user_quiz = $user->start_quiz( $item_id, $course_id, true );
180 + /**
181 + * Require enroll course
182 + *
183 + * @var UserQuizModel $user_quiz
184 + */
185 + $retaken_count = 0;
186 + $attempts = [];
187 + $filter_user_quiz = new LP_User_Items_Filter();
188 + $filter_user_quiz->user_id = $user_id;
189 + $filter_user_quiz->item_id = $item_id;
190 + $filter_user_quiz->ref_id = $course_id;
191 + $user_quiz_exists = UserQuizModel::get_user_item_model_from_db( $filter_user_quiz );
192 + if ( $user_quiz_exists instanceof UserQuizModel
193 + && $user_quiz_exists->status === LP_ITEM_COMPLETED ) {
194 + $user_quiz = $user_quiz_exists;
195 + $user_quiz->retake();
196 + $results['answered'] = []; // Reset answered for js
197 + $retaken_count = $user_quiz->get_retaken_count();
198 + $attempts = $user_quiz->get_attempts();
199 + } else { // Create new user quiz and insert to database.
200 + $user_quiz_new = new UserQuizModel();
201 + $user_quiz_new->user_id = $user_id;
202 + $user_quiz_new->item_id = $item_id;
203 + $user_quiz_new->ref_id = $course_id;
204 + $user_quiz_new->start_quiz();
205 + $user_quiz = $user_quiz_new;
192 206 }
193 207
194 - if ( is_wp_error( $user_quiz ) ) {
195 - throw new Exception( $user_quiz->get_error_message() );
196 - }
208 + $user_item_id = $user_quiz->get_user_item_id();
197 209
198 210 /**
199 211 * Clear cache result quiz
200 212 * Cache set on @see LP_User_Item_Quiz::get_results
@@ -208,34 +220,33 @@
208 220 $duration = $quiz->get_duration();
209 221 $show_correct_review = $quiz->get_show_correct_review();
210 222 $question_ids = $quiz->get_question_ids();
211 223 $status = $user_quiz->get_status();
212 - $checked_questions = $user_quiz->get_checked_questions();
213 - $hinted_questions = $user_quiz->get_hint_questions();
214 - $time_remaining = $user_quiz->get_timestamp_remaining();
224 + $time_remaining = $user_quiz->get_time_remaining();
215 225
216 226 $questions = learn_press_rest_prepare_user_questions(
217 227 $question_ids,
218 228 array(
219 229 'instant_check' => $show_check,
220 - 'quiz_status' => $status,
221 - 'checked_questions' => $checked_questions,
222 - 'hinted_questions' => $hinted_questions,
230 + //'quiz_status' => $status,
231 + //'checked_questions' => $checked_questions,
232 + //'hinted_questions' => $hinted_questions,
223 233 'answered' => [],
224 234 'show_correct_review' => $show_correct_review,
225 235 )
226 236 );
227 237
228 - $results['question_ids'] = $question_ids;
229 - $results['questions'] = $questions;
230 - $results['total_time'] = $time_remaining;
231 - $results['duration'] = $duration ? $duration->get() : false;
232 - $results['status'] = $status; // Must be started
233 - $results['retaken'] = absint( $user_quiz->get_retaken_count() );
234 - $results['attempts'] = $user_quiz->get_attempts();
235 - $results['user_item_id'] = $user_quiz->get_user_item_id();
236 - $response['status'] = 'success';
237 - $response['results'] = $results;
238 + $results['question_ids'] = $question_ids;
239 + $results['questions'] = $questions;
240 + $results['total_time'] = $time_remaining;
241 + $results['duration'] = $duration ? $duration->get() : false;
242 + $results['status'] = $status; // Must be started
243 + $results['retaken'] = $retaken_count;
244 + $results['attempts'] = $attempts;
245 + $results['user_item_id'] = $user_item_id;
246 + $response['status'] = 'success';
247 + $response['results'] = $results;
248 + $response['redirect_url'] = $link;
238 249 } catch ( Throwable $e ) {
239 250 $response['message'] = $e->getMessage();
240 251 }
241 252
@@ -249,11 +260,11 @@
249 260 *
250 261 * @return WP_Error|WP_HTTP_Response|WP_REST_Response
251 262 * @editor tungnx
252 263 * @modify 4.1.4.1
253 - * @version 1.0.1
264 + * @version 1.0.3
254 265 */
255 - public function submit_quiz_new( WP_REST_Request $request ) {
266 + public function submit_quiz( WP_REST_Request $request ) {
256 267 //$response = new LP_REST_Response();
257 268 $response = array(
258 269 'status' => 'error',
259 270 'message' => '',
@@ -267,23 +278,29 @@
267 278 $time_spend = $request['time_spend'] ?? 0;
268 279 $user = learn_press_get_user( $user_id );
269 280 $course = learn_press_get_course( $course_id );
270 281
271 - if ( ! $course ) {
272 - throw new Exception( 'Course is invalid!' );
282 + $courseModel = CourseModel::find( $course_id, true );
283 + if ( ! $courseModel ) {
284 + throw new Exception( esc_html__( 'The course is invalid', 'learnpress' ) );
273 285 }
274 286
275 - // Course is no required enroll
287 + $quizPostModel = $courseModel->get_item_model( $item_id, LP_QUIZ_CPT );
288 + if ( ! $quizPostModel ) {
289 + throw new Exception( esc_html__( 'The quiz is invalid', 'learnpress' ) );
290 + }
291 +
292 + // Use for Review Quiz.
293 + $quiz = learn_press_get_quiz( $item_id );
294 + if ( ! $quiz ) {
295 + throw new Exception( esc_html__( 'The quiz is invalid!', 'learnpress' ) );
296 + }
297 + $quiz->set_course( $course );
298 +
299 + // Course is no required enroll (no need login)
276 300 if ( $course->is_no_required_enroll() ) {
277 301 $no_required_enroll = new LP_Course_No_Required_Enroll( $course );
278 302
279 - // Use for Review Quiz.
280 - $quiz = learn_press_get_quiz( $item_id );
281 -
282 - if ( ! $quiz ) {
283 - throw new Exception( __( 'Quiz is invalid!', 'learnpress' ) );
284 - }
285 -
286 303 $result = $no_required_enroll->get_result_quiz( $quiz, $answered );
287 304
288 305 // Set time spent
289 306 $interval = new LP_Duration( $time_spend );
@@ -297,10 +314,8 @@
297 314 $result['results'] = $result;
298 315 $response['status'] = 'success';
299 316 $response['results'] = $result;
300 317
301 - //learn_press_setcookie( 'quiz_submit_status_' . $course_id . '_' . $item_id . '', 'completed', time() + ( 7 * DAY_IN_SECONDS ), false );
302 -
303 318 return rest_ensure_response( $response );
304 319 }
305 320
306 321 $user_course = $user->get_course_data( $course_id );
@@ -309,17 +324,16 @@
309 324 if ( ! $user_course ) {
310 325 throw new Exception( 'User not enrolled course!' );
311 326 }
312 327
328 + /**
329 + * @var LP_User_Item_Quiz $user_quiz
330 + */
313 331 $user_quiz = $user_course->get_item( $item_id );
314 -
315 332 if ( ! $user_quiz ) {
316 333 throw new Exception();
317 334 }
318 335
319 - $end_time = gmdate( 'Y-m-d H:i:s', strtotime( $user_quiz->get_start_time( 'mysql' ) . " + $time_spend second" ) );
320 - $user_quiz->set_end_time( $end_time );
321 -
322 336 // For case save result when check instant answer
323 337 $result_instant_check = LP_User_Items_Result_DB::instance()->get_result( $user_quiz->get_user_item_id() );
324 338 if ( $result_instant_check ) {
325 339 foreach ( $result_instant_check['questions'] as $question_answer_id => $question_answer ) {
@@ -328,9 +342,13 @@
328 342 }
329 343 }
330 344 }
331 345
332 - // Calculate quiz result and save
346 + // Set end time.
347 + $start_time = $user_quiz->get_start_time()->getTimestamp();
348 + $user_quiz->set_end_time( $start_time + $time_spend );
349 +
350 + // Calculate quiz result and save.
333 351 $result = $user_quiz->calculate_quiz_result( $answered );
334 352 // Save
335 353 LP_User_Items_Result_DB::instance()->update( $user_quiz->get_user_item_id(), wp_json_encode( $result ) );
336 354
@@ -341,10 +359,18 @@
341 359 }
342 360
343 361 $user_quiz->complete();
344 362
345 - do_action( 'learn-press/user/quiz-finished', $item_id, $course_id, $user_id );
363 + /**
364 + * Clear cache result quiz
365 + * Cache set on @see LP_User_Item_Quiz::get_results, and start_quiz() clears the same
366 + * key. Without this the next read still serves the in-progress result and the
367 + * submitted answers all come back as skipped.
368 + */
369 + $user_quiz->clear_results_cache();
346 370
371 + do_action( 'learn-press/user/quiz-finished', $item_id, $course_id, $user_id, $user_quiz );
372 +
347 373 $result['status'] = $user_quiz->get_status(); // Must be completed
348 374 $result['attempts'] = $user_quiz->get_attempts();
349 375 $result['answered'] = $result['questions'];
350 376 $result['results'] = $result;
@@ -357,27 +383,17 @@
357 383 return rest_ensure_response( $response );
358 384 }
359 385
360 386 /**
361 - * Hint the question and response hint content.
387 + * Check answer
362 388 *
363 389 * @param WP_REST_Request $request
364 - *
365 - * @return mixed|WP_REST_Response
390 + * @return WP_Error|WP_HTTP_Response|WP_REST_Response
391 + * @since 4.1.4.1
392 + * @version 1.0.2
366 393 */
367 - public function hint_answer( $request ) {
368 - $question_id = $request['question_id'];
369 - $question = learn_press_get_question( $question_id );
370 -
394 + public function check_answer( WP_REST_Request $request ) {
371 395 $response = array(
372 - 'hint' => $question->get_hint(),
373 - );
374 -
375 - return rest_ensure_response( $response );
376 - }
377 -
378 - public function check_answer( $request ) {
379 - $response = array(
380 396 'status' => 'error',
381 397 'message' => '',
382 398 );
383 399
@@ -388,25 +404,47 @@
388 404 $quiz_id = $request['item_id'] ?? 0;
389 405 $course = learn_press_get_course( $course_id );
390 406 $checked = [];
391 407
408 + $courseModel = CourseModel::find( $course_id, true );
409 + if ( ! $courseModel ) {
410 + throw new Exception( esc_html__( 'The course is invalid', 'learnpress' ) );
411 + }
412 +
413 + $quizPostModel = $courseModel->get_item_model( $quiz_id, LP_QUIZ_CPT );
414 + if ( ! $quizPostModel ) {
415 + throw new Exception( esc_html__( 'The quiz is invalid', 'learnpress' ) );
416 + }
417 +
418 + // Check question has in quiz
419 + $quizQuestionModel = QuizQuestionModel::find( $quiz_id, $question_id );
420 + if ( ! $quizQuestionModel ) {
421 + throw new Exception( esc_html__( 'The quiz question is invalid', 'learnpress' ) );
422 + }
423 +
424 + $questionModel = QuestionPostModel::find( $question_id, true );
425 + if ( ! $questionModel ) {
426 + throw new Exception( esc_html__( 'The question is invalid', 'learnpress' ) );
427 + }
428 +
429 + // For case user not login
392 430 if ( $course->is_no_required_enroll() ) {
393 431 $no_required_enroll = new LP_Course_No_Required_Enroll( $course );
394 432 $checked = $no_required_enroll->guest_check_question( $question_id, $answered );
395 - } else {
433 + } else { // For case user logged in
396 434 $user = learn_press_get_current_user();
397 435 if ( $user->is_guest() ) {
398 - throw new Exception( 'User is invalid!', 'learnrpess' );
436 + throw new Exception( esc_html__( 'You must be logged in to check answer.', 'learnpress' ) );
399 437 }
400 438
401 439 $user_course = $user->get_course_data( $course_id );
402 440 if ( ! $user_course ) {
403 - throw new Exception( 'User\'s course no data!', 'learnrpess' );
441 + throw new Exception( esc_html__( 'User\'s course no data!', 'learnpress' ) );
404 442 }
405 443
406 444 $user_quiz = $user_course->get_item( $quiz_id );
407 445 if ( ! $user_quiz ) {
408 - throw new Exception( 'User\'s quiz no data!', 'learnrpess' );
446 + throw new Exception( esc_html__( 'User\'s quiz no data!', 'learnpress' ) );
409 447 }
410 448
411 449 $checked = $user_quiz->instant_check_question( $question_id, $answered );
412 450 }
@@ -412,9 +450,15 @@
412 450 }
413 451
414 452 $question = learn_press_get_question( $question_id );
415 453 $response['explanation'] = $question->get_explanation();
416 - $response['options'] = learn_press_get_question_options_for_js( $question, array( 'answer' => $answered ) );
454 + $response['options'] = learn_press_get_question_options_for_js(
455 + $question,
456 + array(
457 + 'include_is_true' => $quizPostModel->has_instant_check(),
458 + 'answer' => $answered,
459 + )
460 + );
417 461 $response['result'] = $checked;
418 462 $response['status'] = 'success';
419 463 } catch ( Throwable $e ) {
420 464 $response['message'] = $e->getMessage();