← All changes
|
inc/TemplateHooks/CourseBuilder/Course/BuilderEditCourseTemplate.php
+136
-38
4.3.7
→
4.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 | } |