PluginProbe ʕ •ᴥ•ʔ
Tutor LMS – eLearning and online course solution / 3.9.14
Tutor LMS – eLearning and online course solution v3.9.14
3.9.14 3.9.13 3.9.12 3.9.11 trunk 1.0.0 1.0.0-alpha 1.0.1 1.0.2 1.0.3 1.0.4 1.0.5 1.0.6 1.0.7 1.0.8 1.0.9 1.1.0 1.1.1 1.2.0 1.2.1 1.2.11 1.2.12 1.2.13 1.2.20 1.3.0 1.3.1 1.3.2 1.3.3 1.3.4 1.3.5 1.3.6 1.3.7 1.3.8 1.3.9 1.4.0 1.4.1 1.4.2 1.4.3 1.4.4 1.4.5 1.4.6 1.4.7 1.4.8 1.4.9 1.5.0 1.5.1 1.5.2 1.5.3 1.5.4 1.5.5 1.5.6 1.5.7 1.5.8 1.5.9 1.6.0 1.6.1 1.6.2 1.6.3 1.6.4 1.6.5 1.6.6 1.6.7 1.6.8 1.6.9 1.7.0 1.7.1 1.7.2 1.7.3 1.7.4 1.7.5 1.7.6 1.7.7 1.7.8 1.7.9 1.8.0 1.8.1 1.8.10 1.8.2 1.8.3 1.8.4 1.8.5 1.8.6 1.8.7 1.8.8 1.8.9 1.9.0 1.9.1 1.9.10 1.9.11 1.9.12 1.9.13 1.9.14 1.9.15 1.9.16 1.9.2 1.9.3 1.9.4 1.9.5 1.9.6 1.9.7 1.9.8 1.9.9 2.0.0 2.0.1 2.0.10 2.0.2 2.0.3 2.0.4 2.0.5 2.0.6 2.0.7 2.0.8 2.0.9 2.1.0 2.1.1 2.1.10 2.1.2 2.1.3 2.1.4 2.1.5 2.1.6 2.1.7 2.1.8 2.1.9 2.2.0 2.2.1 2.2.2 2.2.3 2.2.4 2.3.0 2.4.0 2.5.0 2.6.0 2.6.1 2.6.2 2.7.0 2.7.1 2.7.2 2.7.3 2.7.4 2.7.5 2.7.6 2.7.7 3.0.0 3.0.1 3.0.2 3.1.0 3.2.0 3.2.1 3.2.2 3.2.3 3.3.0 3.3.1 3.4.0 3.4.1 3.4.2 3.5.0 3.6.0 3.6.1 3.6.2 3.6.3 3.6.4 3.7.0 3.7.1 3.7.2 3.7.3 3.7.4 3.8.0 3.8.1 3.8.2 3.8.3 3.9.0 3.9.1 3.9.10 3.9.2 3.9.3 3.9.4 3.9.5 3.9.6 3.9.7 3.9.8 3.9.9
tutor / classes / User.php
tutor / classes Last commit date
Addons.php 11 months ago Admin.php 2 months ago Ajax.php 9 months ago Announcements.php 1 year ago Assets.php 2 months ago Backend_Page_Trait.php 1 year ago BaseController.php 1 year ago Config.php 11 months ago Container.php 11 months ago Course.php 2 months ago Course_Embed.php 3 years ago Course_Filter.php 1 year ago Course_List.php 5 months ago Course_Settings_Tabs.php 1 year ago Course_Widget.php 1 year ago Custom_Validation.php 3 years ago Dashboard.php 1 year ago Earnings.php 9 months ago FormHandler.php 2 years ago Frontend.php 1 year ago Gutenberg.php 1 year ago Icon.php 8 months ago Input.php 1 year ago Instructor.php 2 months ago Instructors_List.php 2 months ago Lesson.php 2 weeks ago Options_V2.php 7 months ago Permalink.php 2 years ago Post_types.php 1 year ago Private_Course_Access.php 1 year ago Q_And_A.php 10 months ago Question_Answers_List.php 11 months ago Quiz.php 2 weeks ago QuizBuilder.php 3 days ago Quiz_Attempts_List.php 9 months ago RestAPI.php 2 years ago Reviews.php 9 months ago Rewrite_Rules.php 2 years ago Shortcode.php 9 months ago Singleton.php 1 year ago Student.php 2 months ago Students_List.php 1 year ago Taxonomies.php 1 year ago Template.php 9 months ago Theme_Compatibility.php 3 years ago Tools.php 1 year ago Tools_V2.php 3 weeks ago Tutor.php 2 months ago TutorEDD.php 1 year ago Tutor_Base.php 2 years ago Tutor_Setup.php 8 months ago Upgrader.php 9 months ago User.php 4 months ago Utils.php 3 days ago Video_Stream.php 3 years ago WhatsNew.php 9 months ago Withdraw.php 3 days ago Withdraw_Requests_List.php 11 months ago WooCommerce.php 3 days ago
User.php
582 lines
1 <?php
2 /**
3 * Manage user
4 *
5 * @package Tutor\User
6 * @author Themeum <support@themeum.com>
7 * @link https://themeum.com
8 * @since 1.0.0
9 */
10
11 namespace TUTOR;
12
13 use Tutor\Helpers\HttpHelper;
14 use Tutor\Models\UserModel;
15 use Tutor\Traits\JsonResponse;
16
17 if ( ! defined( 'ABSPATH' ) ) {
18 exit;
19 }
20
21 /**
22 * User class
23 *
24 * @since 1.0.0
25 */
26 class User {
27 use JsonResponse;
28
29 const STUDENT = 'subscriber';
30 const INSTRUCTOR = 'tutor_instructor';
31 const ADMIN = 'administrator';
32
33 /**
34 * User meta keys.
35 */
36 const REVIEW_POPUP_META = 'tutor_review_course_popup';
37 const LAST_LOGIN_META = 'tutor_last_login';
38 const TIMEZONE_META = '_tutor_timezone';
39 const PROFILE_PHOTO_META = '_tutor_profile_photo';
40 const PHONE_NUMBER_META = 'phone_number';
41 const COVER_PHOTO_META = '_tutor_cover_photo';
42 const PROFILE_BIO_META = '_tutor_profile_bio';
43 const PROFILE_JOB_TITLE_META = '_tutor_profile_job_title';
44 const TUTOR_STUDENT_META = '_is_tutor_student';
45
46 /**
47 * User model
48 *
49 * @since 3.0.0
50 *
51 * @var UserModel
52 */
53 private $model;
54
55 /**
56 * Registration notice
57 *
58 * @since 1.0.0
59 *
60 * @var boolean
61 */
62 private static $hide_registration_notice = false;
63
64 /**
65 * Register hooks
66 *
67 * @since 1.0.0
68 * @since 2.2.0 $register_hooks param added to resuse the class without hooks register.
69 *
70 * @param bool $register_hooks register hooks.
71 *
72 * @return void
73 */
74 public function __construct( $register_hooks = true ) {
75 $this->model = new UserModel();
76 if ( ! $register_hooks ) {
77 return;
78 }
79
80 add_action( 'edit_user_profile', array( $this, 'edit_user_profile' ) );
81 add_action( 'show_user_profile', array( $this, 'edit_user_profile' ), 10, 1 );
82
83 add_action( 'profile_update', array( $this, 'profile_update' ) );
84 add_action( 'set_user_role', array( $this, 'set_user_role' ), 10, 3 );
85
86 add_action( 'wp_ajax_tutor_user_photo_remove', array( $this, 'tutor_user_photo_remove' ) );
87 add_action( 'wp_ajax_tutor_user_photo_upload', array( $this, 'update_user_photo' ) );
88
89 add_action( 'admin_notices', array( $this, 'show_registration_disabled' ) );
90 add_action( 'admin_init', array( $this, 'hide_notices' ) );
91 add_action( 'wp_login', array( $this, 'update_user_last_login' ), 10, 2 );
92 add_action( 'login_form', array( $this, 'add_timezone_input' ) );
93 add_action( 'wp_login', array( $this, 'set_timezone' ), 10, 2 );
94
95 add_action( 'wp_ajax_tutor_user_list', array( $this, 'ajax_user_list' ) );
96 }
97
98 /**
99 * Get meta key name for review popup.
100 *
101 * @since 2.4.0
102 *
103 * @param int $course_id course id.
104 *
105 * @return string user meta key name.
106 */
107 public static function get_review_popup_meta( $course_id ) {
108 return self::REVIEW_POPUP_META . '_' . $course_id;
109 }
110
111 /**
112 * Check user has provided role.
113 *
114 * @since 2.2.0
115 *
116 * @param string $role role.
117 *
118 * @return boolean
119 */
120 public static function is( string $role ) {
121 return current_user_can( $role );
122 }
123
124 /**
125 * Check user has any role.
126 *
127 * @since 2.2.0
128 * @since 2.6.2 $user_id param added.
129 *
130 * @param array $roles roles.
131 * @param int $user_id user id.
132 *
133 * @return boolean
134 */
135 public static function has_any_role( array $roles, $user_id = 0 ) {
136 $user = get_userdata( tutor_utils()->get_user_id( $user_id ) );
137 if ( empty( $user->roles ) || empty( $roles ) ) {
138 return false;
139 }
140
141 foreach ( $roles as $role ) {
142 if ( in_array( $role, $user->roles, true ) ) {
143 return true;
144 break;
145 }
146 }
147
148 return false;
149 }
150
151 /**
152 * Check user is student.
153 *
154 * @since 2.2.0
155 * @since 2.6.2 $user_id param added.
156 *
157 * @param int $user_id user id.
158 *
159 * @return boolean
160 */
161 public static function is_student( $user_id = 0 ) {
162 $is_tutor_student = get_user_meta( tutor_utils()->get_user_id( $user_id ), self::TUTOR_STUDENT_META, true );
163 return $is_tutor_student ? true : false;
164 }
165
166 /**
167 * Check user is admin.
168 *
169 * @since 2.2.0
170 * @since 3.2.0 user_id param added.
171 *
172 * @param int $user_id user id.
173 *
174 * @return boolean
175 */
176 public static function is_admin( $user_id = 0 ) {
177 return user_can( tutor_utils()->get_user_id( $user_id ), self::ADMIN );
178 }
179
180 /**
181 * Check current user is instructor.
182 *
183 * @since 2.2.0
184 * @since 3.2.0 user_id param added.
185 *
186 * @param int $user_id user id.
187 * @param bool $is_approved instructor is approved or not.
188 *
189 * @return boolean
190 */
191 public static function is_instructor( $user_id = 0, $is_approved = true ) {
192 return tutils()->is_instructor( $user_id, $is_approved );
193 }
194
195 /**
196 * Check current user is only instructor as admin also has instructor role.
197 *
198 * @since 3.2.0
199 *
200 * @param int $user_id user id.
201 * @param bool $is_approved instructor is approved or not.
202 *
203 * @return boolean
204 */
205 public static function is_only_instructor( $user_id = 0, $is_approved = true ) {
206 return ! self::is_admin( $user_id ) && self::is_instructor( $user_id, $is_approved );
207 }
208
209 /**
210 * Profile layouts
211 *
212 * @since 1.0.0
213 *
214 * @var array
215 */
216 private $profile_layout = array(
217 'pp-circle',
218 'pp-rectangle',
219 'no-cp',
220 );
221
222 /**
223 * Include edit user template
224 *
225 * @since 1.0.0
226 *
227 * @param mixed $user user.
228 *
229 * @return void
230 */
231 public function edit_user_profile( $user ) {
232 include tutor()->path . 'views/metabox/user-profile-fields.php';
233 }
234
235 /**
236 * Delete existing user's photo
237 *
238 * @since 1.0.0
239 *
240 * @param int $user_id user id.
241 * @param string $type photo type.
242 *
243 * @return void
244 */
245 private function delete_existing_user_photo( $user_id, $type ) {
246 $meta_key = 'cover_photo' == $type ? '_tutor_cover_photo' : '_tutor_profile_photo';
247 $photo_id = get_user_meta( $user_id, $meta_key, true );
248 if ( is_numeric( $photo_id ) ) {
249 $attachment = get_post( $photo_id );
250 $post_author = (int) $attachment->post_author ?? 0;
251 if ( $user_id === $post_author ) {
252 wp_delete_attachment( $photo_id, true );
253 }
254 }
255 delete_user_meta( $user_id, $meta_key );
256 }
257
258 /**
259 * User photo remove
260 *
261 * @since 1.0.0
262 *
263 * @return void
264 */
265 public function tutor_user_photo_remove() {
266 tutor_utils()->checking_nonce();
267 $this->delete_existing_user_photo(
268 get_current_user_id(),
269 Input::post( 'photo_type', '' )
270 );
271 }
272
273 /**
274 * User photo update
275 *
276 * @since 1.0.0
277 *
278 * @return void
279 */
280 public function update_user_photo() {
281 tutor_utils()->checking_nonce();
282
283 $user_id = get_current_user_id();
284 $photo_type = Input::post( 'photo_type', '' );
285 $meta_key = 'cover_photo' === $photo_type ? '_tutor_cover_photo' : '_tutor_profile_photo';
286
287 /**
288 * Photo Update from profile
289 */
290 $photo = tutor_utils()->array_get( 'photo_file', $_FILES ); //phpcs:ignore -- already sanitized.
291 $photo_size = tutor_utils()->array_get( 'size', $photo );
292 $photo_type = tutor_utils()->array_get( 'type', $photo );
293
294 if ( $photo_size && strpos( $photo_type, 'image' ) !== false ) {
295 if ( ! function_exists( 'wp_handle_upload' ) ) {
296 require_once ABSPATH . 'wp-admin/includes/file.php';
297 }
298 $upload_overrides = array( 'test_form' => false );
299 $movefile = wp_handle_upload( $photo, $upload_overrides );
300
301 if ( $movefile && ! isset( $movefile['error'] ) ) {
302 $file_path = tutor_utils()->array_get( 'file', $movefile );
303 $file_url = tutor_utils()->array_get( 'url', $movefile );
304 $mime_type = '';
305 if ( file_exists( $file_path ) ) {
306 $image_info = getimagesize( $file_path );
307 $mime_type = is_array( $image_info ) && count( $image_info ) ? $image_info['mime'] : '';
308 }
309
310 $media_id = wp_insert_attachment(
311 array(
312 'guid' => $file_path,
313 'post_mime_type' => $mime_type,
314 'post_title' => preg_replace( '/\.[^.]+$/', '', basename( $file_url ) ),
315 'post_content' => '',
316 'post_status' => 'inherit',
317 ),
318 $file_path,
319 0
320 );
321
322 if ( $media_id ) {
323 // wp_generate_attachment_metadata() won't work if you do not include this file.
324 require_once ABSPATH . 'wp-admin/includes/image.php';
325
326 // Generate and save the attachment metas into the database.
327 wp_update_attachment_metadata( $media_id, wp_generate_attachment_metadata( $media_id, $file_path ) );
328
329 // Update it to user profile.
330 $this->delete_existing_user_photo( $user_id, Input::post( 'photo_type', '' ) );
331 update_user_meta( $user_id, $meta_key, $media_id );
332
333 exit( wp_json_encode( array( 'status' => 'success' ) ) );
334 }
335 }
336 }
337 }
338
339 /**
340 * Get user timezone string.
341 *
342 * @param int|object $user user id or user object. Default: current user.
343 *
344 * @return string user timezone string, fallback site timezone string.
345 */
346 public static function get_user_timezone_string( $user = 0 ) {
347 if ( is_numeric( $user ) ) {
348 $user = get_user_by( 'id', tutor_utils()->get_user_id( $user ) );
349 }
350
351 if ( ! is_a( $user, 'WP_User' ) ) {
352 return wp_timezone_string();
353 }
354
355 $timezone = get_user_meta( $user->ID, self::TIMEZONE_META, true );
356 if ( self::is_admin() || empty( $timezone ) ) {
357 $timezone = wp_timezone_string();
358 }
359
360 return $timezone;
361 }
362
363 /**
364 * Profile update
365 *
366 * @since 1.0.0
367 *
368 * @param int $user_id user id.
369 *
370 * @return void
371 */
372 public function profile_update( $user_id ) {
373 if ( 'tutor_profile_update_by_wp' !== Input::post( 'tutor_action' ) ) {
374 return;
375 }
376
377 $timezone = Input::post( 'timezone', '' );
378 $_tutor_profile_job_title = Input::post( self::PROFILE_JOB_TITLE_META, '' );
379 $_tutor_profile_bio = Input::post( self::PROFILE_BIO_META, '', Input::TYPE_KSES_POST );
380 $_tutor_profile_image = Input::post( self::PROFILE_PHOTO_META, '', Input::TYPE_KSES_POST );
381
382 update_user_meta( $user_id, self::PROFILE_JOB_TITLE_META, $_tutor_profile_job_title );
383 update_user_meta( $user_id, self::PROFILE_BIO_META, $_tutor_profile_bio );
384 update_user_meta( $user_id, self::PROFILE_PHOTO_META, $_tutor_profile_image );
385 update_user_meta( $user_id, self::TIMEZONE_META, $timezone );
386 }
387
388 /**
389 * Set user role
390 *
391 * @since 1.0.0
392 *
393 * @param int $user_id user id.
394 * @param string $role user role.
395 * @param array $old_roles old role.
396 *
397 * @return void
398 */
399 public function set_user_role( $user_id, $role, $old_roles ) {
400 $instructor_role = tutor()->instructor_role;
401
402 if ( $role === $instructor_role || in_array( $instructor_role, $old_roles ) ) {
403 tutor_utils()->add_instructor_role( $user_id );
404 }
405 }
406
407 /**
408 * Hide notices
409 *
410 * @since 1.0.0
411 *
412 * @return void
413 */
414 public function hide_notices() {
415 $hide_notice = Input::get( 'tutor-hide-notice', '' );
416 $is_register_enabled = Input::get( 'tutor-registration', '' );
417 $has_manage_cap = current_user_can( 'manage_options' );
418
419 if ( $has_manage_cap && is_admin() && 'registration' === $hide_notice ) {
420 tutor_utils()->checking_nonce( 'get' );
421
422 if ( 'enable' === $is_register_enabled ) {
423 update_option( 'users_can_register', 1 );
424 } else {
425 self::$hide_registration_notice = true;
426 setcookie( 'tutor_notice_hide_registration', 1, time() + MONTH_IN_SECONDS, tutor()->basepath );
427 }
428 }
429 }
430
431 /**
432 * Show registration disabled
433 *
434 * @since 1.0.0
435 *
436 * @return void
437 */
438 public function show_registration_disabled() {
439 if ( self::$hide_registration_notice ||
440 ( '0' !== get_option( 'users_can_register' ) ) ||
441 isset( $_COOKIE['tutor_notice_hide_registration'] ) ||
442 ! current_user_can( 'manage_options' )
443 ) {
444 return;
445 }
446
447 $hide_url = wp_nonce_url( add_query_arg( 'tutor-hide-notice', 'registration' ), tutor()->nonce_action, tutor()->nonce );
448 ?>
449 <div class="wrap tutor-user-registration-notice-wrapper">
450 <div class="tutor-user-registration-notice">
451 <div>
452 <img src="<?php echo esc_url( tutor()->url . 'assets/images/icon-info-round.svg' ); ?>"/>
453 </div>
454 <div>
455 <?php
456 echo wp_kses(
457 __( 'As membership is turned off, students and instructors will not be able to sign up. <strong>Press Enable</strong> or go to <strong>Settings > General > Membership</strong> and enable "Anyone can register".', 'tutor' ),
458 array( 'strong' => true )
459 );
460 ?>
461 </div>
462 <div>
463 <a href="<?php echo esc_url( add_query_arg( 'tutor-registration', 'enable', $hide_url ) ); ?>">
464 <?php esc_html_e( 'Enable', 'tutor' ); ?>
465 </a>
466 <hr/>
467 <a href="<?php echo esc_url( $hide_url ); ?>">
468 <?php esc_html_e( 'Dismiss', 'tutor' ); ?>
469 </a>
470 </div>
471 </div>
472 </div>
473 <?php
474 }
475
476 /**
477 * Set the user last active timestamp to now.
478 *
479 * @since 2.5.0
480 *
481 * @param string $user_login active user name.
482 * @param \WP_User $user User object data.
483 *
484 * @return void
485 */
486 public function update_user_last_login( $user_login, $user ) {
487 update_user_meta( $user->ID, self::LAST_LOGIN_META, time() );
488 }
489
490 /**
491 * Add timezone input field to login form.
492 *
493 * @since 3.0.0
494 */
495 public function add_timezone_input() {
496 ?>
497 <input type="hidden" name="timezone" value="<?php echo esc_attr( wp_timezone_string() ); ?>" />
498 <script>
499 document.addEventListener('DOMContentLoaded', function() {
500 const timezone = document.querySelector('input[name="timezone"]');
501 if ( timezone) {
502 const tz = Intl.DateTimeFormat().resolvedOptions().timeZone;
503 timezone.value = tz
504 }
505 });
506 </script>
507 <?php
508 }
509
510 /**
511 * Set user timezone if not it set.
512 *
513 * @since 3.0.0
514 *
515 * @param string $user_login active user name.
516 * @param \WP_User $user User object data.
517 *
518 * @return void
519 */
520 public function set_timezone( $user_login, $user ) {
521 if ( Input::has( 'timezone' ) ) {
522 $timezone = get_user_meta( $user->ID, self::TIMEZONE_META, true );
523 if ( empty( $timezone ) ) {
524 update_user_meta( $user->ID, self::TIMEZONE_META, Input::post( 'timezone', wp_timezone_string() ) );
525 }
526 }
527 }
528
529 /**
530 * Get user list with pagination & support
531 * search term
532 *
533 * @since 3.0.0
534 *
535 * @return void
536 */
537 public function ajax_user_list() {
538 tutor_utils()->check_nonce();
539
540 $can_access = apply_filters( 'tutor_user_list_access', current_user_can( 'manage_options' ) );
541 if ( ! $can_access ) {
542 $this->json_response( tutor_utils()->error_message( 'forbidden' ), HttpHelper::STATUS_FORBIDDEN );
543 }
544
545 $response = array(
546 'results' => array(),
547 'total_items' => 0,
548 );
549
550 $limit = Input::post( 'limit', 10, Input::TYPE_INT );
551 $offset = Input::post( 'offset', 0, Input::TYPE_INT );
552
553 $args = array(
554 'limit' => $limit,
555 'offset' => $offset,
556 );
557
558 $filter = json_decode( wp_unslash( $_POST['filter'] ?? '{}' ) );//phpcs:ignore
559 if ( ! empty( $filter ) && property_exists( $filter, 'search' ) && ! empty( $filter->search ) ) {
560 $args['search'] = '*' . Input::sanitize( $filter->search ) . '*';
561 $args['search_columns'] = array( 'user_login', 'user_email', 'user_nicename', 'display_name', 'ID' );
562 }
563
564 $user_list = $this->model->get_users_list( $args );
565
566 if ( is_object( $user_list ) ) {
567 foreach ( $user_list->get_results() as $user ) {
568 // Set user avatar.
569 $user->avatar_url = get_avatar_url( $user->ID, array( 'size' => 32 ) );
570 $response['results'][] = $user;
571 }
572
573 $response['total_items'] = $user_list->get_total();
574 }
575
576 $this->json_response(
577 __( 'User list fetched successfully!', 'tutor' ),
578 $response
579 );
580 }
581 }
582