PluginProbe
LearnPress – WordPress LMS Plugin for Create and Sell Online Courses / 4.4.7
LearnPress – WordPress LMS Plugin for Create and Sell Online Courses v4.4.7
4.4.7 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 All 138 releases
← All changes | inc/Models/UserModel.php +803 -801 4.3.94.4.7 View file →
@@ -1,801 +1,803 @@
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 - * @return false|mixed
194 - */
195 - public function get_meta_value_by_key( string $key, $default_value = false ) {
196 - if ( $this->meta_data instanceof stdClass && isset( $this->meta_data->{$key} ) ) {
197 - return $this->meta_data->{$key};
198 - }
199 -
200 - $value = get_user_meta( $this->ID, $key, true );
201 - if ( empty( $value ) ) {
202 - $value = $default_value;
203 - }
204 -
205 - $this->meta_data->{$key} = $value;
206 -
207 - return $value;
208 - }
209 -
210 - /**
211 - * Set meta value by key.
212 - *
213 - * @param string $key
214 - * @param $value
215 - *
216 - * @return void
217 - * @since 4.2.7.2
218 - * @version 1.0.0
219 - */
220 - public function set_meta_value_by_key( string $key, $value ) {
221 - $this->meta_data->{$key} = $value;
222 - update_user_meta( $this->ID, $key, $value );
223 - }
224 -
225 - /**
226 - * Retrieve the pretty slug used instead of user_name.
227 - *
228 - * This value is used to build links such as instructor links and user profile links.
229 - * If a pretty slug has not been generated yet, it falls back to user_name when
230 - * $fallback_to_username is true.
231 - *
232 - * @param bool $fallback_to_username Whether to fall back to username if no pretty slug exists.
233 - *
234 - * @return string
235 - */
236 - /*public function get_pretty_slug( bool $fallback_to_username = true ): string {
237 - $slug = sanitize_title( (string) $this->get_meta_value_by_key( self::META_KEY_USER_SLUG, '' ) );
238 -
239 - if ( '' !== $slug || ! $fallback_to_username ) {
240 - return $slug;
241 - }
242 -
243 - return $this->get_username();
244 - }*/
245 -
246 - /**
247 - * Get slug link of user.
248 - * Get from column user_nicename of table wp_users
249 - *
250 - * @return string
251 - * @since 4.3.6
252 - * @version 1.0.0
253 - */
254 - public function get_slug_link(): string {
255 - return (string) $this->user_nicename;
256 - }
257 -
258 - /**
259 - * Update pretty slug manually.
260 - *
261 - * @param string $slug
262 - *
263 - * @return string|WP_Error
264 - * @since 4.3.4
265 - * @version 1.0.1
266 - */
267 - public function update_user_nicename( string $slug ) {
268 - try {
269 - $slug = sanitize_title( wp_unslash( $slug ) );
270 -
271 - if ( empty( $slug ) ) {
272 - throw new Exception( __( 'Cannot create a user with an empty nicename.' ) );
273 - } elseif ( mb_strlen( $slug ) > 50 ) {
274 - throw new Exception( __( 'Nicename may not be longer than 50 characters.' ) );
275 - }
276 -
277 - $userModelFind = UserService::instance()->get_user_by_slug_link( $slug );
278 - // If not found any user with the slug, or found user is current user, update slug, else throw error.
279 - if ( ! $userModelFind ) {
280 - $this->user_nicename = $slug;
281 - $this->save();
282 - } elseif ( $userModelFind->get_id() !== $this->get_id() ) {
283 - // Found another user with the slug, throw error.
284 - throw new Exception(
285 - sprintf(
286 - /* translators: 1: user slug */
287 - esc_html__( 'This user slug "%s" already exists.', 'learnpress' ),
288 - $slug
289 - )
290 - );
291 - }
292 - } catch ( Throwable $e ) {
293 - return new WP_Error( 'lp_user_slug_update_failed', $e->getMessage() );
294 - }
295 -
296 - return $slug;
297 - }
298 -
299 - /**
300 - * Get upload profile src.
301 - *
302 - * @return string
303 - */
304 - public function get_image_url(): string {
305 - if ( ! empty( $this->image_url ) ) {
306 - return $this->image_url;
307 - }
308 -
309 - $profile_picture = $this->get_meta_value_by_key( self::META_KEY_IMAGE, '' );
310 - if ( ! empty( $profile_picture ) ) {
311 - // Check if hase slug / at the beginning of the path, if not add it.
312 - $slash = substr( $profile_picture, 0, 1 ) === '/' ? '' : '/';
313 - $profile_picture = $slash . $profile_picture;
314 - // End check.
315 - $upload = learn_press_user_profile_picture_upload_dir();
316 - $file_path = $upload['basedir'] . $profile_picture;
317 -
318 - if ( file_exists( $file_path ) ) {
319 - $this->image_url = $upload['baseurl'] . $profile_picture;
320 - } else { // For remote url.
321 - $this->image_url = $profile_picture;
322 - }
323 - }
324 -
325 - return $this->image_url;
326 - }
327 -
328 - /**
329 - * Get upload cover image src.
330 - *
331 - * @return string
332 - * @since 4.2.7.2
333 - * @version 1.0.0
334 - */
335 - public function get_cover_image_url(): string {
336 - $cover_image = $this->get_meta_value_by_key( self::META_KEY_COVER_IMAGE, '' );
337 - if ( ! empty( $cover_image ) ) {
338 - // Check if hase slug / at the beginning of the path, if not add it.
339 - $slash = substr( $cover_image, 0, 1 ) === '/' ? '' : '/';
340 - $cover_image = $slash . $cover_image;
341 - // End check.
342 - $upload = learn_press_user_profile_picture_upload_dir();
343 - $file_path = $upload['basedir'] . $cover_image;
344 -
345 - if ( file_exists( $file_path ) ) {
346 - return $upload['baseurl'] . $cover_image;
347 - } else { // For remote url.
348 - return $cover_image;
349 - }
350 - }
351 -
352 - return '';
353 - }
354 -
355 - /**
356 - * Set cover image url.
357 - *
358 - * @param string $url
359 - *
360 - * @return void
361 - * @since 4.2.7.2
362 - * @version 1.0.0
363 - */
364 - public function set_cover_image_url( string $url ) {
365 - $this->set_meta_value_by_key( self::META_KEY_COVER_IMAGE, $url );
366 - }
367 -
368 - /**
369 - * Delete cover image.
370 - *
371 - * @return void
372 - * @since 4.2.7.2
373 - * @version 1.0.0
374 - */
375 - public function delete_cover_image() {
376 - $upload_dir = learn_press_user_profile_picture_upload_dir();
377 -
378 - // Delete old image if exists
379 - $image_path = $this->get_meta_value_by_key( UserModel::META_KEY_COVER_IMAGE, '' );
380 - if ( $image_path ) {
381 - $path = $upload_dir['basedir'] . '/' . $image_path;
382 -
383 - if ( file_exists( $path ) ) {
384 - LP_WP_Filesystem::instance()->unlink( $path );
385 - }
386 - }
387 -
388 - // Save empty string to database.
389 - $this->set_cover_image_url( '' );
390 - }
391 -
392 - /**
393 - * Get display name
394 - *
395 - * Hook from function get_the_author_meta of WP
396 - *
397 - * @return string
398 - * @uses get_the_author_meta
399 - * @version 1.0.1
400 - * @since 4.2.7
401 - */
402 - public function get_display_name(): string {
403 - return apply_filters(
404 - 'get_the_author_display_name',
405 - $this->display_name,
406 - $this->get_id(),
407 - $this->get_id()
408 - );
409 - }
410 -
411 - /**
412 - * Get url instructor.
413 - *
414 - * @move from LP_User
415 - * @return string
416 - * @version 1.0.1
417 - * @since 4.2.3
418 - */
419 - public function get_url_instructor(): string {
420 -
421 - $single_instructor_link = '';
422 -
423 - try {
424 - $user_name = $this->get_slug_link();
425 - $single_instructor_page_id = learn_press_get_page_id( 'single_instructor' );
426 - if ( ! $single_instructor_page_id ) {
427 - return $single_instructor_link;
428 - }
429 - $single_instructor_link = trailingslashit(
430 - trailingslashit( get_page_link( $single_instructor_page_id ) ) . $user_name
431 - );
432 - } catch ( Throwable $e ) {
433 - error_log( __METHOD__ . ': ' . $e->getMessage() );
434 - }
435 -
436 - return $single_instructor_link;
437 - }
438 -
439 - /**
440 - * Get profile avatar url
441 - * 1. Get upload avatar src
442 - * 2. If not exists, get form Gravatar
443 - * 3. If not exists, get default image
444 - *
445 - * @return string
446 - * @since 4.2.7.2
447 - * @version 1.0.1
448 - */
449 - public function get_avatar_url(): string {
450 - $avatar_url = $this->get_upload_avatar_src();
451 - if ( empty( $avatar_url ) ) {
452 - // Get form Gravatar.
453 - $args = learn_press_get_avatar_thumb_size();
454 - $args = apply_filters( 'learn-press/gravatar/args', $args );
455 - $avatar_url = get_avatar_url( $this->get_id(), $args );
456 - // If not exists, get default avatar.
457 - if ( empty( $avatar_url ) ) {
458 - $avatar_url = LP_PLUGIN_URL . 'assets/images/avatar-default.png';
459 - }
460 - }
461 -
462 - return $avatar_url;
463 - }
464 -
465 - /**
466 - * Get upload avatar src.
467 - *
468 - * @return string
469 - * @since 4.2.7.2
470 - * @version 1.0.0
471 - * @move from get_upload_profile_src method on LP_Profile class
472 - */
473 - public function get_upload_avatar_src(): string {
474 - $uploaded_avatar_src = '';
475 - $profile_picture = $this->get_meta_value_by_key( self::META_KEY_IMAGE, '' );
476 -
477 - if ( $profile_picture ) {
478 - // Check if hase slug / at the beginning of the path, if not, add it.
479 - $slash = substr( $profile_picture, 0, 1 ) === '/' ? '' : '/';
480 - $profile_picture = $slash . $profile_picture;
481 - // End check.
482 - $upload = learn_press_user_profile_picture_upload_dir();
483 - $file_path = $upload['basedir'] . $profile_picture;
484 -
485 - if ( file_exists( $file_path ) ) {
486 - $uploaded_avatar_src = $upload['baseurl'] . $profile_picture;
487 - }
488 - }
489 -
490 - return apply_filters( 'learn-press/user/upload-avatar-src', $uploaded_avatar_src, $this );
491 - }
492 -
493 - /**
494 - * Get links socials of use on Profile page
495 - * Icon is svg
496 - *
497 - * @move from LP_Abstract_User
498 - * @return array
499 - * @since 4.2.3
500 - * @version 1.0.2
501 - */
502 - public function get_profile_social(): array {
503 - $socials = array();
504 - $extra_info = learn_press_get_user_extra_profile_info( $this->get_id() );
505 -
506 - if ( $extra_info ) {
507 - foreach ( $extra_info as $k => $v ) {
508 - if ( empty( $v ) ) {
509 - continue;
510 - }
511 -
512 - switch ( $k ) {
513 - case 'facebook':
514 - $i = '<i class="lp-user-ico lp-icon-facebook"></i>';
515 - break;
516 - case 'twitter':
517 - $i = '<i class="lp-user-ico lp-icon-twitter"></i>';
518 - break;
519 - case 'linkedin':
520 - $i = '<i class="lp-user-ico lp-icon-linkedin"></i>';
521 - break;
522 - case 'youtube':
523 - $i = '<i class="lp-user-ico lp-icon-youtube-play"></i>';
524 - break;
525 - default:
526 - $i = sprintf(
527 - '<i class="lp-user-ico lp-icon-%s"></i>',
528 - esc_attr( $k )
529 - );
530 - }
531 -
532 - $icon = apply_filters(
533 - 'learn-press/user-profile-social-icon',
534 - $i,
535 - $k,
536 - $this->get_id(),
537 - $this
538 - );
539 - $socials[ $k ] = sprintf(
540 - '<a href="%s">%s</a>',
541 - esc_url_raw( $v ),
542 - $icon
543 - );
544 - }
545 - }
546 -
547 - return apply_filters( 'learn-press/user-profile-socials', $socials, $this->get_id(), $this );
548 - }
549 -
550 - /**
551 - * Update data to database.
552 - *
553 - * If user_item_id is empty, insert new data, else update data.
554 - *
555 - * @throws Exception
556 - * @since 4.2.5
557 - * @version 1.0.1
558 - */
559 - public function save( bool $force_save = false ) {
560 - $data = get_object_vars( $this );
561 -
562 - // Check if exists user id.
563 - if ( empty( $this->ID ) ) { // Insert data.
564 - unset( $data['ID'] );
565 - $user_id = wp_insert_user( $data );
566 - } else { // Update data.
567 - if ( ! $force_save ) {
568 - if ( ! current_user_can( 'edit_user', $this->get_id() ) ) {
569 - throw new Exception( __( 'Sorry, you are not allowed to edit this user.' ) );
570 - }
571 - }
572 -
573 - $user_id = wp_update_user( $data );
574 - }
575 -
576 - if ( is_wp_error( $user_id ) ) {
577 - throw new Exception( $user_id->get_error_message() );
578 - } else {
579 - $this->ID = $user_id;
580 - }
581 -
582 - $this->clean_caches();
583 - }
584 -
585 - /**
586 - * Clean caches.
587 - *
588 - * @return void
589 - */
590 - public function clean_caches() {
591 - // Clear cache.
592 - $key_cache = "userModel/find/id/{$this->get_id()}";
593 - $lp_course_cache = new LP_Cache();
594 - $lp_course_cache->clear( $key_cache );
595 - }
596 -
597 - /**
598 - * @return int
599 - */
600 - public function get_id(): int {
601 - return (int) $this->ID;
602 - }
603 -
604 - /**
605 - * Get description of user.
606 - *
607 - * @return string
608 - * @since 4.2.6.9
609 - * @version 1.0.1
610 - */
611 - public function get_description(): string {
612 - return get_the_author_meta( 'description', $this->get_id() );
613 - }
614 -
615 - /**
616 - * Get email of user.
617 - *
618 - * @return string
619 - * @since 4.2.7.4
620 - * @version 1.0.0
621 - */
622 - public function get_email(): string {
623 - return $this->user_email ?? '';
624 - }
625 -
626 - /**
627 - * Get username of user.
628 - *
629 - * @return string
630 - * @since 4.2.7.4
631 - * @version 1.0.0
632 - */
633 - public function get_username(): string {
634 - return $this->user_login ?? '';
635 - }
636 -
637 - /**
638 - * Get statistic info of instructor user
639 - *
640 - * @param array $params
641 - *
642 - * @return array
643 - * @since 4.1.6
644 - * @version 1.0.7
645 - */
646 - public function get_instructor_statistic( array $params = [] ): array {
647 - $statistic = array(
648 - 'total_course' => 0,
649 - 'published_course' => 0,
650 - 'pending_course' => 0,
651 - 'total_student' => 0,
652 - 'student_completed' => 0,
653 - 'student_in_progress' => 0,
654 - );
655 -
656 - try {
657 - $key_cache_first = "instructor/{$this->get_id()}/statistic";
658 - $statistic_cache = LP_Cache::cache_load_first( 'get', $key_cache_first );
659 - if ( $statistic_cache !== false ) {
660 - return $statistic_cache;
661 - }
662 -
663 - $user_id = $this->get_id();
664 - $lp_user_items_db = LP_User_Items_DB::getInstance();
665 - $courseJsonDB = CourseJsonDB::getInstance();
666 -
667 - // Count total user completed course of author
668 - $filter_course = new CourseJsonFilter();
669 - $filter_course->only_fields = array( 'ID' );
670 - $filter_course->post_author = $user_id;
671 - $filter_course->post_status = [ 'publish', 'private' ];
672 - $filter_course->return_string_query = true;
673 - $query_courses_str = $courseJsonDB::getInstance()->get_courses( $filter_course );
674 -
675 - $filter_count_users = new LP_User_Items_Filter();
676 - $filter_count_users->item_type = LP_COURSE_CPT;
677 - $filter_count_users->where[] = "AND item_id IN ({$query_courses_str})";
678 - $count_student_has_status = $lp_user_items_db->count_status_by_items( $filter_count_users );
679 - // Count total user in progress course of author
680 -
681 - // Get total users attend course of author
682 - $filter_count_users = $lp_user_items_db->count_user_attend_courses_of_author( $user_id );
683 - $count_users_attend_courses_of_author = $lp_user_items_db->get_user_courses( $filter_count_users );
684 -
685 - // Get total courses publish of author
686 - $filter_count_courses = $courseJsonDB->count_courses_of_author( $user_id, [ 'publish' ] );
687 - $total_courses_publish_of_author = $courseJsonDB->get_courses( $filter_count_courses );
688 -
689 - // Get total courses of author
690 - $filter_count_courses = $courseJsonDB->count_courses_of_author( $user_id );
691 - $total_courses_of_author = $courseJsonDB->get_courses( $filter_count_courses );
692 -
693 - // Get total courses pending of author
694 - $filter_count_courses = $courseJsonDB->count_courses_of_author( $user_id, [ 'pending' ] );
695 - $total_courses_pending_of_author = $courseJsonDB->get_courses( $filter_count_courses );
696 -
697 - $statistic['total_course'] = $total_courses_of_author;
698 - $statistic['published_course'] = $total_courses_publish_of_author;
699 - $statistic['pending_course'] = $total_courses_pending_of_author;
700 - $statistic['total_student'] = $count_users_attend_courses_of_author;
701 - $statistic['student_completed'] = $count_student_has_status->{LP_COURSE_FINISHED} ?? 0;
702 - $statistic['student_in_progress'] = $count_student_has_status->{LP_COURSE_GRADUATION_IN_PROGRESS} ?? 0;
703 -
704 - $statistic = apply_filters( 'lp/profile/instructor/statistic', $statistic, $this );
705 -
706 - // Set cache first.
707 - LP_Cache::cache_load_first( 'set', $key_cache_first, $statistic );
708 - } catch ( Throwable $e ) {
709 - LP_Debug::error_log( $e );
710 - }
711 -
712 - return $statistic;
713 - }
714 -
715 - /**
716 - * Get statistic info of student user
717 - *
718 - * @return array
719 - * @since 4.1.6
720 - * @version 1.0.0
721 - */
722 - public function get_student_statistic(): array {
723 - $statistic = array(
724 - 'enrolled_courses' => 0,
725 - 'in_progress_course' => 0,
726 - 'finished_courses' => 0,
727 - 'passed_courses' => 0,
728 - 'failed_courses' => 0,
729 - );
730 -
731 - try {
732 - $user_id = $this->get_id();
733 - $lp_user_items_db = LP_User_Items_DB::getInstance();
734 -
735 - // Count status
736 - $filter = new LP_User_Items_Filter();
737 - $filter->user_id = $user_id;
738 - $count_status = $lp_user_items_db->count_status_by_items( $filter );
739 - $total_courses_enrolled = intval( $count_status->{LP_COURSE_PURCHASED} ?? 0 )
740 - + intval( $count_status->{LP_COURSE_ENROLLED} ?? 0 )
741 - + intval( $count_status->{LP_COURSE_FINISHED} ?? 0 );
742 -
743 - $statistic['enrolled_courses'] = $total_courses_enrolled;
744 - $statistic['in_progress_course'] = $count_status->{LP_COURSE_GRADUATION_IN_PROGRESS} ?? 0;
745 - $statistic['finished_courses'] = $count_status->{LP_COURSE_FINISHED} ?? 0;
746 - $statistic['passed_courses'] = $count_status->{LP_COURSE_GRADUATION_PASSED} ?? 0;
747 - $statistic['failed_courses'] = $count_status->{LP_COURSE_GRADUATION_FAILED} ?? 0;
748 - } catch ( Throwable $e ) {
749 - LP_Debug::error_log( $e );
750 - }
751 -
752 - return apply_filters( 'lp/profile/student/statistic', $statistic, $this );
753 - }
754 -
755 - /**
756 - * Check user is instructor or not.
757 - *
758 - * @return bool
759 - * @since 4.2.7.6
760 - * @version 1.0.1
761 - */
762 - public function is_instructor(): bool {
763 - $wp_user = new WP_User( $this );
764 - return user_can( $wp_user, self::ROLE_INSTRUCTOR )
765 - || user_can( $wp_user, self::ROLE_ADMINISTRATOR );
766 - }
767 -
768 - /**
769 - * Get quizzes attend of user.
770 - *
771 - * @param LP_User_Items_Filter|UserItemsFilter $filter
772 - * @param int $total_rows
773 - *
774 - * @return array|int|string|null
775 - * @throws Exception
776 - * @since 4.2.8.2
777 - * @version 1.0.1
778 - */
779 - public function get_quizzes_attend( $filter, int &$total_rows = 0 ) {
780 - $lp_db_user_items = UserItemsDB::getInstance();
781 - $filter->order_by = 'user_item_id';
782 - $filter->order = 'DESC';
783 - $filter->user_id = $this->get_id();
784 - $filter->item_type = LP_QUIZ_CPT;
785 - $filter->ref_type = LP_COURSE_CPT;
786 -
787 - return $lp_db_user_items->get_user_items( $filter, $total_rows );
788 - }
789 -
790 - /**
791 - * Get roles of user.
792 - *
793 - * @return array
794 - * @since 4.3.6
795 - * @version 1.0.0
796 - */
797 - public function get_roles(): array {
798 - $user = new WP_User( $this );
799 - return is_array( $user->roles ) ? $user->roles : [];
800 - }
801 -}
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 +}