PluginProbe ʕ •ᴥ•ʔ
Tutor LMS – eLearning and online course solution / 3.0.1
Tutor LMS – eLearning and online course solution v3.0.1
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 1 year ago Admin.php 1 year ago Ajax.php 1 year ago Announcements.php 1 year ago Assets.php 1 year ago Backend_Page_Trait.php 1 year ago BaseController.php 1 year ago Course.php 1 year ago Course_Embed.php 3 years ago Course_Filter.php 1 year ago Course_List.php 1 year ago Course_Settings_Tabs.php 1 year ago Course_Widget.php 3 years ago Custom_Validation.php 3 years ago Dashboard.php 1 year ago Earnings.php 1 year ago FormHandler.php 2 years ago Frontend.php 1 year ago Gutenberg.php 1 year ago Input.php 1 year ago Instructor.php 1 year ago Instructors_List.php 1 year ago Lesson.php 1 year ago Options_V2.php 1 year ago Permalink.php 2 years ago Post_types.php 2 years ago Private_Course_Access.php 1 year ago Q_And_A.php 1 year ago Question_Answers_List.php 3 years ago Quiz.php 1 year ago QuizBuilder.php 1 year ago Quiz_Attempts_List.php 1 year ago RestAPI.php 2 years ago Reviews.php 3 years ago Rewrite_Rules.php 2 years ago Shortcode.php 1 year ago Singleton.php 1 year ago Student.php 1 year ago Students_List.php 3 years ago Taxonomies.php 3 years ago Template.php 1 year ago Theme_Compatibility.php 3 years ago Tools.php 3 years ago Tools_V2.php 1 year ago Tutor.php 1 year ago TutorEDD.php 1 year ago Tutor_Base.php 2 years ago Tutor_Setup.php 1 year ago Upgrader.php 1 year ago User.php 1 year ago Utils.php 1 year ago Video_Stream.php 3 years ago WhatsNew.php 2 years ago Withdraw.php 1 year ago Withdraw_Requests_List.php 3 years ago WooCommerce.php 1 year ago
User.php
548 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
45 /**
46 * User model
47 *
48 * @since 3.0.0
49 *
50 * @var UserModel
51 */
52 private $model;
53
54 /**
55 * Registration notice
56 *
57 * @since 1.0.0
58 *
59 * @var boolean
60 */
61 private static $hide_registration_notice = false;
62
63 /**
64 * Register hooks
65 *
66 * @since 1.0.0
67 * @since 2.2.0 $register_hooks param added to resuse the class without hooks register.
68 *
69 * @param bool $register_hooks register hooks.
70 *
71 * @return void
72 */
73 public function __construct( $register_hooks = true ) {
74 $this->model = new UserModel();
75 if ( ! $register_hooks ) {
76 return;
77 }
78
79 add_action( 'edit_user_profile', array( $this, 'edit_user_profile' ) );
80 add_action( 'show_user_profile', array( $this, 'edit_user_profile' ), 10, 1 );
81
82 add_action( 'profile_update', array( $this, 'profile_update' ) );
83 add_action( 'set_user_role', array( $this, 'set_user_role' ), 10, 3 );
84
85 add_action( 'wp_ajax_tutor_user_photo_remove', array( $this, 'tutor_user_photo_remove' ) );
86 add_action( 'wp_ajax_tutor_user_photo_upload', array( $this, 'update_user_photo' ) );
87
88 add_action( 'admin_notices', array( $this, 'show_registration_disabled' ) );
89 add_action( 'admin_init', array( $this, 'hide_notices' ) );
90 add_action( 'wp_login', array( $this, 'update_user_last_login' ), 10, 2 );
91 add_action( 'login_form', array( $this, 'add_timezone_input' ) );
92 add_action( 'wp_login', array( $this, 'set_timezone' ), 10, 2 );
93
94 add_action( 'wp_ajax_tutor_user_list', array( $this, 'ajax_user_list' ) );
95 }
96
97 /**
98 * Get meta key name for review popup.
99 *
100 * @since 2.4.0
101 *
102 * @param int $course_id course id.
103 *
104 * @return string user meta key name.
105 */
106 public static function get_review_popup_meta( $course_id ) {
107 return self::REVIEW_POPUP_META . '_' . $course_id;
108 }
109
110 /**
111 * Check user has provided role.
112 *
113 * @since 2.2.0
114 *
115 * @param string $role role.
116 *
117 * @return boolean
118 */
119 public static function is( string $role ) {
120 return current_user_can( $role );
121 }
122
123 /**
124 * Check user has any role.
125 *
126 * @since 2.2.0
127 * @since 2.6.2 $user_id param added.
128 *
129 * @param array $roles roles.
130 * @param int $user_id user id.
131 *
132 * @return boolean
133 */
134 public static function has_any_role( array $roles, $user_id = 0 ) {
135 $user = get_userdata( tutor_utils()->get_user_id( $user_id ) );
136 if ( empty( $user->roles ) || empty( $roles ) ) {
137 return false;
138 }
139
140 foreach ( $roles as $role ) {
141 if ( in_array( $role, $user->roles, true ) ) {
142 return true;
143 break;
144 }
145 }
146
147 return false;
148 }
149
150 /**
151 * Check user is student.
152 *
153 * @since 2.2.0
154 * @since 2.6.2 $user_id param added.
155 *
156 * @param int $user_id user id.
157 *
158 * @return boolean
159 */
160 public static function is_student( $user_id = 0 ) {
161 return self::has_any_role( array( self::STUDENT ), $user_id );
162 }
163
164 /**
165 * Check user is admin.
166 *
167 * @since 2.2.0
168 *
169 * @return boolean
170 */
171 public static function is_admin() {
172 return current_user_can( self::ADMIN );
173 }
174
175 /**
176 * Check current user is instructor.
177 *
178 * @since 2.2.0
179 *
180 * @param bool $is_approved instructor is approved or not.
181 *
182 * @return boolean
183 */
184 public static function is_instructor( $is_approved = true ) {
185 return tutils()->is_instructor( 0, $is_approved );
186 }
187
188 /**
189 * Profile layouts
190 *
191 * @since 1.0.0
192 *
193 * @var array
194 */
195 private $profile_layout = array(
196 'pp-circle',
197 'pp-rectangle',
198 'no-cp',
199 );
200
201 /**
202 * Include edit user template
203 *
204 * @since 1.0.0
205 *
206 * @param mixed $user user.
207 *
208 * @return void
209 */
210 public function edit_user_profile( $user ) {
211 include tutor()->path . 'views/metabox/user-profile-fields.php';
212 }
213
214 /**
215 * Delete existing user's photo
216 *
217 * @since 1.0.0
218 *
219 * @param int $user_id user id.
220 * @param string $type photo type.
221 *
222 * @return void
223 */
224 private function delete_existing_user_photo( $user_id, $type ) {
225 $meta_key = 'cover_photo' == $type ? '_tutor_cover_photo' : '_tutor_profile_photo';
226 $photo_id = get_user_meta( $user_id, $meta_key, true );
227 is_numeric( $photo_id ) ? wp_delete_attachment( $photo_id, true ) : 0;
228 delete_user_meta( $user_id, $meta_key );
229 }
230
231 /**
232 * User photo remove
233 *
234 * @since 1.0.0
235 *
236 * @return void
237 */
238 public function tutor_user_photo_remove() {
239 tutor_utils()->checking_nonce();
240 $this->delete_existing_user_photo(
241 get_current_user_id(),
242 Input::post( 'photo_type', '' )
243 );
244 }
245
246 /**
247 * User photo update
248 *
249 * @since 1.0.0
250 *
251 * @return void
252 */
253 public function update_user_photo() {
254 tutor_utils()->checking_nonce();
255
256 $user_id = get_current_user_id();
257 $photo_type = Input::post( 'photo_type', '' );
258 $meta_key = 'cover_photo' === $photo_type ? '_tutor_cover_photo' : '_tutor_profile_photo';
259
260 /**
261 * Photo Update from profile
262 */
263 $photo = tutor_utils()->array_get( 'photo_file', $_FILES );
264 $photo_size = tutor_utils()->array_get( 'size', $photo );
265 $photo_type = tutor_utils()->array_get( 'type', $photo );
266
267 if ( $photo_size && strpos( $photo_type, 'image' ) !== false ) {
268 if ( ! function_exists( 'wp_handle_upload' ) ) {
269 require_once ABSPATH . 'wp-admin/includes/file.php';
270 }
271 $upload_overrides = array( 'test_form' => false );
272 $movefile = wp_handle_upload( $photo, $upload_overrides );
273
274 if ( $movefile && ! isset( $movefile['error'] ) ) {
275 $file_path = tutor_utils()->array_get( 'file', $movefile );
276 $file_url = tutor_utils()->array_get( 'url', $movefile );
277 $mime_type = '';
278 if ( file_exists( $file_path ) ) {
279 $image_info = getimagesize( $file_path );
280 $mime_type = is_array( $image_info ) && count( $image_info ) ? $image_info['mime'] : '';
281 }
282
283 $media_id = wp_insert_attachment(
284 array(
285 'guid' => $file_path,
286 'post_mime_type' => $mime_type,
287 'post_title' => preg_replace( '/\.[^.]+$/', '', basename( $file_url ) ),
288 'post_content' => '',
289 'post_status' => 'inherit',
290 ),
291 $file_path,
292 0
293 );
294
295 if ( $media_id ) {
296 // wp_generate_attachment_metadata() won't work if you do not include this file.
297 require_once ABSPATH . 'wp-admin/includes/image.php';
298
299 // Generate and save the attachment metas into the database.
300 wp_update_attachment_metadata( $media_id, wp_generate_attachment_metadata( $media_id, $file_path ) );
301
302 // Update it to user profile.
303 $this->delete_existing_user_photo( $user_id, Input::post( 'photo_type', '' ) );
304 update_user_meta( $user_id, $meta_key, $media_id );
305
306 exit( wp_json_encode( array( 'status' => 'success' ) ) );
307 }
308 }
309 }
310 }
311
312 /**
313 * Get user timezone string.
314 *
315 * @param int|object $user user id or user object. Default: current user.
316 *
317 * @return string user timezone string, fallback site timezone string.
318 */
319 public static function get_user_timezone_string( $user = 0 ) {
320 if ( is_numeric( $user ) ) {
321 $user = get_user_by( 'id', tutor_utils()->get_user_id( $user ) );
322 }
323
324 if ( ! is_a( $user, 'WP_User' ) ) {
325 return wp_timezone_string();
326 }
327
328 $timezone = get_user_meta( $user->ID, self::TIMEZONE_META, true );
329 if ( self::is_admin() || empty( $timezone ) ) {
330 $timezone = wp_timezone_string();
331 }
332
333 return $timezone;
334 }
335
336 /**
337 * Profile update
338 *
339 * @since 1.0.0
340 *
341 * @param int $user_id user id.
342 *
343 * @return void
344 */
345 public function profile_update( $user_id ) {
346 if ( 'tutor_profile_update_by_wp' !== Input::post( 'tutor_action' ) ) {
347 return;
348 }
349
350 $timezone = Input::post( 'timezone', '' );
351 $_tutor_profile_job_title = Input::post( self::PROFILE_JOB_TITLE_META, '' );
352 $_tutor_profile_bio = Input::post( self::PROFILE_BIO_META, '', Input::TYPE_KSES_POST );
353 $_tutor_profile_image = Input::post( self::PROFILE_PHOTO_META, '', Input::TYPE_KSES_POST );
354
355 update_user_meta( $user_id, self::PROFILE_JOB_TITLE_META, $_tutor_profile_job_title );
356 update_user_meta( $user_id, self::PROFILE_BIO_META, $_tutor_profile_bio );
357 update_user_meta( $user_id, self::PROFILE_PHOTO_META, $_tutor_profile_image );
358 update_user_meta( $user_id, self::TIMEZONE_META, $timezone );
359 }
360
361 /**
362 * Set user role
363 *
364 * @since 1.0.0
365 *
366 * @param int $user_id user id.
367 * @param string $role user role.
368 * @param array $old_roles old role.
369 *
370 * @return void
371 */
372 public function set_user_role( $user_id, $role, $old_roles ) {
373 $instructor_role = tutor()->instructor_role;
374
375 if ( $role === $instructor_role || in_array( $instructor_role, $old_roles ) ) {
376 tutor_utils()->add_instructor_role( $user_id );
377 }
378 }
379
380 /**
381 * Hide notices
382 *
383 * @since 1.0.0
384 *
385 * @return void
386 */
387 public function hide_notices() {
388 $hide_notice = Input::get( 'tutor-hide-notice', '' );
389 $is_register_enabled = Input::get( 'tutor-registration', '' );
390 $has_manage_cap = current_user_can( 'manage_options' );
391
392 if ( $has_manage_cap && is_admin() && 'registration' === $hide_notice ) {
393 tutor_utils()->checking_nonce( 'get' );
394
395 if ( 'enable' === $is_register_enabled ) {
396 update_option( 'users_can_register', 1 );
397 } else {
398 self::$hide_registration_notice = true;
399 setcookie( 'tutor_notice_hide_registration', 1, time() + MONTH_IN_SECONDS, tutor()->basepath );
400 }
401 }
402 }
403
404 /**
405 * Show registration disabled
406 *
407 * @since 1.0.0
408 *
409 * @return void
410 */
411 public function show_registration_disabled() {
412 if ( self::$hide_registration_notice ||
413 ( '0' !== get_option( 'users_can_register' ) ) ||
414 isset( $_COOKIE['tutor_notice_hide_registration'] ) ||
415 ! current_user_can( 'manage_options' )
416 ) {
417 return;
418 }
419
420 $hide_url = wp_nonce_url( add_query_arg( 'tutor-hide-notice', 'registration' ), tutor()->nonce_action, tutor()->nonce );
421 ?>
422 <div class="wrap tutor-user-registration-notice-wrapper">
423 <div class="tutor-user-registration-notice">
424 <div>
425 <img src="<?php echo esc_url( tutor()->url . 'assets/images/icon-info-round.svg' ); ?>"/>
426 </div>
427 <div>
428 <?php echo wp_kses( '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".', array( 'strong' => true ) ); ?>
429 </div>
430 <div>
431 <a href="<?php echo esc_url( add_query_arg( 'tutor-registration', 'enable', $hide_url ) ); ?>"><?php esc_html_e( 'Enable', 'tutor' ); ?></a>
432 <hr/>
433 <a href="<?php echo esc_url( $hide_url ); ?>">
434 <?php esc_html_e( 'Dismiss', 'tutor' ); ?>
435 </a>
436 </div>
437 </div>
438 </div>
439 <?php
440 }
441
442 /**
443 * Set the user last active timestamp to now.
444 *
445 * @since 2.5.0
446 *
447 * @param string $user_login active user name.
448 * @param \WP_User $user User object data.
449 *
450 * @return void
451 */
452 public function update_user_last_login( $user_login, $user ) {
453 update_user_meta( $user->ID, self::LAST_LOGIN_META, time() );
454 }
455
456 /**
457 * Add timezone input field to login form.
458 *
459 * @since 3.0.0
460 */
461 public function add_timezone_input() {
462 ?>
463 <input type="hidden" name="timezone" value="<?php echo esc_attr( wp_timezone_string() ); ?>" />
464 <script>
465 document.addEventListener('DOMContentLoaded', function() {
466 const timezone = document.querySelector('input[name="timezone"]');
467 if ( timezone) {
468 const tz = Intl.DateTimeFormat().resolvedOptions().timeZone;
469 timezone.value = tz
470 }
471 });
472 </script>
473 <?php
474 }
475
476 /**
477 * Set user timezone if not it set.
478 *
479 * @since 3.0.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 set_timezone( $user_login, $user ) {
487 if ( Input::has( 'timezone' ) ) {
488 $timezone = get_user_meta( $user->ID, self::TIMEZONE_META, true );
489 if ( empty( $timezone ) ) {
490 update_user_meta( $user->ID, self::TIMEZONE_META, Input::post( 'timezone', wp_timezone_string() ) );
491 }
492 }
493 }
494
495 /**
496 * Get user list with pagination & support
497 * search term
498 *
499 * @since 3.0.0
500 *
501 * @return void
502 */
503 public function ajax_user_list() {
504 tutor_utils()->check_nonce();
505
506 $can_access = apply_filters( 'tutor_user_list_access', current_user_can( 'manage_options' ) );
507 if ( ! $can_access ) {
508 $this->json_response( tutor_utils()->error_message( 'forbidden' ), HttpHelper::STATUS_FORBIDDEN );
509 }
510
511 $response = array(
512 'results' => array(),
513 'total_items' => 0,
514 );
515
516 $limit = Input::post( 'limit', 10, Input::TYPE_INT );
517 $offset = Input::post( 'offset', 0, Input::TYPE_INT );
518
519 $args = array(
520 'limit' => $limit,
521 'offset' => $offset,
522 );
523
524 $filter = json_decode( wp_unslash( $_POST['filter'] ?? '{}' ) );//phpcs:ignore
525 if ( ! empty( $filter ) && property_exists( $filter, 'search' ) && ! empty( $filter->search ) ) {
526 $args['search'] = '*' . Input::sanitize( $filter->search ) . '*';
527 $args['search_columns'] = array( 'user_login', 'user_email', 'user_nicename', 'display_name', 'ID' );
528 }
529
530 $user_list = $this->model->get_users_list( $args );
531
532 if ( is_object( $user_list ) ) {
533 foreach ( $user_list->get_results() as $user ) {
534 // Set user avatar.
535 $user->avatar_url = get_avatar_url( $user->ID, array( 'size' => 32 ) );
536 $response['results'][] = $user;
537 }
538
539 $response['total_items'] = $user_list->get_total();
540 }
541
542 $this->json_response(
543 __( 'User list fetched successfully!', 'tutor' ),
544 $response
545 );
546 }
547 }
548