PluginProbe
LearnPress – WordPress LMS Plugin for Create and Sell Online Courses / 4.4.5
LearnPress – WordPress LMS Plugin for Create and Sell Online Courses v4.4.5
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.5, at inc/custom-post-types/course.php

507 lines 16.3 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.3
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 // Get all metadata of post
359 $all_metadata = $courseModel->get_post_model()->get_all_metadata();
360 $courseModel->meta_data = $all_metadata;
361 // Temporary unset _elementor_page_assets of El, reason by method get_all_metadata get not use maybe_unserialize, make save invalid serialize
362 $courseModel->meta_data->_elementor_page_assets = [];
363
364 // Merge object post and courseModel
365 $new_obj = (array) $post;
366 $old_obj = (array) $courseModel;
367 $old_now = array_merge( $old_obj, $new_obj );
368 $courseModel = new CourseModel( $old_now );
369
370 // Save option single course
371 include_once LP_PLUGIN_PATH . 'inc/admin/class-lp-admin.php';
372 $lp_meta_box_course = new LP_Meta_Box_Course();
373 $ground_fields = $lp_meta_box_course->metabox( $courseModel->ID );
374 // Save meta fields
375 foreach ( $ground_fields as $fields ) {
376 if ( ! isset( $fields['content'] ) ) {
377 continue;
378 }
379 foreach ( $fields['content'] as $meta_key => $option ) {
380 $option->id = $meta_key;
381 if ( ! $option instanceof LP_Meta_Box_Field ) {
382 continue;
383 }
384
385 if ( isset( $_POST[ $meta_key ] ) && ! empty( $wp_screen ) && LP_COURSE_CPT === $wp_screen->id ) {
386 $value_saved = $option->save( $courseModel->ID );
387 if ( ! empty( $value_saved ) ) {
388 $courseModel->meta_data->{$meta_key} = $value_saved;
389 } else {
390 $courseModel->meta_data->{$meta_key} = maybe_unserialize(
391 get_post_meta( $courseModel->ID, $meta_key, true )
392 );
393 }
394 } elseif ( ! $is_update ) {
395 if ( isset( $courseModel->meta_data->{$meta_key} ) ) {
396 continue;
397 }
398 $courseModel->meta_data->{$meta_key} = $option->default ?? '';
399 } elseif ( ! empty( $wp_screen ) && LP_COURSE_CPT === $wp_screen->id ) {
400 $value_saved = $option->save( $courseModel->ID );
401 $courseModel->meta_data->{$meta_key} = $value_saved;
402 }
403 }
404 }
405
406 // For case bulk edit multiple courses change author
407 $bulk_edit = LP_Request::get_param( 'bulk_edit', false );
408 if ( ! empty( $bulk_edit ) ) {
409 $post_author = LP_Request::get_param( 'post_author', 0, 'int' );
410 if ( $post_author != -1 ) { // -1 is for no change author
411 $courseModel->post_author = $post_author;
412 }
413 } elseif ( ! empty( $_REQUEST['_post_author'] ) ) {
414 // Not use key 'post_author' on single edit course, it special key of WP, Gutenberg save will not submit this key.
415 $courseModel->post_author = LP_Request::get_param( '_post_author', 0, 'int' );
416 /**
417 * Save author to post table
418 * Not use PostModel::save() or CoursePostModel::save() here
419 * because it calls wp_update_post(), has hook 'save_post', it will cause infinite loop
420 */
421 $lp_db = LP_Database::getInstance();
422 $filter_update = new LP_Post_Type_Filter();
423 $filter_update->collection = $lp_db->tb_posts;
424 $filter_update->set[] = "post_author = {$courseModel->post_author}";
425 $filter_update->where[] = $lp_db->wpdb->prepare( 'AND ID = %d', $courseModel->ID );
426 $lp_db->update_execute( $filter_update );
427 clean_post_cache( $post->ID );
428 } elseif ( isset( $_POST['post_author'] ) && isset( $_POST['screen'] ) && $_POST['screen'] === 'edit-' . LP_COURSE_CPT ) {
429 // For case quick edit change author.
430 $courseModel->post_author = LP_Request::get_param( 'post_author', 0, 'int' );
431 }
432
433 $this->save_price( $courseModel );
434 $courseModel->save();
435 // End save to table learnpress_courses
436
437 // Save extra info course
438 // Save in background.
439 $bg = LP_Background_Single_Course::instance();
440 $bg->data(
441 array(
442 'handle_name' => 'save_post',
443 'course_id' => $post_id,
444 'data' => $_POST ?? [],
445 )
446 )->dispatch();
447 } catch ( Throwable $e ) {
448 LP_Debug::error_log( $e );
449 }
450 }
451
452 /**
453 * Save price course
454 *
455 * @return void
456 * @throws Exception
457 */
458 protected function save_price( CourseModel &$courseObj ) {
459 $coursePost = new CoursePostModel( $courseObj );
460
461 $regular_price = $courseObj->get_regular_price();
462 $sale_price = $courseObj->get_sale_price();
463 if ( (float) $regular_price < 0 ) {
464 $courseObj->meta_data->{CoursePostModel::META_KEY_REGULAR_PRICE} = '';
465 $regular_price = $courseObj->get_regular_price();
466 }
467
468 if ( $sale_price !== '' && (float) $sale_price > (float) $regular_price ) {
469 $courseObj->meta_data->{CoursePostModel::META_KEY_SALE_PRICE} = '';
470 $sale_price = $courseObj->get_sale_price();
471 }
472
473 // Save sale regular price and sale price to table postmeta
474 $coursePost->save_meta_value_by_key( CoursePostModel::META_KEY_REGULAR_PRICE, $regular_price );
475 $coursePost->save_meta_value_by_key( CoursePostModel::META_KEY_SALE_PRICE, $sale_price );
476
477 $has_sale = $courseObj->has_sale_price();
478 if ( $has_sale ) {
479 $courseObj->is_sale = 1;
480 $coursePost->save_meta_value_by_key( CoursePostModel::META_KEY_IS_SALE, 1 );
481 } else {
482 $courseObj->is_sale = 0;
483 delete_post_meta( $courseObj->get_id(), CoursePostModel::META_KEY_IS_SALE );
484 }
485
486 // Set price to sort on lists.
487 $courseObj->price_to_sort = $courseObj->get_price();
488 $coursePost->save_meta_value_by_key( CoursePostModel::META_KEY_PRICE, $courseObj->price_to_sort );
489 }
490
491 /**
492 * Instance LP_Course_Post_Type.
493 *
494 * @return LP_Course_Post_Type|null
495 */
496 public static function instance() {
497 if ( ! self::$_instance ) {
498 self::$_instance = new self();
499 }
500
501 return self::$_instance;
502 }
503 }
504
505 $course_post_type = LP_Course_Post_Type::instance();
506 }
507