PluginProbe ʕ •ᴥ•ʔ
Tutor LMS – eLearning and online course solution / 3.1.0
Tutor LMS – eLearning and online course solution v3.1.0
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 1 year ago WooCommerce.php 1 year ago
User.php
550 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 *
171 * @return boolean
172 */
173 public static function is_admin() {
174 return current_user_can( self::ADMIN );
175 }
176
177 /**
178 * Check current user is instructor.
179 *
180 * @since 2.2.0
181 *
182 * @param bool $is_approved instructor is approved or not.
183 *
184 * @return boolean
185 */
186 public static function is_instructor( $is_approved = true ) {
187 return tutils()->is_instructor( 0, $is_approved );
188 }
189
190 /**
191 * Profile layouts
192 *
193 * @since 1.0.0
194 *
195 * @var array
196 */
197 private $profile_layout = array(
198 'pp-circle',
199 'pp-rectangle',
200 'no-cp',
201 );
202
203 /**
204 * Include edit user template
205 *
206 * @since 1.0.0
207 *
208 * @param mixed $user user.
209 *
210 * @return void
211 */
212 public function edit_user_profile( $user ) {
213 include tutor()->path . 'views/metabox/user-profile-fields.php';
214 }
215
216 /**
217 * Delete existing user's photo
218 *
219 * @since 1.0.0
220 *
221 * @param int $user_id user id.
222 * @param string $type photo type.
223 *
224 * @return void
225 */
226 private function delete_existing_user_photo( $user_id, $type ) {
227 $meta_key = 'cover_photo' == $type ? '_tutor_cover_photo' : '_tutor_profile_photo';
228 $photo_id = get_user_meta( $user_id, $meta_key, true );
229 is_numeric( $photo_id ) ? wp_delete_attachment( $photo_id, true ) : 0;
230 delete_user_meta( $user_id, $meta_key );
231 }
232
233 /**
234 * User photo remove
235 *
236 * @since 1.0.0
237 *
238 * @return void
239 */
240 public function tutor_user_photo_remove() {
241 tutor_utils()->checking_nonce();
242 $this->delete_existing_user_photo(
243 get_current_user_id(),
244 Input::post( 'photo_type', '' )
245 );
246 }
247
248 /**
249 * User photo update
250 *
251 * @since 1.0.0
252 *
253 * @return void
254 */
255 public function update_user_photo() {
256 tutor_utils()->checking_nonce();
257
258 $user_id = get_current_user_id();
259 $photo_type = Input::post( 'photo_type', '' );
260 $meta_key = 'cover_photo' === $photo_type ? '_tutor_cover_photo' : '_tutor_profile_photo';
261
262 /**
263 * Photo Update from profile
264 */
265 $photo = tutor_utils()->array_get( 'photo_file', $_FILES );
266 $photo_size = tutor_utils()->array_get( 'size', $photo );
267 $photo_type = tutor_utils()->array_get( 'type', $photo );
268
269 if ( $photo_size && strpos( $photo_type, 'image' ) !== false ) {
270 if ( ! function_exists( 'wp_handle_upload' ) ) {
271 require_once ABSPATH . 'wp-admin/includes/file.php';
272 }
273 $upload_overrides = array( 'test_form' => false );
274 $movefile = wp_handle_upload( $photo, $upload_overrides );
275
276 if ( $movefile && ! isset( $movefile['error'] ) ) {
277 $file_path = tutor_utils()->array_get( 'file', $movefile );
278 $file_url = tutor_utils()->array_get( 'url', $movefile );
279 $mime_type = '';
280 if ( file_exists( $file_path ) ) {
281 $image_info = getimagesize( $file_path );
282 $mime_type = is_array( $image_info ) && count( $image_info ) ? $image_info['mime'] : '';
283 }
284
285 $media_id = wp_insert_attachment(
286 array(
287 'guid' => $file_path,
288 'post_mime_type' => $mime_type,
289 'post_title' => preg_replace( '/\.[^.]+$/', '', basename( $file_url ) ),
290 'post_content' => '',
291 'post_status' => 'inherit',
292 ),
293 $file_path,
294 0
295 );
296
297 if ( $media_id ) {
298 // wp_generate_attachment_metadata() won't work if you do not include this file.
299 require_once ABSPATH . 'wp-admin/includes/image.php';
300
301 // Generate and save the attachment metas into the database.
302 wp_update_attachment_metadata( $media_id, wp_generate_attachment_metadata( $media_id, $file_path ) );
303
304 // Update it to user profile.
305 $this->delete_existing_user_photo( $user_id, Input::post( 'photo_type', '' ) );
306 update_user_meta( $user_id, $meta_key, $media_id );
307
308 exit( wp_json_encode( array( 'status' => 'success' ) ) );
309 }
310 }
311 }
312 }
313
314 /**
315 * Get user timezone string.
316 *
317 * @param int|object $user user id or user object. Default: current user.
318 *
319 * @return string user timezone string, fallback site timezone string.
320 */
321 public static function get_user_timezone_string( $user = 0 ) {
322 if ( is_numeric( $user ) ) {
323 $user = get_user_by( 'id', tutor_utils()->get_user_id( $user ) );
324 }
325
326 if ( ! is_a( $user, 'WP_User' ) ) {
327 return wp_timezone_string();
328 }
329
330 $timezone = get_user_meta( $user->ID, self::TIMEZONE_META, true );
331 if ( self::is_admin() || empty( $timezone ) ) {
332 $timezone = wp_timezone_string();
333 }
334
335 return $timezone;
336 }
337
338 /**
339 * Profile update
340 *
341 * @since 1.0.0
342 *
343 * @param int $user_id user id.
344 *
345 * @return void
346 */
347 public function profile_update( $user_id ) {
348 if ( 'tutor_profile_update_by_wp' !== Input::post( 'tutor_action' ) ) {
349 return;
350 }
351
352 $timezone = Input::post( 'timezone', '' );
353 $_tutor_profile_job_title = Input::post( self::PROFILE_JOB_TITLE_META, '' );
354 $_tutor_profile_bio = Input::post( self::PROFILE_BIO_META, '', Input::TYPE_KSES_POST );
355 $_tutor_profile_image = Input::post( self::PROFILE_PHOTO_META, '', Input::TYPE_KSES_POST );
356
357 update_user_meta( $user_id, self::PROFILE_JOB_TITLE_META, $_tutor_profile_job_title );
358 update_user_meta( $user_id, self::PROFILE_BIO_META, $_tutor_profile_bio );
359 update_user_meta( $user_id, self::PROFILE_PHOTO_META, $_tutor_profile_image );
360 update_user_meta( $user_id, self::TIMEZONE_META, $timezone );
361 }
362
363 /**
364 * Set user role
365 *
366 * @since 1.0.0
367 *
368 * @param int $user_id user id.
369 * @param string $role user role.
370 * @param array $old_roles old role.
371 *
372 * @return void
373 */
374 public function set_user_role( $user_id, $role, $old_roles ) {
375 $instructor_role = tutor()->instructor_role;
376
377 if ( $role === $instructor_role || in_array( $instructor_role, $old_roles ) ) {
378 tutor_utils()->add_instructor_role( $user_id );
379 }
380 }
381
382 /**
383 * Hide notices
384 *
385 * @since 1.0.0
386 *
387 * @return void
388 */
389 public function hide_notices() {
390 $hide_notice = Input::get( 'tutor-hide-notice', '' );
391 $is_register_enabled = Input::get( 'tutor-registration', '' );
392 $has_manage_cap = current_user_can( 'manage_options' );
393
394 if ( $has_manage_cap && is_admin() && 'registration' === $hide_notice ) {
395 tutor_utils()->checking_nonce( 'get' );
396
397 if ( 'enable' === $is_register_enabled ) {
398 update_option( 'users_can_register', 1 );
399 } else {
400 self::$hide_registration_notice = true;
401 setcookie( 'tutor_notice_hide_registration', 1, time() + MONTH_IN_SECONDS, tutor()->basepath );
402 }
403 }
404 }
405
406 /**
407 * Show registration disabled
408 *
409 * @since 1.0.0
410 *
411 * @return void
412 */
413 public function show_registration_disabled() {
414 if ( self::$hide_registration_notice ||
415 ( '0' !== get_option( 'users_can_register' ) ) ||
416 isset( $_COOKIE['tutor_notice_hide_registration'] ) ||
417 ! current_user_can( 'manage_options' )
418 ) {
419 return;
420 }
421
422 $hide_url = wp_nonce_url( add_query_arg( 'tutor-hide-notice', 'registration' ), tutor()->nonce_action, tutor()->nonce );
423 ?>
424 <div class="wrap tutor-user-registration-notice-wrapper">
425 <div class="tutor-user-registration-notice">
426 <div>
427 <img src="<?php echo esc_url( tutor()->url . 'assets/images/icon-info-round.svg' ); ?>"/>
428 </div>
429 <div>
430 <?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 ) ); ?>
431 </div>
432 <div>
433 <a href="<?php echo esc_url( add_query_arg( 'tutor-registration', 'enable', $hide_url ) ); ?>"><?php esc_html_e( 'Enable', 'tutor' ); ?></a>
434 <hr/>
435 <a href="<?php echo esc_url( $hide_url ); ?>">
436 <?php esc_html_e( 'Dismiss', 'tutor' ); ?>
437 </a>
438 </div>
439 </div>
440 </div>
441 <?php
442 }
443
444 /**
445 * Set the user last active timestamp to now.
446 *
447 * @since 2.5.0
448 *
449 * @param string $user_login active user name.
450 * @param \WP_User $user User object data.
451 *
452 * @return void
453 */
454 public function update_user_last_login( $user_login, $user ) {
455 update_user_meta( $user->ID, self::LAST_LOGIN_META, time() );
456 }
457
458 /**
459 * Add timezone input field to login form.
460 *
461 * @since 3.0.0
462 */
463 public function add_timezone_input() {
464 ?>
465 <input type="hidden" name="timezone" value="<?php echo esc_attr( wp_timezone_string() ); ?>" />
466 <script>
467 document.addEventListener('DOMContentLoaded', function() {
468 const timezone = document.querySelector('input[name="timezone"]');
469 if ( timezone) {
470 const tz = Intl.DateTimeFormat().resolvedOptions().timeZone;
471 timezone.value = tz
472 }
473 });
474 </script>
475 <?php
476 }
477
478 /**
479 * Set user timezone if not it set.
480 *
481 * @since 3.0.0
482 *
483 * @param string $user_login active user name.
484 * @param \WP_User $user User object data.
485 *
486 * @return void
487 */
488 public function set_timezone( $user_login, $user ) {
489 if ( Input::has( 'timezone' ) ) {
490 $timezone = get_user_meta( $user->ID, self::TIMEZONE_META, true );
491 if ( empty( $timezone ) ) {
492 update_user_meta( $user->ID, self::TIMEZONE_META, Input::post( 'timezone', wp_timezone_string() ) );
493 }
494 }
495 }
496
497 /**
498 * Get user list with pagination & support
499 * search term
500 *
501 * @since 3.0.0
502 *
503 * @return void
504 */
505 public function ajax_user_list() {
506 tutor_utils()->check_nonce();
507
508 $can_access = apply_filters( 'tutor_user_list_access', current_user_can( 'manage_options' ) );
509 if ( ! $can_access ) {
510 $this->json_response( tutor_utils()->error_message( 'forbidden' ), HttpHelper::STATUS_FORBIDDEN );
511 }
512
513 $response = array(
514 'results' => array(),
515 'total_items' => 0,
516 );
517
518 $limit = Input::post( 'limit', 10, Input::TYPE_INT );
519 $offset = Input::post( 'offset', 0, Input::TYPE_INT );
520
521 $args = array(
522 'limit' => $limit,
523 'offset' => $offset,
524 );
525
526 $filter = json_decode( wp_unslash( $_POST['filter'] ?? '{}' ) );//phpcs:ignore
527 if ( ! empty( $filter ) && property_exists( $filter, 'search' ) && ! empty( $filter->search ) ) {
528 $args['search'] = '*' . Input::sanitize( $filter->search ) . '*';
529 $args['search_columns'] = array( 'user_login', 'user_email', 'user_nicename', 'display_name', 'ID' );
530 }
531
532 $user_list = $this->model->get_users_list( $args );
533
534 if ( is_object( $user_list ) ) {
535 foreach ( $user_list->get_results() as $user ) {
536 // Set user avatar.
537 $user->avatar_url = get_avatar_url( $user->ID, array( 'size' => 32 ) );
538 $response['results'][] = $user;
539 }
540
541 $response['total_items'] = $user_list->get_total();
542 }
543
544 $this->json_response(
545 __( 'User list fetched successfully!', 'tutor' ),
546 $response
547 );
548 }
549 }
550