PluginProbe ʕ •ᴥ•ʔ
Tutor LMS – eLearning and online course solution / 4.0.0
Tutor LMS – eLearning and online course solution v4.0.0
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 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 19 hours ago Admin.php 19 hours ago Ajax.php 19 hours ago Announcements.php 19 hours ago Assets.php 19 hours ago Backend_Page_Trait.php 1 year ago BaseController.php 1 year ago Config.php 19 hours ago Container.php 11 months ago Course.php 19 hours ago Course_Embed.php 3 years ago Course_Filter.php 19 hours ago Course_List.php 19 hours ago Course_Settings_Tabs.php 19 hours ago Course_Widget.php 1 year ago Custom_Validation.php 19 hours ago Dashboard.php 19 hours ago Earnings.php 9 months ago FormHandler.php 19 hours ago Frontend.php 19 hours ago Gutenberg.php 1 year ago Icon.php 19 hours ago Input.php 19 hours ago Instructor.php 19 hours ago Instructors_List.php 19 hours ago Lesson.php 19 hours ago Options_V2.php 19 hours ago Permalink.php 19 hours ago Post_types.php 1 day ago Private_Course_Access.php 19 hours ago Q_And_A.php 19 hours ago Question_Answers_List.php 11 months ago Quiz.php 19 hours ago QuizBuilder.php 19 hours ago Quiz_Attempts_List.php 19 hours ago RestAPI.php 2 years ago Reviews.php 19 hours ago Rewrite_Rules.php 2 years ago SampleCourse.php 19 hours ago Shortcode.php 19 hours ago Singleton.php 1 year ago Student.php 19 hours ago Students_List.php 1 year ago Taxonomies.php 1 year ago Template.php 19 hours ago Theme_Compatibility.php 3 years ago Tools.php 1 year ago Tools_V2.php 4 weeks ago Tutor.php 19 hours ago TutorEDD.php 19 hours ago Tutor_Base.php 2 years ago Tutor_Setup.php 19 hours ago Upgrader.php 19 hours ago User.php 19 hours ago UserPreference.php 19 hours ago Utils.php 19 hours ago Video_Stream.php 3 years ago WhatsNew.php 10 months ago Withdraw.php 19 hours ago Withdraw_Requests_List.php 11 months ago WooCommerce.php 19 hours ago
Course_List.php
517 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 defined( 'ABSPATH' ) || exit;
14
15 use Tutor\Helpers\QueryHelper;
16 use Tutor\Models\CourseModel;
17
18 /**
19 * Course List class
20 *
21 * @since 2.0.0
22 */
23 class Course_List {
24 /**
25 * Trait for utilities
26 *
27 * @var $page_title
28 */
29
30 use Backend_Page_Trait;
31
32 /**
33 * Bulk Action
34 *
35 * @var $bulk_action
36 */
37 public $bulk_action = true;
38
39 /**
40 * Constructor
41 *
42 * @return void
43 *
44 * @since 2.0.0
45 */
46 public function __construct() {
47 /**
48 * Handle bulk action
49 *
50 * @since 2.0.0
51 */
52 add_action( 'wp_ajax_tutor_course_list_bulk_action', array( $this, 'course_list_bulk_action' ) );
53 /**
54 * Handle ajax request for updating course status
55 *
56 * @since 2.0.0
57 */
58 add_action( 'wp_ajax_tutor_change_course_status', array( $this, 'tutor_change_course_status' ) );
59 /**
60 * Handle ajax request for delete course
61 *
62 * @since 2.0.0
63 */
64 add_action( 'wp_ajax_tutor_course_delete', array( $this, 'tutor_course_delete' ) );
65 }
66
67 /**
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 * Prepare bulk actions that will show on dropdown options
84 *
85 * @since 2.0.0
86 *
87 * @return array
88 */
89 public function prepare_bulk_actions(): array {
90 $actions = array(
91 $this->bulk_action_default(),
92 $this->bulk_action_publish(),
93 $this->bulk_action_pending(),
94 $this->bulk_action_draft(),
95 );
96
97 $active_tab = Input::get( 'data', '' );
98
99 if ( CourseModel::STATUS_TRASH === $active_tab ) {
100 array_push( $actions, $this->bulk_action_delete() );
101 }
102 if ( CourseModel::STATUS_TRASH !== $active_tab ) {
103 array_push( $actions, $this->bulk_action_trash() );
104 }
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 return apply_filters( 'tutor_course_bulk_actions', $actions );
113 }
114
115 /**
116 * Available tabs that will visible on the right side of page navbar
117 *
118 * @since 2.0.0
119 *
120 * @param string $category_slug category slug.
121 * @param integer $course_id course ID.
122 * @param string $date selected date | optional.
123 * @param string $search search by user name or email | optional.
124 *
125 * @return array
126 */
127 public function tabs_key_value( $category_slug, $course_id, $date, $search ): array {
128 $url = apply_filters( 'tutor_data_tab_base_url', get_pagenum_link() );
129
130 $all = self::count_course( 'all', $category_slug, $course_id, $date, $search );
131 $mine = self::count_course( 'mine', $category_slug, $course_id, $date, $search );
132 $published = self::count_course( 'publish', $category_slug, $course_id, $date, $search );
133 $draft = self::count_course( 'draft', $category_slug, $course_id, $date, $search );
134 $pending = self::count_course( 'pending', $category_slug, $course_id, $date, $search );
135 $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
139 $tabs = array(
140 array(
141 'key' => '',
142 'title' => __( 'All', 'tutor' ),
143 'value' => $all,
144 'url' => $url . '&data=all',
145 ),
146 array(
147 'key' => 'mine',
148 'title' => __( 'Mine', 'tutor' ),
149 'value' => $mine,
150 'url' => $url . '&data=mine',
151 ),
152 array(
153 'key' => 'published',
154 'title' => __( 'Published', 'tutor' ),
155 'value' => $published,
156 'url' => $url . '&data=published',
157 ),
158 array(
159 'key' => 'draft',
160 'title' => __( 'Draft', 'tutor' ),
161 'value' => $draft,
162 'url' => $url . '&data=draft',
163 ),
164 array(
165 'key' => 'pending',
166 'title' => __( 'Pending', 'tutor' ),
167 'value' => $pending,
168 'url' => $url . '&data=pending',
169 ),
170 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 'key' => 'trash',
184 'title' => __( 'Trash', 'tutor' ),
185 'value' => $trash,
186 'url' => $url . '&data=trash',
187 ),
188 );
189 if ( ! tutor_utils()->get_option( 'instructor_can_delete_course' ) && ! current_user_can( 'administrator' ) ) {
190 unset( $tabs[7] );
191 }
192 return apply_filters( 'tutor_course_tabs', $tabs );
193 }
194
195 /**
196 * Count courses by status & filters
197 * Count all | min | published | pending | draft
198 *
199 * @since 2.0.0
200 *
201 * @param string $status | required.
202 * @param string $category_slug course category | optional.
203 * @param string $course_id selected course id | optional.
204 * @param string $date selected date | optional.
205 * @param string $search_term search by user name or email | optional.
206 *
207 * @return int
208 */
209 protected static function count_course( string $status, $category_slug = '', $course_id = '', $date = '', $search_term = '' ): int {
210 $user_id = get_current_user_id();
211 $status = sanitize_text_field( $status );
212 $course_id = sanitize_text_field( $course_id );
213 $date = sanitize_text_field( $date );
214 $search_term = sanitize_text_field( $search_term );
215 $category_slug = sanitize_text_field( $category_slug );
216
217 $args = array(
218 'post_type' => tutor()->course_post_type,
219 );
220
221 if ( 'all' === $status || 'mine' === $status ) {
222 $args['post_status'] = array( 'publish', 'pending', 'draft', 'private', 'future' );
223 } else {
224 $args['post_status'] = array( $status );
225 }
226
227 // Author query.
228 if ( 'mine' === $status || ! current_user_can( 'administrator' ) ) {
229 $args['author'] = $user_id;
230 }
231
232 $date_filter = sanitize_text_field( $date );
233
234 $year = gmdate( 'Y', strtotime( $date_filter ) );
235 $month = gmdate( 'm', strtotime( $date_filter ) );
236 $day = gmdate( 'd', strtotime( $date_filter ) );
237
238 // Add date query.
239 if ( '' !== $date_filter ) {
240 $args['date_query'] = array(
241 array(
242 'year' => $year,
243 'month' => $month,
244 'day' => $day,
245 ),
246 );
247 }
248
249 if ( '' !== $course_id ) {
250 $args['p'] = $course_id;
251 }
252
253 // Search filter.
254 if ( '' !== $search_term ) {
255 $args['s'] = $search_term;
256 }
257
258 // Category filter.
259 if ( '' !== $category_slug ) {
260 $args['tax_query'] = array(
261 array(
262 'taxonomy' => CourseModel::COURSE_CATEGORY,
263 'field' => 'slug',
264 'terms' => $category_slug,
265 ),
266 );
267 }
268
269 $the_query = self::course_list_query( $args, $user_id, $status );
270
271 return ! is_null( $the_query ) && isset( $the_query->found_posts ) ? $the_query->found_posts : $the_query;
272 }
273
274 /**
275 * Handle bulk action for enrollment cancel | delete
276 *
277 * @since 2.0.0
278 *
279 * @return void
280 */
281 public function course_list_bulk_action() {
282
283 tutor_utils()->checking_nonce();
284
285 $action = Input::post( 'bulk-action', '' );
286 $bulk_ids = Input::post( 'bulk-ids', '' );
287
288 // 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
312 }
313
314 if ( '' === $action || '' === $bulk_ids ) {
315 wp_send_json_error( array( 'message' => __( 'Please select appropriate action', 'tutor' ) ) );
316 exit;
317 }
318
319 if ( 'delete' === $action ) {
320 // Do action before delete.
321 do_action( 'before_tutor_course_bulk_action_delete', $bulk_ids );
322
323 $delete_courses = self::bulk_delete_course( $bulk_ids );
324
325 do_action( 'after_tutor_course_bulk_action_delete', $bulk_ids );
326 $delete_courses ? wp_send_json_success() : wp_send_json_error( array( 'message' => __( 'Could not delete selected courses', 'tutor' ) ) );
327 exit;
328 }
329
330 /**
331 * Do action before course update
332 *
333 * @param string $action (publish | pending | draft | trash).
334 * @param array $bulk_ids, course id.
335 */
336 do_action( 'before_tutor_course_bulk_action_update', $action, $bulk_ids );
337
338 $update_status = self::update_course_status( $action, $bulk_ids );
339
340 do_action( 'after_tutor_course_bulk_action_update', $action, $bulk_ids );
341
342 $update_status ? wp_send_json_success() : wp_send_json_error( array( 'message' => __( 'Could not update course status', 'tutor' ) ) );
343
344 exit;
345 }
346
347 /**
348 * Handle ajax request for updating course status
349 *
350 * @since 2.0.0
351 *
352 * @return void
353 */
354 public static function tutor_change_course_status() {
355 tutor_utils()->checking_nonce();
356
357 $status = Input::post( 'status' );
358 $id = Input::post( 'id', 0, Input::TYPE_INT );
359 $course = get_post( $id );
360
361 // 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 }
386 }
387
388 if ( ! CourseModel::get_post_types( $course ) ) {
389 wp_send_json_error( tutor_utils()->error_message() );
390 }
391
392 $args = array(
393 'ID' => $id,
394 'post_status' => $status,
395 );
396
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 wp_send_json_success();
405 exit;
406 }
407
408 /**
409 * Handle ajax request for deleting course
410 *
411 * @since 2.0.0
412 *
413 * @return void JSON response
414 */
415 public static function tutor_course_delete() {
416 tutor_utils()->checking_nonce();
417
418 $user_id = get_current_user_id();
419 $course_id = Input::post( 'id', 0, Input::TYPE_INT );
420
421 // Check if user is privileged.
422 if ( ! tutor_utils()->can_user_edit_course( $user_id, $course_id ) ) {
423 wp_send_json_error( tutor_utils()->error_message() );
424 }
425
426 $delete = CourseModel::delete_course( $course_id );
427
428 if ( $delete ) {
429 wp_send_json_success( __( 'Course has been deleted ', 'tutor' ) );
430 } else {
431 wp_send_json_error( __( 'Course delete failed ', 'tutor' ) );
432 }
433
434 exit;
435 }
436
437 /**
438 * Execute bulk delete action
439 *
440 * @since 2.0.0
441 *
442 * @param string $bulk_ids ids that need to update.
443 *
444 * @return bool
445 */
446 public static function bulk_delete_course( $bulk_ids ): bool {
447 $bulk_ids = explode( ',', sanitize_text_field( $bulk_ids ) );
448
449 foreach ( $bulk_ids as $post_id ) {
450 CourseModel::delete_course( $post_id );
451 }
452
453 return true;
454 }
455
456 /**
457 * Update course status
458 *
459 * @since 2.0.0
460 *
461 * @param string $status for updating course status.
462 * @param string $bulk_ids comma separated ids.
463 *
464 * @return bool
465 */
466 public static function update_course_status( string $status, $bulk_ids ): bool {
467 global $wpdb;
468 $post_table = $wpdb->posts;
469 $status = sanitize_text_field( $status );
470 $bulk_ids = sanitize_text_field( $bulk_ids );
471
472 $ids = array_map( 'intval', explode( ',', $bulk_ids ) );
473 $in_clause = QueryHelper::prepare_in_clause( $ids );
474
475 $wpdb->query(
476 $wpdb->prepare(
477 "UPDATE {$post_table} SET post_status = %s WHERE ID IN ($in_clause)", //phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared
478 $status
479 )
480 );
481
482 return true;
483 }
484
485 /**
486 * Check course is public or not
487 *
488 * @since 1.0.0
489 *
490 * @param integer $course_id course id to check with.
491 *
492 * @return boolean true if public otherwise false.
493 */
494 public static function is_public( int $course_id ): bool {
495 $is_public = get_post_meta( $course_id, '_tutor_is_public_course', true );
496 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 }
516 }
517