Current File : /home/getxxhzo/xpertbee.com/wp-content/plugins/tutor/classes/Course.php
<?php
/**
 * Manage Course Related Logic
 *
 * @package Tutor
 * @author Themeum <support@themeum.com>
 * @link https://themeum.com
 * @since 1.0.0
 */

namespace TUTOR;

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

use TUTOR\Input;
use Tutor\Models\CourseModel;

/**
 * Course Class
 *
 * @since 1.0.0
 */
class Course extends Tutor_Base {

	/**
	 * Additional course meta info
	 *
	 * @var array
	 */
	private $additional_meta = array(
		'_tutor_enable_qa',
		'_tutor_is_public_course',
	);

	/**
	 * Constructor
	 *
	 * @since 1.0.0
	 * @return void
	 */
	public function __construct() {
		parent::__construct();

		add_action( 'add_meta_boxes', array( $this, 'register_meta_box' ) );
		add_action( 'save_post_' . $this->course_post_type, array( $this, 'save_course_meta' ), 10, 2 );
		add_action( 'wp_ajax_tutor_save_topic', array( $this, 'tutor_save_topic' ) );

		/**
		 * Add Column
		 */
		add_filter( "manage_{$this->course_post_type}_posts_columns", array( $this, 'add_column' ), 10, 1 );
		add_action( "manage_{$this->course_post_type}_posts_custom_column", array( $this, 'custom_lesson_column' ), 10, 2 );

		add_action( 'wp_ajax_tutor_delete_topic', array( $this, 'tutor_delete_topic' ) );

		/**
		 * Frontend Action
		 */
		add_action( 'template_redirect', array( $this, 'enroll_now' ) );
		add_action( 'init', array( $this, 'mark_course_complete' ) );

		/**
		 * Frontend Dashboard
		 */
		add_action( 'wp_ajax_tutor_delete_dashboard_course', array( $this, 'tutor_delete_dashboard_course' ) );

		/**
		 * Gutenberg author support
		 */
		add_filter( 'wp_insert_post_data', array( $this, 'tutor_add_gutenberg_author' ), '99', 2 );

		/**
		 * Frontend metabox supports for course builder
		 *
		 * @since  v.1.3.4
		 */
		add_action( 'tutor/dashboard_course_builder_form_field_after', array( $this, 'register_meta_box_in_frontend' ) );

		/**
		 * Do Stuff for the course save from frontend
		 */
		add_action( 'save_tutor_course', array( $this, 'attach_product_with_course' ), 10, 2 );

		/**
		 * Add course level to course settings
		 *
		 * @since v.1.4.1
		 */
		add_filter( 'tutor_course_settings_tabs', array( $this, 'add_course_level_to_settings' ) );

		/**
		 * Enable Disable Course Details Page Feature
		 *
		 * @since v.1.4.8
		 */
		$this->course_elements_enable_disable();

		/**
		 * Check if course starting, set meta if starting
		 *
		 * @since v.1.4.8
		 */
		add_action( 'tutor_lesson_load_before', array( $this, 'tutor_lesson_load_before' ) );

		/**
		 * Filter product in shop page
		 *
		 * @since v.1.4.9
		 */
		$this->filter_product_in_shop_page();

		/**
		 * Remove the course price if enrolled
		 *
		 * @since 1.5.8
		 */
		add_filter( 'tutor_course_price', array( $this, 'remove_price_if_enrolled' ) );

		/**
		 * Remove course complete button if course completion is strict mode
		 *
		 * @since v.1.6.1
		 */
		add_filter( 'tutor_course/single/complete_form', array( $this, 'tutor_lms_hide_course_complete_btn' ) );
		add_filter( 'get_gradebook_generate_form_html', array( $this, 'get_generate_greadbook' ) );

		/**
		 * Add social share content in header
		 *
		 * @since v.1.6.3
		 */
		add_action( 'wp_head', array( $this, 'social_share_content' ) );

		/**
		 * Delete course data after deleted course
		 *
		 * @since v.1.6.6
		 */
		add_action( 'deleted_post', array( new CourseModel(), 'delete_course_data' ) );

		/**
		 * Delete course data after deleted course
		 *
		 * @since v.1.8.2
		 */
		add_action( 'before_delete_post', array( $this, 'delete_associated_enrollment' ) );

		/**
		 * Show only own uploads in media library if user is instructor
		 *
		 * @since v1.8.9
		 */
		add_filter( 'posts_where', array( $this, 'restrict_media' ) );

		/**
		 * Restrict new enrol/purchase button if course member limit reached
		 *
		 * @since v1.9.0
		 */
		add_filter( 'tutor_course_restrict_new_entry', array( $this, 'restrict_new_student_entry' ) );

		/**
		 * Reset course progress on retake
		 *
		 * @since v1.9.5
		 */
		add_action( 'wp_ajax_tutor_reset_course_progress', array( $this, 'tutor_reset_course_progress' ) );

		/**
		 * Popup for review
		 *
		 * @since v1.9.7
		 */
		add_action( 'wp_footer', array( $this, 'popup_review_form' ) );
		add_action( 'wp_ajax_tutor_clear_review_popup_data', array( $this, 'clear_review_popup_data' ) );

		/**
		 * Do enroll after login if guest take enroll attempt
		 *
		 * @since 1.9.8
		 */
		add_action( 'tutor_do_enroll_after_login_if_attempt', array( $this, 'enroll_after_login_if_attempt' ), 10, 2 );

		add_action( 'wp_ajax_tutor_update_course_content_order', array( $this, 'tutor_update_course_content_order' ) );

		add_action( 'wp_ajax_tutor_get_wc_product', array( $this, 'tutor_get_wc_product' ) );

		add_action( 'wp_ajax_tutor_course_enrollment', array( $this, 'course_enrollment' ) );

		/**
		 * After trash a course redirect to course list page
		 *
		 * @since 2.1.7
		 */
		add_action( 'trashed_post', __CLASS__ . '::redirect_to_course_list_page' );

		add_filter( 'tutor_enroll_required_login_class', array( $this, 'add_enroll_required_login_class' ) );
		/**
		 * Remove wp trash button if instructor settings is disabled
		 *
		 * @since 2.7.3
		 */
		add_action( 'tutor_option_save_after', array( $this, 'disable_course_trash_instructor' ) );
	}

	/**
	 * Remove move to trash button on WordPress editor for instructor.
	 *
	 * @since 2.7.3
	 *
	 * @return void
	 */
	public function disable_course_trash_instructor() {
		$can_trash_post = tutor_utils()->get_option( 'instructor_can_delete_course' );
		$role           = get_role( tutor()->instructor_role );
		if ( ! $can_trash_post ) {
			$role->remove_cap( 'delete_tutor_courses' );
			$role->remove_cap( 'delete_tutor_course' );
		} else {
			$role->add_cap( 'delete_tutor_courses' );
			$role->add_cap( 'delete_tutor_course' );
		}
	}

	/**
	 * Add enroll require login class
	 *
	 * @since 2.6.0
	 *
	 * @param string $class_name css class name.
	 *
	 * @return string
	 */
	public function add_enroll_required_login_class( $class_name ) {
		$enabled_tutor_login = tutor_utils()->get_option( 'enable_tutor_native_login', null, true, true );
		if ( ! $enabled_tutor_login ) {
			return '';
		}

		return $class_name;
	}

	/**
	 * Get course associate WC product info by Ajax request
	 *
	 * @since 2.0.7
	 * @return void
	 */
	public function tutor_get_wc_product() {
		tutor_utils()->checking_nonce();
		$product_id = Input::post( 'product_id' );
		$product    = wc_get_product( $product_id );
		$course_id  = Input::post( 'course_id', 0, Input::TYPE_INT );

		$is_linked_with_course = tutor_utils()->product_belongs_with_course( $product_id );
		/**
		 * If selected product is already linked with
		 * a course & it is not the current course the
		 * return error
		 *
		 * @since v2.1.0
		 */
		if ( is_object( $is_linked_with_course ) && $is_linked_with_course->post_id != $course_id ) {
			wp_send_json_error(
				__( 'One product can not be added to multiple course!', 'tutor' )
			);
		}

		if ( $product ) {
			$data = array(
				'name'          => $product->get_name(),
				'regular_price' => $product->get_regular_price(),
				'sale_price'    => $product->get_sale_price(),
			);
			wp_send_json_success( $data );
		} else {
			wp_send_json_error( __( 'Product not found', 'tutor' ) );
		}
	}

	/**
	 * Update course content order
	 *
	 * @since 1.0.0
	 * @return void
	 */
	public function tutor_update_course_content_order() {
		tutor_utils()->checking_nonce();

		if ( Input::has( 'content_parent' ) ) {
			$content_parent = Input::post( 'content_parent', array(), Input::TYPE_ARRAY );
			$topic_id       = tutor_utils()->array_get( 'parent_topic_id', $content_parent );
			$content_id     = tutor_utils()->array_get( 'content_id', $content_parent );

			if ( ! tutor_utils()->can_user_manage( 'topic', $topic_id ) ) {
				wp_send_json_success( array( 'message' => __( 'Access Denied!', 'tutor' ) ) );
				exit;
			}

			// Update the parent topic id of the content.
			global $wpdb;
			$wpdb->update( $wpdb->posts, array( 'post_parent' => $topic_id ), array( 'ID' => $content_id ) );
		}

		// Save course content order.
		$this->save_course_content_order();

		wp_send_json_success();
	}

	/**
	 * Restrict new student entry
	 *
	 * @since 1.0.0
	 * @param mixed $content content.
	 * @return mixed
	 */
	public function restrict_new_student_entry( $content ) {

		if ( ! tutor_utils()->is_course_fully_booked() ) {
			// No restriction if not fully booked.
			return $content;
		}

		return '<div class="list-item-booking booking-full tutor-d-flex tutor-align-center"><div class="booking-progress tutor-d-flex"><span class="tutor-mr-8 tutor-color-warning tutor-icon-circle-info"></span></div><div class="tutor-fs-7 tutor-fw-medium">' .
		__( 'Fully Booked', 'tutor' )
		. '</div></div>';
	}

	/**
	 * Restrict media
	 *
	 * @since 1.0.0
	 * @param string $where where clause.
	 * @return string
	 */
	public function restrict_media( $where ) {
		$action = Input::post( 'action' );
		if ( 'query-attachments' === $action && tutor_utils()->is_instructor() ) {
			if ( ! tutor_utils()->has_user_role( array( 'administrator', 'editor' ) ) ) {
				$where .= ' AND post_author=' . get_current_user_id();
			}
		}

		return $where;
	}

	/**
	 * Registering metabox
	 *
	 * @since 1.0.0
	 * @return void
	 */
	public function register_meta_box() {
		$course_post_type = tutor()->course_post_type;

		tutor_meta_box_wrapper( 'tutor-course-topics', __( 'Course Builder', 'tutor' ), array( $this, 'course_meta_box' ), $course_post_type, 'advanced', 'default', 'tutor-admin-post-meta' );

		tutor_meta_box_wrapper( 'tutor-course-additional-data', __( 'Additional Data', 'tutor' ), array( $this, 'course_additional_data_meta_box' ), $course_post_type, 'advanced', 'default', 'tutor-admin-post-meta' );

		tutor_meta_box_wrapper( 'tutor-course-videos', __( 'Video', 'tutor' ), array( $this, 'video_metabox' ), $course_post_type, 'advanced', 'default', 'tutor-admin-post-meta' );
	}

	/**
	 * Course meta box (Topics)
	 *
	 * @since 1.0.0
	 * @param boolean $echo display or not.
	 * @return string
	 */
	public function course_meta_box( $echo = true ) {
		$file_path = tutor()->path . 'views/metabox/course-topics.php';

		if ( $echo ) {
			/**
			 * Use echo raise WPCS security issue
			 * Helper wp_kses_post break content.
			 */
			include $file_path;
		} else {
			ob_start();
			include $file_path;
			return ob_get_clean();
		}
	}

	/**
	 * Additional data meta box
	 *
	 * @since 1.0.0
	 * @param boolean $echo print data or return.
	 * @return string
	 */
	public function course_additional_data_meta_box( $echo = true ) {
		$file_path = tutor()->path . 'views/metabox/course-additional-data.php';

		if ( $echo ) {
			/**
			 * Use echo raise WPCS security issue
			 * Helper wp_kses_post break content.
			 */
			include $file_path;
		} else {
			ob_start();
			include $file_path;
			return ob_get_clean();
		}
	}

	/**
	 * Video meta box
	 *
	 * @since 1.0.0
	 * @param boolean $echo print data or return.
	 * @return string
	 */
	public function video_metabox( $echo = true ) {
		$file_path = tutor()->path . 'views/metabox/video-metabox.php';

		if ( $echo ) {
			/**
			 * Use echo raise WPCS security issue
			 * Helper wp_kses_post break content.
			 */
			include $file_path;
		} else {
			ob_start();
			include $file_path;
			return ob_get_clean();
		}
	}

	/**
	 * Register metabox in course builder tutor
	 *
	 * @since 1.3.4
	 * @return void
	 */
	public function register_meta_box_in_frontend() {
		global $post;

		do_action( 'tutor_course_builder_metabox_before', get_the_ID() );

		course_builder_section_wrap( $this->video_metabox( false ), __( 'Video', 'tutor' ) );
		do_action( 'tutor/frontend_course_edit/after/video', $post );

		course_builder_section_wrap( $this->course_meta_box( false ), __( 'Course Builder', 'tutor' ) );
		do_action( 'tutor/frontend_course_edit/after/course_builder', $post );

		course_builder_section_wrap( $this->course_additional_data_meta_box( false ), __( 'Additional Data', 'tutor' ) );
		do_action( 'tutor/frontend_course_edit/after/additional_data', $post );

		do_action( 'tutor_course_builder_metabox_after', get_the_ID() );
	}

	/**
	 * Save course content order
	 *
	 * @since 1.0.0
	 * @return void
	 */
	private function save_course_content_order() {
		global $wpdb;

		$new_order = Input::post( 'tutor_topics_lessons_sorting' );
		if ( ! empty( $new_order ) ) {
			$order = json_decode( $new_order, true );

			if ( is_array( $order ) && count( $order ) ) {
				$i = 0;
				foreach ( $order as $topic ) {
					$i++;
					$wpdb->update(
						$wpdb->posts,
						array( 'menu_order' => $i ),
						array( 'ID' => $topic['topic_id'] )
					);

					/**
					 * Removing All lesson with topic
					 */

					$wpdb->update(
						$wpdb->posts,
						array( 'post_parent' => 0 ),
						array( 'post_parent' => $topic['topic_id'] )
					);

					/**
					 * Lesson Attaching with topic ID
					 * Sorting lesson
					 */
					if ( isset( $topic['lesson_ids'] ) ) {
						$lesson_ids = $topic['lesson_ids'];
					} else {
						$lesson_ids = array();
					}
					if ( count( $lesson_ids ) ) {
						foreach ( $lesson_ids as $lesson_key => $lesson_id ) {
							$wpdb->update(
								$wpdb->posts,
								array(
									'post_parent' => $topic['topic_id'],
									'menu_order'  => $lesson_key,
								),
								array( 'ID' => $lesson_id )
							);
						}
					}
				}
			}
		}
	}

	/**
	 * Insert Topic and attached it with Course
	 *
	 * @since 1.0.0
	 *
	 * @param integer $post_ID post ID.
	 * @param object  $post post object.
	 *
	 * @return void
	 */
	public function save_course_meta( $post_ID, $post ) {
		global $wpdb;

		do_action( 'tutor_save_course', $post_ID, $post );

		/**
		 * Save course price type
		 */
		$price_type = Input::post( 'tutor_course_price_type' );
		if ( $price_type ) {
			update_post_meta( $post_ID, '_tutor_course_price_type', $price_type );
		}

		//phpcs:disable WordPress.Security.NonceVerification.Missing
		// Course Duration.
		if ( ! empty( $_POST['course_duration'] ) ) {
			$video = Input::post( 'course_duration', array(), Input::TYPE_ARRAY );
			update_post_meta( $post_ID, '_course_duration', $video );
		}

		if ( ! empty( $_POST['_tutor_course_level'] ) ) {
			$course_level = Input::post( '_tutor_course_level' );
			update_post_meta( $post_ID, '_tutor_course_level', $course_level );
		}

		$additional_data_edit = Input::post( '_tutor_course_additional_data_edit' );
		if ( $additional_data_edit ) {
			if ( ! empty( $_POST['course_benefits'] ) ) {
				$course_benefits = Input::post( 'course_benefits', '', Input::TYPE_KSES_POST );
				update_post_meta( $post_ID, '_tutor_course_benefits', $course_benefits );
			} else {
				if ( ! tutor_is_rest() ) {
					delete_post_meta( $post_ID, '_tutor_course_benefits' );
				}
			}

			if ( ! empty( $_POST['course_requirements'] ) ) {
				$requirements = Input::post( 'course_requirements', '', Input::TYPE_KSES_POST );
				update_post_meta( $post_ID, '_tutor_course_requirements', $requirements );
			} else {
				if ( ! tutor_is_rest() ) {
					delete_post_meta( $post_ID, '_tutor_course_requirements' );
				}
			}

			if ( ! empty( $_POST['course_target_audience'] ) ) {
				$target_audience = Input::post( 'course_target_audience', '', Input::TYPE_KSES_POST );
				update_post_meta( $post_ID, '_tutor_course_target_audience', $target_audience );
			} else {
				if ( ! tutor_is_rest() ) {
					delete_post_meta( $post_ID, '_tutor_course_target_audience' );
				}
			}

			if ( ! empty( $_POST['course_material_includes'] ) ) {
				$material_includes = Input::post( 'course_material_includes', '', Input::TYPE_KSES_POST );
				update_post_meta( $post_ID, '_tutor_course_material_includes', $material_includes );
			} else {
				if ( ! tutor_is_rest() ) {
					delete_post_meta( $post_ID, '_tutor_course_material_includes' );
				}
			}
			//phpcs:enable WordPress.Security.NonceVerification.Missing
		}

		/**
		 * Sorting Topics and lesson
		 */
		$this->save_course_content_order();

		// Additional data like course intro video.
		if ( $additional_data_edit ) {
			// Sanitize data through helper method.
			$video        = Input::sanitize_array(
				$_POST['video'] ?? array(), //phpcs:ignore
				array(
					'source_external_url' => 'esc_url',
					'source_embedded'     => 'wp_kses_post',
				),
				true
			);
			$video_source = tutor_utils()->array_get( 'source', $video );
			if ( -1 !== $video_source ) {
				update_post_meta( $post_ID, '_video', $video );
			} else {
				if ( ! tutor_is_rest() ) {
					delete_post_meta( $post_ID, '_video' );
				}
			}
		}

		/**
		 * Adding author to instructor automatically
		 */

		// Override post author id.
		$author_id = isset( $_POST['post_author_override'] ) ? $_POST['post_author_override'] : $post->post_author; //phpcs:ignore
		$attached  = (int) $wpdb->get_var(
			$wpdb->prepare(
				"SELECT COUNT(umeta_id) FROM {$wpdb->usermeta}
					WHERE user_id = %d
						AND meta_key = '_tutor_instructor_course_id'
						AND meta_value = %d ",
				$author_id,
				$post_ID
			)
		);

		if ( ! $attached ) {
			add_user_meta( $author_id, '_tutor_instructor_course_id', $post_ID );
		}

		/**
		 * Disable question and answer for this course
		 *
		 * @since 1.7.0
		 */
		if ( $additional_data_edit ) {
			foreach ( $this->additional_meta as $key ) {
				//phpcs:ignore WordPress.Security.NonceVerification.Missing
				update_post_meta( $post_ID, $key, ( isset( $_POST[ $key ] ) ? 'yes' : 'no' ) );
			}
		}

		do_action( 'tutor_save_course_after', $post_ID, $post );
	}

	/**
	 * Save course topic
	 *
	 * @since 1.0.0
	 * @return void
	 */
	public function tutor_save_topic() {
		tutor_utils()->checking_nonce();

		// Check required fields.
		if ( empty( Input::post( 'topic_title' ) ) ) {
			wp_send_json_error( array( 'message' => __( 'Topic title is required!', 'tutor' ) ) );
		}

		// Gather parameters.
		$course_id           = Input::post( 'topic_course_id', 0, Input::TYPE_INT );
		$topic_id            = Input::post( 'topic_id', 0, Input::TYPE_INT );
		$topic_title         = Input::post( 'topic_title' );
		$topic_summery       = Input::post( 'topic_summery', '', Input::TYPE_KSES_POST );
		$next_topic_order_id = tutor_utils()->get_next_topic_order_id( $course_id, $topic_id );

		// Validate if user can manage the topic.
		if ( ! tutor_utils()->can_user_manage( 'course', $course_id ) || ( $topic_id && ! tutor_utils()->can_user_manage( 'topic', $topic_id ) ) ) {
			wp_send_json_error( array( 'message' => __( 'Access Denied', 'tutor' ) ) );
		}

		// Create payload to create/update the topic.
		$post_arr                   = array(
			'post_type'    => 'topics',
			'post_title'   => $topic_title,
			'post_content' => $topic_summery,
			'post_status'  => 'publish',
			'post_author'  => get_current_user_id(),
			'post_parent'  => $course_id,
			'menu_order'   => $next_topic_order_id,
		);
		$topic_id ? $post_arr['ID'] = $topic_id : 0;
		$current_topic_id           = wp_insert_post( $post_arr );

		ob_start();
		include tutor()->path . 'views/metabox/course-contents.php';

		wp_send_json_success(
			array(
				'topic_title'     => $topic_title,
				'course_contents' => ob_get_clean(),
			)
		);
	}

	/**
	 * Add columns to course row in default WP list table
	 *
	 * @since 1.0.0
	 *
	 * @param array $columns column list.
	 * @return mixed
	 */
	public function add_column( $columns ) {
		$date_col = $columns['date'];
		unset( $columns['date'] );
		$columns['lessons']  = __( 'Lessons', 'tutor' );
		$columns['students'] = __( 'Students', 'tutor' );
		$columns['price']    = __( 'Price', 'tutor' );
		$columns['date']     = $date_col;

		return $columns;
	}

	/**
	 * Add data to custom column
	 *
	 * @since 1.0.0
	 *
	 * @param string  $column column name.
	 * @param integer $post_id post ID.
	 *
	 * @return void
	 */
	public function custom_lesson_column( $column, $post_id ) {
		if ( 'lessons' === $column ) {
			echo esc_html( tutor_utils()->get_lesson_count_by_course( $post_id ) );
		}

		if ( 'students' === $column ) {
			echo esc_html( tutor_utils()->count_enrolled_users_by_course( $post_id ) );
		}

		if ( 'price' === $column ) {
			$price = tutor_utils()->get_course_price( $post_id );
			if ( $price ) {
				$monetize_by = tutils()->get_option( 'monetize_by' );
				if ( function_exists( 'wc_price' ) && 'wc' === $monetize_by ) {
					echo wp_kses(
						'<span class="tutor-label-success">' . wc_price( $price ) . '</span>',
						array(
							'span' => array( 'class' => true ),
						)
					);
				} else {
					echo wp_kses(
						'<span class="tutor-label-success">' . $price . '</span>',
						array( 'span' => array( 'class' => true ) )
					);
				}
			} else {
				echo esc_html( apply_filters( 'tutor-loop-default-price', __( 'free', 'tutor' ) ) );
			}
		}
	}


	/**
	 * Delete a course topic
	 *
	 * @since 1.0.0
	 * @return void
	 */
	public function tutor_delete_topic() {

		tutor_utils()->checking_nonce();

		global $wpdb;
		$topic_id = Input::post( 'topic_id', '' );

		if ( ! $topic_id || ! is_numeric( $topic_id ) || ! tutor_utils()->can_user_manage( 'topic', $topic_id ) ) {
			wp_send_json_error( array( 'message' => 'Access Forbidden' ) );
		}

		// Assign course ID to orphan content IDs since the topic will be deleted.
		$course_id   = tutor_utils()->get_course_id_by( 'topic', $topic_id );
		$content_ids = tutor_utils()->get_course_content_ids_by( null, 'topic', $topic_id );
		foreach ( $content_ids as $content_id ) {
			update_post_meta( $content_id, '_tutor_course_id_for_lesson', $course_id );
			// Actually all kind of contents.
			// This keyword '_tutor_course_id_for_lesson' used just to support backward compatibillity.
		}

		// Set contents under the topic orphan.
		$wpdb->update( $wpdb->posts, array( 'post_parent' => 0 ), array( 'post_parent' => $topic_id ) );

		// Then delete the topic from database.
		$wpdb->delete( $wpdb->postmeta, array( 'post_id' => $topic_id ) );
		wp_delete_post( $topic_id );

		wp_send_json_success();
	}

	/**
	 * Handle enroll now action
	 *
	 * @since 1.0.0
	 * @return void
	 */
	public function enroll_now() {

		// Checking if action comes from Enroll form.
		// phpcs:ignore WordPress.Security.NonceVerification.Missing
		if ( tutor_utils()->array_get( 'tutor_course_action', tutor_sanitize_data( $_POST ) ) !== '_tutor_course_enroll_now' || ! isset( $_POST['tutor_course_id'] ) ) {
			return;
		}

		// Checking Nonce.
		tutor_utils()->checking_nonce();

		$user_id = get_current_user_id();
		if ( ! $user_id ) {
			exit( esc_html__( 'Please Sign In first', 'tutor' ) );
		}

		$course_id = Input::post( 'tutor_course_id', 0, Input::TYPE_INT );
		$user_id   = get_current_user_id();

		/**
		 * TODO: need to check purchase information
		 */

		$is_purchasable = tutor_utils()->is_course_purchasable( $course_id );

		/**
		 * If is is not purchasable, it's free, and enroll right now
		 * If purchasable, then process purchase.
		 *
		 * @since: v.1.0.0
		 */
		if ( $is_purchasable ) { //phpcs:ignore Generic.CodeAnalysis.EmptyStatement.DetectedIf
			// Process purchase.

		} else {
			// Free enroll.
			tutor_utils()->do_enroll( $course_id );
		}

		$referer_url = wp_get_referer();
		wp_safe_redirect( $referer_url . '?nocache=' . time() );
		exit;
	}

	/**
	 * Mark complete completed
	 *
	 * @since 1.0.0
	 * @return void
	 */
	public function mark_course_complete() {
		$tutor_action = Input::post( 'tutor_action' );
		$course_id    = Input::post( 'course_id', 0, Input::TYPE_INT );
		if ( 'tutor_complete_course' !== $tutor_action || ! $course_id ) {
			return;
		}

		// Checking nonce.
		tutor_utils()->checking_nonce();

		$user_id = get_current_user_id();

		// TODO: need to show view if not signed_in.
		if ( ! $user_id ) {
			die( esc_html__( 'Please Sign-In', 'tutor' ) );
		}

		CourseModel::mark_course_as_completed( $course_id, $user_id );

		$permalink = get_the_permalink( $course_id );

		// Set temporary identifier to show review pop up.
		self::set_review_popup_data( $user_id, $course_id, $permalink );

		wp_safe_redirect( $permalink );
		exit;
	}

	/**
	 * Set data for review popup.
	 *
	 * @since 2.2.5
	 * @since 2.4.0 removed $permalink param. store user meta instead of option data.
	 *
	 * @param int $user_id user id.
	 * @param int $course_id course id.
	 *
	 * @return void
	 */
	public static function set_review_popup_data( $user_id, $course_id ) {
		if ( get_tutor_option( 'enable_course_review' ) ) {
			$rating = tutor_utils()->get_course_rating_by_user( $course_id, $user_id );
			if ( ! $rating || ( empty( $rating->rating ) && empty( $rating->review ) ) ) {
				$meta_key = User::get_review_popup_meta( $course_id );
				add_user_meta( $user_id, $meta_key, $course_id, true );
			}
		}
	}

	/**
	 * Popup review form on course details
	 *
	 * @since 1.0.0
	 * @return void
	 */
	public function popup_review_form() {
		if ( is_user_logged_in() ) {
			$user_id          = get_current_user_id();
			$course_id        = get_the_ID();
			$meta_key         = User::get_review_popup_meta( $course_id );
			$review_course_id = (int) get_user_meta( $user_id, $meta_key, true );

			if ( is_single() && $course_id === $review_course_id ) {
				include tutor()->path . 'views/modal/review.php';
			}
		}
	}

	/**
	 * Review popup data clear
	 *
	 * @since 2.4.0
	 *
	 * @return void
	 */
	public function clear_review_popup_data() {
		tutils()->checking_nonce();

		if ( is_user_logged_in() ) {
			$user_id   = get_current_user_id();
			$course_id = Input::post( 'course_id', 0, Input::TYPE_INT );

			if ( $course_id ) {
				$meta_key = User::get_review_popup_meta( $course_id );
				delete_user_meta( $user_id, $meta_key, $course_id );
			}

			wp_send_json_success();
		}
	}

	/**
	 * Delete course delete from frontend dashboard
	 *
	 * @since 2.0.0
	 * @return void
	 */
	public function tutor_delete_dashboard_course() {
		tutor_utils()->checking_nonce();

		$course_id = Input::post( 'course_id', 0, Input::TYPE_INT );
		if ( ! tutor_utils()->can_user_manage( 'course', $course_id ) ) {
			wp_send_json_error( array( 'message' => __( 'Access Denied', 'tutor' ) ) );
		}

		/**
		 * Co-instructor can not delete a course
		 *
		 * @since 2.1.6
		 */
		if ( false === CourseModel::is_main_instructor( $course_id ) ) {
			wp_send_json_error( array( 'message' => __( 'Only main instructor can delete this course', 'tutor' ) ) );
		}

		// Check if user is only an instructor.
		if ( ! current_user_can( 'administrator' ) ) {
			// Check if instructor can trash course.
			$can_trash_post = tutor_utils()->get_option( 'instructor_can_delete_course' );

			if ( ! $can_trash_post ) {
				wp_send_json_error( tutor_utils()->error_message() );
			}
		}

		$trash_course = wp_update_post(
			array(
				'ID'          => $course_id,
				'post_status' => 'trash',
			)
		);

		if ( $trash_course ) {
			wp_send_json_success( __( 'Course has been trashed successfully ', 'tutor' ) );
		}
		wp_send_json_success();
	}

	/**
	 * Main author change from gutenberg editor
	 *
	 * @since 2.0.0
	 *
	 * @param array $data data.
	 * @param array $postarr post array.
	 *
	 * @return mixed
	 */
	public function tutor_add_gutenberg_author( $data, $postarr ) {
		$gutenberg_enabled = tutor_utils()->get_option( 'enable_gutenberg_course_edit' );
		$post_type         = $postarr['post_type'];
		$courses_post_type = tutor()->course_post_type;

		if ( false === is_admin() || false === $gutenberg_enabled || $post_type !== $courses_post_type ) {
			return $data;
		}

		/**
		 * Only admin can change main author
		 */
		if ( $courses_post_type === $post_type && ! current_user_can( 'administrator' ) ) {
			global $wpdb;
			$post_ID     = (int) tutor_utils()->avalue_dot( 'ID', $postarr );
			$post_author = (int) $wpdb->get_var( $wpdb->prepare( "SELECT post_author FROM {$wpdb->posts} WHERE ID = %d ", $post_ID ) );

			if ( $post_author > 0 ) {
				$data['post_author'] = $post_author;
			} else {
				$data['post_author'] = get_current_user_id();
			}
		}

		return $data;
	}


	/**
	 * Attach product with course when course save from frontend or backend.
	 *
	 * @since 1.3.4
	 *
	 * @param integer $post_ID  course ID.
	 * @param array   $post_data cretaed course post details.
	 *
	 * @return void
	 */
	public function attach_product_with_course( $post_ID, $post_data ) {

		$monetize_by = tutor_utils()->get_option( 'monetize_by' );

		/**
		 * The function is_admin will check only loaded page from WP admin.
		 * It does not check any role
		 *
		 * @since 2.6.0
		 *
		 * tutor_is_rest() check added, if loaded from rest api
		 */
		$is_admin_panel = is_admin() || tutor_is_rest();
		// From backend course select box.
		$product_id = Input::post( '_tutor_course_product_id', 0, Input::TYPE_INT );

		/**
		 * From Admin Panel, Free user can only select product from dropdown
		 */
		if ( $is_admin_panel && 'wc' === $monetize_by && tutor()->has_pro === false ) {
			if ( $product_id > 0 ) {
				update_post_meta( $post_ID, '_tutor_course_product_id', $product_id );
			} elseif ( -1 === $product_id ) {
				if ( ! tutor_is_rest() ) {
					delete_post_meta( $post_ID, '_tutor_course_product_id' );
				}
			}

			return;
		}

		$attached_product_id = tutor_utils()->get_course_product_id( $post_ID );
		$course_price        = Input::post( 'course_price', 0, Input::TYPE_NUMERIC );
		$sale_price          = Input::post( 'course_sale_price', 0, Input::TYPE_NUMERIC );

		if ( ! $course_price || $sale_price >= $course_price ) {
			return;
		}

		$course = get_post( $post_ID );

		if ( 'wc' === $monetize_by ) {

			$is_update = false;
			if ( $attached_product_id ) {
				$wc_product = get_post_meta( $attached_product_id, '_product_version', true );
				if ( $wc_product ) {
					$is_update = true;
				}
			}

			if ( $is_update || ( $product_id > 0 && $is_admin_panel ) ) {
				// Added in @since 2.0.7.
				if ( $product_id > 0 && $is_admin_panel ) {
					$attached_product_id = $product_id;
					update_post_meta( $post_ID, '_tutor_course_product_id', $product_id );
				}

				$product_obj = wc_get_product( $attached_product_id );
				$product_id  = self::create_wc_product( $course->post_title, $course_price, $sale_price, $attached_product_id );
				if ( $product_obj->is_type( 'subscription' ) ) {
					update_post_meta( $attached_product_id, '_subscription_price', $course_price );
				}
			} else {
				$product_id = self::create_wc_product( $course->post_title, $course_price, $sale_price );
				if ( $product_id ) {
					update_post_meta( $post_ID, '_tutor_course_product_id', $product_id );
					// Mark product for woocommerce.
					update_post_meta( $product_id, '_virtual', 'yes' );
					update_post_meta( $product_id, '_tutor_product', 'yes' );

					$course_post_thumbnail = get_post_meta( $post_ID, '_thumbnail_id', true );
					if ( $course_post_thumbnail ) {
						set_post_thumbnail( $product_id, $course_post_thumbnail );
					}
				}
			}
		} elseif ( 'edd' === $monetize_by ) {

			$is_update = false;

			if ( $attached_product_id ) {
				$edd_price = get_post_meta( $attached_product_id, 'edd_price', true );
				if ( $edd_price ) {
					$is_update = true;
				}
			}

			if ( $is_update ) {
				// Update the product.
				update_post_meta( $attached_product_id, 'edd_price', $course_price );
			} else {
				// Create new product.

				$post_arr    = array(
					'post_type'   => 'download',
					'post_title'  => $course->post_title,
					'post_status' => 'publish',
					'post_author' => get_current_user_id(),
				);
				$download_id = wp_insert_post( $post_arr );
				if ( $download_id ) {
					// EDD edd_price.
					update_post_meta( $download_id, 'edd_price', $course_price );

					update_post_meta( $post_ID, '_tutor_course_product_id', $download_id );
					// Mark product for EDD.
					update_post_meta( $download_id, '_tutor_product', 'yes' );

					$course_post_thumbnail = get_post_meta( $post_ID, '_thumbnail_id', true );
					if ( $course_post_thumbnail ) {
						set_post_thumbnail( $download_id, $course_post_thumbnail );
					}
				}
			}
		}
	}

	/**
	 * Add Course level to course settings
	 *
	 * @since 1.4.1
	 *
	 * @param array $args arguments.
	 * @return array
	 */
	public function add_course_level_to_settings( $args ) {
		$course_id    = get_the_ID();
		$levels       = tutor_utils()->course_levels();
		$course_level = get_post_meta( $course_id, '_tutor_course_level', true );

		$args['general']['fields']['_tutor_course_level'] = array(
			'type'        => 'select',
			'label'       => __( 'Difficulty Level', 'tutor' ),
			'label_title' => __( 'Enable', 'tutor' ),
			'options'     => $levels,
			'value'       => $course_level ? $course_level : 'intermediate',
			'desc'        => __( 'Course difficulty level', 'tutor' ),
		);

		return $args;
	}

	/**
	 * Check if course starting
	 *
	 * @since 1.4.8
	 * @return void
	 */
	public function tutor_lesson_load_before() {
		$course_id         = tutor_utils()->get_course_id_by_content( get_the_ID() );
		$completed_lessons = tutor_utils()->get_completed_lesson_count_by_course( $course_id );
		if ( is_user_logged_in() ) {
			$is_course_started = get_post_meta( $course_id, '_tutor_course_started', true );
			if ( ! $completed_lessons && ! $is_course_started ) {
				update_post_meta( $course_id, '_tutor_course_started', tutor_time() );
				do_action( 'tutor/course/started', $course_id );
			}
		}
	}

	/**
	 * Add Course level to course settings
	 *
	 * @since 1.4.8
	 * @return void
	 */
	public function course_elements_enable_disable() {
		add_filter( 'tutor_course/single/completing-progress-bar', array( $this, 'enable_disable_course_progress_bar' ) );
		add_filter( 'tutor_course/single/material_includes', array( $this, 'enable_disable_material_includes' ) );
		add_filter( 'tutor_course/single/content', array( $this, 'enable_disable_course_content' ) );
		add_filter( 'tutor_course/single/benefits_html', array( $this, 'enable_disable_course_benefits' ) );
		add_filter( 'tutor_course/single/requirements_html', array( $this, 'enable_disable_course_requirements' ) );
		add_filter( 'tutor_course/single/audience_html', array( $this, 'enable_disable_course_target_audience' ) );
		add_filter( 'tutor_course/single/nav_items', array( $this, 'enable_disable_course_nav_items' ), 999, 2 );
	}

	/**
	 * Enable disable course progress bar
	 *
	 * @since 1.4.8
	 *
	 * @param string $html HTML string.
	 * @return string
	 */
	public function enable_disable_course_progress_bar( $html ) {
		$disable_option = ! (bool) tutor_utils()->get_option( 'enable_course_progress_bar', true, true );
		if ( $disable_option ) {
			return '';
		}
		return $html;
	}

	/**
	 * Enable disable material includes
	 *
	 * @since 1.4.8
	 *
	 * @param string $html HTML string.
	 * @return string
	 */
	public function enable_disable_material_includes( $html ) {
		$disable_option = ! (bool) get_tutor_option( 'enable_course_material', true, true );
		if ( $disable_option ) {
			return '';
		}
		return $html;
	}

	/**
	 * Enable disable course content
	 *
	 * @since 1.4.8
	 *
	 * @param string $html HTML string.
	 * @return string
	 */
	public function enable_disable_course_content( $html ) {
		$disable_option = ! (bool) tutor_utils()->get_option( 'enable_course_description', true, true );
		if ( $disable_option ) {
			return '';
		}
		return $html;
	}

	/**
	 * Enable disable course benefits
	 *
	 * @since 1.4.8
	 *
	 * @param string $html HTML string.
	 * @return string
	 */
	public function enable_disable_course_benefits( $html ) {
		$disable_option = ! (bool) tutor_utils()->get_option( 'enable_course_benefits', true, true );
		if ( $disable_option ) {
			return '';
		}
		return $html;
	}

	/**
	 * Enable disable course requirements
	 *
	 * @since 1.4.8
	 *
	 * @param string $html HTML string.
	 * @return string
	 */
	public function enable_disable_course_requirements( $html ) {
		$disable_option = ! (bool) tutor_utils()->get_option( 'enable_course_requirements', true, true );
		if ( $disable_option ) {
			return '';
		}
		return $html;
	}

	/**
	 * Enable disable course target audience
	 *
	 * @since 1.4.8
	 *
	 * @param string $html HTML string.
	 * @return string
	 */
	public function enable_disable_course_target_audience( $html ) {
		$disable_option = ! (bool) tutor_utils()->get_option( 'enable_course_target_audience', true, true );
		if ( $disable_option ) {
			return '';
		}
		return $html;
	}

	/**
	 * Enable disable course nav items
	 *
	 * @since 1.4.8
	 *
	 * @param array   $items item list.
	 * @param integer $course_id course ID.
	 *
	 * @return array
	 */
	public function enable_disable_course_nav_items( $items, $course_id ) {
		global $wp_query, $post;
		$enable_q_and_a_on_course     = (bool) get_tutor_option( 'enable_q_and_a_on_course' );
		$disable_course_announcements = ! (bool) tutor_utils()->get_option( 'enable_course_announcements', true, true );
		$disable_qa_for_this_course   = ( $wp_query->is_single && ! empty( $post ) ) ? get_post_meta( $post->ID, '_tutor_enable_qa', true ) != 'yes' : false;

		// Whether Q&A enabled.
		if ( ! $enable_q_and_a_on_course || $disable_qa_for_this_course ) {
			if ( tutor_utils()->array_get( 'questions', $items ) ) {
				unset( $items['questions'] );
			}
		}

		// Whether announcment enabled.
		if ( $disable_course_announcements ) {
			if ( tutor_utils()->array_get( 'announcements', $items ) ) {
				unset( $items['announcements'] );
			}
		}

		// Hide review section if disabled.
		if ( ! get_tutor_option( 'enable_course_review' ) ) {
			unset( $items['reviews'] );
		}

		// Whether enrollment require.
		$is_enrolled = tutor_utils()->is_enrolled();

		return array_filter(
			$items,
			function ( $item ) use ( $is_enrolled ) {
				if ( isset( $item['require_enrolment'] ) && $item['require_enrolment'] ) {
					return $is_enrolled;
				}
				return true;
			}
		);
	}

	/**
	 * Filter product in shop page
	 *
	 * @since 1.4.9
	 * @return void|null
	 */
	public function filter_product_in_shop_page() {
		$hide_course_from_shop_page = (bool) get_tutor_option( 'hide_course_from_shop_page' );
		if ( ! $hide_course_from_shop_page ) {
			return;
		}
		add_action( 'woocommerce_product_query', array( $this, 'filter_woocommerce_product_query' ) );
		add_filter( 'edd_downloads_query', array( $this, 'filter_edd_downloads_query' ), 10, 2 );
		add_action( 'pre_get_posts', array( $this, 'filter_archive_meta_query' ), 1 );
	}


	/**
	 * Tutor product meta query
	 *
	 * @since 1.4.9
	 * @return array
	 */
	public function tutor_product_meta_query() {
		$meta_query = array(
			'key'     => '_tutor_product',
			'compare' => 'NOT EXISTS',
		);
		return $meta_query;
	}

	/**
	 * Filter product in woocommerce shop page
	 *
	 * @since 1.4.9
	 *
	 * @param \WP_Query $wp_query WP Query instance.
	 * @return \WP_Query
	 */
	public function filter_woocommerce_product_query( $wp_query ) {
		$product_ids = $this->get_connected_wc_product_ids();
		$wp_query->set( 'post__not_in', $product_ids );
		return $wp_query;
	}

	/**
	 * Get connected woocommerce product ids for course and course bundle
	 *
	 * @since 2.7.2
	 *
	 * @return array
	 */
	public function get_connected_wc_product_ids() {
		global $wpdb;

		$results = $wpdb->get_results(
			$wpdb->prepare(
				"SELECT DISTINCT pm.meta_value product_id
				FROM {$wpdb->posts} p
				INNER JOIN {$wpdb->postmeta} pm ON pm.post_id = p.ID
				AND pm.meta_key = %s
				WHERE post_type IN( 'courses','course-bundle' )",
				'_tutor_course_product_id'
			)
		);

		$ids = array();
		if ( is_array( $results ) && count( $results ) ) {
			$ids = array_column( $results, 'product_id' );
		}

		return $ids;
	}

	/**
	 * Filter product in edd downloads shortcode page
	 *
	 * @since 1.4.9
	 *
	 * @param \WP_Query $query WP Query instance.
	 * @return \WP_Query
	 */
	public function filter_edd_downloads_query( $query ) {
		$query['meta_query'][] = $this->tutor_product_meta_query();
		return $query;
	}

	/**
	 * Filter product in edd downloads archive page
	 *
	 * @since 1.4.9
	 *
	 * @param \WP_Query $wp_query WP Query instance.
	 * @return \WP_Query
	 */
	public function filter_archive_meta_query( $wp_query ) {
		if ( ! is_admin() && $wp_query->is_archive && $wp_query->get( 'post_type' ) === 'download' ) {
			$wp_query->set( 'meta_query', array( $this->tutor_product_meta_query() ) );
		}
		return $wp_query;
	}

	/**
	 * Removed course price if already enrolled at single course
	 *
	 * @since 1.5.8
	 *
	 * @param string $html HTML string.
	 * @return string
	 */
	public function remove_price_if_enrolled( $html ) {
		$should_removed = apply_filters( 'should_remove_price_if_enrolled', true );

		if ( $should_removed ) {
			$course_id = get_the_ID();
			$enrolled  = tutor_utils()->is_enrolled( $course_id );
			if ( $enrolled ) {
				$html = '';
			}
		}
		return $html;
	}

	/**
	 * Check if all lessons and quizzes done before mark course complete.
	 *
	 * @since 1.5.8
	 *
	 * @param string $html HTML string.
	 * @return string
	 */
	public function tutor_lms_hide_course_complete_btn( $html ) {

		$completion_mode = tutor_utils()->get_option( 'course_completion_process' );
		if ( 'strict' !== $completion_mode ) {
			return $html;
		}

		$completed_lesson = tutor_utils()->get_completed_lesson_count_by_course();
		$lesson_count     = tutor_utils()->get_lesson_count_by_course();

		if ( $completed_lesson < $lesson_count ) {
			return '<div class="tutor-alert tutor-warning tutor-mt-28">
						<div class="tutor-alert-text">
							<span class="tutor-alert-icon tutor-fs-4 tutor-icon-circle-info tutor-mr-12"></span>
							<span>' . __( 'Complete all lessons to mark this course as complete', 'tutor' ) . '</span>
						</div>
					</div>';
		}

		$quizzes     = array();
		$assignments = array();

		$course_contents = tutor_utils()->get_course_contents_by_id();
		if ( tutor_utils()->count( $course_contents ) ) {
			foreach ( $course_contents as $content ) {
				if ( 'tutor_quiz' === $content->post_type ) {
					$quizzes[] = $content;
				}
				if ( 'tutor_assignments' === $content->post_type ) {
					$assignments[] = $content;
				}
			}
		}

		$required_assignment_pass = 0;

		foreach ( $assignments as $row ) {

			$submitted_assignment      = tutor_utils()->is_assignment_submitted( $row->ID );
			$is_reviewed_by_instructor = null === $submitted_assignment
											? false
											: get_comment_meta( $submitted_assignment->comment_ID, 'evaluate_time', true );

			if ( $submitted_assignment && $is_reviewed_by_instructor ) {
				$pass_mark  = tutor_utils()->get_assignment_option( $submitted_assignment->comment_post_ID, 'pass_mark' );
				$given_mark = get_comment_meta( $submitted_assignment->comment_ID, 'assignment_mark', true );

				if ( $given_mark < $pass_mark ) {
					$required_assignment_pass++;
				}
			} else {
				$required_assignment_pass++;
			}
		}

		$is_quiz_pass       = true;
		$required_quiz_pass = 0;

		if ( tutor_utils()->count( $quizzes ) ) {
			foreach ( $quizzes as $quiz ) {

				$attempt = tutor_utils()->get_quiz_attempt( $quiz->ID );
				if ( $attempt ) {
					$passing_grade     = tutor_utils()->get_quiz_option( $quiz->ID, 'passing_grade', 0 );
					$earned_percentage = $attempt->earned_marks > 0 ? ( number_format( ( $attempt->earned_marks * 100 ) / $attempt->total_marks ) ) : 0;

					if ( $earned_percentage < $passing_grade ) {
						$required_quiz_pass++;
						$is_quiz_pass = false;
					}
				} else {
					$required_quiz_pass++;
					$is_quiz_pass = false;
				}
			}
		}

		if ( ! $is_quiz_pass || $required_assignment_pass > 0 ) {
			$_msg           = '';
			$quiz_str       = _n( 'quiz', 'quizzes', $required_quiz_pass, 'tutor' );
			$assignment_str = _n( 'assignment', 'assignments', $required_assignment_pass, 'tutor' );

			if ( ! $is_quiz_pass && 0 == $required_assignment_pass ) {
				/* translators: %1$s: number of quiz/assignment pass required; %2$s: quiz/assignment string */
				$_msg = sprintf( __( 'You have to pass %1$s %2$s to complete this course.', 'tutor' ), $required_quiz_pass, $quiz_str );
			}

			if ( $is_quiz_pass && $required_assignment_pass > 0 ) {
				//phpcs:ignore
				$_msg = sprintf( __( 'You have to pass %1$s %2$s to complete this course.', 'tutor' ), $required_assignment_pass, $assignment_str );
			}

			if ( ! $is_quiz_pass && $required_assignment_pass > 0 ) {
				/* translators: %1$s: number of quiz pass required; %2$s: quiz string; %3$s: number of assignment pass required; %4$s: assignment string */
				$_msg = sprintf( __( 'You have to pass %1$s %2$s and %3$s %4$s to complete this course.', 'tutor' ), $required_quiz_pass, $quiz_str, $required_assignment_pass, $assignment_str );
			}

			return '<div class="tutor-alert tutor-warning tutor-mt-28">
						<div class="tutor-alert-text">
							<span class="tutor-alert-icon tutor-fs-4 tutor-icon-circle-info tutor-mr-12"></span>
							<span>' . $_msg . '</span>
						</div>
					</div>';
		}

		return $html;
	}

	/**
	 * Generate Gradebook
	 *
	 * @since 1.5.8
	 *
	 * @param string $html HTML string.
	 * @return string
	 */
	public function get_generate_greadbook( $html ) {
		if ( ! tutor_utils()->is_completed_course() ) {
			return '';
		}
		return $html;
	}

	/**
	 * Add social share content in header
	 *
	 * @since 1.6.3
	 * @return void
	 */
	public function social_share_content() {
		global $wp_query, $post;
		if ( $wp_query->is_single && ! empty( $wp_query->query_vars['post_type'] ) && $wp_query->query_vars['post_type'] === $this->course_post_type ) { ?>
			<!--Facebook-->
			<meta property="og:type" content="website"/>
			<meta property="og:image" content="<?php echo esc_url( get_tutor_course_thumbnail_src() ); ?>" />
			<meta property="og:description" content="<?php echo esc_html( $post->post_content ); ?>" />
			<!--Twitter-->
			<meta name="twitter:image" content="<?php echo esc_url( get_tutor_course_thumbnail_src() ); ?>">
			<meta name="twitter:description" content="<?php echo esc_html( $post->post_content ); ?>">
			<!--Google+-->
			<meta itemprop="image" content="<?php echo esc_url( get_tutor_course_thumbnail_src() ); ?>">
			<meta itemprop="description" content="<?php echo esc_html( $post->post_content ); ?>">
			<?php
		}
	}

	/**
	 * Delete associated enrollment
	 *
	 * @since 1.8.2
	 *
	 * @param integer $post_id post ID.
	 * @return void
	 */
	public function delete_associated_enrollment( $post_id ) {
		global $wpdb;

		$enroll_id = $wpdb->get_var(
			$wpdb->prepare(
				"SELECT
				post_id
			FROM
				{$wpdb->postmeta}
			WHERE
				meta_key='_tutor_enrolled_by_order_id'
				AND meta_value = %d
			",
				$post_id
			)
		);

		if ( is_numeric( $enroll_id ) && $enroll_id > 0 ) {

			$course_id = get_post_field( 'post_parent', $enroll_id );
			$user_id   = get_post_field( 'post_author', $enroll_id );

			tutor_utils()->cancel_course_enrol( $course_id, $user_id );
		}
	}

	/**
	 * Reset course progress.
	 *
	 * @since 1.5.8
	 * @return void
	 */
	public function tutor_reset_course_progress() {
		tutor_utils()->checking_nonce();
		$course_id = Input::post( 'course_id' );

		if ( ! $course_id || ! is_numeric( $course_id ) || ! tutor_utils()->is_enrolled( $course_id ) ) {
			wp_send_json_error( array( 'message' => __( 'Invalid Course ID or Access Denied.', 'tutor' ) ) );
			return;
		}

		tutor_utils()->delete_course_progress( $course_id );
		wp_send_json_success( array( 'redirect_to' => tutor_utils()->get_course_first_lesson( $course_id ) ) );
	}

	/**
	 * Do enroll if guest attempt to enroll and course is free
	 *
	 * @since 1.9.8
	 *
	 * @param integer $course_id course ID.
	 * @param integer $user_id user ID.

	 * @return void
	 */
	public function enroll_after_login_if_attempt( int $course_id, int $user_id ) {
		$course_id = sanitize_text_field( $course_id );
		if ( $course_id ) {
			$is_purchasable = tutor_utils()->is_course_purchasable( $course_id );
			if ( ! $is_purchasable ) {
				tutor_utils()->do_enroll( $course_id, $order_id = 0, $user_id );
				do_action( 'guest_attempt_after_enrollment', $course_id );
			}
		}
	}

	/**
	 * Handle course enrollment
	 *
	 * @since 2.1.0
	 * @return void
	 */
	public function course_enrollment() {
		tutor_utils()->checking_nonce();

		$course_id = Input::post( 'course_id', 0, Input::TYPE_INT );
		$user_id   = get_current_user_id();

		if ( $course_id ) {
			$enroll = tutor_utils()->do_enroll( $course_id, 0, $user_id );
			if ( $enroll ) {
				wp_send_json_success( __( 'Enrollment successfully done!', 'tutor' ) );
			} else {
				wp_send_json_error( __( 'Enrollment failed, please try again!', 'tutor' ) );
			}
		} else {
			wp_send_json_error( __( 'Invalid course ID', 'tutor' ) );
		}
	}

	/**
	 * After trash a course direct to the course list page
	 *
	 * @since 2.1.7
	 *
	 * @param integer $post_id int course id.
	 *
	 * @return void
	 */
	public static function redirect_to_course_list_page( int $post_id ): void {
		$post = get_post( $post_id );
		if ( tutor()->course_post_type === $post->post_type ) {
			$is_gutenberg_enabled = tutor_utils()->get_option( 'enable_gutenberg_course_edit' );
			if ( ! $is_gutenberg_enabled ) {
				wp_safe_redirect( admin_url( 'admin.php?page=tutor' ) );
				exit;
			}
		}
	}

	/**
	 * Create or update WooCommerce product
	 *
	 * If product id not set it will create new one.
	 *
	 * @since 2.2.0
	 *
	 * @param string $title product title.
	 * @param string $reg_price product price.
	 * @param string $sale_price product sale price.
	 * @param int    $product_id product ID.
	 * @param string $status product status.
	 *
	 * @return integer
	 */
	public static function create_wc_product( $title, $reg_price, $sale_price, $product_id = 0, $status = 'publish' ) {
		$product_obj = new \WC_Product();
		if ( $product_id ) {
			$product_obj = wc_get_product( $product_id );
		}

		$product_obj->set_name( $title );
		$product_obj->set_status( $status );
		$product_obj->set_price( $reg_price );
		$product_obj->set_regular_price( $reg_price );

		if ( $sale_price > 0 ) {
			$product_obj->set_sale_price( $sale_price );
		} else {
			$product_obj->set_sale_price( null );
		}

		$product_obj->set_sold_individually( true );

		return $product_obj->save();
	}
}
SEO Archives - Bornolipi - বর্ণলিপি