PluginProbe
LearnPress – WordPress LMS Plugin for Create and Sell Online Courses / 4.3.9
LearnPress – WordPress LMS Plugin for Create and Sell Online Courses v4.3.9
4.4.7 4.4.6 4.4.5 4.4.4 4.4.3 4.4.2 4.4.1 4.4.0 4.3.9.1 4.3.9 4.3.8 4.3.7 4.1.6.9 4.1.6.9.1 4.1.6.9.2 4.1.6.9.3 4.1.6.9.4 4.1.7 4.1.7.1 4.1.7.2 4.1.7.3 4.1.7.3.1 4.1.7.3.2 4.2.0 4.2.1 All 138 releases
learnpress / inc / question / class-lp-question.php

class-lp-question.php in LearnPress – WordPress LMS Plugin for Create and Sell Online Courses 4.3.9, at inc/question/class-lp-question.php

1,121 lines 28.6 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Base class for types of question
4 *
5 * @author ThimPress
6 * @package LearnPress/Classes
7 * @version 3.0.0
8 */
9
10 /**
11 * Prevent loading this file directly
12 */
13 defined( 'ABSPATH' ) || exit();
14
15 if ( ! class_exists( 'LP_Question' ) ) {
16
17 /**
18 * Class LP_Question
19 *
20 * @extend LP_Course_Item
21 */
22 class LP_Question extends LP_Course_Item {
23
24 /**
25 * @var null
26 */
27 protected $_options = null;
28
29 /**
30 * @var string
31 */
32 protected $_content = '';
33
34 /**
35 * Type of this question.
36 *
37 * @var string
38 */
39 protected $_question_type = 'true_or_false';
40
41 /**
42 * @var string
43 */
44 protected $_item_type = LP_QUESTION_CPT;
45
46 /**
47 * @var array
48 */
49 protected static $_types = array();
50
51 /**
52 * support answer options
53 *
54 * @var bool
55 */
56 protected $_answer_options = true;
57
58 /**
59 * @var int
60 */
61 // protected static $_loaded = 0;
62
63 /**
64 * @var string
65 */
66 public $object_type = 'question';
67
68 /**
69 * @var array
70 */
71 protected $_data = array(
72 'mark' => 0,
73 'answer_options' => array(),
74 'show_correct_answers' => '',
75 'disable_answers' => '',
76 'answered' => '',
77 'explanation' => '',
78 'hint' => '',
79 );
80
81 /**
82 * Construct
83 *
84 * @param mixed
85 * @param array
86 *
87 * @throws Exception
88 */
89 public function __construct( $the_question = null, $args = null ) {
90
91 parent::__construct( $the_question, $args );
92
93 $this->_curd = new LP_Question_CURD();
94 if ( is_numeric( $the_question ) && $the_question > 0 ) {
95 $this->set_id( $the_question );
96 } elseif ( $the_question instanceof self ) {
97 $this->set_id( absint( $the_question->get_id() ) );
98 } elseif ( ! empty( $the_question->ID ) ) {
99 $this->set_id( absint( $the_question->ID ) );
100 }
101
102 if ( $this->_answer_options ) {
103 $this->add_support( 'answer_options' );
104 $this->add_support( 'auto_calculate_point' );
105
106 if ( $this->get_type() !== 'true_or_false' ) {
107 $this->add_support( 'add_answer_option' );
108 }
109 }
110
111 $this->set_data( 'answered', false );
112
113 if ( $this->get_id() > 0 ) {
114 $this->load();
115 }
116
117 $this->_options = $args;
118 //$this->_init();
119 // self::$_loaded ++;
120 }
121
122 public function add_support( $feature, $type = 'yes' ) {
123 $feature = $this->_sanitize_feature_key( $feature );
124 LP_Global::add_object_feature( $this->object_type . '.' . $this->get_type(), $feature, $type );
125 }
126
127 /*public function is_support( $feature, $type = '' ) {
128 $feature = $this->_sanitize_feature_key( $feature );
129
130 return LP_Global::object_is_support_feature( $this->object_type . '.' . $this->get_type(), $feature, $type );
131 }*/
132
133 /**
134 * @deprecated 4.1.7.3
135 */
136 public function get_supports() {
137 _deprecated_function( __FUNCTION__, '4.1.7.3' );
138 if ( empty( LP_Global::$object_support_features ) ) {
139 return false;
140 }
141
142 return LP_Global::get_object_supports( $this->object_type . '.' . $this->get_type() );
143 }
144
145 /**
146 * Load data for question
147 *
148 * @throws Exception
149 */
150 public function load() {
151 $this->_curd->load( $this );
152 }
153
154 /**
155 * Get default question meta.
156 *
157 * @return mixed
158 */
159 public static function get_default_meta() {
160 $meta = array(
161 'mark' => 1,
162 'explanation' => null,
163 'hint' => null,
164 );
165
166 return apply_filters( 'learn-press/question/default-meta', $meta );
167 }
168
169 /**
170 * Save question data.
171 *
172 * @since 3.0.0
173 *
174 * @return int|object|WP_Error
175 */
176 public function save() {
177
178 if ( $this->get_id() ) {
179 $return = $this->_curd->update( $this );
180 } else {
181 $return = $this->_curd->create( $this );
182 }
183
184 return $return;
185 }
186
187 /**
188 * @return array|mixed
189 */
190 public function get_mark() {
191 return $this->get_data( 'mark', 1 );
192 }
193
194 /**
195 * @param $mark
196 */
197 public function set_mark( $mark ) {
198 $this->_set_data( 'mark', abs( $mark ) );
199 }
200
201 /**
202 * Do something before get data.
203 * Some data now is not auto loading when object is created
204 * therefore, we will load it here.
205 *
206 * @param string $name
207 * @param string $default
208 *
209 * @return array|mixed
210 */
211 public function get_data( $name = '', $default = '' ) {
212 switch ( $name ) {
213 case 'answer_options':
214 $answer_options = parent::get_data( $name, $default );
215
216 if ( ! $answer_options ) {
217 $answer_options = $this->_curd->load_answer_options( $this->get_id() );
218 $this->set_data( $name, $answer_options );
219 }
220
221 break;
222 }
223
224 return parent::get_data( $name, $default );
225 }
226
227 /**
228 * @param string $yes_or_no
229 *
230 * @return array|mixed
231 */
232 public function show_correct_answers( $yes_or_no = '' ) {
233 if ( in_array( $yes_or_no, array( 'yes', 'no' ) ) ) {
234 $this->_set_data( 'show_correct_answers', $yes_or_no );
235 }
236
237 return $this->get_data( 'show_correct_answers' );
238 }
239
240 /**
241 * @param string $yes_or_no
242 *
243 * @return array|mixed
244 */
245 public function disable_answers( $yes_or_no = '' ) {
246 if ( in_array( $yes_or_no, array( 'yes', 'no' ) ) ) {
247 $this->_set_data( 'disable_answers', $yes_or_no );
248 }
249
250 return $this->get_data( 'disable_answers' );
251 }
252
253 /**
254 * Set answer for this question.
255 *
256 * @param mixed $answered
257 */
258 public function set_answered( $answered ) {
259 $this->set_data( 'answered', $answered );
260 }
261
262 /**
263 * Get answer for this question if set.
264 *
265 * @return array|mixed
266 */
267 public function get_answered() {
268 return $this->get_data( 'answered' );
269 }
270
271 /**
272 * Get answer options of the question
273 *
274 * @param array $args - Optional.
275 *
276 * @return mixed
277 */
278 public function get_answer_options( $args = array() ) {
279
280 $args = wp_parse_args(
281 $args,
282 array(
283 'exclude' => '',
284 'map' => '',
285 'answer' => '',
286 )
287 );
288
289 if ( $args['exclude'] && is_string( $args['exclude'] ) ) {
290 $exclude = array_map( 'trim', explode( ',', $args['exclude'] ) );
291 } else {
292 $exclude = $args['exclude'];
293 }
294
295 $map = $args['map'];
296
297 $options = $this->get_data( 'answer_options' );
298
299 // Remove key if it in $exclude.
300 if ( $options && ( $exclude || $map ) ) {
301 $exclude = array_flip( $exclude );
302
303 foreach ( $options as $k => $option ) {
304 $option['title'] = do_shortcode( $option['title'] );
305
306 foreach ( $map as $k_map => $v_map ) {
307 if ( array_key_exists( $k_map, $option ) ) {
308 $option[ $v_map ] = $option[ $k_map ];
309 $exclude[ $k_map ] = 1;
310 }
311 }
312 $options[ $k ] = array_diff_key( $option, $exclude );
313 }
314 }
315
316 return apply_filters( 'learn-press/question/answer-options', $options, $this->get_id() );
317 }
318
319 /**
320 * @param string $explanation
321 */
322 public function set_explanation( $explanation = '' ) {
323 $this->_set_data( 'explanation', $explanation );
324 }
325
326 /**
327 * @return mixed
328 */
329 public function get_explanation() {
330 return apply_filters( 'learn-press/question/explanation', do_shortcode( $this->get_data( 'explanation' ) ), $this->get_id() );
331 }
332
333 /**
334 * @param string $hint
335 */
336 public function set_hint( $hint = '' ) {
337 $this->_set_data( 'hint', $hint );
338 }
339
340 /**
341 * @return mixed
342 */
343 public function get_hint() {
344 return apply_filters( 'learn-press/question/hint', do_shortcode( $this->get_data( 'hint' ) ), $this->get_id() );
345 }
346
347 /**
348 * Get all type of questions
349 *
350 * @return mixed
351 */
352 public static function get_types() {
353 $types = apply_filters(
354 'learn-press/question-types',
355 array(
356 'true_or_false' => esc_html__( 'True Or False', 'learnpress' ),
357 'multi_choice' => esc_html__( 'Multi Choice', 'learnpress' ),
358 'single_choice' => esc_html__( 'Single Choice', 'learnpress' ),
359 'fill_in_blanks' => esc_html__( 'Fill In Blanks', 'learnpress' ),
360 )
361 );
362
363 return apply_filters( 'learn_press_question_types', $types );
364 }
365
366 /**
367 * Store question and it's related data into database.
368 *
369 * @return mixed
370 */
371 public function store() {
372 global $wpdb;
373 $id = absint( $this->get_id() );
374 $is_update = $id > 0;
375 $post_data = array(
376 'post_title' => $this->get_data( 'title' ),
377 'post_type' => LP_QUESTION_CPT,
378 'ID' => $id,
379 );
380 if ( $is_update ) {
381 $updated = wp_update_post( $post_data, true );
382 } else {
383 $updated = wp_insert_post( $post_data, true );
384 }
385
386 if ( ! is_numeric( $updated ) ) {
387 return false;
388 }
389
390 // Does this question support answer options?
391 /*if ( ! $this->is_support( 'answer_options' ) ) {
392 return $updated;
393 }*/
394
395 $this->empty_answers();
396 $answer_options = $this->get_data( 'answer_options' );
397 if ( $answer_options ) {
398 $question_order = 1;
399 $query = "INSERT INTO {$wpdb->prefix}learnpress_question_answers(`question_id`, `order`) VALUES";
400 foreach ( $answer_options as $answer_option ) {
401 if ( empty( $answer_option['title'] ) ) {
402 if ( apply_filters( 'learn-press/question/ignore-insert-empty-answer-option', true, $answer_option, $id ) ) {
403 continue;
404 }
405 }
406 $qry = $query . $wpdb->prepare( '(%d, %d)', $id, $question_order ++ );
407 do_action( 'learn-press/question/insert-answer-option', $id, $answer_option );
408 if ( $wpdb->query( $qry ) ) {
409 $inserted_id = $wpdb->insert_id;
410 learn_press_update_question_answer_meta( $inserted_id, 'text', $answer_option['text'] );
411 learn_press_update_question_answer_meta( $inserted_id, 'value', $answer_option['value'] );
412 if ( ! empty( $answer_option['is_true'] ) && ! learn_press_is_negative_value( $answer_option['is_true'] ) ) {
413 learn_press_update_question_answer_meta( $inserted_id, 'checked', 'yes' );
414 }
415 do_action( 'learn-press/question/inserted-answer-option', $inserted_id, $id, $answer_option );
416 }
417 }
418 }
419
420 return $updated;
421 }
422
423 /**
424 * Remove all answers to prepare for inserting new
425 */
426 public function empty_answers() {
427 global $wpdb;
428 $id = absint( $this->get_id() );
429 $table_meta = $wpdb->learnpress_question_answermeta;
430 $table_main = $wpdb->learnpress_question_answers;
431 $query = $wpdb->prepare(
432 "
433 DELETE FROM t1, t2
434 USING {$table_main} AS t1 INNER JOIN {$table_meta} AS t2 ON t1.question_answer_id = t2.learnpress_question_answer_id
435 WHERE t1.question_id = %d
436 ",
437 $id
438 );
439
440 // deprecated
441 do_action( 'learn_press_before_delete_question_answers', $id );
442
443 do_action( 'learn-press/question/delete-answers', $id );
444 if ( $wpdb->query( $query ) ) {
445 do_action( 'learn-press/question/deleted-answers', $id );
446 }
447 // deprecated
448 do_action( 'learn_press_delete_question_answers', $id );
449 }
450
451 /**
452 * @param $meta
453 * @param $field
454 * @param $is_saved
455 *
456 * @return string
457 * @deprecated 4.1.7.3
458 */
459 public function _filter_meta_box_meta( $meta, $field, $is_saved ) {
460 _deprecated_function( __METHOD__, '4.1.7.3' );
461 /*if ( preg_match( '~\[question-content\]~', $field['id'] ) && $field['context'] == 'quiz-list-questions' ) {
462 $post = get_post( $this->get_id() );
463 $meta = $post->post_content;
464 }
465
466 return $meta;*/
467 }
468
469 /**
470 * Set new type of question.
471 * Update _lp_type meta to new type.
472 *
473 * @param string $type
474 *
475 * @return bool
476 */
477 public function set_type( $type = '' ) {
478
479 if ( ! $type ) {
480 return false;
481 }
482
483 if ( ! learn_press_is_support_question_type( $type ) ) {
484 return false;
485 }
486
487 // Change to new type and update meta value
488 $this->_question_type = $type;
489 update_post_meta( $this->get_id(), '_lp_type', $type );
490
491 return true;
492 }
493
494 /**
495 * Update ordering of question answers
496 *
497 * @param array $orders List of answers
498 * @deprecated 4.1.7.3
499 */
500 public function update_answer_orders( $orders ) {
501 _deprecated_function( __METHOD__, '4.1.7.3' );
502 /*global $wpdb;
503 $query = $wpdb->prepare(
504 "
505 SELECT qa.question_answer_id, qam2.meta_value as `name`, qam.meta_value as `value`
506 FROM {$wpdb->learnpress_question_answers} qa
507 INNER JOIN {$wpdb->learnpress_question_answermeta} qam ON qa.question_answer_id = qam.learnpress_question_answer_id AND qam.meta_key = %s
508 INNER JOIN {$wpdb->learnpress_question_answermeta} qam2 ON qa.question_answer_id = qam2.learnpress_question_answer_id AND qam2.meta_key = %s
509 WHERE qa.question_id = %d
510 ORDER BY `order`
511 ",
512 'value',
513 'text',
514 $this->get_id()
515 );
516 if ( $answers = $wpdb->get_results( $query ) ) {
517 $query = "
518 UPDATE {$wpdb->learnpress_question_answers}
519 SET `order` = CASE
520 ";
521 for ( $order = 0, $n = sizeof( $orders ); $order < $n; $order ++ ) {
522 $found_answer = false;
523 foreach ( $answers as $answer ) {
524 if ( $answer->value == $orders[ $order ]['value'] && $answer->name == $orders[ $order ]['text'] ) {
525 $found_answer = $answer;
526 break;
527 }
528 }
529 if ( $found_answer === false ) {
530 continue;
531 }
532 $query .= $wpdb->prepare( 'WHEN question_answer_id = %d THEN %d', $found_answer->question_answer_id, $order + 1 ) . "\n";
533 }
534 $query .= sprintf( 'ELSE `order` END WHERE question_id = %d', $this->get_id() );
535 $wpdb->query( $query );
536 }*/
537 }
538
539 /**
540 * Return type of question.
541 *
542 * @return string
543 */
544 public function get_type() {
545 return $this->_question_type;
546 }
547
548 /**
549 * Return type of question in 'readable text'.
550 *
551 * @return string
552 */
553 public function get_type_label() {
554 return learn_press_question_types()[ $this->get_type() ];
555 }
556
557 protected function _init() {
558 //add_filter( 'learn_press_question_answers', array( $this, '_get_default_answers' ), 10, 2 );
559 }
560
561
562 /**
563 *
564 * @param mixed $answers
565 * @param LP_Question $q
566 *
567 * @return array|bool
568 * @deprecated 4.1.7.3
569 */
570 /*public function _get_default_answers( $answers = false, $q = null ) {
571 if ( ! $answers && ( $q && $q->get_id() == $this->get_id() ) ) {
572 $answers = $this->get_default_answers();
573 }
574
575 return $answers;
576 }*/
577
578 /**
579 * Get default question answer.
580 *
581 * @return array
582 */
583 public static function get_default_answer() {
584 $answer = array(
585 'question_answer_id' => - 1,
586 'title' => '',
587 'is_true' => '',
588 'value' => learn_press_random_value(),
589 );
590
591 return $answer;
592 }
593
594 /**
595 * Get default question list answers.
596 *
597 * @return array|bool
598 */
599 public function get_default_answers() {
600 $answers = array(
601 array(
602 'question_answer_id' => - 1,
603 'is_true' => 'yes',
604 'value' => learn_press_random_value(),
605 'title' => esc_html__( 'First option', 'learnpress' ),
606 ),
607 array(
608 'question_answer_id' => - 2,
609 'is_true' => 'no',
610 'value' => learn_press_random_value(),
611 'title' => esc_html__( 'Second option', 'learnpress' ),
612 ),
613 array(
614 'question_answer_id' => - 3,
615 'is_true' => 'no',
616 'value' => learn_press_random_value(),
617 'title' => esc_html__( 'Third option', 'learnpress' ),
618 ),
619 );
620
621 return $answers;
622 }
623
624
625 /**
626 * Get question answers option.
627 *
628 * @since 3.0.0
629 * @version 4.0.1
630 * @modify 4.1.7.3 by tungnx
631 * @return LP_Question_Answer_Option[]
632 */
633 public function get_answers(): array {
634 $lp_question_cache = LP_Question_Cache::instance();
635 $key_cache = "{$this->get_id()}/option_answers";
636 //$answers = LP_Object_Cache::get( 'answer-options-' . $this->get_id(), 'learn-press/questions' );
637 $answers_option = $lp_question_cache->get_cache( $key_cache );
638
639 if ( false === $answers_option ) {
640 $answers_option = $this->_curd->load_answer_options( $this->get_id() );
641
642 if ( ! $answers_option ) {
643 $answers_option = $this->get_default_answers();
644 }
645
646 $lp_question_cache->set_cache( $key_cache, $answers_option );
647 }
648
649 require_once 'class-lp-question-answers.php';
650
651 $answers_option_tmp = [];
652 foreach ( $answers_option as $k => $answer ) {
653 $answer_option = new LP_Question_Answer_Option( $this, $answer );
654 $answers_option_tmp[] = $answer_option;
655 }
656
657 return $answers_option_tmp;
658 }
659
660 /**
661 * Create default answers.
662 *
663 * @since 3.3.0
664 */
665 public function create_default_answers() {
666 global $wpdb;
667
668 $answers = $this->get_default_answers();
669
670 foreach ( $answers as $index => $answer ) {
671 $answer = array(
672 'question_id' => $this->get_id(),
673 'title' => $answer['title'],
674 'value' => $answer['value'] ?? '',
675 'is_true' => ( $answer['is_true'] == 'yes' ) ? $answer['is_true'] : '',
676 'order' => $index + 1,
677 );
678
679 $wpdb->insert(
680 $wpdb->learnpress_question_answers,
681 $answer,
682 array( '%d', '%s', '%s', '%s', '%d' )
683 );
684
685 $question_answer_id = $wpdb->insert_id;
686
687 if ( $question_answer_id ) {
688 $answer['question_answer_id'] = $question_answer_id;
689 }
690
691 $answers[ $index ] = $answer;
692 }
693
694 $this->set_data( 'answer_options', $answers );
695 }
696
697 public function setup_data( $quiz_id, $course_id = 0, $user_id = 0 ) {
698
699 $quiz = learn_press_get_quiz( $quiz_id );
700 $course = learn_press_get_course( $course_id );
701
702 if ( $user_id ) {
703 $user = learn_press_get_user( $user_id );
704 } else {
705 $user = learn_press_get_current_user();
706 }
707
708 $show_correct = false;
709
710 if ( $user && $quiz && $course ) {
711 $user_quiz = $user->get_quiz_data( $quiz->get_id(), $course->get_id() );
712 if ( $user_quiz ) {
713 $has_checked = $user->has_checked_answer( $this->get_id(), $quiz->get_id(), $course->get_id() );
714 $show_correct = $user_quiz->is_completed() && ( $has_checked || $quiz->get_show_result() ) ? 'yes' : false;
715 $answered = $user_quiz->get_question_answer( $this->get_id() );
716 $this->set_answered( $answered );
717 }
718 }
719
720 $this->show_correct_answers( $show_correct );
721 }
722
723 /**
724 * Get question name.
725 *
726 * @return string
727 * @deprecated 4.1.7.3
728 */
729 public function get_name() {
730 _deprecated_function( __METHOD__, '4.1.7.3' );
731 /*return $this->_options['name'] ?? ucfirst(
732 preg_replace_callback(
733 '!_([a-z])!',
734 array(
735 $this,
736 'sanitize_name_callback',
737 ),
738 $this->get_type()
739 )
740 );*/
741 }
742
743 /**
744 * @param $matches
745 *
746 * @return string
747 */
748 public function sanitize_name_callback( $matches ) {
749 return strtoupper( $matches[1] );
750 }
751
752 /**
753 * Sets the value for a variable of this class
754 *
755 * @param $key string The name of a variable of this class
756 * @param $value mixed The value to set
757 *
758 * @return void
759 */
760 public function set( $key, $value ) {
761 $this->$key = $value;
762 }
763
764 /**
765 * Gets the value of a variable of this class with multiple level of an object or array
766 * example: $obj->get('a.b') -> like this :
767 * - $obj->a->b
768 * - or $obj->a['b']
769 *
770 * @param null $key string Single or multiple level such as a.b.c
771 * @param null $default mixed Return a default value if the key does not exists or is empty
772 * @param null $func string The function to apply the result before return
773 *
774 * @return mixed|null
775 */
776 public function get( $key = null, $default = null, $func = null ) {
777 $val = $this->_get( $this, $key, $default );
778
779 return is_callable( $func ) ? call_user_func_array( $func, array( $val ) ) : $val;
780 }
781
782
783 /**
784 * Magic function to get question data.
785 *
786 * @param $prop
787 * @param $key
788 * @param null $default
789 * @param null $type
790 *
791 * @return mixed|null
792 */
793 protected function _get( $prop, $key, $default = null, $type = null ) {
794 $return = $default;
795
796 if ( $key === false || $key == null ) {
797 return $return;
798 }
799 $deep = explode( '.', $key );
800
801 if ( is_array( $prop ) ) {
802 if ( isset( $prop[ $deep[0] ] ) ) {
803 $return = $prop[ $deep[0] ];
804 if ( count( $deep ) > 1 ) {
805 unset( $deep[0] );
806 $return = $this->_get( $return, implode( '.', $deep ), $default, $type );
807 }
808 }
809 } elseif ( is_object( $prop ) ) {
810 if ( isset( $prop->{$deep[0]} ) ) {
811 $return = $prop->{$deep[0]};
812 if ( count( $deep ) > 1 ) {
813 unset( $deep[0] );
814 $return = $this->_get( $return, implode( '.', $deep ), $default, $type );
815 }
816 }
817 }
818
819 if ( $type == 'object' ) {
820 settype( $return, 'object' );
821 } elseif ( $type == 'array' ) {
822 settype( $return, 'array' );
823 }
824
825 return $return;
826 }
827
828 /**
829 * Find value in answer's option and compare with value answered by user.
830 *
831 * @param LP_Question_Answer_Option $answer
832 * @param mixed $answered
833 *
834 * @return bool
835 */
836 public function is_selected_option( $answer, $answered = false ): bool {
837 if ( is_array( $answered ) ) {
838 $is_selected = in_array( $answer['value'], $answered );
839 } else {
840 $is_selected = $answer['value'] === $answered;
841 }
842
843 return apply_filters( 'learn-press/question/is-selected-option', $is_selected, $answer, $answered, $this->get_id() );
844 }
845
846 /**
847 * Save user question answer.
848 *
849 * @param $answer
850 * @param $quiz_id
851 * @param null $user_id
852 * @deprecated 4.2.7.3
853 */
854 /*public function save_user_answer( $answer, $quiz_id, $user_id = null ) {
855 if ( $user_id ) {
856 $user = LP_User_Factory::get_user( $user_id );
857 } else {
858 $user = learn_press_get_current_user();
859 }
860
861 $progress = $user->get_quiz_progress( $quiz_id );
862
863 if ( $progress ) {
864 if ( ! isset( $progress->question_answers ) ) {
865 $question_answers = array();
866 } else {
867 $question_answers = $progress->question_answers;
868 }
869 $question_answers[ $this->get_id() ] = $answer;
870
871 $question_answers = apply_filters( 'learn_press_update_user_question_answers', $question_answers, $progress->history_id, $user_id, $this, $quiz_id );
872
873 // learn_press_update_user_quiz_meta( $progress->history_id, 'question_answers', $question_answers );
874 }
875 }*/
876
877 /**
878 * Allow check question answer, default disable for True or False and Single choice, override by Multiple choice question.
879 *
880 * @return bool
881 */
882 public function can_check_answer() {
883 return false;
884 }
885
886 /**
887 * Check user answer, override by question type class.
888 *
889 * @param null $args | question answered
890 *
891 * @return array
892 */
893 public function check( $args = null ) {
894 $return = array(
895 'correct' => false,
896 'mark' => 0,
897 );
898
899 return $return;
900 }
901
902 /**
903 * Get answer at position
904 *
905 * @since 3.0.0
906 *
907 * @param int $at
908 *
909 * @return LP_Question_Answer_Option|mixed
910 */
911 public function get_answer_at( $at ) {
912 return $this->get_answers()->get_answer_at( $at );
913 }
914
915 /**
916 * Get user answered data.
917 *
918 * @param $args
919 *
920 * @return null
921 */
922 public function get_user_answered( $args ) {
923 $args = wp_parse_args(
924 $args,
925 array(
926 'history_id' => 0,
927 'quiz_id' => 0,
928 'course_id' => 0,
929 'force' => false,
930 )
931 );
932 $answered = null;
933 if ( $args['history_id'] ) {
934 $user_meta = learn_press_get_user_item_meta( $args['history_id'], 'question_answers', true );
935 if ( $user_meta && array_key_exists( $this->get_id(), $user_meta ) ) {
936 $answered = $user_meta[ $this->get_id() ];
937 }
938 } elseif ( $args['quiz_id'] && $args['course_id'] ) {
939 $user = learn_press_get_current_user();
940 $history = $user->get_quiz_results( $args['quiz_id'], $args['course_id'] );
941
942 if ( $history ) {
943 $user_meta = learn_press_get_user_item_meta( $history->history_id, 'question_answers', true );
944
945 if ( $user_meta && array_key_exists( $this->get_id(), $user_meta ) ) {
946 $answered = $user_meta[ $this->get_id() ];
947 }
948 }
949 }
950
951 return $answered;
952 }
953
954 /**
955 * Get question.
956 *
957 * @param bool $the_question
958 * @param array $args
959 *
960 * @return LP_Question|bool
961 */
962 public static function get_question( $the_question = false, $args = array() ) {
963 // question object
964 $the_question = self::get_question_object( $the_question );
965 if ( ! $the_question ) {
966 return false;
967 }
968
969 if ( ! empty( $args['force'] ) ) {
970 $force = ! ! $args['force'];
971 unset( $args['force'] );
972 } else {
973 $force = false;
974 }
975
976 $key_args = wp_parse_args(
977 $args,
978 array(
979 'id' => $the_question->ID,
980 'type' => $the_question->post_type,
981 )
982 );
983
984 $key = LP_Helper::array_to_md5( $key_args );
985
986 if ( $force ) {
987 LP_Global::$questions[ $key ] = false;
988 }
989
990 if ( empty( LP_Global::$questions[ $key ] ) ) {
991 $class_name = self::get_question_class( $the_question, $args );
992 if ( is_string( $class_name ) && class_exists( $class_name ) ) {
993 $lesson = new $class_name( $the_question->ID, $args );
994 } elseif ( $class_name instanceof LP_Question ) {
995 $lesson = $class_name;
996 } else {
997 $lesson = new self( $the_question->ID, $args );
998 }
999 LP_Global::$questions[ $key ] = $lesson;
1000 }
1001
1002 return LP_Global::$questions[ $key ];
1003 }
1004
1005 /**
1006 * Get the question class name.
1007 *
1008 * @param WP_Post $the_question
1009 * @param array $args (default: array())
1010 *
1011 * @return string
1012 */
1013 private static function get_question_class( $the_question, $args = array() ) {
1014 $question_id = absint( $the_question->ID );
1015 if ( ! empty( $args['type'] ) ) {
1016 $question_type = $args['type'];
1017 } else {
1018 $question_type = get_post_meta( $question_id, '_lp_type', true );
1019 }
1020
1021 $class_name = self::get_class_name_from_question_type( array( $question_type ) );
1022
1023 // Filter class name so that the class can be overridden if extended.
1024 return apply_filters( 'learn-press/question/object-class', $class_name, $question_type, $question_id );
1025 }
1026
1027 /**
1028 * Get question class from question type.
1029 *
1030 * @param string $question_type
1031 *
1032 * @return string|false
1033 */
1034 public static function get_class_name_from_question_type( $question_type ) {
1035
1036 if ( is_array( $question_type ) ) {
1037 $question_type = reset( $question_type );
1038 }
1039
1040 return ! $question_type ? __CLASS__ : 'LP_Question_' . implode( '_', array_map( 'ucfirst', explode( '-', $question_type ) ) );
1041 }
1042
1043 /**
1044 * Get the question object.
1045 *
1046 * @since 3.0.0
1047 *
1048 * @param mixed $the_question
1049 *
1050 * @uses WP_Post
1051 * @return WP_Post|bool false on failure
1052 */
1053 private static function get_question_object( $the_question ) {
1054 if ( false === $the_question ) {
1055 $the_question = get_post_type() === LP_QUESTION_CPT ? $GLOBALS['post'] : false;
1056 } elseif ( is_numeric( $the_question ) ) {
1057 $the_question = get_post( $the_question );
1058 } elseif ( $the_question instanceof LP_Course_Item ) {
1059 $the_question = get_post( $the_question->get_id() );
1060 } elseif ( ! ( $the_question instanceof WP_Post ) ) {
1061 $the_question = false;
1062 }
1063
1064 return apply_filters( 'learn-press/question/post-object', $the_question );
1065 }
1066
1067 protected function _get_checked( $user_answer = null ) {
1068 $key = $user_answer ? md5( serialize( $user_answer ) ) : - 1;
1069
1070 return LP_Object_Cache::get( 'question-' . $this->get_id() . '/' . $key, 'learn-press/answer-checked' );
1071 }
1072
1073 protected function _set_checked( $checked, $user_answer ) {
1074 $key = $user_answer ? md5( serialize( $user_answer ) ) : - 1;
1075
1076 return LP_Object_Cache::set( 'question-' . $this->get_id() . '/' . $key, $checked, 'learn-press/answer-checked' );
1077 }
1078
1079 /**
1080 * Get default json data for editor settings
1081 *
1082 * @since 3.3.0
1083 *
1084 * @return array
1085 */
1086 public function get_editor_default_settings() {
1087 return apply_filters(
1088 'learn-press/question-editor-default-settings',
1089 array(
1090 'id' => $this->get_id(),
1091 'type' => $this->get_type(),
1092 'duration' => $this->get_duration(),
1093 )
1094 );
1095 }
1096
1097 /**
1098 * Get json data for editor settings.
1099 *
1100 * @since 3.3.0
1101 *
1102 * @return array
1103 */
1104 public function get_editor_settings() {
1105 $defaults = $this->get_editor_default_settings();
1106
1107 return apply_filters(
1108 'learn-press/question-editor-settings',
1109 array_merge(
1110 $defaults,
1111 array()
1112 ),
1113 $this->get_type(),
1114 $this->get_id(),
1115 $this
1116 );
1117 }
1118 }
1119
1120 }
1121