Diff: STRATO-apps/wordpress_03/app/wp-content/plugins/tutor-pro/tools/importers/QuizImporter.php
Keine Baseline-Datei – Diff nur gegen leer.
1
-
1
+
<?php
2
+
/**
3
+
* Quiz Importer
4
+
*
5
+
* @package TutorPro\Tools
6
+
* @author Themeum<support@themeum.com>
7
+
* @link https://themeum.com
8
+
* @since 3.6.0
9
+
*/
10
+
11
+
namespace TutorPro\Tools;
12
+
13
+
use Tutor\Helpers\QueryHelper;
14
+
use Tutor\Helpers\ValidationHelper;
15
+
use TutorPro\ContentBank\Models\ContentModel;
16
+
use TUTOR\Quiz;
17
+
18
+
if ( ! defined( 'ABSPATH' ) ) {
19
+
exit;
20
+
}
21
+
22
+
/**
23
+
* Quiz Importer Class.
24
+
*/
25
+
class QuizImporter {
26
+
27
+
/**
28
+
* Quiz Class Instance.
29
+
*
30
+
* @since 3.6.0
31
+
*
32
+
* @var Quiz
33
+
*/
34
+
private $quiz;
35
+
36
+
/**
37
+
* Quiz Importer Class Constructor.
38
+
*/
39
+
public function __construct() {
40
+
$this->quiz = new Quiz();
41
+
}
42
+
43
+
/**
44
+
* Set meta data of quiz post type.
45
+
*
46
+
* @since 3.6.0
47
+
*
48
+
* @param array $quiz_meta the quiz meta data to set.
49
+
* @param int $quiz_id the quiz post id.
50
+
*
51
+
* @return void
52
+
*/
53
+
public function set_quiz_meta( $quiz_meta, $quiz_id ) {
54
+
$quiz_meta = array_map( fn( $val ) => $val[0], $quiz_meta );
55
+
if ( isset( $quiz_meta['_content_drip_settings'] ) ) {
56
+
update_post_meta( $quiz_id, '_content_drip_settings', $quiz_meta['_content_drip_settings'] );
57
+
}
58
+
59
+
if ( isset( $quiz_meta[ QUIZ::META_QUIZ_OPTION ] ) ) {
60
+
update_post_meta( $quiz_id, QUIZ::META_QUIZ_OPTION, $quiz_meta[ QUIZ::META_QUIZ_OPTION ] );
61
+
}
62
+
}
63
+
64
+
/**
65
+
* Flatten all nested quiz question and answer data to a single array.
66
+
*
67
+
* @since 3.6.0
68
+
*
69
+
* @since 3.7.0 param $question and $answers added.
70
+
*
71
+
* @param array $data the quiz question answer data to flatten.
72
+
* @param int $content_id the content id for the question.
73
+
* @param array $question array of question data.
74
+
* @param array $answers array of answer data.
75
+
*
76
+
* @return array
77
+
*/
78
+
public function flatten_quiz_question_answer( $data, $content_id = 0, $question = array(), $answers = array() ) {
79
+
$flatten_content = array();
80
+
81
+
if ( $data ) {
82
+
foreach ( $data as $post_id => $question_answer ) {
83
+
$questions = array_column( $question_answer, 'question' );
84
+
$answers = array_column( $question_answer, 'answers' );
85
+
$answers = array_merge( ...$answers );
86
+
foreach ( $questions as $questions_data ) {
87
+
$questions_data['quiz_id'] = $post_id;
88
+
$flatten_content['question'][] = $questions_data;
89
+
}
90
+
91
+
foreach ( $answers as $answers_data ) {
92
+
$flatten_content['answers'][] = $answers_data;
93
+
}
94
+
}
95
+
}
96
+
97
+
if ( $content_id ) {
98
+
99
+
$question['content_id'] = $content_id;
100
+
$flatten_content['question'][] = $question;
101
+
102
+
foreach ( $answers as $answers_data ) {
103
+
$flatten_content['answers'][] = $answers_data;
104
+
}
105
+
}
106
+
107
+
return $flatten_content;
108
+
}
109
+
110
+
/**
111
+
* Prepare quiz question answer data for bulk insertion.
112
+
*
113
+
* @since 3.6.0
114
+
* @since 3.8.1 param $from_course added.
115
+
*
116
+
* @param array $quiz_questions_answers the quiz question answer data to prepare.
117
+
* @param bool $from_course whether import from course.
118
+
*
119
+
* @return array
120
+
*/
121
+
private function prepare_quiz_questions_answers( $quiz_questions_answers, $from_course = false ) {
122
+
$questions = $quiz_questions_answers['question'];
123
+
$answers = $quiz_questions_answers['answers'];
124
+
$final_questions = array();
125
+
$final_answers = array();
126
+
127
+
$question_types = array_keys( tutor_utils()->get_question_types() );
128
+
129
+
foreach ( $questions as $question ) {
130
+
if ( ! $from_course ) {
131
+
$rules = array(
132
+
'question_mark' => 'required|numeric',
133
+
'question_title' => 'required',
134
+
'question_type' => 'required|match_string:' . implode( ',', $question_types ),
135
+
'question_settings' => 'required',
136
+
);
137
+
138
+
$validate = ValidationHelper::validate( $rules, $question );
139
+
140
+
if ( ! $validate->success ) {
141
+
continue;
142
+
}
143
+
}
144
+
145
+
if ( isset( $question['question_type'] ) ) {
146
+
if ( 'image_matching' === $question['question_type'] ) {
147
+
$question['question_type'] = 'matching';
148
+
}
149
+
150
+
if ( 'single_choice' === $question['question_type'] ) {
151
+
$question['question_type'] = 'multiple_choice';
152
+
}
153
+
}
154
+
155
+
$question['question_title'] = addslashes( $question['question_title'] );
156
+
157
+
if ( isset( $question['question_description'] ) ) {
158
+
$question['question_description'] = addslashes( $question['question_description'] );
159
+
}
160
+
161
+
if ( isset( $question['answer_explanation'] ) ) {
162
+
$question['answer_explanation'] = addslashes( $question['answer_explanation'] );
163
+
}
164
+
165
+
$question_settings = $question['question_settings'];
166
+
167
+
if ( isset( $question_settings['question_type'] ) && ! in_array( $question_settings['question_type'], $question_types ) ) {
168
+
continue;
169
+
}
170
+
171
+
if ( isset( $question_settings['question_type'] ) ) {
172
+
if ( 'single_choice' === $question_settings['question_type'] ) {
173
+
$question_settings['question_type'] = 'multiple_choice';
174
+
}
175
+
176
+
if ( 'image_matching' === $question_settings['question_type'] ) {
177
+
$question_settings['is_image_matching'] = true;
178
+
}
179
+
}
180
+
181
+
$question['question_settings'] = maybe_serialize( $question_settings );
182
+
183
+
if ( isset( $question['question_id'] ) ) {
184
+
unset( $question['question_id'] );
185
+
}
186
+
187
+
array_push( $final_questions, $question );
188
+
}
189
+
190
+
foreach ( $answers as $answer ) {
191
+
if ( ! $from_course ) {
192
+
$rules = array(
193
+
'belongs_question_id' => 'required|numeric',
194
+
'answer_title' => 'required',
195
+
'question_type' => '',
196
+
'belongs_question_type' => 'required|match_string:' . implode( ',', $question_types ),
197
+
);
198
+
199
+
$validate = ValidationHelper::validate( $rules, $answer );
200
+
201
+
if ( ! $validate->success ) {
202
+
continue;
203
+
}
204
+
}
205
+
206
+
if ( isset( $answer['belongs_question_type'] ) ) {
207
+
if ( 'single_choice' === $answer['belongs_question_type'] ) {
208
+
$answer['belongs_question_type'] = 'multiple_choice';
209
+
}
210
+
211
+
if ( 'image_matching' === $answer['belongs_question_type'] ) {
212
+
$answer['belongs_question_type'] = 'matching';
213
+
}
214
+
}
215
+
216
+
if ( isset( $answer['answer_title'] ) ) {
217
+
$answer['answer_title'] = addslashes( $answer['answer_title'] );
218
+
}
219
+
220
+
if ( $answer['image_url'] ) {
221
+
$upload_data = Helper::upload_file_by_url( $answer['image_url'] );
222
+
223
+
if ( ! is_wp_error( $upload_data ) ) {
224
+
$answer['image_id'] = $upload_data['id'];
225
+
}
226
+
}
227
+
228
+
unset( $answer['image_url'] );
229
+
230
+
if ( isset( $answer['answer_id'] ) ) {
231
+
unset( $answer['answer_id'] );
232
+
}
233
+
234
+
array_push( $final_answers, $answer );
235
+
}
236
+
237
+
return array(
238
+
'question' => $final_questions,
239
+
'answers' => $final_answers,
240
+
);
241
+
}
242
+
243
+
244
+
/**
245
+
* Bulk save quiz question and answer into database.
246
+
*
247
+
* @since 3.6.0
248
+
*
249
+
* @param array $quiz_questions_answers the quiz question answers to save.
250
+
* @param bool $from_course is the import for course.
251
+
* @param int $parent_id the parent course id.
252
+
* @param int $quiz_id the quiz id.
253
+
*
254
+
* @return bool|\WP_Error
255
+
*/
256
+
public function save_quiz_questions_answers( $quiz_questions_answers, $from_course = false, $parent_id = 0, $quiz_id = 0 ) {
257
+
global $wpdb;
258
+
259
+
$result = true;
260
+
261
+
$table_question = "{$wpdb->prefix}tutor_quiz_questions";
262
+
$table_answer = "{$wpdb->prefix}tutor_quiz_question_answers";
263
+
264
+
$questions = $quiz_questions_answers['question'];
265
+
$answers = $quiz_questions_answers['answers'];
266
+
$previous_question_ids = array_column( $questions, 'question_id' );
267
+
$previous_answer_ids = $answers ? array_filter( array_column( $answers, 'answer_id' ) ) : null; // filter to remove null values.
268
+
$question_ids = array();
269
+
$answer_ids = array();
270
+
271
+
$quiz_questions_answers = $this->prepare_quiz_questions_answers( $quiz_questions_answers, $from_course );
272
+
273
+
$answers = $quiz_questions_answers['answers'];
274
+
$questions = $quiz_questions_answers['question'];
275
+
276
+
$courses_map = ContentMapHandler::get_content_map()['courses'] ?? null;
277
+
278
+
try {
279
+
$result = QueryHelper::insert_multiple_rows( $table_question, $questions, false, false );
280
+
if ( $result ) {
281
+
$question_ids = $wpdb->get_results(//phpcs:ignore
282
+
"SELECT question_id FROM {$table_question} WHERE question_id >= LAST_INSERT_ID()", //phpcs:ignore
283
+
'ARRAY_N'
284
+
);
285
+
// Flatten ids.
286
+
$question_ids = array_merge( ...$question_ids );
287
+
}
288
+
} catch ( \Exception $e ) {
289
+
return new \WP_Error( 'db_insert_fail', __( 'Error inserting quiz questions', 'tutor-pro' ), $e->getMessage() );
290
+
}
291
+
292
+
if ( count( $question_ids ) !== count( $previous_question_ids ) ) {
293
+
return new \WP_Error( 'question_mismatch_error', __( 'Invalid quiz question data', 'tutor-pro' ) );
294
+
}
295
+
296
+
if ( $question_ids && $previous_question_ids ) {
297
+
$question_ids = array_combine( $previous_question_ids, $question_ids );
298
+
}
299
+
300
+
if ( $answers && $question_ids ) {
301
+
$answers = array_map(
302
+
function ( $val ) use ( $question_ids ) {
303
+
$val['belongs_question_id'] = $question_ids[ $val['belongs_question_id'] ];
304
+
return $val;
305
+
},
306
+
$answers
307
+
);
308
+
}
309
+
310
+
if ( $answers ) {
311
+
try {
312
+
$result = QueryHelper::insert_multiple_rows( $table_answer, $answers, false, false );
313
+
if ( $result ) {
314
+
$answer_ids = $wpdb->get_results(//phpcs:ignore
315
+
"SELECT answer_id FROM {$table_answer} WHERE answer_id >= LAST_INSERT_ID()", //phpcs:ignore
316
+
'ARRAY_N'
317
+
);
318
+
// Flatten ids.
319
+
$answer_ids = array_merge( ...$answer_ids );
320
+
}
321
+
} catch ( \Exception $e ) {
322
+
return new \WP_Error( 'db_insert_fail', __( 'Error inserting quiz questions', 'tutor-pro' ), $e->getMessage() );
323
+
}
324
+
325
+
if ( count( $answer_ids ) !== count( $previous_answer_ids ) ) {
326
+
return new \WP_Error( 'answer_mismatch_error', __( 'Invalid quiz answer data', 'tutor-pro' ) );
327
+
}
328
+
329
+
if ( $answer_ids && $previous_answer_ids ) {
330
+
$answer_ids = array_combine( $previous_answer_ids, $answer_ids );
331
+
}
332
+
}
333
+
334
+
if ( $parent_id && $quiz_id && $courses_map ) {
335
+
if ( isset( $courses_map[ $parent_id ] ) ) {
336
+
$map = $courses_map[ $parent_id ];
337
+
if ( isset( $map[ tutor()->quiz_post_type ] ) && isset( $map[ tutor()->quiz_post_type ][ $quiz_id ] ) ) {
338
+
$quiz_map = $map[ tutor()->quiz_post_type ][ $quiz_id ];
339
+
$quiz_map['question'] = $question_ids;
340
+
$quiz_map['answers'] = $answer_ids;
341
+
342
+
$map[ tutor()->quiz_post_type ][ $quiz_id ] = $quiz_map;
343
+
}
344
+
345
+
$courses_map[ $parent_id ] = $map;
346
+
}
347
+
}
348
+
349
+
ContentMapHandler::update_content_map( 'courses', $courses_map );
350
+
351
+
unset( $questions );
352
+
unset( $answers );
353
+
unset( $previous_question_ids );
354
+
unset( $previous_answer_ids );
355
+
unset( $question_ids );
356
+
unset( $answer_ids );
357
+
unset( $courses_map );
358
+
359
+
return $result;
360
+
}
361
+
362
+
/**
363
+
* Insert quiz contents.
364
+
*
365
+
* @since 3.8.1
366
+
*
367
+
* @param array $content the list of quiz contents.
368
+
* @param boolean $keep_media_files whether to keep media files.
369
+
* @param int $parent_id the parent course id.
370
+
*
371
+
* @return int|\WP_Error
372
+
*/
373
+
public function insert_quiz( $content, $keep_media_files = false, $parent_id = 0 ) {
374
+
global $wpdb;
375
+
376
+
$previous_quiz_id = $content['ID'] ?? 0;
377
+
$meta = $content['meta'] ?? null;
378
+
$question_answer = $content['question_answer'] ?? null;
379
+
$question = $content['question'] ?? null;
380
+
$answers = $content['answers'] ?? null;
381
+
382
+
$courses_map = ContentMapHandler::get_content_map()['courses'] ?? null;
383
+
384
+
$content = Helper::unset_post_data( $content );
385
+
386
+
$content_id = wp_insert_post( $content, true, true );
387
+
388
+
if ( is_wp_error( $content_id ) ) {
389
+
return $content_id;
390
+
}
391
+
392
+
if ( get_post_type( $content_id ) === ContentModel::QUESTION_POST_TYPE ) {
393
+
$quiz_question_answer = $this->flatten_quiz_question_answer( null, $content_id, $question, $answers );
394
+
395
+
$result = $this->save_quiz_questions_answers( $quiz_question_answer );
396
+
if ( is_wp_error( $result ) ) {
397
+
ErrorHandler::set_error( $content['post_type'], 'Error adding question answer for quiz : ' . $content['post_title'] );
398
+
}
399
+
}
400
+
401
+
if ( $meta && tutor()->quiz_post_type === get_post_type( $content_id ) ) {
402
+
$meta = Helper::prepare_meta( $content_id, $meta, $keep_media_files );
403
+
try {
404
+
QueryHelper::insert_multiple_rows( $wpdb->postmeta, $meta, false, false );
405
+
} catch ( \Throwable $th ) {
406
+
ErrorHandler::set_error( $content['post_type'], 'Error saving meta value for quiz : ' . $content['post_title'] );
407
+
}
408
+
}
409
+
410
+
if ( get_tutor_post_types( 'quiz' ) === get_post_type( $content_id ) ) {
411
+
if ( $courses_map && $parent_id ) {
412
+
if ( isset( $courses_map[ $parent_id ] ) ) {
413
+
$map = $courses_map[ $parent_id ];
414
+
$map[ tutor()->quiz_post_type ][ $previous_quiz_id ] = array( 'quiz_id' => $content_id );
415
+
$courses_map[ $parent_id ] = $map;
416
+
}
417
+
}
418
+
419
+
ContentMapHandler::update_content_map( 'courses', $courses_map );
420
+
unset( $courses_map );
421
+
422
+
if ( $question_answer ) {
423
+
$quiz_question_answer = $this->flatten_quiz_question_answer( array( $content_id => $question_answer ) );
424
+
$this->save_quiz_questions_answers( $quiz_question_answer, false, $parent_id, $previous_quiz_id );
425
+
}
426
+
}
427
+
428
+
unset( $meta );
429
+
unset( $quiz_question_answer );
430
+
unset( $question );
431
+
unset( $answers );
432
+
433
+
return $content_id;
434
+
}
435
+
}
436
+