Project

General

Profile

Bug #5376 » exercise.lib.php

Yoselyn Castillo, 15/10/2012 18:26

 
1
<?php
2
/* For licensing terms, see /license.txt */
3
/**
4
 * Exercise library
5
 * @todo convert this lib into a static class
6
 *  
7
 * shows a question and its answers
8
 * @package chamilo.exercise
9
 * @author Olivier Brouckaert <oli.brouckaert@skynet.be>
10
 * @version $Id: exercise.lib.php 22247 2009-07-20 15:57:25Z ivantcholakov $
11
 * Modified by Hubert Borderiou 2011-10-21 Question Category
12
 */
13
/**
14
 * Code
15
 */
16
// The initialization class for the online editor is needed here.
17
require_once dirname(__FILE__).'/../inc/lib/fckeditor/fckeditor.php';
18

    
19
/**
20
 * Shows a question
21
 * 
22
 * @param int   question id
23
 * @param bool  if true only show the questions, no exercise title
24
 * @param bool  origin i.e = learnpath
25
 * @param int   current item from the list of questions
26
 * @param int   number of total questions
27
 * */
28
function showQuestion($questionId, $only_questions = false, $origin = false, $current_item = '', $show_title = true, $freeze = false, $user_choice = array(), $show_comment = false, $exercise_feedback = null, $show_answers = false) {
29
	
30
	// Text direction for the current language
31
	$is_ltr_text_direction = api_get_text_direction() != 'rtl';
32
	
33
	// Change false to true in the following line to enable answer hinting
34
	$debug_mark_answer = $show_answers; //api_is_allowed_to_edit() && false;
35
    
36
	// Reads question information
37
	if (!$objQuestionTmp = Question::read($questionId)) {
38
		// Question not found        
39
		return false;
40
	}
41
    
42
    if ($exercise_feedback != EXERCISE_FEEDBACK_TYPE_END) {
43
        $show_comment = false;
44
    }
45
    
46
	$answerType    = $objQuestionTmp->selectType();
47
	$pictureName   = $objQuestionTmp->selectPicture();
48
	
49
	if ($answerType != HOT_SPOT && $answerType != HOT_SPOT_DELINEATION) {
50
		// Question is not a hotspot
51
        
52
		if (!$only_questions) {
53
			$questionDescription = $objQuestionTmp->selectDescription();
54
			if ($show_title) {
55
				Testcategory::displayCategoryAndTitle($objQuestionTmp->id);	// 				
56
				echo Display::div($current_item.'. '.$objQuestionTmp->selectTitle(), array('class'=>'question_title'));
57
			}
58
			if (!empty($questionDescription)) {
59
                echo Display::div($questionDescription, array('class'=>'question_description'));
60
            }
61
        }
62
		
63
        if (in_array($answerType, array(FREE_ANSWER, ORAL_EXPRESSION)) && $freeze) {
64
            return '';
65
        }
66
        
67
        echo '<div class="question_options">';
68
        
69
		$s = '';
70
        if ($show_comment) {
71
            $s .= '<table class="table table-bordered">';
72
        } else {
73
            //$s .= '<table class="table">';
74
            $s .= '<table class="exercise_options">';
75
        }
76
		// construction of the Answer object (also gets all answers details)
77
		$objAnswerTmp = new Answer($questionId);
78
        
79
		$nbrAnswers     = $objAnswerTmp->selectNbrAnswers();
80
        $course_id      = api_get_course_int_id();
81
        $quiz_question_options = Question::readQuestionOption($questionId, $course_id);
82
        
83
		// For "matching" type here, we need something a little bit special
84
		// because the match between the suggestions and the answers cannot be
85
		// done easily (suggestions and answers are in the same table), so we
86
		// have to go through answers first (elems with "correct" value to 0).
87
		$select_items = array();
88
		//This will contain the number of answers on the left side. We call them
89
		// suggestions here, for the sake of comprehensions, while the ones
90
		// on the right side are called answers
91
		$num_suggestions = 0;
92

    
93
		if ($answerType == MATCHING) {
94
			$x = 1; //iterate through answers
95
			$letter = 'A'; //mark letters for each answer
96
			$answer_matching = $cpt1 = array();			
97

    
98
			for ($answerId=1; $answerId <= $nbrAnswers; $answerId++) {
99
				$answerCorrect = $objAnswerTmp->isCorrect($answerId);
100
				$numAnswer = $objAnswerTmp->selectAutoId($answerId);
101
				$answer=$objAnswerTmp->selectAnswer($answerId);
102
				if ($answerCorrect==0) {
103
					// options (A, B, C, ...) that will be put into the list-box
104
					// have the "correct" field set to 0 because they are answer
105
					$cpt1[$x] = $letter;
106
					$answer_matching[$x]=$objAnswerTmp->selectAnswerByAutoId($numAnswer);
107
					$x++; $letter++;
108
				}
109
			}
110
			$i = 1;
111
			
112
			$select_items[0]['id'] = 0;
113
			$select_items[0]['letter'] = '--';
114
			$select_items[0]['answer'] = '';
115
			
116
			foreach ($answer_matching as $id => $value) {
117
				$select_items[$i]['id'] 	= $value['id'];
118
				$select_items[$i]['letter'] = $cpt1[$id];
119
				$select_items[$i]['answer'] = $value['answer'];
120
				$i ++;
121
			}
122
			$num_suggestions = ($nbrAnswers - $x) + 1;
123
			
124
		} elseif ($answerType == FREE_ANSWER) {
125
			$fck_content = isset($user_choice[0]) && !empty($user_choice[0]['answer']) ? $user_choice[0]['answer']:null;
126
			
127
			$oFCKeditor = new FCKeditor("choice[".$questionId."]") ;
128
			
129
			$oFCKeditor->ToolbarSet = 'TestFreeAnswer';
130
			$oFCKeditor->Width      = '100%';
131
			$oFCKeditor->Height     = '200';
132
			$oFCKeditor->Value      = $fck_content;
133
            $s .= '<tr><td>';
134
            $s .= $oFCKeditor->CreateHtml();
135
            $s .= '</td></tr>';
136
		} elseif ($answerType == ORAL_EXPRESSION) {
137
			//Add nanog
138
			if (api_get_setting('enable_nanogong') == 'true') {				
139
				
140
				require_once api_get_path(LIBRARY_PATH).'nanogong.lib.php';
141
				
142
				//@todo pass this as a parameter
143
				global $exercise_stat_info, $exerciseId, $exe_id;
144
				
145
				if (!empty($exercise_stat_info)) {
146
					$params = array(					
147
						'exercise_id' 	=> $exercise_stat_info['exe_exo_id'],
148
						'exe_id' 		=> $exercise_stat_info['exe_id'],
149
						'question_id'   => $questionId
150
					);				
151
				} else {
152
					$params = array(
153
						'exercise_id' 	=> $exerciseId,
154
						'exe_id' 		=> 'temp_exe',
155
						'question_id'   => $questionId
156
					);
157
				}
158
				
159
				$nano = new Nanogong($params);
160
				echo $nano->show_button();				
161
			}
162
			
163
			$oFCKeditor = new FCKeditor("choice[".$questionId."]") ;			
164
			$oFCKeditor->ToolbarSet = 'TestFreeAnswer';
165
			$oFCKeditor->Width  = '100%';
166
			$oFCKeditor->Height = '150';
167
			$oFCKeditor->ToolbarStartExpanded = false;			
168
			$oFCKeditor->Value	= '' ;
169
			$s .= '<tr><td>';
170
			$s .= $oFCKeditor->CreateHtml();
171
			$s .= '</td></tr>';
172
		} 
173
    
174
		// Now navigate through the possible answers, using the max number of
175
		// answers for the question as a limiter
176
		$lines_count = 1; // a counter for matching-type answers
177
                    
178
        if ($answerType == MULTIPLE_ANSWER_TRUE_FALSE || $answerType ==  MULTIPLE_ANSWER_COMBINATION_TRUE_FALSE) {            
179
            $header = '';
180
            $header .= Display::tag('th', get_lang('Options'));   
181
            foreach ($objQuestionTmp->options as $key=>$item) {                
182
                $header .= Display::tag('th', $item);                           
183
            }                
184
            if ($show_comment) {                
185
                $header .= Display::tag('th', get_lang('Feedback'));                
186
            }
187
            $s.= Display::tag('tr', $header, array('style'=>'text-align:left;'));  
188
        }
189
        
190
        if ($show_comment) {
191
            if (in_array($answerType, array(MULTIPLE_ANSWER, MULTIPLE_ANSWER_COMBINATION, UNIQUE_ANSWER, UNIQUE_ANSWER_NO_OPTION, GLOBAL_MULTIPLE_ANSWER))) {
192
                $header = '';
193
                $header .= Display::tag('th', get_lang('Options'));
194
                if ($exercise_feedback == EXERCISE_FEEDBACK_TYPE_END) {
195
                    $header .= Display::tag('th', get_lang('Feedback'));
196
                }
197
                $s.= Display::tag('tr',$header, array('style'=>'text-align:left;'));
198
            }
199
        }
200

    
201
        $matching_correct_answer = 0;
202
        $user_choice_array = array();
203
        if (!empty($user_choice)) {        	
204
        	foreach($user_choice as $item) {
205
        		$user_choice_array[] = $item['answer'];
206
        	}
207
        }
208
                
209
		for ($answerId=1; $answerId <= $nbrAnswers; $answerId++) {
210
			$answer          = $objAnswerTmp->selectAnswer($answerId);
211
			$answerCorrect   = $objAnswerTmp->isCorrect($answerId);         
212
			$numAnswer       = $objAnswerTmp->selectAutoId($answerId);            
213
            $comment         = $objAnswerTmp->selectComment($answerId);
214

    
215
			// Unique answer
216
			if ($answerType == UNIQUE_ANSWER || $answerType == UNIQUE_ANSWER_NO_OPTION) {								
217
				$input_id = 'choice-'.$questionId.'-'.$answerId;
218
				if (isset($user_choice[0]['answer']) && $user_choice[0]['answer'] == $numAnswer ) {
219
					$attributes = array('id' =>$input_id, 'class'=>'checkbox','checked'=>1, 'selected'=>1);
220
				} else {
221
					$attributes = array('id' =>$input_id, 'class'=>'checkbox');
222
				}
223
                
224
                if ($debug_mark_answer) {
225
					if ($answerCorrect) {
226
						$attributes['checked'] = 1;
227
                        $attributes['selected'] = 1;
228
					}
229
				}
230
				
231
				$answer = Security::remove_XSS($answer, STUDENT);
232
                
233
				$s .= Display::input('hidden','choice2['.$questionId.']','0');
234
				//@todo fix $is_ltr_text_direction
235
				//<p style="float: '.($is_ltr_text_direction ? 'left' : 'right').'; padding-'.($is_ltr_text_direction ? 'right' : 'left').': 4px;">
236
				//$s .= '<div style="margin-'.($is_ltr_text_direction ? 'left' : 'right').': 24px;">'.
237
						
238
				$s .= '<tr><td>';
239
				$s .= '<span class="question_answer">';
240
				$s .= Display::input('radio', 'choice['.$questionId.']', $numAnswer, $attributes);
241
				$s .= Display::tag('label', $answer, array('for'=>$input_id)).'</span>';
242
				$s .= '</td>';
243
               
244
                if ($show_comment) {                
245
                    $s .= '<td>';
246
                    $s .= $comment;
247
                    $s .= '</td>';
248
                }
249
                
250
                $s .= '</tr>';
251
			} elseif ($answerType == MULTIPLE_ANSWER || $answerType == MULTIPLE_ANSWER_TRUE_FALSE || $answerType == GLOBAL_MULTIPLE_ANSWER) {
252
				$input_id = 'choice-'.$questionId.'-'.$answerId;				 
253
				$answer = Security::remove_XSS($answer, STUDENT);
254
				
255
				if (in_array($numAnswer, $user_choice_array)) {
256
					$attributes = array('id' =>$input_id, 'class'=>'checkbox','checked'=>1, 'selected'=>1);
257
				} else {
258
					$attributes = array('id' =>$input_id, 'class'=>'checkbox');
259
				}
260
                
261
                if ($debug_mark_answer) {
262
					if ($answerCorrect) {
263
						$attributes['checked'] = 1;
264
                        $attributes['selected'] = 1;
265
					}
266
				}
267
                
268
                if ($answerType == MULTIPLE_ANSWER || $answerType == GLOBAL_MULTIPLE_ANSWER) {
269
                    $s .= '<input type="hidden" name="choice2['.$questionId.']" value="0" />';                
270
                    $s .= '<tr><td>';
271
                    
272
                    $s .= '<span class="question_answer">';                				
273
                    $s .= Display::tag('span', Display::input('checkbox', 'choice['.$questionId.']['.$numAnswer.']', $numAnswer, $attributes));                                        
274
                    $s .= Display::tag('label', $answer, array('for'=>$input_id)).'</span></td>';                	
275

    
276
                    if ($show_comment) {                
277
                        $s .= '<td>';
278
                        $s .= $comment;
279
                        $s .= '</td>';
280
                    }
281
                    $s .='</tr>';
282
                } elseif ($answerType == MULTIPLE_ANSWER_TRUE_FALSE) {
283
                    
284
                	$my_choice = array();
285
                    if (!empty($user_choice_array)) {
286
                        foreach ($user_choice_array as $item) {
287
                            $item = explode(':', $item);
288
                            $my_choice[$item[0]] = $item[1];
289
                        }
290
                    }
291
                    
292
                    $s .='<tr>';
293
                    $s .= Display::tag('td', $answer);
294
                    
295
                    if (!empty($quiz_question_options)) {
296
                    	foreach ($quiz_question_options as $id => $item) {
297
                            
298
                    		if (isset($my_choice[$numAnswer]) && $id == $my_choice[$numAnswer]) {
299
                    			$attributes = array('class'=>'checkbox','checked'=>1, 'selected'=>1);
300
                    		} else {
301
                    			$attributes = array('class'=>'checkbox');
302
                    		}                            
303
                            
304
                            if ($debug_mark_answer) {
305
                                if ($id == $answerCorrect) {
306
                                    $attributes['checked'] = 1;
307
                                    $attributes['selected'] = 1;
308
                                }
309
                            }                    
310
                    		$s .= Display::tag('td', Display::input('radio', 'choice['.$questionId.']['.$numAnswer.']', $id, $attributes), array('style'=>'text-align:center'));
311
                    	}
312
                    }
313
                    
314
                    if ($show_comment) {                
315
                        $s .= '<td>';
316
                        $s .= $comment;
317
                        $s .= '</td>';
318
                    }
319
                    $s.='</tr>';
320
                }
321
			} elseif ($answerType == MULTIPLE_ANSWER_COMBINATION) {
322
				// multiple answers	
323
				$input_id = 'choice-'.$questionId.'-'.$answerId;
324
				
325
				if (in_array($numAnswer, $user_choice_array)) {				    
326
				    $attributes = array('id'=>$input_id, 'class'=>'checkbox','checked'=>1, 'selected'=>1);
327
				} else {
328
				    $attributes = array('id'=>$input_id, 'class'=>'checkbox');
329
				}
330
                
331
                if ($debug_mark_answer) {
332
					if ($answerCorrect) {
333
						$attributes['checked'] = 1;
334
                        $attributes['selected'] = 1;
335
					}
336
				}
337
								
338
				$answer = Security::remove_XSS($answer, STUDENT);
339
				$s .= '<input type="hidden" name="choice2['.$questionId.']" value="0" />'.
340
                        '<tr><td>';
341
				$s .= '<span class="question_answer">';				
342
				$s .= Display::tag('span', Display::input('checkbox', 'choice['.$questionId.']['.$numAnswer.']', 1, $attributes));				
343
			    $s .= Display::tag('label', $answer, array('for'=>$input_id)).'</span></td>';
344
					
345
                if ($show_comment) {                
346
                    $s .= '<td>';
347
                    $s .= $comment;
348
                    $s .= '</td>';
349
                }
350

    
351
                $s.= '</tr>';
352

    
353
            } elseif ($answerType == MULTIPLE_ANSWER_COMBINATION_TRUE_FALSE) {
354
            	$s .= '<input type="hidden" name="choice2['.$questionId.']" value="0" />';
355
            	
356
            	$my_choice = array();
357
            	if (!empty($user_choice_array)) {
358
            		foreach ($user_choice_array as $item) {
359
            			$item = explode(':', $item);
360
            			$my_choice[$item[0]] = $item[1];
361
            		}
362
            	}
363
            	$answer = Security::remove_XSS($answer, STUDENT);
364
            	$s .='<tr>';
365
            	$s .= Display::tag('td', $answer);
366
            	
367
            	foreach ($objQuestionTmp->options as $key => $item) {            		
368
            		if (isset($my_choice[$numAnswer]) && $key == $my_choice[$numAnswer]) {
369
            			$attributes = array('class'=>'checkbox','checked' => 1, 'selected' => 1);
370
            		} else {
371
            			$attributes = array('class'=>'checkbox');
372
            		}
373
                    
374
                    if ($debug_mark_answer) {
375
                        if ($key == $answerCorrect) {
376
                            $attributes['checked']  = 1;
377
                            $attributes['selected'] = 1;
378
                        }
379
                    }                    
380
            		$s .= Display::tag('td', Display::input('radio', 'choice['.$questionId.']['.$numAnswer.']', $key, $attributes));
381
            	}
382
				
383
                if ($show_comment) {                
384
                    $s .= '<td>';
385
                    $s .= $comment;
386
                    $s .= '</td>';
387
                }			
388
            	$s.='</tr>';
389
				
390
			} elseif ($answerType == FILL_IN_BLANKS) {                
391
				list($answer) = explode('::', $answer);
392
                
393
                //Correct answer
394
				api_preg_match_all('/\[[^]]+\]/', $answer, $correct_answer_list);
395
                
396
                //Student's answezr
397
				if (isset($user_choice[0]['answer'])) {
398
					api_preg_match_all('/\[[^]]+\]/', $user_choice[0]['answer'], $student_answer_list);
399
					$student_answer_list = $student_answer_list[0];
400
				}
401
                
402
                //If debug
403
                if ($debug_mark_answer) {
404
					$student_answer_list = $correct_answer_list[0];                    
405
                }
406
                
407
				if (!empty($correct_answer_list) && !empty($student_answer_list)) {
408
				    $correct_answer_list = $correct_answer_list[0];				    
409
				    $i = 0;				    
410
				    foreach ($correct_answer_list as $correct_item) {
411
				        $value = null;
412
				        if (isset($student_answer_list[$i]) && !empty($student_answer_list[$i])) {
413
                            
414
				        	//Cleaning student answer list
415
				            $value = strip_tags($student_answer_list[$i]);                            
416
				            $value = api_substr($value, 1, api_strlen($value)-2);                            
417
				            $value = explode('/', $value);
418
                            
419
				            if (!empty($value[0])) {				            	
420
				            	$value = str_replace('&nbsp;', '',  trim($value[0]));                                
421
				            }                                
422
                            $correct_item = preg_quote($correct_item);                            
423
				            $answer = api_preg_replace('/'.$correct_item.'/', Display::input('text', "choice[$questionId][]", $value), $answer);                            
424
                            //$answer = api_preg_replace('/\['.$correct_item.'+\]/', Display::input('text', "choice[$questionId][]", $value), $answer);	
425
				        }		        				        
426
				        $i++;				        
427
				    }
428
				} else {                    
429
					$answer = api_preg_replace('/\[[^]]+\]/', Display::input('text', "choice[$questionId][]", '', $attributes), $answer);
430
				}
431
				$s .= '<tr><td>'.$answer.'</td></tr>';
432
            } elseif ($answerType == MATCHING) {
433
				// matching type, showing suggestions and answers
434
				// TODO: replace $answerId by $numAnswer
435
				
436
				if ($answerCorrect != 0) {
437
					// only show elements to be answered (not the contents of
438
					// the select boxes, who are corrrect = 0)
439
					$s .= '<tr><td width="45%" valign="top">';
440
					$parsed_answer = $answer;                    
441
					//left part questions
442
					$s .= ' <span style="float:left; width:8%;"><b>'.$lines_count.'</b>.&nbsp;</span>
443
						 	<span style="float:left; width:92%;">'.$parsed_answer.'</span></td>';
444
					//middle part (matches selects)					
445
					
446
					$s .= '<td width="10%" valign="top" align="center">&nbsp;&nbsp;
447
				            <select name="choice['.$questionId.']['.$numAnswer.']">';
448
					
449
					// fills the list-box
450
					foreach ($select_items as $key=>$val) {
451
						// set $debug_mark_answer to true at function start to
452
						// show the correct answer with a suffix '-x'
453
						$selected = '';
454
						if ($debug_mark_answer) {
455
							if ($val['id'] == $answerCorrect) {								
456
								$selected = 'selected="selected"';
457
							}
458
						}						
459
						if (isset($user_choice[$matching_correct_answer]) && $val['id'] == $user_choice[$matching_correct_answer]['answer']) {
460
						    $selected = 'selected="selected"';
461
						}
462
						$s .= '<option value="'.$val['id'].'" '.$selected.'>'.$val['letter'].$help.'</option>';						
463
				
464
					}  // end foreach()
465

    
466
					$s .= '</select></td>';
467
					//print_r($select_items);
468
					//right part (answers)
469
					$s.='<td width="45%" valign="top" >';
470
					if (isset($select_items[$lines_count])) {
471
						$s.='<span style="float:left; width:5%;"><b>'.$select_items[$lines_count]['letter'].'.</b></span>'.
472
							 '<span style="float:left; width:95%;">'.$select_items[$lines_count]['answer'].'</span>';
473
					} else {
474
						$s.='&nbsp;';
475
					}
476
					$s .= '</td>';
477
					$s .= '</tr>';
478
					$lines_count++;
479
					//if the left side of the "matching" has been completely
480
					// shown but the right side still has values to show...
481
					if (($lines_count -1) == $num_suggestions) {
482
						// if it remains answers to shown at the right side
483
						while (isset($select_items[$lines_count])) {
484
							$s .= '<tr>
485
								  <td colspan="2"></td>
486
								  <td valign="top">';
487
							$s.='<b>'.$select_items[$lines_count]['letter'].'.</b> '.$select_items[$lines_count]['answer'];
488
							$s.="</td>
489
							</tr>";
490
							$lines_count++;
491
						}	// end while()
492
					}  // end if()
493
					$matching_correct_answer++;
494
				}
495
			}
496
		}	// end for()
497
		$s .= '</table>';		
498
		$s .= '</div>';
499
		
500

    
501
		// destruction of the Answer object
502
		unset($objAnswerTmp);
503

    
504
		// destruction of the Question object
505
		unset($objQuestionTmp);
506

    
507
		if ($origin != 'export') {
508
			echo $s;
509
		} else {
510
			return $s;
511
		}
512
	} elseif ($answerType == HOT_SPOT || $answerType == HOT_SPOT_DELINEATION) {
513
		// Question is a HOT_SPOT        
514
        //checking document/images visibility
515
        if (api_is_platform_admin() || api_is_course_admin()) {
516
            require_once api_get_path(LIBRARY_PATH).'document.lib.php';
517
            $course = api_get_course_info();        
518
            $doc_id = DocumentManager::get_document_id($course, '/images/'.$pictureName);  
519
            if (is_numeric($doc_id)) {              
520
                $images_folder_visibility = api_get_item_visibility($course,'document', $doc_id, api_get_session_id());                 
521
                if (!$images_folder_visibility) {
522
                    //This message is shown only to the course/platform admin if the image is set to visibility = false
523
                    Display::display_warning_message(get_lang('ChangeTheVisibilityOfTheCurrentImage'));
524
                }
525
            }
526
        }
527
		$questionName         = $objQuestionTmp->selectTitle();
528
		$questionDescription  = $objQuestionTmp->selectDescription();
529
        
530
        if ($freeze) {
531
            echo Display::img($objQuestionTmp->selectPicturePath());
532
            return;
533
        }        
534

    
535
		// Get the answers, make a list
536
		$objAnswerTmp         = new Answer($questionId);
537
		$nbrAnswers           = $objAnswerTmp->selectNbrAnswers();
538

    
539
		// get answers of hotpost
540
		$answers_hotspot = array();
541
		for ($answerId=1;$answerId <= $nbrAnswers;$answerId++) {
542
			$answers = $objAnswerTmp->selectAnswerByAutoId($objAnswerTmp->selectAutoId($answerId));
543
			$answers_hotspot[$answers['id']] = $objAnswerTmp->selectAnswer($answerId);
544
		}
545

    
546
		// display answers of hotpost order by id
547
		$answer_list = '<div style="padding: 10px; margin-left: 0px; border: 1px solid #A4A4A4; height: 408px; width: 200px;"><b>'.get_lang('HotspotZones').'</b><dl>';
548
		if (!empty($answers_hotspot)) {
549
			ksort($answers_hotspot);
550
			foreach ($answers_hotspot as $key => $value) {
551
				$answer_list .= '<dt>'.$key.'.- '.$value.'</dt><br />';
552
			}
553
		}
554
		$answer_list .= '</dl></div>';
555
		
556
		if ($answerType == HOT_SPOT_DELINEATION) {
557
			$answer_list='';
558
			$swf_file = 'hotspot_delineation_user';
559
			$swf_height = 405;			
560
		} else {
561
			$swf_file = 'hotspot_user';
562
			$swf_height = 436;
563
		}
564

    
565
		if (!$only_questions) {
566
            if ($show_title) {
567
				Testcategory::displayCategoryAndTitle($objQuestionTmp->id);
568
                echo '<div class="question_title">'.$current_item.'. '.$questionName.'</div>';
569
            }
570
			//@todo I need to the get the feedback type
571
			echo '<input type="hidden" name="hidden_hotspot_id" value="'.$questionId.'" />';
572
			echo '<table class="exercise_questions" >
573
				  <tr>
574
			  		<td valign="top" colspan="2">';
575
			echo $questionDescription;
576
			echo '</td></tr>';
577
		}        
578
		$canClick = isset($_GET['editQuestion']) ? '0' : (isset($_GET['modifyAnswers']) ? '0' : '1');
579
        
580
		$s .= '<script type="text/javascript" src="../plugin/hotspot/JavaScriptFlashGateway.js"></script>
581
						<script src="../plugin/hotspot/hotspot.js" type="text/javascript" ></script>
582
						<script type="text/javascript">
583
						<!--
584
						// Globals
585
						// Major version of Flash required
586
						var requiredMajorVersion = 7;
587
						// Minor version of Flash required
588
						var requiredMinorVersion = 0;
589
						// Minor version of Flash required
590
						var requiredRevision = 0;
591
						// the version of javascript supported
592
						var jsVersion = 1.0;
593
						// -->
594
						</script>
595
						<script language="VBScript" type="text/vbscript">
596
						<!-- // Visual basic helper required to detect Flash Player ActiveX control version information
597
						Function VBGetSwfVer(i)
598
						  on error resume next
599
						  Dim swControl, swVersion
600
						  swVersion = 0
601

    
602
						  set swControl = CreateObject("ShockwaveFlash.ShockwaveFlash." + CStr(i))
603
						  if (IsObject(swControl)) then
604
						    swVersion = swControl.GetVariable("$version")
605
						  end if
606
						  VBGetSwfVer = swVersion
607
						End Function
608
						// -->
609
						</script>
610

    
611
						<script language="JavaScript1.1" type="text/javascript">
612
						<!-- // Detect Client Browser type
613
						var isIE  = (navigator.appVersion.indexOf("MSIE") != -1) ? true : false;
614
						var isWin = (navigator.appVersion.toLowerCase().indexOf("win") != -1) ? true : false;
615
						var isOpera = (navigator.userAgent.indexOf("Opera") != -1) ? true : false;
616
						jsVersion = 1.1;
617
						// JavaScript helper required to detect Flash Player PlugIn version information
618
						function JSGetSwfVer(i) {
619
							// NS/Opera version >= 3 check for Flash plugin in plugin array
620
							if (navigator.plugins != null && navigator.plugins.length > 0) {
621
								if (navigator.plugins["Shockwave Flash 2.0"] || navigator.plugins["Shockwave Flash"]) {
622
									var swVer2 = navigator.plugins["Shockwave Flash 2.0"] ? " 2.0" : "";
623
						      		var flashDescription = navigator.plugins["Shockwave Flash" + swVer2].description;
624
									descArray = flashDescription.split(" ");
625
									tempArrayMajor = descArray[2].split(".");
626
									versionMajor = tempArrayMajor[0];
627
									versionMinor = tempArrayMajor[1];
628
									if ( descArray[3] != "" ) {
629
										tempArrayMinor = descArray[3].split("r");
630
									} else {
631
										tempArrayMinor = descArray[4].split("r");
632
									}
633
						      		versionRevision = tempArrayMinor[1] > 0 ? tempArrayMinor[1] : 0;
634
						            flashVer = versionMajor + "." + versionMinor + "." + versionRevision;
635
						      	} else {
636
									flashVer = -1;
637
								}
638
							}
639
							// MSN/WebTV 2.6 supports Flash 4
640
							else if (navigator.userAgent.toLowerCase().indexOf("webtv/2.6") != -1) flashVer = 4;
641
							// WebTV 2.5 supports Flash 3
642
							else if (navigator.userAgent.toLowerCase().indexOf("webtv/2.5") != -1) flashVer = 3;
643
							// older WebTV supports Flash 2
644
							else if (navigator.userAgent.toLowerCase().indexOf("webtv") != -1) flashVer = 2;
645
							// Can\'t detect in all other cases
646
							else
647
							{
648
								flashVer = -1;
649
							}
650
							return flashVer;
651
						}
652
						// When called with reqMajorVer, reqMinorVer, reqRevision returns true if that version or greater is available
653

    
654
						function DetectFlashVer(reqMajorVer, reqMinorVer, reqRevision) {
655
						 	reqVer = parseFloat(reqMajorVer + "." + reqRevision);
656
						   	// loop backwards through the versions until we find the newest version
657
							for (i=25;i>0;i--) {
658
								if (isIE && isWin && !isOpera) {
659
									versionStr = VBGetSwfVer(i);
660
								} else {
661
									versionStr = JSGetSwfVer(i);
662
								}
663
								if (versionStr == -1 ) {
664
									return false;
665
								} else if (versionStr != 0) {
666
									if(isIE && isWin && !isOpera) {
667
										tempArray         = versionStr.split(" ");
668
										tempString        = tempArray[1];
669
										versionArray      = tempString .split(",");
670
									} else {
671
										versionArray      = versionStr.split(".");
672
									}
673
									versionMajor      = versionArray[0];
674
									versionMinor      = versionArray[1];
675
									versionRevision   = versionArray[2];
676

    
677
									versionString     = versionMajor + "." + versionRevision;   // 7.0r24 == 7.24
678
									versionNum        = parseFloat(versionString);
679
						        	// is the major.revision >= requested major.revision AND the minor version >= requested minor
680
									if ( (versionMajor > reqMajorVer) && (versionNum >= reqVer) ) {
681
										return true;
682
									} else {
683
										return ((versionNum >= reqVer && versionMinor >= reqMinorVer) ? true : false );
684
									}
685
								}
686
							}
687
						}
688
						// -->
689
						</script>';
690
		$s .= '<tr><td valign="top" colspan="2" width="520"><table><tr><td width="520">
691
					<script>
692
						<!--
693
						// Version check based upon the values entered above in "Globals"
694
						var hasReqestedVersion = DetectFlashVer(requiredMajorVersion, requiredMinorVersion, requiredRevision);
695

    
696
						// Check to see if the version meets the requirements for playback
697
						if (hasReqestedVersion) {  // if we\'ve detected an acceptable version
698
						    var oeTags = \'<object type="application/x-shockwave-flash" data="../plugin/hotspot/'.$swf_file.'.swf?modifyAnswers='.$questionId.'&amp;canClick:'.$canClick.'" width="600" height="'.$swf_height.'">\'
699
						    			+ \'<param name="wmode" value="transparent">\'
700
										+ \'<param name="movie" value="../plugin/hotspot/'.$swf_file.'.swf?modifyAnswers='.$questionId.'&amp;canClick:'.$canClick.'" />\'
701
										+ \'<\/object>\';
702
						    document.write(oeTags);   // embed the Flash Content SWF when all tests are passed
703
						} else {  // flash is too old or we can\'t detect the plugin
704
							var alternateContent = "Error<br \/>"
705
								+ "Hotspots requires Macromedia Flash 7.<br \/>"
706
								+ "<a href=\"http://www.macromedia.com/go/getflash/\">Get Flash<\/a>";
707
							document.write(alternateContent);  // insert non-flash content
708
						}
709
						// -->
710
					</script>
711
					</td>
712
					<td valign="top" align="left">'.$answer_list.'</td></tr>
713
					</table>
714
		</td></tr>';        
715
		echo $s;
716
	}
717
	echo '</table>';
718
	return $nbrAnswers;
719
}
720

    
721
function get_exercise_track_exercise_info($exe_id) {
722
	$TBL_EXERCICES         	= Database::get_course_table(TABLE_QUIZ_TEST);
723
	$TBL_TRACK_EXERCICES	= Database::get_statistic_table(TABLE_STATISTIC_TRACK_E_EXERCICES);
724
	$exe_id = intval($exe_id);
725
    $result = array();
726
    if (!empty($exe_id)) {
727
	   $sql_fb_type = 'SELECT * FROM '.$TBL_EXERCICES.' as e INNER JOIN '.$TBL_TRACK_EXERCICES.' as te  ON (e.id=te.exe_exo_id) WHERE te.exe_id='.$exe_id;
728
	   $res_fb_type = Database::query($sql_fb_type);
729
	   $result      = Database::fetch_array($res_fb_type, 'ASSOC');
730
    }
731
	return $result;	
732
}
733

    
734

    
735
/**
736
 * Validates the time control key
737
 */
738
function exercise_time_control_is_valid($exercise_id, $lp_id = 0 , $lp_item_id = 0) {
739
    $course_id = api_get_course_int_id();	
740
	$exercise_id = intval($exercise_id);
741
	$TBL_EXERCICES =  Database::get_course_table(TABLE_QUIZ_TEST);
742
	$sql 	= "SELECT expired_time FROM $TBL_EXERCICES WHERE c_id = $course_id AND id = $exercise_id";
743
	$result = Database::query($sql);
744
	$row	= Database::fetch_array($result, 'ASSOC');
745
	if (!empty($row['expired_time']) ) {
746
		$current_expired_time_key = get_time_control_key($exercise_id, $lp_id, $lp_item_id);        
747
		if (isset($_SESSION['expired_time'][$current_expired_time_key])) {                	
748
	        $current_time = time();
749
			$expired_time = api_strtotime($_SESSION['expired_time'][$current_expired_time_key], 'UTC');
750
			$total_time_allowed = $expired_time + 30;
751
			//error_log('expired time converted + 30: '.$total_time_allowed);
752
			//error_log('$current_time: '.$current_time);
753
	        if ($total_time_allowed < $current_time) {
754
	        	return false;
755
	        }
756
	        return true;
757
		} else {
758
			return false;
759
		}
760
	} else {
761
		return true;
762
	}
763
}
764

    
765
/**
766
	Deletes the time control token 
767
*/
768
function exercise_time_control_delete($exercise_id,  $lp_id = 0 , $lp_item_id = 0) {	
769
	$current_expired_time_key = get_time_control_key($exercise_id, $lp_id, $lp_item_id);
770
	unset($_SESSION['expired_time'][$current_expired_time_key]);	
771
}
772

    
773
/**
774
	Generates the time control key
775
*/
776
function get_time_control_key($exercise_id, $lp_id = 0, $lp_item_id = 0) {
777
	$exercise_id = intval($exercise_id);
778
    $lp_id = intval($lp_id);
779
    $lp_item_id = intval($lp_item_id);
780
	return api_get_course_int_id().'_'.api_get_session_id().'_'.$exercise_id.'_'.api_get_user_id().'_'.$lp_id.'_'.$lp_item_id;
781
}
782

    
783
/**
784
 * Get session time control
785
 */
786
function get_session_time_control_key($exercise_id, $lp_id = 0, $lp_item_id = 0) {
787
    $time_control_key = get_time_control_key($exercise_id, $lp_id, $lp_item_id);
788
    $return_value = $_SESSION['expired_time'][$time_control_key];
789
    return $return_value;
790
}
791

    
792
/**
793
 * Gets count of exam results
794
 * @todo this function should be moved in a library  + no global calls 
795
 */
796
function get_count_exam_results($exercise_id, $extra_where_conditions) {
797
    $count = get_exam_results_data(null, null, null, null, $exercise_id, $extra_where_conditions, true);
798
    return $count;
799
}
800

    
801
function get_count_exam_hotpotatoes_results($in_hotpot_path) {
802
    return get_exam_results_hotpotatoes_data(0, 0, '', '', $in_hotpot_path, true, '');
803
}
804

    
805
//function get_exam_results_hotpotatoes_data($from, $number_of_items, $column, $direction, $exercise_id, $extra_where_conditions = null, $get_count = false) {
806
function get_exam_results_hotpotatoes_data($in_from, $in_number_of_items, $in_column, $in_direction, $in_hotpot_path, $in_get_count = false, $where_condition) {
807
    
808
    $tab_res = array();
809
    $course_code = api_get_course_id();
810
    // by default in_column = 1 If parameters given, it is the name of the column witch is the bdd field name
811
    if ($in_column == 1) {
812
        $in_column = 'firstname';
813
    }
814
    
815
    $TBL_TRACK_HOTPOTATOES      = Database :: get_statistic_table(TABLE_STATISTIC_TRACK_E_HOTPOTATOES);    
816
    $TBL_GROUP_REL_USER         = Database :: get_course_table(TABLE_GROUP_USER);
817
    $TBL_GROUP                  = Database :: get_course_table(TABLE_GROUP);
818
    $TBL_USER                   = Database :: get_main_table(TABLE_MAIN_USER);    
819
    
820
    $sql .= "SELECT * FROM $TBL_TRACK_HOTPOTATOES thp JOIN $TBL_USER u ON thp.exe_user_id = u.user_id WHERE thp.exe_cours_id = '$course_code' AND exe_name LIKE '$in_hotpot_path%'";
821

    
822
    // just count how many answers
823
    if ($in_get_count) {
824
        $res = Database::query($sql);
825
        return Database::num_rows($res);
826
    }
827
    
828
    // get a number of sorted results
829
    $sql .= " $where_condition ORDER BY $in_column $in_direction  LIMIT $in_from, $in_number_of_items";
830
    
831
    $res = Database::query($sql);
832
    while ($data = Database::fetch_array($res)) {
833
        $tab_one_res = array();
834
        $tab_one_res['firstname'] = $data['firstname'];
835
        $tab_one_res['lastname'] = $data['lastname'];
836
        $tab_one_res['username'] = $data['username'];
837
        $tab_one_res['group_name'] = implode("<br/>",GroupManager::get_user_group_name($data['user_id']));
838
        $tab_one_res['exe_date'] = $data['exe_date'];
839
        $tab_one_res['score'] = $data['exe_result'].'/'.$data['exe_weighting'];
840
        $tab_one_res['actions'] = "";
841
        $tab_res[] = $tab_one_res;
842
    }
843
    return $tab_res;
844
}
845

    
846
/**
847
 * Gets the exam'data results
848
 * @todo this function should be moved in a library  + no global calls 
849
 */
850
function get_exam_results_data($from, $number_of_items, $column, $direction, $exercise_id, $extra_where_conditions = null, $get_count = false) {
851
    
852
                        
853
    //@todo replace all this globals
854
    global $documentPath, $filter;
855
	
856
	if (empty($extra_where_conditions)) {
857
		$extra_where_conditions = "1 = 1 ";
858
	}
859
    
860
    $course_id = api_get_course_int_id();
861
    $course_code = api_get_course_id();
862
                
863
   	$is_allowedToEdit           = api_is_allowed_to_edit(null,true) || api_is_allowed_to_edit(true) || api_is_drh();
864
	    
865
    $TBL_USER                   = Database :: get_main_table(TABLE_MAIN_USER);    
866
    $TBL_EXERCICES              = Database :: get_course_table(TABLE_QUIZ_TEST);    
867
	$TBL_GROUP_REL_USER         = Database :: get_course_table(TABLE_GROUP_USER);
868
    $TBL_GROUP                  = Database :: get_course_table(TABLE_GROUP);
869
	
870
    $TBL_TRACK_EXERCICES        = Database :: get_statistic_table(TABLE_STATISTIC_TRACK_E_EXERCICES);
871
    $TBL_TRACK_HOTPOTATOES      = Database :: get_statistic_table(TABLE_STATISTIC_TRACK_E_HOTPOTATOES);    
872
    $TBL_TRACK_ATTEMPT_RECORDING= Database :: get_statistic_table(TABLE_STATISTIC_TRACK_E_ATTEMPT_RECORDING);    
873
    
874
    $session_id_and = ' AND te.session_id = '.api_get_session_id().' ';
875
    
876
    $exercise_id = intval($exercise_id);
877
    
878
    $exercise_where = '';
879
    if (!empty($exercise_id)) {
880
        $exercise_where .= ' AND te.exe_exo_id = '.$exercise_id.'  ';
881
    } 
882
    
883
    $hotpotatoe_where = '';
884
    if (!empty($_GET['path'])) {
885
        $hotpotatoe_path = Database::escape_string($_GET['path']);
886
        $hotpotatoe_where .= ' AND exe_name = "'.$hotpotatoe_path.'"  ';
887
    }  
888
         
889
    // sql for chamilo-type tests for teacher / tutor view		
890
    $sql_inner_join_tbl_track_exercices = " (
891
                                            SELECT DISTINCT ttte.*, if(tr.exe_id,1, 0) as revised 
892
                                            FROM $TBL_TRACK_EXERCICES ttte LEFT JOIN $TBL_TRACK_ATTEMPT_RECORDING tr 
893
                                            ON (ttte.exe_id = tr.exe_id) 
894
                                            WHERE exe_cours_id = '$course_code' AND
895
                                                  exe_exo_id = $exercise_id AND
896
                                                  ttte.session_id = ".api_get_session_id()."
897
                                            )";    	
898
    if ($is_allowedToEdit) {
899
        //Teacher view		
900
        if (isset($_GET['gradebook']) && $_GET['gradebook'] == 'view') {
901
            //$exercise_where_query = ' te.exe_exo_id = ce.id AND ';
902
        }
903

    
904
		$sqlFromOption                      = "";
905
		$sqlWhereOption                     = "";           // for hpsql
906
	    		
907
        //@todo fix to work with COURSE_RELATION_TYPE_RRHH in both queries
908
        
909
        //Hack in order to filter groups
910
        $sql_inner_join_tbl_user = '';
911
                
912
        if (strpos($extra_where_conditions, 'group_id')) {
913
            $sql_inner_join_tbl_user = " 
914
            (
915
                SELECT u.user_id, firstname, lastname, email, username, g.name as group_name, g.id as group_id
916
                FROM $TBL_USER u 
917
                INNER JOIN $TBL_GROUP_REL_USER gru ON ( gru.user_id = u.user_id AND gru.c_id=".$course_id.")
918
                INNER JOIN $TBL_GROUP g ON (gru.group_id = g.id)
919
            )";
920

    
921
        }
922
           
923
        if (strpos($extra_where_conditions, 'group_all')) {   
924
            
925
            $extra_where_conditions = str_replace("AND (  group_id = 'group_all'  )", '', $extra_where_conditions);
926
            $extra_where_conditions = str_replace("AND group_id = 'group_all'", '', $extra_where_conditions);
927
            $extra_where_conditions = str_replace("group_id = 'group_all' AND", '', $extra_where_conditions);
928
                        
929
            $sql_inner_join_tbl_user = " 
930
            (
931
                SELECT u.user_id, firstname, lastname, email, username, '' as group_name, '' as group_id
932
                FROM $TBL_USER u
933
            )";
934
            $sql_inner_join_tbl_user = null;
935
        }
936
        
937
        if (strpos($extra_where_conditions, 'group_none')) {
938
            $extra_where_conditions = str_replace("AND (  group_id = 'group_none'  )", "AND (  group_id is null  )", $extra_where_conditions);
939
            $extra_where_conditions = str_replace("AND group_id = 'group_none'", "AND (  group_id is null  )", $extra_where_conditions);
940
            $sql_inner_join_tbl_user = " 
941
            (
942
                SELECT u.user_id, firstname, lastname, email, username, g.name as group_name, g.id as group_id
943
                FROM $TBL_USER u
944
                LEFT OUTER JOIN $TBL_GROUP_REL_USER gru ON ( gru.user_id = u.user_id AND gru.c_id=".$course_id." ) 
945
                LEFT OUTER JOIN $TBL_GROUP g ON (gru.group_id = g.id AND g.c_id = ".$course_id.")
946
            )";
947
        }
948
        
949
        //All
950
        $is_empty_sql_inner_join_tbl_user = false;
951

    
952
        if (empty($sql_inner_join_tbl_user)) {
953
            $is_empty_sql_inner_join_tbl_user = true;
954
             $sql_inner_join_tbl_user = " 
955
            (
956
                SELECT u.user_id, firstname, lastname, email, username, ' ' as group_name, '' as group_id
957
                FROM $TBL_USER u                 
958
            )";
959
        }
960

    
961
        
962
        $sqlFromOption = " , $TBL_GROUP_REL_USER AS gru ";
963
        $sqlWhereOption = "  AND gru.c_id = ".api_get_course_int_id()." AND gru.user_id = user.user_id ";
964
		
965
        $first_and_last_name = api_is_western_name_order() ? "firstname, lastname" : "lastname, firstname";
966
        
967
        if ($get_count) {
968
            $sql_select = "SELECT count(te.exe_id) ";
969
        } else {
970
            $sql_select = "SELECT DISTINCT
971
                    user_id, 
972
                    $first_and_last_name, 
973
                    ce.title, 
974
                    username,
975
                    te.exe_result, 
976
                    te.exe_weighting,
977
                    te.exe_date, 
978
                    te.exe_id, 
979
                    email as exemail, 
980
                    te.start_date, 
981
                    steps_counter, 
982
                    exe_user_id,
983
                    te.exe_duration, 
984
                    propagate_neg,
985
                    revised,
986
                    group_name,
987
                    group_id";
988
        }
989
        
990
        $sql = " $sql_select            
991
                FROM $TBL_EXERCICES AS ce 
992
                INNER JOIN $sql_inner_join_tbl_track_exercices AS te ON (te.exe_exo_id = ce.id) 
993
                INNER JOIN $sql_inner_join_tbl_user  AS user ON (user.user_id = exe_user_id)
994
                WHERE $extra_where_conditions AND
995
                    te.status != 'incomplete' 
996
                    AND te.exe_cours_id='" . api_get_course_id() . "' $session_id_and 
997
                    AND ce.active <>-1 
998
                    AND orig_lp_id = 0 
999
                    AND orig_lp_item_id = 0
1000
                    AND ce.c_id=".api_get_course_int_id()."					
1001
                    $exercise_where ";
1002
         
1003
        // sql for hotpotatoes tests for teacher / tutor view
1004
        
1005
        if ($get_count) {
1006
            $hpsql_select = "SELECT count(username)";
1007
        } else {
1008
            $hpsql_select = "SELECT 
1009
                    $first_and_last_name , 
1010
                    username,
1011
                    tth.exe_name, 
1012
                    tth.exe_result , 
1013
                    tth.exe_weighting, 
1014
                    tth.exe_date";
1015
        }
1016
        
1017
        $hpsql = " $hpsql_select
1018
                FROM 
1019
                    $TBL_TRACK_HOTPOTATOES tth, 
1020
                    $TBL_USER user
1021
                    $sqlFromOption
1022
                WHERE  
1023
                    user.user_id=tth.exe_user_id 
1024
                    AND tth.exe_cours_id = '" . api_get_course_id()."'  
1025
                    $hotpotatoe_where 
1026
                    $sqlWhereOption
1027
					AND $where_condition
1028
                ORDER BY 
1029
                    tth.exe_cours_id ASC, 
1030
                    tth.exe_date DESC";
1031
    }
1032
    
1033
    if ($get_count) {
1034
        $resx = Database::query($sql);
1035
        $rowx = Database::fetch_row($resx,'ASSOC');
1036
        return $rowx[0];
1037
    }
1038
    
1039
    $teacher_list = CourseManager::get_teacher_list_from_course_code(api_get_course_id());
1040
    $teacher_id_list = array();
1041
    foreach ($teacher_list as $teacher) {
1042
    	$teacher_id_list[] = $teacher['user_id'];
1043
    }    
1044
    
1045
    //Simple exercises
1046
    if (empty($hotpotatoe_where)) {
1047
        $column             = !empty($column) ? Database::escape_string($column) : null;
1048
        $from               = intval($from);
1049
        $number_of_items    = intval($number_of_items);
1050
        
1051
        if (!empty($column)) {
1052
            $sql .= " ORDER BY $column $direction ";
1053
        }
1054
        $sql .= " LIMIT $from, $number_of_items";			
1055
        
1056
        $results = array();        
1057
        $resx = Database::query($sql);
1058
        while ($rowx = Database::fetch_array($resx,'ASSOC')) {
1059
            $results[] = $rowx;
1060
        }
1061
    
1062
        $list_info = array();
1063
        
1064
        $group_list = GroupManager::get_group_list();
1065
        $clean_group_list = array();
1066
        
1067
        if (!empty($group_list)) {
1068
            foreach ($group_list as $group) {
1069
                $clean_group_list[$group['id']] = $group['name'];
1070
            }
1071
        }
1072
            
1073
        if (is_array($results)) {
1074
			
1075
            $users_array_id = array();
1076
            if ($_GET['gradebook'] == 'view') {                
1077
                $from_gradebook = true;
1078
            }
1079
            $sizeof = count($results);
1080
    
1081
            $user_list_id = array ();                        
1082
                        
1083
            $locked = api_resource_is_locked_by_gradebook($exercise_id, LINK_EXERCISE);
1084
            
1085
            //Looping results
1086
            for ($i = 0; $i < $sizeof; $i++) {               
1087
                $revised = $results[$i]['revised'];	
1088
                
1089
                if ($from_gradebook && ($is_allowedToEdit)) {
1090
                    if (in_array($results[$i]['username'] . $results[$i]['firstname'] . $results[$i]['lastname'], $users_array_id)) {
1091
                        continue;
1092
                    }
1093
                    $users_array_id[] = $results[$i]['username'] . $results[$i]['firstname'] . $results[$i]['lastname'];
1094
                }
1095
                                 
1096
                //Add all groups by user
1097
                $group_name_list = null;
1098

    
1099
                if ($is_empty_sql_inner_join_tbl_user) {
1100
                    $group_list = GroupManager::get_group_ids(api_get_course_int_id(), $results[$i]['user_id']);
1101

    
1102
                    foreach ($group_list as $id) {               
1103
                        $group_name_list .= $clean_group_list[$id].'<br/>';
1104
                    }
1105
                    $results[$i]['group_name'] = $group_name_list;
1106
                }                
1107
                
1108
                $results[$i]['exe_duration'] =  !empty($results[$i]['exe_duration']) ? round($results[$i]['exe_duration'] / 60) : 0;
1109

    
1110
                $user_list_id[] = $results[$i]['exe_user_id'];
1111
                $id = $results[$i]['exe_id'];   
1112
               
1113
                $dt = api_convert_and_format_date($results[$i]['exe_weighting']);                
1114
                
1115
                // we filter the results if we have the permission to
1116
                if (isset($results[$i]['results_disabled'])) {
1117
                    $result_disabled = intval($results[$i]['results_disabled']);
1118
                }  else {
1119
                    $result_disabled = 0;
1120
                }
1121
    
1122
                if ($result_disabled == 0) {      
1123
                    
1124
                    $my_res     = $results[$i]['exe_result'];
1125
                    $my_total   = $results[$i]['exe_weighting'];
1126
                    
1127
                    $results[$i]['start_date']  =   api_get_local_time($results[$i]['start_date']);
1128
                    $results[$i]['exe_date']    =   api_get_local_time($results[$i]['exe_date']);                    
1129
                    
1130
                    if (!$results[$i]['propagate_neg'] && $my_res < 0) {
1131
                        $my_res = 0;
1132
                    }
1133
                    $score = show_score($my_res, $my_total);
1134
                    
1135
                    $actions = '';
1136
                    if ($is_allowedToEdit) {
1137
                        if (isset($teacher_id_list)) {
1138
                            if (in_array($results[$i]['exe_user_id'], $teacher_id_list)) {
1139
                                $actions .= Display::return_icon('teachers.gif', get_lang('Teacher'));
1140
                            }
1141
                        }
1142
                        if ($revised) {
1143
                            $actions .= "<a href='exercise_show.php?".api_get_cidreq()."&action=edit&id=$id'>".Display :: return_icon('edit.png', get_lang('Edit'), array(), ICON_SIZE_SMALL);
1144
                            $actions .= '&nbsp;';
1145
                        } else {
1146
                            $actions .="<a href='exercise_show.php?".api_get_cidreq()."&action=qualify&id=$id'>".Display :: return_icon('quiz.gif', get_lang('Qualify'));
1147
                            $actions .='&nbsp;';
1148
                        }
1149
                        $actions .="</a>";
1150
                                                
1151
                        if ($filter == 2) {
1152
                            $actions .=' <a href="exercise_history.php?'.api_get_cidreq().'&exe_id=' . $id . '">' .Display :: return_icon('history.gif', get_lang('ViewHistoryChange')).'</a>';
1153
                        }                        
1154
                        
1155
                        //Admin can always delete the attempt
1156
                        if ($locked == false || api_is_platform_admin()) {
1157
                            $actions .=' <a href="exercise_report.php?'.api_get_cidreq().'&filter_by_user='.intval($_GET['filter_by_user']).'&filter=' . $filter . '&exerciseId='.$exercise_id.'&delete=delete&did=' . $id . '" onclick="javascript:if(!confirm(\'' . sprintf(get_lang('DeleteAttempt'), $user, $dt) . '\')) return false;">'.Display :: return_icon('delete.png', get_lang('Delete')).'</a>';                            
1158
                            $actions .='&nbsp;';
1159
                        }
1160
                        
1161
                    } else {
1162
                    	$attempt_url 	= api_get_path(WEB_CODE_PATH).'exercice/result.php?'.api_get_cidreq().'&id='.$results[$i]['exe_id'].'&id_session='.api_get_session_id().'&height=500&width=750';
1163
                    	$attempt_link 	= Display::url(get_lang('Show'), $attempt_url, array('class'=>'ajax btn'));                    	
1164
                    	$actions .= $attempt_link;
1165
                    }                    
1166
                    
1167
                    if ($revised) {
1168
                        $revised = Display::label(get_lang('Validated'), 'success');                            
1169
                    } else {
1170
                        $revised = Display::label(get_lang('NotValidated'), 'info');
1171
                    }
1172
                    
1173
                    if ($is_allowedToEdit) {                        					
1174
						$results[$i]['status']  =  $revised;
1175
						$results[$i]['score']   =  $score;
1176
						$results[$i]['actions'] =  $actions;
1177
						$list_info[] = $results[$i];                        
1178
                    } else {
1179
                        $results[$i]['status']  =  $revised;
1180
						$results[$i]['score']   =  $score;							
1181
                        $results[$i]['actions'] =  $actions;                        
1182
						$list_info[] = $results[$i];
1183
                    }
1184
                }
1185
            }
1186
        }
1187
    } else {
1188
        //echo $hpsql; var_dump($hpsql);
1189
        $hpresults = getManyResultsXCol($hpsql, 6);        
1190
   
1191
        // Print HotPotatoes test results.
1192
        if (is_array($hpresults)) {
1193
            
1194
            for ($i = 0; $i < sizeof($hpresults); $i++) {               
1195
                $hp_title = GetQuizName($hpresults[$i][3], $documentPath);                
1196
                if ($hp_title == '') {
1197
                    $hp_title = basename($hpresults[$i][3]);
1198
                }
1199
                //var_dump($hpresults[$i]);
1200
                
1201
                $hp_date = api_get_local_time($hpresults[$i][6], null, date_default_timezone_get());
1202
                $hp_result = round(($hpresults[$i][4] / ($hpresults[$i][5] != 0 ? $hpresults[$i][5] : 1)) * 100, 2).'% ('.$hpresults[$i][4].' / '.$hpresults[$i][5].')';
1203
                if ($is_allowedToEdit) {                   
1204
                    $list_info[] = array($hpresults[$i][0], $hpresults[$i][1], $hpresults[$i][2], '',  $hp_title, '-',  $hp_date , $hp_result , '-');
1205
                } else {
1206
                    $list_info[] = array($hp_title, '-', $hp_date , $hp_result , '-');
1207
                }
1208
            }
1209
        }
1210
    }	
1211
    
1212
    return $list_info;
1213
}
1214

    
1215

    
1216
/**
1217
 * Converts the score with the exercise_max_note and exercise_min_score the platform settings + formats the results using the float_format function
1218
 * 
1219
 * @param   float   score
1220
 * @param   float   weight
1221
 * @param   bool    show porcentage or not
1222
 * @param	 bool	use or not the platform settings
1223
 * @return  string  an html with the score modified
1224
 */
1225
function show_score($score, $weight, $show_percentage = true, $use_platform_settings = true) {
1226
    if (is_null($score) && is_null($weight)) {
1227
        return '-';
1228
    }
1229
    
1230
    $max_note =  api_get_setting('exercise_max_score');
1231
    $min_note =  api_get_setting('exercise_min_score');
1232
    
1233
    if ($use_platform_settings) {
1234
        if ($max_note != '' && $min_note != '') {        
1235
            if (!empty($weight) && intval($weight) != 0) {
1236
    	       $score        = $min_note + ($max_note - $min_note) * $score /$weight;
1237
            } else {
1238
               $score        = $min_note;
1239
            }            
1240
            $weight         = $max_note;
1241
        }
1242
    }
1243
    $score_rounded = float_format($score, 1);    
1244
    $weight = float_format($weight, 1);    
1245
    
1246
    $percentage = float_format(($score / ($weight != 0 ? $weight : 1)) * 100, 1);
1247
    
1248
    $html  = '';
1249
    if ($show_percentage) {        
1250
        $parent = '(' . $score_rounded . ' / ' . $weight . ')';
1251
        $html = $percentage." %  $parent";	
1252
    } else {    
1253
    	$html = $score_rounded . ' / ' . $weight;
1254
    }  
1255
    $html  = Display::span($html, array('class' => 'score_exercise'));
1256
    return $html;	
1257
}
1258

    
1259
function is_success_exercise_result($score, $weight, $pass_percentage) {
1260
    $percentage = float_format(($score / ($weight != 0 ? $weight : 1)) * 100, 1);    
1261
    if (isset($pass_percentage) && !empty($pass_percentage)) {
1262
        if ($percentage >= $pass_percentage) {
1263
            return true;
1264
        }
1265
    }
1266
    return false;        
1267
}
1268

    
1269
function show_success_message($score, $weight, $pass_percentage) {
1270
    $res = "";
1271
    if (is_pass_pourcentage_enabled($pass_percentage)) {
1272
        $is_success = is_success_exercise_result($score, $weight, $pass_percentage);
1273
        
1274
        $icon = '';
1275
        if ($is_success) {        
1276
            //$html .= Display::return_message(get_lang('CongratulationsYouPassedTheTest'), 'success');
1277
            $html = get_lang('CongratulationsYouPassedTheTest');
1278
            $icon = Display::return_icon('completed.png', get_lang('Correct'), array(), ICON_SIZE_MEDIUM);
1279
        } else {
1280
            //$html .= Display::return_message(get_lang('YouDidNotReachTheMinimumScore'), 'warning');
1281
            $html = get_lang('YouDidNotReachTheMinimumScore');
1282
            $icon = Display::return_icon('warning.png', get_lang('Wrong'), array(), ICON_SIZE_MEDIUM);
1283
        }    
1284
        $html = Display::tag('h4', $html);
1285
        $html .= Display::tag('h5', $icon, array('style' => 'width:40px; padding:5px 10px 0px 0px'));
1286
        $res = $html;
1287
    }
1288
    return $res;
1289
}
1290

    
1291
/**
1292
 * Return true if pass_pourcentage activated (we use the pass pourcentage feature
1293
 * return false if pass_percentage = 0 (we don't use the pass pourcentage feature
1294
 * @param $in_pass_pourcentage
1295
 * @return boolean
1296
 * In this version, pass_percentage and show_success_message are disabled if
1297
 * pass_percentage is set to 0
1298
 */
1299
function is_pass_pourcentage_enabled($in_pass_pourcentage) {
1300
    return $in_pass_pourcentage > 0;
1301
}
1302

    
1303
/**
1304
 * Converts a numeric value in a percentage example 0.66666 to 66.67 %
1305
 * @param $value
1306
 * @return float Converted number
1307
 */
1308
function convert_to_percentage($value) {
1309
    $return = '-';
1310
    if ($value != '') {
1311
        $return = float_format($value * 100, 1).' %';
1312
    }
1313
    return $return;    
1314
}
1315

    
1316
/**
1317
 * Converts a score/weight values to the platform scale 
1318
 * @param   float   score
1319
 * @param   float   weight
1320
 * @return  float   the score rounded converted to the new range
1321
 */
1322
function convert_score($score, $weight) {
1323
    $max_note =  api_get_setting('exercise_max_score');
1324
    $min_note =  api_get_setting('exercise_min_score');  
1325
          
1326
    if ($score != '' && $weight != '') {        
1327
        if ($max_note != '' && $min_note != '') {           
1328
           if (!empty($weight)) {          
1329
               $score   = $min_note + ($max_note - $min_note) * $score / $weight;
1330
           } else {
1331
               $score   = $min_note;
1332
           }                   
1333
        }           
1334
    }    
1335
    $score_rounded  = float_format($score, 1);  
1336
    return $score_rounded;
1337
}
1338

    
1339
/**
1340
 * Getting all active exercises from a course from a session (if a session_id is provided we will show all the exercises in the course + all exercises in the session)
1341
 * @param   array   course data
1342
 * @param   int     session id
1343
 * @return  array   array with exercise data
1344
 */
1345
function get_all_exercises($course_info = null, $session_id = 0, $check_publication_dates = false) {
1346
	$TBL_EXERCICES = Database :: get_course_table(TABLE_QUIZ_TEST);
1347
	$course_id = api_get_course_int_id();
1348
	
1349
    if (!empty($course_info) && !empty($course_info['real_id'])) {
1350
    	$course_id = $course_info['real_id'];
1351
    }    
1352
    
1353
    if ($session_id == -1) {
1354
    	$session_id  = 0;
1355
    }
1356
    
1357
    $now = api_get_utc_datetime();
1358
    $time_conditions = '';
1359
    
1360
    if ($check_publication_dates) {        
1361
        $time_conditions = " AND ((start_time <> '0000-00-00 00:00:00' AND start_time < '$now'  AND end_time <> '0000-00-00 00:00:00' AND end_time > '$now' )  OR "; //start and end are set
1362
        $time_conditions .= " (start_time <> '0000-00-00 00:00:00' AND start_time < '$now'  AND end_time = '0000-00-00 00:00:00') OR "; // only start is set
1363
        $time_conditions .= " (start_time = '0000-00-00 00:00:00'   AND end_time <> '0000-00-00 00:00:00'  AND end_time > '$now') OR   "; // only end is set
1364
        $time_conditions .= " (start_time = '0000-00-00 00:00:00'   AND end_time =  '0000-00-00 00:00:00'))  "; // nothing is set                           
1365
    }
1366
    
1367
    if ($session_id == 0) {       
1368
    	$conditions = array('where'=>array('active = ? AND session_id = ? AND c_id = ? '.$time_conditions => array('1', $session_id, $course_id)), 'order'=>'title');        
1369
    } else {
1370
        //All exercises
1371
    	$conditions = array('where'=>array('active = ? AND  (session_id = 0 OR session_id = ? ) AND c_id = ? '.$time_conditions => array('1', $session_id, $course_id)), 'order'=>'title');        
1372
    }    
1373
    return Database::select('*',$TBL_EXERCICES, $conditions);
1374
}
1375

    
1376

    
1377
/**
1378
 * Getting all active exercises from a course from a session (if a session_id is provided we will show all the exercises in the course + all exercises in the session)
1379
 * @param   array   course data
1380
 * @param   int     session id
1381
 * @param		int			course c_id
1382
 * @return  array   array with exercise data
1383
 * modified by Hubert Borderiou 
1384
 */
1385
function get_all_exercises_for_course_id($course_info = null, $session_id = 0, $course_id=0) {
1386
   	$TBL_EXERCICES = Database :: get_course_table(TABLE_QUIZ_TEST);
1387
    if ($session_id == -1) {
1388
    	$session_id  = 0;
1389
    }
1390
    if ($session_id == 0) {
1391
    	$conditions = array('where'=>array('active = ? AND session_id = ? AND c_id = ?'=>array('1', $session_id, $course_id)), 'order'=>'title');
1392
    } else {
1393
        //All exercises
1394
    	$conditions = array('where'=>array('active = ? AND (session_id = 0 OR session_id = ? ) AND c_id=?' =>array('1', $session_id, $course_id)), 'order'=>'title');
1395
    }
1396
    return Database::select('*',$TBL_EXERCICES, $conditions);
1397
}
1398

    
1399
/**
1400
 * Gets the position of the score based in a given score (result/weight) and the exe_id based in the user list
1401
 * (NO Exercises in LPs )
1402
 * @param   float   user score to be compared *attention* $my_score = score/weight and not just the score
1403
 * @param   int     exe id of the exercise (this is necesary because if 2 students have the same score the one with the minor exe_id will have a best position, just to be fair and FIFO)
1404
 * @param   int     exercise id
1405
 * @param   string  course code
1406
 * @param   int     session id
1407
 * @return  int     the position of the user between his friends in a course (or course within a session)
1408
 */
1409
function get_exercise_result_ranking($my_score, $my_exe_id, $exercise_id, $course_code, $session_id = 0, $user_list = array(), $return_string = true) { 
1410
    //No score given we return 
1411
    if (is_null($my_score)) {
1412
        return '-';
1413
    }   
1414
    if (empty($user_list)) {
1415
        return '-';
1416
    }
1417
    
1418
    $best_attempts = array();   
1419
    foreach ($user_list as $user_data) {
1420
        $user_id = $user_data['user_id'];        
1421
        $best_attempts[$user_id]= get_best_attempt_by_user($user_id, $exercise_id, $course_code, $session_id);   
1422
    }
1423

    
1424
    if (empty($best_attempts)) {
1425
    	return 1;
1426
    } else {
1427
        $position = 1; 
1428
        $my_ranking = array();
1429
        foreach($best_attempts as $user_id => $result) {            
1430
            if (!empty($result['exe_weighting']) && intval($result['exe_weighting']) != 0) {
1431
                $my_ranking[$user_id] = $result['exe_result']/$result['exe_weighting'];
1432
            } else {
1433
                $my_ranking[$user_id] = 0;
1434
            }         
1435
        }         
1436
        //if (!empty($my_ranking)) { 
1437
            asort($my_ranking);
1438
            $position = count($my_ranking);
1439
            if (!empty($my_ranking)) {        
1440
                foreach ($my_ranking as $user_id => $ranking) {
1441
                	if ($my_score >= $ranking) {
1442
                        if ($my_score == $ranking) {
1443
                            $exe_id = $best_attempts[$user_id]['exe_id'];
1444
                            if ($my_exe_id < $exe_id) {
1445
                                $position--;
1446
                            }
1447
                        } else {           
1448
                		  $position--;                    
1449
                        }
1450
                	}
1451
                }        
1452
            }
1453
        //}
1454
        $return_value = array('position'=>$position, 'count'=>count($my_ranking)); 
1455
        //var_dump($my_score, $my_ranking);
1456
        if ($return_string) {
1457
            if (!empty($position) && !empty($my_ranking)) {
1458
               $return_value = $position.'/'.count($my_ranking); 
1459
            } else {
1460
                $return_value = '-';
1461
            }
1462
        }
1463
        return $return_value;    
1464
    }
1465
}
1466

    
1467
/**
1468
 * Gets the position of the score based in a given score (result/weight) and the exe_id based in all attempts
1469
 * (NO Exercises in LPs ) old funcionality by attempt
1470
 * @param   float   user score to be compared attention => score/weight
1471
 * @param   int     exe id of the exercise (this is necesary because if 2 students have the same score the one with the minor exe_id will have a best position, just to be fair and FIFO)
1472
 * @param   int     exercise id
1473
 * @param   string  course code
1474
 * @param   int     session id
1475
 * @return  int     the position of the user between his friends in a course (or course within a session)
1476
 */
1477
function get_exercise_result_ranking_by_attempt($my_score, $my_exe_id, $exercise_id, $course_code, $session_id = 0, $return_string = true) {
1478
    if (empty($session_id)) {
1479
    	$session_id = 0;
1480
    }
1481
    if (is_null($my_score)) {
1482
        return '-';
1483
    }    
1484
    $user_results = get_all_exercise_results($exercise_id, $course_code, $session_id, false);
1485
    $position_data = array();
1486
    if (empty($user_results)) {
1487
    	return 1;
1488
    } else {
1489
        $position = 1; 
1490
        $my_ranking = array();
1491
        foreach($user_results as $result) {
1492
            //print_r($result);
1493
            if (!empty($result['exe_weighting']) && intval($result['exe_weighting']) != 0) {
1494
                $my_ranking[$result['exe_id']] = $result['exe_result']/$result['exe_weighting'];
1495
            } else {
1496
                $my_ranking[$result['exe_id']] = 0;
1497
            }         
1498
        }        
1499
        asort($my_ranking);
1500
        $position = count($my_ranking);
1501
        if (!empty($my_ranking)) {        
1502
            foreach($my_ranking as $exe_id=>$ranking) {
1503
            	if ($my_score >= $ranking) {
1504
                    if ($my_score == $ranking) {
1505
                        if ($my_exe_id < $exe_id) {
1506
                            $position--;
1507
                        }
1508
                    } else {           
1509
            		  $position--;                    
1510
                    }
1511
            	}
1512
            }        
1513
        }
1514
        $return_value = array('position'=>$position, 'count'=>count($my_ranking)); 
1515
        //var_dump($my_score, $my_ranking);
1516
        if ($return_string) {
1517
            if (!empty($position) && !empty($my_ranking)) {
1518
               return $position.'/'.count($my_ranking); 
1519
            }
1520
        }
1521
        return $return_value;    
1522
    }
1523
}
1524

    
1525

    
1526
/*
1527
 *  Get the best attempt in a exercise (NO Exercises in LPs )
1528
 */
1529

    
1530
function get_best_attempt_in_course($exercise_id, $course_code, $session_id) { 
1531
    $user_results = get_all_exercise_results($exercise_id, $course_code, $session_id, false);
1532
    $best_score_data = array();
1533
    $best_score = 0;
1534
    if (!empty($user_results)) {
1535
        foreach($user_results as $result) {
1536
            if (!empty($result['exe_weighting']) && intval($result['exe_weighting']) != 0) {
1537
                $score = $result['exe_result']/$result['exe_weighting'];
1538
                if ($score >= $best_score) {
1539
                    $best_score = $score;
1540
                    $best_score_data = $result;
1541
                }
1542
            }
1543
        }
1544
    }
1545
    return $best_score_data;
1546
}
1547

    
1548
/*
1549
 *  Get the best score in a exercise (NO Exercises in LPs )
1550
 */
1551
function get_best_attempt_by_user($user_id, $exercise_id, $course_code, $session_id) { 
1552
    $user_results = get_all_exercise_results($exercise_id, $course_code, $session_id, false);
1553
    $best_score_data = array();
1554
    $best_score = 0;
1555
    if (!empty($user_results)) {       
1556
        foreach($user_results as $result) {
1557
            if ($result['exe_user_id'] != $user_id) continue;
1558
            if (!empty($result['exe_weighting']) && intval($result['exe_weighting']) != 0) {
1559
                $score = $result['exe_result']/$result['exe_weighting'];                
1560
                if ($score >= $best_score) {
1561
                    $best_score = $score;
1562
                    $best_score_data = $result;
1563
                }
1564
            }
1565
        }
1566
    }    
1567
    return $best_score_data;
1568
}
1569

    
1570

    
1571

    
1572

    
1573
/**
1574
 * Get average score (NO Exercises in LPs )
1575
 * @param 	int	exercise id
1576
 * @param 	string	course code
1577
 * @param 	int	session id
1578
 * @return 	float	Average score
1579
 */
1580
function get_average_score($exercise_id, $course_code, $session_id) { 
1581
    $user_results = get_all_exercise_results($exercise_id, $course_code, $session_id);
1582
    $avg_score_data = array();    
1583
    $avg_score = 0;
1584
    if (!empty($user_results)) {        
1585
        foreach($user_results as $result) {
1586
            if (!empty($result['exe_weighting']) && intval($result['exe_weighting']) != 0) {
1587
                $score = $result['exe_result']/$result['exe_weighting'];
1588
                $avg_score +=$score;                
1589
            }
1590
        }
1591
        $avg_score = float_format($avg_score / count($user_results), 1);
1592
    }
1593
    return $avg_score;
1594
}
1595

    
1596
/**
1597
 * Get average score by score (NO Exercises in LPs )
1598
 * @param 	int	exercise id
1599
 * @param 	string	course code
1600
 * @param 	int	session id
1601
 * @return 	float	Average score
1602
 */
1603
function get_average_score_by_course($course_code, $session_id) { 
1604
    $user_results = get_all_exercise_results_by_course($course_code, $session_id, false);
1605
    //echo $course_code.' - '.$session_id.'<br />';
1606
    $avg_score = 0;    
1607
    if (!empty($user_results)) {        
1608
        foreach($user_results as $result) {
1609
            if (!empty($result['exe_weighting']) && intval($result['exe_weighting']) != 0) { 
1610
                $score = $result['exe_result']/$result['exe_weighting'];
1611
                //var_dump($score);
1612
                $avg_score +=$score;                               
1613
            }
1614
        }
1615
        //We asume that all exe_weighting
1616
        //$avg_score = show_score( $avg_score / count($user_results) , $result['exe_weighting']);
1617
        $avg_score = ($avg_score / count($user_results));
1618
    }   
1619
    //var_dump($avg_score); 
1620
    return $avg_score;
1621
}
1622

    
1623
function get_average_score_by_course_by_user($user_id, $course_code, $session_id) {
1624
	$user_results = get_all_exercise_results_by_user($user_id, $course_code, $session_id);
1625
	$avg_score = 0;
1626
	if (!empty($user_results)) {
1627
		foreach($user_results as $result) {
1628
			if (!empty($result['exe_weighting']) && intval($result['exe_weighting']) != 0) {
1629
				$score = $result['exe_result']/$result['exe_weighting'];
1630
				$avg_score +=$score;
1631
			}
1632
		}
1633
		//We asume that all exe_weighting
1634
		//$avg_score = show_score( $avg_score / count($user_results) , $result['exe_weighting']);
1635
		$avg_score = ($avg_score / count($user_results));
1636
	}
1637
	return $avg_score;
1638
}
1639

    
1640

    
1641
/**
1642
 * Get average score by score (NO Exercises in LPs )
1643
 * @param 	int		exercise id
1644
 * @param 	string	course code
1645
 * @param 	int		session id
1646
 * @return	float	Best average score
1647
 */
1648
function get_best_average_score_by_exercise($exercise_id, $course_code, $session_id, $user_count) { 
1649
    $user_results = get_best_exercise_results_by_user($exercise_id, $course_code, $session_id);
1650
    $avg_score = 0;
1651
    if (!empty($user_results)) {        
1652
        foreach($user_results as $result) {
1653
            if (!empty($result['exe_weighting']) && intval($result['exe_weighting']) != 0) { 
1654
                $score = $result['exe_result']/$result['exe_weighting'];
1655
                $avg_score +=$score;                
1656
            }
1657
        }
1658
        //We asume that all exe_weighting
1659
        //$avg_score = show_score( $avg_score / count($user_results) , $result['exe_weighting']);
1660
        //$avg_score = ($avg_score / count($user_results));
1661
        if(!empty($user_count)) {
1662
            $avg_score = float_format($avg_score / $user_count, 1) * 100;
1663
        } else {
1664
            $avg_score = 0;
1665
        }
1666
    }
1667
    return $avg_score;
1668
}
1669

    
1670
function get_exercises_to_be_taken($course_code, $session_id) {
1671
	$course_info = api_get_course_info($course_code);
1672
	$exercises = get_all_exercises($course_info, $session_id);
1673
	$result = array();
1674
	$now = time() + 15*24*60*60;
1675
	foreach($exercises as $exercise_item) {
1676
		if (isset($exercise_item['end_time'])  && !empty($exercise_item['end_time']) && $exercise_item['end_time'] != '0000-00-00 00:00:00' && api_strtotime($exercise_item['end_time'], 'UTC') < $now) {
1677
			$result[] = $exercise_item;
1678
		}
1679
	}
1680
	return $result;
1681
}
1682

    
1683
/**
1684
 * Get student results (only in completed exercises) stats by question
1685
 * @param 	int		question id
1686
 * @param 	int		exercise id
1687
 * @param 	string	course code
1688
 * @param 	int		session id
1689
 *  
1690
 * */
1691
function get_student_stats_by_question($question_id,  $exercise_id, $course_code, $session_id) {
1692
	$track_exercises	= Database::get_statistic_table(TABLE_STATISTIC_TRACK_E_EXERCICES);
1693
	$track_attempt		= Database::get_statistic_table(TABLE_STATISTIC_TRACK_E_ATTEMPT);
1694
	
1695
	$question_id 		= intval($question_id);
1696
	$exercise_id 		= intval($exercise_id);
1697
	$course_code 		= Database::escape_string($course_code);
1698
	$session_id 		= intval($session_id);
1699
	 
1700
	$sql = "SELECT MAX(marks) as max , MIN(marks) as min, AVG(marks) as average
1701
			FROM $track_exercises e INNER JOIN $track_attempt a ON (a.exe_id = e.exe_id)
1702
			WHERE 	exe_exo_id 		= $exercise_id AND 
1703
					course_code 	= '$course_code' AND 
1704
					e.session_id 	= $session_id AND
1705
					question_id 	= $question_id AND status = '' LIMIT 1";	
1706
	$result = Database::query($sql);	
1707
	$return = array();
1708
	if ($result) {
1709
		$return = Database::fetch_array($result, 'ASSOC');	
1710
			
1711
	}
1712
	return $return; 	
1713
}
1714

    
1715
function get_number_students_question_with_answer_count($question_id, $exercise_id, $course_code, $session_id) {
1716
	$track_exercises	= Database::get_statistic_table(TABLE_STATISTIC_TRACK_E_EXERCICES);
1717
	$track_attempt		= Database::get_statistic_table(TABLE_STATISTIC_TRACK_E_ATTEMPT);
1718
    $course_user        = Database::get_main_table(TABLE_MAIN_COURSE_USER);
1719

    
1720
	$question_id 		= intval($question_id);
1721
	$exercise_id 		= intval($exercise_id);
1722
	$course_code 		= Database::escape_string($course_code);
1723
	$session_id 		= intval($session_id);
1724
  
1725

    
1726
	$sql = "SELECT DISTINCT exe_user_id
1727
			FROM $track_exercises e INNER JOIN $track_attempt a ON (a.exe_id = e.exe_id) INNER JOIN $course_user cu
1728
                ON cu.course_code = a.course_code AND cu.user_id  = exe_user_id
1729
			WHERE 	exe_exo_id 		= $exercise_id AND
1730
					a.course_code 	= '$course_code' AND
1731
					e.session_id 	= $session_id AND            
1732
					question_id 	= $question_id AND
1733
                    answer          <> '0' AND 
1734
                    cu.status       = ".STUDENT." AND 
1735
                    relation_type  <> 2 AND
1736
                    e.status        = ''";    
1737
	$result = Database::query($sql);
1738
	$return = 0;
1739
	if ($result) {
1740
		$return = Database::num_rows($result);
1741
	}
1742
	return $return;
1743
}
1744

    
1745
function get_number_students_answer_hotspot_count($answer_id, $question_id,  $exercise_id, $course_code, $session_id) {
1746
	$track_exercises	= Database::get_statistic_table(TABLE_STATISTIC_TRACK_E_EXERCICES);
1747
	$track_hotspot		= Database::get_statistic_table(TABLE_STATISTIC_TRACK_E_HOTSPOT);
1748
    $course_user        = Database::get_main_table(TABLE_MAIN_COURSE_USER);
1749

    
1750
	$question_id 		= intval($question_id);
1751
    $answer_id          = intval($answer_id);
1752
	$exercise_id 		= intval($exercise_id);
1753
	$course_code 		= Database::escape_string($course_code);
1754
	$session_id 		= intval($session_id);    
1755

    
1756
	$sql = "SELECT DISTINCT exe_user_id
1757
			FROM $track_exercises e INNER JOIN $track_hotspot a ON (a.hotspot_exe_id = e.exe_id) INNER JOIN $course_user cu
1758
                ON cu.course_code = a.hotspot_course_code AND cu.user_id  = exe_user_id
1759
			WHERE 	exe_exo_id              = $exercise_id AND
1760
					a.hotspot_course_code 	= '$course_code' AND
1761
					e.session_id            = $session_id AND
1762
                    hotspot_answer_id       = $answer_id AND                    
1763
					hotspot_question_id     = $question_id AND 
1764
                    cu.status               = ".STUDENT." AND
1765
                    hotspot_correct         =  1 AND
1766
                    relation_type           <> 2 AND
1767
                    e.status                = ''";
1768
    
1769
	$result = Database::query($sql);
1770
	$return = 0;
1771
	if ($result) {
1772
		$return = Database::num_rows($result);
1773
	}
1774
	return $return;
1775
}
1776

    
1777

    
1778
function get_number_students_answer_count($answer_id, $question_id, $exercise_id, $course_code, $session_id, $question_type = null, $correct_answer = null, $current_answer = null) {
1779
	$track_exercises	= Database::get_statistic_table(TABLE_STATISTIC_TRACK_E_EXERCICES);
1780
	$track_attempt		= Database::get_statistic_table(TABLE_STATISTIC_TRACK_E_ATTEMPT);
1781
    $course_user        = Database::get_main_table(TABLE_MAIN_COURSE_USER);
1782

    
1783
	$question_id 		= intval($question_id);
1784
    $answer_id          = intval($answer_id);
1785
	$exercise_id 		= intval($exercise_id);
1786
	$course_code 		= Database::escape_string($course_code);
1787
	$session_id 		= intval($session_id);    
1788
    
1789
    switch ($question_type) {
1790
        case FILL_IN_BLANKS:        
1791
            $answer_condition = "";
1792
            $select_condition = " e.exe_id, answer ";
1793
            break;
1794
        case MATCHING:    
1795
        default:
1796
            $answer_condition = " answer = $answer_id AND ";
1797
            $select_condition = " DISTINCT exe_user_id ";
1798
    }
1799
    
1800
	$sql = "SELECT $select_condition
1801
			FROM $track_exercises e INNER JOIN $track_attempt a ON (a.exe_id = e.exe_id) INNER JOIN $course_user cu
1802
                ON cu.course_code = a.course_code AND cu.user_id  = exe_user_id
1803
			WHERE 	exe_exo_id 		= $exercise_id AND
1804
					a.course_code 	= '$course_code' AND
1805
					e.session_id 	= $session_id AND
1806
                    $answer_condition                              
1807
					question_id 	= $question_id AND 
1808
                    cu.status        = ".STUDENT." AND                     
1809
                    relation_type <> 2 AND
1810
                    e.status = ''";
1811
    //var_dump($sql);
1812
	$result = Database::query($sql);
1813
	$return = 0;
1814
	if ($result) {
1815
        $good_answers = 0;
1816
        switch ($question_type) {
1817
            case FILL_IN_BLANKS:                
1818
                while ($row = Database::fetch_array($result, 'ASSOC')) {
1819
                    $fill_blank = check_fill_in_blanks($correct_answer, $row['answer']);
1820
                    if (isset($fill_blank[$current_answer]) && $fill_blank[$current_answer] == 1 ) {                    
1821
                        $good_answers++;
1822
                    }
1823
                }
1824
                return $good_answers;
1825
                break;
1826
            case MATCHING:  
1827
            default:
1828
                $return = Database::num_rows($result);                
1829
        }
1830
	}
1831
	return $return;
1832
}
1833

    
1834

    
1835
function check_fill_in_blanks($answer, $user_answer) {    
1836
    // the question is encoded like this
1837
    // [A] B [C] D [E] F::10,10,10@1
1838
    // number 1 before the "@" means that is a switchable fill in blank question
1839
    // [A] B [C] D [E] F::10,10,10@ or  [A] B [C] D [E] F::10,10,10
1840
    // means that is a normal fill blank question
1841
    // first we explode the "::"
1842
    $pre_array = explode('::', $answer);
1843
    // is switchable fill blank or not
1844
    $last = count($pre_array) - 1;
1845
    $is_set_switchable = explode('@', $pre_array[$last]);
1846
    $switchable_answer_set = false;
1847
    if (isset ($is_set_switchable[1]) && $is_set_switchable[1] == 1) {
1848
        $switchable_answer_set = true;
1849
    }
1850
    $answer = '';
1851
    for ($k = 0; $k < $last; $k++) {
1852
        $answer .= $pre_array[$k];
1853
    }
1854
    // splits weightings that are joined with a comma
1855
    $answerWeighting = explode(',', $is_set_switchable[0]);
1856

    
1857
    // we save the answer because it will be modified
1858
    //$temp = $answer;
1859
    $temp = $answer;
1860

    
1861
    $answer = '';
1862
    $j = 0;
1863
    //initialise answer tags
1864
    $user_tags = $correct_tags = $real_text = array ();
1865
    // the loop will stop at the end of the text
1866
    while (1) {
1867
        // quits the loop if there are no more blanks (detect '[')
1868
        if (($pos = api_strpos($temp, '[')) === false) {
1869
            // adds the end of the text
1870
            $answer = $temp;
1871
            /* // Deprecated code
1872
             // TeX parsing - replacement of texcode tags
1873
            $texstring = api_parse_tex($texstring);
1874
            $answer = str_replace("{texcode}", $texstring, $answer);
1875
            */
1876
            $real_text[] = $answer;
1877
            break; //no more "blanks", quit the loop
1878
        }
1879
        // adds the piece of text that is before the blank
1880
        //and ends with '[' into a general storage array
1881
        $real_text[] = api_substr($temp, 0, $pos +1);
1882
        $answer .= api_substr($temp, 0, $pos +1);
1883
        //take the string remaining (after the last "[" we found)
1884
        $temp = api_substr($temp, $pos +1);
1885
        // quit the loop if there are no more blanks, and update $pos to the position of next ']'
1886
        if (($pos = api_strpos($temp, ']')) === false) {
1887
            // adds the end of the text
1888
            $answer .= $temp;
1889
            break;
1890
        }
1891
        
1892
        $str = $user_answer;
1893

    
1894
        preg_match_all('#\[([^[]*)\]#', $str, $arr);
1895
        $str = str_replace('\r\n', '', $str);
1896
        $choice = $arr[1];
1897

    
1898
        $tmp = api_strrpos($choice[$j],' / ');
1899
        $choice[$j] = api_substr($choice[$j],0,$tmp);
1900
        $choice[$j] = trim($choice[$j]);
1901

    
1902
        //Needed to let characters ' and " to work as part of an answer
1903
        $choice[$j] = stripslashes($choice[$j]);
1904

    
1905
        $user_tags[] = api_strtolower($choice[$j]);
1906
        //put the contents of the [] answer tag into correct_tags[]
1907
        $correct_tags[] = api_strtolower(api_substr($temp, 0, $pos));
1908
        $j++;
1909
        $temp = api_substr($temp, $pos +1);        
1910
    }
1911
    
1912
    $answer = '';
1913
    $real_correct_tags = $correct_tags;
1914
    $chosen_list = array();
1915
    
1916
    $good_answer = array();
1917
    
1918
    for ($i = 0; $i < count($real_correct_tags); $i++) {        
1919
        if (!$switchable_answer_set) {
1920
            //needed to parse ' and " characters
1921
            $user_tags[$i] = stripslashes($user_tags[$i]);
1922
            if ($correct_tags[$i] == $user_tags[$i]) {                    
1923
                $good_answer[$correct_tags[$i]] = 1;
1924
            } elseif (!empty ($user_tags[$i])) {                
1925
                $good_answer[$correct_tags[$i]] = 0;
1926
            } else {
1927
                $good_answer[$correct_tags[$i]] = 0;
1928
            }
1929
        } else {
1930
            // switchable fill in the blanks
1931
            if (in_array($user_tags[$i], $correct_tags)) {                
1932
                $correct_tags = array_diff($correct_tags, $chosen_list);
1933
                $good_answer[$correct_tags[$i]] = 1;
1934
            } elseif (!empty ($user_tags[$i])) {                
1935
                $good_answer[$correct_tags[$i]] = 0;
1936
            } else {                
1937
                $good_answer[$correct_tags[$i]] = 0;
1938
            }
1939
        }
1940
        // adds the correct word, followed by ] to close the blank
1941
        $answer .= ' / <font color="green"><b>' . $real_correct_tags[$i] . '</b></font>]';
1942
        if (isset ($real_text[$i +1])) {
1943
            $answer .= $real_text[$i +1];
1944
        }
1945
    }    
1946
    return $good_answer;    
1947
}
1948

    
1949

    
1950
function get_number_students_finish_exercise($exercise_id, $course_code, $session_id) {
1951
	$track_exercises	= Database::get_statistic_table(TABLE_STATISTIC_TRACK_E_EXERCICES);
1952
	$track_attempt		= Database::get_statistic_table(TABLE_STATISTIC_TRACK_E_ATTEMPT);
1953

    
1954
    $exercise_id 		= intval($exercise_id);
1955
	$course_code 		= Database::escape_string($course_code);
1956
	$session_id 		= intval($session_id);
1957

    
1958
	$sql = "SELECT DISTINCT exe_user_id
1959
			FROM $track_exercises e INNER JOIN $track_attempt a ON (a.exe_id = e.exe_id)
1960
			WHERE 	exe_exo_id 		= $exercise_id AND
1961
					course_code 	= '$course_code' AND
1962
					e.session_id 	= $session_id AND                    
1963
					status = ''";
1964
	$result = Database::query($sql);
1965
	$return = 0;
1966
	if ($result) {
1967
		$return = Database::num_rows($result);
1968

    
1969
	}
1970
	return $return;
1971
}
1972

    
1973

    
1974

    
1975
/**
1976
// return the HTML code for a menu with students group
1977
// @input : $in_name : is the name and the id of the <select>
1978
//          $in_default : default value for option
1979
// @return : the html code of the <select>
1980
*/
1981
function displayGroupMenu($in_name, $in_default, $in_onchange="") {
1982
    // check the default value of option
1983
    $tabSelected = array($in_default => " selected='selected' ");
1984
    $res = "";
1985
    $res .= "<select name='$in_name' id='$in_name' onchange='".$in_onchange."' >";
1986
    $res .= "<option value='-1'".$tabSelected["-1"].">-- ".get_lang('AllGroups')." --</option>";
1987
    $res .= "<option value='0'".$tabSelected["0"].">- ".get_lang('NotInAGroup')." -</option>";
1988
    $tabGroups = GroupManager::get_group_list();
1989
    $currentCatId = 0;
1990
    for ($i=0; $i < count($tabGroups); $i++) {
1991
        $tabCategory = GroupManager::get_category_from_group($tabGroups[$i]["id"]);
1992
        if ($tabCategory["id"] != $currentCatId) {
1993
            $res .= "<option value='-1' disabled='disabled'>".$tabCategory["title"]."</option>";
1994
            $currentCatId = $tabCategory["id"];
1995
        }
1996
        $res .= "<option ".$tabSelected[$tabGroups[$i]["id"]]."style='margin-left:40px' value='".$tabGroups[$i]["id"]."'>".$tabGroups[$i]["name"]."</option>";
1997
    }
1998
    $res .= "</select>";
1999
    return $res;
2000
}
2001

    
2002

    
2003
/** 
2004
 * Return a list of group for user with user_id=in_userid separated with in_separator 
2005
 * @deprecated ?
2006
 */
2007
function displayGroupsForUser($in_separator, $in_userid) {
2008
    $res = implode($in_separator, GroupManager::get_user_group_name($in_userid));
2009
    if ($res == "") {
2010
        $res = "<div style='text-align:center'>-</div>";
2011
    }
2012
    return $res;
2013
}
2014

    
2015
function create_chat_exercise_session($exe_id) {
2016
    if (!isset($_SESSION['current_exercises'])) {
2017
        $_SESSION['current_exercises'] = array();
2018
    }
2019
    $_SESSION['current_exercises'][$exe_id] = true;
2020
}
2021

    
2022
function delete_chat_exercise_session($exe_id) {    
2023
    if (isset($_SESSION['current_exercises'])) {
2024
        $_SESSION['current_exercises'][$exe_id] = false;
2025
    }    
2026
}
2027

    
2028
/**
2029
 * Display the exercise results
2030
 * @param obj   exercise obj
2031
 * @param int   attempt id (exe_id)
2032
 * @param bool  save users results (true) or just show the results (false)
2033
 */
2034
function display_question_list_by_attempt($objExercise, $exe_id, $save_user_result = false) {
2035
    global $origin, $debug;    
2036
    
2037
    //Getting attempt info
2038
    $exercise_stat_info = $objExercise->get_stat_track_exercise_info_by_exe_id($exe_id);
2039
    $question_list = array();
2040
    if (!empty($exercise_stat_info['data_tracking'])) {
2041
        $question_list		= explode(',', $exercise_stat_info['data_tracking']);
2042
    }
2043
    
2044
    $counter = 1;
2045
    $total_score = $total_weight = 0;
2046
    
2047
    $exercise_content = null;    
2048
    
2049
    //Hide results
2050
    $show_results     = false;
2051
    $show_only_score  = false;
2052

    
2053
    if ($objExercise->results_disabled == EXERCISE_FEEDBACK_TYPE_END) {
2054
        $show_results = true;
2055
    }
2056

    
2057
    if ($objExercise->results_disabled == EXERCISE_FEEDBACK_TYPE_EXAM) {
2058
        $show_only_score = true;
2059
    }
2060

    
2061
    if ($show_results || $show_only_score) {
2062
        $user_info   = api_get_user_info($exercise_stat_info['exe_user_id']);
2063
        //Shows exercise header
2064
        $objExercise->description = '';
2065
        echo $objExercise->show_exercise_result_header(api_get_person_name($user_info['firstName'], $user_info['lastName']), api_convert_and_format_date($exercise_date, DATE_TIME_FORMAT_LONG));
2066
    }
2067
    
2068
    if ($save_user_result) {    
2069
        // Display text when test is finished #4074
2070
        // Don't display the text when finished message if we are from a LP #4227
2071
        // but display it from page exercice_show.php
2072
        $end_of_message = $objExercise->selectTextWhenFinished();
2073
        if (!empty($end_of_message) && ($origin != 'learnpath')) {
2074
            Display::display_normal_message($end_of_message, false);
2075
            echo "<div class='clear'>&nbsp;</div>";
2076
        }
2077
    }
2078
    
2079
    $question_list_answers = array();
2080

    
2081
    // Loop over all question to show results for each of them, one by one
2082
    if (!empty($question_list)) {
2083
        if ($debug) { error_log('Looping question_list '.print_r($question_list,1));}
2084
        foreach ($question_list as $questionId) {
2085

    
2086
            // creates a temporary Question object
2087
            $objQuestionTmp = Question :: read($questionId);
2088

    
2089
            //this variable commes from exercise_submit_modal.php
2090

    
2091
            //$hotspot_delineation_result = $_SESSION['hotspot_delineation_result'][$objExercise->selectId()][$quesId];
2092
            ob_start();
2093
            
2094
            // We're inside *one* question. Go through each possible answer for this question        
2095
            $result = $objExercise->manage_answer($exercise_stat_info['exe_id'], $questionId, null ,'exercise_result', array(), false, true, $show_results, $objExercise->selectPropagateNeg(), $hotspot_delineation_result);
2096
   
2097
            $total_score     += $result['score'];
2098
            $total_weight    += $result['weight'];
2099
            
2100
            $question_list_answers[] = array('question' => $result['open_question'], 'answer' => $result['open_answer']);            
2101
            
2102
            $my_total_score  = $result['score'];
2103
            $my_total_weight = $result['weight'];   
2104

    
2105
            if ($objExercise->selectPropagateNeg() == 0 && $my_total_score < 0) {
2106
                $my_total_score = 0;
2107
            }
2108

    
2109
            $comnt = null;    
2110
            if ($show_results) {
2111
                $comnt = get_comments($exe_id, $questionId);		
2112
                if (!empty($comnt)) {
2113
                    echo '<b>'.get_lang('Feedback').'</b>';
2114
                    echo '<div id="question_feedback">'.$comnt.'</div>';
2115
                }		
2116
            }
2117
            
2118
            $score = array();    
2119
            if ($show_results) {	    
2120
                $score['result'] = get_lang('Score')." : ".show_score($my_total_score, $my_total_weight, false, false);
2121
                $score['pass'] = $my_total_score >= $my_total_weight ? true : false;
2122
                $score['score'] = $my_total_score;
2123
                $score['weight'] = $my_total_weight;
2124
                $score['comments'] = $comnt;                
2125
            }
2126
            
2127
            $contents = ob_get_clean();
2128
    
2129
            $question_content = '<div class="question_row">';
2130

    
2131
            if ($show_results) {           
2132
                //Shows question title an description
2133
                $question_content .= $objQuestionTmp->return_header(null, $counter, $score);
2134
                
2135
                // display question category, if any
2136
                $question_content .= Testcategory::returnCategoryAndTitle($questionId);
2137
            }
2138
            $counter++;
2139
            
2140
            $question_content .= $contents;
2141
            $question_content .= '</div>';
2142
            
2143
            $exercise_content .= $question_content;
2144
            
2145
        } // end foreach() block that loops over all questions
2146
    }
2147
    
2148
    $total_score_text = null;
2149
    
2150
    if ($origin != 'learnpath') {
2151
        if ($show_results || $show_only_score) {
2152
            
2153
            $is_success = is_success_exercise_result($total_score, $total_weight, $objExercise->selectPassPercentage());
2154
            $total_score_text .= '<div class="question_row">';
2155
            $total_score_text .= '<div class="ribbon ribbon-total ">';
2156
            
2157
            // Color the final test score if pass_percentage activated
2158
            $ribbon_total_success_or_error = "";
2159
            if (is_pass_pourcentage_enabled($objExercise->selectPassPercentage())) {
2160
                if ($is_success) {
2161
                    $ribbon_total_success_or_error = ' ribbon-total-success';
2162
                } else {
2163
                    $ribbon_total_success_or_error = ' ribbon-total-error';
2164
                }
2165
            }
2166
            $total_score_text .= '<div class="rib rib-total $ribbon_total_success_or_error">';
2167
            
2168
            $total_score_text .= '<h3>';
2169
            $total_score_text .= get_lang('YourTotalScore')."&nbsp;";
2170
            if ($objExercise->selectPropagateNeg() == 0 && $total_score < 0) {
2171
                $total_score = 0;
2172
            }
2173
            $total_score_text .= show_score($total_score, $total_weight, false, true);
2174
            $total_score_text .= '</h3>';            
2175
            $total_score_text .= '</div>';
2176
            $total_score_text .= show_success_message($total_score, $total_weight, $objExercise->selectPassPercentage());
2177
            
2178
            $total_score_text .= '</div>';
2179
            $total_score_text .= '</div>';
2180
        }
2181
    }
2182
    
2183
    echo $total_score_text;   
2184
    echo $exercise_content;    
2185
    if (!$show_only_score) {
2186
        echo $total_score_text;
2187
    }
2188
    
2189
    if ($save_user_result) {
2190
        
2191
        // Tracking of results
2192
        $learnpath_id           = $exercise_stat_info['orig_lp_id'];
2193
        $learnpath_item_id      = $exercise_stat_info['orig_lp_item_id'];
2194
        $learnpath_item_view_id = $exercise_stat_info['orig_lp_item_view_id'];
2195
        
2196
        if (api_is_allowed_to_session_edit()) {    
2197
            update_event_exercice($exercise_stat_info['exe_id'], $objExercise->selectId(), $total_score, $total_weight, api_get_session_id(), $learnpath_id, $learnpath_item_id, $learnpath_item_view_id, $exercise_stat_info['exe_duration'], $question_list, '', array(), $end_date);
2198
        }
2199
        
2200
        // Send notification ..
2201
        if (!api_is_allowed_to_edit(null,true)) {
2202
            $objExercise->send_notification_for_open_questions($question_list_answers, $origin, $exe_id);
2203
        }
2204
    }
2205
}
(1-1/4)