Diff: STRATO-apps/wordpress_03/app/wp-content/plugins/tutor/classes/Ajax.php
Keine Baseline-Datei – Diff nur gegen leer.
1
-
1
+
<?php
2
+
/**
3
+
* Handle Ajax Request
4
+
*
5
+
* @package Tutor
6
+
* @author Themeum <support@themeum.com>
7
+
* @link https://themeum.com
8
+
* @since 1.0.0
9
+
*/
10
+
11
+
namespace TUTOR;
12
+
13
+
if ( ! defined( 'ABSPATH' ) ) {
14
+
exit;
15
+
}
16
+
17
+
use Tutor\Helpers\HttpHelper;
18
+
use Tutor\Models\LessonModel;
19
+
use Tutor\Traits\JsonResponse;
20
+
21
+
/**
22
+
* Ajax Class
23
+
*
24
+
* @since 1.0.0
25
+
*/
26
+
class Ajax {
27
+
use JsonResponse;
28
+
29
+
const LOGIN_ERRORS_TRANSIENT_KEY = 'tutor_login_errors';
30
+
/**
31
+
* Constructor
32
+
*
33
+
* @since 1.0.0
34
+
* @since 2.6.2 added allow_hooks param.
35
+
*
36
+
* @param bool $allow_hooks default value true.
37
+
*
38
+
* @return void
39
+
*/
40
+
public function __construct( $allow_hooks = true ) {
41
+
if ( $allow_hooks ) {
42
+
add_action( 'wp_ajax_sync_video_playback', array( $this, 'sync_video_playback' ) );
43
+
add_action( 'wp_ajax_nopriv_sync_video_playback', array( $this, 'sync_video_playback_noprev' ) );
44
+
add_action( 'wp_ajax_tutor_place_rating', array( $this, 'tutor_place_rating' ) );
45
+
add_action( 'wp_ajax_delete_tutor_review', array( $this, 'delete_tutor_review' ) );
46
+
47
+
add_action( 'wp_ajax_tutor_course_add_to_wishlist', array( $this, 'tutor_course_add_to_wishlist' ) );
48
+
add_action( 'wp_ajax_nopriv_tutor_course_add_to_wishlist', array( $this, 'tutor_course_add_to_wishlist' ) );
49
+
50
+
/**
51
+
* Ajax login
52
+
*
53
+
* @since v.1.6.3
54
+
*/
55
+
add_action( 'tutor_action_tutor_user_login', array( $this, 'process_tutor_login' ) );
56
+
57
+
/**
58
+
* Announcement
59
+
*
60
+
* @since v.1.7.9
61
+
*/
62
+
add_action( 'wp_ajax_tutor_announcement_create', array( $this, 'create_or_update_annoucement' ) );
63
+
add_action( 'wp_ajax_tutor_announcement_delete', array( $this, 'delete_annoucement' ) );
64
+
65
+
add_action( 'wp_ajax_tutor_youtube_video_duration', array( $this, 'ajax_youtube_video_duration' ) );
66
+
}
67
+
}
68
+
69
+
70
+
71
+
/**
72
+
* Update video information and data when necessary
73
+
*
74
+
* @since 1.0.0
75
+
* @return void
76
+
*/
77
+
public function sync_video_playback() {
78
+
tutor_utils()->checking_nonce();
79
+
80
+
$user_id = get_current_user_id();
81
+
$post_id = Input::post( 'post_id', 0, Input::TYPE_INT );
82
+
$duration = Input::post( 'duration' );
83
+
$current_time = Input::post( 'currentTime' );
84
+
85
+
if ( ! tutor_utils()->has_enrolled_content_access( 'lesson', $post_id ) ) {
86
+
wp_send_json_error( array( 'message' => __( 'Access Denied', 'tutor' ) ) );
87
+
exit;
88
+
}
89
+
90
+
/**
91
+
* Update posts attached video
92
+
*/
93
+
$video = tutor_utils()->get_video( $post_id );
94
+
95
+
if ( $duration ) {
96
+
$video['duration_sec'] = $duration; // Duration in sec.
97
+
$video['playtime'] = tutor_utils()->playtime_string( $duration );
98
+
$video['runtime'] = tutor_utils()->playtime_array( $duration );
99
+
}
100
+
tutor_utils()->update_video( $post_id, $video );
101
+
102
+
/**
103
+
* Sync Lesson Reading Info by Users
104
+
*/
105
+
106
+
$best_watch_time = tutor_utils()->get_lesson_reading_info( $post_id, $user_id, 'video_best_watched_time' );
107
+
if ( $best_watch_time < $current_time ) {
108
+
LessonModel::update_lesson_reading_info( $post_id, $user_id, 'video_best_watched_time', $current_time );
109
+
}
110
+
111
+
if ( Input::post( 'is_ended', false, Input::TYPE_BOOL ) ) {
112
+
LessonModel::mark_lesson_complete( $post_id );
113
+
LessonModel::update_lesson_reading_info( $post_id, $user_id, 'video_best_watched_time', 0 );
114
+
}
115
+
exit();
116
+
}
117
+
118
+
/**
119
+
* Video playback callback for noprev
120
+
*
121
+
* @since 1.0.0
122
+
* @return void
123
+
*/
124
+
public function sync_video_playback_noprev() {
125
+
}
126
+
127
+
/**
128
+
* Place rating
129
+
*
130
+
* @since 1.0.0
131
+
* @return void
132
+
*/
133
+
public function tutor_place_rating() {
134
+
tutor_utils()->checking_nonce();
135
+
136
+
$user_id = get_current_user_id();
137
+
$course_id = Input::post( 'course_id' );
138
+
$rating = Input::post( 'tutor_rating_gen_input', 0, Input::TYPE_INT );
139
+
$review = Input::post( 'review', '', Input::TYPE_TEXTAREA );
140
+
141
+
$rating <= 0 ? $rating = 1 : 0;
142
+
$rating > 5 ? $rating = 5 : 0;
143
+
144
+
$this->add_or_update_review( $user_id, $course_id, $rating, $review );
145
+
}
146
+
147
+
/**
148
+
* Add/Update rating
149
+
*
150
+
* @param int $user_id the user id.
151
+
* @param int $course_id the course id.
152
+
* @param int $rating rating star number.
153
+
* @param string $review review description.
154
+
* @param int $review_id review id needed for api update.
155
+
*
156
+
* @return void|string
157
+
*/
158
+
public function add_or_update_review( $user_id, $course_id, $rating, $review, $review_id = 0 ) {
159
+
global $wpdb;
160
+
161
+
$moderation = tutor_utils()->get_option( 'enable_course_review_moderation', false, true, true );
162
+
$user = get_userdata( $user_id );
163
+
$date = date( 'Y-m-d H:i:s', tutor_time() ); //phpcs:ignore
164
+
165
+
if ( ! tutor_is_rest() && ! tutor_utils()->has_enrolled_content_access( 'course', $course_id ) ) {
166
+
wp_send_json_error( array( 'message' => __( 'Access Denied', 'tutor' ) ) );
167
+
exit;
168
+
}
169
+
170
+
do_action( 'tutor_before_rating_placed' );
171
+
172
+
$is_edit = 0 === $review_id ? false : true;
173
+
174
+
if ( ! tutor_is_rest() ) {
175
+
$previous_rating_id = $wpdb->get_var(
176
+
$wpdb->prepare(
177
+
"SELECT comment_ID
178
+
from {$wpdb->comments}
179
+
WHERE comment_post_ID = %d AND
180
+
user_id = %d AND
181
+
comment_type = 'tutor_course_rating'
182
+
LIMIT 1;",
183
+
$course_id,
184
+
$user_id
185
+
)
186
+
);
187
+
188
+
if ( ! empty( $previous_rating_id ) ) {
189
+
$review_id = $previous_rating_id;
190
+
$is_edit = true;
191
+
}
192
+
}
193
+
194
+
if ( $is_edit ) {
195
+
$wpdb->update(
196
+
$wpdb->comments,
197
+
array(
198
+
'comment_content' => $review,
199
+
'comment_approved' => $moderation ? 'hold' : 'approved',
200
+
'comment_date' => $date,
201
+
'comment_date_gmt' => get_gmt_from_date( $date ),
202
+
),
203
+
array( 'comment_ID' => $review_id )
204
+
);
205
+
206
+
$rating_info = $wpdb->get_row(
207
+
$wpdb->prepare(
208
+
"SELECT * FROM {$wpdb->commentmeta}
209
+
WHERE comment_id = %d
210
+
AND meta_key = 'tutor_rating'; ",
211
+
$review_id
212
+
)
213
+
);
214
+
215
+
if ( $rating_info ) {
216
+
$wpdb->update(
217
+
$wpdb->commentmeta,
218
+
array( 'meta_value' => $rating ),
219
+
array(
220
+
'comment_id' => $review_id,
221
+
'meta_key' => 'tutor_rating',
222
+
)
223
+
);
224
+
} else {
225
+
$wpdb->insert(
226
+
$wpdb->commentmeta,
227
+
array(
228
+
'comment_id' => $review_id,
229
+
'meta_key' => 'tutor_rating',
230
+
'meta_value' => $rating,
231
+
)
232
+
);
233
+
}
234
+
} else {
235
+
$data = array(
236
+
'comment_post_ID' => esc_sql( $course_id ),
237
+
'comment_approved' => $moderation ? 'hold' : 'approved',
238
+
'comment_type' => 'tutor_course_rating',
239
+
'comment_date' => $date,
240
+
'comment_date_gmt' => get_gmt_from_date( $date ),
241
+
'user_id' => $user_id,
242
+
'comment_author' => $user->user_login,
243
+
'comment_agent' => 'TutorLMSPlugin',
244
+
);
245
+
if ( $review ) {
246
+
$data['comment_content'] = $review;
247
+
}
248
+
249
+
$wpdb->insert( $wpdb->comments, $data );
250
+
$comment_id = (int) $wpdb->insert_id;
251
+
$review_id = $comment_id;
252
+
253
+
if ( $comment_id ) {
254
+
$wpdb->insert(
255
+
$wpdb->commentmeta,
256
+
array(
257
+
'comment_id' => $comment_id,
258
+
'meta_key' => 'tutor_rating',
259
+
'meta_value' => $rating,
260
+
)
261
+
);
262
+
263
+
do_action( 'tutor_after_rating_placed', $comment_id );
264
+
}
265
+
}
266
+
267
+
if ( ! tutor_is_rest() ) {
268
+
wp_send_json_success(
269
+
array(
270
+
'message' => __( 'Rating placed successfully!', 'tutor' ),
271
+
'review_id' => $review_id,
272
+
)
273
+
);
274
+
} else {
275
+
return $is_edit ? 'updated' : 'created';
276
+
}
277
+
}
278
+
279
+
/**
280
+
* Delete a review
281
+
*
282
+
* @since 1.0.0
283
+
* @since 2.6.2 added params user_id.
284
+
* @param int $user_id the user id.
285
+
* @return void|bool
286
+
*/
287
+
public function delete_tutor_review( $user_id = 0 ) {
288
+
if ( ! tutor_is_rest() ) {
289
+
tutor_utils()->checking_nonce();
290
+
}
291
+
292
+
$review_id = Input::post( 'review_id' );
293
+
294
+
if ( ! tutor_utils()->can_user_manage( 'review', $review_id, tutils()->get_user_id( $user_id ) ) ) {
295
+
wp_send_json_error( array( 'message' => __( 'Permissioned Denied!', 'tutor' ) ) );
296
+
exit;
297
+
}
298
+
299
+
global $wpdb;
300
+
$wpdb->delete( $wpdb->commentmeta, array( 'comment_id' => $review_id ) );
301
+
$wpdb->delete( $wpdb->comments, array( 'comment_ID' => $review_id ) );
302
+
303
+
if ( tutor_is_rest() ) {
304
+
return true;
305
+
}
306
+
307
+
wp_send_json_success();
308
+
}
309
+
310
+
/**
311
+
* Add course in wishlist
312
+
*
313
+
* @since 1.0.0
314
+
* @return void|string
315
+
*/
316
+
public function tutor_course_add_to_wishlist() {
317
+
tutor_utils()->checking_nonce();
318
+
319
+
$is_enabled_wishlist = tutor_utils()->get_option( 'enable_wishlist', true );
320
+
if ( ! $is_enabled_wishlist ) {
321
+
wp_send_json_error( array( 'message' => __( 'Wishlist option is disabled', 'tutor' ) ) );
322
+
}
323
+
324
+
// Redirect login since only logged in user can add courses to wishlist.
325
+
if ( ! is_user_logged_in() ) {
326
+
wp_send_json_error(
327
+
array(
328
+
'redirect_to' => wp_login_url( wp_get_referer() ),
329
+
)
330
+
);
331
+
}
332
+
333
+
$user_id = get_current_user_id();
334
+
$course_id = Input::post( 'course_id', 0, Input::TYPE_INT );
335
+
336
+
$result = $this->add_or_delete_wishlist( $user_id, $course_id );
337
+
338
+
if ( tutor_is_rest() ) {
339
+
return $result;
340
+
} elseif ( 'added' === $result ) {
341
+
wp_send_json_success(
342
+
array(
343
+
'status' => 'added',
344
+
'message' => __( 'Course added to wish list', 'tutor' ),
345
+
)
346
+
);
347
+
} else {
348
+
wp_send_json_success(
349
+
array(
350
+
'status' => 'removed',
351
+
'message' => __( 'Course removed from wish list', 'tutor' ),
352
+
)
353
+
);
354
+
}
355
+
}
356
+
357
+
/**
358
+
* Add or Delete wishlist by user_id and course_id
359
+
*
360
+
* @since 2.6.2
361
+
*
362
+
* @param int $user_id the user id.
363
+
* @param int $course_id the course_id to add to the wishlist.
364
+
*
365
+
* @return string
366
+
*/
367
+
public function add_or_delete_wishlist( $user_id, $course_id ) {
368
+
global $wpdb;
369
+
370
+
$if_added_to_list = tutor_utils()->is_wishlisted( $course_id, $user_id );
371
+
372
+
$result = '';
373
+
374
+
if ( $if_added_to_list ) {
375
+
$wpdb->delete(
376
+
$wpdb->usermeta,
377
+
array(
378
+
'user_id' => $user_id,
379
+
'meta_key' => '_tutor_course_wishlist',
380
+
'meta_value' => $course_id,
381
+
)
382
+
);
383
+
384
+
$result = 'removed';
385
+
} else {
386
+
add_user_meta( $user_id, '_tutor_course_wishlist', $course_id );
387
+
388
+
$result = 'added';
389
+
}
390
+
391
+
return $result;
392
+
}
393
+
394
+
/**
395
+
* Process tutor login
396
+
*
397
+
* @since 1.6.3
398
+
*
399
+
* @since 2.1.3 Ajax removed, validation errors
400
+
* stores in session.
401
+
*
402
+
* @return void
403
+
*/
404
+
public function process_tutor_login() {
405
+
$validation_error = new \WP_Error();
406
+
407
+
/**
408
+
* Separate nonce verification added to show nonce verification
409
+
* failed message in a proper way.
410
+
*
411
+
* @since 2.1.4
412
+
*/
413
+
if ( ! wp_verify_nonce( $_POST[ tutor()->nonce ], tutor()->nonce_action ) ) { //phpcs:ignore
414
+
$validation_error->add( 401, __( 'Nonce verification failed', 'tutor' ) );
415
+
\set_transient( self::LOGIN_ERRORS_TRANSIENT_KEY, $validation_error->get_error_messages() );
416
+
return;
417
+
}
418
+
//phpcs:disable WordPress.Security.NonceVerification.Missing
419
+
420
+
/**
421
+
* No sanitization/wp_unslash needed for log & pwd since WordPress
422
+
* does itself
423
+
*
424
+
* @since 2.1.3
425
+
*
426
+
* @see https://developer.wordpress.org/reference/functions/wp_signon/
427
+
*/
428
+
$username = tutor_utils()->array_get( 'log', $_POST ); //phpcs:ignore
429
+
$password = tutor_utils()->array_get( 'pwd', $_POST ); //phpcs:ignore
430
+
$redirect_to = isset( $_POST['redirect_to'] ) ? esc_url_raw( wp_unslash( $_POST['redirect_to'] ) ) : '';
431
+
$remember = isset( $_POST['rememberme'] );
432
+
433
+
try {
434
+
$creds = array(
435
+
'user_login' => trim( $username ),
436
+
'user_password' => $password,
437
+
'remember' => $remember,
438
+
);
439
+
440
+
$validation_error = apply_filters( 'tutor_process_login_errors', $validation_error, $creds['user_login'], $creds['user_password'] );
441
+
442
+
if ( $validation_error->get_error_code() ) {
443
+
$validation_error->add(
444
+
$validation_error->get_error_code(),
445
+
$validation_error->get_error_message()
446
+
);
447
+
}
448
+
449
+
if ( empty( $creds['user_login'] ) ) {
450
+
$validation_error->add(
451
+
400,
452
+
__( 'Username is required.', 'tutor' )
453
+
);
454
+
}
455
+
456
+
// On multi-site, ensure user exists on current site, if not add them before allowing login.
457
+
if ( is_multisite() ) {
458
+
$user_data = get_user_by( is_email( $creds['user_login'] ) ? 'email' : 'login', $creds['user_login'] );
459
+
460
+
if ( $user_data && ! is_user_member_of_blog( $user_data->ID, get_current_blog_id() ) ) {
461
+
add_user_to_blog( get_current_blog_id(), $user_data->ID, 'customer' );
462
+
}
463
+
}
464
+
465
+
// Perform the login.
466
+
$user = wp_signon( apply_filters( 'tutor_login_credentials', $creds ), is_ssl() );
467
+
468
+
if ( is_wp_error( $user ) ) {
469
+
// If no error exist then add WP login error, to prevent error duplication.
470
+
if ( ! $validation_error->has_errors() ) {
471
+
$validation_error->add( 400, $user->get_error_message() );
472
+
}
473
+
} else {
474
+
do_action( 'tutor_after_login_success', $user->ID );
475
+
// Since 1.9.8 do enroll if guest attempt to enroll.
476
+
$course_enroll_attempt = Input::post( 'tutor_course_enroll_attempt' );
477
+
if ( ! empty( $course_enroll_attempt ) && is_a( $user, 'WP_User' ) ) {
478
+
do_action( 'tutor_do_enroll_after_login_if_attempt', $course_enroll_attempt, $user->ID );
479
+
}
480
+
wp_safe_redirect( $redirect_to );
481
+
exit();
482
+
}
483
+
} catch ( \Exception $e ) {
484
+
do_action( 'tutor_login_failed' );
485
+
$validation_error->add( 400, $e->getMessage() );
486
+
} finally {
487
+
// Store errors in transient data.
488
+
\set_transient( self::LOGIN_ERRORS_TRANSIENT_KEY, $validation_error->get_error_messages() );
489
+
}
490
+
}
491
+
492
+
/**
493
+
* Create/Update announcement
494
+
*
495
+
* @since 1.7.9
496
+
* @return void
497
+
*/
498
+
public function create_or_update_annoucement() {
499
+
tutor_utils()->checking_nonce();
500
+
501
+
$error = array();
502
+
$course_id = Input::post( 'tutor_announcement_course' );
503
+
$announcement_title = Input::post( 'tutor_announcement_title' );
504
+
$announcement_summary = Input::post( 'tutor_announcement_summary', '', Input::TYPE_TEXTAREA );
505
+
506
+
// Check if user can manage this announcment.
507
+
if ( ! tutor_utils()->can_user_manage( 'course', $course_id ) ) {
508
+
wp_send_json_error( array( 'message' => __( 'Access Denied', 'tutor' ) ) );
509
+
}
510
+
511
+
// Set data and sanitize it.
512
+
$form_data = array(
513
+
'post_type' => 'tutor_announcements',
514
+
'post_title' => $announcement_title,
515
+
'post_content' => $announcement_summary,
516
+
'post_parent' => $course_id,
517
+
'post_status' => 'publish',
518
+
);
519
+
520
+
if ( Input::has( 'announcement_id' ) ) {
521
+
$form_data['ID'] = Input::post( 'announcement_id' );
522
+
}
523
+
524
+
if ( ! empty( $form_data['ID'] ) ) {
525
+
if ( ! tutor_utils()->can_user_manage( 'announcement', $form_data['ID'] ) ) {
526
+
wp_send_json_error( array( 'message' => tutor_utils()->error_message() ) );
527
+
}
528
+
}
529
+
530
+
// Validation message set.
531
+
if ( empty( $form_data['post_parent'] ) ) {
532
+
$error['post_parent'] = __( 'Course name required', 'tutor' );
533
+
534
+
}
535
+
536
+
if ( empty( $form_data['post_title'] ) ) {
537
+
$error['post_title'] = __( 'Announcement title required', 'tutor' );
538
+
}
539
+
540
+
if ( empty( $form_data['post_content'] ) ) {
541
+
$error['post_content'] = __( 'Announcement summary required', 'tutor' );
542
+
543
+
}
544
+
545
+
if ( empty( $form_data['post_content'] ) ) {
546
+
$error['post_content'] = __( 'Announcement summary required', 'tutor' );
547
+
548
+
}
549
+
550
+
// If validation fails.
551
+
if ( count( $error ) > 0 ) {
552
+
wp_send_json_error(
553
+
array(
554
+
'message' => __( 'All fields required!', 'tutor' ),
555
+
'fields' => $error,
556
+
)
557
+
);
558
+
}
559
+
560
+
// Insert or update post.
561
+
$post_id = wp_insert_post( $form_data );
562
+
if ( $post_id > 0 ) {
563
+
$announcement = get_post( $post_id );
564
+
$action_type = Input::post( 'action_type' );
565
+
566
+
do_action( 'tutor_announcements/after/save', $post_id, $announcement, $action_type );
567
+
568
+
$resp_message = 'create' === $action_type ? __( 'Announcement created successfully', 'tutor' ) : __( 'Announcement updated successfully', 'tutor' );
569
+
wp_send_json_success( array( 'message' => $resp_message ) );
570
+
}
571
+
572
+
wp_send_json_error( array( 'message' => __( 'Something Went Wrong!', 'tutor' ) ) );
573
+
}
574
+
575
+
/**
576
+
* Delete announcement
577
+
*
578
+
* @since 1.7.9
579
+
* @return void
580
+
*/
581
+
public function delete_annoucement() {
582
+
tutor_utils()->checking_nonce();
583
+
584
+
$announcement_id = Input::post( 'announcement_id' );
585
+
586
+
if ( ! tutor_utils()->can_user_manage( 'announcement', $announcement_id ) ) {
587
+
wp_send_json_error( array( 'message' => __( 'Access Denied', 'tutor' ) ) );
588
+
}
589
+
590
+
$delete = wp_delete_post( $announcement_id );
591
+
if ( $delete ) {
592
+
wp_send_json_success( array( 'message' => __( 'Announcement deleted successfully', 'tutor' ) ) );
593
+
}
594
+
595
+
wp_send_json_error( array( 'message' => __( 'Announcement delete failed', 'tutor' ) ) );
596
+
}
597
+
598
+
/**
599
+
* Get youtube video duration.
600
+
*
601
+
* @since 3.0.0
602
+
*
603
+
* @return void
604
+
*/
605
+
public function ajax_youtube_video_duration() {
606
+
tutor_utils()->check_nonce();
607
+
608
+
$video_id = Input::post( 'video_id' );
609
+
if ( empty( $video_id ) ) {
610
+
$this->json_response( __( 'Video ID is required', 'tutor' ), null, HttpHelper::STATUS_BAD_REQUEST );
611
+
}
612
+
613
+
tutor_utils()->check_current_user_capability( 'edit_tutor_course' );
614
+
615
+
$api_key = tutor_utils()->get_option( 'lesson_video_duration_youtube_api_key', '' );
616
+
$url = "https://www.googleapis.com/youtube/v3/videos?id=$video_id&part=contentDetails&key=$api_key";
617
+
618
+
$request = HttpHelper::get( $url );
619
+
if ( HttpHelper::STATUS_OK === $request->get_status_code() ) {
620
+
$response = $request->get_json();
621
+
if ( isset( $response->items[0]->contentDetails->duration ) ) {
622
+
$duration = $response->items[0]->contentDetails->duration;
623
+
$this->json_response(
624
+
__( 'Fetched duration successfully', 'tutor' ),
625
+
array(
626
+
'duration' => $duration,
627
+
)
628
+
);
629
+
}
630
+
}
631
+
632
+
$this->json_response(
633
+
__( 'Failed to fetch duration', 'tutor' ),
634
+
null,
635
+
HttpHelper::STATUS_BAD_REQUEST
636
+
);
637
+
}
638
+
}
639
+