diff --git a/.changelogs/3092.yml b/.changelogs/3092.yml new file mode 100644 index 0000000000..84253a3ef7 --- /dev/null +++ b/.changelogs/3092.yml @@ -0,0 +1,7 @@ +significance: patch +type: fixed +links: + - "#3058" +attributions: + - "@faisalahammad" +entry: Show "Mark Complete" button when quiz requirements are already met. diff --git a/includes/functions/llms-functions-progression.php b/includes/functions/llms-functions-progression.php index 3d0db87e9a..8f8c14774b 100644 --- a/includes/functions/llms-functions-progression.php +++ b/includes/functions/llms-functions-progression.php @@ -33,23 +33,48 @@ function llms_allow_lesson_completion( $user_id, $lesson_id, $trigger = '', $arg /** * Determines whether or not a "Mark Complete" button should be displayed for a given lesson * - * @param obj $lesson LLMS_Lesson. - * @return boolean - * @since 3.29.0 - * @version 3.29.0 + * If the lesson has a quiz, the button will only be shown if the current user has + * already met the quiz requirements (passed the quiz, or completed at least one attempt + * if passing is not required). + * + * @since 3.29.0 + * @since [version] Show button when quiz requirements are already met. Fixes issue #3058. + * + * @param LLMS_Lesson $lesson LLMS_Lesson instance. + * @return boolean */ function llms_show_mark_complete_button( $lesson ) { $show = true; + // If a quiz button should be shown, check if user already met quiz requirements. if ( llms_show_take_quiz_button( $lesson ) ) { $show = false; + + // Check if current user has already met quiz requirements. + $user_id = get_current_user_id(); + if ( $user_id && $lesson->is_quiz_enabled() ) { + $student = llms_get_student( $user_id ); + if ( $student ) { + $quiz_id = $lesson->get( 'quiz' ); + $attempt = $student->quizzes()->get_best_attempt( $quiz_id ); + + if ( $attempt ) { + $passing_required = llms_parse_bool( $lesson->get( 'require_passing_grade' ) ); + // Show button if: passing not required, OR attempt is passing. + if ( ! $passing_required || $attempt->is_passing() ) { + $show = true; + } + } + } + } } return apply_filters( 'llms_show_mark_complete_button', $show, $lesson ); } + /** * Determines whether or not a "Take Quiz" button should be displayed for a given lesson. * diff --git a/tests/phpunit/unit-tests/functions/class-llms-test-functions-progression.php b/tests/phpunit/unit-tests/functions/class-llms-test-functions-progression.php index 128a2b664f..85442e408c 100644 --- a/tests/phpunit/unit-tests/functions/class-llms-test-functions-progression.php +++ b/tests/phpunit/unit-tests/functions/class-llms-test-functions-progression.php @@ -57,6 +57,144 @@ public function test_llms_show_mark_complete_button() { } + /** + * Test llms_show_mark_complete_button() when quiz has not been attempted. + * + * @since [version] + * + * @return void + */ + public function test_llms_show_mark_complete_button_quiz_not_attempted() { + + $course = $this->factory->course->create_and_get( array( 'sections' => 1, 'lessons' => 1, 'quizzes' => 1 ) ); + $lesson = $course->get_lessons()[0]; + $student = $this->factory->student->create_and_get(); + + wp_set_current_user( $student->get( 'id' ) ); + $student->enroll( $course->get( 'id' ) ); + + // Quiz exists but no attempts - should not show mark complete. + $this->assertFalse( llms_show_mark_complete_button( $lesson ) ); + + } + + /** + * Test llms_show_mark_complete_button() when quiz has been passed. + * + * @since [version] + * + * @return void + */ + public function test_llms_show_mark_complete_button_quiz_passed() { + + $course = $this->factory->course->create_and_get( array( 'sections' => 1, 'lessons' => 1, 'quizzes' => 1 ) ); + $lesson = $course->get_lessons()[0]; + $quiz = $lesson->get_quiz(); + $student = $this->factory->student->create_and_get(); + + wp_set_current_user( $student->get( 'id' ) ); + $student->enroll( $course->get( 'id' ) ); + + // Set passing grade requirement. + $lesson->set( 'require_passing_grade', 'yes' ); + $quiz->set( 'passing_percent', 50 ); + + // Simulate a passing quiz attempt. + $attempt = LLMS_Quiz_Attempt::init( $quiz->get( 'id' ), $lesson->get( 'id' ), $student->get( 'id' ) ); + $attempt->start(); + + // Get all questions and answer them correctly (100% score). + $questions = $attempt->get_questions(); + foreach ( $questions as $key => $question ) { + $questions[ $key ]['answer'] = 'correct_answer'; + $questions[ $key ]['earned'] = $questions[ $key ]['points']; + } + $attempt->set_questions( $questions, true ); + $attempt->end(); + + // Quiz passed - should show mark complete button. + $this->assertTrue( llms_show_mark_complete_button( $lesson ) ); + + } + + /** + * Test llms_show_mark_complete_button() when quiz failed and passing is required. + * + * @since [version] + * + * @return void + */ + public function test_llms_show_mark_complete_button_quiz_failed_passing_required() { + + $course = $this->factory->course->create_and_get( array( 'sections' => 1, 'lessons' => 1, 'quizzes' => 1 ) ); + $lesson = $course->get_lessons()[0]; + $quiz = $lesson->get_quiz(); + $student = $this->factory->student->create_and_get(); + + wp_set_current_user( $student->get( 'id' ) ); + $student->enroll( $course->get( 'id' ) ); + + // Set passing grade requirement. + $lesson->set( 'require_passing_grade', 'yes' ); + $quiz->set( 'passing_percent', 80 ); + + // Simulate a failing quiz attempt (0% score). + $attempt = LLMS_Quiz_Attempt::init( $quiz->get( 'id' ), $lesson->get( 'id' ), $student->get( 'id' ) ); + $attempt->start(); + + // Get all questions and answer them incorrectly (0% score). + $questions = $attempt->get_questions(); + foreach ( $questions as $key => $question ) { + $questions[ $key ]['answer'] = 'wrong_answer'; + $questions[ $key ]['earned'] = 0; + } + $attempt->set_questions( $questions, true ); + $attempt->end(); + + // Quiz failed and passing required - should NOT show mark complete button. + $this->assertFalse( llms_show_mark_complete_button( $lesson ) ); + + } + + /** + * Test llms_show_mark_complete_button() when quiz failed but passing is NOT required. + * + * @since [version] + * + * @return void + */ + public function test_llms_show_mark_complete_button_quiz_failed_passing_not_required() { + + $course = $this->factory->course->create_and_get( array( 'sections' => 1, 'lessons' => 1, 'quizzes' => 1 ) ); + $lesson = $course->get_lessons()[0]; + $quiz = $lesson->get_quiz(); + $student = $this->factory->student->create_and_get(); + + wp_set_current_user( $student->get( 'id' ) ); + $student->enroll( $course->get( 'id' ) ); + + // Passing grade is NOT required. + $lesson->set( 'require_passing_grade', 'no' ); + $quiz->set( 'passing_percent', 80 ); + + // Simulate a failing quiz attempt (0% score). + $attempt = LLMS_Quiz_Attempt::init( $quiz->get( 'id' ), $lesson->get( 'id' ), $student->get( 'id' ) ); + $attempt->start(); + + // Get all questions and answer them incorrectly (0% score). + $questions = $attempt->get_questions(); + foreach ( $questions as $key => $question ) { + $questions[ $key ]['answer'] = 'wrong_answer'; + $questions[ $key ]['earned'] = 0; + } + $attempt->set_questions( $questions, true ); + $attempt->end(); + + // Quiz failed but passing NOT required - should show mark complete button. + $this->assertTrue( llms_show_mark_complete_button( $lesson ) ); + + } + /** * Test the llms_show_take_quiz_button() * @return void