STRATO-apps/wordpress_03/app/wp-content/plugins/tutor-pro/classes/LessonNotes.php
SHA-256: eeca7e46f7bc5d3f614a8a5043c235194bea4e6c04e21c5027c152dedae7c24b
<?php
/**
* Handle Lesson Notes
*
* @package TutorPro\Classes
* @author Themeum <support@themeum.com>
* @link https://themeum.com
* @since 3.9.0
*/
namespace TUTOR_PRO;
use Tutor\Helpers\HttpHelper;
use TUTOR\Icon;
use TUTOR\Input;
use Tutor\Traits\JsonResponse;
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
/**
* Class Lesson Notes
*/
class LessonNotes {
use JsonResponse;
/**
* Lesson Notes Meta Key
*
* @since 3.9.0
*/
const COMMENT_TYPE = 'lesson_note';
const TYPE_REGULAR = 'regular';
const TYPE_HIGHLIGHT = 'highlight';
const TYPE_VIDEO = 'video';
const NOTE_META_KEY = '_tutor_note_info';
/**
* Register hooks.
*/
public function __construct() {
/**
* Lesson Notes feature
*
* @since 3.3.0
*/
add_action( 'wp_enqueue_scripts', array( $this, 'enqueue_note_scripts' ) );
add_filter( 'tutor_lesson_single_nav_items', array( $this, 'filter_lesson_single_nav_item' ) );
add_filter( 'tutor_lesson_single_nav_contents', array( $this, 'filter_lesson_notes_tab_content' ) );
add_action( 'tutor_lesson_single_after_nav_items', array( $this, 'load_lesson_notes_nav_button' ), 10, 2 );
add_action( 'wp_ajax_tutor_pro_save_lesson_note', array( $this, 'ajax_save_lesson_note' ) );
add_action( 'wp_ajax_tutor_pro_update_lesson_note', array( $this, 'ajax_update_lesson_note' ) );
add_action( 'wp_ajax_tutor_pro_delete_lesson_note', array( $this, 'ajax_delete_lesson_note' ) );
add_action( 'wp_ajax_tutor_pro_get_lesson_notes_html', array( $this, 'ajax_get_lesson_notes_html' ) );
add_action( 'wp_ajax_tutor_pro_get_single_lesson_note_html', array( $this, 'ajax_get_single_lesson_note_html' ) );
add_action( 'wp_ajax_tutor_pro_lesson_notes_load_more', array( $this, 'ajax_lesson_notes_load_more' ) );
}
/**
* Enqueue lesson notes js and css
*
* @since 3.9.0
*/
public function enqueue_note_scripts() {
if ( is_single() && tutor()->lesson_post_type === get_post_type() ) {
wp_enqueue_script( 'tutor-pro-lesson-notes', tutor_pro()->url . 'assets/js/lesson-notes.js', array( 'jquery', 'wp-i18n' ), TUTOR_PRO_VERSION, true );
$lesson_notes = $this->get_lesson_notes( get_the_ID(), get_current_user_id(), 0, 1000 );
$lesson_notes = $this->lesson_decode_unicode_sequences( $lesson_notes );
wp_localize_script( 'tutor-pro-lesson-notes', 'lesson_notes', $lesson_notes );
wp_enqueue_style( 'tutor-pro-lesson-notes', tutor_pro()->url . 'assets/css/lesson-notes.css', array(), TUTOR_PRO_VERSION );
}
}
/**
* Lesson decode unicode sequences.
*
* @param array $lesson_notes Lesson notes to decode.
*
* @return array Decoded lesson notes.
*/
public function lesson_decode_unicode_sequences( $lesson_notes ) {
foreach ( $lesson_notes as $note ) {
$note->highlight_text = tutor_decode_unicode_sequences( $note->highlight_text );
if ( 'highlight' === $note->type && ! empty( $note->highlight_serialized['text'] ) ) {
$note->highlight_serialized['text'] = tutor_decode_unicode_sequences( $note->highlight_serialized['text'] );
}
}
return $lesson_notes;
}
/**
* Add Lesson Notes Nav Item
*
* @since 3.9.0
*
* @param array $nav_items Nav Items.
*
* @return array
*/
public function filter_lesson_single_nav_item( $nav_items ) {
if ( ! self::is_notes_tab_available() ) {
return $nav_items;
}
$notes_tab = array(
'label' => esc_html__( 'Notes', 'tutor-pro' ),
'value' => 'notes',
'icon' => Icon::CALENDAR_LINES,
'icon_type' => 'svg',
);
$new_nav_items = array();
$inserted = false;
foreach ( $nav_items as $key => $item ) {
// Insert before comments tab if it exists.
if ( isset( $item['value'] ) && 'comments' === $item['value'] && ! $inserted ) {
$new_nav_items['notes'] = $notes_tab;
$inserted = true;
}
$new_nav_items[ $key ] = $item;
}
// If 'comments' wasn't found, append at the end.
if ( ! $inserted ) {
$new_nav_items['notes'] = $notes_tab;
}
return $new_nav_items;
}
/**
* Add Lesson Notes Tab Content
*
* @since 3.9.0
*
* @param array $nav_contents Nav Contents.
*
* @return array
*/
public function filter_lesson_notes_tab_content( $nav_contents ) {
if ( ! self::is_notes_tab_available() ) {
return $nav_contents;
}
$nav_contents['notes'] = array(
'label' => esc_html__( 'Notes', 'tutor-pro' ),
'value' => 'notes',
'template_path' => 'lesson-notes.tab-content',
'is_pro' => true,
);
return $nav_contents;
}
/**
* Load Lesson Notes Nav Button
*
* @since 3.9.0
*
* @param int $lesson_id Lesson ID.
* @param string $active_tab Active Page Tab.
*/
public function load_lesson_notes_nav_button( $lesson_id, $active_tab ) {
if ( ! self::is_notes_tab_available() ) {
return;
}
?>
<button id="tutor-lesson-nav-take-note-btn" class="tutor-btn tutor-btn-sm <?php echo esc_attr( ( 'notes' === $active_tab ) ? 'tutor-d-none' : '' ); ?>">
<?php tutor_utils()->render_svg_icon( Icon::FEATHER, 20, 20 ); ?>
<?php esc_html_e( 'Take Note', 'tutor-pro' ); ?>
</button>
<?php
}
/**
* Save Lesson Note
*
* @since 3.9.0
*/
public function ajax_save_lesson_note() {
tutor_utils()->check_nonce();
$current_user = wp_get_current_user();
$lesson_id = Input::post( 'lesson_id', 0, Input::TYPE_INT );
$note_text = Input::post( 'note_text', '', Input::TYPE_TEXTAREA );
$highlight_text = Input::post( 'highlight_text', '', Input::TYPE_KSES_POST );
$highlight_serialized = Input::post( 'highlight_serialized', Input::TYPE_KSES_POST );
$video_start = Input::post( 'video_start_time', '' );
$video_end = Input::post( 'video_end_time', '' );
if ( empty( $lesson_id ) || empty( $note_text ) ) {
$this->response_bad_request( __( 'Invalid lesson or note text', 'tutor-pro' ) );
}
$lesson = get_post( $lesson_id );
if ( ! $lesson || tutor()->lesson_post_type !== $lesson->post_type ) {
$this->response_bad_request( __( 'Lesson not found', 'tutor-pro' ) );
}
if ( ! tutor_utils()->has_enrolled_content_access( 'lesson', $lesson_id ) ) {
$this->json_response( __( 'You do not have access to this lesson', 'tutor-pro' ), null, HttpHelper::STATUS_FORBIDDEN );
}
$comment_data = array(
'comment_post_ID' => $lesson_id,
'comment_content' => $note_text,
'comment_type' => self::COMMENT_TYPE,
'comment_agent' => 'TutorLMSPlugin',
'comment_approved' => 1,
'user_id' => $current_user->ID,
'comment_author' => $current_user->user_login,
'comment_author_email' => $current_user->user_email,
'comment_author_url' => $current_user->user_url,
);
$comment_id = wp_insert_comment( $comment_data );
if ( is_wp_error( $comment_id ) ) {
$this->json_response( __( 'Failed to save note', 'tutor-pro' ), null, HttpHelper::STATUS_INTERNAL_SERVER_ERROR );
}
$note_type = self::TYPE_REGULAR;
if ( $highlight_serialized ) {
$note_type = self::TYPE_HIGHLIGHT;
}
if ( self::has_video_time( $video_start ) ) {
$note_type = self::TYPE_VIDEO;
}
// Save meta data.
$note_meta = array();
if ( self::TYPE_HIGHLIGHT === $note_type ) {
$note_meta = array(
'type' => $note_type,
'text' => $highlight_text,
'serialized' => json_decode( $highlight_serialized ),
);
}
if ( self::TYPE_VIDEO === $note_type ) {
$note_meta = array(
'type' => $note_type,
'video_start' => $video_start,
'video_end' => $video_end,
);
}
if ( ! empty( $note_meta ) ) {
update_comment_meta( $comment_id, self::NOTE_META_KEY, wp_json_encode( $note_meta ) );
}
$data = array(
'comment_ID' => $comment_id,
'comment_post_ID' => $lesson_id,
'comment_content' => $note_text,
'highlight_text' => $highlight_text,
'highlight_serialized' => $highlight_serialized,
'video_start_time' => $video_start,
'video_end_time' => $video_end,
);
$this->json_response( __( 'Note saved successfully', 'tutor-pro' ), $data );
}
/**
* Update Lesson Note
*
* @since 3.9.0
*/
public function ajax_update_lesson_note() {
tutor_utils()->check_nonce();
$note_id = Input::post( 'note_id', 0, Input::TYPE_INT );
$note_text = Input::post( 'note_text', '', Input::TYPE_TEXTAREA );
if ( empty( $note_id ) || empty( $note_text ) ) {
$this->response_bad_request( __( 'Invalid comment or note text', 'tutor-pro' ) );
}
$comment = get_comment( $note_id );
if ( ! $comment || get_current_user_id() !== (int) $comment->user_id ) {
$this->json_response( __( 'You are not authorized to update this note', 'tutor-pro' ), null, HttpHelper::STATUS_FORBIDDEN );
}
$updated = wp_update_comment(
array(
'comment_ID' => $note_id,
'comment_content' => $note_text,
)
);
if ( is_wp_error( $updated ) ) {
$this->json_response( __( 'Failed to update note', 'tutor-pro' ), null, HttpHelper::STATUS_INTERNAL_SERVER_ERROR );
}
$this->json_response( __( 'Note updated successfully', 'tutor-pro' ), array( 'note_id' => $note_id ) );
}
/**
* Delete Lesson Note
*
* @since 3.9.0
*/
public function ajax_delete_lesson_note() {
tutor_utils()->check_nonce();
$note_id = Input::post( 'note_id', 0, Input::TYPE_INT );
if ( empty( $note_id ) ) {
$this->response_bad_request( __( 'Invalid comment', 'tutor-pro' ) );
}
$comment = get_comment( $note_id );
if ( ! $comment || self::COMMENT_TYPE !== $comment->comment_type ) {
$this->json_response( __( 'Note not found', 'tutor-pro' ), null, HttpHelper::STATUS_NOT_FOUND );
}
if ( get_current_user_id() !== (int) $comment->user_id ) {
$this->json_response( __( 'You are not authorized to delete this note', 'tutor-pro' ), null, HttpHelper::STATUS_FORBIDDEN );
}
$deleted = wp_delete_comment( $note_id, true );
if ( ! $deleted ) {
$this->json_response( __( 'Failed to delete note', 'tutor-pro' ), null, HttpHelper::STATUS_INTERNAL_SERVER_ERROR );
}
$this->json_response( __( 'Note deleted successfully', 'tutor-pro' ), array( 'note_id' => $note_id ) );
}
/**
* Get Lesson Notes HTML
*
* @since 3.9.0
*/
public function ajax_get_lesson_notes_html() {
tutor_utils()->check_nonce();
$lesson_id = Input::post( 'lesson_id', 0, Input::TYPE_INT );
if ( empty( $lesson_id ) ) {
$this->response_bad_request( __( 'Invalid lesson', 'tutor-pro' ) );
}
if ( ! tutor_utils()->has_enrolled_content_access( 'lesson', $lesson_id ) ) {
$this->json_response( __( 'You do not have access to this lesson', 'tutor-pro' ), null, HttpHelper::STATUS_FORBIDDEN );
}
ob_start();
tutor_load_template(
'lesson-notes/note-list',
array(
'lesson_id' => $lesson_id,
),
true
);
$html = ob_get_clean();
$this->json_response( __( 'Notes fetched successfully', 'tutor-pro' ), array( 'html' => $html ) );
}
/**
* Get Single Lesson Note HTML
*
* @since 3.9.0
*/
public function ajax_get_single_lesson_note_html() {
tutor_utils()->check_nonce();
$lesson_id = Input::post( 'lesson_id', 0, Input::TYPE_INT );
$note_id = Input::post( 'note_id', 0, Input::TYPE_INT );
if ( empty( $lesson_id ) || empty( $note_id ) ) {
$this->response_bad_request( __( 'Invalid lesson or comment', 'tutor-pro' ) );
}
if ( ! tutor_utils()->has_enrolled_content_access( 'lesson', $lesson_id ) ) {
$this->json_response( __( 'You do not have access to this lesson', 'tutor-pro' ), null, HttpHelper::STATUS_FORBIDDEN );
}
$note = $this->get_single_lesson_note( $note_id );
if ( ! $note ) {
$this->json_response( __( 'Note not found', 'tutor-pro' ), null, HttpHelper::STATUS_NOT_FOUND );
}
ob_start();
tutor_load_template(
'lesson-notes/note-item',
array(
'lesson_id' => $lesson_id,
'note' => $note,
),
true
);
$html = ob_get_clean();
$this->json_response( __( 'Note fetched successfully', 'tutor-pro' ), array( 'html' => $html ) );
}
/**
* Lesson Notes Load More
*
* @since 3.9.0
*/
public function ajax_lesson_notes_load_more() {
tutor_utils()->check_nonce();
$lesson_id = Input::post( 'lesson_id', 0, Input::TYPE_INT );
$offset = Input::post( 'offset', 0, Input::TYPE_INT );
if ( empty( $lesson_id ) ) {
$this->response_bad_request( __( 'Invalid lesson', 'tutor-pro' ) );
}
if ( ! tutor_utils()->has_enrolled_content_access( 'lesson', $lesson_id ) ) {
$this->json_response( __( 'You do not have access to this lesson', 'tutor-pro' ), null, HttpHelper::STATUS_FORBIDDEN );
}
$items_per_page = tutor_utils()->get_option( 'pagination_per_page' );
$note_list = $this->get_lesson_notes( $lesson_id, get_current_user_id(), $offset, $items_per_page );
if ( empty( $note_list ) ) {
$this->json_response( __( 'No more notes found', 'tutor-pro' ), null, HttpHelper::STATUS_NOT_FOUND );
}
ob_start();
foreach ( $note_list as $note ) {
tutor_load_template(
'lesson-notes/note-item',
array(
'lesson_id' => $lesson_id,
'note' => $note,
),
true
);
}
$html = ob_get_clean();
$this->json_response(
__( 'Notes fetched successfully', 'tutor-pro' ),
array(
'html' => $html,
'notes_count' => count( $note_list ),
)
);
}
/**
* Get Lesson Notes
*
* @since 3.9.0
*
* @param int $lesson_id Lesson ID.
* @param int $user_id User ID.
* @param int $offset Offset.
* @param int $item_per_page Items Per Page.
*/
public function get_lesson_notes( $lesson_id, $user_id, $offset = 0, $item_per_page = 20 ) {
if ( ! $lesson_id || ! $user_id ) {
return array();
}
$paged = $offset > 0 ? (int) floor( $offset / $item_per_page ) + 1 : 1;
$args = array(
'post_id' => $lesson_id,
'user_id' => $user_id,
'type' => self::COMMENT_TYPE,
'status' => 'approve',
'number' => $item_per_page,
'offset' => $offset,
'paged' => $paged,
);
$comments = get_comments( $args );
return array_map( array( $this, 'add_note_meta_to_comment' ), $comments );
}
/**
* Get Single Lesson Note
*
* @since 3.9.0
*
* @param int $note_id Note ID.
*
* @return object|null Comment object or null if not found.
*/
public function get_single_lesson_note( $note_id ) {
$comment = get_comment( $note_id );
if ( ! $comment || self::COMMENT_TYPE !== $comment->comment_type ) {
return null;
}
return $this->add_note_meta_to_comment( $comment );
}
/**
* Add note meta to comment
*
* @since 3.9.0
*
* @param object $comment Note comment.
*
* @return object The note.
*/
private function add_note_meta_to_comment( $comment ) {
$highlight_data_json = get_comment_meta( $comment->comment_ID, self::NOTE_META_KEY, true );
if ( ! empty( $highlight_data_json ) ) {
$highlight_data = json_decode( $highlight_data_json, true );
if ( is_array( $highlight_data ) ) {
$comment->type = $highlight_data['type'] ?? '';
$comment->highlight_text = $highlight_data['text'] ?? '';
$comment->highlight_serialized = $highlight_data['serialized'] ?? '';
$comment->video_start_time = $highlight_data['video_start'] ?? '';
$comment->video_end_time = $highlight_data['video_end'] ?? '';
}
}
return $comment;
}
/**
* Get Lesson Notes Count
*
* @since 3.9.0
*
* @param int $lesson_id Lesson ID.
* @param int $user_id User ID.
*
* @return int Number of notes taken for a lesson by a user.
*/
public function get_lesson_notes_count( $lesson_id, $user_id ) {
$args = array(
'post_id' => $lesson_id,
'user_id' => $user_id,
'type' => self::COMMENT_TYPE,
'status' => 'approve',
'count' => true,
);
return get_comments( $args );
}
/**
* Check if video time is valid
*
* @since 3.9.0
*
* @param string|null $time Video time.
*
* @return bool True if time is valid, false otherwise.
*/
public static function has_video_time( $time ) {
return null !== $time && '' !== $time;
}
/**
* Check if notes tab is available for current user
*
* @since 3.9.0
*
* @return bool True if notes tab is available, false otherwise.
*/
public static function is_notes_tab_available() {
$is_user_logged_in = is_user_logged_in();
if ( ! $is_user_logged_in ) {
return false;
}
$user_id = get_current_user_id();
$lesson_id = get_the_ID();
$course_id = tutor_utils()->get_course_id_by_lesson( $lesson_id );
$is_public_course = 'yes' === get_post_meta( $course_id, '_tutor_is_public_course', true );
$has_enrolled_content_access = tutor_utils()->has_enrolled_content_access( 'lesson', $lesson_id, $user_id );
return $is_public_course || $has_enrolled_content_access;
}
}