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
learnpress / inc / rest-api / v1 / frontend / class-lp-rest-users-controller.php

class-lp-rest-users-controller.php in LearnPress – WordPress LMS Plugin for Create and Sell Online Courses 4.4.9, at inc/rest-api/v1/frontend/class-lp-rest-users-controller.php

470 lines 14.1 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
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
8 /**
9 * Class LP_REST_Users_Controller
10 *
11 * @since 3.3.0
12 */
13 class LP_REST_Users_Controller extends LP_Abstract_REST_Controller {
14 /**
15 * @var LP_User
16 */
17 protected $user = null;
18
19 /**
20 * @var LP_Course
21 */
22 protected $course = null;
23
24 /**
25 * @var LP_Course_Item|LP_Quiz|LP_Lesson
26 */
27 protected $item = null;
28
29 /**
30 * @var LP_User_Item_Course
31 */
32 protected $user_course = null;
33
34 /**
35 * @var LP_User_Item|LP_User_Item_Quiz
36 */
37 protected $user_item = null;
38
39 public function __construct() {
40 $this->namespace = 'lp/v1';
41 $this->rest_base = 'users';
42 parent::__construct();
43 }
44
45 /**
46 * Register rest routes.
47 */
48 public function register_routes() {
49 $this->routes = array(
50 'start-quiz' => array(
51 array(
52 'methods' => WP_REST_Server::EDITABLE,
53 'callback' => array( $this, 'start_quiz' ),
54 // 'permission_callback' => array( $this, 'check_admin_permission' ),
55 'permission_callback' => '__return_true',
56 'args' => $this->get_item_endpoint_args(),
57 ),
58 ),
59 'submit-quiz' => array(
60 array(
61 'methods' => WP_REST_Server::EDITABLE,
62 'callback' => array( $this, 'submit_quiz' ),
63 'permission_callback' => '__return_true',
64 'args' => $this->get_item_endpoint_args(),
65 ),
66 ),
67 'check-answer' => array(
68 array(
69 'methods' => WP_REST_Server::EDITABLE,
70 'callback' => array( $this, 'check_answer' ),
71 // 'permission_callback' => array( $this, 'check_admin_permission' ),
72 'permission_callback' => '__return_true',
73 'args' => $this->get_item_endpoint_args(),
74 ),
75 ),
76 );
77
78 parent::register_routes();
79 }
80
81 /**
82 * Get args for user item endpoints.
83 *
84 * @return array
85 */
86 public function get_item_endpoint_args() {
87 return array(
88 'item_id' => array(
89 'description' => __( 'The ID of the course item object.', 'learnpress' ),
90 'type' => 'int',
91 'validate_callback' => array( $this, 'validate_arg' ),
92 'required' => true,
93 ),
94 'course_id' => array(
95 'description' => __( 'The ID of the course object.', 'learnpress' ),
96 'type' => 'int',
97 'validate_callback' => array( $this, 'validate_arg' ),
98 'required' => true,
99 ),
100 );
101 }
102
103 /**
104 * Validation callback to verify rest args.
105 *
106 * @param mixed $value
107 * @param WP_REST_Request $request
108 * @param string $param
109 *
110 * @return bool|WP_Error
111 */
112 public function validate_arg( $value, $request, $param ) {
113 $attributes = $request->get_attributes();
114
115 if ( ! isset( $attributes['args'][ $param ] ) ) {
116 return new WP_Error( 'rest_invalid_param', sprintf( __( '%s was not registered as a request argument.', 'learnpress' ), $param ), array( 'status' => 400 ) );
117 }
118
119 return true;
120 }
121
122 public function check_admin_permission() {
123 return LP_Abstract_API::check_admin_permission();
124 }
125
126 /**
127 * User starts a quiz.
128 *
129 * @param WP_REST_Request $request
130 *
131 * @editor tungnx
132 * @version 1.0.4
133 * @sicne 4.0.0
134 * @return WP_REST_Response
135 */
136 public function start_quiz( WP_REST_Request $request ): WP_REST_Response {
137 $user_id = get_current_user_id();
138 $item_id = $request['item_id'] ?? 0;
139 $course_id = $request['course_id'] ?? 0;
140 $results = array(
141 'question_ids' => array(),
142 'questions' => array(),
143 'total_time' => 0,
144 );
145 $response = array(
146 'status' => 'error',
147 'message' => '',
148 );
149
150 try {
151 $user = learn_press_get_user( $user_id );
152 $course = learn_press_get_course( $course_id );
153 $quiz = learn_press_get_quiz( $item_id );
154
155 $courseModel = CourseModel::find( $course_id, true );
156 if ( ! $courseModel ) {
157 throw new Exception( esc_html__( 'The course is invalid', 'learnpress' ) );
158 }
159
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' ) );
163 }
164
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)
172 if ( $user->is_guest() && $course->is_no_required_enroll() ) {
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
177 return rest_ensure_response( $response );
178 }
179
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;
206 }
207
208 $user_item_id = $user_quiz->get_user_item_id();
209
210 /**
211 * Clear cache result quiz
212 * Cache set on @see LP_User_Item_Quiz::get_results
213 */
214 $lp_quiz_cache = LP_Quiz_Cache::instance();
215 $key_cache = sprintf( '%d/user/%d/course/%d', $item_id, $user_id, $course_id );
216 $lp_quiz_cache->clear( $key_cache );
217 // End
218
219 $show_check = $quiz->get_instant_check();
220 $duration = $quiz->get_duration();
221 $show_correct_review = $quiz->get_show_correct_review();
222 $question_ids = $quiz->get_question_ids();
223 $status = $user_quiz->get_status();
224 $time_remaining = $user_quiz->get_time_remaining();
225
226 $questions = learn_press_rest_prepare_user_questions(
227 $question_ids,
228 array(
229 'instant_check' => $show_check,
230 //'quiz_status' => $status,
231 //'checked_questions' => $checked_questions,
232 //'hinted_questions' => $hinted_questions,
233 'answered' => [],
234 'show_correct_review' => $show_correct_review,
235 )
236 );
237
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;
249 } catch ( Throwable $e ) {
250 $response['message'] = $e->getMessage();
251 }
252
253 return rest_ensure_response( $response );
254 }
255
256 /**
257 * Submit quiz answer
258 *
259 * @param WP_REST_Request $request
260 *
261 * @return WP_Error|WP_HTTP_Response|WP_REST_Response
262 * @editor tungnx
263 * @modify 4.1.4.1
264 * @version 1.0.3
265 */
266 public function submit_quiz( WP_REST_Request $request ) {
267 //$response = new LP_REST_Response();
268 $response = array(
269 'status' => 'error',
270 'message' => '',
271 );
272
273 try {
274 $user_id = get_current_user_id();
275 $item_id = $request['item_id'] ?? 0;
276 $course_id = $request['course_id'] ?? 0;
277 $answered = $request['answered'] ?? [];
278 $time_spend = $request['time_spend'] ?? 0;
279 $user = learn_press_get_user( $user_id );
280 $course = learn_press_get_course( $course_id );
281
282 $courseModel = CourseModel::find( $course_id, true );
283 if ( ! $courseModel ) {
284 throw new Exception( esc_html__( 'The course is invalid', 'learnpress' ) );
285 }
286
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)
300 if ( $course->is_no_required_enroll() ) {
301 $no_required_enroll = new LP_Course_No_Required_Enroll( $course );
302
303 $result = $no_required_enroll->get_result_quiz( $quiz, $answered );
304
305 // Set time spent
306 $interval = new LP_Duration( $time_spend );
307 $interval = $interval->to_timer();
308 $result['time_spend'] = $interval;
309 // End
310
311 $result['status'] = LP_ITEM_COMPLETED;
312 //$result['answered'] = $result['questions'];
313 $result['attempts'] = [];
314 $result['results'] = $result;
315 $response['status'] = 'success';
316 $response['results'] = $result;
317
318 return rest_ensure_response( $response );
319 }
320
321 $user_course = $user->get_course_data( $course_id );
322
323 // Course required enroll
324 if ( ! $user_course ) {
325 throw new Exception( 'User not enrolled course!' );
326 }
327
328 /**
329 * @var LP_User_Item_Quiz $user_quiz
330 */
331 $user_quiz = $user_course->get_item( $item_id );
332 if ( ! $user_quiz ) {
333 throw new Exception();
334 }
335
336 // For case save result when check instant answer
337 $result_instant_check = LP_User_Items_Result_DB::instance()->get_result( $user_quiz->get_user_item_id() );
338 if ( $result_instant_check ) {
339 foreach ( $result_instant_check['questions'] as $question_answer_id => $question_answer ) {
340 if ( ! empty( $question_answer['answered'] ) ) {
341 $answered[ $question_answer_id ] = $question_answer['answered'];
342 }
343 }
344 }
345
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.
351 $result = $user_quiz->calculate_quiz_result( $answered );
352 // Save
353 LP_User_Items_Result_DB::instance()->update( $user_quiz->get_user_item_id(), wp_json_encode( $result ) );
354
355 if ( $result['pass'] ) {
356 $user_quiz->set_graduation( LP_COURSE_GRADUATION_PASSED );
357 } else {
358 $user_quiz->set_graduation( LP_COURSE_GRADUATION_FAILED );
359 }
360
361 $user_quiz->complete();
362
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();
370
371 do_action( 'learn-press/user/quiz-finished', $item_id, $course_id, $user_id, $user_quiz );
372
373 $result['status'] = $user_quiz->get_status(); // Must be completed
374 $result['attempts'] = $user_quiz->get_attempts();
375 $result['answered'] = $result['questions'];
376 $result['results'] = $result;
377 $response['status'] = 'success';
378 $response['results'] = $result;
379 } catch ( Throwable $e ) {
380 $response['message'] = $e->getMessage();
381 }
382
383 return rest_ensure_response( $response );
384 }
385
386 /**
387 * Check answer
388 *
389 * @param WP_REST_Request $request
390 * @return WP_Error|WP_HTTP_Response|WP_REST_Response
391 * @since 4.1.4.1
392 * @version 1.0.2
393 */
394 public function check_answer( WP_REST_Request $request ) {
395 $response = array(
396 'status' => 'error',
397 'message' => '',
398 );
399
400 try {
401 $question_id = $request['question_id'] ?? 0;
402 $answered = $request['answered'] ?? '';
403 $course_id = $request['course_id'] ?? 0;
404 $quiz_id = $request['item_id'] ?? 0;
405 $course = learn_press_get_course( $course_id );
406 $checked = [];
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
430 if ( $course->is_no_required_enroll() ) {
431 $no_required_enroll = new LP_Course_No_Required_Enroll( $course );
432 $checked = $no_required_enroll->guest_check_question( $question_id, $answered );
433 } else { // For case user logged in
434 $user = learn_press_get_current_user();
435 if ( $user->is_guest() ) {
436 throw new Exception( esc_html__( 'You must be logged in to check answer.', 'learnpress' ) );
437 }
438
439 $user_course = $user->get_course_data( $course_id );
440 if ( ! $user_course ) {
441 throw new Exception( esc_html__( 'User\'s course no data!', 'learnpress' ) );
442 }
443
444 $user_quiz = $user_course->get_item( $quiz_id );
445 if ( ! $user_quiz ) {
446 throw new Exception( esc_html__( 'User\'s quiz no data!', 'learnpress' ) );
447 }
448
449 $checked = $user_quiz->instant_check_question( $question_id, $answered );
450 }
451
452 $question = learn_press_get_question( $question_id );
453 $response['explanation'] = $question->get_explanation();
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 );
461 $response['result'] = $checked;
462 $response['status'] = 'success';
463 } catch ( Throwable $e ) {
464 $response['message'] = $e->getMessage();
465 }
466
467 return rest_ensure_response( $response );
468 }
469 }
470