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
← All changes | inc/TemplateHooks/CourseBuilder/Course/BuilderEditCourseTemplate.php +136 -38 4.3.74.4.8 View file →
@@ -66,11 +66,11 @@
66 66 if ( empty( $item_id ) ) {
67 67 throw new Exception( __( 'Invalid course ID', 'learnpress' ) );
68 68 }
69 69
70 - if ( ! CourseBuilderAccessPolicy::can_access_tab_post( 'courses', $item_id ) ) {
70 + /*if ( ! CourseBuilderAccessPolicy::can_access_tab_post( 'courses', $item_id ) ) {
71 71 throw new Exception( __( "Sorry, you don't have permission to access this content", 'learnpress' ) );
72 - }
72 + }*/
73 73
74 74 $is_create_new = $item_id === CourseBuilder::POST_NEW;
75 75 $courseModel = false;
76 76
@@ -79,8 +79,14 @@
79 79 if ( ! $courseModel ) {
80 80 throw new Exception( __( 'Course not found', 'learnpress' ) );
81 81 }
82 82
83 + // Check permission
84 + $coursePostModel = $courseModel->get_post_model();
85 + if ( ! $coursePostModel->check_capabilities_update() ) {
86 + throw new Exception( __( 'You do not have permission to edit this course', 'learnpress' ) );
87 + }
88 +
83 89 if ( $courseModel->get_status() === PostModel::STATUS_TRASH ) {
84 90 throw new Exception(
85 91 __(
86 92 'You cannot edit this item because it is in the Trash. Please restore it and try again.',
@@ -301,21 +307,21 @@
301 307
302 308 public function html_tab_overview( array $data = [] ) {
303 309 wp_enqueue_script( 'lp-course-builder' );
304 310
305 - $courseMoel = $data['courseModel'] ?? null;
306 - $course_id = 0;
307 - if ( $courseMoel instanceof CourseModel ) {
308 - $course_id = $courseMoel->get_id();
311 + $courseModel = $data['courseModel'] ?? null;
312 + $course_id = 0;
313 + if ( $courseModel instanceof CourseModel ) {
314 + $course_id = $courseModel->get_id();
309 315 }
310 316
311 - $html_edit_title = $this->edit_title( $courseMoel );
312 - $html_edit_permalink = $this->edit_permalink( $courseMoel );
313 - $html_edit_features = $this->edit_featured_image( $courseMoel );
314 - $html_edit_publish = $this->edit_publish( $courseMoel );
315 - $html_edit_desc = $this->edit_desc( $courseMoel );
316 - $html_edit_cat = $this->edit_categories( $courseMoel );
317 - $html_edit_tags = $this->edit_tags( $courseMoel );
317 + $html_edit_title = $this->edit_title( $courseModel );
318 + $html_edit_permalink = $this->edit_permalink( $courseModel );
319 + $html_edit_features = $this->edit_featured_image( $courseModel );
320 + $html_edit_publish = $this->edit_publish( $courseModel );
321 + $html_edit_desc = $this->edit_desc( $courseModel );
322 + $html_edit_cat = $this->edit_categories( $courseModel );
323 + $html_edit_tags = $this->edit_tags( $courseModel );
318 324
319 325 $section = [
320 326 'wrapper' => sprintf( '<div class="cb-section__course-edit" data-course-id="%s">', $course_id ),
321 327 'content_wrapper' => '<div class="cb-course-edit-content">',
@@ -440,12 +446,25 @@
440 446
441 447 return $callbacks;
442 448 }
443 449
444 - public function edit_title( $course_model ) {
450 + /**
451 + * Render Course Builder course title field.
452 + *
453 + * Uses the raw post title for existing courses so the edit field does not receive
454 + * display filters. New courses start with an empty value and still render the
455 + * optional AI button when AI settings are enabled.
456 + *
457 + * @param CourseModel|false|null $courseModel Course model being edited, or empty when creating a new course.
458 + *
459 + * @return string HTML markup for the title input, character count, and optional AI button.
460 + * @since 4.3.0
461 + * @version 1.0.0
462 + */
463 + public function edit_title( $courseModel ) {
445 464 $title = '';
446 - if ( ! empty( $course_model ) ) {
447 - $post_id = absint( $course_model->get_id() );
465 + if ( ! empty( $courseModel ) ) {
466 + $post_id = absint( $courseModel->get_id() );
448 467 $title = $post_id ? (string) get_post_field( 'post_title', $post_id, 'raw' ) : '';
449 468 }
450 469 $char_count = mb_strlen( wp_strip_all_tags( $title ) );
451 470 $ai_button = $this->html_overview_ai_button( '#lp-tmpl-edit-title-ai' );
@@ -462,10 +481,23 @@
462 481
463 482 return Template::combine_components( $edit );
464 483 }
465 484
466 - public function edit_permalink( $course_model ) {
467 - $post_id = ! empty( $course_model ) ? $course_model->get_id() : '';
485 + /**
486 + * Render Course Builder permalink editor.
487 + *
488 + * Permalink editing is hidden for unsaved courses. Existing courses use
489 + * get_sample_permalink() when available to display a publish-style URL for slug
490 + * editing, while the preview link keeps the current permalink as its href.
491 + *
492 + * @param CourseModel|false|null $courseModel Course model being edited, or empty when creating a new course.
493 + *
494 + * @return string HTML markup for the permalink display/editor, or an empty string for new courses.
495 + * @since 4.3.0
496 + * @version 1.0.0
497 + */
498 + public function edit_permalink( $courseModel ) {
499 + $post_id = ! empty( $courseModel ) ? $courseModel->get_id() : '';
468 500 $post_name = '';
469 501
470 502 // Hide permalink for new courses
471 503 if ( empty( $post_id ) || $post_id === CourseBuilder::POST_NEW ) {
@@ -607,11 +639,22 @@
607 639 'slug' => urldecode( (string) $sample_slug ),
608 640 ];
609 641 }
610 642
611 - public function edit_desc( $course_model ) {
612 - $desc = ! empty( $course_model ) ? $course_model->get_description() : '';
613 - $word_count = str_word_count( wp_strip_all_tags( $desc ) );
643 + /**
644 + * Render Course Builder description editor.
645 + *
646 + * Uses the current course post content when editing an existing CourseModel.
647 + * For new courses, the editor starts with an empty value. AI button rendering
648 + * is delegated to html_overview_ai_button().
649 + *
650 + * @param CourseModel|false|null $courseModel Course model being edited, or empty when creating a new course.
651 + *
652 + * @return string HTML markup for the course description editor.
653 + */
654 + public function edit_desc( $courseModel ) {
655 + $desc = $courseModel ? $courseModel->post_content : '';
656 +
614 657 $editor_id = 'course_description_editor';
615 658 $ai_button = $this->html_overview_ai_button( '#lp-tmpl-edit-description-ai' );
616 659 $editor_settings = array(
617 660 'textarea_name' => 'course_description',
@@ -628,9 +671,12 @@
628 671
629 672 $edit = [
630 673 'wrapper' => '<div class="cb-course-edit-desc">',
631 674 'label_wrap' => '<div class="cb-course-edit-desc__label-wrap">',
632 - 'label' => sprintf( '<label for="course_description" class="cb-course-edit-desc__label">%s</label>', __( 'Description', 'learnpress' ) ),
675 + 'label' => sprintf(
676 + '<label for="course_description" class="cb-course-edit-desc__label">%s</label>',
677 + __( 'Description', 'learnpress' )
678 + ),
633 679 'ai_button' => $ai_button,
634 680 'label_wrap_end' => '</div>',
635 681 'edit' => AdminTemplate::editor_tinymce(
636 682 $desc,
@@ -711,9 +757,22 @@
711 757
712 758 return '';
713 759 }
714 760
715 - public function edit_categories( $course_model ) {
761 + /**
762 + * Render Course Builder course category selector.
763 + *
764 + * Reuses the WordPress post_categories_meta_box() output for course_category,
765 + * then adds Course Builder search and "add new category" controls. During render,
766 + * checked_ontop is forced off so the checklist keeps taxonomy order.
767 + *
768 + * @param CourseModel|false|null $courseModel Course model being edited, or empty when creating a new course.
769 + *
770 + * @return string HTML markup for the course category checklist, search toolbar, and add-new form.
771 + * @since 4.3.0
772 + * @version 1.0.0
773 + */
774 + public function edit_categories( $courseModel ) {
716 775 if ( ! function_exists( 'post_categories_meta_box' ) ) {
717 776 require_once ABSPATH . 'wp-admin/includes/meta-boxes.php';
718 777 }
719 778 if ( ! function_exists( 'wp_popular_terms_checklist' ) ) {
@@ -719,9 +778,9 @@
719 778 if ( ! function_exists( 'wp_popular_terms_checklist' ) ) {
720 779 require_once ABSPATH . 'wp-admin/includes/template.php';
721 780 }
722 781
723 - $post_id = ! empty( $course_model ) ? $course_model->get_id() : get_the_ID();
782 + $post_id = ! empty( $courseModel ) ? $courseModel->get_id() : get_the_ID();
724 783 $post = get_post( $post_id );
725 784
726 785 $force_checked_ontop_false = function ( $args ) {
727 786 if ( isset( $args['taxonomy'] ) && 'course_category' === $args['taxonomy'] ) {
@@ -815,10 +874,23 @@
815 874
816 875 return Template::combine_components( $edit );
817 876 }
818 877
819 - public function edit_tags( $course_model ) {
820 - $course_terms = ! empty( $course_model ) ? $course_model->get_tags() : [];
878 + /**
879 + * Render Course Builder course tag selector.
880 + *
881 + * Builds tag chips from all course tags, placing selected tags before available
882 + * tags so the current course state is visible first. The returned markup includes
883 + * search, empty-state text, and add-new controls consumed by the builder runtime.
884 + *
885 + * @param CourseModel|false|null $courseModel Course model being edited, or empty when creating a new course.
886 + *
887 + * @return string HTML markup for the selected/available tag chips, search toolbar, and add-new form.
888 + * @since 4.3.0
889 + * @version 1.0.0
890 + */
891 + public function edit_tags( $courseModel ) {
892 + $course_terms = ! empty( $courseModel ) ? $courseModel->get_tags() : [];
821 893 $tags = get_terms(
822 894 [
823 895 'taxonomy' => LP_COURSE_TAXONOMY_TAG,
824 896 'hide_empty' => false,
@@ -938,10 +1010,23 @@
938 1010
939 1011 return $html;
940 1012 }
941 1013
942 - public function edit_featured_image( $course_model ) {
943 - $post_id = ! empty( $course_model ) ? $course_model->get_id() : '';
1014 + /**
1015 + * Render Course Builder featured image uploader.
1016 + *
1017 + * Existing courses render the current thumbnail preview with replace/remove
1018 + * controls. Courses without a thumbnail render the upload dropzone and hidden
1019 + * course_thumbnail_id field used by the builder save flow.
1020 + *
1021 + * @param CourseModel|false|null $courseModel Course model being edited, or empty when creating a new course.
1022 + *
1023 + * @return string HTML markup for the featured image dropzone, thumbnail hidden field, and optional AI button.
1024 + * @since 4.3.0
1025 + * @version 1.0.0
1026 + */
1027 + public function edit_featured_image( $courseModel ) {
1028 + $post_id = ! empty( $courseModel ) ? $courseModel->get_id() : '';
944 1029 $ai_button = $this->html_overview_ai_button( '#lp-tmpl-edit-image-ai' );
945 1030
946 1031 $thumbnail_id = ! empty( $post_id ) ? get_post_thumbnail_id( $post_id ) : '';
947 1032 $thumbnail_url = '';
@@ -1027,14 +1112,14 @@
1027 1112
1028 1113 /**
1029 1114 * Render publish panel (status, visibility, publish date, danger zone) for Course Builder overview.
1030 1115 *
1031 - * @param CourseModel|string $course_model
1116 + * @param CourseModel|false|string|null $courseModel
1032 1117 *
1033 1118 * @return string
1034 1119 */
1035 - public function edit_publish( $course_model ): string {
1036 - $post_id = ! empty( $course_model ) ? absint( $course_model->get_id() ) : 0;
1120 + public function edit_publish( $courseModel ): string {
1121 + $post_id = ! empty( $courseModel ) ? absint( $courseModel->get_id() ) : 0;
1037 1122 $post = $post_id ? get_post( $post_id ) : null;
1038 1123
1039 1124 $current_status = 'draft';
1040 1125 if ( $post && ! empty( $post->post_status ) ) {
@@ -1142,22 +1227,35 @@
1142 1227
1143 1228 return Template::combine_components( $edit );
1144 1229 }
1145 1230
1146 - protected function html_curriculum( CourseModel $course_model ): string {
1231 + /**
1232 + * Render Course Builder curriculum editor.
1233 + *
1234 + * Delegates the main curriculum layout to AdminEditCurriculumTemplate with the
1235 + * is_course_builder context flag, then appends popup templates and optional AI
1236 + * curriculum templates needed by the builder UI.
1237 + *
1238 + * @param CourseModel $courseModel Course model used to render curriculum sections and popup template IDs.
1239 + *
1240 + * @return string HTML markup for the curriculum editor plus Course Builder popup and AI templates.
1241 + * @since 4.3.0
1242 + * @version 1.0.0
1243 + */
1244 + protected function html_curriculum( CourseModel $courseModel ): string {
1147 1245 ob_start();
1148 1246 AdminEditCurriculumTemplate::instance()->edit_course_curriculum_layout(
1149 - $course_model,
1247 + $courseModel,
1150 1248 [ 'is_course_builder' => true ]
1151 1249 );
1152 1250 $html_curriculum = ob_get_clean();
1153 1251
1154 1252 return $html_curriculum
1155 - . $this->html_curriculum_popup_templates( $course_model )
1253 + . $this->html_curriculum_popup_templates( $courseModel )
1156 1254 . $this->html_curriculum_ai_templates();
1157 1255 }
1158 1256
1159 - protected function html_curriculum_popup_templates( CourseModel $course_model ): string {
1257 + protected function html_curriculum_popup_templates( CourseModel $courseModel ): string {
1160 1258 $popup_templates = apply_filters(
1161 1259 'learn-press/course-builder/courses/curriculum/popup-templates',
1162 1260 [
1163 1261 'lesson' => sprintf(
@@ -1164,9 +1262,9 @@
1164 1262 '<script type="text/template" id="%1$s"><div class="lp-builder-popup-overlay"></div><div class="lp-builder-popup lp-builder-popup--loading">%2$s</div></script>',
1165 1263 esc_attr(
1166 1264 sprintf(
1167 1265 'lp-tmpl-builder-popup-curriculum-lesson-course-%d',
1168 - $course_model->get_id()
1266 + $courseModel->get_id()
1169 1267 )
1170 1268 ),
1171 1269 TemplateAJAX::load_content_via_ajax(
1172 1270 [
@@ -1187,9 +1285,9 @@
1187 1285 '<script type="text/template" id="%1$s"><div class="lp-builder-popup-overlay"></div><div class="lp-builder-popup lp-builder-popup--loading">%2$s</div></script>',
1188 1286 esc_attr(
1189 1287 sprintf(
1190 1288 'lp-tmpl-builder-popup-curriculum-quiz-course-%d',
1191 - $course_model->get_id()
1289 + $courseModel->get_id()
1192 1290 )
1193 1291 ),
1194 1292 TemplateAJAX::load_content_via_ajax(
1195 1293 [
@@ -1206,9 +1304,9 @@
1206 1304 ]
1207 1305 )
1208 1306 ),
1209 1307 ],
1210 - $course_model
1308 + $courseModel
1211 1309 );
1212 1310
1213 1311 return Template::combine_components( $popup_templates );
1214 1312 }