PluginProbe ʕ •ᴥ•ʔ
Tutor LMS – eLearning and online course solution / 3.9.6
Tutor LMS – eLearning and online course solution v3.9.6
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 8 months ago Ajax.php 9 months ago Announcements.php 1 year ago Assets.php 7 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 5 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 1 year ago Instructors_List.php 11 months ago Lesson.php 8 months 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 5 months ago QuizBuilder.php 11 months 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 1 year 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 1 year ago Tutor.php 7 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 5 months ago Utils.php 5 months ago Video_Stream.php 3 years ago WhatsNew.php 9 months ago Withdraw.php 1 year ago Withdraw_Requests_List.php 11 months ago WooCommerce.php 7 months ago
User.php
587 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 wp_delete_attachment( $photo_id, true );
250 }
251 delete_user_meta( $user_id, $meta_key );
252 }
253
254 /**
255 * User photo remove
256 *
257 * @since 1.0.0
258 *
259 * @return void
260 */
261 public function tutor_user_photo_remove() {
262 tutor_utils()->checking_nonce();
263 $this->delete_existing_user_photo(
264 get_current_user_id(),
265 Input::post( 'photo_type', '' )
266 );
267 }
268
269 /**
270 * User photo update
271 *
272 * @since 1.0.0
273 *
274 * @return void
275 */
276 public function update_user_photo() {
277 tutor_utils()->checking_nonce();
278
279 $user_id = get_current_user_id();
280 $photo_type = Input::post( 'photo_type', '' );
281 $meta_key = 'cover_photo' === $photo_type ? '_tutor_cover_photo' : '_tutor_profile_photo';
282
283 /**
284 * Photo Update from profile
285 */
286 $photo = tutor_utils()->array_get( 'photo_file', $_FILES ); //phpcs:ignore -- already sanitized.
287 $photo_size = tutor_utils()->array_get( 'size', $photo );
288 $photo_type = tutor_utils()->array_get( 'type', $photo );
289
290 if ( $photo_size && strpos( $photo_type, 'image' ) !== false ) {
291 if ( ! function_exists( 'wp_handle_upload' ) ) {
292 require_once ABSPATH . 'wp-admin/includes/file.php';
293 }
294 $upload_overrides = array( 'test_form' => false );
295 $movefile = wp_handle_upload( $photo, $upload_overrides );
296
297 if ( $movefile && ! isset( $movefile['error'] ) ) {
298 $file_path = tutor_utils()->array_get( 'file', $movefile );
299 $file_url = tutor_utils()->array_get( 'url', $movefile );
300 $mime_type = '';
301 if ( file_exists( $file_path ) ) {
302 $image_info = getimagesize( $file_path );
303 $mime_type = is_array( $image_info ) && count( $image_info ) ? $image_info['mime'] : '';
304 }
305
306 $media_id = wp_insert_attachment(
307 array(
308 'guid' => $file_path,
309 'post_mime_type' => $mime_type,
310 'post_title' => preg_replace( '/\.[^.]+$/', '', basename( $file_url ) ),
311 'post_content' => '',
312 'post_status' => 'inherit',
313 ),
314 $file_path,
315 0
316 );
317
318 if ( $media_id ) {
319 // wp_generate_attachment_metadata() won't work if you do not include this file.
320 require_once ABSPATH . 'wp-admin/includes/image.php';
321
322 // Generate and save the attachment metas into the database.
323 wp_update_attachment_metadata( $media_id, wp_generate_attachment_metadata( $media_id, $file_path ) );
324
325 // Update it to user profile.
326 $this->delete_existing_user_photo( $user_id, Input::post( 'photo_type', '' ) );
327 update_user_meta( $user_id, $meta_key, $media_id );
328
329 exit( wp_json_encode( array( 'status' => 'success' ) ) );
330 }
331 }
332 }
333 }
334
335 /**
336 * Get user timezone string.
337 *
338 * @param int|object $user user id or user object. Default: current user.
339 *
340 * @return string user timezone string, fallback site timezone string.
341 */
342 public static function get_user_timezone_string( $user = 0 ) {
343 if ( is_numeric( $user ) ) {
344 $user = get_user_by( 'id', tutor_utils()->get_user_id( $user ) );
345 }
346
347 if ( ! is_a( $user, 'WP_User' ) ) {
348 return wp_timezone_string();
349 }
350
351 $timezone = get_user_meta( $user->ID, self::TIMEZONE_META, true );
352 if ( self::is_admin() || empty( $timezone ) ) {
353 $timezone = wp_timezone_string();
354 }
355
356 return $timezone;
357 }
358
359 /**
360 * Profile update
361 *
362 * @since 1.0.0
363 *
364 * @param int $user_id user id.
365 *
366 * @return void
367 */
368 public function profile_update( $user_id ) {
369 if ( 'tutor_profile_update_by_wp' !== Input::post( 'tutor_action' ) ) {
370 return;
371 }
372
373 $timezone = Input::post( 'timezone', '' );
374 $_tutor_profile_job_title = Input::post( self::PROFILE_JOB_TITLE_META, '' );
375 $_tutor_profile_bio = Input::post( self::PROFILE_BIO_META, '', Input::TYPE_KSES_POST );
376 $_tutor_profile_image = Input::post( self::PROFILE_PHOTO_META, '', Input::TYPE_KSES_POST );
377
378 // Check if the image uploaded is by the same user.
379 if ( is_numeric( $_tutor_profile_image ) ) {
380 $attachment = get_post( $_tutor_profile_image );
381 $author_id = (int) $attachment->post_author ?? 0;
382 if ( 'attachment' === $attachment->post_type && $user_id !== $author_id ) {
383 return;
384 }
385 }
386
387 update_user_meta( $user_id, self::PROFILE_JOB_TITLE_META, $_tutor_profile_job_title );
388 update_user_meta( $user_id, self::PROFILE_BIO_META, $_tutor_profile_bio );
389 update_user_meta( $user_id, self::PROFILE_PHOTO_META, $_tutor_profile_image );
390 update_user_meta( $user_id, self::TIMEZONE_META, $timezone );
391 }
392
393 /**
394 * Set user role
395 *
396 * @since 1.0.0
397 *
398 * @param int $user_id user id.
399 * @param string $role user role.
400 * @param array $old_roles old role.
401 *
402 * @return void
403 */
404 public function set_user_role( $user_id, $role, $old_roles ) {
405 $instructor_role = tutor()->instructor_role;
406
407 if ( $role === $instructor_role || in_array( $instructor_role, $old_roles ) ) {
408 tutor_utils()->add_instructor_role( $user_id );
409 }
410 }
411
412 /**
413 * Hide notices
414 *
415 * @since 1.0.0
416 *
417 * @return void
418 */
419 public function hide_notices() {
420 $hide_notice = Input::get( 'tutor-hide-notice', '' );
421 $is_register_enabled = Input::get( 'tutor-registration', '' );
422 $has_manage_cap = current_user_can( 'manage_options' );
423
424 if ( $has_manage_cap && is_admin() && 'registration' === $hide_notice ) {
425 tutor_utils()->checking_nonce( 'get' );
426
427 if ( 'enable' === $is_register_enabled ) {
428 update_option( 'users_can_register', 1 );
429 } else {
430 self::$hide_registration_notice = true;
431 setcookie( 'tutor_notice_hide_registration', 1, time() + MONTH_IN_SECONDS, tutor()->basepath );
432 }
433 }
434 }
435
436 /**
437 * Show registration disabled
438 *
439 * @since 1.0.0
440 *
441 * @return void
442 */
443 public function show_registration_disabled() {
444 if ( self::$hide_registration_notice ||
445 ( '0' !== get_option( 'users_can_register' ) ) ||
446 isset( $_COOKIE['tutor_notice_hide_registration'] ) ||
447 ! current_user_can( 'manage_options' )
448 ) {
449 return;
450 }
451
452 $hide_url = wp_nonce_url( add_query_arg( 'tutor-hide-notice', 'registration' ), tutor()->nonce_action, tutor()->nonce );
453 ?>
454 <div class="wrap tutor-user-registration-notice-wrapper">
455 <div class="tutor-user-registration-notice">
456 <div>
457 <img src="<?php echo esc_url( tutor()->url . 'assets/images/icon-info-round.svg' ); ?>"/>
458 </div>
459 <div>
460 <?php
461 echo wp_kses(
462 __( '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' ),
463 array( 'strong' => true )
464 );
465 ?>
466 </div>
467 <div>
468 <a href="<?php echo esc_url( add_query_arg( 'tutor-registration', 'enable', $hide_url ) ); ?>">
469 <?php esc_html_e( 'Enable', 'tutor' ); ?>
470 </a>
471 <hr/>
472 <a href="<?php echo esc_url( $hide_url ); ?>">
473 <?php esc_html_e( 'Dismiss', 'tutor' ); ?>
474 </a>
475 </div>
476 </div>
477 </div>
478 <?php
479 }
480
481 /**
482 * Set the user last active timestamp to now.
483 *
484 * @since 2.5.0
485 *
486 * @param string $user_login active user name.
487 * @param \WP_User $user User object data.
488 *
489 * @return void
490 */
491 public function update_user_last_login( $user_login, $user ) {
492 update_user_meta( $user->ID, self::LAST_LOGIN_META, time() );
493 }
494
495 /**
496 * Add timezone input field to login form.
497 *
498 * @since 3.0.0
499 */
500 public function add_timezone_input() {
501 ?>
502 <input type="hidden" name="timezone" value="<?php echo esc_attr( wp_timezone_string() ); ?>" />
503 <script>
504 document.addEventListener('DOMContentLoaded', function() {
505 const timezone = document.querySelector('input[name="timezone"]');
506 if ( timezone) {
507 const tz = Intl.DateTimeFormat().resolvedOptions().timeZone;
508 timezone.value = tz
509 }
510 });
511 </script>
512 <?php
513 }
514
515 /**
516 * Set user timezone if not it set.
517 *
518 * @since 3.0.0
519 *
520 * @param string $user_login active user name.
521 * @param \WP_User $user User object data.
522 *
523 * @return void
524 */
525 public function set_timezone( $user_login, $user ) {
526 if ( Input::has( 'timezone' ) ) {
527 $timezone = get_user_meta( $user->ID, self::TIMEZONE_META, true );
528 if ( empty( $timezone ) ) {
529 update_user_meta( $user->ID, self::TIMEZONE_META, Input::post( 'timezone', wp_timezone_string() ) );
530 }
531 }
532 }
533
534 /**
535 * Get user list with pagination & support
536 * search term
537 *
538 * @since 3.0.0
539 *
540 * @return void
541 */
542 public function ajax_user_list() {
543 tutor_utils()->check_nonce();
544
545 $can_access = apply_filters( 'tutor_user_list_access', current_user_can( 'manage_options' ) );
546 if ( ! $can_access ) {
547 $this->json_response( tutor_utils()->error_message( 'forbidden' ), HttpHelper::STATUS_FORBIDDEN );
548 }
549
550 $response = array(
551 'results' => array(),
552 'total_items' => 0,
553 );
554
555 $limit = Input::post( 'limit', 10, Input::TYPE_INT );
556 $offset = Input::post( 'offset', 0, Input::TYPE_INT );
557
558 $args = array(
559 'limit' => $limit,
560 'offset' => $offset,
561 );
562
563 $filter = json_decode( wp_unslash( $_POST['filter'] ?? '{}' ) );//phpcs:ignore
564 if ( ! empty( $filter ) && property_exists( $filter, 'search' ) && ! empty( $filter->search ) ) {
565 $args['search'] = '*' . Input::sanitize( $filter->search ) . '*';
566 $args['search_columns'] = array( 'user_login', 'user_email', 'user_nicename', 'display_name', 'ID' );
567 }
568
569 $user_list = $this->model->get_users_list( $args );
570
571 if ( is_object( $user_list ) ) {
572 foreach ( $user_list->get_results() as $user ) {
573 // Set user avatar.
574 $user->avatar_url = get_avatar_url( $user->ID, array( 'size' => 32 ) );
575 $response['results'][] = $user;
576 }
577
578 $response['total_items'] = $user_list->get_total();
579 }
580
581 $this->json_response(
582 __( 'User list fetched successfully!', 'tutor' ),
583 $response
584 );
585 }
586 }
587