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

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

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