PluginProbe ʕ •ᴥ•ʔ
Tutor LMS – eLearning and online course solution / 2.4.0
Tutor LMS – eLearning and online course solution v2.4.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 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 3 years ago Lesson.php 2 years ago Options_V2.php 2 years ago Post_types.php 2 years ago Private_Course_Access.php 3 years ago Q_and_A.php 3 years ago Question_Answers_List.php 3 years ago Quiz.php 2 years ago Quiz_Attempts_List.php 2 years ago RestAPI.php 3 years ago Reviews.php 3 years ago Rewrite_Rules.php 3 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 3 years ago Tutor.php 3 years ago TutorEDD.php 2 years ago Tutor_Base.php 3 years ago Tutor_Setup.php 3 years ago Upgrader.php 3 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
482 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 // Check if user is privileged.
361 $roles = array( User::ADMIN, User::INSTRUCTOR );
362 if ( ! User::has_any_role( $roles ) ) {
363 wp_send_json_error( tutor_utils()->error_message() );
364 }
365
366 $id = Input::post( 'id', 0, Input::TYPE_INT );
367 $delete = CourseModel::delete_course( $id );
368
369 if ( $delete ) {
370 wp_send_json_success( __( 'Course has been deleted ', 'tutor' ) );
371 } else {
372 wp_send_json_error( __( 'Course delete failed ', 'tutor' ) );
373 }
374
375 exit;
376 }
377
378 /**
379 * Execute bulk delete action
380 *
381 * @param string $bulk_ids ids that need to update.
382 * @return bool
383 * @since 2.0.0
384 */
385 public static function bulk_delete_course( $bulk_ids ): bool {
386 $bulk_ids = explode( ',', sanitize_text_field( $bulk_ids ) );
387
388 foreach ( $bulk_ids as $post_id ) {
389 CourseModel::delete_course( $post_id );
390 }
391
392 return true;
393 }
394
395 /**
396 * Update course status
397 *
398 * @param string $status for updating course status.
399 * @param string $bulk_ids comma separated ids.
400 *
401 * @return bool
402 *
403 * @since 2.0.0
404 */
405 public static function update_course_status( string $status, $bulk_ids ): bool {
406 global $wpdb;
407 $post_table = $wpdb->posts;
408 $status = sanitize_text_field( $status );
409 $bulk_ids = sanitize_text_field( $bulk_ids );
410
411 $ids = array_map( 'intval', explode( ',', $bulk_ids ) );
412 $in_clause = QueryHelper::prepare_in_clause( $ids );
413
414 $update = $wpdb->query(
415 $wpdb->prepare(
416 "UPDATE {$post_table} SET post_status = %s WHERE ID IN ($in_clause)", //phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared
417 $status
418 )
419 );
420
421 return true;
422 }
423
424 /**
425 * Get course enrollment list with student info
426 *
427 * @param int $course_id int | required.
428 * @return array
429 * @since 2.0.0
430 */
431 public static function course_enrollments_with_student_details( int $course_id ) {
432 global $wpdb;
433 $course_id = sanitize_text_field( $course_id );
434 $course_completed = 0;
435 $course_inprogress = 0;
436
437 $enrollments = $wpdb->get_results(
438 $wpdb->prepare(
439 "SELECT enroll.ID AS enroll_id, enroll.post_author AS enroll_author, user.*, course.ID AS course_id
440 FROM {$wpdb->posts} AS enroll
441 LEFT JOIN {$wpdb->users} AS user ON user.ID = enroll.post_author
442 LEFT JOIN {$wpdb->posts} AS course ON course.ID = enroll.post_parent
443 WHERE enroll.post_type = %s
444 AND enroll.post_status = %s
445 AND enroll.post_parent = %d
446 ",
447 'tutor_enrolled',
448 'completed',
449 $course_id
450 )
451 );
452
453 foreach ( $enrollments as $enrollment ) {
454 $course_progress = tutor_utils()->get_course_completed_percent( $course_id, $enrollment->enroll_author );
455 if ( 100 == $course_progress ) {
456 $course_completed++;
457 } else {
458 $course_inprogress++;
459 }
460 }
461
462 return array(
463 'enrollments' => $enrollments,
464 'total_completed' => $course_completed,
465 'total_inprogress' => $course_inprogress,
466 'total_enrollments' => count( $enrollments ),
467 );
468 }
469
470 /**
471 * Check wheather course is public or not
472 *
473 * @param integer $course_id course id to check with.
474 * @return boolean true if public otherwise false.
475 * @since 1.0.0
476 */
477 public static function is_public( int $course_id ): bool {
478 $is_public = get_post_meta( $course_id, '_tutor_is_public_course', true );
479 return 'yes' === $is_public ? true : false;
480 }
481 }
482