PluginProbe
Tutor LMS – eLearning and online course solution / 2.2.1
Tutor LMS – eLearning and online course solution v2.2.1
4.0.7 4.0.6 4.0.5 4.0.4 4.0.3 4.0.2 4.0.1 4.0.0 3.9.15 3.9.14 3.9.13 3.9.12 3.9.11 trunk 1.0.0 1.0.0-alpha 1.0.1 1.0.2 1.0.3 1.0.4 1.0.5 1.0.6 1.0.7 1.0.8 1.0.9 All 191 releases
← All changes | models/CourseModel.php +52 -792 3.9.142.2.1 View file →
@@ -9,12 +9,9 @@
9 9 */
10 10
11 11 namespace Tutor\Models;
12 12
13 -use TUTOR\Course;
14 -use Tutor\Ecommerce\Tax;
15 13 use Tutor\Helpers\QueryHelper;
16 -use TUTOR_ASSIGNMENTS\Assignments;
17 14
18 15 /**
19 16 * CourseModel Class
20 17 *
@@ -25,25 +22,21 @@
25 22 * WordPress course type name
26 23 *
27 24 * @var string
28 25 */
29 - const POST_TYPE = 'courses';
30 - const COURSE_CATEGORY = 'course-category';
31 - const COURSE_TAG = 'course-tag';
26 + const POST_TYPE = 'courses';
32 27
33 28 const STATUS_PUBLISH = 'publish';
34 29 const STATUS_DRAFT = 'draft';
35 30 const STATUS_AUTO_DRAFT = 'auto-draft';
36 31 const STATUS_PENDING = 'pending';
37 - const STATUS_PRIVATE = 'private';
38 - const STATUS_FUTURE = 'future';
39 - const STATUS_TRASH = 'trash';
40 32
41 33 /**
42 - * Course completion modes
34 + * Course mapped with the product using this meta key
35 + *
36 + * @var string
43 37 */
44 - const MODE_FLEXIBLE = 'flexible';
45 - const MODE_STRICT = 'strict';
38 + const WC_PRODUCT_META_KEY = '_tutor_course_product_id';
46 39
47 40 /**
48 41 * Course attachment/downloadable resources meta key
49 42 *
@@ -58,49 +51,17 @@
58 51 */
59 52 const BENEFITS_META_KEY = '_tutor_course_benefits';
60 53
61 54 /**
62 - * The constant representing the status when a course is completed.
63 - *
64 - * @since 3.8.1
65 - *
66 - * @var string
67 - */
68 - const COURSE_COMPLETED = 'course_completed';
69 -
70 - /**
71 - * Get available status list.
72 - *
73 - * @since 3.0.0
74 - *
75 - * @return array
76 - */
77 - public static function get_status_list() {
78 - return array(
79 - self::STATUS_DRAFT,
80 - self::STATUS_AUTO_DRAFT,
81 - self::STATUS_PUBLISH,
82 - self::STATUS_PRIVATE,
83 - self::STATUS_FUTURE,
84 - self::STATUS_PENDING,
85 - self::STATUS_TRASH,
86 - );
87 - }
88 -
89 - /**
90 55 * Course record count
91 56 *
92 57 * @since 2.0.7
93 58 *
94 - * @since 3.6.0 $post_type param added
95 - *
96 - * @param string $status Post status.
97 - * @param string $post_type Post type.
98 - *
59 + * @param string $status course status.
99 60 * @return int
100 61 */
101 - public static function count( $status = self::STATUS_PUBLISH, $post_type = self::POST_TYPE ) {
102 - $count_obj = wp_count_posts( $post_type );
62 + public static function count( $status = self::STATUS_PUBLISH ) {
63 + $count_obj = wp_count_posts( self::POST_TYPE );
103 64 if ( 'all' === $status ) {
104 65 return array_sum( (array) $count_obj );
105 66 }
106 67
@@ -107,21 +68,8 @@
107 68 return (int) $count_obj->{$status};
108 69 }
109 70
110 71 /**
111 - * Get tutor post types
112 - *
113 - * @since 3.5.0
114 - *
115 - * @param int|\WP_POST $post the post id or object.
116 - *
117 - * @return bool
118 - */
119 - public static function get_post_types( $post ) {
120 - return apply_filters( 'tutor_check_course_post_type', self::POST_TYPE === get_post_type( $post ), get_post_type( $post ) );
121 - }
122 -
123 - /**
124 72 * Get courses
125 73 *
126 74 * @since 1.0.0
127 75 *
@@ -172,41 +120,13 @@
172 120 return $query;
173 121 }
174 122
175 123 /**
176 - * Get courses using provided args
177 - *
178 - * If user is not admin then it will return only current user's post
179 - *
180 - * @since 3.0.0
181 - *
182 - * @param array $args Args.
183 - *
184 - * @return \WP_Query
185 - */
186 - public static function get_courses_by_args( array $args = array() ) {
187 -
188 - $default_args = array(
189 - 'post_type' => tutor()->course_post_type,
190 - 'posts_per_page' => -1,
191 - 'post_status' => 'publish',
192 - );
193 -
194 - if ( ! current_user_can( 'manage_options' ) ) {
195 - $default_args['author'] = get_current_user_id();
196 - }
197 -
198 - $args = wp_parse_args( $args, apply_filters( 'tutor_get_course_list_filter_args', $default_args ) );
199 -
200 - return new \WP_Query( $args );
201 - }
202 -
203 - /**
204 124 * Get course count by instructor
205 125 *
206 126 * @since 1.0.0
207 127 *
208 - * @param int $instructor_id instructor ID.
128 + * @param $instructor_id
209 129 *
210 130 * @return null|string
211 131 */
212 132 public static function get_course_count_by_instructor( $instructor_id ) {
@@ -239,9 +159,9 @@
239 159 * Get course by quiz
240 160 *
241 161 * @since 1.0.0
242 162 *
243 - * @param int $quiz_id quiz id.
163 + * @param $quiz_id quiz id.
244 164 *
245 165 * @return array|bool|null|object|void
246 166 */
247 167 public static function get_course_by_quiz( $quiz_id ) {
@@ -250,9 +170,9 @@
250 170
251 171 if ( $post ) {
252 172 $course = get_post( $post->post_parent );
253 173 if ( $course ) {
254 - if ( tutor()->course_post_type !== $course->post_type ) {
174 + if ( $course->post_type !== tutor()->course_post_type ) {
255 175 $course = get_post( $course->post_parent );
256 176 }
257 177 return $course;
258 178 }
@@ -260,36 +180,29 @@
260 180
261 181 return false;
262 182 }
263 183
264 - /**
265 - * Get courses by a instructor
266 - *
267 - * @since 1.0.0
268 - * @since 3.5.0 param $post_types added.
269 - *
270 - * @param integer $instructor_id instructor id.
271 - * @param array|string $post_status post status.
272 - * @param integer $offset offset.
273 - * @param integer $limit limit.
274 - * @param boolean $count_only count or not.
275 - * @param array $post_types array of post types.
276 - *
277 - * @return array|null|object
278 - */
279 - public static function get_courses_by_instructor( $instructor_id = 0, $post_status = array( 'publish' ), int $offset = 0, int $limit = PHP_INT_MAX, $count_only = false, $post_types = array() ) {
184 + /**
185 + * Get courses by a instructor
186 + *
187 + * @since 1.0.0
188 + *
189 + * @param integer $instructor_id
190 + * @param array|string $post_status
191 + * @param integer $offset
192 + * @param integer $limit
193 + * @param boolean $count_only
194 + *
195 + * @return array|null|object
196 + */
197 + public static function get_courses_by_instructor( $instructor_id = 0, $post_status = array( 'publish' ), int $offset = 0, int $limit = PHP_INT_MAX, $count_only = false ) {
280 198 global $wpdb;
281 - $offset = sanitize_text_field( $offset );
282 - $limit = sanitize_text_field( $limit );
283 - $instructor_id = tutils()->get_user_id( $instructor_id );
199 + $offset = sanitize_text_field( $offset );
200 + $limit = sanitize_text_field( $limit );
201 + $instructor_id = tutils()->get_user_id( $instructor_id );
202 + $course_post_type = tutor()->course_post_type;
284 203
285 - if ( ! count( $post_types ) ) {
286 - $post_types = array( tutor()->course_post_type );
287 - }
288 -
289 - $post_types = QueryHelper::prepare_in_clause( $post_types );
290 -
291 - if ( empty( $post_status ) || 'any' == $post_status ) {
204 + if ( empty( $post_status ) || $post_status == 'any' ) {
292 205 $where_post_status = '';
293 206 } else {
294 207 ! is_array( $post_status ) ? $post_status = array( $post_status ) : 0;
295 208 $statuses = "'" . implode( "','", $post_status ) . "'";
@@ -298,9 +211,8 @@
298 211
299 212 $select_col = $count_only ? " COUNT(DISTINCT $wpdb->posts.ID) " : " $wpdb->posts.* ";
300 213 $limit_offset = $count_only ? '' : " LIMIT $offset, $limit ";
301 214
302 - //phpcs:disable
303 215 $query = $wpdb->prepare(
304 216 "SELECT $select_col
305 217 FROM $wpdb->posts
306 218 LEFT JOIN {$wpdb->usermeta}
@@ -307,21 +219,18 @@
307 219 ON $wpdb->usermeta.user_id = %d
308 220 AND $wpdb->usermeta.meta_key = %s
309 221 AND $wpdb->usermeta.meta_value = $wpdb->posts.ID
310 222 WHERE 1 = 1 {$where_post_status}
311 - AND $wpdb->posts.post_type IN ({$post_types})
223 + AND $wpdb->posts.post_type = %s
312 224 AND ($wpdb->posts.post_author = %d OR $wpdb->usermeta.user_id = %d)
313 225 ORDER BY $wpdb->posts.post_date DESC $limit_offset",
314 226 $instructor_id,
315 227 '_tutor_instructor_course_id',
228 + $course_post_type,
316 229 $instructor_id,
317 230 $instructor_id
318 231 );
319 - //phpcs:enable
320 232
321 - $query = apply_filters( 'modify_get_courses_by_instructor_query', $query, $instructor_id, $where_post_status, $post_types, $limit_offset, $select_col );
322 -
323 - //phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
324 233 return $count_only ? $wpdb->get_var( $query ) : $wpdb->get_results( $query, OBJECT );
325 234 }
326 235
327 236 /**
@@ -361,9 +270,9 @@
361 270 public static function is_main_instructor( $course_id, $user_id = 0 ) {
362 271 $course = get_post( $course_id );
363 272 $user_id = tutor_utils()->get_user_id( $user_id );
364 273
365 - if ( ! $course || ! self::get_post_types( $course_id ) || $user_id !== (int) $course->post_author ) {
274 + if ( ! $course || self::POST_TYPE !== $course->post_type || $user_id !== (int) $course->post_author ) {
366 275 return false;
367 276 }
368 277
369 278 return true;
@@ -390,9 +299,9 @@
390 299 * Marking course completed at Comment.
391 300 */
392 301 global $wpdb;
393 302
394 - $date = date( 'Y-m-d H:i:s', tutor_time() ); //phpcs:ignore
303 + $date = date( 'Y-m-d H:i:s', tutor_time() );
395 304
396 305 // Making sure that, hash is unique.
397 306 do {
398 307 $hash = substr( md5( wp_generate_password( 32 ) . $date . $course_id . $user_id ), 0, 16 );
@@ -433,9 +342,9 @@
433 342 * @param int $post_id course id that need to delete.
434 343 * @return bool
435 344 */
436 345 public static function delete_course( $post_id ) {
437 - if ( ! self::get_post_types( $post_id ) ) {
346 + if ( get_post_type( $post_id ) !== tutor()->course_post_type ) {
438 347 return false;
439 348 }
440 349
441 350 wp_delete_post( $post_id, true );
@@ -477,10 +386,8 @@
477 386 if ( get_post_type( $post_id ) !== $course_post_type ) {
478 387 return false;
479 388 }
480 389
481 - do_action( 'tutor_before_delete_course_content', $post_id, 0 );
482 -
483 390 global $wpdb;
484 391
485 392 $lesson_post_type = tutor()->lesson_post_type;
486 393 $assignment_post_type = tutor()->assignment_post_type;
@@ -501,15 +408,13 @@
501 408 if ( get_post_type( $content_id ) === 'tutor_quiz' ) {
502 409 $wpdb->delete( $wpdb->prefix . 'tutor_quiz_attempts', array( 'quiz_id' => $content_id ) );
503 410 $wpdb->delete( $wpdb->prefix . 'tutor_quiz_attempt_answers', array( 'quiz_id' => $content_id ) );
504 411
505 - do_action( 'tutor_before_delete_quiz_content', $content_id, null );
506 -
507 412 $questions_ids = $wpdb->get_col( $wpdb->prepare( "SELECT question_id FROM {$wpdb->prefix}tutor_quiz_questions WHERE quiz_id = %d ", $content_id ) );
508 413 if ( is_array( $questions_ids ) && count( $questions_ids ) ) {
509 - $in_clause = QueryHelper::prepare_in_clause( $questions_ids );
414 + $in_question_ids = "'" . implode( "','", $questions_ids ) . "'";
510 415 //phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared
511 - $wpdb->query( $wpdb->prepare( "DELETE FROM {$wpdb->prefix}tutor_quiz_question_answers WHERE belongs_question_id IN({$in_clause}) " ) );
416 + $wpdb->query( "DELETE FROM {$wpdb->prefix}tutor_quiz_question_answers WHERE belongs_question_id IN({$in_question_ids}) " );
512 417 }
513 418 $wpdb->delete( $wpdb->prefix . 'tutor_quiz_questions', array( 'quiz_id' => $content_id ) );
514 419 }
515 420
@@ -636,38 +541,28 @@
636 541 * like WC Product or EDD product meta key will be used
637 542 *
638 543 * @since 2.2.0
639 544 *
640 - * @since 3.0.0
545 + * @param string $meta_key course product id meta key.
546 + * @param array $args wp_query args.
641 547 *
642 - * Meta key removed and default meta query updated
643 - *
644 - * @since 3.0.1
645 - * Course::COURSE_PRICE_META meta key exists clause added
646 - *
647 - * @param array $args wp_query args.
648 - *
649 - * @return \WP_Query
548 + * @return array
650 549 */
651 - public static function get_paid_courses( array $args = array() ) {
550 + public static function get_paid_courses( string $meta_key, array $args = array() ): array {
652 551 $current_user = wp_get_current_user();
653 -
654 552 $default_args = array(
655 - 'post_type' => tutor()->course_post_type,
553 + 'post_type' => 'courses',
554 + 'post_status' => 'publish',
555 + 'no_found_rows' => true,
656 556 'posts_per_page' => -1,
657 - 'offset' => 0,
658 - 'post_status' => 'publish',
557 + 'relation' => 'AND',
659 558 'meta_query' => array(
660 - 'relation' => 'AND',
661 559 array(
662 - 'key' => Course::COURSE_PRICE_TYPE_META,
663 - 'value' => Course::PRICE_TYPE_SUBSCRIPTION,
560 + 'key' => sanitize_text_field( $meta_key ),
561 + 'value' => 0,
664 562 'compare' => '!=',
563 + 'type' => 'NUMERIC',
665 564 ),
666 - array(
667 - 'key' => Course::COURSE_PRICE_META,
668 - 'compare' => 'EXISTS',
669 - ),
670 565 ),
671 566 );
672 567
673 568 // Check if the current user is an admin.
@@ -674,648 +569,13 @@
674 569 if ( ! current_user_can( 'administrator' ) ) {
675 570 $args['author'] = $current_user->ID;
676 571 }
677 572
678 - $args = wp_parse_args( $args, $default_args );
679 - return new \WP_Query( $args );
680 - }
573 + $query = new \WP_Query( wp_parse_args( $args, $default_args ) );
681 574
682 - /**
683 - * Check the course is completeable or not
684 - *
685 - * @since 2.4.0
686 - *
687 - * @param int $course_id course id.
688 - * @param int $user_id user id.
689 - *
690 - * @return boolean
691 - */
692 - public static function can_complete_course( $course_id, $user_id ) {
693 - $mode = tutor_utils()->get_option( 'course_completion_process' );
694 - if ( self::MODE_FLEXIBLE === $mode ) {
695 - return true;
575 + if ( $query->have_posts() ) {
576 + return $query->posts;
696 577 }
697 578
698 - if ( self::MODE_STRICT === $mode ) {
699 - $completed_lesson = tutor_utils()->get_completed_lesson_count_by_course( $course_id, $user_id );
700 - $lesson_count = tutor_utils()->get_lesson_count_by_course( $course_id, $user_id );
701 -
702 - if ( $completed_lesson < $lesson_count ) {
703 - return false;
704 - }
705 -
706 - $quizzes = array();
707 - $assignments = array();
708 -
709 - $course_contents = tutor_utils()->get_course_contents_by_id( $course_id );
710 - if ( tutor_utils()->count( $course_contents ) ) {
711 - foreach ( $course_contents as $content ) {
712 - if ( 'tutor_quiz' === $content->post_type ) {
713 - $quizzes[] = $content;
714 - }
715 - if ( 'tutor_assignments' === $content->post_type ) {
716 - $assignments[] = $content;
717 - }
718 - }
719 - }
720 -
721 - foreach ( $quizzes as $row ) {
722 - $result = QuizModel::get_quiz_result( $row->ID );
723 - if ( 'pass' !== $result ) {
724 - return false;
725 - }
726 - }
727 -
728 - if ( tutor()->has_pro ) {
729 - foreach ( $assignments as $row ) {
730 - $result = \TUTOR_ASSIGNMENTS\Assignments::get_assignment_result( $row->ID, $user_id );
731 - if ( 'pass' !== $result ) {
732 - return false;
733 - }
734 - }
735 - }
736 -
737 - return true;
738 - }
739 -
740 - return false;
741 - }
742 -
743 - /**
744 - * Check a course can be auto complete by an enrolled student.
745 - *
746 - * @since 2.4.0
747 - *
748 - * @param int $course_id course id.
749 - * @param int $user_id user id.
750 - *
751 - * @return boolean
752 - */
753 - public static function can_autocomplete_course( $course_id, $user_id ) {
754 - $auto_course_complete_option = (bool) tutor_utils()->get_option( 'auto_course_complete_on_all_lesson_completion' );
755 - if ( ! $auto_course_complete_option ) {
756 - return false;
757 - }
758 -
759 - $is_course_completed = tutor_utils()->is_completed_course( $course_id, $user_id );
760 - if ( $is_course_completed ) {
761 - return false;
762 - }
763 -
764 - $course_stats = tutor_utils()->get_course_completed_percent( $course_id, $user_id, true );
765 - if ( $course_stats['total_count'] && $course_stats['completed_count'] === $course_stats['total_count'] ) {
766 - return self::can_complete_course( $course_id, $user_id );
767 - } else {
768 - return false;
769 - }
770 - }
771 -
772 - /**
773 - * Get review progress link when course progress 100% and
774 - * User has pending or fail quiz or assignment
775 - *
776 - * @since 2.4.0
777 - *
778 - * @param int $course_id course id.
779 - * @param int $user_id user id.
780 - *
781 - * @return string course content permalink.
782 - */
783 - public static function get_review_progress_link( $course_id, $user_id ) {
784 - $course_progress = tutor_utils()->get_course_completed_percent( $course_id, $user_id, true );
785 - $completed_percent = (int) $course_progress['completed_percent'];
786 - $course_contents = tutor_utils()->get_course_contents_by_id( $course_id );
787 - $permalink = '';
788 -
789 - if ( tutor_utils()->count( $course_contents ) && 100 === $completed_percent ) {
790 - foreach ( $course_contents as $content ) {
791 - if ( 'tutor_quiz' === $content->post_type ) {
792 - $result = QuizModel::get_quiz_result( $content->ID, $user_id );
793 - if ( 'pass' !== $result ) {
794 - $permalink = get_the_permalink( $content->ID );
795 - break;
796 - }
797 - }
798 -
799 - if ( tutor()->has_pro && 'tutor_assignments' === $content->post_type ) {
800 - $result = \TUTOR_ASSIGNMENTS\Assignments::get_assignment_result( $content->ID, $user_id );
801 - if ( 'pass' !== $result ) {
802 - $permalink = get_the_permalink( $content->ID );
803 - break;
804 - }
805 - }
806 - }
807 - }
808 -
809 - // Fallback link.
810 - if ( empty( $permalink ) ) {
811 - $permalink = tutils()->get_course_first_lesson( $course_id );
812 - }
813 -
814 - return $permalink;
815 - }
816 -
817 - /**
818 - * Get course preview image placeholder
819 - *
820 - * @since 3.0.0
821 - *
822 - * @return string
823 - */
824 - public static function get_course_preview_image_placeholder() {
825 - return tutor()->url . 'assets/images/placeholder.svg';
826 - }
827 -
828 - /**
829 - * Retrieve the courses or course bundles that a given coupon code applies to.
830 - *
831 - * This function fetches published courses or course bundles from the database
832 - * based on the specified type. For each course, it retrieves the course prices
833 - * and the course thumbnail URL. If the user has Tutor Pro, it additionally
834 - * retrieves the total number of courses in a course bundle.
835 - *
836 - * @since 3.0.0
837 - *
838 - * @param string $applies_to The type of items the coupon applies to. Accepts 'specific_courses'
839 - * for individual courses or any other value for course bundles.
840 - *
841 - * @global wpdb $wpdb WordPress database abstraction object.
842 - *
843 - * @return array An array of course objects. Each course object contains:
844 - * - int $id: The ID of the course.
845 - * - string $title: The title of the course.
846 - * - string $type: The post type of the course (e.g., 'courses', 'course-bundle').
847 - * - float $price: The regular price of the course.
848 - * - float $sale_price: The sale price of the course.
849 - * - string $image: The URL of the course's thumbnail image.
850 - * - int|null $total_courses: The total number of courses in the bundle
851 - * (only if the user has Tutor Pro and the course type is 'course-bundle').
852 - */
853 - public function get_coupon_applies_to_courses( string $applies_to ) {
854 - global $wpdb;
855 -
856 - $post_type = 'specific_courses' === $applies_to ? 'courses' : 'course-bundle';
857 -
858 - $where = array(
859 - 'post_status' => 'publish',
860 - 'post_type' => $post_type,
861 - );
862 -
863 - $courses = QueryHelper::get_all( $wpdb->posts, $where, 'ID' );
864 -
865 - if ( tutor()->has_pro ) {
866 - $bundle_model = new \TutorPro\CourseBundle\Models\BundleModel();
867 - }
868 -
869 - $final_data = array();
870 -
871 - if ( ! empty( $courses ) ) {
872 - foreach ( $courses as $course ) {
873 - $data = new \stdClass();
874 -
875 - if ( tutor()->has_pro && 'course-bundle' === $course->type ) {
876 - $data->total_courses = count( $bundle_model->get_bundle_course_ids( $course->ID ) );
877 - }
878 -
879 - $author_name = get_the_author_meta( 'display_name', $course->post_author );
880 - $course_prices = tutor_utils()->get_raw_course_price( $course->ID );
881 - $data->id = (int) $course->ID;
882 - $data->title = $course->post_title;
883 - $data->price = $course_prices->regular_price;
884 - $data->sale_price = $course_prices->sale_price;
885 - $data->image = get_the_post_thumbnail_url( $course->ID );
886 - $data->author = $author_name;
887 -
888 - $final_data[] = $data;
889 - }
890 - }
891 -
892 - return ! empty( $final_data ) ? $final_data : array();
893 - }
894 -
895 - /**
896 - * Get course instructor IDs.
897 - *
898 - * @since 3.0.0
899 - *
900 - * @param int $course_id course id.
901 - *
902 - * @return array
903 - */
904 - public static function get_course_instructor_ids( $course_id ) {
905 - global $wpdb;
906 - $instructor_ids = $wpdb->get_col(
907 - $wpdb->prepare(
908 - "SELECT user_id FROM {$wpdb->usermeta} WHERE meta_key=%s AND meta_value=%s",
909 - '_tutor_instructor_course_id',
910 - $course_id
911 - )
912 - );
913 -
914 - return $instructor_ids;
915 - }
916 -
917 - /**
918 - * Check tax collection is enabled for single purchase course/bundle
919 - *
920 - * @since 3.7.0
921 - *
922 - * @param int $post_id course or bundle id.
923 - *
924 - * @return boolean
925 - */
926 - public static function is_tax_enabled_for_single_purchase( $post_id ) {
927 - if ( ! Tax::is_individual_control_enabled() ) {
928 - return true;
929 - }
930 -
931 - $data = get_post_meta( $post_id, Course::TAX_ON_SINGLE_META, true );
932 - return ( '1' === $data || '' === $data );
933 - }
934 -
935 - /**
936 - * Count total attachments available in all courses or specific
937 - *
938 - * @since 3.6.0
939 - *
940 - * @param int|array $course_id Course id or array of ids, to get course's attachment.
941 - *
942 - * @return int
943 - */
944 - public static function count_attachment( $course_id = 0 ) {
945 - global $wpdb;
946 -
947 - $total_count = 0;
948 -
949 - $primary_table = "$wpdb->posts p";
950 - $joining_tables = array(
951 - array(
952 - 'type' => 'INNER',
953 - 'table' => "{$wpdb->postmeta} pm",
954 - 'on' => "p.ID = pm.post_id AND pm.meta_key = '_tutor_attachments' AND pm.meta_value != 'a:0:{}'",
955 - ),
956 - );
957 -
958 - // Prepare query.
959 - $select = array( 'pm.meta_value' );
960 - $where = array();
961 - if ( $course_id ) {
962 - $where['p.ID'] = is_array( $course_id ) ? array( 'IN', $course_id ) : $course_id;
963 - }
964 -
965 - $search = array();
966 - $limit = 0; // Get all.
967 - $offset = 0;
968 - $order_by = '';
969 -
970 - $results = QueryHelper::get_joined_data(
971 - $primary_table,
972 - $joining_tables,
973 - $select,
974 - $where,
975 - $search,
976 - $order_by,
977 - $limit,
978 - $offset
979 - )['results'];
980 -
981 - if ( $results ) {
982 - foreach ( $results as $row ) {
983 - $attachment_ids = maybe_unserialize( $row->meta_value );
984 - if ( ! is_array( $attachment_ids ) || empty( $attachment_ids ) ) {
985 - continue;
986 - }
987 -
988 - $attachments = get_posts(
989 - array(
990 - 'post_type' => 'attachment',
991 - 'post__in' => $attachment_ids,
992 - 'posts_per_page' => -1,
993 - )
994 - );
995 -
996 - $total_count += count( $attachments );
997 - }
998 - }
999 -
1000 - return $total_count;
1001 - }
1002 -
1003 - /**
1004 - * Count total questions available in all or specific courses
1005 - *
1006 - * @since 3.7.1
1007 - *
1008 - * @param int|array $course_id Course id or array of ids, to get course's attachment.
1009 - *
1010 - * @return int
1011 - */
1012 - public static function count_questions( $course_id = 0 ) {
1013 - global $wpdb;
1014 -
1015 - $total_count = 0;
1016 - $quiz_post_type = tutor()->quiz_post_type;
1017 - $topic_post_type = tutor()->topics_post_type;
1018 - $course_post_type = tutor()->course_post_type;
1019 -
1020 - $primary_table = "{$wpdb->prefix}tutor_quiz_questions question";
1021 -
1022 - $joining_tables = array(
1023 - array(
1024 - 'type' => 'INNER',
1025 - 'table' => "{$wpdb->posts} q",
1026 - 'on' => "q.ID = question.quiz_id AND q.post_type='{$quiz_post_type}'",
1027 - ),
1028 - array(
1029 - 'type' => 'INNER',
1030 - 'table' => "{$wpdb->posts} t",
1031 - 'on' => "q.post_parent = t.ID AND t.post_type='{$topic_post_type}'",
1032 - ),
1033 - array(
1034 - 'type' => 'INNER',
1035 - 'table' => "{$wpdb->posts} c",
1036 - 'on' => "c.ID = t.post_parent AND c.post_type='{$course_post_type}'",
1037 - ),
1038 - );
1039 -
1040 - // Prepare query.
1041 - $where = array();
1042 - if ( $course_id ) {
1043 - $where['c.ID'] = is_array( $course_id ) ? array( 'IN', $course_id ) : $course_id;
1044 - }
1045 -
1046 - $search = array();
1047 -
1048 - $total_count = QueryHelper::get_joined_count(
1049 - $primary_table,
1050 - $joining_tables,
1051 - $where,
1052 - $search,
1053 - '*'
1054 - );
1055 -
1056 - return $total_count;
1057 - }
1058 -
1059 - /**
1060 - * Count course content
1061 - *
1062 - * @since 3.6.0
1063 - *
1064 - * @param string $content_type Content type.
1065 - * @param array $course_ids Course ids.
1066 - *
1067 - * @return int
1068 - */
1069 - public static function count_course_content( string $content_type, array $course_ids = array() ): int {
1070 - $total_count = 0;
1071 - switch ( $content_type ) {
1072 - case tutor()->lesson_post_type:
1073 - $total_count = tutor_utils()->get_total_lesson( $course_ids );
1074 - break;
1075 - case tutor()->quiz_post_type:
1076 - $total_count = tutor_utils()->get_total_quiz( $course_ids );
1077 - break;
1078 - case tutor()->assignment_post_type:
1079 - if ( tutor_utils()->is_addon_enabled( 'tutor-assignments' ) ) {
1080 - $total_count = ( new Assignments( false ) )->get_total_assignment( $course_ids );
1081 - }
1082 - break;
1083 - case 'attachments':
1084 - $total_count = self::count_attachment( $course_ids );
1085 - break;
1086 - case 'questions':
1087 - $total_count = self::count_questions( $course_ids );
1088 - break;
1089 - default:
1090 - break;
1091 - }
1092 -
1093 - return (int) $total_count;
1094 - }
1095 -
1096 - /**
1097 - * Get course dropdown options
1098 - *
1099 - * @since 3.7.0
1100 - *
1101 - * @return array
1102 - */
1103 - public static function get_course_dropdown_options() {
1104 - $course_options = array(
1105 - array(
1106 - 'key' => '',
1107 - 'title' => __( 'All Courses', 'tutor' ),
1108 - ),
1109 - );
1110 -
1111 - $courses = current_user_can( 'administrator' ) ? self::get_courses() : self::get_courses_by_instructor();
1112 - if ( ! empty( $courses ) ) {
1113 - foreach ( $courses as $course ) {
1114 - $course_options[] = array(
1115 - 'key' => $course->ID,
1116 - 'title' => $course->post_title,
1117 - );
1118 - }
1119 - }
1120 -
1121 - return $course_options;
1122 - }
1123 -
1124 - /**
1125 - * Get category dropdown options
1126 - *
1127 - * @since 3.7.0
1128 - *
1129 - * @return array
1130 - */
1131 - public static function get_category_dropdown_options() {
1132 - $category_options = array(
1133 - array(
1134 - 'key' => '',
1135 - 'title' => __( 'All Categories', 'tutor' ),
1136 - ),
1137 - );
1138 -
1139 - $categories = get_terms(
1140 - array(
1141 - 'taxonomy' => self::COURSE_CATEGORY,
1142 - 'orderby' => 'term_id',
1143 - 'order' => 'DESC',
1144 - )
1145 - );
1146 - if ( ! is_wp_error( $categories ) && ! empty( $categories ) ) {
1147 - foreach ( $categories as $category ) {
1148 - $category_options[] = array(
1149 - 'key' => $category->slug,
1150 - 'title' => $category->name,
1151 - );
1152 - }
1153 - }
1154 -
1155 - return $category_options;
1156 - }
1157 -
1158 - /**
1159 - * Retrieve all course completion records for a specific course, including meta data.
1160 - *
1161 - * @since 3.8.1
1162 - *
1163 - * @param int $course_id The ID of the course.
1164 - *
1165 - * @return array Array of course completion objects with meta, empty array if none found, or on database error.
1166 - */
1167 - public function get_course_completion_data_by_course_id( int $course_id ): array {
1168 -
1169 - global $wpdb;
1170 -
1171 - $where = array(
1172 - 'comment_type' => self::COURSE_COMPLETED,
1173 - 'comment_post_ID' => $course_id,
1174 - 'comment_agent' => 'TutorLMSPlugin',
1175 - );
1176 -
1177 - $result = QueryHelper::get_all( $wpdb->comments, $where, 'comment_post_ID', -1 );
1178 -
1179 - if ( empty( $result ) ) {
1180 - return array();
1181 - }
1182 -
1183 - return array_map(
1184 - function ( $item ) {
1185 -
1186 - return array(
1187 - 'completion' => $item,
1188 - 'completion_meta' => get_comment_meta( $item->comment_ID ),
1189 - );
1190 - },
1191 - $result
1192 - );
1193 - }
1194 -
1195 - /**
1196 - * Return completed courses by user_id
1197 - *
1198 - * @since 1.0.0
1199 - *
1200 - * @param int $user_id user id.
1201 - * @param int $offset offset.
1202 - * @param int $posts_per_page posts per page.
1203 - * @param array $args Args to override the defaults.
1204 - *
1205 - * @return bool|\WP_Query
1206 - */
1207 - public static function get_completed_courses_by_user( $user_id = 0, $offset = 0, $posts_per_page = -1, $args = array() ) {
1208 - $user_id = tutor_utils()->get_user_id( $user_id );
1209 - $course_ids = tutor_utils()->get_completed_courses_ids_by_user( $user_id );
1210 -
1211 - if ( count( $course_ids ) ) {
1212 - $course_post_type = tutor()->course_post_type;
1213 - $course_args = array(
1214 - 'post_type' => $course_post_type,
1215 - 'post_status' => 'publish',
1216 - 'post__in' => $course_ids,
1217 - 'posts_per_page' => $posts_per_page,
1218 - 'offset' => $offset,
1219 - );
1220 -
1221 - $args = apply_filters( 'tutor_get_completed_courses_by_user', $args, $user_id, $course_post_type );
1222 - $course_args = wp_parse_args( $args, $course_args );
1223 -
1224 - return new \WP_Query( $course_args );
1225 - }
1226 -
1227 - return false;
1228 - }
1229 -
1230 - /**
1231 - * Get the active course by user
1232 - *
1233 - * @since 1.0.0
1234 - *
1235 - * @param int $user_id user id.
1236 - * @param int $offset offset.
1237 - * @param int $posts_per_page posts per page.
1238 - * @param array $args Args to override the defaults.
1239 - *
1240 - * @return bool|\WP_Query
1241 - */
1242 - public static function get_active_courses_by_user( $user_id = 0, $offset = 0, $posts_per_page = -1, $args = array() ) {
1243 - $user_id = tutor_utils()->get_user_id( $user_id );
1244 - $course_ids = tutor_utils()->get_completed_courses_ids_by_user( $user_id );
1245 - $enrolled_course_ids = tutor_utils()->get_enrolled_courses_ids_by_user( $user_id );
1246 - $active_courses = array_diff( $enrolled_course_ids, $course_ids );
1247 -
1248 - if ( count( $active_courses ) ) {
1249 - $course_post_type = tutor()->course_post_type;
1250 - $course_args = array(
1251 - 'post_type' => apply_filters( 'tutor_active_courses_post_types', array( $course_post_type ) ),
1252 - 'post_status' => 'publish',
1253 - 'post__in' => $active_courses,
1254 - 'posts_per_page' => $posts_per_page,
1255 - 'offset' => $offset,
1256 - );
1257 -
1258 - $args = apply_filters( 'tutor_get_active_courses_by_user', $args, $user_id, $course_post_type );
1259 - $course_args = wp_parse_args( $args, $course_args );
1260 -
1261 - return new \WP_Query( $course_args );
1262 - }
1263 -
1264 - return false;
1265 - }
1266 -
1267 - /**
1268 - * Get the enrolled courses by user
1269 - *
1270 - * @since 1.0.0
1271 - * @since 2.5.0 $filters param added to query enrolled courses with additional filters.
1272 - *
1273 - * @since 3.4.0 $filters replaced with $args to override the defaults.
1274 - *
1275 - * @param integer $user_id user id.
1276 - * @param mixed $post_status post status.
1277 - * @param integer $offset offset.
1278 - * @param integer $posts_per_page post per page.
1279 - * @param array $args Args to override the defaults.
1280 - *
1281 - * @return bool|\WP_Query
1282 - */
1283 - public static function get_enrolled_courses_by_user( $user_id = 0, $post_status = 'publish', $offset = 0, $posts_per_page = -1, $args = array() ) {
1284 - $user_id = tutor_utils()->get_user_id( $user_id );
1285 - $course_ids = array_unique( tutor_utils()->get_enrolled_courses_ids_by_user( $user_id ) );
1286 -
1287 - if ( count( $course_ids ) ) {
1288 - $course_post_type = tutor()->course_post_type;
1289 - $course_args = array(
1290 - 'post_type' => $course_post_type,
1291 - 'post_status' => $post_status,
1292 - 'post__in' => $course_ids,
1293 - 'offset' => $offset,
1294 - 'posts_per_page' => $posts_per_page,
1295 - );
1296 -
1297 - $args = apply_filters( 'tutor_get_enrolled_courses_by_user', $args, $user_id, $course_post_type );
1298 - $course_args = wp_parse_args( $args, $course_args );
1299 -
1300 - $result = new \WP_Query( $course_args );
1301 -
1302 - if ( is_object( $result ) && is_array( $result->posts ) ) {
1303 -
1304 - // Sort courses according to the id list.
1305 - $new_array = array();
1306 -
1307 - foreach ( $course_ids as $id ) {
1308 - foreach ( $result->posts as $post ) {
1309 - $post->ID == $id ? $new_array[] = $post : 0;
1310 - }
1311 - }
1312 -
1313 - $result->posts = $new_array;
1314 - }
1315 -
1316 - return $result;
1317 - }
1318 -
1319 - return false;
579 + return array();
1320 580 }
1321 581 }