Warning: This method has been deprecated. LLMS_AJAX_Handler::query_students() is deprecated in favor of the REST API list students endpoint instead.

LLMS_AJAX_Handler::query_students()

Retrieve Students.


Description Description

Used by Select2 AJAX functions to load paginated student results. Also allows querying by: first name last name email.


Top ↑

Return Return

(void)


Top ↑

Source Source

File: includes/class.llms.ajax.handler.php

	/**
	 * Verify the current user has enrollment access to a quiz's lesson/course.
	 *
	 * Users with the `manage_lifterlms` capability bypass enrollment checks.
	 * When a quiz_id is provided, also validates that the lesson actually owns
	 * that quiz to prevent authorization bypass via user-controlled keys.
	 *
	 * @since 10.0.2
	 *
	 * @param LLMS_Student $student   Student object.
	 * @param int          $lesson_id WP Post ID of the lesson.
	 * @param int          $quiz_id   Optional. WP Post ID of the quiz. When provided the method
	 *                                verifies the lesson's assigned quiz matches this ID.
	 * @return true|WP_Error True if access is granted, WP_Error otherwise.
	 */
	private static function verify_quiz_access( $student, $lesson_id, $quiz_id = 0 ) {

		if ( current_user_can( 'manage_lifterlms' ) ) {
			return true;
		}

		$lesson = llms_get_post( absint( $lesson_id ) );
		if ( ! $lesson || ! is_a( $lesson, 'LLMS_Lesson' ) ) {
			return new WP_Error( 403, __( 'Invalid lesson.', 'lifterlms' ) );
		}

		if ( $quiz_id && absint( $lesson->get( 'quiz' ) ) !== absint( $quiz_id ) ) {
			return new WP_Error( 403, __( 'This quiz does not belong to the specified lesson.', 'lifterlms' ) );
		}

		$course = $lesson->get_course();
		if ( ! $course ) {
			return new WP_Error( 403, __( 'This quiz is not associated with a valid course.', 'lifterlms' ) );
		}

		if ( ! $student->is_enrolled( $course->get( 'id' ) ) ) {
			return new WP_Error( 403, __( 'You must be enrolled in this course to take this quiz.', 'lifterlms' ) );
		}

		return true;
	}

	/**
	 * Start a Quiz Attempt.
	 *
	 * @since 3.9.0
	 * @since 3.16.4 Unknown.
	 * @since 6.4.0 Make sure attempts limit was not reached.
	 * @since 7.8.0 Use `$attempt->get( 'status' )` instead of the not existing `$attempt->get_status()` method and added `can_be_resumed` param.
	 *
	 * @param array $request $_POST data.
	 *                       required:
	 *                           (string) attempt_key
	 *                           or
	 *                           (int) quiz_id
	 *                           (int) lesson_id.
	 * @return WP_Error|array WP_Error on error or array containing html template of the first question.
	 */
	public static function quiz_start( $request ) {

		$err = new WP_Error();

		$student = llms_get_student();
		if ( ! $student ) {
			$err->add( 400, __( 'You must be logged in to take quizzes.', 'lifterlms' ) );
			return $err;
		}

		$access_lesson_id = isset( $request['lesson_id'] ) ? absint( $request['lesson_id'] ) : null;
		$access_quiz_id   = isset( $request['quiz_id'] ) ? absint( $request['quiz_id'] ) : 0;
		if ( ! $access_lesson_id && ! empty( $request['attempt_key'] ) ) {
			$existing_attempt = $student->quizzes()->get_attempt_by_key( $request['attempt_key'] );
			if ( $existing_attempt ) {
				$access_lesson_id = absint( $existing_attempt->get( 'lesson_id' ) );
			}
		}
		$access_check = self::verify_quiz_access( $student, $access_lesson_id, $access_quiz_id );
		if ( is_wp_error( $access_check ) ) {
			return $access_check;
		}

		// Limit reached?
		if ( isset( $request['quiz_id'] ) && ! ( new LLMS_Quiz( $request['quiz_id'] ) )->is_open() ) {
			$err->add( 400, __( "You've reached the maximum number of attempts for this quiz.", 'lifterlms' ) );
			return $err;
		}

		$attempt = false;
		if ( ! empty( $request['attempt_key'] ) ) {
			$attempt = $student->quizzes()->get_attempt_by_key( $request['attempt_key'] );
		}

		if ( ! $attempt || 'new' !== $attempt->get( 'status' ) ) {

			if ( ! isset( $request['quiz_id'] ) || ! isset( $request['lesson_id'] ) ) {
				$err->add( 400, __( 'There was an error starting the quiz. Please return to the lesson and begin again.', 'lifterlms' ) );
				return $err;
			}

			// Mark the previous attempt as ended if it could be resumed but we're restarting instead.
			$previous_attempt_key = ( new LLMS_Quiz( $request['quiz_id'] ) )->get_student_last_attempt_key();
			if ( $previous_attempt_key ) {
				$previous_attempt = $student->quizzes()->get_attempt_by_key( $previous_attempt_key );
				if ( $previous_attempt && $previous_attempt->can_be_resumed() ) {
					$previous_attempt->end();
				}
			}

			$attempt = LLMS_Quiz_Attempt::init( absint( $request['quiz_id'] ), absint( $request['lesson_id'] ), $student->get( 'id' ) );

		}

		$question_id = $attempt->get_first_question();
		if ( ! $question_id ) {
			$err->add( 404, __( 'Unable to start quiz because the quiz does not contain any questions.', 'lifterlms' ) );
			return $err;
		}

		$attempt->start();
		$html = llms_get_template_ajax(
			'content-single-question.php',
			array(
				'attempt'  => $attempt,
				'question' => llms_get_post( $question_id ),
			)
		);

		$quiz  = $attempt->get_quiz();
		$limit = $quiz->has_time_limit() && ! $student->has_unlimited_quiz_time() ? $quiz->get( 'time_limit' ) : false;

		return array(
			'attempt_key'    => $attempt->get_key(),
			'html'           => $html,
			'time_limit'     => $limit,
			'question_id'    => $question_id,
			'total'          => $attempt->get_count( 'questions' ),
			'can_be_resumed' => $attempt->can_be_resumed(),
		);
	}

	/**
	 * Resume a Quiz Attempt.
	 *
	 * @since 7.8.0
	 *
	 * @param array $request $_POST data.
	 *                       required:
	 *                           (string) attempt_key
	 * @return WP_Error|array WP_Error on error or array containing html template of the first question to be answered.
	 */
	public static function quiz_resume( $request ) {

		$err = new WP_Error();

		$student = llms_get_student();
		if ( ! $student ) {
			$err->add( 400, __( 'You must be logged in to take quizzes.', 'lifterlms' ) );
			return $err;
		}

		if ( ! isset( $request['attempt_key'] ) ) {
			$err->add( 400, __( 'Attempt key is required.', 'lifterlms' ) );
			return $err;
		}

		$attempt = $student->quizzes()->get_attempt_by_key( $request['attempt_key'] );

		if ( empty( $attempt ) ) {
			$err->add( 404, __( 'The requested attempt could not be found.', 'lifterlms' ) );
			return $err;
		}

		$access_check = self::verify_quiz_access( $student, $attempt->get( 'lesson_id' ) );
		if ( is_wp_error( $access_check ) ) {
			return $access_check;
		}

		$quiz = $attempt->get_quiz();
		if ( empty( $quiz ) ) {
			$err->add( 400, __( 'No quiz found.', 'lifterlms' ) );
			return $err;
		}

		if (
			! $attempt->can_be_resumed() ||
			! $attempt->is_last_attempt()
		) {
			$err->add(
				400,
				__(
					'There was an error resuming the quiz. Please return to the lesson and begin again.',
					'lifterlms'
				)
			);
			return $err;
		}

		$question_ids = array_column( $attempt->get_questions(), 'id' );
		if ( ! $question_ids ) {
			$err->add(
				404,

Top ↑

Changelog Changelog

Changelog
Version Description
6.2.0 LLMS_AJAX_Handler::query_students() is deprecated in favor of the REST API list students endpoint.
5.9.0 Stop using deprecated FILTER_SANITIZE_STRING.
5.5.0 Do not encode quotes when sanitizing search term.
3.14.2 Introduced.

Top ↑

User Contributed Notes User Contributed Notes

You must log in before being able to contribute a note or feedback.