Diff: STRATO-apps/wordpress_03/app/wp-content/plugins/tutor-pro/classes/LessonNotes.php
Keine Baseline-Datei – Diff nur gegen leer.
1
-
1
+
<?php
2
+
/**
3
+
* Handle Lesson Notes
4
+
*
5
+
* @package TutorPro\Classes
6
+
* @author Themeum <support@themeum.com>
7
+
* @link https://themeum.com
8
+
* @since 3.9.0
9
+
*/
10
+
11
+
namespace TUTOR_PRO;
12
+
13
+
use Tutor\Helpers\HttpHelper;
14
+
use TUTOR\Icon;
15
+
use TUTOR\Input;
16
+
use Tutor\Traits\JsonResponse;
17
+
18
+
if ( ! defined( 'ABSPATH' ) ) {
19
+
exit;
20
+
}
21
+
22
+
/**
23
+
* Class Lesson Notes
24
+
*/
25
+
class LessonNotes {
26
+
use JsonResponse;
27
+
28
+
/**
29
+
* Lesson Notes Meta Key
30
+
*
31
+
* @since 3.9.0
32
+
*/
33
+
const COMMENT_TYPE = 'lesson_note';
34
+
35
+
const TYPE_REGULAR = 'regular';
36
+
const TYPE_HIGHLIGHT = 'highlight';
37
+
const TYPE_VIDEO = 'video';
38
+
39
+
const NOTE_META_KEY = '_tutor_note_info';
40
+
41
+
/**
42
+
* Register hooks.
43
+
*/
44
+
public function __construct() {
45
+
/**
46
+
* Lesson Notes feature
47
+
*
48
+
* @since 3.3.0
49
+
*/
50
+
add_action( 'wp_enqueue_scripts', array( $this, 'enqueue_note_scripts' ) );
51
+
add_filter( 'tutor_lesson_single_nav_items', array( $this, 'filter_lesson_single_nav_item' ) );
52
+
add_filter( 'tutor_lesson_single_nav_contents', array( $this, 'filter_lesson_notes_tab_content' ) );
53
+
add_action( 'tutor_lesson_single_after_nav_items', array( $this, 'load_lesson_notes_nav_button' ), 10, 2 );
54
+
add_action( 'wp_ajax_tutor_pro_save_lesson_note', array( $this, 'ajax_save_lesson_note' ) );
55
+
add_action( 'wp_ajax_tutor_pro_update_lesson_note', array( $this, 'ajax_update_lesson_note' ) );
56
+
add_action( 'wp_ajax_tutor_pro_delete_lesson_note', array( $this, 'ajax_delete_lesson_note' ) );
57
+
add_action( 'wp_ajax_tutor_pro_get_lesson_notes_html', array( $this, 'ajax_get_lesson_notes_html' ) );
58
+
add_action( 'wp_ajax_tutor_pro_get_single_lesson_note_html', array( $this, 'ajax_get_single_lesson_note_html' ) );
59
+
add_action( 'wp_ajax_tutor_pro_lesson_notes_load_more', array( $this, 'ajax_lesson_notes_load_more' ) );
60
+
}
61
+
62
+
/**
63
+
* Enqueue lesson notes js and css
64
+
*
65
+
* @since 3.9.0
66
+
*/
67
+
public function enqueue_note_scripts() {
68
+
if ( is_single() && tutor()->lesson_post_type === get_post_type() ) {
69
+
wp_enqueue_script( 'tutor-pro-lesson-notes', tutor_pro()->url . 'assets/js/lesson-notes.js', array( 'jquery', 'wp-i18n' ), TUTOR_PRO_VERSION, true );
70
+
71
+
$lesson_notes = $this->get_lesson_notes( get_the_ID(), get_current_user_id(), 0, 1000 );
72
+
73
+
$lesson_notes = $this->lesson_decode_unicode_sequences( $lesson_notes );
74
+
75
+
wp_localize_script( 'tutor-pro-lesson-notes', 'lesson_notes', $lesson_notes );
76
+
77
+
wp_enqueue_style( 'tutor-pro-lesson-notes', tutor_pro()->url . 'assets/css/lesson-notes.css', array(), TUTOR_PRO_VERSION );
78
+
}
79
+
}
80
+
81
+
/**
82
+
* Lesson decode unicode sequences.
83
+
*
84
+
* @param array $lesson_notes Lesson notes to decode.
85
+
*
86
+
* @return array Decoded lesson notes.
87
+
*/
88
+
public function lesson_decode_unicode_sequences( $lesson_notes ) {
89
+
foreach ( $lesson_notes as $note ) {
90
+
$note->highlight_text = tutor_decode_unicode_sequences( $note->highlight_text );
91
+
if ( 'highlight' === $note->type && ! empty( $note->highlight_serialized['text'] ) ) {
92
+
$note->highlight_serialized['text'] = tutor_decode_unicode_sequences( $note->highlight_serialized['text'] );
93
+
}
94
+
}
95
+
return $lesson_notes;
96
+
}
97
+
98
+
/**
99
+
* Add Lesson Notes Nav Item
100
+
*
101
+
* @since 3.9.0
102
+
*
103
+
* @param array $nav_items Nav Items.
104
+
*
105
+
* @return array
106
+
*/
107
+
public function filter_lesson_single_nav_item( $nav_items ) {
108
+
if ( ! self::is_notes_tab_available() ) {
109
+
return $nav_items;
110
+
}
111
+
112
+
$notes_tab = array(
113
+
'label' => esc_html__( 'Notes', 'tutor-pro' ),
114
+
'value' => 'notes',
115
+
'icon' => Icon::CALENDAR_LINES,
116
+
'icon_type' => 'svg',
117
+
);
118
+
119
+
$new_nav_items = array();
120
+
$inserted = false;
121
+
122
+
foreach ( $nav_items as $key => $item ) {
123
+
// Insert before comments tab if it exists.
124
+
if ( isset( $item['value'] ) && 'comments' === $item['value'] && ! $inserted ) {
125
+
$new_nav_items['notes'] = $notes_tab;
126
+
$inserted = true;
127
+
}
128
+
129
+
$new_nav_items[ $key ] = $item;
130
+
}
131
+
132
+
// If 'comments' wasn't found, append at the end.
133
+
if ( ! $inserted ) {
134
+
$new_nav_items['notes'] = $notes_tab;
135
+
}
136
+
137
+
return $new_nav_items;
138
+
}
139
+
140
+
/**
141
+
* Add Lesson Notes Tab Content
142
+
*
143
+
* @since 3.9.0
144
+
*
145
+
* @param array $nav_contents Nav Contents.
146
+
*
147
+
* @return array
148
+
*/
149
+
public function filter_lesson_notes_tab_content( $nav_contents ) {
150
+
if ( ! self::is_notes_tab_available() ) {
151
+
return $nav_contents;
152
+
}
153
+
154
+
$nav_contents['notes'] = array(
155
+
'label' => esc_html__( 'Notes', 'tutor-pro' ),
156
+
'value' => 'notes',
157
+
'template_path' => 'lesson-notes.tab-content',
158
+
'is_pro' => true,
159
+
);
160
+
161
+
return $nav_contents;
162
+
}
163
+
164
+
/**
165
+
* Load Lesson Notes Nav Button
166
+
*
167
+
* @since 3.9.0
168
+
*
169
+
* @param int $lesson_id Lesson ID.
170
+
* @param string $active_tab Active Page Tab.
171
+
*/
172
+
public function load_lesson_notes_nav_button( $lesson_id, $active_tab ) {
173
+
if ( ! self::is_notes_tab_available() ) {
174
+
return;
175
+
}
176
+
177
+
?>
178
+
<button id="tutor-lesson-nav-take-note-btn" class="tutor-btn tutor-btn-sm <?php echo esc_attr( ( 'notes' === $active_tab ) ? 'tutor-d-none' : '' ); ?>">
179
+
<?php tutor_utils()->render_svg_icon( Icon::FEATHER, 20, 20 ); ?>
180
+
<?php esc_html_e( 'Take Note', 'tutor-pro' ); ?>
181
+
</button>
182
+
<?php
183
+
}
184
+
185
+
/**
186
+
* Save Lesson Note
187
+
*
188
+
* @since 3.9.0
189
+
*/
190
+
public function ajax_save_lesson_note() {
191
+
tutor_utils()->check_nonce();
192
+
193
+
$current_user = wp_get_current_user();
194
+
$lesson_id = Input::post( 'lesson_id', 0, Input::TYPE_INT );
195
+
$note_text = Input::post( 'note_text', '', Input::TYPE_TEXTAREA );
196
+
$highlight_text = Input::post( 'highlight_text', '', Input::TYPE_KSES_POST );
197
+
$highlight_serialized = Input::post( 'highlight_serialized', Input::TYPE_KSES_POST );
198
+
$video_start = Input::post( 'video_start_time', '' );
199
+
$video_end = Input::post( 'video_end_time', '' );
200
+
201
+
if ( empty( $lesson_id ) || empty( $note_text ) ) {
202
+
$this->response_bad_request( __( 'Invalid lesson or note text', 'tutor-pro' ) );
203
+
}
204
+
205
+
$lesson = get_post( $lesson_id );
206
+
if ( ! $lesson || tutor()->lesson_post_type !== $lesson->post_type ) {
207
+
$this->response_bad_request( __( 'Lesson not found', 'tutor-pro' ) );
208
+
}
209
+
210
+
if ( ! tutor_utils()->has_enrolled_content_access( 'lesson', $lesson_id ) ) {
211
+
$this->json_response( __( 'You do not have access to this lesson', 'tutor-pro' ), null, HttpHelper::STATUS_FORBIDDEN );
212
+
}
213
+
214
+
$comment_data = array(
215
+
'comment_post_ID' => $lesson_id,
216
+
'comment_content' => $note_text,
217
+
'comment_type' => self::COMMENT_TYPE,
218
+
'comment_agent' => 'TutorLMSPlugin',
219
+
'comment_approved' => 1,
220
+
'user_id' => $current_user->ID,
221
+
'comment_author' => $current_user->user_login,
222
+
'comment_author_email' => $current_user->user_email,
223
+
'comment_author_url' => $current_user->user_url,
224
+
);
225
+
226
+
$comment_id = wp_insert_comment( $comment_data );
227
+
if ( is_wp_error( $comment_id ) ) {
228
+
$this->json_response( __( 'Failed to save note', 'tutor-pro' ), null, HttpHelper::STATUS_INTERNAL_SERVER_ERROR );
229
+
}
230
+
231
+
$note_type = self::TYPE_REGULAR;
232
+
if ( $highlight_serialized ) {
233
+
$note_type = self::TYPE_HIGHLIGHT;
234
+
}
235
+
if ( self::has_video_time( $video_start ) ) {
236
+
$note_type = self::TYPE_VIDEO;
237
+
}
238
+
239
+
// Save meta data.
240
+
$note_meta = array();
241
+
242
+
if ( self::TYPE_HIGHLIGHT === $note_type ) {
243
+
$note_meta = array(
244
+
'type' => $note_type,
245
+
'text' => $highlight_text,
246
+
'serialized' => json_decode( $highlight_serialized ),
247
+
);
248
+
}
249
+
250
+
if ( self::TYPE_VIDEO === $note_type ) {
251
+
$note_meta = array(
252
+
'type' => $note_type,
253
+
'video_start' => $video_start,
254
+
'video_end' => $video_end,
255
+
);
256
+
}
257
+
258
+
if ( ! empty( $note_meta ) ) {
259
+
update_comment_meta( $comment_id, self::NOTE_META_KEY, wp_json_encode( $note_meta ) );
260
+
}
261
+
262
+
$data = array(
263
+
'comment_ID' => $comment_id,
264
+
'comment_post_ID' => $lesson_id,
265
+
'comment_content' => $note_text,
266
+
'highlight_text' => $highlight_text,
267
+
'highlight_serialized' => $highlight_serialized,
268
+
'video_start_time' => $video_start,
269
+
'video_end_time' => $video_end,
270
+
);
271
+
272
+
$this->json_response( __( 'Note saved successfully', 'tutor-pro' ), $data );
273
+
}
274
+
275
+
/**
276
+
* Update Lesson Note
277
+
*
278
+
* @since 3.9.0
279
+
*/
280
+
public function ajax_update_lesson_note() {
281
+
tutor_utils()->check_nonce();
282
+
283
+
$note_id = Input::post( 'note_id', 0, Input::TYPE_INT );
284
+
$note_text = Input::post( 'note_text', '', Input::TYPE_TEXTAREA );
285
+
286
+
if ( empty( $note_id ) || empty( $note_text ) ) {
287
+
$this->response_bad_request( __( 'Invalid comment or note text', 'tutor-pro' ) );
288
+
}
289
+
290
+
$comment = get_comment( $note_id );
291
+
if ( ! $comment || get_current_user_id() !== (int) $comment->user_id ) {
292
+
$this->json_response( __( 'You are not authorized to update this note', 'tutor-pro' ), null, HttpHelper::STATUS_FORBIDDEN );
293
+
}
294
+
295
+
$updated = wp_update_comment(
296
+
array(
297
+
'comment_ID' => $note_id,
298
+
'comment_content' => $note_text,
299
+
)
300
+
);
301
+
302
+
if ( is_wp_error( $updated ) ) {
303
+
$this->json_response( __( 'Failed to update note', 'tutor-pro' ), null, HttpHelper::STATUS_INTERNAL_SERVER_ERROR );
304
+
}
305
+
306
+
$this->json_response( __( 'Note updated successfully', 'tutor-pro' ), array( 'note_id' => $note_id ) );
307
+
}
308
+
309
+
/**
310
+
* Delete Lesson Note
311
+
*
312
+
* @since 3.9.0
313
+
*/
314
+
public function ajax_delete_lesson_note() {
315
+
tutor_utils()->check_nonce();
316
+
317
+
$note_id = Input::post( 'note_id', 0, Input::TYPE_INT );
318
+
319
+
if ( empty( $note_id ) ) {
320
+
$this->response_bad_request( __( 'Invalid comment', 'tutor-pro' ) );
321
+
}
322
+
323
+
$comment = get_comment( $note_id );
324
+
if ( ! $comment || self::COMMENT_TYPE !== $comment->comment_type ) {
325
+
$this->json_response( __( 'Note not found', 'tutor-pro' ), null, HttpHelper::STATUS_NOT_FOUND );
326
+
}
327
+
328
+
if ( get_current_user_id() !== (int) $comment->user_id ) {
329
+
$this->json_response( __( 'You are not authorized to delete this note', 'tutor-pro' ), null, HttpHelper::STATUS_FORBIDDEN );
330
+
}
331
+
332
+
$deleted = wp_delete_comment( $note_id, true );
333
+
334
+
if ( ! $deleted ) {
335
+
$this->json_response( __( 'Failed to delete note', 'tutor-pro' ), null, HttpHelper::STATUS_INTERNAL_SERVER_ERROR );
336
+
}
337
+
338
+
$this->json_response( __( 'Note deleted successfully', 'tutor-pro' ), array( 'note_id' => $note_id ) );
339
+
}
340
+
341
+
/**
342
+
* Get Lesson Notes HTML
343
+
*
344
+
* @since 3.9.0
345
+
*/
346
+
public function ajax_get_lesson_notes_html() {
347
+
tutor_utils()->check_nonce();
348
+
349
+
$lesson_id = Input::post( 'lesson_id', 0, Input::TYPE_INT );
350
+
351
+
if ( empty( $lesson_id ) ) {
352
+
$this->response_bad_request( __( 'Invalid lesson', 'tutor-pro' ) );
353
+
}
354
+
355
+
if ( ! tutor_utils()->has_enrolled_content_access( 'lesson', $lesson_id ) ) {
356
+
$this->json_response( __( 'You do not have access to this lesson', 'tutor-pro' ), null, HttpHelper::STATUS_FORBIDDEN );
357
+
}
358
+
359
+
ob_start();
360
+
tutor_load_template(
361
+
'lesson-notes/note-list',
362
+
array(
363
+
'lesson_id' => $lesson_id,
364
+
),
365
+
true
366
+
);
367
+
$html = ob_get_clean();
368
+
369
+
$this->json_response( __( 'Notes fetched successfully', 'tutor-pro' ), array( 'html' => $html ) );
370
+
}
371
+
372
+
/**
373
+
* Get Single Lesson Note HTML
374
+
*
375
+
* @since 3.9.0
376
+
*/
377
+
public function ajax_get_single_lesson_note_html() {
378
+
tutor_utils()->check_nonce();
379
+
380
+
$lesson_id = Input::post( 'lesson_id', 0, Input::TYPE_INT );
381
+
$note_id = Input::post( 'note_id', 0, Input::TYPE_INT );
382
+
if ( empty( $lesson_id ) || empty( $note_id ) ) {
383
+
$this->response_bad_request( __( 'Invalid lesson or comment', 'tutor-pro' ) );
384
+
}
385
+
386
+
if ( ! tutor_utils()->has_enrolled_content_access( 'lesson', $lesson_id ) ) {
387
+
$this->json_response( __( 'You do not have access to this lesson', 'tutor-pro' ), null, HttpHelper::STATUS_FORBIDDEN );
388
+
}
389
+
390
+
$note = $this->get_single_lesson_note( $note_id );
391
+
if ( ! $note ) {
392
+
$this->json_response( __( 'Note not found', 'tutor-pro' ), null, HttpHelper::STATUS_NOT_FOUND );
393
+
}
394
+
395
+
ob_start();
396
+
tutor_load_template(
397
+
'lesson-notes/note-item',
398
+
array(
399
+
'lesson_id' => $lesson_id,
400
+
'note' => $note,
401
+
),
402
+
true
403
+
);
404
+
$html = ob_get_clean();
405
+
$this->json_response( __( 'Note fetched successfully', 'tutor-pro' ), array( 'html' => $html ) );
406
+
}
407
+
408
+
/**
409
+
* Lesson Notes Load More
410
+
*
411
+
* @since 3.9.0
412
+
*/
413
+
public function ajax_lesson_notes_load_more() {
414
+
tutor_utils()->check_nonce();
415
+
416
+
$lesson_id = Input::post( 'lesson_id', 0, Input::TYPE_INT );
417
+
$offset = Input::post( 'offset', 0, Input::TYPE_INT );
418
+
419
+
if ( empty( $lesson_id ) ) {
420
+
$this->response_bad_request( __( 'Invalid lesson', 'tutor-pro' ) );
421
+
}
422
+
423
+
if ( ! tutor_utils()->has_enrolled_content_access( 'lesson', $lesson_id ) ) {
424
+
$this->json_response( __( 'You do not have access to this lesson', 'tutor-pro' ), null, HttpHelper::STATUS_FORBIDDEN );
425
+
}
426
+
427
+
$items_per_page = tutor_utils()->get_option( 'pagination_per_page' );
428
+
429
+
$note_list = $this->get_lesson_notes( $lesson_id, get_current_user_id(), $offset, $items_per_page );
430
+
if ( empty( $note_list ) ) {
431
+
$this->json_response( __( 'No more notes found', 'tutor-pro' ), null, HttpHelper::STATUS_NOT_FOUND );
432
+
}
433
+
434
+
ob_start();
435
+
foreach ( $note_list as $note ) {
436
+
tutor_load_template(
437
+
'lesson-notes/note-item',
438
+
array(
439
+
'lesson_id' => $lesson_id,
440
+
'note' => $note,
441
+
),
442
+
true
443
+
);
444
+
}
445
+
$html = ob_get_clean();
446
+
$this->json_response(
447
+
__( 'Notes fetched successfully', 'tutor-pro' ),
448
+
array(
449
+
'html' => $html,
450
+
'notes_count' => count( $note_list ),
451
+
)
452
+
);
453
+
}
454
+
455
+
/**
456
+
* Get Lesson Notes
457
+
*
458
+
* @since 3.9.0
459
+
*
460
+
* @param int $lesson_id Lesson ID.
461
+
* @param int $user_id User ID.
462
+
* @param int $offset Offset.
463
+
* @param int $item_per_page Items Per Page.
464
+
*/
465
+
public function get_lesson_notes( $lesson_id, $user_id, $offset = 0, $item_per_page = 20 ) {
466
+
if ( ! $lesson_id || ! $user_id ) {
467
+
return array();
468
+
}
469
+
470
+
$paged = $offset > 0 ? (int) floor( $offset / $item_per_page ) + 1 : 1;
471
+
472
+
$args = array(
473
+
'post_id' => $lesson_id,
474
+
'user_id' => $user_id,
475
+
'type' => self::COMMENT_TYPE,
476
+
'status' => 'approve',
477
+
'number' => $item_per_page,
478
+
'offset' => $offset,
479
+
'paged' => $paged,
480
+
);
481
+
482
+
$comments = get_comments( $args );
483
+
484
+
return array_map( array( $this, 'add_note_meta_to_comment' ), $comments );
485
+
}
486
+
487
+
/**
488
+
* Get Single Lesson Note
489
+
*
490
+
* @since 3.9.0
491
+
*
492
+
* @param int $note_id Note ID.
493
+
*
494
+
* @return object|null Comment object or null if not found.
495
+
*/
496
+
public function get_single_lesson_note( $note_id ) {
497
+
$comment = get_comment( $note_id );
498
+
if ( ! $comment || self::COMMENT_TYPE !== $comment->comment_type ) {
499
+
return null;
500
+
}
501
+
return $this->add_note_meta_to_comment( $comment );
502
+
}
503
+
504
+
/**
505
+
* Add note meta to comment
506
+
*
507
+
* @since 3.9.0
508
+
*
509
+
* @param object $comment Note comment.
510
+
*
511
+
* @return object The note.
512
+
*/
513
+
private function add_note_meta_to_comment( $comment ) {
514
+
$highlight_data_json = get_comment_meta( $comment->comment_ID, self::NOTE_META_KEY, true );
515
+
516
+
if ( ! empty( $highlight_data_json ) ) {
517
+
$highlight_data = json_decode( $highlight_data_json, true );
518
+
519
+
if ( is_array( $highlight_data ) ) {
520
+
$comment->type = $highlight_data['type'] ?? '';
521
+
$comment->highlight_text = $highlight_data['text'] ?? '';
522
+
$comment->highlight_serialized = $highlight_data['serialized'] ?? '';
523
+
$comment->video_start_time = $highlight_data['video_start'] ?? '';
524
+
$comment->video_end_time = $highlight_data['video_end'] ?? '';
525
+
}
526
+
}
527
+
528
+
return $comment;
529
+
}
530
+
531
+
/**
532
+
* Get Lesson Notes Count
533
+
*
534
+
* @since 3.9.0
535
+
*
536
+
* @param int $lesson_id Lesson ID.
537
+
* @param int $user_id User ID.
538
+
*
539
+
* @return int Number of notes taken for a lesson by a user.
540
+
*/
541
+
public function get_lesson_notes_count( $lesson_id, $user_id ) {
542
+
$args = array(
543
+
'post_id' => $lesson_id,
544
+
'user_id' => $user_id,
545
+
'type' => self::COMMENT_TYPE,
546
+
'status' => 'approve',
547
+
'count' => true,
548
+
);
549
+
550
+
return get_comments( $args );
551
+
}
552
+
553
+
/**
554
+
* Check if video time is valid
555
+
*
556
+
* @since 3.9.0
557
+
*
558
+
* @param string|null $time Video time.
559
+
*
560
+
* @return bool True if time is valid, false otherwise.
561
+
*/
562
+
public static function has_video_time( $time ) {
563
+
return null !== $time && '' !== $time;
564
+
}
565
+
566
+
/**
567
+
* Check if notes tab is available for current user
568
+
*
569
+
* @since 3.9.0
570
+
*
571
+
* @return bool True if notes tab is available, false otherwise.
572
+
*/
573
+
public static function is_notes_tab_available() {
574
+
$is_user_logged_in = is_user_logged_in();
575
+
576
+
if ( ! $is_user_logged_in ) {
577
+
return false;
578
+
}
579
+
580
+
$user_id = get_current_user_id();
581
+
$lesson_id = get_the_ID();
582
+
$course_id = tutor_utils()->get_course_id_by_lesson( $lesson_id );
583
+
$is_public_course = 'yes' === get_post_meta( $course_id, '_tutor_is_public_course', true );
584
+
$has_enrolled_content_access = tutor_utils()->has_enrolled_content_access( 'lesson', $lesson_id, $user_id );
585
+
586
+
return $is_public_course || $has_enrolled_content_access;
587
+
}
588
+
}
589
+