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 +469 -443 4.3.8 → 4.4.9 View file →
@@ -1,443 +1,469 @@
1 -<?php
2 -
3 -use LearnPress\Models\CourseModel;
4 -use LearnPress\Models\UserItemMeta\UserQuizMetaModel;
5 -use LearnPress\Models\UserItems\UserQuizModel;
6 -
7 -/**
8 - * Class LP_REST_Users_Controller
9 - *
10 - * @since 3.3.0
11 - */
12 -class LP_REST_Users_Controller extends LP_Abstract_REST_Controller {
13 - /**
14 - * @var LP_User
15 - */
16 - protected $user = null;
17 -
18 - /**
19 - * @var LP_Course
20 - */
21 - protected $course = null;
22 -
23 - /**
24 - * @var LP_Course_Item|LP_Quiz|LP_Lesson
25 - */
26 - protected $item = null;
27 -
28 - /**
29 - * @var LP_User_Item_Course
30 - */
31 - protected $user_course = null;
32 -
33 - /**
34 - * @var LP_User_Item|LP_User_Item_Quiz
35 - */
36 - protected $user_item = null;
37 -
38 - public function __construct() {
39 - $this->namespace = 'lp/v1';
40 - $this->rest_base = 'users';
41 - parent::__construct();
42 - }
43 -
44 - /**
45 - * Register rest routes.
46 - */
47 - public function register_routes() {
48 - $this->routes = array(
49 - 'start-quiz' => array(
50 - array(
51 - 'methods' => WP_REST_Server::EDITABLE,
52 - 'callback' => array( $this, 'start_quiz' ),
53 - // 'permission_callback' => array( $this, 'check_admin_permission' ),
54 - 'permission_callback' => '__return_true',
55 - 'args' => $this->get_item_endpoint_args(),
56 - ),
57 - ),
58 - 'submit-quiz' => array(
59 - array(
60 - 'methods' => WP_REST_Server::EDITABLE,
61 - 'callback' => array( $this, 'submit_quiz' ),
62 - 'permission_callback' => '__return_true',
63 - 'args' => $this->get_item_endpoint_args(),
64 - ),
65 - ),
66 - /*'hint-answer' => array(
67 - array(
68 - 'methods' => WP_REST_Server::EDITABLE,
69 - 'callback' => array( $this, 'hint_answer' ),
70 - // 'permission_callback' => array( $this, 'check_admin_permission' ),
71 - 'permission_callback' => '__return_true',
72 - 'args' => $this->get_item_endpoint_args(),
73 - ),
74 - ),*/
75 - 'check-answer' => array(
76 - array(
77 - 'methods' => WP_REST_Server::EDITABLE,
78 - 'callback' => array( $this, 'check_answer' ),
79 - // 'permission_callback' => array( $this, 'check_admin_permission' ),
80 - 'permission_callback' => '__return_true',
81 - 'args' => $this->get_item_endpoint_args(),
82 - ),
83 - ),
84 - );
85 -
86 - parent::register_routes();
87 - }
88 -
89 - /**
90 - * Get args for user item endpoints.
91 - *
92 - * @return array
93 - */
94 - public function get_item_endpoint_args() {
95 - return array(
96 - 'item_id' => array(
97 - 'description' => __( 'The ID of the course item object.', 'learnpress' ),
98 - 'type' => 'int',
99 - 'validate_callback' => array( $this, 'validate_arg' ),
100 - 'required' => true,
101 - ),
102 - 'course_id' => array(
103 - 'description' => __( 'The ID of the course object.', 'learnpress' ),
104 - 'type' => 'int',
105 - 'validate_callback' => array( $this, 'validate_arg' ),
106 - 'required' => true,
107 - ),
108 - );
109 - }
110 -
111 - /**
112 - * Validation callback to verify rest args.
113 - *
114 - * @param mixed $value
115 - * @param WP_REST_Request $request
116 - * @param string $param
117 - *
118 - * @return bool|WP_Error
119 - */
120 - public function validate_arg( $value, $request, $param ) {
121 - $attributes = $request->get_attributes();
122 -
123 - if ( ! isset( $attributes['args'][ $param ] ) ) {
124 - return new WP_Error( 'rest_invalid_param', sprintf( __( '%s was not registered as a request argument.', 'learnpress' ), $param ), array( 'status' => 400 ) );
125 - }
126 -
127 - return true;
128 - }
129 -
130 - public function check_admin_permission() {
131 - return LP_Abstract_API::check_admin_permission();
132 - }
133 -
134 - /**
135 - * User starts a quiz.
136 - *
137 - * @param WP_REST_Request $request
138 - *
139 - * @editor tungnx
140 - * @version 1.0.2
141 - * @sicne 4.0.0
142 - * @return WP_REST_Response
143 - */
144 - public function start_quiz( WP_REST_Request $request ): WP_REST_Response {
145 - $user_id = get_current_user_id();
146 - $item_id = $request['item_id'] ?? 0;
147 - $course_id = $request['course_id'] ?? 0;
148 - $results = array(
149 - 'question_ids' => array(),
150 - 'questions' => array(),
151 - 'total_time' => 0,
152 - );
153 - $response = array(
154 - 'status' => 'error',
155 - 'message' => '',
156 - );
157 -
158 - try {
159 - $user = learn_press_get_user( $user_id );
160 - $course = learn_press_get_course( $course_id );
161 - $quiz = learn_press_get_quiz( $item_id );
162 -
163 - if ( ! $course ) {
164 - throw new Exception( __( 'The course is invalid!', 'learnpress' ) );
165 - }
166 -
167 - if ( ! $quiz ) {
168 - throw new Exception( __( 'The quiz is invalid!', 'learnpress' ) );
169 - }
170 -
171 - $quiz->set_course( $course );
172 -
173 - do_action( 'learn-press/user/before/start-quiz', $item_id, $course_id, $user_id );
174 -
175 - $courseModel = CourseModel::find( $course_id, true );
176 - $link = LP_Helper::get_link_no_cache( $courseModel->get_item_link( $item_id, LP_QUIZ_CPT ) );
177 -
178 - // For no required enroll course
179 - if ( $user->is_guest() && $course->is_no_required_enroll() ) {
180 - $no_required_enroll = new LP_Course_No_Required_Enroll( $course );
181 - $response = $no_required_enroll->guest_start_quiz( $quiz );
182 - $response['redirect_url'] = $link;
183 -
184 - return rest_ensure_response( $response );
185 - }
186 -
187 - /**
188 - * Require enroll course
189 - *
190 - * @var UserQuizModel $user_quiz
191 - */
192 - $retaken_count = 0;
193 - $attempts = [];
194 - $filter_user_quiz = new LP_User_Items_Filter();
195 - $filter_user_quiz->user_id = $user_id;
196 - $filter_user_quiz->item_id = $item_id;
197 - $filter_user_quiz->ref_id = $course_id;
198 - $user_quiz_exists = UserQuizModel::get_user_item_model_from_db( $filter_user_quiz );
199 - if ( $user_quiz_exists instanceof UserQuizModel
200 - && $user_quiz_exists->status === LP_ITEM_COMPLETED ) {
201 - $user_quiz = $user_quiz_exists;
202 - $user_quiz->retake();
203 - $results['answered'] = []; // Reset answered for js
204 - $retaken_count = $user_quiz->get_retaken_count();
205 - $attempts = $user_quiz->get_attempts();
206 - } else { // Create new user quiz and insert to database.
207 - $user_quiz_new = new UserQuizModel();
208 - $user_quiz_new->user_id = $user_id;
209 - $user_quiz_new->item_id = $item_id;
210 - $user_quiz_new->ref_id = $course_id;
211 - $user_quiz_new->start_quiz();
212 - $user_quiz = $user_quiz_new;
213 - }
214 -
215 - $user_item_id = $user_quiz->get_user_item_id();
216 -
217 - /**
218 - * Clear cache result quiz
219 - * Cache set on @see LP_User_Item_Quiz::get_results
220 - */
221 - $lp_quiz_cache = LP_Quiz_Cache::instance();
222 - $key_cache = sprintf( '%d/user/%d/course/%d', $item_id, $user_id, $course_id );
223 - $lp_quiz_cache->clear( $key_cache );
224 - // End
225 -
226 - $show_check = $quiz->get_instant_check();
227 - $duration = $quiz->get_duration();
228 - $show_correct_review = $quiz->get_show_correct_review();
229 - $question_ids = $quiz->get_question_ids();
230 - $status = $user_quiz->get_status();
231 - $time_remaining = $user_quiz->get_time_remaining();
232 -
233 - $questions = learn_press_rest_prepare_user_questions(
234 - $question_ids,
235 - array(
236 - 'instant_check' => $show_check,
237 - //'quiz_status' => $status,
238 - //'checked_questions' => $checked_questions,
239 - //'hinted_questions' => $hinted_questions,
240 - 'answered' => [],
241 - 'show_correct_review' => $show_correct_review,
242 - )
243 - );
244 -
245 - $results['question_ids'] = $question_ids;
246 - $results['questions'] = $questions;
247 - $results['total_time'] = $time_remaining;
248 - $results['duration'] = $duration ? $duration->get() : false;
249 - $results['status'] = $status; // Must be started
250 - $results['retaken'] = $retaken_count;
251 - $results['attempts'] = $attempts;
252 - $results['user_item_id'] = $user_item_id;
253 - $response['status'] = 'success';
254 - $response['results'] = $results;
255 - $response['redirect_url'] = $link;
256 - } catch ( Throwable $e ) {
257 - $response['message'] = $e->getMessage();
258 - }
259 -
260 - return rest_ensure_response( $response );
261 - }
262 -
263 - /**
264 - * Submit quiz answer
265 - *
266 - * @param WP_REST_Request $request
267 - *
268 - * @return WP_Error|WP_HTTP_Response|WP_REST_Response
269 - * @editor tungnx
270 - * @modify 4.1.4.1
271 - * @version 1.0.2
272 - */
273 - public function submit_quiz( WP_REST_Request $request ) {
274 - //$response = new LP_REST_Response();
275 - $response = array(
276 - 'status' => 'error',
277 - 'message' => '',
278 - );
279 -
280 - try {
281 - $user_id = get_current_user_id();
282 - $item_id = $request['item_id'] ?? 0;
283 - $course_id = $request['course_id'] ?? 0;
284 - $answered = $request['answered'] ?? [];
285 - $time_spend = $request['time_spend'] ?? 0;
286 - $user = learn_press_get_user( $user_id );
287 - $course = learn_press_get_course( $course_id );
288 -
289 - if ( ! $course ) {
290 - throw new Exception( 'The course is invalid!' );
291 - }
292 - // Use for Review Quiz.
293 - $quiz = learn_press_get_quiz( $item_id );
294 - if ( ! $quiz ) {
295 - throw new Exception( __( 'The quiz is invalid!', 'learnpress' ) );
296 - }
297 - $quiz->set_course( $course );
298 -
299 - // Course is no required enroll
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 - do_action( 'learn-press/user/quiz-finished', $item_id, $course_id, $user_id, $user_quiz );
364 -
365 - $result['status'] = $user_quiz->get_status(); // Must be completed
366 - $result['attempts'] = $user_quiz->get_attempts();
367 - $result['answered'] = $result['questions'];
368 - $result['results'] = $result;
369 - $response['status'] = 'success';
370 - $response['results'] = $result;
371 - } catch ( Throwable $e ) {
372 - $response['message'] = $e->getMessage();
373 - }
374 -
375 - return rest_ensure_response( $response );
376 - }
377 -
378 - /**
379 - * Hint the question and response hint content.
380 - *
381 - * @param WP_REST_Request $request
382 - *
383 - * @return mixed|WP_REST_Response
384 - */
385 - /*public function hint_answer( $request ) {
386 - $question_id = $request['question_id'];
387 - $question = learn_press_get_question( $question_id );
388 -
389 - $response = array(
390 - 'hint' => $question->get_hint(),
391 - );
392 -
393 - return rest_ensure_response( $response );
394 - }*/
395 -
396 - public function check_answer( $request ) {
397 - $response = array(
398 - 'status' => 'error',
399 - 'message' => '',
400 - );
401 -
402 - try {
403 - $question_id = $request['question_id'] ?? 0;
404 - $answered = $request['answered'] ?? '';
405 - $course_id = $request['course_id'] ?? 0;
406 - $quiz_id = $request['item_id'] ?? 0;
407 - $course = learn_press_get_course( $course_id );
408 - $checked = [];
409 -
410 - if ( $course->is_no_required_enroll() ) {
411 - $no_required_enroll = new LP_Course_No_Required_Enroll( $course );
412 - $checked = $no_required_enroll->guest_check_question( $question_id, $answered );
413 - } else {
414 - $user = learn_press_get_current_user();
415 - if ( $user->is_guest() ) {
416 - throw new Exception( 'The user is invalid', 'learnrpess' );
417 - }
418 -
419 - $user_course = $user->get_course_data( $course_id );
420 - if ( ! $user_course ) {
421 - throw new Exception( 'User\'s course no data!', 'learnrpess' );
422 - }
423 -
424 - $user_quiz = $user_course->get_item( $quiz_id );
425 - if ( ! $user_quiz ) {
426 - throw new Exception( 'User\'s quiz no data!', 'learnrpess' );
427 - }
428 -
429 - $checked = $user_quiz->instant_check_question( $question_id, $answered );
430 - }
431 -
432 - $question = learn_press_get_question( $question_id );
433 - $response['explanation'] = $question->get_explanation();
434 - $response['options'] = learn_press_get_question_options_for_js( $question, array( 'answer' => $answered ) );
435 - $response['result'] = $checked;
436 - $response['status'] = 'success';
437 - } catch ( Throwable $e ) {
438 - $response['message'] = $e->getMessage();
439 - }
440 -
441 - return rest_ensure_response( $response );
442 - }
443 -}
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 +}