|
1
|
|
|
<?php |
|
2
|
|
|
/* For licensing terms, see /license.txt */ |
|
3
|
|
|
|
|
4
|
|
|
/** |
|
5
|
|
|
* Class Question |
|
6
|
|
|
* |
|
7
|
|
|
* This class allows to instantiate an object of type Question |
|
8
|
|
|
* |
|
9
|
|
|
* @author Olivier Brouckaert, original author |
|
10
|
|
|
* @author Patrick Cool, LaTeX support |
|
11
|
|
|
* @author Julio Montoya <[email protected]> lot of bug fixes |
|
12
|
|
|
* @author [email protected] - add question categories |
|
13
|
|
|
* @package chamilo.exercise |
|
14
|
|
|
*/ |
|
15
|
|
|
abstract class Question |
|
16
|
|
|
{ |
|
17
|
|
|
public $id; |
|
18
|
|
|
public $question; |
|
19
|
|
|
public $description; |
|
20
|
|
|
public $weighting; |
|
21
|
|
|
public $position; |
|
22
|
|
|
public $type; |
|
23
|
|
|
public $level; |
|
24
|
|
|
public $picture; |
|
25
|
|
|
public $exerciseList; // array with the list of exercises which this question is in |
|
26
|
|
|
public $category_list; |
|
27
|
|
|
public $parent_id; |
|
28
|
|
|
public $category; |
|
29
|
|
|
public $isContent; |
|
30
|
|
|
public $course; |
|
31
|
|
|
public static $typePicture = 'new_question.png'; |
|
32
|
|
|
public static $explanationLangVar = ''; |
|
33
|
|
|
public $question_table_class = 'table table-striped'; |
|
34
|
|
|
public static $questionTypes = array( |
|
35
|
|
|
UNIQUE_ANSWER => array('unique_answer.class.php', 'UniqueAnswer'), |
|
36
|
|
|
MULTIPLE_ANSWER => array('multiple_answer.class.php', 'MultipleAnswer'), |
|
37
|
|
|
FILL_IN_BLANKS => array('fill_blanks.class.php', 'FillBlanks'), |
|
38
|
|
|
MATCHING => array('matching.class.php', 'Matching'), |
|
39
|
|
|
FREE_ANSWER => array('freeanswer.class.php', 'FreeAnswer'), |
|
40
|
|
|
ORAL_EXPRESSION => array('oral_expression.class.php', 'OralExpression'), |
|
41
|
|
|
HOT_SPOT => array('hotspot.class.php', 'HotSpot'), |
|
42
|
|
|
HOT_SPOT_DELINEATION => array('hotspot.class.php', 'HotspotDelineation'), |
|
43
|
|
|
MULTIPLE_ANSWER_COMBINATION => array('multiple_answer_combination.class.php', 'MultipleAnswerCombination'), |
|
44
|
|
|
UNIQUE_ANSWER_NO_OPTION => array('unique_answer_no_option.class.php', 'UniqueAnswerNoOption'), |
|
45
|
|
|
MULTIPLE_ANSWER_TRUE_FALSE => array('multiple_answer_true_false.class.php', 'MultipleAnswerTrueFalse'), |
|
46
|
|
|
MULTIPLE_ANSWER_COMBINATION_TRUE_FALSE => array( |
|
47
|
|
|
'multiple_answer_combination_true_false.class.php', |
|
48
|
|
|
'MultipleAnswerCombinationTrueFalse' |
|
49
|
|
|
), |
|
50
|
|
|
GLOBAL_MULTIPLE_ANSWER => array('global_multiple_answer.class.php' , 'GlobalMultipleAnswer'), |
|
51
|
|
|
CALCULATED_ANSWER => array('calculated_answer.class.php' , 'CalculatedAnswer'), |
|
52
|
|
|
UNIQUE_ANSWER_IMAGE => ['UniqueAnswerImage.php', 'UniqueAnswerImage'], |
|
53
|
|
|
DRAGGABLE => ['Draggable.php', 'Draggable'], |
|
54
|
|
|
MATCHING_DRAGGABLE => ['MatchingDraggable.php', 'MatchingDraggable'] |
|
55
|
|
|
//MEDIA_QUESTION => array('media_question.class.php' , 'MediaQuestion') |
|
56
|
|
|
); |
|
57
|
|
|
|
|
58
|
|
|
/** |
|
59
|
|
|
* constructor of the class |
|
60
|
|
|
* |
|
61
|
|
|
* @author Olivier Brouckaert |
|
62
|
|
|
*/ |
|
63
|
|
|
public function __construct() |
|
64
|
|
|
{ |
|
65
|
|
|
$this->id = 0; |
|
66
|
|
|
$this->question = ''; |
|
67
|
|
|
$this->description = ''; |
|
68
|
|
|
$this->weighting = 0; |
|
69
|
|
|
$this->position = 1; |
|
70
|
|
|
$this->picture = ''; |
|
71
|
|
|
$this->level = 1; |
|
72
|
|
|
$this->category = 0; |
|
73
|
|
|
// This variable is used when loading an exercise like an scenario with |
|
74
|
|
|
// an special hotspot: final_overlap, final_missing, final_excess |
|
75
|
|
|
$this->extra = ''; |
|
76
|
|
|
$this->exerciseList = array(); |
|
77
|
|
|
$this->course = api_get_course_info(); |
|
78
|
|
|
$this->category_list = array(); |
|
79
|
|
|
$this->parent_id = 0; |
|
80
|
|
|
} |
|
81
|
|
|
|
|
82
|
|
|
/** |
|
83
|
|
|
* @return int|null |
|
84
|
|
|
*/ |
|
85
|
|
|
public function getIsContent() |
|
86
|
|
|
{ |
|
87
|
|
|
$isContent = null; |
|
88
|
|
|
if (isset($_REQUEST['isContent'])) { |
|
89
|
|
|
$isContent = intval($_REQUEST['isContent']); |
|
90
|
|
|
} |
|
91
|
|
|
|
|
92
|
|
|
return $this->isContent = $isContent; |
|
93
|
|
|
} |
|
94
|
|
|
|
|
95
|
|
|
/** |
|
96
|
|
|
* Reads question information from the data base |
|
97
|
|
|
* |
|
98
|
|
|
* @param int $id - question ID |
|
99
|
|
|
* @param int $course_id |
|
100
|
|
|
* |
|
101
|
|
|
* @return Question |
|
102
|
|
|
* |
|
103
|
|
|
* @author Olivier Brouckaert |
|
104
|
|
|
*/ |
|
105
|
|
|
public static function read($id, $course_id = null) |
|
106
|
|
|
{ |
|
107
|
|
|
$id = intval($id); |
|
108
|
|
|
|
|
109
|
|
|
if (!empty($course_id)) { |
|
110
|
|
|
$course_info = api_get_course_info_by_id($course_id); |
|
111
|
|
|
} else { |
|
112
|
|
|
$course_info = api_get_course_info(); |
|
113
|
|
|
} |
|
114
|
|
|
|
|
115
|
|
|
$course_id = $course_info['real_id']; |
|
116
|
|
|
|
|
117
|
|
|
if (empty($course_id) || $course_id == -1 ) { |
|
118
|
|
|
|
|
119
|
|
|
return false; |
|
120
|
|
|
} |
|
121
|
|
|
|
|
122
|
|
|
$TBL_QUESTIONS = Database::get_course_table(TABLE_QUIZ_QUESTION); |
|
123
|
|
|
$TBL_EXERCISE_QUESTION = Database::get_course_table(TABLE_QUIZ_TEST_QUESTION); |
|
124
|
|
|
|
|
125
|
|
|
$sql = "SELECT question, description, ponderation, position, type, picture, level, extra |
|
126
|
|
|
FROM $TBL_QUESTIONS |
|
127
|
|
|
WHERE c_id = $course_id AND id = $id "; |
|
128
|
|
|
|
|
129
|
|
|
$result = Database::query($sql); |
|
130
|
|
|
|
|
131
|
|
|
// if the question has been found |
|
132
|
|
|
if ($object = Database::fetch_object($result)) { |
|
133
|
|
|
|
|
134
|
|
|
$objQuestion = Question::getInstance($object->type); |
|
135
|
|
|
if (!empty($objQuestion)) { |
|
136
|
|
|
|
|
137
|
|
|
$objQuestion->id = $id; |
|
138
|
|
|
$objQuestion->question = $object->question; |
|
139
|
|
|
$objQuestion->description = $object->description; |
|
140
|
|
|
$objQuestion->weighting = $object->ponderation; |
|
141
|
|
|
$objQuestion->position = $object->position; |
|
142
|
|
|
$objQuestion->type = $object->type; |
|
143
|
|
|
$objQuestion->picture = $object->picture; |
|
144
|
|
|
$objQuestion->level = (int) $object->level; |
|
145
|
|
|
$objQuestion->extra = $object->extra; |
|
146
|
|
|
$objQuestion->course = $course_info; |
|
147
|
|
|
$objQuestion->category = TestCategory::getCategoryForQuestion($id); |
|
148
|
|
|
|
|
149
|
|
|
$tblQuiz = Database::get_course_table(TABLE_QUIZ_TEST); |
|
150
|
|
|
|
|
151
|
|
|
$sql = "SELECT DISTINCT q.exercice_id |
|
152
|
|
|
FROM $TBL_EXERCISE_QUESTION q |
|
153
|
|
|
INNER JOIN $tblQuiz e |
|
154
|
|
|
ON e.c_id = q.c_id AND e.id = q.exercice_id |
|
155
|
|
|
WHERE |
|
156
|
|
|
q.c_id = $course_id AND |
|
157
|
|
|
q.question_id = $id AND |
|
158
|
|
|
e.active >= 0"; |
|
159
|
|
|
|
|
160
|
|
|
$result = Database::query($sql); |
|
161
|
|
|
|
|
162
|
|
|
// fills the array with the exercises which this question is in |
|
163
|
|
|
if ($result) { |
|
164
|
|
|
while ($obj = Database::fetch_object($result)) { |
|
165
|
|
|
$objQuestion->exerciseList[] = $obj->exercice_id; |
|
166
|
|
|
} |
|
167
|
|
|
} |
|
168
|
|
|
|
|
169
|
|
|
return $objQuestion; |
|
170
|
|
|
} |
|
171
|
|
|
} |
|
172
|
|
|
|
|
173
|
|
|
// question not found |
|
174
|
|
|
return false; |
|
175
|
|
|
} |
|
176
|
|
|
|
|
177
|
|
|
/** |
|
178
|
|
|
* returns the question ID |
|
179
|
|
|
* |
|
180
|
|
|
* @author Olivier Brouckaert |
|
181
|
|
|
* |
|
182
|
|
|
* @return integer - question ID |
|
183
|
|
|
*/ |
|
184
|
|
|
public function selectId() |
|
185
|
|
|
{ |
|
186
|
|
|
return $this->id; |
|
187
|
|
|
} |
|
188
|
|
|
|
|
189
|
|
|
/** |
|
190
|
|
|
* returns the question title |
|
191
|
|
|
* |
|
192
|
|
|
* @author Olivier Brouckaert |
|
193
|
|
|
* @return string - question title |
|
194
|
|
|
*/ |
|
195
|
|
|
public function selectTitle() |
|
196
|
|
|
{ |
|
197
|
|
|
return $this->question; |
|
198
|
|
|
} |
|
199
|
|
|
|
|
200
|
|
|
/** |
|
201
|
|
|
* returns the question description |
|
202
|
|
|
* |
|
203
|
|
|
* @author Olivier Brouckaert |
|
204
|
|
|
* @return string - question description |
|
205
|
|
|
*/ |
|
206
|
|
|
public function selectDescription() |
|
207
|
|
|
{ |
|
208
|
|
|
return $this->description; |
|
209
|
|
|
} |
|
210
|
|
|
|
|
211
|
|
|
/** |
|
212
|
|
|
* returns the question weighting |
|
213
|
|
|
* |
|
214
|
|
|
* @author Olivier Brouckaert |
|
215
|
|
|
* @return integer - question weighting |
|
216
|
|
|
*/ |
|
217
|
|
|
public function selectWeighting() |
|
218
|
|
|
{ |
|
219
|
|
|
return $this->weighting; |
|
220
|
|
|
} |
|
221
|
|
|
|
|
222
|
|
|
/** |
|
223
|
|
|
* returns the question position |
|
224
|
|
|
* |
|
225
|
|
|
* @author Olivier Brouckaert |
|
226
|
|
|
* @return integer - question position |
|
227
|
|
|
*/ |
|
228
|
|
|
public function selectPosition() |
|
229
|
|
|
{ |
|
230
|
|
|
return $this->position; |
|
231
|
|
|
} |
|
232
|
|
|
|
|
233
|
|
|
/** |
|
234
|
|
|
* returns the answer type |
|
235
|
|
|
* |
|
236
|
|
|
* @author Olivier Brouckaert |
|
237
|
|
|
* @return integer - answer type |
|
238
|
|
|
*/ |
|
239
|
|
|
public function selectType() |
|
240
|
|
|
{ |
|
241
|
|
|
return $this->type; |
|
242
|
|
|
} |
|
243
|
|
|
|
|
244
|
|
|
/** |
|
245
|
|
|
* returns the level of the question |
|
246
|
|
|
* |
|
247
|
|
|
* @author Nicolas Raynaud |
|
248
|
|
|
* @return integer - level of the question, 0 by default. |
|
249
|
|
|
*/ |
|
250
|
|
|
public function selectLevel() |
|
251
|
|
|
{ |
|
252
|
|
|
return $this->level; |
|
253
|
|
|
} |
|
254
|
|
|
|
|
255
|
|
|
/** |
|
256
|
|
|
* returns the picture name |
|
257
|
|
|
* |
|
258
|
|
|
* @author Olivier Brouckaert |
|
259
|
|
|
* @return string - picture name |
|
260
|
|
|
*/ |
|
261
|
|
|
public function selectPicture() |
|
262
|
|
|
{ |
|
263
|
|
|
return $this->picture; |
|
264
|
|
|
} |
|
265
|
|
|
|
|
266
|
|
|
/** |
|
267
|
|
|
* @return bool|string |
|
268
|
|
|
*/ |
|
269
|
|
|
public function selectPicturePath() |
|
270
|
|
|
{ |
|
271
|
|
|
if (!empty($this->picture)) { |
|
272
|
|
|
return api_get_path(WEB_COURSE_PATH) . $this->course['path'] . '/document/images/' . $this->picture; |
|
273
|
|
|
} |
|
274
|
|
|
|
|
275
|
|
|
return false; |
|
276
|
|
|
} |
|
277
|
|
|
|
|
278
|
|
|
/** |
|
279
|
|
|
* returns the array with the exercise ID list |
|
280
|
|
|
* |
|
281
|
|
|
* @author Olivier Brouckaert |
|
282
|
|
|
* @return array - list of exercise ID which the question is in |
|
283
|
|
|
*/ |
|
284
|
|
|
public function selectExerciseList() |
|
285
|
|
|
{ |
|
286
|
|
|
return $this->exerciseList; |
|
287
|
|
|
} |
|
288
|
|
|
|
|
289
|
|
|
/** |
|
290
|
|
|
* returns the number of exercises which this question is in |
|
291
|
|
|
* |
|
292
|
|
|
* @author Olivier Brouckaert |
|
293
|
|
|
* @return integer - number of exercises |
|
294
|
|
|
*/ |
|
295
|
|
|
public function selectNbrExercises() |
|
296
|
|
|
{ |
|
297
|
|
|
return sizeof($this->exerciseList); |
|
298
|
|
|
} |
|
299
|
|
|
|
|
300
|
|
|
/** |
|
301
|
|
|
* changes the question title |
|
302
|
|
|
* |
|
303
|
|
|
* @param string $title - question title |
|
304
|
|
|
* |
|
305
|
|
|
* @author Olivier Brouckaert |
|
306
|
|
|
*/ |
|
307
|
|
|
public function updateTitle($title) |
|
308
|
|
|
{ |
|
309
|
|
|
$this->question=$title; |
|
310
|
|
|
} |
|
311
|
|
|
|
|
312
|
|
|
/** |
|
313
|
|
|
* @param int $id |
|
314
|
|
|
*/ |
|
315
|
|
|
public function updateParentId($id) |
|
316
|
|
|
{ |
|
317
|
|
|
$this->parent_id = intval($id); |
|
318
|
|
|
} |
|
319
|
|
|
|
|
320
|
|
|
/** |
|
321
|
|
|
* changes the question description |
|
322
|
|
|
* |
|
323
|
|
|
* @param string $description - question description |
|
324
|
|
|
* |
|
325
|
|
|
* @author Olivier Brouckaert |
|
326
|
|
|
* |
|
327
|
|
|
*/ |
|
328
|
|
|
public function updateDescription($description) |
|
329
|
|
|
{ |
|
330
|
|
|
$this->description = $description; |
|
331
|
|
|
} |
|
332
|
|
|
|
|
333
|
|
|
/** |
|
334
|
|
|
* changes the question weighting |
|
335
|
|
|
* |
|
336
|
|
|
* @param integer $weighting - question weighting |
|
337
|
|
|
* |
|
338
|
|
|
* @author Olivier Brouckaert |
|
339
|
|
|
*/ |
|
340
|
|
|
public function updateWeighting($weighting) |
|
341
|
|
|
{ |
|
342
|
|
|
$this->weighting = $weighting; |
|
343
|
|
|
} |
|
344
|
|
|
|
|
345
|
|
|
/** |
|
346
|
|
|
* @param array $category |
|
347
|
|
|
* |
|
348
|
|
|
* @author Hubert Borderiou 12-10-2011 |
|
349
|
|
|
* |
|
350
|
|
|
*/ |
|
351
|
|
|
public function updateCategory($category) |
|
352
|
|
|
{ |
|
353
|
|
|
$this->category = $category; |
|
354
|
|
|
} |
|
355
|
|
|
|
|
356
|
|
|
/** |
|
357
|
|
|
* @param int $value |
|
358
|
|
|
* |
|
359
|
|
|
* @author Hubert Borderiou 12-10-2011 |
|
360
|
|
|
*/ |
|
361
|
|
|
public function updateScoreAlwaysPositive($value) |
|
362
|
|
|
{ |
|
363
|
|
|
$this->scoreAlwaysPositive = $value; |
|
364
|
|
|
} |
|
365
|
|
|
|
|
366
|
|
|
/** |
|
367
|
|
|
* @param int $value |
|
368
|
|
|
* |
|
369
|
|
|
* @author Hubert Borderiou 12-10-2011 |
|
370
|
|
|
*/ |
|
371
|
|
|
public function updateUncheckedMayScore($value) |
|
372
|
|
|
{ |
|
373
|
|
|
$this->uncheckedMayScore = $value; |
|
374
|
|
|
} |
|
375
|
|
|
|
|
376
|
|
|
/** |
|
377
|
|
|
* Save category of a question |
|
378
|
|
|
* |
|
379
|
|
|
* A question can have n categories if category is empty, |
|
380
|
|
|
* then question has no category then delete the category entry |
|
381
|
|
|
* |
|
382
|
|
|
* @param array $category_list |
|
383
|
|
|
* |
|
384
|
|
|
* @author Julio Montoya - Adding multiple cat support |
|
385
|
|
|
*/ |
|
386
|
|
|
public function saveCategories($category_list) |
|
387
|
|
|
{ |
|
388
|
|
|
if (!empty($category_list)) { |
|
389
|
|
|
$this->deleteCategory(); |
|
390
|
|
|
$TBL_QUESTION_REL_CATEGORY = Database::get_course_table(TABLE_QUIZ_QUESTION_REL_CATEGORY); |
|
391
|
|
|
|
|
392
|
|
|
// update or add category for a question |
|
393
|
|
|
foreach ($category_list as $category_id) { |
|
394
|
|
|
$category_id = intval($category_id); |
|
395
|
|
|
$question_id = intval($this->id); |
|
396
|
|
|
$sql = "SELECT count(*) AS nb |
|
397
|
|
|
FROM $TBL_QUESTION_REL_CATEGORY |
|
398
|
|
|
WHERE |
|
399
|
|
|
category_id = $category_id |
|
400
|
|
|
AND question_id = $question_id |
|
401
|
|
|
AND c_id=".api_get_course_int_id(); |
|
402
|
|
|
$res = Database::query($sql); |
|
403
|
|
|
$row = Database::fetch_array($res); |
|
404
|
|
|
if ($row['nb'] > 0) { |
|
|
|
|
|
|
405
|
|
|
// DO nothing |
|
406
|
|
|
} else { |
|
407
|
|
|
$sql = "INSERT INTO $TBL_QUESTION_REL_CATEGORY (c_id, question_id, category_id) |
|
408
|
|
|
VALUES (" . api_get_course_int_id() . ", $question_id, $category_id)"; |
|
409
|
|
|
Database::query($sql); |
|
410
|
|
|
} |
|
411
|
|
|
} |
|
412
|
|
|
} |
|
413
|
|
|
} |
|
414
|
|
|
|
|
415
|
|
|
/** |
|
416
|
|
|
* in this version, a question can only have 1 category |
|
417
|
|
|
* if category is 0, then question has no category then delete the category entry |
|
418
|
|
|
* @param int $category |
|
419
|
|
|
* |
|
420
|
|
|
* @author Hubert Borderiou 12-10-2011 |
|
421
|
|
|
*/ |
|
422
|
|
|
public function saveCategory($category) |
|
423
|
|
|
{ |
|
424
|
|
|
if ($category <= 0) { |
|
425
|
|
|
$this->deleteCategory(); |
|
426
|
|
|
} else { |
|
427
|
|
|
// update or add category for a question |
|
428
|
|
|
|
|
429
|
|
|
$table = Database::get_course_table(TABLE_QUIZ_QUESTION_REL_CATEGORY); |
|
430
|
|
|
$category_id = intval($category); |
|
431
|
|
|
$question_id = intval($this->id); |
|
432
|
|
|
$sql = "SELECT count(*) AS nb FROM $table |
|
433
|
|
|
WHERE |
|
434
|
|
|
question_id = $question_id AND |
|
435
|
|
|
c_id=" . api_get_course_int_id(); |
|
436
|
|
|
$res = Database::query($sql); |
|
437
|
|
|
$row = Database::fetch_array($res); |
|
438
|
|
|
if ($row['nb'] > 0) { |
|
439
|
|
|
$sql = "UPDATE $table |
|
440
|
|
|
SET category_id = $category_id |
|
441
|
|
|
WHERE |
|
442
|
|
|
question_id = $question_id AND |
|
443
|
|
|
c_id = " . api_get_course_int_id(); |
|
444
|
|
|
Database::query($sql); |
|
445
|
|
|
} else { |
|
446
|
|
|
$sql = "INSERT INTO $table (c_id, question_id, category_id) |
|
447
|
|
|
VALUES (" . api_get_course_int_id().", $question_id, $category_id)"; |
|
448
|
|
|
Database::query($sql); |
|
449
|
|
|
} |
|
450
|
|
|
} |
|
451
|
|
|
} |
|
452
|
|
|
|
|
453
|
|
|
/** |
|
454
|
|
|
* @author hubert borderiou 12-10-2011 |
|
455
|
|
|
* delete any category entry for question id |
|
456
|
|
|
* delete the category for question |
|
457
|
|
|
*/ |
|
458
|
|
|
public function deleteCategory() |
|
459
|
|
|
{ |
|
460
|
|
|
$table = Database::get_course_table(TABLE_QUIZ_QUESTION_REL_CATEGORY); |
|
461
|
|
|
$question_id = intval($this->id); |
|
462
|
|
|
$sql = "DELETE FROM $table |
|
463
|
|
|
WHERE |
|
464
|
|
|
question_id = $question_id AND |
|
465
|
|
|
c_id = " . api_get_course_int_id(); |
|
466
|
|
|
Database::query($sql); |
|
467
|
|
|
} |
|
468
|
|
|
|
|
469
|
|
|
/** |
|
470
|
|
|
* changes the question position |
|
471
|
|
|
* |
|
472
|
|
|
* @param integer $position - question position |
|
473
|
|
|
* |
|
474
|
|
|
* @author Olivier Brouckaert |
|
475
|
|
|
*/ |
|
476
|
|
|
public function updatePosition($position) |
|
477
|
|
|
{ |
|
478
|
|
|
$this->position = $position; |
|
479
|
|
|
} |
|
480
|
|
|
|
|
481
|
|
|
/** |
|
482
|
|
|
* changes the question level |
|
483
|
|
|
* |
|
484
|
|
|
* @param integer $level - question level |
|
485
|
|
|
* |
|
486
|
|
|
* @author Nicolas Raynaud |
|
487
|
|
|
*/ |
|
488
|
|
|
public function updateLevel($level) |
|
489
|
|
|
{ |
|
490
|
|
|
$this->level = $level; |
|
491
|
|
|
} |
|
492
|
|
|
|
|
493
|
|
|
/** |
|
494
|
|
|
* changes the answer type. If the user changes the type from "unique answer" to "multiple answers" |
|
495
|
|
|
* (or conversely) answers are not deleted, otherwise yes |
|
496
|
|
|
* |
|
497
|
|
|
* @param integer $type - answer type |
|
498
|
|
|
* |
|
499
|
|
|
* @author Olivier Brouckaert |
|
500
|
|
|
*/ |
|
501
|
|
|
public function updateType($type) |
|
502
|
|
|
{ |
|
503
|
|
|
$TBL_REPONSES = Database::get_course_table(TABLE_QUIZ_ANSWER); |
|
504
|
|
|
$course_id = $this->course['real_id']; |
|
505
|
|
|
|
|
506
|
|
|
if (empty($course_id)) { |
|
507
|
|
|
$course_id = api_get_course_int_id(); |
|
508
|
|
|
} |
|
509
|
|
|
// if we really change the type |
|
510
|
|
|
if ($type != $this->type) { |
|
511
|
|
|
// if we don't change from "unique answer" to "multiple answers" (or conversely) |
|
512
|
|
|
if ( |
|
513
|
|
|
!in_array($this->type, array(UNIQUE_ANSWER, MULTIPLE_ANSWER)) || |
|
514
|
|
|
!in_array($type, array(UNIQUE_ANSWER, MULTIPLE_ANSWER)) |
|
515
|
|
|
) { |
|
516
|
|
|
// removes old answers |
|
517
|
|
|
$sql = "DELETE FROM $TBL_REPONSES |
|
518
|
|
|
WHERE c_id = $course_id AND question_id = " . intval($this->id); |
|
519
|
|
|
Database::query($sql); |
|
520
|
|
|
} |
|
521
|
|
|
|
|
522
|
|
|
$this->type=$type; |
|
523
|
|
|
} |
|
524
|
|
|
} |
|
525
|
|
|
|
|
526
|
|
|
/** |
|
527
|
|
|
* adds a picture to the question |
|
528
|
|
|
* |
|
529
|
|
|
* @param string $Picture - temporary path of the picture to upload |
|
530
|
|
|
* @param string $PictureName - Name of the picture |
|
531
|
|
|
* @param string $picturePath |
|
532
|
|
|
* |
|
533
|
|
|
* @return boolean - true if uploaded, otherwise false |
|
534
|
|
|
* |
|
535
|
|
|
* @author Olivier Brouckaert |
|
536
|
|
|
*/ |
|
537
|
|
|
public function uploadPicture($Picture, $PictureName, $picturePath = null) |
|
538
|
|
|
{ |
|
539
|
|
|
if (empty($picturePath)) { |
|
540
|
|
|
global $picturePath; |
|
541
|
|
|
} |
|
542
|
|
|
|
|
543
|
|
|
if (!file_exists($picturePath)) { |
|
544
|
|
|
if (mkdir($picturePath, api_get_permissions_for_new_directories())) { |
|
545
|
|
|
// document path |
|
546
|
|
|
$documentPath = api_get_path(SYS_COURSE_PATH) . $this->course['path'] . "/document"; |
|
547
|
|
|
$path = str_replace($documentPath, '', $picturePath); |
|
548
|
|
|
$title_path = basename($picturePath); |
|
549
|
|
|
$doc_id = add_document($this->course, $path, 'folder', 0, $title_path); |
|
550
|
|
|
api_item_property_update( |
|
551
|
|
|
$this->course, |
|
552
|
|
|
TOOL_DOCUMENT, |
|
553
|
|
|
$doc_id, |
|
554
|
|
|
'FolderCreated', |
|
555
|
|
|
api_get_user_id() |
|
556
|
|
|
); |
|
557
|
|
|
} |
|
558
|
|
|
} |
|
559
|
|
|
|
|
560
|
|
|
// if the question has got an ID |
|
561
|
|
|
if ($this->id) { |
|
562
|
|
|
$this->picture = 'quiz-' . $this->id . '.jpg'; |
|
563
|
|
|
$o_img = new Image($Picture); |
|
564
|
|
|
$o_img->send_image($picturePath . '/' . $this->picture, -1, 'jpg'); |
|
565
|
|
|
$document_id = add_document( |
|
566
|
|
|
$this->course, |
|
567
|
|
|
'/images/' . $this->picture, |
|
568
|
|
|
'file', |
|
569
|
|
|
filesize($picturePath . '/' . $this->picture), |
|
570
|
|
|
$this->picture |
|
571
|
|
|
); |
|
572
|
|
|
if ($document_id) { |
|
573
|
|
|
return api_item_property_update( |
|
574
|
|
|
$this->course, |
|
575
|
|
|
TOOL_DOCUMENT, |
|
576
|
|
|
$document_id, |
|
577
|
|
|
'DocumentAdded', |
|
578
|
|
|
api_get_user_id() |
|
579
|
|
|
); |
|
580
|
|
|
} |
|
581
|
|
|
} |
|
582
|
|
|
|
|
583
|
|
|
return false; |
|
584
|
|
|
} |
|
585
|
|
|
|
|
586
|
|
|
/** |
|
587
|
|
|
* Resizes a picture || Warning!: can only be called after uploadPicture, |
|
588
|
|
|
* or if picture is already available in object. |
|
589
|
|
|
* @param string $Dimension - Resizing happens proportional according to given dimension: height|width|any |
|
590
|
|
|
* @param integer $Max - Maximum size |
|
591
|
|
|
* |
|
592
|
|
|
* @return boolean - true if success, false if failed |
|
593
|
|
|
* |
|
594
|
|
|
* @author Toon Keppens |
|
595
|
|
|
*/ |
|
596
|
|
|
public function resizePicture($Dimension, $Max) |
|
597
|
|
|
{ |
|
598
|
|
|
global $picturePath; |
|
599
|
|
|
|
|
600
|
|
|
// if the question has an ID |
|
601
|
|
|
if ($this->id) { |
|
602
|
|
|
// Get dimensions from current image. |
|
603
|
|
|
$my_image = new Image($picturePath . '/' . $this->picture); |
|
604
|
|
|
|
|
605
|
|
|
$current_image_size = $my_image->get_image_size(); |
|
606
|
|
|
$current_width = $current_image_size['width']; |
|
607
|
|
|
$current_height = $current_image_size['height']; |
|
608
|
|
|
|
|
609
|
|
|
if ($current_width < $Max && $current_height < $Max) |
|
610
|
|
|
return true; |
|
611
|
|
|
elseif ($current_height == "") |
|
612
|
|
|
return false; |
|
613
|
|
|
|
|
614
|
|
|
// Resize according to height. |
|
615
|
|
|
if ($Dimension == "height") { |
|
616
|
|
|
$resize_scale = $current_height / $Max; |
|
617
|
|
|
$new_height = $Max; |
|
618
|
|
|
$new_width = ceil($current_width / $resize_scale); |
|
619
|
|
|
} |
|
620
|
|
|
|
|
621
|
|
|
// Resize according to width |
|
622
|
|
|
if ($Dimension == "width") { |
|
623
|
|
|
$resize_scale = $current_width / $Max; |
|
624
|
|
|
$new_width = $Max; |
|
625
|
|
|
$new_height = ceil($current_height / $resize_scale); |
|
626
|
|
|
} |
|
627
|
|
|
|
|
628
|
|
|
// Resize according to height or width, both should not be larger than $Max after resizing. |
|
629
|
|
|
if ($Dimension == "any") { |
|
630
|
|
View Code Duplication |
if ($current_height > $current_width || $current_height == $current_width) { |
|
631
|
|
|
$resize_scale = $current_height / $Max; |
|
632
|
|
|
$new_height = $Max; |
|
633
|
|
|
$new_width = ceil($current_width / $resize_scale); |
|
634
|
|
|
} |
|
635
|
|
View Code Duplication |
if ($current_height < $current_width) { |
|
636
|
|
|
$resize_scale = $current_width / $Max; |
|
637
|
|
|
$new_width = $Max; |
|
638
|
|
|
$new_height = ceil($current_height / $resize_scale); |
|
639
|
|
|
} |
|
640
|
|
|
} |
|
641
|
|
|
|
|
642
|
|
|
$my_image->resize($new_width, $new_height); |
|
643
|
|
|
$result = $my_image->send_image($picturePath . '/' . $this->picture); |
|
644
|
|
|
|
|
645
|
|
|
if ($result) { |
|
646
|
|
|
return true; |
|
647
|
|
|
} else { |
|
648
|
|
|
return false; |
|
649
|
|
|
} |
|
650
|
|
|
} |
|
651
|
|
|
} |
|
652
|
|
|
|
|
653
|
|
|
/** |
|
654
|
|
|
* deletes the picture |
|
655
|
|
|
* |
|
656
|
|
|
* @author Olivier Brouckaert |
|
657
|
|
|
* @return boolean - true if removed, otherwise false |
|
658
|
|
|
*/ |
|
659
|
|
|
public function removePicture() |
|
660
|
|
|
{ |
|
661
|
|
|
global $picturePath; |
|
662
|
|
|
|
|
663
|
|
|
// if the question has got an ID and if the picture exists |
|
664
|
|
|
if ($this->id) { |
|
665
|
|
|
$picture = $this->picture; |
|
666
|
|
|
$this->picture = ''; |
|
667
|
|
|
|
|
668
|
|
|
return @unlink($picturePath . '/' . $picture) ? true : false; |
|
669
|
|
|
} |
|
670
|
|
|
|
|
671
|
|
|
return false; |
|
672
|
|
|
} |
|
673
|
|
|
|
|
674
|
|
|
/** |
|
675
|
|
|
* Exports a picture to another question |
|
676
|
|
|
* |
|
677
|
|
|
* @author Olivier Brouckaert |
|
678
|
|
|
* @param integer $questionId - ID of the target question |
|
679
|
|
|
* @return boolean - true if copied, otherwise false |
|
680
|
|
|
*/ |
|
681
|
|
|
public function exportPicture($questionId, $course_info) |
|
682
|
|
|
{ |
|
683
|
|
|
$course_id = $course_info['real_id']; |
|
684
|
|
|
$TBL_QUESTIONS = Database::get_course_table(TABLE_QUIZ_QUESTION); |
|
685
|
|
|
$destination_path = api_get_path(SYS_COURSE_PATH) . $course_info['path'] . '/document/images'; |
|
686
|
|
|
$source_path = api_get_path(SYS_COURSE_PATH) . $this->course['path'] . '/document/images'; |
|
687
|
|
|
|
|
688
|
|
|
// if the question has got an ID and if the picture exists |
|
689
|
|
|
if ($this->id && !empty($this->picture)) { |
|
690
|
|
|
$picture = explode('.', $this->picture); |
|
691
|
|
|
$extension = $picture[sizeof($picture) - 1]; |
|
692
|
|
|
$picture = 'quiz-' . $questionId . '.' . $extension; |
|
693
|
|
|
$result = @copy($source_path . '/' . $this->picture, $destination_path . '/' . $picture) ? true : false; |
|
694
|
|
|
// If copy was correct then add to the database |
|
695
|
|
|
if ($result) { |
|
696
|
|
|
$sql = "UPDATE $TBL_QUESTIONS SET |
|
697
|
|
|
picture = '" . Database::escape_string($picture) . "' |
|
698
|
|
|
WHERE c_id = $course_id AND id='" . intval($questionId) . "'"; |
|
699
|
|
|
Database::query($sql); |
|
700
|
|
|
|
|
701
|
|
|
$document_id = add_document( |
|
702
|
|
|
$course_info, |
|
703
|
|
|
'/images/' . $picture, |
|
704
|
|
|
'file', |
|
705
|
|
|
filesize($destination_path . '/' . $picture), |
|
706
|
|
|
$picture |
|
707
|
|
|
); |
|
708
|
|
|
if ($document_id) { |
|
709
|
|
|
return api_item_property_update( |
|
710
|
|
|
$course_info, |
|
711
|
|
|
TOOL_DOCUMENT, |
|
712
|
|
|
$document_id, |
|
713
|
|
|
'DocumentAdded', |
|
714
|
|
|
api_get_user_id() |
|
715
|
|
|
); |
|
716
|
|
|
} |
|
717
|
|
|
} |
|
718
|
|
|
|
|
719
|
|
|
return $result; |
|
720
|
|
|
} |
|
721
|
|
|
return false; |
|
722
|
|
|
} |
|
723
|
|
|
|
|
724
|
|
|
/** |
|
725
|
|
|
* Saves the picture coming from POST into a temporary file |
|
726
|
|
|
* Temporary pictures are used when we don't want to save a picture right after a form submission. |
|
727
|
|
|
* For example, if we first show a confirmation box. |
|
728
|
|
|
* |
|
729
|
|
|
* @author Olivier Brouckaert |
|
730
|
|
|
* @param string $Picture - temporary path of the picture to move |
|
731
|
|
|
* @param string $PictureName - Name of the picture |
|
732
|
|
|
*/ |
|
733
|
|
|
public function setTmpPicture($Picture, $PictureName) |
|
734
|
|
|
{ |
|
735
|
|
|
global $picturePath; |
|
736
|
|
|
$PictureName = explode('.', $PictureName); |
|
737
|
|
|
$Extension = $PictureName[sizeof($PictureName) - 1]; |
|
738
|
|
|
|
|
739
|
|
|
// saves the picture into a temporary file |
|
740
|
|
|
@move_uploaded_file($Picture, $picturePath . '/tmp.' . $Extension); |
|
741
|
|
|
} |
|
742
|
|
|
|
|
743
|
|
|
/** |
|
744
|
|
|
* Sets the title |
|
745
|
|
|
*/ |
|
746
|
|
|
public function setTitle($title) |
|
747
|
|
|
{ |
|
748
|
|
|
$this->question = $title; |
|
749
|
|
|
} |
|
750
|
|
|
|
|
751
|
|
|
/** |
|
752
|
|
|
* Sets extra info |
|
753
|
|
|
*/ |
|
754
|
|
|
public function setExtra($extra) |
|
755
|
|
|
{ |
|
756
|
|
|
$this->extra = $extra; |
|
757
|
|
|
} |
|
758
|
|
|
|
|
759
|
|
|
/** |
|
760
|
|
|
* Moves the temporary question "tmp" to "quiz-$questionId" |
|
761
|
|
|
* Temporary pictures are used when we don't want to save a picture right after a form submission. |
|
762
|
|
|
* For example, if we first show a confirmation box. |
|
763
|
|
|
* |
|
764
|
|
|
* @author Olivier Brouckaert |
|
765
|
|
|
* @return boolean - true if moved, otherwise false |
|
766
|
|
|
*/ |
|
767
|
|
|
function getTmpPicture() |
|
768
|
|
|
{ |
|
769
|
|
|
global $picturePath; |
|
770
|
|
|
|
|
771
|
|
|
// if the question has got an ID and if the picture exists |
|
772
|
|
|
if ($this->id) { |
|
773
|
|
|
if (file_exists($picturePath . '/tmp.jpg')) { |
|
774
|
|
|
$Extension = 'jpg'; |
|
775
|
|
|
} elseif (file_exists($picturePath . '/tmp.gif')) { |
|
776
|
|
|
$Extension = 'gif'; |
|
777
|
|
|
} elseif (file_exists($picturePath . '/tmp.png')) { |
|
778
|
|
|
$Extension = 'png'; |
|
779
|
|
|
} |
|
780
|
|
|
$this->picture = 'quiz-' . $this->id . '.' . $Extension; |
|
781
|
|
|
return @rename($picturePath . '/tmp.' . $Extension, $picturePath . '/' . $this->picture) ? true : false; |
|
782
|
|
|
} |
|
783
|
|
|
return false; |
|
784
|
|
|
} |
|
785
|
|
|
|
|
786
|
|
|
/** |
|
787
|
|
|
* updates the question in the data base |
|
788
|
|
|
* if an exercise ID is provided, we add that exercise ID into the exercise list |
|
789
|
|
|
* |
|
790
|
|
|
* @author Olivier Brouckaert |
|
791
|
|
|
* @param integer $exerciseId - exercise ID if saving in an exercise |
|
792
|
|
|
*/ |
|
793
|
|
|
public function save($exerciseId = 0) |
|
794
|
|
|
{ |
|
795
|
|
|
$TBL_EXERCISE_QUESTION = Database::get_course_table(TABLE_QUIZ_TEST_QUESTION); |
|
796
|
|
|
$TBL_QUESTIONS = Database::get_course_table(TABLE_QUIZ_QUESTION); |
|
797
|
|
|
|
|
798
|
|
|
$id = $this->id; |
|
799
|
|
|
$question = $this->question; |
|
800
|
|
|
$description = $this->description; |
|
801
|
|
|
$weighting = $this->weighting; |
|
802
|
|
|
$position = $this->position; |
|
803
|
|
|
$type = $this->type; |
|
804
|
|
|
$picture = $this->picture; |
|
805
|
|
|
$level = $this->level; |
|
806
|
|
|
$extra = $this->extra; |
|
807
|
|
|
$c_id = $this->course['real_id']; |
|
808
|
|
|
$category = $this->category; |
|
809
|
|
|
|
|
810
|
|
|
// question already exists |
|
811
|
|
|
if(!empty($id)) { |
|
812
|
|
|
|
|
813
|
|
|
$params = [ |
|
814
|
|
|
'question' => $question, |
|
815
|
|
|
'description' => $description, |
|
816
|
|
|
'ponderation' => $weighting, |
|
817
|
|
|
'position' => $position, |
|
818
|
|
|
'type' => $type, |
|
819
|
|
|
'picture' => $picture, |
|
820
|
|
|
'extra' => $extra, |
|
821
|
|
|
'level' => $level, |
|
822
|
|
|
]; |
|
823
|
|
|
|
|
824
|
|
|
Database::update( |
|
825
|
|
|
$TBL_QUESTIONS, |
|
826
|
|
|
$params, |
|
827
|
|
|
['c_id = ? AND id = ?' => [$c_id, $id]] |
|
828
|
|
|
); |
|
829
|
|
|
$this->saveCategory($category); |
|
|
|
|
|
|
830
|
|
|
|
|
831
|
|
|
if (!empty($exerciseId)) { |
|
832
|
|
|
api_item_property_update( |
|
833
|
|
|
$this->course, |
|
834
|
|
|
TOOL_QUIZ, |
|
835
|
|
|
$id, |
|
836
|
|
|
'QuizQuestionUpdated', |
|
837
|
|
|
api_get_user_id() |
|
838
|
|
|
); |
|
839
|
|
|
} |
|
840
|
|
|
if (api_get_setting('search_enabled') == 'true') { |
|
841
|
|
|
if ($exerciseId != 0) { |
|
842
|
|
|
$this -> search_engine_edit($exerciseId); |
|
843
|
|
|
} else { |
|
|
|
|
|
|
844
|
|
|
/** |
|
845
|
|
|
* actually there is *not* an user interface for |
|
846
|
|
|
* creating questions without a relation with an exercise |
|
847
|
|
|
*/ |
|
848
|
|
|
} |
|
849
|
|
|
} |
|
850
|
|
|
} else { |
|
851
|
|
|
// creates a new question |
|
852
|
|
|
$sql = "SELECT max(position) |
|
853
|
|
|
FROM $TBL_QUESTIONS as question, |
|
854
|
|
|
$TBL_EXERCISE_QUESTION as test_question |
|
855
|
|
|
WHERE |
|
856
|
|
|
question.id = test_question.question_id AND |
|
857
|
|
|
test_question.exercice_id = " . intval($exerciseId) . " AND |
|
858
|
|
|
question.c_id = $c_id AND |
|
859
|
|
|
test_question.c_id = $c_id "; |
|
860
|
|
|
$result = Database::query($sql); |
|
861
|
|
|
$current_position = Database::result($result,0,0); |
|
862
|
|
|
$this->updatePosition($current_position+1); |
|
863
|
|
|
$position = $this->position; |
|
864
|
|
|
|
|
865
|
|
|
$params = [ |
|
866
|
|
|
'c_id' => $c_id, |
|
867
|
|
|
'question' => $question, |
|
868
|
|
|
'description' => $description, |
|
869
|
|
|
'ponderation' => $weighting, |
|
870
|
|
|
'position' => $position, |
|
871
|
|
|
'type' => $type, |
|
872
|
|
|
'picture' => $picture, |
|
873
|
|
|
'extra' => $extra, |
|
874
|
|
|
'level' => $level |
|
875
|
|
|
]; |
|
876
|
|
|
|
|
877
|
|
|
$this->id = Database::insert($TBL_QUESTIONS, $params); |
|
878
|
|
|
|
|
879
|
|
|
if ($this->id) { |
|
880
|
|
|
|
|
881
|
|
|
$sql = "UPDATE $TBL_QUESTIONS SET id = iid WHERE iid = {$this->id}"; |
|
882
|
|
|
Database::query($sql); |
|
883
|
|
|
|
|
884
|
|
|
api_item_property_update( |
|
885
|
|
|
$this->course, |
|
886
|
|
|
TOOL_QUIZ, |
|
887
|
|
|
$this->id, |
|
888
|
|
|
'QuizQuestionAdded', |
|
889
|
|
|
api_get_user_id() |
|
890
|
|
|
); |
|
891
|
|
|
|
|
892
|
|
|
// If hotspot, create first answer |
|
893
|
|
View Code Duplication |
if ($type == HOT_SPOT || $type == HOT_SPOT_ORDER) { |
|
894
|
|
|
$TBL_ANSWERS = Database::get_course_table( |
|
895
|
|
|
TABLE_QUIZ_ANSWER |
|
896
|
|
|
); |
|
897
|
|
|
$params = [ |
|
898
|
|
|
'c_id' => $c_id, |
|
899
|
|
|
'question_id' => $this->id, |
|
900
|
|
|
'answer' => '', |
|
901
|
|
|
'correct' => '', |
|
902
|
|
|
'comment' => '', |
|
903
|
|
|
'ponderation' => 10, |
|
904
|
|
|
'position' => 1, |
|
905
|
|
|
'hotspot_coordinates' => '0;0|0|0', |
|
906
|
|
|
'hotspot_type' => 'square', |
|
907
|
|
|
]; |
|
908
|
|
|
$id = Database::insert($TBL_ANSWERS, $params); |
|
909
|
|
|
if ($id) { |
|
910
|
|
|
$sql = "UPDATE $TBL_ANSWERS SET id = iid, id_auto = iid WHERE iid = $id"; |
|
911
|
|
|
Database::query($sql); |
|
912
|
|
|
} |
|
913
|
|
|
} |
|
914
|
|
|
|
|
915
|
|
View Code Duplication |
if ($type == HOT_SPOT_DELINEATION) { |
|
916
|
|
|
$TBL_ANSWERS = Database::get_course_table( |
|
917
|
|
|
TABLE_QUIZ_ANSWER |
|
918
|
|
|
); |
|
919
|
|
|
$params = [ |
|
920
|
|
|
'c_id' => $c_id, |
|
921
|
|
|
'question_id' => $this->id, |
|
922
|
|
|
'answer' => '', |
|
923
|
|
|
'correct' => '', |
|
924
|
|
|
'comment' => '', |
|
925
|
|
|
'ponderation' => 10, |
|
926
|
|
|
'position' => 1, |
|
927
|
|
|
'hotspot_coordinates' => '0;0|0|0', |
|
928
|
|
|
'hotspot_type' => 'delineation', |
|
929
|
|
|
]; |
|
930
|
|
|
$id = Database::insert($TBL_ANSWERS, $params); |
|
931
|
|
|
|
|
932
|
|
|
if ($id) { |
|
933
|
|
|
$sql = "UPDATE $TBL_ANSWERS SET id = iid, id_auto = iid WHERE iid = $id"; |
|
934
|
|
|
Database::query($sql); |
|
935
|
|
|
} |
|
936
|
|
|
} |
|
937
|
|
|
|
|
938
|
|
|
if (api_get_setting('search_enabled') == 'true') { |
|
939
|
|
|
if ($exerciseId != 0) { |
|
940
|
|
|
$this->search_engine_edit($exerciseId, true); |
|
941
|
|
|
} else { |
|
|
|
|
|
|
942
|
|
|
/** |
|
943
|
|
|
* actually there is *not* an user interface for |
|
944
|
|
|
* creating questions without a relation with an exercise |
|
945
|
|
|
*/ |
|
946
|
|
|
} |
|
947
|
|
|
} |
|
948
|
|
|
} |
|
949
|
|
|
} |
|
950
|
|
|
|
|
951
|
|
|
// if the question is created in an exercise |
|
952
|
|
|
if ($exerciseId) { |
|
953
|
|
|
// adds the exercise into the exercise list of this question |
|
954
|
|
|
$this->addToList($exerciseId, TRUE); |
|
955
|
|
|
} |
|
956
|
|
|
} |
|
957
|
|
|
|
|
958
|
|
|
public function search_engine_edit($exerciseId, $addQs=false, $rmQs=false) |
|
959
|
|
|
{ |
|
960
|
|
|
// update search engine and its values table if enabled |
|
961
|
|
|
if (api_get_setting('search_enabled')=='true' && extension_loaded('xapian')) { |
|
962
|
|
|
$course_id = api_get_course_id(); |
|
963
|
|
|
// get search_did |
|
964
|
|
|
$tbl_se_ref = Database::get_main_table(TABLE_MAIN_SEARCH_ENGINE_REF); |
|
965
|
|
View Code Duplication |
if ($addQs || $rmQs) { |
|
966
|
|
|
//there's only one row per question on normal db and one document per question on search engine db |
|
967
|
|
|
$sql = 'SELECT * FROM %s |
|
968
|
|
|
WHERE course_code=\'%s\' AND tool_id=\'%s\' AND ref_id_second_level=%s LIMIT 1'; |
|
969
|
|
|
$sql = sprintf($sql, $tbl_se_ref, $course_id, TOOL_QUIZ, $this->id); |
|
970
|
|
|
} else { |
|
971
|
|
|
$sql = 'SELECT * FROM %s |
|
972
|
|
|
WHERE course_code=\'%s\' AND tool_id=\'%s\' |
|
973
|
|
|
AND ref_id_high_level=%s AND ref_id_second_level=%s LIMIT 1'; |
|
974
|
|
|
$sql = sprintf($sql, $tbl_se_ref, $course_id, TOOL_QUIZ, $exerciseId, $this->id); |
|
975
|
|
|
} |
|
976
|
|
|
$res = Database::query($sql); |
|
977
|
|
|
|
|
978
|
|
|
if (Database::num_rows($res) > 0 || $addQs) { |
|
979
|
|
|
require_once(api_get_path(LIBRARY_PATH) . 'search/ChamiloIndexer.class.php'); |
|
980
|
|
|
require_once(api_get_path(LIBRARY_PATH) . 'search/IndexableChunk.class.php'); |
|
981
|
|
|
|
|
982
|
|
|
$di = new ChamiloIndexer(); |
|
983
|
|
|
if ($addQs) { |
|
984
|
|
|
$question_exercises = array((int) $exerciseId); |
|
985
|
|
|
} else { |
|
986
|
|
|
$question_exercises = array(); |
|
987
|
|
|
} |
|
988
|
|
|
isset($_POST['language']) ? $lang = Database::escape_string($_POST['language']) : $lang = 'english'; |
|
989
|
|
|
$di->connectDb(NULL, NULL, $lang); |
|
990
|
|
|
|
|
991
|
|
|
// retrieve others exercise ids |
|
992
|
|
|
$se_ref = Database::fetch_array($res); |
|
993
|
|
|
$se_doc = $di->get_document((int)$se_ref['search_did']); |
|
994
|
|
|
if ($se_doc !== FALSE) { |
|
995
|
|
|
if (($se_doc_data = $di->get_document_data($se_doc)) !== FALSE) { |
|
996
|
|
|
$se_doc_data = unserialize($se_doc_data); |
|
997
|
|
|
if ( |
|
998
|
|
|
isset($se_doc_data[SE_DATA]['type']) && |
|
999
|
|
|
$se_doc_data[SE_DATA]['type'] == SE_DOCTYPE_EXERCISE_QUESTION |
|
1000
|
|
|
) { |
|
1001
|
|
|
if ( |
|
1002
|
|
|
isset($se_doc_data[SE_DATA]['exercise_ids']) && |
|
1003
|
|
|
is_array($se_doc_data[SE_DATA]['exercise_ids']) |
|
1004
|
|
|
) { |
|
1005
|
|
|
foreach ($se_doc_data[SE_DATA]['exercise_ids'] as $old_value) { |
|
1006
|
|
|
if (!in_array($old_value, $question_exercises)) { |
|
1007
|
|
|
$question_exercises[] = $old_value; |
|
1008
|
|
|
} |
|
1009
|
|
|
} |
|
1010
|
|
|
} |
|
1011
|
|
|
} |
|
1012
|
|
|
} |
|
1013
|
|
|
} |
|
1014
|
|
|
if ($rmQs) { |
|
1015
|
|
|
while (($key = array_search($exerciseId, $question_exercises)) !== FALSE) { |
|
1016
|
|
|
unset($question_exercises[$key]); |
|
1017
|
|
|
} |
|
1018
|
|
|
} |
|
1019
|
|
|
|
|
1020
|
|
|
// build the chunk to index |
|
1021
|
|
|
$ic_slide = new IndexableChunk(); |
|
1022
|
|
|
$ic_slide->addValue("title", $this->question); |
|
1023
|
|
|
$ic_slide->addCourseId($course_id); |
|
1024
|
|
|
$ic_slide->addToolId(TOOL_QUIZ); |
|
1025
|
|
|
$xapian_data = array( |
|
1026
|
|
|
SE_COURSE_ID => $course_id, |
|
1027
|
|
|
SE_TOOL_ID => TOOL_QUIZ, |
|
1028
|
|
|
SE_DATA => array( |
|
1029
|
|
|
'type' => SE_DOCTYPE_EXERCISE_QUESTION, |
|
1030
|
|
|
'exercise_ids' => $question_exercises, |
|
1031
|
|
|
'question_id' => (int)$this->id |
|
1032
|
|
|
), |
|
1033
|
|
|
SE_USER => (int)api_get_user_id(), |
|
1034
|
|
|
); |
|
1035
|
|
|
$ic_slide->xapian_data = serialize($xapian_data); |
|
1036
|
|
|
$ic_slide->addValue("content", $this->description); |
|
1037
|
|
|
|
|
1038
|
|
|
//TODO: index answers, see also form validation on question_admin.inc.php |
|
1039
|
|
|
|
|
1040
|
|
|
$di->remove_document((int)$se_ref['search_did']); |
|
1041
|
|
|
$di->addChunk($ic_slide); |
|
1042
|
|
|
|
|
1043
|
|
|
//index and return search engine document id |
|
1044
|
|
|
if (!empty($question_exercises)) { // if empty there is nothing to index |
|
1045
|
|
|
$did = $di->index(); |
|
1046
|
|
|
unset($di); |
|
1047
|
|
|
} |
|
1048
|
|
|
if ($did || $rmQs) { |
|
1049
|
|
|
// save it to db |
|
1050
|
|
View Code Duplication |
if ($addQs || $rmQs) { |
|
1051
|
|
|
$sql = "DELETE FROM %s |
|
1052
|
|
|
WHERE course_code = '%s' AND tool_id = '%s' AND ref_id_second_level = '%s'"; |
|
1053
|
|
|
$sql = sprintf($sql, $tbl_se_ref, $course_id, TOOL_QUIZ, $this->id); |
|
1054
|
|
|
} else { |
|
1055
|
|
|
$sql = "DELETE FROM %S |
|
1056
|
|
|
WHERE |
|
1057
|
|
|
course_code = '%s' |
|
1058
|
|
|
AND tool_id = '%s' |
|
1059
|
|
|
AND tool_id = '%s' |
|
1060
|
|
|
AND ref_id_high_level = '%s' |
|
1061
|
|
|
AND ref_id_second_level = '%s'"; |
|
1062
|
|
|
$sql = sprintf($sql, $tbl_se_ref, $course_id, TOOL_QUIZ, $exerciseId, $this->id); |
|
1063
|
|
|
} |
|
1064
|
|
|
Database::query($sql); |
|
1065
|
|
|
if ($rmQs) { |
|
1066
|
|
|
if (!empty($question_exercises)) { |
|
1067
|
|
|
$sql = "INSERT INTO %s ( |
|
1068
|
|
|
id, course_code, tool_id, ref_id_high_level, ref_id_second_level, search_did |
|
1069
|
|
|
) |
|
1070
|
|
|
VALUES ( |
|
1071
|
|
|
NULL, '%s', '%s', %s, %s, %s |
|
1072
|
|
|
)"; |
|
1073
|
|
|
$sql = sprintf( |
|
1074
|
|
|
$sql, |
|
1075
|
|
|
$tbl_se_ref, |
|
1076
|
|
|
$course_id, |
|
1077
|
|
|
TOOL_QUIZ, |
|
1078
|
|
|
array_shift($question_exercises), |
|
1079
|
|
|
$this->id, |
|
1080
|
|
|
$did |
|
1081
|
|
|
); |
|
1082
|
|
|
Database::query($sql); |
|
1083
|
|
|
} |
|
1084
|
|
|
} else { |
|
1085
|
|
|
$sql = "INSERT INTO %s ( |
|
1086
|
|
|
id, course_code, tool_id, ref_id_high_level, ref_id_second_level, search_did |
|
1087
|
|
|
) |
|
1088
|
|
|
VALUES ( |
|
1089
|
|
|
NULL , '%s', '%s', %s, %s, %s |
|
1090
|
|
|
)"; |
|
1091
|
|
|
$sql = sprintf($sql, $tbl_se_ref, $course_id, TOOL_QUIZ, $exerciseId, $this->id, $did); |
|
1092
|
|
|
Database::query($sql); |
|
1093
|
|
|
} |
|
1094
|
|
|
} |
|
1095
|
|
|
|
|
1096
|
|
|
} |
|
1097
|
|
|
} |
|
1098
|
|
|
} |
|
1099
|
|
|
|
|
1100
|
|
|
/** |
|
1101
|
|
|
* adds an exercise into the exercise list |
|
1102
|
|
|
* |
|
1103
|
|
|
* @author Olivier Brouckaert |
|
1104
|
|
|
* @param integer $exerciseId - exercise ID |
|
1105
|
|
|
* @param boolean $fromSave - from $this->save() or not |
|
1106
|
|
|
*/ |
|
1107
|
|
|
public function addToList($exerciseId, $fromSave = false) |
|
1108
|
|
|
{ |
|
1109
|
|
|
$exerciseRelQuestionTable = Database::get_course_table(TABLE_QUIZ_TEST_QUESTION); |
|
1110
|
|
|
$id = $this->id; |
|
1111
|
|
|
// checks if the exercise ID is not in the list |
|
1112
|
|
|
if (!in_array($exerciseId, $this->exerciseList)) { |
|
1113
|
|
|
$this->exerciseList[] = $exerciseId; |
|
1114
|
|
|
$new_exercise = new Exercise(); |
|
1115
|
|
|
$new_exercise->read($exerciseId); |
|
1116
|
|
|
$count = $new_exercise->selectNbrQuestions(); |
|
1117
|
|
|
$count++; |
|
1118
|
|
|
$sql = "INSERT INTO $exerciseRelQuestionTable (c_id, question_id, exercice_id, question_order) |
|
1119
|
|
|
VALUES ({$this->course['real_id']}, " . intval($id) . ", " . intval($exerciseId) . ", '$count')"; |
|
1120
|
|
|
Database::query($sql); |
|
1121
|
|
|
|
|
1122
|
|
|
// we do not want to reindex if we had just saved adnd indexed the question |
|
1123
|
|
|
if (!$fromSave) { |
|
1124
|
|
|
$this->search_engine_edit($exerciseId, TRUE); |
|
1125
|
|
|
} |
|
1126
|
|
|
} |
|
1127
|
|
|
} |
|
1128
|
|
|
|
|
1129
|
|
|
/** |
|
1130
|
|
|
* removes an exercise from the exercise list |
|
1131
|
|
|
* |
|
1132
|
|
|
* @author Olivier Brouckaert |
|
1133
|
|
|
* @param integer $exerciseId - exercise ID |
|
1134
|
|
|
* @return boolean - true if removed, otherwise false |
|
1135
|
|
|
*/ |
|
1136
|
|
|
function removeFromList($exerciseId) |
|
1137
|
|
|
{ |
|
1138
|
|
|
$TBL_EXERCISE_QUESTION = Database::get_course_table(TABLE_QUIZ_TEST_QUESTION); |
|
1139
|
|
|
|
|
1140
|
|
|
$id = $this->id; |
|
1141
|
|
|
|
|
1142
|
|
|
// searches the position of the exercise ID in the list |
|
1143
|
|
|
$pos = array_search($exerciseId, $this->exerciseList); |
|
1144
|
|
|
|
|
1145
|
|
|
$course_id = api_get_course_int_id(); |
|
1146
|
|
|
|
|
1147
|
|
|
// exercise not found |
|
1148
|
|
|
if($pos === false) { |
|
1149
|
|
|
return false; |
|
1150
|
|
|
} else { |
|
1151
|
|
|
// deletes the position in the array containing the wanted exercise ID |
|
1152
|
|
|
unset($this->exerciseList[$pos]); |
|
1153
|
|
|
//update order of other elements |
|
1154
|
|
|
$sql = "SELECT question_order |
|
1155
|
|
|
FROM $TBL_EXERCISE_QUESTION |
|
1156
|
|
|
WHERE |
|
1157
|
|
|
c_id = $course_id |
|
1158
|
|
|
AND question_id = " . intval($id) . " |
|
1159
|
|
|
AND exercice_id = " . intval($exerciseId); |
|
1160
|
|
|
$res = Database::query($sql); |
|
1161
|
|
View Code Duplication |
if (Database::num_rows($res)>0) { |
|
1162
|
|
|
$row = Database::fetch_array($res); |
|
1163
|
|
|
if (!empty($row['question_order'])) { |
|
1164
|
|
|
$sql = "UPDATE $TBL_EXERCISE_QUESTION |
|
1165
|
|
|
SET question_order = question_order-1 |
|
1166
|
|
|
WHERE |
|
1167
|
|
|
c_id = $course_id |
|
1168
|
|
|
AND exercice_id = " . intval($exerciseId) . " |
|
1169
|
|
|
AND question_order > " . $row['question_order']; |
|
1170
|
|
|
Database::query($sql); |
|
1171
|
|
|
} |
|
1172
|
|
|
} |
|
1173
|
|
|
|
|
1174
|
|
|
$sql = "DELETE FROM $TBL_EXERCISE_QUESTION |
|
1175
|
|
|
WHERE |
|
1176
|
|
|
c_id = $course_id |
|
1177
|
|
|
AND question_id = " . intval($id) . " |
|
1178
|
|
|
AND exercice_id = " . intval($exerciseId); |
|
1179
|
|
|
Database::query($sql); |
|
1180
|
|
|
|
|
1181
|
|
|
return true; |
|
1182
|
|
|
} |
|
1183
|
|
|
} |
|
1184
|
|
|
|
|
1185
|
|
|
/** |
|
1186
|
|
|
* Deletes a question from the database |
|
1187
|
|
|
* the parameter tells if the question is removed from all exercises (value = 0), |
|
1188
|
|
|
* or just from one exercise (value = exercise ID) |
|
1189
|
|
|
* |
|
1190
|
|
|
* @author Olivier Brouckaert |
|
1191
|
|
|
* @param integer $deleteFromEx - exercise ID if the question is only removed from one exercise |
|
1192
|
|
|
*/ |
|
1193
|
|
|
public function delete($deleteFromEx = 0) |
|
1194
|
|
|
{ |
|
1195
|
|
|
$course_id = api_get_course_int_id(); |
|
1196
|
|
|
|
|
1197
|
|
|
$TBL_EXERCISE_QUESTION = Database::get_course_table(TABLE_QUIZ_TEST_QUESTION); |
|
1198
|
|
|
$TBL_QUESTIONS = Database::get_course_table(TABLE_QUIZ_QUESTION); |
|
1199
|
|
|
$TBL_REPONSES = Database::get_course_table(TABLE_QUIZ_ANSWER); |
|
1200
|
|
|
$TBL_QUIZ_QUESTION_REL_CATEGORY = Database::get_course_table(TABLE_QUIZ_QUESTION_REL_CATEGORY); |
|
1201
|
|
|
|
|
1202
|
|
|
$id = intval($this->id); |
|
1203
|
|
|
|
|
1204
|
|
|
// if the question must be removed from all exercises |
|
1205
|
|
|
if (!$deleteFromEx) { |
|
1206
|
|
|
//update the question_order of each question to avoid inconsistencies |
|
1207
|
|
|
$sql = "SELECT exercice_id, question_order FROM $TBL_EXERCISE_QUESTION |
|
1208
|
|
|
WHERE c_id = $course_id AND question_id = ".intval($id).""; |
|
1209
|
|
|
|
|
1210
|
|
|
$res = Database::query($sql); |
|
1211
|
|
View Code Duplication |
if (Database::num_rows($res) > 0) { |
|
1212
|
|
|
while ($row = Database::fetch_array($res)) { |
|
1213
|
|
|
if (!empty($row['question_order'])) { |
|
1214
|
|
|
$sql = "UPDATE $TBL_EXERCISE_QUESTION |
|
1215
|
|
|
SET question_order = question_order-1 |
|
1216
|
|
|
WHERE |
|
1217
|
|
|
c_id= $course_id |
|
1218
|
|
|
AND exercice_id = " . intval($row['exercice_id']) . " |
|
1219
|
|
|
AND question_order > " . $row['question_order']; |
|
1220
|
|
|
Database::query($sql); |
|
1221
|
|
|
} |
|
1222
|
|
|
} |
|
1223
|
|
|
} |
|
1224
|
|
|
|
|
1225
|
|
|
$sql = "DELETE FROM $TBL_EXERCISE_QUESTION |
|
1226
|
|
|
WHERE c_id = $course_id AND question_id = " . $id; |
|
1227
|
|
|
Database::query($sql); |
|
1228
|
|
|
|
|
1229
|
|
|
$sql = "DELETE FROM $TBL_QUESTIONS |
|
1230
|
|
|
WHERE c_id = $course_id AND id = " . $id; |
|
1231
|
|
|
Database::query($sql); |
|
1232
|
|
|
|
|
1233
|
|
|
$sql = "DELETE FROM $TBL_REPONSES |
|
1234
|
|
|
WHERE c_id = $course_id AND question_id = " . $id; |
|
1235
|
|
|
Database::query($sql); |
|
1236
|
|
|
|
|
1237
|
|
|
// remove the category of this question in the question_rel_category table |
|
1238
|
|
|
$sql = "DELETE FROM $TBL_QUIZ_QUESTION_REL_CATEGORY |
|
1239
|
|
|
WHERE |
|
1240
|
|
|
c_id = $course_id AND |
|
1241
|
|
|
question_id = " . $id; |
|
1242
|
|
|
Database::query($sql); |
|
1243
|
|
|
|
|
1244
|
|
|
api_item_property_update( |
|
1245
|
|
|
$this->course, |
|
1246
|
|
|
TOOL_QUIZ, |
|
1247
|
|
|
$id, |
|
1248
|
|
|
'QuizQuestionDeleted', |
|
1249
|
|
|
api_get_user_id() |
|
1250
|
|
|
); |
|
1251
|
|
|
$this->removePicture(); |
|
1252
|
|
|
|
|
1253
|
|
|
} else { |
|
1254
|
|
|
// just removes the exercise from the list |
|
1255
|
|
|
$this->removeFromList($deleteFromEx); |
|
1256
|
|
|
if (api_get_setting('search_enabled') == 'true' && extension_loaded('xapian')) { |
|
1257
|
|
|
// disassociate question with this exercise |
|
1258
|
|
|
$this->search_engine_edit($deleteFromEx, FALSE, TRUE); |
|
1259
|
|
|
} |
|
1260
|
|
|
|
|
1261
|
|
|
api_item_property_update( |
|
1262
|
|
|
$this->course, |
|
1263
|
|
|
TOOL_QUIZ, |
|
1264
|
|
|
$id, |
|
1265
|
|
|
'QuizQuestionDeleted', |
|
1266
|
|
|
api_get_user_id() |
|
1267
|
|
|
); |
|
1268
|
|
|
} |
|
1269
|
|
|
} |
|
1270
|
|
|
|
|
1271
|
|
|
/** |
|
1272
|
|
|
* Duplicates the question |
|
1273
|
|
|
* |
|
1274
|
|
|
* @author Olivier Brouckaert |
|
1275
|
|
|
* @param array $course_info Course info of the destination course |
|
1276
|
|
|
* @return int ID of the new question |
|
1277
|
|
|
*/ |
|
1278
|
|
|
public function duplicate($course_info = []) |
|
1279
|
|
|
{ |
|
1280
|
|
|
if (empty($course_info)) { |
|
1281
|
|
|
$course_info = $this->course; |
|
1282
|
|
|
} else { |
|
1283
|
|
|
$course_info = $course_info; |
|
|
|
|
|
|
1284
|
|
|
} |
|
1285
|
|
|
$TBL_QUESTIONS = Database::get_course_table(TABLE_QUIZ_QUESTION); |
|
1286
|
|
|
$TBL_QUESTION_OPTIONS = Database::get_course_table(TABLE_QUIZ_QUESTION_OPTION); |
|
1287
|
|
|
|
|
1288
|
|
|
$question = $this->question; |
|
1289
|
|
|
$description = $this->description; |
|
1290
|
|
|
$weighting = $this->weighting; |
|
1291
|
|
|
$position = $this->position; |
|
1292
|
|
|
$type = $this->type; |
|
1293
|
|
|
$level = intval($this->level); |
|
1294
|
|
|
$extra = $this->extra; |
|
1295
|
|
|
|
|
1296
|
|
|
//Using the same method used in the course copy to transform URLs |
|
1297
|
|
|
|
|
1298
|
|
|
if ($this->course['id'] != $course_info['id']) { |
|
1299
|
|
|
$description = DocumentManager::replace_urls_inside_content_html_from_copy_course( |
|
1300
|
|
|
$description, |
|
1301
|
|
|
$this->course['code'], |
|
1302
|
|
|
$course_info['id'] |
|
1303
|
|
|
); |
|
1304
|
|
|
$question = DocumentManager::replace_urls_inside_content_html_from_copy_course( |
|
1305
|
|
|
$question, |
|
1306
|
|
|
$this->course['code'], |
|
1307
|
|
|
$course_info['id'] |
|
1308
|
|
|
); |
|
1309
|
|
|
} |
|
1310
|
|
|
|
|
1311
|
|
|
$course_id = $course_info['real_id']; |
|
1312
|
|
|
|
|
1313
|
|
|
//Read the source options |
|
1314
|
|
|
$options = self::readQuestionOption($this->id, $this->course['real_id']); |
|
1315
|
|
|
|
|
1316
|
|
|
// Inserting in the new course db / or the same course db |
|
1317
|
|
|
$params = [ |
|
1318
|
|
|
'c_id' => $course_id, |
|
1319
|
|
|
'question' => $question, |
|
1320
|
|
|
'description' => $description, |
|
1321
|
|
|
'ponderation' => $weighting, |
|
1322
|
|
|
'position' => $position, |
|
1323
|
|
|
'type' => $type, |
|
1324
|
|
|
'level' => $level, |
|
1325
|
|
|
'extra' => $extra |
|
1326
|
|
|
]; |
|
1327
|
|
|
$new_question_id = Database::insert($TBL_QUESTIONS, $params); |
|
1328
|
|
|
|
|
1329
|
|
|
if ($new_question_id) { |
|
1330
|
|
|
|
|
1331
|
|
|
$sql = "UPDATE $TBL_QUESTIONS SET id = iid |
|
1332
|
|
|
WHERE iid = $new_question_id"; |
|
1333
|
|
|
Database::query($sql); |
|
1334
|
|
|
|
|
1335
|
|
|
if (!empty($options)) { |
|
1336
|
|
|
//Saving the quiz_options |
|
1337
|
|
|
foreach ($options as $item) { |
|
1338
|
|
|
$item['question_id'] = $new_question_id; |
|
1339
|
|
|
$item['c_id'] = $course_id; |
|
1340
|
|
|
unset($item['id']); |
|
1341
|
|
|
unset($item['iid']); |
|
1342
|
|
|
$id = Database::insert($TBL_QUESTION_OPTIONS, $item); |
|
1343
|
|
|
if ($id) { |
|
1344
|
|
|
$sql = "UPDATE $TBL_QUESTION_OPTIONS SET id = iid |
|
1345
|
|
|
WHERE iid = $id"; |
|
1346
|
|
|
Database::query($sql); |
|
1347
|
|
|
} |
|
1348
|
|
|
} |
|
1349
|
|
|
} |
|
1350
|
|
|
|
|
1351
|
|
|
// Duplicates the picture of the hotspot |
|
1352
|
|
|
$this->exportPicture($new_question_id, $course_info); |
|
1353
|
|
|
} |
|
1354
|
|
|
|
|
1355
|
|
|
return $new_question_id; |
|
1356
|
|
|
} |
|
1357
|
|
|
|
|
1358
|
|
|
/** |
|
1359
|
|
|
* @return string |
|
1360
|
|
|
*/ |
|
1361
|
|
|
public function get_question_type_name() |
|
1362
|
|
|
{ |
|
1363
|
|
|
$key = self::$questionTypes[$this->type]; |
|
1364
|
|
|
|
|
1365
|
|
|
return get_lang($key[1]); |
|
1366
|
|
|
} |
|
1367
|
|
|
|
|
1368
|
|
|
/** |
|
1369
|
|
|
* @param string $type |
|
1370
|
|
|
* @return null |
|
1371
|
|
|
*/ |
|
1372
|
|
|
public static function get_question_type($type) |
|
1373
|
|
|
{ |
|
1374
|
|
|
if ($type == ORAL_EXPRESSION && api_get_setting('enable_nanogong') != 'true') { |
|
1375
|
|
|
return null; |
|
1376
|
|
|
} |
|
1377
|
|
|
return self::$questionTypes[$type]; |
|
1378
|
|
|
} |
|
1379
|
|
|
|
|
1380
|
|
|
/** |
|
1381
|
|
|
* @return array |
|
1382
|
|
|
*/ |
|
1383
|
|
|
public static function get_question_type_list() |
|
1384
|
|
|
{ |
|
1385
|
|
|
if (api_get_setting('enable_nanogong') != 'true') { |
|
1386
|
|
|
self::$questionTypes[ORAL_EXPRESSION] = null; |
|
1387
|
|
|
unset(self::$questionTypes[ORAL_EXPRESSION]); |
|
1388
|
|
|
} |
|
1389
|
|
|
if (api_get_setting('enable_quiz_scenario') !== 'true') { |
|
1390
|
|
|
self::$questionTypes[HOT_SPOT_DELINEATION] = null; |
|
1391
|
|
|
unset(self::$questionTypes[HOT_SPOT_DELINEATION]); |
|
1392
|
|
|
} |
|
1393
|
|
|
return self::$questionTypes; |
|
1394
|
|
|
} |
|
1395
|
|
|
|
|
1396
|
|
|
/** |
|
1397
|
|
|
* Returns an instance of the class corresponding to the type |
|
1398
|
|
|
* @param integer $type the type of the question |
|
1399
|
|
|
* @return $this instance of a Question subclass (or of Questionc class by default) |
|
1400
|
|
|
*/ |
|
1401
|
|
|
public static function getInstance($type) |
|
1402
|
|
|
{ |
|
1403
|
|
|
if (!is_null($type)) { |
|
1404
|
|
|
list($file_name, $class_name) = self::get_question_type($type); |
|
|
|
|
|
|
1405
|
|
|
if (!empty($file_name)) { |
|
1406
|
|
|
include_once $file_name; |
|
1407
|
|
|
if (class_exists($class_name)) { |
|
1408
|
|
|
return new $class_name(); |
|
1409
|
|
|
} else { |
|
1410
|
|
|
echo 'Can\'t instanciate class ' . $class_name . ' of type ' . $type; |
|
1411
|
|
|
} |
|
1412
|
|
|
} |
|
1413
|
|
|
} |
|
1414
|
|
|
|
|
1415
|
|
|
return null; |
|
1416
|
|
|
} |
|
1417
|
|
|
|
|
1418
|
|
|
/** |
|
1419
|
|
|
* Creates the form to create / edit a question |
|
1420
|
|
|
* A subclass can redefine this function to add fields... |
|
1421
|
|
|
* @param FormValidator $form |
|
1422
|
|
|
*/ |
|
1423
|
|
|
public function createForm(&$form) |
|
1424
|
|
|
{ |
|
1425
|
|
|
echo '<style> |
|
1426
|
|
|
.media { display:none;} |
|
1427
|
|
|
</style>'; |
|
1428
|
|
|
echo '<script> |
|
1429
|
|
|
// hack to hide http://cksource.com/forums/viewtopic.php?f=6&t=8700 |
|
1430
|
|
|
function FCKeditor_OnComplete( editorInstance ) { |
|
1431
|
|
|
if (document.getElementById ( \'HiddenFCK\' + editorInstance.Name)) { |
|
1432
|
|
|
HideFCKEditorByInstanceName (editorInstance.Name); |
|
1433
|
|
|
} |
|
1434
|
|
|
} |
|
1435
|
|
|
|
|
1436
|
|
|
function HideFCKEditorByInstanceName ( editorInstanceName ) { |
|
1437
|
|
|
if (document.getElementById ( \'HiddenFCK\' + editorInstanceName ).className == "HideFCKEditor" ) { |
|
1438
|
|
|
document.getElementById ( \'HiddenFCK\' + editorInstanceName ).className = "media"; |
|
1439
|
|
|
} |
|
1440
|
|
|
} |
|
1441
|
|
|
</script>'; |
|
1442
|
|
|
|
|
1443
|
|
|
// question name |
|
1444
|
|
|
$form->addElement('text', 'questionName', get_lang('Question')); |
|
1445
|
|
|
$form->addRule('questionName', get_lang('GiveQuestion'), 'required'); |
|
1446
|
|
|
|
|
1447
|
|
|
// default content |
|
1448
|
|
|
$isContent = isset($_REQUEST['isContent']) ? intval($_REQUEST['isContent']) : null; |
|
1449
|
|
|
|
|
1450
|
|
|
// Question type |
|
1451
|
|
|
$answerType = isset($_REQUEST['answerType']) ? intval($_REQUEST['answerType']) : null; |
|
1452
|
|
|
$form->addElement('hidden','answerType', $answerType); |
|
1453
|
|
|
|
|
1454
|
|
|
// html editor |
|
1455
|
|
|
$editorConfig = array( |
|
1456
|
|
|
'ToolbarSet' => 'TestQuestionDescription', |
|
1457
|
|
|
'Height' => '150' |
|
1458
|
|
|
); |
|
1459
|
|
|
|
|
1460
|
|
|
if (!api_is_allowed_to_edit(null,true)) { |
|
1461
|
|
|
$editorConfig['UserStatus'] = 'student'; |
|
1462
|
|
|
} |
|
1463
|
|
|
|
|
1464
|
|
|
$form->addButtonAdvancedSettings('advanced_params'); |
|
1465
|
|
|
$form->addElement('html', '<div id="advanced_params_options" style="display:none">'); |
|
1466
|
|
|
|
|
1467
|
|
|
$form->addHtmlEditor('questionDescription', get_lang('QuestionDescription'), false, false, $editorConfig); |
|
1468
|
|
|
|
|
1469
|
|
|
// hidden values |
|
1470
|
|
|
$my_id = isset($_REQUEST['myid']) ? intval($_REQUEST['myid']) : null; |
|
1471
|
|
|
$form->addElement('hidden', 'myid', $my_id); |
|
1472
|
|
|
|
|
1473
|
|
|
if ($this->type != MEDIA_QUESTION) { |
|
1474
|
|
|
|
|
1475
|
|
|
// Advanced parameters |
|
1476
|
|
|
|
|
1477
|
|
|
$select_level = Question::get_default_levels(); |
|
1478
|
|
|
$form->addElement('select', 'questionLevel', get_lang('Difficulty'), $select_level); |
|
1479
|
|
|
|
|
1480
|
|
|
// Categories |
|
1481
|
|
|
$tabCat = TestCategory::getCategoriesIdAndName(); |
|
1482
|
|
|
$form->addElement('select', 'questionCategory', get_lang('Category'), $tabCat); |
|
1483
|
|
|
|
|
1484
|
|
|
switch ($this->type) { |
|
1485
|
|
|
case UNIQUE_ANSWER: |
|
1486
|
|
|
$form->addButton('convertAnswer', get_lang('ConvertToMultipleAnswer'), 'dot-circle-o', 'info'); |
|
1487
|
|
|
break; |
|
1488
|
|
|
case MULTIPLE_ANSWER: |
|
1489
|
|
|
$form->addButton('convertAnswer', get_lang('ConvertToUniqueAnswer'), 'check-square-o', 'info'); |
|
1490
|
|
|
break; |
|
1491
|
|
|
} |
|
1492
|
|
|
|
|
1493
|
|
|
//Medias |
|
1494
|
|
|
//$course_medias = Question::prepare_course_media_select(api_get_course_int_id()); |
|
1495
|
|
|
//$form->addElement('select', 'parent_id', get_lang('AttachToMedia'), $course_medias); |
|
1496
|
|
|
} |
|
1497
|
|
|
|
|
1498
|
|
|
$form->addElement('html', '</div>'); |
|
1499
|
|
|
|
|
1500
|
|
|
if (!isset($_GET['fromExercise'])) { |
|
1501
|
|
|
switch ($answerType) { |
|
1502
|
|
|
case 1: |
|
1503
|
|
|
$this->question = get_lang('DefaultUniqueQuestion'); |
|
1504
|
|
|
break; |
|
1505
|
|
|
case 2: |
|
1506
|
|
|
$this->question = get_lang('DefaultMultipleQuestion'); |
|
1507
|
|
|
break; |
|
1508
|
|
|
case 3: |
|
1509
|
|
|
$this->question = get_lang('DefaultFillBlankQuestion'); |
|
1510
|
|
|
break; |
|
1511
|
|
|
case 4: |
|
1512
|
|
|
$this->question = get_lang('DefaultMathingQuestion'); |
|
1513
|
|
|
break; |
|
1514
|
|
|
case 5: |
|
1515
|
|
|
$this->question = get_lang('DefaultOpenQuestion'); |
|
1516
|
|
|
break; |
|
1517
|
|
|
case 9: |
|
1518
|
|
|
$this->question = get_lang('DefaultMultipleQuestion'); |
|
1519
|
|
|
break; |
|
1520
|
|
|
} |
|
1521
|
|
|
} |
|
1522
|
|
|
|
|
1523
|
|
|
// default values |
|
1524
|
|
|
$defaults = array(); |
|
1525
|
|
|
$defaults['questionName'] = $this->question; |
|
1526
|
|
|
$defaults['questionDescription'] = $this->description; |
|
1527
|
|
|
$defaults['questionLevel'] = $this->level; |
|
1528
|
|
|
$defaults['questionCategory'] = $this->category; |
|
1529
|
|
|
|
|
1530
|
|
|
// Came from he question pool |
|
1531
|
|
|
if (isset($_GET['fromExercise'])) { |
|
1532
|
|
|
$form->setDefaults($defaults); |
|
1533
|
|
|
} |
|
1534
|
|
|
|
|
1535
|
|
|
if (!empty($_REQUEST['myid'])) { |
|
1536
|
|
|
$form->setDefaults($defaults); |
|
1537
|
|
|
} else { |
|
1538
|
|
|
if ($isContent == 1) { |
|
1539
|
|
|
$form->setDefaults($defaults); |
|
1540
|
|
|
} |
|
1541
|
|
|
} |
|
1542
|
|
|
} |
|
1543
|
|
|
|
|
1544
|
|
|
/** |
|
1545
|
|
|
* function which process the creation of questions |
|
1546
|
|
|
* @param FormValidator $form |
|
1547
|
|
|
* @param Exercise $objExercise |
|
1548
|
|
|
*/ |
|
1549
|
|
|
public function processCreation($form, $objExercise = null) |
|
1550
|
|
|
{ |
|
1551
|
|
|
$this->updateTitle($form->getSubmitValue('questionName')); |
|
1552
|
|
|
$this->updateDescription($form->getSubmitValue('questionDescription')); |
|
1553
|
|
|
$this->updateLevel($form->getSubmitValue('questionLevel')); |
|
1554
|
|
|
$this->updateCategory($form->getSubmitValue('questionCategory')); |
|
1555
|
|
|
|
|
1556
|
|
|
//Save normal question if NOT media |
|
1557
|
|
|
if ($this->type != MEDIA_QUESTION) { |
|
1558
|
|
|
$this->save($objExercise->id); |
|
1559
|
|
|
|
|
1560
|
|
|
// modify the exercise |
|
1561
|
|
|
$objExercise->addToList($this->id); |
|
1562
|
|
|
$objExercise->update_question_positions(); |
|
1563
|
|
|
} |
|
1564
|
|
|
} |
|
1565
|
|
|
|
|
1566
|
|
|
/** |
|
1567
|
|
|
* abstract function which creates the form to create / edit the answers of the question |
|
1568
|
|
|
* @param the FormValidator instance |
|
1569
|
|
|
*/ |
|
1570
|
|
|
abstract function createAnswersForm($form); |
|
1571
|
|
|
|
|
1572
|
|
|
/** |
|
1573
|
|
|
* abstract function which process the creation of answers |
|
1574
|
|
|
* @param the FormValidator instance |
|
1575
|
|
|
*/ |
|
1576
|
|
|
abstract function processAnswersCreation($form); |
|
1577
|
|
|
|
|
1578
|
|
|
/** |
|
1579
|
|
|
* Displays the menu of question types |
|
1580
|
|
|
* |
|
1581
|
|
|
* @param Exercise $objExercise |
|
1582
|
|
|
*/ |
|
1583
|
|
|
public static function display_type_menu($objExercise) |
|
1584
|
|
|
{ |
|
1585
|
|
|
$feedback_type = $objExercise->feedback_type; |
|
1586
|
|
|
$exerciseId = $objExercise->id; |
|
1587
|
|
|
|
|
1588
|
|
|
// 1. by default we show all the question types |
|
1589
|
|
|
$question_type_custom_list = self::get_question_type_list(); |
|
1590
|
|
|
|
|
1591
|
|
|
if (!isset($feedback_type)) { |
|
1592
|
|
|
$feedback_type = 0; |
|
1593
|
|
|
} |
|
1594
|
|
|
|
|
1595
|
|
|
if ($feedback_type == 1) { |
|
1596
|
|
|
//2. but if it is a feedback DIRECT we only show the UNIQUE_ANSWER type that is currently available |
|
1597
|
|
|
$question_type_custom_list = array ( |
|
1598
|
|
|
UNIQUE_ANSWER => self::$questionTypes[UNIQUE_ANSWER], |
|
1599
|
|
|
HOT_SPOT_DELINEATION => self::$questionTypes[HOT_SPOT_DELINEATION] |
|
1600
|
|
|
); |
|
1601
|
|
|
} else { |
|
1602
|
|
|
unset($question_type_custom_list[HOT_SPOT_DELINEATION]); |
|
1603
|
|
|
} |
|
1604
|
|
|
|
|
1605
|
|
|
echo '<div class="well">'; |
|
1606
|
|
|
echo '<ul class="question_menu">'; |
|
1607
|
|
|
|
|
1608
|
|
|
foreach ($question_type_custom_list as $i => $a_type) { |
|
1609
|
|
|
// include the class of the type |
|
1610
|
|
|
require_once $a_type[0]; |
|
1611
|
|
|
// get the picture of the type and the langvar which describes it |
|
1612
|
|
|
$img = $explanation = ''; |
|
1613
|
|
|
eval('$img = ' . $a_type[1] . '::$typePicture;'); |
|
|
|
|
|
|
1614
|
|
|
eval('$explanation = get_lang(' . $a_type[1] . '::$explanationLangVar);'); |
|
|
|
|
|
|
1615
|
|
|
echo '<li>'; |
|
1616
|
|
|
echo '<div class="icon-image">'; |
|
1617
|
|
|
if ($objExercise->exercise_was_added_in_lp == true) { |
|
1618
|
|
|
$img = pathinfo($img); |
|
1619
|
|
|
$img = $img['filename'] . '_na.' . $img['extension']; |
|
1620
|
|
|
echo Display::return_icon($img, $explanation, null, ICON_SIZE_BIG); |
|
1621
|
|
|
} else { |
|
1622
|
|
|
echo '<a href="admin.php?' . api_get_cidreq() . '&newQuestion=yes&answerType=' . $i . '">' . |
|
1623
|
|
|
Display::return_icon($img, $explanation, null, ICON_SIZE_BIG) . '</a>'; |
|
1624
|
|
|
} |
|
1625
|
|
|
echo '</div>'; |
|
1626
|
|
|
echo '</li>'; |
|
1627
|
|
|
} |
|
1628
|
|
|
|
|
1629
|
|
|
echo '<li>'; |
|
1630
|
|
|
echo '<div class="icon_image_content">'; |
|
1631
|
|
|
if ($objExercise->exercise_was_added_in_lp == true) { |
|
1632
|
|
|
echo Display::return_icon('database_na.png', get_lang('GetExistingQuestion'), null, ICON_SIZE_BIG); |
|
1633
|
|
|
} else { |
|
1634
|
|
|
if ($feedback_type == 1) { |
|
1635
|
|
|
echo $url = "<a href=\"question_pool.php?" . api_get_cidreq() . "&type=1&fromExercise=$exerciseId\">"; |
|
1636
|
|
|
} else { |
|
1637
|
|
|
echo $url = '<a href="question_pool.php?' . api_get_cidreq() . '&fromExercise=' . $exerciseId . '">'; |
|
1638
|
|
|
} |
|
1639
|
|
|
echo Display::return_icon('database.png', get_lang('GetExistingQuestion'), null, ICON_SIZE_BIG); |
|
1640
|
|
|
} |
|
1641
|
|
|
echo '</a>'; |
|
1642
|
|
|
echo '</div></li>'; |
|
1643
|
|
|
echo '</ul>'; |
|
1644
|
|
|
echo '</div>'; |
|
1645
|
|
|
} |
|
1646
|
|
|
|
|
1647
|
|
|
/** |
|
1648
|
|
|
* @param int $question_id |
|
1649
|
|
|
* @param string $name |
|
1650
|
|
|
* @param int $course_id |
|
1651
|
|
|
* @param int $position |
|
1652
|
|
|
* @return bool|int |
|
1653
|
|
|
*/ |
|
1654
|
|
|
static function saveQuestionOption($question_id, $name, $course_id, $position = 0) |
|
1655
|
|
|
{ |
|
1656
|
|
|
$TBL_EXERCISE_QUESTION_OPTION = Database::get_course_table(TABLE_QUIZ_QUESTION_OPTION); |
|
1657
|
|
|
$params['question_id'] = intval($question_id); |
|
1658
|
|
|
$params['name'] = $name; |
|
1659
|
|
|
$params['position'] = $position; |
|
1660
|
|
|
$params['c_id'] = $course_id; |
|
1661
|
|
|
$result = self::readQuestionOption($question_id, $course_id); |
|
1662
|
|
|
$last_id = Database::insert($TBL_EXERCISE_QUESTION_OPTION, $params); |
|
1663
|
|
|
if ($last_id) { |
|
1664
|
|
|
$sql = "UPDATE $TBL_EXERCISE_QUESTION_OPTION SET id = iid WHERE iid = $last_id"; |
|
1665
|
|
|
Database::query($sql); |
|
1666
|
|
|
} |
|
1667
|
|
|
|
|
1668
|
|
|
return $last_id; |
|
1669
|
|
|
} |
|
1670
|
|
|
|
|
1671
|
|
|
/** |
|
1672
|
|
|
* @param int $question_id |
|
1673
|
|
|
* @param int $course_id |
|
1674
|
|
|
*/ |
|
1675
|
|
|
static function deleteAllQuestionOptions($question_id, $course_id) |
|
1676
|
|
|
{ |
|
1677
|
|
|
$TBL_EXERCISE_QUESTION_OPTION = Database::get_course_table(TABLE_QUIZ_QUESTION_OPTION); |
|
1678
|
|
|
Database::delete( |
|
1679
|
|
|
$TBL_EXERCISE_QUESTION_OPTION, |
|
1680
|
|
|
array( |
|
1681
|
|
|
'c_id = ? AND question_id = ?' => array( |
|
1682
|
|
|
$course_id, |
|
1683
|
|
|
$question_id |
|
1684
|
|
|
) |
|
1685
|
|
|
) |
|
1686
|
|
|
); |
|
1687
|
|
|
} |
|
1688
|
|
|
|
|
1689
|
|
|
/** |
|
1690
|
|
|
* @param int $id |
|
1691
|
|
|
* @param array $params |
|
1692
|
|
|
* @param int $course_id |
|
1693
|
|
|
* @return bool|int |
|
1694
|
|
|
*/ |
|
1695
|
|
|
static function updateQuestionOption($id, $params, $course_id) |
|
1696
|
|
|
{ |
|
1697
|
|
|
$TBL_EXERCISE_QUESTION_OPTION = Database::get_course_table( |
|
1698
|
|
|
TABLE_QUIZ_QUESTION_OPTION |
|
1699
|
|
|
); |
|
1700
|
|
|
$result = Database::update( |
|
1701
|
|
|
$TBL_EXERCISE_QUESTION_OPTION, |
|
1702
|
|
|
$params, |
|
1703
|
|
|
array('c_id = ? AND id = ?' => array($course_id, $id)) |
|
1704
|
|
|
); |
|
1705
|
|
|
return $result; |
|
1706
|
|
|
} |
|
1707
|
|
|
|
|
1708
|
|
|
/** |
|
1709
|
|
|
* @param int $question_id |
|
1710
|
|
|
* @param int $course_id |
|
1711
|
|
|
* @return array |
|
1712
|
|
|
*/ |
|
1713
|
|
|
static function readQuestionOption($question_id, $course_id) |
|
1714
|
|
|
{ |
|
1715
|
|
|
$TBL_EXERCISE_QUESTION_OPTION = Database::get_course_table( |
|
1716
|
|
|
TABLE_QUIZ_QUESTION_OPTION |
|
1717
|
|
|
); |
|
1718
|
|
|
$result = Database::select( |
|
1719
|
|
|
'*', |
|
1720
|
|
|
$TBL_EXERCISE_QUESTION_OPTION, |
|
1721
|
|
|
array( |
|
1722
|
|
|
'where' => array( |
|
1723
|
|
|
'c_id = ? AND question_id = ?' => array( |
|
1724
|
|
|
$course_id, |
|
1725
|
|
|
$question_id |
|
1726
|
|
|
) |
|
1727
|
|
|
), |
|
1728
|
|
|
'order' => 'id ASC' |
|
1729
|
|
|
) |
|
1730
|
|
|
); |
|
1731
|
|
|
|
|
1732
|
|
|
return $result; |
|
1733
|
|
|
} |
|
1734
|
|
|
|
|
1735
|
|
|
/** |
|
1736
|
|
|
* Shows question title an description |
|
1737
|
|
|
* |
|
1738
|
|
|
* @param string $feedback_type |
|
1739
|
|
|
* @param int $counter |
|
1740
|
|
|
* @param float $score |
|
1741
|
|
|
*/ |
|
1742
|
|
|
function return_header($feedback_type = null, $counter = null, $score = null) |
|
1743
|
|
|
{ |
|
1744
|
|
|
$counter_label = ''; |
|
1745
|
|
|
if (!empty($counter)) { |
|
1746
|
|
|
$counter_label = intval($counter); |
|
1747
|
|
|
} |
|
1748
|
|
|
$score_label = get_lang('Wrong'); |
|
1749
|
|
|
$class = 'error'; |
|
1750
|
|
|
if ($score['pass'] == true) { |
|
1751
|
|
|
$score_label = get_lang('Correct'); |
|
1752
|
|
|
$class = 'success'; |
|
1753
|
|
|
} |
|
1754
|
|
|
|
|
1755
|
|
|
if ($this->type == FREE_ANSWER || $this->type == ORAL_EXPRESSION) { |
|
1756
|
|
|
$score['revised'] = isset($score['revised']) ? $score['revised'] : false; |
|
1757
|
|
|
if ($score['revised'] == true) { |
|
1758
|
|
|
$score_label = get_lang('Revised'); |
|
1759
|
|
|
$class = ''; |
|
1760
|
|
|
} else { |
|
1761
|
|
|
$score_label = get_lang('NotRevised'); |
|
1762
|
|
|
$class = 'error'; |
|
1763
|
|
|
} |
|
1764
|
|
|
} |
|
1765
|
|
|
$question_title = $this->question; |
|
1766
|
|
|
|
|
1767
|
|
|
// display question category, if any |
|
1768
|
|
|
$header = TestCategory::returnCategoryAndTitle($this->id); |
|
1769
|
|
|
$show_media = null; |
|
1770
|
|
|
if ($show_media) { |
|
1771
|
|
|
$header .= $this->show_media_content(); |
|
1772
|
|
|
} |
|
1773
|
|
|
|
|
1774
|
|
|
$header .= Display::page_subheader2($counter_label . ". " . $question_title); |
|
1775
|
|
|
$header .= Display::div( |
|
1776
|
|
|
"<div class=\"rib rib-$class\"><h3>$score_label</h3></div> <h4>{$score['result']}</h4>", |
|
1777
|
|
|
array('class' => 'ribbon') |
|
1778
|
|
|
); |
|
1779
|
|
|
$header .= Display::div($this->description, array('id' => 'question_description')); |
|
1780
|
|
|
|
|
1781
|
|
|
return $header; |
|
1782
|
|
|
} |
|
1783
|
|
|
|
|
1784
|
|
|
/** |
|
1785
|
|
|
* Create a question from a set of parameters |
|
1786
|
|
|
* @param int Quiz ID |
|
1787
|
|
|
* @param string Question name |
|
1788
|
|
|
* @param int Maximum result for the question |
|
1789
|
|
|
* @param int Type of question (see constants at beginning of question.class.php) |
|
1790
|
|
|
* @param int Question level/category |
|
1791
|
|
|
*/ |
|
1792
|
|
|
public function create_question( |
|
1793
|
|
|
$quiz_id, |
|
1794
|
|
|
$question_name, |
|
1795
|
|
|
$question_description = "" , |
|
1796
|
|
|
$max_score = 0, |
|
1797
|
|
|
$type = 1, |
|
1798
|
|
|
$level = 1 |
|
1799
|
|
|
) { |
|
1800
|
|
|
$course_id = api_get_course_int_id(); |
|
1801
|
|
|
|
|
1802
|
|
|
$tbl_quiz_question = Database::get_course_table(TABLE_QUIZ_QUESTION); |
|
1803
|
|
|
$tbl_quiz_rel_question = Database::get_course_table(TABLE_QUIZ_TEST_QUESTION); |
|
1804
|
|
|
|
|
1805
|
|
|
$quiz_id = intval($quiz_id); |
|
1806
|
|
|
$max_score = (float) $max_score; |
|
1807
|
|
|
$type = intval($type); |
|
1808
|
|
|
$level = intval($level); |
|
1809
|
|
|
|
|
1810
|
|
|
// Get the max position |
|
1811
|
|
|
$sql = "SELECT max(position) as max_position |
|
1812
|
|
|
FROM $tbl_quiz_question q INNER JOIN $tbl_quiz_rel_question r |
|
1813
|
|
|
ON |
|
1814
|
|
|
q.id = r.question_id AND |
|
1815
|
|
|
exercice_id = $quiz_id AND |
|
1816
|
|
|
q.c_id = $course_id AND |
|
1817
|
|
|
r.c_id = $course_id"; |
|
1818
|
|
|
$rs_max = Database::query($sql); |
|
1819
|
|
|
$row_max = Database::fetch_object($rs_max); |
|
1820
|
|
|
$max_position = $row_max->max_position + 1; |
|
1821
|
|
|
|
|
1822
|
|
|
$params = [ |
|
1823
|
|
|
'c_id' => $course_id, |
|
1824
|
|
|
'question' => $question_name, |
|
1825
|
|
|
'description' => $question_description, |
|
1826
|
|
|
'ponderation' => $max_score, |
|
1827
|
|
|
'position' => $max_position, |
|
1828
|
|
|
'type' => $type, |
|
1829
|
|
|
'level' => $level, |
|
1830
|
|
|
]; |
|
1831
|
|
|
$question_id = Database::insert($tbl_quiz_question, $params); |
|
1832
|
|
|
|
|
1833
|
|
|
if ($question_id) { |
|
1834
|
|
|
|
|
1835
|
|
|
$sql = "UPDATE $tbl_quiz_question SET id = iid WHERE iid = $question_id"; |
|
1836
|
|
|
Database::query($sql); |
|
1837
|
|
|
|
|
1838
|
|
|
// Get the max question_order |
|
1839
|
|
|
$sql = "SELECT max(question_order) as max_order |
|
1840
|
|
|
FROM $tbl_quiz_rel_question |
|
1841
|
|
|
WHERE c_id = $course_id AND exercice_id = $quiz_id "; |
|
1842
|
|
|
$rs_max_order = Database::query($sql); |
|
1843
|
|
|
$row_max_order = Database::fetch_object($rs_max_order); |
|
1844
|
|
|
$max_order = $row_max_order->max_order + 1; |
|
1845
|
|
|
// Attach questions to quiz |
|
1846
|
|
|
$sql = "INSERT INTO $tbl_quiz_rel_question (c_id, question_id, exercice_id, question_order) |
|
1847
|
|
|
VALUES($course_id, $question_id, $quiz_id, $max_order)"; |
|
1848
|
|
|
Database::query($sql); |
|
1849
|
|
|
} |
|
1850
|
|
|
|
|
1851
|
|
|
return $question_id; |
|
1852
|
|
|
} |
|
1853
|
|
|
|
|
1854
|
|
|
/** |
|
1855
|
|
|
* @return array the image filename of the question type |
|
1856
|
|
|
*/ |
|
1857
|
|
|
public function get_type_icon_html() |
|
1858
|
|
|
{ |
|
1859
|
|
|
$type = $this->selectType(); |
|
1860
|
|
|
$tabQuestionList = Question::get_question_type_list(); // [0]=file to include [1]=type name |
|
1861
|
|
|
|
|
1862
|
|
|
require_once $tabQuestionList[$type][0]; |
|
1863
|
|
|
|
|
1864
|
|
|
$img = $explanation = null; |
|
1865
|
|
|
eval('$img = ' . $tabQuestionList[$type][1] . '::$typePicture;'); |
|
|
|
|
|
|
1866
|
|
|
eval('$explanation = get_lang(' . $tabQuestionList[$type][1] . '::$explanationLangVar);'); |
|
|
|
|
|
|
1867
|
|
|
return array($img, $explanation); |
|
1868
|
|
|
} |
|
1869
|
|
|
|
|
1870
|
|
|
/** |
|
1871
|
|
|
* Get course medias |
|
1872
|
|
|
* @param int course id |
|
1873
|
|
|
*/ |
|
1874
|
|
|
static function get_course_medias( |
|
1875
|
|
|
$course_id, |
|
1876
|
|
|
$start = 0, |
|
1877
|
|
|
$limit = 100, |
|
1878
|
|
|
$sidx = "question", |
|
1879
|
|
|
$sord = "ASC", |
|
1880
|
|
|
$where_condition = array() |
|
1881
|
|
|
) { |
|
1882
|
|
|
$table_question = Database::get_course_table(TABLE_QUIZ_QUESTION); |
|
1883
|
|
|
$default_where = array('c_id = ? AND parent_id = 0 AND type = ?' => array($course_id, MEDIA_QUESTION)); |
|
1884
|
|
|
$result = Database::select( |
|
1885
|
|
|
'*', |
|
1886
|
|
|
$table_question, |
|
1887
|
|
|
array( |
|
1888
|
|
|
'limit' => " $start, $limit", |
|
1889
|
|
|
'where' => $default_where, |
|
1890
|
|
|
'order' => "$sidx $sord" |
|
1891
|
|
|
) |
|
1892
|
|
|
); |
|
1893
|
|
|
|
|
1894
|
|
|
return $result; |
|
1895
|
|
|
} |
|
1896
|
|
|
|
|
1897
|
|
|
/** |
|
1898
|
|
|
* Get count course medias |
|
1899
|
|
|
* @param int course id |
|
1900
|
|
|
* |
|
1901
|
|
|
* @return int |
|
1902
|
|
|
*/ |
|
1903
|
|
|
static function get_count_course_medias($course_id) |
|
1904
|
|
|
{ |
|
1905
|
|
|
$table_question = Database::get_course_table(TABLE_QUIZ_QUESTION); |
|
1906
|
|
|
$result = Database::select( |
|
1907
|
|
|
'count(*) as count', |
|
1908
|
|
|
$table_question, |
|
1909
|
|
|
array('where' => array('c_id = ? AND parent_id = 0 AND type = ?' => array($course_id, MEDIA_QUESTION))), |
|
1910
|
|
|
'first' |
|
1911
|
|
|
); |
|
1912
|
|
|
|
|
1913
|
|
|
if ($result && isset($result['count'])) { |
|
1914
|
|
|
return $result['count']; |
|
1915
|
|
|
} |
|
1916
|
|
|
return 0; |
|
1917
|
|
|
} |
|
1918
|
|
|
|
|
1919
|
|
|
/** |
|
1920
|
|
|
* @param int $course_id |
|
1921
|
|
|
* @return array |
|
1922
|
|
|
*/ |
|
1923
|
|
|
public static function prepare_course_media_select($course_id) |
|
1924
|
|
|
{ |
|
1925
|
|
|
$medias = self::get_course_medias($course_id); |
|
1926
|
|
|
$media_list = array(); |
|
1927
|
|
|
$media_list[0] = get_lang('NoMedia'); |
|
1928
|
|
|
|
|
1929
|
|
|
if (!empty($medias)) { |
|
1930
|
|
|
foreach($medias as $media) { |
|
1931
|
|
|
$media_list[$media['id']] = empty($media['question']) ? get_lang('Untitled') : $media['question']; |
|
1932
|
|
|
} |
|
1933
|
|
|
} |
|
1934
|
|
|
return $media_list; |
|
1935
|
|
|
} |
|
1936
|
|
|
|
|
1937
|
|
|
/** |
|
1938
|
|
|
* @return array |
|
1939
|
|
|
*/ |
|
1940
|
|
|
public static function get_default_levels() |
|
1941
|
|
|
{ |
|
1942
|
|
|
$select_level = array( |
|
1943
|
|
|
1 => 1, |
|
1944
|
|
|
2 => 2, |
|
1945
|
|
|
3 => 3, |
|
1946
|
|
|
4 => 4, |
|
1947
|
|
|
5 => 5 |
|
1948
|
|
|
); |
|
1949
|
|
|
return $select_level; |
|
1950
|
|
|
} |
|
1951
|
|
|
|
|
1952
|
|
|
/** |
|
1953
|
|
|
* @return null|string |
|
1954
|
|
|
*/ |
|
1955
|
|
|
public function show_media_content() |
|
1956
|
|
|
{ |
|
1957
|
|
|
$html = null; |
|
1958
|
|
|
if ($this->parent_id != 0) { |
|
1959
|
|
|
$parent_question = Question::read($this->parent_id); |
|
1960
|
|
|
$html = $parent_question->show_media_content(); |
|
1961
|
|
|
} else { |
|
1962
|
|
|
$html .= Display::page_subheader($this->selectTitle()); |
|
1963
|
|
|
$html .= $this->selectDescription(); |
|
1964
|
|
|
} |
|
1965
|
|
|
return $html; |
|
1966
|
|
|
} |
|
1967
|
|
|
|
|
1968
|
|
|
/** |
|
1969
|
|
|
* Swap between unique and multiple type answers |
|
1970
|
|
|
* @return object |
|
1971
|
|
|
*/ |
|
1972
|
|
|
public function swapSimpleAnswerTypes() |
|
1973
|
|
|
{ |
|
1974
|
|
|
$oppositeAnswers = array( |
|
1975
|
|
|
UNIQUE_ANSWER => MULTIPLE_ANSWER, |
|
1976
|
|
|
MULTIPLE_ANSWER => UNIQUE_ANSWER |
|
1977
|
|
|
); |
|
1978
|
|
|
$this->type = $oppositeAnswers[$this->type]; |
|
1979
|
|
|
Database::update( |
|
1980
|
|
|
Database::get_course_table(TABLE_QUIZ_QUESTION), |
|
1981
|
|
|
array('type' => $this->type), |
|
1982
|
|
|
array('c_id = ? AND id = ?' => array($this->course['real_id'], $this->id)) |
|
1983
|
|
|
); |
|
1984
|
|
|
$answerClasses = array( |
|
1985
|
|
|
UNIQUE_ANSWER => 'UniqueAnswer', |
|
1986
|
|
|
MULTIPLE_ANSWER => 'MultipleAnswer' |
|
1987
|
|
|
); |
|
1988
|
|
|
$swappedAnswer = new $answerClasses[$this->type]; |
|
1989
|
|
|
foreach ($this as $key => $value) { |
|
|
|
|
|
|
1990
|
|
|
$swappedAnswer->$key = $value; |
|
1991
|
|
|
} |
|
1992
|
|
|
return $swappedAnswer; |
|
1993
|
|
|
} |
|
1994
|
|
|
} |
|
1995
|
|
|
|
This check looks for the bodies of
ifstatements that have no statements or where all statements have been commented out. This may be the result of changes for debugging or the code may simply be obsolete.These
ifbodies can be removed. If you have an empty if but statements in theelsebranch, consider inverting the condition.could be turned into
This is much more concise to read.