PluginProbe
LearnPress – WordPress LMS Plugin for Create and Sell Online Courses / 4.4.8
LearnPress – WordPress LMS Plugin for Create and Sell Online Courses v4.4.8
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 4.2.0 All 139 releases
learnpress / inc / custom-post-types / course.php

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

599 lines 19.7 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|WP_Query
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 $curren_screen = get_current_screen();
231 if ( ! $curren_screen || $curren_screen->id !== 'edit-' . LP_COURSE_CPT ) {
232 return $posts;
233 }
234
235 $post_type = $wp_query->get( 'post_type' );
236
237 if ( empty( $post_type ) || $post_type !== LP_COURSE_CPT ) {
238 return $posts;
239 }
240
241 // Convert params from WP_Query to CoursePostFilter
242 $posts_per_page = get_user_option( "edit_{$post_type}_per_page", get_current_user_id() );
243 if ( empty( $posts_per_page ) ) {
244 $posts_per_page = 20;
245 }
246
247 $paged = max( 1, get_query_var( 'paged' ) );
248 $author = $wp_query->get( 'author' );
249 $status = $wp_query->get( 'post_status' );
250 $search = $wp_query->get( 's' );
251 $month = $wp_query->get( 'm' );
252 $orderby = LP_Request::get_param( 'orderby', '', 'key' );
253 $order = LP_Request::get_param( 'order', '', 'key' );
254 $filter_price = LP_Helper::sanitize_params_submitted( $_REQUEST['filter_price'] ?? '' );
255
256 $filter = new CoursePostFilter();
257 $filter->page = $paged;
258 $filter->limit = $posts_per_page;
259 $post_db = PostDB::getInstance();
260
261 if ( ! empty( $status ) ) {
262 $filter->post_status = array( $status );
263 }
264
265 if ( ! empty( $author ) ) {
266 $filter->post_author = absint( $author );
267 }
268
269 if ( ! empty( $search ) ) {
270 $filter->post_title = sanitize_text_field( wp_unslash( $search ) );
271 }
272
273 if ( ! empty( $month ) && is_numeric( $month ) && strlen( (string) $month ) === 6 ) {
274 $filter->where[] = $post_db->wpdb->prepare(
275 'AND YEAR(p.post_date) = %d AND MONTH(p.post_date) = %d',
276 substr( $month, 0, 4 ),
277 substr( $month, 4, 2 )
278 );
279 }
280
281 /*$has_price_filter = false;
282 if ( array_key_exists( 'filter_price', $_REQUEST ) && $filter_price !== '' ) {
283 $filter_price = floatval( $filter_price );
284 $has_price_filter = true;
285 }
286
287 if ( $orderby === 'price' || $has_price_filter ) {
288 $filter->join[] = "LEFT JOIN {$post_db->wpdb->postmeta} pm_price ON pm_price.post_id = p.ID AND pm_price.meta_key = '_lp_price'";
289 }
290
291 if ( $has_price_filter ) {
292 if ( $filter_price == 0 ) {
293 $filter->where[] = 'AND ( pm_price.meta_value IS NULL || pm_price.meta_value = 0 )';
294 } else {
295 $filter->where[] = $post_db->wpdb->prepare( ' AND ( pm_price.meta_value = %s )', $filter_price );
296 }
297 }*/
298
299 // Exclude status auto-draft
300 $filter->where[] = $post_db->wpdb->prepare( 'AND p.post_status != %s', PostModel::STATUS_AUTO_DRAFT );
301
302 if ( $orderby === 'price' ) {
303 $filter->join[] = "LEFT JOIN {$post_db->wpdb->postmeta} pm_price
304 ON pm_price.post_id = p.ID
305 AND pm_price.meta_key = '_lp_price'";
306 $filter->order_by = 'CAST(pm_price.meta_value AS UNSIGNED)';
307 } elseif ( $orderby === 'title' ) {
308 $filter->order_by = 'p.post_title';
309 } elseif ( $orderby === 'author' ) {
310 $filter->order_by = 'p.post_author';
311 } elseif ( $orderby === 'date' ) {
312 $filter->order_by = 'p.post_date';
313 } else {
314 $filter->order_by = 'p.menu_order';
315 }
316
317 $filter->order = strtolower( $order ) === 'asc' ? 'ASC' : 'DESC';
318
319 // Get lp courses
320 $total_rows = 0;
321 $lp_courses = $post_db->get_posts( $filter, $total_rows );
322
323 $wp_query->post_count = count( $lp_courses );
324 $wp_query->found_posts = $total_rows;
325 $wp_query->max_num_pages = (int) ceil( $total_rows / $posts_per_page );
326 $posts = $lp_courses;
327 } catch ( Throwable $e ) {
328 LP_Debug::error_log( $e );
329 }
330
331 return $posts;
332 }
333
334 /*public function posts_fields( $fields ): string {
335 if ( ! $this->is_page_list_posts_on_backend() ) {
336 return $fields;
337 }
338
339 $fields = ' DISTINCT ' . $fields;
340 if ( $this->get_order_by() == 'price' ) {
341 $fields .= ', pm_price.meta_value as course_price';
342 }
343
344 return $fields;
345 }
346
347 public function _posts_join_paged_course_items( $join ) {
348 global $wpdb;
349
350 $course_id = $this->_filter_items_by_course();
351 if ( $course_id || LP_Request::get_param( 'orderby' ) === 'course-name' ) {
352 $join .= " LEFT JOIN {$wpdb->prefix}learnpress_section_items si ON {$wpdb->posts}.ID = si.item_id";
353 $join .= " LEFT JOIN {$wpdb->prefix}learnpress_sections s ON s.section_id = si.section_id";
354 $join .= " LEFT JOIN {$wpdb->posts} c ON c.ID = s.section_course_id";
355 }
356
357 return $join;
358 }
359
360 public function _posts_where_paged_course_items( $where ) {
361 global $wpdb;
362
363 $course_id = $this->_filter_items_by_course();
364 if ( $course_id ) {
365 $where .= $wpdb->prepare( ' AND (c.ID = %d)', $course_id );
366 $where .= $wpdb->prepare( " AND {$wpdb->posts}.post_status = %s", 'publish' );
367 }
368
369 return $where;
370 }
371
372 public function posts_join_paged( $join ) {
373 global $wpdb;
374
375 if ( ! $this->is_page_list_posts_on_backend() ) {
376 return $join;
377 }
378
379 if ( ! isset( $_GET['orderby'] ) || $_GET['orderby'] !== 'price' ) {
380 return $join;
381 }
382
383 $join .= " LEFT JOIN {$wpdb->postmeta} pm_price ON pm_price.post_id = {$wpdb->posts}.ID AND pm_price.meta_key = '_lp_price'";
384
385 return $join;
386 }
387
388 public function posts_where_paged( $where ) {
389 global $wpdb;
390
391 if ( ! $this->is_page_list_posts_on_backend() ) {
392 return $where;
393 }
394
395 $filter_price = LP_Helper::sanitize_params_submitted( $_REQUEST['filter_price'] ?? 0 );
396
397 if ( array_key_exists( 'filter_price', $_REQUEST ) ) {
398 if ( $filter_price == 0 ) {
399 $where .= ' AND ( pm_price.meta_value IS NULL || pm_price.meta_value = 0 )';
400 } else {
401 $where .= $wpdb->prepare( ' AND ( pm_price.meta_value = %s )', $filter_price );
402 }
403 }
404
405 return $where;
406 }
407
408 public function posts_orderby( $orderby ) {
409 if ( ! $this->is_page_list_posts_on_backend() ) {
410 return $orderby;
411 }
412
413 $order = $this->get_order_sort();
414 switch ( $this->get_order_by() ) {
415 case 'price':
416 $orderby = "CAST(pm_price.meta_value AS UNSIGNED) {$order}";
417 }
418
419 return $orderby;
420 }*/
421
422 /**
423 * Save course post
424 *
425 * @param int $post_id
426 * @param WP_Post|null $post
427 * @param bool $is_update
428 *
429 * @since 4.2.6.9
430 * @version 1.0.3
431 */
432 public function save_post( int $post_id, ?WP_Post $post = null, bool $is_update = false ) {
433 try {
434 $wp_screen = function_exists( 'get_current_screen' ) ? get_current_screen() : null;
435 // Save to table learnpress_courses
436 LP_Install::instance()->create_table_courses();
437 if ( empty( $post ) ) {
438 $post = get_post( $post_id );
439 }
440
441 /*if ( $post->post_status === 'auto-draft' ) {
442 return;
443 }*/
444
445 $courseModel = CourseModel::find( $post_id, true );
446 if ( ! $courseModel ) {
447 $courseModel = new CourseModel( $post );
448 }
449
450 // Get all metadata of post
451 $all_metadata = $courseModel->get_post_model()->get_all_metadata();
452 $courseModel->meta_data = $all_metadata;
453 // Temporary unset _elementor_page_assets of El, reason by method get_all_metadata get not use maybe_unserialize, make save invalid serialize
454 $courseModel->meta_data->_elementor_page_assets = [];
455
456 // Merge object post and courseModel
457 $new_obj = (array) $post;
458 $old_obj = (array) $courseModel;
459 $old_now = array_merge( $old_obj, $new_obj );
460 $courseModel = new CourseModel( $old_now );
461
462 // Save option single course
463 include_once LP_PLUGIN_PATH . 'inc/admin/class-lp-admin.php';
464 $lp_meta_box_course = new LP_Meta_Box_Course();
465 $ground_fields = $lp_meta_box_course->metabox( $courseModel->ID );
466 // Save meta fields
467 foreach ( $ground_fields as $fields ) {
468 if ( ! isset( $fields['content'] ) ) {
469 continue;
470 }
471 foreach ( $fields['content'] as $meta_key => $option ) {
472 $option->id = $meta_key;
473 if ( ! $option instanceof LP_Meta_Box_Field ) {
474 continue;
475 }
476
477 if ( isset( $_POST[ $meta_key ] ) && ! empty( $wp_screen ) && LP_COURSE_CPT === $wp_screen->id ) {
478 $value_saved = $option->save( $courseModel->ID );
479 if ( ! empty( $value_saved ) ) {
480 $courseModel->meta_data->{$meta_key} = $value_saved;
481 } else {
482 $courseModel->meta_data->{$meta_key} = maybe_unserialize(
483 get_post_meta( $courseModel->ID, $meta_key, true )
484 );
485 }
486 } elseif ( ! $is_update ) {
487 if ( isset( $courseModel->meta_data->{$meta_key} ) ) {
488 continue;
489 }
490 $courseModel->meta_data->{$meta_key} = $option->default ?? '';
491 } elseif ( ! empty( $wp_screen ) && LP_COURSE_CPT === $wp_screen->id ) {
492 $value_saved = $option->save( $courseModel->ID );
493 $courseModel->meta_data->{$meta_key} = $value_saved;
494 }
495 }
496 }
497
498 // For case bulk edit multiple courses change author
499 $bulk_edit = LP_Request::get_param( 'bulk_edit', false );
500 if ( ! empty( $bulk_edit ) ) {
501 $post_author = LP_Request::get_param( 'post_author', 0, 'int' );
502 if ( $post_author != -1 ) { // -1 is for no change author
503 $courseModel->post_author = $post_author;
504 }
505 } elseif ( ! empty( $_REQUEST['_post_author'] ) ) {
506 // Not use key 'post_author' on single edit course, it special key of WP, Gutenberg save will not submit this key.
507 $courseModel->post_author = LP_Request::get_param( '_post_author', 0, 'int' );
508 /**
509 * Save author to post table
510 * Not use PostModel::save() or CoursePostModel::save() here
511 * because it calls wp_update_post(), has hook 'save_post', it will cause infinite loop
512 */
513 $lp_db = LP_Database::getInstance();
514 $filter_update = new LP_Post_Type_Filter();
515 $filter_update->collection = $lp_db->tb_posts;
516 $filter_update->set[] = "post_author = {$courseModel->post_author}";
517 $filter_update->where[] = $lp_db->wpdb->prepare( 'AND ID = %d', $courseModel->ID );
518 $lp_db->update_execute( $filter_update );
519 clean_post_cache( $post->ID );
520 } elseif ( isset( $_POST['post_author'] ) && isset( $_POST['screen'] ) && $_POST['screen'] === 'edit-' . LP_COURSE_CPT ) {
521 // For case quick edit change author.
522 $courseModel->post_author = LP_Request::get_param( 'post_author', 0, 'int' );
523 }
524
525 $this->save_price( $courseModel );
526 $courseModel->save();
527 // End save to table learnpress_courses
528
529 // Save extra info course
530 // Save in background.
531 $bg = LP_Background_Single_Course::instance();
532 $bg->data(
533 array(
534 'handle_name' => 'save_post',
535 'course_id' => $post_id,
536 'data' => $_POST ?? [],
537 )
538 )->dispatch();
539 } catch ( Throwable $e ) {
540 LP_Debug::error_log( $e );
541 }
542 }
543
544 /**
545 * Save price course
546 *
547 * @return void
548 * @throws Exception
549 */
550 protected function save_price( CourseModel &$courseObj ) {
551 $coursePost = new CoursePostModel( $courseObj );
552
553 $regular_price = $courseObj->get_regular_price();
554 $sale_price = $courseObj->get_sale_price();
555 if ( (float) $regular_price < 0 ) {
556 $courseObj->meta_data->{CoursePostModel::META_KEY_REGULAR_PRICE} = '';
557 $regular_price = $courseObj->get_regular_price();
558 }
559
560 if ( $sale_price !== '' && (float) $sale_price > (float) $regular_price ) {
561 $courseObj->meta_data->{CoursePostModel::META_KEY_SALE_PRICE} = '';
562 $sale_price = $courseObj->get_sale_price();
563 }
564
565 // Save sale regular price and sale price to table postmeta
566 $coursePost->save_meta_value_by_key( CoursePostModel::META_KEY_REGULAR_PRICE, $regular_price );
567 $coursePost->save_meta_value_by_key( CoursePostModel::META_KEY_SALE_PRICE, $sale_price );
568
569 $has_sale = $courseObj->has_sale_price();
570 if ( $has_sale ) {
571 $courseObj->is_sale = 1;
572 $coursePost->save_meta_value_by_key( CoursePostModel::META_KEY_IS_SALE, 1 );
573 } else {
574 $courseObj->is_sale = 0;
575 delete_post_meta( $courseObj->get_id(), CoursePostModel::META_KEY_IS_SALE );
576 }
577
578 // Set price to sort on lists.
579 $courseObj->price_to_sort = $courseObj->get_price();
580 $coursePost->save_meta_value_by_key( CoursePostModel::META_KEY_PRICE, $courseObj->price_to_sort );
581 }
582
583 /**
584 * Instance LP_Course_Post_Type.
585 *
586 * @return LP_Course_Post_Type|null
587 */
588 public static function instance() {
589 if ( ! self::$_instance ) {
590 self::$_instance = new self();
591 }
592
593 return self::$_instance;
594 }
595 }
596
597 $course_post_type = LP_Course_Post_Type::instance();
598 }
599