Source: includes/template-functions.php

<?php
if ( ! defined( 'ABSPATH' ) ) {
	exit; } // Exit if accessed directly

/***************************************************************************************************
 *  Output tags.
 ***************************************************************************************************/

/**
 * course_single_lessons function.
 *
 * @access public
 * @return void
 */
function course_single_lessons() {
	if ( ! Sensei_Utils::show_course_lessons( get_the_ID() ) ) {
		return;
	}

	add_filter( 'post_class', [ 'Sensei_Lesson', 'single_course_lessons_classes' ] );

	// load backwards compatible template name if it exists in the users theme
	$located_template = locate_template( Sensei()->template_url . 'single-course/course-lessons.php' );
	if ( $located_template ) {

		Sensei_Templates::get_template( 'single-course/course-lessons.php' );
		return;

	}

	Sensei_Templates::get_template( 'single-course/lessons.php' );

	remove_filter( 'post_class', [ 'Sensei_Lesson', 'single_course_lessons_classes' ] );

}

/**
 * quiz_question_type function.
 *
 * @access public
 * @since  1.3.0
 * @return void
 * @deprecated
 */
function quiz_question_type( $question_type = 'multiple-choice' ) {

	Sensei_Question::load_question_template( $question_type );

}


/**
 * sensei_start_course_form function.
 *
 * @access public
 * @param mixed $course_id
 * @return void
 */
function sensei_start_course_form( $course_id ) {

	$prerequisite_complete = Sensei_Course::is_prerequisite_complete( $course_id );

	if ( $prerequisite_complete ) {
		wp_enqueue_script( 'sensei-stop-double-submission' );

		?><form method="POST" action="<?php echo esc_url( get_permalink( $course_id ) ); ?>">

				<input type="hidden" name="<?php echo esc_attr( 'woothemes_sensei_start_course_noonce' ); ?>" id="<?php echo esc_attr( 'woothemes_sensei_start_course_noonce' ); ?>" value="<?php echo esc_attr( wp_create_nonce( 'woothemes_sensei_start_course_noonce' ) ); ?>" />

				<span><input name="course_start" type="submit" class="course-start sensei-stop-double-submission" value="<?php esc_html_e( 'Take This Course', 'sensei-lms' ); ?>"/></span>

			</form>
			<?php
	}
}

/**
 * sensei_recent_comments_widget_filter function.
 *
 * @access public
 * @param array $widget_args (default: array())
 * @return array
 */
function sensei_recent_comments_widget_filter( $widget_args = array() ) {
	if ( ! isset( $widget_args['post_type'] ) ) {
		$widget_args['post_type'] = array( 'post', 'page' );
	}
	return $widget_args;
}
add_filter( 'widget_comments_args', 'sensei_recent_comments_widget_filter', 10, 1 );

/**
 * sensei_course_archive_filter function.
 *
 * @access public
 * @param WP_Query $query ( default: array ( ) )
 * @return void
 */
function sensei_course_archive_filter( $query ) {

	if ( ! $query->is_main_query() ) {
		return;
	}

	// Apply Filter only if on frontend and when course archive is running
	$course_page_id = intval( Sensei()->settings->settings['course_page'] );

	if ( ! is_admin() && 0 < $course_page_id && 0 < intval( $query->get( 'page_id' ) ) && $query->get( 'page_id' ) == $course_page_id ) {
		// Check for pagination settings
		if ( isset( Sensei()->settings->settings['course_archive_amount'] ) && ( 0 < absint( Sensei()->settings->settings['course_archive_amount'] ) ) ) {
			$amount = absint( Sensei()->settings->settings['course_archive_amount'] );
		} else {
			$amount = $query->get( 'posts_per_page' );
		}
		$query->set( 'posts_per_page', $amount );
	}
}
add_filter( 'pre_get_posts', 'sensei_course_archive_filter', 10, 1 );

/**
 * sensei_complete_lesson_button description
 * since 1.0.3
 *
 * @return html
 */
function sensei_complete_lesson_button() {
	do_action( 'sensei_complete_lesson_button' );
}

/**
 * sensei_reset_lesson_button description
 * since 1.0.3
 *
 * @return html
 */
function sensei_reset_lesson_button() {
	do_action( 'sensei_reset_lesson_button' );
}

/**
 * Returns all of the modules and lessons in a course, in order.
 *
 * @since  1.9.20
 * @param  string|bool $course_id Course ID
 * @return array Course modules and lessons
 */
function sensei_get_modules_and_lessons( $course_id ) {
	$lesson_ids          = array();
	$modules_and_lessons = array();
	$course_modules      = Sensei()->modules->get_course_modules( $course_id );

	// Add all modules and lessons for the current course to an array.
	if ( ! empty( $course_modules ) ) {
		foreach ( (array) $course_modules as $module ) {
			$module_lessons = Sensei()->modules->get_lessons( $course_id, $module->term_id );

			if ( ! $module_lessons ) {
				continue;
			}

			$modules_and_lessons[] = $module;

			foreach ( $module_lessons as $lesson_item ) {
				$modules_and_lessons[] = $lesson_item;
				$lesson_ids[]          = $lesson_item->ID;
			}
		}
	}

	// Append all lessons not associated with a particular module to the array.
	$other_lessons = sensei_get_other_lessons( $course_id, $lesson_ids );

	if ( count( $other_lessons ) > 0 ) {
		foreach ( $other_lessons as $other_lesson ) {
			$modules_and_lessons[] = $other_lesson;
		}
	}

	return $modules_and_lessons;
}

/**
 * Returns the lessons in a course that are not associated with a module.
 *
 * @since  1.9.20
 * @param  string|bool $course_id Course ID
 * @param  array       $lesson_ids Lesson IDs to exclude
 * @return array Other lessons not part of a module
 */
function sensei_get_other_lessons( $course_id, $lesson_ids ) {

	global $wp_query;
	$course_lessons_post_status = isset( $wp_query ) && $wp_query->is_preview() ? 'all' : 'publish';

	$args = array(
		'post_type'        => 'lesson',
		'post_status'      => $course_lessons_post_status,
		'posts_per_page'   => -1,
		'suppress_filters' => 0,
		'meta_key'         => '_order_' . $course_id,
		'orderby'          => 'meta_value_num date',
		'order'            => 'ASC',
		'meta_query'       => array(
			array(
				'key'   => '_lesson_course',
				'value' => intval( $course_id ),
			),
		),
		'post__not_in'     => $lesson_ids,
	);

	return get_posts( $args );
}

/**
 * Returns the URL for a navigation link.
 *
 * @since  1.9.20
 * @param  string|bool     $course_id Course ID
 * @param  WP_Post|WP_Term $item      WP_Post (lesson/quiz) or WP_Term (module)
 * @return string URL or empty string
 */
function sensei_get_navigation_url( $course_id, $item ) {
	if ( ! $item || empty( $course_id ) ) {
		return '';
	}

	if ( $item->term_id ) { // Module
		return add_query_arg(
			'course_id',
			intval( $course_id ),
			get_term_link( $item, Sensei()->modules->taxonomy )
		);
	} else {    // Lesson
		return get_permalink( $item->ID );
	}
}

/**
 * Returns the text for a navigation link.
 *
 * @since  1.9.20
 * @param  WP_Post|WP_Term $item WP_Post for a lesson/quiz or WP_Term for a module
 * @return string Link text or empty string
 */
function sensei_get_navigation_link_text( $item ) {
	if ( ! $item ) {
		return '';
	}

	if ( $item->term_id ) { // Module
		return $item->name;
	} else {    // Lesson
		return $item->post_title;
	}
}

/**
 * Returns navigation links for the modules and lessons in a course.
 *
 * @since  1.0.9
 * @param  int|null $lesson_id Lesson ID.
 * @return array Multi-dimensional array of previous and next links.
 */
function sensei_get_prev_next_lessons( $lesson_id = 0 ) {
	// For modules, $lesson_id is the first lesson in the module.
	$links               = array();
	$course_id           = Sensei()->lesson->get_course_id( $lesson_id );
	$modules_and_lessons = sensei_get_modules_and_lessons( $course_id );

	if ( is_array( $modules_and_lessons ) && count( $modules_and_lessons ) > 0 ) {
		$found = false;

		foreach ( $modules_and_lessons as $item ) {
			$item_is_linkable = true;

			if ( $item instanceof WP_Term
				 && 'module' === $item->taxonomy
				 && ! Sensei()->modules->do_link_to_module( $item, true )
			) {
				$item_is_linkable = false;
			}

			if ( $found && $item_is_linkable ) {
				$next = $item;
				break;
			}

			if (
				// Is it the current module?
				( isset( $item->term_id ) && is_tax( Sensei()->modules->taxonomy, $item->term_id ) )

				// Is it the current lesson?
				|| ( isset( $item->ID ) && absint( $item->ID ) === absint( $lesson_id ) )
			) {
				$found = true;
			} elseif ( $item_is_linkable ) {
				$previous = $item;
			}
		}
	}

	if ( isset( $previous ) ) {
		$links['previous'] = array(
			'url'  => sensei_get_navigation_url( $course_id, $previous ),
			'name' => sensei_get_navigation_link_text( $previous ),
		);
	}

	if ( isset( $next ) ) {
		$links['next'] = array(
			'url'  => sensei_get_navigation_url( $course_id, $next ),
			'name' => sensei_get_navigation_link_text( $next ),
		);
	}

	return $links;
}

/**
 * Determine if a user has completed the pre-requisite lesson.
 *
 * @uses
 *
 * @param int $current_lesson_id
 * @param int $user_id
 * @return bool
 */
function sensei_has_user_completed_prerequisite_lesson( $current_lesson_id, $user_id ) {

	return Sensei_Lesson::is_prerequisite_complete( $current_lesson_id, $user_id );

}

/*******************************
 *
 * Module specific template tags
 ******************************/

/**
 * This function checks if the current course has modules.
 *
 * This must only be used within the loop.
 *
 * I checks the current global post (course) if it has modules.
 *
 * @since 1.9.0
 *
 * @param string $course_post_id options
 * @return bool
 */
function sensei_have_modules() {

	global $sensei_modules_loop;

	// check the current item compared to the total number of modules
	if ( $sensei_modules_loop['current'] + 1 > $sensei_modules_loop['total'] ) {

		return false;

	} else {

		return true;

	}

} //sensei_have_modules


/**
 * Setup the next module int the module loop
 *
 * @since 1.9.0
 */
function sensei_setup_module() {

	global  $sensei_modules_loop, $wp_query;

	// increment the index
	$sensei_modules_loop['current']++;
	$index = $sensei_modules_loop['current'];
	if ( isset( $sensei_modules_loop['modules'][ $index ] ) ) {

		$sensei_modules_loop['current_module'] = $sensei_modules_loop['modules'][ $index ];
		// setup the query for the module lessons
		$course_id      = $sensei_modules_loop['course_id'];
		$module_term_id = $sensei_modules_loop['current_module']->term_id;
		$modules_query  = Sensei()->modules->get_lessons_query( $course_id, $module_term_id );

		// setup the global wp-query only if the lessons
		if ( $modules_query->have_posts() ) {

			// phpcs:ignore WordPress.WP.GlobalVariablesOverride.Prohibited -- Use modules query for modules loop. Reset in `Sensei_Core_Modules::teardown_single_course_module_loop()`
			$wp_query = $modules_query;

		} else {

			wp_reset_query();

		}
	} else {

		wp_reset_query();

	}

}

/**
 * Check if the current module in the modules loop has any lessons.
 * This relies on the global $wp_query. Which will be setup for each module
 * by sensei_the_module(). This function must only be used withing the module lessons loop.
 *
 * If the loop has not been initiated this function will check if the first
 * module has lessons.
 *
 * @return bool
 */
function sensei_module_has_lessons() {

	global $wp_query, $sensei_modules_loop;

	if ( 'lesson' == $wp_query->get( 'post_type' ) ) {

		return have_posts();

	} else {

		// if the loop has not been initiated check the first module has lessons
		if ( -1 == $sensei_modules_loop['current'] ) {

			$index = 0;

			if ( isset( $sensei_modules_loop['modules'][ $index ] ) ) {
				// setup the query for the module lessons
				$course_id = $sensei_modules_loop['course_id'];

				$module_term_id = $sensei_modules_loop['modules'][ $index ]->term_id;
				$modules_query  = Sensei()->modules->get_lessons_query( $course_id, $module_term_id );

				// setup the global wp-query only if the lessons
				if ( $modules_query->have_posts() ) {

					return true;

				}
			}
		}
		// default to false if the first module doesn't have posts
		return false;

	}

}

/**
 * This function return the Module title to be used as an html element attribute value.
 *
 * Should only be used within the Sensei modules loop.
 *
 * @since 1.9.0
 *
 * @uses sensei_the_module_title
 * @return string
 */
function sensei_the_module_title_attribute() {

	echo esc_attr( sensei_get_the_module_title() );

}

/**
 * Returns a permalink to the module currently loaded within the Single Course module loop.
 *
 * This function should only be used with the Sensei modules loop.
 *
 * @return string
 */
function sensei_the_module_permalink() {

	global $sensei_modules_loop;
	$course_id      = $sensei_modules_loop['course_id'];
	$module_url     = add_query_arg( 'course_id', $course_id, get_term_link( $sensei_modules_loop['current_module'], 'module' ) );
	$module_term_id = $sensei_modules_loop['current_module']->term_id;

	/**
	 * Filter the module permalink url. This fires within the sensei_the_module_permalink function.
	 *
	 * @since 1.9.0
	 *
	 * @hook sensei_the_module_permalink
	 *
	 * @param {string} $module_url     The module permalink url.
	 * @param {int}    $module_term_id The module term id.
	 * @param {string} $course_id      The course id.
	 * @return {string} The module permalink url.
	 */
	 echo esc_url_raw( apply_filters( 'sensei_the_module_permalink', $module_url, $module_term_id, $course_id ) );

}

/**
 * Returns the current module name. This must be used
 * within the Sensei module loop.
 *
 * @since 1.9.0
 *
 * @return string
 */
function sensei_get_the_module_title() {

	global $sensei_modules_loop;

	$module_title   = $sensei_modules_loop['current_module']->name;
	$module_term_id = $sensei_modules_loop['current_module']->term_id;
	$course_id      = $sensei_modules_loop['course_id'];

	/**
	 * Filter the module title.
	 *
	 * This fires within the sensei_the_module_title function.
	 *
	 * @since 1.9.0
	 *
	 * @hook sensei_the_module_title
	 *
	 * @param {string} $module_title   The module title.
	 * @param {int}    $module_term_id The module term id.
	 * @param {int}    $course_id      The course id.
	 * @return {string} The module title.
	 */
	return apply_filters( 'sensei_the_module_title', $module_title, $module_term_id, $course_id );

}

/**
 * Ouputs the current module name. This must be used
 * within the Sensei module loop.
 *
 * @since 1.9.0
 * @uses sensei_get_the_module_title
 * @return string
 */
function sensei_the_module_title() {

	echo esc_html( sensei_get_the_module_title() );

}

/**
 * Give the current user's lesson progress status
 * Used in the module loop on the courses page
 *
 * @since 1.9.0
 * @return string
 */
function sensei_get_the_module_status() {

	if ( ! is_user_logged_in() ) {
		return '';
	}

	global $sensei_modules_loop;

	$module_term_id  = $sensei_modules_loop['current_module']->term_id;
	$course_id       = $sensei_modules_loop['course_id'];
	$module_progress = Sensei()->modules->get_user_module_progress( $module_term_id, $course_id, get_current_user_id() );

	$module_status = '';
	$status_class  = '';
	if ( $module_progress && $module_progress > 0 ) {

		$module_status = __( 'Completed', 'sensei-lms' );
		$status_class  = 'completed';

		if ( $module_progress < 100 ) {

			$module_status = __( 'In progress', 'sensei-lms' );
			$status_class  = 'in-progress';

		}
	}

	if ( empty( $module_status ) ) {
		return '';
	}

	$module_status_html = '<span class="status module-status ' . esc_attr( $status_class ) . '">'
							. esc_html( $module_status )
							. '</span>';

	/**
	 * Filter the module status.
	 *
	 * This fires within the sensei_get_the_module_status function.
	 *
	 * @since 1.9.0
	 *
	 * @hook sensei_the_module_status_html
	 *
	 * @param {string} $module_status_html The module status html.
	 * @param {int}    $module_term_id     The module term id.
	 * @param {int}    $course_id          The course id.
	 * @return {string} The module status html.
	 */
	return apply_filters( 'sensei_the_module_status_html', $module_status_html, $module_term_id, $course_id );

}

/**
 * Print out the current module status
 *
 * @since 1.9.0
 */
function sensei_the_module_status() {

	echo wp_kses_post( sensei_get_the_module_status() );

}

/**
 * Get the module ID.
 * This must be used within the Sensei module loop.
 *
 * @since 1.9.7
 *
 * @return int $id Module ID.
 */
function sensei_get_the_module_id() {
	global $sensei_modules_loop;

	$module_term_id = $sensei_modules_loop['current_module']->term_id;

	/**
	 * Filter the module ID.
	 *
	 * This fires within the sensei_get_the_module_id function.
	 *
	 * @since 1.9.7
	 *
	 * @hook sensei_the_module_id
	 *
	 * @param {int} $module_term_id Module ID.
	 * @return {int} Module ID.
	 */
	return apply_filters( 'sensei_the_module_id', $module_term_id );
}

/**
 * Print out the current module ID
 *
 * @since 1.9.7
 */
function sensei_the_module_id() {

	echo esc_html( sensei_get_the_module_id() );

}

/**
 * Gets a count of the lessons in the current module in the modules loop.
 *
 * @since 3.1.0
 *
 * @return int Number of lessons in the current module.
 */
function sensei_module_lesson_count() {
	global $sensei_modules_loop;

	if ( ! isset( $sensei_modules_loop['course_id'] ) || ! isset( $sensei_modules_loop['current_module'] ) || ! isset( $sensei_modules_loop['current_module']->term_id ) ) {
		return 0;
	}

	$course_id      = $sensei_modules_loop['course_id'];
	$module_term_id = $sensei_modules_loop['current_module']->term_id;

	return count( Sensei()->modules->get_lessons( $course_id, $module_term_id ) );
}

/************************
 *
 * Single Quiz Functions
 ***********************/

/**
 * This function can only be run inside the the quiz question lessons loop.
 *
 * It will check if the current lessons loop has questions
 *
 * @since 1.9.0
 *
 * @return bool
 */
function sensei_quiz_has_questions() {

	global $sensei_question_loop;

	if ( empty( $sensei_question_loop['questions'] ) ) {
		return false;
	}

	$questions_count = is_countable( $sensei_question_loop['questions'] ) ? count( $sensei_question_loop['questions'] ) : 0;

	if ( 0 === $questions_count ) {
		return false;
	}

	return $sensei_question_loop['current'] + 1 < $questions_count;
}

/**
 * This funciton must only be run inside the quiz question loop.
 *
 * It will setup the next question in the loop into the current spot within the loop for further
 * execution.
 *
 * @since 1.9.0
 */
function sensei_setup_the_question() {

	global $sensei_question_loop;

	$sensei_question_loop['current']++;
	$index                                    = $sensei_question_loop['current'];
	$sensei_question_loop['current_question'] = $sensei_question_loop['questions'][ $index ];

}

/**
 * This function must only be run inside the quiz question loop.
 *
 * This function gets the type and loads the template that will handle it.
 */
function sensei_the_question_content() {

	global $sensei_question_loop;

	$question_type = Sensei()->question->get_question_type( $sensei_question_loop['current_question']->ID );

	// load the template that displays the question information.
	?>

	<div class="wp-block-sensei-lms-question-answers">
		<?php
			/**
			 * Fires before the question answers are displayed inside the answers block.
			 *
			 * @hook sensei_quiz_question_answers_inside_before
			 *
			 * @since 4.17.0
			 *
			 * @param {int} $question_id The ID of the question.
			 */
			do_action( 'sensei_quiz_question_answers_inside_before', $sensei_question_loop['current_question']->ID );

			Sensei_Question::load_question_template( $question_type );

			/**
			 * Fires after the question answers are displayed inside the answers block.
			 *
			 * @hook sensei_quiz_question_answers_inside_after
			 *
			 * @since 4.17.0
			 *
			 * @param {int} $question_id The ID of the question.
			 */
			do_action( 'sensei_quiz_question_answers_inside_after', $sensei_question_loop['current_question']->ID );
		?>
	</div>

	<?php
}

/**
 * Outputs the question class. This must only be run withing the single quiz question loop.
 *
 * @since 1.9.0
 */
function sensei_the_question_class() {

	global $sensei_question_loop;

	$question_type = Sensei()->question->get_question_type( $sensei_question_loop['current_question']->ID );

	/**
	 * filter the sensei question class within the quiz question loop.
	 *
	 * @since 1.9.0
	 *
	 * @hook sensei_question_classes
	 *
	 * @param {array} $classes The classes to be applied to the question.
	 * @return {array} The classes to be applied to the question.
	 */
	 $classes = apply_filters( 'sensei_question_classes', array( $question_type ) );

	$html_classes = 'wp-block-sensei-lms-quiz-question ';
	foreach ( $classes as $class ) {

		$html_classes .= $class . ' ';

	}

	echo esc_attr( trim( $html_classes ) );

}

/**
 * Output the ID of the current question within the quiz question loop.
 *
 * @since 1.9.0
 */
function sensei_get_the_question_id() {

	global $sensei_question_loop;
	if ( isset( $sensei_question_loop['current_question']->ID ) ) {

		return $sensei_question_loop['current_question']->ID;

	}

}

/**
 * Return the number of the current question within the quiz question loop.
 *
 * @since 3.15.0
 *
 * @return int
 */
function sensei_get_the_question_number() {

	global $sensei_question_loop;

	$question_number = ( $sensei_question_loop['current_page'] - 1 )
		* $sensei_question_loop['posts_per_page']
		+ $sensei_question_loop['current']
		+ 1;

	/**
	 * Filters the number of the current question within the quiz question loop.
	 *
	 * @hook  sensei_the_question_number
	 * @since 3.15.0
	 *
	 * @param {int} $question_number The question number.
	 * @param {int} $question_id     The question ID.
	 * @param {int} $quiz_id         The quiz ID.
	 *
	 * @return {int}
	 */
	return apply_filters(
		'sensei_the_question_number',
		$question_number,
		$sensei_question_loop['current_question']->ID,
		$sensei_question_loop['quiz_id']
	);

}

/************************
 *
 * Single Lesson Functions
 ***********************/

/**
 * Ouput the single lesson meta
 *
 * The function should only be called on the single lesson
 */
function sensei_the_single_lesson_meta() {

	// if the lesson meta is included within theme load that instead of the function content
	$template = Sensei_Templates::locate_template( 'single-lesson/lesson-meta.php' );
	if ( ! empty( $template ) ) {

		Sensei_Templates::get_template( 'single-lesson/lesson-meta.php' );
		return;

	}

	$lesson_id = get_the_ID();
	if ( ! $lesson_id ) {
		return;
	}

	// Get the meta info
	$lesson_course_id = absint( get_post_meta( $lesson_id, '_lesson_course', true ) );
	$is_preview       = $lesson_id && Sensei_Utils::is_preview_lesson( $lesson_id );

	// Complete Lesson Logic
	do_action( 'sensei_complete_lesson' );
	// Check that the course has been started
	if ( Sensei()->access_settings()
		|| Sensei_Course::is_user_enrolled( $lesson_course_id )
		|| $is_preview ) {
		?>
		<section class="lesson-meta">
			<?php
			/**
			 * Filters the position of the video in the lesson meta.
			 *
			 * Fires before the lesson meta is displayed.
			 *
			 * @hook sensei_video_position
			 *
			 * @param {string} $position  The position of the video in the lesson meta, default: top.
			 * @param {int}    $lesson_id The lesson ID.
			 * @return {string} The position of the video in the lesson meta.
			 */
			if ( apply_filters( 'sensei_video_position', 'top', get_the_ID() ) == 'bottom' ) {

				do_action( 'sensei_lesson_video', get_the_ID() );

			}
			?>
			<?php do_action( 'sensei_frontend_messages' ); ?>

		</section>

		<?php do_action( 'sensei_lesson_back_link', $lesson_course_id ); ?>

		<?php
	}

	do_action( 'sensei_lesson_meta_extra', get_the_ID() );

}

/**
 * This function runs the most common header hooks and ensures
 * templates are setup correctly.
 *
 * This function also runs the get_header for the general WP header setup.
 *
 * @uses get_header
 *
 * @since 1.9.0
 */
function get_sensei_header() {

	/**
	 * Allow user to stop the output of get_sensei_header which also includes a call to get_header.
	 *
	 * @since 1.9.5
	 *
	 * @hook sensei_show_main_header
	 *
	 * @param {bool} $show_main_header Whether to show the main header.
	 * @return {bool} Whether to show the main header.
	 */
	if ( ! apply_filters( 'sensei_show_main_header', true ) ) {
		return;
	}

	if ( ! defined( 'ABSPATH' ) ) {
		exit;
	}

	get_header();

	/**
	 * sensei_before_main_content hook
	 *
	 * @hooked sensei_output_content_wrapper - 10 (outputs opening divs for the content)
	 */
	do_action( 'sensei_before_main_content' );

}

/**
 * This function runs the most common footer hooks and ensures
 * templates are setup correctly.
 *
 * This function also runs the get_header for the general WP header setup.
 *
 * @uses get_footer
 *
 * @since 1.9.0
 */
function get_sensei_footer() {

	/**
	 * Allow user to stop the output of get_sensei_footer which also includes a call to get_header.
	 *
	 * @since 1.9.5
	 *
	 * @hook sensei_show_main_footer
	 *
	 * @param {bool} $show_main_footer Whether to show the main footer.
	 * @return {bool} Whether to show the main footer.
	 */
	if ( ! apply_filters( 'sensei_show_main_footer', true ) ) {
		return;
	}

	/**
	 * sensei_pagination hook
	 *
	 * @hooked sensei_pagination - 10 (outputs pagination)
	 */
	do_action( 'sensei_pagination' );

	/**
	 * sensei_after_main_content hook
	 *
	 * @hooked sensei_output_content_wrapper_end - 10 (outputs closing divs for the content)
	 */
	do_action( 'sensei_after_main_content' );

	/**
	 * sensei_sidebar hook
	 *
	 * @hooked sensei_get_sidebar - 10
	 */
	do_action( 'sensei_sidebar' );

	get_footer();

}

/**
 * Output the permissions message
 * title.
 *
 * @since 1.9.0
 */
function the_no_permissions_title() {

	/**
	 * Filter the no permissions title just before it is echo'd on the no-permissions.php file.
	 *
	 * @since 1.9.0
	 *
	 * @hook sensei_the_no_permissions_title
	 *
	 * @param {string} $no_permissions_title The no permissions title.
	 * @return {string} Filtered no permissions title.
	 */
	echo wp_kses_post( apply_filters( 'sensei_the_no_permissions_title', Sensei()->permissions_message['title'] ) );

}

/**
 * Output the permissions message.
 *
 * @since 1.9.0
 */
function the_no_permissions_message( $post_id ) {

	/**
	 * Filter the no permissions message just before it is echo'd on the no-permissions.php file.
	 *
	 * @since 1.9.0
	 *
	 * @hook sensei_the_no_permissions_message
	 *
	 * @param {string} $no_permissions_message The no permissions message.
	 * @param {int}    $post_id                The post ID.
	 * @return {string} Filtered no permissions message.
	 */
	echo wp_kses_post( apply_filters( 'sensei_the_no_permissions_message', Sensei()->permissions_message['message'], $post_id ) );
}

/**
 * Output the sensei excerpt
 *
 * @since 1.9.0
 * @deprecated 3.2.0
 */
function sensei_the_excerpt( $post_id ) {

	_deprecated_function( __FUNCTION__, '3.2.0' );

	global $post;
	the_excerpt( $post );

}

/**
 * Get current url on the frontend
 *
 * @since 1.9.0
 *
 * @global WP $wp
 * @return string $current_page_url
 */
function sensei_get_current_page_url() {

	global $wp;
	$current_page_url = home_url( $wp->request );
	return $current_page_url;

}

/**
 * Outputs the content for the my courses page
 *
 * @since 1.9.0
 */
function sensei_the_my_courses_content() {

	// phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- Output escaped in called method.
	echo Sensei()->course->load_user_courses_content( wp_get_current_user() );

} // sensei_the_my_courses_content

/**
 * This is a wrapper function for Sensei_Templates::get_template
 * It helps simplify templates for designers by removing the class::function call.
 *
 * @param string $template_name the name of the template.
 *              If it is in a sub directory please suply the directory name as well e.g. globals/wrapper-end.php
 *
 * @since 1.9.0
 */
function sensei_load_template( $template_name ) {

	Sensei_Templates::get_template( $template_name );

}

/**
 * This is a wrapper function for Sensei_Templates::get_part
 * It helps simplify templates for designers by removing the class::function call.
 *
 * @param string $slug the first part to the template file name
 * @param string $name the name of the template.
 * @since 1.9.0
 */
function sensei_load_template_part( $slug, $name ) {

	Sensei_Templates::get_part( $slug, $name );

}

/**
 * Returns the the lesson excerpt.
 *
 * This function will not wrap the the excerpt with <p> tags.
 * For the p tags call Sensei_Lesson::lesson_excerpt( $lesson)
 *
 * This function will only work for the lesson post type. All other post types will
 * be ignored.
 *
 * @since 1.9.0
 * @access public
 * @param string $lesson_id
 */
function sensei_the_lesson_excerpt( $lesson_id = '' ) {

	if ( empty( $lesson_id ) ) {

		$lesson_id = get_the_ID();

	}

	if ( 'lesson' != get_post_type( $lesson_id ) ) {
		return;
	}

	echo wp_kses_post( Sensei_Lesson::lesson_excerpt( get_post( $lesson_id ), false ) );

}

/**
 * The the course result lessons template
 *
 * @since 1.9.0
 */
function sensei_the_course_results_lessons() {
	// load backwards compatible template name if it exists in the users theme
	$located_template = locate_template( Sensei()->template_url . 'course-results/course-lessons.php' );
	if ( $located_template ) {

		Sensei_Templates::get_template( 'course-results/course-lessons.php' );
		return;

	}

	Sensei_Templates::get_template( 'course-results/lessons.php' );
}

/**
 * Echo the number of columns (also number of items per row) on the
 * the course archive.
 *
 * @uses Sensei_Course::get_loop_number_of_columns
 * @since 1.9.0
 */
function sensei_courses_per_row() {

	echo esc_html( Sensei_Course::get_loop_number_of_columns() );

}

/**
 * Wrapper function for Sensei_Templates::get_template( $template_name, $args, $path )
 *
 * @since 1.9.0
 * @param $template_name
 * @param $args
 * @param $path
 */
function sensei_get_template( $template_name, $args, $path ) {

	Sensei_Templates::get_template( $template_name, $args, $path );

}

/**
 * Returns the lesson status class
 *
 * must be used in the loop.
 *
 * @since 1.9.0
 *
 * @return string $status_class
 */
function get_the_lesson_status_class() {

	$lesson_id = get_the_ID();
	if ( ! $lesson_id ) {
		return '';
	}

	$status_class     = '';
	$lesson_completed = Sensei_Utils::user_completed_lesson( $lesson_id, get_current_user_id() );

	if ( $lesson_completed ) {
		$status_class = 'completed';
	}

	return $status_class;

}
/**
 * Outputs the lesson status class
 *
 * must be used in the lesson loop
 *
 * @since 1.9.0
 */
function sensei_the_lesson_status_class() {

	echo esc_html( get_the_lesson_status_class() );
}

/**
 * Get the module description.
 * This is to be used within the Sensei module loop.
 *
 * @return string Module description.
 */
function sensei_get_the_module_description() {
	global $sensei_modules_loop;
	$module_description = $sensei_modules_loop['current_module']->description;
	/**
	 * Filter the module description.
	 *
	 * This fires within the sensei_get_the_module_description function.
	 *
	 * @hook sensei_the_module_description
	 *
	 * @param {string} $module_description Module Description.
	 * @return {string} Filtered module description.
	 */
	return apply_filters( 'sensei_the_module_description', $module_description );
}

/**
 * Print out the current module Description
 */
function sensei_the_module_description() {
	echo esc_html( sensei_get_the_module_description() );
}