diff --git a/assets/css/sensei-course-theme/content.scss b/assets/css/sensei-course-theme/content.scss index 64ccb8d3e2..e2cc7a3609 100644 --- a/assets/css/sensei-course-theme/content.scss +++ b/assets/css/sensei-course-theme/content.scss @@ -61,13 +61,13 @@ body { .wp-block-post-content { margin-top: 40px; - & > *, .wp-block-group__inner-container > * { + & > *, & ~ div > *, .wp-block-group__inner-container > * { max-width: var(--content-size) !important; margin-left: auto; margin-right: auto; } - &, & > & { + &, & > &, & ~ div { max-width: unset !important; width: unset !important; padding: 0 !important; diff --git a/assets/css/sensei-course-theme/quiz.scss b/assets/css/sensei-course-theme/quiz.scss index f8fc7e3f27..0e6d8a97ca 100644 --- a/assets/css/sensei-course-theme/quiz.scss +++ b/assets/css/sensei-course-theme/quiz.scss @@ -202,10 +202,19 @@ $vertical-spacing-desktop: 80px; } .wp-block-sensei-lms-quiz-actions { + + & > div:first-child.sensei-quiz-actions-secondary { + flex-grow: 1; + justify-content: end; + } + + .sensei-course-theme__button.is-primary { + width: auto; + } + button:disabled { cursor: not-allowed; pointer-events: none; - width: auto; } } diff --git a/changelog/add-footer-design-for-graded-quiz b/changelog/add-footer-design-for-graded-quiz new file mode 100644 index 0000000000..2c72637795 --- /dev/null +++ b/changelog/add-footer-design-for-graded-quiz @@ -0,0 +1,4 @@ +Significance: minor +Type: changed + +Updated stylings of graded quizzes footer in learning mode diff --git a/includes/blocks/course-theme/class-lesson-actions.php b/includes/blocks/course-theme/class-lesson-actions.php index 89fb2f30d4..5121b12c28 100644 --- a/includes/blocks/course-theme/class-lesson-actions.php +++ b/includes/blocks/course-theme/class-lesson-actions.php @@ -161,10 +161,9 @@ public function render( array $attributes = [] ): string { $course_id = Sensei()->lesson->get_course_id( $lesson_id ); - $is_learning_mode = \Sensei_Course_Theme_Option::has_learning_mode_enabled( $course_id ); - $is_awaiting_grade = \Sensei_Quiz::is_quiz_awaiting_grade_for_user( $lesson_id, $user_id ); + $is_learning_mode = \Sensei_Course_Theme_Option::has_learning_mode_enabled( $course_id ); - if ( $is_learning_mode && $is_awaiting_grade && 'quiz' === get_post_type() ) { + if ( $is_learning_mode && 'quiz' === get_post_type() ) { return ''; } diff --git a/includes/class-sensei-quiz.php b/includes/class-sensei-quiz.php index 1cf059a75e..a470dfe20f 100755 --- a/includes/class-sensei-quiz.php +++ b/includes/class-sensei-quiz.php @@ -1859,6 +1859,7 @@ public static function action_buttons() { $course_id = Sensei()->lesson->get_course_id( $lesson_id ); $is_learning_mode = Sensei_Course_Theme_Option::has_learning_mode_enabled( $course_id ); $is_awaiting_grade = self::is_quiz_awaiting_grade_for_user( $lesson_id, get_current_user_id() ); + $post_grade_action = self::maybe_get_button_html_for_quiz_footer( $lesson_id, get_current_user_id() ); $show_grade_pending_button = $is_learning_mode && $is_awaiting_grade; @@ -1868,7 +1869,7 @@ public static function action_buttons() { ] ); - $has_actions = $is_reset_allowed || ! $is_quiz_completed || $show_grade_pending_button; + $has_actions = $is_reset_allowed || ! $is_quiz_completed || $show_grade_pending_button || ! empty( $post_grade_action ); if ( ! $has_actions ) { return; @@ -1896,7 +1897,7 @@ public static function action_buttons() { class="wp-block-button__link button quiz-submit complete sensei-course-theme__button sensei-stop-double-submission" style="" > - + @@ -1904,9 +1905,13 @@ class="wp-block-button__link button quiz-submit complete sensei-course-theme__bu + + + + @@ -1914,7 +1919,7 @@ class="wp-block-button__link button quiz-submit complete sensei-course-theme__bu
@@ -1924,7 +1929,7 @@ class="wp-block-button__link button quiz-submit complete sensei-course-theme__bu
@@ -2417,6 +2422,75 @@ public static function is_quiz_awaiting_grade_for_user( $lesson_id = null, $user return $lesson_status && 'ungraded' === $lesson_status->comment_approved; } + + /** + * Returns the HTML for the next lesson button or the contact Teacher button based on condition. + * If none of the conditions are met, returns null. + * + * @param ?int $lesson_id The lesson ID. + * @param ?int $user_id The user ID. + * + * @return string|null Next lesson or Contact Teacher button if condition holds, null otherwise. + */ + private static function maybe_get_button_html_for_quiz_footer( $lesson_id = null, $user_id = null ) { + if ( empty( $lesson_id ) ) { + $lesson_id = Sensei()->quiz->get_lesson_id(); + } + + if ( empty( $user_id ) ) { + $user_id = get_current_user_id(); + } + + if ( empty( $lesson_id ) || empty( $user_id ) || 'lesson' !== get_post_type( $lesson_id ) || 'quiz' !== get_post_type() ) { + return null; + } + + $lesson_status = \Sensei_Utils::user_lesson_status( $lesson_id, $user_id ); + + $is_complete = $lesson_status && in_array( $lesson_status->comment_approved, [ 'complete', 'graded', 'passed', 'failed' ], true ); + + if ( ! $is_complete ) { + return null; + } + + $is_pass_required = Sensei()->lesson->lesson_has_quiz_with_questions_and_pass_required( $lesson_id ); + $is_reset_allowed = self::is_reset_allowed( $lesson_id ); + + if ( $is_pass_required && 'failed' === $lesson_status->comment_approved ) { + + if ( $is_reset_allowed ) { + return null; + } + + $block = new Sensei_Block_Contact_Teacher(); + $button = self::get_primary_button_anchor_html( __( 'Contact teacher', 'sensei-lms' ), '#' ); + return $block->render_contact_teacher_block( [], $button ); + } + + $prev_next_urls = sensei_get_prev_next_lessons( $lesson_id ); + $next_lesson_url = $prev_next_urls['next']['url'] ?? null; + + if ( $next_lesson_url ) { + return self::get_primary_button_anchor_html( __( 'Continue to next lesson', 'sensei-lms' ), $next_lesson_url ); + } + + return null; + } + + /** + * Returns the HTML for a primary button anchor. + * + * @param string $button_text The button text. + * @param string $url The URL. + * + * @return string The HTML for the primary button anchor. + */ + private static function get_primary_button_anchor_html( $button_text, $url ) { + return '' . + esc_html( $button_text ) . + ''; + } + } /** diff --git a/tests/unit-tests/test-class-quiz.php b/tests/unit-tests/test-class-quiz.php index 6a896a45d7..227d47a254 100644 --- a/tests/unit-tests/test-class-quiz.php +++ b/tests/unit-tests/test-class-quiz.php @@ -2096,4 +2096,356 @@ public function testQuizFooterActions_WhenInProgressButInLearningMode_DoesNotRen /* Assert */ $this->assertStringNotContainsString( 'Pending teacher grade', $result ); } + + public function testActionButtons_WhenPassedInLearningMode_ShowsTheNextLessonButton() { + /* Arrange */ + $user_id = $this->factory->user->create(); + $course_id = $this->factory->course->create(); + $lesson_1 = $this->factory->lesson->create( + [ + 'meta_input' => [ + '_lesson_course' => $course_id, + '_order_' . $course_id => 1, + ], + ] + ); + $this->factory->lesson->create( + [ + 'meta_input' => [ + '_lesson_course' => $course_id, + '_order_' . $course_id => 2, + ], + ] + ); + + $quiz_id = $this->factory->maybe_create_quiz_for_lesson( $lesson_1 ); + $course_enrolment = Sensei_Course_Enrolment::get_course_instance( $course_id ); + $course_enrolment->enrol( $user_id ); + + wp_set_current_user( $user_id ); + + // Enable course theme; + update_post_meta( $course_id, Sensei_Course_Theme_Option::THEME_POST_META_NAME, Sensei_Course_Theme_Option::SENSEI_THEME ); + + $comment_id = Sensei_Utils::update_lesson_status( $user_id, $lesson_1, 'passed' ); + update_comment_meta( $comment_id, 'grade', 2 ); + + $this->go_to( get_permalink( $quiz_id ) ); + + WP_Block_Supports::$block_to_render = [ + 'attrs' => [], + 'blockName' => 'sensei-lms/quiz-actions', + ]; + + global $sensei_question_loop; + $sensei_question_loop['total_pages'] = 1; + + /* Act */ + $result = ( new \Sensei\Blocks\Course_Theme\Quiz_Actions() )->render(); + + /* Assert */ + $this->assertStringContainsString( 'Continue to next lesson', $result ); + } + + public function testActionButtons_WhenFailedInLearningModeButPassRequired_DoesNotShowTheNextLessonButton() { + /* Arrange */ + $user_id = $this->factory->user->create(); + $course_id = $this->factory->course->create(); + $lesson_1 = $this->factory->lesson->create( + [ + 'meta_input' => [ + '_lesson_course' => $course_id, + '_order_' . $course_id => 1, + '_quiz_has_questions' => 1, + ], + ] + ); + $this->factory->lesson->create( + [ + 'meta_input' => [ + '_lesson_course' => $course_id, + '_order_' . $course_id => 2, + ], + ] + ); + + $quiz_id = $this->factory->maybe_create_quiz_for_lesson( $lesson_1 ); + $this->factory->question->create( + [ + 'quiz_id' => $quiz_id, + 'question_type' => 'multiple-choice', + 'question_right_answers' => [ ' ', ' ', ' ' ], + 'question_wrong_answers' => [ ' ' ], + ] + ); + + $course_enrolment = Sensei_Course_Enrolment::get_course_instance( $course_id ); + $course_enrolment->enrol( $user_id ); + + wp_set_current_user( $user_id ); + + // Enable course theme; + update_post_meta( $course_id, Sensei_Course_Theme_Option::THEME_POST_META_NAME, Sensei_Course_Theme_Option::SENSEI_THEME ); + + $comment_id = Sensei_Utils::update_lesson_status( $user_id, $lesson_1, 'failed' ); + update_comment_meta( $comment_id, 'grade', 2 ); + update_post_meta( $quiz_id, '_pass_required', 'on' ); + + $this->go_to( get_permalink( $quiz_id ) ); + + WP_Block_Supports::$block_to_render = [ + 'attrs' => [], + 'blockName' => 'sensei-lms/quiz-actions', + ]; + + global $sensei_question_loop; + $sensei_question_loop['total_pages'] = 1; + + /* Act */ + $result = ( new \Sensei\Blocks\Course_Theme\Quiz_Actions() )->render(); + + /* Assert */ + $this->assertStringContainsString( 'Contact teacher', $result ); + } + + public function testActionButtons_WhenFailedInLearningModeButPassNotRequired_ShowsTheNextLessonButton() { + /* Arrange */ + $user_id = $this->factory->user->create(); + $course_id = $this->factory->course->create(); + $lesson_1 = $this->factory->lesson->create( + [ + 'meta_input' => [ + '_lesson_course' => $course_id, + '_order_' . $course_id => 1, + '_quiz_has_questions' => 1, + ], + ] + ); + $this->factory->lesson->create( + [ + 'meta_input' => [ + '_lesson_course' => $course_id, + '_order_' . $course_id => 2, + ], + ] + ); + + $quiz_id = $this->factory->maybe_create_quiz_for_lesson( $lesson_1 ); + $this->factory->question->create( + [ + 'quiz_id' => $quiz_id, + 'question_type' => 'multiple-choice', + 'question_right_answers' => [ ' ', ' ', ' ' ], + 'question_wrong_answers' => [ ' ' ], + ] + ); + + $course_enrolment = Sensei_Course_Enrolment::get_course_instance( $course_id ); + $course_enrolment->enrol( $user_id ); + + wp_set_current_user( $user_id ); + + // Enable course theme; + update_post_meta( $course_id, Sensei_Course_Theme_Option::THEME_POST_META_NAME, Sensei_Course_Theme_Option::SENSEI_THEME ); + + $comment_id = Sensei_Utils::update_lesson_status( $user_id, $lesson_1, 'failed' ); + update_comment_meta( $comment_id, 'grade', 2 ); + update_post_meta( $quiz_id, '_pass_required', 0 ); + + $this->go_to( get_permalink( $quiz_id ) ); + + WP_Block_Supports::$block_to_render = [ + 'attrs' => [], + 'blockName' => 'sensei-lms/quiz-actions', + ]; + + global $sensei_question_loop; + $sensei_question_loop['total_pages'] = 1; + + /* Act */ + $result = ( new \Sensei\Blocks\Course_Theme\Quiz_Actions() )->render(); + + /* Assert */ + $this->assertStringContainsString( 'Continue to next lesson', $result ); + } + + public function testActionButtons_WhenPassedButNotInLearningMode_DoesNotShowTheNextLessonButton() { + /* Arrange */ + $user_id = $this->factory->user->create(); + $course_id = $this->factory->course->create(); + $lesson_1 = $this->factory->lesson->create( + [ + 'meta_input' => [ + '_lesson_course' => $course_id, + '_order_' . $course_id => 1, + '_quiz_has_questions' => 1, + ], + ] + ); + $this->factory->lesson->create( + [ + 'meta_input' => [ + '_lesson_course' => $course_id, + '_order_' . $course_id => 2, + ], + ] + ); + + $quiz_id = $this->factory->maybe_create_quiz_for_lesson( $lesson_1 ); + $this->factory->question->create( + [ + 'quiz_id' => $quiz_id, + 'question_type' => 'multiple-choice', + 'question_right_answers' => [ ' ', ' ', ' ' ], + 'question_wrong_answers' => [ ' ' ], + ] + ); + + $course_enrolment = Sensei_Course_Enrolment::get_course_instance( $course_id ); + $course_enrolment->enrol( $user_id ); + + wp_set_current_user( $user_id ); + + $comment_id = Sensei_Utils::update_lesson_status( $user_id, $lesson_1, 'passed' ); + update_comment_meta( $comment_id, 'grade', 2 ); + update_post_meta( $quiz_id, '_pass_required', 0 ); + + $this->go_to( get_permalink( $quiz_id ) ); + + WP_Block_Supports::$block_to_render = [ + 'attrs' => [], + 'blockName' => 'sensei-lms/quiz-actions', + ]; + + global $sensei_question_loop; + $sensei_question_loop['total_pages'] = 1; + + /* Act */ + $result = ( new \Sensei\Blocks\Course_Theme\Quiz_Actions() )->render(); + + /* Assert */ + $this->assertStringNotContainsString( 'Continue to next lesson', $result ); + } + + public function testActionButtons_WhenPassedButNextLessonHasLowerOrder_DoesNotShowTheNextLessonButton() { + /* Arrange */ + $user_id = $this->factory->user->create(); + $course_id = $this->factory->course->create(); + $lesson_1 = $this->factory->lesson->create( + [ + 'meta_input' => [ + '_lesson_course' => $course_id, + '_order_' . $course_id => 2, + '_quiz_has_questions' => 1, + ], + ] + ); + $this->factory->lesson->create( + [ + 'meta_input' => [ + '_lesson_course' => $course_id, + '_order_' . $course_id => 1, + ], + ] + ); + + $quiz_id = $this->factory->maybe_create_quiz_for_lesson( $lesson_1 ); + $this->factory->question->create( + [ + 'quiz_id' => $quiz_id, + 'question_type' => 'multiple-choice', + 'question_right_answers' => [ ' ', ' ', ' ' ], + 'question_wrong_answers' => [ ' ' ], + ] + ); + + $course_enrolment = Sensei_Course_Enrolment::get_course_instance( $course_id ); + $course_enrolment->enrol( $user_id ); + + wp_set_current_user( $user_id ); + + // Enable course theme; + update_post_meta( $course_id, Sensei_Course_Theme_Option::THEME_POST_META_NAME, Sensei_Course_Theme_Option::SENSEI_THEME ); + + $comment_id = Sensei_Utils::update_lesson_status( $user_id, $lesson_1, 'passed' ); + update_comment_meta( $comment_id, 'grade', 2 ); + update_post_meta( $quiz_id, '_pass_required', 0 ); + + $this->go_to( get_permalink( $quiz_id ) ); + + WP_Block_Supports::$block_to_render = [ + 'attrs' => [], + 'blockName' => 'sensei-lms/quiz-actions', + ]; + + global $sensei_question_loop; + $sensei_question_loop['total_pages'] = 1; + + /* Act */ + $result = ( new \Sensei\Blocks\Course_Theme\Quiz_Actions() )->render(); + + /* Assert */ + $this->assertStringNotContainsString( 'Continue to next lesson', $result ); + } + + public function testActionButtons_WhenQuizPassedButOnLessonPage_DoesNotShowTheNextLessonButton() { + /* Arrange */ + $user_id = $this->factory->user->create(); + $course_id = $this->factory->course->create(); + $lesson_1 = $this->factory->lesson->create( + [ + 'meta_input' => [ + '_lesson_course' => $course_id, + '_order_' . $course_id => 1, + '_quiz_has_questions' => 1, + ], + ] + ); + $this->factory->lesson->create( + [ + 'meta_input' => [ + '_lesson_course' => $course_id, + '_order_' . $course_id => 2, + ], + ] + ); + + $quiz_id = $this->factory->maybe_create_quiz_for_lesson( $lesson_1 ); + $this->factory->question->create( + [ + 'quiz_id' => $quiz_id, + 'question_type' => 'multiple-choice', + 'question_right_answers' => [ ' ', ' ', ' ' ], + 'question_wrong_answers' => [ ' ' ], + ] + ); + + $course_enrolment = Sensei_Course_Enrolment::get_course_instance( $course_id ); + $course_enrolment->enrol( $user_id ); + + wp_set_current_user( $user_id ); + + // Enable course theme; + update_post_meta( $course_id, Sensei_Course_Theme_Option::THEME_POST_META_NAME, Sensei_Course_Theme_Option::SENSEI_THEME ); + + $comment_id = Sensei_Utils::update_lesson_status( $user_id, $lesson_1, 'passed' ); + update_comment_meta( $comment_id, 'grade', 2 ); + update_post_meta( $quiz_id, '_pass_required', 0 ); + + $this->go_to( get_permalink( $lesson_1 ) ); + + WP_Block_Supports::$block_to_render = [ + 'attrs' => [], + 'blockName' => 'sensei-lms/quiz-actions', + ]; + + global $sensei_question_loop; + $sensei_question_loop['total_pages'] = 1; + + /* Act */ + $result = ( new \Sensei\Blocks\Course_Theme\Quiz_Actions() )->render(); + + /* Assert */ + $this->assertStringNotContainsString( 'Continue to next lesson', $result ); + } }