PluginProbe ʕ •ᴥ•ʔ
Tutor LMS – eLearning and online course solution / 3.3.0
Tutor LMS – eLearning and online course solution v3.3.0
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 1 year ago Admin.php 1 year ago Ajax.php 1 year ago Announcements.php 1 year ago Assets.php 1 year ago Backend_Page_Trait.php 1 year ago BaseController.php 1 year 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 1 year ago Course_Widget.php 1 year ago Custom_Validation.php 3 years ago Dashboard.php 1 year ago Earnings.php 1 year ago FormHandler.php 2 years ago Frontend.php 1 year ago Gutenberg.php 1 year ago Input.php 1 year ago Instructor.php 1 year ago Instructors_List.php 1 year ago Lesson.php 1 year ago Options_V2.php 1 year ago Permalink.php 2 years ago Post_types.php 1 year ago Private_Course_Access.php 1 year ago Q_And_A.php 1 year ago Question_Answers_List.php 3 years ago Quiz.php 1 year ago QuizBuilder.php 1 year ago Quiz_Attempts_List.php 1 year ago RestAPI.php 2 years ago Reviews.php 3 years ago Rewrite_Rules.php 2 years ago Shortcode.php 1 year ago Singleton.php 1 year ago Student.php 1 year ago Students_List.php 3 years ago Taxonomies.php 1 year ago Template.php 1 year ago Theme_Compatibility.php 3 years ago Tools.php 1 year ago Tools_V2.php 1 year ago Tutor.php 1 year ago TutorEDD.php 1 year ago Tutor_Base.php 2 years ago Tutor_Setup.php 1 year ago Upgrader.php 1 year ago User.php 1 year ago Utils.php 1 year ago Video_Stream.php 3 years ago WhatsNew.php 2 years ago Withdraw.php 1 year ago Withdraw_Requests_List.php 1 year ago WooCommerce.php 1 year ago
Course_List.php
524 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 = apply_filters( 'tutor_data_tab_base_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' => CourseModel::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( array( 'message' => __( 'Could not update course status', 'tutor' ) ) );
327
328 exit;
329 }
330
331 /**
332 * Handle ajax request for updating course status
333 *
334 * @return void
335 * @since 2.0.0
336 */
337 public static function tutor_change_course_status() {
338 tutor_utils()->checking_nonce();
339
340 $status = Input::post( 'status' );
341 $id = Input::post( 'id' );
342 $course = get_post( $id );
343
344 // Check if user is privileged.
345 if ( ! current_user_can( 'administrator' ) ) {
346
347 if ( ! tutor_utils()->can_user_edit_course( get_current_user_id(), $course->ID ) ) {
348 wp_send_json_error( tutor_utils()->error_message() );
349 }
350
351 $can_delete_course = tutor_utils()->get_option( 'instructor_can_delete_course' );
352 $can_publish_course = tutor_utils()->get_option( 'instructor_can_publish_course' );
353
354 if ( 'publish' === $status && ! $can_publish_course ) {
355 wp_send_json_error( tutor_utils()->error_message() );
356 }
357
358 if ( 'trash' === $status && $can_delete_course ) {
359 $args = array(
360 'ID' => $id,
361 'post_status' => $status,
362 );
363 $trash_post = wp_update_post( $args );
364
365 if ( $trash_post ) {
366 wp_send_json_success( __( 'Course trashed successfully', 'tutor' ) );
367 }
368 }
369 }
370
371 if ( CourseModel::POST_TYPE !== $course->post_type ) {
372 wp_send_json_error( tutor_utils()->error_message() );
373 }
374
375 $args = array(
376 'ID' => $id,
377 'post_status' => $status,
378 );
379
380 if ( CourseModel::STATUS_FUTURE === $course->post_status && CourseModel::STATUS_PUBLISH === $status ) {
381 $args['post_status'] = CourseModel::STATUS_PUBLISH;
382 $args['post_date'] = current_time( 'mysql' );
383 $args['post_date_gmt'] = current_time( 'mysql', 1 );
384 }
385
386 wp_update_post( $args );
387 wp_send_json_success();
388 exit;
389 }
390
391 /**
392 * Handle ajax request for deleting course
393 *
394 * @since 2.0.0
395 *
396 * @return void JSON response
397 */
398 public static function tutor_course_delete() {
399 tutor_utils()->checking_nonce();
400
401 $user_id = get_current_user_id();
402 $course_id = Input::post( 'id', 0, Input::TYPE_INT );
403
404 // Check if user is privileged.
405 if ( ! tutor_utils()->can_user_edit_course( $user_id, $course_id ) ) {
406 wp_send_json_error( tutor_utils()->error_message() );
407 }
408
409 $delete = CourseModel::delete_course( $course_id );
410
411 if ( $delete ) {
412 wp_send_json_success( __( 'Course has been deleted ', 'tutor' ) );
413 } else {
414 wp_send_json_error( __( 'Course delete failed ', 'tutor' ) );
415 }
416
417 exit;
418 }
419
420 /**
421 * Execute bulk delete action
422 *
423 * @param string $bulk_ids ids that need to update.
424 * @return bool
425 * @since 2.0.0
426 */
427 public static function bulk_delete_course( $bulk_ids ): bool {
428 $bulk_ids = explode( ',', sanitize_text_field( $bulk_ids ) );
429
430 foreach ( $bulk_ids as $post_id ) {
431 CourseModel::delete_course( $post_id );
432 }
433
434 return true;
435 }
436
437 /**
438 * Update course status
439 *
440 * @param string $status for updating course status.
441 * @param string $bulk_ids comma separated ids.
442 *
443 * @return bool
444 *
445 * @since 2.0.0
446 */
447 public static function update_course_status( string $status, $bulk_ids ): bool {
448 global $wpdb;
449 $post_table = $wpdb->posts;
450 $status = sanitize_text_field( $status );
451 $bulk_ids = sanitize_text_field( $bulk_ids );
452
453 $ids = array_map( 'intval', explode( ',', $bulk_ids ) );
454 $in_clause = QueryHelper::prepare_in_clause( $ids );
455
456 $update = $wpdb->query(
457 $wpdb->prepare(
458 "UPDATE {$post_table} SET post_status = %s WHERE ID IN ($in_clause)", //phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared
459 $status
460 )
461 );
462
463 return true;
464 }
465
466 /**
467 * Get course enrollment list with student info
468 *
469 * @param int $course_id int | required.
470 * @return array
471 * @since 2.0.0
472 */
473 public static function course_enrollments_with_student_details( int $course_id ) {
474 global $wpdb;
475 $course_id = sanitize_text_field( $course_id );
476 $course_completed = 0;
477 $course_inprogress = 0;
478
479 $enrollments = $wpdb->get_results(
480 $wpdb->prepare(
481 "SELECT enroll.ID AS enroll_id, enroll.post_author AS enroll_author, user.*, course.ID AS course_id
482 FROM {$wpdb->posts} AS enroll
483 LEFT JOIN {$wpdb->users} AS user ON user.ID = enroll.post_author
484 LEFT JOIN {$wpdb->posts} AS course ON course.ID = enroll.post_parent
485 WHERE enroll.post_type = %s
486 AND enroll.post_status = %s
487 AND enroll.post_parent = %d
488 ",
489 'tutor_enrolled',
490 'completed',
491 $course_id
492 )
493 );
494
495 foreach ( $enrollments as $enrollment ) {
496 $course_progress = tutor_utils()->get_course_completed_percent( $course_id, $enrollment->enroll_author );
497 if ( 100 == $course_progress ) {
498 $course_completed++;
499 } else {
500 $course_inprogress++;
501 }
502 }
503
504 return array(
505 'enrollments' => $enrollments,
506 'total_completed' => $course_completed,
507 'total_inprogress' => $course_inprogress,
508 'total_enrollments' => count( $enrollments ),
509 );
510 }
511
512 /**
513 * Check wheather course is public or not
514 *
515 * @param integer $course_id course id to check with.
516 * @return boolean true if public otherwise false.
517 * @since 1.0.0
518 */
519 public static function is_public( int $course_id ): bool {
520 $is_public = get_post_meta( $course_id, '_tutor_is_public_course', true );
521 return 'yes' === $is_public ? true : false;
522 }
523 }
524