PluginProbe
LearnPress – WordPress LMS Plugin for Create and Sell Online Courses / 4.1.6.9
LearnPress – WordPress LMS Plugin for Create and Sell Online Courses v4.1.6.9
4.4.8 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 All 139 releases
learnpress / inc / user / abstract-lp-user.php

abstract-lp-user.php in LearnPress – WordPress LMS Plugin for Create and Sell Online Courses 4.1.6.9, at inc/user/abstract-lp-user.php

2,279 lines 55.5 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 /**
4 * Class LP_Abstract_User
5 *
6 * @author ThimPress
7 * @package LearnPress/Classes
8 * @version 3.0.0
9 */
10
11 /**
12 * Prevent loading this file directly
13 */
14 defined( 'ABSPATH' ) || exit();
15
16 if ( ! class_exists( 'LP_Abstract_User' ) ) {
17
18 /**
19 * Class LP_Abstract_User
20 */
21 class LP_Abstract_User extends LP_Abstract_Object_Data {
22
23 /**
24 * @var WP_User object
25 */
26 public $user = false;
27
28 /**
29 * @var null
30 */
31 public $profile_picture_src = null;
32
33 /**
34 * @var null
35 */
36 public $profile_picture_type = null;
37
38 /**
39 * @var array
40 */
41 protected $_data = array(
42 'email' => '',
43 'user_login' => '',
44 'description' => '',
45 'first_name' => '',
46 'last_name' => '',
47 'nickname' => '',
48 'display_name' => '',
49 'date_created' => '',
50 'date_modified' => '',
51 'role' => '',
52 'roles' => array(),
53 );
54
55 /**
56 * @var LP_User_CURD
57 */
58 protected $_curd = null;
59
60 /**
61 * LP_Abstract_User constructor.
62 *
63 * @param int $the_user
64 * @param array $args
65 */
66 public function __construct( $the_user = 0, $args = array() ) {
67
68 parent::__construct( $the_user, $args );
69
70 $this->_curd = new LP_User_CURD();
71
72 if ( is_numeric( $the_user ) && $the_user > 0 ) {
73 $this->set_id( $the_user );
74 } elseif ( $the_user instanceof self ) {
75 $this->set_id( absint( $the_user->get_id() ) );
76 } elseif ( ! empty( $the_user->ID ) ) {
77 $this->set_id( absint( $the_user->ID ) );
78 }
79
80 if ( $this->get_id() > 0 ) {
81 $this->load();
82 }
83
84 }
85
86 /**
87 * Load user data from curd
88 */
89 public function load() {
90 $this->_curd->load( $this );
91 }
92
93 /**
94 * Get data for a course user has enrolled.
95 *
96 * @param int $course_id .
97 *
98 * @return LP_User_Item_Course|bool
99 * @version 3.1.3
100 * @editor tungnx
101 * @modify 4.1.3
102 */
103 public function get_course_data( int $course_id = 0 ) {
104 $lp_user_items_db = LP_User_Items_DB::getInstance();
105
106 try {
107 if ( ! $course_id ) {
108 $course_id = get_the_ID();
109 }
110
111 /*if ( $this instanceof LP_User_Guest ) {
112 throw new Exception( 'User is Guest' );
113 }*/
114
115 $filter = new LP_User_Items_Filter();
116 $filter->item_id = $course_id;
117 $filter->user_id = $this->get_id();
118 $last_user_course = $lp_user_items_db->get_last_user_course( $filter );
119
120 if ( $last_user_course ) {
121 $object_course_data = new LP_User_Item_Course( $last_user_course );
122 } else {
123 $object_course_data = false;
124 /**
125 * Todo: some themes still not check false, so still use below code.\
126 * @editor tungnx 4.1.6.9
127 */
128 $object_course_data = new LP_User_Item_Course( $course_id );
129 }
130 } catch ( Throwable $e ) {
131 $object_course_data = false;
132 }
133
134 return $object_course_data;
135 }
136
137 /**
138 * Get user course item.
139 *
140 * @param int $item_id
141 * @param int $course_id
142 * @param string $field - Optional. Value of field to return.
143 *
144 * @return LP_User_Item_Quiz|LP_User_Item|bool
145 */
146 public function get_item_data( $item_id, $course_id, $field = '' ) {
147 $user_item = $this->get_user_item( $item_id, $course_id );
148
149 switch ( $field ) {
150 case 'end_time':
151 return $user_item->get_end_time();
152 }
153
154 return $user_item;
155 }
156
157 /**
158 * Get data for a item user started in table user-items
159 *
160 * @param int $item_id
161 * @param int $course_id
162 *
163 * @return LP_User_Item_Quiz|LP_User_Item|bool
164 */
165 public function get_user_item( $item_id, $course_id ) {
166 $data = false;
167
168 $course_data = $this->get_course_data( $course_id );
169
170 if ( $course_data ) {
171 $data = $course_data->get_item( $item_id );
172 }
173
174 return $data;
175 }
176
177 /**
178 * Return TRUE if user has used function for checking the question.
179 *
180 * @param int $question_id
181 * @param int $quiz_id
182 * @param int $course_id
183 *
184 * @return mixed
185 * @since 3.0.0
186 */
187 public function has_checked_question( $question_id, $quiz_id, $course_id = 0 ) {
188 $checked = false;
189
190 if ( $this->get_quiz_data( $quiz_id, $course_id ) ) {
191 $data = $this->get_quiz_data( $quiz_id, $course_id );
192 $checked = $data->has_checked_question( $question_id );
193 }
194
195 return apply_filters(
196 'learn-press/user/checked-question',
197 $checked,
198 $question_id,
199 $quiz_id,
200 $course_id,
201 $this->get_id()
202 );
203 }
204
205 /**
206 * Magic function to get user data
207 *
208 * @param $key
209 *
210 * @return bool
211 */
212 public function __get( $key ) {
213 $return = false;
214
215 if ( strtolower( $key ) !== 'id' ) {
216 _deprecated_argument( __CLASS__ . '::' . $key, '3.0.0' );
217 }
218
219 if ( ! empty( $this->user->data->{$key} ) ) {
220 $return = $this->user->data->{$key};
221 } else {
222 if ( isset( $this->{$key} ) ) {
223 $return = $this->{$key};
224 } elseif ( strpos( $key, '_lp_' ) === false ) {
225 $key = '_lp_' . $key;
226 $return = get_user_meta( $this->get_id(), $key, true );
227 if ( ! empty( $value ) ) {
228 $this->$key = $return;
229 }
230 }
231 }
232
233 return $return;
234 }
235
236 /**
237 * Check if a course is exists then return it's ID.
238 * Try to get it from global.
239 *
240 * @param int $course_id
241 * @param string $return
242 *
243 * @return bool|false|int|LP_Course
244 */
245 protected function _get_course( $course_id, $return = 'id' ) {
246
247 // if $course_id is not passed then try to get it from global
248 if ( ! $course_id && learn_press_is_course() ) {
249 $course_id = get_the_ID();
250 }
251
252 // Validate course
253 $course = learn_press_get_course( $course_id );
254
255 if ( $course ) {
256 switch ( $return ) {
257 case 'id':
258 return $course_id;
259 case 'object':
260 return $course;
261 }
262 }
263
264 return false;
265 }
266
267 /**
268 *
269 * @param int $item_id
270 * @param int $course_id
271 *
272 * @return bool
273 *
274 * @since 3.0.0
275 */
276 protected function _verify_course_item( $item_id, $course_id = 0 ) {
277 $course = $this->_get_course( $course_id, 'object' );
278
279 if ( false !== $course ) {
280 return $course->has_item( $item_id ) ? $course_id : false;
281 }
282
283 return false;
284 }
285
286 /**
287 * Return TRUE if an item has a status.
288 *
289 * @param array $statuses
290 * @param int $item_id
291 * @param int $course_id
292 *
293 * @return mixed
294 *
295 * @since 3.0.0
296 */
297 public function has_item_status( $statuses, $item_id, $course_id ) {
298 settype( $statuses, 'array' );
299 $status = $this->get_item_status( $item_id, $course_id );
300
301 //Todo: tungnx
302 //$status = $this->getItemStatus( $item_id, $course_id );
303
304 return apply_filters(
305 'learn-press/user-has-item-status',
306 in_array( $status, $statuses ),
307 $statuses,
308 $item_id,
309 $course_id,
310 $this->get_id()
311 );
312 }
313
314 /**
315 * Get all records of an item.
316 *
317 * @param int $item_id
318 * @param int $course_id
319 * @param bool $return_last
320 *
321 * @return bool|mixed
322 */
323 public function get_item_archive( $item_id, $course_id = 0, $return_last = false ) {
324 $records = LP_Object_Cache::get(
325 'course-item-' . $this->get_id() . '-' . $course_id . '-' . $item_id,
326 'lp-user-course-items'
327 );
328
329 if ( $records ) {
330 // $records = array_filter( $records );
331 }
332
333 if ( $return_last && is_array( $records ) ) {
334 $records = reset( $records );
335 }
336
337 return $records;
338 }
339
340 /**
341 * Count number of rows for an item in user-items
342 *
343 * @param int $item_id
344 * @param int $course_id
345 *
346 * @return int
347 * @editor tungnx
348 * @modify 4.1.3 - comment - not user
349 */
350 /*public function count_item_archive( $item_id, $course_id = 0 ) {
351 $count = 0;
352
353 if ( $items = $this->get_item_archive( $item_id, $course_id ) ) {
354 $count = sizeof( $items );
355 }
356
357 return $count;
358 }*/
359
360 /**
361 * Check quiz can retake?
362 *
363 * @param [type] $quiz_id
364 * @param [type] $course_id
365 *
366 * @return boolean
367 */
368 public function has_retake_quiz( $quiz_id, $course_id ): bool {
369 $user_quiz = $this->get_item_data( $quiz_id, $course_id );
370 $flag = false;
371
372 if ( $user_quiz ) {
373 $retaken = $user_quiz->get_retaken_count();
374 $retake_config = get_post_meta( $quiz_id, '_lp_retake_count', true );
375
376 if ( $retake_config == '-1' ) { // For no limit
377 $flag = true;
378 } elseif ( absint( $retaken ) < absint( $retake_config ) ) {
379 $flag = true;
380 }
381 }
382
383 return apply_filters( 'lp/quiz/can-retake', $flag );
384 }
385
386 /**
387 * Get quiz status for the user
388 *
389 * @param int $quiz_id
390 * @param int $course_id
391 *
392 * @return mixed
393 */
394 public function get_quiz_status( $quiz_id, $course_id = 0 ) {
395 return $this->get_item_status( $quiz_id, $course_id );
396 }
397
398 /**
399 * Get quiz status for the user
400 *
401 * @param int $lesson_id
402 * @param int $course_id
403 *
404 * @return mixed
405 */
406 public function get_lesson_status( $lesson_id, $course_id = 0 ) {
407 return $this->get_item_status( $lesson_id, $course_id );
408 }
409
410 /**
411 * @param int $item_id
412 * @param int $course_id
413 * @param bool $last
414 *
415 * @return mixed
416 * @since 3.0.0
417 */
418 public function get_item( $item_id, $course_id = 0, $last = false ) {
419 if ( ! $course_id ) {
420 $course_id = get_the_ID();
421 }
422
423 if ( ! $course_id ) {
424 return false;
425 }
426
427 $course_data = $this->get_course_data( $course_id );
428 if ( $course_data ) {
429 return $course_data->get_item( $item_id );
430 }
431
432 return false;
433 }
434
435 /**
436 * @param int $item_id
437 * @param int $course_id
438 *
439 * @return mixed
440 * @since 3.0.0
441 * @version 1.0.1
442 * @editor tungnx
443 * @modify 4.1.4.1
444 */
445 public function get_item_grade( $item_id, $course_id = 0 ) {
446 if ( ! $course_id ) {
447 $course_id = get_the_ID();
448 }
449
450 $grade = false;
451
452 $course_data = $this->get_course_data( $course_id );
453
454 if ( $course_data ) {
455 $grade = $course_data->get_item_result( $item_id, 'grade' );
456 }
457
458 return apply_filters( 'learn-press/user-item-grade', $grade, $item_id, $this->get_id(), $course_id );
459 }
460
461 /**
462 * Get current status of an item for user.
463 *
464 * @param int $item_id
465 * @param int $course_id
466 *
467 * @return bool|mixed
468 */
469 public function get_item_status( $item_id, $course_id = 0 ) {
470 $status = '';
471
472 if ( ! $course_id ) {
473 $course_id = get_the_ID();
474 }
475
476 if ( ! $course_id ) {
477 return $status;
478 }
479
480 $item = $this->get_item( $item_id, $course_id, true );
481
482 if ( false !== $item ) {
483 $status = $item['status'];
484 }
485
486 return apply_filters( 'learn-press/user-item-status', $status, $item_id, $this->get_id(), $course_id );
487 }
488
489 /**
490 * To rewrite get_item_status on abstract-lp-user.
491 *
492 * @throws Exception
493 * @author tungnx
494 */
495 /*public function getItemStatus( $item_id, $course_id ) {
496 $status = LP_User_Items_DB::getInstance()->get_item_status( $item_id, $course_id );
497
498 return $status;
499 }*/
500
501 /**
502 * Update viewing item data into database.
503 *
504 * @param int $item_id
505 * @param int $course_id
506 *
507 * @return bool
508 * @since 3.0.0
509 * @version 4.0.1
510 * @depecated 4.1.6.9
511 */
512 /*public function maybe_update_item( $item_id, $course_id ) {
513 $return = false;
514
515 try {
516 $course_data = $this->get_course_data( $course_id );
517
518 if ( $course_data ) {
519 $item = $course_data->get_item( $item_id );
520
521 if ( ! $item ) {
522 $item = LP_User_Item::get_item_object( $item_id );
523
524 if ( ! $item ) {
525 return $return;
526 }
527
528 if ( $item instanceof LP_User_Item_Quiz ) {
529 return $return;
530 }
531
532 $item->set_ref_id( $course_id );
533 $item->set_parent_id( $course_data->get_user_item_id() );
534
535 $return = $item->update();
536 }
537 }
538 } catch ( Throwable $e ) {
539 error_log( $e->getMessage() );
540 }
541
542 return $return;
543 }*/
544
545 /**
546 * Get item user has accessed in last time.
547 *
548 * @param int $course_id
549 * @param bool $permalink - Optional. TRUE will return permalink instead of ID.
550 *
551 * @return mixed
552 * @depecated 4.1.6.9
553 */
554 public function get_current_item( $course_id, $permalink = false ) {
555 _deprecated_function( __FUNCTION__, '4.1.6.9' );
556 return 0;
557 /*$course_data = $this->get_course_data( $course_id );
558 if ( ! $course_data ) {
559 return false;
560 }
561
562 $course = learn_press_get_course( $course_id );
563 $id = learn_press_get_user_item_meta( $course_data->get_user_item_id(), '_current_item' );
564 if ( ! $id || $this->has_completed_item( $id, $course_id ) ) {
565 $items = $course->get_items( '', false );
566 if ( $items ) {
567 foreach ( $items as $item_id ) {
568 if ( ! $this->has_completed_item( $item_id, $course_id ) ) {
569 $id = $item_id;
570 break;
571 }
572 }
573
574 if ( ! $id ) {
575 $id = reset( $items );
576 }
577 }
578
579 if ( $id ) {
580 learn_press_update_user_item_meta( $course_data->get_user_item_id(), '_current_item', $id );
581 }
582 }
583
584 if ( $permalink && $id ) {
585 return apply_filters(
586 'learn-press/current-course-item-permalink',
587 $course->get_item_link( $id ),
588 $course_id,
589 $this->get_id()
590 );
591 } else {
592 return apply_filters( 'learn-press/current-course-item', $id, $course_id, $this->get_id() );
593 }*/
594 }
595
596 /**
597 * Get current question's ID/Permalink inside quiz.
598 *
599 * @param int $quiz_id
600 * @param int $course_id
601 * @param bool $permalink
602 *
603 * @return bool|int|string
604 * @depecated 4.1.6.9
605 */
606 /*public function get_current_question( $quiz_id, $course_id, $permalink = false ) {
607 _deprecated_function( sprintf( '%s::%s', __CLASS__, __FUNCTION__ ), '4.0.0' );
608 }*/
609
610 /**
611 * Get previous Question
612 *
613 * @param null $quiz_id
614 * @param int $course_id
615 * @param false $permalink
616 * @depecated 4.1.6.9
617 */
618 /*public function get_prev_question( $quiz_id = null, $course_id = 0, $permalink = false ) {
619 _deprecated_function( sprintf( '%s::%s', __CLASS__, __FUNCTION__ ), '4.0.0' );
620 }*/
621
622 /**
623 * Get next Question
624 *
625 * @param null $quiz_id
626 * @param int $course_id
627 * @param false $permalink
628 * @depecated 4.1.6.9
629 */
630 /*public function get_next_question( $quiz_id = null, $course_id = 0, $permalink = false ) {
631 _deprecated_function( sprintf( '%s::%s', __CLASS__, __FUNCTION__ ), '4.0.0' );
632 }*/
633
634 /**
635 * Checks if has status of a quiz for user
636 *
637 * @param string|array $statuses
638 * @param int $quiz_id
639 * @param int $course_id
640 * @param boolean $force
641 *
642 * @return bool
643 */
644 public function has_quiz_status( $statuses, $quiz_id, $course_id = 0, $force = false ) {
645 $status = $this->get_quiz_status( $quiz_id, $course_id );
646
647 settype( $statuses, 'array' );
648
649 return apply_filters(
650 'learn_press_user_has_quiz_status',
651 in_array( $status, $statuses ),
652 $statuses,
653 $status,
654 $quiz_id,
655 $course_id,
656 $this->get_id()
657 );
658 }
659
660 /**
661 * Get current results of a quiz
662 *
663 * @param int $quiz_id
664 * @param int $course_id
665 * @param string $prop
666 *
667 * @return mixed
668 */
669 public function get_quiz_results( $quiz_id, $course_id = 0, $prop = 'result' ) {
670 $user_quiz = $this->get_item_data( $quiz_id, $course_id );
671
672 return $user_quiz ? $user_quiz->get_results( $prop ) : false;
673 }
674
675 /**
676 * Get current progress of user's quiz.
677 *
678 * @param int $quiz_id
679 * @param int $course_id
680 *
681 * @return LP_User_Item_Quiz|false
682 */
683 public function get_quiz_data( $quiz_id, $course_id = 0 ) {
684 $result = false;
685 $course_result = $this->get_course_data( $course_id );
686 if ( $course_result ) {
687 $result = $course_result->get_item( $quiz_id );
688 }
689
690 return $result;
691 }
692
693 /**
694 * Mark question that user has checked.
695 *
696 * @param int $question_id
697 * @param int $quiz_id
698 * @param int $course_id
699 *
700 * @return WP_Error|mixed
701 * @since 3.0.0
702 * @editor tungnx
703 * @modify 4.1.4.1 - comment - not use
704 */
705 /*public function check_question( $question_id, $quiz_id, $course_id ) {
706 if ( ! $course = learn_press_get_course( $course_id ) ) {
707 return false;
708 }
709
710 if ( ! $course->has_item( $quiz_id ) ) {
711 return false;
712 }
713
714 $quiz = $course->get_item( $quiz_id );
715
716 if ( ! $quiz->has_question( $question_id ) ) {
717 return false;
718 }
719
720 $quiz_data = $this->get_item_data( $quiz_id, $course_id );
721
722 return $quiz_data->check_question( $question_id );
723 }*/
724
725 /**
726 * Mark question that user has checked.
727 *
728 * @param int $question_id
729 * @param int $quiz_id
730 * @param int $course_id
731 *
732 * @return WP_Error|mixed
733 * @since 3.0.0
734 */
735 public function hint( $question_id, $quiz_id, $course_id ) {
736 $course = learn_press_get_course( $course_id );
737 if ( ! $course ) {
738 return false;
739 }
740
741 if ( ! $course->has_item( $quiz_id ) ) {
742 return false;
743 }
744
745 /**
746 * @var $quiz LP_Quiz
747 */
748 $quiz = $course->get_item( $quiz_id );
749 if ( ! $quiz instanceof LP_Course_Item_Quiz ) {
750 return false;
751 }
752
753 if ( ! $quiz->has_question( $question_id ) ) {
754 return false;
755 }
756
757 $quiz_data = $this->get_item_data( $quiz_id, $course_id );
758 $remain = $quiz_data->hint( $question_id );
759 if ( false === $remain ) {
760 return new WP_Error( 1001, __( 'You can not hint question.', 'learnpress' ) );
761 }
762
763 return $remain;
764 }
765
766 /**
767 * Return true if check answer is enabled.
768 *
769 * @param int $quiz_id
770 * @param int $course_id
771 *
772 * @return bool
773 * @deprecated 4.1.4.1
774 */
775 /*public function can_check_answer( $quiz_id, $course_id = 0 ) {
776 _deprecated_function( __FUNCTION__, '4.1.4.1' );
777 if ( ! $course_id ) {
778 $course_id = get_the_ID();
779 }
780
781 if ( $quiz_data = $this->get_item_data( $quiz_id, $course_id ) ) {
782 return $quiz_data->can_check_answer();
783 }
784
785 return false;
786 }*/
787
788 /**
789 * Return true if check answer is enabled.
790 *
791 * @param int $quiz_id
792 * @param int $course_id
793 *
794 * @return bool
795 * @depecated 4.1.6.9
796 */
797 /*public function can_hint_answer( $quiz_id, $course_id = 0 ) {
798
799 if ( ! $course_id ) {
800 $course_id = get_the_ID();
801 }
802
803 if ( $quiz_data = $this->get_item_data( $quiz_id, $course_id ) ) {
804 return $quiz_data->can_hint_answer();
805 }
806
807 return false;
808 }*/
809
810
811 public function get_quiz_last_results( $quiz_id ) {
812 $results = $this->get_course_info( $quiz_id );
813 if ( $results ) {
814 $results = reset( $results );
815 }
816
817 return apply_filters( 'learn_press_user_quiz_last_results', $results, $quiz_id, $this );
818 }
819
820 /**
821 * Get history of a quiz for an user
822 *
823 * @param int $quiz_id
824 * @param int $course_id
825 * @param int $history_id
826 * @param bool $force
827 *
828 * @return mixed|null|void
829 * @editor tungnx
830 * @modify 4.1.4.1 - comment - not use
831 */
832 /*public function get_quiz_history( $quiz_id, $course_id = 0, $history_id = null, $force = false ) {
833 $course_id = $this->_get_course( $course_id );
834
835 $course = learn_press_get_course( $course_id );
836 if ( $course ) {
837 $quizzes = $course->get_quizzes( 'ID' );
838 } else {
839 $quizzes = array();
840 }
841 $key = $this->get_id() . '-' . $course_id . '-' . $quiz_id;
842
843 $cached = LP_Cache::get_quiz_history( false, array() );
844
845 if ( ( ! array_key_exists( $key, $cached ) || $force ) && $quizzes && in_array( $quiz_id, $quizzes ) ) {
846 global $wpdb;
847 $t1 = $wpdb->learnpress_user_items;
848 $t2 = $wpdb->learnpress_user_itemmeta;
849 $in = array_fill( 0, sizeof( $quizzes ), '%d' );
850 $prepare_params = array_merge(
851 array( 'lp_quiz', $this->get_id(), $course_id ),
852 $quizzes
853 );
854 $query = $wpdb->prepare(
855 "
856 SELECT *
857 FROM $t1 uq
858 WHERE uq.item_type = %s
859 AND uq.user_id = %d
860 AND uq.ref_id = %d
861 AND uq.item_id IN(" . join( ',', $in ) . ')
862 ORDER BY uq.user_item_id DESC
863 ',
864 $prepare_params
865 );
866
867 $history = array();
868 foreach ( $quizzes as $_quiz_id ) {
869 $history[ $this->get_id() . '-' . $course_id . '-' . $_quiz_id ] = array();
870 }
871 if ( $results = $wpdb->get_results( $query ) ) {
872 $item_ids = array();
873 foreach ( $results as $result ) {
874 $item_ids[] = $result->user_item_id;
875 $cache_key = $this->get_id() . '-' . $course_id . '-' . $result->item_id;
876 if ( empty( $history[ $cache_key ] ) ) {
877 $history[ $cache_key ] = array();
878 }
879 // limit newest 10 items
880 if ( sizeof( $history[ $cache_key ] ) >= 10 ) {
881 // break;
882 }
883
884 $history[ $cache_key ][ $result->user_item_id ] = (object) array(
885 'history_id' => $result->user_item_id,
886 'start' => $result->start_time,
887 'end' => $result->end_time,
888 'status' => $result->status,
889 'question' => '',
890 'questions' => array(),
891 'question_answers' => array(),
892 );
893 }
894 if ( $item_ids && $meta = $this->_get_quiz_meta( $item_ids ) ) {
895 $maps = array(
896 'questions' => 'questions',
897 'current_question' => 'question',
898 'question_answers' => 'question_answers',
899 'question_checked' => 'question_checked',
900 );
901 foreach ( $meta as $k => $v ) {
902 $_key = $this->get_id() . '-' . $course_id . '-' . $v->item_id;
903 if ( empty( $history[ $_key ] ) ) {
904 continue;
905 }
906 if ( empty( $history[ $_key ][ $v->user_item_id ] ) ) {
907 continue;
908 }
909 $obj_key = ! empty( $maps[ $v->meta_key ] ) ? $maps[ $v->meta_key ] : $v->meta_key;
910 if ( ! $obj_key ) {
911 continue;
912 }
913 $history[ $_key ][ $v->user_item_id ]->{$obj_key} = LP_Helper::maybe_unserialize( $v->meta_value );
914 }
915 }
916 }
917
918 if ( $history ) {
919 foreach ( $history as $k1 => $v1 ) {
920 if ( empty( $cached[ $k1 ] ) ) {
921 $cached[ $k1 ] = $v1;
922 continue;
923 }
924 foreach ( $v1 as $k2 => $v2 ) {
925 $cached[ $k1 ][ $k2 ] = $v2;
926 }
927 }
928 }
929 LP_Cache::set_quiz_history( $cached );
930 }
931
932 return apply_filters(
933 'learn_press_user_quiz_history',
934 isset( $cached[ $key ] ) ? $cached[ $key ] : array(),
935 $this,
936 $quiz_id
937 );
938 }*/
939
940 /**
941 * @editor tungnx
942 * @modify 4.1.4.1 - comment - not use
943 */
944 /*private function _get_quiz_meta( $user_item_id ) {
945 global $wpdb;
946 settype( $user_item_id, 'array' );
947 $in = array_fill( 0, sizeof( $user_item_id ), '%d' );
948
949 $query = $wpdb->prepare(
950 "
951 SELECT learnpress_user_item_id as user_item_id, meta_key, meta_value, item_id
952 FROM {$wpdb->prefix}learnpress_user_itemmeta im
953 INNER JOIN {$wpdb->prefix}learnpress_user_items i ON i.user_item_id = im.learnpress_user_item_id
954 WHERE learnpress_user_item_id IN(" . join( ',', $in ) . ')
955 ',
956 $user_item_id
957 );
958
959 return $wpdb->get_results( $query );
960 }*/
961
962 /**
963 * @editor tungnx
964 * @modify 4.1.4.1 comment - not use
965 */
966 /*public function get_current_results( $quiz_id, $course_id = 0 ) {
967 $course_id = $this->_get_course( $course_id );
968
969 $history = $this->get_quiz_history( $quiz_id, $course_id );
970 $current = false;
971 if ( $history ) {
972 $current = reset( $history );
973 }
974
975 return $current;
976 }*/
977
978 /**
979 * Check if user has at least one role.
980 *
981 * @param array|string $roles
982 *
983 * @return array
984 */
985 public function has_role( $roles ) {
986 settype( $roles, 'array' );
987
988 return array_intersect( $roles, $this->get_roles() );
989 }
990
991 /**
992 * Detect the type of user
993 *
994 * @param string|int $type
995 *
996 * @return bool
997 */
998 public function is( $type ) {
999 $is = false;
1000 if ( $type === 'current' ) {
1001 $is = $this->is( get_current_user_id() );
1002 } elseif ( is_string( $type ) ) {
1003 $name = preg_replace( '!LP_User(_?)!', '', get_class( $this ) );
1004 $is = strtolower( $name ) == strtolower( $type );
1005 } elseif ( is_numeric( $type ) ) {
1006 $is = $this->get_id() && ( $this->get_id() == $type );
1007 }
1008
1009 return $is;
1010 }
1011
1012 /**
1013 *
1014 * Check what the user can do
1015 *
1016 * @param $role
1017 *
1018 * @return mixed
1019 * @throws Exception
1020 */
1021 public function can( $role ) {
1022 _deprecated_function( __FUNCTION__, '3.0.8' );
1023 $args = func_get_args();
1024 unset( $args[0] );
1025 $method = 'can_' . preg_replace( '!-!', '_', $role );
1026 $callback = array( $this, $method );
1027 if ( is_callable( $callback ) ) {
1028 return call_user_func_array( $callback, $args );
1029 } else {
1030 throw new Exception( sprintf( __( 'The role %s for user doesn\'t exist', 'learnpress' ), $role ) );
1031 }
1032 }
1033
1034 public function can_edit_item( $item_id, $course_id = 0 ) {
1035 $return = $this->is_admin();
1036
1037 if ( ! $return ) {
1038 $course_id = $this->_get_course( $course_id );
1039
1040 $course_author = learn_press_get_course_user( $course_id );
1041 if ( $course_author && $course_author->get_id() == $this->get_id() ) {
1042 $return = true;
1043 }
1044 }
1045
1046 return apply_filters( 'learn_press_user_can_edit_item', $return, $item_id, $course_id, $this->get_id() );
1047 }
1048
1049 /**
1050 * Check if user can finish course by getting current progress
1051 * and compares with course passing condition.
1052 *
1053 * @param int $course_id
1054 *
1055 * @return bool
1056 * @editor tungnx
1057 * @modify 4.1.3
1058 */
1059 public function can_finish_course( int $course_id ) {
1060 _deprecated_function( __FUNCTION__, '4.1.3', 'is_course_finished' );
1061
1062 return true;
1063 }
1064
1065 /**
1066 * Check if course has any passed status for an user.
1067 * Statuses: depending on value of column `status` in user_items.
1068 * - purchased: bought and order is completed, `start_date` and `end_date` is null
1069 * - enrolled: value of column `status` in user_items is enrolled
1070 * - started: value of column `status` in user_items is started
1071 * - enrolled: value of column `status` in user_items is enrolled
1072 *
1073 * @param int $course_id
1074 * @param string|array $statuses
1075 *
1076 * @return bool
1077 * @since 2.0
1078 */
1079 public function has_course_status( $course_id, $statuses ) {
1080 $status = $this->get_course_status( $course_id );
1081
1082 if ( is_array( $statuses ) ) {
1083 return in_array( $status, $statuses );
1084 } elseif ( is_string( $statuses ) ) {
1085 return $statuses == $status;
1086 }
1087
1088 return false;
1089 }
1090
1091 public function get_completed_items( $course_id ) {
1092 $this->_curd->get_user_items( $this->get_id(), $course_id );
1093
1094 return $this->_curd->get_user_completed_items( $this->get_id(), $course_id );
1095 }
1096
1097 /**
1098 * Finish course
1099 *
1100 * @param int $course_id
1101 *
1102 * @return int|bool
1103 */
1104 public function finish_course( int $course_id ) {
1105 $return = false;
1106 $course = learn_press_get_course( $course_id );
1107
1108 if ( $course ) {
1109 $user_course = $this->get_course_data( $course_id );
1110
1111 $result = $user_course->calculate_course_results();
1112
1113 // Save result for course
1114 LP_User_Items_Result_DB::instance()->update( $user_course->get_user_item_id(), wp_json_encode( $result ) );
1115
1116 if ( $result['pass'] ) {
1117 $graduation = LP_COURSE_GRADUATION_PASSED;
1118 } else {
1119 $graduation = LP_COURSE_GRADUATION_FAILED;
1120 }
1121
1122 $user_course->set_graduation( $graduation );
1123 $user_course->save();
1124
1125 $return = $user_course->complete( 'finished' );
1126
1127 if ( $return ) {
1128 do_action( 'learn-press/user-course-finished', $course_id, $this->get_id(), $return );
1129 }
1130 }
1131
1132 return apply_filters( 'learn-press/user-course-finished-data', $return, $course_id, $this->get_id() );
1133 }
1134
1135 /**
1136 * Check user instructor.
1137 *
1138 * @return bool
1139 */
1140 public function is_instructor(): bool {
1141
1142 $roles = $this->get_data( 'roles' ) ? $this->get_data( 'roles' ) : array();
1143
1144 return in_array( LP_TEACHER_ROLE, $roles );
1145 }
1146
1147 /**
1148 * Check user admin.
1149 *
1150 * @return bool
1151 */
1152 public function is_admin() {
1153 $roles = $this->get_data( 'roles' ) ? $this->get_data( 'roles' ) : array();
1154
1155 return in_array( 'administrator', $roles );
1156 }
1157
1158
1159 public function can_create_course() {
1160 return $this->is_instructor() || $this->is_admin();
1161 }
1162
1163 /**
1164 * Wrap function to check this user is author of a post.
1165 *
1166 * @param int $post_id
1167 *
1168 * @return bool
1169 * @since 3.1.0
1170 */
1171 public function is_author_of( $post_id ) {
1172 return absint( get_post_field( 'post_author', $post_id ) ) === $this->get_id();
1173 }
1174
1175 public function has( $role ) {
1176 _deprecated_function( __FUNCTION__, '3.0.8' );
1177
1178 $args = func_get_args();
1179 unset( $args[0] );
1180 $method = 'has_' . preg_replace( '!-!', '_', $role );
1181 $callback = array( $this, $method );
1182 if ( is_callable( $callback ) ) {
1183 return call_user_func_array( $callback, $args );
1184 } else {
1185 throw new Exception( sprintf( __( 'The role %s for user doesn\'t exist', 'learnpress' ), $role ) );
1186 }
1187 }
1188
1189 public function get( $role ) {
1190 $args = func_get_args();
1191 unset( $args[0] );
1192 $method = 'get_' . preg_replace( '!-!', '_', $role );
1193 $callback = array( $this, $method );
1194 if ( is_callable( $callback ) ) {
1195 return call_user_func_array( $callback, $args );
1196 } else {
1197 throw new Exception( sprintf( __( 'The role %s for user doesn\'t exist', 'learnpress' ), $role ) );
1198 }
1199 }
1200
1201 /**
1202 * Check user has passed course.
1203 *
1204 * @param $course_id
1205 *
1206 * @return mixed
1207 */
1208 public function has_passed_course( $course_id ) {
1209 $user_course = $this->get_course_data( $course_id );
1210
1211 if ( $user_course && $user_course->is_passed() ) {
1212 return true;
1213 } else {
1214 return false;
1215 }
1216 }
1217
1218 /**
1219 * Checks if user has started a quiz
1220 * - FALSE if user has not started quiz
1221 * - String if user has started quiz (status of quiz)
1222 *
1223 * @param int $quiz_id
1224 * @param int $course_id
1225 *
1226 * @return mixed
1227 */
1228 public function has_started_quiz( $quiz_id, $course_id = 0 ) {
1229 $course_id = $this->_get_course( $course_id );
1230 $started = false;
1231 $started = $this->has_quiz_status( array( 'started', 'completed' ), $quiz_id, $course_id );
1232
1233 return apply_filters( 'learn_press_user_started_quiz', $started, $quiz_id, $course_id, $this->get_id() );
1234 }
1235
1236 /**
1237 * Return true if user has completed a quiz
1238 *
1239 * @param int $quiz_id
1240 * @param int $course_id
1241 *
1242 * @return bool
1243 * @depecated 4.1.6.9
1244 */
1245 /*public function has_completed_quiz( $quiz_id, $course_id = 0 ): bool {
1246 return $this->get_item_status( $quiz_id, $course_id ) == 'completed';
1247 }*/
1248
1249 /**
1250 * Mark a lesson is completed for user
1251 *
1252 * @param int $lesson_id
1253 * @param int $course_id
1254 * @param bool $return_wp_error
1255 *
1256 * @return bool|WP_Error
1257 */
1258 public function complete_lesson( $lesson_id, $course_id = 0, $return_wp_error = true ) {
1259 global $wpdb;
1260
1261 try {
1262 do_action( 'learn-press/before-complete-lesson', $lesson_id, $course_id, $this->get_id() );
1263 $course_id = $this->_get_course( $course_id );
1264
1265 $course_data = $this->get_course_data( $course_id );
1266
1267 $result = false;
1268
1269 /**
1270 * If user has stared a lesson, get user lesson information
1271 */
1272 $item = $course_data->get_item( $lesson_id );
1273 if ( $item ) {
1274
1275 if ( $item->is_completed() ) {
1276 throw new Exception(
1277 __( 'You have already completed this lesson.', 'learnpress' ),
1278 LP_COMPLETE_ITEM_FAIL
1279 );
1280 }
1281 // TODO: conflict???
1282 //$time = new LP_Datetime();
1283 $item->set_end_time( current_time( 'mysql', 1 ) );
1284 $item->set_status( 'completed' );
1285 $item->set_graduation( apply_filters( 'learn-press/complete-lesson-graduation', 'passed' ) );
1286
1287 $updated = $item->update( true, true );
1288
1289 if ( is_wp_error( $updated ) ) {
1290 return $return_wp_error ? $updated : false;
1291 } else {
1292 $result = true;
1293 }
1294
1295 //$result = $this->evaluate_course_results( $this->get_id() );
1296 }
1297
1298 do_action( 'learn-press/user-completed-lesson', $lesson_id, $course_id, $this->get_id() );
1299 } catch ( Exception $ex ) {
1300 $result = $return_wp_error ? new WP_Error( $ex->getCode(), $ex->getMessage() ) : false;
1301 }
1302
1303 return $result;
1304 }
1305
1306 /**
1307 * Returns TRUE if user has already completed a lesson
1308 *
1309 * @param int $lesson_id Lesson id.
1310 * @param null $course_id Course id.
1311 *
1312 * @return bool
1313 * @depecated 4.1.6.9
1314 */
1315 public function has_completed_lesson( $lesson_id = 0, $course_id = null ): bool {
1316 return 'completed' === $this->get_item_status( $lesson_id, $course_id );
1317 }
1318
1319 /**
1320 * Return current status of course for user
1321 *
1322 * @param int $course_id
1323 * @param string $field
1324 * @param bool $force
1325 *
1326 * @return mixed
1327 */
1328 public function get_course_info( int $course_id, $field = null, $force = false ) {
1329 $user_data = $this->get_course_data( $course_id );
1330 if ( $user_data ) {
1331 return $user_data->get_results( $field );
1332 }
1333
1334 return false;
1335 }
1336
1337 /**
1338 * @param $course_id
1339 *
1340 * @return int
1341 * @depecated 4.1.6.9
1342 */
1343 public function get_course_history_id( $course_id ) {
1344 $history = $this->get_course_info( $course_id );
1345
1346 return ! empty( $history['history_id'] ) ? $history['history_id'] : 0;
1347 }
1348
1349 /**
1350 * Get current status of a course for user.
1351 *
1352 * @param int $course_id
1353 *
1354 * @return mixed
1355 * @editor tungnx
1356 * @modify 4.1.3
1357 * @version 1.0.1
1358 */
1359 public function get_course_status( int $course_id ): string {
1360 $status = '';
1361
1362 try {
1363 $user_data = $this->get_course_data( $course_id );
1364
1365 if ( $user_data ) {
1366 $status = $user_data->get_status();
1367 }
1368 } catch ( Throwable $e ) {
1369 if ( LP_Debug::is_debug() ) {
1370 error_log( $e->getMessage() );
1371 }
1372 }
1373
1374 return apply_filters( 'learn-press/user-course-status', $status, $course_id, $this->get_id() );
1375 }
1376
1377 /**
1378 * Controls what this user can do with a course.
1379 *
1380 * 0 => No accessible
1381 * 10 => Normal users (like not logged in)
1382 * 20 => Author of course
1383 * 30 => Admin site
1384 * 35 => No require enrollment
1385 * 40 => Ordered but not completed
1386 * 50 => Order is completed but not enrolled
1387 * 60 => User has already enrolled course
1388 * 70 => User has already finished course
1389 *
1390 * @param int $course_id
1391 *
1392 * @return int
1393 * @since 3.1.0
1394 * @editor tungnx
1395 * @modify 4.1.3 - comment - not use
1396 */
1397 /*public function get_course_access_level( $course_id ) {
1398 $access_level = LP_Object_Cache::get(
1399 'course-' . $course_id . '-' . $this->get_id(),
1400 'learn-press/course-access-levels'
1401 );
1402
1403 if ( false === $access_level ) {
1404 $course = learn_press_get_course( $course_id );
1405
1406 if ( ! $course ) {
1407 $access_level = LP_COURSE_ACCESS_LEVEL_0;
1408 } elseif ( $this->is_admin() ) {
1409 $access_level = LP_COURSE_ACCESS_LEVEL_30;
1410 } elseif ( $this->is_author_of( $course_id ) ) {
1411 $access_level = LP_COURSE_ACCESS_LEVEL_20;
1412 } else {
1413 $access_level = LP_COURSE_ACCESS_LEVEL_10;
1414 }
1415
1416 // Default level
1417 $access_level = apply_filters(
1418 'learn-press/course-access-level-default',
1419 $access_level,
1420 $course_id,
1421 $this->get_id()
1422 );
1423 $course_data = $this->get_course_data( $course_id );
1424
1425 if ( $course_data && $course_data->get_user_item_id() ) {
1426 // if ( $course_data->get_access_level() >= 50 ) {
1427 switch ( $course_data->get_status() ) {
1428 case 'completed':
1429 case 'failed':
1430 $access_level = LP_COURSE_ACCESS_LEVEL_60;
1431 break;
1432 case 'in-progress':
1433 case 'enrolled':
1434 $access_level = LP_COURSE_ACCESS_LEVEL_70;
1435 break;
1436 }
1437 // }
1438 } else {
1439 $order = $this->get_course_order( $course_id );
1440
1441 if ( $order ) {
1442 switch ( $order->get_status() ) {
1443 case 'completed':
1444 $access_level = LP_COURSE_ACCESS_LEVEL_50;
1445 break;
1446 default:
1447 $access_level = LP_COURSE_ACCESS_LEVEL_40;
1448 }
1449 }
1450 }
1451
1452 LP_Object_Cache::set(
1453 'course-' . $course_id . '-' . $this->get_id(),
1454 $access_level,
1455 'learn-press/course-access-levels'
1456 );
1457 }
1458
1459 return apply_filters( 'learn-press/course-access-level', $access_level, $course_id, $this->get_id() );
1460 }*/
1461
1462 /**
1463 * @editor tungnx
1464 * @reason comment - not use
1465 * @modify 4.1.2
1466 */
1467 /*public function get_item_access_level( $item_id, $course_id ) {
1468 $access_level = 0;
1469
1470 if ( $course = learn_press_get_course( $course_id ) ) {
1471 if ( $course->has_item( $item_id ) ) {
1472 if ( 10 < $this->get_course_access_level( $course_id ) ) {
1473 $access_level = 10;
1474 } else {
1475 $item = $course->get_item( $item_id );
1476 if ( $item->is_preview() ) {
1477 $access_level = 10;
1478 }
1479 }
1480 }
1481 }
1482
1483 return apply_filters(
1484 'learn-press/course-item-access-level',
1485 $access_level,
1486 $item_id,
1487 $course_id,
1488 $this->get_id()
1489 );
1490 }*/
1491
1492 /**
1493 * Set new access-level of an user with a course.
1494 *
1495 * @param int $access_level
1496 * @param int $course_id
1497 *
1498 * @return mixed
1499 * @since 3.1.0
1500 * @editor tungnx
1501 * @reason comment - not use
1502 * @modify 4.1.2
1503 */
1504 /*public function set_course_access_level( $access_level, $course_id ) {
1505 if ( $access_level !== $this->get_course_access_level( $course_id ) ) {
1506 LP_Object_Cache::set(
1507 'course-' . $course_id . '-' . $this->get_id(),
1508 $access_level,
1509 'learn-press/course-access-levels'
1510 );
1511 }
1512
1513 return $access_level;
1514 }*/
1515
1516 /**
1517 * Check if user have an access-level.
1518 * Consider the passed access-level is max level user have.
1519 *
1520 * @param int[] $access_level
1521 * @param int $course_id
1522 * @param string $compare
1523 *
1524 * @return bool
1525 * @since 3.1.0
1526 * @editor tungnx
1527 * @modify 4.1.3 - not - use
1528 */
1529 /*public function has_course_access_level( $access_level, $course_id, $compare = '<=' ) {
1530 $user_access_level = $this->get_course_access_level( $course_id );
1531
1532 switch ( $compare ) {
1533 case 'any':
1534 settype( $access_level, 'array' );
1535 $has = in_array( $user_access_level, $access_level );
1536 break;
1537 default:
1538 $has = version_compare( $user_access_level, $access_level );
1539 }
1540
1541 return $has;
1542 }*/
1543
1544 /**
1545 * Check if user has an access-level with a course.
1546 *
1547 * @param int $access_level
1548 * @param int $course_id
1549 *
1550 * @return bool
1551 * @since 3.1.0
1552 * @editor tungnx
1553 * @modify 4.1.2
1554 * @reason comment - not use
1555 */
1556 /*public function is_access_level( $access_level, $course_id ) {
1557 $user_access_level = $this->get_course_access_level( $course_id );
1558
1559 return $user_access_level === $access_level;
1560 }*/
1561
1562 /**
1563 * Check if user is already ordered a course.
1564 *
1565 * @param int $course_id
1566 *
1567 * @return mixed|LP_Order
1568 * @editor tungnx
1569 * @modify 4.1.3 - comment - not use
1570 */
1571 /*public function has_ordered_course( $course_id ) {
1572 $return = apply_filters(
1573 'learn-press/user-has-ordered-course',
1574 $this->get_course_order( $course_id ),
1575 $course_id,
1576 $this->get_id()
1577 );
1578
1579 // Deprecated since 3.0.0
1580 $return = apply_filters( 'learn_press_user_has_ordered_course', $return, $course_id, $this->get_id() );
1581
1582 return $return;
1583 }*/
1584
1585 /**
1586 * Get order status of a course.
1587 *
1588 * @param int $course_id
1589 *
1590 * @return mixed
1591 */
1592 public function get_order_status( $course_id ) {
1593 try {
1594 $order = $this->get_course_order( $course_id );
1595
1596 if ( ! $order ) {
1597 throw new Exception( 'Order not exists' );
1598 }
1599
1600 $order_status = apply_filters(
1601 'learn-press/course-order-status',
1602 get_post_status( $order->get_id() ),
1603 $course_id,
1604 $this->get_id()
1605 );
1606 } catch ( Throwable $e ) {
1607 $order_status = false;
1608 }
1609
1610 return $order_status;
1611 }
1612
1613 /**
1614 * @param $item
1615 * @param int $course_id
1616 * @param bool $force
1617 *
1618 * @return mixed|void
1619 */
1620 public function has_completed_item( $item, $course_id = 0, $force = false ) {
1621
1622 $course_id = $this->_get_course( $course_id );
1623
1624 $return = false;
1625 $item_id = 0;
1626 if ( is_numeric( $item ) ) {
1627 $item_id = absint( $item );
1628 } else {
1629 settype( $item, 'array' );
1630 if ( ! empty( $item['ID'] ) ) {
1631 $item_id = absint( $item['ID'] );
1632 }
1633 }
1634 if ( $item_id ) {
1635 if ( empty( $item['item_type'] ) ) {
1636 $type = learn_press_get_post_type( $item_id );
1637 } else {
1638 $type = $item['item_type'];
1639 }
1640 if ( ! $type ) {
1641 $type = learn_press_get_post_type( $item_id );
1642 }
1643
1644 if ( $type == 'lp_lesson' || $type == 'lp_quiz' ) {
1645 $return = 'completed' === $this->get_item_status( $item_id, $course_id );
1646 }
1647 }
1648
1649 return apply_filters( 'learn_press_user_has_completed_item', $return, $item );
1650 }
1651
1652 /**
1653 * Get the remaining time of a course for the user.
1654 *
1655 * @param int $course_id
1656 *
1657 * @return bool|int|string
1658 */
1659 public function get_course_remaining_time( $course_id ) {
1660 $course = learn_press_get_course( $course_id );
1661 $remain = false;
1662
1663 if ( $course && $course->get_id() ) {
1664 $course_data = $this->get_course_data( $course_id, true );
1665 if ( $course_data ) {
1666 $remain = $course_data->is_exceeded();
1667 }
1668 }
1669
1670 return $remain > 0 ? learn_press_seconds_to_weeks( $remain ) : false;
1671 }
1672
1673 /**
1674 * Get the order that contains the course.
1675 *
1676 * @param int $course_id
1677 *
1678 * @return bool|LP_Order
1679 * @editor tungnx
1680 * @throws Exception
1681 * @version 1.0.2
1682 * @since 4.1.1
1683 */
1684 public function get_course_order( int $course_id ) {
1685 $lp_order = false;
1686 $lp_order_db = LP_Order_DB::getInstance();
1687 $lp_order_id = $lp_order_db->get_last_lp_order_id_of_user_course( $this->get_id(), $course_id );
1688
1689 if ( $lp_order_id ) {
1690 $lp_order = new LP_Order( $lp_order_id );
1691 }
1692
1693 return $lp_order;
1694 }
1695
1696 /**
1697 * Enroll this user to a course.
1698 *
1699 * @param int $course_id
1700 * @param int $order_id - Optional. An user can be enrolled to a course
1701 *
1702 * @return bool|WP_Error
1703 * @throws Exception
1704 * @since 3.3.0
1705 * @editor tungnx
1706 * @version 3.3.1
1707 * @modify 4.1.3 - comment - not use
1708 */
1709 // public function enroll_course( int $course_id = 0, int $order_id = 0 ) {
1710 // $lp_user_items_db = LP_User_Items_DB::getInstance();
1711 //
1712 // try {
1713 // /*$user_item_api = new LP_User_Item_CURD();
1714 // $find_query = array(
1715 // 'item_id' => $course_id,
1716 // 'user_id' => $this->get_id(),
1717 // );
1718 //
1719 // if ( $order_id ) {
1720 // $find_query['ref_id'] = $order_id;
1721 // }*/
1722 //
1723 // $filter = new LP_User_Items_Filter();
1724 // $filter->user_id = get_current_user_id();
1725 // $filter->item_id = $course_id;
1726 // $course_item = $lp_user_items_db->get_last_user_course( $filter );
1727 //
1728 // if ( ! $course_item ) {
1729 // $course_item = LP_User_Item::get_empty_item();
1730 // } else {
1731 // $course_item = (array) $course_item;
1732 // }
1733 //
1734 // $user_id = $this->get_id();
1735 //
1736 // $course_item['user_id'] = $user_id;
1737 // $course_item['item_id'] = $course_id;
1738 // $course_item['item_type'] = learn_press_get_post_type( $course_id );
1739 // $course_item['ref_id'] = $order_id;
1740 // $course_item['ref_type'] = ( $order_id != 0 ) ? learn_press_get_post_type( $order_id ) : '';
1741 // $course_item['start_time'] = current_time( 'mysql', true );
1742 // $course_item['access_level'] = 50;
1743 //
1744 // /**
1745 // * @editor tungnx
1746 // * @fixed: case no auto enroll
1747 // */
1748 // if ( 'yes' == LP_Settings::get_option( 'auto_enroll' ) ) {
1749 // $course_item['graduation'] = 'in-progress';
1750 // }
1751 //
1752 // $user_course = new LP_User_Item_Course( $course_item );
1753 // $user_course->set_status( LP_COURSE_PURCHASED );
1754 //
1755 // if ( ! $user_course->update( true ) ) {
1756 // throw new Exception( __( 'Update user item error.', 'learnpress' ) );
1757 // }
1758 //
1759 // /*$user_id = is_user_logged_in() ? $this->get_id() : 0;
1760 //
1761 // global $wpdb;
1762 // $query = $wpdb->prepare(
1763 // "
1764 // UPDATE {$wpdb->learnpress_user_items}
1765 // SET access_level = %d
1766 // WHERE user_id = %d
1767 // AND item_id = %d
1768 // AND user_item_id NOT IN(%d)
1769 // ",
1770 // 0,
1771 // $user_id,
1772 // $course_id,
1773 // $user_course->get_user_item_id()
1774 // );
1775 // $wpdb->query( $query );*/
1776 //
1777 // $return = $user_course->get_user_item_id();
1778 // } catch ( Exception $ex ) {
1779 // error_log( $ex->getMessage() );
1780 // return false;
1781 // }
1782 //
1783 // return $return;
1784 // }
1785
1786 /**
1787 * Enroll this user to a course.
1788 *
1789 * @param int $course_id
1790 * @param int $order_id
1791 * @param bool $force - Optional. Force create db record for preview quiz case
1792 * @param bool $wp_error - Optional. TRUE will return WP_Error object if there is an error.
1793 * @editor tungnx - comment - not use
1794 * @return bool|mixed|WP_Error
1795 */
1796 /*public function enroll( $course_id, $order_id, $force = false, $wp_error = false ) {
1797 global $wpdb;
1798
1799 _deprecated_function( __FUNCTION__, '4.1.0' );
1800
1801 try {
1802 $course = learn_press_get_course( $course_id );
1803 $user_id = $this->get_id();
1804
1805 if ( $course->is_required_enroll() && ! $force ) {
1806 $order = learn_press_get_order( $order_id );
1807
1808 if ( ! $order ) {
1809 throw new Exception( __( 'Failed to enroll course.', 'learnpress' ), 10000 );
1810 }
1811
1812 if ( ! $this->can_enroll_course( $course_id ) ) {
1813 throw new Exception( __( 'Failed to enroll course.', 'learnpress' ), 10001 );
1814 }
1815
1816 if ( ! $this->get_id() ) {
1817 throw new Exception( __( 'Please login to enroll course.', 'learnpress' ), 10002 );
1818 }
1819 }
1820
1821 $return = $this->enroll_course( $course_id, $order_id, false, $wp_error );
1822
1823 return $return;
1824
1825 } catch ( Exception $ex ) {
1826 return new WP_Error( $ex->getCode(), $ex->getMessage() );
1827 }
1828 }*/
1829
1830 /**
1831 * @param $question_id
1832 *
1833 * @return null|string
1834 */
1835 public function get_quiz_by_question( $question_id ) {
1836 global $wpdb;
1837 $query = $wpdb->prepare(
1838 "
1839 SELECT quiz_id
1840 FROM {$wpdb->prefix}learnpress_user_items uq
1841 INNER JOIN {$wpdb->prefix}learnpress_user_itemmeta uqm ON uqm.learnpress_user_item_id = uq.user_item_id AND uqm.meta_key = %s AND uqm.meta_value LIKE %s
1842 ",
1843 'questions',
1844 '%i:' . $wpdb->esc_like( $question_id . '' ) . ';%'
1845 );
1846
1847 return $wpdb->get_var( $query );
1848 }
1849
1850 /**
1851 * @param $question_id
1852 * @param null $quiz_id
1853 *
1854 * @return bool
1855 */
1856 public function get_answer_results( $question_id, $quiz_id = null ) {
1857
1858 _deprecated_function( __CLASS__ . '::' . __FUNCTION__, '3.0.0' );
1859
1860 $data = false;
1861 if ( ! $quiz_id ) {
1862 $quiz_id = $this->get_quiz_by_question( $question_id );
1863 }
1864 if ( $quiz_id ) {
1865 $question = LP_Question::get_question( $question_id );
1866
1867 if ( $question ) {
1868 $quiz_results = $this->get_quiz_results( $quiz_id );
1869 if ( ! empty( $quiz_results->question_answers ) ) {
1870 $question_answer = array_key_exists(
1871 $question_id,
1872 $quiz_results->question_answers
1873 ) ? $quiz_results->question_answers[ $question_id ] : null;
1874 $data = $question->check( $question_answer );
1875 }
1876 }
1877 }
1878
1879 return $data;
1880 }
1881
1882 /**
1883 * @param $question_id
1884 * @param null $quiz_id
1885 *
1886 * @return bool
1887 */
1888 public function is_answered_question( $question_id, $quiz_id = null ) {
1889 if ( empty( $this->answered_questions ) ) {
1890 $this->answered_questions = array();
1891 }
1892 if ( ! $quiz_id ) {
1893 $quiz_id = $this->get_quiz_by_question( $question_id );
1894 }
1895
1896 $results = $this->get_quiz_results( $quiz_id );
1897 $answered = ! empty( $results->question_answers ) ? $results->question_answers : array();
1898
1899 return $answered ? array_key_exists( $question_id, $answered ) : false;
1900 }
1901
1902 /**
1903 * @param array $args
1904 *
1905 * @return LP_Query_List_Table
1906 * @editor tungnx
1907 * @deprecated 4.1.6
1908 */
1909 /*public function get_purchased_courses( array $args = array() ): LP_Query_List_Table {
1910 $filter = new LP_User_Items_Filter();
1911 $filter->fields = array( 'item_id' );
1912 $filter->user_id = $this->get_id();
1913 $filter->status = $args['status'] ?? '';
1914 $filter->page = $args['paged'] ?? 1;
1915 $filter->limit = $args['limit'] ?? $filter->limit;
1916 $total_rows = 0;
1917 $result_courses = LP_User_Item_Course::get_user_courses( $filter, $total_rows );
1918
1919 $course_ids = LP_Course::get_course_ids( $result_courses, 'item_id' );
1920
1921 $courses = array(
1922 'total' => $total_rows,
1923 'paged' => $filter->page,
1924 'limit' => $filter->limit,
1925 'items' => $course_ids,
1926 );
1927
1928 return new LP_Query_List_Table( $courses );
1929 }*/
1930
1931 /**
1932 * @return array
1933 */
1934 public function get_roles() {
1935 return (array) $this->get_data( 'roles' );
1936 }
1937
1938 /**
1939 * @param $question_id
1940 * @param $quiz_id
1941 * @param int $course_id
1942 *
1943 * @return bool
1944 */
1945 public function has_checked_answer( $question_id, $quiz_id, $course_id = 0 ) {
1946 if ( ! $course_id ) {
1947 $course_id = get_the_ID();
1948 }
1949
1950 $quiz_data = $this->get_item_data( $quiz_id, $course_id );
1951
1952 return $quiz_data ? $quiz_data->has_checked_question( $question_id ) : false;
1953 }
1954
1955 /**
1956 * @param $question_id
1957 * @param $quiz_id
1958 * @param int $course_id
1959 *
1960 * @return bool
1961 */
1962 public function has_hinted_answer( $question_id, $quiz_id, $course_id = 0 ) {
1963 if ( ! $course_id ) {
1964 $course_id = get_the_ID();
1965 }
1966
1967 $quiz_data = $this->get_item_data( $quiz_id, $course_id );
1968
1969 return $quiz_data ? $quiz_data->has_hinted_question( $question_id ) : false;
1970 }
1971
1972 /**
1973 * Return TRUE if user is already exists.
1974 *
1975 * @return bool
1976 */
1977 public function is_exists() {
1978 return ! ! get_user_by( 'id', $this->get_id() );
1979 }
1980
1981 /**
1982 * Check if the user is logged in.
1983 *
1984 * @return bool
1985 */
1986 public function is_logged_in() {
1987 return $this->get_id() == get_current_user_id();
1988 }
1989
1990 /**
1991 * Get upload profile src
1992 * Option: null: get origin picture, "thumbnail": get thumbnail picture
1993 *
1994 * @param mixed $size
1995 *
1996 * @return string
1997 */
1998 public function get_upload_profile_src( $size = '' ) {
1999 return LP_Profile::instance( $this->get_id() )->get_upload_profile_src( $size );
2000 }
2001
2002 /**
2003 * @param string $type
2004 * @param int $size
2005 * @param bool $src_only
2006 *
2007 * @return false|string
2008 */
2009 public function get_profile_picture( $type = '', $size = 96, $src_only = false ) {
2010 return LP_Profile::instance( $this->get_id() )->get_profile_picture( $type, $size );
2011 }
2012
2013 /**
2014 * Get links socials of use on Profile page
2015 *
2016 * @param int $user_id
2017 * @return array
2018 */
2019 public function get_profile_socials( int $user_id = 0 ): array {
2020 $socials = array();
2021 $extra_info = learn_press_get_user_extra_profile_info( $user_id );
2022
2023 if ( $extra_info ) {
2024 foreach ( $extra_info as $k => $v ) {
2025 if ( empty( $v ) ) {
2026 continue;
2027 }
2028
2029 switch ( $k ) {
2030 case 'facebook':
2031 $i = '<i class="fab fa-facebook-f"></i>';
2032 break;
2033 case 'twitter':
2034 $i = '<i class="fab fa-twitter"></i>';
2035 break;
2036 case 'googleplus':
2037 $i = '<i class="fab fa-google-plus-g"></i>';
2038 break;
2039 case 'youtube':
2040 $i = '<i class="fab fa-youtube"></i>';
2041 break;
2042 default:
2043 $i = sprintf( '<i class="fab fa-%s"></i>', $k );
2044 }
2045
2046 $icon = apply_filters(
2047 'learn-press/user-profile-social-icon',
2048 $i,
2049 $k,
2050 $this->get_id(),
2051 $this
2052 );
2053 $socials[ $k ] = sprintf( '<a href="%s">%s</a>', esc_url_raw( $v ), $icon );
2054 }
2055 }
2056
2057 return apply_filters( 'learn-press/user-profile-socials', $socials, $this->get_id(), $this );
2058 }
2059
2060 /**
2061 * Check if user can access to a course.
2062 *
2063 * @param int $course_id
2064 *
2065 * @return mixed
2066 * @editor tungnx
2067 * @modify 4.1.3 - comment - not use
2068 */
2069 /*public function can_access_course( $course_id ) {
2070
2071 $accessible = $this->has_course_access_level(
2072 array(
2073 LP_COURSE_ACCESS_LEVEL_60,
2074 LP_COURSE_ACCESS_LEVEL_70,
2075 ),
2076 $course_id,
2077 'any'
2078 );
2079
2080 $accessible = apply_filters(
2081 'learn-press/user-can-access-course',
2082 $accessible,
2083 $course_id,
2084 $this->get_id()
2085 );
2086
2087 return $accessible;
2088 }*/
2089
2090 /**
2091 * Check course of user has graduation is in-progress
2092 *
2093 * @param $course_id
2094 * @return bool
2095 * @throws Exception
2096 */
2097 public function is_course_in_progress( $course_id ): bool {
2098 $user = learn_press_get_user( $this->get_id() );
2099 $user_course = $user->get_course_data( $course_id );
2100
2101 return $user_course && LP_COURSE_GRADUATION_IN_PROGRESS === $user_course->get_graduation();
2102 }
2103
2104 /**
2105 * Return TRUE if user can do a quiz
2106 *
2107 * @param $quiz_id
2108 * @param int $course_id
2109 *
2110 * @return bool
2111 * @throws Exception
2112 */
2113 public function can_do_quiz( $quiz_id, $course_id = 0 ) {
2114 $course = learn_press_get_course( $course_id );
2115
2116 if ( $course->is_required_enroll() ) {
2117 $can = $this->has_course_status(
2118 $course_id,
2119 array( 'enrolled' )
2120 ) && ! $this->has_started_quiz( $quiz_id, $course_id );
2121 } else {
2122 $can = ! $this->has_started_quiz( $quiz_id, $course_id );
2123 }
2124
2125 return apply_filters( 'learn_press_user_can_do_quiz', $can, $quiz_id, $this->get_id(), $course_id );
2126 }
2127
2128 public function evaluate_course_results( $course_id ) {
2129 $user_course = $this->get_course_data( $course_id );
2130
2131 return isset( $user_course ) ? $user_course->get_results( 'result' ) : 0;
2132 }
2133
2134 /**
2135 * @editor tungnx
2136 * @modify 4.1.4.1 - comment - not use
2137 */
2138 /*public function has_reached_passing_condition( $course_id ) {
2139 $course = learn_press_get_course( $course_id );
2140 $result = $this->evaluate_course_results( $course_id );
2141
2142 return $return = $result >= $course->get_passing_condition();
2143 }*/
2144
2145 /**
2146 * Check if all items in course completed.
2147 *
2148 * @return boolean
2149 * @author Nhamdv <email@email.com>
2150 */
2151 public function is_completed_all_items( $course_id ) {
2152 $course = learn_press_get_course( $course_id );
2153
2154 $course_data = $this->get_course_data( $course_id );
2155
2156 $course_results = $course_data->get_result();
2157
2158 if ( ! isset( $course_results['completed_items'] ) ) {
2159 return false;
2160 }
2161
2162 return $course_results['completed_items'] >= $course->count_items();
2163 }
2164
2165 public function get_role() {
2166 return $this->is_admin() ? 'admin' : ( $this->is_instructor() ? 'instructor' : 'user' );
2167 }
2168
2169 /**
2170 * Get user course's grade.
2171 * Possible values:
2172 * + passed User has finished and passed course.
2173 * + failed User has finished but failed.
2174 * + in-progress User still is learning course.
2175 *
2176 * @param $course_id
2177 *
2178 * @return string|bool
2179 */
2180 public function get_course_grade( $course_id ) {
2181 $grade = false;
2182 $course_data = $this->get_course_data( $course_id );
2183
2184 if ( $course_data ) {
2185 $grade = $course_data->get_status( 'graduation' );
2186 }
2187
2188 return apply_filters( 'learn-press/user-course-grade', $grade, $this->get_id(), $course_id );
2189 }
2190
2191 /**
2192 * Check if user is a GUEST by checking the meta _lp_temp_user is exists.
2193 *
2194 * @return bool
2195 */
2196 public function is_guest(): bool {
2197 return ! $this->get_id() || ! get_user_by( 'id', $this->get_id() );
2198 }
2199
2200 /**
2201 * Check if user can edit a post.
2202 *
2203 * @param int $post_id
2204 *
2205 * @return bool
2206 */
2207 public function can_edit( int $post_id ): bool {
2208 if ( $this->get_id() !== get_current_user_id() ) {
2209 return false;
2210 }
2211
2212 return current_user_can( 'edit_post', $post_id );
2213 }
2214
2215 /**
2216 * Get email of user
2217 *
2218 * @return string
2219 */
2220 public function get_email(): string {
2221 return $this->get_data( 'email', '' );
2222 }
2223
2224 /**
2225 * Return user_login of the user.
2226 *
2227 * @return string
2228 */
2229 public function get_username(): string {
2230 return $this->get_data( 'user_login', '' );
2231 }
2232
2233 /**
2234 * Return user bio information.
2235 *
2236 * @return string
2237 */
2238 public function get_description(): string {
2239 return $this->get_data( 'description', '' );
2240 }
2241
2242 /**
2243 * Return user first name.
2244 *
2245 * @return string
2246 */
2247 public function get_first_name(): string {
2248 return $this->get_data( 'first_name', '' );
2249 }
2250
2251 /**
2252 * Return user last name.
2253 *
2254 * @return string
2255 */
2256 public function get_last_name(): string {
2257 return $this->get_data( 'last_name', '' );
2258 }
2259
2260 /**
2261 * Return user nickname.
2262 *
2263 * @return string
2264 */
2265 public function get_nickname(): string {
2266 return $this->get_data( 'nickname', '' );
2267 }
2268
2269 /**
2270 * Return user display name.
2271 *
2272 * @return string
2273 */
2274 public function get_display_name(): string {
2275 return $this->get_data( 'display_name', '' );
2276 }
2277 }
2278 }
2279