Viewing File: /home/rtsgrob/ww5/wp-content/plugins/learnpress/inc/Ajax/CourseBuilder/CourseBuilderAjax.php
<?php
/**
* class CourseBuilderAjax
*
* @since 4.3
* @version 1.0.0
*/
namespace LearnPress\Ajax\CourseBuilder;
use DateTimeImmutable;
use DateTimeZone;
use Exception;
use LearnPress\Ajax\AbstractAjax;
use LearnPress\CourseBuilder\CourseBuilder;
use LearnPress\CourseBuilder\CourseBuilderAccessPolicy;
use LearnPress\Helpers\Template;
use LearnPress\Models\CourseModel;
use LearnPress\Models\CoursePostModel;
use LearnPress\Models\CourseSectionItemModel;
use LearnPress\Models\LessonPostModel;
use LearnPress\Models\Question\QuestionPostModel;
use LearnPress\Models\QuizPostModel;
use LearnPress\TemplateHooks\Course\AdminEditCurriculumTemplate;
use LearnPress\TemplateHooks\CourseBuilder\Course\BuilderEditCourseTemplate;
use LearnPress\TemplateHooks\CourseBuilder\Course\BuilderListCoursesTemplate;
use LearnPress\TemplateHooks\CourseBuilder\CourseBuilderTemplate;
use LearnPress\TemplateHooks\CourseBuilder\Lesson\BuilderListLessonsTemplate;
use LearnPress\TemplateHooks\CourseBuilder\Question\BuilderListQuestionsTemplate;
use LearnPress\TemplateHooks\CourseBuilder\Question\BuilderQuestionTemplate;
use LearnPress\TemplateHooks\CourseBuilder\Quiz\BuilderListQuizzesTemplate;
use LearnPress\TemplateHooks\CourseBuilder\Quiz\BuilderQuizTemplate;
use LP_Course_CURD;
use LP_Helper;
use LP_Lesson_CURD;
use LP_Question_CURD;
use LP_Quiz_CURD;
use LP_REST_Response;
use LP_Settings;
use stdClass;
use Throwable;
class CourseBuilderAjax extends AbstractAjax {
/**
* Check permissions and validate parameters.
*
* @throws Exception
*
* @since 4.3
* @version 1.0.0
*/
public static function check_valid_course() {
$params = wp_unslash( $_REQUEST['data'] ?? '' );
if ( empty( $params ) ) {
throw new Exception( 'Error: params invalid!' );
}
$params = LP_Helper::json_decode( $params, true );
$course_id = ! empty( $params['course_id'] ) ? (int) $params['course_id'] : 0;
$courseModel = CourseModel::find( $course_id, true );
if ( empty( $courseModel ) ) {
$params['insert'] = true;
$params['course_model'] = '';
} else {
$params['insert'] = false;
$params['course_model'] = $courseModel;
}
return $params;
}
public static function check_valid_lesson() {
$params = wp_unslash( $_REQUEST['data'] ?? '' );
if ( empty( $params ) ) {
throw new Exception( 'Error: params invalid!' );
}
$params = LP_Helper::json_decode( $params, true );
$lesson_id = ! empty( $params['lesson_id'] ) ? (int) $params['lesson_id'] : 0;
$lesson_model = LessonPostModel::find( $lesson_id, true );
if ( empty( $lesson_model ) ) {
$params['insert'] = true;
$params['lesson_model'] = '';
} else {
$params['insert'] = false;
$params['lesson_model'] = $lesson_model;
}
return $params;
}
public static function check_valid_quiz() {
$params = wp_unslash( $_REQUEST['data'] ?? '' );
if ( empty( $params ) ) {
throw new Exception( 'Error: params invalid!' );
}
$params = LP_Helper::json_decode( $params, true );
$quiz_id = ! empty( $params['quiz_id'] ) ? (int) $params['quiz_id'] : 0;
$quiz_model = QuizPostModel::find( $quiz_id, true );
if ( empty( $quiz_model ) ) {
$params['insert'] = true;
} else {
$params['insert'] = false;
$params['quiz_model'] = $quiz_model;
}
return $params;
}
public static function check_valid_question() {
$params = wp_unslash( $_REQUEST['data'] ?? '' );
if ( empty( $params ) ) {
throw new Exception( 'Error: params invalid!' );
}
$params = LP_Helper::json_decode( $params, true );
$question_id = ! empty( $params['question_id'] ) ? (int) $params['question_id'] : 0;
$question_model = QuestionPostModel::find( $question_id, true );
if ( empty( $question_model ) ) {
$params['insert'] = true;
} else {
$params['insert'] = false;
$params['question_model'] = $question_model;
}
return $params;
}
/**
* Prepare desired slug when restoring a trashed post with a new permalink.
*
* WordPress stores `_wp_desired_post_slug` on trash and may restore that value
* when status changes from `trash` to a non-trash status.
*
* @param int $post_id
* @param string $target_status
* @param string $requested_slug
*
* @return bool
*/
protected function prepare_desired_slug_for_restore( int $post_id, string $target_status, string $requested_slug ): bool {
if ( $post_id <= 0 || '' === $requested_slug ) {
return false;
}
if ( ! in_array( $target_status, [ 'publish', 'draft' ], true ) ) {
return false;
}
if ( 'trash' !== get_post_status( $post_id ) ) {
return false;
}
update_post_meta( $post_id, '_wp_desired_post_slug', $requested_slug );
return true;
}
/**
* Ensure restored post keeps requested slug after trash status transition.
*
* @param int $post_id
* @param string $requested_slug
*
* @return void
*/
protected function sync_slug_after_restore( int $post_id, string $requested_slug ): void {
if ( $post_id <= 0 || '' === $requested_slug ) {
return;
}
$post = get_post( $post_id );
if ( ! $post || 'trash' === $post->post_status ) {
return;
}
if ( $post->post_name === $requested_slug ) {
return;
}
wp_update_post(
[
'ID' => $post_id,
'post_name' => $requested_slug,
]
);
}
/**
* Permalink notice when lesson/quiz is not assigned to a course.
*
* @return string
*/
protected function get_item_permalink_unavailable_message(): string {
return __( 'Permalink is only available if the item is already assigned to a course.', 'learnpress' );
}
/**
* Check whether a post status is public.
*
* @param string $status
*
* @return bool
*/
protected function is_public_post_status( string $status ): bool {
$status_object = get_post_status_object( $status );
return (bool) ( $status_object && ! empty( $status_object->public ) );
}
/**
* Normalize target course status based on role/capability and current status.
* Mirrors wp-admin behavior for instructors without publish capability.
*
* @param string $requested_status
* @param bool $insert
* @param CourseModel|null $courseModel
*
* @return string
*/
protected function normalize_course_status_for_save( string $requested_status, bool $insert, ?CourseModel $courseModel ): string {
$allowed_statuses = [ 'publish', 'future', 'draft', 'pending', 'private' ];
if ( ! in_array( $requested_status, $allowed_statuses, true ) ) {
$requested_status = 'draft';
}
$is_admin_user = current_user_can( ADMIN_ROLE );
if ( $is_admin_user ) {
return $requested_status;
}
$is_instructor_user = current_user_can( LP_TEACHER_ROLE );
$required_review = LP_Settings::get_option( 'required_review', 'yes' ) === 'yes';
$can_publish_course = current_user_can( 'publish_' . LP_COURSE_CPT . 's' );
$current_status = $insert ? 'auto-draft' : (string) ( $courseModel->post_status ?? 'draft' );
$is_public_state = $this->is_public_post_status( $current_status );
// Require moderation for instructors when review is enabled.
if ( $is_instructor_user && $required_review && ! $is_public_state && in_array( $requested_status, [ 'publish', 'private', 'future' ], true ) ) {
return 'pending';
}
// Keep wp-admin behavior: users without publish capability submit for review.
if ( ! $can_publish_course && ! $is_public_state && in_array( $requested_status, [ 'publish', 'private', 'future' ], true ) ) {
return 'pending';
}
return $requested_status;
}
/**
* Normalize publish date from Course Builder UI datetime value.
*
* @param string $datetime_value
*
* @return array<string, string>|null
*/
protected function normalize_course_post_date_for_save( string $datetime_value ): ?array {
$datetime_value = trim( $datetime_value );
if ( '' === $datetime_value ) {
return null;
}
$timezone = wp_timezone();
$formats = [ 'Y-m-d\TH:i', 'Y-m-d\TH:i:s', 'Y-m-d H:i:s' ];
$parsed = null;
foreach ( $formats as $format ) {
$date_candidate = DateTimeImmutable::createFromFormat( $format, $datetime_value, $timezone );
if ( ! $date_candidate instanceof DateTimeImmutable ) {
continue;
}
$errors = DateTimeImmutable::getLastErrors();
if ( is_array( $errors ) && ( ! empty( $errors['warning_count'] ) || ! empty( $errors['error_count'] ) ) ) {
continue;
}
$parsed = $date_candidate;
break;
}
if ( ! $parsed instanceof DateTimeImmutable ) {
return null;
}
$gmt_timezone = new DateTimeZone( 'UTC' );
return [
'post_date' => $parsed->format( 'Y-m-d H:i:s' ),
'post_date_gmt' => $parsed->setTimezone( $gmt_timezone )->format( 'Y-m-d H:i:s' ),
];
}
/**
* Save Course.
*
* @since 4.3
* @version 1.0.1
*/
public function save_courses() {
$response = new LP_REST_Response();
$response->data = new stdClass();
try {
$data = self::check_valid_course();
$course_id = $data['course_id'] ?? 0;
$settings = $data['course_settings'] ?? false;
$insert = $data['insert'];
$course_title = isset( $data['course_title'] ) ? trim( sanitize_text_field( wp_unslash( (string) $data['course_title'] ) ) ) : '';
$course_status_requested = ! empty( $data['course_status'] ) ? sanitize_text_field( $data['course_status'] ) : 'publish';
$course_visibility = ! empty( $data['course_visibility'] ) ? sanitize_key( $data['course_visibility'] ) : '';
$course_password_raw = isset( $data['course_password'] ) ? wp_unslash( (string) $data['course_password'] ) : '';
$course_password = sanitize_text_field( $course_password_raw );
$course_post_date_raw = ! empty( $data['course_post_date'] ) ? sanitize_text_field( $data['course_post_date'] ) : '';
$course_post_date_data = $this->normalize_course_post_date_for_save( $course_post_date_raw );
$post_password_to_update = null;
if ( '' === $course_title ) {
throw new Exception( __( 'Course title is required.', 'learnpress' ) );
}
if ( $insert ) {
// Check user capability before insert
if ( ! current_user_can( 'edit_lp_courses' ) ) {
throw new Exception( __( 'You are not allowed to create courses', 'learnpress' ) );
}
$course_status = $this->normalize_course_status_for_save( $course_status_requested, true, null );
$categories = ! empty( $data['course_categories'] ) ? array_map( 'absint', explode( ',', $data['course_categories'] ) ) : array();
$tags = ! empty( $data['course_tags'] ) ? array_map( 'absint', explode( ',', $data['course_tags'] ) ) : array();
$insert_post_data = array(
'post_type' => LP_COURSE_CPT,
'post_title' => $course_title,
'post_content' => Template::sanitize_html_content( $data['course_description'] ?? '' ),
'post_status' => $course_status,
'tax_input' => array(
'course_category' => $categories,
'course_tag' => $tags,
),
);
if ( ! empty( $course_post_date_data['post_date'] ) ) {
$insert_post_data['post_date'] = $course_post_date_data['post_date'];
$insert_post_data['post_date_gmt'] = $course_post_date_data['post_date_gmt'];
}
if ( in_array( $course_visibility, [ 'public', 'private' ], true ) ) {
$insert_post_data['post_password'] = '';
} elseif ( 'password' === $course_visibility ) {
if ( '' === $course_password ) {
throw new Exception( __( 'Password is required for password protected visibility.', 'learnpress' ) );
}
$insert_post_data['post_password'] = $course_password;
}
$course_id = wp_insert_post( $insert_post_data, true );
if ( is_wp_error( $course_id ) ) {
throw new Exception( $course_id->get_error_message() );
}
// Load the newly created course as CourseModel
$courseModel = CourseModel::find( $course_id, true );
if ( ! $courseModel ) {
throw new Exception( __( 'Failed to load course model', 'learnpress' ) );
}
} else {
$courseModel = $data['course_model'];
$course_status = $this->normalize_course_status_for_save( $course_status_requested, false, $courseModel );
$co_instructor_ids = $courseModel->get_meta_value_by_key( '_lp_co_teacher', [] );
if ( absint( $courseModel->post_author ) !== get_current_user_id() &&
! current_user_can( 'manage_options' ) &&
! in_array( get_current_user_id(), $co_instructor_ids ) ) {
throw new Exception( __( 'You are not allowed to update this course', 'learnpress' ) );
}
$courseModel->post_status = $course_status;
if ( ! empty( $course_post_date_data['post_date'] ) ) {
$courseModel->post_date = $course_post_date_data['post_date'];
$courseModel->post_date_gmt = $course_post_date_data['post_date_gmt'];
}
if ( in_array( $course_visibility, [ 'public', 'private' ], true ) ) {
$post_password_to_update = '';
} elseif ( 'password' === $course_visibility ) {
if ( '' !== $course_password ) {
$post_password_to_update = $course_password;
} else {
$existing_post = get_post( $courseModel->ID );
if ( $existing_post && ! empty( $existing_post->post_password ) ) {
$post_password_to_update = (string) $existing_post->post_password;
} else {
throw new Exception( __( 'Password is required for password protected visibility.', 'learnpress' ) );
}
}
}
if ( '' !== $course_title ) {
$categories = ! empty( $data['course_categories'] ) ? array_map( 'absint', explode( ',', $data['course_categories'] ) ) : array();
$tags = ! empty( $data['course_tags'] ) ? array_map( 'absint', explode( ',', $data['course_tags'] ) ) : array();
$courseModel->post_title = $course_title;
$courseModel->post_content = Template::sanitize_html_content( $data['course_description'] ?? '' );
wp_set_post_terms( $courseModel->ID, $categories, 'course_category' );
wp_set_post_terms( $courseModel->ID, $tags, 'course_tag' );
}
// Update permalink/slug if provided
if ( ! empty( $data['course_permalink'] ) ) {
$new_slug = sanitize_title( $data['course_permalink'] );
if ( $new_slug && $new_slug !== $courseModel->post_name ) {
wp_update_post(
array(
'ID' => $courseModel->ID,
'post_name' => $new_slug,
)
);
$courseModel->post_name = $new_slug;
}
}
$course_id = $courseModel->ID;
}
if ( $settings ) {
$this->save_course_settings_to_model( $courseModel, $data );
}
if ( ! empty( $courseModel->meta_data ) ) {
$coursePostModel = new CoursePostModel( $courseModel );
foreach ( $courseModel->meta_data as $meta_key => $meta_value ) {
$coursePostModel->save_meta_value_by_key( $meta_key, $meta_value );
}
$coursePostModel->save();
}
// Handle course thumbnail - AFTER coursePostModel->save() to avoid being overwritten
if ( isset( $data['course_thumbnail_id'] ) ) {
$thumbnail_id = absint( $data['course_thumbnail_id'] );
if ( $thumbnail_id > 0 ) {
$result = set_post_thumbnail( $course_id, $thumbnail_id );
} else {
$result = delete_post_thumbnail( $course_id );
}
$current_thumbnail = get_post_thumbnail_id( $course_id );
}
if ( null !== $post_password_to_update ) {
wp_update_post(
array(
'ID' => $course_id,
'post_password' => $post_password_to_update,
)
);
}
// Use persisted status after save to avoid UI drift when WP normalizes status by capability.
$saved_course_status = get_post_status( $course_id );
if ( ! is_string( $saved_course_status ) || '' === $saved_course_status ) {
$saved_course_status = $course_status;
}
$response->status = 'success';
$response->message = $insert ? __( 'Insert course successfully!', 'learnpress' ) : __( 'Update course successfully!', 'learnpress' );
$response->data->status = $saved_course_status;
$response->data->button_title = 'publish' === $saved_course_status
? __( 'Update', 'learnpress' )
: ( 'pending' === $saved_course_status ? __( 'Submit for Review', 'learnpress' ) : __( 'Publish', 'learnpress' ) );
$response->data->course_id_new = $insert ? $course_id : '';
// Return the actual saved permalink data (important if WordPress auto-generated a unique slug)
$saved_post = get_post( $course_id );
if ( $saved_post ) {
$visibility = 'public';
if ( 'private' === $saved_post->post_status ) {
$visibility = 'private';
} elseif ( ! empty( $saved_post->post_password ) ) {
$visibility = 'password';
}
$response->data->visibility = $visibility;
$response->data->course_slug = $saved_post->post_name;
$response->data->course_permalink = get_permalink( $course_id );
}
// Return full redirect URL for new courses
if ( $insert && $course_id ) {
$response->data->redirect_url = CourseBuilder::get_link_course_builder( "courses/{$course_id}" );
}
wp_send_json( $response );
} catch ( Throwable $th ) {
$response->message = $th->getMessage();
wp_send_json( $response );
}
}
/**
* Save Course Settings only (from Settings tab).
*
* @since 4.3
* @version 1.0.0
*/
public function save_course_settings() {
$response = new LP_REST_Response();
$response->data = new stdClass();
try {
$data = self::check_valid_course();
$course_id = $data['course_id'] ?? 0;
$insert = $data['insert'];
$courseModel = $data['course_model'];
if ( $insert || empty( $courseModel ) ) {
throw new Exception( __( 'Course not found. Please save the course first.', 'learnpress' ) );
}
$co_instructor_ids = $courseModel->get_meta_value_by_key( '_lp_co_teacher', [] );
if ( absint( $courseModel->post_author ) !== get_current_user_id() &&
! current_user_can( 'manage_options' ) &&
! in_array( get_current_user_id(), $co_instructor_ids ) ) {
throw new Exception( __( 'You are not allowed to update this course', 'learnpress' ) );
}
// Save course settings
$this->save_course_settings_to_model( $courseModel, $data );
// Save meta data
if ( ! empty( $courseModel->meta_data ) ) {
$coursePostModel = new CoursePostModel( $courseModel );
foreach ( $courseModel->meta_data as $meta_key => $meta_value ) {
$coursePostModel->save_meta_value_by_key( $meta_key, $meta_value );
}
$coursePostModel->save();
}
$response->status = 'success';
$response->message = __( 'Settings saved successfully!', 'learnpress' );
wp_send_json( $response );
} catch ( Throwable $th ) {
$response->status = 'error';
$response->message = $th->getMessage();
wp_send_json( $response );
}
}
/**
* Save all course settings to CourseModel
*
* @param CoursePostModel $courseModel
* @param array $data
*
* @throws Exception
*/
public function save_course_settings_to_model( CoursePostModel &$courseModel, array $data ) {
// General settings
$this->save_general_settings_to_model( $courseModel, $data );
// Offline course settings
$this->save_offline_settings_to_model( $courseModel, $data );
// Price settings
$this->save_price_settings_to_model( $courseModel, $data );
// Extra info settings
$this->save_extra_settings_to_model( $courseModel, $data );
// Assessment settings
$this->save_assessment_settings_to_model( $courseModel, $data );
// Author settings
$this->save_author_settings_to_model( $courseModel, $data );
}
/**
* Save general settings to CourseModel
*
* @param CoursePostModel $courseModel
* @param array $data
*/
protected function save_general_settings_to_model( CoursePostModel &$courseModel, array $data ) {
if ( isset( $data['_lp_duration'] ) ) {
$duration_value = ! empty( $data['_lp_duration'] ) ? str_replace( ',', ' ', $data['_lp_duration'] ) : '0 minute';
$explode = explode( ' ', $duration_value );
$number = (float) $explode[0] < 0 ? 0 : absint( $explode[0] );
$unit = $explode[1] ?? 'minute';
$courseModel->meta_data->{CoursePostModel::META_KEY_DURATION} = $number . ' ' . $unit;
}
$checkbox_fields = [
'_lp_block_expire_duration' => CoursePostModel::META_KEY_BLOCK_EXPIRE_DURATION,
'_lp_block_finished' => CoursePostModel::META_KEY_BLOCK_FINISH,
'_lp_allow_course_repurchase' => CoursePostModel::META_KEY_ALLOW_COURSE_REPURCHASE,
'_lp_has_finish' => CoursePostModel::META_KEY_HAS_FINISH,
'_lp_featured' => CoursePostModel::META_KEY_FEATURED,
];
foreach ( $checkbox_fields as $key => $meta_key ) {
if ( isset( $data[ $key ] ) ) {
$courseModel->meta_data->{$meta_key} = $data[ $key ] === 'yes' ? 'yes' : '';
}
}
$simple_fields = [
'_lp_course_repurchase_option' => CoursePostModel::META_KEY_COURSE_REPURCHASE_OPTION,
'_lp_level' => CoursePostModel::META_KEY_LEVEL,
'_lp_featured_review' => CoursePostModel::META_KEY_FEATURED_REVIEW,
'_lp_external_link_buy_course' => CoursePostModel::META_KEY_EXTERNAL_LINK_BY_COURSE,
];
foreach ( $simple_fields as $key => $meta_key ) {
if ( isset( $data[ $key ] ) ) {
$courseModel->meta_data->{$meta_key} = sanitize_text_field( $data[ $key ] );
}
}
$numeric_fields = [
'_lp_students' => CoursePostModel::META_KEY_STUDENTS,
'_lp_max_students' => CoursePostModel::META_KEY_MAX_STUDENTS,
'_lp_retake_count' => CoursePostModel::META_KEY_RETAKE_COUNT,
];
foreach ( $numeric_fields as $key => $meta_key ) {
if ( isset( $data[ $key ] ) ) {
$value = absint( $data[ $key ] );
$courseModel->meta_data->{$meta_key} = $value;
}
}
}
/**
* Save offline course settings to CourseModel
*
* @param CoursePostModel $courseModel
* @param array $data
*/
protected function save_offline_settings_to_model( CoursePostModel &$courseModel, array $data ) {
if ( isset( $data['_lp_offline_course'] ) ) {
$courseModel->meta_data->{CoursePostModel::META_KEY_OFFLINE_COURSE} = $data['_lp_offline_course'] === 'yes' ? 'yes' : '';
}
if ( isset( $data['_lp_offline_lesson_count'] ) ) {
$courseModel->meta_data->{CoursePostModel::META_KEY_OFFLINE_LESSON_COUNT} = absint( $data['_lp_offline_lesson_count'] );
}
if ( isset( $data['_lp_deliver_type'] ) ) {
$courseModel->meta_data->{CoursePostModel::META_KEY_DELIVER} = sanitize_text_field( $data['_lp_deliver_type'] );
}
if ( isset( $data['_lp_address'] ) ) {
$courseModel->meta_data->{CoursePostModel::META_KEY_ADDRESS} = sanitize_text_field( $data['_lp_address'] );
}
}
/**
* Save price settings to CourseModel
*
* @param CoursePostModel $courseModel
* @param array $data
*/
protected function save_price_settings_to_model( CoursePostModel &$courseModel, array $data ) {
// Regular price
if ( isset( $data['_lp_regular_price'] ) ) {
$regular_price = floatval( $data['_lp_regular_price'] );
if ( $regular_price < 0 ) {
$regular_price = '';
}
$courseModel->meta_data->{CoursePostModel::META_KEY_REGULAR_PRICE} = $regular_price;
}
// Sale price
if ( isset( $data['_lp_sale_price'] ) ) {
$sale_price = $data['_lp_sale_price'] !== '' ? floatval( $data['_lp_sale_price'] ) : '';
$regular_price = $courseModel->get_regular_price();
if ( $sale_price !== '' && $sale_price > $regular_price ) {
$sale_price = '';
}
$courseModel->meta_data->{CoursePostModel::META_KEY_SALE_PRICE} = $sale_price;
}
// Sale dates
if ( isset( $data['_lp_sale_start'] ) ) {
$courseModel->meta_data->{CoursePostModel::META_KEY_SALE_START} = sanitize_text_field( $data['_lp_sale_start'] );
}
if ( isset( $data['_lp_sale_end'] ) ) {
$courseModel->meta_data->{CoursePostModel::META_KEY_SALE_END} = sanitize_text_field( $data['_lp_sale_end'] );
}
// Price prefix/suffix
if ( isset( $data['_lp_price_prefix'] ) ) {
$courseModel->meta_data->{CoursePostModel::META_KEY_PRICE_PREFIX} = sanitize_text_field( $data['_lp_price_prefix'] );
}
if ( isset( $data['_lp_price_suffix'] ) ) {
$courseModel->meta_data->{CoursePostModel::META_KEY_PRICE_SUFFIX} = sanitize_text_field( $data['_lp_price_suffix'] );
}
// No required enroll
if ( isset( $data['_lp_no_required_enroll'] ) ) {
$courseModel->meta_data->{CoursePostModel::META_KEY_NO_REQUIRED_ENROLL} = $data['_lp_no_required_enroll'] === 'yes' ? 'yes' : '';
}
}
/**
* Save extra info settings to CourseModel
*
* @param CoursePostModel $courseModel
* @param array $data
*/
protected function save_extra_settings_to_model( CoursePostModel &$courseModel, array $data ) {
// Requirements
if ( isset( $data['_lp_requirements'] ) ) {
$requirements = ! empty( $data['_lp_requirements'] ) ? explode( ',', $data['_lp_requirements'] ) : [];
$requirements = array_filter(
$requirements,
function ( $item ) {
return ! is_null( $item ) && $item !== '';
}
);
$courseModel->meta_data->{CoursePostModel::META_KEY_REQUIREMENTS} = array_map( 'sanitize_text_field', array_values( $requirements ) );
}
if ( isset( $data['_lp_target_audiences'] ) ) {
$target_audiences = ! empty( $data['_lp_target_audiences'] ) ? explode( ',', $data['_lp_target_audiences'] ) : [];
$target_audiences = array_filter(
$target_audiences,
function ( $item ) {
return ! is_null( $item ) && $item !== '';
}
);
$courseModel->meta_data->{CoursePostModel::META_KEY_TARGET} = array_map( 'sanitize_text_field', array_values( $target_audiences ) );
}
if ( isset( $data['_lp_key_features'] ) ) {
$key_features = ! empty( $data['_lp_key_features'] ) ? explode( ',', $data['_lp_key_features'] ) : [];
$key_features = array_filter(
$key_features,
function ( $item ) {
return ! is_null( $item ) && $item !== '';
}
);
$courseModel->meta_data->{CoursePostModel::META_KEY_FEATURES} = array_map( 'sanitize_text_field', array_values( $key_features ) );
}
// FAQs
if ( isset( $data['_lp_faqs_question'] ) ) {
$questions = ! empty( $data['_lp_faqs_question'] ) ? explode( ',', $data['_lp_faqs_question'] ) : [];
$answers = ! empty( $data['_lp_faqs_answer'] ) ? explode( ',', $data['_lp_faqs_answer'] ) : [];
$faqs = [];
if ( ! empty( $questions ) ) {
foreach ( $questions as $index => $question ) {
$clean_question = trim( $question );
if ( ! empty( $clean_question ) ) {
$answer_content = $answers[ $index ] ?? '';
$faqs[] = [ sanitize_text_field( $clean_question ), wp_kses_post( $answer_content ) ];
}
}
}
$courseModel->meta_data->{CoursePostModel::META_KEY_FAQS} = $faqs;
}
}
/**
* Save assessment settings to CourseModel
*
* @param CoursePostModel $courseModel
* @param array $data
*/
protected function save_assessment_settings_to_model( CoursePostModel &$courseModel, array $data ) {
// Course result evaluation type
if ( isset( $data['_lp_course_result'] ) ) {
$courseModel->meta_data->{CoursePostModel::META_KEY_EVALUATION_TYPE} = sanitize_text_field( $data['_lp_course_result'] );
}
// Passing condition
if ( isset( $data['_lp_passing_condition'] ) ) {
$passing_condition = floatval( $data['_lp_passing_condition'] );
if ( $passing_condition < 0 ) {
$passing_condition = 0;
} elseif ( $passing_condition > 100 ) {
$passing_condition = 100;
}
$courseModel->meta_data->{CoursePostModel::META_KEY_PASSING_CONDITION} = $passing_condition;
}
}
/**
* Save author settings to CourseModel
*
* @param CoursePostModel $courseModel
* @param array $data
*/
protected function save_author_settings_to_model( CoursePostModel &$courseModel, array $data ) {
if ( ! isset( $data['_post_author'] ) ) {
return;
}
$new_author_id = absint( $data['_post_author'] );
if ( $new_author_id <= 0 || $new_author_id === (int) $courseModel->post_author ) {
return;
}
$courseModel->meta_data->_post_author = $new_author_id;
$courseModel->post_author = $new_author_id;
}
/**
* Duplicate for course.
*
*/
public function duplicate_course() {
$response = new LP_REST_Response();
$response->data = new stdClass();
try {
$data = self::check_valid_course();
$course_id = $data['course_id'] ?? 0;
$courseModel = $data['course_model'];
if ( absint( $courseModel->post_author ) !== get_current_user_id() && ! current_user_can( 'manage_options' ) ) {
throw new Exception( __( 'You are not allowed to duplicate this course', 'learnpress' ) );
}
if ( ! function_exists( 'learn_press_duplicate_post' ) ) {
require_once LP_PLUGIN_PATH . 'inc/admin/lp-admin-functions.php';
}
$curd = new LP_Course_CURD();
$new_item_id = $curd->duplicate(
$course_id,
array(
'exclude_meta' => array(
'order-pending',
'order-processing',
'order-completed',
'order-cancelled',
'order-failed',
'count_enrolled_users',
'_lp_sample_data',
'_lp_retake_count',
),
)
);
if ( is_wp_error( $new_item_id ) ) {
throw new Exception( $new_item_id->get_error_message() );
}
$courseModel = CourseModel::find( $new_item_id, true );
$html = BuilderListCoursesTemplate::render_course( $courseModel );
$response->status = 'success';
$response->data->html = $html;
$response->data->course_id_new = $new_item_id;
$response->data->redirect_url = CourseBuilder::get_link_course_builder( "courses/{$new_item_id}" );
$response->message = __( 'Course duplicated successfully', 'learnpress' );
wp_send_json( $response );
} catch ( Throwable $th ) {
$response->status = 'error';
$response->message = $th->getMessage();
wp_send_json( $response );
}
}
public function move_trash_course() {
$response = new LP_REST_Response();
$response->data = new stdClass();
try {
$data = self::check_valid_course();
$course_id = $data['course_id'] ?? 0;
$courseModel = $data['course_model'];
$status = $data['status'] ?? 'trash';
$co_instructor_ids = get_post_meta( $course_id, '_lp_co_teacher', false );
$co_instructor_ids = ! empty( $co_instructor_ids ) ? $co_instructor_ids : array();
if ( absint( $courseModel->post_author ) !== get_current_user_id() && ! current_user_can( 'manage_options' ) && ! in_array( get_current_user_id(), $co_instructor_ids ) ) {
throw new Exception( __( 'You are not allowed to delete this course', 'learnpress' ) );
}
if ( $status === 'delete' ) {
if ( ! current_user_can( 'manage_options' ) ) {
throw new Exception( __( 'You are not allowed to delete this course', 'learnpress' ) );
}
wp_delete_post( $course_id, true );
$message = __( 'Course has been deleted', 'learnpress' );
} elseif ( $status === 'draft' ) {
$update = wp_update_post(
array(
'ID' => $course_id,
'post_type' => LP_COURSE_CPT,
'post_status' => 'draft',
)
);
if ( ! $update ) {
throw new Exception( __( 'Course cannot be moved to draft', 'learnpress' ) );
}
$message = __( 'Course has been moved to draft', 'learnpress' );
} else {
// Store original slug before trashing (wp_trash_post adds __trashed suffix)
$original_slug = $courseModel->post_name;
$delete = wp_trash_post( $course_id );
if ( ! $delete ) {
throw new Exception( __( 'Course cannot be moved to trash', 'learnpress' ) );
}
// Restore original slug after trashing
if ( $original_slug ) {
wp_update_post(
array(
'ID' => $course_id,
'post_name' => $original_slug,
)
);
}
$message = __( 'Course moved to trash', 'learnpress' );
}
$response->status = 'success';
$response->data->button_title = __( 'Publish', 'learnpress' );
$response->data->status = $status;
$response->message = $message;
wp_send_json( $response );
} catch ( Throwable $th ) {
$response->status = 'error';
$response->message = $th->getMessage();
wp_send_json( $response );
}
}
public function add_course_category() {
$response = new LP_REST_Response();
$response->data = new stdClass();
try {
$data = self::check_valid_course();
$course_id = absint( $data['course_id'] ?? 0 );
if ( $course_id > 0 ) {
if ( ! CourseBuilderAccessPolicy::can_edit_course_by_id( $course_id ) ) {
throw new Exception( __( 'You are not allowed to update this course', 'learnpress' ) );
}
} elseif ( ! CourseBuilderAccessPolicy::can_create_item_type( LP_COURSE_CPT ) ) {
throw new Exception( __( 'You are not allowed to create courses', 'learnpress' ) );
}
if ( ! current_user_can( 'edit_lp_courses' ) ) {
throw new Exception( __( 'You are not allowed to create categories', 'learnpress' ) );
}
$name = sanitize_text_field( $data['name'] ?? '' );
$parent = isset( $data['parent'] ) ? (int) $data['parent'] : 0;
if ( $parent < 0 ) {
$parent = 0;
}
$term = wp_insert_term( $name, 'course_category', array( 'parent' => $parent ) );
if ( is_wp_error( $term ) ) {
throw new Exception( $term->get_error_message() );
}
$term_id = $term['term_id'];
$html = sprintf(
'<li id="in-course_category-%1$s" class="popular-category">
<label class="selectit">
<input value="%1$s" type="checkbox" name="tax_input[course_category][]" id="in-course_category-%1$s" checked="checked">
%2$s
</label>
</li>',
esc_attr( $term_id ),
esc_html( $name )
);
$response->status = 'success';
$response->data->html = $html;
$response->data->term_id = $term_id;
$response->data->parent = $parent;
$response->message = __( 'Insert category successfully!', 'learnpress' );
wp_send_json( $response );
} catch ( Throwable $th ) {
$response->status = 'error';
$response->message = $th->getMessage();
wp_send_json( $response );
}
}
public function add_course_tag() {
$response = new LP_REST_Response();
$response->data = new stdClass();
try {
$data = self::check_valid_course();
$course_id = absint( $data['course_id'] ?? 0 );
if ( $course_id > 0 ) {
if ( ! CourseBuilderAccessPolicy::can_edit_course_by_id( $course_id ) ) {
throw new Exception( __( 'You are not allowed to update this course', 'learnpress' ) );
}
} elseif ( ! CourseBuilderAccessPolicy::can_create_item_type( LP_COURSE_CPT ) ) {
throw new Exception( __( 'You are not allowed to create courses', 'learnpress' ) );
}
if ( ! current_user_can( 'edit_lp_courses' ) ) {
throw new Exception( __( 'You are not allowed to create tags', 'learnpress' ) );
}
$name = sanitize_text_field( wp_unslash( $data['name'] ?? '' ) );
$term = wp_insert_term( $name, 'course_tag', array() );
if ( is_wp_error( $term ) ) {
throw new Exception( $term->get_error_message() );
}
$html = BuilderEditCourseTemplate::instance()->input_checkbox_tag_item( $term['term_id'], $name, false );
$response->status = 'success';
$response->data->html = $html;
$response->message = __( 'Insert term successfully!', 'learnpress' );
wp_send_json( $response );
} catch ( Throwable $th ) {
$response->status = 'error';
$response->message = $th->getMessage();
wp_send_json( $response );
}
}
/**
* Duplicate for lesson.
*
*/
public function duplicate_lesson() {
$response = new LP_REST_Response();
$response->data = new stdClass();
try {
$data = self::check_valid_lesson();
$lesson_id = $data['lesson_id'] ?? 0;
$lesson_model = $data['lesson_model'];
if ( absint( $lesson_model->post_author ) !== get_current_user_id() && ! current_user_can( 'manage_options' ) ) {
throw new Exception( __( 'You are not allowed to duplicate this lesson', 'learnpress' ) );
}
if ( ! function_exists( 'learn_press_duplicate_post' ) ) {
require_once LP_PLUGIN_PATH . 'inc/admin/lp-admin-functions.php';
}
$duplicate_args = apply_filters( 'learn-press/duplicate-post-args', array( 'post_status' => 'publish' ) );
$curd = new LP_Lesson_CURD();
$new_item_id = $curd->duplicate( $lesson_id, $duplicate_args );
if ( is_wp_error( $new_item_id ) ) {
throw new Exception( $new_item_id->get_error_message() );
}
$lesson_model_new = LessonPostModel::find( $new_item_id, true );
$html = BuilderListLessonsTemplate::render_lesson( $lesson_model_new );
$response->status = 'success';
$response->data->html = $html;
$response->message = __( 'Lesson duplicated successfully', 'learnpress' );
wp_send_json( $response );
} catch ( Throwable $th ) {
$response->status = 'error';
$response->message = $th->getMessage();
wp_send_json( $response );
}
}
/**
* Update Lesson from Course Builder.
* Supports partial updates (title only, description only, or settings only).
*
* @since 4.3
* @version 1.0.2
*/
public function builder_update_lesson() {
$response = new LP_REST_Response();
$response->data = new stdClass();
try {
$data = self::check_valid_lesson();
$lesson_id = $data['lesson_id'] ?? 0;
$title = $data['lesson_title'] ?? null;
$description = $data['lesson_description'] ?? null;
$settings = $data['lesson_settings'] ?? false;
$is_elementor = $data['is_elementor'] ?? false;
$return_html = ( $data['return_html'] ?? 'no' ) === 'yes';
$insert = $data['insert'];
$lesson_slug = ! empty( $data['lesson_permalink'] )
? sanitize_title( wp_unslash( (string) $data['lesson_permalink'] ) )
: '';
$course_id = absint( $data['course_id'] ?? 0 );
// Determine target status (draft or publish)
$target_status = ! empty( $data['lesson_status'] ) && in_array( $data['lesson_status'], [ 'draft', 'publish' ], true )
? sanitize_text_field( $data['lesson_status'] )
: 'publish';
$restore_with_custom_slug = false;
if ( $insert ) {
if ( ! CourseBuilderAccessPolicy::can_create_item_type( LP_LESSON_CPT ) ) {
throw new Exception( __( 'You are not allowed to create lessons', 'learnpress' ) );
}
$insert_arg = array(
'post_type' => LP_LESSON_CPT,
'post_title' => sanitize_text_field( $title ?? '' ),
'post_content' => Template::sanitize_html_content( $description ?? '' ),
'post_status' => $target_status,
);
if ( ! empty( $lesson_slug ) ) {
$insert_arg['post_name'] = $lesson_slug;
}
$lesson_id = wp_insert_post( $insert_arg, true );
if ( is_wp_error( $lesson_id ) ) {
throw new Exception( $lesson_id->get_error_message() );
}
$lesson_model = LessonPostModel::find( $lesson_id, true );
} else {
$lesson_model = $data['lesson_model'];
if ( ! $lesson_model ) {
throw new Exception( __( 'Lesson not found', 'learnpress' ) );
}
if ( ! $course_id ) {
$course_id = absint( $this->get_course_by_item_id( $lesson_id ) );
}
// Support for co-instructor.
$co_instructor_ids = get_post_meta( $course_id, '_lp_co_teacher', false );
$co_instructor_ids = ! empty( $co_instructor_ids ) ? $co_instructor_ids : array();
if ( absint( $lesson_model->post_author ) !== get_current_user_id() && ! current_user_can( 'manage_options' ) && ! in_array( get_current_user_id(), $co_instructor_ids ) ) {
throw new Exception( __( 'You are not allowed to update this lesson', 'learnpress' ) );
}
$update_arg = array(
'ID' => $lesson_id,
'post_type' => LP_LESSON_CPT,
'post_status' => $target_status,
);
if ( defined( 'ELEMENTOR_VERSION' ) ) {
\Elementor\Plugin::$instance->documents->get( $lesson_id )->set_is_built_with_elementor( ! empty( $is_elementor ) );
}
if ( isset( $data['lesson_title'] ) ) {
$update_arg['post_title'] = sanitize_text_field( $title );
}
if ( isset( $data['lesson_description'] ) ) {
$update_arg['post_content'] = Template::sanitize_html_content( $description ?? '' );
}
if ( ! empty( $lesson_slug ) ) {
$update_arg['post_name'] = $lesson_slug;
}
$restore_with_custom_slug = $this->prepare_desired_slug_for_restore( $lesson_id, $target_status, $lesson_slug );
$update = wp_update_post( $update_arg );
if ( is_wp_error( $update ) ) {
throw new Exception( $update->get_error_message() );
}
if ( $restore_with_custom_slug ) {
$this->sync_slug_after_restore( $lesson_id, $lesson_slug );
}
if ( $course_id ) {
$courseModelCache = CourseModel::find( $course_id, true );
if ( $courseModelCache ) {
$courseModelCache->sections_items = null;
$courseModelCache->save();
}
}
}
if ( $settings && $lesson_model ) {
$this->save_lesson_settings_to_model( $lesson_model, $data );
}
// Remove lesson from curriculum if status is not public
if ( $target_status !== 'publish' ) {
$this->remove_course_item_from_curriculum( $lesson_id, $course_id );
}
do_action( 'learn-press/course-builder/update-lesson', $data, $lesson_model );
$saved_lesson_status = get_post_status( $lesson_id );
if ( ! is_string( $saved_lesson_status ) || '' === $saved_lesson_status ) {
$saved_lesson_status = $target_status;
}
$response->status = 'success';
$response->data->status = $saved_lesson_status;
$response->data->button_title = $saved_lesson_status === 'publish' ? __( 'Update', 'learnpress' ) : __( 'Publish', 'learnpress' );
$response->data->lesson_id_new = $insert ? $lesson_id : '';
$response->message = $target_status === 'draft'
? esc_html__( 'Lesson saved as draft', 'learnpress' )
: ( $insert ? esc_html__( 'Insert lesson successfully', 'learnpress' ) : esc_html__( 'Update lesson successfully', 'learnpress' ) );
$saved_lesson_post = get_post( $lesson_id );
if ( $saved_lesson_post ) {
$response->data->lesson_slug = $saved_lesson_post->post_name;
}
$course_id_of_item = $this->get_course_by_item_id( $lesson_id );
$response->data->permalink_available = false;
$response->data->permalink_notice = $this->get_item_permalink_unavailable_message();
if ( 'publish' === $saved_lesson_status && $course_id_of_item ) {
$course = learn_press_get_course( $course_id_of_item );
if ( $course ) {
$response->data->permalink_available = true;
$response->data->lesson_permalink = urldecode( $course->get_item_link( $lesson_id ) );
}
}
$lesson_model_for_html = LessonPostModel::find( $lesson_id, true );
if ( $return_html ) {
$response->data->list_item_html = $lesson_model_for_html
? BuilderListLessonsTemplate::render_lesson( $lesson_model_for_html )
: '';
if ( $course_id ) {
$courseModelForHtml = CourseModel::find( $course_id, true );
if ( $courseModelForHtml && $lesson_model_for_html ) {
$item = new stdClass();
$item->item_id = $lesson_id;
$item->title = $lesson_model_for_html->post_title;
$item->item_type = LP_LESSON_CPT;
AdminEditCurriculumTemplate::instance()->context_data = [ 'is_course_builder' => true ];
$response->data->section_item_html = AdminEditCurriculumTemplate::instance()->html_section_item( $courseModelForHtml, $item );
}
}
}
wp_send_json( $response );
} catch ( Throwable $th ) {
$response->status = 'error';
$response->message = $th->getMessage();
wp_send_json( $response );
}
}
public function move_trash_lesson() {
$response = new LP_REST_Response();
$response->data = new stdClass();
try {
$data = self::check_valid_lesson();
$lesson_id = $data['lesson_id'] ?? 0;
$status = $data['status'] ?? 'trash';
$lesson_model = $data['lesson_model'] ?? [];
$lesson_slug = ! empty( $data['lesson_permalink'] )
? sanitize_title( wp_unslash( (string) $data['lesson_permalink'] ) )
: '';
if ( ! $lesson_model ) {
throw new Exception( __( 'Lesson not found', 'learnpress' ) );
}
if ( absint( $lesson_model->post_author ) !== get_current_user_id() && ! current_user_can( 'manage_options' ) ) {
throw new Exception( __( 'You are not allowed to delete this lesson', 'learnpress' ) );
}
$course_id = absint( $data['course_id'] ?? 0 );
if ( ! $course_id ) {
$course_id = absint( $this->get_course_by_item_id( $lesson_id ) );
}
if ( $status === 'trash' ) {
$original_slug = (string) get_post_field( 'post_name', $lesson_id );
$move_trash = wp_trash_post( $lesson_id );
if ( is_wp_error( $move_trash ) ) {
throw new Exception( esc_html__( 'Cannot move this lesson to trash', 'learnpress' ) );
}
if ( '' !== $original_slug ) {
wp_update_post(
[
'ID' => $lesson_id,
'post_name' => $original_slug,
]
);
}
$message = esc_html__( 'This lesson has been moved to trash.', 'learnpress' );
} elseif ( $status === 'delete' ) {
if ( $lesson_model->post_status !== 'trash' ) {
throw new Exception( esc_html__( 'Lesson must be trashed before deleting.', 'learnpress' ) );
}
$delete = wp_delete_post( $lesson_id );
if ( is_wp_error( $delete ) ) {
throw new Exception( esc_html__( 'Cannot delete this lesson.', 'learnpress' ) );
}
$message = esc_html__( 'Delete this lesson successfully', 'learnpress' );
} elseif ( in_array( $status, [ 'publish', 'draft' ], true ) ) {
$restore_with_custom_slug = $this->prepare_desired_slug_for_restore( $lesson_id, $status, $lesson_slug );
$update_args = [
'ID' => $lesson_id,
'post_type' => LP_LESSON_CPT,
'post_status' => $status,
];
if ( '' !== $lesson_slug ) {
$update_args['post_name'] = $lesson_slug;
}
$update = wp_update_post( $update_args );
if ( ! $update ) {
if ( 'draft' === $status ) {
throw new Exception( __( 'Lesson cannot be moved to draft', 'learnpress' ) );
}
throw new Exception( __( 'Lesson cannot be moved to publish', 'learnpress' ) );
}
if ( $restore_with_custom_slug ) {
$this->sync_slug_after_restore( $lesson_id, $lesson_slug );
}
$message = 'draft' === $status
? __( 'Lesson has been moved to draft', 'learnpress' )
: __( 'Lesson has been moved to publish', 'learnpress' );
} else {
throw new Exception( __( 'Invalid lesson status transition', 'learnpress' ) );
}
$saved_lesson_status = get_post_status( $lesson_id );
if ( ! is_string( $saved_lesson_status ) || '' === $saved_lesson_status ) {
$saved_lesson_status = $status;
}
if ( $saved_lesson_status !== 'publish' ) {
$this->remove_course_item_from_curriculum( $lesson_id, $course_id );
}
$response->data->status = $saved_lesson_status;
$response->data->button_title = 'publish' === $saved_lesson_status ? __( 'Update', 'learnpress' ) : __( 'Publish', 'learnpress' );
$response->data->lesson_slug = (string) get_post_field( 'post_name', $lesson_id );
$response->data->permalink_available = false;
$response->data->permalink_notice = $this->get_item_permalink_unavailable_message();
$course_id_of_item = $this->get_course_by_item_id( $lesson_id );
if ( 'publish' === $saved_lesson_status && $course_id_of_item ) {
$course = learn_press_get_course( $course_id_of_item );
if ( $course ) {
$response->data->permalink_available = true;
$response->data->lesson_permalink = urldecode( $course->get_item_link( $lesson_id ) );
}
}
if ( 'delete' !== $status ) {
$lesson_model_new = LessonPostModel::find( $lesson_id, true );
$response->data->html = $lesson_model_new ? BuilderListLessonsTemplate::render_lesson( $lesson_model_new ) : '';
}
$response->status = 'success';
$response->message = $message;
wp_send_json( $response );
} catch ( Throwable $th ) {
$response->status = 'error';
$response->message = $th->getMessage();
wp_send_json( $response );
}
}
/**
* Save Lesson Settings
*/
protected function save_lesson_settings_to_model( LessonPostModel $lessonModel, array $data ) {
if ( isset( $data['_lp_duration'] ) ) {
$duration = ! empty( $data['_lp_duration'] ) ? str_replace( ',', ' ', $data['_lp_duration'] ) : '0 minute';
$explode = explode( ' ', $duration );
$number = (float) $explode[0] < 0 ? 0 : absint( $explode[0] );
$unit = $explode[1] ?? 'minute';
$lessonModel->save_meta_value_by_key( '_lp_duration', $number . ' ' . $unit );
}
if ( isset( $data['_lp_preview'] ) ) {
$enable = $data['_lp_preview'] === 'yes';
$lessonModel->set_preview( $enable );
}
}
/**
* Save Quiz Settings to QuizPostModel
*
* @param QuizPostModel $quizModel
* @param array $data
*
* @since 4.3
* @version 1.0.0
*/
protected function save_quiz_settings_to_model( QuizPostModel $quizModel, array $data ) {
if ( isset( $data['_lp_duration'] ) ) {
$duration = ! empty( $data['_lp_duration'] ) ? str_replace( ',', ' ', $data['_lp_duration'] ) : '0 minute';
$explode = explode( ' ', $duration );
$number = (float) $explode[0] < 0 ? 0 : absint( $explode[0] );
$unit = $explode[1] ?? 'minute';
$quizModel->save_meta_value_by_key( '_lp_duration', $number . ' ' . $unit );
}
$checkbox_keys = [
'_lp_instant_check',
'_lp_negative_marking',
'_lp_minus_skip_questions',
'_lp_review',
'_lp_show_correct_review',
'_lp_show_check_answer',
'_lp_show_hint',
];
foreach ( $checkbox_keys as $key ) {
if ( isset( $data[ $key ] ) ) {
$value = $data[ $key ] === 'yes' ? 'yes' : 'no';
$quizModel->save_meta_value_by_key( $key, $value );
}
}
$numeric_keys = [
'_lp_passing_grade',
'_lp_retake_count',
'_lp_pagination',
];
foreach ( $numeric_keys as $key ) {
if ( isset( $data[ $key ] ) ) {
$value = absint( $data[ $key ] );
if ( '_lp_passing_grade' === $key && $value > 100 ) {
$value = 100;
}
$quizModel->save_meta_value_by_key( $key, $value );
}
}
}
/**
* Update Quiz from Course Builder.
* Supports partial updates (title only, description only, or settings only).
*
* @since 4.3
* @version 1.0.2
*/
public function builder_update_quiz() {
$response = new LP_REST_Response();
$response->data = new stdClass();
try {
$data = self::check_valid_quiz();
$quiz_id = $data['quiz_id'] ?? 0;
$title = $data['quiz_title'] ?? null;
$description = $data['quiz_description'] ?? null;
$settings = $data['quiz_settings'] ?? false;
$is_elementor = $data['is_elementor'] ?? false;
$return_html = ( $data['return_html'] ?? 'no' ) === 'yes';
$insert = $data['insert'];
$quiz_slug = ! empty( $data['quiz_permalink'] )
? sanitize_title( wp_unslash( (string) $data['quiz_permalink'] ) )
: '';
$course_id = absint( $data['course_id'] ?? 0 );
// Determine target status (draft or publish)
$target_status = ! empty( $data['quiz_status'] ) && in_array( $data['quiz_status'], [ 'draft', 'publish' ], true )
? sanitize_text_field( $data['quiz_status'] )
: 'publish';
$restore_with_custom_slug = false;
if ( $insert ) {
if ( ! CourseBuilderAccessPolicy::can_create_item_type( LP_QUIZ_CPT ) ) {
throw new Exception( __( 'You are not allowed to create quizzes', 'learnpress' ) );
}
$insert_arg = array(
'post_type' => LP_QUIZ_CPT,
'post_title' => sanitize_text_field( $title ?? '' ),
'post_content' => Template::sanitize_html_content( $description ?? '' ),
'post_status' => $target_status,
);
if ( ! empty( $quiz_slug ) ) {
$insert_arg['post_name'] = $quiz_slug;
}
$quiz_id = wp_insert_post( $insert_arg, true );
if ( is_wp_error( $quiz_id ) ) {
throw new Exception( $quiz_id->get_error_message() );
}
$quiz_model = QuizPostModel::find( $quiz_id, true );
} else {
$quiz_model = $data['quiz_model'];
if ( ! $quiz_model ) {
throw new Exception( __( 'Quiz not found', 'learnpress' ) );
}
if ( ! $course_id ) {
$course_id = absint( $this->get_course_by_item_id( $quiz_id ) );
}
// Support for co-instructor.
$co_instructor_ids = get_post_meta( $course_id, '_lp_co_teacher', false );
$co_instructor_ids = ! empty( $co_instructor_ids ) ? $co_instructor_ids : array();
if ( absint( $quiz_model->post_author ) !== get_current_user_id() && ! current_user_can( 'manage_options' ) && ! in_array( get_current_user_id(), $co_instructor_ids ) ) {
throw new Exception( __( 'You are not allowed to update this quiz', 'learnpress' ) );
}
$update_arg = array(
'ID' => $quiz_id,
'post_type' => LP_QUIZ_CPT,
'post_status' => $target_status,
);
if ( isset( $data['quiz_title'] ) ) {
$update_arg['post_title'] = sanitize_text_field( $title );
}
if ( isset( $data['quiz_description'] ) ) {
$update_arg['post_content'] = Template::sanitize_html_content( $description ?? '' );
}
if ( ! empty( $quiz_slug ) ) {
$update_arg['post_name'] = $quiz_slug;
}
if ( defined( 'ELEMENTOR_VERSION' ) ) {
\Elementor\Plugin::$instance->documents->get( $quiz_id )->set_is_built_with_elementor( ! empty( $is_elementor ) );
}
$restore_with_custom_slug = $this->prepare_desired_slug_for_restore( $quiz_id, $target_status, $quiz_slug );
$update = wp_update_post( $update_arg );
if ( is_wp_error( $update ) ) {
throw new Exception( $update->get_error_message() );
}
if ( $restore_with_custom_slug ) {
$this->sync_slug_after_restore( $quiz_id, $quiz_slug );
}
if ( $course_id ) {
$courseModelCache = CourseModel::find( $course_id, true );
if ( $courseModelCache ) {
$courseModelCache->sections_items = null;
$courseModelCache->save();
}
}
}
if ( $settings && $quiz_model ) {
$this->save_quiz_settings_to_model( $quiz_model, $data );
}
// Remove quiz from curriculum if status is not public
if ( $target_status !== 'publish' ) {
$this->remove_course_item_from_curriculum( $quiz_id, $course_id );
}
do_action( 'learn-press/course-builder/update-quiz', $data, $quiz_model );
$saved_quiz_status = get_post_status( $quiz_id );
if ( ! is_string( $saved_quiz_status ) || '' === $saved_quiz_status ) {
$saved_quiz_status = $target_status;
}
if ( $insert ) {
$response->data->redirect_url = CourseBuilder::get_link_course_builder(
CourseBuilderTemplate::MENU_QUIZZES . "/{$quiz_id}"
);
}
$response->status = 'success';
$response->data->status = $saved_quiz_status;
$response->data->button_title = $saved_quiz_status === 'publish' ? __( 'Update', 'learnpress' ) : __( 'Publish', 'learnpress' );
$response->message = $target_status === 'draft'
? esc_html__( 'Quiz saved as draft', 'learnpress' )
: ( $insert ? esc_html__( 'Insert quiz successfully', 'learnpress' ) : esc_html__( 'Update quiz successfully', 'learnpress' ) );
$saved_quiz_post = get_post( $quiz_id );
if ( $saved_quiz_post ) {
$response->data->quiz_slug = $saved_quiz_post->post_name;
}
$course_id_of_item = $this->get_course_by_item_id( $quiz_id );
$response->data->permalink_available = false;
$response->data->permalink_notice = $this->get_item_permalink_unavailable_message();
if ( 'publish' === $saved_quiz_status && $course_id_of_item ) {
$course = learn_press_get_course( $course_id_of_item );
if ( $course ) {
$response->data->permalink_available = true;
$response->data->quiz_permalink = urldecode( $course->get_item_link( $quiz_id ) );
}
}
if ( $return_html ) {
$quiz_model_new = QuizPostModel::find( $quiz_id, true );
$response->data->list_item_html = $quiz_model_new
? BuilderListQuizzesTemplate::render_quiz( $quiz_model_new )
: '';
if ( $course_id ) {
$courseModelForHtml = CourseModel::find( $course_id, true );
if ( $courseModelForHtml && $quiz_model_new ) {
$item = new stdClass();
$item->item_id = $quiz_id;
$item->title = $quiz_model_new->post_title;
$item->item_type = LP_QUIZ_CPT;
AdminEditCurriculumTemplate::instance()->context_data = [ 'is_course_builder' => true ];
$response->data->section_item_html = AdminEditCurriculumTemplate::instance()->html_section_item( $courseModelForHtml, $item );
}
}
}
wp_send_json( $response );
} catch ( Throwable $th ) {
$response->status = 'error';
$response->message = $th->getMessage();
wp_send_json( $response );
}
}
public function duplicate_quiz() {
$response = new LP_REST_Response();
$response->data = new stdClass();
try {
$data = self::check_valid_quiz();
$quiz_id = $data['quiz_id'] ?? 0;
$quiz_model = $data['quiz_model'];
if ( absint( $quiz_model->post_author ) !== get_current_user_id() && ! current_user_can( 'manage_options' ) ) {
throw new Exception( __( 'You are not allowed to duplicate this quiz', 'learnpress' ) );
}
if ( ! function_exists( 'learn_press_duplicate_post' ) ) {
require_once LP_PLUGIN_PATH . 'inc/admin/lp-admin-functions.php';
}
$duplicate_args = apply_filters( 'learn-press/duplicate-post-args', array( 'post_status' => 'publish' ) );
$curd = new LP_Quiz_CURD();
$new_item_id = $curd->duplicate( $quiz_id, $duplicate_args );
if ( is_wp_error( $new_item_id ) ) {
throw new Exception( $new_item_id->get_error_message() );
}
$response->status = 'success';
$response->data->redirect_url = CourseBuilder::get_link_course_builder( CourseBuilderTemplate::MENU_QUIZZES . "/{$new_item_id}" );
$response->message = __( 'Quiz duplicated successfully', 'learnpress' );
wp_send_json( $response );
} catch ( Throwable $th ) {
$response->status = 'error';
$response->message = $th->getMessage();
wp_send_json( $response );
}
}
public function move_trash_quiz() {
$response = new LP_REST_Response();
$response->data = new stdClass();
try {
$data = self::check_valid_quiz();
$quiz_id = $data['quiz_id'] ?? 0;
$status = $data['status'] ?? 'trash';
$quiz_model = $data['quiz_model'] ?? [];
$quiz_slug = ! empty( $data['quiz_permalink'] )
? sanitize_title( wp_unslash( (string) $data['quiz_permalink'] ) )
: '';
if ( ! $quiz_model ) {
throw new Exception( __( 'Quiz not found', 'learnpress' ) );
}
if ( absint( $quiz_model->post_author ) !== get_current_user_id() && ! current_user_can( 'manage_options' ) ) {
throw new Exception( __( 'You are not allowed to delete this quiz', 'learnpress' ) );
}
$course_id = absint( $data['course_id'] ?? 0 );
if ( ! $course_id ) {
$course_id = absint( $this->get_course_by_item_id( $quiz_id ) );
}
if ( $status === 'trash' ) {
$original_slug = (string) get_post_field( 'post_name', $quiz_id );
$move_trash = wp_trash_post( $quiz_id );
if ( is_wp_error( $move_trash ) ) {
throw new Exception( esc_html__( 'Cannot move this quiz to trash', 'learnpress' ) );
}
if ( '' !== $original_slug ) {
wp_update_post(
[
'ID' => $quiz_id,
'post_name' => $original_slug,
]
);
}
$message = esc_html__( 'This quiz has been moved to trash.', 'learnpress' );
} elseif ( $status === 'delete' ) {
if ( $quiz_model->post_status !== 'trash' ) {
throw new Exception( esc_html__( 'Quiz must be trashed before deleting.', 'learnpress' ) );
}
$delete = wp_delete_post( $quiz_id );
if ( is_wp_error( $delete ) ) {
throw new Exception( esc_html__( 'Cannot delete this quiz.', 'learnpress' ) );
}
$message = esc_html__( 'Delete this quiz successfully', 'learnpress' );
} elseif ( in_array( $status, [ 'publish', 'draft' ], true ) ) {
$restore_with_custom_slug = $this->prepare_desired_slug_for_restore( $quiz_id, $status, $quiz_slug );
$update_args = [
'ID' => $quiz_id,
'post_type' => LP_QUIZ_CPT,
'post_status' => $status,
];
if ( '' !== $quiz_slug ) {
$update_args['post_name'] = $quiz_slug;
}
$update = wp_update_post( $update_args );
if ( ! $update ) {
if ( 'draft' === $status ) {
throw new Exception( __( 'Quiz cannot be moved to draft', 'learnpress' ) );
}
throw new Exception( __( 'Quiz cannot be moved to publish', 'learnpress' ) );
}
if ( $restore_with_custom_slug ) {
$this->sync_slug_after_restore( $quiz_id, $quiz_slug );
}
$message = 'draft' === $status
? __( 'Quiz has been moved to draft', 'learnpress' )
: __( 'Quiz has been moved to publish', 'learnpress' );
} else {
throw new Exception( __( 'Invalid quiz status transition', 'learnpress' ) );
}
$saved_quiz_status = get_post_status( $quiz_id );
if ( ! is_string( $saved_quiz_status ) || '' === $saved_quiz_status ) {
$saved_quiz_status = $status;
}
if ( $saved_quiz_status !== 'publish' ) {
$this->remove_course_item_from_curriculum( $quiz_id, $course_id );
}
$response->data->status = $saved_quiz_status;
$response->data->button_title = 'publish' === $saved_quiz_status ? __( 'Update', 'learnpress' ) : __( 'Publish', 'learnpress' );
$response->data->quiz_slug = (string) get_post_field( 'post_name', $quiz_id );
$response->data->permalink_available = false;
$response->data->permalink_notice = $this->get_item_permalink_unavailable_message();
$course_id_of_item = $this->get_course_by_item_id( $quiz_id );
if ( 'publish' === $saved_quiz_status && $course_id_of_item ) {
$course = learn_press_get_course( $course_id_of_item );
if ( $course ) {
$response->data->permalink_available = true;
$response->data->quiz_permalink = urldecode( $course->get_item_link( $quiz_id ) );
}
}
if ( 'delete' !== $status ) {
$fresh_quiz_model = QuizPostModel::find( $quiz_id, true );
$response->data->html = $fresh_quiz_model
? BuilderListQuizzesTemplate::render_quiz( $fresh_quiz_model )
: '';
}
$response->status = 'success';
$response->message = $message;
wp_send_json( $response );
} catch ( Throwable $th ) {
$response->status = 'error';
$response->message = $th->getMessage();
wp_send_json( $response );
}
}
public function duplicate_question() {
$response = new LP_REST_Response();
$response->data = new stdClass();
try {
$data = self::check_valid_question();
$question_id = $data['question_id'] ?? 0;
$question_model = $data['question_model'];
if ( absint( $question_model->post_author ) !== get_current_user_id() && ! current_user_can( 'manage_options' ) ) {
throw new Exception( __( 'You are not allowed to duplicate this question', 'learnpress' ) );
}
if ( ! function_exists( 'learn_press_duplicate_post' ) ) {
require_once LP_PLUGIN_PATH . 'inc/admin/lp-admin-functions.php';
}
$duplicate_args = apply_filters( 'learn-press/duplicate-post-args', array( 'post_status' => 'publish' ) );
$curd = new LP_Question_CURD();
$new_item_id = $curd->duplicate( $question_id, $duplicate_args );
if ( is_wp_error( $new_item_id ) ) {
throw new Exception( $new_item_id->get_error_message() );
}
$response->status = 'success';
$response->data->redirect_url = CourseBuilder::get_link_course_builder( CourseBuilderTemplate::MENU_QUESTIONS . "/{$new_item_id}" );
$response->message = __( 'Question duplicated successfully', 'learnpress' );
wp_send_json( $response );
} catch ( Throwable $th ) {
$response->status = 'error';
$response->message = $th->getMessage();
wp_send_json( $response );
}
}
public function builder_update_question() {
$response = new LP_REST_Response();
$response->data = new stdClass();
try {
$data = self::check_valid_question();
$question_id = $data['question_id'] ?? 0;
$title = $data['question_title'] ?? '';
$description = $data['question_description'] ?? '';
$is_elementor = $data['is_elementor'] ?? false;
$return_html = ( $data['return_html'] ?? 'no' ) === 'yes';
$insert = $data['insert'];
$question_slug = ! empty( $data['question_permalink'] )
? sanitize_title( wp_unslash( (string) $data['question_permalink'] ) )
: '';
// Determine target status (draft or publish)
$target_status = ! empty( $data['question_status'] ) && in_array( $data['question_status'], [ 'draft', 'publish' ], true )
? sanitize_text_field( $data['question_status'] )
: 'publish';
if ( $insert ) {
if ( ! CourseBuilderAccessPolicy::can_create_item_type( LP_QUESTION_CPT ) ) {
throw new Exception( __( 'You are not allowed to create questions', 'learnpress' ) );
}
$insert_arg = array(
'post_type' => LP_QUESTION_CPT,
'post_title' => sanitize_text_field( $title ?? '' ),
'post_content' => Template::sanitize_html_content( $description ?? '' ),
'post_status' => $target_status,
);
if ( ! empty( $question_slug ) ) {
$insert_arg['post_name'] = $question_slug;
}
$question_id = wp_insert_post( $insert_arg, true );
if ( is_wp_error( $question_id ) ) {
throw new Exception( $question_id->get_error_message() );
}
} else {
$question_model = $data['question_model'];
if ( ! $question_model ) {
throw new Exception( __( 'Question not found', 'learnpress' ) );
}
$course_id = $this->get_course_by_item_id( $question_id );
// Support for co-instructor.
$co_instructor_ids = get_post_meta( $course_id, '_lp_co_teacher', false );
$co_instructor_ids = ! empty( $co_instructor_ids ) ? $co_instructor_ids : array();
if ( absint( $question_model->post_author ) !== get_current_user_id() && ! current_user_can( 'manage_options' ) && ! in_array( get_current_user_id(), $co_instructor_ids ) ) {
throw new Exception( __( 'You are not allowed to update this question', 'learnpress' ) );
}
$update_arg = array(
'ID' => $question_id,
'post_type' => LP_QUESTION_CPT,
'post_status' => $target_status,
);
if ( defined( 'ELEMENTOR_VERSION' ) ) {
\Elementor\Plugin::$instance->documents->get( $question_id )->set_is_built_with_elementor( ! empty( $is_elementor ) );
}
if ( isset( $data['question_title'] ) ) {
$update_arg['post_title'] = sanitize_text_field( $title );
}
if ( isset( $data['question_description'] ) ) {
$update_arg['post_content'] = Template::sanitize_html_content( $description ?? '' );
}
if ( ! empty( $question_slug ) ) {
$update_arg['post_name'] = $question_slug;
}
$update = wp_update_post( $update_arg );
if ( is_wp_error( $update ) ) {
throw new Exception( $update->get_error_message() );
}
}
// Remove question from quizzes if status is not public
if ( $target_status !== 'publish' ) {
$this->remove_question_from_assigned_quizzes( $question_id );
}
do_action( 'learn-press/course-builder/update-question', $data, $question_model ?? null );
$saved_question_status = get_post_status( $question_id );
if ( ! is_string( $saved_question_status ) || '' === $saved_question_status ) {
$saved_question_status = $target_status;
}
if ( $insert ) {
$response->data->redirect_url = CourseBuilder::get_link_course_builder(
CourseBuilderTemplate::MENU_QUESTIONS . "/{$question_id}"
);
}
$response->status = 'success';
$response->data->status = $saved_question_status;
$response->data->button_title = $saved_question_status === 'publish' ? __( 'Update', 'learnpress' ) : __( 'Publish', 'learnpress' );
$response->message = $target_status === 'draft'
? esc_html__( 'Question saved as draft', 'learnpress' )
: ( $insert ? esc_html__( 'Insert question successfully', 'learnpress' ) : esc_html__( 'Update question successfully', 'learnpress' ) );
$saved_question = get_post( $question_id );
if ( $saved_question ) {
$response->data->question_slug = $saved_question->post_name;
$response->data->question_permalink = get_permalink( $question_id );
}
if ( $return_html ) {
$question_model_new = QuestionPostModel::find( $question_id, true );
$response->data->list_item_html = $question_model_new
? BuilderListQuestionsTemplate::render_question( $question_model_new )
: '';
}
wp_send_json( $response );
} catch ( Throwable $th ) {
$response->status = 'error';
$response->message = $th->getMessage();
wp_send_json( $response );
}
}
public function move_trash_question() {
$response = new LP_REST_Response();
$response->data = new stdClass();
try {
$data = self::check_valid_question();
$question_id = $data['question_id'] ?? 0;
$status = $data['status'] ?? 'trash';
$question_model = $data['question_model'] ?? [];
if ( ! $question_model ) {
throw new Exception( __( 'Question not found', 'learnpress' ) );
}
if ( absint( $question_model->post_author ) !== get_current_user_id() && ! current_user_can( 'manage_options' ) ) {
throw new Exception( __( 'You are not allowed to delete this question', 'learnpress' ) );
}
if ( $status === 'trash' ) {
$move_trash = wp_trash_post( $question_id );
if ( is_wp_error( $move_trash ) ) {
throw new Exception( esc_html__( 'Cannot move this question to trash', 'learnpress' ) );
}
$message = esc_html__( 'This question has been moved to trash.', 'learnpress' );
} elseif ( $status === 'delete' ) {
if ( $question_model->post_status !== 'trash' ) {
throw new Exception( esc_html__( 'Question must be trashed before deleting.', 'learnpress' ) );
}
$delete = wp_delete_post( $question_id );
if ( is_wp_error( $delete ) ) {
throw new Exception( esc_html__( 'Cannot delete this question.', 'learnpress' ) );
}
$message = esc_html__( 'Delete this question successfully', 'learnpress' );
} elseif ( $status === 'publish' ) {
$update = wp_update_post(
array(
'ID' => $question_id,
'post_type' => LP_QUESTION_CPT,
'post_status' => 'publish',
)
);
if ( ! $update ) {
throw new Exception( __( 'Question cannot be moved to publish', 'learnpress' ) );
}
$message = __( 'Question has been moved to publish', 'learnpress' );
} elseif ( $status === 'draft' ) {
$update = wp_update_post(
array(
'ID' => $question_id,
'post_type' => LP_QUESTION_CPT,
'post_status' => 'draft',
)
);
if ( ! $update ) {
throw new Exception( __( 'Question cannot be restored to draft', 'learnpress' ) );
}
$message = __( 'Question has been restored to draft', 'learnpress' );
}
if ( $status !== 'publish' ) {
$this->remove_question_from_assigned_quizzes( $question_id );
}
$response->data->status = $status;
$response->data->button_title = __( 'Publish', 'learnpress' );
if ( 'delete' !== $status ) {
$fresh_question_model = QuestionPostModel::find( $question_id, true );
$response->data->html = $fresh_question_model
? BuilderListQuestionsTemplate::render_question( $fresh_question_model )
: '';
}
$response->status = 'success';
$response->message = $message;
wp_send_json( $response );
} catch ( Throwable $th ) {
$response->status = 'error';
$response->message = $th->getMessage();
wp_send_json( $response );
}
}
/**
* Remove lesson/quiz assignment from curriculum sections.
*
* @param int $item_id
* @param int $course_id_fallback
*
* @return void
*/
protected function remove_course_item_from_curriculum( int $item_id, int $course_id_fallback = 0 ) {
if ( $item_id <= 0 ) {
return;
}
global $wpdb;
$section_items = $wpdb->get_results(
$wpdb->prepare(
"
SELECT si.section_id, s.section_course_id
FROM {$wpdb->learnpress_section_items} si
INNER JOIN {$wpdb->learnpress_sections} s ON s.section_id = si.section_id
WHERE si.item_id = %d
",
$item_id
)
);
if ( ! $section_items ) {
if ( $course_id_fallback > 0 ) {
$this->clean_course_curriculum_cache( $course_id_fallback );
}
return;
}
foreach ( $section_items as $section_item ) {
$section_id = absint( $section_item->section_id ?? 0 );
$course_id = absint( $section_item->section_course_id ?? 0 );
if ( ! $section_id || ! $course_id ) {
continue;
}
$course_section_item_model = CourseSectionItemModel::find( $section_id, $item_id );
if ( ! $course_section_item_model ) {
continue;
}
$course_section_item_model->section_course_id = $course_id;
$course_section_item_model->delete();
}
}
/**
* Keep course curriculum caches in sync after direct section item removal.
*
* @param int $course_id
*
* @return void
*/
protected function clean_course_curriculum_cache( int $course_id ) {
if ( $course_id <= 0 ) {
return;
}
try {
$courseModel = CourseModel::find( $course_id, true );
if ( ! $courseModel ) {
return;
}
$courseModel->sections_items = null;
$courseModel->total_items = null;
$courseModel->save( true );
} catch ( Throwable $e ) {
error_log( __METHOD__ . ': ' . $e->getMessage() );
}
}
/**
* Remove question assignment from all quizzes.
*
* @param int $question_id
*
* @return void
*/
protected function remove_question_from_assigned_quizzes( int $question_id ) {
if ( $question_id <= 0 ) {
return;
}
global $wpdb;
$wpdb->delete(
$wpdb->prefix . 'learnpress_quiz_questions',
array( 'question_id' => $question_id ),
array( '%d' )
);
}
protected function get_course_by_item_id( $item_id ) {
static $cache = [];
global $wpdb;
if ( empty( $item_id ) ) {
return false;
}
$item_id = absint( $item_id );
if ( isset( $cache[ $item_id ] ) ) {
return $cache[ $item_id ] ? $cache[ $item_id ] : false;
}
$course_id = $wpdb->get_var(
$wpdb->prepare(
"SELECT c.ID FROM {$wpdb->posts} c
INNER JOIN {$wpdb->learnpress_sections} s ON c.ID = s.section_course_id
INNER JOIN {$wpdb->learnpress_section_items} si ON si.section_id = s.section_id
WHERE si.item_id = %d ORDER BY si.section_id DESC LIMIT 1
",
$item_id
)
);
$cache[ $item_id ] = absint( $course_id );
if ( $cache[ $item_id ] ) {
return $cache[ $item_id ];
}
return false;
}
/**
* Save global settings for Course Builder.
*
* @since 4.3.6
* @version 1.0.0
*/
public function save_global_settings() {
$response = new LP_REST_Response();
$response->data = new stdClass();
try {
$params = wp_unslash( $_REQUEST['data'] ?? '' );
if ( empty( $params ) ) {
throw new Exception( 'Error: params invalid!' );
}
$data = LP_Helper::json_decode( $params, true );
if ( ! current_user_can( ADMIN_ROLE ) ) {
throw new Exception( __( 'Permission denied', 'learnpress' ) );
}
$hide_instructor_access_admin_screen = ! empty( $data['hide_instructor_access_admin_screen'] ) && $data['hide_instructor_access_admin_screen'] === 'yes' ? 'yes' : 'no';
$logo_remove = ! empty( $data['course_builder_logo_remove'] ) && $data['course_builder_logo_remove'] === 'yes';
$logo_id = absint( $data['course_builder_logo_id'] ?? 0 );
if ( $logo_remove ) {
$logo_id = 0;
}
LP_Settings::update_option( 'hide_instructor_access_admin_screen', $hide_instructor_access_admin_screen );
LP_Settings::update_option( 'course_builder_logo_id', $logo_id );
$logo_url = '';
if ( $logo_id ) {
$logo_url = wp_get_attachment_image_url( $logo_id, 'full' );
}
$response->status = 'success';
$response->message = __( 'Course Builder settings updated.', 'learnpress' );
$response->data->hide_instructor_access_admin_screen = $hide_instructor_access_admin_screen;
$response->data->course_builder_logo_id = $logo_id;
$response->data->course_builder_logo_url = $logo_url;
wp_send_json( $response );
} catch ( Throwable $th ) {
$response->status = 'error';
$response->message = $th->getMessage();
wp_send_json( $response );
}
}
}
Back to Directory
File Manager