post_type !== LP_QUIZ_CPT ) { return $r; } return 'html'; }, 1000 ); } /** * Layout for edit course curriculum. * * @since 4.2.9 * @version 1.0.0 */ public function edit_quiz_layout( QuizPostModel $quizPostModel ) { wp_enqueue_style( 'lp-edit-quiz' ); wp_enqueue_script( 'lp-edit-quiz' ); $args = [ 'id_url' => 'edit-quiz', 'quiz_id' => $quizPostModel->ID, ]; /** * @uses self::render_edit_quiz */ $call_back = array( 'class' => self::class, 'method' => 'render_edit_quiz', ); echo TemplateAJAX::load_content_via_ajax( $args, $call_back ); } /** * Allow callback for AJAX. * * @param array $callbacks * * @return array */ public function allow_callback( array $callbacks ): array { $callbacks[] = get_class( $this ) . ':render_edit_quiz'; $callbacks[] = get_class( $this ) . ':render_list_items_not_assign'; return $callbacks; } /** * Render edit course curriculum html. * * @throws Exception * @since 4.2.9 * @version 1.0.1 */ public static function render_edit_quiz( array $data ): stdClass { $content = new stdClass(); try { $quiz_id = $data['quiz_id'] ?? 0; $quizPostModel = QuizPostModel::find( $quiz_id, true ); if ( ! $quizPostModel ) { throw new Exception( __( 'Quiz not found', 'learnpress' ) ); } // Check permission $quizPostModel->check_capabilities_create_item_course(); self::instance()->quizPostModel = $quizPostModel; $content->content = self::instance()->html_edit_quiz( $quizPostModel ); } catch ( Throwable $e ) { $content->content = Template::print_message( $e->getMessage(), Response::STATUS_ERROR, false ); } return $content; } /** * Render html for edit quiz. * * @param QuizPostModel $quizPostModel * * @return string * @throws Exception */ public function html_edit_quiz( QuizPostModel $quizPostModel ): string { $html_questions = ''; // Get sections items $question_ids = $quizPostModel->get_question_ids(); $count_questions = count( $question_ids ); foreach ( $question_ids as $question_id ) { $questionPostModel = QuestionPostModel::find( $question_id, true ); $html_questions .= $this->html_edit_question( $questionPostModel ); } $section_questions = [ 'wrap' => '
', 'list-sections' => $html_questions, 'question-clone' => $this->html_edit_question(), 'wrap_end' => '
', ]; $section = [ 'wrap' => '
', 'heading' => '
', 'h4' => sprintf( '

%s

', __( 'Details', 'learnpress' ) ), 'count-questions' => sprintf( '
%s
', $count_questions, sprintf( __( '%1$d %2$s', 'learnpress' ), $count_questions, sprintf( '%s%s', __( 'Question', 'learnpress' ), __( 'Questions', 'learnpress' ) ) ) ), 'quiz-toggle' => '
', 'heading_end' => '
', 'questions' => Template::combine_components( $section_questions ), 'add_new_question' => $this->html_add_new_question(), 'select_items' => $this->html_popup_items_to_select_clone( $quizPostModel ), 'wrap_end' => '
', ]; return Template::combine_components( $section ); } /** * HTML for edit question. * * @param $questionPostModel * * @return string */ public function html_edit_question( $questionPostModel = null ): string { $is_clone = false; $question_id = 0; $question_title = ''; $html_edit_main = ''; if ( $questionPostModel instanceof QuestionPostModel ) { $question_id = $questionPostModel->ID; $question_title = $questionPostModel->post_title; $section_edit_details = [ 'wrap' => '
', 'header' => sprintf( '
', __( 'Option Details', 'learnpress' ) ), 'collapse' => '
', 'point' => AdminEditQuestionTemplate::instance()->html_edit_mark( $questionPostModel ), 'hint' => AdminEditQuestionTemplate::instance()->html_edit_hint( $questionPostModel ), 'explanation' => AdminEditQuestionTemplate::instance()->html_edit_explanation( $questionPostModel ), 'collapse_end' => '
', 'wrap_end' => '
', ]; $section_edit_main = [ 'wrap' => sprintf( '
', $question_id ), 'left' => '
', 'description' => AdminEditQuestionTemplate::instance()->html_edit_question_description( $questionPostModel ), 'answers' => AdminEditQuestionTemplate::instance()->html_edit_question_by_type( $questionPostModel ), 'left_end' => '
', 'details' => Template::combine_components( $section_edit_details ), 'wrap_end' => '
', ]; $html_edit_main = Template::combine_components( $section_edit_main ); } else { $is_clone = true; } $section = [ 'wrap' => sprintf( '
', esc_attr( $question_id ), esc_attr( $is_clone ? 'clone lp-hidden' : '' ), esc_attr( $is_clone ? '' : $questionPostModel->get_type() ) ), 'head' => '
', 'drag' => sprintf( '', esc_attr__( 'Drag to reorder section', 'learnpress' ) ), 'loading' => '', 'title' => AdminEditQuestionTemplate::instance()->html_input_question_title( $question_title ), 'btn-update' => sprintf( '', __( 'Update' ) ), 'btn-cancel' => sprintf( '', __( 'Cancel' ) ), 'type' => sprintf( '%s', esc_html( $questionPostModel instanceof QuestionPostModel ? $questionPostModel->get_type_label() : '' ) ), 'btn-edit' => sprintf( '', esc_attr__( 'Edit question detail', 'learnpress' ), esc_url( $questionPostModel instanceof QuestionPostModel ? $questionPostModel->get_edit_link() : '#' ) ), 'btn-delete' => sprintf( '', esc_attr__( 'Remove question', 'learnpress' ), esc_attr__( 'Are you sure?', 'learnpress' ), esc_attr__( 'This question will be removed from this quiz. The question will no longer be assigned to this quiz, but will not be permanently deleted.', 'learnpress' ) ), 'toggle' => '
', 'head_end' => '
', 'edit_main' => $html_edit_main, 'wrap_end' => '
', ]; return Template::combine_components( $section ); } /** * HTML for add new question. * * @return string */ public function html_add_new_question(): string { $html_question_types = sprintf( '', esc_html__( 'Select question type', 'learnpress' ) ); $question_types = QuestionPostModel::get_types(); foreach ( $question_types as $type => $label ) { $html_question_types .= sprintf( '', esc_attr( $type ), esc_html( $label ) ); } $html_question_types = Template::instance()->nest_elements( [ sprintf( '', ], $html_question_types ); $section = [ 'wrap' => '
', 'icon' => '', 'input' => sprintf( '', esc_attr__( 'Create a new question', 'learnpress' ), esc_attr__( 'Question title is required', 'learnpress' ), Template::convert_data_to_json( [ 'id_url' => 'create-question-add-to-quiz', 'quiz_id' => $this->quizPostModel->get_id(), 'action' => 'create_question_add_to_quiz', ] ), ), 'types' => $html_question_types, 'button' => sprintf( '', esc_attr__( 'Enter title question and choice type', 'learnpress' ), __( 'Add Question', 'learnpress' ) ), 'btn-select-items' => sprintf( '', __( 'Question Bank', 'learnpress' ) ), 'wrap_end' => '
', ]; return Template::combine_components( $section ); } /** * HTML for popup items to select clone. * * @param QuizPostModel $quizPostModel * * @return string */ public function html_popup_items_to_select_clone( QuizPostModel $quizPostModel ): string { $tabs = [ LP_QUESTION_CPT => __( 'Questions', 'learnpress' ), ]; /** * @uses self::render_list_items_not_assign */ $html_items = TemplateAJAX::load_content_via_ajax( [ 'id_url' => 'list-questions-not-assign', 'enableScrollToView' => false, 'quiz_id' => $quizPostModel->ID, 'paged' => 1, ], [ 'class' => self::class, 'method' => 'render_list_items_not_assign', ] ); $section = [ 'wrap-script-template' => '', ]; return Template::combine_components( $section ); } /** * Render list items not assign to any quiz. * * @throws Exception * * @since 4.2.8.7 * @version 1.0.2 */ public static function render_list_items_not_assign( $data ): stdClass { $content = new stdClass(); try { // Check permission if ( ! current_user_can( UserModel::ROLE_ADMINISTRATOR ) ) { throw new Exception( esc_html__( 'Access denied.', 'learnpress' ) ); } $quiz_id = $data['quiz_id'] ?? 0; $item_selecting = $data['item_selecting'] ?? []; $search_title = $data['search_title'] ?? ''; $paged = intval( $data['paged'] ?? 1 ); $item_selecting_compare = new stdClass(); $quizPostModel = QuizPostModel::find( $quiz_id, true ); if ( ! $quizPostModel ) { throw new Exception( __( 'Quiz not found', 'learnpress' ) ); } $lp_posts_db = LP_Post_DB::getInstance(); $filter = new PostFilter(); $filter->only_fields = [ 'DISTINCT(p.ID) AS ID', 'p.post_title', 'p.post_type', ]; $filter->post_type = LP_QUESTION_CPT; $filter->post_status = [ 'publish' ]; $filter->order_by = 'p.ID'; $filter->page = $paged; if ( ! empty( $search_title ) ) { $filter->post_title = $search_title; } // Old logic: Get all questions not assigned to any quiz. // New logic: Get all questions not assigned to the quiz. $filter->where[] = $lp_posts_db->wpdb->prepare( "AND p.ID NOT IN ( SELECT question_id FROM {$lp_posts_db->tb_lp_quiz_questions} WHERE quiz_id = %d )", $quizPostModel->ID ); $total_rows = 0; $posts = $lp_posts_db->get_posts( $filter, $total_rows ); $total_pages = LP_Database::get_total_pages( $filter->limit, $total_rows ); $html_lis = ''; if ( empty( $posts ) ) { $html_lis = sprintf( '
  • %s
  • ', __( 'No items found', 'learnpress' ) ); } else { if ( ! empty( $item_selecting ) ) { foreach ( $item_selecting as $item ) { if ( ! isset( $item['id'] ) || ! isset( $item['type'] ) ) { continue; } $item_selecting_compare->{$item['id']} = new stdClass(); } } foreach ( $posts as $post ) { /** * @var $questionPostModel QuestionPostModel */ $questionPostModel = QuestionPostModel::find( $post->ID, true ); if ( ! $questionPostModel ) { continue; } $checked = ''; if ( isset( $item_selecting_compare->{$post->ID} ) ) { $checked = ' checked="checked"'; } $title_display = sprintf( '%s(#%d - %s)', esc_html( $post->post_title ), $post->ID, esc_html( $questionPostModel->get_type_label() ) ); $html_lis .= sprintf( '
  • %s%s
  • ', sprintf( '', esc_attr( $post->ID ?? 0 ), esc_attr( $questionPostModel->get_type_label() ?? '' ), esc_attr( $questionPostModel->get_type() ?? '' ), esc_attr( $title_display ), // For JS display on list selected. esc_attr( $checked ), esc_url( $questionPostModel->get_edit_link() ), esc_attr( $questionPostModel->get_the_title() ?? '' ) ), $title_display ); } } $section = [ 'ul' => '', 'pagination' => Template::instance()->html_pagination( [ 'total_pages' => $total_pages, 'paged' => $paged, ] ), ]; $content->content = Template::combine_components( $section ); } catch ( Throwable $e ) { $content->content = Template::print_message( $e->getMessage(), Response::STATUS_ERROR, false ); } return $content; } }