PluginProbe
LearnPress – WordPress LMS Plugin for Create and Sell Online Courses / 4.4.7
LearnPress – WordPress LMS Plugin for Create and Sell Online Courses v4.4.7
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 / Models / QuizPostModel.php

QuizPostModel.php in LearnPress – WordPress LMS Plugin for Create and Sell Online Courses 4.4.7, at inc/Models/QuizPostModel.php

412 lines 10.9 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 /**
4 * Class Quiz Post Model
5 *
6 * @package LearnPress/Classes
7 * @version 1.0.1
8 * @since 4.2.7.6
9 */
10
11 namespace LearnPress\Models;
12
13 use Exception;
14 use LearnPress\Databases\QuizQuestionsDB;
15 use LearnPress\Models\Question\QuestionAnswerModel;
16 use LearnPress\Models\Question\QuestionPostModel;
17 use LearnPress\Models\Quiz\QuizQuestionModel;
18 use LP_Cache;
19 use LP_Question;
20 use LP_Question_DB;
21 use LP_Question_Filter;
22 use LP_Quiz;
23 use LP_Quiz_Filter;
24 use Throwable;
25
26
27 class QuizPostModel extends PostModel {
28 /**
29 * @var string Post Type
30 */
31 public $post_type = LP_QUIZ_CPT;
32
33 /**
34 * Const meta key
35 */
36 const META_KEY_PASSING_GRADE = '_lp_passing_grade';
37 const META_KEY_RETAKE_COUNT = '_lp_retake_count';
38 const META_KEY_DURATION = '_lp_duration';
39 const META_KEY_PAGINATION = '_lp_pagination';
40 const META_KEY_REVIEW = '_lp_review';
41 const META_KEY_SHOW_CORRECT_REVIEW = '_lp_show_correct_review';
42 const META_KEY_INSTANT_CHECK = '_lp_instant_check';
43 const META_KEY_NEGATIVE_MARKING = '_lp_negative_marking';
44 const META_KEY_MINUS_SKIP_QUESTIONS = '_lp_minus_skip_questions';
45
46 /**
47 * Get post assignment by ID
48 *
49 * @param int $post_id
50 * @param bool $check_cache
51 *
52 * @return false|static
53 */
54 public static function find( int $post_id, bool $check_cache = false ) {
55 $filter_post = new LP_Quiz_Filter();
56 $filter_post->ID = $post_id;
57
58 $key_cache = "quizPostModel/find/{$post_id}";
59 $lpQuizCache = new LP_Cache();
60
61 // Check cache
62 if ( $check_cache ) {
63 $quizPostModel = $lpQuizCache->get_cache( $key_cache );
64 if ( $quizPostModel instanceof QuizPostModel ) {
65 return $quizPostModel;
66 }
67 }
68
69 $quizPostModel = self::get_item_model_from_db( $filter_post );
70 // Set cache
71 if ( $quizPostModel instanceof QuizPostModel ) {
72 $lpQuizCache->set_cache( $key_cache, $quizPostModel );
73 }
74
75 return $quizPostModel;
76 }
77
78 /**
79 * Get max mark of assignment
80 *
81 * @return string
82 */
83 public function get_duration(): string {
84 return $this->get_meta_value_by_key( self::META_KEY_DURATION, '0 minute' );
85 }
86
87 /**
88 * Get max mark of assignment
89 *
90 * @return float
91 */
92 public function get_passing_grade(): float {
93 return (float) $this->get_meta_value_by_key( self::META_KEY_PASSING_GRADE, 80 );
94 }
95
96 /**
97 * Get retake count option.
98 *
99 * @return int
100 */
101 public function get_retake_count(): int {
102 return (int) $this->get_meta_value_by_key( self::META_KEY_RETAKE_COUNT, 0 );
103 }
104
105 /**
106 * Get all question's ids of the quiz.
107 *
108 * @param array $statuses
109 *
110 * @return int[]
111 * @version 1.0.0
112 * @since 4.2.7.6
113 */
114 public function get_question_ids( array $statuses = [ 'publish' ] ): array {
115 $lp_question_db = LP_Question_DB::getInstance();
116 $question_ids = [];
117
118 try {
119 $filter = new LP_Question_Filter();
120 $filter->ID = $this->get_id();
121 $filter->post_status = $statuses;
122 $question_ids = $lp_question_db->get_list_question_ids_of_quiz( $filter );
123
124 // Hook old
125 if ( has_filter( 'learn-press/quiz/get-question-ids' ) ) {
126 $quiz_old = new LP_Quiz( $this->get_id() );
127 $course_id_of_quiz_old = $quiz_old->get_course_id();
128
129 $question_ids = apply_filters(
130 'learn-press/quiz/get-question-ids',
131 $question_ids,
132 $this->get_id(),
133 $course_id_of_quiz_old
134 );
135 }
136
137 $question_ids = apply_filters(
138 'learn-press/quiz/question-ids',
139 $question_ids,
140 $this,
141 $statuses
142 );
143
144 if ( ! is_array( $question_ids ) ) {
145 $question_ids = array();
146 }
147 } catch ( Throwable $e ) {
148 error_log( $e->getMessage() );
149 }
150
151 return $question_ids;
152 }
153
154 /**
155 * Get number questions in quiz.
156 *
157 * @return int
158 */
159 public function count_questions(): int {
160 $size = 0;
161 $questions = $this->get_question_ids();
162
163 if ( $questions ) {
164 $size = sizeof( $questions );
165 }
166
167 return (int) apply_filters( 'learn-press/quiz/count-questions', $size, $this->get_id() );
168 }
169
170 /**
171 * Get Mark of the quiz (total mark questions).
172 *
173 * @return mixed|null
174 */
175 public function get_mark() {
176 $questions = $this->get_question_ids();
177 $mark = 0;
178
179 foreach ( $questions as $question_id ) {
180 $question = LP_Question::get_question( $question_id );
181 if ( $question ) {
182 $mark += $question->get_mark();
183 }
184 }
185
186 return apply_filters( 'learn-press/quiz-mark', $mark, $this->get_id() );
187 }
188
189 /**
190 * Check option instant check has enabled.
191 *
192 * @return bool
193 */
194 public function has_instant_check(): bool {
195 return $this->get_meta_value_by_key( self::META_KEY_INSTANT_CHECK, 'no' ) === 'yes';
196 }
197
198 /**
199 * Check option negative marking has enabled.
200 *
201 * @return bool
202 */
203 public function has_negative_marking(): bool {
204 return $this->get_meta_value_by_key( self::META_KEY_NEGATIVE_MARKING, 'no' ) === 'yes';
205 }
206
207 /**
208 * Check option minus skip questions has enabled.
209 *
210 * @return bool
211 */
212 public function has_minus_skip_questions(): bool {
213 return $this->get_meta_value_by_key( self::META_KEY_MINUS_SKIP_QUESTIONS, 'no' ) === 'yes';
214 }
215
216 /**
217 * Check option minus skip questions has enabled.
218 *
219 * @return bool
220 */
221 public function has_show_correct_review(): bool {
222 return $this->get_meta_value_by_key( self::META_KEY_SHOW_CORRECT_REVIEW, 'yes' ) === 'yes';
223 }
224
225 /**
226 * Create question and add to quiz.
227 *
228 * @param array $data [ question_title, question_type, question_content ]
229 *
230 * @return QuizQuestionModel
231 * @throws Exception
232 * @since 4.2.9
233 * @version 1.0.1
234 */
235 public function create_question_and_add( array $data ): QuizQuestionModel {
236 $question_title = $data['question_title'] ?? '';
237 $question_type = $data['question_type'] ?? '';
238 $question_content = $data['question_content'] ?? '';
239 $question_options = $data['question_options'] ?? [];
240
241 if ( empty( $question_title ) ) {
242 throw new Exception( __( 'Question title is required', 'learnpress' ) );
243 }
244
245 if ( ! QuestionPostModel::check_type_valid( $question_type ) ) {
246 throw new Exception( __( 'Invalid question type', 'learnpress' ) );
247 }
248
249 // Create question
250 $questionPostModelNew = new QuestionPostModel();
251 $questionPostModelNew->post_title = $question_title;
252 $questionPostModelNew->post_content = $question_content;
253 $questionPostModelNew->post_status = 'publish';
254 $questionPostModelNew->post_author = get_current_user_id();
255 $questionPostModelNew->save();
256 $questionPostModelNew->save_meta_value_by_key(
257 QuestionPostModel::META_KEY_TYPE,
258 $question_type
259 );
260
261 // Get question object by type
262 $questionClassName = $questionPostModelNew::get_question_obj_by_type( $question_type );
263 if ( class_exists( $questionClassName ) ) {
264 /**
265 * @var QuestionPostModel $questionPostTyeModel
266 */
267 $questionPostTyeModel = new $questionClassName( $questionPostModelNew );
268 if ( ! empty( $question_options ) ) {
269 foreach ( $question_options as $index => $answer ) {
270 $answer = array(
271 'question_id' => $questionPostTyeModel->get_id(),
272 'title' => $answer['title'],
273 'value' => $questionPostTyeModel->random_value(),
274 'is_true' => $answer['is_true'] ?? '',
275 'order' => $index + 1,
276 );
277
278 $questionAnswerModel = new QuestionAnswerModel( $answer );
279 $questionAnswerModel->save();
280 $answers[ $index ]['question_answer_id'] = $questionAnswerModel->question_answer_id;
281 }
282 } elseif ( method_exists( $questionPostTyeModel, 'create_default_answers' ) ) {
283 $questionPostTyeModel->create_default_answers();
284 }
285 } else {
286 throw new Exception( __( 'Question type not found', 'learnpress' ) );
287 }
288
289 $quizQuestionsDB = QuizQuestionsDB::getInstance();
290 $max_order = $quizQuestionsDB->get_last_number_order( $this->get_id() );
291
292 // Add question to quiz
293 $quizQuestionModel = new QuizQuestionModel();
294 $quizQuestionModel->quiz_id = $this->get_id();
295 $quizQuestionModel->question_id = $questionPostModelNew->get_id();
296 $quizQuestionModel->question_order = $max_order + 1;
297 $quizQuestionModel->save();
298
299 return $quizQuestionModel;
300 }
301
302 /**
303 * Add questions exists (from Question Bank) to quiz.
304 *
305 * @param array $data [ 'question_ids' => [] ]
306 *
307 * @throws Exception
308 * @since 4.3.2
309 * @version 1.0.0
310 */
311 public function add_questions_to_quiz( array $data ): array {
312 $this->check_capabilities_update_item_course();
313
314 $question_ids = $data['question_ids'] ?? [];
315 if ( empty( $question_ids ) ) {
316 throw new Exception( __( 'Question IDs are required', 'learnpress' ) );
317 }
318
319 $quiz_questions_added = [];
320 foreach ( $question_ids as $question_id ) {
321 $questionPostModel = QuestionPostModel::find( $question_id, true );
322 if ( ! $questionPostModel ) {
323 throw new Exception( __( 'Question not found', 'learnpress' ) );
324 }
325
326 // Check if question already exists in quiz
327 $quizQuestionModel = QuizQuestionModel::find( $this->get_id(), $question_id, true );
328 if ( $quizQuestionModel ) {
329 continue; // Skip if question already exists in quiz
330 }
331
332 $quizQuestionsDB = QuizQuestionsDB::getInstance();
333 $max_order = $quizQuestionsDB->get_last_number_order( $this->get_id() );
334
335 // Add question to quiz
336 $quizQuestionModel = new QuizQuestionModel();
337 $quizQuestionModel->quiz_id = $this->get_id();
338 $quizQuestionModel->question_id = $question_id;
339 $quizQuestionModel->question_order = $max_order + 1;
340 $quizQuestionModel->save();
341
342 $quiz_questions_added[] = $quizQuestionModel;
343 }
344
345 return $quiz_questions_added;
346 }
347
348 /**
349 * Update question positions in quiz.
350 *
351 * @param int[] $data [ 'question_ids' => [] ]
352 *
353 * @throws Exception
354 * @since 4.3.2
355 * @version 1.0.0
356 */
357 public function update_question_position( array $data ) {
358 $this->check_capabilities_update_item_course();
359
360 $question_ids = $data['question_ids'] ?? [];
361 if ( empty( $question_ids ) ) {
362 throw new Exception( __( 'Question IDs are required', 'learnpress' ) );
363 }
364
365 $question_ids = $data['question_ids'] ?? [];
366 QuizQuestionsDB::getInstance()->update_question_position( $question_ids, $this->get_id() );
367 }
368
369 /**
370 * Remove question from quiz.
371 *
372 * @param int $question_id
373 *
374 * @throws Exception
375 * @since 4.3.2
376 * @version 1.0.0
377 */
378 public function remove_question_from_quiz( int $question_id ) {
379 $this->check_capabilities_update_item_course();
380
381 $questionPostModel = QuestionPostModel::find( $question_id, true );
382 if ( ! $questionPostModel ) {
383 throw new Exception( __( 'Question not found', 'learnpress' ) );
384 }
385
386 $quizQuestionModel = QuizQuestionModel::find( $this->get_id(), $question_id );
387 if ( ! $quizQuestionModel ) {
388 throw new Exception( __( 'Question not found in quiz', 'learnpress' ) );
389 }
390
391 $quizQuestionModel->delete();
392 }
393
394 /**
395 * Check create
396 *
397 * @return bool
398 */
399 public function check_capabilities_create(): bool {
400 return current_user_can( 'edit_' . LP_LESSON_CPT . 's' );
401 }
402
403 /**
404 * Check update
405 *
406 * @return bool
407 */
408 public function check_capabilities_update(): bool {
409 return current_user_can( 'edit_' . LP_LESSON_CPT, $this->ID );
410 }
411 }
412