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 / jwt / rest-api / version1 / class-lp-rest-quiz-v1-controller.php

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

842 lines 25.6 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 class LP_Jwt_Quiz_V1_Controller extends LP_REST_Jwt_Posts_Controller {
3 protected $namespace = 'learnpress/v1';
4
5 protected $rest_base = 'quiz';
6
7 protected $post_type = LP_QUIZ_CPT;
8
9 protected $hierarchical = true;
10
11 public function register_routes() {
12 register_rest_route(
13 $this->namespace,
14 '/' . $this->rest_base,
15 array(
16 array(
17 'methods' => WP_REST_Server::READABLE,
18 'callback' => array( $this, 'get_items' ),
19 'permission_callback' => array( $this, 'get_items_permissions_check' ),
20 'args' => $this->get_collection_params(),
21 ),
22 'schema' => array( $this, 'get_public_item_schema' ),
23 )
24 );
25
26 register_rest_route(
27 $this->namespace,
28 '/' . $this->rest_base . '/(?P<id>[\d]+)',
29 array(
30 'args' => array(
31 'id' => array(
32 'description' => esc_html__( 'A unique identifier for the resource.', 'learnpress' ),
33 'type' => 'integer',
34 ),
35 ),
36 array(
37 'methods' => WP_REST_Server::READABLE,
38 'callback' => array( $this, 'get_item' ),
39 'permission_callback' => array( $this, 'get_item_permissions_check' ),
40 'args' => array(
41 'context' => $this->get_context_param(
42 array(
43 'default' => 'view',
44 )
45 ),
46 ),
47 ),
48 'schema' => array( $this, 'get_public_item_schema' ),
49 )
50 );
51
52 register_rest_route(
53 $this->namespace,
54 '/' . $this->rest_base . '/start',
55 array(
56 'args' => array(
57 'id' => array(
58 'description' => esc_html__( 'Quiz ID.', 'learnpress' ),
59 'type' => 'integer',
60 ),
61 ),
62 array(
63 'methods' => WP_REST_Server::CREATABLE,
64 'callback' => array( $this, 'start_quiz' ),
65 'permission_callback' => array( $this, 'get_item_permissions_check' ),
66 ),
67 )
68 );
69
70 register_rest_route(
71 $this->namespace,
72 '/' . $this->rest_base . '/check_answer',
73 array(
74 'args' => array(
75 'id' => array(
76 'description' => esc_html__( 'Quiz ID.', 'learnpress' ),
77 'type' => 'integer',
78 ),
79 'question_id' => array(
80 'description' => esc_html__( 'Question ID.', 'learnpress' ),
81 'type' => 'integer',
82 ),
83 ),
84 array(
85 'methods' => WP_REST_Server::CREATABLE,
86 'callback' => array( $this, 'check_quiz' ),
87 'permission_callback' => array( $this, 'get_item_permissions_check' ),
88 ),
89 )
90 );
91
92 register_rest_route(
93 $this->namespace,
94 '/' . $this->rest_base . '/finish',
95 array(
96 'args' => array(
97 'id' => array(
98 'description' => esc_html__( 'Quiz ID.', 'learnpress' ),
99 'type' => 'integer',
100 ),
101 'answered' => array(
102 'description' => esc_html__( 'Answer all questions.', 'learnpress' ),
103 'type' => 'object',
104 ),
105 ),
106 array(
107 'methods' => WP_REST_Server::CREATABLE,
108 'callback' => array( $this, 'submit_quiz' ),
109 'permission_callback' => array( $this, 'get_item_permissions_check' ),
110 ),
111 )
112 );
113 }
114
115 /**
116 * Allow guests/unauthenticated users to query the list endpoint (filtered by slug).
117 * Per-item visibility is still enforced by check_read_permission, which only
118 * allows preview items for guests.
119 */
120 public function get_items_permissions_check( $request ) {
121 return true;
122 }
123
124 /**
125 * Checks if a course can be read.
126 *
127 * Correctly handles courses with the inherit status.
128 *
129 * @author Nhamdv
130 *
131 * @return bool Whether the post can be read.
132 * */
133 public function check_read_permission( $post_id ) {
134 if ( empty( absint( $post_id ) ) ) {
135 return false;
136 }
137
138 $post = get_post( $post_id );
139
140 if ( ! $post ) {
141 return false;
142 }
143
144 if ( lp_rest_check_post_permissions( $this->post_type, 'read', $post_id ) ) {
145 return true;
146 }
147
148 $post_status_obj = get_post_status_object( $post->post_status );
149 if ( ! $post_status_obj || ! $post_status_obj->public ) {
150 return false;
151 }
152
153 // Allow guests and unenrolled users to read items marked as preview.
154 $course_id = $this->get_course_by_item_id( $post_id );
155 if ( $course_id ) {
156 $course = learn_press_get_course( $course_id );
157 if ( $course ) {
158 $item = $course->get_item( $post_id );
159 if ( $item && $item->is_preview() ) {
160 return true;
161 }
162 }
163 }
164
165 $user_id = get_current_user_id();
166
167 if ( ! $user_id ) {
168 return false;
169 }
170
171 $user = learn_press_get_user( $user_id );
172
173 if ( empty( $course_id ) ) {
174 return false;
175 }
176
177 $can_view_content_course = $user->can_view_content_course( $course_id );
178
179 $can_view_item = $user->can_view_item( $post_id, $can_view_content_course );
180
181 if ( ! $can_view_item->flag ) {
182 return false;
183 }
184
185 // Can we read the parent if we're inheriting?
186 if ( 'inherit' === $post->post_status && $post->post_parent > 0 ) {
187 $parent = get_post( $post->post_parent );
188
189 if ( $parent ) {
190 return $this->check_read_permission( $parent );
191 }
192 }
193
194 return true;
195 }
196
197 protected function get_course_by_item_id( $item_id ) {
198 static $output;
199
200 global $wpdb;
201
202 if ( empty( $item_id ) ) {
203 return false;
204 }
205
206 if ( ! isset( $output ) ) {
207 $output = $wpdb->get_var(
208 $wpdb->prepare(
209 "SELECT c.ID FROM {$wpdb->posts} c
210 INNER JOIN {$wpdb->learnpress_sections} s ON c.ID = s.section_course_id
211 INNER JOIN {$wpdb->learnpress_section_items} si ON si.section_id = s.section_id
212 WHERE si.item_id = %d ORDER BY si.section_id DESC LIMIT 1
213 ",
214 $item_id
215 )
216 );
217 }
218
219 if ( $output ) {
220 return absint( $output );
221 }
222
223 return false;
224 }
225
226 protected function get_object( $quiz = 0 ) {
227 global $post;
228
229 if ( false === $quiz && isset( $post, $post->ID ) && LP_QUIZ_CPT === get_post_type( $post->ID ) ) {
230 $id = absint( $post->ID );
231 } elseif ( is_numeric( $quiz ) ) {
232 $id = $quiz;
233 } elseif ( $quiz instanceof LP_Quiz ) {
234 $id = $quiz->get_id();
235 } elseif ( ! empty( $quiz->ID ) ) {
236 $id = $quiz->ID;
237 }
238
239 return new LP_Quiz( $id );
240 }
241
242 public function start_quiz( $request ) {
243 $response = new LP_REST_Response();
244 $quiz_id = isset( $request['id'] ) ? absint( $request['id'] ) : '';
245 $course_id = $this->get_course_by_item_id( $quiz_id );
246
247 if ( empty( $quiz_id ) || empty( $course_id ) ) {
248 $response->status = 'error';
249 $response->message = esc_html__( 'There is no Quiz ID or Quiz assigned in the course.', 'learnpress' );
250
251 return rest_ensure_response( $response );
252 }
253
254 if ( ! class_exists( 'LP_REST_Users_Controller' ) ) {
255 include_once LP_PLUGIN_PATH . 'inc/rest-api/v1/frontend/class-lp-rest-users-controller.php';
256 }
257
258 $controller = new LP_REST_Users_Controller();
259
260 $request->set_param( 'course_id', $course_id );
261 $request->set_param( 'item_id', $quiz_id );
262
263 return $controller->start_quiz( $request );
264 }
265
266 public function check_quiz( $request ) {
267 $response = new LP_REST_Response();
268 $quiz_id = isset( $request['id'] ) ? absint( $request['id'] ) : '';
269 $question_id = isset( $request['question_id'] ) ? absint( $request['question_id'] ) : '';
270 $answered = isset( $request['answered'] ) ? wp_unslash( $request['answered'] ) : '';
271 $course_id = $this->get_course_by_item_id( $quiz_id );
272
273 if ( empty( $quiz_id ) || empty( $course_id ) ) {
274 $response->status = 'error';
275 $response->message = esc_html__( 'There is no Quiz ID or Quiz assigned in the course.', 'learnpress' );
276
277 return rest_ensure_response( $response );
278 }
279
280 if ( ! class_exists( 'LP_REST_Users_Controller' ) ) {
281 include_once LP_PLUGIN_PATH . 'inc/rest-api/v1/frontend/class-lp-rest-users-controller.php';
282 }
283
284 $controller = new LP_REST_Users_Controller();
285
286 $request->set_param( 'answered', $answered );
287 $request->set_param( 'question_id', $question_id );
288 $request->set_param( 'course_id', $course_id );
289 $request->set_param( 'item_id', $quiz_id );
290
291 return $controller->check_answer( $request );
292 }
293
294 public function submit_quiz( $request ) {
295 $response = new LP_REST_Response();
296 $quiz_id = isset( $request['id'] ) ? absint( $request['id'] ) : '';
297 $answered = isset( $request['answered'] ) ? wp_unslash( $request['answered'] ) : '';
298
299 $course_id = $this->get_course_by_item_id( $quiz_id );
300
301 if ( empty( $quiz_id ) || empty( $course_id ) ) {
302 $response->status = 'error';
303 $response->message = esc_html__( 'There is no Quiz ID or Quiz assigned in the course.', 'learnpress' );
304
305 return rest_ensure_response( $response );
306 }
307
308 if ( ! isset( $request['answered'] ) ) {
309 $response->status = 'error';
310 $response->message = esc_html__( 'No Answer param.', 'learnpress' );
311
312 return rest_ensure_response( $response );
313 }
314
315 if ( ! class_exists( 'LP_REST_Users_Controller' ) ) {
316 include_once LP_PLUGIN_PATH . 'inc/rest-api/v1/frontend/class-lp-rest-users-controller.php';
317 }
318
319 $controller = new LP_REST_Users_Controller();
320
321 $request->set_param( 'answered', $answered );
322 $request->set_param( 'course_id', $course_id );
323 $request->set_param( 'item_id', $quiz_id );
324
325 return $controller->submit_quiz( $request );
326 }
327
328 public function prepare_object_for_response( $object, $request ) {
329 $context = ! empty( $request['context'] ) ? $request['context'] : 'view';
330 $data = $this->get_quiz_data( $object, $context, $request );
331
332 $response = rest_ensure_response( $data );
333
334 return apply_filters( "lp_jwt_rest_prepare_{$this->post_type}_object", $response, $object, $request );
335 }
336
337 protected function get_quiz_data( $object, $context = 'view' ) {
338 $request = func_num_args() >= 2 ? func_get_arg( 2 ) : new WP_REST_Request( '', '', array( 'context' => $context ) );
339 $fields = $this->get_fields_for_response( $request );
340
341 $id = ! empty( $object->ID ) ? $object->ID : $object->get_id();
342 $post = get_post( $id );
343 $data = array();
344
345 $assigned = $this->get_assigned( $id );
346 if ( ! empty( $assigned ) && method_exists( $object, 'set_course' ) ) {
347 $course_id = $assigned['course']['id'];
348 $object->set_course( $course_id );
349 }
350
351 foreach ( $fields as $field ) {
352 switch ( $field ) {
353 case 'id':
354 $data['id'] = $id;
355 break;
356 case 'name':
357 $data['name'] = $post->post_title;
358 break;
359 case 'slug':
360 $data['slug'] = $post->post_name;
361 break;
362 case 'permalink':
363 $data['permalink'] = $object->get_permalink();
364 break;
365 case 'date_created':
366 $data['date_created'] = lp_jwt_prepare_date_response( $post->post_date_gmt, $post->post_date );
367 break;
368 case 'date_created_gmt':
369 $data['date_created_gmt'] = lp_jwt_prepare_date_response( $post->post_date_gmt );
370 break;
371 case 'date_modified':
372 $data['date_modified'] = lp_jwt_prepare_date_response( $post->post_modified_gmt, $post->post_modified );
373 break;
374 case 'date_modified_gmt':
375 $data['date_modified_gmt'] = lp_jwt_prepare_date_response( $post->post_modified_gmt );
376 break;
377 case 'status':
378 $data['status'] = $post->post_status;
379 break;
380 case 'content':
381 $data['content'] = 'view' === $context ? apply_filters( 'the_content', $post->post_content ) : $post->post_content;
382 break;
383 case 'excerpt':
384 $data['excerpt'] = $post->post_excerpt;
385 break;
386 case 'can_finish_course':
387 $data['can_finish_course'] = $this->check_can_finish_course( $id );
388 break;
389 case 'duration':
390 $data['duration'] = learn_press_get_post_translated_duration( $id, esc_html__( 'Lifetime', 'learnpress' ) );
391 break;
392 case 'assigned':
393 $data['assigned'] = $assigned;
394 break;
395 case 'questions':
396 $data['questions'] = $this->get_quiz_results( $object, true )['questions'] ?? array();
397 break;
398 case 'results':
399 $data['results'] = $this->get_quiz_results( $object );
400 break;
401 }
402 }
403
404 $data['meta_data'] = $this->get_course_meta( $id );
405
406 return $data;
407 }
408
409 public function check_can_finish_course( $id ) {
410 $user = learn_press_get_current_user();
411
412 if ( ! $user || ! $id ) {
413 return false;
414 }
415
416 $course_id = $this->get_course_by_item_id( $id );
417
418 if ( empty( $course_id ) ) {
419 return false;
420 }
421
422 $course = learn_press_get_course( $course_id );
423
424 if ( $user && $course ) {
425 $check = $user->can_show_finish_course_btn( $course );
426
427 if ( $check['status'] === 'success' ) {
428 return true;
429 }
430
431 return false;
432 }
433
434 return false;
435 }
436
437 public function get_quiz_results( $quiz, $get_question = false ) {
438 $output = array(
439 'status' => '',
440 );
441
442 $user = learn_press_get_current_user();
443
444 if ( ! $user || ! $quiz ) {
445 return $output;
446 }
447
448 $id = ! empty( $quiz->ID ) ? $quiz->ID : $quiz->get_id();
449
450 $course_id = $this->get_course_by_item_id( $id );
451
452 if ( empty( $course_id ) ) {
453 return $output;
454 }
455
456 $user_course = $user->get_course_data( $course_id );
457 $user_quiz = $user_course ? $user_course->get_item( $id ) : false;
458 $show_check = $quiz->get_instant_check();
459 $show_correct_review = $quiz->get_show_correct_review();
460 $answered = array();
461 $status = '';
462 $checked_questions = array();
463 $question_ids = $quiz->get_question_ids();
464
465 if ( $user_quiz ) {
466 $status = $user_quiz->get_status();
467 $quiz_results = $user_quiz->get_results( '' );
468 $checked_questions = $user_quiz->get_checked_questions();
469 $expiration_time = $user_quiz->get_expiration_time();
470
471 $output = array(
472 'status' => $status,
473 'attempts' => $user_quiz->get_attempts(),
474 'checked_questions' => $checked_questions,
475 'start_time' => lp_jwt_prepare_date_response( $user_quiz->get_start_time()->toSql() ),
476 'retaken' => absint( $user_quiz->get_retaken_count() ),
477 );
478
479 $output['total_time'] = $user_quiz->get_timestamp_remaining();
480
481 if ( $quiz_results ) {
482 $output['results'] = $quiz_results->get();
483 $answered = $quiz_results->getQuestions();
484 $question_ids = $quiz_results->getQuestions( 'ids' );
485 }
486
487 $output['answered'] = ! empty( $answered ) ? (object) $answered : new stdClass();
488 $output['question_ids'] = array_map( 'absint', array_values( $question_ids ) );
489 }
490
491 $duration = $quiz->get_duration();
492
493 // Plugin's get_pagination() returns 0 when value <= 1 (treats 1 as "no pagination").
494 // Override here so admins can set 1 question per page for step-by-step quizzes.
495 $pagination_raw = absint( get_post_meta( $quiz->get_id(), '_lp_pagination', true ) );
496
497 $array = array(
498 'passing_grade' => $quiz->get_passing_grade(),
499 'negative_marking' => $quiz->get_negative_marking(),
500 'instant_check' => $quiz->get_instant_check(),
501 'retake_count' => (float) $quiz->get_retake_count(),
502 'questions_per_page' => $pagination_raw > 0 ? $pagination_raw : 0,
503 'page_numbers' => get_post_meta( $quiz->get_id(), '_lp_pagination_numbers', true ) === 'yes',
504 'review_questions' => $quiz->get_review_questions(),
505 'support_options' => learn_press_get_question_support_answer_options(),
506 'duration' => $duration ? $duration->get() : false,
507 );
508
509 if ( function_exists( 'learn_press_rest_prepare_user_questions' ) && $get_question ) {
510 $questions = learn_press_rest_prepare_user_questions(
511 $question_ids,
512 array(
513 'instant_check' => $show_check,
514 'quiz_status' => $status,
515 'checked_questions' => $checked_questions,
516 'answered' => $answered,
517 'show_correct_review' => $show_correct_review,
518 )
519 );
520
521 // Post-process FIB questions: the actual question content (with [fib] shortcodes)
522 // is stored in lp_question_answers.title — NOT post_content. Read it from there,
523 // strip `fill=` answers, replace shortcodes with {{FIB_<id>}} placeholders for
524 // the client. On reveal, attach `fib_answers` with per-blank correctness.
525 foreach ( $questions as &$q ) {
526 if ( ! isset( $q['type'] ) || $q['type'] !== 'fill_in_blanks' ) {
527 continue;
528 }
529
530 $question_obj = $q['object'] ?? null;
531 if ( ! $question_obj || ! method_exists( $question_obj, 'get_data' ) ) {
532 continue;
533 }
534
535 $question_id = $q['id'] ?? 0;
536 $answer_options = $question_obj->get_data( 'answer_options' );
537 if ( empty( $answer_options ) || ! is_array( $answer_options ) ) {
538 continue;
539 }
540
541 $first_answer = reset( $answer_options );
542 $raw_content = $first_answer['title'] ?? '';
543 $answer_id = $first_answer['question_answer_id'] ?? 0;
544 $user_answer = $answered[ $question_id ]['answered'] ?? '';
545 $revealed = $show_check || $status === 'completed';
546
547 $q['content'] = apply_filters(
548 'learn-press/question/fib/regex-content',
549 $raw_content,
550 $answer_id,
551 $revealed,
552 $user_answer
553 );
554
555 // FIB has no answer "options" in radio/checkbox sense; clear them.
556 $q['options'] = array();
557
558 if ( $revealed && method_exists( $question_obj, 'get_answer_data' ) ) {
559 $q['fib_answers'] = $question_obj->get_answer_data(
560 $raw_content,
561 $answer_id,
562 $user_answer
563 );
564 }
565 }
566 unset( $q );
567
568 // Plugin's `learn_press_rest_prepare_user_questions()` only emits `explanation`
569 // when the user explicitly triggered instant_check on a question (it reads
570 // `$args['status']` which is never set in the defaults). For completed quizzes
571 // fill in explanations here so clients can render them on the review screen.
572 if ( $status === 'completed' ) {
573 foreach ( $questions as &$q ) {
574 if ( ! empty( $q['explanation'] ) ) {
575 continue;
576 }
577 $question_obj = $q['object'] ?? null;
578 if ( $question_obj && method_exists( $question_obj, 'get_explanation' ) ) {
579 $explanation = $question_obj->get_explanation();
580 if ( $explanation ) {
581 $q['explanation'] = $explanation;
582 $q['has_explanation'] = true;
583 }
584 }
585 }
586 unset( $q );
587 }
588
589 $output['questions'] = $questions;
590 }
591
592 return array_merge( $array, $output );
593 }
594
595 public function get_course_meta( $id ) {
596 if ( ! class_exists( 'LP_Meta_Box_Quiz' ) ) {
597 include_once LP_PLUGIN_PATH . 'inc/admin/views/meta-boxes/quiz/settings.php';
598 }
599
600 $metabox = new LP_Meta_Box_Quiz();
601
602 $output = array();
603 foreach ( $metabox->metabox( $id ) as $meta_key => $object ) {
604 if ( is_a( $object, 'LP_Meta_Box_Field' ) ) {
605 $object->id = $meta_key;
606 $output[ $meta_key ] = $object->meta_value( $id );
607 }
608 }
609
610 return $output;
611 }
612
613 public function get_assigned( int $id ) : array {
614 $courses = learn_press_get_item_courses( $id );
615
616 $output = array();
617
618 if ( $courses ) {
619 foreach ( $courses as $course ) {
620 $output['course'] = array(
621 'id' => $course->ID,
622 'title' => $course->post_title,
623 'slug' => $course->post_name,
624 'content' => $course->post_content,
625 'author' => $course->post_author,
626 );
627 }
628 }
629
630 return $output;
631 }
632
633 public function get_all_question( object $quiz ) : array {
634 $questions = array();
635
636 if ( function_exists( 'learn_press_rest_prepare_user_questions' ) ) {
637 $questions = learn_press_rest_prepare_user_questions(
638 $quiz->get_question_ids(),
639 array(
640 'instant_check' => $quiz->get_instant_check(),
641 'show_correct_review' => $quiz->get_show_correct_review(),
642 )
643 );
644 }
645
646 return $questions;
647 }
648
649 public function get_item_schema() {
650 $schema = array(
651 '$schema' => 'http://json-schema.org/draft-04/schema#',
652 'title' => $this->post_type,
653 'type' => 'object',
654 'properties' => array(
655 'id' => array(
656 'description' => __( 'A unique identifier for the resource.', 'learnpress' ),
657 'type' => 'integer',
658 'context' => array( 'view', 'edit' ),
659 'readonly' => true,
660 ),
661 'name' => array(
662 'description' => __( 'Course name.', 'learnpress' ),
663 'type' => 'string',
664 'context' => array( 'view', 'edit' ),
665 ),
666 'slug' => array(
667 'description' => __( 'Course slug.', 'learnpress' ),
668 'type' => 'string',
669 'context' => array( 'view', 'edit' ),
670 ),
671 'permalink' => array(
672 'description' => __( 'Course URL.', 'learnpress' ),
673 'type' => 'string',
674 'format' => 'uri',
675 'context' => array( 'view', 'edit' ),
676 'readonly' => true,
677 ),
678 'date_created' => array(
679 'description' => __( "The date the Course was created, in the site's timezone.", 'learnpress' ),
680 'type' => 'date-time',
681 'context' => array( 'view', 'edit' ),
682 'readonly' => true,
683 ),
684 'date_created_gmt' => array(
685 'description' => __( 'The date the Course was created, as GMT.', 'learnpress' ),
686 'type' => 'date-time',
687 'context' => array( 'view', 'edit' ),
688 'readonly' => true,
689 ),
690 'date_modified' => array(
691 'description' => __( "The date the Course was last modified, in the site's timezone.", 'learnpress' ),
692 'type' => 'date-time',
693 'context' => array( 'view', 'edit' ),
694 'readonly' => true,
695 ),
696 'date_modified_gmt' => array(
697 'description' => __( 'The date the Course was last modified, as GMT.', 'learnpress' ),
698 'type' => 'date-time',
699 'context' => array( 'view', 'edit' ),
700 'readonly' => true,
701 ),
702 'status' => array(
703 'description' => __( 'Course status (post status).', 'learnpress' ),
704 'type' => 'string',
705 'default' => 'publish',
706 'enum' => array_merge( array_keys( get_post_statuses() ), array( 'future' ) ),
707 'context' => array( 'view', 'edit' ),
708 ),
709 'content' => array(
710 'description' => __( 'Course content.', 'learnpress' ),
711 'type' => 'string',
712 'context' => array( 'view', 'edit' ),
713 ),
714 'excerpt' => array(
715 'description' => __( 'Retrieves the course excerpt..', 'learnpress' ),
716 'type' => 'string',
717 'context' => array( 'view', 'edit' ),
718 ),
719 'can_finish_course' => array(
720 'description' => __( 'Can finish the course', 'learnpress' ),
721 'type' => 'boolean',
722 'context' => array( 'view' ),
723 'readonly' => true,
724 ),
725 'duration' => array(
726 'description' => __( 'Duration', 'learnpress' ),
727 'type' => 'string',
728 'context' => array( 'view' ),
729 ),
730 'assigned' => array(
731 'description' => __( 'Assigned.', 'learnpress' ),
732 'type' => 'array',
733 'context' => array( 'view', 'edit' ),
734 'items' => array(
735 'id' => array(
736 'description' => __( 'Item ID.', 'learnpress' ),
737 'type' => 'integer',
738 'context' => array( 'view', 'edit' ),
739 ),
740 'title' => array(
741 'description' => __( 'Title.', 'learnpress' ),
742 'type' => 'string',
743 'context' => array( 'view', 'edit' ),
744 ),
745 'slug' => array(
746 'description' => __( 'Item slug.', 'learnpress' ),
747 'type' => 'string',
748 'context' => array( 'view', 'edit' ),
749 ),
750 'content' => array(
751 'description' => __( 'Item Content.', 'learnpress' ),
752 'type' => 'string',
753 'context' => array( 'view', 'edit' ),
754 ),
755 'author' => array(
756 'description' => __( 'Item Author.', 'learnpress' ),
757 'type' => 'integer',
758 'context' => array( 'view', 'edit' ),
759 ),
760 ),
761 ),
762 'questions' => array(
763 'description' => __( 'List all of the Quiz Questions.', 'learnpress' ),
764 'type' => 'array',
765 'context' => array( 'view', 'edit' ),
766 'items' => array(
767 'description' => __( 'Question items.', 'learnpress' ),
768 'type' => 'object',
769 'context' => array( 'view', 'edit' ),
770 'items' => array(
771 'id' => array(
772 'description' => __( 'Item ID.', 'learnpress' ),
773 'type' => 'integer',
774 'context' => array( 'view', 'edit' ),
775 ),
776 'type' => array(
777 'description' => __( 'Item Type.', 'learnpress' ),
778 'type' => 'string',
779 'context' => array( 'view', 'edit' ),
780 ),
781 'title' => array(
782 'description' => __( 'Item title.', 'learnpress' ),
783 'type' => 'string',
784 'context' => array( 'view', 'edit' ),
785 ),
786 'content' => array(
787 'description' => __( 'Item Content.', 'learnpress' ),
788 'type' => 'string',
789 'context' => array( 'view', 'edit' ),
790 ),
791 'point' => array(
792 'description' => __( 'Point.', 'learnpress' ),
793 'type' => 'integer',
794 'context' => array( 'view', 'edit' ),
795 ),
796 'hint' => array(
797 'description' => __( 'Question Hint.', 'learnpress' ),
798 'type' => 'string',
799 'context' => array( 'view', 'edit' ),
800 ),
801 'options' => array(
802 'description' => __( 'Question Options.', 'learnpress' ),
803 'type' => 'array',
804 'context' => array( 'view', 'edit' ),
805 'items' => array(
806 'description' => __( 'Question items.', 'learnpress' ),
807 'type' => 'object',
808 'context' => array( 'view', 'edit' ),
809 'items' => array(
810 'title' => array(
811 'description' => __( 'Item title.', 'learnpress' ),
812 'type' => 'string',
813 'context' => array( 'view', 'edit' ),
814 ),
815 'value' => array(
816 'description' => __( 'Item value.', 'learnpress' ),
817 'type' => 'string',
818 'context' => array( 'view', 'edit' ),
819 ),
820 'uid' => array(
821 'description' => __( 'Item id.', 'learnpress' ),
822 'type' => 'integer',
823 'context' => array( 'view', 'edit' ),
824 ),
825 ),
826 ),
827 ),
828 ),
829 ),
830 ),
831 'results' => array(
832 'description' => __( 'Retrieves the quiz result..', 'learnpress' ),
833 'type' => 'array',
834 'context' => array( 'view', 'edit' ),
835 ),
836 ),
837 );
838
839 return $this->add_additional_fields_schema( $schema );
840 }
841 }
842