PluginProbe ʕ •ᴥ•ʔ
Tutor LMS – eLearning and online course solution / 4.0.3
Tutor LMS – eLearning and online course solution v4.0.3
4.0.3 4.0.2 4.0.1 4.0.0 3.9.15 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 3 weeks ago Admin.php 1 week ago Ajax.php 3 weeks ago Announcements.php 3 weeks ago Assets.php 2 weeks ago Backend_Page_Trait.php 1 year ago BaseController.php 1 year ago Config.php 3 weeks ago Container.php 1 year ago Course.php 3 weeks ago Course_Embed.php 3 years ago Course_Filter.php 3 weeks ago Course_List.php 3 weeks ago Course_Settings_Tabs.php 3 weeks ago Course_Widget.php 1 year ago Custom_Validation.php 3 weeks ago Dashboard.php 3 weeks ago Earnings.php 10 months ago FormHandler.php 3 weeks ago Frontend.php 3 weeks ago Gutenberg.php 1 year ago Icon.php 1 week ago Input.php 3 weeks ago Instructor.php 3 weeks ago Instructors_List.php 3 weeks ago Lesson.php 3 weeks ago Options_V2.php 3 weeks ago Permalink.php 3 weeks ago Post_types.php 3 weeks ago Private_Course_Access.php 3 weeks ago Q_And_A.php 3 weeks ago Question_Answers_List.php 1 year ago Quiz.php 1 week ago QuizBuilder.php 3 weeks ago Quiz_Attempts_List.php 2 weeks ago RestAPI.php 2 weeks ago Reviews.php 3 weeks ago Rewrite_Rules.php 2 years ago SampleCourse.php 3 weeks ago Shortcode.php 3 weeks ago Singleton.php 1 year ago Student.php 3 weeks ago Students_List.php 1 year ago Taxonomies.php 1 year ago Template.php 3 weeks ago Theme_Compatibility.php 3 years ago Tools.php 1 year ago Tools_V2.php 1 month ago Tutor.php 3 weeks ago TutorEDD.php 3 weeks ago Tutor_Base.php 2 years ago Tutor_Setup.php 3 weeks ago Upgrader.php 3 weeks ago User.php 1 week ago UserPreference.php 3 weeks ago Utils.php 1 week ago Video_Stream.php 3 years ago WhatsNew.php 2 weeks ago Withdraw.php 3 weeks ago Withdraw_Requests_List.php 1 year ago WooCommerce.php 3 weeks ago
User.php
947 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 defined( 'ABSPATH' ) || exit;
14
15 use Tutor\Helpers\HttpHelper;
16 use Tutor\Models\UserModel;
17 use Tutor\Traits\JsonResponse;
18 use TUTOR\InstructorList;
19
20 /**
21 * User class
22 *
23 * @since 1.0.0
24 */
25 class User {
26 use JsonResponse;
27
28 const STUDENT = 'subscriber';
29 const INSTRUCTOR = 'tutor_instructor';
30 const ADMIN = 'administrator';
31
32 /**
33 * User meta keys.
34 */
35 const REVIEW_POPUP_META = 'tutor_review_course_popup';
36 const LAST_LOGIN_META = 'tutor_last_login';
37 const TIMEZONE_META = '_tutor_timezone';
38 const PROFILE_PHOTO_META = '_tutor_profile_photo';
39 const PHONE_NUMBER_META = 'phone_number';
40 const COVER_PHOTO_META = '_tutor_cover_photo';
41 const PROFILE_BIO_META = '_tutor_profile_bio';
42 const PROFILE_JOB_TITLE_META = '_tutor_profile_job_title';
43 const TUTOR_STUDENT_META = '_is_tutor_student';
44 const TOUR_COMPLETED_META = '_tutor_tour_completed';
45 const APPLICATION_SOURCE_META = '_tutor_application_source';
46 const INSTRUCTOR_APPROVAL_NOTICE_META = 'tutor_instructor_show_approval_message';
47
48 const SOURCE_INSTRUCTOR_REGISTRATION = 'instructor_registration';
49 const SOURCE_STUDENT_DASHBOARD = 'student_dashboard';
50
51 /**
52 * View as constants
53 *
54 * @since 4.0.0
55 */
56 const VIEW_AS_INSTRUCTOR = 'instructor';
57 const VIEW_AS_STUDENT = 'student';
58
59 /**
60 * User meta key for storing view as mode
61 *
62 * @since 4.0.0
63 *
64 * @var string
65 */
66 const VIEW_MODE_USER_META = 'tutor_profile_view_mode';
67
68 /**
69 * User model
70 *
71 * @since 3.0.0
72 *
73 * @var UserModel
74 */
75 private $model;
76
77 /**
78 * Registration notice
79 *
80 * @since 1.0.0
81 *
82 * @var boolean
83 */
84 private static $hide_registration_notice = false;
85
86 /**
87 * Register hooks
88 *
89 * @since 1.0.0
90 * @since 2.2.0 $register_hooks param added to resuse the class without hooks register.
91 *
92 * @param bool $register_hooks register hooks.
93 *
94 * @return void
95 */
96 public function __construct( $register_hooks = true ) {
97 $this->model = new UserModel();
98 if ( ! $register_hooks ) {
99 return;
100 }
101
102 add_action( 'edit_user_profile', array( $this, 'edit_user_profile' ) );
103 add_action( 'show_user_profile', array( $this, 'edit_user_profile' ), 10, 1 );
104
105 add_action( 'profile_update', array( $this, 'profile_update' ) );
106 add_action( 'set_user_role', array( $this, 'set_user_role' ), 10, 3 );
107
108 add_action( 'wp_ajax_tutor_user_photo_remove', array( $this, 'tutor_user_photo_remove' ) );
109 add_action( 'wp_ajax_tutor_user_photo_upload', array( $this, 'update_user_photo' ) );
110
111 add_action( 'admin_notices', array( $this, 'show_registration_disabled' ) );
112 add_action( 'admin_init', array( $this, 'hide_notices' ) );
113 add_action( 'wp_login', array( $this, 'update_user_last_login' ), 10, 2 );
114 add_action( 'login_form', array( $this, 'add_timezone_input' ) );
115 add_action( 'wp_login', array( $this, 'set_timezone' ), 10, 2 );
116
117 add_action( 'wp_ajax_tutor_user_list', array( $this, 'ajax_user_list' ) );
118 add_action( 'wp_ajax_tutor_switch_profile', array( $this, 'ajax_switch_profile' ) );
119 add_action( 'wp_ajax_tutor_complete_tour', array( $this, 'ajax_complete_tour' ) );
120
121 add_filter( 'retrieve_password_message', array( $this, 'maybe_update_password_reset_link' ), 10, 3 );
122 }
123
124 /**
125 * Get meta key name for review popup.
126 *
127 * @since 2.4.0
128 *
129 * @param int $course_id course id.
130 *
131 * @return string user meta key name.
132 */
133 public static function get_review_popup_meta( $course_id ) {
134 return self::REVIEW_POPUP_META . '_' . $course_id;
135 }
136
137 /**
138 * Check user has provided role.
139 *
140 * @since 2.2.0
141 *
142 * @param string $role role.
143 *
144 * @return boolean
145 */
146 public static function is( string $role ) {
147 return current_user_can( $role );
148 }
149
150 /**
151 * Check current user has capability.
152 *
153 * Example usage:
154 *
155 * User::can( 'edit_posts' );
156 * User::can( 'edit_post', $post->ID );
157 * User::can( 'edit_post_meta', $post->ID, $meta_key );
158 *
159 * @since 4.0.0
160 *
161 * @param string $capability capability.
162 * @param mixed ...$args args.
163 *
164 * @return boolean
165 */
166 public static function can( string $capability = 'manage_options', ...$args ) {
167 return current_user_can( $capability, ...$args );
168 }
169
170 /**
171 * Check user has any role.
172 *
173 * @since 2.2.0
174 * @since 2.6.2 $user_id param added.
175 *
176 * @param array $roles roles.
177 * @param int $user_id user id.
178 *
179 * @return boolean
180 */
181 public static function has_any_role( array $roles, $user_id = 0 ) {
182 $user = get_userdata( tutor_utils()->get_user_id( $user_id ) );
183 if ( empty( $user->roles ) || empty( $roles ) ) {
184 return false;
185 }
186
187 foreach ( $roles as $role ) {
188 if ( in_array( $role, $user->roles, true ) ) {
189 return true;
190 }
191 }
192
193 return false;
194 }
195
196 /**
197 * Check user is student.
198 *
199 * @since 2.2.0
200 * @since 2.6.2 $user_id param added.
201 *
202 * @param int $user_id user id.
203 *
204 * @return boolean
205 */
206 public static function is_student( $user_id = 0 ) {
207 $is_tutor_student = get_user_meta( tutor_utils()->get_user_id( $user_id ), self::TUTOR_STUDENT_META, true );
208 return $is_tutor_student ? true : false;
209 }
210
211 /**
212 * Check user is admin.
213 *
214 * @since 2.2.0
215 * @since 3.2.0 user_id param added.
216 *
217 * @param int $user_id user id.
218 *
219 * @return boolean
220 */
221 public static function is_admin( $user_id = 0 ) {
222 return user_can( tutor_utils()->get_user_id( $user_id ), self::ADMIN );
223 }
224
225 /**
226 * Check current user is instructor.
227 *
228 * @since 2.2.0
229 * @since 3.2.0 user_id param added.
230 *
231 * @param int $user_id user id.
232 * @param bool $is_approved instructor is approved or not.
233 *
234 * @return boolean
235 */
236 public static function is_instructor( $user_id = 0, $is_approved = true ) {
237 return tutils()->is_instructor( $user_id, $is_approved );
238 }
239
240 /**
241 * Get Tutor application source for a user.
242 *
243 * @since 4.0.0
244 *
245 * @param int $user_id user id.
246 *
247 * @return string
248 */
249 public static function get_application_source( $user_id = 0 ): string {
250 return (string) get_user_meta(
251 tutor_utils()->get_user_id( $user_id ),
252 self::APPLICATION_SOURCE_META,
253 true
254 );
255 }
256
257 /**
258 * Check if the user came through instructor registration.
259 *
260 * @since 4.0.0
261 *
262 * @param int $user_id user id.
263 *
264 * @return boolean
265 */
266 public static function used_instructor_registration( $user_id = 0 ): bool {
267 return self::SOURCE_INSTRUCTOR_REGISTRATION === self::get_application_source( $user_id );
268 }
269
270 /**
271 * Check if a user can view instructor dashboard screens.
272 *
273 * @since 4.0.0
274 *
275 * @param int $user_id user id.
276 *
277 * @return boolean
278 */
279 public static function can_view_instructor_dashboard( $user_id = 0 ): bool {
280 $user_id = tutor_utils()->get_user_id( $user_id );
281
282 if ( self::is_admin( $user_id ) || self::is_instructor( $user_id ) ) {
283 return true;
284 }
285
286 if ( ! self::used_instructor_registration( $user_id ) ) {
287 return false;
288 }
289
290 return in_array(
291 tutor_utils()->instructor_status( $user_id, false ),
292 array( Instructors_List::STATUS_PENDING, Instructors_List::STATUS_APPROVED ),
293 true
294 );
295 }
296
297 /**
298 * Check if user has a pending instructor application.
299 *
300 * @since 4.0.0
301 *
302 * @param int $user_id user id.
303 *
304 * @return boolean
305 */
306 public static function has_pending_instructor_application( $user_id = 0 ): bool {
307 return Instructors_List::STATUS_PENDING === tutor_utils()->instructor_status( $user_id, false );
308 }
309
310 /**
311 * Check current user is only instructor as admin also has instructor role.
312 *
313 * @since 3.2.0
314 *
315 * @param int $user_id user id.
316 * @param bool $is_approved instructor is approved or not.
317 *
318 * @return boolean
319 */
320 public static function is_only_instructor( $user_id = 0, $is_approved = true ) {
321 return ! self::is_admin( $user_id ) && self::is_instructor( $user_id, $is_approved );
322 }
323
324 /**
325 * Profile layouts
326 *
327 * @since 1.0.0
328 *
329 * @var array
330 */
331 private $profile_layout = array(
332 'pp-circle',
333 'pp-rectangle',
334 'no-cp',
335 );
336
337 /**
338 * Include edit user template
339 *
340 * @since 1.0.0
341 *
342 * @param mixed $user user.
343 *
344 * @return void
345 */
346 public function edit_user_profile( $user ) {
347 include tutor()->path . 'views/metabox/user-profile-fields.php';
348 }
349
350 /**
351 * Delete existing user's photo
352 *
353 * @since 1.0.0
354 *
355 * @param int $user_id user id.
356 * @param string $type photo type.
357 *
358 * @return void
359 */
360 private function delete_existing_user_photo( $user_id, $type ) {
361 $meta_key = 'cover_photo' == $type ? '_tutor_cover_photo' : '_tutor_profile_photo';
362 $photo_id = get_user_meta( $user_id, $meta_key, true );
363 if ( is_numeric( $photo_id ) ) {
364 $attachment = get_post( $photo_id );
365 $post_author = (int) $attachment->post_author ?? 0;
366 if ( $user_id === $post_author ) {
367 wp_delete_attachment( $photo_id, true );
368 }
369 }
370 delete_user_meta( $user_id, $meta_key );
371 }
372
373 /**
374 * User photo remove
375 *
376 * @since 1.0.0
377 *
378 * @return void
379 */
380 public function tutor_user_photo_remove() {
381 tutor_utils()->checking_nonce();
382 $this->delete_existing_user_photo(
383 get_current_user_id(),
384 Input::post( 'photo_type', '' )
385 );
386 }
387
388 /**
389 * User photo update
390 *
391 * @since 1.0.0
392 *
393 * @return void
394 */
395 public function update_user_photo() {
396 tutor_utils()->checking_nonce();
397
398 $user_id = get_current_user_id();
399 $photo_type = Input::post( 'photo_type', '' );
400 $meta_key = 'cover_photo' === $photo_type ? '_tutor_cover_photo' : '_tutor_profile_photo';
401
402 /**
403 * Photo Update from profile
404 */
405 $photo = tutor_utils()->array_get( 'photo_file', $_FILES ); //phpcs:ignore -- already sanitized.
406 $photo_size = tutor_utils()->array_get( 'size', $photo );
407 $photo_type = tutor_utils()->array_get( 'type', $photo );
408
409 if ( $photo_size && strpos( $photo_type, 'image' ) !== false ) {
410 if ( ! function_exists( 'wp_handle_upload' ) ) {
411 require_once ABSPATH . 'wp-admin/includes/file.php';
412 }
413 $upload_overrides = array( 'test_form' => false );
414 $movefile = wp_handle_upload( $photo, $upload_overrides );
415
416 if ( $movefile && ! isset( $movefile['error'] ) ) {
417 $file_path = tutor_utils()->array_get( 'file', $movefile );
418 $file_url = tutor_utils()->array_get( 'url', $movefile );
419 $mime_type = '';
420 if ( file_exists( $file_path ) ) {
421 $image_info = getimagesize( $file_path );
422 $mime_type = is_array( $image_info ) && count( $image_info ) ? $image_info['mime'] : '';
423 }
424
425 $media_id = wp_insert_attachment(
426 array(
427 'guid' => $file_path,
428 'post_mime_type' => $mime_type,
429 'post_title' => preg_replace( '/\.[^.]+$/', '', basename( $file_url ) ),
430 'post_content' => '',
431 'post_status' => 'inherit',
432 ),
433 $file_path,
434 0
435 );
436
437 if ( $media_id ) {
438 // wp_generate_attachment_metadata() won't work if you do not include this file.
439 require_once ABSPATH . 'wp-admin/includes/image.php';
440
441 // Generate and save the attachment metas into the database.
442 wp_update_attachment_metadata( $media_id, wp_generate_attachment_metadata( $media_id, $file_path ) );
443
444 // Update it to user profile.
445 $this->delete_existing_user_photo( $user_id, Input::post( 'photo_type', '' ) );
446 update_user_meta( $user_id, $meta_key, $media_id );
447
448 exit( wp_json_encode( array( 'status' => 'success' ) ) );
449 }
450 }
451 }
452 }
453
454 /**
455 * Get user timezone string.
456 *
457 * @param int|object $user user id or user object. Default: current user.
458 *
459 * @return string user timezone string, fallback site timezone string.
460 */
461 public static function get_user_timezone_string( $user = 0 ) {
462 if ( is_numeric( $user ) ) {
463 $user = get_user_by( 'id', tutor_utils()->get_user_id( $user ) );
464 }
465
466 if ( ! is_a( $user, 'WP_User' ) ) {
467 return wp_timezone_string();
468 }
469
470 $timezone = get_user_meta( $user->ID, self::TIMEZONE_META, true );
471 if ( self::is_admin() || empty( $timezone ) ) {
472 $timezone = wp_timezone_string();
473 }
474
475 return $timezone;
476 }
477
478 /**
479 * Profile update
480 *
481 * @since 1.0.0
482 *
483 * @param int $user_id user id.
484 *
485 * @return void
486 */
487 public function profile_update( $user_id ) {
488 if ( 'tutor_profile_update_by_wp' !== Input::post( 'tutor_action' ) ) {
489 return;
490 }
491
492 $timezone = Input::post( 'timezone', '' );
493 $_tutor_profile_job_title = Input::post( self::PROFILE_JOB_TITLE_META, '' );
494 $_tutor_profile_bio = Input::post( self::PROFILE_BIO_META, '', Input::TYPE_KSES_POST );
495 $_tutor_profile_image = Input::post( self::PROFILE_PHOTO_META, '', Input::TYPE_KSES_POST );
496
497 update_user_meta( $user_id, self::PROFILE_JOB_TITLE_META, $_tutor_profile_job_title );
498 update_user_meta( $user_id, self::PROFILE_BIO_META, $_tutor_profile_bio );
499 update_user_meta( $user_id, self::PROFILE_PHOTO_META, $_tutor_profile_image );
500 update_user_meta( $user_id, self::TIMEZONE_META, $timezone );
501 }
502
503 /**
504 * Set user role
505 *
506 * @since 1.0.0
507 *
508 * @param int $user_id user id.
509 * @param string $role user role.
510 * @param array $old_roles old role.
511 *
512 * @return void
513 */
514 public function set_user_role( $user_id, $role, $old_roles ) {
515 $instructor_role = tutor()->instructor_role;
516
517 if ( $role === $instructor_role || in_array( $instructor_role, $old_roles ) ) {
518 tutor_utils()->add_instructor_role( $user_id );
519 }
520 }
521
522 /**
523 * Hide notices
524 *
525 * @since 1.0.0
526 *
527 * @return void
528 */
529 public function hide_notices() {
530 $hide_notice = Input::get( 'tutor-hide-notice', '' );
531 $is_register_enabled = Input::get( 'tutor-registration', '' );
532 $has_manage_cap = current_user_can( 'manage_options' );
533
534 if ( $has_manage_cap && is_admin() && 'registration' === $hide_notice ) {
535 tutor_utils()->checking_nonce( 'get' );
536
537 if ( 'enable' === $is_register_enabled ) {
538 update_option( 'users_can_register', 1 );
539 } else {
540 self::$hide_registration_notice = true;
541 setcookie( 'tutor_notice_hide_registration', 1, time() + MONTH_IN_SECONDS, tutor()->basepath );
542 }
543 }
544 }
545
546 /**
547 * Show registration disabled
548 *
549 * @since 1.0.0
550 *
551 * @return void
552 */
553 public function show_registration_disabled() {
554 if ( self::$hide_registration_notice ||
555 ( '0' !== get_option( 'users_can_register' ) ) ||
556 isset( $_COOKIE['tutor_notice_hide_registration'] ) ||
557 ! current_user_can( 'manage_options' )
558 ) {
559 return;
560 }
561
562 $hide_url = wp_nonce_url( add_query_arg( 'tutor-hide-notice', 'registration' ), tutor()->nonce_action, tutor()->nonce );
563 ?>
564 <div class="wrap tutor-user-registration-notice-wrapper">
565 <div class="tutor-user-registration-notice">
566 <div>
567 <img src="<?php echo esc_url( tutor()->url . 'assets/images/icon-info-round.svg' ); ?>"/>
568 </div>
569 <div>
570 <?php
571 echo wp_kses(
572 __( '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' ),
573 array( 'strong' => true )
574 );
575 ?>
576 </div>
577 <div>
578 <a href="<?php echo esc_url( add_query_arg( 'tutor-registration', 'enable', $hide_url ) ); ?>">
579 <?php esc_html_e( 'Enable', 'tutor' ); ?>
580 </a>
581 <hr/>
582 <a href="<?php echo esc_url( $hide_url ); ?>">
583 <?php esc_html_e( 'Dismiss', 'tutor' ); ?>
584 </a>
585 </div>
586 </div>
587 </div>
588 <?php
589 }
590
591 /**
592 * Set the user last active timestamp to now.
593 *
594 * @since 2.5.0
595 *
596 * @param string $user_login active user name.
597 * @param \WP_User $user User object data.
598 *
599 * @return void
600 */
601 public function update_user_last_login( $user_login, $user ) {
602 update_user_meta( $user->ID, self::LAST_LOGIN_META, time() );
603 }
604
605 /**
606 * Add timezone input field to login form.
607 *
608 * @since 3.0.0
609 */
610 public function add_timezone_input() {
611 ?>
612 <input type="hidden" name="timezone" value="<?php echo esc_attr( wp_timezone_string() ); ?>" />
613 <script>
614 document.addEventListener('DOMContentLoaded', function() {
615 const timezone = document.querySelector('input[name="timezone"]');
616 if ( timezone) {
617 const tz = Intl.DateTimeFormat().resolvedOptions().timeZone;
618 timezone.value = tz
619 }
620 });
621 </script>
622 <?php
623 }
624
625 /**
626 * Set user timezone if not it set.
627 *
628 * @since 3.0.0
629 *
630 * @param string $user_login active user name.
631 * @param \WP_User $user User object data.
632 *
633 * @return void
634 */
635 public function set_timezone( $user_login, $user ) {
636 if ( Input::has( 'timezone' ) ) {
637 $timezone = get_user_meta( $user->ID, self::TIMEZONE_META, true );
638 if ( empty( $timezone ) ) {
639 update_user_meta( $user->ID, self::TIMEZONE_META, Input::post( 'timezone', wp_timezone_string() ) );
640 }
641 }
642 }
643
644 /**
645 * Get profile settings data
646 *
647 * @since 4.0.0
648 *
649 * @param int $user_id user id.
650 *
651 * @return array
652 */
653 public static function get_profile_settings_data( $user_id = 0 ) {
654 $user_id = tutor_utils()->get_user_id( $user_id );
655 $user = get_userdata( $user_id );
656
657 // Prepare profile pic.
658 $profile_placeholder = apply_filters( 'tutor_login_default_avatar', tutor()->url . 'assets/images/profile-photo.png' );
659 $profile_photo_src = $profile_placeholder;
660 $profile_photo_id = get_user_meta( $user->ID, self::PROFILE_PHOTO_META, true );
661
662 if ( $profile_photo_id ) {
663 $url = wp_get_attachment_image_url( $profile_photo_id, 'full' );
664 if ( ! empty( $url ) ) {
665 $profile_photo_src = $url;
666 }
667 }
668
669 $timezone = self::get_user_timezone_string( $user );
670
671 // Prepare cover photo.
672 $cover_placeholder = tutor()->url . 'assets/images/cover-photo.webp';
673 $cover_photo_src = $cover_placeholder;
674 $cover_photo_id = get_user_meta( $user->ID, self::COVER_PHOTO_META, true );
675
676 if ( $cover_photo_id ) {
677 $url = wp_get_attachment_image_url( $cover_photo_id, 'full' );
678 if ( ! empty( $url ) ) {
679 $cover_photo_src = $url;
680 }
681 }
682
683 // Prepare display name.
684 $public_display = array();
685 $public_display['display_nickname'] = $user->nickname;
686 $public_display['display_username'] = $user->user_login;
687
688 if ( ! empty( $user->first_name ) ) {
689 $public_display['display_firstname'] = $user->first_name;
690 }
691
692 if ( ! empty( $user->last_name ) ) {
693 $public_display['display_lastname'] = $user->last_name;
694 }
695
696 if ( ! empty( $user->first_name ) && ! empty( $user->last_name ) ) {
697 $public_display['display_firstlast'] = $user->first_name . ' ' . $user->last_name;
698 $public_display['display_lastfirst'] = $user->last_name . ' ' . $user->first_name;
699 }
700
701 if ( ! in_array( $user->display_name, $public_display, true ) ) { // Only add this if it isn't duplicated elsewhere.
702 $public_display = array( 'display_displayname' => $user->display_name ) + $public_display;
703 }
704
705 $public_display = array_map( 'trim', $public_display );
706 $public_display = array_unique( $public_display );
707 $max_filesize = floatval( ini_get( 'upload_max_filesize' ) ) * ( 1024 * 1024 );
708
709 $profile_bio = wp_kses_post( get_user_meta( $user->ID, self::PROFILE_BIO_META, true ) );
710 $job_title = get_user_meta( $user->ID, self::PROFILE_JOB_TITLE_META, true );
711 $phone = get_user_meta( $user->ID, self::PHONE_NUMBER_META, true );
712
713 return array(
714 'user' => $user,
715 'profile_placeholder' => $profile_placeholder,
716 'profile_photo_src' => $profile_photo_src,
717 'profile_photo_id' => $profile_photo_id,
718 'cover_placeholder' => $cover_placeholder,
719 'cover_photo_src' => $cover_photo_src,
720 'cover_photo_id' => $cover_photo_id,
721 'timezone' => $timezone,
722 'public_display' => $public_display,
723 'max_filesize' => $max_filesize,
724 'profile_bio' => $profile_bio,
725 'job_title' => $job_title,
726 'phone_number' => $phone,
727 );
728 }
729
730 /**
731 * Get user list with pagination & support
732 * search term
733 *
734 * @since 3.0.0
735 *
736 * @return void
737 */
738 public function ajax_user_list() {
739 tutor_utils()->check_nonce();
740
741 $can_access = apply_filters( 'tutor_user_list_access', current_user_can( 'manage_options' ) );
742 if ( ! $can_access ) {
743 $this->json_response( tutor_utils()->error_message( 'forbidden' ), HttpHelper::STATUS_FORBIDDEN );
744 }
745
746 $response = array(
747 'results' => array(),
748 'total_items' => 0,
749 );
750
751 $limit = Input::post( 'limit', 10, Input::TYPE_INT );
752 $offset = Input::post( 'offset', 0, Input::TYPE_INT );
753
754 $args = array(
755 'limit' => $limit,
756 'offset' => $offset,
757 );
758
759 $filter = json_decode( wp_unslash( $_POST['filter'] ?? '{}' ) );//phpcs:ignore
760 if ( ! empty( $filter ) && property_exists( $filter, 'search' ) && ! empty( $filter->search ) ) {
761 $args['search'] = '*' . Input::sanitize( $filter->search ) . '*';
762 $args['search_columns'] = array( 'user_login', 'user_email', 'user_nicename', 'display_name', 'ID' );
763 }
764
765 $user_list = $this->model->get_users_list( $args );
766
767 if ( is_object( $user_list ) ) {
768 foreach ( $user_list->get_results() as $user ) {
769 // Set user avatar.
770 $user->avatar_url = get_avatar_url( $user->ID, array( 'size' => 32 ) );
771 $response['results'][] = $user;
772 }
773
774 $response['total_items'] = $user_list->get_total();
775 }
776
777 $this->json_response(
778 __( 'User list fetched successfully!', 'tutor' ),
779 $response
780 );
781 }
782
783 /**
784 * Switch user profile ajax-handler
785 *
786 * @since 4.0.0
787 *
788 * @return void send wp_json response
789 */
790 public function ajax_switch_profile() {
791 tutor_utils()->checking_nonce();
792
793 $user_id = get_current_user_id();
794 if ( ! self::can_switch_mode( $user_id ) ) {
795 $this->json_response(
796 tutor_utils()->error_message(),
797 null,
798 HttpHelper::STATUS_UNAUTHORIZED
799 );
800 }
801
802 $switch_mode = '';
803 $current_mode = Input::post( 'current_mode' );
804
805 if ( ! in_array( $current_mode, array( self::VIEW_AS_INSTRUCTOR, self::VIEW_AS_STUDENT ), true ) ) {
806 $this->response_bad_request( tutor_utils()->error_message( 'invalid_req' ) );
807 }
808
809 if ( self::VIEW_AS_INSTRUCTOR === $current_mode ) {
810 $switch_mode = self::VIEW_AS_STUDENT;
811 } elseif ( self::VIEW_AS_STUDENT === $current_mode ) {
812 $switch_mode = self::VIEW_AS_INSTRUCTOR;
813 }
814
815 update_user_meta( $user_id, self::VIEW_MODE_USER_META, $switch_mode );
816
817 // translators:%s for switching mode.
818 $this->response_success( sprintf( __( 'Profile switched to %s!', 'tutor' ), $switch_mode ) );
819 }
820
821 /**
822 * Get current view mode STUDENT/INSTRUCTOR
823 *
824 * @since 4.0.0
825 *
826 * @return string
827 */
828 public static function get_current_view_mode(): string {
829 $user_id = get_current_user_id();
830 $can_switch = self::can_switch_mode( $user_id );
831 $default_mode = $can_switch ? self::VIEW_AS_INSTRUCTOR : self::VIEW_AS_STUDENT;
832 $current_mode = get_user_meta( $user_id, self::VIEW_MODE_USER_META, true );
833
834 if ( $can_switch && in_array( $current_mode, array( self::VIEW_AS_INSTRUCTOR, self::VIEW_AS_STUDENT ), true ) ) {
835 return $current_mode;
836 }
837
838 if ( self::used_instructor_registration( $user_id ) ) {
839 $instructor_status = tutor_utils()->instructor_status( $user_id, false );
840 if ( in_array( $instructor_status, array( Instructors_List::STATUS_PENDING, Instructors_List::STATUS_APPROVED ), true ) ) {
841 return self::VIEW_AS_INSTRUCTOR;
842 }
843 }
844
845 return $default_mode;
846 }
847
848 /**
849 * Check if the user is in instructor view
850 *
851 * @since 4.0.0
852 *
853 * @return bool
854 */
855 public static function is_instructor_view(): bool {
856 return self::VIEW_AS_INSTRUCTOR === self::get_current_view_mode();
857 }
858
859 /**
860 * Check if the user is in student view
861 *
862 * @since 4.0.0
863 *
864 * @return bool
865 */
866 public static function is_student_view(): bool {
867 return self::VIEW_AS_STUDENT === self::get_current_view_mode();
868 }
869
870 /**
871 * Check if the user can switch between learner and instructor dashboard modes.
872 *
873 * @since 4.0.0
874 *
875 * @param int $user_id User ID.
876 *
877 * @return bool
878 */
879 public static function can_switch_mode( int $user_id = 0 ): bool {
880 return self::is_admin( $user_id ) || self::is_instructor( $user_id );
881 }
882
883 /**
884 * Mark dashboard tour as completed for the current user.
885 *
886 * @since 4.0.0
887 *
888 * @return void JSON response.
889 */
890 public function ajax_complete_tour() {
891 tutor_utils()->check_nonce();
892
893 update_user_meta( get_current_user_id(), self::TOUR_COMPLETED_META, true );
894
895 $this->json_response( __( 'Tour completed', 'tutor' ) );
896 }
897
898 /**
899 * If user don't have pro and using tutor login then change the password
900 * reset email link
901 *
902 * @since 4.0.2
903 *
904 * @param string $message Email message.
905 * @param string $key Reset key.
906 * @param string $user_login User login name.
907 *
908 * @return string
909 */
910 public function maybe_update_password_reset_link( $message, $key, $user_login ) {
911 if ( tutor()->has_pro && tutor_utils()->is_addon_enabled( 'tutor-email' ) ) {
912 return $message;
913 }
914
915 $is_tutor_login_enabled = tutor_utils()->get_option( 'enable_tutor_native_login', false );
916 if ( ! $is_tutor_login_enabled ) {
917 return $message;
918 }
919
920 $default_url = add_query_arg(
921 array(
922 'login' => $user_login,
923 'key' => $key,
924 'action' => 'rp',
925 ),
926 network_site_url( 'wp-login.php' )
927 );
928
929 $user = get_user_by( 'login', $user_login );
930 if ( ! $user ) {
931 return $message;
932 }
933
934 $tutor_reset_url = add_query_arg(
935 array(
936 'reset_key' => $key,
937 'user_id' => $user->ID,
938 ),
939 tutor_utils()->tutor_dashboard_url( 'retrieve-password' )
940 );
941
942 $message = str_replace( $default_url, $tutor_reset_url, $message );
943
944 return $message;
945 }
946 }
947