PluginProbe ʕ •ᴥ•ʔ
Tutor LMS – eLearning and online course solution / 2.7.2
Tutor LMS – eLearning and online course solution v2.7.2
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 2 years ago Course_Embed.php 3 years ago Course_Filter.php 2 years ago Course_List.php 2 years 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 2 years ago Lesson.php 2 years ago Options_V2.php 2 years ago Permalink.php 2 years ago Post_types.php 2 years ago Private_Course_Access.php 3 years ago Q_And_A.php 2 years 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 2 years 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 2 years 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
483 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 return apply_filters( 'tutor_course_bulk_actions', $actions );
98 }
99
100 /**
101 * Available tabs that will visible on the right side of page navbar
102 *
103 * @param string $category_slug category slug.
104 * @param integer $course_id course ID.
105 * @param string $date selected date | optional.
106 * @param string $search search by user name or email | optional.
107 *
108 * @return array
109 *
110 * @since v2.0.0
111 */
112 public function tabs_key_value( $category_slug, $course_id, $date, $search ): array {
113 $url = get_pagenum_link();
114
115 $all = self::count_course( 'all', $category_slug, $course_id, $date, $search );
116 $mine = self::count_course( 'mine', $category_slug, $course_id, $date, $search );
117 $published = self::count_course( 'publish', $category_slug, $course_id, $date, $search );
118 $draft = self::count_course( 'draft', $category_slug, $course_id, $date, $search );
119 $pending = self::count_course( 'pending', $category_slug, $course_id, $date, $search );
120 $trash = self::count_course( 'trash', $category_slug, $course_id, $date, $search );
121 $private = self::count_course( 'private', $category_slug, $course_id, $date, $search );
122 $future = self::count_course( 'future', $category_slug, $course_id, $date, $search );
123
124 $tabs = array(
125 array(
126 'key' => 'all',
127 'title' => __( 'All', 'tutor' ),
128 'value' => $all,
129 'url' => $url . '&data=all',
130 ),
131 array(
132 'key' => 'mine',
133 'title' => __( 'Mine', 'tutor' ),
134 'value' => $mine,
135 'url' => $url . '&data=mine',
136 ),
137 array(
138 'key' => 'published',
139 'title' => __( 'Published', 'tutor' ),
140 'value' => $published,
141 'url' => $url . '&data=published',
142 ),
143 array(
144 'key' => 'draft',
145 'title' => __( 'Draft', 'tutor' ),
146 'value' => $draft,
147 'url' => $url . '&data=draft',
148 ),
149 array(
150 'key' => 'pending',
151 'title' => __( 'Pending', 'tutor' ),
152 'value' => $pending,
153 'url' => $url . '&data=pending',
154 ),
155 array(
156 'key' => 'future',
157 'title' => __( 'Scheduled', 'tutor' ),
158 'value' => $future,
159 'url' => $url . '&data=future',
160 ),
161 array(
162 'key' => 'private',
163 'title' => __( 'Private', 'tutor' ),
164 'value' => $private,
165 'url' => $url . '&data=private',
166 ),
167 array(
168 'key' => 'trash',
169 'title' => __( 'Trash', 'tutor' ),
170 'value' => $trash,
171 'url' => $url . '&data=trash',
172 ),
173 );
174 return apply_filters( 'tutor_course_tabs', $tabs );
175 }
176
177 /**
178 * Count courses by status & filters
179 * Count all | min | published | pending | draft
180 *
181 * @param string $status | required.
182 * @param string $category_slug course category | optional.
183 * @param string $course_id selected course id | optional.
184 * @param string $date selected date | optional.
185 * @param string $search_term search by user name or email | optional.
186 *
187 * @return int
188 *
189 * @since 2.0.0
190 */
191 protected static function count_course( string $status, $category_slug = '', $course_id = '', $date = '', $search_term = '' ): int {
192 $user_id = get_current_user_id();
193 $status = sanitize_text_field( $status );
194 $course_id = sanitize_text_field( $course_id );
195 $date = sanitize_text_field( $date );
196 $search_term = sanitize_text_field( $search_term );
197 $category_slug = sanitize_text_field( $category_slug );
198
199 $args = array(
200 'post_type' => tutor()->course_post_type,
201 );
202
203 if ( 'all' === $status || 'mine' === $status ) {
204 $args['post_status'] = array( 'publish', 'pending', 'draft', 'private', 'future' );
205 } else {
206 $args['post_status'] = array( $status );
207 }
208
209 // Author query.
210 if ( 'mine' === $status || ! current_user_can( 'administrator' ) ) {
211 $args['author'] = $user_id;
212 }
213
214 $date_filter = sanitize_text_field( $date );
215
216 $year = date( 'Y', strtotime( $date_filter ) );
217 $month = date( 'm', strtotime( $date_filter ) );
218 $day = date( 'd', strtotime( $date_filter ) );
219
220 // Add date query.
221 if ( '' !== $date_filter ) {
222 $args['date_query'] = array(
223 array(
224 'year' => $year,
225 'month' => $month,
226 'day' => $day,
227 ),
228 );
229 }
230
231 if ( '' !== $course_id ) {
232 $args['p'] = $course_id;
233 }
234
235 // Search filter.
236 if ( '' !== $search_term ) {
237 $args['s'] = $search_term;
238 }
239
240 // Category filter.
241 if ( '' !== $category_slug ) {
242 $args['tax_query'] = array(
243 array(
244 'taxonomy' => 'course-category',
245 'field' => 'slug',
246 'terms' => $category_slug,
247 ),
248 );
249 }
250
251 $the_query = new \WP_Query( $args );
252
253 return ! is_null( $the_query ) && isset( $the_query->found_posts ) ? $the_query->found_posts : $the_query;
254
255 }
256
257 /**
258 * Handle bulk action for enrollment cancel | delete
259 *
260 * @return void
261 * @since 2.0.0
262 */
263 public function course_list_bulk_action() {
264
265 tutor_utils()->checking_nonce();
266
267 // Check if user is privileged.
268 if ( ! current_user_can( 'administrator' ) ) {
269 wp_send_json_error( tutor_utils()->error_message() );
270 }
271
272 $action = Input::post( 'bulk-action', '' );
273 $bulk_ids = Input::post( 'bulk-ids', '' );
274 if ( '' === $action || '' === $bulk_ids ) {
275 wp_send_json_error( array( 'message' => __( 'Please select appropriate action', 'tutor' ) ) );
276 exit;
277 }
278
279 if ( 'delete' === $action ) {
280 // Do action before delete.
281 do_action( 'before_tutor_course_bulk_action_delete', $bulk_ids );
282
283 $delete_courses = self::bulk_delete_course( $bulk_ids );
284
285 do_action( 'after_tutor_course_bulk_action_delete', $bulk_ids );
286 $delete_courses ? wp_send_json_success() : wp_send_json_error( array( 'message' => __( 'Could not delete selected courses', 'tutor' ) ) );
287 exit;
288 }
289
290 /**
291 * Do action before course update
292 *
293 * @param string $action (publish | pending | draft | trash).
294 * @param array $bulk_ids, course id.
295 */
296 do_action( 'before_tutor_course_bulk_action_update', $action, $bulk_ids );
297
298 $update_status = self::update_course_status( $action, $bulk_ids );
299
300 do_action( 'after_tutor_course_bulk_action_update', $action, $bulk_ids );
301
302 $update_status ? wp_send_json_success() : wp_send_json_error(
303 array(
304 'message' => 'Could not update course status',
305 'tutor',
306 )
307 );
308
309 exit;
310 }
311
312 /**
313 * Handle ajax request for updating course status
314 *
315 * @return void
316 * @since 2.0.0
317 */
318 public static function tutor_change_course_status() {
319 tutor_utils()->checking_nonce();
320
321 // Check if user is privileged.
322 if ( ! current_user_can( 'administrator' ) ) {
323 wp_send_json_error( tutor_utils()->error_message() );
324 }
325
326 $status = Input::post( 'status' );
327 $id = Input::post( 'id' );
328 $course = get_post( $id );
329
330 if ( CourseModel::POST_TYPE !== $course->post_type ) {
331 wp_send_json_error( tutor_utils()->error_message() );
332 }
333
334 $args = array(
335 'ID' => $id,
336 'post_status' => $status,
337 );
338
339 if ( CourseModel::STATUS_FUTURE === $course->post_status && CourseModel::STATUS_PUBLISH === $status ) {
340 $args['post_status'] = CourseModel::STATUS_PUBLISH;
341 $args['post_date'] = current_time( 'mysql' );
342 $args['post_date_gmt'] = current_time( 'mysql', 1 );
343 }
344
345 wp_update_post( $args );
346 wp_send_json_success();
347 exit;
348 }
349
350 /**
351 * Handle ajax request for deleting course
352 *
353 * @since 2.0.0
354 *
355 * @return void JSON response
356 */
357 public static function tutor_course_delete() {
358 tutor_utils()->checking_nonce();
359
360 $user_id = get_current_user_id();
361 $course_id = Input::post( 'id', 0, Input::TYPE_INT );
362
363 // Check if user is privileged.
364 if ( ! tutor_utils()->can_user_edit_course( $user_id, $course_id ) ) {
365 wp_send_json_error( tutor_utils()->error_message() );
366 }
367
368 $delete = CourseModel::delete_course( $course_id );
369
370 if ( $delete ) {
371 wp_send_json_success( __( 'Course has been deleted ', 'tutor' ) );
372 } else {
373 wp_send_json_error( __( 'Course delete failed ', 'tutor' ) );
374 }
375
376 exit;
377 }
378
379 /**
380 * Execute bulk delete action
381 *
382 * @param string $bulk_ids ids that need to update.
383 * @return bool
384 * @since 2.0.0
385 */
386 public static function bulk_delete_course( $bulk_ids ): bool {
387 $bulk_ids = explode( ',', sanitize_text_field( $bulk_ids ) );
388
389 foreach ( $bulk_ids as $post_id ) {
390 CourseModel::delete_course( $post_id );
391 }
392
393 return true;
394 }
395
396 /**
397 * Update course status
398 *
399 * @param string $status for updating course status.
400 * @param string $bulk_ids comma separated ids.
401 *
402 * @return bool
403 *
404 * @since 2.0.0
405 */
406 public static function update_course_status( string $status, $bulk_ids ): bool {
407 global $wpdb;
408 $post_table = $wpdb->posts;
409 $status = sanitize_text_field( $status );
410 $bulk_ids = sanitize_text_field( $bulk_ids );
411
412 $ids = array_map( 'intval', explode( ',', $bulk_ids ) );
413 $in_clause = QueryHelper::prepare_in_clause( $ids );
414
415 $update = $wpdb->query(
416 $wpdb->prepare(
417 "UPDATE {$post_table} SET post_status = %s WHERE ID IN ($in_clause)", //phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared
418 $status
419 )
420 );
421
422 return true;
423 }
424
425 /**
426 * Get course enrollment list with student info
427 *
428 * @param int $course_id int | required.
429 * @return array
430 * @since 2.0.0
431 */
432 public static function course_enrollments_with_student_details( int $course_id ) {
433 global $wpdb;
434 $course_id = sanitize_text_field( $course_id );
435 $course_completed = 0;
436 $course_inprogress = 0;
437
438 $enrollments = $wpdb->get_results(
439 $wpdb->prepare(
440 "SELECT enroll.ID AS enroll_id, enroll.post_author AS enroll_author, user.*, course.ID AS course_id
441 FROM {$wpdb->posts} AS enroll
442 LEFT JOIN {$wpdb->users} AS user ON user.ID = enroll.post_author
443 LEFT JOIN {$wpdb->posts} AS course ON course.ID = enroll.post_parent
444 WHERE enroll.post_type = %s
445 AND enroll.post_status = %s
446 AND enroll.post_parent = %d
447 ",
448 'tutor_enrolled',
449 'completed',
450 $course_id
451 )
452 );
453
454 foreach ( $enrollments as $enrollment ) {
455 $course_progress = tutor_utils()->get_course_completed_percent( $course_id, $enrollment->enroll_author );
456 if ( 100 == $course_progress ) {
457 $course_completed++;
458 } else {
459 $course_inprogress++;
460 }
461 }
462
463 return array(
464 'enrollments' => $enrollments,
465 'total_completed' => $course_completed,
466 'total_inprogress' => $course_inprogress,
467 'total_enrollments' => count( $enrollments ),
468 );
469 }
470
471 /**
472 * Check wheather course is public or not
473 *
474 * @param integer $course_id course id to check with.
475 * @return boolean true if public otherwise false.
476 * @since 1.0.0
477 */
478 public static function is_public( int $course_id ): bool {
479 $is_public = get_post_meta( $course_id, '_tutor_is_public_course', true );
480 return 'yes' === $is_public ? true : false;
481 }
482 }
483