PluginProbe
LearnPress – WordPress LMS Plugin for Create and Sell Online Courses / 4.4.9
LearnPress – WordPress LMS Plugin for Create and Sell Online Courses v4.4.9
4.4.9 4.4.8 4.4.7 4.4.6 4.4.5 4.4.4 4.4.3 4.4.2 4.4.1 4.4.0 4.3.9.1 4.3.9 4.3.8 4.3.7 4.1.6.9 4.1.6.9.1 4.1.6.9.2 4.1.6.9.3 4.1.6.9.4 4.1.7 4.1.7.1 4.1.7.2 4.1.7.3 4.1.7.3.1 4.1.7.3.2 All 140 releases
learnpress / inc / custom-post-types / course.php

course.php in LearnPress – WordPress LMS Plugin for Create and Sell Online Courses 4.4.9, at inc/custom-post-types/course.php

610 lines 20.0 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Class LP_Course_Post_Type
4 *
5 * @author ThimPress
6 * @package LearnPress/Classes
7 * @version 3.0.3
8 */
9
10 use LearnPress\Databases\PostDB;
11 use LearnPress\Filters\CoursePostFilter;
12 use LearnPress\Models\CourseModel;
13 use LearnPress\Models\CoursePostModel;
14 use LearnPress\Models\PostModel;
15 use LearnPress\Models\WPTables\CoursesTable;
16
17 defined( 'ABSPATH' ) || exit();
18
19 if ( ! class_exists( 'LP_Course_Post_Type' ) ) {
20
21 /**
22 * Class LP_Course_Post_Type
23 */
24 final class LP_Course_Post_Type extends LP_Abstract_Post_Type {
25 /**
26 * @var null
27 */
28 protected static $_instance = null;
29 protected $_post_type = LP_COURSE_CPT;
30 protected $_screen_list = 'edit-' . LP_COURSE_CPT;
31
32 /**
33 * Constructor
34 */
35 public function __construct() {
36 parent::__construct();
37
38 add_action( 'init', array( $this, 'register_taxonomy' ) );
39 add_action( 'posts_pre_query', array( $this, 'posts_pre_query' ), 999, 2 );
40 // add_filter( 'posts_where_paged', array( $this, '_posts_where_paged_course_items' ), 10 );
41 // add_filter( 'posts_join_paged', array( $this, '_posts_join_paged_course_items' ), 10 );
42 }
43
44 /**
45 * Register course post type.
46 */
47 public function args_register_post_type(): array {
48 // Support Quick Edit multiple courses change author
49 add_post_type_support( LP_COURSE_CPT, 'author' );
50
51 $labels = array(
52 'name' => _x( 'Courses', 'Post Type General Name', 'learnpress' ),
53 'singular_name' => _x( 'Course', 'Post Type Singular Name', 'learnpress' ),
54 'menu_name' => __( 'Courses', 'learnpress' ),
55 'parent_item_colon' => __( 'Parent Item:', 'learnpress' ),
56 'all_items' => __( 'Courses', 'learnpress' ),
57 'view_item' => __( 'View Course', 'learnpress' ),
58 'add_new_item' => __( 'Add a New Course', 'learnpress' ),
59 'add_new' => __( 'Add New', 'learnpress' ),
60 'edit_item' => __( 'Edit Course', 'learnpress' ),
61 'update_item' => __( 'Update Course', 'learnpress' ),
62 'search_items' => __( 'Search Courses', 'learnpress' ),
63 'not_found' => sprintf(
64 __( 'You have not had any courses yet. Click <a href="%s">Add new</a> to start', 'learnpress' ),
65 admin_url( 'post-new.php?post_type=lp_course' )
66 ),
67 'not_found_in_trash' => __( 'There was no course found in the trash', 'learnpress' ),
68 );
69 $course_base = LP_Settings::get_option( 'course_base', 'courses' );
70 $course_permalink = empty( $course_base ) ? 'courses' : $course_base;
71
72 // Set to $has_archive return link to courses page, is_archive will check is true
73 $courses_page_id = learn_press_get_page_id( 'courses' );
74 $has_archive = $courses_page_id ? urldecode( get_page_uri( $courses_page_id ) ) : 'courses';
75
76 $args = array(
77 'labels' => $labels,
78 'public' => true,
79 'query_var' => true,
80 'publicly_queryable' => true,
81 'show_ui' => true,
82 'has_archive' => $has_archive,
83 'capability_type' => $this->_post_type,
84 'map_meta_cap' => true,
85 'show_in_menu' => 'learn_press',
86 'show_in_admin_bar' => true,
87 'show_in_nav_menus' => true,
88 'show_in_rest' => true,
89 'taxonomies' => array( 'course_category', 'course_tag' ),
90 'supports' => array( 'title', 'editor', 'thumbnail', 'revisions', 'comments', 'excerpt' ),
91 'hierarchical' => false,
92 'rewrite' => ! empty( $course_permalink ) ? array(
93 'slug' => untrailingslashit( $course_permalink ),
94 'with_front' => false,
95 ) : false,
96 );
97
98 return $args;
99 }
100
101 /**
102 * Register course taxonomy.
103 */
104 public function register_taxonomy() {
105 $category_base = LP_Settings::get_option( 'course_category_base' );
106 register_taxonomy(
107 'course_category',
108 array( LP_COURSE_CPT ),
109 array(
110 'label' => __( 'Course Categories', 'learnpress' ),
111 'labels' => array(
112 'name' => __( 'Course Categories', 'learnpress' ),
113 'menu_name' => __( 'Course Category', 'learnpress' ),
114 'singular_name' => __( 'Category', 'learnpress' ),
115 'add_new_item' => __( 'Add A New Course Category', 'learnpress' ),
116 'all_items' => __( 'All Categories', 'learnpress' ),
117 ),
118 'query_var' => true,
119 'public' => true,
120 'hierarchical' => true,
121 'show_ui' => true,
122 'show_in_menu' => 'learn_press',
123 'show_admin_column' => true,
124 'show_in_admin_bar' => true,
125 'show_in_nav_menus' => true,
126 'show_in_rest' => true,
127 'rewrite' => array(
128 'slug' => empty( $category_base ) ? 'course-category' : $category_base,
129 'hierarchical' => true,
130 'with_front' => false,
131 ),
132 )
133 );
134
135 $tag_base = LP_Settings::get_option( 'course_tag_base' );
136 register_taxonomy(
137 'course_tag',
138 array( LP_COURSE_CPT ),
139 array(
140 'labels' => array(
141 'name' => __( 'Course Tags', 'learnpress' ),
142 'singular_name' => __( 'Tag', 'learnpress' ),
143 'search_items' => __( 'Search Course Tags', 'learnpress' ),
144 'popular_items' => __( 'Popular Course Tags', 'learnpress' ),
145 'all_items' => __( 'All Course Tags', 'learnpress' ),
146 'parent_item' => null,
147 'parent_item_colon' => null,
148 'edit_item' => __( 'Edit Course Tag', 'learnpress' ),
149 'update_item' => __( 'Update Course Tag', 'learnpress' ),
150 'add_new_item' => __( 'Add A New Course Tag', 'learnpress' ),
151 'new_item_name' => __( 'New Course Tag Name', 'learnpress' ),
152 'separate_items_with_commas' => __( 'Separate tags with commas', 'learnpress' ),
153 'add_or_remove_items' => __( 'Add or remove tags', 'learnpress' ),
154 'choose_from_most_used' => __( 'Choose from the most used tags', 'learnpress' ),
155 'menu_name' => __( 'Course Tags', 'learnpress' ),
156 ),
157 'public' => true,
158 'hierarchical' => false,
159 'show_ui' => true,
160 'show_in_menu' => 'learn_press',
161 'update_count_callback' => '_update_post_term_count',
162 'query_var' => true,
163 'show_in_rest' => true,
164 'rewrite' => array(
165 'slug' => empty( $tag_base ) ? 'course-tag' : $tag_base,
166 'with_front' => false,
167 ),
168 )
169 );
170 }
171
172 /**
173 * Declare class name of table list courses.
174 *
175 * @param string $class_name
176 * @param array $args
177 *
178 * @return string
179 * @since 4.2.9.5
180 */
181 public function wp_list_table_class_name( $class_name, $args ) {
182 if ( $this->check_class_name_handle_table( $args['screen'] ) ) {
183 $class_name = CoursesTable::class;
184 }
185
186 return $class_name;
187 }
188
189 /**
190 * Delete course sections before delete course.
191 *
192 * @param int $post_id
193 *
194 * @throws Exception
195 * @since modify 4.0.9
196 * @since 3.0.0
197 * @editor tungnx
198 */
199 public function before_delete( int $post_id ) {
200 // Delete course from table learnpress_courses
201 $courseModel = CourseModel::find( $post_id, true );
202 if ( $courseModel ) {
203 $courseModel->delete();
204 }
205
206 $course = learn_press_get_course( $post_id );
207 if ( ! $course ) {
208 return;
209 }
210 $course->delete_relate_data_when_delete_course();
211 }
212
213 /**
214 * Query lp courses in admin via Post DB
215 *
216 * @param array $posts
217 * @param WP_Query $wp_query
218 *
219 * @return array
220 * @since 4.4.8
221 * @version 1.0.0
222 */
223 public function posts_pre_query( $posts, $wp_query ) {
224 try {
225 if ( ! is_admin() ) {
226 return $posts;
227 }
228
229 // Check screen
230 if ( ! function_exists( 'get_current_screen' ) ) {
231 return $posts;
232 }
233
234 $curren_screen = get_current_screen();
235 if ( ! $curren_screen || $curren_screen->id !== 'edit-' . LP_COURSE_CPT ) {
236 return $posts;
237 }
238
239 $post_type = $wp_query->get( 'post_type' );
240
241 if ( empty( $post_type ) || $post_type !== LP_COURSE_CPT ) {
242 return $posts;
243 }
244
245 // Convert params from WP_Query to CoursePostFilter
246 $posts_per_page = get_user_option( "edit_{$post_type}_per_page", get_current_user_id() );
247 if ( empty( $posts_per_page ) ) {
248 $posts_per_page = 20;
249 }
250
251 $paged = max( 1, get_query_var( 'paged' ) );
252 $author = $wp_query->get( 'author' );
253 $status = $wp_query->get( 'post_status' );
254 $search = $wp_query->get( 's' );
255 $month = $wp_query->get( 'm' );
256 $orderby = LP_Request::get_param( 'orderby', '', 'key' );
257 $order = LP_Request::get_param( 'order', '', 'key' );
258 $filter_price = LP_Helper::sanitize_params_submitted( $_REQUEST['filter_price'] ?? '' );
259
260 $filter = new CoursePostFilter();
261 $filter->page = $paged;
262 $filter->limit = $posts_per_page;
263 $filter->only_fields = array_map(
264 function ( $field ) {
265 return "p.{$field}";
266 },
267 $filter->all_fields
268 );
269 $filter->field_count = 'p.ID';
270 $post_db = PostDB::getInstance();
271
272 if ( ! empty( $status ) ) {
273 $filter->post_status = array( $status );
274 }
275
276 if ( ! empty( $author ) ) {
277 $filter->post_author = absint( $author );
278 }
279
280 if ( ! empty( $search ) ) {
281 $filter->post_title = sanitize_text_field( wp_unslash( $search ) );
282 }
283
284 if ( ! empty( $month ) && is_numeric( $month ) && strlen( (string) $month ) === 6 ) {
285 $filter->where[] = $post_db->wpdb->prepare(
286 'AND YEAR(p.post_date) = %d AND MONTH(p.post_date) = %d',
287 substr( $month, 0, 4 ),
288 substr( $month, 4, 2 )
289 );
290 }
291
292 /*$has_price_filter = false;
293 if ( array_key_exists( 'filter_price', $_REQUEST ) && $filter_price !== '' ) {
294 $filter_price = floatval( $filter_price );
295 $has_price_filter = true;
296 }
297
298 if ( $orderby === 'price' || $has_price_filter ) {
299 $filter->join[] = "LEFT JOIN {$post_db->wpdb->postmeta} pm_price ON pm_price.post_id = p.ID AND pm_price.meta_key = '_lp_price'";
300 }
301
302 if ( $has_price_filter ) {
303 if ( $filter_price == 0 ) {
304 $filter->where[] = 'AND ( pm_price.meta_value IS NULL || pm_price.meta_value = 0 )';
305 } else {
306 $filter->where[] = $post_db->wpdb->prepare( ' AND ( pm_price.meta_value = %s )', $filter_price );
307 }
308 }*/
309
310 // Exclude status auto-draft
311 $filter->where[] = $post_db->wpdb->prepare( 'AND p.post_status != %s', PostModel::STATUS_AUTO_DRAFT );
312
313 if ( $orderby === 'price' ) {
314 $filter->join[] = "LEFT JOIN {$post_db->wpdb->postmeta} pm_price
315 ON pm_price.post_id = p.ID
316 AND pm_price.meta_key = '_lp_price'";
317 $filter->order_by = 'CAST(pm_price.meta_value AS UNSIGNED)';
318 } elseif ( $orderby === 'title' ) {
319 $filter->order_by = 'p.post_title';
320 } elseif ( $orderby === 'author' ) {
321 $filter->order_by = 'p.post_author';
322 } elseif ( $orderby === 'date' ) {
323 $filter->order_by = 'p.post_date';
324 } else {
325 $filter->order_by = 'p.menu_order';
326 }
327
328 $filter->order = strtolower( $order ) === 'asc' ? 'ASC' : 'DESC';
329
330 // Get lp courses
331 $total_rows = 0;
332 $lp_courses = $post_db->get_posts( $filter, $total_rows );
333
334 $wp_query->post_count = count( $lp_courses );
335 $wp_query->found_posts = $total_rows;
336 $wp_query->max_num_pages = (int) ceil( $total_rows / $posts_per_page );
337 $posts = $lp_courses;
338 } catch ( Throwable $e ) {
339 LP_Debug::error_log( $e );
340 }
341
342 return $posts;
343 }
344
345 /*public function posts_fields( $fields ): string {
346 if ( ! $this->is_page_list_posts_on_backend() ) {
347 return $fields;
348 }
349
350 $fields = ' DISTINCT ' . $fields;
351 if ( $this->get_order_by() == 'price' ) {
352 $fields .= ', pm_price.meta_value as course_price';
353 }
354
355 return $fields;
356 }
357
358 public function _posts_join_paged_course_items( $join ) {
359 global $wpdb;
360
361 $course_id = $this->_filter_items_by_course();
362 if ( $course_id || LP_Request::get_param( 'orderby' ) === 'course-name' ) {
363 $join .= " LEFT JOIN {$wpdb->prefix}learnpress_section_items si ON {$wpdb->posts}.ID = si.item_id";
364 $join .= " LEFT JOIN {$wpdb->prefix}learnpress_sections s ON s.section_id = si.section_id";
365 $join .= " LEFT JOIN {$wpdb->posts} c ON c.ID = s.section_course_id";
366 }
367
368 return $join;
369 }
370
371 public function _posts_where_paged_course_items( $where ) {
372 global $wpdb;
373
374 $course_id = $this->_filter_items_by_course();
375 if ( $course_id ) {
376 $where .= $wpdb->prepare( ' AND (c.ID = %d)', $course_id );
377 $where .= $wpdb->prepare( " AND {$wpdb->posts}.post_status = %s", 'publish' );
378 }
379
380 return $where;
381 }
382
383 public function posts_join_paged( $join ) {
384 global $wpdb;
385
386 if ( ! $this->is_page_list_posts_on_backend() ) {
387 return $join;
388 }
389
390 if ( ! isset( $_GET['orderby'] ) || $_GET['orderby'] !== 'price' ) {
391 return $join;
392 }
393
394 $join .= " LEFT JOIN {$wpdb->postmeta} pm_price ON pm_price.post_id = {$wpdb->posts}.ID AND pm_price.meta_key = '_lp_price'";
395
396 return $join;
397 }
398
399 public function posts_where_paged( $where ) {
400 global $wpdb;
401
402 if ( ! $this->is_page_list_posts_on_backend() ) {
403 return $where;
404 }
405
406 $filter_price = LP_Helper::sanitize_params_submitted( $_REQUEST['filter_price'] ?? 0 );
407
408 if ( array_key_exists( 'filter_price', $_REQUEST ) ) {
409 if ( $filter_price == 0 ) {
410 $where .= ' AND ( pm_price.meta_value IS NULL || pm_price.meta_value = 0 )';
411 } else {
412 $where .= $wpdb->prepare( ' AND ( pm_price.meta_value = %s )', $filter_price );
413 }
414 }
415
416 return $where;
417 }
418
419 public function posts_orderby( $orderby ) {
420 if ( ! $this->is_page_list_posts_on_backend() ) {
421 return $orderby;
422 }
423
424 $order = $this->get_order_sort();
425 switch ( $this->get_order_by() ) {
426 case 'price':
427 $orderby = "CAST(pm_price.meta_value AS UNSIGNED) {$order}";
428 }
429
430 return $orderby;
431 }*/
432
433 /**
434 * Save course post
435 *
436 * @param int $post_id
437 * @param WP_Post|null $post
438 * @param bool $is_update
439 *
440 * @since 4.2.6.9
441 * @version 1.0.3
442 */
443 public function save_post( int $post_id, ?WP_Post $post = null, bool $is_update = false ) {
444 try {
445 $wp_screen = function_exists( 'get_current_screen' ) ? get_current_screen() : null;
446 // Save to table learnpress_courses
447 LP_Install::instance()->create_table_courses();
448 if ( empty( $post ) ) {
449 $post = get_post( $post_id );
450 }
451
452 /*if ( $post->post_status === 'auto-draft' ) {
453 return;
454 }*/
455
456 $courseModel = CourseModel::find( $post_id, true );
457 if ( ! $courseModel ) {
458 $courseModel = new CourseModel( $post );
459 }
460
461 // Get all metadata of post
462 $all_metadata = $courseModel->get_post_model()->get_all_metadata();
463 $courseModel->meta_data = $all_metadata;
464 // Temporary unset _elementor_page_assets of El, reason by method get_all_metadata get not use maybe_unserialize, make save invalid serialize
465 $courseModel->meta_data->_elementor_page_assets = [];
466
467 // Merge object post and courseModel
468 $new_obj = (array) $post;
469 $old_obj = (array) $courseModel;
470 $old_now = array_merge( $old_obj, $new_obj );
471 $courseModel = new CourseModel( $old_now );
472
473 // Save option single course
474 include_once LP_PLUGIN_PATH . 'inc/admin/class-lp-admin.php';
475 $lp_meta_box_course = new LP_Meta_Box_Course();
476 $ground_fields = $lp_meta_box_course->metabox( $courseModel->ID );
477 // Save meta fields
478 foreach ( $ground_fields as $fields ) {
479 if ( ! isset( $fields['content'] ) ) {
480 continue;
481 }
482 foreach ( $fields['content'] as $meta_key => $option ) {
483 $option->id = $meta_key;
484 if ( ! $option instanceof LP_Meta_Box_Field ) {
485 continue;
486 }
487
488 if ( isset( $_POST[ $meta_key ] ) && ! empty( $wp_screen ) && LP_COURSE_CPT === $wp_screen->id ) {
489 $value_saved = $option->save( $courseModel->ID );
490 if ( ! empty( $value_saved ) ) {
491 $courseModel->meta_data->{$meta_key} = $value_saved;
492 } else {
493 $courseModel->meta_data->{$meta_key} = maybe_unserialize(
494 get_post_meta( $courseModel->ID, $meta_key, true )
495 );
496 }
497 } elseif ( ! $is_update ) {
498 if ( isset( $courseModel->meta_data->{$meta_key} ) ) {
499 continue;
500 }
501 $courseModel->meta_data->{$meta_key} = $option->default ?? '';
502 } elseif ( ! empty( $wp_screen ) && LP_COURSE_CPT === $wp_screen->id ) {
503 $value_saved = $option->save( $courseModel->ID );
504 $courseModel->meta_data->{$meta_key} = $value_saved;
505 }
506 }
507 }
508
509 // For case bulk edit multiple courses change author
510 $bulk_edit = LP_Request::get_param( 'bulk_edit', false );
511 if ( ! empty( $bulk_edit ) ) {
512 $post_author = LP_Request::get_param( 'post_author', 0, 'int' );
513 if ( $post_author != -1 ) { // -1 is for no change author
514 $courseModel->post_author = $post_author;
515 }
516 } elseif ( ! empty( $_REQUEST['_post_author'] ) ) {
517 // Not use key 'post_author' on single edit course, it special key of WP, Gutenberg save will not submit this key.
518 $courseModel->post_author = LP_Request::get_param( '_post_author', 0, 'int' );
519 /**
520 * Save author to post table
521 * Not use PostModel::save() or CoursePostModel::save() here
522 * because it calls wp_update_post(), has hook 'save_post', it will cause infinite loop
523 */
524 $lp_db = LP_Database::getInstance();
525 $filter_update = new LP_Post_Type_Filter();
526 $filter_update->collection = $lp_db->tb_posts;
527 $filter_update->set[] = "post_author = {$courseModel->post_author}";
528 $filter_update->where[] = $lp_db->wpdb->prepare( 'AND ID = %d', $courseModel->ID );
529 $lp_db->update_execute( $filter_update );
530 clean_post_cache( $post->ID );
531 } elseif ( isset( $_POST['post_author'] ) && isset( $_POST['screen'] ) && $_POST['screen'] === 'edit-' . LP_COURSE_CPT ) {
532 // For case quick edit change author.
533 $courseModel->post_author = LP_Request::get_param( 'post_author', 0, 'int' );
534 }
535
536 $this->save_price( $courseModel );
537 $courseModel->save();
538 // End save to table learnpress_courses
539
540 // Save extra info course
541 // Save in background.
542 $bg = LP_Background_Single_Course::instance();
543 $bg->data(
544 array(
545 'handle_name' => 'save_post',
546 'course_id' => $post_id,
547 'data' => $_POST ?? [],
548 )
549 )->dispatch();
550 } catch ( Throwable $e ) {
551 LP_Debug::error_log( $e );
552 }
553 }
554
555 /**
556 * Save price course
557 *
558 * @return void
559 * @throws Exception
560 */
561 protected function save_price( CourseModel &$courseObj ) {
562 $coursePost = new CoursePostModel( $courseObj );
563
564 $regular_price = $courseObj->get_regular_price();
565 $sale_price = $courseObj->get_sale_price();
566 if ( (float) $regular_price < 0 ) {
567 $courseObj->meta_data->{CoursePostModel::META_KEY_REGULAR_PRICE} = '';
568 $regular_price = $courseObj->get_regular_price();
569 }
570
571 if ( $sale_price !== '' && (float) $sale_price > (float) $regular_price ) {
572 $courseObj->meta_data->{CoursePostModel::META_KEY_SALE_PRICE} = '';
573 $sale_price = $courseObj->get_sale_price();
574 }
575
576 // Save sale regular price and sale price to table postmeta
577 $coursePost->save_meta_value_by_key( CoursePostModel::META_KEY_REGULAR_PRICE, $regular_price );
578 $coursePost->save_meta_value_by_key( CoursePostModel::META_KEY_SALE_PRICE, $sale_price );
579
580 $has_sale = $courseObj->has_sale_price();
581 if ( $has_sale ) {
582 $courseObj->is_sale = 1;
583 $coursePost->save_meta_value_by_key( CoursePostModel::META_KEY_IS_SALE, 1 );
584 } else {
585 $courseObj->is_sale = 0;
586 delete_post_meta( $courseObj->get_id(), CoursePostModel::META_KEY_IS_SALE );
587 }
588
589 // Set price to sort on lists.
590 $courseObj->price_to_sort = $courseObj->get_price();
591 $coursePost->save_meta_value_by_key( CoursePostModel::META_KEY_PRICE, $courseObj->price_to_sort );
592 }
593
594 /**
595 * Instance LP_Course_Post_Type.
596 *
597 * @return LP_Course_Post_Type|null
598 */
599 public static function instance() {
600 if ( ! self::$_instance ) {
601 self::$_instance = new self();
602 }
603
604 return self::$_instance;
605 }
606 }
607
608 $course_post_type = LP_Course_Post_Type::instance();
609 }
610