Lesson.php 7.79 KB
<?php
/**
 * This class provides the easy way to operate a lesson.
 *
 * @since 4.6.0
 *
 * @package LearnDash\Core
 */

/** NOTICE: This code is currently under development and may not be stable.
 *  Its functionality, behavior, and interfaces may change at any time without notice.
 *  Please refrain from using it in production or other critical systems.
 *  By using this code, you assume all risks and liabilities associated with its use.
 *  Thank you for your understanding and cooperation.
 **/

namespace LearnDash\Core\Models;

use LDLMS_Post_Types;
use LearnDash\Core\Models\Traits\Has_Course;
use LearnDash\Core\Models\Traits\Has_Quizzes;
use LearnDash\Core\Models\Traits\Supports_Timer;
use WP_User;

/**
 * Lesson model class.
 *
 * @since 4.6.0
 */
class Lesson extends Post implements Interfaces\Course_Step {
	use Has_Course {
		get_course as get_course_from_trait;
	}
	use Has_Quizzes {
		get_quizzes as get_quizzes_from_trait;
		get_quizzes_number as get_quizzes_number_from_trait;
	}
	use Supports_Timer {
		get_time_limit_in_seconds as get_time_limit_in_seconds_from_trait;
		get_time_limit_formatted as get_time_limit_formatted_from_trait;
	}
	use Traits\Has_Materials;
	use Traits\Supports_Video_Progression;

	/**
	 * Returns allowed post types.
	 *
	 * @since 4.6.0
	 *
	 * @return string[]
	 */
	public static function get_allowed_post_types(): array {
		return array(
			LDLMS_Post_Types::get_post_type_slug( LDLMS_Post_Types::LESSON ),
		);
	}

	/**
	 * Returns a course of the lesson or null if the lesson is not associated with a course.
	 *
	 * @since 4.6.0
	 *
	 * @return Course|null
	 */
	public function get_course(): ?Course {
		/**
		 * Filters a lesson course.
		 *
		 * @since 4.6.0
		 *
		 * @param Course|null $course Course model.
		 * @param Lesson      $lesson Lesson model.
		 *
		 * @return Course|null Lesson course model.
		 *
		 * @ignore
		 */
		return apply_filters(
			'learndash_model_lesson_course',
			$this->get_course_from_trait(),
			$this
		);
	}

	/**
	 * Returns related topics models.
	 *
	 * @since 4.6.0
	 *
	 * @param int $limit  Optional. Limit. Default is 0 which will be changed with LD settings.
	 * @param int $offset Optional. Offset. Default 0.
	 *
	 * @return Topic[]
	 */
	public function get_topics( int $limit = 0, int $offset = 0 ): array {
		/**
		 * Filters lesson topics.
		 *
		 * @since 4.6.0
		 *
		 * @param Topic[] $topics Topics.
		 * @param Lesson  $lesson Lesson model.
		 *
		 * @ignore
		 */
		return apply_filters(
			'learndash_model_lesson_topics',
			$this->memoize(
				function() use ( $limit, $offset ): array {
					$course = $this->get_course();

					if ( ! $course ) {
						return [];
					}

					// TODO: This must be refactored to the direct call to the instance with disabling steps objects loading.
					$topic_ids = learndash_course_get_children_of_step(
						$course->get_id(),
						$this->get_id(),
						LDLMS_Post_Types::get_post_type_slug( LDLMS_Post_Types::TOPIC )
					);

					// TODO: Course model has a similar logic, maybe refactor.

					// TODO: Here we need to remove those lessons that a user can't read. We need to refactor it, it can be an additional call to filter by statuses, but a loop is too inefficient.
					// It was done in the legacy code with the following method:
					// protected function can_user_read_step( $step_post_id = 0 ) {
					//
					// if ( ! empty( $lesson_ids ) ) {
					// foreach ( $lesson_ids as $lesson_id => $lesson_post ) {
					// if ( ! $this->can_user_read_step( $lesson_post->ID ) ) {
					// unset( $lessons[ $lesson_id ] );
					// }
					// }
					// }.

					$topic_ids = array_slice( $topic_ids, $offset, $limit > 0 ? $limit : null );

					return Topic::find_many( $topic_ids );
				}
			),
			$this
		);
	}

	/**
	 * Returns the total number of related topics.
	 *
	 * @since 4.6.0
	 *
	 * @return int
	 */
	public function get_topics_number(): int {
		/**
		 * Filters lesson topics number.
		 *
		 * @since 4.6.0
		 *
		 * @param int    $number  Number of lessons.
		 * @param Lesson $lesson  Lesson model.
		 *
		 * @ignore
		 */
		return apply_filters(
			'learndash_model_lesson_topics_number',
			$this->memoize(
				function() : int {
					$course = $this->get_course();

					if ( ! $course ) {
						return 0;
					}

					return count(
						// TODO: Inefficient, we need to refactor it to the direct call to the instance with disabling steps objects loading.
						learndash_course_get_children_of_step(
							$course->get_id(),
							$this->get_id(),
							LDLMS_Post_Types::get_post_type_slug( LDLMS_Post_Types::TOPIC )
						)
					);
				}
			),
			$this
		);
	}

	/**
	 * Returns related quizzes models.
	 *
	 * TODO: Move it to the respective trait.
	 *
	 * @since 4.6.0
	 *
	 * @param int $limit  Optional. Limit. Default is 0 which will be changed with LD settings.
	 * @param int $offset Optional. Offset. Default 0.
	 *
	 * @return Quiz[]
	 */
	public function get_quizzes( int $limit = 0, int $offset = 0 ): array {
		/**
		 * Filters lesson quizzes.
		 *
		 * @since 4.6.0
		 *
		 * @param Quiz[] $quizzes Quizzes.
		 * @param Lesson $lesson  Lesson model.
		 *
		 * @ignore
		 */
		return apply_filters(
			'learndash_model_lesson_quizzes',
			$this->get_quizzes_from_trait( $limit, $offset ),
			$this
		);
	}

	/**
	 * Returns the total number of related quizzes.
	 *
	 * @since 4.6.0
	 *
	 * @return int
	 */
	public function get_quizzes_number(): int {
		/**
		 * Filters lesson quizzes number.
		 *
		 * @since 4.6.0
		 *
		 * @param int    $number  Number of quizzes.
		 * @param Lesson $lesson  Lesson model.
		 *
		 * @ignore
		 */
		return apply_filters(
			'learndash_model_lesson_quizzes_number',
			$this->get_quizzes_number_from_trait(),
			$this
		);
	}

	/**
	 * Gets the lesson time limit in seconds.
	 *
	 * @since 4.6.0
	 *
	 * @return int The number of seconds.
	 */
	public function get_time_limit_in_seconds(): int {
		/**
		 * Filters the lesson time limit in seconds.
		 *
		 * @since 4.6.0
		 *
		 * @param int    $time_limit_in_seconds The lesson time limit in seconds.
		 * @param Lesson $lesson                The lesson model.
		 *
		 * @ignore
		 */
		return apply_filters(
			'learndash_model_lesson_time_limit',
			$this->get_time_limit_in_seconds_from_trait(),
			$this
		);
	}

	/**
	 * Gets the lesson time limit as a H:M:S string.
	 *
	 * @since 4.6.0
	 *
	 * @return string
	 */
	public function get_time_limit_formatted(): string {
		/**
		 * Filters the lesson time limit.
		 *
		 * @since 4.6.0
		 *
		 * @param string $time_limit            The lesson time limit as a H:M:S string.
		 * @param int    $time_limit_in_seconds The lesson time limit in seconds.
		 * @param Lesson $lesson                The lesson model.
		 *
		 * @ignore
		 */
		return apply_filters(
			'learndash_model_lesson_time_limit_formatted',
			$this->get_time_limit_formatted_from_trait(),
			$this->get_time_limit_in_seconds(),
			$this
		);
	}

	/**
	 * Returns whether lesson content should be visible.
	 *
	 * TODO: Add a hook, tests. Maybe it should be in the Product.
	 *
	 * @since 4.6.0
	 *
	 * @param WP_User $user User.
	 *
	 * @return bool
	 */
	public function is_content_visible( WP_User $user ): bool {
		// TODO: It was moved to the product.
		return true;
	}

	/**
	 * Returns whether lesson can be completed.
	 *
	 * @since 4.6.0
	 *
	 * @return bool
	 */
	public function can_be_completed(): bool {
		// TODO: Implement this.
		return false;
	}

	/**
	 * Returns whether lesson is locked (for example when the previous lesson is not completed).
	 *
	 * @since 4.6.0
	 *
	 * @return bool
	 */
	public function is_locked(): bool {
		// TODO: Implement this.
		return true;
	}

	/**
	 * Returns the progress percentage for a user.
	 *
	 * @since 4.6.0
	 *
	 * @param WP_User $user User.
	 *
	 * @return int
	 */
	public function get_progress_percentage( WP_User $user ): int {
		// TODO: Memoize, tests, hook.

		// TODO: Implement it.
		return 0;
	}
}