| @@ -582,8 +582,19 @@ | ||
| 582 | 582 | $this->save_assessment_settings_to_model( $courseModel, $data ); |
| 583 | 583 | |
| 584 | 584 | // Author settings |
| 585 | 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 ); | |
| 586 | 597 | } |
| 587 | 598 | |
| 588 | 599 | /** |
| 589 | 600 | * Save general settings to CourseModel |
| @@ -717,8 +728,27 @@ | ||
| 717 | 728 | } |
| 718 | 729 | } |
| 719 | 730 | |
| 720 | 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 | + /** | |
| 721 | 751 | * Save extra info settings to CourseModel |
| 722 | 752 | * |
| 723 | 753 | * @param CoursePostModel $courseModel |
| 724 | 754 | * @param array $data |
| @@ -725,11 +755,10 @@ | ||
| 725 | 755 | */ |
| 726 | 756 | protected function save_extra_settings_to_model( CoursePostModel &$courseModel, array $data ) { |
| 727 | 757 | // Requirements |
| 728 | 758 | if ( isset( $data['_lp_requirements'] ) ) { |
| 729 | - $requirements = ! empty( $data['_lp_requirements'] ) ? explode( ',', $data['_lp_requirements'] ) : []; | |
| 730 | 759 | $requirements = array_filter( |
| 731 | - $requirements, | |
| 760 | + $this->extra_field_to_array( $data['_lp_requirements'] ), | |
| 732 | 761 | function ( $item ) { |
| 733 | 762 | return ! is_null( $item ) && $item !== ''; |
| 734 | 763 | } |
| 735 | 764 | ); |
| @@ -736,11 +765,10 @@ | ||
| 736 | 765 | $courseModel->meta_data->{CoursePostModel::META_KEY_REQUIREMENTS} = array_map( 'sanitize_text_field', array_values( $requirements ) ); |
| 737 | 766 | } |
| 738 | 767 | |
| 739 | 768 | if ( isset( $data['_lp_target_audiences'] ) ) { |
| 740 | - $target_audiences = ! empty( $data['_lp_target_audiences'] ) ? explode( ',', $data['_lp_target_audiences'] ) : []; | |
| 741 | 769 | $target_audiences = array_filter( |
| 742 | - $target_audiences, | |
| 770 | + $this->extra_field_to_array( $data['_lp_target_audiences'] ), | |
| 743 | 771 | function ( $item ) { |
| 744 | 772 | return ! is_null( $item ) && $item !== ''; |
| 745 | 773 | } |
| 746 | 774 | ); |
| @@ -747,11 +775,10 @@ | ||
| 747 | 775 | $courseModel->meta_data->{CoursePostModel::META_KEY_TARGET} = array_map( 'sanitize_text_field', array_values( $target_audiences ) ); |
| 748 | 776 | } |
| 749 | 777 | |
| 750 | 778 | if ( isset( $data['_lp_key_features'] ) ) { |
| 751 | - $key_features = ! empty( $data['_lp_key_features'] ) ? explode( ',', $data['_lp_key_features'] ) : []; | |
| 752 | 779 | $key_features = array_filter( |
| 753 | - $key_features, | |
| 780 | + $this->extra_field_to_array( $data['_lp_key_features'] ), | |
| 754 | 781 | function ( $item ) { |
| 755 | 782 | return ! is_null( $item ) && $item !== ''; |
| 756 | 783 | } |
| 757 | 784 | ); |
| @@ -759,10 +786,10 @@ | ||
| 759 | 786 | } |
| 760 | 787 | |
| 761 | 788 | // FAQs |
| 762 | 789 | if ( isset( $data['_lp_faqs_question'] ) ) { |
| 763 | - $questions = ! empty( $data['_lp_faqs_question'] ) ? explode( ',', $data['_lp_faqs_question'] ) : []; | |
| 764 | - $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'] ) : []; | |
| 765 | 792 | $faqs = []; |
| 766 | 793 | |
| 767 | 794 | if ( ! empty( $questions ) ) { |
| 768 | 795 | foreach ( $questions as $index => $question ) { |
| @@ -1083,8 +1110,10 @@ | ||
| 1083 | 1110 | |
| 1084 | 1111 | try { |
| 1085 | 1112 | $data = self::check_valid_lesson(); |
| 1086 | 1113 | $lesson_id = $data['lesson_id'] ?? 0; |
| 1114 | + $has_title = array_key_exists( 'lesson_title', $data ); | |
| 1115 | + $has_content = array_key_exists( 'lesson_description', $data ); | |
| 1087 | 1116 | $title = LP_Helper::sanitize_params_submitted( $data['lesson_title'] ?? '' ); |
| 1088 | 1117 | $description = LP_Helper::sanitize_params_submitted( |
| 1089 | 1118 | $data['lesson_description'] ?? '', |
| 1090 | 1119 | 'html' |
| @@ -1105,9 +1134,9 @@ | ||
| 1105 | 1134 | 'key' |
| 1106 | 1135 | ); |
| 1107 | 1136 | $restore_with_custom_slug = false; |
| 1108 | 1137 | |
| 1109 | - if ( empty( $title ) ) { | |
| 1138 | + if ( ( $insert || $has_title ) && empty( $title ) ) { | |
| 1110 | 1139 | throw new Exception( __( 'Lesson title is required', 'learnpress' ) ); |
| 1111 | 1140 | } |
| 1112 | 1141 | |
| 1113 | 1142 | if ( $insert ) { |
| @@ -1114,8 +1143,9 @@ | ||
| 1114 | 1143 | $insert_arg = array( |
| 1115 | 1144 | 'post_title' => $title, |
| 1116 | 1145 | 'post_content' => $description, |
| 1117 | 1146 | 'post_status' => $target_status, |
| 1147 | + 'post_author' => get_current_user_id(), | |
| 1118 | 1148 | ); |
| 1119 | 1149 | |
| 1120 | 1150 | $lessonPostModelNew = new LessonPostModel( $insert_arg ); |
| 1121 | 1151 | $lessonPostModelNew->check_capabilities_create_item_course(); |
| @@ -1150,23 +1180,39 @@ | ||
| 1150 | 1180 | 'post_type' => LP_LESSON_CPT, |
| 1151 | 1181 | 'post_status' => $target_status, |
| 1152 | 1182 | );*/ |
| 1153 | 1183 | |
| 1154 | - if ( defined( 'ELEMENTOR_VERSION' ) ) { | |
| 1184 | + if ( defined( 'ELEMENTOR_VERSION' ) && array_key_exists( 'is_elementor', $data ) ) { | |
| 1155 | 1185 | \Elementor\Plugin::$instance->documents->get( $lesson_id )->set_is_built_with_elementor( ! empty( $is_elementor ) ); |
| 1156 | 1186 | } |
| 1157 | 1187 | |
| 1158 | - $lesson_model->post_title = $title; | |
| 1159 | - $lesson_model->post_content = $description; | |
| 1160 | - $lesson_model->post_status = $target_status; | |
| 1188 | + $must_save_lesson = false; | |
| 1189 | + if ( $has_title ) { | |
| 1190 | + $lesson_model->post_title = $title; | |
| 1191 | + $must_save_lesson = true; | |
| 1192 | + } | |
| 1193 | + | |
| 1194 | + if ( $has_content ) { | |
| 1195 | + $lesson_model->post_content = $description; | |
| 1196 | + $must_save_lesson = true; | |
| 1197 | + } | |
| 1198 | + | |
| 1199 | + if ( ! empty( $target_status ) ) { | |
| 1200 | + $lesson_model->post_status = $target_status; | |
| 1201 | + $must_save_lesson = true; | |
| 1202 | + } | |
| 1203 | + | |
| 1161 | 1204 | if ( ! empty( $lesson_slug ) ) { |
| 1162 | 1205 | $lesson_model->post_name = $lesson_slug; |
| 1206 | + $must_save_lesson = true; | |
| 1163 | 1207 | } |
| 1164 | 1208 | |
| 1165 | 1209 | //$restore_with_custom_slug = $this->prepare_desired_slug_for_restore( $lesson_id, $target_status, $lesson_slug ); |
| 1166 | 1210 | |
| 1167 | 1211 | //$update = wp_update_post( $update_arg ); |
| 1168 | - $lesson_model->save(); | |
| 1212 | + if ( $must_save_lesson ) { | |
| 1213 | + $lesson_model->save(); | |
| 1214 | + } | |
| 1169 | 1215 | |
| 1170 | 1216 | /*if ( $restore_with_custom_slug ) { |
| 1171 | 1217 | $this->sync_slug_after_restore( $lesson_id, $lesson_slug ); |
| 1172 | 1218 | }*/ |
| @@ -1186,9 +1232,9 @@ | ||
| 1186 | 1232 | $this->save_lesson_settings_to_model( $lesson_model, $data ); |
| 1187 | 1233 | } |
| 1188 | 1234 | |
| 1189 | 1235 | // Remove lesson from curriculum if status is not public |
| 1190 | - if ( $target_status !== 'publish' ) { | |
| 1236 | + if ( ! empty( $target_status ) && $target_status !== 'publish' ) { | |
| 1191 | 1237 | $this->remove_course_item_from_curriculum( $lesson_id, $course_id ); |
| 1192 | 1238 | } |
| 1193 | 1239 | |
| 1194 | 1240 | $response->status = 'success'; |
| @@ -1213,9 +1259,9 @@ | ||
| 1213 | 1259 | $response->data->lesson_permalink = urldecode( $course->get_item_link( $lesson_id ) ); |
| 1214 | 1260 | } |
| 1215 | 1261 | } |
| 1216 | 1262 | |
| 1217 | - $lesson_model_for_html = LessonPostModel::find( $lesson_id, true ); | |
| 1263 | + $lesson_model_for_html = LessonPostModel::find( $lesson_id, false ); | |
| 1218 | 1264 | if ( $return_html ) { |
| 1219 | 1265 | $response->data->list_item_html = $lesson_model_for_html |
| 1220 | 1266 | ? BuilderListLessonsTemplate::render_lesson( $lesson_model_for_html ) |
| 1221 | 1267 | : ''; |
| @@ -1248,9 +1294,9 @@ | ||
| 1248 | 1294 | try { |
| 1249 | 1295 | $data = self::check_valid_lesson(); |
| 1250 | 1296 | $lesson_id = $data['lesson_id'] ?? 0; |
| 1251 | 1297 | $status = $data['status'] ?? 'trash'; |
| 1252 | - $lesson_model = $data['lesson_model'] ?? []; | |
| 1298 | + $lesson_model = LessonPostModel::find( $lesson_id, false ); | |
| 1253 | 1299 | $lesson_slug = ! empty( $data['lesson_permalink'] ) |
| 1254 | 1300 | ? sanitize_title( wp_unslash( (string) $data['lesson_permalink'] ) ) |
| 1255 | 1301 | : ''; |
| 1256 | 1302 | |
| @@ -1352,9 +1398,9 @@ | ||
| 1352 | 1398 | } |
| 1353 | 1399 | } |
| 1354 | 1400 | |
| 1355 | 1401 | if ( 'delete' !== $status ) { |
| 1356 | - $lesson_model_new = LessonPostModel::find( $lesson_id, true ); | |
| 1402 | + $lesson_model_new = LessonPostModel::find( $lesson_id, false ); | |
| 1357 | 1403 | $response->data->html = $lesson_model_new ? BuilderListLessonsTemplate::render_lesson( $lesson_model_new ) : ''; |
| 1358 | 1404 | } |
| 1359 | 1405 | |
| 1360 | 1406 | $response->status = 'success'; |
| @@ -1368,10 +1414,13 @@ | ||
| 1368 | 1414 | } |
| 1369 | 1415 | |
| 1370 | 1416 | /** |
| 1371 | 1417 | * Save Lesson Settings |
| 1418 | + * @throws Exception | |
| 1372 | 1419 | */ |
| 1373 | 1420 | protected function save_lesson_settings_to_model( LessonPostModel $lessonModel, array $data ) { |
| 1421 | + $must_save = 0; | |
| 1422 | + | |
| 1374 | 1423 | if ( isset( $data['_lp_duration'] ) ) { |
| 1375 | 1424 | $duration = ! empty( $data['_lp_duration'] ) ? str_replace( ',', ' ', $data['_lp_duration'] ) : '0 minute'; |
| 1376 | 1425 | $explode = explode( ' ', $duration ); |
| 1377 | 1426 | $number = (float) $explode[0] < 0 ? 0 : absint( $explode[0] ); |
| @@ -1376,15 +1425,32 @@ | ||
| 1376 | 1425 | $explode = explode( ' ', $duration ); |
| 1377 | 1426 | $number = (float) $explode[0] < 0 ? 0 : absint( $explode[0] ); |
| 1378 | 1427 | $unit = $explode[1] ?? 'minute'; |
| 1379 | 1428 | |
| 1380 | - $lessonModel->save_meta_value_by_key( '_lp_duration', $number . ' ' . $unit ); | |
| 1429 | + $lessonModel->set_meta_value_by_key( $lessonModel::META_KEY_DURATION, $number . ' ' . $unit ); | |
| 1430 | + $must_save = 1; | |
| 1381 | 1431 | } |
| 1382 | 1432 | |
| 1383 | 1433 | if ( isset( $data['_lp_preview'] ) ) { |
| 1384 | 1434 | $enable = $data['_lp_preview'] === 'yes'; |
| 1385 | 1435 | $lessonModel->set_preview( $enable ); |
| 1436 | + $must_save = 1; | |
| 1386 | 1437 | } |
| 1438 | + | |
| 1439 | + if ( $must_save ) { | |
| 1440 | + $lessonModel->save(); | |
| 1441 | + } | |
| 1442 | + | |
| 1443 | + /** | |
| 1444 | + * Allow addons to persist their own lesson settings fields added to the | |
| 1445 | + * settings metabox tabs when saving from Course Builder. | |
| 1446 | + * | |
| 1447 | + * @param LessonPostModel $lessonModel Lesson model being saved. | |
| 1448 | + * @param array $data Submitted settings data. | |
| 1449 | + * | |
| 1450 | + * @since 4.3.8 | |
| 1451 | + */ | |
| 1452 | + do_action( 'learn-press/course-builder/save-lesson-settings', $lessonModel, $data ); | |
| 1387 | 1453 | } |
| 1388 | 1454 | |
| 1389 | 1455 | /** |
| 1390 | 1456 | * Save Quiz Settings to QuizPostModel |
| @@ -1436,8 +1502,19 @@ | ||
| 1436 | 1502 | } |
| 1437 | 1503 | $quizModel->save_meta_value_by_key( $key, $value ); |
| 1438 | 1504 | } |
| 1439 | 1505 | } |
| 1506 | + | |
| 1507 | + /** | |
| 1508 | + * Allow addons to persist their own quiz settings fields added to the | |
| 1509 | + * settings metabox tabs when saving from Course Builder. | |
| 1510 | + * | |
| 1511 | + * @param QuizPostModel $quizModel Quiz model being saved. | |
| 1512 | + * @param array $data Submitted settings data. | |
| 1513 | + * | |
| 1514 | + * @since 4.3.8 | |
| 1515 | + */ | |
| 1516 | + do_action( 'learn-press/course-builder/save-quiz-settings', $quizModel, $data ); | |
| 1440 | 1517 | } |
| 1441 | 1518 | |
| 1442 | 1519 | /** |
| 1443 | 1520 | * Update Quiz from Course Builder. |
| @@ -1482,8 +1559,9 @@ | ||
| 1482 | 1559 | $insert_arg = array( |
| 1483 | 1560 | 'post_title' => $title, |
| 1484 | 1561 | 'post_content' => $description, |
| 1485 | 1562 | 'post_status' => $target_status, |
| 1563 | + 'post_author' => get_current_user_id(), | |
| 1486 | 1564 | ); |
| 1487 | 1565 | |
| 1488 | 1566 | $quizPostModelNew = new QuizPostModel( $insert_arg ); |
| 1489 | 1567 | $quizPostModelNew->check_capabilities_create_item_course(); |
| @@ -1633,9 +1711,9 @@ | ||
| 1633 | 1711 | try { |
| 1634 | 1712 | $data = self::check_valid_quiz(); |
| 1635 | 1713 | $quiz_id = $data['quiz_id'] ?? 0; |
| 1636 | 1714 | $status = $data['status'] ?? 'trash'; |
| 1637 | - $quiz_model = $data['quiz_model'] ?? []; | |
| 1715 | + $quiz_model = QuizPostModel::find( $quiz_id, false ); | |
| 1638 | 1716 | $quiz_slug = ! empty( $data['quiz_permalink'] ) |
| 1639 | 1717 | ? sanitize_title( wp_unslash( (string) $data['quiz_permalink'] ) ) |
| 1640 | 1718 | : ''; |
| 1641 | 1719 | |
| @@ -1737,9 +1815,9 @@ | ||
| 1737 | 1815 | } |
| 1738 | 1816 | } |
| 1739 | 1817 | |
| 1740 | 1818 | if ( 'delete' !== $status ) { |
| 1741 | - $fresh_quiz_model = QuizPostModel::find( $quiz_id, true ); | |
| 1819 | + $fresh_quiz_model = QuizPostModel::find( $quiz_id, false ); | |
| 1742 | 1820 | $response->data->html = $fresh_quiz_model |
| 1743 | 1821 | ? BuilderListQuizzesTemplate::render_quiz( $fresh_quiz_model ) |
| 1744 | 1822 | : ''; |
| 1745 | 1823 | } |
| @@ -1823,8 +1901,9 @@ | ||
| 1823 | 1901 | $insert_arg = array( |
| 1824 | 1902 | 'post_title' => $title, |
| 1825 | 1903 | 'post_content' => $description, |
| 1826 | 1904 | 'post_status' => $target_status, |
| 1905 | + 'post_author' => get_current_user_id(), | |
| 1827 | 1906 | ); |
| 1828 | 1907 | |
| 1829 | 1908 | $questionPostModelNew = new QuestionPostModel( $insert_arg ); |
| 1830 | 1909 | $questionPostModelNew->check_capabilities_create_item_course(); |
| @@ -1903,9 +1982,9 @@ | ||
| 1903 | 1982 | try { |
| 1904 | 1983 | $data = self::check_valid_question(); |
| 1905 | 1984 | $question_id = $data['question_id'] ?? 0; |
| 1906 | 1985 | $status = $data['status'] ?? 'trash'; |
| 1907 | - $question_model = $data['question_model'] ?? []; | |
| 1986 | + $question_model = QuestionPostModel::find( $question_id, false ); | |
| 1908 | 1987 | |
| 1909 | 1988 | if ( ! $question_model ) { |
| 1910 | 1989 | throw new Exception( __( 'Question not found', 'learnpress' ) ); |
| 1911 | 1990 | } |
| @@ -1969,9 +2048,9 @@ | ||
| 1969 | 2048 | $response->data->status = $status; |
| 1970 | 2049 | $response->data->button_title = __( 'Publish', 'learnpress' ); |
| 1971 | 2050 | |
| 1972 | 2051 | if ( 'delete' !== $status ) { |
| 1973 | - $fresh_question_model = QuestionPostModel::find( $question_id, true ); | |
| 2052 | + $fresh_question_model = QuestionPostModel::find( $question_id, false ); | |
| 1974 | 2053 | $response->data->html = $fresh_question_model |
| 1975 | 2054 | ? BuilderListQuestionsTemplate::render_question( $fresh_question_model ) |
| 1976 | 2055 | : ''; |
| 1977 | 2056 | } |