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 | classes/Course_List.php +102 -162 trunk2.2.1 View file →
@@ -9,13 +9,14 @@
9 9 */
10 10
11 11 namespace TUTOR;
12 12
13 -defined( 'ABSPATH' ) || exit;
14 -
15 13 use Tutor\Helpers\QueryHelper;
16 14 use Tutor\Models\CourseModel;
17 15
16 +if ( ! defined( 'ABSPATH' ) ) {
17 + exit;
18 +}
18 19 /**
19 20 * Course List class
20 21 *
21 22 * @since 2.0.0
@@ -29,8 +30,15 @@
29 30
30 31 use Backend_Page_Trait;
31 32
32 33 /**
34 + * Page Title
35 + *
36 + * @var $page_title
37 + */
38 + public $page_title;
39 +
40 + /**
33 41 * Bulk Action
34 42 *
35 43 * @var $bulk_action
36 44 */
@@ -39,53 +47,37 @@
39 47 /**
40 48 * Constructor
41 49 *
42 50 * @return void
43 - *
44 51 * @since 2.0.0
45 52 */
46 53 public function __construct() {
54 + $this->page_title = __( 'Courses', 'tutor' );
47 55 /**
48 56 * Handle bulk action
49 57 *
50 - * @since 2.0.0
58 + * @since v2.0.0
51 59 */
52 60 add_action( 'wp_ajax_tutor_course_list_bulk_action', array( $this, 'course_list_bulk_action' ) );
53 61 /**
54 62 * Handle ajax request for updating course status
55 63 *
56 - * @since 2.0.0
64 + * @since v2.0.0
57 65 */
58 66 add_action( 'wp_ajax_tutor_change_course_status', array( $this, 'tutor_change_course_status' ) );
59 67 /**
60 68 * Handle ajax request for delete course
61 69 *
62 - * @since 2.0.0
70 + * @since v2.0.0
63 71 */
64 72 add_action( 'wp_ajax_tutor_course_delete', array( $this, 'tutor_course_delete' ) );
65 73 }
66 74
67 75 /**
68 - * Page title fallback
69 - *
70 - * @since 3.5.0
71 - *
72 - * @param string $name Property name.
73 - *
74 - * @return string
75 - */
76 - public function __get( $name ) {
77 - if ( 'page_title' === $name ) {
78 - return esc_html__( 'Courses', 'tutor' );
79 - }
80 - }
81 -
82 - /**
83 76 * Prepare bulk actions that will show on dropdown options
84 77 *
78 + * @return array
85 79 * @since 2.0.0
86 - *
87 - * @return array
88 80 */
89 81 public function prepare_bulk_actions(): array {
90 82 $actions = array(
91 83 $this->bulk_action_default(),
@@ -95,21 +87,14 @@
95 87 );
96 88
97 89 $active_tab = Input::get( 'data', '' );
98 90
99 - if ( CourseModel::STATUS_TRASH === $active_tab ) {
91 + if ( 'trash' === $active_tab ) {
100 92 array_push( $actions, $this->bulk_action_delete() );
101 93 }
102 - if ( CourseModel::STATUS_TRASH !== $active_tab ) {
94 + if ( 'trash' !== $active_tab ) {
103 95 array_push( $actions, $this->bulk_action_trash() );
104 96 }
105 -
106 - if ( ! User::is_admin() ) {
107 - $can_trash_post = tutor_utils()->get_option( 'instructor_can_delete_course' ) && current_user_can( 'edit_tutor_courses' );
108 - if ( ! $can_trash_post ) {
109 - $actions = array_filter( $actions, fn ( $val ) => CourseModel::STATUS_TRASH !== $val['value'] );
110 - }
111 - }
112 97 return apply_filters( 'tutor_course_bulk_actions', $actions );
113 98 }
114 99
115 100 /**
@@ -114,10 +99,8 @@
114 99
115 100 /**
116 101 * Available tabs that will visible on the right side of page navbar
117 102 *
118 - * @since 2.0.0
119 - *
120 103 * @param string $category_slug category slug.
121 104 * @param integer $course_id course ID.
122 105 * @param string $date selected date | optional.
123 106 * @param string $search search by user name or email | optional.
@@ -122,11 +105,13 @@
122 105 * @param string $date selected date | optional.
123 106 * @param string $search search by user name or email | optional.
124 107 *
125 108 * @return array
109 + *
110 + * @since v2.0.0
126 111 */
127 112 public function tabs_key_value( $category_slug, $course_id, $date, $search ): array {
128 - $url = apply_filters( 'tutor_data_tab_base_url', get_pagenum_link() );
113 + $url = get_pagenum_link();
129 114
130 115 $all = self::count_course( 'all', $category_slug, $course_id, $date, $search );
131 116 $mine = self::count_course( 'mine', $category_slug, $course_id, $date, $search );
132 117 $published = self::count_course( 'publish', $category_slug, $course_id, $date, $search );
@@ -132,14 +117,12 @@
132 117 $published = self::count_course( 'publish', $category_slug, $course_id, $date, $search );
133 118 $draft = self::count_course( 'draft', $category_slug, $course_id, $date, $search );
134 119 $pending = self::count_course( 'pending', $category_slug, $course_id, $date, $search );
135 120 $trash = self::count_course( 'trash', $category_slug, $course_id, $date, $search );
136 - $private = self::count_course( 'private', $category_slug, $course_id, $date, $search );
137 - $future = self::count_course( 'future', $category_slug, $course_id, $date, $search );
138 121
139 122 $tabs = array(
140 123 array(
141 - 'key' => '',
124 + 'key' => 'all',
142 125 'title' => __( 'All', 'tutor' ),
143 126 'value' => $all,
144 127 'url' => $url . '&data=all',
145 128 ),
@@ -167,20 +150,8 @@
167 150 'value' => $pending,
168 151 'url' => $url . '&data=pending',
169 152 ),
170 153 array(
171 - 'key' => 'future',
172 - 'title' => __( 'Scheduled', 'tutor' ),
173 - 'value' => $future,
174 - 'url' => $url . '&data=future',
175 - ),
176 - array(
177 - 'key' => 'private',
178 - 'title' => __( 'Private', 'tutor' ),
179 - 'value' => $private,
180 - 'url' => $url . '&data=private',
181 - ),
182 - array(
183 154 'key' => 'trash',
184 155 'title' => __( 'Trash', 'tutor' ),
185 156 'value' => $trash,
186 157 'url' => $url . '&data=trash',
@@ -185,11 +156,8 @@
185 156 'value' => $trash,
186 157 'url' => $url . '&data=trash',
187 158 ),
188 159 );
189 - if ( ! tutor_utils()->get_option( 'instructor_can_delete_course' ) && ! current_user_can( 'administrator' ) ) {
190 - unset( $tabs[7] );
191 - }
192 160 return apply_filters( 'tutor_course_tabs', $tabs );
193 161 }
194 162
195 163 /**
@@ -195,10 +163,8 @@
195 163 /**
196 164 * Count courses by status & filters
197 165 * Count all | min | published | pending | draft
198 166 *
199 - * @since 2.0.0
200 - *
201 167 * @param string $status | required.
202 168 * @param string $category_slug course category | optional.
203 169 * @param string $course_id selected course id | optional.
204 170 * @param string $date selected date | optional.
@@ -204,8 +170,10 @@
204 170 * @param string $date selected date | optional.
205 171 * @param string $search_term search by user name or email | optional.
206 172 *
207 173 * @return int
174 + *
175 + * @since 2.0.0
208 176 */
209 177 protected static function count_course( string $status, $category_slug = '', $course_id = '', $date = '', $search_term = '' ): int {
210 178 $user_id = get_current_user_id();
211 179 $status = sanitize_text_field( $status );
@@ -218,9 +186,9 @@
218 186 'post_type' => tutor()->course_post_type,
219 187 );
220 188
221 189 if ( 'all' === $status || 'mine' === $status ) {
222 - $args['post_status'] = array( 'publish', 'pending', 'draft', 'private', 'future' );
190 + $args['post_status'] = array( 'publish', 'pending', 'draft', 'private' );
223 191 } else {
224 192 $args['post_status'] = array( $status );
225 193 }
226 194
@@ -230,11 +198,11 @@
230 198 }
231 199
232 200 $date_filter = sanitize_text_field( $date );
233 201
234 - $year = gmdate( 'Y', strtotime( $date_filter ) );
235 - $month = gmdate( 'm', strtotime( $date_filter ) );
236 - $day = gmdate( 'd', strtotime( $date_filter ) );
202 + $year = date( 'Y', strtotime( $date_filter ) );
203 + $month = date( 'm', strtotime( $date_filter ) );
204 + $day = date( 'd', strtotime( $date_filter ) );
237 205
238 206 // Add date query.
239 207 if ( '' !== $date_filter ) {
240 208 $args['date_query'] = array(
@@ -258,9 +226,9 @@
258 226 // Category filter.
259 227 if ( '' !== $category_slug ) {
260 228 $args['tax_query'] = array(
261 229 array(
262 - 'taxonomy' => CourseModel::COURSE_CATEGORY,
230 + 'taxonomy' => 'course-category',
263 231 'field' => 'slug',
264 232 'terms' => $category_slug,
265 233 ),
266 234 );
@@ -265,53 +233,31 @@
265 233 ),
266 234 );
267 235 }
268 236
269 - $the_query = self::course_list_query( $args, $user_id, $status );
237 + $the_query = new \WP_Query( $args );
270 238
271 239 return ! is_null( $the_query ) && isset( $the_query->found_posts ) ? $the_query->found_posts : $the_query;
240 +
272 241 }
273 242
274 243 /**
275 244 * Handle bulk action for enrollment cancel | delete
276 245 *
246 + * @return void
277 247 * @since 2.0.0
278 - *
279 - * @return void
280 248 */
281 249 public function course_list_bulk_action() {
282 250
283 251 tutor_utils()->checking_nonce();
284 252
285 - $action = Input::post( 'bulk-action', '' );
286 - $bulk_ids = Input::post( 'bulk-ids', '' );
287 -
288 253 // Check if user is privileged.
289 - if ( ! User::is_admin() ) {
290 - $course_ids = explode( ',', $bulk_ids );
291 -
292 - if ( current_user_can( 'edit_tutor_courses' ) ) {
293 - $can_publish_course = tutor_utils()->get_option( 'instructor_can_publish_course' );
294 -
295 - if ( CourseModel::STATUS_PUBLISH === $action && ! $can_publish_course ) {
296 - wp_send_json_error( tutor_utils()->error_message() );
297 - }
298 - } else {
299 - wp_send_json_error( tutor_utils()->error_message() );
300 - }
301 -
302 - // Check if the course ids are instructors own course.
303 - $course_ids = array_filter(
304 - $course_ids,
305 - function ( $course_id ) {
306 - return tutor_utils()->is_instructor_of_this_course( get_current_user_id(), $course_id );
307 - }
308 - );
309 -
310 - $bulk_ids = implode( ',', $course_ids );
311 -
254 + if ( ! current_user_can( 'administrator' ) ) {
255 + wp_send_json_error( tutor_utils()->error_message() );
312 256 }
313 257
258 + $action = Input::post( 'bulk-action', '' );
259 + $bulk_ids = Input::post( 'bulk-ids', '' );
314 260 if ( '' === $action || '' === $bulk_ids ) {
315 261 wp_send_json_error( array( 'message' => __( 'Please select appropriate action', 'tutor' ) ) );
316 262 exit;
317 263 }
@@ -338,9 +284,14 @@
338 284 $update_status = self::update_course_status( $action, $bulk_ids );
339 285
340 286 do_action( 'after_tutor_course_bulk_action_update', $action, $bulk_ids );
341 287
342 - $update_status ? wp_send_json_success() : wp_send_json_error( array( 'message' => __( 'Could not update course status', 'tutor' ) ) );
288 + $update_status ? wp_send_json_success() : wp_send_json_error(
289 + array(
290 + 'message' => 'Could not update course status',
291 + 'tutor',
292 + )
293 + );
343 294
344 295 exit;
345 296 }
346 297
@@ -346,62 +297,28 @@
346 297
347 298 /**
348 299 * Handle ajax request for updating course status
349 300 *
301 + * @return void
350 302 * @since 2.0.0
351 - *
352 - * @return void
353 303 */
354 304 public static function tutor_change_course_status() {
355 305 tutor_utils()->checking_nonce();
356 306
357 - $status = Input::post( 'status' );
358 - $id = Input::post( 'id', 0, Input::TYPE_INT );
359 - $course = get_post( $id );
360 -
361 307 // Check if user is privileged.
362 - if ( ! User::is_admin() ) {
363 -
364 - if ( ! tutor_utils()->can_user_edit_course( get_current_user_id(), $course->ID ) ) {
365 - wp_send_json_error( tutor_utils()->error_message() );
366 - }
367 -
368 - $can_delete_course = tutor_utils()->get_option( 'instructor_can_delete_course' );
369 - $can_publish_course = tutor_utils()->get_option( 'instructor_can_publish_course' );
370 -
371 - if ( CourseModel::STATUS_PUBLISH === $status && ! $can_publish_course ) {
372 - wp_send_json_error( tutor_utils()->error_message() );
373 - }
374 -
375 - if ( CourseModel::STATUS_TRASH === $status && $can_delete_course ) {
376 - $args = array(
377 - 'ID' => $id,
378 - 'post_status' => $status,
379 - );
380 - $trash_post = wp_update_post( $args );
381 -
382 - if ( $trash_post ) {
383 - wp_send_json_success( __( 'Course trashed successfully', 'tutor' ) );
384 - }
385 - }
308 + if ( ! current_user_can( 'administrator' ) ) {
309 + wp_send_json_error( tutor_utils()->error_message() );
386 310 }
387 311
388 - if ( ! CourseModel::get_post_types( $course ) ) {
389 - wp_send_json_error( tutor_utils()->error_message() );
390 - }
312 + $status = Input::post( 'status' );
313 + $id = Input::post( 'id' );
391 314
392 315 $args = array(
393 316 'ID' => $id,
394 317 'post_status' => $status,
395 318 );
319 + wp_update_post( $args );
396 320
397 - if ( CourseModel::STATUS_FUTURE === $course->post_status && CourseModel::STATUS_PUBLISH === $status ) {
398 - $args['post_status'] = CourseModel::STATUS_PUBLISH;
399 - $args['post_date'] = current_time( 'mysql' );
400 - $args['post_date_gmt'] = current_time( 'mysql', 1 );
401 - }
402 -
403 - wp_update_post( $args );
404 321 wp_send_json_success();
405 322 exit;
406 323 }
407 324
@@ -414,17 +331,16 @@
414 331 */
415 332 public static function tutor_course_delete() {
416 333 tutor_utils()->checking_nonce();
417 334
418 - $user_id = get_current_user_id();
419 - $course_id = Input::post( 'id', 0, Input::TYPE_INT );
420 -
421 335 // Check if user is privileged.
422 - if ( ! tutor_utils()->can_user_edit_course( $user_id, $course_id ) ) {
336 + $roles = array( User::ADMIN, User::INSTRUCTOR );
337 + if ( ! User::has_any_role( $roles ) ) {
423 338 wp_send_json_error( tutor_utils()->error_message() );
424 339 }
425 340
426 - $delete = CourseModel::delete_course( $course_id );
341 + $id = Input::post( 'id', 0, Input::TYPE_INT );
342 + $delete = CourseModel::delete_course( $id );
427 343
428 344 if ( $delete ) {
429 345 wp_send_json_success( __( 'Course has been deleted ', 'tutor' ) );
430 346 } else {
@@ -436,13 +352,11 @@
436 352
437 353 /**
438 354 * Execute bulk delete action
439 355 *
440 - * @since 2.0.0
441 - *
442 356 * @param string $bulk_ids ids that need to update.
443 - *
444 357 * @return bool
358 + * @since 2.0.0
445 359 */
446 360 public static function bulk_delete_course( $bulk_ids ): bool {
447 361 $bulk_ids = explode( ',', sanitize_text_field( $bulk_ids ) );
448 362
@@ -455,14 +369,14 @@
455 369
456 370 /**
457 371 * Update course status
458 372 *
459 - * @since 2.0.0
460 - *
461 373 * @param string $status for updating course status.
462 374 * @param string $bulk_ids comma separated ids.
463 375 *
464 376 * @return bool
377 + *
378 + * @since 2.0.0
465 379 */
466 380 public static function update_course_status( string $status, $bulk_ids ): bool {
467 381 global $wpdb;
468 382 $post_table = $wpdb->posts;
@@ -471,9 +385,9 @@
471 385
472 386 $ids = array_map( 'intval', explode( ',', $bulk_ids ) );
473 387 $in_clause = QueryHelper::prepare_in_clause( $ids );
474 388
475 - $wpdb->query(
389 + $update = $wpdb->query(
476 390 $wpdb->prepare(
477 391 "UPDATE {$post_table} SET post_status = %s WHERE ID IN ($in_clause)", //phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared
478 392 $status
479 393 )
@@ -482,35 +396,61 @@
482 396 return true;
483 397 }
484 398
485 399 /**
486 - * Check course is public or not
400 + * Get course enrollment list with student info
487 401 *
488 - * @since 1.0.0
402 + * @param int $course_id int | required.
403 + * @return array
404 + * @since 2.0.0
405 + */
406 + public static function course_enrollments_with_student_details( int $course_id ) {
407 + global $wpdb;
408 + $course_id = sanitize_text_field( $course_id );
409 + $course_completed = 0;
410 + $course_inprogress = 0;
411 +
412 + $enrollments = $wpdb->get_results(
413 + $wpdb->prepare(
414 + "SELECT enroll.ID AS enroll_id, enroll.post_author AS enroll_author, user.*, course.ID AS course_id
415 + FROM {$wpdb->posts} AS enroll
416 + LEFT JOIN {$wpdb->users} AS user ON user.ID = enroll.post_author
417 + LEFT JOIN {$wpdb->posts} AS course ON course.ID = enroll.post_parent
418 + WHERE enroll.post_type = %s
419 + AND enroll.post_status = %s
420 + AND enroll.post_parent = %d
421 + ",
422 + 'tutor_enrolled',
423 + 'completed',
424 + $course_id
425 + )
426 + );
427 +
428 + foreach ( $enrollments as $enrollment ) {
429 + $course_progress = tutor_utils()->get_course_completed_percent( $course_id, $enrollment->enroll_author );
430 + if ( 100 == $course_progress ) {
431 + $course_completed++;
432 + } else {
433 + $course_inprogress++;
434 + }
435 + }
436 +
437 + return array(
438 + 'enrollments' => $enrollments,
439 + 'total_completed' => $course_completed,
440 + 'total_inprogress' => $course_inprogress,
441 + 'total_enrollments' => count( $enrollments ),
442 + );
443 + }
444 +
445 + /**
446 + * Check wheather course is public or not
489 447 *
490 448 * @param integer $course_id course id to check with.
491 - *
492 449 * @return boolean true if public otherwise false.
450 + * @since 1.0.0
493 451 */
494 452 public static function is_public( int $course_id ): bool {
495 453 $is_public = get_post_meta( $course_id, '_tutor_is_public_course', true );
496 454 return 'yes' === $is_public ? true : false;
497 - }
498 -
499 - /**
500 - * Query for obtaining course list.
501 - *
502 - * @since 3.4.0
503 - *
504 - * @param array $args the query args.
505 - * @param int $user_id the user id.
506 - * @param string $status the post status.
507 - * @param bool $all_post_types should keep all post types.
508 - *
509 - * @return \WP_Query
510 - */
511 - public static function course_list_query( $args, $user_id, $status, $all_post_types = false ) {
512 -
513 - $course_list_query = new \WP_Query( apply_filters( 'tutor_admin_course_list', $args, $user_id, $status, $all_post_types ) );
514 - return $course_list_query;
515 455 }
516 456 }