LLMS_Template_Loader
Template loader class.
Contents
Source Source
File: includes/class.llms.template.loader.php
class LLMS_Template_Loader { /** * Constructor. * * @since 1.0.0 * @since 3.20.0 Unknown. * @since 3.41.1 Predispose posts content restriction in REST requests. * @since 5.8.0 Handle block templates loading. * @since 6.2.0 Added 'llms_template_loader_priority' filter. * @since 6.4.0 Reverted back the priority of the `$this->template_loader()` callback * (`template_include` hook's callback) from 100 to 10. */ public function __construct() { // Template loading for FSE themes. add_action( 'template_redirect', array( $this, 'hook_block_template_loader' ) ); /** * Filters the template loading priority. * * Callback for the WP core filter `template_include`. * * @since 6.2.0 * * @param int $priority The filter callback priority. */ $template_loader_cb_priority = apply_filters( 'llms_template_loader_priority', 10 ); /** * Do template loading. * * The default priority is 10, so to allow theme builders, like Divi and Elementor (Pro), * to override our templates (except single content restricted). * see https://github.com/gocodebox/lifterlms/issues/2111 */ add_filter( 'template_include', array( $this, 'template_loader' ), $template_loader_cb_priority ); add_action( 'rest_api_init', array( $this, 'maybe_prepare_post_content_restriction' ) ); // Restriction actions for each kind of restriction. $reasons = apply_filters( 'llms_restriction_reasons', array( 'course_prerequisite', 'course_track_prerequisite', 'course_time_period', 'enrollment_lesson', 'lesson_drip', 'lesson_prerequisite', 'membership', 'sitewide_membership', 'quiz', ) ); foreach ( $reasons as $reason ) { add_action( 'llms_content_restricted_by_' . $reason, array( $this, 'restricted_by_' . $reason ), 10, 1 ); } add_action( 'wp', array( $this, 'maybe_redirect_to_sales_page' ) ); } /** * Add a notice and/or redirect during restriction actions. * * @since 3.0.0 * @since 7.4.0 Added `nocache_headers()` to prevent caching of redirects. * * @param string $msg Notice message to display. * @param string $redirect Optional. Url to redirect to after setting a notice. Default empty string. * @param string $msg_type Optional. Type of message to display [notice|success|error|debug]. Default 'notice'. * @return void */ private function handle_restriction( $msg = '', $redirect = '', $msg_type = 'notice' ) { if ( $msg ) { llms_add_notice( do_shortcode( $msg ), $msg_type ); } if ( $redirect ) { nocache_headers(); wp_redirect( $redirect ); exit; } } /** * Handle sales page redirects for courses & memberships * * @since 3.20.0 * @since 3.37.2 Flag to print notices, if there are, when landing on the redirected sales page. * * @return void */ public function maybe_redirect_to_sales_page() { // Only proceed for courses and memberships. if ( ! in_array( get_post_type(), array( 'course', 'llms_membership' ), true ) ) { return; } $page_restricted = llms_page_restricted( get_the_id() ); // Only proceed if the page isn't restricted. if ( ! $page_restricted['is_restricted'] ) { return; } /** @var LLMS_Trait_Sales_Page $post */ $post = llms_get_post( get_the_ID() ); if ( ! $post->has_sales_page_redirect() ) { return; } llms_redirect_and_exit( llms_notice_count() ? add_query_arg( array( 'llms_print_notices' => 1, ), $post->get_sales_page_url() ) : $post->get_sales_page_url(), array( 'safe' => false, ) ); } /** * Handle redirects and messages when a user attempts to access an item * restricted by a course track prerequisite. * * Redirect to parent course and display message. * If course do nothing. * * @since 3.7.3 * * @param array $info Array of restriction info from `llms_page_restricted()`. * @return void */ public function restricted_by_course_track_prerequisite( $info ) { if ( 'course' === get_post_type( $info['content_id'] ) ) { return; } $msg = llms_get_restriction_message( $info ); $course = llms_get_post_parent_course( $info['content_id'] ); $redirect = get_permalink( $course->get( 'id' ) ); $this->handle_restriction( apply_filters( 'llms_restricted_by_course_track_prerequisite_message', $msg, $info ), apply_filters( 'llms_restricted_by_course_track_prerequisite_redirect', $redirect, $info ), 'error' ); } /** * Handle redirects and messages when a user attempts to access an item * restricted by a course prerequisite. * * Redirect to parent course and display message. * If course do nothing. * * @since 3.7.3 * * @param array $info Array of restriction info from `llms_page_restricted()`. * @return void */ public function restricted_by_course_prerequisite( $info ) { if ( 'course' === get_post_type( $info['content_id'] ) ) { return; } $msg = llms_get_restriction_message( $info ); $course = llms_get_post_parent_course( $info['content_id'] ); $redirect = get_permalink( $course->get( 'id' ) ); $this->handle_restriction( apply_filters( 'llms_restricted_by_course_prerequisite_message', $msg, $info ), apply_filters( 'llms_restricted_by_course_prerequisite_redirect', $redirect, $info ), 'error' ); } /** * Handle redirects and messages when a course or associated quiz or lesson has time period * date restrictions placed upon it. * * Quizzes & Lessons redirect to the parent course. * Courses display a notice until the course opens and an error once the course closes. * * @since 3.0.0 * * @param array $info Array of restriction info from `llms_page_restricted()`. * @return void */ public function restricted_by_course_time_period( $info ) { $post_type = get_post_type( $info['content_id'] ); // If this restriction occurs when attempting to view a lesson, // redirect the user to the course, course restriction will handle display of the // message once we get there. // This prevents duplicate messages from being displayed. if ( 'lesson' === $post_type || 'llms_quiz' === $post_type ) { $msg = ''; $redirect = get_permalink( $info['restriction_id'] ); } if ( ! $msg && ! $redirect ) { return; } // Handle the restriction action & allow developers to filter the results. $this->handle_restriction( apply_filters( 'llms_restricted_by_course_time_period_message', $msg, $info ), apply_filters( 'llms_restricted_by_course_time_period_redirect', $redirect, $info ), 'notice' ); } /** * Handle redirects and messages when a user attempts to access a lesson * for a course they're not enrolled in. * * Redirect to parent course and display message. * * @since 3.0.0 * @since 3.2.4 Moved message generation to `llms_get_restriction_message()` * * @param array $info Array of restriction info from `llms_page_restricted()`. * @return void */ public function restricted_by_enrollment_lesson( $info ) { $msg = llms_get_restriction_message( $info ); $redirect = get_permalink( $info['restriction_id'] ); $this->handle_restriction( apply_filters( 'llms_restricted_by_enrollment_lesson_message', $msg, $info ), apply_filters( 'llms_restricted_by_enrollment_lesson_redirect', $redirect, $info ), 'error' ); } /** * Handle redirects and messages when a user attempts to access a lesson * for that is restricted by lesson drip settings. * * Redirect to parent course and display message. * * @since 3.0.0 * @since 3.2.4 Moved message generation to `llms_get_restriction_message()` * @since 5.7.0 Replaced the call to the deprecated `LLMS_Lesson::get_parent_course()` method with `LLMS_Lesson::get( 'parent_course' )`. * * @param array $info Array of restriction info from `llms_page_restricted()`. * @return void */ public function restricted_by_lesson_drip( $info ) { $lesson = new LLMS_Lesson( $info['restriction_id'] ); $msg = llms_get_restriction_message( $info ); $redirect = get_permalink( $lesson->get( 'parent_course' ) ); $this->handle_restriction( apply_filters( 'llms_restricted_by_lesson_drip_message', $msg, $info ), apply_filters( 'llms_restricted_by_lesson_drip_redirect', $redirect, $info ), 'error' ); } /** * Handle redirects and messages when a user attempts to access a lesson * for that is restricted by prerequisite lesson. * * Redirect to parent course and display message. * * @since 3.0.0 * @since 3.2.4 Moved message generation to `llms_get_restriction_message()` * * @param array $info Array of restriction info from `llms_page_restricted()`. * @return void */ public function restricted_by_lesson_prerequisite( $info ) { $msg = llms_get_restriction_message( $info ); $redirect = get_permalink( $info['restriction_id'] ); $this->handle_restriction( apply_filters( 'llms_restricted_by_lesson_prerequisite_message', $msg, $info ), apply_filters( 'llms_restricted_by_lesson_prerequisite_redirect', $redirect, $info ), 'error' ); } /** * Handle content restricted to a membership. * * Parses and obeys Membership "Restriction Behavior" settings. * * @since 3.0.0 * @since 3.37.10 Added Flag to print notices when landing on the redirected page. * * @param array $info Array of restriction info from `llms_page_restricted()`. * @return void */ public function restricted_by_membership( $info ) { $membership_id = $info['restriction_id']; // Do nothing if we don't have a membership id. if ( ! empty( $membership_id ) && is_numeric( $membership_id ) ) { // Instantiate the membership. $membership = new LLMS_Membership( $membership_id ); $msg = ''; $redirect = ''; if ( 'yes' === $membership->get( 'restriction_add_notice' ) ) { $msg = $membership->get( 'restriction_notice' ); } // Get the redirect based on the redirect type (if set). switch ( $membership->get( 'restriction_redirect_type' ) ) { case 'custom': $redirect = $membership->get( 'redirect_custom_url' ); break; case 'membership': $redirect = get_permalink( $membership->get( 'id' ) ); break; case 'page': $redirect = get_permalink( $membership->get( 'redirect_page_id' ) ); // Make sure to print notices in wp pages. $redirect = empty( $msg ) ? $redirect : add_query_arg( array( 'llms_print_notices' => 1, ), $redirect ); break; } // Handle the restriction action & allow developers to filter the results. $this->handle_restriction( apply_filters( 'llms_restricted_by_membership_message', $msg, $info ), apply_filters( 'llms_restricted_by_membership_redirect', $redirect, $info ) ); } } /** * Handle attempts to access quizzes. * * @since 3.1.6 * @since 3.16.1 Unknown. * * @param array $info Array of restriction info from `llms_page_restricted()`. * @return void */ public function restricted_by_quiz( $info ) { $msg = ''; $redirect = ''; if ( get_current_user_id() ) { $msg = __( 'You must be enrolled in the course to access this quiz.', 'lifterlms' ); $quiz = llms_get_post( $info['restriction_id'] ); if ( $quiz ) { $course = $quiz->get_course(); if ( $course ) { $redirect = get_permalink( $course->get( 'id' ) ); } } } else { $msg = __( 'You must be logged in to take quizzes.', 'lifterlms' ); $redirect = llms_person_my_courses_url(); } $this->handle_restriction( apply_filters( 'llms_restricted_by_membership_message', $msg, $info ), apply_filters( 'llms_restricted_by_membership_redirect', $redirect, $info ), 'error' ); } /** * Handle content restricted to a membership * * Parses and obeys Membership "Restriction Behavior" settings. * * @since 3.0.0 * * @param array $info Array of restriction info from `llms_page_restricted()`. * @return void */ public function restricted_by_sitewide_membership( $info ) { $this->restricted_by_membership( $info ); } /** * Hooks the callback to load FSE block templates. * * @since 5.8.0 * * @return void */ public function hook_block_template_loader() { add_filter( 'pre_get_block_templates', array( $this, 'block_template_loader' ), 99, 3 ); } /** * Filter blocks templates. * * @since 5.8.0 * @since 6.0.0 Remove LifterLMS 6.0 version check about the certificate template. * Use `llms_is_block_theme()` in favor of `wp_is_block_theme()`. * * @param WP_Block_Template[] $result Array of found block templates. * @param array $query { * Optional. Arguments to retrieve templates. * * @type array $slug__in List of slugs to include. * @type int $wp_id Post ID of customized template. * } * @param array $template_type wp_template or wp_template_part. * @return array Templates. */ public function block_template_loader( $result, $query, $template_type ) { // Bail if it's not a block theme, or is being retrieved a non wp_template file. if ( ! llms_is_block_theme() || 'wp_template' !== $template_type ) { return $result; } $template_name = $this->get_maybe_forced_template(); /** * Since LifterLMS 6.0.0 certificates have their own PHP template that do no depend on the theme. * This means that we can use the PHP template loaded in the method `LLMS_Template_Loader::template_loader()` below. */ $template_name = is_singular( array( 'llms_certificate', 'llms_my_certificate' ) ) ? '' : $template_name; /** * Filters the block template to be loded forced. * * @since 5.8.0 * * @param string $template_slug The template slug to be force loaded. * @param string $template The name of template to be force loaded. */ $template_slug = apply_filters( 'llms_forced_block_template_slug', $template_name ? LLMS_Block_Templates::LLMS_BLOCK_TEMPLATES_PREFIX . $template_name : '', $template_name ); if ( empty( $template_slug ) ) { return $result; } // Prevent template_loader to load a php template. add_filter( 'llms_force_php_template_loading', '__return_false' ); return llms()->block_templates()->add_llms_block_templates( array(), array( 'slug__in' => array( $template_slug ) ) ); } /** * Check if content should be restricted and include overrides where appropriate. * * Triggers actions based on content restrictions. * * @since 1.0.0 * @since 3.16.11 Unknown. * @since 3.37.2 Make sure to print notices on sales page redirect. * @since 4.10.1 Refactor to reduce code duplication and replace usage of `llms_shop` with `courses` for catalog check. * @since 5.8.0 Refactor: moved the template guessing in a specific method. * @since 6.4.0 Defer single content restricted template loading. * * @param string $template The template to load. * @return string */ public function template_loader( $template ) { $page_restricted = llms_page_restricted( get_the_ID() ); $this->maybe_print_notices_on_sales_page_redirect(); if ( $page_restricted['is_restricted'] ) { /** * Generic action triggered when content is restricted. * * @since Unknown * * @see llms_content_restricted_by_{$page_restricted['reason']} A specific hook triggered by a specific restriction reason. * * @param array $page_restricted Restriction information from `llms_page_restricted()`. */ do_action( 'lifterlms_content_restricted', $page_restricted ); /** * Action triggered when content is restricted for the specified reason. * * The dynamic portion of this hook, `{$page_restricted['reason']}` refers to the restriction reason * code generated by `llms_page_restricted()`. * * @since Unknown * * @see llms_content_restricted A generic hook triggered at the same time. * * @param array $page_restricted Restriction information from `llms_page_restricted()`. */ do_action( "llms_content_restricted_by_{$page_restricted['reason']}", $page_restricted ); if ( is_home() && 'sitewide_membership' === $page_restricted['reason'] ) { // Prints notices on the blog page when there's not redirects setup. add_action( 'loop_start', 'llms_print_notices', 5 ); } } $forced_template = $this->maybe_force_php_template( $template ); /** * When restricting single content use a lower priority so to always override * theme builders like Divi and Elementor (Pro). * see https://github.com/gocodebox/lifterlms/issues/2063. */ if ( llms_template_file_path( 'single-no-access.php' ) === $forced_template ) { /** * Filters the template loading priority for single restricted content. * * @since 6.4.0 * * @param int $priority The filter callback priority. */ $template_loader_restricted_cb_priority = apply_filters( 'llms_template_loader_restricted_priority', 100 ); add_filter( 'template_include', array( $this, 'maybe_force_php_template' ), $template_loader_restricted_cb_priority ); } else { $template = $forced_template; } return $template; } /** * Force the PHP template to be loaded. * * @since 6.4.0 * * @param string $template The original template to load. * @return string */ public function maybe_force_php_template( $template ) { /** * Filters whether or not forcing a LifterLMS php template to be loaded. * * @since 5.8.0 * * @param bool $force Whether or not forcing a LifterLMS PHP template to be loaded. */ $forced_template = apply_filters( 'llms_force_php_template_loading', true ) ? $this->get_maybe_forced_template() : false; return $forced_template ? llms_template_file_path( "{$forced_template}.php" ) : $template; } /** * Retrieve the hierarchical template to be loaded. * * @since 5.8.0 * * @return null|string */ private function get_maybe_forced_template() { $page_restricted = llms_page_restricted( get_the_ID() ); $template = null; if ( $page_restricted['is_restricted'] ) { // Blog should bypass checks, except when sitewide restrictions are enabled. if ( ( is_home() && 'sitewide_membership' === $page_restricted['reason'] ) || // Course and membership content restrictions are handled by conditional elements in the editor. ( in_array( get_post_type(), array( 'course', 'llms_membership' ), true ) ) ) { return; } // Content is restricted. $template = 'single-no-access'; } elseif ( is_post_type_archive( 'course' ) || is_page( llms_get_page_id( 'courses' ) ) ) { $template = 'archive-course'; } elseif ( is_post_type_archive( 'llms_membership' ) || is_page( llms_get_page_id( 'memberships' ) ) ) { $template = 'archive-llms_membership'; } elseif ( is_tax( array( 'course_cat', 'course_tag', 'course_difficulty', 'course_track', 'membership_tag', 'membership_cat' ) ) ) { global $wp_query; $obj = $wp_query->get_queried_object(); $template = 'taxonomy-' . $obj->taxonomy; } elseif ( is_singular( array( 'llms_certificate', 'llms_my_certificate' ) ) ) { $template = 'single-certificate'; } /** * Filters the template to be loded forced. * * @since 5.8.0 * * @param string $template The template slug to be loaded forced. */ return apply_filters( 'llms_forced_template', $template ); } /** * Maybe print notices after redirection. * * @since 3.37.2 * * @return void */ private function maybe_print_notices_on_sales_page_redirect() { if ( llms_filter_input( INPUT_GET, 'llms_print_notices' ) ) { // Prints notices on the page at loop start. add_action( 'loop_start', 'llms_print_notices', 5 ); } } /** * Maybe restrict the post content in REST requets * * @since 3.41.1 * * @return void */ public function maybe_prepare_post_content_restriction() { // Fired on `setup_postdata()` see `WP_REST_Posts_Controller::prepare_item_for_response()`. add_action( 'the_post', array( $this, 'maybe_restrict_post_content' ), 9999, 2 ); } /** * Maybe restrict the post content in the REST loop * * @since 3.41.1 * @since 4.0.0 Don't pass by reference because it's unnecessary. * @since 4.10.1 Fixed incorrect position of `true` in `in_array()`. * * @param WP_Post $post Post Object. * @param WP_Query $query Query object. * @return void */ public function maybe_restrict_post_content( $post, $query ) { /** * Filters the post types that must be skipped. * * The LifterLMS post types content restriction should be handled by the LifterLMS rest-api. * * @since 3.41.1 * * @param string[] $post_types The array of post types to skip. */ $skip = apply_filters( 'llms_in_rest_restrict_content_skip_post_types', array( 'course', 'lesson', 'llms_quiz', 'llms_membership', 'llms_question', 'llms_certificate', 'llms_my_certificate', ) ); if ( in_array( get_post_type( $post ), $skip, true ) ) { return; } // Needed by `llms_page_restricted()` to work as expected. $is_singular = $query->is_singular; $query->is_singular = true; $page_restricted = llms_page_restricted( get_the_ID() ); if ( $page_restricted['is_restricted'] ) { $msg = __( 'This content is restricted', 'lifterlms' ); $reason = $page_restricted['reason']; if ( in_array( $reason, array( 'membership', 'sitewide_membership' ), true ) ) { $membership_id = $page_restricted['restriction_id']; if ( ! empty( $membership_id ) && is_numeric( $membership_id ) ) { $membership = new LLMS_Membership( $membership_id ); if ( 'yes' === $membership->get( 'restriction_add_notice' ) ) { $msg = $membership->get( 'restriction_notice' ); } } } /** * Filters the restriction message. * * The dynamic portion of the hook name, `$reason`, refers to the restriction reason. * * @since 3.41.1 * * @param string $message Restriction message. * @param array $restriction Array of restriction info from `llms_page_restricted()`. */ $msg = apply_filters( "llms_in_rest_restricted_by_{$reason}_message", $msg, $page_restricted ); $post->post_content = $msg; $post->post_excerpt = $msg; } $query->is_singular = $is_singular; }
Expand full source code Collapse full source code View on GitHub
Methods Methods
- __construct — Constructor.
- block_template_loader — Filter blocks templates.
- get_maybe_forced_template — Retrieve the hierarchical template to be loaded.
- handle_restriction — Add a notice and/or redirect during restriction actions
- hook_block_template_loader — Hooks the callback to load FSE block templates.
- maybe_force_php_template — Force the PHP template to be loaded.
- maybe_prepare_post_content_restriction — Maybe restrict the post content in REST requets
- maybe_print_notices_on_sales_page_redirect — Maybe print notices after redirection.
- maybe_redirect_to_sales_page — Handle sales page redirects for courses & memberships
- maybe_restrict_post_content — Maybe restrict the post content in the REST loop
- restricted_by_course_prerequisite — Handle redirects and messages when a user attempts to access an item restricted by a course prerequisite.
- restricted_by_course_time_period — Handle redirects and messages when a course or associated quiz or lesson has time period date restrictions placed upon it.
- restricted_by_course_track_prerequisite — Handle redirects and messages when a user attempts to access an item restricted by a course track prerequisite.
- restricted_by_enrollment_lesson — Handle redirects and messages when a user attempts to access a lesson for a course they're not enrolled in.
- restricted_by_lesson_drip — Handle redirects and messages when a user attempts to access a lesson for that is restricted by lesson drip settings.
- restricted_by_lesson_prerequisite — Handle redirects and messages when a user attempts to access a lesson for that is restricted by prerequisite lesson.
- restricted_by_membership — Handle content restricted to a membership.
- restricted_by_quiz — Handle attempts to access quizzes.
- restricted_by_sitewide_membership — Handle content restricted to a membership
- template_loader — Check if content should be restricted and include overrides where appropriate.
Changelog Changelog
Version | Description |
---|---|
4.0.0 | Don't pass objects by reference because it's unnecessary. |
3.41.1 | Fixed content membership restricted post's content not restricted in REST requests. |
3.37.2 | Notices are printed on sales pages too. |
3.37.10 | Notices are printed on pages configured as a membership restriction redirect page. |
3.20.0 | Unknown. |
1.0.0 | Introduced. |