PluginProbe
LearnPress – WordPress LMS Plugin for Create and Sell Online Courses / 4.4.7
LearnPress – WordPress LMS Plugin for Create and Sell Online Courses v4.4.7
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 / TemplateHooks / Course / SingleCourseTemplate.php

SingleCourseTemplate.php in LearnPress – WordPress LMS Plugin for Create and Sell Online Courses 4.4.7, at inc/TemplateHooks/Course/SingleCourseTemplate.php

1,952 lines 52.6 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Template hooks Single Course.
4 *
5 * @since 4.2.3
6 * @version 1.0.5
7 */
8
9 namespace LearnPress\TemplateHooks\Course;
10
11 use LearnPress\Helpers\Singleton;
12 use LearnPress\Helpers\Template;
13 use LearnPress\Models\CourseModel;
14 use LearnPress\Models\CoursePostModel;
15 use LearnPress\Models\LessonPostModel;
16 use LearnPress\Models\PostModel;
17 use LearnPress\Models\QuizPostModel;
18 use LearnPress\Models\UserItems\UserCourseModel;
19 use LearnPress\Models\UserItems\UserItemModel;
20 use LearnPress\Models\UserModel;
21 use LearnPress\TemplateHooks\Instructor\SingleInstructorTemplate;
22 use LearnPress\TemplateHooks\TemplateAJAX;
23 use LearnPress\TemplateHooks\UserTemplate;
24 use LearnPressAssignment\Models\AssignmentPostModel;
25 use LP_Checkout;
26 use LP_Course;
27 use LP_Course_Item;
28 use LP_Datetime;
29 use LP_Debug;
30 use LP_Global;
31 use LP_Material_Files_DB;
32 use LP_Settings;
33 use stdClass;
34 use Throwable;
35
36 class SingleCourseTemplate {
37 use Singleton;
38
39 /**
40 * @var false|LessonPostModel|QuizPostModel|PostModel|mixed $currentItemModel
41 */
42 public $currentItemModel = false;
43
44 public function init() {
45 add_filter( 'lp/rest/ajax/allow_callback', [ $this, 'allow_callback' ] );
46 }
47
48 /**
49 * Allow callback for AJAX.
50 * @use self::render_html_comments
51 *
52 * @param array $callbacks
53 *
54 * @return array
55 */
56 public function allow_callback( array $callbacks ): array {
57 $callbacks[] = get_class( $this ) . ':render_html_comments';
58
59 return $callbacks;
60 }
61
62 /**
63 * Get display title course.
64 *
65 * @param LP_Course|CourseModel $course
66 * @param string $tag_html
67 *
68 * @return string
69 */
70 public function html_title( $course, string $tag_html = 'span' ): string {
71 $tag_html = esc_attr( sanitize_key( $tag_html ) );
72 $html_wrapper = apply_filters(
73 'learn-press/single-course/html-title',
74 [
75 "<{$tag_html} class='course-title'>" => "</{$tag_html}>",
76 ],
77 $course
78 );
79
80 return Template::instance()->nest_elements( $html_wrapper, $course->get_title() );
81 }
82
83 /**
84 * Get short description course.
85 *
86 * @param LP_Course|CourseModel $course
87 * @param int $number_words
88 *
89 * @return string
90 */
91 public function html_short_description( $course, int $number_words = 0 ): string {
92 $html_wrapper = [
93 '<p class="course-short-description">' => '</p>',
94 ];
95
96 if ( $course instanceof LP_Course ) {
97 $course = CourseModel::find( $course->get_id(), true );
98 }
99
100 $short_description = $course->get_short_description();
101 if ( empty( $short_description ) ) {
102 return '';
103 }
104
105 if ( $number_words > 0 ) {
106 $short_description = wp_trim_words( $short_description, $number_words, '...' );
107 }
108
109 return Template::instance()->nest_elements( $html_wrapper, $short_description );
110 }
111
112 /**
113 * Get description course.
114 *
115 * @param LP_Course|CourseModel $course
116 *
117 * @return string
118 */
119 public function html_description( $course ): string {
120 $content = '';
121
122 if ( $course instanceof LP_Course ) {
123 $content = $course->get_data( 'description' );
124 } elseif ( $course instanceof CourseModel ) {
125 $content = $course->get_description();
126 }
127
128 $section = apply_filters(
129 'learn-press/course/html-description',
130 [
131 'wrapper' => '<div class="lp-course-description">',
132 'content' => $content,
133 'wrapper_end' => '</div>',
134 ],
135 $course
136 );
137
138 return Template::combine_components( $section );
139 }
140
141 /**
142 * Get display categories course.
143 *
144 * @param LP_Course|CourseModel $course
145 * @param array $setting
146 *
147 * @return string
148 * @since 4.2.6
149 * @version 1.0.3
150 */
151 public function html_categories( $course, array $setting = [] ): string {
152 $html = '';
153
154 try {
155 if ( $course instanceof LP_Course ) {
156 $course = CourseModel::find( $course->get_id(), true );
157 }
158
159 if ( empty( $course ) ) {
160 return '';
161 }
162
163 $cats = $course->get_categories();
164 if ( empty( $cats ) ) {
165 return '';
166 }
167
168 $is_link = $setting['is_link'] ?? true;
169 $attribute_target = ! empty( $setting['new_tab'] ) ? 'target="_blank"' : '';
170 $cat_names = [];
171 foreach ( $cats as $cat ) {
172 if ( $is_link ) {
173 $term = sprintf(
174 '<a href="%s" %s>%s</a>',
175 get_term_link( $cat ),
176 $attribute_target,
177 $cat->name
178 );
179 } else {
180 $term = $cat->name;
181 }
182
183 $cat_names[] = $term;
184 }
185
186 $content = implode( ', ', $cat_names );
187
188 $section = apply_filters(
189 'learn-press/course/html-categories',
190 [
191 'wrapper' => '<div class="course-categories">',
192 'content' => $content,
193 'wrapper_end' => '</div>',
194 ],
195 $course,
196 $cats,
197 $cat_names
198 );
199
200 $html = Template::combine_components( $section );
201 } catch ( Throwable $e ) {
202 error_log( __METHOD__ . ': ' . $e->getMessage() );
203 }
204
205 return $html;
206 }
207
208 /**
209 * Get display tags course.
210 *
211 * @param LP_Course|CourseModel $course
212 *
213 * @return string
214 * @since 4.2.6
215 * @version 1.0.2
216 */
217 public function html_tags( $course ): string {
218 $html = '';
219
220 try {
221 if ( $course instanceof LP_Course ) {
222 $course = CourseModel::find( $course->get_id(), true );
223 }
224
225 if ( empty( $course ) ) {
226 return '';
227 }
228
229 $tags = $course->get_tags();
230 if ( empty( $tags ) ) {
231 return '';
232 }
233
234 $tag_names = [];
235 foreach ( $tags as $tag ) {
236 $term = sprintf(
237 '<a href="%s">%s</a>',
238 get_term_link( $tag ),
239 $tag->name
240 );
241 $tag_names[] = $term;
242 }
243
244 $content = implode( ', ', $tag_names );
245
246 $section = apply_filters(
247 'learn-press/course/html-tags',
248 [
249 'wrapper' => '<div class="course-tags">',
250 'content' => $content,
251 'wrapper_end' => '</div>',
252 ],
253 $course,
254 $tags,
255 $tag_names
256 );
257
258 $html = Template::combine_components( $section );
259 } catch ( Throwable $e ) {
260 error_log( __METHOD__ . ': ' . $e->getMessage() );
261 }
262
263 return $html;
264 }
265
266 /**
267 * Get display image course.
268 *
269 * @param LP_Course|CourseModel $course
270 * @param array $data ['size'] Size of image to get, Ex: [500, 300] or string 'thumbnail', 'medium', 'large', 'full' etc.
271 *
272 * @return string
273 * @since 4.2.3.2
274 * @version 1.0.3
275 */
276 public function html_image( $course, array $data = [] ): string {
277 $content = '';
278
279 try {
280 $courseModel = $course;
281 if ( $course instanceof LP_Course ) {
282 $courseModel = CourseModel::find( $course->get_id(), true );
283 }
284
285 if ( ! $courseModel instanceof CourseModel ) {
286 return '';
287 }
288
289 if ( ! empty( $data['size'] ) ) {
290 $size_img_send = $data['size'];
291
292 // If custom size, data size is type int[], like [500, 300], not [ width => 500, height => 300 ]
293 // Convert if data is [ width => 500, height => 300 ]
294 if ( is_array( $size_img_send ) && array_key_exists( 'width', $size_img_send ) ) {
295 $size_img_send = [
296 $size_img_send['width'] ?? 500,
297 $size_img_send['height'] ?? 300,
298 ];
299 }
300 } else {
301 $size_img_setting = LP_Settings::get_option( 'course_thumbnail_dimensions', [] );
302 $size_img_send = [
303 $size_img_setting['width'] ?? 500,
304 $size_img_setting['height'] ?? 300,
305 ];
306 }
307
308 // Check cache before get image url
309 $cache = new \LP_Course_Cache();
310 $key_cache = 'image_url/' . $courseModel->get_id();
311 if ( is_array( $size_img_send ) && count( $size_img_send ) === 2 ) {
312 $key_cache .= '/' . implode( 'x', $size_img_send );
313 } elseif ( is_string( $size_img_send ) ) {
314 $key_cache .= '/' . $size_img_send;
315 }
316
317 // Set cache for image url
318 $course_img_url = $cache->get_cache( $key_cache );
319 if ( false === $course_img_url ) {
320 $course_img_url = $courseModel->get_image_url( $size_img_send );
321 $cache->set_cache( $key_cache, $course_img_url );
322 $cache->save_cache_keys( sprintf( 'image_urls/%s', $courseModel->get_id() ), $key_cache );
323 }
324
325 $content = sprintf(
326 '<img src="%s" alt="%s">',
327 esc_url_raw( $course_img_url ),
328 _x( 'course thumbnail', 'no course thumbnail', 'learnpress' )
329 );
330
331 $section = apply_filters(
332 'learn-press/course/html-image',
333 [
334 'wrapper' => '<div class="course-img">',
335 'content' => $content,
336 'wrapper_end' => '</div>',
337 ],
338 $courseModel
339 );
340
341 $content = Template::combine_components( $section );
342 } catch ( Throwable $e ) {
343 error_log( __METHOD__ . ': ' . $e->getMessage() );
344 }
345
346 return $content;
347 }
348
349 /**
350 * Get display instructor course.
351 *
352 * @param CourseModel $course
353 * @param bool $with_avatar
354 *
355 * @return string
356 * @since 4.2.5.8
357 * @version 1.0.2
358 */
359 public function html_instructor( $course, bool $with_avatar = false, $setting = [] ): string {
360 $content = '';
361
362 try {
363 $instructor = $course->get_author_model();
364 if ( ! $instructor ) {
365 return '';
366 }
367
368 $singleInstructorTemplate = SingleInstructorTemplate::instance();
369 $userTemplate = new UserTemplate( 'instructor' );
370 $is_link = $setting['is_link'] ?? true;
371 if ( $is_link ) {
372 $attribute_target = ! empty( $setting['new_tab'] ) ? 'target="_blank"' : '';
373 $link_instructor = sprintf(
374 '<a href="%s" %s >%s %s</a>',
375 $instructor->get_url_instructor(),
376 $attribute_target,
377 $with_avatar ? $userTemplate->html_avatar( $instructor ) : '',
378 $singleInstructorTemplate->html_display_name( $instructor )
379 );
380 } else {
381 $link_instructor = sprintf(
382 '%s %s',
383 $with_avatar ? $userTemplate->html_avatar( $instructor ) : '',
384 $singleInstructorTemplate->html_display_name( $instructor )
385 );
386 }
387
388 $section = apply_filters(
389 'learn-press/course/instructor-html',
390 [
391 'wrapper' => '<div class="course-instructor">',
392 'content' => $link_instructor,
393 'wrapper_end' => '</div>',
394 ],
395 $course,
396 $instructor,
397 $with_avatar
398 );
399
400 $content = Template::combine_components( $section );
401 } catch ( Throwable $e ) {
402 error_log( __METHOD__ . ': ' . $e->getMessage() );
403 }
404
405 return $content;
406 }
407
408 /**
409 * Get html regular price
410 *
411 * @param CourseModel $course
412 *
413 * @return string
414 */
415 public function html_regular_price( CourseModel $course ): string {
416 $price = learn_press_format_price( $course->get_regular_price() );
417 $price = apply_filters( 'learn-press/course/regular-price-html', $price, $course, $this );
418
419 return sprintf( '<span class="origin-price">%s</span>', $price );
420 }
421
422 /**
423 * Get display price course.
424 *
425 * @param LP_Course|CourseModel $course
426 *
427 * @return string
428 */
429 public function html_price( $course ): string {
430 $price_html = '';
431
432 if ( $course instanceof LP_Course ) {
433 $course = CourseModel::find( $course->get_id(), true );
434 }
435
436 if ( ! $course ) {
437 return $price_html;
438 }
439
440 if ( $course->is_free() ) {
441 if ( '' != $course->get_sale_price() ) {
442 $price_html .= $this->html_regular_price( $course );
443 }
444
445 $price_html .= sprintf( '<span class="free">%s</span>', esc_html__( 'Free', 'learnpress' ) );
446 $price_html = apply_filters( 'learn_press_course_price_html_free', $price_html, $this );
447 } elseif ( $course->has_no_enroll_requirement() ) {
448 $price_html .= '';
449 } else {
450 if ( $course->has_sale_price() ) {
451 $price_html .= $this->html_regular_price( $course );
452 }
453
454 $price_html .= sprintf(
455 '<span class="price">%s</span>',
456 learn_press_format_price( $course->get_price(), true )
457 );
458 $price_html = sprintf(
459 '%1$s %2$s %3$s',
460 $this->html_price_prefix( $course ),
461 $price_html,
462 $this->html_price_suffix( $course )
463 );
464 $price_html = apply_filters(
465 'learn_press_course_price_html',
466 $price_html,
467 $course->has_sale_price(),
468 $course->get_id()
469 );
470 }
471
472 // @since 4.2.7
473 $price_html = sprintf( '<span class="course-price"><span class="course-item-price">%s</span></span>', $price_html );
474
475 return apply_filters( 'learn-press/course/html-price', $price_html, $course );
476 }
477
478 /**
479 * Get deliver type
480 *
481 * @param CourseModel $course
482 *
483 * @return string
484 */
485 public function html_capacity( CourseModel $course ): string {
486 $content = '';
487
488 $html_wrapper = [
489 '<span class="course-capacity">' => '</span>',
490 ];
491 $capacity = $course->get_meta_value_by_key( CoursePostModel::META_KEY_MAX_STUDENTS, 0 );
492
493 if ( $capacity == 0 ) {
494 $content = __( 'Unlimited', 'learnpress' );
495 } else {
496 $content = sprintf( '%d %s', $capacity, _n( 'Student', 'Students', $capacity, 'learnpress' ) );
497 }
498
499 return Template::instance()->nest_elements( $html_wrapper, $content );
500 }
501
502 /**
503 * Get display total student's course.
504 *
505 * @param LP_Course|CourseModel $course
506 *
507 * @return string
508 * @since 4.2.3.4
509 * @version 1.0.2
510 */
511 public function html_count_student( $course ): string {
512 if ( $course instanceof LP_Course ) {
513 $course = CourseModel::find( $course->get_id(), true );
514 }
515
516 if ( empty( $course ) ) {
517 return '';
518 }
519
520 $count_student = $course->get_total_user_enrolled_or_purchased();
521 $count_student += $course->get_fake_students();
522 $content = sprintf( '%d %s', $count_student, _n( 'Student', 'Students', $count_student, 'learnpress' ) );
523 $html_wrapper = [
524 '<div class="course-count-student">' => '</div>',
525 ];
526
527 return Template::instance()->nest_elements( $html_wrapper, $content );
528 }
529
530 /**
531 * Get display total lesson's course.
532 *
533 * @param LP_Course|CourseModel $course
534 * @param string $item_type custom post type item
535 * @param bool $show_only_number
536 *
537 * @return string
538 */
539 public function html_count_item( $course, string $item_type, bool $show_only_number = false ): string {
540 if ( $course instanceof LP_Course ) {
541 $course = CourseModel::find( $course->get_id(), true );
542 }
543
544 if ( empty( $course ) ) {
545 return '';
546 }
547
548 $info_total_items = $course->get_total_items();
549 if ( empty( $info_total_items ) ) {
550 return '';
551 }
552
553 $count_item = $info_total_items->{$item_type} ?? 0;
554
555 if ( $show_only_number ) {
556 $content = $count_item;
557 } else {
558 switch ( $item_type ) {
559 case LP_LESSON_CPT:
560 $content = sprintf( '%d %s', $count_item, _n( 'Lesson', 'Lessons', $count_item, 'learnpress' ) );
561 break;
562 case LP_QUIZ_CPT:
563 $content = sprintf( '%d %s', $count_item, _n( 'Quiz', 'Quizzes', $count_item, 'learnpress' ) );
564 break;
565 case 'lp_assignment':
566 $content = sprintf( '%d %s', $count_item, _n( 'Assignment', 'Assignments', $count_item, 'learnpress' ) );
567 break;
568 case 'lp_h5p':
569 $content = sprintf( '%d %s', $count_item, _n( 'H5P', 'H5Ps', $count_item, 'learnpress' ) );
570 break;
571 default:
572 $content = apply_filters( 'learn-press/single-course/i18n/count-item', $count_item, $course, $item_type );
573 break;
574 }
575 }
576
577 $section = apply_filters(
578 'learn-press/single-course/html-count-item',
579 [
580 'wrapper' => sprintf( '<div class="course-count-item %s">', $item_type ),
581 'content' => $content,
582 'wrapper_end' => '</div>',
583 ],
584 $course,
585 $item_type,
586 $count_item
587 );
588
589 return Template::combine_components( $section );
590 }
591
592 /**
593 * Get html level course.
594 *
595 * @param LP_Course|CourseModel $course
596 *
597 * @return string
598 * @since 4.2.3.5
599 * @version 1.0.1
600 */
601 public function html_level( $course ): string {
602 $content = '';
603
604 try {
605 if ( $course instanceof LP_Course ) {
606 $course = CourseModel::find( $course->get_id(), true );
607 }
608
609 $level = $course->get_meta_value_by_key( CoursePostModel::META_KEY_LEVEL, '' );
610 if ( empty( $level ) ) {
611 $level = 'all';
612 }
613
614 $levels = lp_course_level();
615 $level = $levels[ $level ] ?? $levels['all'];
616
617 $html_wrapper = [
618 '<span class="course-level">' => '</span>',
619 ];
620 $content = Template::instance()->nest_elements( $html_wrapper, $level );
621 } catch ( Throwable $e ) {
622 error_log( __METHOD__ . ': ' . $e->getMessage() );
623 }
624
625 return $content;
626 }
627
628 /**
629 * Get html duration course.
630 *
631 * @param LP_Course|CourseModel $course
632 *
633 * @return string
634 * @since 4.2.3.5
635 * @version 1.0.0
636 */
637 public function html_duration( $course ): string {
638 $content = '';
639
640 try {
641 if ( $course instanceof LP_Course ) {
642 $course = CourseModel::find( $course->get_id(), true );
643 }
644
645 $duration = $course->get_meta_value_by_key( CoursePostModel::META_KEY_DURATION, '' );
646 $duration_arr = explode( ' ', $duration );
647 $duration_number = floatval( $duration_arr[0] ?? 0 );
648 $duration_type = $duration_arr[1] ?? '';
649 if ( empty( $duration_number ) ) {
650 $duration_str = __( 'Lifetime', 'learnpress' );
651 } else {
652 $duration_str = LP_Datetime::get_string_plural_duration( $duration_number, $duration_type );
653 }
654
655 $html_wrapper = [
656 '<span class="course-duration">' => '</span>',
657 ];
658 $content = Template::instance()->nest_elements( $html_wrapper, $duration_str );
659 } catch ( Throwable $e ) {
660 error_log( __METHOD__ . ': ' . $e->getMessage() );
661 }
662
663 return $content;
664 }
665
666 /**
667 * Get feature review
668 *
669 * @param CourseModel $course
670 * @param UserModel|false $userModel
671 * @param array $data
672 *
673 * @return string
674 * @since 4.2.7
675 * @version 1.0.2
676 */
677 public function html_feature_review( CourseModel $course, $userModel = false, array $data = [] ): string {
678 $feature_review = $course->get_meta_value_by_key( CoursePostModel::META_KEY_FEATURED_REVIEW, '' );
679 if ( empty( $feature_review ) ) {
680 return '';
681 }
682
683 if ( $userModel instanceof UserModel ) {
684 $userCourseModel = UserCourseModel::find( $userModel->get_id(), $course->get_id(), true );
685 if ( $userCourseModel ) {
686 return '';
687 }
688 }
689
690 $stars = '';
691 foreach ( range( 1, 5 ) as $star ) {
692 $stars .= '<i class="lp-icon-star"></i>';
693 }
694
695 $section = [
696 'wrapper' => sprintf(
697 '<div class="course-featured-review %s">',
698 esc_attr( $data['lp_display_on'] ?? '' )
699 ),
700 'title' => sprintf(
701 '<div class="featured-review__title">%s</div>',
702 esc_html__( 'Featured Review', 'learnpress' )
703 ),
704 'stars' => sprintf(
705 '<div class="featured-review__stars">%s</div>',
706 $stars
707 ),
708 'content' => sprintf(
709 '<div class="featured-review__content">%s</div>',
710 wp_kses_post( wpautop( $feature_review ) )
711 ),
712 'wrapper_end' => '</div>',
713 ];
714
715 return Template::combine_components( $section );
716 }
717
718 /**
719 * Get button external
720 *
721 * @param CourseModel $courseModel
722 * @param UserModel|false $userModel
723 *
724 * @return string
725 * @since 4.2.7
726 * @version 1.0.3
727 */
728 public function html_btn_external( CourseModel $courseModel, $userModel ): string {
729 $external_link = $courseModel->get_meta_value_by_key( CoursePostModel::META_KEY_EXTERNAL_LINK_BY_COURSE, '' );
730 if ( empty( $external_link ) ) {
731 return '';
732 }
733
734 // Check user has enrolled, finished or purchased course
735 if ( $userModel instanceof UserModel ) {
736 $userCourse = UserCourseModel::find( $userModel->get_id(), $courseModel->get_id(), true );
737 if ( $userCourse && ( $userCourse->has_enrolled_or_finished() || $userCourse->has_purchased() ) ) {
738 return '';
739 }
740 }
741
742 $content = sprintf(
743 '<a href="%s" class="lp-button course-btn-extra" target="_blank">%s</a>',
744 esc_url_raw( $external_link ),
745 __( 'Contact To Request', 'learnpress' )
746 );
747
748 return apply_filters( 'learn-press/course/html-button-external', $content, $courseModel, $userModel );
749 }
750
751 /**
752 * HTML button purchase course
753 *
754 * @param CourseModel $courseModel
755 * @param false|UserModel $userModel
756 *
757 * @return string
758 * @since 4.2.7.2
759 * @version 1.0.2
760 */
761 public function html_btn_purchase_course( CourseModel $courseModel, $userModel ): string {
762 $html_btn = '';
763 $can_purchase = $courseModel->can_purchase( $userModel );
764 if ( is_wp_error( $can_purchase ) ) {
765 $error_code_show = apply_filters(
766 'learn-press/course/html-button-purchase/show-messages',
767 []
768 );
769 if ( in_array( $can_purchase->get_error_code(), $error_code_show )
770 && ! empty( $can_purchase->get_error_message() ) ) {
771 $html_btn = Template::print_message( $can_purchase->get_error_message(), 'warning', false );
772 }
773 } else {
774 $html_btn = sprintf(
775 '<button class="lp-button button-purchase-course">%s</button>',
776 __( 'Buy Now', 'learnpress' )
777 );
778 }
779
780 if ( empty( $html_btn ) ) {
781 return apply_filters(
782 'learn-press/course/html-button-purchase/empty',
783 $html_btn,
784 $courseModel,
785 $userModel
786 );
787 }
788
789 $class_guest_checkout = LP_Checkout::instance()->is_enable_guest_checkout() ? 'guest_checkout' : '';
790
791 // Hook action old
792 $html_hook_old = '';
793 if ( has_action( 'learn-press/after-purchase-button' ) ) {
794 ob_start();
795 do_action( 'learn-press/after-purchase-button' );
796 $html_hook_old = ob_get_clean();
797 }
798
799 if ( has_filter( 'learnpress/course/template/button-purchase/can-show' ) ) {
800 $user_id = 0;
801 if ( $userModel instanceof UserModel ) {
802 $user_id = $userModel->get_id();
803 }
804 $user_old = learn_press_get_user( $user_id );
805 $course_old = learn_press_get_course( $courseModel->get_id() );
806 $can_show = apply_filters( 'learnpress/course/template/button-purchase/can-show', true, $user_old, $course_old );
807 if ( ! $can_show ) {
808 return '';
809 }
810 }
811 // End hook action old
812
813 $section = apply_filters(
814 'learn-press/course/html-button-purchase',
815 [
816 'form' => sprintf(
817 '<form name="purchase-course" class="purchase-course %s" method="post">',
818 esc_attr( $class_guest_checkout )
819 ),
820 'input' => sprintf(
821 '<input type="hidden" name="purchase-course" value="%d"/>',
822 esc_attr( $courseModel->get_id() )
823 ),
824 'btn' => $html_btn,
825 'hook_old' => $html_hook_old,
826 'form_end' => '</form>',
827 ],
828 $courseModel,
829 $userModel
830 );
831
832 return Template::combine_components( $section );
833 }
834
835 /**
836 * HTML button enroll course
837 *
838 * @param CourseModel $courseModel
839 * @param false|UserModel $userModel
840 *
841 * @return string
842 * @since 4.2.7.3
843 * @version 1.0.0
844 */
845 public function html_btn_enroll_course( CourseModel $courseModel, $userModel ): string {
846 $html_btn = '';
847 $can_enroll = $courseModel->can_enroll( $userModel );
848 if ( is_wp_error( $can_enroll ) ) {
849 $error_code_show = apply_filters(
850 'learn-press/course/html-button-enroll/show-messages',
851 [ 'course_is_no_required_enroll_not_login', 'course_out_of_stock' ]
852 );
853 if ( in_array( $can_enroll->get_error_code(), $error_code_show )
854 && ! empty( $can_enroll->get_error_message() ) ) {
855 $html_btn = Template::print_message( $can_enroll->get_error_message(), 'warning', false );
856 }
857 } else {
858 $html_btn = sprintf(
859 '<button type="submit" class="lp-button button-enroll-course">%s</button>',
860 __( 'Start Now', 'learnpress' )
861 );
862 }
863
864 if ( empty( $html_btn ) ) {
865 return $html_btn;
866 }
867
868 // Hook old
869 $html_hook_before_old = '';
870 if ( has_action( 'learn-press/before-enroll-button' ) ) {
871 ob_start();
872 do_action( 'learn-press/before-enroll-button' );
873 $html_hook_before_old = ob_get_clean();
874 }
875
876 $html_hook_after_old = '';
877 if ( has_action( 'learn-press/after-enroll-button' ) ) {
878 ob_start();
879 do_action( 'learn-press/after-enroll-button' );
880 $html_hook_after_old = ob_get_clean();
881 }
882
883 if ( has_filter( 'learnpress/course/template/button-enroll/can-show' ) ) {
884 $user_id = 0;
885 if ( $userModel instanceof UserModel ) {
886 $user_id = $userModel->get_id();
887 }
888 $user_old = learn_press_get_user( $user_id );
889 $course_old = learn_press_get_course( $courseModel->get_id() );
890 $can_show = apply_filters( 'learnpress/course/template/button-enroll/can-show', true, $user_old, $course_old );
891 if ( ! $can_show ) {
892 return '';
893 }
894 }
895 // End hook old
896
897 $section = apply_filters(
898 'learn-press/course/html-button-enroll',
899 [
900 'form' => '<form name="enroll-course" class="enroll-course" method="post">',
901 'hook_before_old' => $html_hook_before_old,
902 'input' => sprintf(
903 '<input type="hidden" name="enroll-course" value="%s"/>',
904 esc_attr( $courseModel->get_id() )
905 ),
906 'btn' => $html_btn,
907 'hook_after_old' => $html_hook_after_old,
908 'form_end' => '</form>',
909 ],
910 $courseModel,
911 $userModel
912 );
913
914 return Template::combine_components( $section );
915 }
916
917 /**
918 * Sidebar
919 *
920 * @param CourseModel $courseModel
921 * @param array $data
922 *
923 * @return void
924 * @version 1.0.1
925 * @since 4.2.7
926 */
927 public function html_sidebar( CourseModel $courseModel, array $data = [] ): string {
928 $html = '';
929
930 try {
931 if ( is_active_sidebar( 'course-sidebar' ) ) {
932 ob_start();
933 dynamic_sidebar( 'course-sidebar' );
934 $sidebar_content = ob_get_clean();
935
936 $section = apply_filters(
937 'learn-press/course/html-sidebar',
938 [
939 'wrapper' => sprintf(
940 '<div class="lp-single-course-sidebar %s">',
941 esc_attr( $data['lp_display_on'] ?? '' )
942 ),
943 'content' => $sidebar_content,
944 'wrapper_end' => '</div>',
945 ],
946 $courseModel
947 );
948
949 $html = Template::combine_components( $section );
950 }
951 } catch ( Throwable $e ) {
952 error_log( $e->getMessage() );
953 }
954
955 return $html;
956 }
957
958 /**
959 * HTML meta faqs
960 *
961 * @param CourseModel $courseModel
962 * @param array $data
963 *
964 * @return string
965 * @since 4.2.7.2
966 * @version 1.0.1
967 */
968 public function html_faqs( CourseModel $courseModel, array $data = [] ): string {
969 $html = '';
970
971 try {
972 $show_heading = $data['show_heading'] ?? true;
973 $faqs = $courseModel->get_meta_value_by_key( CoursePostModel::META_KEY_FAQS, [] );
974 if ( ! is_array( $faqs ) && ! is_object( $faqs ) ) {
975 return '';
976 }
977 if ( empty( $faqs ) ) {
978 return '';
979 }
980
981 foreach ( $faqs as $faq ) {
982 $title = $faq[0];
983 $description = $faq[1];
984 if ( empty( $title ) || empty( $description ) ) {
985 continue;
986 }
987
988 $key = uniqid();
989 $section_item = [
990 'input_checkbox' => sprintf(
991 '<input type="checkbox" name="course-faqs-box-ratio" id="course-faqs-box-ratio-%s">',
992 $key
993 ),
994 'wrapper' => '<div class="course-faqs-box">',
995 'title' => sprintf(
996 '<label class="course-faqs-box__title" for="course-faqs-box-ratio-%s">%s</label>',
997 $key,
998 $title
999 ),
1000 'content' => '<div class="course-faqs-box__content">',
1001 'content_inner' => '<div class="course-faqs-box__content-inner">',
1002 'content_main' => wp_kses_post( $description ),
1003 'content_inner_end' => '</div>',
1004 'content_end' => '</div>',
1005 'wrapper_end' => '</div>',
1006 ];
1007 $html .= Template::combine_components( $section_item );
1008 }
1009
1010 $section = apply_filters(
1011 'learn-press/course/html-faqs',
1012 [
1013 'wrapper' => '<div class="course-faqs course-tab-panel-faqs">',
1014 'title' => $show_heading ? sprintf(
1015 '<h3 class="course-faqs__title">%s</h3>',
1016 __( 'FAQs', 'learnpress' )
1017 ) : '',
1018 'content' => $html,
1019 'wrapper_end' => '</div>',
1020 ],
1021 $courseModel
1022 );
1023 $html = Template::combine_components( $section );
1024 } catch ( Throwable $e ) {
1025 error_log( $e->getMessage() );
1026 }
1027
1028 return $html;
1029 }
1030
1031 /**
1032 * HTML struct course box extra
1033 *
1034 * @param CourseModel $courseModel
1035 * @param $title
1036 * @param string $html_list
1037 *
1038 * @return string
1039 * @since 4.2.7.2
1040 * @version 1.0.0
1041 */
1042 public function html_course_box_extra( CourseModel $courseModel, $title, string $html_list ): string {
1043 if ( empty( $html_list ) ) {
1044 return '';
1045 }
1046
1047 $section = apply_filters(
1048 'learn-press/course/html-course-box-extra',
1049 [
1050 'wrapper' => '<div class="course-extra-box">',
1051 'title' => sprintf( '<h3 class="course-extra-box__title">%s</h3>', $title ),
1052 'content' => '<div class="course-extra-box__content">',
1053 'content_inner' => '<div class="course-extra-box__content-inner">',
1054 'list' => $html_list,
1055 'content_inner_end' => '</div>',
1056 'content_end' => '</div>',
1057 'wrapper_end' => '</div>',
1058 ],
1059 $courseModel
1060 );
1061
1062 return Template::combine_components( $section );
1063 }
1064
1065 /**
1066 * HTML meta requirements
1067 *
1068 * @param CourseModel $courseModel
1069 *
1070 * @return string
1071 * @since 4.2.7.2
1072 * @version 1.0.0
1073 */
1074 public function html_requirements( CourseModel $courseModel ): string {
1075 $html = '';
1076
1077 try {
1078 $requirements = $courseModel->get_meta_value_by_key( CoursePostModel::META_KEY_REQUIREMENTS, [] );
1079 if ( ! is_array( $requirements ) && ! is_object( $requirements ) ) {
1080 return '';
1081 }
1082 if ( empty( $requirements ) ) {
1083 return '';
1084 }
1085
1086 $html_lis = '';
1087 foreach ( $requirements as $requirement ) {
1088 $html_lis .= sprintf( '<li>%s</li>', $requirement );
1089 }
1090
1091 $section_list = [
1092 'wrapper' => '<ul>',
1093 'content' => $html_lis,
1094 'wrapper_end' => '</ul>',
1095 ];
1096
1097 $section = apply_filters(
1098 'learn-press/course/html-requirements',
1099 [
1100 'wrapper' => '<div class="course-requirements extra-box">',
1101 'title' => sprintf( '<h3 class="extra-box__title">%s</h3>', __( 'Requirements', 'learnpress' ) ),
1102 'content' => Template::combine_components( $section_list ),
1103 'wrapper_end' => '</div>',
1104 ],
1105 $courseModel,
1106 $requirements
1107 );
1108 $html = Template::combine_components( $section );
1109 } catch ( Throwable $e ) {
1110 error_log( $e->getMessage() );
1111 }
1112
1113 return $html;
1114 }
1115
1116 /**
1117 * HTML meta features
1118 *
1119 * @param CourseModel $courseModel
1120 *
1121 * @return string
1122 * @since 4.2.7.2
1123 * @version 1.0.0
1124 */
1125 public function html_features( CourseModel $courseModel ): string {
1126 $html = '';
1127
1128 try {
1129 $features = $courseModel->get_meta_value_by_key( CoursePostModel::META_KEY_FEATURES, [] );
1130 if ( ! is_array( $features ) && ! is_object( $features ) ) {
1131 return '';
1132 }
1133 if ( empty( $features ) ) {
1134 return '';
1135 }
1136
1137 $html_lis = '';
1138 foreach ( $features as $feature ) {
1139 $html_lis .= sprintf( '<li>%s</li>', $feature );
1140 }
1141
1142 $section_list = [
1143 'wrapper' => '<ul>',
1144 'content' => $html_lis,
1145 'wrapper_end' => '</ul>',
1146 ];
1147
1148 $section = apply_filters(
1149 'learn-press/course/html-features',
1150 [
1151 'wrapper' => '<div class="course-features extra-box">',
1152 'title' => sprintf( '<h3 class="extra-box__title">%s</h3>', __( 'Features', 'learnpress' ) ),
1153 'content' => Template::combine_components( $section_list ),
1154 'wrapper_end' => '</div>',
1155 ],
1156 $courseModel,
1157 $features
1158 );
1159 $html = Template::combine_components( $section );
1160 } catch ( Throwable $e ) {
1161 error_log( $e->getMessage() );
1162 }
1163
1164 return $html;
1165 }
1166
1167 /**
1168 * HTML meta target
1169 *
1170 * @param CourseModel $courseModel
1171 *
1172 * @return string
1173 * @since 4.2.7.2
1174 * @version 1.0.0
1175 */
1176 public function html_target( CourseModel $courseModel ): string {
1177 $html = '';
1178
1179 try {
1180 $targets = $courseModel->get_meta_value_by_key( CoursePostModel::META_KEY_TARGET, [] );
1181 if ( ! is_array( $targets ) && ! is_object( $targets ) ) {
1182 return '';
1183 }
1184 if ( empty( $targets ) ) {
1185 return '';
1186 }
1187
1188 $html_lis = '';
1189 foreach ( $targets as $target ) {
1190 $html_lis .= sprintf( '<li>%s</li>', $target );
1191 }
1192
1193 $section_list = [
1194 'wrapper' => '<ul>',
1195 'content' => $html_lis,
1196 'wrapper_end' => '</ul>',
1197 ];
1198
1199 $section = apply_filters(
1200 'learn-press/course/html-target',
1201 [
1202 'wrapper' => '<div class="course-target extra-box">',
1203 'title' => sprintf( '<h3 class="extra-box__title">%s</h3>', __( 'Target audiences', 'learnpress' ) ),
1204 'content' => Template::combine_components( $section_list ),
1205 'wrapper_end' => '</div>',
1206 ],
1207 $courseModel,
1208 $targets
1209 );
1210 $html = Template::combine_components( $section );
1211 } catch ( Throwable $e ) {
1212 error_log( $e->getMessage() );
1213 }
1214
1215 return $html;
1216 }
1217
1218 /**
1219 * HTML material
1220 *
1221 * @param CourseModel $courseModel
1222 * @param UserModel|false $userModel
1223 * @param array $data
1224 *
1225 * @return string
1226 * @since 4.2.7.2
1227 * @version 1.0.4
1228 */
1229 public function html_material( CourseModel $courseModel, $userModel = false, array $data = [] ): string {
1230 $html = '';
1231
1232 try {
1233 $show_heading = $data['show_heading'] ?? true;
1234 $can_show = false;
1235
1236 if ( $userModel instanceof UserModel ) {
1237 if ( $courseModel->check_user_is_author( $userModel )
1238 || user_can( $userModel->get_id(), ADMIN_ROLE ) ) {
1239 $can_show = true;
1240 } else {
1241 $userCourseModel = UserCourseModel::find( $userModel->get_id(), $courseModel->get_id(), true );
1242 if ( $userCourseModel &&
1243 ( $userCourseModel->has_enrolled_or_finished()
1244 || $userCourseModel->has_purchased() ) ) {
1245 $can_show = true;
1246 }
1247 }
1248 } elseif ( $courseModel->has_no_enroll_requirement() ) {
1249 $can_show = true;
1250 }
1251
1252 $can_show = apply_filters( 'learn-press/course-material/can-show', $can_show, $courseModel, $userModel );
1253
1254 $file_per_page = LP_Settings::get_option( 'material_file_per_page', - 1 );
1255 $count_files = LP_Material_Files_DB::getInstance()->get_total( $courseModel->get_id() );
1256 if ( ! $can_show || $file_per_page == 0 || $count_files <= 0 ) {
1257 return $html;
1258 }
1259
1260 ob_start();
1261 do_action( 'learn-press/course-material/layout', [] );
1262 $html_content = ob_get_clean();
1263
1264 $section = [
1265 'wrapper' => '<div class="course-material">',
1266 'title' => $show_heading ? sprintf(
1267 '<h3 class="course-material__title">%s</h3>',
1268 __( 'Course Material', 'learnpress' )
1269 ) : '',
1270 'content' => $html_content,
1271 'wrapper_end' => '</div>',
1272 ];
1273
1274 $html = Template::combine_components( $section );
1275 } catch ( Throwable $e ) {
1276 error_log( __METHOD__ . ': ' . $e->getMessage() );
1277 }
1278
1279 return $html;
1280 }
1281
1282 /**
1283 * Get html level course.
1284 *
1285 * @param LP_Course|CourseModel $course
1286 *
1287 * @return string
1288 * @since 4.2.7.5
1289 * @version 1.0.1
1290 */
1291 public function html_price_prefix( $course ): string {
1292 $html = '';
1293
1294 try {
1295 if ( $course instanceof LP_Course ) {
1296 $course = CourseModel::find( $course->get_id(), true );
1297 if ( ! $course instanceof CourseModel ) {
1298 return $html;
1299 }
1300 }
1301
1302 $price_prefix_str = $course->get_meta_value_by_key( CoursePostModel::META_KEY_PRICE_PREFIX, '' );
1303 if ( empty( $price_prefix_str ) ) {
1304 return $html;
1305 }
1306
1307 $html = sprintf( '<span class="course-price-prefix">%s</span>', $price_prefix_str );
1308 } catch ( Throwable $e ) {
1309 error_log( __METHOD__ . ': ' . $e->getMessage() );
1310 }
1311
1312 return $html;
1313 }
1314
1315 /**
1316 * Get html level course.
1317 *
1318 * @param LP_Course|CourseModel $course
1319 *
1320 * @return string
1321 * @since 4.2.7.5
1322 * @version 1.0.0
1323 */
1324 public function html_price_suffix( $course ): string {
1325 $html = '';
1326
1327 try {
1328 $price_suffix_str = $course->get_meta_value_by_key( CoursePostModel::META_KEY_PRICE_SUFFIX, '' );
1329 if ( empty( $price_suffix_str ) ) {
1330 return $html;
1331 }
1332
1333 $html = sprintf( '<span class="course-price-suffix">%s</span>', $price_suffix_str );
1334 } catch ( Throwable $e ) {
1335 error_log( __METHOD__ . ': ' . $e->getMessage() );
1336 }
1337
1338 return $html;
1339 }
1340
1341 /**
1342 * Get html featured course.
1343 *
1344 * @param CourseModel $courseModel
1345 *
1346 * @return string
1347 * @since 4.2.9
1348 * @version 1.0.0
1349 */
1350 public function html_featured( CourseModel $courseModel ): string {
1351 $html = '';
1352
1353 try {
1354 $is_featured = $courseModel->get_meta_value_by_key( CoursePostModel::META_KEY_FEATURED, 'no' );
1355 if ( $is_featured !== 'yes' ) {
1356 return $html;
1357 }
1358
1359 $html = sprintf( '<span class="course-featured">%s</span>', __( 'Featured', 'learnpress' ) );
1360 } catch ( Throwable $e ) {
1361 LP_Debug::error_log( $e );
1362 }
1363
1364 return $html;
1365 }
1366
1367 /**
1368 * GET HTML comment default of WP
1369 *
1370 * @param CourseModel $courseModel
1371 * @param false|UserModel $userModel
1372 *
1373 * @return string
1374 * @since 4.2.7.6
1375 * @version 1.0.0
1376 */
1377 public function html_comment( CourseModel $courseModel, $userModel = false ): string {
1378 $args = [
1379 'id_url' => 'course-comments',
1380 'course_id' => $courseModel->get_id(),
1381 'user_id' => $userModel instanceof UserModel ? $userModel->get_id() : 0,
1382 ];
1383 $callBack = [
1384 'class' => __CLASS__,
1385 'method' => 'render_html_comments',
1386 ];
1387 $html = TemplateAJAX::load_content_via_ajax( $args, $callBack );
1388
1389 $section_comment = apply_filters(
1390 'learn-press/single-course/html-comment',
1391 [
1392 'wrapper' => '<div class="lp-course-comment">',
1393 'content' => $html,
1394 'wrapper_end' => '</div>',
1395 ],
1396 $courseModel,
1397 $userModel
1398 );
1399
1400 return Template::combine_components( $section_comment );
1401 }
1402
1403 /**
1404 * Render HTML comments of course.
1405 *
1406 * @param array $data ['course_id']
1407 *
1408 * @return stdClass
1409 * @since 4.2.7.6
1410 * @version 1.0.0
1411 */
1412 public static function render_html_comments( array $data ): stdClass {
1413 $response = new stdClass();
1414 $response->content = '';
1415
1416 try {
1417 $course_id = $data['course_id'] ?? 0;
1418 $user_id = $data['user_id'] ?? 0;
1419 if ( empty( $course_id ) ) {
1420 return $response;
1421 }
1422
1423 global $withcomments, $post;
1424
1425 $post = get_post( $course_id );
1426 if ( $post->comment_status !== 'open' ) {
1427 return $response;
1428 } else {
1429 $withcomments = true;
1430 }
1431
1432 $withcomments = apply_filters(
1433 'learn-press/single-course/render-html-comment/is-show',
1434 $withcomments,
1435 $post,
1436 $user_id
1437 );
1438
1439 //$post->comment_count = 0;
1440
1441 ob_start();
1442 add_filter( 'deprecated_file_trigger_error', '__return_false' );
1443 comments_template();
1444 remove_filter( 'deprecated_file_trigger_error', '__return_false' );
1445 $html = ob_get_clean();
1446 $response->content = $html;
1447 } catch ( Throwable $e ) {
1448 $response->content = Template::print_message( $e->getMessage(), 'error', false );
1449 }
1450
1451 return $response;
1452 }
1453
1454 /**
1455 * Get HTML curriculum of course.
1456 *
1457 * @param CourseModel $courseModel
1458 * @param UserModel|false $userModel
1459 * @param LessonPostModel|QuizPostModel|PostModel|mixed $itemModelCurrent
1460 *
1461 * @return string
1462 * @since 4.2.7.6
1463 * @version 1.0.3
1464 */
1465 public function html_curriculum( CourseModel $courseModel, $userModel, $itemModelCurrent = false ): string {
1466 $html = '';
1467
1468 try {
1469 // Get current item viewing
1470 /**
1471 * @var $lp_course_item LP_Course_Item
1472 * @var $post \WP_Post
1473 */
1474 global $lp_course_item, $post;
1475 /**
1476 * @var $itemModelCurrent LessonPostModel|QuizPostModel|AssignmentPostModel|PostModel...
1477 */
1478 $itemModelCurrent = $courseModel->get_item_model( $post->ID, $post->post_type );
1479 if ( $lp_course_item ) {
1480 $itemModelCurrent = $courseModel->get_item_model( $lp_course_item->get_id(), $lp_course_item->get_item_type() );
1481 }
1482
1483 if ( $itemModelCurrent ) {
1484 $item_types = CourseModel::item_types_support();
1485 // Check item type is support
1486 if ( in_array( $itemModelCurrent->post_type, $item_types ) ) {
1487 $this->currentItemModel = $itemModelCurrent;
1488 }
1489 }
1490 // End get current item viewing
1491
1492 $section_items = $courseModel->get_section_items();
1493 $html = Template::print_message(
1494 esc_html__( 'There are no items in the curriculum yet.', 'learnpress' ),
1495 'info',
1496 false
1497 );
1498 if ( empty( $section_items ) ) {
1499 return $html;
1500 }
1501
1502 wp_enqueue_script( 'lp-curriculum' );
1503 $li_section_items = '';
1504 foreach ( $section_items as $section_item ) {
1505 $li_section_items .= $this->render_html_section_item( $courseModel, $userModel, $section_item );
1506 }
1507
1508 $section = apply_filters(
1509 'learn-press/course/html-curriculum',
1510 [
1511 'wrapper' => '<div class="lp-course-curriculum">',
1512 'title' => sprintf(
1513 '<h3 class="lp-course-curriculum__title">%s</h3>',
1514 esc_html__( 'Curriculum', 'learnpress' )
1515 ),
1516 'curriculum_info' => '<div class="course-curriculum-info">',
1517 'curriculum_info_left' => '<ul class="course-curriculum-info__left">',
1518 'count_sections' => sprintf(
1519 '<li class="course-count-section">%s</li>',
1520 sprintf(
1521 _n( '%d Section', '%d Sections', $courseModel->get_total_sections(), 'learnpress' ),
1522 $courseModel->get_total_sections()
1523 )
1524 ),
1525 'count_lesson' => sprintf(
1526 '<li class="course-count-lesson">%s</li>',
1527 sprintf(
1528 _n( '%d Lesson', '%d Lessons', $courseModel->count_items( LP_LESSON_CPT ), 'learnpress' ),
1529 $courseModel->count_items( LP_LESSON_CPT )
1530 )
1531 ),
1532 'duration' => sprintf(
1533 '<li class="course-duration">%s</li>',
1534 $this->html_duration( $courseModel )
1535 ),
1536 'curriculum_info_left_end' => '</ul>',
1537 'curriculum_info_right' => '<div class="course-curriculum-info__right">',
1538 'expand_all' => sprintf(
1539 '<span class="course-toggle-all-sections">%s</span>',
1540 esc_html__( 'Expand all sections', 'learnpress' )
1541 ),
1542 'collapse_all' => sprintf(
1543 '<span class="course-toggle-all-sections lp-collapse lp-hidden">%s</span>',
1544 esc_html__( 'Collapse all sections', 'learnpress' )
1545 ),
1546 'curriculum_info_right_end' => '</div>',
1547 'curriculum_info_end' => '</div>',
1548 'curriculum' => '<div class="course-curriculum">',
1549 'sections' => '<ul class="course-sections">',
1550 'li_section_items' => $li_section_items,
1551 'sections_end' => '</ul>',
1552 'curriculum_end' => '</div>',
1553 'wrapper_end' => '</div>',
1554 ],
1555 $courseModel,
1556 $userModel,
1557 $itemModelCurrent
1558 );
1559
1560 $html = Template::combine_components( $section );
1561 } catch ( Throwable $e ) {
1562 error_log( __METHOD__ . ': ' . $e->getMessage() );
1563 }
1564
1565 return $html;
1566 }
1567
1568 /**
1569 * Render HTML section item
1570 *
1571 * @param CourseModel $courseModel
1572 * @param UserModel|false $userModel
1573 * @param $section_item object {section_id, section_name, section_description, items[{item_id, item_order, item_type, title, preview}]}
1574 *
1575 * @return string
1576 * @since 4.2.7.6
1577 * @version 1.0.1
1578 */
1579 public function render_html_section_item( CourseModel $courseModel, $userModel, $section_item ): string {
1580 if ( ! $section_item instanceof stdClass ) {
1581 return '';
1582 }
1583
1584 $section_id = $section_item->section_id ?? 0;
1585 $section_name = $section_item->section_name ?? '';
1586 $section_description = $section_item->section_description ?? '';
1587 $section_order = $section_item->section_order ?? 1;
1588 $items = $section_item->items ?? [];
1589 $html_section_description = '';
1590 if ( ! empty( $section_description ) ) {
1591 $html_section_description = sprintf(
1592 '<div class="course-section__description">%s</div>',
1593 wp_kses_post( $section_description )
1594 );
1595 }
1596
1597 $section_header = [
1598 'start' => '<div class="course-section-header">',
1599 'toggle' => '<div class="section-toggle">
1600 <i class="lp-icon-angle-down"></i>
1601 <i class="lp-icon-angle-up"></i>
1602 </div>',
1603 'info' => '<div class="course-section-info">',
1604 'title' => sprintf( '<div class="course-section__title">%s</div>', wp_kses_post( $section_name ) ),
1605 'description' => $html_section_description,
1606 'info_end' => '</div>',
1607 'count_items' => sprintf(
1608 '<div class="section-count-items">%d</div>',
1609 count( $items )
1610 ),
1611 'end' => '</div>',
1612 ];
1613
1614 $li_items = '';
1615 foreach ( $items as $item ) {
1616 $li_items .= $this->render_html_course_item( $courseModel, $userModel, $item, $section_item );
1617 }
1618 $section_items = [
1619 'start' => '<ul class="course-section__items">',
1620 'li_items' => $li_items,
1621 'end' => '</ul>',
1622 ];
1623
1624 $curriculum_display_setting = LP_Settings::get_option( 'curriculum_display', 'expand_first_section' );
1625 $class_section_toggle = '';
1626
1627 if ( $this->currentItemModel ) {
1628 $current_section = $courseModel->get_section_of_item( $this->currentItemModel->get_id() );
1629 if ( $current_section != $section_id ) {
1630 $class_section_toggle = 'lp-collapse';
1631 }
1632 } else {
1633 if ( $curriculum_display_setting === 'collapse_all' ) {
1634 $class_section_toggle = 'lp-collapse';
1635 } elseif ( $curriculum_display_setting === 'expand_first_section' ) {
1636 if ( $section_order > 1 ) {
1637 $class_section_toggle = 'lp-collapse';
1638 }
1639 }
1640 }
1641
1642 $class_section_toggle = apply_filters(
1643 'learn-press/course/html-section-item/class-section-toggle',
1644 $class_section_toggle,
1645 $courseModel,
1646 $userModel,
1647 $section_item
1648 );
1649
1650 $section_item = apply_filters(
1651 'learn-press/course/html-section-item',
1652 [
1653 'start' => sprintf(
1654 '<li class="course-section %s" data-section-id="%s">',
1655 $class_section_toggle,
1656 $section_id
1657 ),
1658 'header' => Template::combine_components( $section_header ),
1659 'items' => Template::combine_components( $section_items ),
1660 'end' => '</li>',
1661 ],
1662 $courseModel,
1663 $userModel,
1664 $section_item
1665 );
1666
1667 return Template::combine_components( $section_item );
1668 }
1669
1670 /**
1671 * @param CourseModel $courseModel
1672 * @param UserModel|false $userModel
1673 * @param $item stdClass {item_id, item_order, item_type, title, preview}
1674 * @param $section_item stdClass {section_id, section_name, section_description, items[{item_id, item_order, item_type, title, preview}]}
1675 *
1676 * @return string
1677 * @since 4.2.7.6
1678 * @version 1.0.2
1679 */
1680 public function render_html_course_item( CourseModel $courseModel, $userModel, $item, $section_item ): string {
1681 $html = '';
1682
1683 if ( ! $item instanceof stdClass ) {
1684 return $html;
1685 }
1686
1687 $item_id = (int) ( $item->item_id ?? $item->id ?? 0 );
1688 $item_order = $item->item_order ?? $item->order ?? 0;
1689 $item_type = $item->item_type ?? $item->type ?? '';
1690 $title = $item->title ?? '';
1691 $has_preview = $item->preview ?? '';
1692 $class_current = '';
1693 if ( $this->currentItemModel ) {
1694 $current_item_id = $this->currentItemModel->get_id();
1695 if ( $current_item_id == $item_id ) {
1696 $class_current = 'current';
1697 }
1698 }
1699
1700 //LP_Course_Item::get_item( $item_id, $course->get_id() );
1701 $itemModel = $courseModel->get_item_model( $item_id, $item_type );
1702 if ( empty( $itemModel ) ) {
1703 return $html;
1704 }
1705
1706 $link_item = $courseModel->get_item_link( $item_id, $item_type );
1707
1708 $item_duration = '';
1709 $html_item_duration = '';
1710 if ( is_callable( [ $itemModel, 'get_duration' ] ) ) {
1711 $item_duration = $itemModel->get_duration();
1712 } else {
1713 $item_duration = get_post_meta( $item_id, '_lp_duration', true );
1714 }
1715
1716 $duration_arr = explode( ' ', $item_duration );
1717 $duration_number = floatval( $duration_arr[0] ?? 0 );
1718 $duration_type = $duration_arr[1] ?? '';
1719
1720 if ( $duration_number > 0 ) {
1721 $item_duration_plural = LP_Datetime::get_string_plural_duration( $duration_number, $duration_type );
1722
1723 $html_item_duration = sprintf(
1724 '<span class="duration">%s</span>',
1725 $item_duration_plural
1726 );
1727 }
1728
1729 // Count question of quiz
1730 $html_item_count_questions = '';
1731 if ( $item_type === LP_QUIZ_CPT ) {
1732 $quizPostModel = QuizPostModel::find( $item_id, true );
1733 if ( $quizPostModel instanceof QuizPostModel ) {
1734 $question_count = $quizPostModel->count_questions();
1735 $html_item_duration .= sprintf(
1736 '<span class="question-count">%s</span>',
1737 sprintf(
1738 _n( '%d Question', '%d Questions', $question_count, 'learnpress' ),
1739 $question_count
1740 )
1741 );
1742 }
1743 }
1744
1745 $user_item_status_ico_flag = 'locked';
1746 $user_attended_course = false;
1747 if ( $userModel instanceof UserModel ) {
1748 $userCourseModel = UserCourseModel::find( $userModel->get_id(), $courseModel->get_id(), true );
1749 if ( $userCourseModel
1750 && $userCourseModel->get_status() !== UserItemModel::STATUS_CANCEL
1751 && $userCourseModel->get_status() !== UserCourseModel::STATUS_PURCHASED ) {
1752 $user_attended_course = true;
1753
1754 // Check status of item's course
1755 $userCourseItem = $userCourseModel->get_item_attend( $item_id, $item_type );
1756 if ( ! $userCourseItem instanceof UserItemModel ) {
1757 $user_item_status_ico_flag = UserItemModel::GRADUATION_IN_PROGRESS;
1758 } else {
1759 $user_item_status_ico_flag = $userCourseItem->get_status();
1760 $user_item_graduation = $userCourseItem->get_graduation();
1761 if ( ! empty( $user_item_graduation ) ) {
1762 $user_item_status_ico_flag = $user_item_graduation . ' completed';
1763 }
1764
1765 if ( empty( $user_item_status_ico_flag ) ) {
1766 $user_item_status_ico_flag = UserItemModel::GRADUATION_IN_PROGRESS;
1767 }
1768 }
1769
1770 if ( $user_item_status_ico_flag === UserItemModel::GRADUATION_IN_PROGRESS ) {
1771 if ( $userCourseModel->get_time_remaining() === 0 ) {
1772 $user_item_status_ico_flag = 'locked';
1773 } elseif ( $userCourseModel->get_status() === UserItemModel::STATUS_FINISHED
1774 && $courseModel->enable_block_when_finished() ) {
1775 $user_item_status_ico_flag = 'locked';
1776 }
1777 }
1778 }
1779 }
1780
1781 if ( $has_preview && ! $user_attended_course ) {
1782 $user_item_status_ico_flag = 'preview';
1783 }
1784
1785 if ( $courseModel->has_no_enroll_requirement()
1786 && ! $user_attended_course && ! $userModel ) {
1787 $user_item_status_ico_flag = UserItemModel::GRADUATION_IN_PROGRESS;
1788 }
1789
1790 $user_item_status_ico_flag = apply_filters(
1791 'learn-press/course/item/status-ico-flag',
1792 $user_item_status_ico_flag,
1793 $courseModel,
1794 $userModel,
1795 $item,
1796 $section_item
1797 );
1798
1799 $html_item_status = sprintf(
1800 '<div class="course-item__status"><span class="course-item-ico %1$s"></span></div>',
1801 $user_item_status_ico_flag
1802 );
1803
1804 $html_item_right = [];
1805 if ( $html_item_count_questions != '' || $html_item_duration != '' ) {
1806 $html_item_right = [
1807 'item_right' => '<div class="course-item__right">',
1808 'question_count' => $html_item_count_questions,
1809 'duration' => $html_item_duration,
1810 'item_right_end' => '</div>',
1811 ];
1812 }
1813
1814 $section_item = apply_filters(
1815 'learn-press/course/html-course-item',
1816 [
1817 'start' => sprintf(
1818 '<li class="course-item %s" data-item-id="%s" data-item-order="%s" data-item-type="%s">',
1819 $class_current,
1820 $item_id,
1821 $item_order,
1822 $item_type
1823 ),
1824 'link' => sprintf(
1825 '<a href="%s" class="course-item__link">',
1826 esc_url_raw( $link_item )
1827 ),
1828 'item_info' => '<div class="course-item__info">',
1829 'icon' => sprintf(
1830 '<span class="course-item-ico %s"></span>',
1831 esc_attr( $item_type )
1832 ),
1833 'item_order' => sprintf(
1834 '<span class="course-item-order lp-hidden">%s.%s</span>',
1835 $section_item->section_order,
1836 $item_order
1837 ),
1838 'item_info_end' => '</div>',
1839 'item_content' => '<div class="course-item__content">',
1840 'item_left' => '<div class="course-item__left">',
1841 'title' => sprintf( '<div class="course-item-title">%s</div>', wp_kses_post( $title ) ),
1842 'item_left_end' => '</div>',
1843 'item_right' => Template::combine_components( $html_item_right ),
1844 'item_content_end' => '</div>',
1845 'status' => $html_item_status,
1846 'link_end' => '</a>',
1847 'end' => '</li>',
1848 ],
1849 $courseModel,
1850 $userModel,
1851 $item,
1852 $section_item
1853 );
1854
1855 return Template::combine_components( $section_item );
1856 }
1857
1858 /**
1859 * Render string to data content
1860 *
1861 * @param CourseModel $course
1862 * @param string $data_content
1863 *
1864 * @return string
1865 */
1866 public function render_data( CourseModel $course, string $data_content = '' ): string {
1867 $author_of_course = $course->get_author_model();
1868 $singleInstructorTemplate = SingleInstructorTemplate::instance();
1869
1870 // render count items
1871 $pattern_count_items = '/{{course_count_.*?}}/';
1872 preg_match_all( $pattern_count_items, $data_content, $matches_count_items );
1873 if ( ! empty( $matches_count_items ) ) {
1874 $items = $matches_count_items[0];
1875 foreach ( $items as $item ) {
1876 $method = str_replace( [ '{{', '}}' ], '', $item );
1877 $post_type_item = str_replace( 'course_count_', '', $method );
1878 $data_content = str_replace( $item, $this->html_count_item( $course, $post_type_item ), $data_content );
1879 }
1880 }
1881
1882 return str_replace(
1883 [
1884 '{{course_id}}',
1885 '{{course_title}}',
1886 '{{course_image}}',
1887 '{{course_url}}',
1888 '{{course_short_description}}',
1889 '{{course_price}}',
1890 '{{course_categories}}',
1891 '{{course_count_student}}',
1892 '{{course_author_display_name}}',
1893 '{{course_author_url}}',
1894 '{{course_author_avatar}}',
1895 ],
1896 [
1897 $course->get_id(),
1898 $this->html_title( $course ),
1899 $this->html_image( $course ),
1900 $course->get_permalink(),
1901 $this->html_short_description( $course ),
1902 $this->html_price( $course ),
1903 $this->html_categories( $course ),
1904 $this->html_count_student( $course ),
1905 $singleInstructorTemplate->html_display_name( $author_of_course ),
1906 $author_of_course->get_url_instructor(),
1907 $singleInstructorTemplate->html_avatar( $author_of_course ),
1908 ],
1909 $data_content
1910 );
1911 }
1912
1913 /**
1914 * Get text button read more of course on list courses.
1915 *
1916 * @param CourseModel $courseModel
1917 *
1918 * @return string
1919 * @since 4.3.4
1920 * @version 1.0.0
1921 */
1922 public static function text_button_course( CourseModel $courseModel ): string {
1923 $btn_text = __( 'Enroll Now', 'learnpress' );
1924 $userModel = UserModel::find( get_current_user_id(), true );
1925
1926 $userCourseModel = null;
1927 if ( $userModel instanceof UserModel ) {
1928 $userCourseModel = UserCourseModel::find( $userModel->get_id(), $courseModel->get_id(), true );
1929 }
1930
1931 if ( $userCourseModel instanceof UserCourseModel ) {
1932 if ( $userCourseModel->has_enrolled() ) {
1933 $btn_text = __( 'Continue Learning', 'learnpress' );
1934 } elseif ( $userCourseModel->has_purchased() ) {
1935 $btn_text = __( 'Start Learning', 'learnpress' );
1936 } else {
1937 $btn_text = __( 'View Detail', 'learnpress' );
1938 }
1939 } else {
1940 if ( $courseModel->has_no_enroll_requirement() ) {
1941 $btn_text = __( 'Start Learning', 'learnpress' );
1942 } elseif ( ! $courseModel->is_free() ) {
1943 $btn_text = __( 'Buy Now', 'learnpress' );
1944 } elseif ( ! empty( $courseModel->get_external_link() ) ) {
1945 $btn_text = __( 'View Detail', 'learnpress' );
1946 }
1947 }
1948
1949 return $btn_text;
1950 }
1951 }
1952