← All changes
|
inc/jwt/rest-api/version1/class-lp-rest-quiz-v1-controller.php
+108
-18
4.1.7
→
4.4.10
View file →
| @@ -28,9 +28,9 @@ | ||
| 28 | 28 | '/' . $this->rest_base . '/(?P<id>[\d]+)', |
| 29 | 29 | array( |
| 30 | 30 | 'args' => array( |
| 31 | 31 | 'id' => array( |
| 32 | - 'description' => esc_html__( 'Unique identifier for the resource.', 'learnpress' ), | |
| 32 | + 'description' => esc_html__( 'A unique identifier for the resource.', 'learnpress' ), | |
| 33 | 33 | 'type' => 'integer', |
| 34 | 34 | ), |
| 35 | 35 | ), |
| 36 | 36 | array( |
| @@ -98,9 +98,9 @@ | ||
| 98 | 98 | 'description' => esc_html__( 'Quiz ID.', 'learnpress' ), |
| 99 | 99 | 'type' => 'integer', |
| 100 | 100 | ), |
| 101 | 101 | 'answered' => array( |
| 102 | - 'description' => esc_html__( 'Answer all question.', 'learnpress' ), | |
| 102 | + 'description' => esc_html__( 'Answer all questions.', 'learnpress' ), | |
| 103 | 103 | 'type' => 'object', |
| 104 | 104 | ), |
| 105 | 105 | ), |
| 106 | 106 | array( |
| @@ -112,8 +112,17 @@ | ||
| 112 | 112 | ); |
| 113 | 113 | } |
| 114 | 114 | |
| 115 | 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 | + /** | |
| 116 | 125 | * Checks if a course can be read. |
| 117 | 126 | * |
| 118 | 127 | * Correctly handles courses with the inherit status. |
| 119 | 128 | * |
| @@ -140,8 +149,20 @@ | ||
| 140 | 149 | if ( ! $post_status_obj || ! $post_status_obj->public ) { |
| 141 | 150 | return false; |
| 142 | 151 | } |
| 143 | 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 | + | |
| 144 | 165 | $user_id = get_current_user_id(); |
| 145 | 166 | |
| 146 | 167 | if ( ! $user_id ) { |
| 147 | 168 | return false; |
| @@ -148,11 +169,8 @@ | ||
| 148 | 169 | } |
| 149 | 170 | |
| 150 | 171 | $user = learn_press_get_user( $user_id ); |
| 151 | 172 | |
| 152 | - // Get course ID by lesson ID assigned. | |
| 153 | - $course_id = $this->get_course_by_item_id( $post_id ); | |
| 154 | - | |
| 155 | 173 | if ( empty( $course_id ) ) { |
| 156 | 174 | return false; |
| 157 | 175 | } |
| 158 | 176 | |
| @@ -227,9 +245,9 @@ | ||
| 227 | 245 | $course_id = $this->get_course_by_item_id( $quiz_id ); |
| 228 | 246 | |
| 229 | 247 | if ( empty( $quiz_id ) || empty( $course_id ) ) { |
| 230 | 248 | $response->status = 'error'; |
| 231 | - $response->message = esc_html__( 'No Quiz ID or Quiz is not assigned in Course.', 'learnpress' ); | |
| 249 | + $response->message = esc_html__( 'There is no Quiz ID or Quiz assigned in the course.', 'learnpress' ); | |
| 232 | 250 | |
| 233 | 251 | return rest_ensure_response( $response ); |
| 234 | 252 | } |
| 235 | 253 | |
| @@ -253,9 +271,9 @@ | ||
| 253 | 271 | $course_id = $this->get_course_by_item_id( $quiz_id ); |
| 254 | 272 | |
| 255 | 273 | if ( empty( $quiz_id ) || empty( $course_id ) ) { |
| 256 | 274 | $response->status = 'error'; |
| 257 | - $response->message = esc_html__( 'No Quiz ID or Quiz is not assigned in Course.', 'learnpress' ); | |
| 275 | + $response->message = esc_html__( 'There is no Quiz ID or Quiz assigned in the course.', 'learnpress' ); | |
| 258 | 276 | |
| 259 | 277 | return rest_ensure_response( $response ); |
| 260 | 278 | } |
| 261 | 279 | |
| @@ -281,9 +299,9 @@ | ||
| 281 | 299 | $course_id = $this->get_course_by_item_id( $quiz_id ); |
| 282 | 300 | |
| 283 | 301 | if ( empty( $quiz_id ) || empty( $course_id ) ) { |
| 284 | 302 | $response->status = 'error'; |
| 285 | - $response->message = esc_html__( 'No Quiz ID or Quiz is not assigned in Course.', 'learnpress' ); | |
| 303 | + $response->message = esc_html__( 'There is no Quiz ID or Quiz assigned in the course.', 'learnpress' ); | |
| 286 | 304 | |
| 287 | 305 | return rest_ensure_response( $response ); |
| 288 | 306 | } |
| 289 | 307 | |
| @@ -288,9 +306,9 @@ | ||
| 288 | 306 | } |
| 289 | 307 | |
| 290 | 308 | if ( ! isset( $request['answered'] ) ) { |
| 291 | 309 | $response->status = 'error'; |
| 292 | - $response->message = esc_html__( 'No Answed param.', 'learnpress' ); | |
| 310 | + $response->message = esc_html__( 'No Answer param.', 'learnpress' ); | |
| 293 | 311 | |
| 294 | 312 | return rest_ensure_response( $response ); |
| 295 | 313 | } |
| 296 | 314 | |
| @@ -303,9 +321,9 @@ | ||
| 303 | 321 | $request->set_param( 'answered', $answered ); |
| 304 | 322 | $request->set_param( 'course_id', $course_id ); |
| 305 | 323 | $request->set_param( 'item_id', $quiz_id ); |
| 306 | 324 | |
| 307 | - return $controller->submit_quiz_new( $request ); | |
| 325 | + return $controller->submit_quiz( $request ); | |
| 308 | 326 | } |
| 309 | 327 | |
| 310 | 328 | public function prepare_object_for_response( $object, $request ) { |
| 311 | 329 | $context = ! empty( $request['context'] ) ? $request['context'] : 'view'; |
| @@ -391,9 +409,9 @@ | ||
| 391 | 409 | public function check_can_finish_course( $id ) { |
| 392 | 410 | $user = learn_press_get_current_user(); |
| 393 | 411 | |
| 394 | 412 | if ( ! $user || ! $id ) { |
| 395 | - return falase; | |
| 413 | + return false; | |
| 396 | 414 | } |
| 397 | 415 | |
| 398 | 416 | $course_id = $this->get_course_by_item_id( $id ); |
| 399 | 417 | |
| @@ -453,9 +471,9 @@ | ||
| 453 | 471 | $output = array( |
| 454 | 472 | 'status' => $status, |
| 455 | 473 | 'attempts' => $user_quiz->get_attempts(), |
| 456 | 474 | 'checked_questions' => $checked_questions, |
| 457 | - 'start_time' => lp_jwt_prepare_date_response( $user_quiz->get_start_time()->toSql( false ) ), | |
| 475 | + 'start_time' => lp_jwt_prepare_date_response( $user_quiz->get_start_time()->toSql() ), | |
| 458 | 476 | 'retaken' => absint( $user_quiz->get_retaken_count() ), |
| 459 | 477 | ); |
| 460 | 478 | |
| 461 | 479 | $output['total_time'] = $user_quiz->get_timestamp_remaining(); |
| @@ -471,14 +489,18 @@ | ||
| 471 | 489 | } |
| 472 | 490 | |
| 473 | 491 | $duration = $quiz->get_duration(); |
| 474 | 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 | + | |
| 475 | 497 | $array = array( |
| 476 | 498 | 'passing_grade' => $quiz->get_passing_grade(), |
| 477 | 499 | 'negative_marking' => $quiz->get_negative_marking(), |
| 478 | 500 | 'instant_check' => $quiz->get_instant_check(), |
| 479 | - 'retake_count' => absint( $quiz->get_retake_count() ), | |
| 480 | - 'questions_per_page' => $quiz->get_pagination(), | |
| 501 | + 'retake_count' => (float) $quiz->get_retake_count(), | |
| 502 | + 'questions_per_page' => $pagination_raw > 0 ? $pagination_raw : 0, | |
| 481 | 503 | 'page_numbers' => get_post_meta( $quiz->get_id(), '_lp_pagination_numbers', true ) === 'yes', |
| 482 | 504 | 'review_questions' => $quiz->get_review_questions(), |
| 483 | 505 | 'support_options' => learn_press_get_question_support_answer_options(), |
| 484 | 506 | 'duration' => $duration ? $duration->get() : false, |
| @@ -495,8 +517,76 @@ | ||
| 495 | 517 | 'show_correct_review' => $show_correct_review, |
| 496 | 518 | ) |
| 497 | 519 | ); |
| 498 | 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 | + | |
| 499 | 589 | $output['questions'] = $questions; |
| 500 | 590 | } |
| 501 | 591 | |
| 502 | 592 | return array_merge( $array, $output ); |
| @@ -562,9 +652,9 @@ | ||
| 562 | 652 | 'title' => $this->post_type, |
| 563 | 653 | 'type' => 'object', |
| 564 | 654 | 'properties' => array( |
| 565 | 655 | 'id' => array( |
| 566 | - 'description' => __( 'Unique identifier for the resource.', 'learnpress' ), | |
| 656 | + 'description' => __( 'A unique identifier for the resource.', 'learnpress' ), | |
| 567 | 657 | 'type' => 'integer', |
| 568 | 658 | 'context' => array( 'view', 'edit' ), |
| 569 | 659 | 'readonly' => true, |
| 570 | 660 | ), |
| @@ -616,9 +706,9 @@ | ||
| 616 | 706 | 'enum' => array_merge( array_keys( get_post_statuses() ), array( 'future' ) ), |
| 617 | 707 | 'context' => array( 'view', 'edit' ), |
| 618 | 708 | ), |
| 619 | 709 | 'content' => array( |
| 620 | - 'description' => __( 'Content course.', 'learnpress' ), | |
| 710 | + 'description' => __( 'Course content.', 'learnpress' ), | |
| 621 | 711 | 'type' => 'string', |
| 622 | 712 | 'context' => array( 'view', 'edit' ), |
| 623 | 713 | ), |
| 624 | 714 | 'excerpt' => array( |
| @@ -626,9 +716,9 @@ | ||
| 626 | 716 | 'type' => 'string', |
| 627 | 717 | 'context' => array( 'view', 'edit' ), |
| 628 | 718 | ), |
| 629 | 719 | 'can_finish_course' => array( |
| 630 | - 'description' => __( 'Can finish course', 'learnpress' ), | |
| 720 | + 'description' => __( 'Can finish the course', 'learnpress' ), | |
| 631 | 721 | 'type' => 'boolean', |
| 632 | 722 | 'context' => array( 'view' ), |
| 633 | 723 | 'readonly' => true, |
| 634 | 724 | ), |
| @@ -669,9 +759,9 @@ | ||
| 669 | 759 | ), |
| 670 | 760 | ), |
| 671 | 761 | ), |
| 672 | 762 | 'questions' => array( |
| 673 | - 'description' => __( 'List all Question in Quiz.', 'learnpress' ), | |
| 763 | + 'description' => __( 'List all of the Quiz Questions.', 'learnpress' ), | |
| 674 | 764 | 'type' => 'array', |
| 675 | 765 | 'context' => array( 'view', 'edit' ), |
| 676 | 766 | 'items' => array( |
| 677 | 767 | 'description' => __( 'Question items.', 'learnpress' ), |