post_type !== LP_QUESTION_CPT ) {
return $r;
}
return 'html';
},
1000
);
}
/**
* Layout for edit question.
*
* @use self::render_edit_question
*
* @since 4.2.9
* @version 1.0.0
*/
public function edit_layout( QuestionPostModel $questionPostModel ) {
wp_enqueue_style( 'lp-edit-question' );
wp_enqueue_script( 'lp-edit-question' );
$args = [
'id_url' => 'edit-question',
'question_id' => $questionPostModel->ID,
];
$call_back = array(
'class' => self::class,
'method' => 'render_edit_question',
);
echo TemplateAJAX::load_content_via_ajax( $args, $call_back );
}
/**
* Allow callback for AJAX.
* @use self::render_edit_quiz
* @use self::render_list_items_not_assign
*
* @param array $callbacks
*
* @return array
*/
public function allow_callback( array $callbacks ): array {
$callbacks[] = self::class . ':render_edit_question';
return $callbacks;
}
/**
* Render edit question html.
*
* @throws Exception
*/
public static function render_edit_question( array $data ): stdClass {
$question_id = $data['question_id'] ?? 0;
$questionPostModel = QuestionPostModel::find( $question_id, true );
if ( ! $questionPostModel ) {
throw new Exception( __( 'Question not found', 'learnpress' ) );
}
// Check permission
$questionPostModel->check_capabilities_create_item_course();
$content = new stdClass();
$content->content = self::instance()->html_edit_question( $questionPostModel );
return $content;
}
public function html_edit_question( QuestionPostModel $questionPostModel ): string {
$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(
'',
esc_attr( $questionPostModel->ID )
),
'left' => '
',
'answers' => AdminEditQuestionTemplate::instance()->html_edit_question_by_type( $questionPostModel ),
'left_end' => '
',
'details' => Template::combine_components( $section_edit_details ),
'wrap_end' => '
',
];
return Template::combine_components( $section_edit_main );
}
/**
* HTML for field settings.
*
* @param array $args
*
* @return string
*/
public function html_field_settings( array $args = [] ): string {
$section = [
'wrap' => '',
'label' => sprintf(
'
%s
',
$args['label'] ?? ''
),
'input' => $args['input'] ?? '',
'description' => sprintf(
'
%s
',
$args['description'] ?? ''
),
'wrap_end' => '
',
];
return Template::combine_components( $section );
}
/**
* HTML for edit question mark.
*
* @param QuestionPostModel $questionPostModel
*
* @return string
*/
public function html_edit_mark( QuestionPostModel $questionPostModel ): string {
$point = $questionPostModel->get_mark();
$args = [
'label' => __( 'Points', 'learnpress' ),
'input' => sprintf(
' ',
esc_attr( $point )
),
'description' => __( 'Points for choosing the correct answer.', 'learnpress' ),
];
return $this->html_field_settings( $args );
}
/**
* HTML for edit question hint.
*
* @param QuestionPostModel $questionPostModel
*
* @return string
*/
public function html_edit_hint( QuestionPostModel $questionPostModel ): string {
$question_id = $questionPostModel->ID;
$hint = $questionPostModel->get_hint();
$editor_id = 'lp-question-hint-' . $question_id;
$args = [
'label' => __( 'Hint', 'learnpress' ),
'input' => AdminTemplate::editor_tinymce(
$hint,
$editor_id,
[
'editor_height' => 50,
'editor_class' => 'lp-editor-tinymce lp-auto-save-question',
]
),
'description' => __( 'The instructions for the user to select the right answer. The text will be shown when users click the "Hint" button.', 'learnpress' ),
];
return $this->html_field_settings( $args );
}
/**
* HTML for edit question explanation.
*
* @param QuestionPostModel|null $questionPostModel
*
* @return string
*/
public function html_edit_explanation( QuestionPostModel $questionPostModel ): string {
$question_id = $questionPostModel->ID;
$explanation = $questionPostModel->get_explanation();
$editor_id = 'lp-question-explanation-' . $question_id;
$args = [
'label' => __( 'Explanation', 'learnpress' ),
'input' => AdminTemplate::editor_tinymce(
$explanation,
$editor_id,
[
'editor_height' => 50,
'editor_class' => 'lp-editor-tinymce lp-auto-save-question',
]
),
'description' => __( 'The explanation will be displayed when students click the "Check Answer" button.', 'learnpress' ),
];
return $this->html_field_settings( $args );
}
/**
* HTML for edit question description.
*
* @param QuestionPostModel|null $questionPostModel
*
* @return string
*/
public function html_edit_question_description( QuestionPostModel $questionPostModel ): string {
$question_id = $questionPostModel->ID;
$question_description = $questionPostModel->get_the_content();
$editor_id = 'lp-question-description-' . $question_id;
$section = [
'wrap' => '',
'header' => sprintf(
'',
__( 'Description', 'learnpress' )
),
'tinymce' => AdminTemplate::editor_tinymce(
$question_description,
$editor_id,
[
'editor_class' => 'lp-editor-tinymce lp-auto-save-question',
'editor_height' => 50,
]
),
'wrap_end' => '
',
];
return Template::combine_components( $section );
}
/**
* HTML for edit question by type.
*
* @param QuestionPostModel|null $questionPostModel
*
* @return string
*/
public function html_edit_question_by_type( QuestionPostModel $questionPostModel ): string {
$html_answers_config = AdminEditQuestionTemplate::instance()->html_answer_option( $questionPostModel );
$section = [
'wrap' => '',
'header' => sprintf(
'',
__( 'Config Your Answer', 'learnpress' ),
esc_html( $questionPostModel->get_type_label() )
),
/*'button-save' => sprintf(
'%s ',
__( 'Save Answer', 'learnpress' )
),*/
'answers-config' => $html_answers_config,
'wrap_end' => '
',
];
return Template::combine_components( $section );
}
/**
* HTML input question title.
*
* @param string $question_title
*
* @return string
*/
public function html_input_question_title( string $question_title = '' ): string {
return sprintf(
' ',
esc_attr( $question_title ),
esc_attr__( 'Update question title', 'learnpress' ),
esc_attr__( 'Question title is required', 'learnpress' )
);
}
/**
* Get html edit question type.
*
* @param QuestionPostModel $questionPostModel
*
* @return string
*/
public function html_answer_option( QuestionPostModel $questionPostModel ): string {
$type = $questionPostModel->get_type();
// For addon sorting choice old <= v4.0.1
if ( class_exists( 'LP_Addon_Sorting_Choice_Preload' ) && $type === 'sorting_choice' ) {
if ( version_compare( LP_ADDON_SORTING_CHOICE_VER, '4.0.1', '<=' ) ) {
$type = 'sorting_choice_old';
}
}
switch ( $type ) {
case 'true_or_false':
$html = self::html_answer_type_true_or_false( $questionPostModel );
break;
case 'single_choice':
$html = self::html_answer_type_single_choice( $questionPostModel );
break;
case 'multi_choice':
$html = self::html_answer_type_multiple_choice( $questionPostModel );
break;
case 'fill_in_blanks':
$html = self::html_answer_type_fill_in_blanks( $questionPostModel );
break;
case 'sorting_choice_old':
$html = self::html_answer_type_sorting_choice_old( $questionPostModel );
break;
case $type:
$html = apply_filters( 'learn-press/edit-question/type/html', '', $type, $questionPostModel );
break;
default:
$html = '';
break;
}
// For create new question, type is empty.
if ( empty( $type ) ) {
$html_question_types = sprintf(
'%s ',
esc_html__( 'Select question type', 'learnpress' )
);
$question_types = QuestionPostModel::get_types();
foreach ( $question_types as $type => $label ) {
$html_question_types .= sprintf(
'%s ',
esc_attr( $type ),
esc_html( $label )
);
}
$html = Template::instance()->nest_elements(
[
sprintf(
'',
esc_attr__( 'Question type is required', 'learnpress' )
)
=> ' ',
],
$html_question_types
);
$html .= sprintf(
'%s %s
',
' ',
__( 'Create Question Type', 'learnpress' )
);
$html = Template::instance()->nest_elements(
[ '' => '
' ],
$html
);
}
$answers = $questionPostModel->get_answer_option();
if ( $questionPostModel->get_type() === 'fill_in_blanks' ) {
$answers = $answers[0] ?? [];
}
$section = [
'wrap' => sprintf(
'',
esc_attr( $type ),
Template::convert_data_to_json( $answers )
),
'answers' => $html,
'wrap_end' => '
',
];
return Template::combine_components( $section );
}
/**
* Get html edit question type true or false.
*
* @param QuestionPostModel $questionPostModel
*
* @return string
*/
public function html_answer_type_true_or_false( QuestionPostModel $questionPostModel ): string {
$name_radio = 'lp-input-answer-set-true-' . $questionPostModel->ID;
$answers = $questionPostModel->get_answer_option();
$html_answers = '';
foreach ( $answers as $answer ) {
$questionAnswerModel = new QuestionAnswerModel( $answer );
$html_answers .= sprintf(
'
',
'lp-question-answer-title-input lp-auto-save-question-answer',
esc_attr( $questionAnswerModel->title ),
$questionAnswerModel->is_true === 'yes' ? 'checked' : '',
$name_radio,
esc_attr( $questionAnswerModel->is_true ),
esc_attr( $questionAnswerModel->question_answer_id )
);
}
$section = [
'header' => sprintf(
'',
__( 'Answers', 'learnpress' ),
__( 'Corrects', 'learnpress' )
),
'answers' => $html_answers,
];
return Template::combine_components( $section );
}
/**
* Get html edit single choice.
*
* @param QuestionPostModel $questionPostModel
*
* @return string
*/
public function html_answer_type_single_choice( QuestionPostModel $questionPostModel ): string {
$name_radio = 'lp-input-answer-set-true-' . $questionPostModel->ID;
$answers = $questionPostModel->get_answer_option();
$answers[] = null;
$html_answers = '';
foreach ( $answers as $answer ) {
$is_clone = true;
$title = '';
$is_true = 'no';
$question_answer_id = 0;
if ( ! is_null( $answer ) ) {
$is_clone = false;
$questionAnswerModel = new QuestionAnswerModel( $answer );
$title = $questionAnswerModel->title;
$is_true = $questionAnswerModel->is_true;
$question_answer_id = $questionAnswerModel->question_answer_id;
}
$html_answers .= sprintf(
'
%8$s
',
'lp-question-answer-title-input lp-auto-save-question-answer',
esc_attr( $title ),
$is_true === 'yes' ? 'checked' : '',
$name_radio,
esc_attr( $is_true ),
esc_attr( $question_answer_id ),
$is_clone ? 'clone lp-hidden' : '',
' '
);
}
$html_answers .= sprintf(
'
%3$s
',
'lp-question-answer-title-new-input',
esc_attr__( 'Answer title is required', 'learnpress' ),
__( 'Add Option', 'learnpress' )
);
$section = [
'header' => sprintf(
'',
__( 'Answers', 'learnpress' ),
__( 'Corrects', 'learnpress' )
),
'answers' => $html_answers,
];
return Template::combine_components( $section );
}
/**
* Get html edit question type multiple choice.
*
* @param QuestionPostModel $questionPostModel
*
* @return string
*/
public function html_answer_type_multiple_choice( QuestionPostModel $questionPostModel ): string {
$name_checkbox = 'lp-input-answer-set-true-' . $questionPostModel->ID;
$answers = $questionPostModel->get_answer_option();
$answers[] = null;
$html_answers = '';
foreach ( $answers as $answer ) {
$is_clone = true;
$title = '';
$is_true = 'no';
$question_answer_id = 0;
if ( ! is_null( $answer ) ) {
$is_clone = false;
$questionAnswerModel = new QuestionAnswerModel( $answer );
$title = $questionAnswerModel->title;
$is_true = $questionAnswerModel->is_true;
$question_answer_id = $questionAnswerModel->question_answer_id;
}
$html_answers .= sprintf(
'
%8$s
',
'lp-question-answer-title-input lp-auto-save-question-answer',
esc_attr( $title ),
$is_true === 'yes' ? 'checked' : '',
$name_checkbox,
esc_attr( $is_true ),
esc_attr( $question_answer_id ),
$is_clone ? 'clone lp-hidden' : '',
' '
);
}
$html_answers .= sprintf(
'
%3$s
',
'lp-question-answer-title-new-input',
esc_attr__( 'Answer title is required', 'learnpress' ),
__( 'Add Option', 'learnpress' )
);
$section = [
'header' => sprintf(
'',
__( 'Answers', 'learnpress' ),
__( 'Corrects', 'learnpress' )
),
'answers' => $html_answers,
];
return Template::combine_components( $section );
}
/**
* Get html edit question type multiple choice.
*
* @param QuestionPostModel $questionPostModel
*
* @return string
*/
public function html_answer_type_sorting_choice_old( QuestionPostModel $questionPostModel ): string {
$answers = $questionPostModel->get_answer_option();
$answers[] = null;
$html_answers = '';
foreach ( $answers as $answer ) {
$is_clone = true;
$title = '';
$question_answer_id = 0;
if ( ! is_null( $answer ) ) {
$is_clone = false;
$questionAnswerModel = new QuestionAnswerModel( $answer );
$title = $questionAnswerModel->title;
$question_answer_id = $questionAnswerModel->question_answer_id;
}
$html_answers .= sprintf(
'
',
$is_clone ? 'clone lp-hidden' : '',
esc_attr( $question_answer_id ),
'lp-question-answer-title-input lp-auto-save-question-answer',
'lp-question-answer-title-input',
esc_attr( $title )
);
}
$html_answers .= sprintf(
'
%3$s
',
'lp-question-answer-title-new-input',
esc_attr__( 'Answer title is required', 'learnpress' ),
__( 'Add Option', 'learnpress' )
);
$section = [
'header' => sprintf(
'',
__( 'Answers', 'learnpress' ),
__( 'Corrects', 'learnpress' )
),
'answers' => $html_answers,
];
return Template::combine_components( $section );
}
/**
* Get html edit question type fill in blanks.
*
* @param QuestionPostModel $questionPostModel
*
* @return string
*/
public function html_answer_type_fill_in_blanks( QuestionPostModel $questionPostModel ): string {
$questionPostFIBModel = new QuestionPostFIBModel( $questionPostModel );
$name = 'lp-question-fib-input-' . $questionPostFIBModel->ID;
$answers = $questionPostFIBModel->get_answer_option();
$options = [
'clone' =>
[
'fill' => '',
'match_case' => 0,
'comparison' => 'equal',
'id' => 'clone',
],
];
/**
* @var QuestionAnswerModel[] $answers
*/
$questionAnswerModel = $answers[0] ?? null;
if ( $questionAnswerModel instanceof QuestionAnswerModel ) {
$meta_data = $questionAnswerModel->get_all_metadata();
if ( is_array( $meta_data ) ) {
$options = array_merge(
$meta_data,
$options
);
}
}
$content = '';
if ( $questionAnswerModel instanceof QuestionAnswerModel ) {
$content = $this->convert_content_fib_to_span( $questionAnswerModel->title );
}
$section = [
'input' => AdminTemplate::editor_tinymce(
$content,
$name,
[
'default_editor' => 'html', // Use HTML editor by default tinymce
'tinymce' => true,
'quicktags' => true, // Set true to show tab "Code" in editor
]
),
'desc' => sprintf(
'%s
',
__( 'Select a word in the passage above and click "Insert a new blank" to make that word a blank for filling.', 'learnpress' )
),
'buttons' => '',
'btn-insert-new' => sprintf(
'%s %s ',
esc_html__( 'Enter answer correct on here', 'learnpress' ),
__( 'This text inserted to blank', 'learnpress' ),
__( 'You must select a text or position on Editor to insert new blank.', 'learnpress' ),
' ',
__( 'Insert new blank', 'learnpress' )
),
'btn-delete-all-blanks' => sprintf(
'%s %s
',
esc_attr__( 'Are you sure?', 'learnpress' ),
esc_html__( 'This action will delete all blanks in the editor, only keep text content.', 'learnpress' ),
esc_attr__( 'Delete all blanks on this editor.', 'learnpress' ),
' ',
__( 'Delete all blanks', 'learnpress' )
),
'btn-save' => sprintf(
'%s %s ',
' ',
__( 'Save content', 'learnpress' )
),
'btn-clear' => sprintf(
'%s %s
',
esc_attr__( 'Are you sure?', 'learnpress' ),
esc_html__( 'This action will delete all content in the editor.', 'learnpress' ),
esc_attr__( 'Clear all content on this editor.', 'learnpress' ),
' ',
__( 'Clear content', 'learnpress' )
),
'buttons_end' => '
',
'options' => $this->html_fib_options( $options ),
];
return Template::combine_components( $section );
}
public function html_fib_options( $options ): string {
if ( empty( $options ) || ! is_array( $options ) ) {
return '';
}
$html = '';
$i = 1;
foreach ( $options as $option ) {
$match_case = $option['match_case'] ?? 0;
$comparison = $option['comparison'] ?? '';
$id = $option['id'] ?? '';
/*if ( $id === 'clone' ) {
$i = 0;
}*/
$section_header = [
'wrap' => '',
];
$section_detail = [
'wrap' => '',
'match-case' => sprintf(
'
%s
',
$match_case ? 'checked' : '',
esc_attr( 'lp-question-fib-option-match-case-input' . $id ),
'match_case',
__( 'Match case', 'learnpress' )
),
'wrap_match_case' => sprintf(
'
',
$match_case ? '' : 'lp-hidden'
),
'separator-label' => sprintf(
'
%s
',
__( 'Comparison', 'learnpress' )
),
'equal' => sprintf(
'
',
esc_attr( $id ),
$comparison === 'equal' ? 'checked' : '',
__( 'Equal', 'learnpress' ),
__( 'Match two words are equality.', 'learnpress' )
),
'range' => sprintf(
'
',
esc_attr( $id ),
$comparison === 'range' ? 'checked' : '',
__( 'Range', 'learnpress' ),
__( 'Match any number in a range. Use 100, 200 to match any value from 100 to 200.', 'learnpress' )
),
'any' => sprintf(
'
',
esc_attr( $id ),
$comparison === 'any' ? 'checked' : '',
__( 'Any', 'learnpress' ),
__( 'Match any value in a set of words. Use fill, blank, or question to match any value in the set.', 'learnpress' )
),
'wrap_match_case_end' => '
', // End match case wrap
'wrap_end' => '
',
];
$section = [
'wrap' => sprintf(
'',
$id === 'clone' ? 'clone lp-hidden' : '',
esc_attr( $id )
),
'header' => Template::combine_components( $section_header ),
'detail' => Template::combine_components( $section_detail ),
'wrap_end' => '
',
];
$html .= Template::combine_components( $section );
}
$html = Template::instance()->nest_elements(
[ '' => '
' ],
$html
);
return $html;
}
/**
* Convert content of fill in blanks shortcode to span elements display on Editor.
*
* @param string $content
*
* @return string
*/
public function convert_content_fib_to_span( string $content = '' ): string {
$regex_str = get_shortcode_regex( array( 'fib' ) );
$pattern = "/{$regex_str}/";
preg_match_all(
$pattern,
$content,
$all_shortcode,
PREG_SET_ORDER
);
if ( ! empty( $all_shortcode ) ) {
foreach ( $all_shortcode as $shortcode ) {
$atts = shortcode_parse_atts( $shortcode[0] );
$fill = $atts['fill'] ?? '';
$id = $atts['id'] ?? '';
if ( empty( $id ) ) {
$ida = explode( '=', str_replace( ']', '', $atts[1] ) );
$id = isset( $ida[1] ) ? str_replace( '"', '', $ida[1] ) : '';
}
$new_str = sprintf(
'%s ',
esc_attr( $id ),
esc_html( $fill )
);
$content = str_replace( $shortcode[0], $new_str, $content );
}
}
return $content;
}
}