PluginProbe ʕ •ᴥ•ʔ
Tutor LMS – eLearning and online course solution / 2.7.3
Tutor LMS – eLearning and online course solution v2.7.3
3.9.14 3.9.13 3.9.12 3.9.11 trunk 1.0.0 1.0.0-alpha 1.0.1 1.0.2 1.0.3 1.0.4 1.0.5 1.0.6 1.0.7 1.0.8 1.0.9 1.1.0 1.1.1 1.2.0 1.2.1 1.2.11 1.2.12 1.2.13 1.2.20 1.3.0 1.3.1 1.3.2 1.3.3 1.3.4 1.3.5 1.3.6 1.3.7 1.3.8 1.3.9 1.4.0 1.4.1 1.4.2 1.4.3 1.4.4 1.4.5 1.4.6 1.4.7 1.4.8 1.4.9 1.5.0 1.5.1 1.5.2 1.5.3 1.5.4 1.5.5 1.5.6 1.5.7 1.5.8 1.5.9 1.6.0 1.6.1 1.6.2 1.6.3 1.6.4 1.6.5 1.6.6 1.6.7 1.6.8 1.6.9 1.7.0 1.7.1 1.7.2 1.7.3 1.7.4 1.7.5 1.7.6 1.7.7 1.7.8 1.7.9 1.8.0 1.8.1 1.8.10 1.8.2 1.8.3 1.8.4 1.8.5 1.8.6 1.8.7 1.8.8 1.8.9 1.9.0 1.9.1 1.9.10 1.9.11 1.9.12 1.9.13 1.9.14 1.9.15 1.9.16 1.9.2 1.9.3 1.9.4 1.9.5 1.9.6 1.9.7 1.9.8 1.9.9 2.0.0 2.0.1 2.0.10 2.0.2 2.0.3 2.0.4 2.0.5 2.0.6 2.0.7 2.0.8 2.0.9 2.1.0 2.1.1 2.1.10 2.1.2 2.1.3 2.1.4 2.1.5 2.1.6 2.1.7 2.1.8 2.1.9 2.2.0 2.2.1 2.2.2 2.2.3 2.2.4 2.3.0 2.4.0 2.5.0 2.6.0 2.6.1 2.6.2 2.7.0 2.7.1 2.7.2 2.7.3 2.7.4 2.7.5 2.7.6 2.7.7 3.0.0 3.0.1 3.0.2 3.1.0 3.2.0 3.2.1 3.2.2 3.2.3 3.3.0 3.3.1 3.4.0 3.4.1 3.4.2 3.5.0 3.6.0 3.6.1 3.6.2 3.6.3 3.6.4 3.7.0 3.7.1 3.7.2 3.7.3 3.7.4 3.8.0 3.8.1 3.8.2 3.8.3 3.9.0 3.9.1 3.9.10 3.9.2 3.9.3 3.9.4 3.9.5 3.9.6 3.9.7 3.9.8 3.9.9
tutor / classes / Course_List.php
tutor / classes Last commit date
Addons.php 2 years ago Admin.php 2 years ago Ajax.php 2 years ago Announcements.php 3 years ago Assets.php 2 years ago Backend_Page_Trait.php 3 years ago Course.php 1 year ago Course_Embed.php 3 years ago Course_Filter.php 1 year ago Course_List.php 1 year ago Course_Settings_Tabs.php 3 years ago Course_Widget.php 3 years ago Custom_Validation.php 3 years ago Dashboard.php 3 years ago FormHandler.php 2 years ago Frontend.php 2 years ago Gutenberg.php 3 years ago Input.php 3 years ago Instructor.php 2 years ago Instructors_List.php 1 year ago Lesson.php 2 years ago Options_V2.php 1 year ago Permalink.php 2 years ago Post_types.php 2 years ago Private_Course_Access.php 3 years ago Q_And_A.php 1 year ago Question_Answers_List.php 3 years ago Quiz.php 2 years ago Quiz_Attempts_List.php 2 years ago RestAPI.php 2 years ago Reviews.php 3 years ago Rewrite_Rules.php 2 years ago Shortcode.php 1 year ago Student.php 2 years ago Students_List.php 3 years ago Taxonomies.php 3 years ago Template.php 2 years ago Theme_Compatibility.php 3 years ago Tools.php 3 years ago Tools_V2.php 2 years ago Tutor.php 2 years ago TutorEDD.php 2 years ago Tutor_Base.php 2 years ago Tutor_Setup.php 2 years ago Upgrader.php 2 years ago User.php 2 years ago Utils.php 1 year ago Video_Stream.php 3 years ago WhatsNew.php 2 years ago Withdraw.php 2 years ago Withdraw_Requests_List.php 3 years ago WooCommerce.php 2 years ago
Course_List.php
529 lines
1 <?php
2 /**
3 * Manage Course List
4 *
5 * @package Tutor
6 * @author Themeum <support@themeum.com>
7 * @link https://themeum.com
8 * @since 2.0.0
9 */
10
11 namespace TUTOR;
12
13 use Tutor\Helpers\QueryHelper;
14 use Tutor\Models\CourseModel;
15
16 if ( ! defined( 'ABSPATH' ) ) {
17 exit;
18 }
19 /**
20 * Course List class
21 *
22 * @since 2.0.0
23 */
24 class Course_List {
25 /**
26 * Trait for utilities
27 *
28 * @var $page_title
29 */
30
31 use Backend_Page_Trait;
32
33 /**
34 * Page Title
35 *
36 * @var $page_title
37 */
38 public $page_title;
39
40 /**
41 * Bulk Action
42 *
43 * @var $bulk_action
44 */
45 public $bulk_action = true;
46
47 /**
48 * Constructor
49 *
50 * @return void
51 * @since 2.0.0
52 */
53 public function __construct() {
54 $this->page_title = __( 'Courses', 'tutor' );
55 /**
56 * Handle bulk action
57 *
58 * @since v2.0.0
59 */
60 add_action( 'wp_ajax_tutor_course_list_bulk_action', array( $this, 'course_list_bulk_action' ) );
61 /**
62 * Handle ajax request for updating course status
63 *
64 * @since v2.0.0
65 */
66 add_action( 'wp_ajax_tutor_change_course_status', array( $this, 'tutor_change_course_status' ) );
67 /**
68 * Handle ajax request for delete course
69 *
70 * @since v2.0.0
71 */
72 add_action( 'wp_ajax_tutor_course_delete', array( $this, 'tutor_course_delete' ) );
73 }
74
75 /**
76 * Prepare bulk actions that will show on dropdown options
77 *
78 * @return array
79 * @since 2.0.0
80 */
81 public function prepare_bulk_actions(): array {
82 $actions = array(
83 $this->bulk_action_default(),
84 $this->bulk_action_publish(),
85 $this->bulk_action_pending(),
86 $this->bulk_action_draft(),
87 );
88
89 $active_tab = Input::get( 'data', '' );
90
91 if ( 'trash' === $active_tab ) {
92 array_push( $actions, $this->bulk_action_delete() );
93 }
94 if ( 'trash' !== $active_tab ) {
95 array_push( $actions, $this->bulk_action_trash() );
96 }
97
98 if ( ! current_user_can( 'administrator' ) ) {
99 $can_trash_post = tutor_utils()->get_option( 'instructor_can_delete_course' ) && current_user_can( 'edit_tutor_course' );
100 if ( ! $can_trash_post ) {
101 $actions = array_filter(
102 $actions,
103 function ( $val ) {
104 return 'trash' !== $val['value'];
105 }
106 );
107 }
108 }
109 return apply_filters( 'tutor_course_bulk_actions', $actions );
110 }
111
112 /**
113 * Available tabs that will visible on the right side of page navbar
114 *
115 * @param string $category_slug category slug.
116 * @param integer $course_id course ID.
117 * @param string $date selected date | optional.
118 * @param string $search search by user name or email | optional.
119 *
120 * @return array
121 *
122 * @since v2.0.0
123 */
124 public function tabs_key_value( $category_slug, $course_id, $date, $search ): array {
125 $url = get_pagenum_link();
126
127 $all = self::count_course( 'all', $category_slug, $course_id, $date, $search );
128 $mine = self::count_course( 'mine', $category_slug, $course_id, $date, $search );
129 $published = self::count_course( 'publish', $category_slug, $course_id, $date, $search );
130 $draft = self::count_course( 'draft', $category_slug, $course_id, $date, $search );
131 $pending = self::count_course( 'pending', $category_slug, $course_id, $date, $search );
132 $trash = self::count_course( 'trash', $category_slug, $course_id, $date, $search );
133 $private = self::count_course( 'private', $category_slug, $course_id, $date, $search );
134 $future = self::count_course( 'future', $category_slug, $course_id, $date, $search );
135
136 $tabs = array(
137 array(
138 'key' => 'all',
139 'title' => __( 'All', 'tutor' ),
140 'value' => $all,
141 'url' => $url . '&data=all',
142 ),
143 array(
144 'key' => 'mine',
145 'title' => __( 'Mine', 'tutor' ),
146 'value' => $mine,
147 'url' => $url . '&data=mine',
148 ),
149 array(
150 'key' => 'published',
151 'title' => __( 'Published', 'tutor' ),
152 'value' => $published,
153 'url' => $url . '&data=published',
154 ),
155 array(
156 'key' => 'draft',
157 'title' => __( 'Draft', 'tutor' ),
158 'value' => $draft,
159 'url' => $url . '&data=draft',
160 ),
161 array(
162 'key' => 'pending',
163 'title' => __( 'Pending', 'tutor' ),
164 'value' => $pending,
165 'url' => $url . '&data=pending',
166 ),
167 array(
168 'key' => 'future',
169 'title' => __( 'Scheduled', 'tutor' ),
170 'value' => $future,
171 'url' => $url . '&data=future',
172 ),
173 array(
174 'key' => 'private',
175 'title' => __( 'Private', 'tutor' ),
176 'value' => $private,
177 'url' => $url . '&data=private',
178 ),
179 array(
180 'key' => 'trash',
181 'title' => __( 'Trash', 'tutor' ),
182 'value' => $trash,
183 'url' => $url . '&data=trash',
184 ),
185 );
186 if ( ! tutor_utils()->get_option( 'instructor_can_delete_course' ) && ! current_user_can( 'administrator' ) ) {
187 unset( $tabs[7] );
188 }
189 return apply_filters( 'tutor_course_tabs', $tabs );
190 }
191
192 /**
193 * Count courses by status & filters
194 * Count all | min | published | pending | draft
195 *
196 * @param string $status | required.
197 * @param string $category_slug course category | optional.
198 * @param string $course_id selected course id | optional.
199 * @param string $date selected date | optional.
200 * @param string $search_term search by user name or email | optional.
201 *
202 * @return int
203 *
204 * @since 2.0.0
205 */
206 protected static function count_course( string $status, $category_slug = '', $course_id = '', $date = '', $search_term = '' ): int {
207 $user_id = get_current_user_id();
208 $status = sanitize_text_field( $status );
209 $course_id = sanitize_text_field( $course_id );
210 $date = sanitize_text_field( $date );
211 $search_term = sanitize_text_field( $search_term );
212 $category_slug = sanitize_text_field( $category_slug );
213
214 $args = array(
215 'post_type' => tutor()->course_post_type,
216 );
217
218 if ( 'all' === $status || 'mine' === $status ) {
219 $args['post_status'] = array( 'publish', 'pending', 'draft', 'private', 'future' );
220 } else {
221 $args['post_status'] = array( $status );
222 }
223
224 // Author query.
225 if ( 'mine' === $status || ! current_user_can( 'administrator' ) ) {
226 $args['author'] = $user_id;
227 }
228
229 $date_filter = sanitize_text_field( $date );
230
231 $year = date( 'Y', strtotime( $date_filter ) );
232 $month = date( 'm', strtotime( $date_filter ) );
233 $day = date( 'd', strtotime( $date_filter ) );
234
235 // Add date query.
236 if ( '' !== $date_filter ) {
237 $args['date_query'] = array(
238 array(
239 'year' => $year,
240 'month' => $month,
241 'day' => $day,
242 ),
243 );
244 }
245
246 if ( '' !== $course_id ) {
247 $args['p'] = $course_id;
248 }
249
250 // Search filter.
251 if ( '' !== $search_term ) {
252 $args['s'] = $search_term;
253 }
254
255 // Category filter.
256 if ( '' !== $category_slug ) {
257 $args['tax_query'] = array(
258 array(
259 'taxonomy' => 'course-category',
260 'field' => 'slug',
261 'terms' => $category_slug,
262 ),
263 );
264 }
265
266 $the_query = new \WP_Query( $args );
267
268 return ! is_null( $the_query ) && isset( $the_query->found_posts ) ? $the_query->found_posts : $the_query;
269
270 }
271
272 /**
273 * Handle bulk action for enrollment cancel | delete
274 *
275 * @return void
276 * @since 2.0.0
277 */
278 public function course_list_bulk_action() {
279
280 tutor_utils()->checking_nonce();
281
282 $action = Input::post( 'bulk-action', '' );
283 $bulk_ids = Input::post( 'bulk-ids', '' );
284
285 // Check if user is privileged.
286 if ( ! current_user_can( 'administrator' ) ) {
287 if ( current_user_can( 'edit_tutor_course' ) ) {
288 $can_publish_course = tutor_utils()->get_option( 'instructor_can_publish_course' );
289
290 if ( 'publish' === $action && ! $can_publish_course ) {
291 wp_send_json_error( tutor_utils()->error_message() );
292 }
293 } else {
294 wp_send_json_error( tutor_utils()->error_message() );
295 }
296 }
297
298 if ( '' === $action || '' === $bulk_ids ) {
299 wp_send_json_error( array( 'message' => __( 'Please select appropriate action', 'tutor' ) ) );
300 exit;
301 }
302
303 if ( 'delete' === $action ) {
304 // Do action before delete.
305 do_action( 'before_tutor_course_bulk_action_delete', $bulk_ids );
306
307 $delete_courses = self::bulk_delete_course( $bulk_ids );
308
309 do_action( 'after_tutor_course_bulk_action_delete', $bulk_ids );
310 $delete_courses ? wp_send_json_success() : wp_send_json_error( array( 'message' => __( 'Could not delete selected courses', 'tutor' ) ) );
311 exit;
312 }
313
314 /**
315 * Do action before course update
316 *
317 * @param string $action (publish | pending | draft | trash).
318 * @param array $bulk_ids, course id.
319 */
320 do_action( 'before_tutor_course_bulk_action_update', $action, $bulk_ids );
321
322 $update_status = self::update_course_status( $action, $bulk_ids );
323
324 do_action( 'after_tutor_course_bulk_action_update', $action, $bulk_ids );
325
326 $update_status ? wp_send_json_success() : wp_send_json_error(
327 array(
328 'message' => 'Could not update course status',
329 'tutor',
330 )
331 );
332
333 exit;
334 }
335
336 /**
337 * Handle ajax request for updating course status
338 *
339 * @return void
340 * @since 2.0.0
341 */
342 public static function tutor_change_course_status() {
343 tutor_utils()->checking_nonce();
344
345 $status = Input::post( 'status' );
346 $id = Input::post( 'id' );
347 $course = get_post( $id );
348
349 // Check if user is privileged.
350 if ( ! current_user_can( 'administrator' ) ) {
351
352 if ( ! tutor_utils()->can_user_edit_course( get_current_user_id(), $course->ID ) ) {
353 wp_send_json_error( tutor_utils()->error_message() );
354 }
355
356 $can_delete_course = tutor_utils()->get_option( 'instructor_can_delete_course' );
357 $can_publish_course = tutor_utils()->get_option( 'instructor_can_publish_course' );
358
359 if ( 'publish' === $status && ! $can_publish_course ) {
360 wp_send_json_error( tutor_utils()->error_message() );
361 }
362
363 if ( 'trash' === $status && $can_delete_course ) {
364 $args = array(
365 'ID' => $id,
366 'post_status' => $status,
367 );
368 $trash_post = wp_update_post( $args );
369
370 if ( $trash_post ) {
371 wp_send_json_success( __( 'Course trashed successfully', 'tutor' ) );
372 }
373 }
374 }
375
376 if ( CourseModel::POST_TYPE !== $course->post_type ) {
377 wp_send_json_error( tutor_utils()->error_message() );
378 }
379
380 $args = array(
381 'ID' => $id,
382 'post_status' => $status,
383 );
384
385 if ( CourseModel::STATUS_FUTURE === $course->post_status && CourseModel::STATUS_PUBLISH === $status ) {
386 $args['post_status'] = CourseModel::STATUS_PUBLISH;
387 $args['post_date'] = current_time( 'mysql' );
388 $args['post_date_gmt'] = current_time( 'mysql', 1 );
389 }
390
391 wp_update_post( $args );
392 wp_send_json_success();
393 exit;
394 }
395
396 /**
397 * Handle ajax request for deleting course
398 *
399 * @since 2.0.0
400 *
401 * @return void JSON response
402 */
403 public static function tutor_course_delete() {
404 tutor_utils()->checking_nonce();
405
406 $user_id = get_current_user_id();
407 $course_id = Input::post( 'id', 0, Input::TYPE_INT );
408
409 // Check if user is privileged.
410 if ( ! tutor_utils()->can_user_edit_course( $user_id, $course_id ) ) {
411 wp_send_json_error( tutor_utils()->error_message() );
412 }
413
414 $delete = CourseModel::delete_course( $course_id );
415
416 if ( $delete ) {
417 wp_send_json_success( __( 'Course has been deleted ', 'tutor' ) );
418 } else {
419 wp_send_json_error( __( 'Course delete failed ', 'tutor' ) );
420 }
421
422 exit;
423 }
424
425 /**
426 * Execute bulk delete action
427 *
428 * @param string $bulk_ids ids that need to update.
429 * @return bool
430 * @since 2.0.0
431 */
432 public static function bulk_delete_course( $bulk_ids ): bool {
433 $bulk_ids = explode( ',', sanitize_text_field( $bulk_ids ) );
434
435 foreach ( $bulk_ids as $post_id ) {
436 CourseModel::delete_course( $post_id );
437 }
438
439 return true;
440 }
441
442 /**
443 * Update course status
444 *
445 * @param string $status for updating course status.
446 * @param string $bulk_ids comma separated ids.
447 *
448 * @return bool
449 *
450 * @since 2.0.0
451 */
452 public static function update_course_status( string $status, $bulk_ids ): bool {
453 global $wpdb;
454 $post_table = $wpdb->posts;
455 $status = sanitize_text_field( $status );
456 $bulk_ids = sanitize_text_field( $bulk_ids );
457
458 $ids = array_map( 'intval', explode( ',', $bulk_ids ) );
459 $in_clause = QueryHelper::prepare_in_clause( $ids );
460
461 $update = $wpdb->query(
462 $wpdb->prepare(
463 "UPDATE {$post_table} SET post_status = %s WHERE ID IN ($in_clause)", //phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared
464 $status
465 )
466 );
467
468 return true;
469 }
470
471 /**
472 * Get course enrollment list with student info
473 *
474 * @param int $course_id int | required.
475 * @return array
476 * @since 2.0.0
477 */
478 public static function course_enrollments_with_student_details( int $course_id ) {
479 global $wpdb;
480 $course_id = sanitize_text_field( $course_id );
481 $course_completed = 0;
482 $course_inprogress = 0;
483
484 $enrollments = $wpdb->get_results(
485 $wpdb->prepare(
486 "SELECT enroll.ID AS enroll_id, enroll.post_author AS enroll_author, user.*, course.ID AS course_id
487 FROM {$wpdb->posts} AS enroll
488 LEFT JOIN {$wpdb->users} AS user ON user.ID = enroll.post_author
489 LEFT JOIN {$wpdb->posts} AS course ON course.ID = enroll.post_parent
490 WHERE enroll.post_type = %s
491 AND enroll.post_status = %s
492 AND enroll.post_parent = %d
493 ",
494 'tutor_enrolled',
495 'completed',
496 $course_id
497 )
498 );
499
500 foreach ( $enrollments as $enrollment ) {
501 $course_progress = tutor_utils()->get_course_completed_percent( $course_id, $enrollment->enroll_author );
502 if ( 100 == $course_progress ) {
503 $course_completed++;
504 } else {
505 $course_inprogress++;
506 }
507 }
508
509 return array(
510 'enrollments' => $enrollments,
511 'total_completed' => $course_completed,
512 'total_inprogress' => $course_inprogress,
513 'total_enrollments' => count( $enrollments ),
514 );
515 }
516
517 /**
518 * Check wheather course is public or not
519 *
520 * @param integer $course_id course id to check with.
521 * @return boolean true if public otherwise false.
522 * @since 1.0.0
523 */
524 public static function is_public( int $course_id ): bool {
525 $is_public = get_post_meta( $course_id, '_tutor_is_public_course', true );
526 return 'yes' === $is_public ? true : false;
527 }
528 }
529