PluginProbe
LearnPress – WordPress LMS Plugin for Create and Sell Online Courses / trunk
LearnPress – WordPress LMS Plugin for Create and Sell Online Courses vtrunk
4.4.6 4.4.5 4.4.4 4.4.3 4.4.2 4.4.1 4.4.0 4.3.9.1 4.3.9 4.3.8 4.3.7 4.1.6.9 4.1.6.9.1 4.1.6.9.2 4.1.6.9.3 4.1.6.9.4 4.1.7 4.1.7.1 4.1.7.2 4.1.7.3 4.1.7.3.1 4.1.7.3.2 4.2.0 4.2.1 4.2.1.1 All 137 releases
learnpress / inc / Models / UserModel.php

UserModel.php in LearnPress – WordPress LMS Plugin for Create and Sell Online Courses trunk, at inc/Models/UserModel.php

804 lines 20.2 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 namespace LearnPress\Models;
4
5 /**
6 * Class UserModel
7 *
8 * @version 1.0.3
9 * @since 4.2.6.9
10 */
11
12 use Exception;
13 use LearnPress\Databases\Course\CourseJsonDB;
14 use LearnPress\Databases\UserItemsDB;
15 use LearnPress\Filters\Course\CourseJsonFilter;
16 use LearnPress\Filters\UserItemsFilter;
17 use LearnPress\Services\UserService;
18 use LP_Cache;
19 use LP_Debug;
20 use LearnPress\Databases\UserDB;
21 use LearnPress\Filters\UserFilter;
22
23 use LP_User_Items_DB;
24 use LP_User_Items_Filter;
25 use LP_WP_Filesystem;
26 use stdClass;
27 use Throwable;
28 use WP_Error;
29 use WP_User;
30
31 class UserModel {
32 /**
33 * Auto increment, Primary key
34 *
35 * @var int
36 */
37 public $ID = 0;
38 /**
39 * @var string
40 */
41 public $user_login = '';
42 /**
43 * @var string
44 */
45 public $user_nicename;
46 /**
47 * @var string
48 */
49 public $user_email = null;
50 /**
51 * @var string
52 */
53 public $user_url = null;
54 /**
55 * @var string
56 */
57 public $user_register = '';
58 /**
59 * Display name of user.
60 *
61 * @var string
62 */
63 public $display_name = '';
64 /**
65 * @var stdClass all meta data
66 */
67 public $meta_data = null;
68 /**
69 * @var string image url
70 */
71 public $image_url = '';
72
73 // Meta keys
74 const META_KEY_IMAGE = '_lp_profile_picture';
75 const META_KEY_COVER_IMAGE = '_lp_profile_cover_image';
76 const META_KEY_USER_SLUG = '_lp_user_slug';
77 // Roles
78 const ROLE_INSTRUCTOR = LP_TEACHER_ROLE;
79 const ROLE_ADMINISTRATOR = 'administrator';
80
81 /**
82 * If data get from database, map to object.
83 * Else create new object to save data to database.
84 *
85 * @param array|object|mixed $data
86 */
87 public function __construct( $data = null ) {
88 if ( $data ) {
89 $this->map_to_object( $data );
90 }
91
92 if ( is_null( $this->meta_data ) ) {
93 $this->meta_data = new stdClass();
94 }
95 }
96
97 /**
98 * Map array, object data to UserItemModel.
99 * Use for data get from database.
100 *
101 * @param array|object|mixed $data
102 *
103 * @return UserModel
104 */
105 public function map_to_object( $data ): UserModel {
106 foreach ( $data as $key => $value ) {
107 if ( property_exists( $this, $key ) ) {
108 $this->{$key} = $value;
109 }
110 }
111
112 return $this;
113 }
114
115 /**
116 * Get course by ID
117 *
118 * @param int $user_id
119 * @param bool $check_cache
120 *
121 * @return false|static
122 */
123 public static function find( int $user_id, bool $check_cache = false ) {
124 $filter_user = new UserFilter();
125 $filter_user->ID = $user_id;
126 $key_cache = "userModel/find/id/{$user_id}";
127 $lp_course_cache = new LP_Cache();
128
129 // Check cache
130 if ( $check_cache ) {
131 $user_model = $lp_course_cache->get_cache( $key_cache );
132 if ( $user_model instanceof UserModel ) {
133 return $user_model;
134 }
135 }
136
137 // Query database no cache.
138 $user_model = self::get_user_model_from_db( $filter_user );
139
140 // Set cache
141 if ( $user_model instanceof UserModel ) {
142 $lp_course_cache->set_cache( $key_cache, $user_model );
143 }
144
145 return $user_model;
146 }
147
148 /**
149 * Get course from database.
150 * If not exists, return false.
151 * If exists, return CoursePostModel.
152 *
153 * @param UserFilter $filter
154 *
155 * @return UserModel|false|static
156 * @since 4.2.6.9
157 * @version 1.0.1
158 */
159 public static function get_user_model_from_db( UserFilter $filter ) {
160 $lp_user_db = UserDB::getInstance();
161 $user_model = false;
162
163 try {
164 $filter->only_fields = [ 'ID', 'user_nicename', 'user_login', 'user_email', 'display_name' ];
165 $lp_user_db->get_query_single_row( $filter );
166 $query_single_row = $lp_user_db->get_users( $filter );
167 $user_rs = $lp_user_db->wpdb->get_row( $query_single_row );
168 if ( $user_rs instanceof stdClass ) {
169 $user_model = new UserModel( $user_rs );
170 }
171 } catch ( Throwable $e ) {
172 error_log( __METHOD__ . ': ' . $e->getMessage() );
173 }
174
175 return $user_model;
176 }
177
178 /**
179 * Get all meta_data, all keys of a user it
180 *
181 * @return stdClass|null
182 * @throws Exception
183 */
184 public function get_all_metadata() {
185 }
186
187 /**
188 * Get meta value by key.
189 *
190 * @param string $key
191 * @param mixed $default_value
192 *
193 * @sicne 4.2.6.9
194 * @version 1.0.3
195 * @return false|mixed
196 */
197 public function get_meta_value_by_key( string $key, $default_value = false ) {
198 if ( $this->meta_data instanceof stdClass && isset( $this->meta_data->{$key} ) ) {
199 return maybe_unserialize( $this->meta_data->{$key} );
200 }
201
202 $value = get_user_meta( $this->ID, $key, true );
203 if ( empty( $value ) ) {
204 $value = $default_value;
205 }
206
207 $this->meta_data->{$key} = maybe_unserialize( $value );
208
209 return $value;
210 }
211
212 /**
213 * Set meta value by key.
214 *
215 * @param string $key
216 * @param $value
217 *
218 * @return void
219 * @since 4.2.7.2
220 * @version 1.0.0
221 */
222 public function set_meta_value_by_key( string $key, $value ) {
223 $this->meta_data->{$key} = $value;
224 update_user_meta( $this->ID, $key, $value );
225 }
226
227 /**
228 * Retrieve the pretty slug used instead of user_name.
229 *
230 * This value is used to build links such as instructor links and user profile links.
231 * If a pretty slug has not been generated yet, it falls back to user_name when
232 * $fallback_to_username is true.
233 *
234 * @param bool $fallback_to_username Whether to fall back to username if no pretty slug exists.
235 *
236 * @return string
237 */
238 /*public function get_pretty_slug( bool $fallback_to_username = true ): string {
239 $slug = sanitize_title( (string) $this->get_meta_value_by_key( self::META_KEY_USER_SLUG, '' ) );
240
241 if ( '' !== $slug || ! $fallback_to_username ) {
242 return $slug;
243 }
244
245 return $this->get_username();
246 }*/
247
248 /**
249 * Get slug link of user.
250 * Get from column user_nicename of table wp_users
251 *
252 * @return string
253 * @since 4.3.6
254 * @version 1.0.0
255 */
256 public function get_slug_link(): string {
257 return (string) $this->user_nicename;
258 }
259
260 /**
261 * Update pretty slug manually.
262 *
263 * @param string $slug
264 *
265 * @return string|WP_Error
266 * @since 4.3.4
267 * @version 1.0.1
268 */
269 public function update_user_nicename( string $slug ) {
270 try {
271 $slug = sanitize_title( wp_unslash( $slug ) );
272
273 if ( empty( $slug ) ) {
274 throw new Exception( __( 'Cannot create a user with an empty nicename.' ) );
275 } elseif ( mb_strlen( $slug ) > 50 ) {
276 throw new Exception( __( 'Nicename may not be longer than 50 characters.' ) );
277 }
278
279 $userModelFind = UserService::instance()->get_user_by_slug_link( $slug );
280 // If not found any user with the slug, or found user is current user, update slug, else throw error.
281 if ( ! $userModelFind ) {
282 $this->user_nicename = $slug;
283 $this->save();
284 } elseif ( $userModelFind->get_id() !== $this->get_id() ) {
285 // Found another user with the slug, throw error.
286 throw new Exception(
287 sprintf(
288 /* translators: 1: user slug */
289 esc_html__( 'This user slug "%s" already exists.', 'learnpress' ),
290 $slug
291 )
292 );
293 }
294 } catch ( Throwable $e ) {
295 return new WP_Error( 'lp_user_slug_update_failed', $e->getMessage() );
296 }
297
298 return $slug;
299 }
300
301 /**
302 * Get upload profile src.
303 *
304 * @return string
305 */
306 public function get_image_url(): string {
307 if ( ! empty( $this->image_url ) ) {
308 return $this->image_url;
309 }
310
311 $profile_picture = $this->get_meta_value_by_key( self::META_KEY_IMAGE, '' );
312 if ( ! empty( $profile_picture ) ) {
313 // Check if hase slug / at the beginning of the path, if not add it.
314 $slash = substr( $profile_picture, 0, 1 ) === '/' ? '' : '/';
315 $profile_picture = $slash . $profile_picture;
316 // End check.
317 $upload = learn_press_user_profile_picture_upload_dir();
318 $file_path = $upload['basedir'] . $profile_picture;
319
320 if ( file_exists( $file_path ) ) {
321 $this->image_url = $upload['baseurl'] . $profile_picture;
322 } else { // For remote url.
323 $this->image_url = $profile_picture;
324 }
325 }
326
327 return $this->image_url;
328 }
329
330 /**
331 * Get upload cover image src.
332 *
333 * @return string
334 * @since 4.2.7.2
335 * @version 1.0.0
336 */
337 public function get_cover_image_url(): string {
338 $cover_image = $this->get_meta_value_by_key( self::META_KEY_COVER_IMAGE, '' );
339 if ( ! empty( $cover_image ) ) {
340 // Check if hase slug / at the beginning of the path, if not add it.
341 $slash = substr( $cover_image, 0, 1 ) === '/' ? '' : '/';
342 $cover_image = $slash . $cover_image;
343 // End check.
344 $upload = learn_press_user_profile_picture_upload_dir();
345 $file_path = $upload['basedir'] . $cover_image;
346
347 if ( file_exists( $file_path ) ) {
348 return $upload['baseurl'] . $cover_image;
349 } else { // For remote url.
350 return $cover_image;
351 }
352 }
353
354 return '';
355 }
356
357 /**
358 * Set cover image url.
359 *
360 * @param string $url
361 *
362 * @return void
363 * @since 4.2.7.2
364 * @version 1.0.0
365 */
366 public function set_cover_image_url( string $url ) {
367 $this->set_meta_value_by_key( self::META_KEY_COVER_IMAGE, $url );
368 }
369
370 /**
371 * Delete cover image.
372 *
373 * @return void
374 * @since 4.2.7.2
375 * @version 1.0.0
376 */
377 public function delete_cover_image() {
378 $upload_dir = learn_press_user_profile_picture_upload_dir();
379
380 // Delete old image if exists
381 $image_path = $this->get_meta_value_by_key( UserModel::META_KEY_COVER_IMAGE, '' );
382 if ( $image_path ) {
383 $path = $upload_dir['basedir'] . '/' . $image_path;
384
385 if ( file_exists( $path ) ) {
386 LP_WP_Filesystem::instance()->unlink( $path );
387 }
388 }
389
390 // Save empty string to database.
391 $this->set_cover_image_url( '' );
392 }
393
394 /**
395 * Get display name
396 *
397 * Hook from function get_the_author_meta of WP
398 *
399 * @return string
400 * @uses get_the_author_meta
401 * @version 1.0.1
402 * @since 4.2.7
403 */
404 public function get_display_name(): string {
405 return apply_filters(
406 'get_the_author_display_name',
407 $this->display_name,
408 $this->get_id(),
409 $this->get_id()
410 );
411 }
412
413 /**
414 * Get url instructor.
415 *
416 * @move from LP_User
417 * @return string
418 * @version 1.0.1
419 * @since 4.2.3
420 */
421 public function get_url_instructor(): string {
422
423 $single_instructor_link = '';
424
425 try {
426 $user_name = $this->get_slug_link();
427 $single_instructor_page_id = learn_press_get_page_id( 'single_instructor' );
428 if ( ! $single_instructor_page_id ) {
429 return $single_instructor_link;
430 }
431 $single_instructor_link = trailingslashit(
432 trailingslashit( get_page_link( $single_instructor_page_id ) ) . $user_name
433 );
434 } catch ( Throwable $e ) {
435 error_log( __METHOD__ . ': ' . $e->getMessage() );
436 }
437
438 return $single_instructor_link;
439 }
440
441 /**
442 * Get profile avatar url
443 * 1. Get upload avatar src
444 * 2. If not exists, get form Gravatar
445 * 3. If not exists, get default image
446 *
447 * @return string
448 * @since 4.2.7.2
449 * @version 1.0.1
450 */
451 public function get_avatar_url(): string {
452 $avatar_url = $this->get_upload_avatar_src();
453 if ( empty( $avatar_url ) ) {
454 // Get form Gravatar.
455 $args = learn_press_get_avatar_thumb_size();
456 $args = apply_filters( 'learn-press/gravatar/args', $args );
457 $avatar_url = get_avatar_url( $this->get_id(), $args );
458 // If not exists, get default avatar.
459 if ( empty( $avatar_url ) ) {
460 $avatar_url = LP_PLUGIN_URL . 'assets/images/avatar-default.png';
461 }
462 }
463
464 return $avatar_url;
465 }
466
467 /**
468 * Get upload avatar src.
469 *
470 * @return string
471 * @since 4.2.7.2
472 * @version 1.0.0
473 * @move from get_upload_profile_src method on LP_Profile class
474 */
475 public function get_upload_avatar_src(): string {
476 $uploaded_avatar_src = '';
477 $profile_picture = $this->get_meta_value_by_key( self::META_KEY_IMAGE, '' );
478
479 if ( $profile_picture ) {
480 // Check if hase slug / at the beginning of the path, if not, add it.
481 $slash = substr( $profile_picture, 0, 1 ) === '/' ? '' : '/';
482 $profile_picture = $slash . $profile_picture;
483 // End check.
484 $upload = learn_press_user_profile_picture_upload_dir();
485 $file_path = $upload['basedir'] . $profile_picture;
486
487 if ( file_exists( $file_path ) ) {
488 $uploaded_avatar_src = $upload['baseurl'] . $profile_picture;
489 }
490 }
491
492 return apply_filters( 'learn-press/user/upload-avatar-src', $uploaded_avatar_src, $this );
493 }
494
495 /**
496 * Get links socials of use on Profile page
497 * Icon is svg
498 *
499 * @move from LP_Abstract_User
500 * @return array
501 * @since 4.2.3
502 * @version 1.0.2
503 */
504 public function get_profile_social(): array {
505 $socials = array();
506 $extra_info = learn_press_get_user_extra_profile_info( $this->get_id() );
507
508 if ( $extra_info ) {
509 foreach ( $extra_info as $k => $v ) {
510 if ( empty( $v ) ) {
511 continue;
512 }
513
514 switch ( $k ) {
515 case 'facebook':
516 $i = '<i class="lp-user-ico lp-icon-facebook"></i>';
517 break;
518 case 'twitter':
519 $i = '<i class="lp-user-ico lp-icon-twitter"></i>';
520 break;
521 case 'linkedin':
522 $i = '<i class="lp-user-ico lp-icon-linkedin"></i>';
523 break;
524 case 'youtube':
525 $i = '<i class="lp-user-ico lp-icon-youtube-play"></i>';
526 break;
527 default:
528 $i = sprintf(
529 '<i class="lp-user-ico lp-icon-%s"></i>',
530 esc_attr( $k )
531 );
532 }
533
534 $icon = apply_filters(
535 'learn-press/user-profile-social-icon',
536 $i,
537 $k,
538 $this->get_id(),
539 $this
540 );
541 $socials[ $k ] = sprintf(
542 '<a href="%s">%s</a>',
543 esc_url_raw( $v ),
544 $icon
545 );
546 }
547 }
548
549 return apply_filters( 'learn-press/user-profile-socials', $socials, $this->get_id(), $this );
550 }
551
552 /**
553 * Update data to database.
554 *
555 * If user_item_id is empty, insert new data, else update data.
556 *
557 * @throws Exception
558 * @since 4.2.5
559 * @version 1.0.1
560 */
561 public function save( bool $force_save = false ) {
562 $data = get_object_vars( $this );
563
564 // Check if exists user id.
565 if ( empty( $this->ID ) ) { // Insert data.
566 unset( $data['ID'] );
567 $user_id = wp_insert_user( $data );
568 } else { // Update data.
569 if ( ! $force_save ) {
570 if ( ! current_user_can( 'edit_user', $this->get_id() ) ) {
571 throw new Exception( __( 'Sorry, you are not allowed to edit this user.' ) );
572 }
573 }
574
575 $user_id = wp_update_user( $data );
576 }
577
578 if ( is_wp_error( $user_id ) ) {
579 throw new Exception( $user_id->get_error_message() );
580 } else {
581 $this->ID = $user_id;
582 }
583
584 $this->clean_caches();
585 }
586
587 /**
588 * Clean caches.
589 *
590 * @return void
591 */
592 public function clean_caches() {
593 // Clear cache.
594 $key_cache = "userModel/find/id/{$this->get_id()}";
595 $lp_course_cache = new LP_Cache();
596 $lp_course_cache->clear( $key_cache );
597 }
598
599 /**
600 * @return int
601 */
602 public function get_id(): int {
603 return (int) $this->ID;
604 }
605
606 /**
607 * Get description of user.
608 *
609 * @return string
610 * @since 4.2.6.9
611 * @version 1.0.1
612 */
613 public function get_description(): string {
614 return get_the_author_meta( 'description', $this->get_id() );
615 }
616
617 /**
618 * Get email of user.
619 *
620 * @return string
621 * @since 4.2.7.4
622 * @version 1.0.0
623 */
624 public function get_email(): string {
625 return $this->user_email ?? '';
626 }
627
628 /**
629 * Get username of user.
630 *
631 * @return string
632 * @since 4.2.7.4
633 * @version 1.0.0
634 */
635 public function get_username(): string {
636 return $this->user_login ?? '';
637 }
638
639 /**
640 * Get statistic info of instructor user
641 *
642 * @param array $params
643 *
644 * @return array
645 * @since 4.1.6
646 * @version 1.0.7
647 */
648 public function get_instructor_statistic( array $params = [] ): array {
649 $statistic = array(
650 'total_course' => 0,
651 'published_course' => 0,
652 'pending_course' => 0,
653 'total_student' => 0,
654 'student_completed' => 0,
655 'student_in_progress' => 0,
656 );
657
658 try {
659 $key_cache_first = "instructor/{$this->get_id()}/statistic";
660 $statistic_cache = LP_Cache::cache_load_first( 'get', $key_cache_first );
661 if ( $statistic_cache !== false ) {
662 return $statistic_cache;
663 }
664
665 $user_id = $this->get_id();
666 $lp_user_items_db = LP_User_Items_DB::getInstance();
667 $courseJsonDB = CourseJsonDB::getInstance();
668
669 // Count total user completed course of author
670 $filter_course = new CourseJsonFilter();
671 $filter_course->only_fields = array( 'ID' );
672 $filter_course->post_author = $user_id;
673 $filter_course->post_status = [ 'publish', 'private' ];
674 $filter_course->return_string_query = true;
675 $query_courses_str = $courseJsonDB::getInstance()->get_courses( $filter_course );
676
677 $filter_count_users = new LP_User_Items_Filter();
678 $filter_count_users->item_type = LP_COURSE_CPT;
679 $filter_count_users->where[] = "AND item_id IN ({$query_courses_str})";
680 $count_student_has_status = $lp_user_items_db->count_status_by_items( $filter_count_users );
681 // Count total user in progress course of author
682
683 // Get total users attend course of author
684 $filter_count_users = $lp_user_items_db->count_user_attend_courses_of_author( $user_id );
685 $count_users_attend_courses_of_author = $lp_user_items_db->get_user_courses( $filter_count_users );
686
687 // Get total courses publish of author
688 $filter_count_courses = $courseJsonDB->count_courses_of_author( $user_id, [ 'publish' ] );
689 $total_courses_publish_of_author = $courseJsonDB->get_courses( $filter_count_courses );
690
691 // Get total courses of author
692 $filter_count_courses = $courseJsonDB->count_courses_of_author( $user_id );
693 $total_courses_of_author = $courseJsonDB->get_courses( $filter_count_courses );
694
695 // Get total courses pending of author
696 $filter_count_courses = $courseJsonDB->count_courses_of_author( $user_id, [ 'pending' ] );
697 $total_courses_pending_of_author = $courseJsonDB->get_courses( $filter_count_courses );
698
699 $statistic['total_course'] = $total_courses_of_author;
700 $statistic['published_course'] = $total_courses_publish_of_author;
701 $statistic['pending_course'] = $total_courses_pending_of_author;
702 $statistic['total_student'] = $count_users_attend_courses_of_author;
703 $statistic['student_completed'] = $count_student_has_status->{LP_COURSE_FINISHED} ?? 0;
704 $statistic['student_in_progress'] = $count_student_has_status->{LP_COURSE_GRADUATION_IN_PROGRESS} ?? 0;
705
706 $statistic = apply_filters( 'lp/profile/instructor/statistic', $statistic, $this );
707
708 // Set cache first.
709 LP_Cache::cache_load_first( 'set', $key_cache_first, $statistic );
710 } catch ( Throwable $e ) {
711 LP_Debug::error_log( $e );
712 }
713
714 return $statistic;
715 }
716
717 /**
718 * Get statistic info of student user
719 *
720 * @return array
721 * @since 4.1.6
722 * @version 1.0.0
723 */
724 public function get_student_statistic(): array {
725 $statistic = array(
726 'enrolled_courses' => 0,
727 'in_progress_course' => 0,
728 'finished_courses' => 0,
729 'passed_courses' => 0,
730 'failed_courses' => 0,
731 );
732
733 try {
734 $user_id = $this->get_id();
735 $lp_user_items_db = LP_User_Items_DB::getInstance();
736
737 // Count status
738 $filter = new LP_User_Items_Filter();
739 $filter->user_id = $user_id;
740 $count_status = $lp_user_items_db->count_status_by_items( $filter );
741 $total_courses_enrolled = intval( $count_status->{LP_COURSE_PURCHASED} ?? 0 )
742 + intval( $count_status->{LP_COURSE_ENROLLED} ?? 0 )
743 + intval( $count_status->{LP_COURSE_FINISHED} ?? 0 );
744
745 $statistic['enrolled_courses'] = $total_courses_enrolled;
746 $statistic['in_progress_course'] = $count_status->{LP_COURSE_GRADUATION_IN_PROGRESS} ?? 0;
747 $statistic['finished_courses'] = $count_status->{LP_COURSE_FINISHED} ?? 0;
748 $statistic['passed_courses'] = $count_status->{LP_COURSE_GRADUATION_PASSED} ?? 0;
749 $statistic['failed_courses'] = $count_status->{LP_COURSE_GRADUATION_FAILED} ?? 0;
750 } catch ( Throwable $e ) {
751 LP_Debug::error_log( $e );
752 }
753
754 return apply_filters( 'lp/profile/student/statistic', $statistic, $this );
755 }
756
757 /**
758 * Check user is instructor or not.
759 *
760 * @return bool
761 * @since 4.2.7.6
762 * @version 1.0.1
763 */
764 public function is_instructor(): bool {
765 $wp_user = new WP_User( $this );
766 return user_can( $wp_user, self::ROLE_INSTRUCTOR )
767 || user_can( $wp_user, self::ROLE_ADMINISTRATOR );
768 }
769
770 /**
771 * Get quizzes attend of user.
772 *
773 * @param LP_User_Items_Filter|UserItemsFilter $filter
774 * @param int $total_rows
775 *
776 * @return array|int|string|null
777 * @throws Exception
778 * @since 4.2.8.2
779 * @version 1.0.1
780 */
781 public function get_quizzes_attend( $filter, int &$total_rows = 0 ) {
782 $lp_db_user_items = UserItemsDB::getInstance();
783 $filter->order_by = 'user_item_id';
784 $filter->order = 'DESC';
785 $filter->user_id = $this->get_id();
786 $filter->item_type = LP_QUIZ_CPT;
787 $filter->ref_type = LP_COURSE_CPT;
788
789 return $lp_db_user_items->get_user_items( $filter, $total_rows );
790 }
791
792 /**
793 * Get roles of user.
794 *
795 * @return array
796 * @since 4.3.6
797 * @version 1.0.0
798 */
799 public function get_roles(): array {
800 $user = new WP_User( $this );
801 return is_array( $user->roles ) ? $user->roles : [];
802 }
803 }
804