1
|
|
|
<?php |
2
|
|
|
/* For licensing terms, see /license.txt */ |
3
|
|
|
|
4
|
|
|
use ChamiloSession as Session; |
5
|
|
|
|
6
|
|
|
/** |
7
|
|
|
* Class Exercise |
8
|
|
|
* |
9
|
|
|
* Allows to instantiate an object of type Exercise |
10
|
|
|
* @package chamilo.exercise |
11
|
|
|
* @author Olivier Brouckaert |
12
|
|
|
* @author Julio Montoya Cleaning exercises |
13
|
|
|
* Modified by Hubert Borderiou #294 |
14
|
|
|
*/ |
15
|
|
|
class Exercise |
16
|
|
|
{ |
17
|
|
|
public $id; |
18
|
|
|
public $name; |
19
|
|
|
public $title; |
20
|
|
|
public $exercise; |
21
|
|
|
public $description; |
22
|
|
|
public $sound; |
23
|
|
|
public $type; //ALL_ON_ONE_PAGE or ONE_PER_PAGE |
24
|
|
|
public $random; |
25
|
|
|
public $random_answers; |
26
|
|
|
public $active; |
27
|
|
|
public $timeLimit; |
28
|
|
|
public $attempts; |
29
|
|
|
public $feedback_type; |
30
|
|
|
public $end_time; |
31
|
|
|
public $start_time; |
32
|
|
|
public $questionList; // array with the list of this exercise's questions |
33
|
|
|
/* including question list of the media */ |
34
|
|
|
public $questionListUncompressed; |
35
|
|
|
public $results_disabled; |
36
|
|
|
public $expired_time; |
37
|
|
|
public $course; |
38
|
|
|
public $course_id; |
39
|
|
|
public $propagate_neg; |
40
|
|
|
public $review_answers; |
41
|
|
|
public $randomByCat; |
42
|
|
|
public $text_when_finished; |
43
|
|
|
public $display_category_name; |
44
|
|
|
public $pass_percentage; |
45
|
|
|
public $edit_exercise_in_lp = false; |
46
|
|
|
public $is_gradebook_locked = false; |
47
|
|
|
public $exercise_was_added_in_lp = false; |
48
|
|
|
public $lpList = array(); |
49
|
|
|
public $force_edit_exercise_in_lp = false; |
50
|
|
|
public $categories; |
51
|
|
|
public $categories_grouping = true; |
52
|
|
|
public $endButton = 0; |
53
|
|
|
public $categoryWithQuestionList; |
54
|
|
|
public $mediaList; |
55
|
|
|
public $loadQuestionAJAX = false; |
56
|
|
|
// Notification send to the teacher. |
57
|
|
|
public $emailNotificationTemplate = null; |
58
|
|
|
// Notification send to the student. |
59
|
|
|
public $emailNotificationTemplateToUser = null; |
60
|
|
|
public $countQuestions = 0; |
61
|
|
|
public $fastEdition = false; |
62
|
|
|
public $modelType = 1; |
63
|
|
|
public $questionSelectionType = EX_Q_SELECTION_ORDERED; |
64
|
|
|
public $hideQuestionTitle = 0; |
65
|
|
|
public $scoreTypeModel = 0; |
66
|
|
|
public $categoryMinusOne = true; // Shows the category -1: See BT#6540 |
67
|
|
|
public $globalCategoryId = null; |
68
|
|
|
public $onSuccessMessage = null; |
69
|
|
|
public $onFailedMessage = null; |
70
|
|
|
public $emailAlert; |
71
|
|
|
public $notifyUserByEmail = 0; |
72
|
|
|
public $sessionId = 0; |
73
|
|
|
public $specialCategoryOrders = false; |
74
|
|
|
public $quizRelCategoryTable = false; |
75
|
|
|
// CREATE TABLE c_quiz_rel_category (iid BIGINT AUTO_INCREMENT NOT NULL, c_id INT NOT NULL, category_id INT NOT NULL, exercise_id INT NOT NULL, count_questions INT NOT NULL, PRIMARY KEY(iid)); |
76
|
|
|
// ALTER TABLE c_quiz ADD COLUMN question_selection_type INT; |
77
|
|
|
|
78
|
|
|
/** |
79
|
|
|
* Constructor of the class |
80
|
|
|
* |
81
|
|
|
* @author Olivier Brouckaert |
82
|
|
|
*/ |
83
|
|
|
public function __construct($course_id = null) |
84
|
|
|
{ |
85
|
|
|
$this->id = 0; |
86
|
|
|
$this->exercise = ''; |
87
|
|
|
$this->description = ''; |
88
|
|
|
$this->sound = ''; |
89
|
|
|
$this->type = ALL_ON_ONE_PAGE; |
90
|
|
|
$this->random = 0; |
91
|
|
|
$this->random_answers = 0; |
92
|
|
|
$this->active = 1; |
93
|
|
|
$this->questionList = array(); |
94
|
|
|
$this->timeLimit = 0; |
95
|
|
|
$this->end_time = '0000-00-00 00:00:00'; |
96
|
|
|
$this->start_time = '0000-00-00 00:00:00'; |
97
|
|
|
$this->results_disabled = 1; |
98
|
|
|
$this->expired_time = '0000-00-00 00:00:00'; |
99
|
|
|
$this->propagate_neg = 0; |
100
|
|
|
$this->review_answers = false; |
101
|
|
|
$this->randomByCat = 0; |
102
|
|
|
$this->text_when_finished = ''; |
103
|
|
|
$this->display_category_name = 0; |
104
|
|
|
$this->pass_percentage = ''; |
105
|
|
|
|
106
|
|
|
$this->modelType = 1; |
107
|
|
|
$this->questionSelectionType = EX_Q_SELECTION_ORDERED; |
108
|
|
|
$this->endButton = 0; |
109
|
|
|
$this->scoreTypeModel = 0; |
110
|
|
|
$this->globalCategoryId = null; |
111
|
|
|
|
112
|
|
|
if (!empty($course_id)) { |
113
|
|
|
$course_info = api_get_course_info_by_id($course_id); |
114
|
|
|
} else { |
115
|
|
|
$course_info = api_get_course_info(); |
116
|
|
|
} |
117
|
|
|
$this->course_id = $course_info['real_id']; |
118
|
|
|
$this->course = $course_info; |
119
|
|
|
$this->specialCategoryOrders = api_get_configuration_value('exercise_enable_category_order'); |
120
|
|
|
} |
121
|
|
|
|
122
|
|
|
/** |
123
|
|
|
* Reads exercise information from the data base |
124
|
|
|
* |
125
|
|
|
* @author Olivier Brouckaert |
126
|
|
|
* @param integer $id - exercise Id |
127
|
|
|
* |
128
|
|
|
* @return boolean - true if exercise exists, otherwise false |
129
|
|
|
*/ |
130
|
|
|
public function read($id, $parseQuestionList = true) |
131
|
|
|
{ |
132
|
|
|
$TBL_EXERCISES = Database::get_course_table(TABLE_QUIZ_TEST); |
133
|
|
|
$table_lp_item = Database::get_course_table(TABLE_LP_ITEM); |
134
|
|
|
|
135
|
|
|
$id = intval($id); |
136
|
|
|
if (empty($this->course_id)) { |
137
|
|
|
|
138
|
|
|
return false; |
139
|
|
|
} |
140
|
|
|
$sql = "SELECT * FROM $TBL_EXERCISES WHERE c_id = ".$this->course_id." AND id = ".$id; |
141
|
|
|
$result = Database::query($sql); |
142
|
|
|
|
143
|
|
|
// if the exercise has been found |
144
|
|
|
if ($object = Database::fetch_object($result)) { |
145
|
|
|
$this->id = $id; |
146
|
|
|
$this->exercise = $object->title; |
147
|
|
|
$this->name = $object->title; |
148
|
|
|
$this->title = $object->title; |
149
|
|
|
$this->description = $object->description; |
150
|
|
|
$this->sound = $object->sound; |
151
|
|
|
$this->type = $object->type; |
152
|
|
|
if (empty($this->type)) { |
153
|
|
|
$this->type = ONE_PER_PAGE; |
154
|
|
|
} |
155
|
|
|
$this->random = $object->random; |
156
|
|
|
$this->random_answers = $object->random_answers; |
157
|
|
|
$this->active = $object->active; |
158
|
|
|
$this->results_disabled = $object->results_disabled; |
159
|
|
|
$this->attempts = $object->max_attempt; |
160
|
|
|
$this->feedback_type = $object->feedback_type; |
161
|
|
|
$this->propagate_neg = $object->propagate_neg; |
162
|
|
|
$this->randomByCat = $object->random_by_category; |
163
|
|
|
$this->text_when_finished = $object->text_when_finished; |
164
|
|
|
$this->display_category_name = $object->display_category_name; |
165
|
|
|
$this->pass_percentage = $object->pass_percentage; |
166
|
|
|
$this->sessionId = $object->session_id; |
167
|
|
|
$this->is_gradebook_locked = api_resource_is_locked_by_gradebook($id, LINK_EXERCISE); |
168
|
|
|
$this->review_answers = (isset($object->review_answers) && $object->review_answers == 1) ? true : false; |
169
|
|
|
$this->globalCategoryId = isset($object->global_category_id) ? $object->global_category_id : null; |
170
|
|
|
$this->questionSelectionType = isset($object->question_selection_type) ? $object->question_selection_type : null; |
171
|
|
|
|
172
|
|
|
$sql = "SELECT lp_id, max_score |
173
|
|
|
FROM $table_lp_item |
174
|
|
|
WHERE c_id = {$this->course_id} AND |
175
|
|
|
item_type = '".TOOL_QUIZ."' AND |
176
|
|
|
path = '".$id."'"; |
177
|
|
|
$result = Database::query($sql); |
178
|
|
|
|
179
|
|
|
if (Database::num_rows($result) > 0) { |
180
|
|
|
$this->exercise_was_added_in_lp = true; |
181
|
|
|
$this->lpList = Database::store_result($result, 'ASSOC'); |
182
|
|
|
} |
183
|
|
|
|
184
|
|
|
$this->force_edit_exercise_in_lp = api_get_configuration_value('force_edit_exercise_in_lp'); |
185
|
|
|
|
186
|
|
|
if ($this->exercise_was_added_in_lp) { |
187
|
|
|
$this->edit_exercise_in_lp = $this->force_edit_exercise_in_lp == true; |
188
|
|
|
} else { |
189
|
|
|
$this->edit_exercise_in_lp = true; |
190
|
|
|
} |
191
|
|
|
|
192
|
|
|
if ($object->end_time != '0000-00-00 00:00:00') { |
193
|
|
|
$this->end_time = $object->end_time; |
194
|
|
|
} |
195
|
|
|
if ($object->start_time != '0000-00-00 00:00:00') { |
196
|
|
|
$this->start_time = $object->start_time; |
197
|
|
|
} |
198
|
|
|
|
199
|
|
|
//control time |
200
|
|
|
$this->expired_time = $object->expired_time; |
201
|
|
|
|
202
|
|
|
//Checking if question_order is correctly set |
203
|
|
|
|
204
|
|
|
//$this->questionList = $this->selectQuestionList(true); |
205
|
|
|
if ($parseQuestionList) { |
206
|
|
|
$this->setQuestionList(); |
207
|
|
|
} |
208
|
|
|
|
209
|
|
|
//overload questions list with recorded questions list |
210
|
|
|
//load questions only for exercises of type 'one question per page' |
211
|
|
|
//this is needed only is there is no questions |
212
|
|
|
/* |
213
|
|
|
// @todo not sure were in the code this is used somebody mess with the exercise tool |
214
|
|
|
// @todo don't know who add that config and why $_configuration['live_exercise_tracking'] |
215
|
|
|
global $_configuration, $questionList; |
216
|
|
|
if ($this->type == ONE_PER_PAGE && $_SERVER['REQUEST_METHOD'] != 'POST' && defined('QUESTION_LIST_ALREADY_LOGGED') && |
217
|
|
|
isset($_configuration['live_exercise_tracking']) && $_configuration['live_exercise_tracking']) { |
218
|
|
|
$this->questionList = $questionList; |
219
|
|
|
}*/ |
220
|
|
|
return true; |
221
|
|
|
} |
222
|
|
|
|
223
|
|
|
return false; |
224
|
|
|
} |
225
|
|
|
|
226
|
|
|
/** |
227
|
|
|
* @return string |
228
|
|
|
*/ |
229
|
|
|
public function getCutTitle() |
230
|
|
|
{ |
231
|
|
|
return cut($this->exercise, EXERCISE_MAX_NAME_SIZE); |
232
|
|
|
} |
233
|
|
|
|
234
|
|
|
/** |
235
|
|
|
* returns the exercise ID |
236
|
|
|
* |
237
|
|
|
* @author Olivier Brouckaert |
238
|
|
|
* @return int - exercise ID |
239
|
|
|
*/ |
240
|
|
|
public function selectId() |
241
|
|
|
{ |
242
|
|
|
return $this->id; |
243
|
|
|
} |
244
|
|
|
|
245
|
|
|
/** |
246
|
|
|
* returns the exercise title |
247
|
|
|
* |
248
|
|
|
* @author Olivier Brouckaert |
249
|
|
|
* @return string - exercise title |
250
|
|
|
*/ |
251
|
|
|
public function selectTitle() |
252
|
|
|
{ |
253
|
|
|
return $this->exercise; |
254
|
|
|
} |
255
|
|
|
|
256
|
|
|
/** |
257
|
|
|
* returns the number of attempts setted |
258
|
|
|
* |
259
|
|
|
* @return int - exercise attempts |
260
|
|
|
*/ |
261
|
|
|
public function selectAttempts() |
262
|
|
|
{ |
263
|
|
|
return $this->attempts; |
264
|
|
|
} |
265
|
|
|
|
266
|
|
|
/** returns the number of FeedbackType * |
267
|
|
|
* 0=>Feedback , 1=>DirectFeedback, 2=>NoFeedback |
268
|
|
|
* @return int - exercise attempts |
269
|
|
|
*/ |
270
|
|
|
public function selectFeedbackType() |
271
|
|
|
{ |
272
|
|
|
return $this->feedback_type; |
273
|
|
|
} |
274
|
|
|
|
275
|
|
|
/** |
276
|
|
|
* returns the time limit |
277
|
|
|
*/ |
278
|
|
|
public function selectTimeLimit() |
279
|
|
|
{ |
280
|
|
|
return $this->timeLimit; |
281
|
|
|
} |
282
|
|
|
|
283
|
|
|
/** |
284
|
|
|
* returns the exercise description |
285
|
|
|
* |
286
|
|
|
* @author Olivier Brouckaert |
287
|
|
|
* @return string - exercise description |
288
|
|
|
*/ |
289
|
|
|
public function selectDescription() |
290
|
|
|
{ |
291
|
|
|
return $this->description; |
292
|
|
|
} |
293
|
|
|
|
294
|
|
|
/** |
295
|
|
|
* returns the exercise sound file |
296
|
|
|
* |
297
|
|
|
* @author Olivier Brouckaert |
298
|
|
|
* @return string - exercise description |
299
|
|
|
*/ |
300
|
|
|
public function selectSound() |
301
|
|
|
{ |
302
|
|
|
return $this->sound; |
303
|
|
|
} |
304
|
|
|
|
305
|
|
|
/** |
306
|
|
|
* returns the exercise type |
307
|
|
|
* |
308
|
|
|
* @author Olivier Brouckaert |
309
|
|
|
* @return integer - exercise type |
310
|
|
|
*/ |
311
|
|
|
public function selectType() |
312
|
|
|
{ |
313
|
|
|
return $this->type; |
314
|
|
|
} |
315
|
|
|
|
316
|
|
|
/** |
317
|
|
|
* @return int |
318
|
|
|
*/ |
319
|
|
|
public function getModelType() |
320
|
|
|
{ |
321
|
|
|
return $this->modelType; |
322
|
|
|
} |
323
|
|
|
|
324
|
|
|
/** |
325
|
|
|
* @return int |
326
|
|
|
*/ |
327
|
|
|
public function selectEndButton() |
328
|
|
|
{ |
329
|
|
|
return $this->endButton; |
330
|
|
|
} |
331
|
|
|
|
332
|
|
|
/** |
333
|
|
|
* @return string |
334
|
|
|
*/ |
335
|
|
|
public function getOnSuccessMessage() |
336
|
|
|
{ |
337
|
|
|
return $this->onSuccessMessage; |
338
|
|
|
} |
339
|
|
|
|
340
|
|
|
/** |
341
|
|
|
* @return string |
342
|
|
|
*/ |
343
|
|
|
public function getOnFailedMessage() |
344
|
|
|
{ |
345
|
|
|
return $this->onFailedMessage; |
346
|
|
|
} |
347
|
|
|
|
348
|
|
|
/** |
349
|
|
|
* @author hubert borderiou 30-11-11 |
350
|
|
|
* @return integer : do we display the question category name for students |
351
|
|
|
*/ |
352
|
|
|
public function selectDisplayCategoryName() |
353
|
|
|
{ |
354
|
|
|
return $this->display_category_name; |
355
|
|
|
} |
356
|
|
|
|
357
|
|
|
/** |
358
|
|
|
* @return int |
359
|
|
|
*/ |
360
|
|
|
public function selectPassPercentage() |
361
|
|
|
{ |
362
|
|
|
return $this->pass_percentage; |
363
|
|
|
} |
364
|
|
|
|
365
|
|
|
/** |
366
|
|
|
* |
367
|
|
|
* Modify object to update the switch display_category_name |
368
|
|
|
* @author hubert borderiou 30-11-11 |
369
|
|
|
* @param int $in_txt is an integer 0 or 1 |
370
|
|
|
*/ |
371
|
|
|
public function updateDisplayCategoryName($in_txt) |
372
|
|
|
{ |
373
|
|
|
$this->display_category_name = $in_txt; |
374
|
|
|
} |
375
|
|
|
|
376
|
|
|
/** |
377
|
|
|
* @author hubert borderiou 28-11-11 |
378
|
|
|
* @return string html text : the text to display ay the end of the test. |
379
|
|
|
*/ |
380
|
|
|
public function selectTextWhenFinished() |
381
|
|
|
{ |
382
|
|
|
return $this->text_when_finished; |
383
|
|
|
} |
384
|
|
|
|
385
|
|
|
/** |
386
|
|
|
* @author hubert borderiou 28-11-11 |
387
|
|
|
* @return string html text : update the text to display ay the end of the test. |
388
|
|
|
*/ |
389
|
|
|
public function updateTextWhenFinished($in_txt) |
390
|
|
|
{ |
391
|
|
|
$this->text_when_finished = $in_txt; |
392
|
|
|
} |
393
|
|
|
|
394
|
|
|
/** |
395
|
|
|
* return 1 or 2 if randomByCat |
396
|
|
|
* @author hubert borderiou |
397
|
|
|
* @return integer - quiz random by category |
398
|
|
|
*/ |
399
|
|
|
public function selectRandomByCat() |
400
|
|
|
{ |
401
|
|
|
return $this->randomByCat; |
402
|
|
|
} |
403
|
|
|
|
404
|
|
|
/** |
405
|
|
|
* return 0 if no random by cat |
406
|
|
|
* return 1 if random by cat, categories shuffled |
407
|
|
|
* return 2 if random by cat, categories sorted by alphabetic order |
408
|
|
|
* @author hubert borderiou |
409
|
|
|
* @return integer - quiz random by category |
410
|
|
|
*/ |
411
|
|
|
public function isRandomByCat() |
412
|
|
|
{ |
413
|
|
|
/*$res = 0; |
414
|
|
|
if ($this->randomByCat == 1) { |
415
|
|
|
$res = 1; |
416
|
|
|
} else if ($this->randomByCat == 2) { |
417
|
|
|
$res = 2; |
418
|
|
|
} |
419
|
|
|
*/ |
420
|
|
|
|
421
|
|
|
$res = EXERCISE_CATEGORY_RANDOM_DISABLED; |
422
|
|
|
if ($this->randomByCat == EXERCISE_CATEGORY_RANDOM_SHUFFLED) { |
423
|
|
|
$res = EXERCISE_CATEGORY_RANDOM_SHUFFLED; |
424
|
|
|
} else if ($this->randomByCat == EXERCISE_CATEGORY_RANDOM_ORDERED) { |
425
|
|
|
$res = EXERCISE_CATEGORY_RANDOM_ORDERED; |
426
|
|
|
} |
427
|
|
|
|
428
|
|
|
return $res; |
429
|
|
|
} |
430
|
|
|
|
431
|
|
|
/** |
432
|
|
|
* return nothing |
433
|
|
|
* update randomByCat value for object |
434
|
|
|
* @param int $random |
435
|
|
|
* |
436
|
|
|
* @author hubert borderiou |
437
|
|
|
*/ |
438
|
|
|
public function updateRandomByCat($random) |
439
|
|
|
{ |
440
|
|
|
if ($this->specialCategoryOrders) { |
441
|
|
|
if (in_array($random, array( |
442
|
|
|
EXERCISE_CATEGORY_RANDOM_SHUFFLED, |
443
|
|
|
EXERCISE_CATEGORY_RANDOM_ORDERED, |
444
|
|
|
EXERCISE_CATEGORY_RANDOM_DISABLED |
445
|
|
|
) |
446
|
|
|
)) { |
447
|
|
|
$this->randomByCat = $random; |
448
|
|
|
} else { |
449
|
|
|
$this->randomByCat = EXERCISE_CATEGORY_RANDOM_DISABLED; |
450
|
|
|
} |
451
|
|
|
} else { |
452
|
|
|
if ($random == 1) { |
453
|
|
|
$this->randomByCat = 1; |
454
|
|
|
} else if ($random == 2) { |
455
|
|
|
$this->randomByCat = 2; |
456
|
|
|
} else { |
457
|
|
|
$this->randomByCat = 0; |
458
|
|
|
} |
459
|
|
|
} |
460
|
|
|
} |
461
|
|
|
|
462
|
|
|
/** |
463
|
|
|
* Tells if questions are selected randomly, and if so returns the draws |
464
|
|
|
* |
465
|
|
|
* @author Carlos Vargas |
466
|
|
|
* @return integer - results disabled exercise |
467
|
|
|
*/ |
468
|
|
|
public function selectResultsDisabled() |
469
|
|
|
{ |
470
|
|
|
return $this->results_disabled; |
471
|
|
|
} |
472
|
|
|
|
473
|
|
|
/** |
474
|
|
|
* tells if questions are selected randomly, and if so returns the draws |
475
|
|
|
* |
476
|
|
|
* @author Olivier Brouckaert |
477
|
|
|
* @return integer - 0 if not random, otherwise the draws |
478
|
|
|
*/ |
479
|
|
|
public function isRandom() |
480
|
|
|
{ |
481
|
|
|
if($this->random > 0 || $this->random == -1) { |
482
|
|
|
return true; |
483
|
|
|
} else { |
484
|
|
|
return false; |
485
|
|
|
} |
486
|
|
|
} |
487
|
|
|
|
488
|
|
|
/** |
489
|
|
|
* returns random answers status. |
490
|
|
|
* |
491
|
|
|
* @author Juan Carlos Rana |
492
|
|
|
*/ |
493
|
|
|
public function selectRandomAnswers() |
494
|
|
|
{ |
495
|
|
|
return $this->random_answers; |
496
|
|
|
} |
497
|
|
|
|
498
|
|
|
/** |
499
|
|
|
* Same as isRandom() but has a name applied to values different than 0 or 1 |
500
|
|
|
*/ |
501
|
|
|
public function getShuffle() |
502
|
|
|
{ |
503
|
|
|
return $this->random; |
504
|
|
|
} |
505
|
|
|
|
506
|
|
|
/** |
507
|
|
|
* returns the exercise status (1 = enabled ; 0 = disabled) |
508
|
|
|
* |
509
|
|
|
* @author Olivier Brouckaert |
510
|
|
|
* @return boolean - true if enabled, otherwise false |
511
|
|
|
*/ |
512
|
|
|
public function selectStatus() |
513
|
|
|
{ |
514
|
|
|
return $this->active; |
515
|
|
|
} |
516
|
|
|
|
517
|
|
|
/** |
518
|
|
|
* If false the question list will be managed as always if true the question will be filtered |
519
|
|
|
* depending of the exercise settings (table c_quiz_rel_category) |
520
|
|
|
* @param bool active or inactive grouping |
521
|
|
|
**/ |
522
|
|
|
public function setCategoriesGrouping($status) |
523
|
|
|
{ |
524
|
|
|
$this->categories_grouping = (bool) $status; |
525
|
|
|
} |
526
|
|
|
|
527
|
|
|
/** |
528
|
|
|
* @return int |
529
|
|
|
*/ |
530
|
|
|
public function getHideQuestionTitle() |
531
|
|
|
{ |
532
|
|
|
return $this->hideQuestionTitle; |
533
|
|
|
} |
534
|
|
|
|
535
|
|
|
/** |
536
|
|
|
* @param $value |
537
|
|
|
*/ |
538
|
|
|
public function setHideQuestionTitle($value) |
539
|
|
|
{ |
540
|
|
|
$this->hideQuestionTitle = intval($value); |
541
|
|
|
} |
542
|
|
|
|
543
|
|
|
/** |
544
|
|
|
* @return int |
545
|
|
|
*/ |
546
|
|
|
public function getScoreTypeModel() |
547
|
|
|
{ |
548
|
|
|
return $this->scoreTypeModel; |
549
|
|
|
} |
550
|
|
|
|
551
|
|
|
/** |
552
|
|
|
* @param int $value |
553
|
|
|
*/ |
554
|
|
|
public function setScoreTypeModel($value) |
555
|
|
|
{ |
556
|
|
|
$this->scoreTypeModel = intval($value); |
557
|
|
|
} |
558
|
|
|
|
559
|
|
|
/** |
560
|
|
|
* @return int |
561
|
|
|
*/ |
562
|
|
|
public function getGlobalCategoryId() |
563
|
|
|
{ |
564
|
|
|
return $this->globalCategoryId; |
565
|
|
|
} |
566
|
|
|
|
567
|
|
|
/** |
568
|
|
|
* @param int $value |
569
|
|
|
*/ |
570
|
|
|
public function setGlobalCategoryId($value) |
571
|
|
|
{ |
572
|
|
|
if (is_array($value) && isset($value[0])) { |
573
|
|
|
$value = $value[0]; |
574
|
|
|
} |
575
|
|
|
$this->globalCategoryId = intval($value); |
576
|
|
|
} |
577
|
|
|
|
578
|
|
|
/** |
579
|
|
|
* |
580
|
|
|
* @param int $start |
581
|
|
|
* @param int $limit |
582
|
|
|
* @param int $sidx |
583
|
|
|
* @param string $sord |
584
|
|
|
* @param array $where_condition |
585
|
|
|
* @param array $extraFields |
586
|
|
|
*/ |
587
|
|
|
public function getQuestionListPagination($start, $limit, $sidx, $sord, $where_condition = array(), $extraFields = array()) |
588
|
|
|
{ |
589
|
|
|
if (!empty($this->id)) { |
590
|
|
|
$category_list = TestCategory::getListOfCategoriesNameForTest($this->id, false); |
591
|
|
|
$TBL_EXERCICE_QUESTION = Database::get_course_table(TABLE_QUIZ_TEST_QUESTION); |
592
|
|
|
$TBL_QUESTIONS = Database::get_course_table(TABLE_QUIZ_QUESTION); |
593
|
|
|
|
594
|
|
|
$sql = "SELECT q.iid |
595
|
|
|
FROM $TBL_EXERCICE_QUESTION e INNER JOIN $TBL_QUESTIONS q |
596
|
|
|
ON (e.question_id = q.iid AND e.c_id = ".$this->course_id." ) |
597
|
|
|
WHERE e.exercice_id = '".Database::escape_string($this->id)."' |
598
|
|
|
"; |
599
|
|
|
|
600
|
|
|
$orderCondition = "ORDER BY question_order"; |
601
|
|
|
|
602
|
|
|
if (!empty($sidx) && !empty($sord)) { |
603
|
|
|
if ($sidx == 'question') { |
604
|
|
|
|
605
|
|
|
if (in_array(strtolower($sord), array('desc', 'asc'))) { |
606
|
|
|
$orderCondition = " ORDER BY q.$sidx $sord"; |
607
|
|
|
} |
608
|
|
|
} |
609
|
|
|
} |
610
|
|
|
|
611
|
|
|
$sql .= $orderCondition; |
612
|
|
|
|
613
|
|
|
$limitCondition = null; |
614
|
|
|
|
615
|
|
|
if (isset($start) && isset($limit)) { |
616
|
|
|
$start = intval($start); |
617
|
|
|
$limit = intval($limit); |
618
|
|
|
$limitCondition = " LIMIT $start, $limit"; |
619
|
|
|
} |
620
|
|
|
$sql .= $limitCondition; |
621
|
|
|
$result = Database::query($sql); |
622
|
|
|
$questions = array(); |
623
|
|
|
if (Database::num_rows($result)) { |
624
|
|
|
if (!empty($extraFields)) { |
625
|
|
|
$extraFieldValue = new ExtraFieldValue('question'); |
626
|
|
|
} |
627
|
|
|
while ($question = Database::fetch_array($result, 'ASSOC')) { |
628
|
|
|
/** @var Question $objQuestionTmp */ |
629
|
|
|
$objQuestionTmp = Question::read($question['iid']); |
630
|
|
|
$category_labels = TestCategory::return_category_labels($objQuestionTmp->category_list, $category_list); |
631
|
|
|
|
632
|
|
|
if (empty($category_labels)) { |
633
|
|
|
$category_labels = "-"; |
634
|
|
|
} |
635
|
|
|
|
636
|
|
|
// Question type |
637
|
|
|
list($typeImg, $typeExpl) = $objQuestionTmp->get_type_icon_html(); |
638
|
|
|
|
639
|
|
|
$question_media = null; |
640
|
|
|
if (!empty($objQuestionTmp->parent_id)) { |
641
|
|
|
$objQuestionMedia = Question::read($objQuestionTmp->parent_id); |
642
|
|
|
$question_media = Question::getMediaLabel($objQuestionMedia->question); |
643
|
|
|
} |
644
|
|
|
|
645
|
|
|
$questionType = Display::tag('div', Display::return_icon($typeImg, $typeExpl, array(), ICON_SIZE_MEDIUM).$question_media); |
646
|
|
|
|
647
|
|
|
$question = array( |
648
|
|
|
'id' => $question['iid'], |
649
|
|
|
'question' => $objQuestionTmp->selectTitle(), |
650
|
|
|
'type' => $questionType, |
651
|
|
|
'category' => Display::tag('div', '<a href="#" style="padding:0px; margin:0px;">'.$category_labels.'</a>'), |
652
|
|
|
'score' => $objQuestionTmp->selectWeighting(), |
653
|
|
|
'level' => $objQuestionTmp->level |
654
|
|
|
); |
655
|
|
|
if (!empty($extraFields)) { |
656
|
|
|
foreach ($extraFields as $extraField) { |
657
|
|
|
$value = $extraFieldValue->get_values_by_handler_and_field_id($question['id'], $extraField['id']); |
658
|
|
|
$stringValue = null; |
659
|
|
|
if ($value) { |
660
|
|
|
$stringValue = $value['field_value']; |
661
|
|
|
} |
662
|
|
|
$question[$extraField['field_variable']] = $stringValue; |
663
|
|
|
} |
664
|
|
|
} |
665
|
|
|
$questions[] = $question; |
666
|
|
|
} |
667
|
|
|
} |
668
|
|
|
return $questions; |
669
|
|
|
} |
670
|
|
|
} |
671
|
|
|
|
672
|
|
|
/** |
673
|
|
|
* Get question count per exercise from DB (any special treatment) |
674
|
|
|
* @return int |
675
|
|
|
*/ |
676
|
|
|
public function getQuestionCount() |
677
|
|
|
{ |
678
|
|
|
$TBL_EXERCICE_QUESTION = Database::get_course_table(TABLE_QUIZ_TEST_QUESTION); |
679
|
|
|
$TBL_QUESTIONS = Database::get_course_table(TABLE_QUIZ_QUESTION); |
680
|
|
|
$sql = "SELECT count(q.id) as count |
681
|
|
|
FROM $TBL_EXERCICE_QUESTION e INNER JOIN $TBL_QUESTIONS q |
682
|
|
|
ON (e.question_id = q.id) |
683
|
|
|
WHERE e.c_id = {$this->course_id} AND e.exercice_id = ".Database::escape_string($this->id); |
684
|
|
|
$result = Database::query($sql); |
685
|
|
|
|
686
|
|
|
$count = 0; |
687
|
|
|
if (Database::num_rows($result)) { |
688
|
|
|
$row = Database::fetch_array($result); |
689
|
|
|
$count = $row['count']; |
690
|
|
|
} |
691
|
|
|
|
692
|
|
|
return $count; |
693
|
|
|
} |
694
|
|
|
|
695
|
|
|
/** |
696
|
|
|
* @return array |
697
|
|
|
*/ |
698
|
|
|
public function getQuestionOrderedListByName() |
699
|
|
|
{ |
700
|
|
|
$TBL_EXERCICE_QUESTION = Database::get_course_table(TABLE_QUIZ_TEST_QUESTION); |
701
|
|
|
$TBL_QUESTIONS = Database::get_course_table(TABLE_QUIZ_QUESTION); |
702
|
|
|
|
703
|
|
|
// Getting question list from the order (question list drag n drop interface ). |
704
|
|
|
$sql = "SELECT e.question_id |
705
|
|
|
FROM $TBL_EXERCICE_QUESTION e INNER JOIN $TBL_QUESTIONS q |
706
|
|
|
ON (e.question_id= q.id) |
707
|
|
|
WHERE e.c_id = {$this->course_id} AND e.exercice_id = '".Database::escape_string($this->id)."' |
708
|
|
|
ORDER BY q.question"; |
709
|
|
|
$result = Database::query($sql); |
710
|
|
|
$list = array(); |
711
|
|
|
if (Database::num_rows($result)) { |
712
|
|
|
$list = Database::store_result($result, 'ASSOC'); |
713
|
|
|
} |
714
|
|
|
return $list; |
715
|
|
|
} |
716
|
|
|
|
717
|
|
|
/** |
718
|
|
|
* Gets the question list ordered by the question_order setting (drag and drop) |
719
|
|
|
* @return array |
720
|
|
|
*/ |
721
|
|
|
private function getQuestionOrderedList() |
722
|
|
|
{ |
723
|
|
|
$questionList = array(); |
724
|
|
|
|
725
|
|
|
$TBL_EXERCICE_QUESTION = Database::get_course_table(TABLE_QUIZ_TEST_QUESTION); |
726
|
|
|
$TBL_QUESTIONS = Database::get_course_table(TABLE_QUIZ_QUESTION); |
727
|
|
|
|
728
|
|
|
// Getting question_order to verify that the question |
729
|
|
|
// list is correct and all question_order's were set |
730
|
|
|
$sql = "SELECT DISTINCT e.question_order |
731
|
|
|
FROM $TBL_EXERCICE_QUESTION e |
732
|
|
|
INNER JOIN $TBL_QUESTIONS q |
733
|
|
|
ON (e.question_id = q.id) |
734
|
|
|
WHERE |
735
|
|
|
e.c_id = {$this->course_id} AND |
736
|
|
|
e.exercice_id = ".Database::escape_string($this->id); |
737
|
|
|
|
738
|
|
|
$result = Database::query($sql); |
739
|
|
|
|
740
|
|
|
$count_question_orders = Database::num_rows($result); |
741
|
|
|
|
742
|
|
|
// Getting question list from the order (question list drag n drop interface ). |
743
|
|
|
$sql = "SELECT DISTINCT e.question_id, e.question_order |
744
|
|
|
FROM $TBL_EXERCICE_QUESTION e |
745
|
|
|
INNER JOIN $TBL_QUESTIONS q |
746
|
|
|
ON (e.question_id= q.id) |
747
|
|
|
WHERE |
748
|
|
|
e.c_id = {$this->course_id} AND |
749
|
|
|
e.exercice_id = '".Database::escape_string($this->id)."' |
750
|
|
|
ORDER BY question_order"; |
751
|
|
|
|
752
|
|
|
$result = Database::query($sql); |
753
|
|
|
|
754
|
|
|
// Fills the array with the question ID for this exercise |
755
|
|
|
// the key of the array is the question position |
756
|
|
|
$temp_question_list = array(); |
757
|
|
|
|
758
|
|
|
$counter = 1; |
759
|
|
|
while ($new_object = Database::fetch_object($result)) { |
760
|
|
|
// Correct order. |
761
|
|
|
$questionList[$new_object->question_order] = $new_object->question_id; |
762
|
|
|
// Just in case we save the order in other array |
763
|
|
|
$temp_question_list[$counter] = $new_object->question_id; |
764
|
|
|
$counter++; |
765
|
|
|
} |
766
|
|
|
|
767
|
|
|
if (!empty($temp_question_list)) { |
768
|
|
|
/* If both array don't match it means that question_order was not correctly set |
769
|
|
|
for all questions using the default mysql order */ |
770
|
|
|
if (count($temp_question_list) != $count_question_orders) { |
771
|
|
|
$questionList = $temp_question_list; |
772
|
|
|
} |
773
|
|
|
} |
774
|
|
|
|
775
|
|
|
return $questionList; |
776
|
|
|
} |
777
|
|
|
|
778
|
|
|
/** |
779
|
|
|
* Select N values from the questions per category array |
780
|
|
|
* |
781
|
|
|
* @param array $categoriesAddedInExercise |
782
|
|
|
* @param array $question_list |
783
|
|
|
* @param array $questions_by_category per category |
784
|
|
|
* @param bool $flatResult |
785
|
|
|
* @param bool $randomizeQuestions |
786
|
|
|
* |
787
|
|
|
* @return array |
788
|
|
|
*/ |
789
|
|
|
private function pickQuestionsPerCategory( |
790
|
|
|
$categoriesAddedInExercise, |
791
|
|
|
$question_list, |
792
|
|
|
& $questions_by_category, |
793
|
|
|
$flatResult = true, |
794
|
|
|
$randomizeQuestions = false |
795
|
|
|
) { |
796
|
|
|
$addAll = true; |
797
|
|
|
$categoryCountArray = array(); |
798
|
|
|
|
799
|
|
|
// Getting how many questions will be selected per category. |
800
|
|
|
if (!empty($categoriesAddedInExercise)) { |
801
|
|
|
$addAll = false; |
802
|
|
|
// Parsing question according the category rel exercise settings |
803
|
|
|
foreach ($categoriesAddedInExercise as $category_info) { |
804
|
|
|
$category_id = $category_info['category_id']; |
805
|
|
|
if (isset($questions_by_category[$category_id])) { |
806
|
|
|
// How many question will be picked from this category. |
807
|
|
|
$count = $category_info['count_questions']; |
808
|
|
|
// -1 means all questions |
809
|
|
|
if ($count == -1) { |
810
|
|
|
$categoryCountArray[$category_id] = 999; |
811
|
|
|
} else { |
812
|
|
|
$categoryCountArray[$category_id] = $count; |
813
|
|
|
} |
814
|
|
|
} |
815
|
|
|
} |
816
|
|
|
} |
817
|
|
|
|
818
|
|
|
if (!empty($questions_by_category)) { |
819
|
|
|
$temp_question_list = array(); |
820
|
|
|
|
821
|
|
|
foreach ($questions_by_category as $category_id => & $categoryQuestionList) { |
822
|
|
|
if (isset($categoryCountArray) && !empty($categoryCountArray)) { |
823
|
|
|
if (isset($categoryCountArray[$category_id])) { |
824
|
|
|
$numberOfQuestions = $categoryCountArray[$category_id]; |
825
|
|
|
} else { |
826
|
|
|
$numberOfQuestions = 0; |
827
|
|
|
} |
828
|
|
|
} |
829
|
|
|
|
830
|
|
|
if ($addAll) { |
831
|
|
|
$numberOfQuestions = 999; |
832
|
|
|
} |
833
|
|
|
|
834
|
|
|
if (!empty($numberOfQuestions)) { |
835
|
|
|
$elements = TestCategory::getNElementsFromArray( |
836
|
|
|
$categoryQuestionList, |
837
|
|
|
$numberOfQuestions, |
838
|
|
|
$randomizeQuestions |
839
|
|
|
); |
840
|
|
|
|
841
|
|
|
if (!empty($elements)) { |
842
|
|
|
$temp_question_list[$category_id] = $elements; |
843
|
|
|
$categoryQuestionList = $elements; |
844
|
|
|
} |
845
|
|
|
} |
846
|
|
|
} |
847
|
|
|
|
848
|
|
|
if (!empty($temp_question_list)) { |
849
|
|
|
if ($flatResult) { |
850
|
|
|
$temp_question_list = array_flatten($temp_question_list); |
851
|
|
|
} |
852
|
|
|
$question_list = $temp_question_list; |
853
|
|
|
} |
854
|
|
|
} |
855
|
|
|
|
856
|
|
|
return $question_list; |
857
|
|
|
} |
858
|
|
|
|
859
|
|
|
/** |
860
|
|
|
* Selecting question list depending in the exercise-category |
861
|
|
|
* relationship (category table in exercise settings) |
862
|
|
|
* |
863
|
|
|
* @param array $question_list |
864
|
|
|
* @param int $questionSelectionType |
865
|
|
|
* @return array |
866
|
|
|
*/ |
867
|
|
|
public function getQuestionListWithCategoryListFilteredByCategorySettings($question_list, $questionSelectionType) |
868
|
|
|
{ |
869
|
|
|
$result = array( |
870
|
|
|
'question_list' => array(), |
871
|
|
|
'category_with_questions_list' => array() |
872
|
|
|
); |
873
|
|
|
|
874
|
|
|
// Order/random categories |
875
|
|
|
$cat = new TestCategory(); |
876
|
|
|
|
877
|
|
|
// Setting category order. |
878
|
|
|
|
879
|
|
|
switch ($questionSelectionType) { |
880
|
|
|
case EX_Q_SELECTION_ORDERED: // 1 |
881
|
|
|
case EX_Q_SELECTION_RANDOM: // 2 |
882
|
|
|
// This options are not allowed here. |
883
|
|
|
break; |
884
|
|
|
case EX_Q_SELECTION_CATEGORIES_ORDERED_QUESTIONS_ORDERED: // 3 |
885
|
|
|
$categoriesAddedInExercise = $cat->getCategoryExerciseTree( |
886
|
|
|
$this, |
887
|
|
|
$this->course['real_id'], |
888
|
|
|
'title ASC', |
889
|
|
|
false, |
890
|
|
|
true |
891
|
|
|
); |
892
|
|
|
|
893
|
|
|
$questions_by_category = TestCategory::getQuestionsByCat( |
894
|
|
|
$this->id, |
895
|
|
|
$question_list, |
896
|
|
|
$categoriesAddedInExercise |
897
|
|
|
); |
898
|
|
|
|
899
|
|
|
|
900
|
|
|
$question_list = $this->pickQuestionsPerCategory( |
901
|
|
|
$categoriesAddedInExercise, |
902
|
|
|
$question_list, |
903
|
|
|
$questions_by_category, |
904
|
|
|
true, |
905
|
|
|
false |
906
|
|
|
); |
907
|
|
|
break; |
908
|
|
|
case EX_Q_SELECTION_CATEGORIES_RANDOM_QUESTIONS_ORDERED: // 4 |
909
|
|
|
case EX_Q_SELECTION_CATEGORIES_RANDOM_QUESTIONS_ORDERED_NO_GROUPED: // 7 |
910
|
|
|
$categoriesAddedInExercise = $cat->getCategoryExerciseTree( |
911
|
|
|
$this, |
912
|
|
|
$this->course['real_id'], |
913
|
|
|
null, |
914
|
|
|
true, |
915
|
|
|
true |
916
|
|
|
); |
917
|
|
|
$questions_by_category = TestCategory::getQuestionsByCat( |
918
|
|
|
$this->id, |
919
|
|
|
$question_list, |
920
|
|
|
$categoriesAddedInExercise |
921
|
|
|
); |
922
|
|
|
$question_list = $this->pickQuestionsPerCategory( |
923
|
|
|
$categoriesAddedInExercise, |
924
|
|
|
$question_list, |
925
|
|
|
$questions_by_category, |
926
|
|
|
true, |
927
|
|
|
false |
928
|
|
|
); |
929
|
|
|
break; |
930
|
|
|
case EX_Q_SELECTION_CATEGORIES_ORDERED_QUESTIONS_RANDOM: // 5 |
931
|
|
|
$categoriesAddedInExercise = $cat->getCategoryExerciseTree( |
932
|
|
|
$this, |
933
|
|
|
$this->course['real_id'], |
934
|
|
|
'title DESC', |
935
|
|
|
false, |
936
|
|
|
true |
937
|
|
|
); |
938
|
|
|
$questions_by_category = TestCategory::getQuestionsByCat( |
939
|
|
|
$this->id, |
940
|
|
|
$question_list, |
941
|
|
|
$categoriesAddedInExercise |
942
|
|
|
); |
943
|
|
|
$question_list = $this->pickQuestionsPerCategory( |
944
|
|
|
$categoriesAddedInExercise, |
945
|
|
|
$question_list, |
946
|
|
|
$questions_by_category, |
947
|
|
|
true, |
948
|
|
|
true |
949
|
|
|
); |
950
|
|
|
break; |
951
|
|
|
case EX_Q_SELECTION_CATEGORIES_RANDOM_QUESTIONS_RANDOM: // 6 |
952
|
|
|
case EX_Q_SELECTION_CATEGORIES_RANDOM_QUESTIONS_RANDOM_NO_GROUPED: |
953
|
|
|
$categoriesAddedInExercise = $cat->getCategoryExerciseTree( |
954
|
|
|
$this, |
955
|
|
|
$this->course['real_id'], |
956
|
|
|
null, |
957
|
|
|
true, |
958
|
|
|
true |
959
|
|
|
); |
960
|
|
|
$questions_by_category = TestCategory::getQuestionsByCat( |
961
|
|
|
$this->id, |
962
|
|
|
$question_list, |
963
|
|
|
$categoriesAddedInExercise |
964
|
|
|
); |
965
|
|
|
$question_list = $this->pickQuestionsPerCategory( |
966
|
|
|
$categoriesAddedInExercise, |
967
|
|
|
$question_list, |
968
|
|
|
$questions_by_category, |
969
|
|
|
true, |
970
|
|
|
true |
971
|
|
|
); |
972
|
|
|
break; |
973
|
|
|
case EX_Q_SELECTION_CATEGORIES_RANDOM_QUESTIONS_ORDERED_NO_GROUPED: // 7 |
974
|
|
|
break; |
975
|
|
|
case EX_Q_SELECTION_CATEGORIES_RANDOM_QUESTIONS_RANDOM_NO_GROUPED: // 8 |
976
|
|
|
break; |
977
|
|
|
case EX_Q_SELECTION_CATEGORIES_ORDERED_BY_PARENT_QUESTIONS_ORDERED: // 9 |
978
|
|
|
$categoriesAddedInExercise = $cat->getCategoryExerciseTree( |
979
|
|
|
$this, |
980
|
|
|
$this->course['real_id'], |
981
|
|
|
'root ASC, lft ASC', |
982
|
|
|
false, |
983
|
|
|
true |
984
|
|
|
); |
985
|
|
|
$questions_by_category = TestCategory::getQuestionsByCat( |
986
|
|
|
$this->id, |
987
|
|
|
$question_list, |
988
|
|
|
$categoriesAddedInExercise |
989
|
|
|
); |
990
|
|
|
$question_list = $this->pickQuestionsPerCategory( |
991
|
|
|
$categoriesAddedInExercise, |
992
|
|
|
$question_list, |
993
|
|
|
$questions_by_category, |
994
|
|
|
true, |
995
|
|
|
false |
996
|
|
|
); |
997
|
|
|
break; |
998
|
|
|
case EX_Q_SELECTION_CATEGORIES_ORDERED_BY_PARENT_QUESTIONS_RANDOM: // 10 |
999
|
|
|
$categoriesAddedInExercise = $cat->getCategoryExerciseTree( |
1000
|
|
|
$this, |
1001
|
|
|
$this->course['real_id'], |
1002
|
|
|
'root, lft ASC', |
1003
|
|
|
false, |
1004
|
|
|
true |
1005
|
|
|
); |
1006
|
|
|
$questions_by_category = TestCategory::getQuestionsByCat( |
1007
|
|
|
$this->id, |
1008
|
|
|
$question_list, |
1009
|
|
|
$categoriesAddedInExercise |
1010
|
|
|
); |
1011
|
|
|
$question_list = $this->pickQuestionsPerCategory( |
1012
|
|
|
$categoriesAddedInExercise, |
1013
|
|
|
$question_list, |
1014
|
|
|
$questions_by_category, |
1015
|
|
|
true, |
1016
|
|
|
true |
1017
|
|
|
); |
1018
|
|
|
break; |
1019
|
|
|
} |
1020
|
|
|
|
1021
|
|
|
$result['question_list'] = isset($question_list) ? $question_list : array(); |
1022
|
|
|
$result['category_with_questions_list'] = isset($questions_by_category) ? $questions_by_category : array(); |
1023
|
|
|
|
1024
|
|
|
// Adding category info in the category list with question list: |
1025
|
|
|
|
1026
|
|
|
if (!empty($questions_by_category)) { |
1027
|
|
|
|
1028
|
|
|
/*$em = Database::getManager(); |
1029
|
|
|
$repo = $em->getRepository('ChamiloCoreBundle:CQuizCategory');*/ |
1030
|
|
|
|
1031
|
|
|
$newCategoryList = array(); |
1032
|
|
|
|
1033
|
|
|
foreach ($questions_by_category as $categoryId => $questionList) { |
1034
|
|
|
$cat = new TestCategory($categoryId); |
1035
|
|
|
$cat = (array)$cat; |
1036
|
|
|
$cat['iid'] = $cat['id']; |
1037
|
|
|
//*$cat['name'] = $cat['name']; |
1038
|
|
|
|
1039
|
|
|
$categoryParentInfo = null; |
1040
|
|
|
// Parent is not set no loop here |
1041
|
|
|
if (!empty($cat['parent_id'])) { |
1042
|
|
|
if (!isset($parentsLoaded[$cat['parent_id']])) { |
1043
|
|
|
$categoryEntity = $em->find('ChamiloCoreBundle:CQuizCategory', $cat['parent_id']); |
1044
|
|
|
$parentsLoaded[$cat['parent_id']] = $categoryEntity; |
1045
|
|
|
} else { |
1046
|
|
|
$categoryEntity = $parentsLoaded[$cat['parent_id']]; |
1047
|
|
|
} |
1048
|
|
|
$path = $repo->getPath($categoryEntity); |
1049
|
|
|
$index = 0; |
1050
|
|
|
if ($this->categoryMinusOne) { |
|
|
|
|
1051
|
|
|
//$index = 1; |
1052
|
|
|
} |
1053
|
|
|
/** @var \Chamilo\Entity\CQuizCategory $categoryParent*/ |
1054
|
|
|
|
1055
|
|
|
foreach ($path as $categoryParent) { |
1056
|
|
|
$visibility = $categoryParent->getVisibility(); |
1057
|
|
|
|
1058
|
|
|
if ($visibility == 0) { |
1059
|
|
|
$categoryParentId = $categoryId; |
1060
|
|
|
$categoryTitle = $cat['title']; |
1061
|
|
|
if (count($path) > 1) { |
1062
|
|
|
continue; |
1063
|
|
|
} |
1064
|
|
|
} else { |
1065
|
|
|
$categoryParentId = $categoryParent->getIid(); |
1066
|
|
|
$categoryTitle = $categoryParent->getTitle(); |
1067
|
|
|
} |
1068
|
|
|
|
1069
|
|
|
$categoryParentInfo['id'] = $categoryParentId; |
1070
|
|
|
$categoryParentInfo['iid'] = $categoryParentId; |
1071
|
|
|
$categoryParentInfo['parent_path'] = null; |
1072
|
|
|
$categoryParentInfo['title'] = $categoryTitle; |
1073
|
|
|
$categoryParentInfo['name'] = $categoryTitle; |
1074
|
|
|
$categoryParentInfo['parent_id'] = null; |
1075
|
|
|
break; |
1076
|
|
|
} |
1077
|
|
|
} |
1078
|
|
|
$cat['parent_info'] = $categoryParentInfo; |
1079
|
|
|
$newCategoryList[$categoryId] = array( |
1080
|
|
|
'category' => $cat, |
1081
|
|
|
'question_list' => $questionList |
1082
|
|
|
); |
1083
|
|
|
} |
1084
|
|
|
|
1085
|
|
|
$result['category_with_questions_list'] = $newCategoryList; |
1086
|
|
|
} |
1087
|
|
|
return $result; |
1088
|
|
|
} |
1089
|
|
|
|
1090
|
|
|
/** |
1091
|
|
|
* returns the array with the question ID list |
1092
|
|
|
* |
1093
|
|
|
* @author Olivier Brouckaert |
1094
|
|
|
* @return array - question ID list |
1095
|
|
|
*/ |
1096
|
|
|
public function selectQuestionList($from_db = false) |
1097
|
|
|
{ |
1098
|
|
|
if ($this->specialCategoryOrders == false) { |
1099
|
|
|
if ($from_db && !empty($this->id)) { |
1100
|
|
|
$TBL_EXERCISE_QUESTION = Database::get_course_table(TABLE_QUIZ_TEST_QUESTION); |
1101
|
|
|
$TBL_QUESTIONS = Database::get_course_table(TABLE_QUIZ_QUESTION); |
1102
|
|
|
|
1103
|
|
|
$sql = "SELECT DISTINCT e.question_order |
1104
|
|
|
FROM $TBL_EXERCISE_QUESTION e |
1105
|
|
|
INNER JOIN $TBL_QUESTIONS q |
1106
|
|
|
ON (e.question_id = q.id AND e.c_id = ".$this->course_id." AND q.c_id = ".$this->course_id.") |
1107
|
|
|
WHERE e.exercice_id = ".intval($this->id); |
1108
|
|
|
$result = Database::query($sql); |
1109
|
|
|
|
1110
|
|
|
$count_question_orders = Database::num_rows($result); |
1111
|
|
|
|
1112
|
|
|
$sql = "SELECT DISTINCT e.question_id, e.question_order |
1113
|
|
|
FROM $TBL_EXERCISE_QUESTION e |
1114
|
|
|
INNER JOIN $TBL_QUESTIONS q |
1115
|
|
|
ON (e.question_id= q.id AND e.c_id = ".$this->course_id." AND q.c_id = ".$this->course_id.") |
1116
|
|
|
WHERE e.exercice_id = ".intval($this->id)." |
1117
|
|
|
ORDER BY question_order"; |
1118
|
|
|
$result = Database::query($sql); |
1119
|
|
|
|
1120
|
|
|
// fills the array with the question ID for this exercise |
1121
|
|
|
// the key of the array is the question position |
1122
|
|
|
$temp_question_list = array(); |
1123
|
|
|
$counter = 1; |
1124
|
|
|
$question_list = array(); |
1125
|
|
|
|
1126
|
|
|
while ($new_object = Database::fetch_object($result)) { |
1127
|
|
|
$question_list[$new_object->question_order]= $new_object->question_id; |
1128
|
|
|
$temp_question_list[$counter] = $new_object->question_id; |
1129
|
|
|
$counter++; |
1130
|
|
|
} |
1131
|
|
|
|
1132
|
|
|
if (!empty($temp_question_list)) { |
1133
|
|
|
if (count($temp_question_list) != $count_question_orders) { |
1134
|
|
|
$question_list = $temp_question_list; |
1135
|
|
|
} |
1136
|
|
|
} |
1137
|
|
|
|
1138
|
|
|
return $question_list; |
1139
|
|
|
} |
1140
|
|
|
|
1141
|
|
|
return $this->questionList; |
1142
|
|
|
} else { |
1143
|
|
|
|
1144
|
|
|
if ($from_db && !empty($this->id)) { |
1145
|
|
|
|
1146
|
|
|
$nbQuestions = $this->getQuestionCount(); |
1147
|
|
|
$questionSelectionType = $this->getQuestionSelectionType(); |
1148
|
|
|
|
1149
|
|
|
switch ($questionSelectionType) { |
1150
|
|
|
case EX_Q_SELECTION_ORDERED: |
1151
|
|
|
$questionList = $this->getQuestionOrderedList(); |
1152
|
|
|
break; |
1153
|
|
|
case EX_Q_SELECTION_RANDOM: |
1154
|
|
|
// Not a random exercise, or if there are not at least 2 questions |
1155
|
|
|
if ($this->random == 0 || $nbQuestions < 2) { |
1156
|
|
|
$questionList = $this->getQuestionOrderedList(); |
1157
|
|
|
} else { |
1158
|
|
|
$questionList = $this->selectRandomList(); |
1159
|
|
|
} |
1160
|
|
|
break; |
1161
|
|
|
default: |
1162
|
|
|
$questionList = $this->getQuestionOrderedList(); |
1163
|
|
|
$result = $this->getQuestionListWithCategoryListFilteredByCategorySettings( |
1164
|
|
|
$questionList, |
1165
|
|
|
$questionSelectionType |
1166
|
|
|
); |
1167
|
|
|
$this->categoryWithQuestionList = $result['category_with_questions_list']; |
1168
|
|
|
$questionList = $result['question_list']; |
1169
|
|
|
break; |
1170
|
|
|
} |
1171
|
|
|
|
1172
|
|
|
return $questionList; |
1173
|
|
|
} |
1174
|
|
|
return $this->questionList; |
1175
|
|
|
} |
1176
|
|
|
} |
1177
|
|
|
|
1178
|
|
|
/** |
1179
|
|
|
* returns the number of questions in this exercise |
1180
|
|
|
* |
1181
|
|
|
* @author Olivier Brouckaert |
1182
|
|
|
* @return integer - number of questions |
1183
|
|
|
*/ |
1184
|
|
|
public function selectNbrQuestions() |
1185
|
|
|
{ |
1186
|
|
|
return sizeof($this->questionList); |
1187
|
|
|
} |
1188
|
|
|
|
1189
|
|
|
/** |
1190
|
|
|
* @return int |
1191
|
|
|
*/ |
1192
|
|
|
public function selectPropagateNeg() |
1193
|
|
|
{ |
1194
|
|
|
return $this->propagate_neg; |
1195
|
|
|
} |
1196
|
|
|
|
1197
|
|
|
/** |
1198
|
|
|
* Selects questions randomly in the question list |
1199
|
|
|
* |
1200
|
|
|
* @author Olivier Brouckaert |
1201
|
|
|
* @author Hubert Borderiou 15 nov 2011 |
1202
|
|
|
* @return array - if the exercise is not set to take questions randomly, returns the question list |
1203
|
|
|
* without randomizing, otherwise, returns the list with questions selected randomly |
1204
|
|
|
*/ |
1205
|
|
|
public function selectRandomList() |
1206
|
|
|
{ |
1207
|
|
|
/*$nbQuestions = $this->selectNbrQuestions(); |
1208
|
|
|
$temp_list = $this->questionList; |
1209
|
|
|
|
1210
|
|
|
//Not a random exercise, or if there are not at least 2 questions |
1211
|
|
|
if($this->random == 0 || $nbQuestions < 2) { |
1212
|
|
|
return $this->questionList; |
1213
|
|
|
} |
1214
|
|
|
if ($nbQuestions != 0) { |
1215
|
|
|
shuffle($temp_list); |
1216
|
|
|
$my_random_list = array_combine(range(1,$nbQuestions),$temp_list); |
1217
|
|
|
$my_question_list = array(); |
1218
|
|
|
// $this->random == -1 if random with all questions |
1219
|
|
|
if ($this->random > 0) { |
1220
|
|
|
$i = 0; |
1221
|
|
|
foreach ($my_random_list as $item) { |
1222
|
|
|
if ($i < $this->random) { |
1223
|
|
|
$my_question_list[$i] = $item; |
1224
|
|
|
} else { |
1225
|
|
|
break; |
1226
|
|
|
} |
1227
|
|
|
$i++; |
1228
|
|
|
} |
1229
|
|
|
} else { |
1230
|
|
|
$my_question_list = $my_random_list; |
1231
|
|
|
} |
1232
|
|
|
return $my_question_list; |
1233
|
|
|
}*/ |
1234
|
|
|
|
1235
|
|
|
$TBL_EXERCISE_QUESTION = Database::get_course_table(TABLE_QUIZ_TEST_QUESTION); |
1236
|
|
|
$TBL_QUESTIONS = Database::get_course_table(TABLE_QUIZ_QUESTION); |
1237
|
|
|
|
1238
|
|
|
$random = isset($this->random) && !empty($this->random) ? $this->random : 0; |
1239
|
|
|
|
1240
|
|
|
$randomLimit = "LIMIT $random"; |
1241
|
|
|
// Random all questions so no limit |
1242
|
|
|
if ($random == -1) { |
1243
|
|
|
$randomLimit = null; |
1244
|
|
|
} |
1245
|
|
|
|
1246
|
|
|
// @todo improve this query |
1247
|
|
|
$sql = "SELECT e.question_id |
1248
|
|
|
FROM $TBL_EXERCISE_QUESTION e INNER JOIN $TBL_QUESTIONS q |
1249
|
|
|
ON (e.question_id= q.iid) |
1250
|
|
|
WHERE e.c_id = {$this->course_id} AND e.exercice_id = '".Database::escape_string($this->id)."' |
1251
|
|
|
ORDER BY RAND() |
1252
|
|
|
$randomLimit "; |
1253
|
|
|
$result = Database::query($sql); |
1254
|
|
|
$questionList = array(); |
1255
|
|
|
while ($row = Database::fetch_object($result)) { |
1256
|
|
|
$questionList[] = $row->question_id; |
1257
|
|
|
} |
1258
|
|
|
return $questionList; |
1259
|
|
|
} |
1260
|
|
|
|
1261
|
|
|
/** |
1262
|
|
|
* returns 'true' if the question ID is in the question list |
1263
|
|
|
* |
1264
|
|
|
* @author Olivier Brouckaert |
1265
|
|
|
* @param integer $questionId - question ID |
1266
|
|
|
* @return boolean - true if in the list, otherwise false |
1267
|
|
|
*/ |
1268
|
|
|
public function isInList($questionId) |
1269
|
|
|
{ |
1270
|
|
|
if (is_array($this->questionList)) |
1271
|
|
|
return in_array($questionId,$this->questionList); |
1272
|
|
|
else |
1273
|
|
|
return false; |
1274
|
|
|
} |
1275
|
|
|
|
1276
|
|
|
/** |
1277
|
|
|
* changes the exercise title |
1278
|
|
|
* |
1279
|
|
|
* @author Olivier Brouckaert |
1280
|
|
|
* @param string $title - exercise title |
1281
|
|
|
*/ |
1282
|
|
|
public function updateTitle($title) |
1283
|
|
|
{ |
1284
|
|
|
$this->exercise=$title; |
1285
|
|
|
} |
1286
|
|
|
|
1287
|
|
|
/** |
1288
|
|
|
* changes the exercise max attempts |
1289
|
|
|
* |
1290
|
|
|
* @param int $attempts - exercise max attempts |
1291
|
|
|
*/ |
1292
|
|
|
public function updateAttempts($attempts) |
1293
|
|
|
{ |
1294
|
|
|
$this->attempts=$attempts; |
1295
|
|
|
} |
1296
|
|
|
|
1297
|
|
|
/** |
1298
|
|
|
* changes the exercise feedback type |
1299
|
|
|
* |
1300
|
|
|
* @param int $feedback_type |
1301
|
|
|
*/ |
1302
|
|
|
public function updateFeedbackType($feedback_type) |
1303
|
|
|
{ |
1304
|
|
|
$this->feedback_type=$feedback_type; |
1305
|
|
|
} |
1306
|
|
|
|
1307
|
|
|
/** |
1308
|
|
|
* changes the exercise description |
1309
|
|
|
* |
1310
|
|
|
* @author Olivier Brouckaert |
1311
|
|
|
* @param string $description - exercise description |
1312
|
|
|
*/ |
1313
|
|
|
public function updateDescription($description) |
1314
|
|
|
{ |
1315
|
|
|
$this->description=$description; |
1316
|
|
|
} |
1317
|
|
|
|
1318
|
|
|
/** |
1319
|
|
|
* changes the exercise expired_time |
1320
|
|
|
* |
1321
|
|
|
* @author Isaac flores |
1322
|
|
|
* @param int $expired_time The expired time of the quiz |
1323
|
|
|
*/ |
1324
|
|
|
public function updateExpiredTime($expired_time) |
1325
|
|
|
{ |
1326
|
|
|
$this->expired_time = $expired_time; |
1327
|
|
|
} |
1328
|
|
|
|
1329
|
|
|
/** |
1330
|
|
|
* @param $value |
1331
|
|
|
*/ |
1332
|
|
|
public function updatePropagateNegative($value) |
1333
|
|
|
{ |
1334
|
|
|
$this->propagate_neg = $value; |
1335
|
|
|
} |
1336
|
|
|
|
1337
|
|
|
/** |
1338
|
|
|
* @param $value |
1339
|
|
|
*/ |
1340
|
|
|
public function updateReviewAnswers($value) |
1341
|
|
|
{ |
1342
|
|
|
$this->review_answers = isset($value) && $value ? true : false; |
1343
|
|
|
} |
1344
|
|
|
|
1345
|
|
|
/** |
1346
|
|
|
* @param $value |
1347
|
|
|
*/ |
1348
|
|
|
public function updatePassPercentage($value) |
1349
|
|
|
{ |
1350
|
|
|
$this->pass_percentage = $value; |
1351
|
|
|
} |
1352
|
|
|
|
1353
|
|
|
|
1354
|
|
|
/** |
1355
|
|
|
* @param string $text |
1356
|
|
|
*/ |
1357
|
|
|
public function updateEmailNotificationTemplate($text) |
1358
|
|
|
{ |
1359
|
|
|
$this->emailNotificationTemplate = $text; |
1360
|
|
|
} |
1361
|
|
|
|
1362
|
|
|
/** |
1363
|
|
|
* @param string $text |
1364
|
|
|
*/ |
1365
|
|
|
public function updateEmailNotificationTemplateToUser($text) |
1366
|
|
|
{ |
1367
|
|
|
$this->emailNotificationTemplateToUser = $text; |
1368
|
|
|
} |
1369
|
|
|
|
1370
|
|
|
/** |
1371
|
|
|
* @param string $value |
1372
|
|
|
*/ |
1373
|
|
|
public function setNotifyUserByEmail($value) |
1374
|
|
|
{ |
1375
|
|
|
$this->notifyUserByEmail = $value; |
1376
|
|
|
} |
1377
|
|
|
|
1378
|
|
|
|
1379
|
|
|
/** |
1380
|
|
|
* @param int $value |
1381
|
|
|
*/ |
1382
|
|
|
public function updateEndButton($value) |
1383
|
|
|
{ |
1384
|
|
|
$this->endButton = intval($value); |
1385
|
|
|
} |
1386
|
|
|
|
1387
|
|
|
/** |
1388
|
|
|
* @param string $value |
1389
|
|
|
*/ |
1390
|
|
|
public function setOnSuccessMessage($value) |
1391
|
|
|
{ |
1392
|
|
|
$this->onSuccessMessage = $value; |
1393
|
|
|
} |
1394
|
|
|
|
1395
|
|
|
/** |
1396
|
|
|
* @param string $value |
1397
|
|
|
*/ |
1398
|
|
|
public function setOnFailedMessage($value) |
1399
|
|
|
{ |
1400
|
|
|
$this->onFailedMessage = $value; |
1401
|
|
|
} |
1402
|
|
|
|
1403
|
|
|
/** |
1404
|
|
|
* @param $value |
1405
|
|
|
*/ |
1406
|
|
|
public function setModelType($value) |
1407
|
|
|
{ |
1408
|
|
|
$this->modelType = intval($value); |
1409
|
|
|
} |
1410
|
|
|
|
1411
|
|
|
/** |
1412
|
|
|
* @param intval $value |
1413
|
|
|
*/ |
1414
|
|
|
public function setQuestionSelectionType($value) |
1415
|
|
|
{ |
1416
|
|
|
$this->questionSelectionType = intval($value); |
1417
|
|
|
} |
1418
|
|
|
|
1419
|
|
|
/** |
1420
|
|
|
* @return int |
1421
|
|
|
*/ |
1422
|
|
|
public function getQuestionSelectionType() |
1423
|
|
|
{ |
1424
|
|
|
return $this->questionSelectionType; |
1425
|
|
|
} |
1426
|
|
|
|
1427
|
|
|
/** |
1428
|
|
|
* @param array $categories |
1429
|
|
|
*/ |
1430
|
|
|
public function updateCategories($categories) |
1431
|
|
|
{ |
1432
|
|
|
if (!empty($categories)) { |
1433
|
|
|
$categories = array_map('intval', $categories); |
1434
|
|
|
$this->categories = $categories; |
1435
|
|
|
} |
1436
|
|
|
} |
1437
|
|
|
|
1438
|
|
|
/** |
1439
|
|
|
* changes the exercise sound file |
1440
|
|
|
* |
1441
|
|
|
* @author Olivier Brouckaert |
1442
|
|
|
* @param string $sound - exercise sound file |
1443
|
|
|
* @param string $delete - ask to delete the file |
1444
|
|
|
*/ |
1445
|
|
|
public function updateSound($sound,$delete) |
1446
|
|
|
{ |
1447
|
|
|
global $audioPath, $documentPath; |
1448
|
|
|
$TBL_DOCUMENT = Database::get_course_table(TABLE_DOCUMENT); |
1449
|
|
|
|
1450
|
|
|
if ($sound['size'] && (strstr($sound['type'],'audio') || strstr($sound['type'],'video'))) { |
1451
|
|
|
$this->sound=$sound['name']; |
1452
|
|
|
|
1453
|
|
|
if (@move_uploaded_file($sound['tmp_name'],$audioPath.'/'.$this->sound)) { |
1454
|
|
|
$query = "SELECT 1 FROM $TBL_DOCUMENT |
1455
|
|
|
WHERE c_id = ".$this->course_id." AND path='".str_replace($documentPath,'',$audioPath).'/'.$this->sound."'"; |
1456
|
|
|
$result=Database::query($query); |
1457
|
|
|
|
1458
|
|
|
if (!Database::num_rows($result)) { |
1459
|
|
|
$id = add_document( |
1460
|
|
|
$this->course, |
1461
|
|
|
str_replace($documentPath,'',$audioPath).'/'.$this->sound, |
1462
|
|
|
'file', |
1463
|
|
|
$sound['size'], |
1464
|
|
|
$sound['name'] |
1465
|
|
|
); |
1466
|
|
|
api_item_property_update( |
1467
|
|
|
$this->course, |
1468
|
|
|
TOOL_DOCUMENT, |
1469
|
|
|
$id, |
1470
|
|
|
'DocumentAdded', |
1471
|
|
|
api_get_user_id() |
1472
|
|
|
); |
1473
|
|
|
item_property_update_on_folder( |
1474
|
|
|
$this->course, |
1475
|
|
|
str_replace($documentPath, '', $audioPath), |
1476
|
|
|
api_get_user_id() |
1477
|
|
|
); |
1478
|
|
|
} |
1479
|
|
|
} |
1480
|
|
|
} elseif($delete && is_file($audioPath.'/'.$this->sound)) { |
1481
|
|
|
$this->sound=''; |
1482
|
|
|
} |
1483
|
|
|
} |
1484
|
|
|
|
1485
|
|
|
/** |
1486
|
|
|
* changes the exercise type |
1487
|
|
|
* |
1488
|
|
|
* @author Olivier Brouckaert |
1489
|
|
|
* @param integer $type - exercise type |
1490
|
|
|
*/ |
1491
|
|
|
public function updateType($type) |
1492
|
|
|
{ |
1493
|
|
|
$this->type=$type; |
1494
|
|
|
} |
1495
|
|
|
|
1496
|
|
|
/** |
1497
|
|
|
* sets to 0 if questions are not selected randomly |
1498
|
|
|
* if questions are selected randomly, sets the draws |
1499
|
|
|
* |
1500
|
|
|
* @author Olivier Brouckaert |
1501
|
|
|
* @param integer $random - 0 if not random, otherwise the draws |
1502
|
|
|
*/ |
1503
|
|
|
public function setRandom($random) |
1504
|
|
|
{ |
1505
|
|
|
/*if ($random == 'all') { |
1506
|
|
|
$random = $this->selectNbrQuestions(); |
1507
|
|
|
}*/ |
1508
|
|
|
$this->random = $random; |
1509
|
|
|
} |
1510
|
|
|
|
1511
|
|
|
/** |
1512
|
|
|
* sets to 0 if answers are not selected randomly |
1513
|
|
|
* if answers are selected randomly |
1514
|
|
|
* @author Juan Carlos Rana |
1515
|
|
|
* @param integer $random_answers - random answers |
1516
|
|
|
*/ |
1517
|
|
|
public function updateRandomAnswers($random_answers) |
1518
|
|
|
{ |
1519
|
|
|
$this->random_answers = $random_answers; |
1520
|
|
|
} |
1521
|
|
|
|
1522
|
|
|
/** |
1523
|
|
|
* enables the exercise |
1524
|
|
|
* |
1525
|
|
|
* @author Olivier Brouckaert |
1526
|
|
|
*/ |
1527
|
|
|
public function enable() |
1528
|
|
|
{ |
1529
|
|
|
$this->active=1; |
1530
|
|
|
} |
1531
|
|
|
|
1532
|
|
|
/** |
1533
|
|
|
* disables the exercise |
1534
|
|
|
* |
1535
|
|
|
* @author Olivier Brouckaert |
1536
|
|
|
*/ |
1537
|
|
|
public function disable() |
1538
|
|
|
{ |
1539
|
|
|
$this->active=0; |
1540
|
|
|
} |
1541
|
|
|
|
1542
|
|
|
/** |
1543
|
|
|
* Set disable results |
1544
|
|
|
*/ |
1545
|
|
|
public function disable_results() |
1546
|
|
|
{ |
1547
|
|
|
$this->results_disabled = true; |
1548
|
|
|
} |
1549
|
|
|
|
1550
|
|
|
/** |
1551
|
|
|
* Enable results |
1552
|
|
|
*/ |
1553
|
|
|
public function enable_results() |
1554
|
|
|
{ |
1555
|
|
|
$this->results_disabled = false; |
1556
|
|
|
} |
1557
|
|
|
|
1558
|
|
|
/** |
1559
|
|
|
* @param int $results_disabled |
1560
|
|
|
*/ |
1561
|
|
|
public function updateResultsDisabled($results_disabled) |
1562
|
|
|
{ |
1563
|
|
|
$this->results_disabled = intval($results_disabled); |
1564
|
|
|
} |
1565
|
|
|
|
1566
|
|
|
/** |
1567
|
|
|
* updates the exercise in the data base |
1568
|
|
|
* |
1569
|
|
|
* @author Olivier Brouckaert |
1570
|
|
|
*/ |
1571
|
|
|
public function save($type_e = '') |
1572
|
|
|
{ |
1573
|
|
|
$_course = $this->course; |
1574
|
|
|
$TBL_EXERCISES = Database::get_course_table(TABLE_QUIZ_TEST); |
1575
|
|
|
|
1576
|
|
|
$id = $this->id; |
1577
|
|
|
$exercise = $this->exercise; |
1578
|
|
|
$description = $this->description; |
1579
|
|
|
$sound = $this->sound; |
1580
|
|
|
$type = $this->type; |
1581
|
|
|
$attempts = isset($this->attempts) ? $this->attempts : 0; |
1582
|
|
|
$feedback_type = isset($this->feedback_type) ? $this->feedback_type : 0; |
1583
|
|
|
$random = $this->random; |
1584
|
|
|
$random_answers = $this->random_answers; |
1585
|
|
|
$active = $this->active; |
1586
|
|
|
$propagate_neg = $this->propagate_neg; |
1587
|
|
|
$review_answers = isset($this->review_answers) && $this->review_answers ? 1 : 0; |
1588
|
|
|
$randomByCat = intval($this->randomByCat); |
1589
|
|
|
$text_when_finished = $this->text_when_finished; |
1590
|
|
|
$display_category_name = intval($this->display_category_name); |
1591
|
|
|
$pass_percentage = intval($this->pass_percentage); |
1592
|
|
|
$session_id = $this->sessionId; |
1593
|
|
|
|
1594
|
|
|
//If direct we do not show results |
1595
|
|
|
if ($feedback_type == EXERCISE_FEEDBACK_TYPE_DIRECT) { |
1596
|
|
|
$results_disabled = 0; |
1597
|
|
|
} else { |
1598
|
|
|
$results_disabled = intval($this->results_disabled); |
1599
|
|
|
} |
1600
|
|
|
|
1601
|
|
|
$expired_time = intval($this->expired_time); |
1602
|
|
|
|
1603
|
|
|
// Exercise already exists |
1604
|
|
|
if ($id) { |
1605
|
|
|
// we prepare date in the database using the api_get_utc_datetime() function |
1606
|
|
|
if (!empty($this->start_time) && $this->start_time != '0000-00-00 00:00:00') { |
1607
|
|
|
$start_time = $this->start_time; |
1608
|
|
|
} else { |
1609
|
|
|
$start_time = '0000-00-00 00:00:00'; |
1610
|
|
|
} |
1611
|
|
|
|
1612
|
|
|
if (!empty($this->end_time) && $this->end_time != '0000-00-00 00:00:00') { |
1613
|
|
|
$end_time = $this->end_time; |
1614
|
|
|
} else { |
1615
|
|
|
$end_time = '0000-00-00 00:00:00'; |
1616
|
|
|
} |
1617
|
|
|
|
1618
|
|
|
$params = [ |
1619
|
|
|
'title' => $exercise, |
1620
|
|
|
'description' => $description, |
1621
|
|
|
]; |
1622
|
|
|
|
1623
|
|
|
$paramsExtra = []; |
1624
|
|
|
if ($type_e != 'simple') { |
1625
|
|
|
$paramsExtra = [ |
1626
|
|
|
'sound' => $sound, |
1627
|
|
|
'type' => $type, |
1628
|
|
|
'random' => $random, |
1629
|
|
|
'random_answers' => $random_answers, |
1630
|
|
|
'active' => $active, |
1631
|
|
|
'feedback_type' => $feedback_type, |
1632
|
|
|
'start_time' => $start_time, |
1633
|
|
|
'end_time' => $end_time, |
1634
|
|
|
'max_attempt' => $attempts, |
1635
|
|
|
'expired_time' => $expired_time, |
1636
|
|
|
'propagate_neg' => $propagate_neg, |
1637
|
|
|
'review_answers' => $review_answers, |
1638
|
|
|
'random_by_category' => $randomByCat, |
1639
|
|
|
'text_when_finished' => $text_when_finished, |
1640
|
|
|
'display_category_name' => $display_category_name, |
1641
|
|
|
'pass_percentage' => $pass_percentage, |
1642
|
|
|
'results_disabled' => $results_disabled, |
1643
|
|
|
]; |
1644
|
|
|
} |
1645
|
|
|
|
1646
|
|
|
$params = array_merge($params, $paramsExtra); |
1647
|
|
|
|
1648
|
|
|
if ($this->specialCategoryOrders) { |
1649
|
|
|
$params['question_selection_type'] = intval($this->getQuestionSelectionType()); |
1650
|
|
|
} |
1651
|
|
|
|
1652
|
|
|
Database::update( |
1653
|
|
|
$TBL_EXERCISES, |
1654
|
|
|
$params, |
1655
|
|
|
['c_id = ? AND id = ?' => [$this->course_id, $id]] |
1656
|
|
|
); |
1657
|
|
|
|
1658
|
|
|
// update into the item_property table |
1659
|
|
|
api_item_property_update( |
1660
|
|
|
$_course, |
1661
|
|
|
TOOL_QUIZ, |
1662
|
|
|
$id, |
1663
|
|
|
'QuizUpdated', |
1664
|
|
|
api_get_user_id() |
1665
|
|
|
); |
1666
|
|
|
|
1667
|
|
|
if (api_get_setting('search_enabled')=='true') { |
1668
|
|
|
$this->search_engine_edit(); |
1669
|
|
|
} |
1670
|
|
|
} else { |
1671
|
|
|
// Creates a new exercise |
1672
|
|
|
|
1673
|
|
|
// In this case of new exercise, we don't do the api_get_utc_datetime() |
1674
|
|
|
// for date because, bellow, we call function api_set_default_visibility() |
1675
|
|
|
// In this function, api_set_default_visibility, |
1676
|
|
|
// the Quiz is saved too, with an $id and api_get_utc_datetime() is done. |
1677
|
|
|
// If we do it now, it will be done twice (cf. https://support.chamilo.org/issues/6586) |
1678
|
|
|
if (!empty($this->start_time) && $this->start_time != '0000-00-00 00:00:00') { |
1679
|
|
|
$start_time = $this->start_time; |
1680
|
|
|
} else { |
1681
|
|
|
$start_time = '0000-00-00 00:00:00'; |
1682
|
|
|
} |
1683
|
|
|
|
1684
|
|
|
if (!empty($this->end_time) && $this->end_time != '0000-00-00 00:00:00') { |
1685
|
|
|
$end_time = $this->end_time; |
1686
|
|
|
} else { |
1687
|
|
|
$end_time = '0000-00-00 00:00:00'; |
1688
|
|
|
} |
1689
|
|
|
|
1690
|
|
|
$params = [ |
1691
|
|
|
'c_id' => $this->course_id, |
1692
|
|
|
'start_time' => $start_time, |
1693
|
|
|
'end_time' => $end_time, |
1694
|
|
|
'title' => $exercise, |
1695
|
|
|
'description' => $description, |
1696
|
|
|
'sound' => $sound, |
1697
|
|
|
'type' => $type, |
1698
|
|
|
'random' => $random, |
1699
|
|
|
'random_answers' => $random_answers, |
1700
|
|
|
'active' => $active, |
1701
|
|
|
'results_disabled' => $results_disabled, |
1702
|
|
|
'max_attempt' => $attempts, |
1703
|
|
|
'feedback_type' => $feedback_type, |
1704
|
|
|
'expired_time' => $expired_time, |
1705
|
|
|
'session_id' => $session_id, |
1706
|
|
|
'review_answers' => $review_answers, |
1707
|
|
|
'random_by_category' => $randomByCat, |
1708
|
|
|
'text_when_finished' => $text_when_finished, |
1709
|
|
|
'display_category_name' => $display_category_name, |
1710
|
|
|
'pass_percentage' => $pass_percentage |
1711
|
|
|
]; |
1712
|
|
|
|
1713
|
|
|
$this->id = Database::insert($TBL_EXERCISES, $params); |
1714
|
|
|
|
1715
|
|
|
if ($this->id) { |
1716
|
|
|
|
1717
|
|
|
$sql = "UPDATE $TBL_EXERCISES SET id = iid WHERE iid = {$this->id} "; |
1718
|
|
|
Database::query($sql); |
1719
|
|
|
|
1720
|
|
|
if ($this->specialCategoryOrders) { |
1721
|
|
|
$sql = "UPDATE $TBL_EXERCISES |
1722
|
|
|
SET question_selection_type= ".intval($this->getQuestionSelectionType())." |
1723
|
|
|
WHERE id = ".$this->id." AND c_id = ".$this->course_id; |
1724
|
|
|
Database::query($sql); |
1725
|
|
|
} |
1726
|
|
|
|
1727
|
|
|
// insert into the item_property table |
1728
|
|
|
api_item_property_update( |
1729
|
|
|
$this->course, |
1730
|
|
|
TOOL_QUIZ, |
1731
|
|
|
$this->id, |
1732
|
|
|
'QuizAdded', |
1733
|
|
|
api_get_user_id() |
1734
|
|
|
); |
1735
|
|
|
|
1736
|
|
|
// This function save the quiz again, carefull about start_time |
1737
|
|
|
// and end_time if you remove this line (see above) |
1738
|
|
|
api_set_default_visibility( |
1739
|
|
|
$this->id, |
1740
|
|
|
TOOL_QUIZ, |
1741
|
|
|
null, |
1742
|
|
|
$this->course |
1743
|
|
|
); |
1744
|
|
|
|
1745
|
|
|
if (api_get_setting('search_enabled') == 'true' && extension_loaded('xapian')) { |
1746
|
|
|
$this->search_engine_save(); |
1747
|
|
|
} |
1748
|
|
|
} |
1749
|
|
|
} |
1750
|
|
|
|
1751
|
|
|
$this->save_categories_in_exercise($this->categories); |
1752
|
|
|
|
1753
|
|
|
// Updates the question position |
1754
|
|
|
$this->update_question_positions(); |
1755
|
|
|
} |
1756
|
|
|
|
1757
|
|
|
/** |
1758
|
|
|
* Updates question position |
1759
|
|
|
*/ |
1760
|
|
|
public function update_question_positions() |
1761
|
|
|
{ |
1762
|
|
|
$quiz_question_table = Database::get_course_table(TABLE_QUIZ_TEST_QUESTION); |
1763
|
|
|
//Fixes #3483 when updating order |
1764
|
|
|
$question_list = $this->selectQuestionList(true); |
1765
|
|
|
if (!empty($question_list)) { |
1766
|
|
|
foreach ($question_list as $position => $questionId) { |
1767
|
|
|
$sql = "UPDATE $quiz_question_table SET |
1768
|
|
|
question_order ='".intval($position)."' |
1769
|
|
|
WHERE |
1770
|
|
|
c_id = ".$this->course_id." AND |
1771
|
|
|
question_id = ".intval($questionId)." AND |
1772
|
|
|
exercice_id=".intval($this->id); |
1773
|
|
|
Database::query($sql); |
1774
|
|
|
} |
1775
|
|
|
} |
1776
|
|
|
} |
1777
|
|
|
|
1778
|
|
|
/** |
1779
|
|
|
* Adds a question into the question list |
1780
|
|
|
* |
1781
|
|
|
* @author Olivier Brouckaert |
1782
|
|
|
* @param integer $questionId - question ID |
1783
|
|
|
* @return boolean - true if the question has been added, otherwise false |
1784
|
|
|
*/ |
1785
|
|
|
public function addToList($questionId) |
1786
|
|
|
{ |
1787
|
|
|
// checks if the question ID is not in the list |
1788
|
|
|
if (!$this->isInList($questionId)) { |
1789
|
|
|
// selects the max position |
1790
|
|
|
if (!$this->selectNbrQuestions()) { |
1791
|
|
|
$pos = 1; |
1792
|
|
|
} else { |
1793
|
|
|
if (is_array($this->questionList)) { |
1794
|
|
|
$pos = max(array_keys($this->questionList)) + 1; |
1795
|
|
|
} |
1796
|
|
|
} |
1797
|
|
|
$this->questionList[$pos] = $questionId; |
1798
|
|
|
|
1799
|
|
|
return true; |
1800
|
|
|
} |
1801
|
|
|
|
1802
|
|
|
return false; |
1803
|
|
|
} |
1804
|
|
|
|
1805
|
|
|
/** |
1806
|
|
|
* removes a question from the question list |
1807
|
|
|
* |
1808
|
|
|
* @author Olivier Brouckaert |
1809
|
|
|
* @param integer $questionId - question ID |
1810
|
|
|
* @return boolean - true if the question has been removed, otherwise false |
1811
|
|
|
*/ |
1812
|
|
|
public function removeFromList($questionId) |
1813
|
|
|
{ |
1814
|
|
|
// searches the position of the question ID in the list |
1815
|
|
|
$pos = array_search($questionId,$this->questionList); |
1816
|
|
|
|
1817
|
|
|
// question not found |
1818
|
|
|
if ($pos === false) { |
1819
|
|
|
return false; |
1820
|
|
|
} else { |
1821
|
|
|
// dont reduce the number of random question if we use random by category option, or if |
1822
|
|
|
// random all questions |
1823
|
|
|
if ($this->isRandom() && $this->isRandomByCat() == 0) { |
1824
|
|
|
if (count($this->questionList) >= $this->random && $this->random > 0) { |
1825
|
|
|
$this->random -= 1; |
1826
|
|
|
$this->save(); |
1827
|
|
|
} |
1828
|
|
|
} |
1829
|
|
|
// deletes the position from the array containing the wanted question ID |
1830
|
|
|
unset($this->questionList[$pos]); |
1831
|
|
|
return true; |
1832
|
|
|
} |
1833
|
|
|
} |
1834
|
|
|
|
1835
|
|
|
/** |
1836
|
|
|
* deletes the exercise from the database |
1837
|
|
|
* Notice : leaves the question in the data base |
1838
|
|
|
* |
1839
|
|
|
* @author Olivier Brouckaert |
1840
|
|
|
*/ |
1841
|
|
|
public function delete() |
1842
|
|
|
{ |
1843
|
|
|
$TBL_EXERCISES = Database::get_course_table(TABLE_QUIZ_TEST); |
1844
|
|
|
$sql = "UPDATE $TBL_EXERCISES SET active='-1' |
1845
|
|
|
WHERE c_id = ".$this->course_id." AND id = ".intval($this->id).""; |
1846
|
|
|
Database::query($sql); |
1847
|
|
|
api_item_property_update($this->course, TOOL_QUIZ, $this->id, 'QuizDeleted', api_get_user_id()); |
1848
|
|
|
api_item_property_update($this->course, TOOL_QUIZ, $this->id, 'delete', api_get_user_id()); |
1849
|
|
|
|
1850
|
|
|
if (api_get_setting('search_enabled')=='true' && extension_loaded('xapian') ) { |
1851
|
|
|
$this->search_engine_delete(); |
1852
|
|
|
} |
1853
|
|
|
} |
1854
|
|
|
|
1855
|
|
|
/** |
1856
|
|
|
* Creates the form to create / edit an exercise |
1857
|
|
|
* @param FormValidator $form |
1858
|
|
|
*/ |
1859
|
|
|
public function createForm($form, $type='full') |
1860
|
|
|
{ |
1861
|
|
|
if (empty($type)) { |
1862
|
|
|
$type = 'full'; |
1863
|
|
|
} |
1864
|
|
|
|
1865
|
|
|
// form title |
1866
|
|
|
if (!empty($_GET['exerciseId'])) { |
1867
|
|
|
$form_title = get_lang('ModifyExercise'); |
1868
|
|
|
} else { |
1869
|
|
|
$form_title = get_lang('NewEx'); |
1870
|
|
|
} |
1871
|
|
|
|
1872
|
|
|
$form->addElement('header', $form_title); |
1873
|
|
|
|
1874
|
|
|
// Title. |
1875
|
|
|
$form->addElement( |
1876
|
|
|
'text', |
1877
|
|
|
'exerciseTitle', |
1878
|
|
|
get_lang('ExerciseName'), |
1879
|
|
|
array('id' => 'exercise_title') |
1880
|
|
|
); |
1881
|
|
|
|
1882
|
|
|
$form->addElement('advanced_settings', 'advanced_params', get_lang('AdvancedParameters')); |
1883
|
|
|
$form->addElement('html', '<div id="advanced_params_options" style="display:none">'); |
1884
|
|
|
|
1885
|
|
|
$editor_config = array( |
1886
|
|
|
'ToolbarSet' => 'TestQuestionDescription', |
1887
|
|
|
'Width' => '100%', |
1888
|
|
|
'Height' => '150', |
1889
|
|
|
); |
1890
|
|
|
if (is_array($type)){ |
1891
|
|
|
$editor_config = array_merge($editor_config, $type); |
1892
|
|
|
} |
1893
|
|
|
|
1894
|
|
|
$form->addHtmlEditor( |
1895
|
|
|
'exerciseDescription', |
1896
|
|
|
get_lang('ExerciseDescription'), |
1897
|
|
|
false, |
1898
|
|
|
false, |
1899
|
|
|
$editor_config |
1900
|
|
|
); |
1901
|
|
|
|
1902
|
|
|
if ($type == 'full') { |
1903
|
|
|
//Can't modify a DirectFeedback question |
1904
|
|
|
if ($this->selectFeedbackType() != EXERCISE_FEEDBACK_TYPE_DIRECT) { |
1905
|
|
|
// feedback type |
1906
|
|
|
$radios_feedback = array(); |
1907
|
|
|
$radios_feedback[] = $form->createElement( |
1908
|
|
|
'radio', |
1909
|
|
|
'exerciseFeedbackType', |
1910
|
|
|
null, |
1911
|
|
|
get_lang('ExerciseAtTheEndOfTheTest'), |
1912
|
|
|
'0', |
1913
|
|
|
array( |
1914
|
|
|
'id' => 'exerciseType_0', |
1915
|
|
|
'onclick' => 'check_feedback()', |
1916
|
|
|
) |
1917
|
|
|
); |
1918
|
|
|
|
1919
|
|
|
if (api_get_setting('enable_quiz_scenario') == 'true') { |
1920
|
|
|
//Can't convert a question from one feedback to another if there is more than 1 question already added |
1921
|
|
|
if ($this->selectNbrQuestions() == 0) { |
1922
|
|
|
$radios_feedback[] = $form->createElement( |
1923
|
|
|
'radio', |
1924
|
|
|
'exerciseFeedbackType', |
1925
|
|
|
null, |
1926
|
|
|
get_lang('DirectFeedback'), |
1927
|
|
|
'1', |
1928
|
|
|
array( |
1929
|
|
|
'id' => 'exerciseType_1', |
1930
|
|
|
'onclick' => 'check_direct_feedback()', |
1931
|
|
|
) |
1932
|
|
|
); |
1933
|
|
|
} |
1934
|
|
|
} |
1935
|
|
|
|
1936
|
|
|
$radios_feedback[] = $form->createElement( |
1937
|
|
|
'radio', |
1938
|
|
|
'exerciseFeedbackType', |
1939
|
|
|
null, |
1940
|
|
|
get_lang('NoFeedback'), |
1941
|
|
|
'2', |
1942
|
|
|
array('id' => 'exerciseType_2') |
1943
|
|
|
); |
1944
|
|
|
$form->addGroup($radios_feedback, null, array(get_lang('FeedbackType'),get_lang('FeedbackDisplayOptions')), ''); |
1945
|
|
|
|
1946
|
|
|
// Type of results display on the final page |
1947
|
|
|
$radios_results_disabled = array(); |
1948
|
|
|
$radios_results_disabled[] = $form->createElement( |
1949
|
|
|
'radio', |
1950
|
|
|
'results_disabled', |
1951
|
|
|
null, |
1952
|
|
|
get_lang('ShowScoreAndRightAnswer'), |
1953
|
|
|
'0', |
1954
|
|
|
array('id' => 'result_disabled_0') |
1955
|
|
|
); |
1956
|
|
|
$radios_results_disabled[] = $form->createElement( |
1957
|
|
|
'radio', |
1958
|
|
|
'results_disabled', |
1959
|
|
|
null, |
1960
|
|
|
get_lang('DoNotShowScoreNorRightAnswer'), |
1961
|
|
|
'1', |
1962
|
|
|
array('id' => 'result_disabled_1', 'onclick' => 'check_results_disabled()') |
1963
|
|
|
); |
1964
|
|
|
$radios_results_disabled[] = $form->createElement( |
1965
|
|
|
'radio', |
1966
|
|
|
'results_disabled', |
1967
|
|
|
null, |
1968
|
|
|
get_lang('OnlyShowScore'), |
1969
|
|
|
'2', |
1970
|
|
|
array('id' => 'result_disabled_2') |
1971
|
|
|
); |
1972
|
|
|
//$radios_results_disabled[] = $form->createElement('radio', 'results_disabled', null, get_lang('ExamModeWithFinalScoreShowOnlyFinalScoreWithCategoriesIfAvailable'), '3', array('id'=>'result_disabled_3','onclick' => 'check_results_disabled()')); |
1973
|
|
|
|
1974
|
|
|
$radios_results_disabled[] = $form->createElement( |
1975
|
|
|
'radio', |
1976
|
|
|
'results_disabled', |
1977
|
|
|
null, |
1978
|
|
|
get_lang('ShowScoreEveryAttemptShowAnswersLastAttempt'), |
1979
|
|
|
'4', |
1980
|
|
|
array('id' => 'result_disabled_4') |
1981
|
|
|
); |
1982
|
|
|
|
1983
|
|
|
$form->addGroup($radios_results_disabled, null, get_lang('ShowResultsToStudents'), ''); |
1984
|
|
|
|
1985
|
|
|
// Type of questions disposition on page |
1986
|
|
|
$radios = array(); |
1987
|
|
|
|
1988
|
|
|
$radios[] = $form->createElement('radio', 'exerciseType', null, get_lang('SimpleExercise'), '1', array('onclick' => 'check_per_page_all()', 'id'=>'option_page_all')); |
1989
|
|
|
$radios[] = $form->createElement('radio', 'exerciseType', null, get_lang('SequentialExercise'),'2', array('onclick' => 'check_per_page_one()', 'id'=>'option_page_one')); |
1990
|
|
|
|
1991
|
|
|
$form->addGroup($radios, null, get_lang('QuestionsPerPage'), ''); |
1992
|
|
|
|
1993
|
|
|
} else { |
1994
|
|
|
// if is Directfeedback but has not questions we can allow to modify the question type |
1995
|
|
|
if ($this->selectNbrQuestions() == 0) { |
1996
|
|
|
|
1997
|
|
|
// feedback type |
1998
|
|
|
$radios_feedback = array(); |
1999
|
|
|
$radios_feedback[] = $form->createElement('radio', 'exerciseFeedbackType', null, get_lang('ExerciseAtTheEndOfTheTest'),'0',array('id' =>'exerciseType_0', 'onclick' => 'check_feedback()')); |
2000
|
|
|
|
2001
|
|
|
if (api_get_setting('enable_quiz_scenario') == 'true') { |
2002
|
|
|
$radios_feedback[] = $form->createElement('radio', 'exerciseFeedbackType', null, get_lang('DirectFeedback'), '1', array('id' =>'exerciseType_1' , 'onclick' => 'check_direct_feedback()')); |
2003
|
|
|
} |
2004
|
|
|
$radios_feedback[] = $form->createElement('radio', 'exerciseFeedbackType', null, get_lang('NoFeedback'),'2',array('id' =>'exerciseType_2')); |
2005
|
|
|
$form->addGroup($radios_feedback, null, array(get_lang('FeedbackType'),get_lang('FeedbackDisplayOptions'))); |
2006
|
|
|
|
2007
|
|
|
//$form->addElement('select', 'exerciseFeedbackType',get_lang('FeedbackType'),$feedback_option,'onchange="javascript:feedbackselection()"'); |
2008
|
|
|
$radios_results_disabled = array(); |
2009
|
|
|
$radios_results_disabled[] = $form->createElement('radio', 'results_disabled', null, get_lang('ShowScoreAndRightAnswer'), '0', array('id'=>'result_disabled_0')); |
2010
|
|
|
$radios_results_disabled[] = $form->createElement('radio', 'results_disabled', null, get_lang('DoNotShowScoreNorRightAnswer'), '1',array('id'=>'result_disabled_1','onclick' => 'check_results_disabled()')); |
2011
|
|
|
$radios_results_disabled[] = $form->createElement('radio', 'results_disabled', null, get_lang('OnlyShowScore'), '2',array('id'=>'result_disabled_2','onclick' => 'check_results_disabled()')); |
2012
|
|
|
$form->addGroup($radios_results_disabled, null, get_lang('ShowResultsToStudents'),''); |
2013
|
|
|
|
2014
|
|
|
// Type of questions disposition on page |
2015
|
|
|
$radios = array(); |
2016
|
|
|
$radios[] = $form->createElement('radio', 'exerciseType', null, get_lang('SimpleExercise'), '1'); |
2017
|
|
|
$radios[] = $form->createElement('radio', 'exerciseType', null, get_lang('SequentialExercise'),'2'); |
2018
|
|
|
$form->addGroup($radios, null, get_lang('ExerciseType')); |
2019
|
|
|
|
2020
|
|
|
} else { |
2021
|
|
|
//Show options freeze |
2022
|
|
|
$radios_results_disabled[] = $form->createElement('radio', 'results_disabled', null, get_lang('ShowScoreAndRightAnswer'), '0', array('id'=>'result_disabled_0')); |
2023
|
|
|
$radios_results_disabled[] = $form->createElement('radio', 'results_disabled', null, get_lang('DoNotShowScoreNorRightAnswer'), '1',array('id'=>'result_disabled_1','onclick' => 'check_results_disabled()')); |
2024
|
|
|
$radios_results_disabled[] = $form->createElement('radio', 'results_disabled', null, get_lang('OnlyShowScore'), '2',array('id'=>'result_disabled_2','onclick' => 'check_results_disabled()')); |
2025
|
|
|
$result_disable_group = $form->addGroup($radios_results_disabled, null, get_lang('ShowResultsToStudents'),''); |
2026
|
|
|
$result_disable_group->freeze(); |
2027
|
|
|
|
2028
|
|
|
//we force the options to the DirectFeedback exercisetype |
2029
|
|
|
$form->addElement('hidden', 'exerciseFeedbackType', EXERCISE_FEEDBACK_TYPE_DIRECT); |
2030
|
|
|
$form->addElement('hidden', 'exerciseType', ONE_PER_PAGE); |
2031
|
|
|
|
2032
|
|
|
// Type of questions disposition on page |
2033
|
|
|
$radios[] = $form->createElement('radio', 'exerciseType', null, get_lang('SimpleExercise'), '1', array('onclick' => 'check_per_page_all()', 'id'=>'option_page_all')); |
2034
|
|
|
$radios[] = $form->createElement('radio', 'exerciseType', null, get_lang('SequentialExercise'),'2', array('onclick' => 'check_per_page_one()', 'id'=>'option_page_one')); |
2035
|
|
|
|
2036
|
|
|
$type_group = $form->addGroup($radios, null, get_lang('QuestionsPerPage'), ''); |
2037
|
|
|
$type_group->freeze(); |
2038
|
|
|
} |
2039
|
|
|
} |
2040
|
|
|
|
2041
|
|
|
if ($this->specialCategoryOrders) { |
2042
|
|
|
$option = array( |
2043
|
|
|
EX_Q_SELECTION_ORDERED => get_lang('OrderedByUser'), |
2044
|
|
|
// defined by user |
2045
|
|
|
EX_Q_SELECTION_RANDOM => get_lang('Random'), |
2046
|
|
|
// 1-10, All |
2047
|
|
|
'per_categories' => '--------'.get_lang( |
2048
|
|
|
'UsingCategories' |
2049
|
|
|
).'----------', |
2050
|
|
|
|
2051
|
|
|
// Base (A 123 {3} B 456 {3} C 789{2} D 0{0}) --> Matrix {3, 3, 2, 0} |
2052
|
|
|
EX_Q_SELECTION_CATEGORIES_ORDERED_QUESTIONS_ORDERED => get_lang( |
2053
|
|
|
'OrderedCategoriesAlphabeticallyWithQuestionsOrdered' |
2054
|
|
|
), |
2055
|
|
|
// A 123 B 456 C 78 (0, 1, all) |
2056
|
|
|
EX_Q_SELECTION_CATEGORIES_RANDOM_QUESTIONS_ORDERED => get_lang( |
2057
|
|
|
'RandomCategoriesWithQuestionsOrdered' |
2058
|
|
|
), |
2059
|
|
|
// C 78 B 456 A 123 |
2060
|
|
|
|
2061
|
|
|
EX_Q_SELECTION_CATEGORIES_ORDERED_QUESTIONS_RANDOM => get_lang( |
2062
|
|
|
'OrderedCategoriesAlphabeticallyWithRandomQuestions' |
2063
|
|
|
), |
2064
|
|
|
// A 321 B 654 C 87 |
2065
|
|
|
EX_Q_SELECTION_CATEGORIES_RANDOM_QUESTIONS_RANDOM => get_lang( |
2066
|
|
|
'RandomCategoriesWithRandomQuestions' |
2067
|
|
|
), |
2068
|
|
|
//C 87 B 654 A 321 |
2069
|
|
|
|
2070
|
|
|
//EX_Q_SELECTION_CATEGORIES_RANDOM_QUESTIONS_ORDERED_NO_GROUPED => get_lang('RandomCategoriesWithQuestionsOrderedNoQuestionGrouped'), |
2071
|
|
|
/* B 456 C 78 A 123 |
2072
|
|
|
456 78 123 |
2073
|
|
|
123 456 78 |
2074
|
|
|
*/ |
2075
|
|
|
//EX_Q_SELECTION_CATEGORIES_RANDOM_QUESTIONS_RANDOM_NO_GROUPED => get_lang('RandomCategoriesWithRandomQuestionsNoQuestionGrouped'), |
2076
|
|
|
/* |
2077
|
|
|
A 123 B 456 C 78 |
2078
|
|
|
B 456 C 78 A 123 |
2079
|
|
|
B 654 C 87 A 321 |
2080
|
|
|
654 87 321 |
2081
|
|
|
165 842 73 |
2082
|
|
|
*/ |
2083
|
|
|
//EX_Q_SELECTION_CATEGORIES_ORDERED_BY_PARENT_QUESTIONS_ORDERED => get_lang('OrderedCategoriesByParentWithQuestionsOrdered'), |
2084
|
|
|
//EX_Q_SELECTION_CATEGORIES_ORDERED_BY_PARENT_QUESTIONS_RANDOM => get_lang('OrderedCategoriesByParentWithQuestionsRandom'), |
2085
|
|
|
); |
2086
|
|
|
|
2087
|
|
|
$form->addElement( |
2088
|
|
|
'select', |
2089
|
|
|
'question_selection_type', |
2090
|
|
|
array(get_lang('QuestionSelection')), |
2091
|
|
|
$option, |
2092
|
|
|
array( |
2093
|
|
|
'id' => 'questionSelection', |
2094
|
|
|
'onclick' => 'checkQuestionSelection()' |
2095
|
|
|
) |
2096
|
|
|
); |
2097
|
|
|
|
2098
|
|
|
$displayMatrix = 'none'; |
2099
|
|
|
$displayRandom = 'none'; |
2100
|
|
|
$selectionType = $this->getQuestionSelectionType(); |
2101
|
|
|
switch ($selectionType) { |
2102
|
|
|
case EX_Q_SELECTION_RANDOM: |
2103
|
|
|
$displayRandom = 'block'; |
2104
|
|
|
break; |
2105
|
|
|
case $selectionType >= EX_Q_SELECTION_CATEGORIES_ORDERED_QUESTIONS_ORDERED: |
2106
|
|
|
$displayMatrix = 'block'; |
2107
|
|
|
break; |
2108
|
|
|
} |
2109
|
|
|
|
2110
|
|
|
$form->addElement( |
2111
|
|
|
'html', |
2112
|
|
|
'<div id="hidden_random" style="display:'.$displayRandom.'">' |
2113
|
|
|
); |
2114
|
|
|
// Number of random question. |
2115
|
|
|
$max = ($this->id > 0) ? $this->selectNbrQuestions() : 10; |
2116
|
|
|
$option = range(0, $max); |
2117
|
|
|
$option[0] = get_lang('No'); |
2118
|
|
|
$option[-1] = get_lang('AllQuestionsShort'); |
2119
|
|
|
$form->addElement( |
2120
|
|
|
'select', |
2121
|
|
|
'randomQuestions', |
2122
|
|
|
array( |
2123
|
|
|
get_lang('RandomQuestions'), |
2124
|
|
|
get_lang('RandomQuestionsHelp') |
2125
|
|
|
), |
2126
|
|
|
$option, |
2127
|
|
|
array('id' => 'randomQuestions') |
2128
|
|
|
); |
2129
|
|
|
$form->addElement('html', '</div>'); |
2130
|
|
|
|
2131
|
|
|
$form->addElement( |
2132
|
|
|
'html', |
2133
|
|
|
'<div id="hidden_matrix" style="display:'.$displayMatrix.'">' |
2134
|
|
|
); |
2135
|
|
|
|
2136
|
|
|
// Category selection. |
2137
|
|
|
$cat = new TestCategory(); |
2138
|
|
|
$cat_form = $cat->returnCategoryForm($this); |
2139
|
|
|
$form->addElement('label', null, $cat_form); |
2140
|
|
|
$form->addElement('html', '</div>'); |
2141
|
|
|
|
2142
|
|
|
// Category name. |
2143
|
|
|
$radio_display_cat_name = array( |
2144
|
|
|
$form->createElement('radio', 'display_category_name', null, get_lang('Yes'), '1'), |
2145
|
|
|
$form->createElement('radio', 'display_category_name', null, get_lang('No'), '0') |
2146
|
|
|
); |
2147
|
|
|
$form->addGroup($radio_display_cat_name, null, get_lang('QuestionDisplayCategoryName'), ''); |
2148
|
|
|
|
2149
|
|
|
// Random answers. |
2150
|
|
|
$radios_random_answers = array( |
2151
|
|
|
$form->createElement('radio', 'randomAnswers', null, get_lang('Yes'), '1'), |
2152
|
|
|
$form->createElement('radio', 'randomAnswers', null, get_lang('No'), '0') |
2153
|
|
|
); |
2154
|
|
|
$form->addGroup($radios_random_answers, null, get_lang('RandomAnswers'), ''); |
2155
|
|
|
|
2156
|
|
|
// Hide question title. |
2157
|
|
|
$group = array( |
2158
|
|
|
$form->createElement('radio', 'hide_question_title', null, get_lang('Yes'), '1'), |
2159
|
|
|
$form->createElement('radio', 'hide_question_title', null, get_lang('No'), '0') |
2160
|
|
|
); |
2161
|
|
|
$form->addGroup($group, null, get_lang('HideQuestionTitle'), ''); |
2162
|
|
|
} else { |
2163
|
|
|
|
2164
|
|
|
// number of random question |
2165
|
|
|
|
2166
|
|
|
$max = ($this->id > 0) ? $this->selectNbrQuestions() : 10 ; |
2167
|
|
|
$option = range(0, $max); |
2168
|
|
|
$option[0] = get_lang('No'); |
2169
|
|
|
$option[-1] = get_lang('AllQuestionsShort'); |
2170
|
|
|
$form->addElement('select', 'randomQuestions',array(get_lang('RandomQuestions'), get_lang('RandomQuestionsHelp')), $option, array('id'=>'randomQuestions')); |
2171
|
|
|
|
2172
|
|
|
// Random answers |
2173
|
|
|
$radios_random_answers = array(); |
2174
|
|
|
$radios_random_answers[] = $form->createElement('radio', 'randomAnswers', null, get_lang('Yes'),'1'); |
2175
|
|
|
$radios_random_answers[] = $form->createElement('radio', 'randomAnswers', null, get_lang('No'),'0'); |
2176
|
|
|
$form->addGroup($radios_random_answers, null, get_lang('RandomAnswers'), ''); |
2177
|
|
|
|
2178
|
|
|
// Random by category |
2179
|
|
|
$form->addElement('html','<div class="clear"> </div>'); |
2180
|
|
|
$radiocat = array(); |
2181
|
|
|
$radiocat[] = $form->createElement('radio', 'randomByCat', null, get_lang('YesWithCategoriesShuffled'),'1'); |
2182
|
|
|
$radiocat[] = $form->createElement('radio', 'randomByCat', null, get_lang('YesWithCategoriesSorted'),'2'); |
2183
|
|
|
$radiocat[] = $form->createElement('radio', 'randomByCat', null, get_lang('No'),'0'); |
2184
|
|
|
$radioCatGroup = $form->addGroup($radiocat, null, get_lang('RandomQuestionByCategory'), ''); |
2185
|
|
|
$form->addElement('html','<div class="clear"> </div>'); |
2186
|
|
|
|
2187
|
|
|
// add the radio display the category name for student |
2188
|
|
|
$radio_display_cat_name = array(); |
2189
|
|
|
$radio_display_cat_name[] = $form->createElement('radio', 'display_category_name', null, get_lang('Yes'), '1'); |
2190
|
|
|
$radio_display_cat_name[] = $form->createElement('radio', 'display_category_name', null, get_lang('No'), '0'); |
2191
|
|
|
$form->addGroup($radio_display_cat_name, null, get_lang('QuestionDisplayCategoryName'), ''); |
2192
|
|
|
} |
2193
|
|
|
// Attempts |
2194
|
|
|
$attempt_option = range(0, 10); |
2195
|
|
|
$attempt_option[0] = get_lang('Infinite'); |
2196
|
|
|
|
2197
|
|
|
$form->addElement( |
2198
|
|
|
'select', |
2199
|
|
|
'exerciseAttempts', |
2200
|
|
|
get_lang('ExerciseAttempts'), |
2201
|
|
|
$attempt_option, |
2202
|
|
|
['id' => 'exerciseAttempts'] |
2203
|
|
|
); |
2204
|
|
|
|
2205
|
|
|
// Exercise time limit |
2206
|
|
|
$form->addElement('checkbox', 'activate_start_date_check',null, get_lang('EnableStartTime'), array('onclick' => 'activate_start_date()')); |
2207
|
|
|
|
2208
|
|
|
$var = Exercise::selectTimeLimit(); |
2209
|
|
|
|
2210
|
|
|
if (($this->start_time != '0000-00-00 00:00:00')) |
2211
|
|
|
$form->addElement('html','<div id="start_date_div" style="display:block;">'); |
2212
|
|
|
else |
2213
|
|
|
$form->addElement('html','<div id="start_date_div" style="display:none;">'); |
2214
|
|
|
|
2215
|
|
|
$form->addElement('date_time_picker', 'start_time'); |
2216
|
|
|
|
2217
|
|
|
$form->addElement('html','</div>'); |
2218
|
|
|
|
2219
|
|
|
$form->addElement('checkbox', 'activate_end_date_check', null , get_lang('EnableEndTime'), array('onclick' => 'activate_end_date()')); |
2220
|
|
|
|
2221
|
|
|
if (($this->end_time != '0000-00-00 00:00:00')) |
2222
|
|
|
$form->addElement('html','<div id="end_date_div" style="display:block;">'); |
2223
|
|
|
else |
2224
|
|
|
$form->addElement('html','<div id="end_date_div" style="display:none;">'); |
2225
|
|
|
|
2226
|
|
|
$form->addElement('date_time_picker', 'end_time'); |
2227
|
|
|
$form->addElement('html','</div>'); |
2228
|
|
|
|
2229
|
|
|
//$check_option=$this->selectType(); |
2230
|
|
|
$diplay = 'block'; |
2231
|
|
|
$form->addElement('checkbox', 'propagate_neg', null, get_lang('PropagateNegativeResults')); |
2232
|
|
|
$form->addElement('html','<div class="clear"> </div>'); |
2233
|
|
|
$form->addElement('checkbox', 'review_answers', null, get_lang('ReviewAnswers')); |
2234
|
|
|
|
2235
|
|
|
$form->addElement('html','<div id="divtimecontrol" style="display:'.$diplay.';">'); |
2236
|
|
|
|
2237
|
|
|
//Timer control |
2238
|
|
|
//$time_hours_option = range(0,12); |
2239
|
|
|
//$time_minutes_option = range(0,59); |
2240
|
|
|
$form->addElement( |
2241
|
|
|
'checkbox', |
2242
|
|
|
'enabletimercontrol', |
2243
|
|
|
null, |
2244
|
|
|
get_lang('EnableTimerControl'), |
2245
|
|
|
array( |
2246
|
|
|
'onclick' => 'option_time_expired()', |
2247
|
|
|
'id' => 'enabletimercontrol', |
2248
|
|
|
'onload' => 'check_load_time()', |
2249
|
|
|
) |
2250
|
|
|
); |
2251
|
|
|
$expired_date = (int)$this->selectExpiredTime(); |
2252
|
|
|
|
2253
|
|
|
if (($expired_date!='0')) { |
2254
|
|
|
$form->addElement('html','<div id="timercontrol" style="display:block;">'); |
2255
|
|
|
} else { |
2256
|
|
|
$form->addElement('html','<div id="timercontrol" style="display:none;">'); |
2257
|
|
|
} |
2258
|
|
|
$form->addText( |
2259
|
|
|
'enabletimercontroltotalminutes', |
2260
|
|
|
get_lang('ExerciseTotalDurationInMinutes'), |
2261
|
|
|
false, |
2262
|
|
|
[ |
2263
|
|
|
'id' => 'enabletimercontroltotalminutes', |
2264
|
|
|
'cols-size' => [2, 2, 8] |
2265
|
|
|
] |
2266
|
|
|
); |
2267
|
|
|
$form->addElement('html','</div>'); |
2268
|
|
|
|
2269
|
|
|
$form->addElement( |
2270
|
|
|
'text', |
2271
|
|
|
'pass_percentage', |
2272
|
|
|
array(get_lang('PassPercentage'), null, '%'), |
2273
|
|
|
array('id' => 'pass_percentage') |
2274
|
|
|
); |
2275
|
|
|
$form->addRule('pass_percentage', get_lang('Numeric'), 'numeric'); |
2276
|
|
|
$form->addRule('pass_percentage', get_lang('ValueTooSmall'), 'min_numeric_length', 0); |
2277
|
|
|
$form->addRule('pass_percentage', get_lang('ValueTooBig'), 'max_numeric_length', 100); |
2278
|
|
|
|
2279
|
|
|
// add the text_when_finished textbox |
2280
|
|
|
$form->addHtmlEditor( |
2281
|
|
|
'text_when_finished', |
2282
|
|
|
get_lang('TextWhenFinished'), |
2283
|
|
|
false, |
2284
|
|
|
false, |
2285
|
|
|
$editor_config |
2286
|
|
|
); |
2287
|
|
|
|
2288
|
|
|
$defaults = array(); |
2289
|
|
|
|
2290
|
|
|
if (api_get_setting('search_enabled') === 'true') { |
2291
|
|
|
require_once api_get_path(LIBRARY_PATH) . 'specific_fields_manager.lib.php'; |
2292
|
|
|
|
2293
|
|
|
$form->addElement ('checkbox', 'index_document','', get_lang('SearchFeatureDoIndexDocument')); |
2294
|
|
|
$form->addElement ('select_language', 'language', get_lang('SearchFeatureDocumentLanguage')); |
2295
|
|
|
|
2296
|
|
|
$specific_fields = get_specific_field_list(); |
2297
|
|
|
|
2298
|
|
|
foreach ($specific_fields as $specific_field) { |
2299
|
|
|
$form->addElement ('text', $specific_field['code'], $specific_field['name']); |
2300
|
|
|
$filter = array( |
2301
|
|
|
'c_id' => api_get_course_int_id(), |
2302
|
|
|
'field_id' => $specific_field['id'], |
2303
|
|
|
'ref_id' => $this->id, |
2304
|
|
|
'tool_id' => "'" . TOOL_QUIZ . "'" |
2305
|
|
|
); |
2306
|
|
|
$values = get_specific_field_values_list($filter, array('value')); |
2307
|
|
|
if ( !empty($values) ) { |
2308
|
|
|
$arr_str_values = array(); |
2309
|
|
|
foreach ($values as $value) { |
2310
|
|
|
$arr_str_values[] = $value['value']; |
2311
|
|
|
} |
2312
|
|
|
$defaults[$specific_field['code']] = implode(', ', $arr_str_values); |
2313
|
|
|
} |
2314
|
|
|
} |
2315
|
|
|
//$form->addElement ('html','</div>'); |
2316
|
|
|
} |
2317
|
|
|
|
2318
|
|
|
$form->addElement('html','</div>'); //End advanced setting |
2319
|
|
|
$form->addElement('html','</div>'); |
2320
|
|
|
} |
2321
|
|
|
|
2322
|
|
|
// submit |
2323
|
|
|
if (isset($_GET['exerciseId'])) { |
2324
|
|
|
$form->addButtonSave(get_lang('ModifyExercise'), 'submitExercise'); |
2325
|
|
|
} else { |
2326
|
|
|
$form->addButtonUpdate(get_lang('ProcedToQuestions'), 'submitExercise'); |
2327
|
|
|
} |
2328
|
|
|
|
2329
|
|
|
$form->addRule('exerciseTitle', get_lang('GiveExerciseName'), 'required'); |
2330
|
|
|
|
2331
|
|
|
if ($type == 'full') { |
2332
|
|
|
// rules |
2333
|
|
|
$form->addRule('exerciseAttempts', get_lang('Numeric'), 'numeric'); |
2334
|
|
|
$form->addRule('start_time', get_lang('InvalidDate'), 'datetime'); |
2335
|
|
|
$form->addRule('end_time', get_lang('InvalidDate'), 'datetime'); |
2336
|
|
|
} |
2337
|
|
|
|
2338
|
|
|
// defaults |
2339
|
|
|
if ($type=='full') { |
2340
|
|
|
if ($this->id > 0) { |
2341
|
|
|
if ($this->random > $this->selectNbrQuestions()) { |
2342
|
|
|
$defaults['randomQuestions'] = $this->selectNbrQuestions(); |
2343
|
|
|
} else { |
2344
|
|
|
$defaults['randomQuestions'] = $this->random; |
2345
|
|
|
} |
2346
|
|
|
|
2347
|
|
|
$defaults['randomAnswers'] = $this->selectRandomAnswers(); |
2348
|
|
|
$defaults['exerciseType'] = $this->selectType(); |
2349
|
|
|
$defaults['exerciseTitle'] = $this->get_formated_title(); |
2350
|
|
|
$defaults['exerciseDescription'] = $this->selectDescription(); |
2351
|
|
|
$defaults['exerciseAttempts'] = $this->selectAttempts(); |
2352
|
|
|
$defaults['exerciseFeedbackType'] = $this->selectFeedbackType(); |
2353
|
|
|
$defaults['results_disabled'] = $this->selectResultsDisabled(); |
2354
|
|
|
$defaults['propagate_neg'] = $this->selectPropagateNeg(); |
2355
|
|
|
$defaults['review_answers'] = $this->review_answers; |
2356
|
|
|
$defaults['randomByCat'] = $this->selectRandomByCat(); |
2357
|
|
|
$defaults['text_when_finished'] = $this->selectTextWhenFinished(); |
2358
|
|
|
$defaults['display_category_name'] = $this->selectDisplayCategoryName(); |
2359
|
|
|
$defaults['pass_percentage'] = $this->selectPassPercentage(); |
2360
|
|
|
$defaults['question_selection_type'] = $this->getQuestionSelectionType(); |
2361
|
|
|
|
2362
|
|
|
if (($this->start_time != '0000-00-00 00:00:00')) { |
2363
|
|
|
$defaults['activate_start_date_check'] = 1; |
2364
|
|
|
} |
2365
|
|
|
if ($this->end_time != '0000-00-00 00:00:00') { |
2366
|
|
|
$defaults['activate_end_date_check'] = 1; |
2367
|
|
|
} |
2368
|
|
|
|
2369
|
|
|
$defaults['start_time'] = ($this->start_time!='0000-00-00 00:00:00') ? api_get_local_time($this->start_time) : date('Y-m-d 12:00:00'); |
2370
|
|
|
$defaults['end_time'] = ($this->end_time!='0000-00-00 00:00:00') ? api_get_local_time($this->end_time) : date('Y-m-d 12:00:00', time()+84600); |
2371
|
|
|
|
2372
|
|
|
// Get expired time |
2373
|
|
|
if ($this->expired_time != '0') { |
2374
|
|
|
$defaults['enabletimercontrol'] = 1; |
2375
|
|
|
$defaults['enabletimercontroltotalminutes'] = $this->expired_time; |
2376
|
|
|
} else { |
2377
|
|
|
$defaults['enabletimercontroltotalminutes'] = 0; |
2378
|
|
|
} |
2379
|
|
|
} else { |
2380
|
|
|
$defaults['exerciseType'] = 2; |
2381
|
|
|
$defaults['exerciseAttempts'] = 0; |
2382
|
|
|
$defaults['randomQuestions'] = 0; |
2383
|
|
|
$defaults['randomAnswers'] = 0; |
2384
|
|
|
$defaults['exerciseDescription'] = ''; |
2385
|
|
|
$defaults['exerciseFeedbackType'] = 0; |
2386
|
|
|
$defaults['results_disabled'] = 0; |
2387
|
|
|
$defaults['randomByCat'] = 0; |
2388
|
|
|
$defaults['text_when_finished'] = ""; |
2389
|
|
|
$defaults['start_time'] = date('Y-m-d 12:00:00'); |
2390
|
|
|
$defaults['display_category_name'] = 1; |
2391
|
|
|
$defaults['end_time'] = date('Y-m-d 12:00:00', time()+84600); |
2392
|
|
|
$defaults['pass_percentage'] = ''; |
2393
|
|
|
$defaults['end_button'] = $this->selectEndButton(); |
2394
|
|
|
$defaults['question_selection_type'] = 1; |
2395
|
|
|
$defaults['hide_question_title'] = 0; |
2396
|
|
|
$defaults['on_success_message'] = null; |
2397
|
|
|
$defaults['on_failed_message'] = null; |
2398
|
|
|
} |
2399
|
|
|
} else { |
2400
|
|
|
$defaults['exerciseTitle'] = $this->selectTitle(); |
2401
|
|
|
$defaults['exerciseDescription'] = $this->selectDescription(); |
2402
|
|
|
} |
2403
|
|
|
if (api_get_setting('search_enabled') === 'true') { |
2404
|
|
|
$defaults['index_document'] = 'checked="checked"'; |
2405
|
|
|
} |
2406
|
|
|
$form->setDefaults($defaults); |
2407
|
|
|
|
2408
|
|
|
// Freeze some elements. |
2409
|
|
|
if ($this->id != 0 && $this->edit_exercise_in_lp == false) { |
2410
|
|
|
$elementsToFreeze = array( |
2411
|
|
|
'randomQuestions', |
2412
|
|
|
//'randomByCat', |
2413
|
|
|
'exerciseAttempts', |
2414
|
|
|
'propagate_neg', |
2415
|
|
|
'enabletimercontrol', |
2416
|
|
|
'review_answers' |
2417
|
|
|
); |
2418
|
|
|
|
2419
|
|
|
foreach ($elementsToFreeze as $elementName) { |
2420
|
|
|
/** @var HTML_QuickForm_element $element */ |
2421
|
|
|
$element = $form->getElement($elementName); |
2422
|
|
|
$element->freeze(); |
2423
|
|
|
} |
2424
|
|
|
|
2425
|
|
|
//$radioCatGroup->freeze(); |
2426
|
|
|
} |
2427
|
|
|
} |
2428
|
|
|
|
2429
|
|
|
/** |
2430
|
|
|
* function which process the creation of exercises |
2431
|
|
|
* @param FormValidator $form |
2432
|
|
|
* @param string |
2433
|
|
|
*/ |
2434
|
|
|
function processCreation($form, $type = '') |
2435
|
|
|
{ |
2436
|
|
|
$this->updateTitle(Exercise::format_title_variable($form->getSubmitValue('exerciseTitle'))); |
2437
|
|
|
$this->updateDescription($form->getSubmitValue('exerciseDescription')); |
2438
|
|
|
$this->updateAttempts($form->getSubmitValue('exerciseAttempts')); |
2439
|
|
|
$this->updateFeedbackType($form->getSubmitValue('exerciseFeedbackType')); |
2440
|
|
|
$this->updateType($form->getSubmitValue('exerciseType')); |
2441
|
|
|
$this->setRandom($form->getSubmitValue('randomQuestions')); |
2442
|
|
|
$this->updateRandomAnswers($form->getSubmitValue('randomAnswers')); |
2443
|
|
|
$this->updateResultsDisabled($form->getSubmitValue('results_disabled')); |
2444
|
|
|
$this->updateExpiredTime($form->getSubmitValue('enabletimercontroltotalminutes')); |
2445
|
|
|
$this->updatePropagateNegative($form->getSubmitValue('propagate_neg')); |
2446
|
|
|
$this->updateRandomByCat($form->getSubmitValue('randomByCat')); |
2447
|
|
|
$this->updateTextWhenFinished($form->getSubmitValue('text_when_finished')); |
2448
|
|
|
$this->updateDisplayCategoryName($form->getSubmitValue('display_category_name')); |
2449
|
|
|
$this->updateReviewAnswers($form->getSubmitValue('review_answers')); |
2450
|
|
|
$this->updatePassPercentage($form->getSubmitValue('pass_percentage')); |
2451
|
|
|
$this->updateCategories($form->getSubmitValue('category')); |
2452
|
|
|
$this->updateEndButton($form->getSubmitValue('end_button')); |
2453
|
|
|
$this->setOnSuccessMessage($form->getSubmitValue('on_success_message')); |
2454
|
|
|
$this->setOnFailedMessage($form->getSubmitValue('on_failed_message')); |
2455
|
|
|
$this->updateEmailNotificationTemplate($form->getSubmitValue('email_notification_template')); |
2456
|
|
|
$this->updateEmailNotificationTemplateToUser($form->getSubmitValue('email_notification_template_to_user')); |
2457
|
|
|
$this->setNotifyUserByEmail($form->getSubmitValue('notify_user_by_email')); |
2458
|
|
|
$this->setModelType($form->getSubmitValue('model_type')); |
2459
|
|
|
$this->setQuestionSelectionType($form->getSubmitValue('question_selection_type')); |
2460
|
|
|
$this->setHideQuestionTitle($form->getSubmitValue('hide_question_title')); |
2461
|
|
|
$this->sessionId = api_get_session_id(); |
2462
|
|
|
$this->setQuestionSelectionType($form->getSubmitValue('question_selection_type')); |
2463
|
|
|
$this->setScoreTypeModel($form->getSubmitValue('score_type_model')); |
2464
|
|
|
$this->setGlobalCategoryId($form->getSubmitValue('global_category_id')); |
2465
|
|
|
|
2466
|
|
|
if ($form->getSubmitValue('activate_start_date_check') == 1) { |
2467
|
|
|
$start_time = $form->getSubmitValue('start_time'); |
2468
|
|
|
$this->start_time = api_get_utc_datetime($start_time); |
2469
|
|
|
} else { |
2470
|
|
|
$this->start_time = '0000-00-00 00:00:00'; |
2471
|
|
|
} |
2472
|
|
|
|
2473
|
|
|
if ($form->getSubmitValue('activate_end_date_check') == 1) { |
2474
|
|
|
$end_time = $form->getSubmitValue('end_time'); |
2475
|
|
|
$this->end_time = api_get_utc_datetime($end_time); |
2476
|
|
|
} else { |
2477
|
|
|
$this->end_time = '0000-00-00 00:00:00'; |
2478
|
|
|
} |
2479
|
|
|
|
2480
|
|
|
if ($form->getSubmitValue('enabletimercontrol') == 1) { |
2481
|
|
|
$expired_total_time = $form->getSubmitValue('enabletimercontroltotalminutes'); |
2482
|
|
|
if ($this->expired_time == 0) { |
2483
|
|
|
$this->expired_time = $expired_total_time; |
2484
|
|
|
} |
2485
|
|
|
} else { |
2486
|
|
|
$this->expired_time = 0; |
2487
|
|
|
} |
2488
|
|
|
|
2489
|
|
|
if ($form->getSubmitValue('randomAnswers') == 1) { |
2490
|
|
|
$this->random_answers=1; |
2491
|
|
|
} else { |
2492
|
|
|
$this->random_answers=0; |
2493
|
|
|
} |
2494
|
|
|
$this->save($type); |
2495
|
|
|
} |
2496
|
|
|
|
2497
|
|
|
function search_engine_save() |
2498
|
|
|
{ |
2499
|
|
|
if ($_POST['index_document'] != 1) { |
2500
|
|
|
return; |
2501
|
|
|
} |
2502
|
|
|
$course_id = api_get_course_id(); |
2503
|
|
|
|
2504
|
|
|
require_once api_get_path(LIBRARY_PATH) . 'search/ChamiloIndexer.class.php'; |
2505
|
|
|
require_once api_get_path(LIBRARY_PATH) . 'search/IndexableChunk.class.php'; |
2506
|
|
|
require_once api_get_path(LIBRARY_PATH) . 'specific_fields_manager.lib.php'; |
2507
|
|
|
|
2508
|
|
|
$specific_fields = get_specific_field_list(); |
2509
|
|
|
$ic_slide = new IndexableChunk(); |
2510
|
|
|
|
2511
|
|
|
$all_specific_terms = ''; |
2512
|
|
|
foreach ($specific_fields as $specific_field) { |
2513
|
|
|
if (isset($_REQUEST[$specific_field['code']])) { |
2514
|
|
|
$sterms = trim($_REQUEST[$specific_field['code']]); |
2515
|
|
|
if (!empty($sterms)) { |
2516
|
|
|
$all_specific_terms .= ' '. $sterms; |
2517
|
|
|
$sterms = explode(',', $sterms); |
2518
|
|
|
foreach ($sterms as $sterm) { |
2519
|
|
|
$ic_slide->addTerm(trim($sterm), $specific_field['code']); |
2520
|
|
|
add_specific_field_value($specific_field['id'], $course_id, TOOL_QUIZ, $this->id, $sterm); |
2521
|
|
|
} |
2522
|
|
|
} |
2523
|
|
|
} |
2524
|
|
|
} |
2525
|
|
|
|
2526
|
|
|
// build the chunk to index |
2527
|
|
|
$ic_slide->addValue("title", $this->exercise); |
2528
|
|
|
$ic_slide->addCourseId($course_id); |
2529
|
|
|
$ic_slide->addToolId(TOOL_QUIZ); |
2530
|
|
|
$xapian_data = array( |
2531
|
|
|
SE_COURSE_ID => $course_id, |
2532
|
|
|
SE_TOOL_ID => TOOL_QUIZ, |
2533
|
|
|
SE_DATA => array('type' => SE_DOCTYPE_EXERCISE_EXERCISE, 'exercise_id' => (int)$this->id), |
2534
|
|
|
SE_USER => (int)api_get_user_id(), |
2535
|
|
|
); |
2536
|
|
|
$ic_slide->xapian_data = serialize($xapian_data); |
2537
|
|
|
$exercise_description = $all_specific_terms .' '. $this->description; |
2538
|
|
|
$ic_slide->addValue("content", $exercise_description); |
2539
|
|
|
|
2540
|
|
|
$di = new ChamiloIndexer(); |
2541
|
|
|
isset($_POST['language'])? $lang=Database::escape_string($_POST['language']): $lang = 'english'; |
2542
|
|
|
$di->connectDb(NULL, NULL, $lang); |
2543
|
|
|
$di->addChunk($ic_slide); |
2544
|
|
|
|
2545
|
|
|
//index and return search engine document id |
2546
|
|
|
$did = $di->index(); |
2547
|
|
|
if ($did) { |
2548
|
|
|
// save it to db |
2549
|
|
|
$tbl_se_ref = Database::get_main_table(TABLE_MAIN_SEARCH_ENGINE_REF); |
2550
|
|
|
$sql = 'INSERT INTO %s (id, course_code, tool_id, ref_id_high_level, search_did) |
2551
|
|
|
VALUES (NULL , \'%s\', \'%s\', %s, %s)'; |
2552
|
|
|
$sql = sprintf($sql, $tbl_se_ref, $course_id, TOOL_QUIZ, $this->id, $did); |
2553
|
|
|
Database::query($sql); |
2554
|
|
|
} |
2555
|
|
|
} |
2556
|
|
|
|
2557
|
|
|
function search_engine_edit() |
2558
|
|
|
{ |
2559
|
|
|
// update search enchine and its values table if enabled |
2560
|
|
|
if (api_get_setting('search_enabled')=='true' && extension_loaded('xapian')) { |
2561
|
|
|
$course_id = api_get_course_id(); |
2562
|
|
|
|
2563
|
|
|
// actually, it consists on delete terms from db, |
2564
|
|
|
// insert new ones, create a new search engine document, and remove the old one |
2565
|
|
|
// get search_did |
2566
|
|
|
$tbl_se_ref = Database::get_main_table(TABLE_MAIN_SEARCH_ENGINE_REF); |
2567
|
|
|
$sql = 'SELECT * FROM %s WHERE course_code=\'%s\' AND tool_id=\'%s\' AND ref_id_high_level=%s LIMIT 1'; |
2568
|
|
|
$sql = sprintf($sql, $tbl_se_ref, $course_id, TOOL_QUIZ, $this->id); |
2569
|
|
|
$res = Database::query($sql); |
2570
|
|
|
|
2571
|
|
|
if (Database::num_rows($res) > 0) { |
2572
|
|
|
require_once(api_get_path(LIBRARY_PATH) . 'search/ChamiloIndexer.class.php'); |
2573
|
|
|
require_once(api_get_path(LIBRARY_PATH) . 'search/IndexableChunk.class.php'); |
2574
|
|
|
require_once(api_get_path(LIBRARY_PATH) . 'specific_fields_manager.lib.php'); |
2575
|
|
|
|
2576
|
|
|
$se_ref = Database::fetch_array($res); |
2577
|
|
|
$specific_fields = get_specific_field_list(); |
2578
|
|
|
$ic_slide = new IndexableChunk(); |
2579
|
|
|
|
2580
|
|
|
$all_specific_terms = ''; |
2581
|
|
|
foreach ($specific_fields as $specific_field) { |
2582
|
|
|
delete_all_specific_field_value($course_id, $specific_field['id'], TOOL_QUIZ, $this->id); |
2583
|
|
|
if (isset($_REQUEST[$specific_field['code']])) { |
2584
|
|
|
$sterms = trim($_REQUEST[$specific_field['code']]); |
2585
|
|
|
$all_specific_terms .= ' '. $sterms; |
2586
|
|
|
$sterms = explode(',', $sterms); |
2587
|
|
|
foreach ($sterms as $sterm) { |
2588
|
|
|
$ic_slide->addTerm(trim($sterm), $specific_field['code']); |
2589
|
|
|
add_specific_field_value($specific_field['id'], $course_id, TOOL_QUIZ, $this->id, $sterm); |
2590
|
|
|
} |
2591
|
|
|
} |
2592
|
|
|
} |
2593
|
|
|
|
2594
|
|
|
// build the chunk to index |
2595
|
|
|
$ic_slide->addValue("title", $this->exercise); |
2596
|
|
|
$ic_slide->addCourseId($course_id); |
2597
|
|
|
$ic_slide->addToolId(TOOL_QUIZ); |
2598
|
|
|
$xapian_data = array( |
2599
|
|
|
SE_COURSE_ID => $course_id, |
2600
|
|
|
SE_TOOL_ID => TOOL_QUIZ, |
2601
|
|
|
SE_DATA => array('type' => SE_DOCTYPE_EXERCISE_EXERCISE, 'exercise_id' => (int)$this->id), |
2602
|
|
|
SE_USER => (int)api_get_user_id(), |
2603
|
|
|
); |
2604
|
|
|
$ic_slide->xapian_data = serialize($xapian_data); |
2605
|
|
|
$exercise_description = $all_specific_terms .' '. $this->description; |
2606
|
|
|
$ic_slide->addValue("content", $exercise_description); |
2607
|
|
|
|
2608
|
|
|
$di = new ChamiloIndexer(); |
2609
|
|
|
isset($_POST['language'])? $lang=Database::escape_string($_POST['language']): $lang = 'english'; |
2610
|
|
|
$di->connectDb(NULL, NULL, $lang); |
2611
|
|
|
$di->remove_document((int)$se_ref['search_did']); |
2612
|
|
|
$di->addChunk($ic_slide); |
2613
|
|
|
|
2614
|
|
|
//index and return search engine document id |
2615
|
|
|
$did = $di->index(); |
2616
|
|
|
if ($did) { |
2617
|
|
|
// save it to db |
2618
|
|
|
$sql = 'DELETE FROM %s WHERE course_code=\'%s\' AND tool_id=\'%s\' AND ref_id_high_level=\'%s\''; |
2619
|
|
|
$sql = sprintf($sql, $tbl_se_ref, $course_id, TOOL_QUIZ, $this->id); |
2620
|
|
|
Database::query($sql); |
2621
|
|
|
$sql = 'INSERT INTO %s (id, course_code, tool_id, ref_id_high_level, search_did) |
2622
|
|
|
VALUES (NULL , \'%s\', \'%s\', %s, %s)'; |
2623
|
|
|
$sql = sprintf($sql, $tbl_se_ref, $course_id, TOOL_QUIZ, $this->id, $did); |
2624
|
|
|
Database::query($sql); |
2625
|
|
|
} |
2626
|
|
|
} else { |
2627
|
|
|
$this->search_engine_save(); |
2628
|
|
|
} |
2629
|
|
|
} |
2630
|
|
|
|
2631
|
|
|
} |
2632
|
|
|
|
2633
|
|
|
function search_engine_delete() |
2634
|
|
|
{ |
2635
|
|
|
// remove from search engine if enabled |
2636
|
|
|
if (api_get_setting('search_enabled') == 'true' && extension_loaded('xapian') ) { |
2637
|
|
|
$course_id = api_get_course_id(); |
2638
|
|
|
$tbl_se_ref = Database::get_main_table(TABLE_MAIN_SEARCH_ENGINE_REF); |
2639
|
|
|
$sql = 'SELECT * FROM %s WHERE course_code=\'%s\' AND tool_id=\'%s\' AND ref_id_high_level=%s AND ref_id_second_level IS NULL LIMIT 1'; |
2640
|
|
|
$sql = sprintf($sql, $tbl_se_ref, $course_id, TOOL_QUIZ, $this->id); |
2641
|
|
|
$res = Database::query($sql); |
2642
|
|
|
if (Database::num_rows($res) > 0) { |
2643
|
|
|
$row = Database::fetch_array($res); |
2644
|
|
|
require_once(api_get_path(LIBRARY_PATH) .'search/ChamiloIndexer.class.php'); |
2645
|
|
|
$di = new ChamiloIndexer(); |
2646
|
|
|
$di->remove_document((int)$row['search_did']); |
2647
|
|
|
unset($di); |
2648
|
|
|
$tbl_quiz_question = Database::get_course_table(TABLE_QUIZ_QUESTION); |
2649
|
|
|
foreach ( $this->questionList as $question_i) { |
2650
|
|
|
$sql = 'SELECT type FROM %s WHERE id=%s'; |
2651
|
|
|
$sql = sprintf($sql, $tbl_quiz_question, $question_i); |
2652
|
|
|
$qres = Database::query($sql); |
2653
|
|
|
if (Database::num_rows($qres) > 0) { |
2654
|
|
|
$qrow = Database::fetch_array($qres); |
2655
|
|
|
$objQuestion = Question::getInstance($qrow['type']); |
2656
|
|
|
$objQuestion = Question::read((int)$question_i); |
2657
|
|
|
$objQuestion->search_engine_edit($this->id, FALSE, TRUE); |
2658
|
|
|
unset($objQuestion); |
2659
|
|
|
} |
2660
|
|
|
} |
2661
|
|
|
} |
2662
|
|
|
$sql = 'DELETE FROM %s WHERE course_code=\'%s\' AND tool_id=\'%s\' AND ref_id_high_level=%s AND ref_id_second_level IS NULL LIMIT 1'; |
2663
|
|
|
$sql = sprintf($sql, $tbl_se_ref, $course_id, TOOL_QUIZ, $this->id); |
2664
|
|
|
Database::query($sql); |
2665
|
|
|
|
2666
|
|
|
// remove terms from db |
2667
|
|
|
require_once api_get_path(LIBRARY_PATH) .'specific_fields_manager.lib.php'; |
2668
|
|
|
delete_all_values_for_item($course_id, TOOL_QUIZ, $this->id); |
2669
|
|
|
} |
2670
|
|
|
} |
2671
|
|
|
|
2672
|
|
|
function selectExpiredTime() |
2673
|
|
|
{ |
2674
|
|
|
return $this->expired_time; |
2675
|
|
|
} |
2676
|
|
|
|
2677
|
|
|
/** |
2678
|
|
|
* Cleans the student's results only for the Exercise tool (Not from the LP) |
2679
|
|
|
* The LP results are NOT deleted by default, otherwise put $cleanLpTests = true |
2680
|
|
|
* Works with exercises in sessions |
2681
|
|
|
* @param bool $cleanLpTests |
2682
|
|
|
* @param string $cleanResultBeforeDate |
2683
|
|
|
* |
2684
|
|
|
* @return int quantity of user's exercises deleted |
2685
|
|
|
*/ |
2686
|
|
|
public function clean_results($cleanLpTests = false, $cleanResultBeforeDate = null) |
2687
|
|
|
{ |
2688
|
|
|
$table_track_e_exercises = Database::get_main_table(TABLE_STATISTIC_TRACK_E_EXERCISES); |
2689
|
|
|
$table_track_e_attempt = Database::get_main_table(TABLE_STATISTIC_TRACK_E_ATTEMPT); |
2690
|
|
|
|
2691
|
|
|
$sql_where = ' AND |
2692
|
|
|
orig_lp_id = 0 AND |
2693
|
|
|
orig_lp_item_id = 0'; |
2694
|
|
|
|
2695
|
|
|
// if we want to delete results from LP too |
2696
|
|
|
if ($cleanLpTests) { |
2697
|
|
|
$sql_where = ""; |
2698
|
|
|
} |
2699
|
|
|
|
2700
|
|
|
// if we want to delete attempts before date $cleanResultBeforeDate |
2701
|
|
|
// $cleanResultBeforeDate must be a valid UTC-0 date yyyy-mm-dd |
2702
|
|
|
|
2703
|
|
|
if (!empty($cleanResultBeforeDate)) { |
2704
|
|
|
$cleanResultBeforeDate = Database::escape_string($cleanResultBeforeDate); |
2705
|
|
|
if (api_is_valid_date($cleanResultBeforeDate)) { |
2706
|
|
|
$sql_where .= " AND exe_date <= '$cleanResultBeforeDate' "; |
2707
|
|
|
} else { |
2708
|
|
|
return 0; |
2709
|
|
|
} |
2710
|
|
|
} |
2711
|
|
|
|
2712
|
|
|
$sql = "SELECT exe_id |
2713
|
|
|
FROM $table_track_e_exercises |
2714
|
|
|
WHERE |
2715
|
|
|
c_id = ".api_get_course_int_id()." AND |
2716
|
|
|
exe_exo_id = ".$this->id." AND |
2717
|
|
|
session_id = ".api_get_session_id()." ". |
2718
|
|
|
$sql_where; |
2719
|
|
|
|
2720
|
|
|
$result = Database::query($sql); |
2721
|
|
|
$exe_list = Database::store_result($result); |
2722
|
|
|
|
2723
|
|
|
// deleting TRACK_E_ATTEMPT table |
2724
|
|
|
// check if exe in learning path or not |
2725
|
|
|
$i = 0; |
2726
|
|
|
if (is_array($exe_list) && count($exe_list) > 0) { |
2727
|
|
|
foreach ($exe_list as $item) { |
2728
|
|
|
$sql = "DELETE FROM $table_track_e_attempt |
2729
|
|
|
WHERE exe_id = '".$item['exe_id']."'"; |
2730
|
|
|
Database::query($sql); |
2731
|
|
|
$i++; |
2732
|
|
|
} |
2733
|
|
|
} |
2734
|
|
|
|
2735
|
|
|
$session_id = api_get_session_id(); |
2736
|
|
|
// delete TRACK_E_EXERCISES table |
2737
|
|
|
$sql = "DELETE FROM $table_track_e_exercises |
2738
|
|
|
WHERE c_id = ".api_get_course_int_id()." |
2739
|
|
|
AND exe_exo_id = ".$this->id." |
2740
|
|
|
$sql_where |
2741
|
|
|
AND session_id = ".$session_id.""; |
2742
|
|
|
Database::query($sql); |
2743
|
|
|
|
2744
|
|
|
Event::addEvent( |
2745
|
|
|
LOG_EXERCISE_RESULT_DELETE, |
2746
|
|
|
LOG_EXERCISE_ID, |
2747
|
|
|
$this->id, |
2748
|
|
|
null, |
2749
|
|
|
null, |
2750
|
|
|
api_get_course_int_id(), |
2751
|
|
|
$session_id |
2752
|
|
|
); |
2753
|
|
|
|
2754
|
|
|
return $i; |
2755
|
|
|
} |
2756
|
|
|
|
2757
|
|
|
/** |
2758
|
|
|
* Copies an exercise (duplicate all questions and answers) |
2759
|
|
|
*/ |
2760
|
|
|
public function copy_exercise() |
2761
|
|
|
{ |
2762
|
|
|
$exercise_obj= new Exercise(); |
2763
|
|
|
$exercise_obj = $this; |
2764
|
|
|
|
2765
|
|
|
// force the creation of a new exercise |
2766
|
|
|
$exercise_obj->updateTitle($exercise_obj->selectTitle().' - '.get_lang('Copy')); |
2767
|
|
|
//Hides the new exercise |
2768
|
|
|
$exercise_obj->updateStatus(false); |
2769
|
|
|
$exercise_obj->updateId(0); |
2770
|
|
|
$exercise_obj->save(); |
2771
|
|
|
|
2772
|
|
|
$new_exercise_id = $exercise_obj->selectId(); |
2773
|
|
|
$question_list = $exercise_obj->selectQuestionList(); |
2774
|
|
|
|
2775
|
|
|
if (!empty($question_list)) { |
2776
|
|
|
//Question creation |
2777
|
|
|
|
2778
|
|
|
foreach ($question_list as $old_question_id) { |
2779
|
|
|
$old_question_obj = Question::read($old_question_id); |
2780
|
|
|
$new_id = $old_question_obj->duplicate(); |
2781
|
|
|
if ($new_id) { |
2782
|
|
|
$new_question_obj = Question::read($new_id); |
2783
|
|
|
|
2784
|
|
|
if (isset($new_question_obj) && $new_question_obj) { |
2785
|
|
|
$new_question_obj->addToList($new_exercise_id); |
2786
|
|
|
// This should be moved to the duplicate function |
2787
|
|
|
$new_answer_obj = new Answer($old_question_id); |
2788
|
|
|
$new_answer_obj->read(); |
2789
|
|
|
$new_answer_obj->duplicate($new_id); |
2790
|
|
|
} |
2791
|
|
|
} |
2792
|
|
|
} |
2793
|
|
|
} |
2794
|
|
|
} |
2795
|
|
|
|
2796
|
|
|
/** |
2797
|
|
|
* Changes the exercise id |
2798
|
|
|
* |
2799
|
|
|
* @param int $id - exercise id |
2800
|
|
|
*/ |
2801
|
|
|
private function updateId($id) |
2802
|
|
|
{ |
2803
|
|
|
$this->id = $id; |
2804
|
|
|
} |
2805
|
|
|
|
2806
|
|
|
/** |
2807
|
|
|
* Changes the exercise status |
2808
|
|
|
* |
2809
|
|
|
* @param string $status - exercise status |
2810
|
|
|
*/ |
2811
|
|
|
function updateStatus($status) |
2812
|
|
|
{ |
2813
|
|
|
$this->active = $status; |
2814
|
|
|
} |
2815
|
|
|
|
2816
|
|
|
/** |
2817
|
|
|
* @param int $lp_id |
2818
|
|
|
* @param int $lp_item_id |
2819
|
|
|
* @param int $lp_item_view_id |
2820
|
|
|
* @param string $status |
2821
|
|
|
* @return array |
2822
|
|
|
*/ |
2823
|
|
|
public function get_stat_track_exercise_info( |
2824
|
|
|
$lp_id = 0, |
2825
|
|
|
$lp_item_id = 0, |
2826
|
|
|
$lp_item_view_id = 0, |
2827
|
|
|
$status = 'incomplete' |
2828
|
|
|
) { |
2829
|
|
|
$track_exercises = Database :: get_main_table(TABLE_STATISTIC_TRACK_E_EXERCISES); |
2830
|
|
|
if (empty($lp_id)) { |
2831
|
|
|
$lp_id = 0; |
2832
|
|
|
} |
2833
|
|
|
if (empty($lp_item_id)) { |
2834
|
|
|
$lp_item_id = 0; |
2835
|
|
|
} |
2836
|
|
|
if (empty($lp_item_view_id)) { |
2837
|
|
|
$lp_item_view_id = 0; |
2838
|
|
|
} |
2839
|
|
|
$condition = ' WHERE exe_exo_id = ' . "'" . $this->id . "'" .' AND |
2840
|
|
|
exe_user_id = ' . "'" . api_get_user_id() . "'" . ' AND |
2841
|
|
|
c_id = ' . api_get_course_int_id() . ' AND |
2842
|
|
|
status = ' . "'" . Database::escape_string($status). "'" . ' AND |
2843
|
|
|
orig_lp_id = ' . "'" . $lp_id . "'" . ' AND |
2844
|
|
|
orig_lp_item_id = ' . "'" . $lp_item_id . "'" . ' AND |
2845
|
|
|
orig_lp_item_view_id = ' . "'" . $lp_item_view_id . "'" . ' AND |
2846
|
|
|
session_id = ' . "'" . api_get_session_id() . "' LIMIT 1"; //Adding limit 1 just in case |
2847
|
|
|
|
2848
|
|
|
$sql_track = 'SELECT * FROM '.$track_exercises.$condition; |
2849
|
|
|
|
2850
|
|
|
$result = Database::query($sql_track); |
2851
|
|
|
$new_array = array(); |
2852
|
|
|
if (Database::num_rows($result) > 0 ) { |
2853
|
|
|
$new_array = Database::fetch_array($result, 'ASSOC'); |
2854
|
|
|
$new_array['num_exe'] = Database::num_rows($result); |
2855
|
|
|
} |
2856
|
|
|
return $new_array; |
2857
|
|
|
} |
2858
|
|
|
|
2859
|
|
|
/** |
2860
|
|
|
* Saves a test attempt |
2861
|
|
|
* |
2862
|
|
|
* @param int clock_expired_time |
2863
|
|
|
* @param int int lp id |
2864
|
|
|
* @param int int lp item id |
2865
|
|
|
* @param int int lp item_view id |
2866
|
|
|
* @param float $weight |
2867
|
|
|
* @param array question list |
2868
|
|
|
*/ |
2869
|
|
|
public function save_stat_track_exercise_info( |
2870
|
|
|
$clock_expired_time = 0, |
2871
|
|
|
$safe_lp_id = 0, |
2872
|
|
|
$safe_lp_item_id = 0, |
2873
|
|
|
$safe_lp_item_view_id = 0, |
2874
|
|
|
$questionList = array(), |
2875
|
|
|
$weight = 0 |
2876
|
|
|
) { |
2877
|
|
|
$track_exercises = Database :: get_main_table(TABLE_STATISTIC_TRACK_E_EXERCISES); |
2878
|
|
|
$safe_lp_id = intval($safe_lp_id); |
2879
|
|
|
$safe_lp_item_id = intval($safe_lp_item_id); |
2880
|
|
|
$safe_lp_item_view_id = intval($safe_lp_item_view_id); |
2881
|
|
|
|
2882
|
|
|
if (empty($safe_lp_id)) { |
2883
|
|
|
$safe_lp_id = 0; |
2884
|
|
|
} |
2885
|
|
|
if (empty($safe_lp_item_id)) { |
2886
|
|
|
$safe_lp_item_id = 0; |
2887
|
|
|
} |
2888
|
|
|
if (empty($clock_expired_time)) { |
2889
|
|
|
$clock_expired_time = 0; |
2890
|
|
|
} |
2891
|
|
|
|
2892
|
|
|
$questionList = array_map('intval', $questionList); |
2893
|
|
|
|
2894
|
|
|
$params = array( |
2895
|
|
|
'exe_exo_id' => $this->id , |
2896
|
|
|
'exe_user_id' => api_get_user_id(), |
2897
|
|
|
'c_id' => api_get_course_int_id(), |
2898
|
|
|
'status' => 'incomplete', |
2899
|
|
|
'session_id' => api_get_session_id(), |
2900
|
|
|
'data_tracking' => implode(',', $questionList) , |
2901
|
|
|
'start_date' => api_get_utc_datetime(), |
2902
|
|
|
'orig_lp_id' => $safe_lp_id, |
2903
|
|
|
'orig_lp_item_id' => $safe_lp_item_id, |
2904
|
|
|
'orig_lp_item_view_id' => $safe_lp_item_view_id, |
2905
|
|
|
'exe_weighting'=> $weight, |
2906
|
|
|
'user_ip' => api_get_real_ip() |
2907
|
|
|
); |
2908
|
|
|
|
2909
|
|
|
if ($this->expired_time != 0) { |
2910
|
|
|
$params['expired_time_control'] = $clock_expired_time; |
2911
|
|
|
} |
2912
|
|
|
|
2913
|
|
|
$id = Database::insert($track_exercises, $params); |
2914
|
|
|
|
2915
|
|
|
return $id; |
2916
|
|
|
} |
2917
|
|
|
|
2918
|
|
|
/** |
2919
|
|
|
* @param int $question_id |
2920
|
|
|
* @param int $questionNum |
2921
|
|
|
* @param array $questions_in_media |
2922
|
|
|
* @param string $currentAnswer |
2923
|
|
|
* @return string |
2924
|
|
|
*/ |
2925
|
|
|
public function show_button($question_id, $questionNum, $questions_in_media = array(), $currentAnswer = '') |
2926
|
|
|
{ |
2927
|
|
|
global $origin, $safe_lp_id, $safe_lp_item_id, $safe_lp_item_view_id; |
2928
|
|
|
|
2929
|
|
|
$nbrQuestions = $this->get_count_question_list(); |
2930
|
|
|
|
2931
|
|
|
$all_button = $html = $label = ''; |
2932
|
|
|
$hotspot_get = isset($_POST['hotspot']) ? Security::remove_XSS($_POST['hotspot']):null; |
2933
|
|
|
|
2934
|
|
|
if ($this->selectFeedbackType() == EXERCISE_FEEDBACK_TYPE_DIRECT && $this->type == ONE_PER_PAGE) { |
2935
|
|
|
$urlTitle = get_lang('ContinueTest'); |
2936
|
|
|
|
2937
|
|
|
if ($questionNum == count($this->questionList)) { |
2938
|
|
|
$urlTitle = get_lang('EndTest'); |
2939
|
|
|
} |
2940
|
|
|
|
2941
|
|
|
$html .= Display::url( |
2942
|
|
|
$urlTitle, |
2943
|
|
|
'exercise_submit_modal.php?' . http_build_query([ |
2944
|
|
|
'learnpath_id' => $safe_lp_id, |
2945
|
|
|
'learnpath_item_id' => $safe_lp_item_id, |
2946
|
|
|
'learnpath_item_view_id' => $safe_lp_item_view_id, |
2947
|
|
|
'origin' => $origin, |
2948
|
|
|
'hotspot' => $hotspot_get, |
2949
|
|
|
'nbrQuestions' => $nbrQuestions, |
2950
|
|
|
'num' => $questionNum, |
2951
|
|
|
'exerciseType' => $this->type, |
2952
|
|
|
'exerciseId' => $this->id |
2953
|
|
|
]), |
2954
|
|
|
[ |
2955
|
|
|
'class' => 'ajax btn btn-default', |
2956
|
|
|
'data-title' => $urlTitle, |
2957
|
|
|
'data-size' => 'md' |
2958
|
|
|
] |
2959
|
|
|
); |
2960
|
|
|
$html .='<br />'; |
2961
|
|
|
} else { |
2962
|
|
|
// User |
2963
|
|
|
if (api_is_allowed_to_session_edit()) { |
2964
|
|
|
if ($this->type == ALL_ON_ONE_PAGE || $nbrQuestions == $questionNum) { |
2965
|
|
|
if ($this->review_answers) { |
2966
|
|
|
$label = get_lang('ReviewQuestions'); |
2967
|
|
|
$class = 'btn btn-success'; |
2968
|
|
|
} else { |
2969
|
|
|
$label = get_lang('EndTest'); |
2970
|
|
|
$class = 'btn btn-warning'; |
2971
|
|
|
} |
2972
|
|
|
} else { |
2973
|
|
|
$label = get_lang('NextQuestion'); |
2974
|
|
|
$class = 'btn btn-primary'; |
2975
|
|
|
} |
2976
|
|
|
$class .= ' question-validate-btn'; // used to select it with jquery |
2977
|
|
|
if ($this->type == ONE_PER_PAGE) { |
2978
|
|
|
if ($questionNum != 1) { |
2979
|
|
|
$prev_question = $questionNum - 2; |
2980
|
|
|
$all_button .= '<a href="javascript://" class="btn btn-default" onclick="previous_question_and_save('.$prev_question.', '.$question_id.' ); ">'.get_lang('PreviousQuestion').'</a>'; |
2981
|
|
|
} |
2982
|
|
|
|
2983
|
|
|
//Next question |
2984
|
|
|
if (!empty($questions_in_media)) { |
2985
|
|
|
$questions_in_media = "['".implode("','",$questions_in_media)."']"; |
2986
|
|
|
$all_button .= ' <a href="javascript://" class="'.$class.'" onclick="save_question_list('.$questions_in_media.'); ">'.$label.'</a>'; |
2987
|
|
|
} else { |
2988
|
|
|
$all_button .= ' <a href="javascript://" class="'.$class.'" onclick="save_now('.$question_id.', \'\', \''.$currentAnswer.'\'); ">'.$label.'</a>'; |
2989
|
|
|
} |
2990
|
|
|
$all_button .= '<span id="save_for_now_'.$question_id.'" class="exercise_save_mini_message"></span> '; |
2991
|
|
|
|
2992
|
|
|
$html .= $all_button; |
2993
|
|
|
} else { |
2994
|
|
|
if ($this->review_answers) { |
2995
|
|
|
$all_label = get_lang('ReviewQuestions'); |
2996
|
|
|
$class = 'btn btn-success'; |
2997
|
|
|
} else { |
2998
|
|
|
$all_label = get_lang('EndTest'); |
2999
|
|
|
$class = 'btn btn-warning'; |
3000
|
|
|
} |
3001
|
|
|
$class .= ' question-validate-btn'; // used to select it with jquery |
3002
|
|
|
$all_button = ' <a href="javascript://" class="'.$class.'" onclick="validate_all(); ">'.$all_label.'</a>'; |
3003
|
|
|
$all_button .= ' ' . Display::span(null, ['id' => 'save_all_reponse']); |
3004
|
|
|
$html .= $all_button; |
3005
|
|
|
} |
3006
|
|
|
} |
3007
|
|
|
} |
3008
|
|
|
return $html; |
3009
|
|
|
} |
3010
|
|
|
|
3011
|
|
|
/** |
3012
|
|
|
* So the time control will work |
3013
|
|
|
* |
3014
|
|
|
* @param string $time_left |
3015
|
|
|
* @return string |
3016
|
|
|
*/ |
3017
|
|
|
public function show_time_control_js($time_left) |
3018
|
|
|
{ |
3019
|
|
|
$time_left = intval($time_left); |
3020
|
|
|
return "<script> |
3021
|
|
|
|
3022
|
|
|
function get_expired_date_string(expired_time) { |
3023
|
|
|
var months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']; |
3024
|
|
|
var day, month, year, hours, minutes, seconds, date_string; |
3025
|
|
|
var obj_date = new Date(expired_time); |
3026
|
|
|
day = obj_date.getDate(); |
3027
|
|
|
if (day < 10) day = '0' + day; |
3028
|
|
|
month = obj_date.getMonth(); |
3029
|
|
|
year = obj_date.getFullYear(); |
3030
|
|
|
hours = obj_date.getHours(); |
3031
|
|
|
if (hours < 10) hours = '0' + hours; |
3032
|
|
|
minutes = obj_date.getMinutes(); |
3033
|
|
|
if (minutes < 10) minutes = '0' + minutes; |
3034
|
|
|
seconds = obj_date.getSeconds(); |
3035
|
|
|
if (seconds < 10) seconds = '0' + seconds; |
3036
|
|
|
date_string = months[month] +' ' + day + ', ' + year + ' ' + hours + ':' + minutes + ':' + seconds; |
3037
|
|
|
return date_string; |
3038
|
|
|
} |
3039
|
|
|
|
3040
|
|
|
function open_clock_warning() { |
3041
|
|
|
$('#clock_warning').dialog({ |
3042
|
|
|
modal:true, |
3043
|
|
|
height:250, |
3044
|
|
|
closeOnEscape: false, |
3045
|
|
|
resizable: false, |
3046
|
|
|
buttons: { |
3047
|
|
|
'".addslashes(get_lang("EndTest"))."': function() { |
3048
|
|
|
$('#clock_warning').dialog('close'); |
3049
|
|
|
} |
3050
|
|
|
}, |
3051
|
|
|
close: function() { |
3052
|
|
|
send_form(); |
3053
|
|
|
} |
3054
|
|
|
}); |
3055
|
|
|
$('#clock_warning').dialog('open'); |
3056
|
|
|
|
3057
|
|
|
$('#counter_to_redirect').epiclock({ |
3058
|
|
|
mode: $.epiclock.modes.countdown, |
3059
|
|
|
offset: {seconds: 5}, |
3060
|
|
|
format: 's' |
3061
|
|
|
}).bind('timer', function () { |
3062
|
|
|
send_form(); |
3063
|
|
|
}); |
3064
|
|
|
|
3065
|
|
|
} |
3066
|
|
|
|
3067
|
|
|
function send_form() { |
3068
|
|
|
if ($('#exercise_form').length) { |
3069
|
|
|
$('#exercise_form').submit(); |
3070
|
|
|
} else { |
3071
|
|
|
//In reminder |
3072
|
|
|
final_submit(); |
3073
|
|
|
} |
3074
|
|
|
} |
3075
|
|
|
|
3076
|
|
|
function onExpiredTimeExercise() { |
3077
|
|
|
$('#wrapper-clock').hide(); |
3078
|
|
|
$('#exercise_form').hide(); |
3079
|
|
|
$('#expired-message-id').show(); |
3080
|
|
|
|
3081
|
|
|
//Fixes bug #5263 |
3082
|
|
|
$('#num_current_id').attr('value', '".$this->selectNbrQuestions()."'); |
3083
|
|
|
open_clock_warning(); |
3084
|
|
|
} |
3085
|
|
|
|
3086
|
|
|
$(document).ready(function() { |
3087
|
|
|
|
3088
|
|
|
var current_time = new Date().getTime(); |
3089
|
|
|
var time_left = parseInt(".$time_left."); // time in seconds when using minutes there are some seconds lost |
3090
|
|
|
var expired_time = current_time + (time_left*1000); |
3091
|
|
|
var expired_date = get_expired_date_string(expired_time); |
3092
|
|
|
|
3093
|
|
|
$('#exercise_clock_warning').epiclock({ |
3094
|
|
|
mode: $.epiclock.modes.countdown, |
3095
|
|
|
offset: {seconds: time_left}, |
3096
|
|
|
format: 'x:i:s', |
3097
|
|
|
renderer: 'minute' |
3098
|
|
|
}).bind('timer', function () { |
3099
|
|
|
onExpiredTimeExercise(); |
3100
|
|
|
}); |
3101
|
|
|
$('#submit_save').click(function () {}); |
3102
|
|
|
}); |
3103
|
|
|
</script>"; |
3104
|
|
|
} |
3105
|
|
|
|
3106
|
|
|
/** |
3107
|
|
|
* Lp javascript for hotspots |
3108
|
|
|
*/ |
3109
|
|
|
public function show_lp_javascript() |
3110
|
|
|
{ |
3111
|
|
|
return ""; |
3112
|
|
|
} |
3113
|
|
|
|
3114
|
|
|
/** |
3115
|
|
|
* This function was originally found in the exercise_show.php |
3116
|
|
|
* @param int $exeId |
3117
|
|
|
* @param int $questionId |
3118
|
|
|
* @param int $choice the user selected |
3119
|
|
|
* @param string $from function is called from 'exercise_show' or 'exercise_result' |
3120
|
|
|
* @param array $exerciseResultCoordinates the hotspot coordinates $hotspot[$question_id] = coordinates |
3121
|
|
|
* @param bool $saved_results save results in the DB or just show the reponse |
3122
|
|
|
* @param bool $from_database gets information from DB or from the current selection |
3123
|
|
|
* @param bool $show_result show results or not |
3124
|
|
|
* @param int $propagate_neg |
3125
|
|
|
* @param array $hotspot_delineation_result |
3126
|
|
|
* |
3127
|
|
|
* @todo reduce parameters of this function |
3128
|
|
|
* @return string html code |
3129
|
|
|
*/ |
3130
|
|
|
public function manage_answer( |
3131
|
|
|
$exeId, |
3132
|
|
|
$questionId, |
3133
|
|
|
$choice, |
3134
|
|
|
$from = 'exercise_show', |
3135
|
|
|
$exerciseResultCoordinates = array(), |
3136
|
|
|
$saved_results = true, |
3137
|
|
|
$from_database = false, |
3138
|
|
|
$show_result = true, |
3139
|
|
|
$propagate_neg = 0, |
3140
|
|
|
$hotspot_delineation_result = array(), |
3141
|
|
|
$showTotalScoreAndUserChoices = false |
3142
|
|
|
) { |
3143
|
|
|
global $debug; |
3144
|
|
|
//needed in order to use in the exercise_attempt() for the time |
3145
|
|
|
global $learnpath_id, $learnpath_item_id; |
3146
|
|
|
require_once api_get_path(LIBRARY_PATH).'geometry.lib.php'; |
3147
|
|
|
|
3148
|
|
|
$em = Database::getManager(); |
3149
|
|
|
|
3150
|
|
|
$feedback_type = $this->selectFeedbackType(); |
3151
|
|
|
$results_disabled = $this->selectResultsDisabled(); |
3152
|
|
|
|
3153
|
|
|
if ($debug) { |
3154
|
|
|
error_log("<------ manage_answer ------> "); |
3155
|
|
|
error_log('exe_id: '.$exeId); |
3156
|
|
|
error_log('$from: '.$from); |
3157
|
|
|
error_log('$saved_results: '.intval($saved_results)); |
3158
|
|
|
error_log('$from_database: '.intval($from_database)); |
3159
|
|
|
error_log('$show_result: '.$show_result); |
3160
|
|
|
error_log('$propagate_neg: '.$propagate_neg); |
3161
|
|
|
error_log('$exerciseResultCoordinates: '.print_r($exerciseResultCoordinates, 1)); |
3162
|
|
|
error_log('$hotspot_delineation_result: '.print_r($hotspot_delineation_result, 1)); |
3163
|
|
|
error_log('$learnpath_id: '.$learnpath_id); |
3164
|
|
|
error_log('$learnpath_item_id: '.$learnpath_item_id); |
3165
|
|
|
error_log('$choice: '.print_r($choice, 1)); |
3166
|
|
|
} |
3167
|
|
|
|
3168
|
|
|
$extra_data = array(); |
3169
|
|
|
$final_overlap = 0; |
3170
|
|
|
$final_missing = 0; |
3171
|
|
|
$final_excess = 0; |
3172
|
|
|
$overlap_color = 0; |
3173
|
|
|
$missing_color = 0; |
3174
|
|
|
$excess_color = 0; |
3175
|
|
|
$threadhold1 = 0; |
3176
|
|
|
$threadhold2 = 0; |
3177
|
|
|
$threadhold3 = 0; |
3178
|
|
|
|
3179
|
|
|
$arrques = null; |
3180
|
|
|
$arrans = null; |
3181
|
|
|
|
3182
|
|
|
$questionId = intval($questionId); |
3183
|
|
|
$exeId = intval($exeId); |
3184
|
|
|
$TBL_TRACK_ATTEMPT = Database::get_main_table(TABLE_STATISTIC_TRACK_E_ATTEMPT); |
3185
|
|
|
$table_ans = Database::get_course_table(TABLE_QUIZ_ANSWER); |
3186
|
|
|
|
3187
|
|
|
// Creates a temporary Question object |
3188
|
|
|
$course_id = $this->course_id; |
3189
|
|
|
$objQuestionTmp = Question::read($questionId, $course_id); |
3190
|
|
|
|
3191
|
|
|
if ($objQuestionTmp === false) { |
3192
|
|
|
return false; |
3193
|
|
|
} |
3194
|
|
|
|
3195
|
|
|
$questionName = $objQuestionTmp->selectTitle(); |
3196
|
|
|
$questionWeighting = $objQuestionTmp->selectWeighting(); |
3197
|
|
|
$answerType = $objQuestionTmp->selectType(); |
3198
|
|
|
$quesId = $objQuestionTmp->selectId(); |
3199
|
|
|
$extra = $objQuestionTmp->extra; |
3200
|
|
|
|
3201
|
|
|
$next = 1; //not for now |
3202
|
|
|
|
3203
|
|
|
// Extra information of the question |
3204
|
|
|
if (!empty($extra)) { |
3205
|
|
|
$extra = explode(':', $extra); |
3206
|
|
|
if ($debug) { |
3207
|
|
|
error_log(print_r($extra, 1)); |
3208
|
|
|
} |
3209
|
|
|
// Fixes problems with negatives values using intval |
3210
|
|
|
$true_score = floatval(trim($extra[0])); |
3211
|
|
|
$false_score = floatval(trim($extra[1])); |
3212
|
|
|
$doubt_score = floatval(trim($extra[2])); |
3213
|
|
|
} |
3214
|
|
|
|
3215
|
|
|
$totalWeighting = 0; |
3216
|
|
|
$totalScore = 0; |
3217
|
|
|
|
3218
|
|
|
// Destruction of the Question object |
3219
|
|
|
unset($objQuestionTmp); |
3220
|
|
|
|
3221
|
|
|
// Construction of the Answer object |
3222
|
|
|
$objAnswerTmp = new Answer($questionId); |
3223
|
|
|
$nbrAnswers = $objAnswerTmp->selectNbrAnswers(); |
3224
|
|
|
|
3225
|
|
|
if ($debug) { |
3226
|
|
|
error_log('Count of answers: '.$nbrAnswers); |
3227
|
|
|
error_log('$answerType: '.$answerType); |
3228
|
|
|
} |
3229
|
|
|
|
3230
|
|
|
if ($answerType == FREE_ANSWER || |
3231
|
|
|
$answerType == ORAL_EXPRESSION || |
3232
|
|
|
$answerType == CALCULATED_ANSWER |
3233
|
|
|
) { |
3234
|
|
|
$nbrAnswers = 1; |
3235
|
|
|
} |
3236
|
|
|
|
3237
|
|
|
$nano = null; |
3238
|
|
|
|
3239
|
|
|
if ($answerType == ORAL_EXPRESSION) { |
3240
|
|
|
$exe_info = Event::get_exercise_results_by_attempt($exeId); |
3241
|
|
|
$exe_info = isset($exe_info[$exeId]) ? $exe_info[$exeId] : null; |
3242
|
|
|
|
3243
|
|
|
$params = array(); |
3244
|
|
|
$params['course_id'] = $course_id; |
3245
|
|
|
$params['session_id'] = api_get_session_id(); |
3246
|
|
|
$params['user_id'] = isset($exe_info['exe_user_id'])? $exe_info['exe_user_id'] : api_get_user_id(); |
3247
|
|
|
$params['exercise_id'] = isset($exe_info['exe_exo_id'])? $exe_info['exe_exo_id'] : $this->id; |
3248
|
|
|
$params['question_id'] = $questionId; |
3249
|
|
|
$params['exe_id'] = isset($exe_info['exe_id']) ? $exe_info['exe_id'] : $exeId; |
3250
|
|
|
|
3251
|
|
|
$nano = new Nanogong($params); |
3252
|
|
|
|
3253
|
|
|
//probably this attempt came in an exercise all question by page |
3254
|
|
|
if ($feedback_type == 0) { |
3255
|
|
|
$nano->replace_with_real_exe($exeId); |
3256
|
|
|
} |
3257
|
|
|
} |
3258
|
|
|
|
3259
|
|
|
$user_answer = ''; |
3260
|
|
|
|
3261
|
|
|
// Get answer list for matching |
3262
|
|
|
$sql = "SELECT id_auto, id, answer |
3263
|
|
|
FROM $table_ans |
3264
|
|
|
WHERE c_id = $course_id AND question_id = $questionId"; |
3265
|
|
|
$res_answer = Database::query($sql); |
3266
|
|
|
|
3267
|
|
|
$answerMatching = array(); |
3268
|
|
|
while ($real_answer = Database::fetch_array($res_answer)) { |
3269
|
|
|
$answerMatching[$real_answer['id_auto']] = $real_answer['answer']; |
3270
|
|
|
} |
3271
|
|
|
|
3272
|
|
|
$real_answers = array(); |
3273
|
|
|
$quiz_question_options = Question::readQuestionOption( |
3274
|
|
|
$questionId, |
3275
|
|
|
$course_id |
3276
|
|
|
); |
3277
|
|
|
|
3278
|
|
|
$organs_at_risk_hit = 0; |
3279
|
|
|
$questionScore = 0; |
3280
|
|
|
|
3281
|
|
|
if ($debug) error_log('Start answer loop '); |
3282
|
|
|
|
3283
|
|
|
$orderedHotspots = []; |
3284
|
|
|
|
3285
|
|
|
if ($answerType == HOT_SPOT) { |
3286
|
|
|
$orderedHotspots = $em |
3287
|
|
|
->getRepository('ChamiloCoreBundle:TrackEHotspot') |
3288
|
|
|
->findBy([ |
3289
|
|
|
'hotspotQuestionId' => $questionId, |
3290
|
|
|
'cId' => $course_id, |
3291
|
|
|
'hotspotExeId' => $exeId |
3292
|
|
|
], |
3293
|
|
|
['hotspotId' => 'ASC'] |
3294
|
|
|
); |
3295
|
|
|
} |
3296
|
|
|
for ($answerId = 1; $answerId <= $nbrAnswers; $answerId++) { |
3297
|
|
|
$answer = $objAnswerTmp->selectAnswer($answerId); |
3298
|
|
|
$answerComment = $objAnswerTmp->selectComment($answerId); |
3299
|
|
|
$answerCorrect = $objAnswerTmp->isCorrect($answerId); |
3300
|
|
|
$answerWeighting = (float)$objAnswerTmp->selectWeighting($answerId); |
3301
|
|
|
$answerAutoId = $objAnswerTmp->selectAutoId($answerId); |
3302
|
|
|
|
3303
|
|
|
if ($debug) { |
3304
|
|
|
error_log("answer auto id: $answerAutoId "); |
3305
|
|
|
error_log("answer correct: $answerCorrect "); |
3306
|
|
|
} |
3307
|
|
|
|
3308
|
|
|
// Delineation |
3309
|
|
|
$delineation_cord = $objAnswerTmp->selectHotspotCoordinates(1); |
3310
|
|
|
$answer_delineation_destination=$objAnswerTmp->selectDestination(1); |
3311
|
|
|
|
3312
|
|
|
switch ($answerType) { |
3313
|
|
|
// for unique answer |
3314
|
|
|
case UNIQUE_ANSWER: |
3315
|
|
|
case UNIQUE_ANSWER_IMAGE: |
3316
|
|
|
case UNIQUE_ANSWER_NO_OPTION: |
3317
|
|
|
if ($from_database) { |
3318
|
|
|
$sql = "SELECT answer FROM $TBL_TRACK_ATTEMPT |
3319
|
|
|
WHERE |
3320
|
|
|
exe_id = '".$exeId."' AND |
3321
|
|
|
question_id= '".$questionId."'"; |
3322
|
|
|
$result = Database::query($sql); |
3323
|
|
|
$choice = Database::result($result,0,"answer"); |
3324
|
|
|
|
3325
|
|
|
$studentChoice = $choice == $answerAutoId ? 1 : 0; |
3326
|
|
|
if ($studentChoice) { |
3327
|
|
|
$questionScore += $answerWeighting; |
3328
|
|
|
$totalScore += $answerWeighting; |
3329
|
|
|
} |
3330
|
|
|
} else { |
3331
|
|
|
$studentChoice = $choice == $answerAutoId ? 1 : 0; |
3332
|
|
|
if ($studentChoice) { |
3333
|
|
|
$questionScore += $answerWeighting; |
3334
|
|
|
$totalScore += $answerWeighting; |
3335
|
|
|
} |
3336
|
|
|
} |
3337
|
|
|
break; |
3338
|
|
|
// for multiple answers |
3339
|
|
|
case MULTIPLE_ANSWER_TRUE_FALSE: |
3340
|
|
|
if ($from_database) { |
3341
|
|
|
$choice = array(); |
3342
|
|
|
$sql = "SELECT answer FROM $TBL_TRACK_ATTEMPT |
3343
|
|
|
WHERE |
3344
|
|
|
exe_id = $exeId AND |
3345
|
|
|
question_id = ".$questionId; |
3346
|
|
|
|
3347
|
|
|
$result = Database::query($sql); |
3348
|
|
|
while ($row = Database::fetch_array($result)) { |
3349
|
|
|
$ind = $row['answer']; |
3350
|
|
|
$values = explode(':', $ind); |
3351
|
|
|
$my_answer_id = isset($values[0]) ? $values[0] : ''; |
3352
|
|
|
$option = isset($values[1]) ? $values[1] : ''; |
3353
|
|
|
$choice[$my_answer_id] = $option; |
3354
|
|
|
} |
3355
|
|
|
} |
3356
|
|
|
|
3357
|
|
|
$studentChoice = isset($choice[$answerAutoId]) ? $choice[$answerAutoId] : null; |
3358
|
|
|
|
3359
|
|
|
if (!empty($studentChoice)) { |
3360
|
|
|
if ($studentChoice == $answerCorrect) { |
3361
|
|
|
$questionScore += $true_score; |
3362
|
|
|
} else { |
3363
|
|
|
if ($quiz_question_options[$studentChoice]['name'] == "Don't know" || |
3364
|
|
|
$quiz_question_options[$studentChoice]['name'] == "DoubtScore" |
3365
|
|
|
) { |
3366
|
|
|
$questionScore += $doubt_score; |
3367
|
|
|
} else { |
3368
|
|
|
$questionScore += $false_score; |
3369
|
|
|
} |
3370
|
|
|
} |
3371
|
|
|
} else { |
3372
|
|
|
// If no result then the user just hit don't know |
3373
|
|
|
$studentChoice = 3; |
3374
|
|
|
$questionScore += $doubt_score; |
3375
|
|
|
} |
3376
|
|
|
$totalScore = $questionScore; |
3377
|
|
|
break; |
3378
|
|
|
case MULTIPLE_ANSWER: //2 |
3379
|
|
|
if ($from_database) { |
3380
|
|
|
$choice = array(); |
3381
|
|
|
$sql = "SELECT answer FROM ".$TBL_TRACK_ATTEMPT." |
3382
|
|
|
WHERE exe_id = '".$exeId."' AND question_id= '".$questionId."'"; |
3383
|
|
|
$resultans = Database::query($sql); |
3384
|
|
|
while ($row = Database::fetch_array($resultans)) { |
3385
|
|
|
$ind = $row['answer']; |
3386
|
|
|
$choice[$ind] = 1; |
3387
|
|
|
} |
3388
|
|
|
|
3389
|
|
|
$studentChoice = isset($choice[$answerAutoId]) ? $choice[$answerAutoId] : null; |
3390
|
|
|
$real_answers[$answerId] = (bool)$studentChoice; |
3391
|
|
|
|
3392
|
|
|
if ($studentChoice) { |
3393
|
|
|
$questionScore +=$answerWeighting; |
3394
|
|
|
} |
3395
|
|
|
} else { |
3396
|
|
|
$studentChoice = isset($choice[$answerAutoId]) ? $choice[$answerAutoId] : null; |
3397
|
|
|
$real_answers[$answerId] = (bool)$studentChoice; |
3398
|
|
|
|
3399
|
|
|
if (isset($studentChoice)) { |
3400
|
|
|
$questionScore += $answerWeighting; |
3401
|
|
|
} |
3402
|
|
|
} |
3403
|
|
|
$totalScore += $answerWeighting; |
3404
|
|
|
|
3405
|
|
|
if ($debug) error_log("studentChoice: $studentChoice"); |
3406
|
|
|
break; |
3407
|
|
|
case GLOBAL_MULTIPLE_ANSWER: |
3408
|
|
|
if ($from_database) { |
3409
|
|
|
$choice = array(); |
3410
|
|
|
$sql = "SELECT answer FROM $TBL_TRACK_ATTEMPT |
3411
|
|
|
WHERE exe_id = '".$exeId."' AND question_id= '".$questionId."'"; |
3412
|
|
|
$resultans = Database::query($sql); |
3413
|
|
|
while ($row = Database::fetch_array($resultans)) { |
3414
|
|
|
$ind = $row['answer']; |
3415
|
|
|
$choice[$ind] = 1; |
3416
|
|
|
} |
3417
|
|
|
$studentChoice = isset($choice[$answerAutoId]) ? $choice[$answerAutoId] : null; |
3418
|
|
|
$real_answers[$answerId] = (bool)$studentChoice; |
3419
|
|
|
if ($studentChoice) { |
3420
|
|
|
$questionScore +=$answerWeighting; |
3421
|
|
|
} |
3422
|
|
|
} else { |
3423
|
|
|
$studentChoice = isset($choice[$answerAutoId]) ? $choice[$answerAutoId] : null; |
3424
|
|
|
if (isset($studentChoice)) { |
3425
|
|
|
$questionScore += $answerWeighting; |
3426
|
|
|
} |
3427
|
|
|
$real_answers[$answerId] = (bool)$studentChoice; |
3428
|
|
|
} |
3429
|
|
|
$totalScore += $answerWeighting; |
3430
|
|
|
if ($debug) error_log("studentChoice: $studentChoice"); |
3431
|
|
|
break; |
3432
|
|
|
case MULTIPLE_ANSWER_COMBINATION_TRUE_FALSE: |
3433
|
|
|
if ($from_database) { |
3434
|
|
|
$sql = "SELECT answer FROM ".$TBL_TRACK_ATTEMPT." |
3435
|
|
|
WHERE exe_id = $exeId AND question_id= ".$questionId; |
3436
|
|
|
$resultans = Database::query($sql); |
3437
|
|
|
while ($row = Database::fetch_array($resultans)) { |
3438
|
|
|
$ind = $row['answer']; |
3439
|
|
|
$result = explode(':',$ind); |
3440
|
|
|
if (isset($result[0])) { |
3441
|
|
|
$my_answer_id = isset($result[0]) ? $result[0] : ''; |
3442
|
|
|
$option = isset($result[1]) ? $result[1] : ''; |
3443
|
|
|
$choice[$my_answer_id] = $option; |
3444
|
|
|
} |
3445
|
|
|
} |
3446
|
|
|
$studentChoice = isset($choice[$answerAutoId]) ? $choice[$answerAutoId] : ''; |
3447
|
|
|
|
3448
|
|
|
if ($answerCorrect == $studentChoice) { |
3449
|
|
|
//$answerCorrect = 1; |
3450
|
|
|
$real_answers[$answerId] = true; |
3451
|
|
|
} else { |
3452
|
|
|
//$answerCorrect = 0; |
3453
|
|
|
$real_answers[$answerId] = false; |
3454
|
|
|
} |
3455
|
|
|
} else { |
3456
|
|
|
$studentChoice = isset($choice[$answerAutoId]) ? $choice[$answerAutoId] : ''; |
3457
|
|
|
if ($answerCorrect == $studentChoice) { |
3458
|
|
|
//$answerCorrect = 1; |
3459
|
|
|
$real_answers[$answerId] = true; |
3460
|
|
|
} else { |
3461
|
|
|
//$answerCorrect = 0; |
3462
|
|
|
$real_answers[$answerId] = false; |
3463
|
|
|
} |
3464
|
|
|
} |
3465
|
|
|
break; |
3466
|
|
|
case MULTIPLE_ANSWER_COMBINATION: |
3467
|
|
|
if ($from_database) { |
3468
|
|
|
$sql = "SELECT answer FROM $TBL_TRACK_ATTEMPT |
3469
|
|
|
WHERE exe_id = $exeId AND question_id= $questionId"; |
3470
|
|
|
$resultans = Database::query($sql); |
3471
|
|
|
while ($row = Database::fetch_array($resultans)) { |
3472
|
|
|
$ind = $row['answer']; |
3473
|
|
|
$choice[$ind] = 1; |
3474
|
|
|
} |
3475
|
|
|
|
3476
|
|
|
$studentChoice = isset($choice[$answerAutoId]) ? $choice[$answerAutoId] : null; |
3477
|
|
|
|
3478
|
|
|
if ($answerCorrect == 1) { |
3479
|
|
|
if ($studentChoice) { |
3480
|
|
|
$real_answers[$answerId] = true; |
3481
|
|
|
} else { |
3482
|
|
|
$real_answers[$answerId] = false; |
3483
|
|
|
} |
3484
|
|
|
} else { |
3485
|
|
|
if ($studentChoice) { |
3486
|
|
|
$real_answers[$answerId] = false; |
3487
|
|
|
} else { |
3488
|
|
|
$real_answers[$answerId] = true; |
3489
|
|
|
} |
3490
|
|
|
} |
3491
|
|
|
} else { |
3492
|
|
|
$studentChoice = isset($choice[$answerAutoId]) ? $choice[$answerAutoId] : null; |
3493
|
|
|
if ($answerCorrect == 1) { |
3494
|
|
|
if ($studentChoice) { |
3495
|
|
|
$real_answers[$answerId] = true; |
3496
|
|
|
} else { |
3497
|
|
|
$real_answers[$answerId] = false; |
3498
|
|
|
} |
3499
|
|
|
} else { |
3500
|
|
|
if ($studentChoice) { |
3501
|
|
|
$real_answers[$answerId] = false; |
3502
|
|
|
} else { |
3503
|
|
|
$real_answers[$answerId] = true; |
3504
|
|
|
} |
3505
|
|
|
} |
3506
|
|
|
} |
3507
|
|
|
break; |
3508
|
|
|
case FILL_IN_BLANKS: |
3509
|
|
|
$str = ''; |
3510
|
|
|
if ($from_database) { |
3511
|
|
|
$sql = "SELECT answer |
3512
|
|
|
FROM $TBL_TRACK_ATTEMPT |
3513
|
|
|
WHERE |
3514
|
|
|
exe_id = $exeId AND |
3515
|
|
|
question_id= ".intval($questionId); |
3516
|
|
|
$result = Database::query($sql); |
3517
|
|
|
$str = Database::result($result, 0, 'answer'); |
3518
|
|
|
} |
3519
|
|
|
|
3520
|
|
|
if ($saved_results == false && strpos($str, 'font color') !== false) { |
3521
|
|
|
// the question is encoded like this |
3522
|
|
|
// [A] B [C] D [E] F::10,10,10@1 |
3523
|
|
|
// number 1 before the "@" means that is a switchable fill in blank question |
3524
|
|
|
// [A] B [C] D [E] F::10,10,10@ or [A] B [C] D [E] F::10,10,10 |
3525
|
|
|
// means that is a normal fill blank question |
3526
|
|
|
// first we explode the "::" |
3527
|
|
|
$pre_array = explode('::', $answer); |
3528
|
|
|
|
3529
|
|
|
// is switchable fill blank or not |
3530
|
|
|
$last = count($pre_array) - 1; |
3531
|
|
|
$is_set_switchable = explode('@', $pre_array[$last]); |
3532
|
|
|
$switchable_answer_set = false; |
3533
|
|
|
if (isset ($is_set_switchable[1]) && $is_set_switchable[1] == 1) { |
3534
|
|
|
$switchable_answer_set = true; |
3535
|
|
|
} |
3536
|
|
|
$answer = ''; |
3537
|
|
|
for ($k = 0; $k < $last; $k++) { |
3538
|
|
|
$answer .= $pre_array[$k]; |
3539
|
|
|
} |
3540
|
|
|
// splits weightings that are joined with a comma |
3541
|
|
|
$answerWeighting = explode(',', $is_set_switchable[0]); |
3542
|
|
|
// we save the answer because it will be modified |
3543
|
|
|
$temp = $answer; |
3544
|
|
|
$answer = ''; |
3545
|
|
|
$j = 0; |
3546
|
|
|
//initialise answer tags |
3547
|
|
|
$user_tags = $correct_tags = $real_text = array(); |
3548
|
|
|
// the loop will stop at the end of the text |
3549
|
|
|
while (1) { |
3550
|
|
|
// quits the loop if there are no more blanks (detect '[') |
3551
|
|
|
if (($pos = api_strpos($temp, '[')) === false) { |
3552
|
|
|
// adds the end of the text |
3553
|
|
|
$answer = $temp; |
3554
|
|
|
$real_text[] = $answer; |
3555
|
|
|
break; //no more "blanks", quit the loop |
3556
|
|
|
} |
3557
|
|
|
// adds the piece of text that is before the blank |
3558
|
|
|
//and ends with '[' into a general storage array |
3559
|
|
|
$real_text[] = api_substr($temp, 0, $pos +1); |
3560
|
|
|
$answer .= api_substr($temp, 0, $pos +1); |
3561
|
|
|
//take the string remaining (after the last "[" we found) |
3562
|
|
|
$temp = api_substr($temp, $pos +1); |
3563
|
|
|
// quit the loop if there are no more blanks, and update $pos to the position of next ']' |
3564
|
|
|
if (($pos = api_strpos($temp, ']')) === false) { |
3565
|
|
|
// adds the end of the text |
3566
|
|
|
$answer .= $temp; |
3567
|
|
|
break; |
3568
|
|
|
} |
3569
|
|
|
if ($from_database) { |
3570
|
|
|
$queryfill = "SELECT answer FROM ".$TBL_TRACK_ATTEMPT." |
3571
|
|
|
WHERE |
3572
|
|
|
exe_id = '".$exeId."' AND |
3573
|
|
|
question_id= ".intval($questionId).""; |
3574
|
|
|
$resfill = Database::query($queryfill); |
3575
|
|
|
$str = Database::result($resfill, 0, 'answer'); |
3576
|
|
|
api_preg_match_all('#\[([^[]*)\]#', $str, $arr); |
3577
|
|
|
$str = str_replace('\r\n', '', $str); |
3578
|
|
|
|
3579
|
|
|
$choice = $arr[1]; |
3580
|
|
|
if (isset($choice[$j])) { |
3581
|
|
|
$tmp = api_strrpos($choice[$j], ' / '); |
3582
|
|
|
$choice[$j] = api_substr($choice[$j], 0, $tmp); |
3583
|
|
|
$choice[$j] = trim($choice[$j]); |
3584
|
|
|
// Needed to let characters ' and " to work as part of an answer |
3585
|
|
|
$choice[$j] = stripslashes($choice[$j]); |
3586
|
|
|
} else { |
3587
|
|
|
$choice[$j] = null; |
3588
|
|
|
} |
3589
|
|
|
} else { |
3590
|
|
|
// This value is the user input, not escaped while correct answer is escaped by fckeditor |
3591
|
|
|
$choice[$j] = api_htmlentities(trim($choice[$j])); |
3592
|
|
|
} |
3593
|
|
|
|
3594
|
|
|
$user_tags[] = $choice[$j]; |
3595
|
|
|
//put the contents of the [] answer tag into correct_tags[] |
3596
|
|
|
$correct_tags[] = api_substr($temp, 0, $pos); |
3597
|
|
|
$j++; |
3598
|
|
|
$temp = api_substr($temp, $pos +1); |
3599
|
|
|
} |
3600
|
|
|
$answer = ''; |
3601
|
|
|
$real_correct_tags = $correct_tags; |
3602
|
|
|
$chosen_list = array(); |
3603
|
|
|
|
3604
|
|
|
for ($i = 0; $i < count($real_correct_tags); $i++) { |
3605
|
|
|
if ($i == 0) { |
3606
|
|
|
$answer .= $real_text[0]; |
3607
|
|
|
} |
3608
|
|
|
if (!$switchable_answer_set) { |
3609
|
|
|
// Needed to parse ' and " characters |
3610
|
|
|
$user_tags[$i] = stripslashes($user_tags[$i]); |
3611
|
|
|
if ($correct_tags[$i] == $user_tags[$i]) { |
3612
|
|
|
// gives the related weighting to the student |
3613
|
|
|
$questionScore += $answerWeighting[$i]; |
3614
|
|
|
// increments total score |
3615
|
|
|
$totalScore += $answerWeighting[$i]; |
3616
|
|
|
// adds the word in green at the end of the string |
3617
|
|
|
$answer .= $correct_tags[$i]; |
3618
|
|
|
} elseif (!empty($user_tags[$i])) { |
3619
|
|
|
// else if the word entered by the student IS NOT the same as the one defined by the professor |
3620
|
|
|
// adds the word in red at the end of the string, and strikes it |
3621
|
|
|
$answer .= '<font color="red"><s>' . $user_tags[$i] . '</s></font>'; |
3622
|
|
|
} else { |
3623
|
|
|
// adds a tabulation if no word has been typed by the student |
3624
|
|
|
$answer .= ''; // remove that causes issue |
3625
|
|
|
} |
3626
|
|
|
} else { |
3627
|
|
|
// switchable fill in the blanks |
3628
|
|
|
if (in_array($user_tags[$i], $correct_tags)) { |
3629
|
|
|
$chosen_list[] = $user_tags[$i]; |
3630
|
|
|
$correct_tags = array_diff($correct_tags, $chosen_list); |
3631
|
|
|
// gives the related weighting to the student |
3632
|
|
|
$questionScore += $answerWeighting[$i]; |
3633
|
|
|
// increments total score |
3634
|
|
|
$totalScore += $answerWeighting[$i]; |
3635
|
|
|
// adds the word in green at the end of the string |
3636
|
|
|
$answer .= $user_tags[$i]; |
3637
|
|
|
} elseif (!empty ($user_tags[$i])) { |
3638
|
|
|
// else if the word entered by the student IS NOT the same as the one defined by the professor |
3639
|
|
|
// adds the word in red at the end of the string, and strikes it |
3640
|
|
|
$answer .= '<font color="red"><s>' . $user_tags[$i] . '</s></font>'; |
3641
|
|
|
} else { |
3642
|
|
|
// adds a tabulation if no word has been typed by the student |
3643
|
|
|
$answer .= ''; // remove that causes issue |
3644
|
|
|
} |
3645
|
|
|
} |
3646
|
|
|
|
3647
|
|
|
// adds the correct word, followed by ] to close the blank |
3648
|
|
|
$answer .= ' / <font color="green"><b>' . $real_correct_tags[$i] . '</b></font>]'; |
3649
|
|
|
if (isset($real_text[$i +1])) { |
3650
|
|
|
$answer .= $real_text[$i +1]; |
3651
|
|
|
} |
3652
|
|
|
} |
3653
|
|
|
} else { |
3654
|
|
|
// insert the student result in the track_e_attempt table, field answer |
3655
|
|
|
// $answer is the answer like in the c_quiz_answer table for the question |
3656
|
|
|
// student data are choice[] |
3657
|
|
|
$listCorrectAnswers = FillBlanks::getAnswerInfo( |
3658
|
|
|
$answer |
3659
|
|
|
); |
3660
|
|
|
$switchableAnswerSet = $listCorrectAnswers["switchable"]; |
3661
|
|
|
$answerWeighting = $listCorrectAnswers["tabweighting"]; |
3662
|
|
|
// user choices is an array $choice |
3663
|
|
|
|
3664
|
|
|
// get existing user data in n the BDD |
3665
|
|
|
if ($from_database) { |
3666
|
|
|
$sql = "SELECT answer |
3667
|
|
|
FROM $TBL_TRACK_ATTEMPT |
3668
|
|
|
WHERE |
3669
|
|
|
exe_id = $exeId AND |
3670
|
|
|
question_id= ".intval($questionId); |
3671
|
|
|
$result = Database::query($sql); |
3672
|
|
|
$str = Database::result($result, 0, 'answer'); |
3673
|
|
|
$listStudentResults = FillBlanks::getAnswerInfo( |
3674
|
|
|
$str, |
3675
|
|
|
true |
3676
|
|
|
); |
3677
|
|
|
$choice = $listStudentResults['studentanswer']; |
3678
|
|
|
} |
3679
|
|
|
|
3680
|
|
|
// loop other all blanks words |
3681
|
|
|
if (!$switchableAnswerSet) { |
3682
|
|
|
// not switchable answer, must be in the same place than teacher order |
3683
|
|
|
for ($i = 0; $i < count($listCorrectAnswers['tabwords']); $i++) { |
3684
|
|
|
$studentAnswer = isset($choice[$i]) ? trim($choice[$i]) : ''; |
3685
|
|
|
|
3686
|
|
|
// This value is the user input, not escaped while correct answer is escaped by fckeditor |
3687
|
|
|
// Works with cyrillic alphabet and when using ">" chars see #7718 #7610 #7618 |
3688
|
|
|
if (!$from_database) { |
3689
|
|
|
$studentAnswer = htmlentities( |
3690
|
|
|
api_utf8_encode($studentAnswer) |
3691
|
|
|
); |
3692
|
|
|
} |
3693
|
|
|
|
3694
|
|
|
$correctAnswer = $listCorrectAnswers['tabwords'][$i]; |
3695
|
|
|
$isAnswerCorrect = 0; |
3696
|
|
|
if (FillBlanks::isGoodStudentAnswer( |
3697
|
|
|
$studentAnswer, |
3698
|
|
|
$correctAnswer |
3699
|
|
|
) |
3700
|
|
|
) { |
3701
|
|
|
// gives the related weighting to the student |
3702
|
|
|
$questionScore += $answerWeighting[$i]; |
3703
|
|
|
// increments total score |
3704
|
|
|
$totalScore += $answerWeighting[$i]; |
3705
|
|
|
$isAnswerCorrect = 1; |
3706
|
|
|
} |
3707
|
|
|
$listCorrectAnswers['studentanswer'][$i] = $studentAnswer; |
3708
|
|
|
$listCorrectAnswers['studentscore'][$i] = $isAnswerCorrect; |
3709
|
|
|
} |
3710
|
|
|
} else { |
3711
|
|
|
// switchable answer |
3712
|
|
|
$listStudentAnswerTemp = $choice; |
3713
|
|
|
$listTeacherAnswerTemp = $listCorrectAnswers['tabwords']; |
3714
|
|
|
// for every teacher answer, check if there is a student answer |
3715
|
|
|
for ($i = 0; $i < count($listStudentAnswerTemp); $i++) { |
3716
|
|
|
$studentAnswer = trim( |
3717
|
|
|
$listStudentAnswerTemp[$i] |
3718
|
|
|
); |
3719
|
|
|
$found = false; |
3720
|
|
|
for ($j = 0; $j < count($listTeacherAnswerTemp); $j++) { |
3721
|
|
|
$correctAnswer = $listTeacherAnswerTemp[$j]; |
3722
|
|
|
if (!$found) { |
3723
|
|
|
if (FillBlanks::isGoodStudentAnswer( |
3724
|
|
|
$studentAnswer, |
3725
|
|
|
$correctAnswer |
3726
|
|
|
) |
3727
|
|
|
) { |
3728
|
|
|
$questionScore += $answerWeighting[$i]; |
3729
|
|
|
$totalScore += $answerWeighting[$i]; |
3730
|
|
|
$listTeacherAnswerTemp[$j] = ""; |
3731
|
|
|
$found = true; |
3732
|
|
|
} |
3733
|
|
|
} |
3734
|
|
|
} |
3735
|
|
|
$listCorrectAnswers['studentanswer'][$i] = $studentAnswer; |
3736
|
|
|
if (!$found) { |
3737
|
|
|
$listCorrectAnswers['studentscore'][$i] = 0; |
3738
|
|
|
} else { |
3739
|
|
|
$listCorrectAnswers['studentscore'][$i] = 1; |
3740
|
|
|
} |
3741
|
|
|
} |
3742
|
|
|
} |
3743
|
|
|
$answer = FillBlanks::getAnswerInStudentAttempt( |
3744
|
|
|
$listCorrectAnswers |
3745
|
|
|
); |
3746
|
|
|
} |
3747
|
|
|
break; |
3748
|
|
|
case CALCULATED_ANSWER: |
3749
|
|
|
$calculatedAnswerId = Session::read('calculatedAnswerId'); |
3750
|
|
|
$answer = ''; |
3751
|
|
|
if ($calculatedAnswerId) { |
3752
|
|
|
$calculatedAnswerInfo = Session::read('calculatedAnswerInfo'); |
3753
|
|
|
if (isset($calculatedAnswerInfo[$questionId])) { |
3754
|
|
|
$answer = $calculatedAnswerInfo[$questionId]; |
3755
|
|
|
} else { |
3756
|
|
|
$answer = $objAnswerTmp->selectAnswer($calculatedAnswerId[$questionId]); |
3757
|
|
|
} |
3758
|
|
|
} |
3759
|
|
|
$preArray = explode('@@', $answer); |
3760
|
|
|
$last = count($preArray) - 1; |
3761
|
|
|
$answer = ''; |
3762
|
|
|
for ($k = 0; $k < $last; $k++) { |
3763
|
|
|
$answer .= $preArray[$k]; |
3764
|
|
|
} |
3765
|
|
|
$answerWeighting = array($answerWeighting); |
3766
|
|
|
// we save the answer because it will be modified |
3767
|
|
|
$temp = $answer; |
3768
|
|
|
$answer = ''; |
3769
|
|
|
$j = 0; |
3770
|
|
|
//initialise answer tags |
3771
|
|
|
$userTags = $correctTags = $realText = array(); |
3772
|
|
|
// the loop will stop at the end of the text |
3773
|
|
|
while (1) { |
3774
|
|
|
// quits the loop if there are no more blanks (detect '[') |
3775
|
|
|
if (($pos = api_strpos($temp, '[')) === false) { |
3776
|
|
|
// adds the end of the text |
3777
|
|
|
$answer = $temp; |
3778
|
|
|
$realText[] = $answer; |
3779
|
|
|
break; //no more "blanks", quit the loop |
3780
|
|
|
} |
3781
|
|
|
// adds the piece of text that is before the blank |
3782
|
|
|
//and ends with '[' into a general storage array |
3783
|
|
|
$realText[] = api_substr($temp, 0, $pos +1); |
3784
|
|
|
$answer .= api_substr($temp, 0, $pos +1); |
3785
|
|
|
//take the string remaining (after the last "[" we found) |
3786
|
|
|
$temp = api_substr($temp, $pos +1); |
3787
|
|
|
// quit the loop if there are no more blanks, and update $pos to the position of next ']' |
3788
|
|
|
if (($pos = api_strpos($temp, ']')) === false) { |
3789
|
|
|
// adds the end of the text |
3790
|
|
|
$answer .= $temp; |
3791
|
|
|
break; |
3792
|
|
|
} |
3793
|
|
|
if ($from_database) { |
3794
|
|
|
$queryfill = "SELECT answer FROM ".$TBL_TRACK_ATTEMPT." |
3795
|
|
|
WHERE |
3796
|
|
|
exe_id = '".$exeId."' AND |
3797
|
|
|
question_id= ".intval($questionId).""; |
3798
|
|
|
$resfill = Database::query($queryfill); |
3799
|
|
|
$str = Database::result($resfill, 0, 'answer'); |
3800
|
|
|
api_preg_match_all('#\[([^[]*)\]#', $str, $arr); |
3801
|
|
|
$str = str_replace('\r\n', '', $str); |
3802
|
|
|
$choice = $arr[1]; |
3803
|
|
|
if (isset($choice[$j])) { |
3804
|
|
|
$tmp = api_strrpos($choice[$j], ' / '); |
3805
|
|
|
$choice[$j] = api_substr($choice[$j], 0, $tmp); |
3806
|
|
|
$choice[$j] = trim($choice[$j]); |
3807
|
|
|
// Needed to let characters ' and " to work as part of an answer |
3808
|
|
|
$choice[$j] = stripslashes($choice[$j]); |
3809
|
|
|
} else { |
3810
|
|
|
$choice[$j] = null; |
3811
|
|
|
} |
3812
|
|
|
} else { |
3813
|
|
|
// This value is the user input, not escaped while correct answer is escaped by fckeditor |
3814
|
|
|
$choice[$j] = api_htmlentities(trim($choice[$j])); |
3815
|
|
|
} |
3816
|
|
|
$userTags[] = $choice[$j]; |
3817
|
|
|
//put the contents of the [] answer tag into correct_tags[] |
3818
|
|
|
$correctTags[] = api_substr($temp, 0, $pos); |
3819
|
|
|
$j++; |
3820
|
|
|
$temp = api_substr($temp, $pos +1); |
3821
|
|
|
} |
3822
|
|
|
$answer = ''; |
3823
|
|
|
$realCorrectTags = $correctTags; |
3824
|
|
|
|
3825
|
|
|
if ($from_database && empty($calculatedAnswerId)) { |
3826
|
|
|
$queryfill = "SELECT answer FROM ".$TBL_TRACK_ATTEMPT." |
3827
|
|
|
WHERE |
3828
|
|
|
exe_id = '".$exeId."' AND |
3829
|
|
|
question_id= ".intval($questionId); |
3830
|
|
|
$resfill = Database::query($queryfill); |
3831
|
|
|
$rowFill = Database::fetch_assoc($resfill); |
3832
|
|
|
$answer = $rowFill['answer']; |
3833
|
|
|
$questionScore = $rowFill['marks']; |
3834
|
|
|
} |
3835
|
|
|
|
3836
|
|
|
for ($i = 0; $i < count($realCorrectTags); $i++) { |
3837
|
|
|
if ($i == 0) { |
3838
|
|
|
$answer .= $realText[0]; |
3839
|
|
|
} |
3840
|
|
|
// Needed to parse ' and " characters |
3841
|
|
|
$userTags[$i] = stripslashes($userTags[$i]); |
3842
|
|
|
if ($correctTags[$i] == $userTags[$i]) { |
3843
|
|
|
// gives the related weighting to the student |
3844
|
|
|
$questionScore += $answerWeighting[$i]; |
3845
|
|
|
// increments total score |
3846
|
|
|
$totalScore += $answerWeighting[$i]; |
3847
|
|
|
// adds the word in green at the end of the string |
3848
|
|
|
$answer .= $correctTags[$i]; |
3849
|
|
|
} elseif (!empty($userTags[$i])) { |
3850
|
|
|
// else if the word entered by the student IS NOT the same as the one defined by the professor |
3851
|
|
|
// adds the word in red at the end of the string, and strikes it |
3852
|
|
|
$answer .= '<font color="red"><s>' . $userTags[$i] . '</s></font>'; |
3853
|
|
|
} else { |
3854
|
|
|
// adds a tabulation if no word has been typed by the student |
3855
|
|
|
$answer .= ''; // remove that causes issue |
3856
|
|
|
} |
3857
|
|
|
// adds the correct word, followed by ] to close the blank |
3858
|
|
|
$answer .= ' / <font color="green"><b>' . $realCorrectTags[$i] . '</b></font>]'; |
3859
|
|
|
if (isset($realText[$i +1])) { |
3860
|
|
|
$answer .= $realText[$i +1]; |
3861
|
|
|
} |
3862
|
|
|
} |
3863
|
|
|
break; |
3864
|
|
|
case FREE_ANSWER: |
3865
|
|
|
if ($from_database) { |
3866
|
|
|
$query = "SELECT answer, marks FROM ".$TBL_TRACK_ATTEMPT." |
3867
|
|
|
WHERE exe_id = '".$exeId."' AND question_id= '".$questionId."'"; |
3868
|
|
|
$resq = Database::query($query); |
3869
|
|
|
$data = Database::fetch_array($resq); |
3870
|
|
|
|
3871
|
|
|
$choice = $data['answer']; |
3872
|
|
|
$choice = str_replace('\r\n', '', $choice); |
3873
|
|
|
$choice = stripslashes($choice); |
3874
|
|
|
$questionScore = $data['marks']; |
3875
|
|
|
|
3876
|
|
|
if ($questionScore == -1) { |
3877
|
|
|
$totalScore+= 0; |
3878
|
|
|
} else { |
3879
|
|
|
$totalScore+= $questionScore; |
3880
|
|
|
} |
3881
|
|
|
if ($questionScore == '') { |
3882
|
|
|
$questionScore = 0; |
3883
|
|
|
} |
3884
|
|
|
$arrques = $questionName; |
3885
|
|
|
$arrans = $choice; |
3886
|
|
|
} else { |
3887
|
|
|
$studentChoice = $choice; |
3888
|
|
|
if ($studentChoice) { |
3889
|
|
|
//Fixing negative puntation see #2193 |
3890
|
|
|
$questionScore = 0; |
3891
|
|
|
$totalScore += 0; |
3892
|
|
|
} |
3893
|
|
|
} |
3894
|
|
|
break; |
3895
|
|
|
case ORAL_EXPRESSION: |
3896
|
|
|
if ($from_database) { |
3897
|
|
|
$query = "SELECT answer, marks FROM ".$TBL_TRACK_ATTEMPT." |
3898
|
|
|
WHERE exe_id = '".$exeId."' AND question_id= '".$questionId."'"; |
3899
|
|
|
$resq = Database::query($query); |
3900
|
|
|
$choice = Database::result($resq,0,'answer'); |
3901
|
|
|
$choice = str_replace('\r\n', '', $choice); |
3902
|
|
|
$choice = stripslashes($choice); |
3903
|
|
|
$questionScore = Database::result($resq,0,"marks"); |
3904
|
|
|
if ($questionScore == -1) { |
3905
|
|
|
$totalScore+=0; |
3906
|
|
|
} else { |
3907
|
|
|
$totalScore+=$questionScore; |
3908
|
|
|
} |
3909
|
|
|
$arrques = $questionName; |
3910
|
|
|
$arrans = $choice; |
3911
|
|
|
} else { |
3912
|
|
|
$studentChoice = $choice; |
3913
|
|
|
if ($studentChoice) { |
3914
|
|
|
//Fixing negative puntation see #2193 |
3915
|
|
|
$questionScore = 0; |
3916
|
|
|
$totalScore += 0; |
3917
|
|
|
} |
3918
|
|
|
} |
3919
|
|
|
break; |
3920
|
|
|
case DRAGGABLE: |
3921
|
|
|
//no break |
3922
|
|
|
case MATCHING_DRAGGABLE: |
3923
|
|
|
//no break |
3924
|
|
|
case MATCHING: |
|
|
|
|
3925
|
|
|
if ($from_database) { |
3926
|
|
|
$sql = 'SELECT id, answer, id_auto |
3927
|
|
|
FROM '.$table_ans.' |
3928
|
|
|
WHERE |
3929
|
|
|
c_id = '.$course_id.' AND |
3930
|
|
|
question_id = "'.$questionId.'" AND |
3931
|
|
|
correct = 0'; |
3932
|
|
|
$res_answer = Database::query($sql); |
3933
|
|
|
// Getting the real answer |
3934
|
|
|
$real_list = array(); |
3935
|
|
|
while ($real_answer = Database::fetch_array($res_answer)) { |
3936
|
|
|
$real_list[$real_answer['id_auto']] = $real_answer['answer']; |
3937
|
|
|
} |
3938
|
|
|
|
3939
|
|
|
$sql = 'SELECT id, answer, correct, id_auto, ponderation |
3940
|
|
|
FROM '.$table_ans.' |
3941
|
|
|
WHERE |
3942
|
|
|
c_id = '.$course_id.' AND |
3943
|
|
|
question_id="'.$questionId.'" AND |
3944
|
|
|
correct <> 0 |
3945
|
|
|
ORDER BY id_auto'; |
3946
|
|
|
$res_answers = Database::query($sql); |
3947
|
|
|
|
3948
|
|
|
$questionScore = 0; |
3949
|
|
|
|
3950
|
|
|
while ($a_answers = Database::fetch_array($res_answers)) { |
3951
|
|
|
$i_answer_id = $a_answers['id']; //3 |
3952
|
|
|
$s_answer_label = $a_answers['answer']; // your daddy - your mother |
3953
|
|
|
$i_answer_correct_answer = $a_answers['correct']; //1 - 2 |
3954
|
|
|
$i_answer_id_auto = $a_answers['id_auto']; // 3 - 4 |
3955
|
|
|
|
3956
|
|
|
$sql = "SELECT answer FROM $TBL_TRACK_ATTEMPT |
3957
|
|
|
WHERE |
3958
|
|
|
exe_id = '$exeId' AND |
3959
|
|
|
question_id = '$questionId' AND |
3960
|
|
|
position = '$i_answer_id_auto'"; |
3961
|
|
|
|
3962
|
|
|
$res_user_answer = Database::query($sql); |
3963
|
|
|
|
3964
|
|
|
if (Database::num_rows($res_user_answer) > 0) { |
3965
|
|
|
// rich - good looking |
3966
|
|
|
$s_user_answer = Database::result($res_user_answer, 0, 0); |
3967
|
|
|
} else { |
3968
|
|
|
$s_user_answer = 0; |
3969
|
|
|
} |
3970
|
|
|
|
3971
|
|
|
$i_answerWeighting = $a_answers['ponderation']; |
3972
|
|
|
|
3973
|
|
|
$user_answer = ''; |
3974
|
|
|
if (!empty($s_user_answer)) { |
3975
|
|
|
if ($answerType == DRAGGABLE) { |
3976
|
|
|
if ($s_user_answer == $i_answer_correct_answer) { |
3977
|
|
|
$questionScore += $i_answerWeighting; |
3978
|
|
|
$totalScore += $i_answerWeighting; |
3979
|
|
|
$user_answer = Display::label(get_lang('Correct'), 'success'); |
3980
|
|
|
} else { |
3981
|
|
|
$user_answer = Display::label(get_lang('Incorrect'), 'danger'); |
3982
|
|
|
} |
3983
|
|
|
} else { |
3984
|
|
|
if ($s_user_answer == $i_answer_correct_answer) { |
3985
|
|
|
$questionScore += $i_answerWeighting; |
3986
|
|
|
$totalScore += $i_answerWeighting; |
3987
|
|
|
|
3988
|
|
|
if (isset($real_list[$i_answer_id])) { |
3989
|
|
|
$user_answer = Display::span($real_list[$i_answer_id]); |
3990
|
|
|
} |
3991
|
|
|
} else { |
3992
|
|
|
$user_answer = Display::span( |
3993
|
|
|
$real_list[$s_user_answer], |
3994
|
|
|
['style' => 'color: #FF0000; text-decoration: line-through;'] |
3995
|
|
|
); |
3996
|
|
|
} |
3997
|
|
|
} |
3998
|
|
|
} elseif ($answerType == DRAGGABLE) { |
3999
|
|
|
$user_answer = Display::label(get_lang('Incorrect'), 'danger'); |
4000
|
|
|
} |
4001
|
|
|
|
4002
|
|
|
if ($show_result) { |
4003
|
|
|
if ($showTotalScoreAndUserChoices == true) { |
4004
|
|
|
$user_answer = ''; |
4005
|
|
|
} |
4006
|
|
|
echo '<tr>'; |
4007
|
|
|
echo '<td>' . $s_answer_label . '</td>'; |
4008
|
|
|
echo '<td>' . $user_answer; |
4009
|
|
|
|
4010
|
|
|
if (in_array($answerType, [MATCHING, MATCHING_DRAGGABLE])) { |
4011
|
|
|
if (isset($real_list[$i_answer_correct_answer]) && $showTotalScoreAndUserChoices == false) { |
4012
|
|
|
echo Display::span( |
4013
|
|
|
$real_list[$i_answer_correct_answer], |
4014
|
|
|
['style' => 'color: #008000; font-weight: bold;'] |
4015
|
|
|
); |
4016
|
|
|
} |
4017
|
|
|
} |
4018
|
|
|
echo '</td>'; |
4019
|
|
|
echo '</tr>'; |
4020
|
|
|
} |
4021
|
|
|
} |
4022
|
|
|
break(2); // break the switch and the "for" condition |
4023
|
|
|
} else { |
4024
|
|
|
if ($answerCorrect) { |
4025
|
|
|
if (isset($choice[$answerAutoId]) && |
4026
|
|
|
$answerCorrect == $choice[$answerAutoId] |
4027
|
|
|
) { |
4028
|
|
|
$questionScore += $answerWeighting; |
4029
|
|
|
$totalScore += $answerWeighting; |
4030
|
|
|
$user_answer = Display::span($answerMatching[$choice[$answerAutoId]]); |
4031
|
|
|
} else { |
4032
|
|
|
if (isset($answerMatching[$choice[$answerAutoId]])) { |
4033
|
|
|
$user_answer = Display::span( |
4034
|
|
|
$answerMatching[$choice[$answerAutoId]], |
4035
|
|
|
['style' => 'color: #FF0000; text-decoration: line-through;'] |
4036
|
|
|
); |
4037
|
|
|
} |
4038
|
|
|
} |
4039
|
|
|
$matching[$answerAutoId] = $choice[$answerAutoId]; |
4040
|
|
|
} |
4041
|
|
|
break; |
4042
|
|
|
} |
4043
|
|
|
case HOT_SPOT: |
4044
|
|
|
if ($from_database) { |
4045
|
|
|
$TBL_TRACK_HOTSPOT = Database::get_main_table(TABLE_STATISTIC_TRACK_E_HOTSPOT); |
4046
|
|
|
$sql = "SELECT hotspot_correct |
4047
|
|
|
FROM $TBL_TRACK_HOTSPOT |
4048
|
|
|
WHERE |
4049
|
|
|
hotspot_exe_id = '".$exeId."' AND |
4050
|
|
|
hotspot_question_id= '".$questionId."' AND |
4051
|
|
|
hotspot_answer_id = ".intval($answerAutoId).""; |
4052
|
|
|
$result = Database::query($sql); |
4053
|
|
|
$studentChoice = Database::result($result, 0, "hotspot_correct"); |
4054
|
|
|
|
4055
|
|
|
if ($studentChoice) { |
4056
|
|
|
$questionScore += $answerWeighting; |
4057
|
|
|
$totalScore += $answerWeighting; |
4058
|
|
|
} |
4059
|
|
|
} else { |
4060
|
|
|
if (!isset($choice[$answerAutoId])) { |
4061
|
|
|
$choice[$answerAutoId] = 0; |
4062
|
|
|
} else { |
4063
|
|
|
$studentChoice = $choice[$answerAutoId]; |
4064
|
|
|
|
4065
|
|
|
$choiceIsValid = false; |
4066
|
|
|
|
4067
|
|
|
if (!empty($studentChoice)) { |
4068
|
|
|
$hotspotType = $objAnswerTmp->selectHotspotType($answerId); |
4069
|
|
|
$hotspotCoordinates = $objAnswerTmp->selectHotspotCoordinates($answerId); |
4070
|
|
|
$choicePoint = Geometry::decodePoint($studentChoice); |
4071
|
|
|
|
4072
|
|
|
switch ($hotspotType) { |
4073
|
|
|
case 'square': |
4074
|
|
|
$hotspotProperties = Geometry::decodeSquare($hotspotCoordinates); |
4075
|
|
|
$choiceIsValid = Geometry::pointIsInSquare($hotspotProperties, $choicePoint); |
4076
|
|
|
break; |
4077
|
|
|
|
4078
|
|
|
case 'circle': |
4079
|
|
|
$hotspotProperties = Geometry::decodeEllipse($hotspotCoordinates); |
4080
|
|
|
$choiceIsValid = Geometry::pointIsInEllipse($hotspotProperties, $choicePoint); |
4081
|
|
|
break; |
4082
|
|
|
|
4083
|
|
|
case 'poly': |
4084
|
|
|
$hotspotProperties = Geometry::decodePolygon($hotspotCoordinates); |
4085
|
|
|
$choiceIsValid = Geometry::pointIsInPolygon($hotspotProperties, $choicePoint); |
4086
|
|
|
break; |
4087
|
|
|
} |
4088
|
|
|
} |
4089
|
|
|
|
4090
|
|
|
$choice[$answerAutoId] = 0; |
4091
|
|
|
if ($choiceIsValid) { |
4092
|
|
|
$questionScore += $answerWeighting; |
4093
|
|
|
$totalScore += $answerWeighting; |
4094
|
|
|
$choice[$answerAutoId] = 1; |
4095
|
|
|
} |
4096
|
|
|
} |
4097
|
|
|
} |
4098
|
|
|
break; |
4099
|
|
|
// @todo never added to chamilo |
4100
|
|
|
//for hotspot with fixed order |
4101
|
|
|
case HOT_SPOT_ORDER : |
4102
|
|
|
$studentChoice = $choice['order'][$answerId]; |
4103
|
|
|
if ($studentChoice == $answerId) { |
4104
|
|
|
$questionScore += $answerWeighting; |
4105
|
|
|
$totalScore += $answerWeighting; |
4106
|
|
|
$studentChoice = true; |
4107
|
|
|
} else { |
4108
|
|
|
$studentChoice = false; |
4109
|
|
|
} |
4110
|
|
|
break; |
4111
|
|
|
// for hotspot with delineation |
4112
|
|
|
case HOT_SPOT_DELINEATION : |
4113
|
|
|
if ($from_database) { |
4114
|
|
|
// getting the user answer |
4115
|
|
|
$TBL_TRACK_HOTSPOT = Database::get_main_table(TABLE_STATISTIC_TRACK_E_HOTSPOT); |
4116
|
|
|
$query = "SELECT hotspot_correct, hotspot_coordinate |
4117
|
|
|
FROM $TBL_TRACK_HOTSPOT |
4118
|
|
|
WHERE |
4119
|
|
|
hotspot_exe_id = '".$exeId."' AND |
4120
|
|
|
hotspot_question_id= '".$questionId."' AND |
4121
|
|
|
hotspot_answer_id='1'"; |
4122
|
|
|
//by default we take 1 because it's a delineation |
4123
|
|
|
$resq = Database::query($query); |
4124
|
|
|
$row = Database::fetch_array($resq,'ASSOC'); |
4125
|
|
|
|
4126
|
|
|
$choice = $row['hotspot_correct']; |
4127
|
|
|
$user_answer = $row['hotspot_coordinate']; |
4128
|
|
|
|
4129
|
|
|
// THIS is very important otherwise the poly_compile will throw an error!! |
4130
|
|
|
// round-up the coordinates |
4131
|
|
|
$coords = explode('/',$user_answer); |
4132
|
|
|
$user_array = ''; |
4133
|
|
|
foreach ($coords as $coord) { |
4134
|
|
|
list($x,$y) = explode(';',$coord); |
4135
|
|
|
$user_array .= round($x).';'.round($y).'/'; |
4136
|
|
|
} |
4137
|
|
|
$user_array = substr($user_array,0,-1); |
4138
|
|
|
} else { |
4139
|
|
|
if (!empty($studentChoice)) { |
4140
|
|
|
$newquestionList[] = $questionId; |
4141
|
|
|
} |
4142
|
|
|
|
4143
|
|
|
if ($answerId === 1) { |
4144
|
|
|
$studentChoice = $choice[$answerId]; |
4145
|
|
|
$questionScore += $answerWeighting; |
4146
|
|
|
|
4147
|
|
|
if ($hotspot_delineation_result[1]==1) { |
4148
|
|
|
$totalScore += $answerWeighting; //adding the total |
4149
|
|
|
} |
4150
|
|
|
} |
4151
|
|
|
} |
4152
|
|
|
$_SESSION['hotspot_coord'][1] = $delineation_cord; |
4153
|
|
|
$_SESSION['hotspot_dest'][1] = $answer_delineation_destination; |
4154
|
|
|
break; |
4155
|
|
|
} // end switch Answertype |
4156
|
|
|
|
4157
|
|
|
if ($show_result) { |
4158
|
|
|
if ($debug) error_log('Showing questions $from '.$from); |
4159
|
|
|
if ($from == 'exercise_result') { |
4160
|
|
|
// display answers (if not matching type, or if the answer is correct) |
4161
|
|
|
if ( |
4162
|
|
|
!in_array($answerType, [MATCHING, DRAGGABLE, MATCHING_DRAGGABLE]) || |
4163
|
|
|
$answerCorrect |
4164
|
|
|
) { |
4165
|
|
|
if ( |
4166
|
|
|
in_array( |
4167
|
|
|
$answerType, |
4168
|
|
|
array( |
4169
|
|
|
UNIQUE_ANSWER, |
4170
|
|
|
UNIQUE_ANSWER_IMAGE, |
4171
|
|
|
UNIQUE_ANSWER_NO_OPTION, |
4172
|
|
|
MULTIPLE_ANSWER, |
4173
|
|
|
MULTIPLE_ANSWER_COMBINATION, |
4174
|
|
|
GLOBAL_MULTIPLE_ANSWER |
4175
|
|
|
) |
4176
|
|
|
) |
4177
|
|
|
) { |
4178
|
|
|
ExerciseShowFunctions::display_unique_or_multiple_answer( |
4179
|
|
|
$feedback_type, |
4180
|
|
|
$answerType, |
4181
|
|
|
$studentChoice, |
4182
|
|
|
$answer, |
4183
|
|
|
$answerComment, |
4184
|
|
|
$answerCorrect, |
4185
|
|
|
0, |
4186
|
|
|
0, |
4187
|
|
|
0, |
4188
|
|
|
$results_disabled, |
4189
|
|
|
$showTotalScoreAndUserChoices |
4190
|
|
|
); |
4191
|
|
|
} elseif ($answerType == MULTIPLE_ANSWER_TRUE_FALSE) { |
4192
|
|
|
ExerciseShowFunctions::display_multiple_answer_true_false( |
4193
|
|
|
$feedback_type, |
4194
|
|
|
$answerType, |
4195
|
|
|
$studentChoice, |
4196
|
|
|
$answer, |
4197
|
|
|
$answerComment, |
4198
|
|
|
$answerCorrect, |
4199
|
|
|
0, |
4200
|
|
|
$questionId, |
4201
|
|
|
0, |
4202
|
|
|
$results_disabled, |
4203
|
|
|
$showTotalScoreAndUserChoices |
4204
|
|
|
); |
4205
|
|
|
} elseif ($answerType == MULTIPLE_ANSWER_COMBINATION_TRUE_FALSE) { |
4206
|
|
|
ExerciseShowFunctions::display_multiple_answer_combination_true_false( |
4207
|
|
|
$feedback_type, |
4208
|
|
|
$answerType, |
4209
|
|
|
$studentChoice, |
4210
|
|
|
$answer, |
4211
|
|
|
$answerComment, |
4212
|
|
|
$answerCorrect, |
4213
|
|
|
0, |
4214
|
|
|
0, |
4215
|
|
|
0, |
4216
|
|
|
$results_disabled, |
4217
|
|
|
$showTotalScoreAndUserChoices |
4218
|
|
|
); |
4219
|
|
|
} elseif ($answerType == FILL_IN_BLANKS) { |
4220
|
|
|
ExerciseShowFunctions::display_fill_in_blanks_answer( |
4221
|
|
|
$feedback_type, |
4222
|
|
|
$answer, |
4223
|
|
|
0, |
4224
|
|
|
0, |
4225
|
|
|
$results_disabled, |
4226
|
|
|
'', |
4227
|
|
|
$showTotalScoreAndUserChoices |
4228
|
|
|
); |
4229
|
|
|
} elseif ($answerType == CALCULATED_ANSWER) { |
4230
|
|
|
ExerciseShowFunctions::display_calculated_answer( |
4231
|
|
|
$feedback_type, |
4232
|
|
|
$answer, |
4233
|
|
|
0, |
4234
|
|
|
0, |
4235
|
|
|
$results_disabled, |
4236
|
|
|
$showTotalScoreAndUserChoices |
4237
|
|
|
); |
4238
|
|
|
} elseif ($answerType == FREE_ANSWER) { |
4239
|
|
|
ExerciseShowFunctions::display_free_answer( |
4240
|
|
|
$feedback_type, |
4241
|
|
|
$choice, |
4242
|
|
|
$exeId, |
4243
|
|
|
$questionId, |
4244
|
|
|
$questionScore, |
4245
|
|
|
$results_disabled |
4246
|
|
|
); |
4247
|
|
|
} elseif ($answerType == ORAL_EXPRESSION) { |
4248
|
|
|
// to store the details of open questions in an array to be used in mail |
4249
|
|
|
ExerciseShowFunctions::display_oral_expression_answer( |
4250
|
|
|
$feedback_type, |
4251
|
|
|
$choice, |
4252
|
|
|
0, |
4253
|
|
|
0, |
4254
|
|
|
$nano, |
4255
|
|
|
$results_disabled |
4256
|
|
|
); |
4257
|
|
|
} elseif ($answerType == HOT_SPOT) { |
4258
|
|
|
foreach ($orderedHotspots as $correctAnswerId => $hotspot) { |
4259
|
|
|
if ($hotspot->getHotspotAnswerId() == $answerAutoId) { |
4260
|
|
|
break; |
4261
|
|
|
} |
4262
|
|
|
} |
4263
|
|
|
|
4264
|
|
|
ExerciseShowFunctions::display_hotspot_answer( |
4265
|
|
|
$feedback_type, |
4266
|
|
|
++$correctAnswerId, |
4267
|
|
|
$answer, |
4268
|
|
|
$studentChoice, |
4269
|
|
|
$answerComment, |
4270
|
|
|
$results_disabled, |
4271
|
|
|
$answerId, |
4272
|
|
|
$showTotalScoreAndUserChoices |
4273
|
|
|
); |
4274
|
|
|
} elseif ($answerType == HOT_SPOT_ORDER) { |
4275
|
|
|
ExerciseShowFunctions::display_hotspot_order_answer( |
4276
|
|
|
$feedback_type, |
4277
|
|
|
$answerId, |
4278
|
|
|
$answer, |
4279
|
|
|
$studentChoice, |
4280
|
|
|
$answerComment |
4281
|
|
|
); |
4282
|
|
|
} elseif ($answerType == HOT_SPOT_DELINEATION) { |
4283
|
|
|
$user_answer = $_SESSION['exerciseResultCoordinates'][$questionId]; |
4284
|
|
|
|
4285
|
|
|
//round-up the coordinates |
4286
|
|
|
$coords = explode('/',$user_answer); |
4287
|
|
|
$user_array = ''; |
4288
|
|
|
foreach ($coords as $coord) { |
4289
|
|
|
list($x,$y) = explode(';',$coord); |
4290
|
|
|
$user_array .= round($x).';'.round($y).'/'; |
4291
|
|
|
} |
4292
|
|
|
$user_array = substr($user_array,0,-1); |
4293
|
|
|
|
4294
|
|
|
if ($next) { |
4295
|
|
|
|
4296
|
|
|
$user_answer = $user_array; |
4297
|
|
|
|
4298
|
|
|
// we compare only the delineation not the other points |
4299
|
|
|
$answer_question = $_SESSION['hotspot_coord'][1]; |
4300
|
|
|
$answerDestination = $_SESSION['hotspot_dest'][1]; |
4301
|
|
|
|
4302
|
|
|
//calculating the area |
4303
|
|
|
$poly_user = convert_coordinates($user_answer, '/'); |
4304
|
|
|
$poly_answer = convert_coordinates($answer_question, '|'); |
4305
|
|
|
$max_coord = poly_get_max($poly_user, $poly_answer); |
4306
|
|
|
$poly_user_compiled = poly_compile($poly_user, $max_coord); |
4307
|
|
|
$poly_answer_compiled = poly_compile($poly_answer, $max_coord); |
4308
|
|
|
$poly_results = poly_result($poly_answer_compiled, $poly_user_compiled, $max_coord); |
4309
|
|
|
|
4310
|
|
|
$overlap = $poly_results['both']; |
4311
|
|
|
$poly_answer_area = $poly_results['s1']; |
4312
|
|
|
$poly_user_area = $poly_results['s2']; |
4313
|
|
|
$missing = $poly_results['s1Only']; |
4314
|
|
|
$excess = $poly_results['s2Only']; |
4315
|
|
|
|
4316
|
|
|
//$overlap = round(polygons_overlap($poly_answer,$poly_user)); |
4317
|
|
|
// //this is an area in pixels |
4318
|
|
|
if ($debug > 0) { |
4319
|
|
|
error_log(__LINE__ . ' - Polygons results are ' . print_r($poly_results, 1), 0); |
4320
|
|
|
} |
4321
|
|
|
|
4322
|
|
|
if ($overlap < 1) { |
4323
|
|
|
//shortcut to avoid complicated calculations |
4324
|
|
|
$final_overlap = 0; |
4325
|
|
|
$final_missing = 100; |
4326
|
|
|
$final_excess = 100; |
4327
|
|
|
} else { |
4328
|
|
|
// the final overlap is the percentage of the initial polygon |
4329
|
|
|
// that is overlapped by the user's polygon |
4330
|
|
|
$final_overlap = round(((float) $overlap / (float) $poly_answer_area) * 100); |
4331
|
|
|
if ($debug > 1) { |
4332
|
|
|
error_log(__LINE__ . ' - Final overlap is ' . $final_overlap, 0); |
4333
|
|
|
} |
4334
|
|
|
// the final missing area is the percentage of the initial polygon |
4335
|
|
|
// that is not overlapped by the user's polygon |
4336
|
|
|
$final_missing = 100 - $final_overlap; |
4337
|
|
|
if ($debug > 1) { |
4338
|
|
|
error_log(__LINE__ . ' - Final missing is ' . $final_missing, 0); |
4339
|
|
|
} |
4340
|
|
|
// the final excess area is the percentage of the initial polygon's size |
4341
|
|
|
// that is covered by the user's polygon outside of the initial polygon |
4342
|
|
|
$final_excess = round((((float) $poly_user_area - (float) $overlap) / (float) $poly_answer_area) * 100); |
4343
|
|
|
if ($debug > 1) { |
4344
|
|
|
error_log(__LINE__ . ' - Final excess is ' . $final_excess, 0); |
4345
|
|
|
} |
4346
|
|
|
} |
4347
|
|
|
|
4348
|
|
|
//checking the destination parameters parsing the "@@" |
4349
|
|
|
$destination_items= explode('@@', $answerDestination); |
4350
|
|
|
$threadhold_total = $destination_items[0]; |
4351
|
|
|
$threadhold_items=explode(';',$threadhold_total); |
4352
|
|
|
$threadhold1 = $threadhold_items[0]; // overlap |
4353
|
|
|
$threadhold2 = $threadhold_items[1]; // excess |
4354
|
|
|
$threadhold3 = $threadhold_items[2]; //missing |
4355
|
|
|
|
4356
|
|
|
// if is delineation |
4357
|
|
|
if ($answerId===1) { |
4358
|
|
|
//setting colors |
4359
|
|
|
if ($final_overlap>=$threadhold1) { |
4360
|
|
|
$overlap_color=true; //echo 'a'; |
4361
|
|
|
} |
4362
|
|
|
//echo $excess.'-'.$threadhold2; |
4363
|
|
|
if ($final_excess<=$threadhold2) { |
4364
|
|
|
$excess_color=true; //echo 'b'; |
4365
|
|
|
} |
4366
|
|
|
//echo '--------'.$missing.'-'.$threadhold3; |
4367
|
|
|
if ($final_missing<=$threadhold3) { |
4368
|
|
|
$missing_color=true; //echo 'c'; |
4369
|
|
|
} |
4370
|
|
|
|
4371
|
|
|
// if pass |
4372
|
|
|
if ( |
4373
|
|
|
$final_overlap >= $threadhold1 && |
4374
|
|
|
$final_missing <= $threadhold3 && |
4375
|
|
|
$final_excess <= $threadhold2 |
4376
|
|
|
) { |
4377
|
|
|
$next=1; //go to the oars |
4378
|
|
|
$result_comment=get_lang('Acceptable'); |
4379
|
|
|
$final_answer = 1; // do not update with update_exercise_attempt |
4380
|
|
|
} else { |
4381
|
|
|
$next=0; |
4382
|
|
|
$result_comment=get_lang('Unacceptable'); |
4383
|
|
|
$comment=$answerDestination=$objAnswerTmp->selectComment(1); |
4384
|
|
|
$answerDestination=$objAnswerTmp->selectDestination(1); |
4385
|
|
|
//checking the destination parameters parsing the "@@" |
4386
|
|
|
$destination_items= explode('@@', $answerDestination); |
4387
|
|
|
} |
4388
|
|
|
} elseif($answerId>1) { |
4389
|
|
|
if ($objAnswerTmp->selectHotspotType($answerId) == 'noerror') { |
4390
|
|
|
if ($debug>0) { |
4391
|
|
|
error_log(__LINE__.' - answerId is of type noerror',0); |
4392
|
|
|
} |
4393
|
|
|
//type no error shouldn't be treated |
4394
|
|
|
$next = 1; |
4395
|
|
|
continue; |
4396
|
|
|
} |
4397
|
|
|
if ($debug>0) { |
4398
|
|
|
error_log(__LINE__.' - answerId is >1 so we\'re probably in OAR',0); |
4399
|
|
|
} |
4400
|
|
|
//check the intersection between the oar and the user |
4401
|
|
|
//echo 'user'; print_r($x_user_list); print_r($y_user_list); |
4402
|
|
|
//echo 'official';print_r($x_list);print_r($y_list); |
4403
|
|
|
//$result = get_intersection_data($x_list,$y_list,$x_user_list,$y_user_list); |
4404
|
|
|
$inter= $result['success']; |
4405
|
|
|
|
4406
|
|
|
//$delineation_cord=$objAnswerTmp->selectHotspotCoordinates($answerId); |
4407
|
|
|
$delineation_cord=$objAnswerTmp->selectHotspotCoordinates($answerId); |
4408
|
|
|
|
4409
|
|
|
$poly_answer = convert_coordinates($delineation_cord,'|'); |
4410
|
|
|
$max_coord = poly_get_max($poly_user,$poly_answer); |
4411
|
|
|
$poly_answer_compiled = poly_compile($poly_answer,$max_coord); |
4412
|
|
|
$overlap = poly_touch($poly_user_compiled, $poly_answer_compiled,$max_coord); |
4413
|
|
|
|
4414
|
|
|
if ($overlap == false) { |
4415
|
|
|
//all good, no overlap |
4416
|
|
|
$next = 1; |
4417
|
|
|
continue; |
4418
|
|
|
} else { |
4419
|
|
|
if ($debug>0) { |
4420
|
|
|
error_log(__LINE__.' - Overlap is '.$overlap.': OAR hit',0); |
4421
|
|
|
} |
4422
|
|
|
$organs_at_risk_hit++; |
4423
|
|
|
//show the feedback |
4424
|
|
|
$next=0; |
4425
|
|
|
$comment=$answerDestination=$objAnswerTmp->selectComment($answerId); |
4426
|
|
|
$answerDestination=$objAnswerTmp->selectDestination($answerId); |
4427
|
|
|
|
4428
|
|
|
$destination_items= explode('@@', $answerDestination); |
4429
|
|
|
$try_hotspot=$destination_items[1]; |
4430
|
|
|
$lp_hotspot=$destination_items[2]; |
4431
|
|
|
$select_question_hotspot=$destination_items[3]; |
4432
|
|
|
$url_hotspot=$destination_items[4]; |
4433
|
|
|
} |
4434
|
|
|
} |
4435
|
|
|
} else { // the first delineation feedback |
4436
|
|
|
if ($debug>0) { |
4437
|
|
|
error_log(__LINE__.' first',0); |
4438
|
|
|
} |
4439
|
|
|
} |
4440
|
|
|
} elseif (in_array($answerType, [MATCHING, MATCHING_DRAGGABLE])) { |
4441
|
|
|
echo '<tr>'; |
4442
|
|
|
echo Display::tag('td', $answerMatching[$answerId]); |
4443
|
|
|
echo Display::tag( |
4444
|
|
|
'td', |
4445
|
|
|
"$user_answer / " . Display::tag( |
4446
|
|
|
'strong', |
4447
|
|
|
$answerMatching[$answerCorrect], |
4448
|
|
|
['style' => 'color: #008000; font-weight: bold;'] |
4449
|
|
|
) |
4450
|
|
|
); |
4451
|
|
|
echo '</tr>'; |
4452
|
|
|
} |
4453
|
|
|
} |
4454
|
|
|
} else { |
4455
|
|
|
if ($debug) error_log('Showing questions $from '.$from); |
4456
|
|
|
|
4457
|
|
|
switch ($answerType) { |
4458
|
|
|
case UNIQUE_ANSWER: |
4459
|
|
|
case UNIQUE_ANSWER_IMAGE: |
4460
|
|
|
case UNIQUE_ANSWER_NO_OPTION: |
4461
|
|
|
case MULTIPLE_ANSWER: |
4462
|
|
|
case GLOBAL_MULTIPLE_ANSWER : |
4463
|
|
|
case MULTIPLE_ANSWER_COMBINATION: |
4464
|
|
|
if ($answerId == 1) { |
4465
|
|
|
ExerciseShowFunctions::display_unique_or_multiple_answer( |
4466
|
|
|
$feedback_type, |
4467
|
|
|
$answerType, |
4468
|
|
|
$studentChoice, |
4469
|
|
|
$answer, |
4470
|
|
|
$answerComment, |
4471
|
|
|
$answerCorrect, |
4472
|
|
|
$exeId, |
4473
|
|
|
$questionId, |
4474
|
|
|
$answerId, |
4475
|
|
|
$results_disabled, |
4476
|
|
|
$showTotalScoreAndUserChoices |
4477
|
|
|
); |
4478
|
|
|
} else { |
4479
|
|
|
ExerciseShowFunctions::display_unique_or_multiple_answer( |
4480
|
|
|
$feedback_type, |
4481
|
|
|
$answerType, |
4482
|
|
|
$studentChoice, |
4483
|
|
|
$answer, |
4484
|
|
|
$answerComment, |
4485
|
|
|
$answerCorrect, |
4486
|
|
|
$exeId, |
4487
|
|
|
$questionId, |
4488
|
|
|
'', |
4489
|
|
|
$results_disabled, |
4490
|
|
|
$showTotalScoreAndUserChoices |
4491
|
|
|
); |
4492
|
|
|
} |
4493
|
|
|
break; |
4494
|
|
|
case MULTIPLE_ANSWER_COMBINATION_TRUE_FALSE: |
4495
|
|
|
if ($answerId == 1) { |
4496
|
|
|
ExerciseShowFunctions::display_multiple_answer_combination_true_false( |
4497
|
|
|
$feedback_type, |
4498
|
|
|
$answerType, |
4499
|
|
|
$studentChoice, |
4500
|
|
|
$answer, |
4501
|
|
|
$answerComment, |
4502
|
|
|
$answerCorrect, |
4503
|
|
|
$exeId, |
4504
|
|
|
$questionId, |
4505
|
|
|
$answerId, |
4506
|
|
|
$results_disabled, |
4507
|
|
|
$showTotalScoreAndUserChoices |
4508
|
|
|
); |
4509
|
|
|
} else { |
4510
|
|
|
ExerciseShowFunctions::display_multiple_answer_combination_true_false( |
4511
|
|
|
$feedback_type, |
4512
|
|
|
$answerType, |
4513
|
|
|
$studentChoice, |
4514
|
|
|
$answer, |
4515
|
|
|
$answerComment, |
4516
|
|
|
$answerCorrect, |
4517
|
|
|
$exeId, |
4518
|
|
|
$questionId, |
4519
|
|
|
'', |
4520
|
|
|
$results_disabled, |
4521
|
|
|
$showTotalScoreAndUserChoices |
4522
|
|
|
); |
4523
|
|
|
} |
4524
|
|
|
break; |
4525
|
|
|
case MULTIPLE_ANSWER_TRUE_FALSE: |
4526
|
|
|
if ($answerId == 1) { |
4527
|
|
|
ExerciseShowFunctions::display_multiple_answer_true_false( |
4528
|
|
|
$feedback_type, |
4529
|
|
|
$answerType, |
4530
|
|
|
$studentChoice, |
4531
|
|
|
$answer, |
4532
|
|
|
$answerComment, |
4533
|
|
|
$answerCorrect, |
4534
|
|
|
$exeId, |
4535
|
|
|
$questionId, |
4536
|
|
|
$answerId, |
4537
|
|
|
$results_disabled, |
4538
|
|
|
$showTotalScoreAndUserChoices |
4539
|
|
|
); |
4540
|
|
|
} else { |
4541
|
|
|
ExerciseShowFunctions::display_multiple_answer_true_false( |
4542
|
|
|
$feedback_type, |
4543
|
|
|
$answerType, |
4544
|
|
|
$studentChoice, |
4545
|
|
|
$answer, |
4546
|
|
|
$answerComment, |
4547
|
|
|
$answerCorrect, |
4548
|
|
|
$exeId, |
4549
|
|
|
$questionId, |
4550
|
|
|
'', |
4551
|
|
|
$results_disabled, |
4552
|
|
|
$showTotalScoreAndUserChoices |
4553
|
|
|
); |
4554
|
|
|
} |
4555
|
|
|
break; |
4556
|
|
|
case FILL_IN_BLANKS: |
4557
|
|
|
ExerciseShowFunctions::display_fill_in_blanks_answer( |
4558
|
|
|
$feedback_type, |
4559
|
|
|
$answer, |
4560
|
|
|
$exeId, |
4561
|
|
|
$questionId, |
4562
|
|
|
$results_disabled, |
4563
|
|
|
$str, |
4564
|
|
|
$showTotalScoreAndUserChoices |
4565
|
|
|
); |
4566
|
|
|
break; |
4567
|
|
|
case CALCULATED_ANSWER: |
4568
|
|
|
ExerciseShowFunctions::display_calculated_answer( |
4569
|
|
|
$feedback_type, |
4570
|
|
|
$answer, |
4571
|
|
|
$exeId, |
4572
|
|
|
$questionId, |
4573
|
|
|
$results_disabled, |
4574
|
|
|
'', |
4575
|
|
|
$showTotalScoreAndUserChoices |
4576
|
|
|
); |
4577
|
|
|
break; |
4578
|
|
|
case FREE_ANSWER: |
4579
|
|
|
echo ExerciseShowFunctions::display_free_answer( |
4580
|
|
|
$feedback_type, |
4581
|
|
|
$choice, |
4582
|
|
|
$exeId, |
4583
|
|
|
$questionId, |
4584
|
|
|
$questionScore, |
4585
|
|
|
$results_disabled |
4586
|
|
|
); |
4587
|
|
|
break; |
4588
|
|
|
case ORAL_EXPRESSION: |
4589
|
|
|
echo '<tr> |
4590
|
|
|
<td valign="top">' . ExerciseShowFunctions::display_oral_expression_answer( |
4591
|
|
|
$feedback_type, |
4592
|
|
|
$choice, |
4593
|
|
|
$exeId, |
4594
|
|
|
$questionId, |
4595
|
|
|
$nano, |
4596
|
|
|
$results_disabled |
4597
|
|
|
) . '</td> |
4598
|
|
|
</tr> |
4599
|
|
|
</table>'; |
4600
|
|
|
break; |
4601
|
|
|
case HOT_SPOT: |
4602
|
|
|
ExerciseShowFunctions::display_hotspot_answer( |
4603
|
|
|
$feedback_type, |
4604
|
|
|
$answerId, |
4605
|
|
|
$answer, |
4606
|
|
|
$studentChoice, |
4607
|
|
|
$answerComment, |
4608
|
|
|
$results_disabled |
4609
|
|
|
$results_disabled, |
|
|
|
|
4610
|
|
|
$answerId |
4611
|
|
|
); |
4612
|
|
|
break; |
4613
|
|
|
case HOT_SPOT_DELINEATION: |
4614
|
|
|
$user_answer = $user_array; |
4615
|
|
|
if ($next) { |
4616
|
|
|
//$tbl_track_e_hotspot = Database::get_main_table(TABLE_STATISTIC_TRACK_E_HOTSPOT); |
4617
|
|
|
// Save into db |
4618
|
|
|
/* $sql = "INSERT INTO $tbl_track_e_hotspot ( |
4619
|
|
|
* hotspot_user_id, |
4620
|
|
|
* hotspot_course_code, |
4621
|
|
|
* hotspot_exe_id, |
4622
|
|
|
* hotspot_question_id, |
4623
|
|
|
* hotspot_answer_id, |
4624
|
|
|
* hotspot_correct, |
4625
|
|
|
* hotspot_coordinate |
4626
|
|
|
* ) |
4627
|
|
|
VALUES ( |
4628
|
|
|
* '".Database::escape_string($_user['user_id'])."', |
4629
|
|
|
* '".Database::escape_string($_course['id'])."', |
4630
|
|
|
* '".Database::escape_string($exeId)."', '".Database::escape_string($questionId)."', |
4631
|
|
|
* '".Database::escape_string($answerId)."', |
4632
|
|
|
* '".Database::escape_string($studentChoice)."', |
4633
|
|
|
* '".Database::escape_string($user_array)."')"; |
4634
|
|
|
$result = Database::query($sql,__FILE__,__LINE__); |
4635
|
|
|
*/ |
4636
|
|
|
$user_answer = $user_array; |
4637
|
|
|
|
4638
|
|
|
// we compare only the delineation not the other points |
4639
|
|
|
$answer_question = $_SESSION['hotspot_coord'][1]; |
4640
|
|
|
$answerDestination = $_SESSION['hotspot_dest'][1]; |
4641
|
|
|
|
4642
|
|
|
//calculating the area |
4643
|
|
|
$poly_user = convert_coordinates($user_answer, '/'); |
4644
|
|
|
$poly_answer = convert_coordinates($answer_question, '|'); |
4645
|
|
|
|
4646
|
|
|
$max_coord = poly_get_max($poly_user, $poly_answer); |
4647
|
|
|
$poly_user_compiled = poly_compile($poly_user, $max_coord); |
4648
|
|
|
$poly_answer_compiled = poly_compile($poly_answer, $max_coord); |
4649
|
|
|
$poly_results = poly_result($poly_answer_compiled, $poly_user_compiled, $max_coord); |
4650
|
|
|
|
4651
|
|
|
$overlap = $poly_results['both']; |
4652
|
|
|
$poly_answer_area = $poly_results['s1']; |
4653
|
|
|
$poly_user_area = $poly_results['s2']; |
4654
|
|
|
$missing = $poly_results['s1Only']; |
4655
|
|
|
$excess = $poly_results['s2Only']; |
4656
|
|
|
|
4657
|
|
|
//$overlap = round(polygons_overlap($poly_answer,$poly_user)); //this is an area in pixels |
4658
|
|
|
if ($debug > 0) { |
4659
|
|
|
error_log(__LINE__ . ' - Polygons results are ' . print_r($poly_results, 1), 0); |
4660
|
|
|
} |
4661
|
|
|
if ($overlap < 1) { |
4662
|
|
|
//shortcut to avoid complicated calculations |
4663
|
|
|
$final_overlap = 0; |
4664
|
|
|
$final_missing = 100; |
4665
|
|
|
$final_excess = 100; |
4666
|
|
|
} else { |
4667
|
|
|
// the final overlap is the percentage of the initial polygon that is overlapped by the user's polygon |
4668
|
|
|
$final_overlap = round(((float) $overlap / (float) $poly_answer_area) * 100); |
4669
|
|
|
if ($debug > 1) { |
4670
|
|
|
error_log(__LINE__ . ' - Final overlap is ' . $final_overlap, 0); |
4671
|
|
|
} |
4672
|
|
|
// the final missing area is the percentage of the initial polygon that is not overlapped by the user's polygon |
4673
|
|
|
$final_missing = 100 - $final_overlap; |
4674
|
|
|
if ($debug > 1) { |
4675
|
|
|
error_log(__LINE__ . ' - Final missing is ' . $final_missing, 0); |
4676
|
|
|
} |
4677
|
|
|
// the final excess area is the percentage of the initial polygon's size that is covered by the user's polygon outside of the initial polygon |
4678
|
|
|
$final_excess = round((((float) $poly_user_area - (float) $overlap) / (float) $poly_answer_area) * 100); |
4679
|
|
|
if ($debug > 1) { |
4680
|
|
|
error_log(__LINE__ . ' - Final excess is ' . $final_excess, 0); |
4681
|
|
|
} |
4682
|
|
|
} |
4683
|
|
|
|
4684
|
|
|
//checking the destination parameters parsing the "@@" |
4685
|
|
|
$destination_items = explode('@@', $answerDestination); |
4686
|
|
|
$threadhold_total = $destination_items[0]; |
4687
|
|
|
$threadhold_items = explode(';', $threadhold_total); |
4688
|
|
|
$threadhold1 = $threadhold_items[0]; // overlap |
4689
|
|
|
$threadhold2 = $threadhold_items[1]; // excess |
4690
|
|
|
$threadhold3 = $threadhold_items[2]; //missing |
4691
|
|
|
// if is delineation |
4692
|
|
|
if ($answerId === 1) { |
4693
|
|
|
//setting colors |
4694
|
|
|
if ($final_overlap >= $threadhold1) { |
4695
|
|
|
$overlap_color = true; //echo 'a'; |
4696
|
|
|
} |
4697
|
|
|
//echo $excess.'-'.$threadhold2; |
4698
|
|
|
if ($final_excess <= $threadhold2) { |
4699
|
|
|
$excess_color = true; //echo 'b'; |
4700
|
|
|
} |
4701
|
|
|
//echo '--------'.$missing.'-'.$threadhold3; |
4702
|
|
|
if ($final_missing <= $threadhold3) { |
4703
|
|
|
$missing_color = true; //echo 'c'; |
4704
|
|
|
} |
4705
|
|
|
|
4706
|
|
|
// if pass |
4707
|
|
|
if ($final_overlap >= $threadhold1 && $final_missing <= $threadhold3 && $final_excess <= $threadhold2) { |
4708
|
|
|
$next = 1; //go to the oars |
4709
|
|
|
$result_comment = get_lang('Acceptable'); |
4710
|
|
|
$final_answer = 1; // do not update with update_exercise_attempt |
4711
|
|
|
} else { |
4712
|
|
|
$next = 0; |
4713
|
|
|
$result_comment = get_lang('Unacceptable'); |
4714
|
|
|
$comment = $answerDestination = $objAnswerTmp->selectComment(1); |
4715
|
|
|
$answerDestination = $objAnswerTmp->selectDestination(1); |
4716
|
|
|
//checking the destination parameters parsing the "@@" |
4717
|
|
|
$destination_items = explode('@@', $answerDestination); |
4718
|
|
|
} |
4719
|
|
|
} elseif ($answerId > 1) { |
4720
|
|
|
if ($objAnswerTmp->selectHotspotType($answerId) == 'noerror') { |
4721
|
|
|
if ($debug > 0) { |
4722
|
|
|
error_log(__LINE__ . ' - answerId is of type noerror', 0); |
4723
|
|
|
} |
4724
|
|
|
//type no error shouldn't be treated |
4725
|
|
|
$next = 1; |
4726
|
|
|
continue; |
4727
|
|
|
} |
4728
|
|
|
if ($debug > 0) { |
4729
|
|
|
error_log(__LINE__ . ' - answerId is >1 so we\'re probably in OAR', 0); |
4730
|
|
|
} |
4731
|
|
|
//check the intersection between the oar and the user |
4732
|
|
|
//echo 'user'; print_r($x_user_list); print_r($y_user_list); |
4733
|
|
|
//echo 'official';print_r($x_list);print_r($y_list); |
4734
|
|
|
//$result = get_intersection_data($x_list,$y_list,$x_user_list,$y_user_list); |
4735
|
|
|
$inter = $result['success']; |
4736
|
|
|
|
4737
|
|
|
//$delineation_cord=$objAnswerTmp->selectHotspotCoordinates($answerId); |
4738
|
|
|
$delineation_cord = $objAnswerTmp->selectHotspotCoordinates($answerId); |
4739
|
|
|
|
4740
|
|
|
$poly_answer = convert_coordinates($delineation_cord, '|'); |
4741
|
|
|
$max_coord = poly_get_max($poly_user, $poly_answer); |
4742
|
|
|
$poly_answer_compiled = poly_compile($poly_answer, $max_coord); |
4743
|
|
|
$overlap = poly_touch($poly_user_compiled, $poly_answer_compiled,$max_coord); |
4744
|
|
|
|
4745
|
|
|
if ($overlap == false) { |
4746
|
|
|
//all good, no overlap |
4747
|
|
|
$next = 1; |
4748
|
|
|
continue; |
4749
|
|
|
} else { |
4750
|
|
|
if ($debug > 0) { |
4751
|
|
|
error_log(__LINE__ . ' - Overlap is ' . $overlap . ': OAR hit', 0); |
4752
|
|
|
} |
4753
|
|
|
$organs_at_risk_hit++; |
4754
|
|
|
//show the feedback |
4755
|
|
|
$next = 0; |
4756
|
|
|
$comment = $answerDestination = $objAnswerTmp->selectComment($answerId); |
4757
|
|
|
$answerDestination = $objAnswerTmp->selectDestination($answerId); |
4758
|
|
|
|
4759
|
|
|
$destination_items = explode('@@', $answerDestination); |
4760
|
|
|
$try_hotspot = $destination_items[1]; |
4761
|
|
|
$lp_hotspot = $destination_items[2]; |
4762
|
|
|
$select_question_hotspot = $destination_items[3]; |
4763
|
|
|
$url_hotspot=$destination_items[4]; |
4764
|
|
|
} |
4765
|
|
|
} |
4766
|
|
|
} else { // the first delineation feedback |
4767
|
|
|
if ($debug > 0) { |
4768
|
|
|
error_log(__LINE__ . ' first', 0); |
4769
|
|
|
} |
4770
|
|
|
} |
4771
|
|
|
break; |
4772
|
|
|
case HOT_SPOT_ORDER: |
4773
|
|
|
ExerciseShowFunctions::display_hotspot_order_answer( |
4774
|
|
|
$feedback_type, |
4775
|
|
|
$answerId, |
4776
|
|
|
$answer, |
4777
|
|
|
$studentChoice, |
4778
|
|
|
$answerComment |
4779
|
|
|
); |
4780
|
|
|
break; |
4781
|
|
|
case DRAGGABLE: |
4782
|
|
|
//no break |
4783
|
|
|
case MATCHING_DRAGGABLE: |
4784
|
|
|
//no break |
4785
|
|
|
case MATCHING: |
4786
|
|
|
echo '<tr>'; |
4787
|
|
|
echo Display::tag('td', $answerMatching[$answerId]); |
4788
|
|
|
echo Display::tag( |
4789
|
|
|
'td', |
4790
|
|
|
"$user_answer / " . Display::tag( |
4791
|
|
|
'strong', |
4792
|
|
|
$answerMatching[$answerCorrect], |
4793
|
|
|
['style' => 'color: #008000; font-weight: bold;'] |
4794
|
|
|
) |
4795
|
|
|
); |
4796
|
|
|
echo '</tr>'; |
4797
|
|
|
|
4798
|
|
|
break; |
4799
|
|
|
} |
4800
|
|
|
} |
4801
|
|
|
} |
4802
|
|
|
if ($debug) error_log(' ------ '); |
4803
|
|
|
} // end for that loops over all answers of the current question |
4804
|
|
|
|
4805
|
|
|
if ($debug) error_log('-- end answer loop --'); |
4806
|
|
|
|
4807
|
|
|
$final_answer = true; |
4808
|
|
|
|
4809
|
|
|
foreach ($real_answers as $my_answer) { |
4810
|
|
|
if (!$my_answer) { |
4811
|
|
|
$final_answer = false; |
4812
|
|
|
} |
4813
|
|
|
} |
4814
|
|
|
|
4815
|
|
|
//we add the total score after dealing with the answers |
4816
|
|
|
if ($answerType == MULTIPLE_ANSWER_COMBINATION || |
4817
|
|
|
$answerType == MULTIPLE_ANSWER_COMBINATION_TRUE_FALSE |
4818
|
|
|
) { |
4819
|
|
|
if ($final_answer) { |
4820
|
|
|
//getting only the first score where we save the weight of all the question |
4821
|
|
|
$answerWeighting = $objAnswerTmp->selectWeighting(1); |
4822
|
|
|
$questionScore += $answerWeighting; |
4823
|
|
|
$totalScore += $answerWeighting; |
4824
|
|
|
} |
4825
|
|
|
} |
4826
|
|
|
|
4827
|
|
|
//Fixes multiple answer question in order to be exact |
4828
|
|
|
//if ($answerType == MULTIPLE_ANSWER || $answerType == GLOBAL_MULTIPLE_ANSWER) { |
4829
|
|
|
/* if ($answerType == GLOBAL_MULTIPLE_ANSWER) { |
4830
|
|
|
$diff = @array_diff($answer_correct_array, $real_answers); |
4831
|
|
|
|
4832
|
|
|
// All good answers or nothing works like exact |
4833
|
|
|
|
4834
|
|
|
$counter = 1; |
4835
|
|
|
$correct_answer = true; |
4836
|
|
|
foreach ($real_answers as $my_answer) { |
4837
|
|
|
if ($debug) |
4838
|
|
|
error_log(" my_answer: $my_answer answer_correct_array[counter]: ".$answer_correct_array[$counter]); |
4839
|
|
|
if ($my_answer != $answer_correct_array[$counter]) { |
4840
|
|
|
$correct_answer = false; |
4841
|
|
|
break; |
4842
|
|
|
} |
4843
|
|
|
$counter++; |
4844
|
|
|
} |
4845
|
|
|
|
4846
|
|
|
if ($debug) error_log(" answer_correct_array: ".print_r($answer_correct_array, 1).""); |
4847
|
|
|
if ($debug) error_log(" real_answers: ".print_r($real_answers, 1).""); |
4848
|
|
|
if ($debug) error_log(" correct_answer: ".$correct_answer); |
4849
|
|
|
|
4850
|
|
|
if ($correct_answer == false) { |
4851
|
|
|
$questionScore = 0; |
4852
|
|
|
} |
4853
|
|
|
|
4854
|
|
|
// This makes the result non exact |
4855
|
|
|
if (!empty($diff)) { |
4856
|
|
|
$questionScore = 0; |
4857
|
|
|
} |
4858
|
|
|
}*/ |
4859
|
|
|
|
4860
|
|
|
$extra_data = array( |
4861
|
|
|
'final_overlap' => $final_overlap, |
4862
|
|
|
'final_missing'=>$final_missing, |
4863
|
|
|
'final_excess'=> $final_excess, |
4864
|
|
|
'overlap_color' => $overlap_color, |
4865
|
|
|
'missing_color'=>$missing_color, |
4866
|
|
|
'excess_color'=> $excess_color, |
4867
|
|
|
'threadhold1' => $threadhold1, |
4868
|
|
|
'threadhold2'=>$threadhold2, |
4869
|
|
|
'threadhold3'=> $threadhold3, |
4870
|
|
|
); |
4871
|
|
|
if ($from == 'exercise_result') { |
4872
|
|
|
// if answer is hotspot. To the difference of exercise_show.php, |
4873
|
|
|
// we use the results from the session (from_db=0) |
4874
|
|
|
// TODO Change this, because it is wrong to show the user |
4875
|
|
|
// some results that haven't been stored in the database yet |
4876
|
|
|
if ($answerType == HOT_SPOT || $answerType == HOT_SPOT_ORDER || $answerType == HOT_SPOT_DELINEATION ) { |
4877
|
|
|
|
4878
|
|
|
if ($debug) error_log('$from AND this is a hotspot kind of question '); |
4879
|
|
|
|
4880
|
|
|
$my_exe_id = 0; |
4881
|
|
|
$from_database = 0; |
4882
|
|
|
if ($answerType == HOT_SPOT_DELINEATION) { |
4883
|
|
|
if (0) { |
4884
|
|
|
if ($overlap_color) { |
4885
|
|
|
$overlap_color='green'; |
4886
|
|
|
} else { |
4887
|
|
|
$overlap_color='red'; |
4888
|
|
|
} |
4889
|
|
|
if ($missing_color) { |
4890
|
|
|
$missing_color='green'; |
4891
|
|
|
} else { |
4892
|
|
|
$missing_color='red'; |
4893
|
|
|
} |
4894
|
|
|
if ($excess_color) { |
4895
|
|
|
$excess_color='green'; |
4896
|
|
|
} else { |
4897
|
|
|
$excess_color='red'; |
4898
|
|
|
} |
4899
|
|
|
if (!is_numeric($final_overlap)) { |
4900
|
|
|
$final_overlap = 0; |
4901
|
|
|
} |
4902
|
|
|
if (!is_numeric($final_missing)) { |
4903
|
|
|
$final_missing = 0; |
4904
|
|
|
} |
4905
|
|
|
if (!is_numeric($final_excess)) { |
4906
|
|
|
$final_excess = 0; |
4907
|
|
|
} |
4908
|
|
|
|
4909
|
|
|
if ($final_overlap>100) { |
4910
|
|
|
$final_overlap = 100; |
4911
|
|
|
} |
4912
|
|
|
|
4913
|
|
|
$table_resume='<table class="data_table"> |
4914
|
|
|
<tr class="row_odd" > |
4915
|
|
|
<td></td> |
4916
|
|
|
<td ><b>' . get_lang('Requirements') . '</b></td> |
4917
|
|
|
<td><b>' . get_lang('YourAnswer') . '</b></td> |
4918
|
|
|
</tr> |
4919
|
|
|
<tr class="row_even"> |
4920
|
|
|
<td><b>' . get_lang('Overlap') . '</b></td> |
4921
|
|
|
<td>' . get_lang('Min') . ' ' . $threadhold1 . '</td> |
4922
|
|
|
<td><div style="color:' . $overlap_color . '">' |
4923
|
|
|
. (($final_overlap < 0) ? 0 : intval($final_overlap)) . '</div></td> |
4924
|
|
|
</tr> |
4925
|
|
|
<tr> |
4926
|
|
|
<td><b>' . get_lang('Excess') . '</b></td> |
4927
|
|
|
<td>' . get_lang('Max') . ' ' . $threadhold2 . '</td> |
4928
|
|
|
<td><div style="color:' . $excess_color . '">' |
4929
|
|
|
. (($final_excess < 0) ? 0 : intval($final_excess)) . '</div></td> |
4930
|
|
|
</tr> |
4931
|
|
|
<tr class="row_even"> |
4932
|
|
|
<td><b>' . get_lang('Missing') . '</b></td> |
4933
|
|
|
<td>' . get_lang('Max') . ' ' . $threadhold3 . '</td> |
4934
|
|
|
<td><div style="color:' . $missing_color . '">' |
4935
|
|
|
. (($final_missing < 0) ? 0 : intval($final_missing)) . '</div></td> |
4936
|
|
|
</tr> |
4937
|
|
|
</table>'; |
4938
|
|
|
if ($next == 0) { |
4939
|
|
|
$try = $try_hotspot; |
4940
|
|
|
$lp = $lp_hotspot; |
4941
|
|
|
$destinationid = $select_question_hotspot; |
4942
|
|
|
$url = $url_hotspot; |
4943
|
|
|
} else { |
4944
|
|
|
//show if no error |
4945
|
|
|
//echo 'no error'; |
4946
|
|
|
$comment = $answerComment = $objAnswerTmp->selectComment($nbrAnswers); |
4947
|
|
|
$answerDestination = $objAnswerTmp->selectDestination($nbrAnswers); |
4948
|
|
|
} |
4949
|
|
|
|
4950
|
|
|
echo '<h1><div style="color:#333;">' . get_lang('Feedback') . '</div></h1> |
4951
|
|
|
<p style="text-align:center">'; |
4952
|
|
|
|
4953
|
|
|
$message = '<p>' . get_lang('YourDelineation') . '</p>'; |
4954
|
|
|
$message .= $table_resume; |
4955
|
|
|
$message .= '<br />' . get_lang('ResultIs') . ' ' . $result_comment . '<br />'; |
4956
|
|
|
if ($organs_at_risk_hit > 0) { |
4957
|
|
|
$message .= '<p><b>' . get_lang('OARHit') . '</b></p>'; |
4958
|
|
|
} |
4959
|
|
|
$message .='<p>' . $comment . '</p>'; |
4960
|
|
|
echo $message; |
4961
|
|
|
} else { |
4962
|
|
|
echo $hotspot_delineation_result[0]; //prints message |
4963
|
|
|
$from_database = 1; // the hotspot_solution.swf needs this variable |
4964
|
|
|
} |
4965
|
|
|
|
4966
|
|
|
//save the score attempts |
4967
|
|
|
|
4968
|
|
|
if (1) { |
4969
|
|
|
//getting the answer 1 or 0 comes from exercise_submit_modal.php |
4970
|
|
|
$final_answer = $hotspot_delineation_result[1]; |
4971
|
|
|
if ($final_answer == 0) { |
4972
|
|
|
$questionScore = 0; |
4973
|
|
|
} |
4974
|
|
|
// we always insert the answer_id 1 = delineation |
4975
|
|
|
Event::saveQuestionAttempt($questionScore, 1, $quesId, $exeId, 0); |
4976
|
|
|
//in delineation mode, get the answer from $hotspot_delineation_result[1] |
4977
|
|
|
Event::saveExerciseAttemptHotspot( |
4978
|
|
|
$exeId, |
4979
|
|
|
$quesId, |
4980
|
|
|
1, |
4981
|
|
|
$hotspot_delineation_result[1], |
4982
|
|
|
$exerciseResultCoordinates[$quesId] |
4983
|
|
|
); |
4984
|
|
|
} else { |
4985
|
|
|
if ($final_answer==0) { |
4986
|
|
|
$questionScore = 0; |
4987
|
|
|
$answer=0; |
4988
|
|
|
Event::saveQuestionAttempt($questionScore, $answer, $quesId, $exeId, 0); |
4989
|
|
|
if (is_array($exerciseResultCoordinates[$quesId])) { |
4990
|
|
|
foreach($exerciseResultCoordinates[$quesId] as $idx => $val) { |
4991
|
|
|
Event::saveExerciseAttemptHotspot($exeId,$quesId,$idx,0,$val); |
4992
|
|
|
} |
4993
|
|
|
} |
4994
|
|
|
} else { |
4995
|
|
|
Event::saveQuestionAttempt($questionScore, $answer, $quesId, $exeId, 0); |
4996
|
|
|
if (is_array($exerciseResultCoordinates[$quesId])) { |
4997
|
|
|
foreach($exerciseResultCoordinates[$quesId] as $idx => $val) { |
4998
|
|
|
Event::saveExerciseAttemptHotspot($exeId,$quesId,$idx,$choice[$idx],$val); |
4999
|
|
|
} |
5000
|
|
|
} |
5001
|
|
|
} |
5002
|
|
|
} |
5003
|
|
|
$my_exe_id = $exeId; |
5004
|
|
|
} |
5005
|
|
|
} |
5006
|
|
|
|
5007
|
|
|
if ($answerType == HOT_SPOT || $answerType == HOT_SPOT_ORDER) { |
5008
|
|
|
// We made an extra table for the answers |
5009
|
|
|
|
5010
|
|
|
if ($show_result) { |
5011
|
|
|
$relPath = api_get_path(REL_PATH); |
5012
|
|
|
// if ($origin != 'learnpath') { |
5013
|
|
|
echo '</table></td></tr>'; |
5014
|
|
|
echo " |
5015
|
|
|
<tr> |
5016
|
|
|
<td colspan=\"2\"> |
5017
|
|
|
<p><em>" . get_lang('HotSpot') . "</em></p> |
5018
|
|
|
|
5019
|
|
|
<div id=\"hotspot-solution-$questionId\"></div> |
5020
|
|
|
|
5021
|
|
|
<script> |
5022
|
|
|
$(document).on('ready', function () { |
5023
|
|
|
new HotspotQuestion({ |
5024
|
|
|
questionId: $questionId, |
5025
|
|
|
exerciseId: $exeId, |
5026
|
|
|
selector: '#hotspot-solution-$questionId', |
5027
|
|
|
for: 'solution', |
5028
|
|
|
relPath: '$relPath' |
5029
|
|
|
}); |
5030
|
|
|
}); |
5031
|
|
|
|
5032
|
|
|
</script> |
5033
|
|
|
</td> |
5034
|
|
|
</tr> |
5035
|
|
|
"; |
5036
|
|
|
// } |
5037
|
|
|
} |
5038
|
|
|
} |
5039
|
|
|
|
5040
|
|
|
//if ($origin != 'learnpath') { |
5041
|
|
|
if ($show_result) { |
5042
|
|
|
echo '</table>'; |
5043
|
|
|
} |
5044
|
|
|
// } |
5045
|
|
|
} |
5046
|
|
|
unset ($objAnswerTmp); |
5047
|
|
|
|
5048
|
|
|
$totalWeighting += $questionWeighting; |
5049
|
|
|
// Store results directly in the database |
5050
|
|
|
// For all in one page exercises, the results will be |
5051
|
|
|
// stored by exercise_results.php (using the session) |
5052
|
|
|
|
5053
|
|
|
if ($saved_results) { |
5054
|
|
|
if ($debug) error_log("Save question results $saved_results"); |
5055
|
|
|
if ($debug) error_log(print_r($choice ,1 )); |
5056
|
|
|
|
5057
|
|
|
if (empty($choice)) { |
5058
|
|
|
$choice = 0; |
5059
|
|
|
} |
5060
|
|
|
if ($answerType == MULTIPLE_ANSWER_TRUE_FALSE || $answerType == MULTIPLE_ANSWER_COMBINATION_TRUE_FALSE) { |
5061
|
|
|
if ($choice != 0) { |
5062
|
|
|
$reply = array_keys($choice); |
5063
|
|
|
for ($i = 0; $i < sizeof($reply); $i++) { |
5064
|
|
|
$ans = $reply[$i]; |
5065
|
|
|
Event::saveQuestionAttempt( |
5066
|
|
|
$questionScore, |
5067
|
|
|
$ans . ':' . $choice[$ans], |
5068
|
|
|
$quesId, |
5069
|
|
|
$exeId, |
5070
|
|
|
$i, |
5071
|
|
|
$this->id |
5072
|
|
|
); |
5073
|
|
|
if ($debug) { |
5074
|
|
|
error_log('result =>' . $questionScore . ' ' . $ans . ':' . $choice[$ans]); |
5075
|
|
|
} |
5076
|
|
|
} |
5077
|
|
|
} else { |
5078
|
|
|
Event::saveQuestionAttempt($questionScore, 0, $quesId, $exeId, 0, $this->id); |
5079
|
|
|
} |
5080
|
|
|
} elseif ($answerType == MULTIPLE_ANSWER || $answerType == GLOBAL_MULTIPLE_ANSWER) { |
5081
|
|
|
if ($choice != 0) { |
5082
|
|
|
$reply = array_keys($choice); |
5083
|
|
|
|
5084
|
|
|
if ($debug) { |
5085
|
|
|
error_log("reply " . print_r($reply, 1) . ""); |
5086
|
|
|
} |
5087
|
|
|
for ($i = 0; $i < sizeof($reply); $i++) { |
5088
|
|
|
$ans = $reply[$i]; |
5089
|
|
|
Event::saveQuestionAttempt($questionScore, $ans, $quesId, $exeId, $i, $this->id); |
5090
|
|
|
} |
5091
|
|
|
} else { |
5092
|
|
|
Event::saveQuestionAttempt($questionScore, 0, $quesId, $exeId, 0, $this->id); |
5093
|
|
|
} |
5094
|
|
|
} elseif ($answerType == MULTIPLE_ANSWER_COMBINATION) { |
5095
|
|
|
if ($choice != 0) { |
5096
|
|
|
$reply = array_keys($choice); |
5097
|
|
|
for ($i = 0; $i < sizeof($reply); $i++) { |
5098
|
|
|
$ans = $reply[$i]; |
5099
|
|
|
Event::saveQuestionAttempt($questionScore, $ans, $quesId, $exeId, $i, $this->id); |
5100
|
|
|
} |
5101
|
|
|
} else { |
5102
|
|
|
Event::saveQuestionAttempt($questionScore, 0, $quesId, $exeId, 0, $this->id); |
5103
|
|
|
} |
5104
|
|
|
} elseif (in_array($answerType, [MATCHING, DRAGGABLE, MATCHING_DRAGGABLE])) { |
5105
|
|
|
if (isset($matching)) { |
5106
|
|
|
foreach ($matching as $j => $val) { |
5107
|
|
|
Event::saveQuestionAttempt($questionScore, $val, $quesId, $exeId, $j, $this->id); |
5108
|
|
|
} |
5109
|
|
|
} |
5110
|
|
|
} elseif ($answerType == FREE_ANSWER) { |
5111
|
|
|
$answer = $choice; |
5112
|
|
|
Event::saveQuestionAttempt($questionScore, $answer, $quesId, $exeId, 0, $this->id); |
5113
|
|
|
} elseif ($answerType == ORAL_EXPRESSION) { |
5114
|
|
|
$answer = $choice; |
5115
|
|
|
Event::saveQuestionAttempt($questionScore, $answer, $quesId, $exeId, 0, $this->id, $nano); |
5116
|
|
|
} elseif (in_array($answerType, [UNIQUE_ANSWER, UNIQUE_ANSWER_IMAGE, UNIQUE_ANSWER_NO_OPTION])) { |
5117
|
|
|
$answer = $choice; |
5118
|
|
|
Event::saveQuestionAttempt($questionScore, $answer, $quesId, $exeId, 0, $this->id); |
5119
|
|
|
// } elseif ($answerType == HOT_SPOT || $answerType == HOT_SPOT_DELINEATION) { |
5120
|
|
|
} elseif ($answerType == HOT_SPOT) { |
5121
|
|
|
$answer = []; |
5122
|
|
|
|
5123
|
|
|
if (isset($exerciseResultCoordinates[$questionId]) && !empty($exerciseResultCoordinates[$questionId])) { |
5124
|
|
|
Database::delete( |
5125
|
|
|
Database::get_main_table(TABLE_STATISTIC_TRACK_E_HOTSPOT), |
5126
|
|
|
[ |
5127
|
|
|
'hotspot_exe_id = ? AND hotspot_question_id = ? AND c_id = ?' => [ |
5128
|
|
|
$exeId, |
5129
|
|
|
$questionId, |
5130
|
|
|
api_get_course_int_id() |
5131
|
|
|
] |
5132
|
|
|
] |
5133
|
|
|
); |
5134
|
|
|
|
5135
|
|
|
foreach ($exerciseResultCoordinates[$questionId] as $idx => $val) { |
5136
|
|
|
$answer[] = $val; |
5137
|
|
|
|
5138
|
|
|
Event::saveExerciseAttemptHotspot($exeId, $quesId, $idx, $choice[$idx], $val, false, $this->id); |
5139
|
|
|
} |
5140
|
|
|
} |
5141
|
|
|
|
5142
|
|
|
Event::saveQuestionAttempt($questionScore, implode('|', $answer), $quesId, $exeId, 0, $this->id); |
5143
|
|
|
} else { |
5144
|
|
|
Event::saveQuestionAttempt($questionScore, $answer, $quesId, $exeId, 0,$this->id); |
5145
|
|
|
} |
5146
|
|
|
} |
5147
|
|
|
|
5148
|
|
|
if ($propagate_neg == 0 && $questionScore < 0) { |
5149
|
|
|
$questionScore = 0; |
5150
|
|
|
} |
5151
|
|
|
|
5152
|
|
|
if ($saved_results) { |
5153
|
|
|
$stat_table = Database :: get_main_table(TABLE_STATISTIC_TRACK_E_EXERCISES); |
5154
|
|
|
$sql = 'UPDATE ' . $stat_table . ' SET |
5155
|
|
|
exe_result = exe_result + ' . floatval($questionScore) . ' |
5156
|
|
|
WHERE exe_id = ' . $exeId; |
5157
|
|
|
if ($debug) error_log($sql); |
5158
|
|
|
Database::query($sql); |
5159
|
|
|
} |
5160
|
|
|
|
5161
|
|
|
$return_array = array( |
5162
|
|
|
'score' => $questionScore, |
5163
|
|
|
'weight' => $questionWeighting, |
5164
|
|
|
'extra' => $extra_data, |
5165
|
|
|
'open_question' => $arrques, |
5166
|
|
|
'open_answer' => $arrans, |
5167
|
|
|
'answer_type' => $answerType |
5168
|
|
|
); |
5169
|
|
|
|
5170
|
|
|
return $return_array; |
5171
|
|
|
} |
5172
|
|
|
|
5173
|
|
|
/** |
5174
|
|
|
* Sends a notification when a user ends an examn |
5175
|
|
|
* |
5176
|
|
|
*/ |
5177
|
|
|
public function send_mail_notification_for_exam($question_list_answers, $origin, $exe_id) |
5178
|
|
|
{ |
5179
|
|
|
if (api_get_course_setting('email_alert_manager_on_new_quiz') != 1 ) { |
5180
|
|
|
return null; |
5181
|
|
|
} |
5182
|
|
|
// Email configuration settings |
5183
|
|
|
$courseCode = api_get_course_id(); |
5184
|
|
|
$courseInfo = api_get_course_info($courseCode); |
5185
|
|
|
$sessionId = api_get_session_id(); |
5186
|
|
|
|
5187
|
|
|
if (empty($courseInfo)) { |
5188
|
|
|
return false; |
5189
|
|
|
} |
5190
|
|
|
|
5191
|
|
|
$url_email = api_get_path(WEB_CODE_PATH) |
5192
|
|
|
. 'exercice/exercise_show.php?' |
5193
|
|
|
. api_get_cidreq() |
5194
|
|
|
. '&id_session=' |
5195
|
|
|
. $sessionId |
5196
|
|
|
. '&id=' |
5197
|
|
|
. $exe_id |
5198
|
|
|
. '&action=qualify'; |
5199
|
|
|
$user_info = api_get_user_info(api_get_user_id()); |
5200
|
|
|
|
5201
|
|
|
$msg = get_lang('ExerciseAttempted').'<br /><br />' |
5202
|
|
|
.get_lang('AttemptDetails').' : <br /><br />'. |
5203
|
|
|
'<table>' |
5204
|
|
|
.'<tr>' |
5205
|
|
|
.'<td><em>'.get_lang('CourseName').'</em></td>' |
5206
|
|
|
.'<td> <b>#course#</b></td>' |
5207
|
|
|
.'</tr>' |
5208
|
|
|
.'<tr>' |
5209
|
|
|
.'<td>'.get_lang('TestAttempted').'</td>' |
5210
|
|
|
.'<td> #exercise#</td>' |
5211
|
|
|
.'</tr>' |
5212
|
|
|
.'<tr>' |
5213
|
|
|
.'<td>'.get_lang('StudentName').'</td>' |
5214
|
|
|
.'<td> #firstName# #lastName#</td>' |
5215
|
|
|
.'</tr>' |
5216
|
|
|
.'<tr>' |
5217
|
|
|
.'<td>'.get_lang('StudentEmail').'</td>' |
5218
|
|
|
.'<td> #email#</td>' |
5219
|
|
|
.'</tr>' |
5220
|
|
|
.'</table>'; |
5221
|
|
|
$open_question_list = null; |
5222
|
|
|
|
5223
|
|
|
$msg = str_replace("#email#", $user_info['email'], $msg); |
5224
|
|
|
$msg1 = str_replace("#exercise#", $this->exercise, $msg); |
5225
|
|
|
$msg = str_replace("#firstName#", $user_info['firstname'], $msg1); |
5226
|
|
|
$msg1 = str_replace("#lastName#", $user_info['lastname'], $msg); |
5227
|
|
|
$msg = str_replace("#course#", $courseInfo['name'], $msg1); |
5228
|
|
|
|
5229
|
|
|
if ($origin != 'learnpath') { |
5230
|
|
|
$msg.= '<br /><a href="#url#">'.get_lang('ClickToCommentAndGiveFeedback').'</a>'; |
5231
|
|
|
} |
5232
|
|
|
$msg1 = str_replace("#url#", $url_email, $msg); |
5233
|
|
|
$mail_content = $msg1; |
5234
|
|
|
$subject = get_lang('ExerciseAttempted'); |
5235
|
|
|
|
5236
|
|
|
if (!empty($sessionId)) { |
5237
|
|
|
$teachers = CourseManager::get_coach_list_from_course_code($courseCode, $sessionId); |
5238
|
|
|
} else { |
5239
|
|
|
$teachers = CourseManager::get_teacher_list_from_course_code($courseCode); |
5240
|
|
|
} |
5241
|
|
|
|
5242
|
|
|
if (!empty($teachers)) { |
5243
|
|
|
foreach ($teachers as $user_id => $teacher_data) { |
5244
|
|
|
MessageManager::send_message_simple( |
5245
|
|
|
$user_id, |
5246
|
|
|
$subject, |
5247
|
|
|
$mail_content |
5248
|
|
|
); |
5249
|
|
|
} |
5250
|
|
|
} |
5251
|
|
|
} |
5252
|
|
|
|
5253
|
|
|
/** |
5254
|
|
|
* Sends a notification when a user ends an examn |
5255
|
|
|
* |
5256
|
|
|
*/ |
5257
|
|
|
function send_notification_for_open_questions($question_list_answers, $origin, $exe_id) |
5258
|
|
|
{ |
5259
|
|
|
if (api_get_course_setting('email_alert_manager_on_new_quiz') != 1 ) { |
5260
|
|
|
return null; |
5261
|
|
|
} |
5262
|
|
|
// Email configuration settings |
5263
|
|
|
$courseCode = api_get_course_id(); |
5264
|
|
|
$course_info = api_get_course_info($courseCode); |
5265
|
|
|
|
5266
|
|
|
$url_email = api_get_path(WEB_CODE_PATH) |
5267
|
|
|
. 'exercice/exercise_show.php?' |
5268
|
|
|
. api_get_cidreq() |
5269
|
|
|
. '&id_session=' |
5270
|
|
|
. api_get_session_id() |
5271
|
|
|
. '&id=' |
5272
|
|
|
. $exe_id |
5273
|
|
|
. '&action=qualify'; |
5274
|
|
|
$user_info = api_get_user_info(api_get_user_id()); |
5275
|
|
|
|
5276
|
|
|
$msg = get_lang('OpenQuestionsAttempted').'<br /><br />' |
5277
|
|
|
.get_lang('AttemptDetails').' : <br /><br />' |
5278
|
|
|
.'<table>' |
5279
|
|
|
.'<tr>' |
5280
|
|
|
.'<td><em>'.get_lang('CourseName').'</em></td>' |
5281
|
|
|
.'<td> <b>#course#</b></td>' |
5282
|
|
|
.'</tr>' |
5283
|
|
|
.'<tr>' |
5284
|
|
|
.'<td>'.get_lang('TestAttempted').'</td>' |
5285
|
|
|
.'<td> #exercise#</td>' |
5286
|
|
|
.'</tr>' |
5287
|
|
|
.'<tr>' |
5288
|
|
|
.'<td>'.get_lang('StudentName').'</td>' |
5289
|
|
|
.'<td> #firstName# #lastName#</td>' |
5290
|
|
|
.'</tr>' |
5291
|
|
|
.'<tr>' |
5292
|
|
|
.'<td>'.get_lang('StudentEmail').'</td>' |
5293
|
|
|
.'<td> #mail#</td>' |
5294
|
|
|
.'</tr>' |
5295
|
|
|
.'</table>'; |
5296
|
|
|
$open_question_list = null; |
5297
|
|
|
foreach ($question_list_answers as $item) { |
5298
|
|
|
$question = $item['question']; |
5299
|
|
|
$answer = $item['answer']; |
5300
|
|
|
$answer_type = $item['answer_type']; |
5301
|
|
|
|
5302
|
|
|
if (!empty($question) && !empty($answer) && $answer_type == FREE_ANSWER) { |
5303
|
|
|
$open_question_list .= |
5304
|
|
|
'<tr>' |
5305
|
|
|
.'<td width="220" valign="top" bgcolor="#E5EDF8"> '.get_lang('Question').'</td>' |
5306
|
|
|
.'<td width="473" valign="top" bgcolor="#F3F3F3">'.$question.'</td>' |
5307
|
|
|
.'</tr>' |
5308
|
|
|
.'<tr>' |
5309
|
|
|
.'<td width="220" valign="top" bgcolor="#E5EDF8"> '.get_lang('Answer').'</td>' |
5310
|
|
|
.'<td valign="top" bgcolor="#F3F3F3">'.$answer.'</td>' |
5311
|
|
|
.'</tr>'; |
5312
|
|
|
} |
5313
|
|
|
} |
5314
|
|
|
|
5315
|
|
|
if (!empty($open_question_list)) { |
5316
|
|
|
$msg .= '<p><br />'.get_lang('OpenQuestionsAttemptedAre').' :</p>'. |
5317
|
|
|
'<table width="730" height="136" border="0" cellpadding="3" cellspacing="3">'; |
5318
|
|
|
$msg .= $open_question_list; |
5319
|
|
|
$msg .= '</table><br />'; |
5320
|
|
|
|
5321
|
|
|
|
5322
|
|
|
$msg1 = str_replace("#exercise#", $this->exercise, $msg); |
5323
|
|
|
$msg = str_replace("#firstName#", $user_info['firstname'],$msg1); |
5324
|
|
|
$msg1 = str_replace("#lastName#", $user_info['lastname'],$msg); |
5325
|
|
|
$msg = str_replace("#mail#", $user_info['email'],$msg1); |
5326
|
|
|
$msg = str_replace("#course#", $course_info['name'],$msg1); |
5327
|
|
|
|
5328
|
|
|
if ($origin != 'learnpath') { |
5329
|
|
|
$msg .= '<br /><a href="#url#">'.get_lang('ClickToCommentAndGiveFeedback').'</a>'; |
5330
|
|
|
} |
5331
|
|
|
$msg1 = str_replace("#url#", $url_email, $msg); |
5332
|
|
|
$mail_content = $msg1; |
5333
|
|
|
$subject = get_lang('OpenQuestionsAttempted'); |
5334
|
|
|
|
5335
|
|
|
if (api_get_session_id()) { |
5336
|
|
|
$teachers = CourseManager::get_coach_list_from_course_code($courseCode, api_get_session_id()); |
5337
|
|
|
} else { |
5338
|
|
|
$teachers = CourseManager::get_teacher_list_from_course_code($courseCode); |
5339
|
|
|
} |
5340
|
|
|
|
5341
|
|
|
if (!empty($teachers)) { |
5342
|
|
|
foreach ($teachers as $user_id => $teacher_data) { |
5343
|
|
|
MessageManager::send_message_simple( |
5344
|
|
|
$user_id, |
5345
|
|
|
$subject, |
5346
|
|
|
$mail_content |
5347
|
|
|
); |
5348
|
|
|
} |
5349
|
|
|
} |
5350
|
|
|
} |
5351
|
|
|
} |
5352
|
|
|
|
5353
|
|
|
function send_notification_for_oral_questions($question_list_answers, $origin, $exe_id) |
5354
|
|
|
{ |
5355
|
|
|
if (api_get_course_setting('email_alert_manager_on_new_quiz') != 1 ) { |
5356
|
|
|
return null; |
5357
|
|
|
} |
5358
|
|
|
// Email configuration settings |
5359
|
|
|
$courseCode = api_get_course_id(); |
5360
|
|
|
$course_info = api_get_course_info($courseCode); |
5361
|
|
|
|
5362
|
|
|
$url_email = api_get_path(WEB_CODE_PATH) |
5363
|
|
|
. 'exercice/exercise_show.php?' |
5364
|
|
|
. api_get_cidreq() |
5365
|
|
|
. '&id_session=' |
5366
|
|
|
. api_get_session_id() |
5367
|
|
|
. '&id=' |
5368
|
|
|
. $exe_id |
5369
|
|
|
. '&action=qualify'; |
5370
|
|
|
$user_info = api_get_user_info(api_get_user_id()); |
5371
|
|
|
|
5372
|
|
|
$oral_question_list = null; |
5373
|
|
|
foreach ($question_list_answers as $item) { |
5374
|
|
|
$question = $item['question']; |
5375
|
|
|
$answer = $item['answer']; |
5376
|
|
|
$answer_type = $item['answer_type']; |
5377
|
|
|
|
5378
|
|
|
if (!empty($question) && !empty($answer) && $answer_type == ORAL_EXPRESSION) { |
5379
|
|
|
$oral_question_list.='<br /><table width="730" height="136" border="0" cellpadding="3" cellspacing="3">' |
5380
|
|
|
.'<tr>' |
5381
|
|
|
.'<td width="220" valign="top" bgcolor="#E5EDF8"> '.get_lang('Question').'</td>' |
5382
|
|
|
.'<td width="473" valign="top" bgcolor="#F3F3F3">'.$question.'</td>' |
5383
|
|
|
.'</tr>' |
5384
|
|
|
.'<tr>' |
5385
|
|
|
.'<td width="220" valign="top" bgcolor="#E5EDF8"> '.get_lang('Answer').'</td>' |
5386
|
|
|
.'<td valign="top" bgcolor="#F3F3F3">'.$answer.'</td>' |
5387
|
|
|
.'</tr></table>'; |
5388
|
|
|
} |
5389
|
|
|
} |
5390
|
|
|
|
5391
|
|
|
if (!empty($oral_question_list)) { |
5392
|
|
|
$msg = get_lang('OralQuestionsAttempted').'<br /><br /> |
5393
|
|
|
'.get_lang('AttemptDetails').' : <br /><br />' |
5394
|
|
|
.'<table>' |
5395
|
|
|
.'<tr>' |
5396
|
|
|
.'<td><em>'.get_lang('CourseName').'</em></td>' |
5397
|
|
|
.'<td> <b>#course#</b></td>' |
5398
|
|
|
.'</tr>' |
5399
|
|
|
.'<tr>' |
5400
|
|
|
.'<td>'.get_lang('TestAttempted').'</td>' |
5401
|
|
|
.'<td> #exercise#</td>' |
5402
|
|
|
.'</tr>' |
5403
|
|
|
.'<tr>' |
5404
|
|
|
.'<td>'.get_lang('StudentName').'</td>' |
5405
|
|
|
.'<td> #firstName# #lastName#</td>' |
5406
|
|
|
.'</tr>' |
5407
|
|
|
.'<tr>' |
5408
|
|
|
.'<td>'.get_lang('StudentEmail').'</td>' |
5409
|
|
|
.'<td> #mail#</td>' |
5410
|
|
|
.'</tr>' |
5411
|
|
|
.'</table>'; |
5412
|
|
|
$msg .= '<br />'.sprintf(get_lang('OralQuestionsAttemptedAreX'),$oral_question_list).'<br />'; |
5413
|
|
|
$msg1 = str_replace("#exercise#", $this->exercise, $msg); |
5414
|
|
|
$msg = str_replace("#firstName#", $user_info['firstname'], $msg1); |
5415
|
|
|
$msg1 = str_replace("#lastName#", $user_info['lastname'], $msg); |
5416
|
|
|
$msg = str_replace("#mail#", $user_info['email'], $msg1); |
5417
|
|
|
$msg = str_replace("#course#", $course_info['name'], $msg1); |
5418
|
|
|
|
5419
|
|
|
if ($origin != 'learnpath') { |
5420
|
|
|
$msg.= '<br /><a href="#url#">'.get_lang('ClickToCommentAndGiveFeedback').'</a>'; |
5421
|
|
|
} |
5422
|
|
|
$msg1 = str_replace("#url#", $url_email, $msg); |
5423
|
|
|
$mail_content = $msg1; |
5424
|
|
|
$subject = get_lang('OralQuestionsAttempted'); |
5425
|
|
|
|
5426
|
|
|
if (api_get_session_id()) { |
5427
|
|
|
$teachers = CourseManager::get_coach_list_from_course_code($courseCode, api_get_session_id()); |
5428
|
|
|
} else { |
5429
|
|
|
$teachers = CourseManager::get_teacher_list_from_course_code($courseCode); |
5430
|
|
|
} |
5431
|
|
|
|
5432
|
|
|
if (!empty($teachers)) { |
5433
|
|
|
foreach ($teachers as $user_id => $teacher_data) { |
5434
|
|
|
MessageManager::send_message_simple( |
5435
|
|
|
$user_id, |
5436
|
|
|
$subject, |
5437
|
|
|
$mail_content |
5438
|
|
|
); |
5439
|
|
|
} |
5440
|
|
|
} |
5441
|
|
|
} |
5442
|
|
|
} |
5443
|
|
|
|
5444
|
|
|
/** |
5445
|
|
|
* @param array $user_data result of api_get_user_info() |
5446
|
|
|
* @param null $start_date |
5447
|
|
|
* @param null $duration |
5448
|
|
|
* @param string $ip Optional. The user IP |
5449
|
|
|
* @return string |
5450
|
|
|
*/ |
5451
|
|
|
public function show_exercise_result_header($user_data, $start_date = null, $duration = null, $ip = null) |
5452
|
|
|
{ |
5453
|
|
|
$array = array(); |
5454
|
|
|
|
5455
|
|
|
if (!empty($user_data)) { |
5456
|
|
|
$array[] = array('title' => get_lang('Name'), 'content' => $user_data['complete_name']); |
5457
|
|
|
$array[] = array('title' => get_lang('Username'), 'content' => $user_data['username']); |
5458
|
|
|
if (!empty($user_data['official_code'])) { |
5459
|
|
|
$array[] = array( |
5460
|
|
|
'title' => get_lang('OfficialCode'), |
5461
|
|
|
'content' => $user_data['official_code'] |
5462
|
|
|
); |
5463
|
|
|
} |
5464
|
|
|
} |
5465
|
|
|
// Description can be very long and is generally meant to explain |
5466
|
|
|
// rules *before* the exam. Leaving here to make display easier if |
5467
|
|
|
// necessary |
5468
|
|
|
/* |
5469
|
|
|
if (!empty($this->description)) { |
5470
|
|
|
$array[] = array('title' => get_lang("Description"), 'content' => $this->description); |
5471
|
|
|
} |
5472
|
|
|
*/ |
5473
|
|
|
if (!empty($start_date)) { |
5474
|
|
|
$array[] = array('title' => get_lang('StartDate'), 'content' => $start_date); |
5475
|
|
|
} |
5476
|
|
|
|
5477
|
|
|
if (!empty($duration)) { |
5478
|
|
|
$array[] = array('title' => get_lang('Duration'), 'content' => $duration); |
5479
|
|
|
} |
5480
|
|
|
|
5481
|
|
|
if (!empty($ip)) { |
5482
|
|
|
$array[] = array('title' => get_lang('IP'), 'content' => $ip); |
5483
|
|
|
} |
5484
|
|
|
$html = '<div class="question-result">'; |
5485
|
|
|
$html .= Display::page_header( |
5486
|
|
|
Display::return_icon('test-quiz.png', get_lang('Result'),null, ICON_SIZE_MEDIUM).' '.$this->exercise.' : '.get_lang('Result') |
5487
|
|
|
); |
5488
|
|
|
$html .= Display::description($array); |
5489
|
|
|
$html .="</div>"; |
5490
|
|
|
return $html; |
5491
|
|
|
} |
5492
|
|
|
|
5493
|
|
|
/** |
5494
|
|
|
* Create a quiz from quiz data |
5495
|
|
|
* @param string Title |
5496
|
|
|
* @param int Time before it expires (in minutes) |
5497
|
|
|
* @param int Type of exercise |
5498
|
|
|
* @param int Whether it's randomly picked questions (1) or not (0) |
5499
|
|
|
* @param int Whether the exercise is visible to the user (1) or not (0) |
5500
|
|
|
* @param int Whether the results are show to the user (0) or not (1) |
5501
|
|
|
* @param int Maximum number of attempts (0 if no limit) |
5502
|
|
|
* @param int Feedback type |
5503
|
|
|
* @todo this was function was added due the import exercise via CSV |
5504
|
|
|
* @return int New exercise ID |
5505
|
|
|
*/ |
5506
|
|
|
public function createExercise( |
5507
|
|
|
$title, |
5508
|
|
|
$expired_time = 0, |
5509
|
|
|
$type = 2, |
5510
|
|
|
$random = 0, |
5511
|
|
|
$active = 1, |
5512
|
|
|
$results_disabled = 0, |
5513
|
|
|
$max_attempt = 0, |
5514
|
|
|
$feedback = 3, |
5515
|
|
|
$propagateNegative = 0 |
5516
|
|
|
) { |
5517
|
|
|
$tbl_quiz = Database::get_course_table(TABLE_QUIZ_TEST); |
5518
|
|
|
$type = intval($type); |
5519
|
|
|
$random = intval($random); |
5520
|
|
|
$active = intval($active); |
5521
|
|
|
$results_disabled = intval($results_disabled); |
5522
|
|
|
$max_attempt = intval($max_attempt); |
5523
|
|
|
$feedback = intval($feedback); |
5524
|
|
|
$expired_time = intval($expired_time); |
5525
|
|
|
$title = Database::escape_string($title); |
5526
|
|
|
$propagateNegative = intval($propagateNegative); |
5527
|
|
|
$sessionId = api_get_session_id(); |
5528
|
|
|
$course_id = api_get_course_int_id(); |
5529
|
|
|
// Save a new quiz |
5530
|
|
|
$sql = "INSERT INTO $tbl_quiz ( |
5531
|
|
|
c_id, |
5532
|
|
|
title, |
5533
|
|
|
type, |
5534
|
|
|
random, |
5535
|
|
|
active, |
5536
|
|
|
results_disabled, |
5537
|
|
|
max_attempt, |
5538
|
|
|
start_time, |
5539
|
|
|
end_time, |
5540
|
|
|
feedback_type, |
5541
|
|
|
expired_time, |
5542
|
|
|
session_id, |
5543
|
|
|
propagate_neg |
5544
|
|
|
) |
5545
|
|
|
VALUES ( |
5546
|
|
|
'$course_id', |
5547
|
|
|
'$title', |
5548
|
|
|
$type, |
5549
|
|
|
$random, |
5550
|
|
|
$active, |
5551
|
|
|
$results_disabled, |
5552
|
|
|
$max_attempt, |
5553
|
|
|
'', |
5554
|
|
|
'', |
5555
|
|
|
$feedback, |
5556
|
|
|
$expired_time, |
5557
|
|
|
$sessionId, |
5558
|
|
|
$propagateNegative |
5559
|
|
|
)"; |
5560
|
|
|
Database::query($sql); |
5561
|
|
|
$quiz_id = Database::insert_id(); |
5562
|
|
|
|
5563
|
|
|
if ($quiz_id) { |
5564
|
|
|
|
5565
|
|
|
$sql = "UPDATE $tbl_quiz SET id = iid WHERE iid = {$quiz_id} "; |
5566
|
|
|
Database::query($sql); |
5567
|
|
|
} |
5568
|
|
|
|
5569
|
|
|
return $quiz_id; |
5570
|
|
|
} |
5571
|
|
|
|
5572
|
|
|
function process_geometry() |
5573
|
|
|
{ |
5574
|
|
|
|
5575
|
|
|
} |
5576
|
|
|
|
5577
|
|
|
/** |
5578
|
|
|
* Returns the exercise result |
5579
|
|
|
* @param int attempt id |
5580
|
|
|
* @return float exercise result |
5581
|
|
|
*/ |
5582
|
|
|
public function get_exercise_result($exe_id) |
5583
|
|
|
{ |
5584
|
|
|
$result = array(); |
5585
|
|
|
$track_exercise_info = ExerciseLib::get_exercise_track_exercise_info($exe_id); |
5586
|
|
|
|
5587
|
|
|
if (!empty($track_exercise_info)) { |
5588
|
|
|
$totalScore = 0; |
5589
|
|
|
$objExercise = new Exercise(); |
5590
|
|
|
$objExercise->read($track_exercise_info['exe_exo_id']); |
5591
|
|
|
if (!empty($track_exercise_info['data_tracking'])) { |
5592
|
|
|
$question_list = explode(',', $track_exercise_info['data_tracking']); |
5593
|
|
|
} |
5594
|
|
|
foreach ($question_list as $questionId) { |
5595
|
|
|
$question_result = $objExercise->manage_answer( |
5596
|
|
|
$exe_id, |
5597
|
|
|
$questionId, |
5598
|
|
|
'', |
5599
|
|
|
'exercise_show', |
5600
|
|
|
array(), |
5601
|
|
|
false, |
5602
|
|
|
true, |
5603
|
|
|
false, |
5604
|
|
|
$objExercise->selectPropagateNeg() |
5605
|
|
|
); |
5606
|
|
|
$totalScore += $question_result['score']; |
5607
|
|
|
} |
5608
|
|
|
|
5609
|
|
|
if ($objExercise->selectPropagateNeg() == 0 && $totalScore < 0) { |
5610
|
|
|
$totalScore = 0; |
5611
|
|
|
} |
5612
|
|
|
$result = array( |
5613
|
|
|
'score' => $totalScore, |
5614
|
|
|
'weight' => $track_exercise_info['exe_weighting'] |
5615
|
|
|
); |
5616
|
|
|
} |
5617
|
|
|
return $result; |
5618
|
|
|
} |
5619
|
|
|
|
5620
|
|
|
/** |
5621
|
|
|
* Checks if the exercise is visible due a lot of conditions |
5622
|
|
|
* visibility, time limits, student attempts |
5623
|
|
|
* Return associative array |
5624
|
|
|
* value : true if execise visible |
5625
|
|
|
* message : HTML formated message |
5626
|
|
|
* rawMessage : text message |
5627
|
|
|
* @param int $lpId |
5628
|
|
|
* @param int $lpItemId |
5629
|
|
|
* @param int $lpItemViewId |
5630
|
|
|
* @param bool $filterByAdmin |
5631
|
|
|
* @return array |
5632
|
|
|
*/ |
5633
|
|
|
public function is_visible( |
5634
|
|
|
$lpId = 0, |
5635
|
|
|
$lpItemId = 0, |
5636
|
|
|
$lpItemViewId = 0, |
5637
|
|
|
$filterByAdmin = true |
5638
|
|
|
) { |
5639
|
|
|
// 1. By default the exercise is visible |
5640
|
|
|
$isVisible = true; |
5641
|
|
|
$message = null; |
5642
|
|
|
|
5643
|
|
|
// 1.1 Admins and teachers can access to the exercise |
5644
|
|
|
if ($filterByAdmin) { |
5645
|
|
|
if (api_is_platform_admin() || api_is_course_admin()) { |
5646
|
|
|
return array('value' => true, 'message' => ''); |
5647
|
|
|
} |
5648
|
|
|
} |
5649
|
|
|
|
5650
|
|
|
// Deleted exercise. |
5651
|
|
|
if ($this->active == -1) { |
5652
|
|
|
return array( |
5653
|
|
|
'value' => false, |
5654
|
|
|
'message' => Display::return_message(get_lang('ExerciseNotFound'), 'warning', false), |
5655
|
|
|
'rawMessage' => get_lang('ExerciseNotFound') |
5656
|
|
|
); |
5657
|
|
|
} |
5658
|
|
|
|
5659
|
|
|
// Checking visibility in the item_property table. |
5660
|
|
|
$visibility = api_get_item_visibility( |
5661
|
|
|
api_get_course_info(), |
5662
|
|
|
TOOL_QUIZ, |
5663
|
|
|
$this->id, |
5664
|
|
|
api_get_session_id() |
5665
|
|
|
); |
5666
|
|
|
|
5667
|
|
|
if ($visibility == 0 || $visibility == 2) { |
5668
|
|
|
$this->active = 0; |
5669
|
|
|
} |
5670
|
|
|
|
5671
|
|
|
// 2. If the exercise is not active. |
5672
|
|
|
if (empty($lpId)) { |
5673
|
|
|
// 2.1 LP is OFF |
5674
|
|
|
if ($this->active == 0) { |
5675
|
|
|
return array( |
5676
|
|
|
'value' => false, |
5677
|
|
|
'message' => Display::return_message(get_lang('ExerciseNotFound'), 'warning', false), |
5678
|
|
|
'rawMessage' => get_lang('ExerciseNotFound') |
5679
|
|
|
); |
5680
|
|
|
} |
5681
|
|
|
} else { |
5682
|
|
|
// 2.1 LP is loaded |
5683
|
|
|
if ($this->active == 0 && !learnpath::is_lp_visible_for_student($lpId, api_get_user_id())) { |
5684
|
|
|
return array( |
5685
|
|
|
'value' => false, |
5686
|
|
|
'message' => Display::return_message(get_lang('ExerciseNotFound'), 'warning', false), |
5687
|
|
|
'rawMessage' => get_lang('ExerciseNotFound') |
5688
|
|
|
); |
5689
|
|
|
} |
5690
|
|
|
} |
5691
|
|
|
|
5692
|
|
|
//3. We check if the time limits are on |
5693
|
|
|
if ((!empty($this->start_time) && $this->start_time != '0000-00-00 00:00:00') |
5694
|
|
|
|| (!empty($this->end_time) && $this->end_time != '0000-00-00 00:00:00')) { |
5695
|
|
|
$limitTimeExists = true; |
5696
|
|
|
} else { |
5697
|
|
|
$limitTimeExists = false; |
5698
|
|
|
} |
5699
|
|
|
|
5700
|
|
|
if ($limitTimeExists) { |
5701
|
|
|
$timeNow = time(); |
5702
|
|
|
|
5703
|
|
|
$existsStartDate = false; |
5704
|
|
|
$nowIsAfterStartDate = true; |
5705
|
|
|
$existsEndDate = false; |
5706
|
|
|
$nowIsBeforeEndDate = true; |
5707
|
|
|
|
5708
|
|
|
|
5709
|
|
|
if (!empty($this->start_time) && $this->start_time != '0000-00-00 00:00:00') { |
5710
|
|
|
$existsStartDate = true; |
5711
|
|
|
} |
5712
|
|
|
|
5713
|
|
|
if (!empty($this->end_time) && $this->end_time != '0000-00-00 00:00:00') { |
5714
|
|
|
$existsEndDate = true; |
5715
|
|
|
} |
5716
|
|
|
|
5717
|
|
|
// check if we are before-or-after end-or-start date |
5718
|
|
|
if ($existsStartDate && $timeNow < api_strtotime($this->start_time, 'UTC')) { |
5719
|
|
|
$nowIsAfterStartDate = false; |
5720
|
|
|
} |
5721
|
|
|
|
5722
|
|
|
if ($existsEndDate & $timeNow >= api_strtotime($this->end_time, 'UTC')) { |
5723
|
|
|
$nowIsBeforeEndDate = false; |
5724
|
|
|
} |
5725
|
|
|
|
5726
|
|
|
// lets check all cases |
5727
|
|
|
if ($existsStartDate && !$existsEndDate) { |
5728
|
|
|
// exists start date and dont exists end date |
5729
|
|
|
if ($nowIsAfterStartDate) { |
5730
|
|
|
// after start date, no end date |
5731
|
|
|
$isVisible = true; |
5732
|
|
|
$message = sprintf(get_lang('ExerciseAvailableSinceX'), |
5733
|
|
|
api_convert_and_format_date($this->start_time)); |
5734
|
|
|
} else { |
5735
|
|
|
// before start date, no end date |
5736
|
|
|
$isVisible = false; |
5737
|
|
|
$message = sprintf(get_lang('ExerciseAvailableFromX'), |
5738
|
|
|
api_convert_and_format_date($this->start_time)); |
5739
|
|
|
} |
5740
|
|
|
} else if (!$existsStartDate && $existsEndDate) { |
5741
|
|
|
// doesnt exist start date, exists end date |
5742
|
|
|
if ($nowIsBeforeEndDate) { |
5743
|
|
|
// before end date, no start date |
5744
|
|
|
$isVisible = true; |
5745
|
|
|
$message = sprintf(get_lang('ExerciseAvailableUntilX'), |
5746
|
|
|
api_convert_and_format_date($this->end_time)); |
5747
|
|
|
} else { |
5748
|
|
|
// after end date, no start date |
5749
|
|
|
$isVisible = false; |
5750
|
|
|
$message = sprintf(get_lang('ExerciseAvailableUntilX'), |
5751
|
|
|
api_convert_and_format_date($this->end_time)); |
5752
|
|
|
} |
5753
|
|
|
} elseif ($existsStartDate && $existsEndDate) { |
5754
|
|
|
// exists start date and end date |
5755
|
|
|
if ($nowIsAfterStartDate) { |
5756
|
|
|
if ($nowIsBeforeEndDate) { |
5757
|
|
|
// after start date and before end date |
5758
|
|
|
$isVisible = true; |
5759
|
|
|
$message = sprintf(get_lang('ExerciseIsActivatedFromXToY'), |
5760
|
|
|
api_convert_and_format_date($this->start_time), |
5761
|
|
|
api_convert_and_format_date($this->end_time)); |
5762
|
|
|
} else { |
5763
|
|
|
// after start date and after end date |
5764
|
|
|
$isVisible = false; |
5765
|
|
|
$message = sprintf(get_lang('ExerciseWasActivatedFromXToY'), |
5766
|
|
|
api_convert_and_format_date($this->start_time), |
5767
|
|
|
api_convert_and_format_date($this->end_time)); |
5768
|
|
|
} |
5769
|
|
|
} else { |
5770
|
|
|
if ($nowIsBeforeEndDate) { |
5771
|
|
|
// before start date and before end date |
5772
|
|
|
$isVisible = false; |
5773
|
|
|
$message = sprintf(get_lang('ExerciseWillBeActivatedFromXToY'), |
5774
|
|
|
api_convert_and_format_date($this->start_time), |
5775
|
|
|
api_convert_and_format_date($this->end_time)); |
5776
|
|
|
} |
5777
|
|
|
// case before start date and after end date is impossible |
5778
|
|
|
} |
5779
|
|
|
} elseif (!$existsStartDate && !$existsEndDate) { |
5780
|
|
|
// doesnt exist start date nor end date |
5781
|
|
|
$isVisible = true; |
5782
|
|
|
$message = ""; |
5783
|
|
|
} |
5784
|
|
|
} |
5785
|
|
|
|
5786
|
|
|
// 4. We check if the student have attempts |
5787
|
|
|
$exerciseAttempts = $this->selectAttempts(); |
5788
|
|
|
|
5789
|
|
|
if ($isVisible) { |
5790
|
|
|
if ($exerciseAttempts > 0) { |
5791
|
|
|
|
5792
|
|
|
$attemptCount = Event::get_attempt_count_not_finished( |
5793
|
|
|
api_get_user_id(), |
5794
|
|
|
$this->id, |
5795
|
|
|
$lpId, |
5796
|
|
|
$lpItemId, |
5797
|
|
|
$lpItemViewId |
5798
|
|
|
); |
5799
|
|
|
|
5800
|
|
|
if ($attemptCount >= $exerciseAttempts) { |
5801
|
|
|
$message = sprintf( |
5802
|
|
|
get_lang('ReachedMaxAttempts'), |
5803
|
|
|
$this->name, |
5804
|
|
|
$exerciseAttempts |
5805
|
|
|
); |
5806
|
|
|
$isVisible = false; |
5807
|
|
|
} |
5808
|
|
|
} |
5809
|
|
|
} |
5810
|
|
|
|
5811
|
|
|
$rawMessage = ""; |
5812
|
|
|
if (!empty($message)){ |
5813
|
|
|
$rawMessage = $message; |
5814
|
|
|
$message = Display::return_message($message, 'warning', false); |
5815
|
|
|
} |
5816
|
|
|
|
5817
|
|
|
return array( |
5818
|
|
|
'value' => $isVisible, |
5819
|
|
|
'message' => $message, |
5820
|
|
|
'rawMessage' => $rawMessage |
5821
|
|
|
); |
5822
|
|
|
} |
5823
|
|
|
|
5824
|
|
|
public function added_in_lp() |
5825
|
|
|
{ |
5826
|
|
|
$TBL_LP_ITEM = Database::get_course_table(TABLE_LP_ITEM); |
5827
|
|
|
$sql = "SELECT max_score FROM $TBL_LP_ITEM |
5828
|
|
|
WHERE c_id = {$this->course_id} AND item_type = '" . TOOL_QUIZ . "' AND path = '{$this->id}'"; |
5829
|
|
|
$result = Database::query($sql); |
5830
|
|
|
if (Database::num_rows($result) > 0) { |
5831
|
|
|
return true; |
5832
|
|
|
} |
5833
|
|
|
return false; |
5834
|
|
|
} |
5835
|
|
|
|
5836
|
|
|
/** |
5837
|
|
|
* Returns an array with the media list |
5838
|
|
|
* @param array question list |
5839
|
|
|
* @example there's 1 question with iid 5 that belongs to the media question with iid = 100 |
5840
|
|
|
* <code> |
5841
|
|
|
* array (size=2) |
5842
|
|
|
* 999 => |
5843
|
|
|
* array (size=3) |
5844
|
|
|
* 0 => int 7 |
5845
|
|
|
* 1 => int 6 |
5846
|
|
|
* 2 => int 3254 |
5847
|
|
|
* 100 => |
5848
|
|
|
* array (size=1) |
5849
|
|
|
* 0 => int 5 |
5850
|
|
|
* </code> |
5851
|
|
|
* @return array |
5852
|
|
|
*/ |
5853
|
|
|
private function setMediaList($questionList) |
5854
|
|
|
{ |
5855
|
|
|
$mediaList = array(); |
5856
|
|
|
if (!empty($questionList)) { |
5857
|
|
|
foreach ($questionList as $questionId) { |
5858
|
|
|
$objQuestionTmp = Question::read($questionId, $this->course_id); |
5859
|
|
|
|
5860
|
|
|
// If a media question exists |
5861
|
|
|
if (isset($objQuestionTmp->parent_id) && $objQuestionTmp->parent_id != 0) { |
5862
|
|
|
$mediaList[$objQuestionTmp->parent_id][] = $objQuestionTmp->id; |
5863
|
|
|
} else { |
5864
|
|
|
//Always the last item |
5865
|
|
|
$mediaList[999][] = $objQuestionTmp->id; |
5866
|
|
|
} |
5867
|
|
|
} |
5868
|
|
|
} |
5869
|
|
|
$this->mediaList = $mediaList; |
5870
|
|
|
} |
5871
|
|
|
|
5872
|
|
|
/** |
5873
|
|
|
* Returns an array with this form |
5874
|
|
|
* @example |
5875
|
|
|
* <code> |
5876
|
|
|
* array (size=3) |
5877
|
|
|
999 => |
5878
|
|
|
array (size=3) |
5879
|
|
|
0 => int 3422 |
5880
|
|
|
1 => int 3423 |
5881
|
|
|
2 => int 3424 |
5882
|
|
|
100 => |
5883
|
|
|
array (size=2) |
5884
|
|
|
0 => int 3469 |
5885
|
|
|
1 => int 3470 |
5886
|
|
|
101 => |
5887
|
|
|
array (size=1) |
5888
|
|
|
0 => int 3482 |
5889
|
|
|
* </code> |
5890
|
|
|
* The array inside the key 999 means the question list that belongs to the media id = 999, |
5891
|
|
|
* this case is special because 999 means "no media". |
5892
|
|
|
* @return array |
5893
|
|
|
*/ |
5894
|
|
|
public function getMediaList() |
5895
|
|
|
{ |
5896
|
|
|
return $this->mediaList; |
5897
|
|
|
} |
5898
|
|
|
|
5899
|
|
|
/** |
5900
|
|
|
* Is media question activated? |
5901
|
|
|
* @return bool |
5902
|
|
|
*/ |
5903
|
|
|
public function mediaIsActivated() |
5904
|
|
|
{ |
5905
|
|
|
$mediaQuestions = $this->getMediaList(); |
5906
|
|
|
$active = false; |
5907
|
|
|
if (isset($mediaQuestions) && !empty($mediaQuestions)) { |
5908
|
|
|
$media_count = count($mediaQuestions); |
5909
|
|
|
if ($media_count > 1) { |
5910
|
|
|
return true; |
5911
|
|
|
} elseif ($media_count == 1) { |
5912
|
|
|
if (isset($mediaQuestions[999])) { |
5913
|
|
|
return false; |
5914
|
|
|
} else { |
5915
|
|
|
return true; |
5916
|
|
|
} |
5917
|
|
|
} |
5918
|
|
|
} |
5919
|
|
|
|
5920
|
|
|
return $active; |
5921
|
|
|
} |
5922
|
|
|
|
5923
|
|
|
/** |
5924
|
|
|
* Gets question list from the exercise |
5925
|
|
|
* |
5926
|
|
|
* @return array |
5927
|
|
|
*/ |
5928
|
|
|
public function getQuestionList() |
5929
|
|
|
{ |
5930
|
|
|
return $this->questionList; |
5931
|
|
|
} |
5932
|
|
|
|
5933
|
|
|
/** |
5934
|
|
|
* Question list with medias compressed like this |
5935
|
|
|
* @example |
5936
|
|
|
* <code> |
5937
|
|
|
* array( |
5938
|
|
|
* question_id_1, |
5939
|
|
|
* question_id_2, |
5940
|
|
|
* media_id, <- this media id contains question ids |
5941
|
|
|
* question_id_3, |
5942
|
|
|
* ) |
5943
|
|
|
* </code> |
5944
|
|
|
* @return array |
5945
|
|
|
*/ |
5946
|
|
|
public function getQuestionListWithMediasCompressed() |
5947
|
|
|
{ |
5948
|
|
|
return $this->questionList; |
5949
|
|
|
} |
5950
|
|
|
|
5951
|
|
|
/** |
5952
|
|
|
* Question list with medias uncompressed like this |
5953
|
|
|
* @example |
5954
|
|
|
* <code> |
5955
|
|
|
* array( |
5956
|
|
|
* question_id, |
5957
|
|
|
* question_id, |
5958
|
|
|
* question_id, <- belongs to a media id |
5959
|
|
|
* question_id, <- belongs to a media id |
5960
|
|
|
* question_id, |
5961
|
|
|
* ) |
5962
|
|
|
* </code> |
5963
|
|
|
* @return array |
5964
|
|
|
*/ |
5965
|
|
|
public function getQuestionListWithMediasUncompressed() |
5966
|
|
|
{ |
5967
|
|
|
return $this->questionListUncompressed; |
5968
|
|
|
} |
5969
|
|
|
|
5970
|
|
|
/** |
5971
|
|
|
* Sets the question list when the exercise->read() is executed |
5972
|
|
|
*/ |
5973
|
|
|
public function setQuestionList() |
5974
|
|
|
{ |
5975
|
|
|
// Getting question list. |
5976
|
|
|
$questionList = $this->selectQuestionList(true); |
5977
|
|
|
|
5978
|
|
|
$this->setMediaList($questionList); |
5979
|
|
|
|
5980
|
|
|
$this->questionList = $this->transformQuestionListWithMedias($questionList, false); |
5981
|
|
|
$this->questionListUncompressed = $this->transformQuestionListWithMedias($questionList, true); |
5982
|
|
|
} |
5983
|
|
|
|
5984
|
|
|
/** |
5985
|
|
|
* |
5986
|
|
|
* @params array question list |
5987
|
|
|
* @params bool expand or not question list (true show all questions, false show media question id instead of the question ids) |
5988
|
|
|
* |
5989
|
|
|
**/ |
5990
|
|
|
public function transformQuestionListWithMedias($question_list, $expand_media_questions = false) |
5991
|
|
|
{ |
5992
|
|
|
$new_question_list = array(); |
5993
|
|
|
if (!empty($question_list)) { |
5994
|
|
|
$media_questions = $this->getMediaList(); |
5995
|
|
|
|
5996
|
|
|
$media_active = $this->mediaIsActivated($media_questions); |
5997
|
|
|
|
5998
|
|
|
if ($media_active) { |
5999
|
|
|
$counter = 1; |
6000
|
|
|
foreach ($question_list as $question_id) { |
6001
|
|
|
$add_question = true; |
6002
|
|
|
foreach ($media_questions as $media_id => $question_list_in_media) { |
6003
|
|
|
if ($media_id != 999 && in_array($question_id, $question_list_in_media)) { |
6004
|
|
|
$add_question = false; |
6005
|
|
|
if (!in_array($media_id, $new_question_list)) { |
6006
|
|
|
$new_question_list[$counter] = $media_id; |
6007
|
|
|
$counter++; |
6008
|
|
|
} |
6009
|
|
|
break; |
6010
|
|
|
} |
6011
|
|
|
} |
6012
|
|
|
if ($add_question) { |
6013
|
|
|
$new_question_list[$counter] = $question_id; |
6014
|
|
|
$counter++; |
6015
|
|
|
} |
6016
|
|
|
} |
6017
|
|
|
if ($expand_media_questions) { |
6018
|
|
|
$media_key_list = array_keys($media_questions); |
6019
|
|
|
foreach ($new_question_list as &$question_id) { |
6020
|
|
|
if (in_array($question_id, $media_key_list)) { |
6021
|
|
|
$question_id = $media_questions[$question_id]; |
6022
|
|
|
} |
6023
|
|
|
} |
6024
|
|
|
$new_question_list = array_flatten($new_question_list); |
6025
|
|
|
} |
6026
|
|
|
} else { |
6027
|
|
|
$new_question_list = $question_list; |
6028
|
|
|
} |
6029
|
|
|
} |
6030
|
|
|
|
6031
|
|
|
return $new_question_list; |
6032
|
|
|
} |
6033
|
|
|
|
6034
|
|
|
function get_validated_question_list() |
6035
|
|
|
{ |
6036
|
|
|
$tabres = array(); |
6037
|
|
|
$isRandomByCategory = $this->isRandomByCat(); |
6038
|
|
|
if ($isRandomByCategory == 0) { |
6039
|
|
|
if ($this->isRandom()) { |
6040
|
|
|
$tabres = $this->selectRandomList(); |
6041
|
|
|
} else { |
6042
|
|
|
$tabres = $this->selectQuestionList(); |
6043
|
|
|
} |
6044
|
|
|
} else { |
6045
|
|
|
if ($this->isRandom()) { |
6046
|
|
|
// USE question categories |
6047
|
|
|
// get questions by category for this exercise |
6048
|
|
|
// we have to choice $objExercise->random question in each array values of $tabCategoryQuestions |
6049
|
|
|
// key of $tabCategoryQuestions are the categopy id (0 for not in a category) |
6050
|
|
|
// value is the array of question id of this category |
6051
|
|
|
$questionList = array(); |
6052
|
|
|
$tabCategoryQuestions = TestCategory::getQuestionsByCat($this->id); |
6053
|
|
|
$isRandomByCategory = $this->selectRandomByCat(); |
6054
|
|
|
// on tri les categories en fonction du terme entre [] en tete de la description de la categorie |
6055
|
|
|
/* |
6056
|
|
|
* ex de catégories : |
6057
|
|
|
* [biologie] Maitriser les mecanismes de base de la genetique |
6058
|
|
|
* [biologie] Relier les moyens de depenses et les agents infectieux |
6059
|
|
|
* [biologie] Savoir ou est produite l'enrgie dans les cellules et sous quelle forme |
6060
|
|
|
* [chimie] Classer les molles suivant leur pouvoir oxydant ou reacteur |
6061
|
|
|
* [chimie] Connaître la denition de la theoie acide/base selon Brönsted |
6062
|
|
|
* [chimie] Connaître les charges des particules |
6063
|
|
|
* On veut dans l'ordre des groupes definis par le terme entre crochet au debut du titre de la categorie |
6064
|
|
|
*/ |
6065
|
|
|
// If test option is Grouped By Categories |
6066
|
|
|
if ($isRandomByCategory == 2) { |
6067
|
|
|
$tabCategoryQuestions = TestCategory::sortTabByBracketLabel($tabCategoryQuestions); |
6068
|
|
|
} |
6069
|
|
|
while (list($cat_id, $tabquestion) = each($tabCategoryQuestions)) { |
6070
|
|
|
$number_of_random_question = $this->random; |
6071
|
|
|
if ($this->random == -1) { |
6072
|
|
|
$number_of_random_question = count($this->questionList); |
6073
|
|
|
} |
6074
|
|
|
$questionList = array_merge( |
6075
|
|
|
$questionList, |
6076
|
|
|
TestCategory::getNElementsFromArray( |
6077
|
|
|
$tabquestion, |
6078
|
|
|
$number_of_random_question |
6079
|
|
|
) |
6080
|
|
|
); |
6081
|
|
|
} |
6082
|
|
|
// shuffle the question list if test is not grouped by categories |
6083
|
|
|
if ($isRandomByCategory == 1) { |
6084
|
|
|
shuffle($questionList); // or not |
6085
|
|
|
} |
6086
|
|
|
$tabres = $questionList; |
6087
|
|
|
} else { |
|
|
|
|
6088
|
|
|
// Problem, random by category has been selected and |
6089
|
|
|
// we have no $this->isRandom number of question selected |
6090
|
|
|
// Should not happened |
6091
|
|
|
} |
6092
|
|
|
} |
6093
|
|
|
return $tabres; |
6094
|
|
|
} |
6095
|
|
|
|
6096
|
|
|
function get_question_list($expand_media_questions = false) |
6097
|
|
|
{ |
6098
|
|
|
$question_list = $this->get_validated_question_list(); |
6099
|
|
|
$question_list = $this->transform_question_list_with_medias($question_list, $expand_media_questions); |
6100
|
|
|
return $question_list; |
6101
|
|
|
} |
6102
|
|
|
|
6103
|
|
|
function transform_question_list_with_medias($question_list, $expand_media_questions = false) |
6104
|
|
|
{ |
6105
|
|
|
$new_question_list = array(); |
6106
|
|
|
if (!empty($question_list)) { |
6107
|
|
|
$media_questions = $this->getMediaList(); |
6108
|
|
|
$media_active = $this->mediaIsActivated($media_questions); |
6109
|
|
|
|
6110
|
|
|
if ($media_active) { |
6111
|
|
|
$counter = 1; |
6112
|
|
|
foreach ($question_list as $question_id) { |
6113
|
|
|
$add_question = true; |
6114
|
|
|
foreach ($media_questions as $media_id => $question_list_in_media) { |
6115
|
|
|
if ($media_id != 999 && in_array($question_id, $question_list_in_media)) { |
6116
|
|
|
$add_question = false; |
6117
|
|
|
if (!in_array($media_id, $new_question_list)) { |
6118
|
|
|
$new_question_list[$counter] = $media_id; |
6119
|
|
|
$counter++; |
6120
|
|
|
} |
6121
|
|
|
break; |
6122
|
|
|
} |
6123
|
|
|
} |
6124
|
|
|
if ($add_question) { |
6125
|
|
|
$new_question_list[$counter] = $question_id; |
6126
|
|
|
$counter++; |
6127
|
|
|
} |
6128
|
|
|
} |
6129
|
|
|
if ($expand_media_questions) { |
6130
|
|
|
$media_key_list = array_keys($media_questions); |
6131
|
|
|
foreach ($new_question_list as &$question_id) { |
6132
|
|
|
if (in_array($question_id, $media_key_list)) { |
6133
|
|
|
$question_id = $media_questions[$question_id]; |
6134
|
|
|
} |
6135
|
|
|
} |
6136
|
|
|
$new_question_list = array_flatten($new_question_list); |
6137
|
|
|
} |
6138
|
|
|
} else { |
6139
|
|
|
$new_question_list = $question_list; |
6140
|
|
|
} |
6141
|
|
|
} |
6142
|
|
|
return $new_question_list; |
6143
|
|
|
} |
6144
|
|
|
|
6145
|
|
|
/** |
6146
|
|
|
* @param int $exe_id |
6147
|
|
|
* @return array|mixed |
6148
|
|
|
*/ |
6149
|
|
|
public function get_stat_track_exercise_info_by_exe_id($exe_id) |
6150
|
|
|
{ |
6151
|
|
|
$track_exercises = Database :: get_main_table(TABLE_STATISTIC_TRACK_E_EXERCISES); |
6152
|
|
|
$exe_id = intval($exe_id); |
6153
|
|
|
$sql_track = "SELECT * FROM $track_exercises WHERE exe_id = $exe_id "; |
6154
|
|
|
$result = Database::query($sql_track); |
6155
|
|
|
$new_array = array(); |
6156
|
|
|
if (Database::num_rows($result) > 0 ) { |
6157
|
|
|
$new_array = Database::fetch_array($result, 'ASSOC'); |
6158
|
|
|
|
6159
|
|
|
$new_array['duration'] = null; |
6160
|
|
|
|
6161
|
|
|
$start_date = api_get_utc_datetime($new_array['start_date'], true); |
6162
|
|
|
$end_date = api_get_utc_datetime($new_array['exe_date'], true); |
6163
|
|
|
|
6164
|
|
|
if (!empty($start_date) && !empty($end_date)) { |
6165
|
|
|
$start_date = api_strtotime($start_date, 'UTC'); |
6166
|
|
|
$end_date = api_strtotime($end_date, 'UTC'); |
6167
|
|
|
if ($start_date && $end_date) { |
6168
|
|
|
$mytime = $end_date- $start_date; |
6169
|
|
|
$new_learnpath_item = new learnpathItem(null); |
6170
|
|
|
$time_attemp = $new_learnpath_item->get_scorm_time('js', $mytime); |
6171
|
|
|
$h = get_lang('h'); |
6172
|
|
|
$time_attemp = str_replace('NaN', '00' . $h . '00\'00"', $time_attemp); |
6173
|
|
|
$new_array['duration'] = $time_attemp; |
6174
|
|
|
} |
6175
|
|
|
} |
6176
|
|
|
} |
6177
|
|
|
return $new_array; |
6178
|
|
|
} |
6179
|
|
|
|
6180
|
|
|
public function edit_question_to_remind($exe_id, $question_id, $action = 'add') |
6181
|
|
|
{ |
6182
|
|
|
$exercise_info = self::get_stat_track_exercise_info_by_exe_id($exe_id); |
6183
|
|
|
$question_id = intval($question_id); |
6184
|
|
|
$exe_id = intval($exe_id); |
6185
|
|
|
$track_exercises = Database :: get_main_table(TABLE_STATISTIC_TRACK_E_EXERCISES); |
6186
|
|
|
if ($exercise_info) { |
6187
|
|
|
|
6188
|
|
|
if (empty($exercise_info['questions_to_check'])) { |
6189
|
|
|
if ($action == 'add') { |
6190
|
|
|
$sql = "UPDATE $track_exercises SET questions_to_check = '$question_id' WHERE exe_id = $exe_id "; |
6191
|
|
|
$result = Database::query($sql); |
6192
|
|
|
} |
6193
|
|
|
} else { |
6194
|
|
|
$remind_list = explode(',',$exercise_info['questions_to_check']); |
6195
|
|
|
|
6196
|
|
|
$remind_list_string = ''; |
6197
|
|
|
if ($action == 'add') { |
6198
|
|
|
if (!in_array($question_id, $remind_list)) { |
6199
|
|
|
$remind_list[] = $question_id; |
6200
|
|
|
if (!empty($remind_list)) { |
6201
|
|
|
sort($remind_list); |
6202
|
|
|
array_filter($remind_list); |
6203
|
|
|
} |
6204
|
|
|
$remind_list_string = implode(',', $remind_list); |
6205
|
|
|
} |
6206
|
|
|
} elseif ($action == 'delete') { |
6207
|
|
|
if (!empty($remind_list)) { |
6208
|
|
|
if (in_array($question_id, $remind_list)) { |
6209
|
|
|
$remind_list = array_flip($remind_list); |
6210
|
|
|
unset($remind_list[$question_id]); |
6211
|
|
|
$remind_list = array_flip($remind_list); |
6212
|
|
|
|
6213
|
|
|
if (!empty($remind_list)) { |
6214
|
|
|
sort($remind_list); |
6215
|
|
|
array_filter($remind_list); |
6216
|
|
|
$remind_list_string = implode(',', $remind_list); |
6217
|
|
|
} |
6218
|
|
|
} |
6219
|
|
|
} |
6220
|
|
|
} |
6221
|
|
|
$remind_list_string = Database::escape_string($remind_list_string); |
6222
|
|
|
$sql = "UPDATE $track_exercises SET questions_to_check = '$remind_list_string' WHERE exe_id = $exe_id "; |
6223
|
|
|
Database::query($sql); |
6224
|
|
|
} |
6225
|
|
|
} |
6226
|
|
|
} |
6227
|
|
|
|
6228
|
|
|
public function fill_in_blank_answer_to_array($answer) |
6229
|
|
|
{ |
6230
|
|
|
api_preg_match_all('/\[[^]]+\]/', $answer, $teacher_answer_list); |
6231
|
|
|
$teacher_answer_list = $teacher_answer_list[0]; |
6232
|
|
|
return $teacher_answer_list; |
6233
|
|
|
} |
6234
|
|
|
|
6235
|
|
|
public function fill_in_blank_answer_to_string($answer) |
6236
|
|
|
{ |
6237
|
|
|
$teacher_answer_list = $this->fill_in_blank_answer_to_array($answer); |
6238
|
|
|
$result = ''; |
6239
|
|
|
if (!empty($teacher_answer_list)) { |
6240
|
|
|
$i = 0; |
6241
|
|
|
foreach ($teacher_answer_list as $teacher_item) { |
6242
|
|
|
$value = null; |
6243
|
|
|
//Cleaning student answer list |
6244
|
|
|
$value = strip_tags($teacher_item); |
6245
|
|
|
$value = api_substr($value, 1, api_strlen($value) - 2); |
6246
|
|
|
$value = explode('/', $value); |
6247
|
|
|
if (!empty($value[0])) { |
6248
|
|
|
$value = trim($value[0]); |
6249
|
|
|
$value = str_replace(' ', '', $value); |
6250
|
|
|
$result .= $value; |
6251
|
|
|
} |
6252
|
|
|
} |
6253
|
|
|
} |
6254
|
|
|
return $result; |
6255
|
|
|
} |
6256
|
|
|
|
6257
|
|
|
function return_time_left_div() |
6258
|
|
|
{ |
6259
|
|
|
$html = '<div id="clock_warning" style="display:none">'; |
6260
|
|
|
$html .= Display::return_message( |
6261
|
|
|
get_lang('ReachedTimeLimit'), |
6262
|
|
|
'warning' |
6263
|
|
|
); |
6264
|
|
|
$html .= ' '; |
6265
|
|
|
$html .= sprintf( |
6266
|
|
|
get_lang('YouWillBeRedirectedInXSeconds'), |
6267
|
|
|
'<span id="counter_to_redirect" class="red_alert"></span>' |
6268
|
|
|
); |
6269
|
|
|
$html .= '</div>'; |
6270
|
|
|
$html .= '<div id="exercise_clock_warning" class="count_down"></div>'; |
6271
|
|
|
return $html; |
6272
|
|
|
} |
6273
|
|
|
|
6274
|
|
|
function get_count_question_list() |
6275
|
|
|
{ |
6276
|
|
|
//Real question count |
6277
|
|
|
$question_count = 0; |
6278
|
|
|
$question_list = $this->get_question_list(); |
6279
|
|
|
if (!empty($question_list)) { |
6280
|
|
|
$question_count = count($question_list); |
6281
|
|
|
} |
6282
|
|
|
return $question_count; |
6283
|
|
|
} |
6284
|
|
|
|
6285
|
|
|
function get_exercise_list_ordered() |
6286
|
|
|
{ |
6287
|
|
|
$table_exercise_order = Database::get_course_table(TABLE_QUIZ_ORDER); |
6288
|
|
|
$course_id = api_get_course_int_id(); |
6289
|
|
|
$session_id = api_get_session_id(); |
6290
|
|
|
$sql = "SELECT exercise_id, exercise_order |
6291
|
|
|
FROM $table_exercise_order |
6292
|
|
|
WHERE c_id = $course_id AND session_id = $session_id |
6293
|
|
|
ORDER BY exercise_order"; |
6294
|
|
|
$result = Database::query($sql); |
6295
|
|
|
$list = array(); |
6296
|
|
|
if (Database::num_rows($result)) { |
6297
|
|
|
while ($row = Database::fetch_array($result, 'ASSOC')) { |
6298
|
|
|
$list[$row['exercise_order']] = $row['exercise_id']; |
6299
|
|
|
} |
6300
|
|
|
} |
6301
|
|
|
return $list; |
6302
|
|
|
} |
6303
|
|
|
|
6304
|
|
|
/** |
6305
|
|
|
* Get categories added in the exercise--category matrix |
6306
|
|
|
* @return bool |
6307
|
|
|
*/ |
6308
|
|
|
public function get_categories_in_exercise() |
6309
|
|
|
{ |
6310
|
|
|
if (!$this->specialCategoryOrders) { |
6311
|
|
|
return false; |
6312
|
|
|
} |
6313
|
|
|
$table = Database::get_course_table(TABLE_QUIZ_REL_CATEGORY); |
6314
|
|
|
if (!empty($this->id)) { |
6315
|
|
|
$sql = "SELECT * FROM $table |
6316
|
|
|
WHERE exercise_id = {$this->id} AND c_id = {$this->course_id} "; |
6317
|
|
|
$result = Database::query($sql); |
6318
|
|
|
$list = array(); |
6319
|
|
|
if (Database::num_rows($result)) { |
6320
|
|
|
while ($row = Database::fetch_array($result, 'ASSOC')) { |
6321
|
|
|
$list[$row['category_id']] = $row; |
6322
|
|
|
} |
6323
|
|
|
return $list; |
6324
|
|
|
} |
6325
|
|
|
} |
6326
|
|
|
return false; |
6327
|
|
|
} |
6328
|
|
|
|
6329
|
|
|
/** |
6330
|
|
|
* @param null $order |
6331
|
|
|
* @return bool |
6332
|
|
|
*/ |
6333
|
|
|
public function get_categories_with_name_in_exercise($order = null) |
6334
|
|
|
{ |
6335
|
|
|
if (!$this->specialCategoryOrders) { |
6336
|
|
|
return false; |
6337
|
|
|
} |
6338
|
|
|
$table = Database::get_course_table(TABLE_QUIZ_REL_CATEGORY); |
6339
|
|
|
$table_category = Database::get_course_table(TABLE_QUIZ_QUESTION_CATEGORY); |
6340
|
|
|
$sql = "SELECT * FROM $table qc |
6341
|
|
|
INNER JOIN $table_category c |
6342
|
|
|
ON (category_id = c.iid) |
6343
|
|
|
WHERE exercise_id = {$this->id} AND qc.c_id = {$this->course_id} "; |
6344
|
|
|
if (!empty($order)) { |
6345
|
|
|
$sql .= "ORDER BY $order "; |
6346
|
|
|
} |
6347
|
|
|
$result = Database::query($sql); |
6348
|
|
|
if (Database::num_rows($result)) { |
6349
|
|
|
while ($row = Database::fetch_array($result, 'ASSOC')) { |
6350
|
|
|
$list[$row['category_id']] = $row; |
6351
|
|
|
} |
6352
|
|
|
return $list; |
6353
|
|
|
} |
6354
|
|
|
return false; |
6355
|
|
|
} |
6356
|
|
|
|
6357
|
|
|
/** |
6358
|
|
|
* Get total number of question that will be parsed when using the category/exercise |
6359
|
|
|
*/ |
6360
|
|
|
public function getNumberQuestionExerciseCategory() |
6361
|
|
|
{ |
6362
|
|
|
if (!$this->specialCategoryOrders) { |
6363
|
|
|
return false; |
6364
|
|
|
} |
6365
|
|
|
|
6366
|
|
|
$table = Database::get_course_table(TABLE_QUIZ_REL_CATEGORY); |
6367
|
|
|
if (!empty($this->id)) { |
6368
|
|
|
$sql = "SELECT SUM(count_questions) count_questions |
6369
|
|
|
FROM $table |
6370
|
|
|
WHERE exercise_id = {$this->id} AND c_id = {$this->course_id}"; |
6371
|
|
|
$result = Database::query($sql); |
6372
|
|
|
if (Database::num_rows($result)) { |
6373
|
|
|
$row = Database::fetch_array($result); |
6374
|
|
|
return $row['count_questions']; |
6375
|
|
|
} |
6376
|
|
|
} |
6377
|
|
|
return 0; |
6378
|
|
|
} |
6379
|
|
|
|
6380
|
|
|
/** |
6381
|
|
|
* Save categories in the TABLE_QUIZ_REL_CATEGORY table |
6382
|
|
|
* @param array $categories |
6383
|
|
|
*/ |
6384
|
|
|
public function save_categories_in_exercise($categories) |
6385
|
|
|
{ |
6386
|
|
|
if (!$this->specialCategoryOrders) { |
6387
|
|
|
return false; |
6388
|
|
|
} |
6389
|
|
|
if (!empty($categories) && !empty($this->id)) { |
6390
|
|
|
$table = Database::get_course_table(TABLE_QUIZ_REL_CATEGORY); |
6391
|
|
|
$sql = "DELETE FROM $table |
6392
|
|
|
WHERE exercise_id = {$this->id} AND c_id = {$this->course_id}"; |
6393
|
|
|
Database::query($sql); |
6394
|
|
|
if (!empty($categories)) { |
6395
|
|
|
foreach ($categories as $category_id => $count_questions) { |
6396
|
|
|
$params = array( |
6397
|
|
|
'c_id' => $this->course_id, |
6398
|
|
|
'exercise_id' => $this->id, |
6399
|
|
|
'category_id' => $category_id, |
6400
|
|
|
'count_questions' => $count_questions |
6401
|
|
|
); |
6402
|
|
|
Database::insert($table, $params); |
6403
|
|
|
} |
6404
|
|
|
} |
6405
|
|
|
} |
6406
|
|
|
} |
6407
|
|
|
|
6408
|
|
|
/** |
6409
|
|
|
* @param array $questionList |
6410
|
|
|
* @param int $currentQuestion |
6411
|
|
|
* @param array $conditions |
6412
|
|
|
* @param string $link |
6413
|
|
|
* @return string |
6414
|
|
|
*/ |
6415
|
|
|
public function progressExercisePaginationBar($questionList, $currentQuestion, $conditions, $link) |
6416
|
|
|
{ |
6417
|
|
|
$mediaQuestions = $this->getMediaList(); |
6418
|
|
|
|
6419
|
|
|
$html = '<div class="exercise_pagination pagination pagination-mini"><ul>'; |
6420
|
|
|
$counter = 0; |
6421
|
|
|
$nextValue = 0; |
6422
|
|
|
$wasMedia = false; |
6423
|
|
|
$before = 0; |
6424
|
|
|
$counterNoMedias = 0; |
6425
|
|
|
foreach ($questionList as $questionId) { |
6426
|
|
|
$isCurrent = $currentQuestion == ($counterNoMedias + 1) ? true : false; |
6427
|
|
|
|
6428
|
|
|
if (!empty($nextValue)) { |
6429
|
|
|
if ($wasMedia) { |
6430
|
|
|
$nextValue = $nextValue - $before + 1; |
6431
|
|
|
} |
6432
|
|
|
} |
6433
|
|
|
|
6434
|
|
|
if (isset($mediaQuestions) && isset($mediaQuestions[$questionId])) { |
6435
|
|
|
$fixedValue = $counterNoMedias; |
6436
|
|
|
|
6437
|
|
|
$html .= Display::progressPaginationBar( |
6438
|
|
|
$nextValue, |
6439
|
|
|
$mediaQuestions[$questionId], |
6440
|
|
|
$currentQuestion, |
6441
|
|
|
$fixedValue, |
6442
|
|
|
$conditions, |
6443
|
|
|
$link, |
6444
|
|
|
true, |
6445
|
|
|
true |
6446
|
|
|
); |
6447
|
|
|
|
6448
|
|
|
$counter += count($mediaQuestions[$questionId]) - 1 ; |
6449
|
|
|
$before = count($questionList); |
6450
|
|
|
$wasMedia = true; |
6451
|
|
|
$nextValue += count($questionList); |
6452
|
|
|
} else { |
6453
|
|
|
$html .= Display::parsePaginationItem($questionId, $isCurrent, $conditions, $link, $counter); |
6454
|
|
|
$counter++; |
6455
|
|
|
$nextValue++; |
6456
|
|
|
$wasMedia = false; |
6457
|
|
|
} |
6458
|
|
|
$counterNoMedias++; |
6459
|
|
|
} |
6460
|
|
|
$html .= '</ul></div>'; |
6461
|
|
|
return $html; |
6462
|
|
|
} |
6463
|
|
|
|
6464
|
|
|
|
6465
|
|
|
/** |
6466
|
|
|
* Shows a list of numbers that represents the question to answer in a exercise |
6467
|
|
|
* |
6468
|
|
|
* @param array $categories |
6469
|
|
|
* @param int $current |
6470
|
|
|
* @param array $conditions |
6471
|
|
|
* @param string $link |
6472
|
|
|
* @return string |
6473
|
|
|
*/ |
6474
|
|
|
public function progressExercisePaginationBarWithCategories( |
6475
|
|
|
$categories, |
6476
|
|
|
$current, |
6477
|
|
|
$conditions = array(), |
6478
|
|
|
$link = null |
6479
|
|
|
) { |
6480
|
|
|
$html = null; |
6481
|
|
|
$counterNoMedias = 0; |
6482
|
|
|
$nextValue = 0; |
6483
|
|
|
$wasMedia = false; |
6484
|
|
|
$before = 0; |
6485
|
|
|
|
6486
|
|
|
if (!empty($categories)) { |
6487
|
|
|
$selectionType = $this->getQuestionSelectionType(); |
6488
|
|
|
$useRootAsCategoryTitle = false; |
6489
|
|
|
|
6490
|
|
|
// Grouping questions per parent category see BT#6540 |
6491
|
|
|
|
6492
|
|
|
if (in_array( |
6493
|
|
|
$selectionType, |
6494
|
|
|
array( |
6495
|
|
|
EX_Q_SELECTION_CATEGORIES_ORDERED_BY_PARENT_QUESTIONS_ORDERED, |
6496
|
|
|
EX_Q_SELECTION_CATEGORIES_ORDERED_BY_PARENT_QUESTIONS_RANDOM |
6497
|
|
|
) |
6498
|
|
|
)) { |
6499
|
|
|
$useRootAsCategoryTitle = true; |
6500
|
|
|
} |
6501
|
|
|
|
6502
|
|
|
// If the exercise is set to only show the titles of the categories |
6503
|
|
|
// at the root of the tree, then pre-order the categories tree by |
6504
|
|
|
// removing children and summing their questions into the parent |
6505
|
|
|
// categories |
6506
|
|
|
|
6507
|
|
|
if ($useRootAsCategoryTitle) { |
6508
|
|
|
// The new categories list starts empty |
6509
|
|
|
$newCategoryList = array(); |
6510
|
|
|
foreach ($categories as $category) { |
6511
|
|
|
$rootElement = $category['root']; |
6512
|
|
|
|
6513
|
|
|
if (isset($category['parent_info'])) { |
6514
|
|
|
$rootElement = $category['parent_info']['id']; |
6515
|
|
|
} |
6516
|
|
|
|
6517
|
|
|
//$rootElement = $category['id']; |
6518
|
|
|
// If the current category's ancestor was never seen |
6519
|
|
|
// before, then declare it and assign the current |
6520
|
|
|
// category to it. |
6521
|
|
|
if (!isset($newCategoryList[$rootElement])) { |
6522
|
|
|
$newCategoryList[$rootElement] = $category; |
6523
|
|
|
} else { |
6524
|
|
|
// If it was already seen, then merge the previous with |
6525
|
|
|
// the current category |
6526
|
|
|
$oldQuestionList = $newCategoryList[$rootElement]['question_list']; |
6527
|
|
|
$category['question_list'] = array_merge($oldQuestionList , $category['question_list']); |
6528
|
|
|
$newCategoryList[$rootElement] = $category; |
6529
|
|
|
} |
6530
|
|
|
} |
6531
|
|
|
// Now use the newly built categories list, with only parents |
6532
|
|
|
$categories = $newCategoryList; |
6533
|
|
|
} |
6534
|
|
|
|
6535
|
|
|
foreach ($categories as $category) { |
6536
|
|
|
$questionList = $category['question_list']; |
6537
|
|
|
// Check if in this category there questions added in a media |
6538
|
|
|
$mediaQuestionId = $category['media_question']; |
6539
|
|
|
$isMedia = false; |
6540
|
|
|
$fixedValue = null; |
6541
|
|
|
|
6542
|
|
|
// Media exists! |
6543
|
|
|
if ($mediaQuestionId != 999) { |
6544
|
|
|
$isMedia = true; |
6545
|
|
|
$fixedValue = $counterNoMedias; |
6546
|
|
|
} |
6547
|
|
|
|
6548
|
|
|
//$categoryName = $category['path']; << show the path |
6549
|
|
|
$categoryName = $category['name']; |
6550
|
|
|
|
6551
|
|
|
if ($useRootAsCategoryTitle) { |
6552
|
|
|
if (isset($category['parent_info'])) { |
6553
|
|
|
$categoryName = $category['parent_info']['title']; |
6554
|
|
|
} |
6555
|
|
|
} |
6556
|
|
|
$html .= '<div class="row">'; |
6557
|
|
|
$html .= '<div class="span2">'.$categoryName.'</div>'; |
6558
|
|
|
$html .= '<div class="span8">'; |
6559
|
|
|
|
6560
|
|
|
if (!empty($nextValue)) { |
6561
|
|
|
if ($wasMedia) { |
6562
|
|
|
$nextValue = $nextValue - $before + 1; |
6563
|
|
|
} |
6564
|
|
|
} |
6565
|
|
|
$html .= Display::progressPaginationBar( |
6566
|
|
|
$nextValue, |
6567
|
|
|
$questionList, |
6568
|
|
|
$current, |
6569
|
|
|
$fixedValue, |
6570
|
|
|
$conditions, |
6571
|
|
|
$link, |
6572
|
|
|
$isMedia, |
6573
|
|
|
true |
6574
|
|
|
); |
6575
|
|
|
$html .= '</div>'; |
6576
|
|
|
$html .= '</div>'; |
6577
|
|
|
|
6578
|
|
|
if ($mediaQuestionId == 999) { |
6579
|
|
|
$counterNoMedias += count($questionList); |
6580
|
|
|
} else { |
6581
|
|
|
$counterNoMedias++; |
6582
|
|
|
} |
6583
|
|
|
|
6584
|
|
|
$nextValue += count($questionList); |
6585
|
|
|
$before = count($questionList); |
6586
|
|
|
|
6587
|
|
|
if ($mediaQuestionId != 999) { |
6588
|
|
|
$wasMedia = true; |
6589
|
|
|
} else { |
6590
|
|
|
$wasMedia = false; |
6591
|
|
|
} |
6592
|
|
|
|
6593
|
|
|
} |
6594
|
|
|
} |
6595
|
|
|
return $html; |
6596
|
|
|
} |
6597
|
|
|
|
6598
|
|
|
/** |
6599
|
|
|
* Renders a question list |
6600
|
|
|
* |
6601
|
|
|
* @param array $questionList (with media questions compressed) |
6602
|
|
|
* @param int $currentQuestion |
6603
|
|
|
* @param array $exerciseResult |
6604
|
|
|
* @param array $attemptList |
6605
|
|
|
* @param array $remindList |
6606
|
|
|
*/ |
6607
|
|
|
public function renderQuestionList($questionList, $currentQuestion, $exerciseResult, $attemptList, $remindList) |
6608
|
|
|
{ |
6609
|
|
|
$mediaQuestions = $this->getMediaList(); |
6610
|
|
|
$i = 0; |
6611
|
|
|
|
6612
|
|
|
// Normal question list render (medias compressed) |
6613
|
|
|
foreach ($questionList as $questionId) { |
6614
|
|
|
$i++; |
6615
|
|
|
// For sequential exercises |
6616
|
|
|
if ($this->type == ONE_PER_PAGE) { |
6617
|
|
|
// If it is not the right question, goes to the next loop iteration |
6618
|
|
|
if ($currentQuestion != $i) { |
6619
|
|
|
continue; |
6620
|
|
|
} else { |
6621
|
|
|
if ($this->feedback_type != EXERCISE_FEEDBACK_TYPE_DIRECT) { |
6622
|
|
|
// if the user has already answered this question |
6623
|
|
|
if (isset($exerciseResult[$questionId])) { |
6624
|
|
|
Display::display_normal_message(get_lang('AlreadyAnswered')); |
6625
|
|
|
break; |
6626
|
|
|
} |
6627
|
|
|
} |
6628
|
|
|
} |
6629
|
|
|
} |
6630
|
|
|
|
6631
|
|
|
// The $questionList contains the media id we check if this questionId is a media question type |
6632
|
|
|
|
6633
|
|
|
if (isset($mediaQuestions[$questionId]) && $mediaQuestions[$questionId] != 999) { |
6634
|
|
|
|
6635
|
|
|
// The question belongs to a media |
6636
|
|
|
$mediaQuestionList = $mediaQuestions[$questionId]; |
6637
|
|
|
$objQuestionTmp = Question::read($questionId); |
6638
|
|
|
|
6639
|
|
|
$counter = 1; |
6640
|
|
|
if ($objQuestionTmp->type == MEDIA_QUESTION) { |
6641
|
|
|
echo $objQuestionTmp->show_media_content(); |
6642
|
|
|
|
6643
|
|
|
$countQuestionsInsideMedia = count($mediaQuestionList); |
6644
|
|
|
|
6645
|
|
|
// Show questions that belongs to a media |
6646
|
|
|
if (!empty($mediaQuestionList)) { |
6647
|
|
|
// In order to parse media questions we use letters a, b, c, etc. |
6648
|
|
|
$letterCounter = 97; |
6649
|
|
|
foreach ($mediaQuestionList as $questionIdInsideMedia) { |
6650
|
|
|
$isLastQuestionInMedia = false; |
6651
|
|
|
if ($counter == $countQuestionsInsideMedia) { |
6652
|
|
|
$isLastQuestionInMedia = true; |
6653
|
|
|
} |
6654
|
|
|
$this->renderQuestion( |
6655
|
|
|
$questionIdInsideMedia, |
6656
|
|
|
$attemptList, |
6657
|
|
|
$remindList, |
6658
|
|
|
chr($letterCounter), |
6659
|
|
|
$currentQuestion, |
6660
|
|
|
$mediaQuestionList, |
6661
|
|
|
$isLastQuestionInMedia, |
6662
|
|
|
$questionList |
6663
|
|
|
); |
6664
|
|
|
$letterCounter++; |
6665
|
|
|
$counter++; |
6666
|
|
|
} |
6667
|
|
|
} |
6668
|
|
|
} else { |
6669
|
|
|
$this->renderQuestion( |
6670
|
|
|
$questionId, |
6671
|
|
|
$attemptList, |
6672
|
|
|
$remindList, |
6673
|
|
|
$i, |
6674
|
|
|
$currentQuestion, |
6675
|
|
|
null, |
6676
|
|
|
null, |
6677
|
|
|
$questionList |
6678
|
|
|
); |
6679
|
|
|
$i++; |
6680
|
|
|
} |
6681
|
|
|
} else { |
6682
|
|
|
// Normal question render. |
6683
|
|
|
$this->renderQuestion($questionId, $attemptList, $remindList, $i, $currentQuestion, null, null, $questionList); |
6684
|
|
|
} |
6685
|
|
|
|
6686
|
|
|
// For sequential exercises. |
6687
|
|
|
if ($this->type == ONE_PER_PAGE) { |
6688
|
|
|
// quits the loop |
6689
|
|
|
break; |
6690
|
|
|
} |
6691
|
|
|
} |
6692
|
|
|
// end foreach() |
6693
|
|
|
|
6694
|
|
|
if ($this->type == ALL_ON_ONE_PAGE) { |
6695
|
|
|
$exercise_actions = $this->show_button($questionId, $currentQuestion); |
6696
|
|
|
echo Display::div($exercise_actions, array('class'=>'exercise_actions')); |
6697
|
|
|
} |
6698
|
|
|
} |
6699
|
|
|
|
6700
|
|
|
/** |
6701
|
|
|
* @param int $questionId |
6702
|
|
|
* @param array $attemptList |
6703
|
|
|
* @param array $remindList |
6704
|
|
|
* @param int $i |
6705
|
|
|
* @param int $current_question |
6706
|
|
|
* @param array $questions_in_media |
6707
|
|
|
* @param bool $last_question_in_media |
6708
|
|
|
* @param array $realQuestionList |
6709
|
|
|
* @param bool $generateJS |
6710
|
|
|
* @return null |
6711
|
|
|
*/ |
6712
|
|
|
public function renderQuestion( |
6713
|
|
|
$questionId, |
6714
|
|
|
$attemptList, |
6715
|
|
|
$remindList, |
6716
|
|
|
$i, |
6717
|
|
|
$current_question, |
6718
|
|
|
$questions_in_media = array(), |
6719
|
|
|
$last_question_in_media = false, |
6720
|
|
|
$realQuestionList, |
6721
|
|
|
$generateJS = true |
6722
|
|
|
) { |
6723
|
|
|
|
6724
|
|
|
// With this option on the question is loaded via AJAX |
6725
|
|
|
//$generateJS = true; |
6726
|
|
|
//$this->loadQuestionAJAX = true; |
6727
|
|
|
|
6728
|
|
|
if ($generateJS && $this->loadQuestionAJAX) { |
6729
|
|
|
$url = api_get_path(WEB_AJAX_PATH).'exercise.ajax.php?a=get_question&id='.$questionId; |
6730
|
|
|
$params = array( |
6731
|
|
|
'questionId' => $questionId, |
6732
|
|
|
'attemptList'=> $attemptList, |
6733
|
|
|
'remindList' => $remindList, |
6734
|
|
|
'i' => $i, |
6735
|
|
|
'current_question' => $current_question, |
6736
|
|
|
'questions_in_media' => $questions_in_media, |
6737
|
|
|
'last_question_in_media' => $last_question_in_media |
6738
|
|
|
); |
6739
|
|
|
$params = json_encode($params); |
6740
|
|
|
|
6741
|
|
|
$script = '<script> |
6742
|
|
|
$(function(){ |
6743
|
|
|
var params = '.$params.'; |
6744
|
|
|
$.ajax({ |
6745
|
|
|
type: "GET", |
6746
|
|
|
async: false, |
6747
|
|
|
data: params, |
6748
|
|
|
url: "'.$url.'", |
6749
|
|
|
success: function(return_value) { |
6750
|
|
|
$("#ajaxquestiondiv'.$questionId.'").html(return_value); |
6751
|
|
|
} |
6752
|
|
|
}); |
6753
|
|
|
}); |
6754
|
|
|
</script> |
6755
|
|
|
<div id="ajaxquestiondiv'.$questionId.'"></div>'; |
6756
|
|
|
echo $script; |
6757
|
|
|
} else { |
6758
|
|
|
|
6759
|
|
|
global $origin; |
6760
|
|
|
$question_obj = Question::read($questionId); |
6761
|
|
|
$user_choice = isset($attemptList[$questionId]) ? $attemptList[$questionId] : null; |
6762
|
|
|
|
6763
|
|
|
$remind_highlight = null; |
6764
|
|
|
|
6765
|
|
|
//Hides questions when reviewing a ALL_ON_ONE_PAGE exercise see #4542 no_remind_highlight class hide with jquery |
6766
|
|
|
if ($this->type == ALL_ON_ONE_PAGE && isset($_GET['reminder']) && $_GET['reminder'] == 2) { |
6767
|
|
|
$remind_highlight = 'no_remind_highlight'; |
6768
|
|
|
if (in_array($question_obj->type, Question::question_type_no_review())) { |
6769
|
|
|
return null; |
6770
|
|
|
} |
6771
|
|
|
} |
6772
|
|
|
|
6773
|
|
|
$attributes = array('id' =>'remind_list['.$questionId.']'); |
6774
|
|
|
if (is_array($remindList) && in_array($questionId, $remindList)) { |
|
|
|
|
6775
|
|
|
//$attributes['checked'] = 1; |
6776
|
|
|
//$remind_highlight = ' remind_highlight '; |
6777
|
|
|
} |
6778
|
|
|
|
6779
|
|
|
// Showing the question |
6780
|
|
|
|
6781
|
|
|
$exercise_actions = null; |
6782
|
|
|
|
6783
|
|
|
echo '<a id="questionanchor'.$questionId.'"></a><br />'; |
6784
|
|
|
echo '<div id="question_div_'.$questionId.'" class="main_question '.$remind_highlight.'" >'; |
6785
|
|
|
|
6786
|
|
|
// Shows the question + possible answers |
6787
|
|
|
$showTitle = $this->getHideQuestionTitle() == 1 ? false : true; |
6788
|
|
|
echo $this->showQuestion($question_obj, false, $origin, $i, $showTitle, false, $user_choice, false, null, false, $this->getModelType(), $this->categoryMinusOne); |
6789
|
|
|
|
6790
|
|
|
// Button save and continue |
6791
|
|
|
switch ($this->type) { |
6792
|
|
|
case ONE_PER_PAGE: |
6793
|
|
|
$exercise_actions .= $this->show_button($questionId, $current_question, null, $remindList); |
6794
|
|
|
break; |
6795
|
|
|
case ALL_ON_ONE_PAGE: |
6796
|
|
|
$button = '<a href="javascript://" class="btn" onclick="save_now(\''.$questionId.'\', null, true, 1); ">'.get_lang('SaveForNow').'</a>'; |
6797
|
|
|
$button .= '<span id="save_for_now_'.$questionId.'" class="exercise_save_mini_message"></span> '; |
6798
|
|
|
$exercise_actions .= Display::div($button, array('class'=>'exercise_save_now_button')); |
6799
|
|
|
break; |
6800
|
|
|
} |
6801
|
|
|
|
6802
|
|
|
if (!empty($questions_in_media)) { |
6803
|
|
|
$count_of_questions_inside_media = count($questions_in_media); |
6804
|
|
|
if ($count_of_questions_inside_media > 1) { |
6805
|
|
|
$button = '<a href="javascript://" class="btn" onclick="save_now(\''.$questionId.'\', false, false, 0); ">'.get_lang('SaveForNow').'</a>'; |
6806
|
|
|
$button .= '<span id="save_for_now_'.$questionId.'" class="exercise_save_mini_message"></span> '; |
6807
|
|
|
$exercise_actions = Display::div($button, array('class'=>'exercise_save_now_button')); |
6808
|
|
|
} |
6809
|
|
|
|
6810
|
|
|
if ($last_question_in_media && $this->type == ONE_PER_PAGE) { |
6811
|
|
|
$exercise_actions = $this->show_button($questionId, $current_question, $questions_in_media); |
6812
|
|
|
} |
6813
|
|
|
} |
6814
|
|
|
|
6815
|
|
|
// Checkbox review answers |
6816
|
|
|
if ($this->review_answers && !in_array($question_obj->type, Question::question_type_no_review())) { |
6817
|
|
|
$remind_question_div = Display::tag('label', Display::input('checkbox', 'remind_list['.$questionId.']', '', $attributes).get_lang('ReviewQuestionLater'), array('class' => 'checkbox', 'for' =>'remind_list['.$questionId.']')); |
6818
|
|
|
$exercise_actions .= Display::div($remind_question_div, array('class'=>'exercise_save_now_button')); |
6819
|
|
|
} |
6820
|
|
|
|
6821
|
|
|
echo Display::div(' ', array('class'=>'clear')); |
6822
|
|
|
|
6823
|
|
|
$paginationCounter = null; |
6824
|
|
|
if ($this->type == ONE_PER_PAGE) { |
6825
|
|
|
if (empty($questions_in_media)) { |
6826
|
|
|
$paginationCounter = Display::paginationIndicator($current_question, count($realQuestionList)); |
6827
|
|
|
} else { |
6828
|
|
|
if ($last_question_in_media) { |
6829
|
|
|
$paginationCounter = Display::paginationIndicator($current_question, count($realQuestionList)); |
6830
|
|
|
} |
6831
|
|
|
} |
6832
|
|
|
} |
6833
|
|
|
|
6834
|
|
|
echo '<div class="row"><div class="pull-right">'.$paginationCounter.'</div></div>'; |
6835
|
|
|
echo Display::div($exercise_actions, array('class'=>'form-actions')); |
6836
|
|
|
echo '</div>'; |
6837
|
|
|
} |
6838
|
|
|
} |
6839
|
|
|
|
6840
|
|
|
/** |
6841
|
|
|
* Shows a question |
6842
|
|
|
* @param Question $objQuestionTmp |
6843
|
|
|
* @param bool $only_questions if true only show the questions, no exercise title |
6844
|
|
|
* @param bool $origin origin i.e = learnpath |
6845
|
|
|
* @param string $current_item current item from the list of questions |
6846
|
|
|
* @param bool $show_title |
6847
|
|
|
* @param bool $freeze |
6848
|
|
|
* @param array $user_choice |
6849
|
|
|
* @param bool $show_comment |
6850
|
|
|
* @param null $exercise_feedback |
6851
|
|
|
* @param bool $show_answers |
6852
|
|
|
* @param null $modelType |
6853
|
|
|
* @param bool $categoryMinusOne |
6854
|
|
|
* @return bool|null|string |
6855
|
|
|
*/ |
6856
|
|
|
public function showQuestion( |
6857
|
|
|
Question $objQuestionTmp, |
6858
|
|
|
$only_questions = false, |
6859
|
|
|
$origin = false, |
6860
|
|
|
$current_item = '', |
6861
|
|
|
$show_title = true, |
6862
|
|
|
$freeze = false, |
6863
|
|
|
$user_choice = array(), |
6864
|
|
|
$show_comment = false, |
6865
|
|
|
$exercise_feedback = null, |
6866
|
|
|
$show_answers = false, |
6867
|
|
|
$modelType = null, |
6868
|
|
|
$categoryMinusOne = true |
6869
|
|
|
) { |
6870
|
|
|
// Text direction for the current language |
6871
|
|
|
//$is_ltr_text_direction = api_get_text_direction() != 'rtl'; |
6872
|
|
|
// Change false to true in the following line to enable answer hinting |
6873
|
|
|
$debug_mark_answer = $show_answers; //api_is_allowed_to_edit() && false; |
6874
|
|
|
// Reads question information |
6875
|
|
|
if (!$objQuestionTmp) { |
6876
|
|
|
// Question not found |
6877
|
|
|
return false; |
6878
|
|
|
} |
6879
|
|
|
|
6880
|
|
|
$html = null; |
6881
|
|
|
|
6882
|
|
|
$questionId = $objQuestionTmp->id; |
6883
|
|
|
|
6884
|
|
|
if ($exercise_feedback != EXERCISE_FEEDBACK_TYPE_END) { |
6885
|
|
|
$show_comment = false; |
6886
|
|
|
} |
6887
|
|
|
|
6888
|
|
|
$answerType = $objQuestionTmp->selectType(); |
6889
|
|
|
$pictureName = $objQuestionTmp->selectPicture(); |
6890
|
|
|
|
6891
|
|
|
$s = null; |
6892
|
|
|
$form = new FormValidator('question'); |
6893
|
|
|
$renderer = $form->defaultRenderer(); |
6894
|
|
|
$form_template = '{content}'; |
6895
|
|
|
$renderer->setFormTemplate($form_template); |
6896
|
|
|
|
6897
|
|
|
if ($answerType != HOT_SPOT && $answerType != HOT_SPOT_DELINEATION) { |
6898
|
|
|
// Question is not a hotspot |
6899
|
|
|
if (!$only_questions) { |
6900
|
|
|
$questionDescription = $objQuestionTmp->selectDescription(); |
6901
|
|
|
if ($show_title) { |
6902
|
|
|
$categoryName = TestCategory::getCategoryNamesForQuestion($objQuestionTmp->id, null, true, $categoryMinusOne); |
6903
|
|
|
$html .= $categoryName; |
6904
|
|
|
$html .= Display::div($current_item.'. '.$objQuestionTmp->selectTitle(), array('class' => 'question_title')); |
6905
|
|
|
if (!empty($questionDescription)) { |
6906
|
|
|
$html .= Display::div($questionDescription, array('class' => 'question_description')); |
6907
|
|
|
} |
6908
|
|
|
} else { |
6909
|
|
|
$html .= '<div class="media">'; |
6910
|
|
|
$html .= '<div class="pull-left">'; |
6911
|
|
|
$html .= '<div class="media-object">'; |
6912
|
|
|
$html .= Display::div($current_item, array('class' => 'question_no_title')); |
6913
|
|
|
$html .= '</div>'; |
6914
|
|
|
$html .= '</div>'; |
6915
|
|
|
$html .= '<div class="media-body">'; |
6916
|
|
|
if (!empty($questionDescription)) { |
6917
|
|
|
$html .= Display::div($questionDescription, array('class' => 'question_description')); |
6918
|
|
|
} |
6919
|
|
|
$html .= '</div>'; |
6920
|
|
|
$html .= '</div>'; |
6921
|
|
|
} |
6922
|
|
|
} |
6923
|
|
|
|
6924
|
|
|
if (in_array($answerType, array(FREE_ANSWER, ORAL_EXPRESSION)) && $freeze) { |
6925
|
|
|
return null; |
6926
|
|
|
} |
6927
|
|
|
|
6928
|
|
|
$html .= '<div class="question_options">'; |
6929
|
|
|
// construction of the Answer object (also gets all answers details) |
6930
|
|
|
$objAnswerTmp = new Answer($questionId, null, $this); |
6931
|
|
|
|
6932
|
|
|
$nbrAnswers = $objAnswerTmp->selectNbrAnswers(); |
6933
|
|
|
$course_id = api_get_course_int_id(); |
6934
|
|
|
$quiz_question_options = Question::readQuestionOption($questionId, $course_id); |
6935
|
|
|
|
6936
|
|
|
// For "matching" type here, we need something a little bit special |
6937
|
|
|
// because the match between the suggestions and the answers cannot be |
6938
|
|
|
// done easily (suggestions and answers are in the same table), so we |
6939
|
|
|
// have to go through answers first (elems with "correct" value to 0). |
6940
|
|
|
$select_items = array(); |
6941
|
|
|
//This will contain the number of answers on the left side. We call them |
6942
|
|
|
// suggestions here, for the sake of comprehensions, while the ones |
6943
|
|
|
// on the right side are called answers |
6944
|
|
|
$num_suggestions = 0; |
6945
|
|
|
|
6946
|
|
|
if ($answerType == MATCHING || $answerType == DRAGGABLE) { |
6947
|
|
|
if ($answerType == DRAGGABLE) { |
6948
|
|
|
$s .= '<div class="ui-widget ui-helper-clearfix"> |
6949
|
|
|
<ul class="drag_question ui-helper-reset ui-helper-clearfix">'; |
6950
|
|
|
} else { |
6951
|
|
|
$s .= '<div id="drag'.$questionId.'_question" class="drag_question">'; |
6952
|
|
|
$s .= '<table class="data_table">'; |
6953
|
|
|
} |
6954
|
|
|
|
6955
|
|
|
$j = 1; //iterate through answers |
6956
|
|
|
$letter = 'A'; //mark letters for each answer |
6957
|
|
|
$answer_matching = array(); |
6958
|
|
|
$capital_letter = array(); |
6959
|
|
|
//for ($answerId=1; $answerId <= $nbrAnswers; $answerId++) { |
6960
|
|
|
foreach ($objAnswerTmp->answer as $answerId => $answer_item) { |
6961
|
|
|
$answerCorrect = $objAnswerTmp->isCorrect($answerId); |
6962
|
|
|
$answer = $objAnswerTmp->selectAnswer($answerId); |
6963
|
|
|
if ($answerCorrect == 0) { |
6964
|
|
|
// options (A, B, C, ...) that will be put into the list-box |
6965
|
|
|
// have the "correct" field set to 0 because they are answer |
6966
|
|
|
$capital_letter[$j] = $letter; |
6967
|
|
|
//$answer_matching[$j]=$objAnswerTmp->selectAnswerByAutoId($numAnswer); |
6968
|
|
|
$answer_matching[$j] = array('id' => $answerId, 'answer' => $answer); |
6969
|
|
|
$j++; |
6970
|
|
|
$letter++; |
6971
|
|
|
} |
6972
|
|
|
} |
6973
|
|
|
|
6974
|
|
|
$i = 1; |
6975
|
|
|
|
6976
|
|
|
$select_items[0]['id'] = 0; |
6977
|
|
|
$select_items[0]['letter'] = '--'; |
6978
|
|
|
$select_items[0]['answer'] = ''; |
6979
|
|
|
|
6980
|
|
|
foreach ($answer_matching as $id => $value) { |
6981
|
|
|
$select_items[$i]['id'] = $value['id']; |
6982
|
|
|
$select_items[$i]['letter'] = $capital_letter[$id]; |
6983
|
|
|
$select_items[$i]['answer'] = $value['answer']; |
6984
|
|
|
$i++; |
6985
|
|
|
} |
6986
|
|
|
$num_suggestions = ($nbrAnswers - $j) + 1; |
6987
|
|
|
} elseif ($answerType == FREE_ANSWER) { |
6988
|
|
|
$content = isset($user_choice[0]) && !empty($user_choice[0]['answer']) ? $user_choice[0]['answer'] : null; |
6989
|
|
|
$toolBar = 'TestFreeAnswer'; |
6990
|
|
|
if ($modelType == EXERCISE_MODEL_TYPE_COMMITTEE) { |
6991
|
|
|
$toolBar = 'TestFreeAnswerStrict'; |
6992
|
|
|
} |
6993
|
|
|
$form->addElement('html_editor', "choice[".$questionId."]", null, array('id' => "choice[".$questionId."]"), array('ToolbarSet' => $toolBar)); |
6994
|
|
|
$form->setDefaults(array("choice[".$questionId."]" => $content)); |
6995
|
|
|
$s .= $form->return_form(); |
6996
|
|
|
} elseif ($answerType == ORAL_EXPRESSION) { |
6997
|
|
|
// Add nanogong |
6998
|
|
|
if (api_get_setting('document.enable_nanogong') == 'true') { |
6999
|
|
|
|
7000
|
|
|
//@todo pass this as a parameter |
7001
|
|
|
global $exercise_stat_info, $exerciseId; |
7002
|
|
|
|
7003
|
|
|
if (!empty($exercise_stat_info)) { |
7004
|
|
|
$params = array( |
7005
|
|
|
'exercise_id' => $exercise_stat_info['exe_exo_id'], |
7006
|
|
|
'exe_id' => $exercise_stat_info['exe_id'], |
7007
|
|
|
'question_id' => $questionId |
7008
|
|
|
); |
7009
|
|
|
} else { |
7010
|
|
|
$params = array( |
7011
|
|
|
'exercise_id' => $exerciseId, |
7012
|
|
|
'exe_id' => 'temp_exe', |
7013
|
|
|
'question_id' => $questionId |
7014
|
|
|
); |
7015
|
|
|
} |
7016
|
|
|
|
7017
|
|
|
$nano = new Nanogong($params); |
7018
|
|
|
$s .= $nano->show_button(); |
7019
|
|
|
} |
7020
|
|
|
|
7021
|
|
|
$form->addElement('html_editor', "choice[".$questionId."]", null, array('id' => "choice[".$questionId."]"), array('ToolbarSet' => 'TestFreeAnswer')); |
7022
|
|
|
//$form->setDefaults(array("choice[".$questionId."]" => $content)); |
7023
|
|
|
$s .= $form->return_form(); |
7024
|
|
|
} |
7025
|
|
|
|
7026
|
|
|
// Now navigate through the possible answers, using the max number of |
7027
|
|
|
// answers for the question as a limiter |
7028
|
|
|
$lines_count = 1; // a counter for matching-type answers |
7029
|
|
|
|
7030
|
|
|
if ($answerType == MULTIPLE_ANSWER_TRUE_FALSE || $answerType == MULTIPLE_ANSWER_COMBINATION_TRUE_FALSE) { |
7031
|
|
|
$header = Display::tag('th', get_lang('Options')); |
7032
|
|
|
foreach ($objQuestionTmp->options as $item) { |
7033
|
|
|
$header .= Display::tag('th', $item); |
7034
|
|
|
} |
7035
|
|
|
if ($show_comment) { |
7036
|
|
|
$header .= Display::tag('th', get_lang('Feedback')); |
7037
|
|
|
} |
7038
|
|
|
$s .= '<table class="data_table">'; |
7039
|
|
|
$s .= Display::tag('tr', $header, array('style' => 'text-align:left;')); |
7040
|
|
|
} |
7041
|
|
|
|
7042
|
|
|
if ($show_comment) { |
7043
|
|
|
if (in_array($answerType, array(MULTIPLE_ANSWER, MULTIPLE_ANSWER_COMBINATION, UNIQUE_ANSWER, UNIQUE_ANSWER_NO_OPTION, GLOBAL_MULTIPLE_ANSWER))) { |
7044
|
|
|
$header = Display::tag('th', get_lang('Options')); |
7045
|
|
|
if ($exercise_feedback == EXERCISE_FEEDBACK_TYPE_END) { |
7046
|
|
|
$header .= Display::tag('th', get_lang('Feedback')); |
7047
|
|
|
} |
7048
|
|
|
$s .= '<table class="data_table">'; |
7049
|
|
|
$s.= Display::tag('tr', $header, array('style' => 'text-align:left;')); |
7050
|
|
|
} |
7051
|
|
|
} |
7052
|
|
|
|
7053
|
|
|
$matching_correct_answer = 0; |
7054
|
|
|
$user_choice_array = array(); |
7055
|
|
|
if (!empty($user_choice)) { |
7056
|
|
|
foreach ($user_choice as $item) { |
7057
|
|
|
$user_choice_array[] = $item['answer']; |
7058
|
|
|
} |
7059
|
|
|
} |
7060
|
|
|
|
7061
|
|
|
foreach ($objAnswerTmp->answer as $answerId => $answer_item) { |
7062
|
|
|
$answer = $objAnswerTmp->selectAnswer($answerId); |
7063
|
|
|
$answerCorrect = $objAnswerTmp->isCorrect($answerId); |
7064
|
|
|
$comment = $objAnswerTmp->selectComment($answerId); |
7065
|
|
|
|
7066
|
|
|
//$numAnswer = $objAnswerTmp->selectAutoId($answerId); |
7067
|
|
|
$numAnswer = $answerId; |
7068
|
|
|
|
7069
|
|
|
$attributes = array(); |
7070
|
|
|
// Unique answer |
7071
|
|
|
if (in_array($answerType, array(UNIQUE_ANSWER, UNIQUE_ANSWER_IMAGE, UNIQUE_ANSWER_NO_OPTION))) { |
7072
|
|
|
|
7073
|
|
|
$input_id = 'choice-'.$questionId.'-'.$answerId; |
7074
|
|
|
if (isset($user_choice[0]['answer']) && $user_choice[0]['answer'] == $numAnswer) { |
7075
|
|
|
$attributes = array('id' => $input_id, 'checked' => 1, 'selected' => 1); |
7076
|
|
|
} else { |
7077
|
|
|
$attributes = array('id' => $input_id); |
7078
|
|
|
} |
7079
|
|
|
|
7080
|
|
|
if ($debug_mark_answer) { |
7081
|
|
|
if ($answerCorrect) { |
7082
|
|
|
$attributes['checked'] = 1; |
7083
|
|
|
$attributes['selected'] = 1; |
7084
|
|
|
} |
7085
|
|
|
} |
7086
|
|
|
|
7087
|
|
|
$answer = Security::remove_XSS($answer); |
7088
|
|
|
$s .= Display::input('hidden', 'choice2['.$questionId.']', '0'); |
7089
|
|
|
|
7090
|
|
|
$answer_input = null; |
7091
|
|
|
if ($answerType == UNIQUE_ANSWER_IMAGE) { |
7092
|
|
|
$attributes['style'] = 'display:none'; |
7093
|
|
|
$answer_input .= '<div id="answer'.$questionId.$numAnswer.'" style="float:left" class="highlight_image_default highlight_image">'; |
7094
|
|
|
} |
7095
|
|
|
|
7096
|
|
|
$answer_input .= '<label class="radio">'; |
7097
|
|
|
$answer_input .= Display::input('radio', 'choice['.$questionId.']', $numAnswer, $attributes); |
7098
|
|
|
$answer_input .= $answer; |
7099
|
|
|
$answer_input .= '</label>'; |
7100
|
|
|
|
7101
|
|
|
if ($answerType == UNIQUE_ANSWER_IMAGE) { |
7102
|
|
|
$answer_input .= "</div>"; |
7103
|
|
|
} |
7104
|
|
|
|
7105
|
|
|
if ($show_comment) { |
7106
|
|
|
$s .= '<tr><td>'; |
7107
|
|
|
$s .= $answer_input; |
7108
|
|
|
$s .= '</td>'; |
7109
|
|
|
$s .= '<td>'; |
7110
|
|
|
$s .= $comment; |
7111
|
|
|
$s .= '</td>'; |
7112
|
|
|
$s .= '</tr>'; |
7113
|
|
|
} else { |
7114
|
|
|
$s .= $answer_input; |
7115
|
|
|
} |
7116
|
|
|
|
7117
|
|
|
} elseif (in_array($answerType, array(MULTIPLE_ANSWER, MULTIPLE_ANSWER_TRUE_FALSE, GLOBAL_MULTIPLE_ANSWER))) { |
7118
|
|
|
$input_id = 'choice-'.$questionId.'-'.$answerId; |
7119
|
|
|
$answer = Security::remove_XSS($answer); |
7120
|
|
|
|
7121
|
|
|
if (in_array($numAnswer, $user_choice_array)) { |
7122
|
|
|
$attributes = array('id' => $input_id, 'checked' => 1, 'selected' => 1); |
7123
|
|
|
} else { |
7124
|
|
|
$attributes = array('id' => $input_id); |
7125
|
|
|
} |
7126
|
|
|
|
7127
|
|
|
if ($debug_mark_answer) { |
7128
|
|
|
if ($answerCorrect) { |
7129
|
|
|
$attributes['checked'] = 1; |
7130
|
|
|
$attributes['selected'] = 1; |
7131
|
|
|
} |
7132
|
|
|
} |
7133
|
|
|
|
7134
|
|
|
if ($answerType == MULTIPLE_ANSWER || $answerType == GLOBAL_MULTIPLE_ANSWER) { |
7135
|
|
|
$s .= '<input type="hidden" name="choice2['.$questionId.']" value="0" />'; |
7136
|
|
|
|
7137
|
|
|
$answer_input = '<label class="checkbox">'; |
7138
|
|
|
$answer_input .= Display::input('checkbox', 'choice['.$questionId.']['.$numAnswer.']', $numAnswer, $attributes); |
7139
|
|
|
$answer_input .= $answer; |
7140
|
|
|
$answer_input .= '</label>'; |
7141
|
|
|
|
7142
|
|
|
if ($show_comment) { |
7143
|
|
|
$s .= '<tr><td>'; |
7144
|
|
|
$s .= $answer_input; |
7145
|
|
|
$s .= '</td>'; |
7146
|
|
|
$s .= '<td>'; |
7147
|
|
|
$s .= $comment; |
7148
|
|
|
$s .= '</td>'; |
7149
|
|
|
$s .='</tr>'; |
7150
|
|
|
} else { |
7151
|
|
|
$s .= $answer_input; |
7152
|
|
|
} |
7153
|
|
|
} elseif ($answerType == MULTIPLE_ANSWER_TRUE_FALSE) { |
7154
|
|
|
|
7155
|
|
|
$my_choice = array(); |
7156
|
|
|
if (!empty($user_choice_array)) { |
7157
|
|
|
foreach ($user_choice_array as $item) { |
7158
|
|
|
$item = explode(':', $item); |
7159
|
|
|
$my_choice[$item[0]] = $item[1]; |
7160
|
|
|
} |
7161
|
|
|
} |
7162
|
|
|
|
7163
|
|
|
$s .='<tr>'; |
7164
|
|
|
$s .= Display::tag('td', $answer); |
7165
|
|
|
|
7166
|
|
|
if (!empty($quiz_question_options)) { |
7167
|
|
|
foreach ($quiz_question_options as $id => $item) { |
7168
|
|
|
$id = $item['iid']; |
7169
|
|
|
if (isset($my_choice[$numAnswer]) && $id == $my_choice[$numAnswer]) { |
7170
|
|
|
$attributes = array('checked' => 1, 'selected' => 1); |
7171
|
|
|
} else { |
7172
|
|
|
$attributes = array(); |
7173
|
|
|
} |
7174
|
|
|
|
7175
|
|
|
if ($debug_mark_answer) { |
7176
|
|
|
if ($id == $answerCorrect) { |
7177
|
|
|
$attributes['checked'] = 1; |
7178
|
|
|
$attributes['selected'] = 1; |
7179
|
|
|
} |
7180
|
|
|
} |
7181
|
|
|
$s .= Display::tag('td', Display::input('radio', 'choice['.$questionId.']['.$numAnswer.']', $id, $attributes), array('style' => '')); |
7182
|
|
|
} |
7183
|
|
|
} |
7184
|
|
|
|
7185
|
|
|
if ($show_comment) { |
7186
|
|
|
$s .= '<td>'; |
7187
|
|
|
$s .= $comment; |
7188
|
|
|
$s .= '</td>'; |
7189
|
|
|
} |
7190
|
|
|
$s.='</tr>'; |
7191
|
|
|
} |
7192
|
|
|
|
7193
|
|
|
} elseif ($answerType == MULTIPLE_ANSWER_COMBINATION) { |
7194
|
|
|
|
7195
|
|
|
// multiple answers |
7196
|
|
|
$input_id = 'choice-'.$questionId.'-'.$answerId; |
7197
|
|
|
|
7198
|
|
|
if (in_array($numAnswer, $user_choice_array)) { |
7199
|
|
|
$attributes = array('id' => $input_id, 'checked' => 1, 'selected' => 1); |
7200
|
|
|
} else { |
7201
|
|
|
$attributes = array('id' => $input_id); |
7202
|
|
|
} |
7203
|
|
|
|
7204
|
|
|
if ($debug_mark_answer) { |
7205
|
|
|
if ($answerCorrect) { |
7206
|
|
|
$attributes['checked'] = 1; |
7207
|
|
|
$attributes['selected'] = 1; |
7208
|
|
|
} |
7209
|
|
|
} |
7210
|
|
|
|
7211
|
|
|
$answer = Security::remove_XSS($answer); |
7212
|
|
|
$answer_input = '<input type="hidden" name="choice2['.$questionId.']" value="0" />'; |
7213
|
|
|
$answer_input .= '<label class="checkbox">'; |
7214
|
|
|
$answer_input .= Display::input('checkbox', 'choice['.$questionId.']['.$numAnswer.']', 1, $attributes); |
7215
|
|
|
$answer_input .= $answer; |
7216
|
|
|
$answer_input .= '</label>'; |
7217
|
|
|
|
7218
|
|
|
if ($show_comment) { |
7219
|
|
|
$s.= '<tr>'; |
7220
|
|
|
$s .= '<td>'; |
7221
|
|
|
$s.= $answer_input; |
7222
|
|
|
$s .= '</td>'; |
7223
|
|
|
$s .= '<td>'; |
7224
|
|
|
$s .= $comment; |
7225
|
|
|
$s .= '</td>'; |
7226
|
|
|
$s.= '</tr>'; |
7227
|
|
|
} else { |
7228
|
|
|
$s.= $answer_input; |
7229
|
|
|
} |
7230
|
|
|
} elseif ($answerType == MULTIPLE_ANSWER_COMBINATION_TRUE_FALSE) { |
7231
|
|
|
$s .= '<input type="hidden" name="choice2['.$questionId.']" value="0" />'; |
7232
|
|
|
|
7233
|
|
|
$my_choice = array(); |
7234
|
|
|
if (!empty($user_choice_array)) { |
7235
|
|
|
foreach ($user_choice_array as $item) { |
7236
|
|
|
$item = explode(':', $item); |
7237
|
|
|
$my_choice[$item[0]] = $item[1]; |
7238
|
|
|
} |
7239
|
|
|
} |
7240
|
|
|
$answer = Security::remove_XSS($answer); |
7241
|
|
|
$s .='<tr>'; |
7242
|
|
|
$s .= Display::tag('td', $answer); |
7243
|
|
|
|
7244
|
|
|
foreach ($objQuestionTmp->options as $key => $item) { |
7245
|
|
|
if (isset($my_choice[$numAnswer]) && $key == $my_choice[$numAnswer]) { |
7246
|
|
|
$attributes = array('checked' => 1, 'selected' => 1); |
7247
|
|
|
} else { |
7248
|
|
|
$attributes = array(); |
7249
|
|
|
} |
7250
|
|
|
|
7251
|
|
|
if ($debug_mark_answer) { |
7252
|
|
|
if ($key == $answerCorrect) { |
7253
|
|
|
$attributes['checked'] = 1; |
7254
|
|
|
$attributes['selected'] = 1; |
7255
|
|
|
} |
7256
|
|
|
} |
7257
|
|
|
$s .= Display::tag('td', Display::input('radio', 'choice['.$questionId.']['.$numAnswer.']', $key, $attributes)); |
7258
|
|
|
} |
7259
|
|
|
|
7260
|
|
|
if ($show_comment) { |
7261
|
|
|
$s .= '<td>'; |
7262
|
|
|
$s .= $comment; |
7263
|
|
|
$s .= '</td>'; |
7264
|
|
|
} |
7265
|
|
|
$s.='</tr>'; |
7266
|
|
|
} elseif ($answerType == FILL_IN_BLANKS) { |
7267
|
|
|
list($answer) = explode('::', $answer); |
7268
|
|
|
|
7269
|
|
|
//Correct answer |
7270
|
|
|
api_preg_match_all('/\[[^]]+\]/', $answer, $correct_answer_list); |
7271
|
|
|
|
7272
|
|
|
//Student's answezr |
7273
|
|
|
if (isset($user_choice[0]['answer'])) { |
7274
|
|
|
api_preg_match_all('/\[[^]]+\]/', $user_choice[0]['answer'], $student_answer_list); |
7275
|
|
|
$student_answer_list = $student_answer_list[0]; |
7276
|
|
|
} |
7277
|
|
|
|
7278
|
|
|
//If debug |
7279
|
|
|
if ($debug_mark_answer) { |
7280
|
|
|
$student_answer_list = $correct_answer_list[0]; |
7281
|
|
|
} |
7282
|
|
|
|
7283
|
|
|
if (!empty($correct_answer_list) && !empty($student_answer_list)) { |
7284
|
|
|
$correct_answer_list = $correct_answer_list[0]; |
7285
|
|
|
$i = 0; |
7286
|
|
|
foreach ($correct_answer_list as $correct_item) { |
7287
|
|
|
$value = null; |
7288
|
|
|
if (isset($student_answer_list[$i]) && !empty($student_answer_list[$i])) { |
7289
|
|
|
|
7290
|
|
|
//Cleaning student answer list |
7291
|
|
|
$value = strip_tags($student_answer_list[$i]); |
7292
|
|
|
$value = api_substr($value, 1, api_strlen($value) - 2); |
7293
|
|
|
$value = explode('/', $value); |
7294
|
|
|
|
7295
|
|
|
if (!empty($value[0])) { |
7296
|
|
|
$value = str_replace(' ', '', trim($value[0])); |
7297
|
|
|
} |
7298
|
|
|
$correct_item = preg_quote($correct_item); |
7299
|
|
|
$correct_item = api_preg_replace('|/|', '\/', $correct_item); // to prevent error if there is a / in the text to find |
7300
|
|
|
$answer = api_preg_replace('/'.$correct_item.'/', Display::input('text', "choice[$questionId][]", $value), $answer, 1); |
7301
|
|
|
} |
7302
|
|
|
$i++; |
7303
|
|
|
} |
7304
|
|
|
} else { |
7305
|
|
|
$answer = api_preg_replace('/\[[^]]+\]/', Display::input('text', "choice[$questionId][]", '', $attributes), $answer); |
7306
|
|
|
} |
7307
|
|
|
$s .= $answer; |
7308
|
|
|
} elseif ($answerType == MATCHING) { |
7309
|
|
|
// matching type, showing suggestions and answers |
7310
|
|
|
// TODO: replace $answerId by $numAnswer |
7311
|
|
|
|
7312
|
|
|
if ($lines_count == 1) { |
7313
|
|
|
$s .= $objAnswerTmp->getJs(); |
7314
|
|
|
} |
7315
|
|
|
if ($answerCorrect != 0) { |
7316
|
|
|
// only show elements to be answered (not the contents of |
7317
|
|
|
// the select boxes, who are correct = 0) |
7318
|
|
|
$s .= '<tr><td width="45%">'; |
7319
|
|
|
$parsed_answer = $answer; |
7320
|
|
|
$windowId = $questionId.'_'.$lines_count; |
7321
|
|
|
//left part questions |
7322
|
|
|
$s .= ' <div id="window_'.$windowId.'" class="window window_left_question window'.$questionId.'_question"> |
7323
|
|
|
<b>'.$lines_count.'</b>. '.$parsed_answer.' |
7324
|
|
|
</div> |
7325
|
|
|
</td>'; |
7326
|
|
|
|
7327
|
|
|
// middle part (matches selects) |
7328
|
|
|
|
7329
|
|
|
$s .= '<td width="10%" align="center"> '; |
7330
|
|
|
$s .= '<div style="display:block">'; |
7331
|
|
|
|
7332
|
|
|
$s .= '<select id="window_'.$windowId.'_select" name="choice['.$questionId.']['.$numAnswer.']">'; |
7333
|
|
|
$selectedValue = 0; |
7334
|
|
|
// fills the list-box |
7335
|
|
|
$item = 0; |
7336
|
|
|
foreach ($select_items as $val) { |
7337
|
|
|
// set $debug_mark_answer to true at public static function start to |
7338
|
|
|
// show the correct answer with a suffix '-x' |
7339
|
|
|
$selected = ''; |
7340
|
|
|
if ($debug_mark_answer) { |
7341
|
|
|
if ($val['id'] == $answerCorrect) { |
7342
|
|
|
$selected = 'selected="selected"'; |
7343
|
|
|
$selectedValue = $val['id']; |
7344
|
|
|
} |
7345
|
|
|
} |
7346
|
|
|
if (isset($user_choice[$matching_correct_answer]) && $val['id'] == $user_choice[$matching_correct_answer]['answer']) { |
7347
|
|
|
$selected = 'selected="selected"'; |
7348
|
|
|
$selectedValue = $val['id']; |
7349
|
|
|
} |
7350
|
|
|
//$s .= '<option value="'.$val['id'].'" '.$selected.'>'.$val['letter'].'</option>'; |
7351
|
|
|
$s .= '<option value="'.$item.'" '.$selected.'>'.$val['letter'].'</option>'; |
7352
|
|
|
$item++; |
7353
|
|
|
} |
7354
|
|
|
|
7355
|
|
|
if (!empty($answerCorrect) && !empty($selectedValue)) { |
7356
|
|
|
$s.= '<script> |
7357
|
|
|
jsPlumb.ready(function() { |
7358
|
|
|
jsPlumb.connect({ |
7359
|
|
|
source: "window_'.$windowId.'", |
7360
|
|
|
target: "window_'.$questionId.'_'.$selectedValue.'_answer", |
7361
|
|
|
endpoint:["Blank", { radius:15 }], |
7362
|
|
|
anchor:["RightMiddle","LeftMiddle"], |
7363
|
|
|
paintStyle:{ strokeStyle:"#8a8888" , lineWidth:8 }, |
7364
|
|
|
connector: [connectorType, { curviness: curvinessValue } ], |
7365
|
|
|
}) |
7366
|
|
|
}); |
7367
|
|
|
</script>'; |
7368
|
|
|
} |
7369
|
|
|
$s .= '</select></div></td>'; |
7370
|
|
|
|
7371
|
|
|
$s.='<td width="45%" valign="top" >'; |
7372
|
|
|
|
7373
|
|
|
if (isset($select_items[$lines_count])) { |
7374
|
|
|
$s.= '<div id="window_'.$windowId.'_answer" class="window window_right_question"> |
7375
|
|
|
<b>'.$select_items[$lines_count]['letter'].'.</b> '.$select_items[$lines_count]['answer'].' |
7376
|
|
|
</div>'; |
7377
|
|
|
} else { |
7378
|
|
|
$s.=' '; |
7379
|
|
|
} |
7380
|
|
|
|
7381
|
|
|
$s .= '</td>'; |
7382
|
|
|
$s .= '</tr>'; |
7383
|
|
|
$lines_count++; |
7384
|
|
|
//if the left side of the "matching" has been completely |
7385
|
|
|
// shown but the right side still has values to show... |
7386
|
|
|
if (($lines_count - 1) == $num_suggestions) { |
7387
|
|
|
// if it remains answers to shown at the right side |
7388
|
|
|
while (isset($select_items[$lines_count])) { |
7389
|
|
|
$s .= '<tr> |
7390
|
|
|
<td colspan="2"></td> |
7391
|
|
|
<td valign="top">'; |
7392
|
|
|
$s.='<b>'.$select_items[$lines_count]['letter'].'.</b>'; |
7393
|
|
|
$s .= $select_items[$lines_count]['answer']; |
7394
|
|
|
$s.="</td> |
7395
|
|
|
</tr>"; |
7396
|
|
|
$lines_count++; |
7397
|
|
|
} // end while() |
7398
|
|
|
} // end if() |
7399
|
|
|
$matching_correct_answer++; |
7400
|
|
|
} |
7401
|
|
|
} elseif ($answerType == DRAGGABLE) { |
7402
|
|
|
// matching type, showing suggestions and answers |
7403
|
|
|
// TODO: replace $answerId by $numAnswer |
7404
|
|
|
|
7405
|
|
|
if ($answerCorrect != 0) { |
7406
|
|
|
// only show elements to be answered (not the contents of |
7407
|
|
|
// the select boxes, who are correct = 0) |
7408
|
|
|
$s .= '<td>'; |
7409
|
|
|
$parsed_answer = $answer; |
7410
|
|
|
$windowId = $questionId.'_'.$numAnswer; //67_293 - 67_294 |
7411
|
|
|
|
7412
|
|
|
//left part questions |
7413
|
|
|
$s .= '<li class="ui-state-default" id="'.$windowId.'">'; |
7414
|
|
|
$s .= ' <div id="window_'.$windowId.'" class="window'.$questionId.'_question_draggable question_draggable"> |
7415
|
|
|
'.$parsed_answer.' |
7416
|
|
|
</div>'; |
7417
|
|
|
|
7418
|
|
|
$s .= '<div style="display:none">'; |
7419
|
|
|
$s .= '<select id="window_'.$windowId.'_select" name="choice['.$questionId.']['.$numAnswer.']" class="select_option">'; |
7420
|
|
|
$selectedValue = 0; |
7421
|
|
|
// fills the list-box |
7422
|
|
|
$item = 0; |
7423
|
|
|
foreach ($select_items as $val) { |
7424
|
|
|
// set $debug_mark_answer to true at function start to |
7425
|
|
|
// show the correct answer with a suffix '-x' |
7426
|
|
|
$selected = ''; |
7427
|
|
|
if ($debug_mark_answer) { |
7428
|
|
|
if ($val['id'] == $answerCorrect) { |
7429
|
|
|
$selected = 'selected="selected"'; |
7430
|
|
|
$selectedValue = $val['id']; |
7431
|
|
|
} |
7432
|
|
|
} |
7433
|
|
|
if (isset($user_choice[$matching_correct_answer]) && $val['id'] == $user_choice[$matching_correct_answer]['answer']) { |
7434
|
|
|
$selected = 'selected="selected"'; |
7435
|
|
|
$selectedValue = $val['id']; |
7436
|
|
|
} |
7437
|
|
|
$s .= '<option value="'.$item.'" '.$selected.'>'.$val['letter'].'</option>'; |
7438
|
|
|
$item++; |
7439
|
|
|
} |
7440
|
|
|
$s .= '</select>'; |
7441
|
|
|
|
7442
|
|
|
if (!empty($answerCorrect) && !empty($selectedValue)) { |
7443
|
|
|
$s.= '<script> |
7444
|
|
|
$(function() { |
7445
|
|
|
deleteItem($("#'.$questionId.'_'.$selectedValue.'"), $("#drop_'.$windowId.'")); |
7446
|
|
|
}); |
7447
|
|
|
</script>'; |
7448
|
|
|
} |
7449
|
|
|
|
7450
|
|
|
if (isset($select_items[$lines_count])) { |
7451
|
|
|
$s.= '<div id="window_'.$windowId.'_answer" class=""> |
7452
|
|
|
<b>'.$select_items[$lines_count]['letter'].'.</b> '.$select_items[$lines_count]['answer'].' |
7453
|
|
|
</div>'; |
7454
|
|
|
} else { |
7455
|
|
|
$s.=' '; |
7456
|
|
|
} |
7457
|
|
|
$lines_count++; |
7458
|
|
|
//if the left side of the "matching" has been completely |
7459
|
|
|
// shown but the right side still has values to show... |
7460
|
|
|
|
7461
|
|
|
if (($lines_count - 1) == $num_suggestions) { |
7462
|
|
|
// if it remains answers to shown at the right side |
7463
|
|
|
while (isset($select_items[$lines_count])) { |
7464
|
|
|
$s.='<b>'.$select_items[$lines_count]['letter'].'.</b>'; |
7465
|
|
|
$s .= $select_items[$lines_count]['answer']; |
7466
|
|
|
$lines_count++; |
7467
|
|
|
} |
7468
|
|
|
} |
7469
|
|
|
$s .= '</div>'; |
7470
|
|
|
$matching_correct_answer++; |
7471
|
|
|
$s .= '</li>'; |
7472
|
|
|
} |
7473
|
|
|
} |
7474
|
|
|
} // end for() |
7475
|
|
|
|
7476
|
|
|
if ($show_comment) { |
7477
|
|
|
$s .= '</table>'; |
7478
|
|
|
} else { |
7479
|
|
|
if ($answerType == MATCHING || $answerType == UNIQUE_ANSWER_NO_OPTION || $answerType == MULTIPLE_ANSWER_TRUE_FALSE || |
7480
|
|
|
$answerType == MULTIPLE_ANSWER_COMBINATION_TRUE_FALSE) { |
7481
|
|
|
$s .= '</table>'; |
7482
|
|
|
} |
7483
|
|
|
} |
7484
|
|
|
|
7485
|
|
|
if ($answerType == DRAGGABLE) { |
7486
|
|
|
$s .= '</ul><div class="clear"></div>'; |
7487
|
|
|
|
7488
|
|
|
$counterAnswer = 1; |
7489
|
|
|
foreach ($objAnswerTmp->answer as $answerId => $answer_item) { |
7490
|
|
|
$answerCorrect = $objAnswerTmp->isCorrect($answerId); |
7491
|
|
|
$windowId = $questionId.'_'.$counterAnswer; |
7492
|
|
|
if ($answerCorrect == 0) { |
7493
|
|
|
$s .= '<div id="drop_'.$windowId.'" class="droppable ui-state-default">'.$counterAnswer.'</div>'; |
7494
|
|
|
$counterAnswer++; |
7495
|
|
|
} |
7496
|
|
|
} |
7497
|
|
|
} |
7498
|
|
|
|
7499
|
|
|
if ($answerType == MATCHING) { |
7500
|
|
|
$s .= '</div>'; |
7501
|
|
|
} |
7502
|
|
|
|
7503
|
|
|
$s .= '</div>'; |
7504
|
|
|
|
7505
|
|
|
// destruction of the Answer object |
7506
|
|
|
unset($objAnswerTmp); |
7507
|
|
|
|
7508
|
|
|
// destruction of the Question object |
7509
|
|
|
unset($objQuestionTmp); |
7510
|
|
|
|
7511
|
|
|
$html .= $s; |
7512
|
|
|
return $html; |
7513
|
|
|
} elseif ($answerType == HOT_SPOT || $answerType == HOT_SPOT_DELINEATION) { |
7514
|
|
|
// Question is a HOT_SPOT |
7515
|
|
|
//checking document/images visibility |
7516
|
|
|
if (api_is_platform_admin() || api_is_course_admin()) { |
7517
|
|
|
$course = api_get_course_info(); |
7518
|
|
|
$doc_id = DocumentManager::get_document_id($course, '/images/'.$pictureName); |
7519
|
|
|
if (is_numeric($doc_id)) { |
7520
|
|
|
$images_folder_visibility = api_get_item_visibility($course, 'document', $doc_id, api_get_session_id()); |
7521
|
|
|
if (!$images_folder_visibility) { |
7522
|
|
|
//This message is shown only to the course/platform admin if the image is set to visibility = false |
7523
|
|
|
Display::display_warning_message(get_lang('ChangeTheVisibilityOfTheCurrentImage')); |
7524
|
|
|
} |
7525
|
|
|
} |
7526
|
|
|
} |
7527
|
|
|
$questionName = $objQuestionTmp->selectTitle(); |
7528
|
|
|
$questionDescription = $objQuestionTmp->selectDescription(); |
7529
|
|
|
|
7530
|
|
|
if ($freeze) { |
7531
|
|
|
$s .= Display::img($objQuestionTmp->selectPicturePath()); |
7532
|
|
|
$html .= $s; |
7533
|
|
|
return $html; |
7534
|
|
|
} |
7535
|
|
|
|
7536
|
|
|
// Get the answers, make a list |
7537
|
|
|
$objAnswerTmp = new Answer($questionId); |
7538
|
|
|
|
7539
|
|
|
// get answers of hotpost |
7540
|
|
|
$answers_hotspot = array(); |
7541
|
|
|
foreach ($objAnswerTmp->answer as $answerId => $answer_item) { |
7542
|
|
|
//$answers = $objAnswerTmp->selectAnswerByAutoId($objAnswerTmp->selectAutoId($answerId)); |
7543
|
|
|
$answers_hotspot[$answerId] = $objAnswerTmp->selectAnswer($answerId); |
7544
|
|
|
} |
7545
|
|
|
|
7546
|
|
|
// display answers of hotpost order by id |
7547
|
|
|
$answer_list = '<div style="padding: 10px; margin-left: 0px; border: 1px solid #A4A4A4; height: 408px; width: 200px;"><b>'.get_lang('HotspotZones').'</b><dl>'; |
7548
|
|
|
if (!empty($answers_hotspot)) { |
7549
|
|
|
ksort($answers_hotspot); |
7550
|
|
|
foreach ($answers_hotspot as $key => $value) { |
7551
|
|
|
$answer_list .= '<dt>'.$key.'.- '.$value.'</dt><br />'; |
7552
|
|
|
} |
7553
|
|
|
} |
7554
|
|
|
$answer_list .= '</dl></div>'; |
7555
|
|
|
|
7556
|
|
|
if ($answerType == HOT_SPOT_DELINEATION) { |
7557
|
|
|
$answer_list = ''; |
7558
|
|
|
$swf_file = 'hotspot_delineation_user'; |
7559
|
|
|
$swf_height = 405; |
7560
|
|
|
} else { |
7561
|
|
|
$swf_file = 'hotspot_user'; |
7562
|
|
|
$swf_height = 436; |
7563
|
|
|
} |
7564
|
|
|
|
7565
|
|
|
if (!$only_questions) { |
7566
|
|
|
if ($show_title) { |
7567
|
|
|
$html .= TestCategory::getCategoryNamesForQuestion($objQuestionTmp->id); |
7568
|
|
|
$html .= '<div class="question_title">'.$current_item.'. '.$questionName.'</div>'; |
7569
|
|
|
$html .= $questionDescription; |
7570
|
|
|
} else { |
7571
|
|
|
$html .= '<div class="media">'; |
7572
|
|
|
$html .= '<div class="pull-left">'; |
7573
|
|
|
$html .= '<div class="media-object">'; |
7574
|
|
|
$html .= Display::div($current_item.'. ', array('class' => 'question_no_title')); |
7575
|
|
|
$html .= '</div>'; |
7576
|
|
|
$html .= '</div>'; |
7577
|
|
|
$html .= '<div class="media-body">'; |
7578
|
|
|
if (!empty($questionDescription)) { |
7579
|
|
|
$html .= Display::div($questionDescription, array('class' => 'question_description')); |
7580
|
|
|
} |
7581
|
|
|
$html .= '</div>'; |
7582
|
|
|
$html .= '</div>'; |
7583
|
|
|
} |
7584
|
|
|
//@todo I need to the get the feedback type |
7585
|
|
|
$html .= '<input type="hidden" name="hidden_hotspot_id" value="'.$questionId.'" />'; |
7586
|
|
|
$html .= '<table class="exercise_questions"> |
7587
|
|
|
<tr> |
7588
|
|
|
<td valign="top" colspan="2">'; |
7589
|
|
|
$html .= '</td></tr>'; |
7590
|
|
|
} |
7591
|
|
|
|
7592
|
|
|
$canClick = isset($_GET['editQuestion']) ? '0' : (isset($_GET['modifyAnswers']) ? '0' : '1'); |
7593
|
|
|
|
7594
|
|
|
$s .= ' <script type="text/javascript" src="../plugin/hotspot/JavaScriptFlashGateway.js"></script> |
7595
|
|
|
<script src="../plugin/hotspot/hotspot.js" type="text/javascript" ></script> |
7596
|
|
|
<script type="text/javascript"> |
7597
|
|
|
<!-- |
7598
|
|
|
// Globals |
7599
|
|
|
// Major version of Flash required |
7600
|
|
|
var requiredMajorVersion = 7; |
7601
|
|
|
// Minor version of Flash required |
7602
|
|
|
var requiredMinorVersion = 0; |
7603
|
|
|
// Minor version of Flash required |
7604
|
|
|
var requiredRevision = 0; |
7605
|
|
|
// the version of javascript supported |
7606
|
|
|
var jsVersion = 1.0; |
7607
|
|
|
// --> |
7608
|
|
|
</script> |
7609
|
|
|
<script language="VBScript" type="text/vbscript"> |
7610
|
|
|
<!-- // Visual basic helper required to detect Flash Player ActiveX control version information |
7611
|
|
|
Function VBGetSwfVer(i) |
7612
|
|
|
on error resume next |
7613
|
|
|
Dim swControl, swVersion |
7614
|
|
|
swVersion = 0 |
7615
|
|
|
|
7616
|
|
|
set swControl = CreateObject("ShockwaveFlash.ShockwaveFlash." + CStr(i)) |
7617
|
|
|
if (IsObject(swControl)) then |
7618
|
|
|
swVersion = swControl.GetVariable("$version") |
7619
|
|
|
end if |
7620
|
|
|
VBGetSwfVer = swVersion |
7621
|
|
|
End Function |
7622
|
|
|
// --> |
7623
|
|
|
</script> |
7624
|
|
|
|
7625
|
|
|
<script language="JavaScript1.1" type="text/javascript"> |
7626
|
|
|
<!-- // Detect Client Browser type |
7627
|
|
|
var isIE = (navigator.appVersion.indexOf("MSIE") != -1) ? true : false; |
7628
|
|
|
var isWin = (navigator.appVersion.toLowerCase().indexOf("win") != -1) ? true : false; |
7629
|
|
|
var isOpera = (navigator.userAgent.indexOf("Opera") != -1) ? true : false; |
7630
|
|
|
jsVersion = 1.1; |
7631
|
|
|
// JavaScript helper required to detect Flash Player PlugIn version information |
7632
|
|
|
function JSGetSwfVer(i) { |
7633
|
|
|
// NS/Opera version >= 3 check for Flash plugin in plugin array |
7634
|
|
|
if (navigator.plugins != null && navigator.plugins.length > 0) { |
7635
|
|
|
if (navigator.plugins["Shockwave Flash 2.0"] || navigator.plugins["Shockwave Flash"]) { |
7636
|
|
|
var swVer2 = navigator.plugins["Shockwave Flash 2.0"] ? " 2.0" : ""; |
7637
|
|
|
var flashDescription = navigator.plugins["Shockwave Flash" + swVer2].description; |
7638
|
|
|
descArray = flashDescription.split(" "); |
7639
|
|
|
tempArrayMajor = descArray[2].split("."); |
7640
|
|
|
versionMajor = tempArrayMajor[0]; |
7641
|
|
|
versionMinor = tempArrayMajor[1]; |
7642
|
|
|
if ( descArray[3] != "" ) { |
7643
|
|
|
tempArrayMinor = descArray[3].split("r"); |
7644
|
|
|
} else { |
7645
|
|
|
tempArrayMinor = descArray[4].split("r"); |
7646
|
|
|
} |
7647
|
|
|
versionRevision = tempArrayMinor[1] > 0 ? tempArrayMinor[1] : 0; |
7648
|
|
|
flashVer = versionMajor + "." + versionMinor + "." + versionRevision; |
7649
|
|
|
} else { |
7650
|
|
|
flashVer = -1; |
7651
|
|
|
} |
7652
|
|
|
} |
7653
|
|
|
// MSN/WebTV 2.6 supports Flash 4 |
7654
|
|
|
else if (navigator.userAgent.toLowerCase().indexOf("webtv/2.6") != -1) flashVer = 4; |
7655
|
|
|
// WebTV 2.5 supports Flash 3 |
7656
|
|
|
else if (navigator.userAgent.toLowerCase().indexOf("webtv/2.5") != -1) flashVer = 3; |
7657
|
|
|
// older WebTV supports Flash 2 |
7658
|
|
|
else if (navigator.userAgent.toLowerCase().indexOf("webtv") != -1) flashVer = 2; |
7659
|
|
|
// Can\'t detect in all other cases |
7660
|
|
|
else { |
7661
|
|
|
flashVer = -1; |
7662
|
|
|
} |
7663
|
|
|
return flashVer; |
7664
|
|
|
} |
7665
|
|
|
// When called with reqMajorVer, reqMinorVer, reqRevision returns true if that version or greater is available |
7666
|
|
|
|
7667
|
|
|
function DetectFlashVer(reqMajorVer, reqMinorVer, reqRevision) { |
7668
|
|
|
reqVer = parseFloat(reqMajorVer + "." + reqRevision); |
7669
|
|
|
// loop backwards through the versions until we find the newest version |
7670
|
|
|
for (i=25;i>0;i--) { |
7671
|
|
|
if (isIE && isWin && !isOpera) { |
7672
|
|
|
versionStr = VBGetSwfVer(i); |
7673
|
|
|
} else { |
7674
|
|
|
versionStr = JSGetSwfVer(i); |
7675
|
|
|
} |
7676
|
|
|
if (versionStr == -1 ) { |
7677
|
|
|
return false; |
7678
|
|
|
} else if (versionStr != 0) { |
7679
|
|
|
if(isIE && isWin && !isOpera) { |
7680
|
|
|
tempArray = versionStr.split(" "); |
7681
|
|
|
tempString = tempArray[1]; |
7682
|
|
|
versionArray = tempString .split(","); |
7683
|
|
|
} else { |
7684
|
|
|
versionArray = versionStr.split("."); |
7685
|
|
|
} |
7686
|
|
|
versionMajor = versionArray[0]; |
7687
|
|
|
versionMinor = versionArray[1]; |
7688
|
|
|
versionRevision = versionArray[2]; |
7689
|
|
|
|
7690
|
|
|
versionString = versionMajor + "." + versionRevision; // 7.0r24 == 7.24 |
7691
|
|
|
versionNum = parseFloat(versionString); |
7692
|
|
|
// is the major.revision >= requested major.revision AND the minor version >= requested minor |
7693
|
|
|
if ( (versionMajor > reqMajorVer) && (versionNum >= reqVer) ) { |
7694
|
|
|
return true; |
7695
|
|
|
} else { |
7696
|
|
|
return ((versionNum >= reqVer && versionMinor >= reqMinorVer) ? true : false ); |
7697
|
|
|
} |
7698
|
|
|
} |
7699
|
|
|
} |
7700
|
|
|
} |
7701
|
|
|
// --> |
7702
|
|
|
</script>'; |
7703
|
|
|
$s .= '<tr><td valign="top" colspan="2" width="520"><table><tr><td width="520"> |
7704
|
|
|
<script> |
7705
|
|
|
// Version check based upon the values entered above in "Globals" |
7706
|
|
|
var hasReqestedVersion = DetectFlashVer(requiredMajorVersion, requiredMinorVersion, requiredRevision); |
7707
|
|
|
|
7708
|
|
|
// Check to see if the version meets the requirements for playback |
7709
|
|
|
if (hasReqestedVersion) { // if we\'ve detected an acceptable version |
7710
|
|
|
var oeTags = \'<object type="application/x-shockwave-flash" data="../plugin/hotspot/'.$swf_file.'.swf?modifyAnswers='.$questionId.'&canClick:'.$canClick.'" width="600" height="'.$swf_height.'">\' |
7711
|
|
|
+ \'<param name="wmode" value="transparent">\' |
7712
|
|
|
+ \'<param name="movie" value="../plugin/hotspot/'.$swf_file.'.swf?modifyAnswers='.$questionId.'&canClick:'.$canClick.'" />\' |
7713
|
|
|
+ \'<\/object>\'; |
7714
|
|
|
document.write(oeTags); // embed the Flash Content SWF when all tests are passed |
7715
|
|
|
} else { // flash is too old or we can\'t detect the plugin |
7716
|
|
|
var alternateContent = "Error<br \/>" |
7717
|
|
|
+ "Hotspots requires Macromedia Flash 7.<br \/>" |
7718
|
|
|
+ "<a href=\"http://www.macromedia.com/go/getflash/\">Get Flash<\/a>"; |
7719
|
|
|
document.write(alternateContent); // insert non-flash content |
7720
|
|
|
} |
7721
|
|
|
</script> |
7722
|
|
|
</td> |
7723
|
|
|
<td valign="top" align="left">'.$answer_list.'</td></tr> |
7724
|
|
|
</table> |
7725
|
|
|
</td></tr>'; |
7726
|
|
|
$html .= $s; |
7727
|
|
|
$html .= '</table>'; |
7728
|
|
|
return $html; |
7729
|
|
|
} |
7730
|
|
|
return $nbrAnswers; |
7731
|
|
|
} |
7732
|
|
|
|
7733
|
|
|
/** |
7734
|
|
|
* @param int $exeId |
7735
|
|
|
* @return array |
7736
|
|
|
*/ |
7737
|
|
|
public function returnQuestionListByAttempt($exeId) |
7738
|
|
|
{ |
7739
|
|
|
return $this->displayQuestionListByAttempt($exeId, false, true); |
7740
|
|
|
} |
7741
|
|
|
|
7742
|
|
|
/** |
7743
|
|
|
* Display the exercise results |
7744
|
|
|
* @param int $exe_id |
7745
|
|
|
* @param bool $saveUserResult save users results (true) or just show the results (false) |
7746
|
|
|
* @param bool $returnExerciseResult return array with exercise result info |
7747
|
|
|
* @return mixed |
7748
|
|
|
*/ |
7749
|
|
|
public function displayQuestionListByAttempt($exe_id, $saveUserResult = false, $returnExerciseResult = false) |
7750
|
|
|
{ |
7751
|
|
|
global $origin, $debug; |
7752
|
|
|
|
7753
|
|
|
//Getting attempt info |
7754
|
|
|
$exercise_stat_info = $this->getStatTrackExerciseInfoByExeId($exe_id); |
7755
|
|
|
|
7756
|
|
|
//Getting question list |
7757
|
|
|
$question_list = array(); |
7758
|
|
|
if (!empty($exercise_stat_info['data_tracking'])) { |
7759
|
|
|
$question_list = explode(',', $exercise_stat_info['data_tracking']); |
7760
|
|
|
} else { |
7761
|
|
|
//Try getting the question list only if save result is off |
7762
|
|
|
if ($saveUserResult == false) { |
7763
|
|
|
$question_list = $this->selectQuestionList(); |
7764
|
|
|
} |
7765
|
|
|
error_log("Data tracking is empty! exe_id: $exe_id"); |
7766
|
|
|
} |
7767
|
|
|
|
7768
|
|
|
$counter = 1; |
7769
|
|
|
$total_score = 0; |
7770
|
|
|
$total_weight = 0; |
7771
|
|
|
|
7772
|
|
|
$exercise_content = null; |
7773
|
|
|
|
7774
|
|
|
// Hide results |
7775
|
|
|
$show_results = false; |
7776
|
|
|
$show_only_score = false; |
7777
|
|
|
|
7778
|
|
|
if ($this->results_disabled == RESULT_DISABLE_SHOW_SCORE_AND_EXPECTED_ANSWERS) { |
7779
|
|
|
$show_results = true; |
7780
|
|
|
} |
7781
|
|
|
|
7782
|
|
|
$showScoreOptions = [ |
7783
|
|
|
RESULT_DISABLE_SHOW_SCORE_ONLY, |
7784
|
|
|
RESULT_DISABLE_SHOW_FINAL_SCORE_ONLY_WITH_CATEGORIES, |
7785
|
|
|
RESULT_DISABLE_SHOW_SCORE_ATTEMPT_SHOW_ANSWERS_LAST_ATTEMPT |
7786
|
|
|
]; |
7787
|
|
|
|
7788
|
|
|
if (in_array($this->results_disabled, $showScoreOptions)) { |
7789
|
|
|
$show_only_score = true; |
7790
|
|
|
} |
7791
|
|
|
|
7792
|
|
|
if ($show_results || $show_only_score) { |
7793
|
|
|
$user_info = api_get_user_info($exercise_stat_info['exe_user_id']); |
7794
|
|
|
// Shows exercise header. |
7795
|
|
|
echo $this->show_exercise_result_header( |
7796
|
|
|
$user_info['complete_name'], |
7797
|
|
|
api_convert_and_format_date($exercise_stat_info['start_date'], DATE_TIME_FORMAT_LONG), |
7798
|
|
|
$exercise_stat_info['duration'] |
7799
|
|
|
); |
7800
|
|
|
} |
7801
|
|
|
|
7802
|
|
|
// Display text when test is finished #4074 and for LP #4227 |
7803
|
|
|
$end_of_message = $this->selectTextWhenFinished(); |
7804
|
|
|
if (!empty($end_of_message)) { |
7805
|
|
|
Display::display_normal_message($end_of_message, false); |
7806
|
|
|
echo "<div class='clear'> </div>"; |
7807
|
|
|
} |
7808
|
|
|
|
7809
|
|
|
$question_list_answers = array(); |
7810
|
|
|
$media_list = array(); |
7811
|
|
|
$category_list = array(); |
7812
|
|
|
$tempParentId = null; |
7813
|
|
|
$mediaCounter = 0; |
7814
|
|
|
|
7815
|
|
|
$exerciseResultInfo = array(); |
7816
|
|
|
|
7817
|
|
|
// Loop over all question to show results for each of them, one by one |
7818
|
|
|
if (!empty($question_list)) { |
7819
|
|
|
if ($debug) { |
7820
|
|
|
error_log('Looping question_list '.print_r($question_list, 1)); |
7821
|
|
|
} |
7822
|
|
|
|
7823
|
|
|
foreach ($question_list as $questionId) { |
7824
|
|
|
|
7825
|
|
|
// Creates a temporary Question object |
7826
|
|
|
$objQuestionTmp = Question::read($questionId); |
7827
|
|
|
|
7828
|
|
|
// This variable commes from exercise_submit_modal.php |
7829
|
|
|
ob_start(); |
7830
|
|
|
$hotspot_delineation_result = null; |
7831
|
|
|
|
7832
|
|
|
// We're inside *one* question. Go through each possible answer for this question |
7833
|
|
|
$result = $this->manageAnswers( |
7834
|
|
|
$exercise_stat_info['exe_id'], |
7835
|
|
|
$questionId, |
7836
|
|
|
null, |
7837
|
|
|
'exercise_result', |
7838
|
|
|
array(), |
7839
|
|
|
$saveUserResult, |
7840
|
|
|
true, |
7841
|
|
|
$show_results, |
7842
|
|
|
$hotspot_delineation_result |
7843
|
|
|
); |
7844
|
|
|
|
7845
|
|
|
if (empty($result)) { |
7846
|
|
|
continue; |
7847
|
|
|
} |
7848
|
|
|
|
7849
|
|
|
$total_score += $result['score']; |
7850
|
|
|
$total_weight += $result['weight']; |
7851
|
|
|
|
7852
|
|
|
$question_list_answers[] = array( |
7853
|
|
|
'question' => $result['open_question'], |
7854
|
|
|
'answer' => $result['open_answer'], |
7855
|
|
|
'answer_type' => $result['answer_type'] |
7856
|
|
|
); |
7857
|
|
|
|
7858
|
|
|
$my_total_score = $result['score']; |
7859
|
|
|
$my_total_weight = $result['weight']; |
7860
|
|
|
|
7861
|
|
|
// Category report |
7862
|
|
|
$category_was_added_for_this_test = false; |
7863
|
|
|
$categoryExerciseList = $this->getListOfCategoriesWithQuestionForTest(); |
7864
|
|
|
|
7865
|
|
|
$category_list = array(); |
7866
|
|
|
if (isset($categoryExerciseList) && !empty($categoryExerciseList)) { |
7867
|
|
|
foreach ($categoryExerciseList as $category_id => $categoryInfo) { |
7868
|
|
|
if (!isset($category_list[$category_id])) { |
7869
|
|
|
$category_list[$category_id] = array(); |
7870
|
|
|
$category_list[$category_id]['score'] = 0; |
7871
|
|
|
$category_list[$category_id]['total'] = 0; |
7872
|
|
|
} |
7873
|
|
|
$category_list[$category_id]['score'] += $my_total_score; |
7874
|
|
|
$category_list[$category_id]['total'] += $my_total_weight; |
7875
|
|
|
$category_was_added_for_this_test = true; |
7876
|
|
|
} |
7877
|
|
|
} |
7878
|
|
|
|
7879
|
|
|
// No category for this question! |
7880
|
|
|
if ($category_was_added_for_this_test == false) { |
7881
|
|
|
if (!isset($category_list['none'])) { |
7882
|
|
|
$category_list['none'] = array(); |
7883
|
|
|
$category_list['none']['score'] = 0; |
7884
|
|
|
$category_list['none']['total'] = 0; |
7885
|
|
|
} |
7886
|
|
|
|
7887
|
|
|
$category_list['none']['score'] += $my_total_score; |
7888
|
|
|
$category_list['none']['total'] += $my_total_weight; |
7889
|
|
|
} |
7890
|
|
|
|
7891
|
|
|
if ($this->selectPropagateNeg() == 0 && $my_total_score < 0) { |
7892
|
|
|
$my_total_score = 0; |
7893
|
|
|
} |
7894
|
|
|
|
7895
|
|
|
$comnt = null; |
7896
|
|
|
if ($show_results) { |
7897
|
|
|
$comnt = get_comments($exe_id, $questionId); |
7898
|
|
|
if (!empty($comnt)) { |
7899
|
|
|
echo '<b>'.get_lang('Feedback').'</b>'; |
7900
|
|
|
echo '<div id="question_feedback">'.$comnt.'</div>'; |
7901
|
|
|
} |
7902
|
|
|
} |
7903
|
|
|
|
7904
|
|
|
$score = array(); |
7905
|
|
|
$score['result'] = get_lang('Score')." : ".ExerciseLib::show_score($my_total_score, $my_total_weight, false, true); |
7906
|
|
|
$score['pass'] = $my_total_score >= $my_total_weight ? true : false; |
7907
|
|
|
$score['score'] = $my_total_score; |
7908
|
|
|
$score['weight'] = $my_total_weight; |
7909
|
|
|
$score['comments'] = $comnt; |
7910
|
|
|
|
7911
|
|
|
$exerciseResultInfo[$questionId]['score'] = $score; |
7912
|
|
|
$exerciseResultInfo[$questionId]['details'] = $result; |
7913
|
|
|
|
7914
|
|
|
// If no results we hide the results |
7915
|
|
|
if ($show_results == false) { |
7916
|
|
|
$score = array(); |
7917
|
|
|
} |
7918
|
|
|
$contents = ob_get_clean(); |
7919
|
|
|
|
7920
|
|
|
$question_content = '<div class="question_row">'; |
7921
|
|
|
|
7922
|
|
|
if ($show_results) { |
7923
|
|
|
|
7924
|
|
|
$show_media = false; |
7925
|
|
|
$counterToShow = $counter; |
7926
|
|
|
if ($objQuestionTmp->parent_id != 0) { |
7927
|
|
|
|
7928
|
|
|
if (!in_array($objQuestionTmp->parent_id, $media_list)) { |
7929
|
|
|
$media_list[] = $objQuestionTmp->parent_id; |
7930
|
|
|
$show_media = true; |
7931
|
|
|
} |
7932
|
|
|
if ($tempParentId == $objQuestionTmp->parent_id) { |
7933
|
|
|
$mediaCounter++; |
7934
|
|
|
} else { |
7935
|
|
|
$mediaCounter = 0; |
7936
|
|
|
} |
7937
|
|
|
$counterToShow = chr(97 + $mediaCounter); |
7938
|
|
|
$tempParentId = $objQuestionTmp->parent_id; |
7939
|
|
|
} |
7940
|
|
|
|
7941
|
|
|
// Shows question title an description. |
7942
|
|
|
$question_content .= $objQuestionTmp->return_header(null, $counterToShow, $score, $show_media, $this->getHideQuestionTitle()); |
7943
|
|
|
|
7944
|
|
|
// display question category, if any |
7945
|
|
|
$question_content .= TestCategory::getCategoryNamesForQuestion($questionId, null, true, $this->categoryMinusOne); |
7946
|
|
|
} |
7947
|
|
|
$counter++; |
7948
|
|
|
|
7949
|
|
|
$question_content .= $contents; |
7950
|
|
|
$question_content .= '</div>'; |
7951
|
|
|
|
7952
|
|
|
$exercise_content .= $question_content; |
7953
|
|
|
} // end foreach() block that loops over all questions |
7954
|
|
|
} |
7955
|
|
|
|
7956
|
|
|
$total_score_text = null; |
7957
|
|
|
|
7958
|
|
|
if ($returnExerciseResult) { |
7959
|
|
|
return $exerciseResultInfo; |
7960
|
|
|
} |
7961
|
|
|
|
7962
|
|
|
if ($origin != 'learnpath') { |
7963
|
|
|
if ($show_results || $show_only_score) { |
7964
|
|
|
$total_score_text .= $this->get_question_ribbon($total_score, $total_weight, true); |
7965
|
|
|
} |
7966
|
|
|
} |
7967
|
|
|
|
7968
|
|
|
if (!empty($category_list) && ($show_results || $show_only_score)) { |
7969
|
|
|
//Adding total |
7970
|
|
|
$category_list['total'] = array('score' => $total_score, 'total' => $total_weight); |
7971
|
|
|
echo TestCategory::get_stats_table_by_attempt($this->id, $category_list, $this->categoryMinusOne); |
7972
|
|
|
} |
7973
|
|
|
|
7974
|
|
|
echo $total_score_text; |
7975
|
|
|
echo $exercise_content; |
7976
|
|
|
|
7977
|
|
|
if (!$show_only_score) { |
7978
|
|
|
echo $total_score_text; |
7979
|
|
|
} |
7980
|
|
|
|
7981
|
|
|
if ($saveUserResult) { |
7982
|
|
|
|
7983
|
|
|
// Tracking of results |
7984
|
|
|
$learnpath_id = $exercise_stat_info['orig_lp_id']; |
7985
|
|
|
$learnpath_item_id = $exercise_stat_info['orig_lp_item_id']; |
7986
|
|
|
$learnpath_item_view_id = $exercise_stat_info['orig_lp_item_view_id']; |
7987
|
|
|
|
7988
|
|
|
if (api_is_allowed_to_session_edit()) { |
7989
|
|
|
update_event_exercise( |
7990
|
|
|
$exercise_stat_info['exe_id'], |
7991
|
|
|
$this->selectId(), |
7992
|
|
|
$total_score, |
7993
|
|
|
$total_weight, |
7994
|
|
|
api_get_session_id(), |
7995
|
|
|
$learnpath_id, |
7996
|
|
|
$learnpath_item_id, |
7997
|
|
|
$learnpath_item_view_id, |
7998
|
|
|
$exercise_stat_info['exe_duration'], |
7999
|
|
|
'', |
8000
|
|
|
array() |
8001
|
|
|
); |
8002
|
|
|
} |
8003
|
|
|
|
8004
|
|
|
// Send notification. |
8005
|
|
|
if (!api_is_allowed_to_edit(null, true)) { |
8006
|
|
|
$isSuccess = ExerciseLib::is_success_exercise_result($total_score, $total_weight, $this->selectPassPercentage()); |
8007
|
|
|
$this->sendCustomNotification($exe_id, $exerciseResultInfo, $isSuccess); |
8008
|
|
|
$this->sendNotificationForOpenQuestions($question_list_answers, $origin, $exe_id); |
8009
|
|
|
$this->sendNotificationForOralQuestions($question_list_answers, $origin, $exe_id); |
8010
|
|
|
} |
8011
|
|
|
} |
8012
|
|
|
} |
8013
|
|
|
|
8014
|
|
|
/** |
8015
|
|
|
* Returns an HTML ribbon to show on top of the exercise result, with |
8016
|
|
|
* colouring depending on the success or failure of the student |
8017
|
|
|
* @param $score |
8018
|
|
|
* @param $weight |
8019
|
|
|
* @param bool $check_pass_percentage |
8020
|
|
|
* @return string |
8021
|
|
|
*/ |
8022
|
|
|
public function get_question_ribbon($score, $weight, $check_pass_percentage = false) |
8023
|
|
|
{ |
8024
|
|
|
$eventMessage = null; |
8025
|
|
|
$ribbon = '<div class="question_row">'; |
8026
|
|
|
$ribbon .= '<div class="ribbon">'; |
8027
|
|
|
if ($check_pass_percentage) { |
8028
|
|
|
$is_success = ExerciseLib::is_success_exercise_result($score, $weight, $this->selectPassPercentage()); |
8029
|
|
|
// Color the final test score if pass_percentage activated |
8030
|
|
|
$ribbon_total_success_or_error = ""; |
8031
|
|
|
if (ExerciseLib::is_pass_pourcentage_enabled($this->selectPassPercentage())) { |
8032
|
|
|
if ($is_success) { |
8033
|
|
|
$eventMessage = $this->getOnSuccessMessage(); |
8034
|
|
|
$ribbon_total_success_or_error = ' ribbon-total-success'; |
8035
|
|
|
} else { |
8036
|
|
|
$eventMessage = $this->getOnFailedMessage(); |
8037
|
|
|
$ribbon_total_success_or_error = ' ribbon-total-error'; |
8038
|
|
|
} |
8039
|
|
|
} |
8040
|
|
|
$ribbon .= '<div class="rib rib-total '.$ribbon_total_success_or_error.'">'; |
8041
|
|
|
} else { |
8042
|
|
|
$ribbon .= '<div class="rib rib-total">'; |
8043
|
|
|
} |
8044
|
|
|
$ribbon .= '<h3>'.get_lang('YourTotalScore').": "; |
8045
|
|
|
$ribbon .= ExerciseLib::show_score($score, $weight, false, true); |
8046
|
|
|
$ribbon .= '</h3>'; |
8047
|
|
|
$ribbon .= '</div>'; |
8048
|
|
|
|
8049
|
|
|
if ($check_pass_percentage) { |
8050
|
|
|
$ribbon .= ExerciseLib::show_success_message($score, $weight, $this->selectPassPercentage()); |
8051
|
|
|
} |
8052
|
|
|
$ribbon .= '</div>'; |
8053
|
|
|
$ribbon .= '</div>'; |
8054
|
|
|
|
8055
|
|
|
$ribbon .= $eventMessage; |
8056
|
|
|
|
8057
|
|
|
return $ribbon; |
8058
|
|
|
} |
8059
|
|
|
|
8060
|
|
|
/** |
8061
|
|
|
* Returns an array of categories details for the questions of the current |
8062
|
|
|
* exercise. |
8063
|
|
|
* @return array |
8064
|
|
|
*/ |
8065
|
|
|
public function getQuestionWithCategories() |
8066
|
|
|
{ |
8067
|
|
|
$categoryTable = Database::get_course_table(TABLE_QUIZ_QUESTION_CATEGORY); |
8068
|
|
|
$categoryRelTable = Database::get_course_table(TABLE_QUIZ_QUESTION_REL_CATEGORY); |
8069
|
|
|
$TBL_EXERCICE_QUESTION = Database::get_course_table(TABLE_QUIZ_TEST_QUESTION); |
8070
|
|
|
$TBL_QUESTIONS = Database::get_course_table(TABLE_QUIZ_QUESTION); |
8071
|
|
|
$sql = "SELECT DISTINCT cat.* |
8072
|
|
|
FROM $TBL_EXERCICE_QUESTION e |
8073
|
|
|
INNER JOIN $TBL_QUESTIONS q |
8074
|
|
|
ON (e.question_id = q.id AND e.c_id = q.c_id) |
8075
|
|
|
INNER JOIN $categoryRelTable catRel |
8076
|
|
|
ON (catRel.question_id = e.question_id) |
8077
|
|
|
INNER JOIN $categoryTable cat |
8078
|
|
|
ON (cat.id = catRel.category_id) |
8079
|
|
|
WHERE |
8080
|
|
|
e.c_id = {$this->course_id} AND |
8081
|
|
|
e.exercice_id = ".intval($this->id); |
8082
|
|
|
|
8083
|
|
|
$result = Database::query($sql); |
8084
|
|
|
$categoriesInExercise = array(); |
8085
|
|
|
if (Database::num_rows($result)) { |
8086
|
|
|
$categoriesInExercise = Database::store_result($result, 'ASSOC'); |
8087
|
|
|
} |
8088
|
|
|
|
8089
|
|
|
return $categoriesInExercise; |
8090
|
|
|
} |
8091
|
|
|
|
8092
|
|
|
/** |
8093
|
|
|
* Calculate the max_score of the quiz, depending of question inside, and quiz advanced option |
8094
|
|
|
*/ |
8095
|
|
|
public function get_max_score() |
8096
|
|
|
{ |
8097
|
|
|
$out_max_score = 0; |
8098
|
|
|
// list of question's id !!! the array key start at 1 !!! |
8099
|
|
|
$questionList = $this->selectQuestionList(true); |
8100
|
|
|
|
8101
|
|
|
// test is randomQuestions - see field random of test |
8102
|
|
|
if ($this->random > 0 && $this->randomByCat == 0) { |
8103
|
|
|
$numberRandomQuestions = $this->random; |
8104
|
|
|
$questionScoreList = array(); |
8105
|
|
|
for ($i = 1; $i <= count($questionList); $i++) { |
8106
|
|
|
$tmpobj_question = Question::read($questionList[$i]); |
8107
|
|
|
$questionScoreList[] = $tmpobj_question->weighting; |
8108
|
|
|
} |
8109
|
|
|
rsort($questionScoreList); |
8110
|
|
|
// add the first $numberRandomQuestions value of score array to get max_score |
8111
|
|
|
for ($i = 0; $i < min($numberRandomQuestions, count($questionScoreList)); $i++) { |
8112
|
|
|
$out_max_score += $questionScoreList[$i]; |
8113
|
|
|
} |
8114
|
|
|
} else if ($this->random > 0 && $this->randomByCat > 0) { |
8115
|
|
|
// test is random by category |
8116
|
|
|
// get the $numberRandomQuestions best score question of each category |
8117
|
|
|
|
8118
|
|
|
$numberRandomQuestions = $this->random; |
8119
|
|
|
$tab_categories_scores = array(); |
8120
|
|
|
for ($i = 1; $i <= count($questionList); $i++) { |
8121
|
|
|
$question_category_id = TestCategory::getCategoryForQuestion($questionList[$i]); |
8122
|
|
|
if (!is_array($tab_categories_scores[$question_category_id])) { |
8123
|
|
|
$tab_categories_scores[$question_category_id] = array(); |
8124
|
|
|
} |
8125
|
|
|
$tmpobj_question = Question::read($questionList[$i]); |
8126
|
|
|
$tab_categories_scores[$question_category_id][] = $tmpobj_question->weighting; |
8127
|
|
|
} |
8128
|
|
|
|
8129
|
|
|
// here we've got an array with first key, the category_id, second key, score of question for this cat |
8130
|
|
|
while (list($key, $tab_scores) = each($tab_categories_scores)) { |
8131
|
|
|
rsort($tab_scores); |
8132
|
|
|
for ($i = 0; $i < min($numberRandomQuestions, count($tab_scores)); $i++) { |
8133
|
|
|
$out_max_score += $tab_scores[$i]; |
8134
|
|
|
} |
8135
|
|
|
} |
8136
|
|
|
} else { |
8137
|
|
|
// standard test, just add each question score |
8138
|
|
|
foreach ($questionList as $questionId) { |
8139
|
|
|
$question = Question::read($questionId, $this->course_id); |
8140
|
|
|
$out_max_score += $question->weighting; |
8141
|
|
|
} |
8142
|
|
|
} |
8143
|
|
|
|
8144
|
|
|
return $out_max_score; |
8145
|
|
|
} |
8146
|
|
|
|
8147
|
|
|
/** |
8148
|
|
|
* @return string |
8149
|
|
|
*/ |
8150
|
|
|
public function get_formated_title() |
8151
|
|
|
{ |
8152
|
|
|
return api_html_entity_decode($this->selectTitle()); |
8153
|
|
|
} |
8154
|
|
|
|
8155
|
|
|
/** |
8156
|
|
|
* @param $in_title |
8157
|
|
|
* @return string |
8158
|
|
|
*/ |
8159
|
|
|
public static function get_formated_title_variable($in_title) |
8160
|
|
|
{ |
8161
|
|
|
return api_html_entity_decode($in_title); |
8162
|
|
|
} |
8163
|
|
|
|
8164
|
|
|
/** |
8165
|
|
|
* @return string |
8166
|
|
|
*/ |
8167
|
|
|
public function format_title() |
8168
|
|
|
{ |
8169
|
|
|
return api_htmlentities($this->title); |
8170
|
|
|
} |
8171
|
|
|
|
8172
|
|
|
/** |
8173
|
|
|
* @param $in_title |
8174
|
|
|
* @return string |
8175
|
|
|
*/ |
8176
|
|
|
public static function format_title_variable($in_title) |
8177
|
|
|
{ |
8178
|
|
|
return api_htmlentities($in_title); |
8179
|
|
|
} |
8180
|
|
|
|
8181
|
|
|
/** |
8182
|
|
|
* @param int $courseId |
8183
|
|
|
* @param int $sessionId |
8184
|
|
|
* @return array exercises |
8185
|
|
|
*/ |
8186
|
|
|
public function getExercisesByCouseSession($courseId, $sessionId) |
8187
|
|
|
{ |
8188
|
|
|
$courseId = intval($courseId); |
8189
|
|
|
$sessionId = intval($sessionId); |
8190
|
|
|
|
8191
|
|
|
$tbl_quiz = Database::get_course_table(TABLE_QUIZ_TEST); |
8192
|
|
|
$sql = "SELECT * FROM $tbl_quiz cq |
8193
|
|
|
WHERE |
8194
|
|
|
cq.c_id = %s AND |
8195
|
|
|
(cq.session_id = %s OR cq.session_id = 0) AND |
8196
|
|
|
cq.active = 0 |
8197
|
|
|
ORDER BY cq.id"; |
8198
|
|
|
$sql = sprintf($sql, $courseId, $sessionId); |
8199
|
|
|
|
8200
|
|
|
$result = Database::query($sql); |
8201
|
|
|
|
8202
|
|
|
$rows = array(); |
8203
|
|
|
while ($row = Database::fetch_array($result, 'ASSOC')) { |
8204
|
|
|
$rows[] = $row; |
8205
|
|
|
} |
8206
|
|
|
|
8207
|
|
|
return $rows; |
8208
|
|
|
} |
8209
|
|
|
|
8210
|
|
|
/** |
8211
|
|
|
* |
8212
|
|
|
* @param int $courseId |
8213
|
|
|
* @param int $sessionId |
8214
|
|
|
* @param array $quizId |
8215
|
|
|
* @return array exercises |
8216
|
|
|
*/ |
8217
|
|
|
public function getExerciseAndResult($courseId, $sessionId, $quizId = array()) |
8218
|
|
|
{ |
8219
|
|
|
if (empty($quizId)) { |
8220
|
|
|
return array(); |
8221
|
|
|
} |
8222
|
|
|
|
8223
|
|
|
$sessionId = intval($sessionId); |
8224
|
|
|
|
8225
|
|
|
$ids = is_array($quizId) ? $quizId : array($quizId); |
8226
|
|
|
$ids = array_map('intval', $ids); |
8227
|
|
|
$ids = implode(',', $ids); |
8228
|
|
|
$track_exercises = Database :: get_main_table(TABLE_STATISTIC_TRACK_E_EXERCISES); |
8229
|
|
|
if ($sessionId != 0) { |
8230
|
|
|
$sql = "SELECT * FROM $track_exercises te |
8231
|
|
|
INNER JOIN c_quiz cq ON cq.id = te.exe_exo_id AND te.c_id = cq.c_id |
8232
|
|
|
WHERE |
8233
|
|
|
te.id = %s AND |
8234
|
|
|
te.session_id = %s AND |
8235
|
|
|
cq.id IN (%s) |
8236
|
|
|
ORDER BY cq.id"; |
8237
|
|
|
|
8238
|
|
|
$sql = sprintf($sql, $courseId, $sessionId, $ids); |
8239
|
|
|
} else { |
8240
|
|
|
$sql = "SELECT * FROM $track_exercises te |
8241
|
|
|
INNER JOIN c_quiz cq ON cq.id = te.exe_exo_id AND te.c_id = cq.c_id |
8242
|
|
|
WHERE |
8243
|
|
|
te.id = %s AND |
8244
|
|
|
cq.id IN (%s) |
8245
|
|
|
ORDER BY cq.id"; |
8246
|
|
|
$sql = sprintf($sql, $courseId, $ids); |
8247
|
|
|
} |
8248
|
|
|
$result = Database::query($sql); |
8249
|
|
|
$rows = array(); |
8250
|
|
|
while ($row = Database::fetch_array($result, 'ASSOC')) { |
8251
|
|
|
$rows[] = $row; |
8252
|
|
|
} |
8253
|
|
|
|
8254
|
|
|
return $rows; |
8255
|
|
|
} |
8256
|
|
|
|
8257
|
|
|
/** |
8258
|
|
|
* @param $exeId |
8259
|
|
|
* @param $exercise_stat_info |
8260
|
|
|
* @param $remindList |
8261
|
|
|
* @param $currentQuestion |
8262
|
|
|
* @return int|null |
8263
|
|
|
*/ |
8264
|
|
|
public static function getNextQuestionId($exeId, $exercise_stat_info, $remindList, $currentQuestion) |
8265
|
|
|
{ |
8266
|
|
|
$result = get_exercise_results_by_attempt($exeId, 'incomplete'); |
8267
|
|
|
|
8268
|
|
|
if (isset($result[$exeId])) { |
8269
|
|
|
$result = $result[$exeId]; |
8270
|
|
|
} else { |
8271
|
|
|
return null; |
8272
|
|
|
} |
8273
|
|
|
|
8274
|
|
|
$data_tracking = $exercise_stat_info['data_tracking']; |
8275
|
|
|
$data_tracking = explode(',', $data_tracking); |
8276
|
|
|
|
8277
|
|
|
// if this is the final question do nothing. |
8278
|
|
|
if ($currentQuestion == count($data_tracking)) { |
8279
|
|
|
return null; |
8280
|
|
|
} |
8281
|
|
|
|
8282
|
|
|
$currentQuestion = $currentQuestion - 1; |
8283
|
|
|
|
8284
|
|
|
if (!empty($result['question_list'])) { |
8285
|
|
|
$answeredQuestions = array(); |
8286
|
|
|
|
8287
|
|
|
foreach ($result['question_list'] as $question) { |
8288
|
|
|
if (!empty($question['answer'])) { |
8289
|
|
|
$answeredQuestions[] = $question['question_id']; |
8290
|
|
|
} |
8291
|
|
|
} |
8292
|
|
|
|
8293
|
|
|
// Checking answered questions |
8294
|
|
|
|
8295
|
|
|
$counterAnsweredQuestions = 0; |
8296
|
|
|
foreach ($data_tracking as $questionId) { |
8297
|
|
|
if (!in_array($questionId, $answeredQuestions)) { |
8298
|
|
|
if ($currentQuestion != $counterAnsweredQuestions) { |
8299
|
|
|
break; |
8300
|
|
|
} |
8301
|
|
|
} |
8302
|
|
|
$counterAnsweredQuestions++; |
8303
|
|
|
} |
8304
|
|
|
|
8305
|
|
|
$counterRemindListQuestions = 0; |
8306
|
|
|
// Checking questions saved in the reminder list |
8307
|
|
|
|
8308
|
|
|
if (!empty($remindList)) { |
8309
|
|
|
foreach ($data_tracking as $questionId) { |
8310
|
|
|
if (in_array($questionId, $remindList)) { |
8311
|
|
|
// Skip the current question |
8312
|
|
|
if ($currentQuestion != $counterRemindListQuestions) { |
8313
|
|
|
break; |
8314
|
|
|
} |
8315
|
|
|
} |
8316
|
|
|
$counterRemindListQuestions++; |
8317
|
|
|
} |
8318
|
|
|
|
8319
|
|
|
if ($counterRemindListQuestions < $currentQuestion) { |
8320
|
|
|
return null; |
8321
|
|
|
} |
8322
|
|
|
|
8323
|
|
|
if (!empty($counterRemindListQuestions)) { |
8324
|
|
|
if ($counterRemindListQuestions > $counterAnsweredQuestions) { |
8325
|
|
|
return $counterAnsweredQuestions; |
8326
|
|
|
} else { |
8327
|
|
|
return $counterRemindListQuestions; |
8328
|
|
|
} |
8329
|
|
|
} |
8330
|
|
|
} |
8331
|
|
|
|
8332
|
|
|
return $counterAnsweredQuestions; |
8333
|
|
|
} |
8334
|
|
|
} |
8335
|
|
|
|
8336
|
|
|
/** |
8337
|
|
|
* Gets the position of a questionId in the question list |
8338
|
|
|
* @param $questionId |
8339
|
|
|
* @return int |
8340
|
|
|
*/ |
8341
|
|
|
public function getPositionInCompressedQuestionList($questionId) |
8342
|
|
|
{ |
8343
|
|
|
$questionList = $this->getQuestionListWithMediasCompressed(); |
8344
|
|
|
$mediaQuestions = $this->getMediaList(); |
8345
|
|
|
$position = 1; |
8346
|
|
|
foreach ($questionList as $id) { |
8347
|
|
|
if (isset($mediaQuestions[$id]) && in_array($questionId, $mediaQuestions[$id])) { |
8348
|
|
|
$mediaQuestionList = $mediaQuestions[$id]; |
8349
|
|
|
if (in_array($questionId, $mediaQuestionList)) { |
8350
|
|
|
return $position; |
8351
|
|
|
} else { |
8352
|
|
|
$position++; |
8353
|
|
|
} |
8354
|
|
|
} else { |
8355
|
|
|
if ($id == $questionId) { |
8356
|
|
|
return $position; |
8357
|
|
|
} else { |
8358
|
|
|
$position++; |
8359
|
|
|
} |
8360
|
|
|
} |
8361
|
|
|
} |
8362
|
|
|
return 1; |
8363
|
|
|
} |
8364
|
|
|
} |
8365
|
|
|
|
This check looks for the bodies of
if
statements 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
if
bodies can be removed. If you have an empty if but statements in theelse
branch, consider inverting the condition.could be turned into
This is much more concise to read.