| @@ -13,8 +13,10 @@ | ||
| 13 | 13 | use Exception; |
| 14 | 14 | use LearnPress\Ajax\AbstractAjax; |
| 15 | 15 | use LearnPress\CourseBuilder\CourseBuilder; |
| 16 | 16 | use LearnPress\CourseBuilder\CourseBuilderAccessPolicy; |
| 17 | +use LearnPress\Helpers\Response; | |
| 18 | +use LearnPress\Helpers\Template; | |
| 17 | 19 | use LearnPress\Models\CourseModel; |
| 18 | 20 | use LearnPress\Models\CoursePostModel; |
| 19 | 21 | use LearnPress\Models\CourseSectionItemModel; |
| 20 | 22 | use LearnPress\Models\LessonPostModel; |
| @@ -53,22 +55,25 @@ | ||
| 53 | 55 | if ( empty( $params ) ) { |
| 54 | 56 | throw new Exception( 'Error: params invalid!' ); |
| 55 | 57 | } |
| 56 | 58 | |
| 57 | - $params = LP_Helper::json_decode( $params, true ); | |
| 58 | - $course_id = ! empty( $params['course_id'] ) ? (int) $params['course_id'] : 0; | |
| 59 | - $course_model = CourseModel::find( $course_id, true ); | |
| 60 | - if ( empty( $course_model ) ) { | |
| 59 | + $params = LP_Helper::json_decode( $params, true ); | |
| 60 | + $course_id = ! empty( $params['course_id'] ) ? (int) $params['course_id'] : 0; | |
| 61 | + $courseModel = CourseModel::find( $course_id, true ); | |
| 62 | + if ( empty( $courseModel ) ) { | |
| 61 | 63 | $params['insert'] = true; |
| 62 | 64 | $params['course_model'] = ''; |
| 63 | 65 | } else { |
| 64 | 66 | $params['insert'] = false; |
| 65 | - $params['course_model'] = $course_model; | |
| 67 | + $params['course_model'] = $courseModel; | |
| 66 | 68 | } |
| 67 | 69 | |
| 68 | 70 | return $params; |
| 69 | 71 | } |
| 70 | 72 | |
| 73 | + /** | |
| 74 | + * @throws Exception | |
| 75 | + */ | |
| 71 | 76 | public static function check_valid_lesson() { |
| 72 | 77 | $params = wp_unslash( $_REQUEST['data'] ?? '' ); |
| 73 | 78 | if ( empty( $params ) ) { |
| 74 | 79 | throw new Exception( 'Error: params invalid!' ); |
| @@ -74,9 +79,9 @@ | ||
| 74 | 79 | throw new Exception( 'Error: params invalid!' ); |
| 75 | 80 | } |
| 76 | 81 | |
| 77 | 82 | $params = LP_Helper::json_decode( $params, true ); |
| 78 | - $lesson_id = ! empty( $params['lesson_id'] ) ? (int) $params['lesson_id'] : 0; | |
| 83 | + $lesson_id = absint( $params['lesson_id'] ?? 0 ); | |
| 79 | 84 | $lesson_model = LessonPostModel::find( $lesson_id, true ); |
| 80 | 85 | if ( empty( $lesson_model ) ) { |
| 81 | 86 | $params['insert'] = true; |
| 82 | 87 | $params['lesson_model'] = ''; |
| @@ -213,13 +218,13 @@ | ||
| 213 | 218 | * Mirrors wp-admin behavior for instructors without publish capability. |
| 214 | 219 | * |
| 215 | 220 | * @param string $requested_status |
| 216 | 221 | * @param bool $insert |
| 217 | - * @param CourseModel|null $course_model | |
| 222 | + * @param CourseModel|null $courseModel | |
| 218 | 223 | * |
| 219 | 224 | * @return string |
| 220 | 225 | */ |
| 221 | - protected function normalize_course_status_for_save( string $requested_status, bool $insert, ?CourseModel $course_model ): string { | |
| 226 | + protected function normalize_course_status_for_save( string $requested_status, bool $insert, ?CourseModel $courseModel ): string { | |
| 222 | 227 | $allowed_statuses = [ 'publish', 'future', 'draft', 'pending', 'private' ]; |
| 223 | 228 | if ( ! in_array( $requested_status, $allowed_statuses, true ) ) { |
| 224 | 229 | $requested_status = 'draft'; |
| 225 | 230 | } |
| @@ -232,9 +237,9 @@ | ||
| 232 | 237 | $is_instructor_user = current_user_can( LP_TEACHER_ROLE ); |
| 233 | 238 | $required_review = LP_Settings::get_option( 'required_review', 'yes' ) === 'yes'; |
| 234 | 239 | $can_publish_course = current_user_can( 'publish_' . LP_COURSE_CPT . 's' ); |
| 235 | 240 | |
| 236 | - $current_status = $insert ? 'auto-draft' : (string) ( $course_model->post_status ?? 'draft' ); | |
| 241 | + $current_status = $insert ? 'auto-draft' : (string) ( $courseModel->post_status ?? 'draft' ); | |
| 237 | 242 | $is_public_state = $this->is_public_post_status( $current_status ); |
| 238 | 243 | |
| 239 | 244 | // Require moderation for instructors when review is enabled. |
| 240 | 245 | if ( $is_instructor_user && $required_review && ! $is_public_state && in_array( $requested_status, [ 'publish', 'private', 'future' ], true ) ) { |
| @@ -334,9 +339,9 @@ | ||
| 334 | 339 | |
| 335 | 340 | $insert_post_data = array( |
| 336 | 341 | 'post_type' => LP_COURSE_CPT, |
| 337 | 342 | 'post_title' => $course_title, |
| 338 | - 'post_content' => wp_unslash( $data['course_description'] ?? '' ), | |
| 343 | + 'post_content' => Template::sanitize_html_content( $data['course_description'] ?? '' ), | |
| 339 | 344 | 'post_status' => $course_status, |
| 340 | 345 | 'tax_input' => array( |
| 341 | 346 | 'course_category' => $categories, |
| 342 | 347 | 'course_tag' => $tags, |
| @@ -404,9 +409,9 @@ | ||
| 404 | 409 | $categories = ! empty( $data['course_categories'] ) ? array_map( 'absint', explode( ',', $data['course_categories'] ) ) : array(); |
| 405 | 410 | $tags = ! empty( $data['course_tags'] ) ? array_map( 'absint', explode( ',', $data['course_tags'] ) ) : array(); |
| 406 | 411 | |
| 407 | 412 | $courseModel->post_title = $course_title; |
| 408 | - $courseModel->post_content = wp_unslash( $data['course_description'] ?? '' ); | |
| 413 | + $courseModel->post_content = Template::sanitize_html_content( $data['course_description'] ?? '' ); | |
| 409 | 414 | |
| 410 | 415 | wp_set_post_terms( $courseModel->ID, $categories, 'course_category' ); |
| 411 | 416 | wp_set_post_terms( $courseModel->ID, $tags, 'course_tag' ); |
| 412 | 417 | } |
| @@ -577,8 +582,19 @@ | ||
| 577 | 582 | $this->save_assessment_settings_to_model( $courseModel, $data ); |
| 578 | 583 | |
| 579 | 584 | // Author settings |
| 580 | 585 | $this->save_author_settings_to_model( $courseModel, $data ); |
| 586 | + | |
| 587 | + /** | |
| 588 | + * Allow addons to persist their own course settings fields added to the | |
| 589 | + * settings metabox tabs (e.g. Co-Instructor) when saving from Course Builder. | |
| 590 | + * | |
| 591 | + * @param CoursePostModel $courseModel Course model being saved. | |
| 592 | + * @param array $data Submitted settings data. | |
| 593 | + * | |
| 594 | + * @since 4.3.8 | |
| 595 | + */ | |
| 596 | + do_action( 'learn-press/course-builder/save-course-settings', $courseModel, $data ); | |
| 581 | 597 | } |
| 582 | 598 | |
| 583 | 599 | /** |
| 584 | 600 | * Save general settings to CourseModel |
| @@ -712,8 +728,27 @@ | ||
| 712 | 728 | } |
| 713 | 729 | } |
| 714 | 730 | |
| 715 | 731 | /** |
| 732 | + * Normalize an extra-info field value to an array. | |
| 733 | + * | |
| 734 | + * The Course Builder sends these fields as arrays (one entry per input); | |
| 735 | + * older paths may send a comma-separated string. Splitting on comma must | |
| 736 | + * never be applied to array input, or values that legitimately contain a | |
| 737 | + * comma get broken into multiple entries. | |
| 738 | + * | |
| 739 | + * @param mixed $value | |
| 740 | + * @return array | |
| 741 | + */ | |
| 742 | + protected function extra_field_to_array( $value ): array { | |
| 743 | + if ( is_array( $value ) ) { | |
| 744 | + return $value; | |
| 745 | + } | |
| 746 | + | |
| 747 | + return $value !== '' ? explode( ',', (string) $value ) : []; | |
| 748 | + } | |
| 749 | + | |
| 750 | + /** | |
| 716 | 751 | * Save extra info settings to CourseModel |
| 717 | 752 | * |
| 718 | 753 | * @param CoursePostModel $courseModel |
| 719 | 754 | * @param array $data |
| @@ -720,11 +755,10 @@ | ||
| 720 | 755 | */ |
| 721 | 756 | protected function save_extra_settings_to_model( CoursePostModel &$courseModel, array $data ) { |
| 722 | 757 | // Requirements |
| 723 | 758 | if ( isset( $data['_lp_requirements'] ) ) { |
| 724 | - $requirements = ! empty( $data['_lp_requirements'] ) ? explode( ',', $data['_lp_requirements'] ) : []; | |
| 725 | 759 | $requirements = array_filter( |
| 726 | - $requirements, | |
| 760 | + $this->extra_field_to_array( $data['_lp_requirements'] ), | |
| 727 | 761 | function ( $item ) { |
| 728 | 762 | return ! is_null( $item ) && $item !== ''; |
| 729 | 763 | } |
| 730 | 764 | ); |
| @@ -731,11 +765,10 @@ | ||
| 731 | 765 | $courseModel->meta_data->{CoursePostModel::META_KEY_REQUIREMENTS} = array_map( 'sanitize_text_field', array_values( $requirements ) ); |
| 732 | 766 | } |
| 733 | 767 | |
| 734 | 768 | if ( isset( $data['_lp_target_audiences'] ) ) { |
| 735 | - $target_audiences = ! empty( $data['_lp_target_audiences'] ) ? explode( ',', $data['_lp_target_audiences'] ) : []; | |
| 736 | 769 | $target_audiences = array_filter( |
| 737 | - $target_audiences, | |
| 770 | + $this->extra_field_to_array( $data['_lp_target_audiences'] ), | |
| 738 | 771 | function ( $item ) { |
| 739 | 772 | return ! is_null( $item ) && $item !== ''; |
| 740 | 773 | } |
| 741 | 774 | ); |
| @@ -742,11 +775,10 @@ | ||
| 742 | 775 | $courseModel->meta_data->{CoursePostModel::META_KEY_TARGET} = array_map( 'sanitize_text_field', array_values( $target_audiences ) ); |
| 743 | 776 | } |
| 744 | 777 | |
| 745 | 778 | if ( isset( $data['_lp_key_features'] ) ) { |
| 746 | - $key_features = ! empty( $data['_lp_key_features'] ) ? explode( ',', $data['_lp_key_features'] ) : []; | |
| 747 | 779 | $key_features = array_filter( |
| 748 | - $key_features, | |
| 780 | + $this->extra_field_to_array( $data['_lp_key_features'] ), | |
| 749 | 781 | function ( $item ) { |
| 750 | 782 | return ! is_null( $item ) && $item !== ''; |
| 751 | 783 | } |
| 752 | 784 | ); |
| @@ -754,10 +786,10 @@ | ||
| 754 | 786 | } |
| 755 | 787 | |
| 756 | 788 | // FAQs |
| 757 | 789 | if ( isset( $data['_lp_faqs_question'] ) ) { |
| 758 | - $questions = ! empty( $data['_lp_faqs_question'] ) ? explode( ',', $data['_lp_faqs_question'] ) : []; | |
| 759 | - $answers = ! empty( $data['_lp_faqs_answer'] ) ? explode( ',', $data['_lp_faqs_answer'] ) : []; | |
| 790 | + $questions = $this->extra_field_to_array( $data['_lp_faqs_question'] ); | |
| 791 | + $answers = ! empty( $data['_lp_faqs_answer'] ) ? $this->extra_field_to_array( $data['_lp_faqs_answer'] ) : []; | |
| 760 | 792 | $faqs = []; |
| 761 | 793 | |
| 762 | 794 | if ( ! empty( $questions ) ) { |
| 763 | 795 | foreach ( $questions as $index => $question ) { |
| @@ -823,13 +855,13 @@ | ||
| 823 | 855 | $response = new LP_REST_Response(); |
| 824 | 856 | $response->data = new stdClass(); |
| 825 | 857 | |
| 826 | 858 | try { |
| 827 | - $data = self::check_valid_course(); | |
| 828 | - $course_id = $data['course_id'] ?? 0; | |
| 829 | - $course_model = $data['course_model']; | |
| 859 | + $data = self::check_valid_course(); | |
| 860 | + $course_id = $data['course_id'] ?? 0; | |
| 861 | + $courseModel = $data['course_model']; | |
| 830 | 862 | |
| 831 | - if ( absint( $course_model->post_author ) !== get_current_user_id() && ! current_user_can( 'manage_options' ) ) { | |
| 863 | + if ( absint( $courseModel->post_author ) !== get_current_user_id() && ! current_user_can( 'manage_options' ) ) { | |
| 832 | 864 | throw new Exception( __( 'You are not allowed to duplicate this course', 'learnpress' ) ); |
| 833 | 865 | } |
| 834 | 866 | |
| 835 | 867 | if ( ! function_exists( 'learn_press_duplicate_post' ) ) { |
| @@ -856,10 +888,10 @@ | ||
| 856 | 888 | if ( is_wp_error( $new_item_id ) ) { |
| 857 | 889 | throw new Exception( $new_item_id->get_error_message() ); |
| 858 | 890 | } |
| 859 | 891 | |
| 860 | - $course_model = CourseModel::find( $new_item_id, true ); | |
| 861 | - $html = BuilderListCoursesTemplate::render_course( $course_model ); | |
| 892 | + $courseModel = CourseModel::find( $new_item_id, true ); | |
| 893 | + $html = BuilderListCoursesTemplate::render_course( $courseModel ); | |
| 862 | 894 | $response->status = 'success'; |
| 863 | 895 | $response->data->html = $html; |
| 864 | 896 | $response->data->course_id_new = $new_item_id; |
| 865 | 897 | $response->data->redirect_url = CourseBuilder::get_link_course_builder( "courses/{$new_item_id}" ); |
| @@ -876,17 +908,17 @@ | ||
| 876 | 908 | $response = new LP_REST_Response(); |
| 877 | 909 | $response->data = new stdClass(); |
| 878 | 910 | |
| 879 | 911 | try { |
| 880 | - $data = self::check_valid_course(); | |
| 881 | - $course_id = $data['course_id'] ?? 0; | |
| 882 | - $course_model = $data['course_model']; | |
| 883 | - $status = $data['status'] ?? 'trash'; | |
| 912 | + $data = self::check_valid_course(); | |
| 913 | + $course_id = $data['course_id'] ?? 0; | |
| 914 | + $courseModel = $data['course_model']; | |
| 915 | + $status = $data['status'] ?? 'trash'; | |
| 884 | 916 | |
| 885 | 917 | $co_instructor_ids = get_post_meta( $course_id, '_lp_co_teacher', false ); |
| 886 | 918 | $co_instructor_ids = ! empty( $co_instructor_ids ) ? $co_instructor_ids : array(); |
| 887 | 919 | |
| 888 | - if ( absint( $course_model->post_author ) !== get_current_user_id() && ! current_user_can( 'manage_options' ) && ! in_array( get_current_user_id(), $co_instructor_ids ) ) { | |
| 920 | + if ( absint( $courseModel->post_author ) !== get_current_user_id() && ! current_user_can( 'manage_options' ) && ! in_array( get_current_user_id(), $co_instructor_ids ) ) { | |
| 889 | 921 | throw new Exception( __( 'You are not allowed to delete this course', 'learnpress' ) ); |
| 890 | 922 | } |
| 891 | 923 | |
| 892 | 924 | if ( $status === 'delete' ) { |
| @@ -912,9 +944,9 @@ | ||
| 912 | 944 | |
| 913 | 945 | $message = __( 'Course has been moved to draft', 'learnpress' ); |
| 914 | 946 | } else { |
| 915 | 947 | // Store original slug before trashing (wp_trash_post adds __trashed suffix) |
| 916 | - $original_slug = $course_model->post_name; | |
| 948 | + $original_slug = $courseModel->post_name; | |
| 917 | 949 | |
| 918 | 950 | $delete = wp_trash_post( $course_id ); |
| 919 | 951 | |
| 920 | 952 | if ( ! $delete ) { |
| @@ -950,18 +982,9 @@ | ||
| 950 | 982 | $response = new LP_REST_Response(); |
| 951 | 983 | $response->data = new stdClass(); |
| 952 | 984 | |
| 953 | 985 | try { |
| 954 | - $data = self::check_valid_course(); | |
| 955 | - $course_id = absint( $data['course_id'] ?? 0 ); | |
| 956 | - if ( $course_id > 0 ) { | |
| 957 | - if ( ! CourseBuilderAccessPolicy::can_edit_course_by_id( $course_id ) ) { | |
| 958 | - throw new Exception( __( 'You are not allowed to update this course', 'learnpress' ) ); | |
| 959 | - } | |
| 960 | - } elseif ( ! CourseBuilderAccessPolicy::can_create_item_type( LP_COURSE_CPT ) ) { | |
| 961 | - throw new Exception( __( 'You are not allowed to create courses', 'learnpress' ) ); | |
| 962 | - } | |
| 963 | - | |
| 986 | + $data = self::check_valid_course(); | |
| 964 | 987 | if ( ! current_user_can( 'edit_lp_courses' ) ) { |
| 965 | 988 | throw new Exception( __( 'You are not allowed to create categories', 'learnpress' ) ); |
| 966 | 989 | } |
| 967 | 990 | |
| @@ -1008,18 +1031,9 @@ | ||
| 1008 | 1031 | $response = new LP_REST_Response(); |
| 1009 | 1032 | $response->data = new stdClass(); |
| 1010 | 1033 | |
| 1011 | 1034 | try { |
| 1012 | - $data = self::check_valid_course(); | |
| 1013 | - $course_id = absint( $data['course_id'] ?? 0 ); | |
| 1014 | - if ( $course_id > 0 ) { | |
| 1015 | - if ( ! CourseBuilderAccessPolicy::can_edit_course_by_id( $course_id ) ) { | |
| 1016 | - throw new Exception( __( 'You are not allowed to update this course', 'learnpress' ) ); | |
| 1017 | - } | |
| 1018 | - } elseif ( ! CourseBuilderAccessPolicy::can_create_item_type( LP_COURSE_CPT ) ) { | |
| 1019 | - throw new Exception( __( 'You are not allowed to create courses', 'learnpress' ) ); | |
| 1020 | - } | |
| 1021 | - | |
| 1035 | + $data = self::check_valid_course(); | |
| 1022 | 1036 | if ( ! current_user_can( 'edit_lp_courses' ) ) { |
| 1023 | 1037 | throw new Exception( __( 'You are not allowed to create tags', 'learnpress' ) ); |
| 1024 | 1038 | } |
| 1025 | 1039 | |
| @@ -1087,120 +1101,117 @@ | ||
| 1087 | 1101 | * Update Lesson from Course Builder. |
| 1088 | 1102 | * Supports partial updates (title only, description only, or settings only). |
| 1089 | 1103 | * |
| 1090 | 1104 | * @since 4.3 |
| 1091 | - * @version 1.0.2 | |
| 1105 | + * @version 1.0.3 | |
| 1092 | 1106 | */ |
| 1093 | 1107 | public function builder_update_lesson() { |
| 1094 | - $response = new LP_REST_Response(); | |
| 1108 | + $response = new Response(); | |
| 1095 | 1109 | $response->data = new stdClass(); |
| 1096 | 1110 | |
| 1097 | 1111 | try { |
| 1098 | 1112 | $data = self::check_valid_lesson(); |
| 1099 | 1113 | $lesson_id = $data['lesson_id'] ?? 0; |
| 1100 | - $title = $data['lesson_title'] ?? null; | |
| 1101 | - $description = $data['lesson_description'] ?? null; | |
| 1114 | + $title = LP_Helper::sanitize_params_submitted( $data['lesson_title'] ?? '' ); | |
| 1115 | + $description = LP_Helper::sanitize_params_submitted( | |
| 1116 | + $data['lesson_description'] ?? '', | |
| 1117 | + 'html' | |
| 1118 | + ); | |
| 1102 | 1119 | $settings = $data['lesson_settings'] ?? false; |
| 1103 | 1120 | $is_elementor = $data['is_elementor'] ?? false; |
| 1104 | 1121 | $return_html = ( $data['return_html'] ?? 'no' ) === 'yes'; |
| 1105 | 1122 | $insert = $data['insert']; |
| 1106 | - $lesson_slug = ! empty( $data['lesson_permalink'] ) | |
| 1107 | - ? sanitize_title( wp_unslash( (string) $data['lesson_permalink'] ) ) | |
| 1108 | - : ''; | |
| 1123 | + $lesson_slug = LP_Helper::sanitize_params_submitted( | |
| 1124 | + $data['lesson_permalink'] ?? '', | |
| 1125 | + 'key' | |
| 1126 | + ); | |
| 1109 | 1127 | $course_id = absint( $data['course_id'] ?? 0 ); |
| 1110 | 1128 | |
| 1111 | 1129 | // Determine target status (draft or publish) |
| 1112 | - $target_status = ! empty( $data['lesson_status'] ) && in_array( $data['lesson_status'], [ 'draft', 'publish' ], true ) | |
| 1113 | - ? sanitize_text_field( $data['lesson_status'] ) | |
| 1114 | - : 'publish'; | |
| 1130 | + $target_status = LP_Helper::sanitize_params_submitted( | |
| 1131 | + $data['lesson_status'] ?? '', | |
| 1132 | + 'key' | |
| 1133 | + ); | |
| 1115 | 1134 | $restore_with_custom_slug = false; |
| 1116 | 1135 | |
| 1136 | + if ( empty( $title ) ) { | |
| 1137 | + throw new Exception( __( 'Lesson title is required', 'learnpress' ) ); | |
| 1138 | + } | |
| 1139 | + | |
| 1117 | 1140 | if ( $insert ) { |
| 1118 | - if ( ! CourseBuilderAccessPolicy::can_create_item_type( LP_LESSON_CPT ) ) { | |
| 1119 | - throw new Exception( __( 'You are not allowed to create lessons', 'learnpress' ) ); | |
| 1120 | - } | |
| 1121 | - | |
| 1122 | 1141 | $insert_arg = array( |
| 1123 | - 'post_type' => LP_LESSON_CPT, | |
| 1124 | - 'post_title' => sanitize_text_field( $title ?? '' ), | |
| 1125 | - 'post_content' => wp_kses_post( $description ?? '' ), | |
| 1142 | + 'post_title' => $title, | |
| 1143 | + 'post_content' => $description, | |
| 1126 | 1144 | 'post_status' => $target_status, |
| 1145 | + 'post_author' => get_current_user_id(), | |
| 1127 | 1146 | ); |
| 1128 | 1147 | |
| 1129 | - if ( ! empty( $lesson_slug ) ) { | |
| 1130 | - $insert_arg['post_name'] = $lesson_slug; | |
| 1131 | - } | |
| 1148 | + $lessonPostModelNew = new LessonPostModel( $insert_arg ); | |
| 1149 | + $lessonPostModelNew->check_capabilities_create_item_course(); | |
| 1132 | 1150 | |
| 1133 | - $lesson_id = wp_insert_post( $insert_arg, true ); | |
| 1151 | + $lessonPostModelNew->save(); | |
| 1152 | + $lesson_id = $lessonPostModelNew->ID; | |
| 1134 | 1153 | |
| 1135 | - if ( is_wp_error( $lesson_id ) ) { | |
| 1136 | - throw new Exception( $lesson_id->get_error_message() ); | |
| 1137 | - } | |
| 1138 | - | |
| 1139 | - $lesson_model = LessonPostModel::find( $lesson_id, true ); | |
| 1154 | + $lesson_model = $lessonPostModelNew; | |
| 1155 | + $response->message = __( 'Create lesson successfully', 'learnpress' ); | |
| 1140 | 1156 | } else { |
| 1141 | - $lesson_model = $data['lesson_model']; | |
| 1142 | - | |
| 1143 | - if ( ! $lesson_model ) { | |
| 1157 | + $lesson_model = $data['lesson_model'] ?? null; | |
| 1158 | + if ( ! $lesson_model instanceof LessonPostModel ) { | |
| 1144 | 1159 | throw new Exception( __( 'Lesson not found', 'learnpress' ) ); |
| 1145 | 1160 | } |
| 1146 | 1161 | |
| 1162 | + $lesson_model->check_capabilities_update_item_course(); | |
| 1163 | + | |
| 1147 | 1164 | if ( ! $course_id ) { |
| 1148 | 1165 | $course_id = absint( $this->get_course_by_item_id( $lesson_id ) ); |
| 1149 | 1166 | } |
| 1150 | 1167 | |
| 1151 | 1168 | // Support for co-instructor. |
| 1152 | - $co_instructor_ids = get_post_meta( $course_id, '_lp_co_teacher', false ); | |
| 1169 | + /*$co_instructor_ids = get_post_meta( $course_id, '_lp_co_teacher', false ); | |
| 1153 | 1170 | $co_instructor_ids = ! empty( $co_instructor_ids ) ? $co_instructor_ids : array(); |
| 1154 | 1171 | |
| 1155 | 1172 | if ( absint( $lesson_model->post_author ) !== get_current_user_id() && ! current_user_can( 'manage_options' ) && ! in_array( get_current_user_id(), $co_instructor_ids ) ) { |
| 1156 | 1173 | throw new Exception( __( 'You are not allowed to update this lesson', 'learnpress' ) ); |
| 1157 | - } | |
| 1174 | + }*/ | |
| 1158 | 1175 | |
| 1159 | - $update_arg = array( | |
| 1176 | + /*$update_arg = array( | |
| 1160 | 1177 | 'ID' => $lesson_id, |
| 1161 | 1178 | 'post_type' => LP_LESSON_CPT, |
| 1162 | 1179 | 'post_status' => $target_status, |
| 1163 | - ); | |
| 1180 | + );*/ | |
| 1164 | 1181 | |
| 1165 | 1182 | if ( defined( 'ELEMENTOR_VERSION' ) ) { |
| 1166 | 1183 | \Elementor\Plugin::$instance->documents->get( $lesson_id )->set_is_built_with_elementor( ! empty( $is_elementor ) ); |
| 1167 | 1184 | } |
| 1168 | 1185 | |
| 1169 | - if ( isset( $data['lesson_title'] ) ) { | |
| 1170 | - $update_arg['post_title'] = sanitize_text_field( $title ); | |
| 1171 | - } | |
| 1172 | - | |
| 1173 | - if ( isset( $data['lesson_description'] ) ) { | |
| 1174 | - $update_arg['post_content'] = wp_kses_post( $description ); | |
| 1175 | - } | |
| 1176 | - | |
| 1186 | + $lesson_model->post_title = $title; | |
| 1187 | + $lesson_model->post_content = $description; | |
| 1188 | + $lesson_model->post_status = $target_status; | |
| 1177 | 1189 | if ( ! empty( $lesson_slug ) ) { |
| 1178 | - $update_arg['post_name'] = $lesson_slug; | |
| 1190 | + $lesson_model->post_name = $lesson_slug; | |
| 1179 | 1191 | } |
| 1180 | 1192 | |
| 1181 | - $restore_with_custom_slug = $this->prepare_desired_slug_for_restore( $lesson_id, $target_status, $lesson_slug ); | |
| 1193 | + //$restore_with_custom_slug = $this->prepare_desired_slug_for_restore( $lesson_id, $target_status, $lesson_slug ); | |
| 1182 | 1194 | |
| 1183 | - $update = wp_update_post( $update_arg ); | |
| 1195 | + //$update = wp_update_post( $update_arg ); | |
| 1196 | + $lesson_model->save(); | |
| 1184 | 1197 | |
| 1185 | - if ( is_wp_error( $update ) ) { | |
| 1186 | - throw new Exception( $update->get_error_message() ); | |
| 1187 | - } | |
| 1188 | - | |
| 1189 | - if ( $restore_with_custom_slug ) { | |
| 1198 | + /*if ( $restore_with_custom_slug ) { | |
| 1190 | 1199 | $this->sync_slug_after_restore( $lesson_id, $lesson_slug ); |
| 1191 | - } | |
| 1200 | + }*/ | |
| 1192 | 1201 | |
| 1193 | 1202 | if ( $course_id ) { |
| 1194 | - $course_model_cache = CourseModel::find( $course_id, true ); | |
| 1195 | - if ( $course_model_cache ) { | |
| 1196 | - $course_model_cache->sections_items = null; | |
| 1197 | - $course_model_cache->save(); | |
| 1203 | + $courseModelCache = CourseModel::find( $course_id, true ); | |
| 1204 | + if ( $courseModelCache ) { | |
| 1205 | + $courseModelCache->sections_items = null; | |
| 1206 | + $courseModelCache->save(); | |
| 1198 | 1207 | } |
| 1199 | 1208 | } |
| 1209 | + | |
| 1210 | + $response->message = __( 'Update lesson successfully', 'learnpress' ); | |
| 1200 | 1211 | } |
| 1201 | 1212 | |
| 1202 | - if ( $settings && $lesson_model ) { | |
| 1213 | + if ( $settings ) { | |
| 1203 | 1214 | $this->save_lesson_settings_to_model( $lesson_model, $data ); |
| 1204 | 1215 | } |
| 1205 | 1216 | |
| 1206 | 1217 | // Remove lesson from curriculum if status is not public |
| @@ -1207,32 +1218,24 @@ | ||
| 1207 | 1218 | if ( $target_status !== 'publish' ) { |
| 1208 | 1219 | $this->remove_course_item_from_curriculum( $lesson_id, $course_id ); |
| 1209 | 1220 | } |
| 1210 | 1221 | |
| 1211 | - do_action( 'learn-press/course-builder/update-lesson', $data, $lesson_model ); | |
| 1212 | - | |
| 1213 | - $saved_lesson_status = get_post_status( $lesson_id ); | |
| 1214 | - if ( ! is_string( $saved_lesson_status ) || '' === $saved_lesson_status ) { | |
| 1215 | - $saved_lesson_status = $target_status; | |
| 1216 | - } | |
| 1217 | - | |
| 1218 | 1222 | $response->status = 'success'; |
| 1219 | - $response->data->status = $saved_lesson_status; | |
| 1220 | - $response->data->button_title = $saved_lesson_status === 'publish' ? __( 'Update', 'learnpress' ) : __( 'Publish', 'learnpress' ); | |
| 1223 | + $response->data->status = $lesson_model->post_status; | |
| 1224 | + $response->data->button_title = $lesson_model->post_status === 'publish' ? | |
| 1225 | + __( 'Update', 'learnpress' ) : | |
| 1226 | + __( 'Publish', 'learnpress' ); | |
| 1221 | 1227 | $response->data->lesson_id_new = $insert ? $lesson_id : ''; |
| 1222 | 1228 | $response->message = $target_status === 'draft' |
| 1223 | 1229 | ? esc_html__( 'Lesson saved as draft', 'learnpress' ) |
| 1224 | 1230 | : ( $insert ? esc_html__( 'Insert lesson successfully', 'learnpress' ) : esc_html__( 'Update lesson successfully', 'learnpress' ) ); |
| 1225 | 1231 | |
| 1226 | - $saved_lesson_post = get_post( $lesson_id ); | |
| 1227 | - if ( $saved_lesson_post ) { | |
| 1228 | - $response->data->lesson_slug = $saved_lesson_post->post_name; | |
| 1229 | - } | |
| 1232 | + $response->data->lesson_slug = $lesson_model->post_name; | |
| 1230 | 1233 | |
| 1231 | 1234 | $course_id_of_item = $this->get_course_by_item_id( $lesson_id ); |
| 1232 | 1235 | $response->data->permalink_available = false; |
| 1233 | 1236 | $response->data->permalink_notice = $this->get_item_permalink_unavailable_message(); |
| 1234 | - if ( 'publish' === $saved_lesson_status && $course_id_of_item ) { | |
| 1237 | + if ( 'publish' === $lesson_model->post_status && $course_id_of_item ) { | |
| 1235 | 1238 | $course = learn_press_get_course( $course_id_of_item ); |
| 1236 | 1239 | if ( $course ) { |
| 1237 | 1240 | $response->data->permalink_available = true; |
| 1238 | 1241 | $response->data->lesson_permalink = urldecode( $course->get_item_link( $lesson_id ) ); |
| @@ -1245,26 +1248,26 @@ | ||
| 1245 | 1248 | ? BuilderListLessonsTemplate::render_lesson( $lesson_model_for_html ) |
| 1246 | 1249 | : ''; |
| 1247 | 1250 | |
| 1248 | 1251 | if ( $course_id ) { |
| 1249 | - $course_model_for_html = CourseModel::find( $course_id, true ); | |
| 1250 | - if ( $course_model_for_html && $lesson_model_for_html ) { | |
| 1252 | + $courseModelForHtml = CourseModel::find( $course_id, true ); | |
| 1253 | + if ( $courseModelForHtml && $lesson_model_for_html ) { | |
| 1251 | 1254 | $item = new stdClass(); |
| 1252 | 1255 | $item->item_id = $lesson_id; |
| 1253 | 1256 | $item->title = $lesson_model_for_html->post_title; |
| 1254 | 1257 | $item->item_type = LP_LESSON_CPT; |
| 1255 | 1258 | AdminEditCurriculumTemplate::instance()->context_data = [ 'is_course_builder' => true ]; |
| 1256 | - $response->data->section_item_html = AdminEditCurriculumTemplate::instance()->html_section_item( $course_model_for_html, $item ); | |
| 1259 | + $response->data->section_item_html = AdminEditCurriculumTemplate::instance()->html_section_item( $courseModelForHtml, $item ); | |
| 1257 | 1260 | } |
| 1258 | 1261 | } |
| 1259 | 1262 | } |
| 1260 | 1263 | |
| 1261 | - wp_send_json( $response ); | |
| 1262 | - } catch ( Throwable $th ) { | |
| 1263 | - $response->status = 'error'; | |
| 1264 | - $response->message = $th->getMessage(); | |
| 1265 | - wp_send_json( $response ); | |
| 1264 | + $response->status = Response::STATUS_SUCCESS; | |
| 1265 | + } catch ( Throwable $e ) { | |
| 1266 | + $response->message = $e->getMessage(); | |
| 1266 | 1267 | } |
| 1268 | + | |
| 1269 | + wp_send_json( $response ); | |
| 1267 | 1270 | } |
| 1268 | 1271 | |
| 1269 | 1272 | public function move_trash_lesson() { |
| 1270 | 1273 | $response = new LP_REST_Response(); |
| @@ -1393,10 +1396,13 @@ | ||
| 1393 | 1396 | } |
| 1394 | 1397 | |
| 1395 | 1398 | /** |
| 1396 | 1399 | * Save Lesson Settings |
| 1400 | + * @throws Exception | |
| 1397 | 1401 | */ |
| 1398 | 1402 | protected function save_lesson_settings_to_model( LessonPostModel $lessonModel, array $data ) { |
| 1403 | + $must_save = 0; | |
| 1404 | + | |
| 1399 | 1405 | if ( isset( $data['_lp_duration'] ) ) { |
| 1400 | 1406 | $duration = ! empty( $data['_lp_duration'] ) ? str_replace( ',', ' ', $data['_lp_duration'] ) : '0 minute'; |
| 1401 | 1407 | $explode = explode( ' ', $duration ); |
| 1402 | 1408 | $number = (float) $explode[0] < 0 ? 0 : absint( $explode[0] ); |
| @@ -1401,15 +1407,32 @@ | ||
| 1401 | 1407 | $explode = explode( ' ', $duration ); |
| 1402 | 1408 | $number = (float) $explode[0] < 0 ? 0 : absint( $explode[0] ); |
| 1403 | 1409 | $unit = $explode[1] ?? 'minute'; |
| 1404 | 1410 | |
| 1405 | - $lessonModel->save_meta_value_by_key( '_lp_duration', $number . ' ' . $unit ); | |
| 1411 | + $lessonModel->set_meta_value_by_key( $lessonModel::META_KEY_DURATION, $number . ' ' . $unit ); | |
| 1412 | + $must_save = 1; | |
| 1406 | 1413 | } |
| 1407 | 1414 | |
| 1408 | 1415 | if ( isset( $data['_lp_preview'] ) ) { |
| 1409 | 1416 | $enable = $data['_lp_preview'] === 'yes'; |
| 1410 | 1417 | $lessonModel->set_preview( $enable ); |
| 1418 | + $must_save = 1; | |
| 1411 | 1419 | } |
| 1420 | + | |
| 1421 | + if ( $must_save ) { | |
| 1422 | + $lessonModel->save(); | |
| 1423 | + } | |
| 1424 | + | |
| 1425 | + /** | |
| 1426 | + * Allow addons to persist their own lesson settings fields added to the | |
| 1427 | + * settings metabox tabs when saving from Course Builder. | |
| 1428 | + * | |
| 1429 | + * @param LessonPostModel $lessonModel Lesson model being saved. | |
| 1430 | + * @param array $data Submitted settings data. | |
| 1431 | + * | |
| 1432 | + * @since 4.3.8 | |
| 1433 | + */ | |
| 1434 | + do_action( 'learn-press/course-builder/save-lesson-settings', $lessonModel, $data ); | |
| 1412 | 1435 | } |
| 1413 | 1436 | |
| 1414 | 1437 | /** |
| 1415 | 1438 | * Save Quiz Settings to QuizPostModel |
| @@ -1461,8 +1484,19 @@ | ||
| 1461 | 1484 | } |
| 1462 | 1485 | $quizModel->save_meta_value_by_key( $key, $value ); |
| 1463 | 1486 | } |
| 1464 | 1487 | } |
| 1488 | + | |
| 1489 | + /** | |
| 1490 | + * Allow addons to persist their own quiz settings fields added to the | |
| 1491 | + * settings metabox tabs when saving from Course Builder. | |
| 1492 | + * | |
| 1493 | + * @param QuizPostModel $quizModel Quiz model being saved. | |
| 1494 | + * @param array $data Submitted settings data. | |
| 1495 | + * | |
| 1496 | + * @since 4.3.8 | |
| 1497 | + */ | |
| 1498 | + do_action( 'learn-press/course-builder/save-quiz-settings', $quizModel, $data ); | |
| 1465 | 1499 | } |
| 1466 | 1500 | |
| 1467 | 1501 | /** |
| 1468 | 1502 | * Update Quiz from Course Builder. |
| @@ -1471,117 +1505,92 @@ | ||
| 1471 | 1505 | * @since 4.3 |
| 1472 | 1506 | * @version 1.0.2 |
| 1473 | 1507 | */ |
| 1474 | 1508 | public function builder_update_quiz() { |
| 1475 | - $response = new LP_REST_Response(); | |
| 1509 | + $response = new Response(); | |
| 1476 | 1510 | $response->data = new stdClass(); |
| 1477 | 1511 | |
| 1478 | 1512 | try { |
| 1479 | 1513 | $data = self::check_valid_quiz(); |
| 1480 | 1514 | $quiz_id = $data['quiz_id'] ?? 0; |
| 1481 | - $title = $data['quiz_title'] ?? null; | |
| 1482 | - $description = $data['quiz_description'] ?? null; | |
| 1515 | + $title = LP_Helper::sanitize_params_submitted( $data['quiz_title'] ?? '' ); | |
| 1516 | + $description = LP_Helper::sanitize_params_submitted( | |
| 1517 | + $data['quiz_description'] ?? '', | |
| 1518 | + 'html' | |
| 1519 | + ); | |
| 1483 | 1520 | $settings = $data['quiz_settings'] ?? false; |
| 1484 | 1521 | $is_elementor = $data['is_elementor'] ?? false; |
| 1485 | 1522 | $return_html = ( $data['return_html'] ?? 'no' ) === 'yes'; |
| 1486 | 1523 | $insert = $data['insert']; |
| 1487 | - $quiz_slug = ! empty( $data['quiz_permalink'] ) | |
| 1488 | - ? sanitize_title( wp_unslash( (string) $data['quiz_permalink'] ) ) | |
| 1489 | - : ''; | |
| 1524 | + $quiz_slug = LP_Helper::sanitize_params_submitted( | |
| 1525 | + $data['quiz_permalink'] ?? '', | |
| 1526 | + 'key' | |
| 1527 | + ); | |
| 1490 | 1528 | $course_id = absint( $data['course_id'] ?? 0 ); |
| 1491 | 1529 | |
| 1492 | 1530 | // Determine target status (draft or publish) |
| 1493 | - $target_status = ! empty( $data['quiz_status'] ) && in_array( $data['quiz_status'], [ 'draft', 'publish' ], true ) | |
| 1494 | - ? sanitize_text_field( $data['quiz_status'] ) | |
| 1495 | - : 'publish'; | |
| 1496 | - $restore_with_custom_slug = false; | |
| 1531 | + $target_status = LP_Helper::sanitize_params_submitted( | |
| 1532 | + $data['quiz_status'] ?? '', | |
| 1533 | + 'key' | |
| 1534 | + ); | |
| 1497 | 1535 | |
| 1536 | + if ( empty( $title ) ) { | |
| 1537 | + throw new Exception( __( 'Quiz title is required', 'learnpress' ) ); | |
| 1538 | + } | |
| 1539 | + | |
| 1498 | 1540 | if ( $insert ) { |
| 1499 | - if ( ! CourseBuilderAccessPolicy::can_create_item_type( LP_QUIZ_CPT ) ) { | |
| 1500 | - throw new Exception( __( 'You are not allowed to create quizzes', 'learnpress' ) ); | |
| 1501 | - } | |
| 1502 | - | |
| 1503 | 1541 | $insert_arg = array( |
| 1504 | - 'post_type' => LP_QUIZ_CPT, | |
| 1505 | - 'post_title' => sanitize_text_field( $title ?? '' ), | |
| 1506 | - 'post_content' => wp_kses_post( $description ?? '' ), | |
| 1542 | + 'post_title' => $title, | |
| 1543 | + 'post_content' => $description, | |
| 1507 | 1544 | 'post_status' => $target_status, |
| 1545 | + 'post_author' => get_current_user_id(), | |
| 1508 | 1546 | ); |
| 1509 | 1547 | |
| 1510 | - if ( ! empty( $quiz_slug ) ) { | |
| 1511 | - $insert_arg['post_name'] = $quiz_slug; | |
| 1512 | - } | |
| 1548 | + $quizPostModelNew = new QuizPostModel( $insert_arg ); | |
| 1549 | + $quizPostModelNew->check_capabilities_create_item_course(); | |
| 1513 | 1550 | |
| 1514 | - $quiz_id = wp_insert_post( $insert_arg, true ); | |
| 1551 | + $quizPostModelNew->save(); | |
| 1552 | + $quiz_id = $quizPostModelNew->ID; | |
| 1515 | 1553 | |
| 1516 | - if ( is_wp_error( $quiz_id ) ) { | |
| 1517 | - throw new Exception( $quiz_id->get_error_message() ); | |
| 1518 | - } | |
| 1519 | - | |
| 1520 | - $quiz_model = QuizPostModel::find( $quiz_id, true ); | |
| 1554 | + $quiz_model = $quizPostModelNew; | |
| 1555 | + $response->message = __( 'Create quiz successfully', 'learnpress' ); | |
| 1521 | 1556 | } else { |
| 1522 | - $quiz_model = $data['quiz_model']; | |
| 1523 | - | |
| 1524 | - if ( ! $quiz_model ) { | |
| 1557 | + $quiz_model = $data['quiz_model'] ?? null; | |
| 1558 | + if ( ! $quiz_model instanceof QuizPostModel ) { | |
| 1525 | 1559 | throw new Exception( __( 'Quiz not found', 'learnpress' ) ); |
| 1526 | 1560 | } |
| 1527 | 1561 | |
| 1562 | + $quiz_model->check_capabilities_update_item_course(); | |
| 1563 | + | |
| 1528 | 1564 | if ( ! $course_id ) { |
| 1529 | 1565 | $course_id = absint( $this->get_course_by_item_id( $quiz_id ) ); |
| 1530 | 1566 | } |
| 1531 | 1567 | |
| 1532 | - // Support for co-instructor. | |
| 1533 | - $co_instructor_ids = get_post_meta( $course_id, '_lp_co_teacher', false ); | |
| 1534 | - $co_instructor_ids = ! empty( $co_instructor_ids ) ? $co_instructor_ids : array(); | |
| 1535 | - | |
| 1536 | - if ( absint( $quiz_model->post_author ) !== get_current_user_id() && ! current_user_can( 'manage_options' ) && ! in_array( get_current_user_id(), $co_instructor_ids ) ) { | |
| 1537 | - throw new Exception( __( 'You are not allowed to update this quiz', 'learnpress' ) ); | |
| 1538 | - } | |
| 1539 | - | |
| 1540 | - $update_arg = array( | |
| 1541 | - 'ID' => $quiz_id, | |
| 1542 | - 'post_type' => LP_QUIZ_CPT, | |
| 1543 | - 'post_status' => $target_status, | |
| 1544 | - ); | |
| 1545 | - | |
| 1546 | - if ( isset( $data['quiz_title'] ) ) { | |
| 1547 | - $update_arg['post_title'] = sanitize_text_field( $title ); | |
| 1548 | - } | |
| 1549 | - | |
| 1550 | - if ( isset( $data['quiz_description'] ) ) { | |
| 1551 | - $update_arg['post_content'] = wp_kses_post( $description ); | |
| 1552 | - } | |
| 1553 | - | |
| 1554 | - if ( ! empty( $quiz_slug ) ) { | |
| 1555 | - $update_arg['post_name'] = $quiz_slug; | |
| 1556 | - } | |
| 1557 | - | |
| 1558 | 1568 | if ( defined( 'ELEMENTOR_VERSION' ) ) { |
| 1559 | 1569 | \Elementor\Plugin::$instance->documents->get( $quiz_id )->set_is_built_with_elementor( ! empty( $is_elementor ) ); |
| 1560 | 1570 | } |
| 1561 | 1571 | |
| 1562 | - $restore_with_custom_slug = $this->prepare_desired_slug_for_restore( $quiz_id, $target_status, $quiz_slug ); | |
| 1563 | - | |
| 1564 | - $update = wp_update_post( $update_arg ); | |
| 1565 | - | |
| 1566 | - if ( is_wp_error( $update ) ) { | |
| 1567 | - throw new Exception( $update->get_error_message() ); | |
| 1572 | + $quiz_model->post_title = $title; | |
| 1573 | + $quiz_model->post_content = $description; | |
| 1574 | + $quiz_model->post_status = $target_status; | |
| 1575 | + if ( ! empty( $quiz_slug ) ) { | |
| 1576 | + $quiz_model->post_name = $quiz_slug; | |
| 1568 | 1577 | } |
| 1569 | 1578 | |
| 1570 | - if ( $restore_with_custom_slug ) { | |
| 1571 | - $this->sync_slug_after_restore( $quiz_id, $quiz_slug ); | |
| 1572 | - } | |
| 1579 | + $quiz_model->save(); | |
| 1573 | 1580 | |
| 1574 | 1581 | if ( $course_id ) { |
| 1575 | - $course_model_cache = CourseModel::find( $course_id, true ); | |
| 1576 | - if ( $course_model_cache ) { | |
| 1577 | - $course_model_cache->sections_items = null; | |
| 1578 | - $course_model_cache->save(); | |
| 1582 | + $courseModelCache = CourseModel::find( $course_id, true ); | |
| 1583 | + if ( $courseModelCache ) { | |
| 1584 | + $courseModelCache->sections_items = null; | |
| 1585 | + $courseModelCache->save(); | |
| 1579 | 1586 | } |
| 1580 | 1587 | } |
| 1588 | + | |
| 1589 | + $response->message = __( 'Update quiz successfully', 'learnpress' ); | |
| 1581 | 1590 | } |
| 1582 | 1591 | |
| 1583 | - if ( $settings && $quiz_model ) { | |
| 1592 | + if ( $settings ) { | |
| 1584 | 1593 | $this->save_quiz_settings_to_model( $quiz_model, $data ); |
| 1585 | 1594 | } |
| 1586 | 1595 | |
| 1587 | 1596 | // Remove quiz from curriculum if status is not public |
| @@ -1588,37 +1597,27 @@ | ||
| 1588 | 1597 | if ( $target_status !== 'publish' ) { |
| 1589 | 1598 | $this->remove_course_item_from_curriculum( $quiz_id, $course_id ); |
| 1590 | 1599 | } |
| 1591 | 1600 | |
| 1592 | - do_action( 'learn-press/course-builder/update-quiz', $data, $quiz_model ); | |
| 1593 | - | |
| 1594 | - $saved_quiz_status = get_post_status( $quiz_id ); | |
| 1595 | - if ( ! is_string( $saved_quiz_status ) || '' === $saved_quiz_status ) { | |
| 1596 | - $saved_quiz_status = $target_status; | |
| 1601 | + $response->status = 'success'; | |
| 1602 | + $response->data->status = $quiz_model->post_status; | |
| 1603 | + $response->data->button_title = $quiz_model->post_status === 'publish' ? | |
| 1604 | + __( 'Update', 'learnpress' ) : | |
| 1605 | + __( 'Publish', 'learnpress' ); | |
| 1606 | + $response->data->quiz_id_new = $insert ? $quiz_id : ''; | |
| 1607 | + if ( $insert && $quiz_id ) { | |
| 1608 | + $response->data->redirect_url = CourseBuilder::get_link_course_builder( CourseBuilderTemplate::MENU_QUIZZES . "/{$quiz_id}" ); | |
| 1597 | 1609 | } |
| 1598 | - | |
| 1599 | - if ( $insert ) { | |
| 1600 | - $response->data->redirect_url = CourseBuilder::get_link_course_builder( | |
| 1601 | - CourseBuilderTemplate::MENU_QUIZZES . "/{$quiz_id}" | |
| 1602 | - ); | |
| 1603 | - } | |
| 1604 | - | |
| 1605 | - $response->status = 'success'; | |
| 1606 | - $response->data->status = $saved_quiz_status; | |
| 1607 | - $response->data->button_title = $saved_quiz_status === 'publish' ? __( 'Update', 'learnpress' ) : __( 'Publish', 'learnpress' ); | |
| 1608 | - $response->message = $target_status === 'draft' | |
| 1610 | + $response->message = $target_status === 'draft' | |
| 1609 | 1611 | ? esc_html__( 'Quiz saved as draft', 'learnpress' ) |
| 1610 | 1612 | : ( $insert ? esc_html__( 'Insert quiz successfully', 'learnpress' ) : esc_html__( 'Update quiz successfully', 'learnpress' ) ); |
| 1611 | 1613 | |
| 1612 | - $saved_quiz_post = get_post( $quiz_id ); | |
| 1613 | - if ( $saved_quiz_post ) { | |
| 1614 | - $response->data->quiz_slug = $saved_quiz_post->post_name; | |
| 1615 | - } | |
| 1614 | + $response->data->quiz_slug = $quiz_model->post_name; | |
| 1616 | 1615 | |
| 1617 | 1616 | $course_id_of_item = $this->get_course_by_item_id( $quiz_id ); |
| 1618 | 1617 | $response->data->permalink_available = false; |
| 1619 | 1618 | $response->data->permalink_notice = $this->get_item_permalink_unavailable_message(); |
| 1620 | - if ( 'publish' === $saved_quiz_status && $course_id_of_item ) { | |
| 1619 | + if ( 'publish' === $quiz_model->post_status && $course_id_of_item ) { | |
| 1621 | 1620 | $course = learn_press_get_course( $course_id_of_item ); |
| 1622 | 1621 | if ( $course ) { |
| 1623 | 1622 | $response->data->permalink_available = true; |
| 1624 | 1623 | $response->data->quiz_permalink = urldecode( $course->get_item_link( $quiz_id ) ); |
| @@ -1624,33 +1623,33 @@ | ||
| 1624 | 1623 | $response->data->quiz_permalink = urldecode( $course->get_item_link( $quiz_id ) ); |
| 1625 | 1624 | } |
| 1626 | 1625 | } |
| 1627 | 1626 | |
| 1627 | + $quiz_model_for_html = QuizPostModel::find( $quiz_id, true ); | |
| 1628 | 1628 | if ( $return_html ) { |
| 1629 | - $quiz_model_new = QuizPostModel::find( $quiz_id, true ); | |
| 1630 | - $response->data->list_item_html = $quiz_model_new | |
| 1631 | - ? BuilderListQuizzesTemplate::render_quiz( $quiz_model_new ) | |
| 1629 | + $response->data->list_item_html = $quiz_model_for_html | |
| 1630 | + ? BuilderListQuizzesTemplate::render_quiz( $quiz_model_for_html ) | |
| 1632 | 1631 | : ''; |
| 1633 | 1632 | |
| 1634 | 1633 | if ( $course_id ) { |
| 1635 | - $course_model_for_html = CourseModel::find( $course_id, true ); | |
| 1636 | - if ( $course_model_for_html && $quiz_model_new ) { | |
| 1634 | + $courseModelForHtml = CourseModel::find( $course_id, true ); | |
| 1635 | + if ( $courseModelForHtml && $quiz_model_for_html ) { | |
| 1637 | 1636 | $item = new stdClass(); |
| 1638 | 1637 | $item->item_id = $quiz_id; |
| 1639 | - $item->title = $quiz_model_new->post_title; | |
| 1638 | + $item->title = $quiz_model_for_html->post_title; | |
| 1640 | 1639 | $item->item_type = LP_QUIZ_CPT; |
| 1641 | 1640 | AdminEditCurriculumTemplate::instance()->context_data = [ 'is_course_builder' => true ]; |
| 1642 | - $response->data->section_item_html = AdminEditCurriculumTemplate::instance()->html_section_item( $course_model_for_html, $item ); | |
| 1641 | + $response->data->section_item_html = AdminEditCurriculumTemplate::instance()->html_section_item( $courseModelForHtml, $item ); | |
| 1643 | 1642 | } |
| 1644 | 1643 | } |
| 1645 | 1644 | } |
| 1646 | 1645 | |
| 1647 | - wp_send_json( $response ); | |
| 1648 | - } catch ( Throwable $th ) { | |
| 1649 | - $response->status = 'error'; | |
| 1650 | - $response->message = $th->getMessage(); | |
| 1651 | - wp_send_json( $response ); | |
| 1646 | + $response->status = Response::STATUS_SUCCESS; | |
| 1647 | + } catch ( Throwable $e ) { | |
| 1648 | + $response->message = $e->getMessage(); | |
| 1652 | 1649 | } |
| 1650 | + | |
| 1651 | + wp_send_json( $response ); | |
| 1653 | 1652 | } |
| 1654 | 1653 | |
| 1655 | 1654 | public function duplicate_quiz() { |
| 1656 | 1655 | $response = new LP_REST_Response(); |
| @@ -1850,93 +1849,75 @@ | ||
| 1850 | 1849 | } |
| 1851 | 1850 | } |
| 1852 | 1851 | |
| 1853 | 1852 | public function builder_update_question() { |
| 1854 | - $response = new LP_REST_Response(); | |
| 1853 | + $response = new Response(); | |
| 1855 | 1854 | $response->data = new stdClass(); |
| 1856 | 1855 | |
| 1857 | 1856 | try { |
| 1858 | 1857 | $data = self::check_valid_question(); |
| 1859 | 1858 | $question_id = $data['question_id'] ?? 0; |
| 1860 | - $title = $data['question_title'] ?? ''; | |
| 1861 | - $description = $data['question_description'] ?? ''; | |
| 1859 | + $title = LP_Helper::sanitize_params_submitted( $data['question_title'] ?? '' ); | |
| 1860 | + $description = LP_Helper::sanitize_params_submitted( | |
| 1861 | + $data['question_description'] ?? '', | |
| 1862 | + 'html' | |
| 1863 | + ); | |
| 1862 | 1864 | $is_elementor = $data['is_elementor'] ?? false; |
| 1863 | 1865 | $return_html = ( $data['return_html'] ?? 'no' ) === 'yes'; |
| 1864 | 1866 | $insert = $data['insert']; |
| 1865 | - $question_slug = ! empty( $data['question_permalink'] ) | |
| 1866 | - ? sanitize_title( wp_unslash( (string) $data['question_permalink'] ) ) | |
| 1867 | - : ''; | |
| 1867 | + $question_slug = LP_Helper::sanitize_params_submitted( | |
| 1868 | + $data['question_permalink'] ?? '', | |
| 1869 | + 'key' | |
| 1870 | + ); | |
| 1868 | 1871 | |
| 1869 | 1872 | // Determine target status (draft or publish) |
| 1870 | - $target_status = ! empty( $data['question_status'] ) && in_array( $data['question_status'], [ 'draft', 'publish' ], true ) | |
| 1871 | - ? sanitize_text_field( $data['question_status'] ) | |
| 1872 | - : 'publish'; | |
| 1873 | + $target_status = LP_Helper::sanitize_params_submitted( | |
| 1874 | + $data['question_status'] ?? '', | |
| 1875 | + 'key' | |
| 1876 | + ); | |
| 1873 | 1877 | |
| 1878 | + if ( empty( $title ) ) { | |
| 1879 | + throw new Exception( __( 'Question title is required', 'learnpress' ) ); | |
| 1880 | + } | |
| 1881 | + | |
| 1874 | 1882 | if ( $insert ) { |
| 1875 | - if ( ! CourseBuilderAccessPolicy::can_create_item_type( LP_QUESTION_CPT ) ) { | |
| 1876 | - throw new Exception( __( 'You are not allowed to create questions', 'learnpress' ) ); | |
| 1877 | - } | |
| 1878 | - | |
| 1879 | 1883 | $insert_arg = array( |
| 1880 | - 'post_type' => LP_QUESTION_CPT, | |
| 1881 | - 'post_title' => sanitize_text_field( $title ?? '' ), | |
| 1882 | - 'post_content' => $description ?? '', | |
| 1884 | + 'post_title' => $title, | |
| 1885 | + 'post_content' => $description, | |
| 1883 | 1886 | 'post_status' => $target_status, |
| 1887 | + 'post_author' => get_current_user_id(), | |
| 1884 | 1888 | ); |
| 1885 | 1889 | |
| 1886 | - if ( ! empty( $question_slug ) ) { | |
| 1887 | - $insert_arg['post_name'] = $question_slug; | |
| 1888 | - } | |
| 1890 | + $questionPostModelNew = new QuestionPostModel( $insert_arg ); | |
| 1891 | + $questionPostModelNew->check_capabilities_create_item_course(); | |
| 1889 | 1892 | |
| 1890 | - $question_id = wp_insert_post( $insert_arg, true ); | |
| 1893 | + $questionPostModelNew->save(); | |
| 1894 | + $question_id = $questionPostModelNew->ID; | |
| 1891 | 1895 | |
| 1892 | - if ( is_wp_error( $question_id ) ) { | |
| 1893 | - throw new Exception( $question_id->get_error_message() ); | |
| 1894 | - } | |
| 1896 | + $question_model = $questionPostModelNew; | |
| 1897 | + $response->message = __( 'Create question successfully', 'learnpress' ); | |
| 1895 | 1898 | } else { |
| 1896 | - $question_model = $data['question_model']; | |
| 1897 | - | |
| 1898 | - if ( ! $question_model ) { | |
| 1899 | + $question_model = $data['question_model'] ?? null; | |
| 1900 | + if ( ! $question_model instanceof QuestionPostModel ) { | |
| 1899 | 1901 | throw new Exception( __( 'Question not found', 'learnpress' ) ); |
| 1900 | 1902 | } |
| 1901 | 1903 | |
| 1902 | - $course_id = $this->get_course_by_item_id( $question_id ); | |
| 1904 | + $question_model->check_capabilities_update_item_course(); | |
| 1903 | 1905 | |
| 1904 | - // Support for co-instructor. | |
| 1905 | - $co_instructor_ids = get_post_meta( $course_id, '_lp_co_teacher', false ); | |
| 1906 | - $co_instructor_ids = ! empty( $co_instructor_ids ) ? $co_instructor_ids : array(); | |
| 1907 | - | |
| 1908 | - if ( absint( $question_model->post_author ) !== get_current_user_id() && ! current_user_can( 'manage_options' ) && ! in_array( get_current_user_id(), $co_instructor_ids ) ) { | |
| 1909 | - throw new Exception( __( 'You are not allowed to update this question', 'learnpress' ) ); | |
| 1910 | - } | |
| 1911 | - | |
| 1912 | - $update_arg = array( | |
| 1913 | - 'ID' => $question_id, | |
| 1914 | - 'post_type' => LP_QUESTION_CPT, | |
| 1915 | - 'post_status' => $target_status, | |
| 1916 | - ); | |
| 1917 | - | |
| 1918 | 1906 | if ( defined( 'ELEMENTOR_VERSION' ) ) { |
| 1919 | 1907 | \Elementor\Plugin::$instance->documents->get( $question_id )->set_is_built_with_elementor( ! empty( $is_elementor ) ); |
| 1920 | 1908 | } |
| 1921 | 1909 | |
| 1922 | - if ( isset( $data['question_title'] ) ) { | |
| 1923 | - $update_arg['post_title'] = sanitize_text_field( $title ); | |
| 1924 | - } | |
| 1925 | - | |
| 1926 | - if ( isset( $data['question_description'] ) ) { | |
| 1927 | - $update_arg['post_content'] = wp_kses_post( $description ); | |
| 1928 | - } | |
| 1929 | - | |
| 1910 | + $question_model->post_title = $title; | |
| 1911 | + $question_model->post_content = $description; | |
| 1912 | + $question_model->post_status = $target_status; | |
| 1930 | 1913 | if ( ! empty( $question_slug ) ) { |
| 1931 | - $update_arg['post_name'] = $question_slug; | |
| 1914 | + $question_model->post_name = $question_slug; | |
| 1932 | 1915 | } |
| 1933 | 1916 | |
| 1934 | - $update = wp_update_post( $update_arg ); | |
| 1917 | + $question_model->save(); | |
| 1935 | 1918 | |
| 1936 | - if ( is_wp_error( $update ) ) { | |
| 1937 | - throw new Exception( $update->get_error_message() ); | |
| 1938 | - } | |
| 1919 | + $response->message = __( 'Update question successfully', 'learnpress' ); | |
| 1939 | 1920 | } |
| 1940 | 1921 | |
| 1941 | 1922 | // Remove question from quizzes if status is not public |
| 1942 | 1923 | if ( $target_status !== 'publish' ) { |
| @@ -1942,48 +1923,39 @@ | ||
| 1942 | 1923 | if ( $target_status !== 'publish' ) { |
| 1943 | 1924 | $this->remove_question_from_assigned_quizzes( $question_id ); |
| 1944 | 1925 | } |
| 1945 | 1926 | |
| 1946 | - do_action( 'learn-press/course-builder/update-question', $data, $question_model ?? null ); | |
| 1927 | + do_action( 'learn-press/course-builder/update-question', $data, $question_model ); | |
| 1947 | 1928 | |
| 1948 | - $saved_question_status = get_post_status( $question_id ); | |
| 1949 | - if ( ! is_string( $saved_question_status ) || '' === $saved_question_status ) { | |
| 1950 | - $saved_question_status = $target_status; | |
| 1929 | + $response->status = 'success'; | |
| 1930 | + $response->data->status = $question_model->post_status; | |
| 1931 | + $response->data->button_title = $question_model->post_status === 'publish' ? | |
| 1932 | + __( 'Update', 'learnpress' ) : | |
| 1933 | + __( 'Publish', 'learnpress' ); | |
| 1934 | + $response->data->question_id_new = $insert ? $question_id : ''; | |
| 1935 | + if ( $insert && $question_id ) { | |
| 1936 | + $response->data->redirect_url = CourseBuilder::get_link_course_builder( CourseBuilderTemplate::MENU_QUESTIONS . "/{$question_id}" ); | |
| 1951 | 1937 | } |
| 1952 | - | |
| 1953 | - if ( $insert ) { | |
| 1954 | - $response->data->redirect_url = CourseBuilder::get_link_course_builder( | |
| 1955 | - CourseBuilderTemplate::MENU_QUESTIONS . "/{$question_id}" | |
| 1956 | - ); | |
| 1957 | - } | |
| 1958 | - | |
| 1959 | - $response->status = 'success'; | |
| 1960 | - $response->data->status = $saved_question_status; | |
| 1961 | - $response->data->button_title = $saved_question_status === 'publish' ? __( 'Update', 'learnpress' ) : __( 'Publish', 'learnpress' ); | |
| 1962 | - | |
| 1963 | 1938 | $response->message = $target_status === 'draft' |
| 1964 | 1939 | ? esc_html__( 'Question saved as draft', 'learnpress' ) |
| 1965 | 1940 | : ( $insert ? esc_html__( 'Insert question successfully', 'learnpress' ) : esc_html__( 'Update question successfully', 'learnpress' ) ); |
| 1966 | 1941 | |
| 1967 | - $saved_question = get_post( $question_id ); | |
| 1968 | - if ( $saved_question ) { | |
| 1969 | - $response->data->question_slug = $saved_question->post_name; | |
| 1970 | - $response->data->question_permalink = get_permalink( $question_id ); | |
| 1971 | - } | |
| 1942 | + $response->data->question_slug = $question_model->post_name; | |
| 1943 | + $response->data->question_permalink = get_permalink( $question_id ); | |
| 1972 | 1944 | |
| 1945 | + $question_model_for_html = QuestionPostModel::find( $question_id, true ); | |
| 1973 | 1946 | if ( $return_html ) { |
| 1974 | - $question_model_new = QuestionPostModel::find( $question_id, true ); | |
| 1975 | - $response->data->list_item_html = $question_model_new | |
| 1976 | - ? BuilderListQuestionsTemplate::render_question( $question_model_new ) | |
| 1947 | + $response->data->list_item_html = $question_model_for_html | |
| 1948 | + ? BuilderListQuestionsTemplate::render_question( $question_model_for_html ) | |
| 1977 | 1949 | : ''; |
| 1978 | 1950 | } |
| 1979 | 1951 | |
| 1980 | - wp_send_json( $response ); | |
| 1981 | - } catch ( Throwable $th ) { | |
| 1982 | - $response->status = 'error'; | |
| 1983 | - $response->message = $th->getMessage(); | |
| 1984 | - wp_send_json( $response ); | |
| 1952 | + $response->status = Response::STATUS_SUCCESS; | |
| 1953 | + } catch ( Throwable $e ) { | |
| 1954 | + $response->message = $e->getMessage(); | |
| 1985 | 1955 | } |
| 1956 | + | |
| 1957 | + wp_send_json( $response ); | |
| 1986 | 1958 | } |
| 1987 | 1959 | |
| 1988 | 1960 | public function move_trash_question() { |
| 1989 | 1961 | $response = new LP_REST_Response(); |
| @@ -2138,16 +2110,16 @@ | ||
| 2138 | 2110 | return; |
| 2139 | 2111 | } |
| 2140 | 2112 | |
| 2141 | 2113 | try { |
| 2142 | - $course_model = CourseModel::find( $course_id, true ); | |
| 2143 | - if ( ! $course_model ) { | |
| 2114 | + $courseModel = CourseModel::find( $course_id, true ); | |
| 2115 | + if ( ! $courseModel ) { | |
| 2144 | 2116 | return; |
| 2145 | 2117 | } |
| 2146 | 2118 | |
| 2147 | - $course_model->sections_items = null; | |
| 2148 | - $course_model->total_items = null; | |
| 2149 | - $course_model->save( true ); | |
| 2119 | + $courseModel->sections_items = null; | |
| 2120 | + $courseModel->total_items = null; | |
| 2121 | + $courseModel->save( true ); | |
| 2150 | 2122 | } catch ( Throwable $e ) { |
| 2151 | 2123 | error_log( __METHOD__ . ': ' . $e->getMessage() ); |
| 2152 | 2124 | } |
| 2153 | 2125 | } |
| @@ -2227,10 +2199,10 @@ | ||
| 2227 | 2199 | throw new Exception( __( 'Permission denied', 'learnpress' ) ); |
| 2228 | 2200 | } |
| 2229 | 2201 | |
| 2230 | 2202 | $hide_instructor_access_admin_screen = ! empty( $data['hide_instructor_access_admin_screen'] ) && $data['hide_instructor_access_admin_screen'] === 'yes' ? 'yes' : 'no'; |
| 2231 | - $logo_remove = ! empty( $data['course_builder_logo_remove'] ) && $data['course_builder_logo_remove'] === 'yes'; | |
| 2232 | - $logo_id = absint( $data['course_builder_logo_id'] ?? 0 ); | |
| 2203 | + $logo_remove = ! empty( $data['course_builder_logo_remove'] ) && $data['course_builder_logo_remove'] === 'yes'; | |
| 2204 | + $logo_id = absint( $data['course_builder_logo_id'] ?? 0 ); | |
| 2233 | 2205 | |
| 2234 | 2206 | if ( $logo_remove ) { |
| 2235 | 2207 | $logo_id = 0; |
| 2236 | 2208 | } |
| @@ -2242,13 +2214,13 @@ | ||
| 2242 | 2214 | if ( $logo_id ) { |
| 2243 | 2215 | $logo_url = wp_get_attachment_image_url( $logo_id, 'full' ); |
| 2244 | 2216 | } |
| 2245 | 2217 | |
| 2246 | - $response->status = 'success'; | |
| 2247 | - $response->message = __( 'Course Builder settings updated.', 'learnpress' ); | |
| 2248 | - $response->data->hide_instructor_access_admin_screen = $hide_instructor_access_admin_screen; | |
| 2249 | - $response->data->course_builder_logo_id = $logo_id; | |
| 2250 | - $response->data->course_builder_logo_url = $logo_url; | |
| 2218 | + $response->status = 'success'; | |
| 2219 | + $response->message = __( 'Course Builder settings updated.', 'learnpress' ); | |
| 2220 | + $response->data->hide_instructor_access_admin_screen = $hide_instructor_access_admin_screen; | |
| 2221 | + $response->data->course_builder_logo_id = $logo_id; | |
| 2222 | + $response->data->course_builder_logo_url = $logo_url; | |
| 2251 | 2223 | |
| 2252 | 2224 | wp_send_json( $response ); |
| 2253 | 2225 | } catch ( Throwable $th ) { |
| 2254 | 2226 | $response->status = 'error'; |